srctree

Jakub Konka parent 616a8f98 8bd01eb7
elf: refactor archive specific object parsing logic

inlinesplit
src/link/Elf.zig added: 100, removed: 56, total 44
@@ -1075,6 +1075,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
// --verbose-link
if (comp.verbose_link) try self.dumpArgv(comp);
 
if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
 
const csu = try CsuObjects.init(arena, comp);
const compiler_rt_path: ?[]const u8 = blk: {
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
@@ -1082,10 +1086,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
break :blk null;
};
 
if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
 
// Here we will parse input positional and library files (if referenced).
// This will roughly match in any linker backend we support.
var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
@@ -1249,13 +1249,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
 
if (comp.link_errors.items.len > 0) return error.FlushFailure;
 
// Init all objects
for (self.objects.items) |index| {
try self.file(index).?.object.init(self);
}
 
if (comp.link_errors.items.len > 0) return error.FlushFailure;
 
// Dedup shared objects
{
var seen_dsos = std.StringHashMap(void).init(gpa);
@@ -1651,7 +1644,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
Compilation.dump_argv(argv.items);
}
 
const ParseError = error{
pub const ParseError = error{
MalformedObject,
MalformedArchive,
InvalidCpuArch,
@@ -1662,6 +1655,7 @@ const ParseError = error{
FileSystem,
NotSupported,
InvalidCharacter,
UnknownFileType,
} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
 
pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
 
src/link/Elf/Object.zig added: 100, removed: 56, total 44
@@ -55,12 +55,26 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
 
pub fn parse(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
const offset = if (self.archive) |ar| ar.offset else 0;
const handle = elf_file.fileHandle(self.file_handle);
 
try self.parseCommon(gpa, handle, elf_file);
try self.initAtoms(gpa, handle, elf_file);
try self.initSymtab(gpa, elf_file);
 
for (self.shdrs.items, 0..) |shdr, i| {
const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
if (!atom.flags.alive) continue;
if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
}
}
 
fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
const offset = if (self.archive) |ar| ar.offset else 0;
const file_size = (try handle.stat()).size;
 
const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr));
defer gpa.free(header_buffer);
const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr));
defer allocator.free(header_buffer);
self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
 
const target = elf_file.base.comp.root_mod.resolved_target.result;
@@ -87,10 +101,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
return error.MalformedObject;
}
 
const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize);
defer gpa.free(shdrs_buffer);
const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize);
defer allocator.free(shdrs_buffer);
const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
try self.shdrs.appendUnalignedSlice(gpa, shdrs);
try self.shdrs.appendUnalignedSlice(allocator, shdrs);
 
for (self.shdrs.items) |shdr| {
if (shdr.sh_type != elf.SHT_NOBITS) {
@@ -101,15 +115,15 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
}
}
 
const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx);
defer gpa.free(shstrtab);
const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx);
defer allocator.free(shstrtab);
for (self.shdrs.items) |shdr| {
if (shdr.sh_name >= shstrtab.len) {
try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});
return error.MalformedObject;
}
}
try self.strtab.appendSlice(gpa, shstrtab);
try self.strtab.appendSlice(allocator, shstrtab);
 
const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
@@ -120,8 +134,8 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
const shdr = self.shdrs.items[index];
self.first_global = shdr.sh_info;
 
const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index);
defer gpa.free(raw_symtab);
const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index);
defer allocator.free(raw_symtab);
const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});
return error.MalformedObject;
@@ -129,11 +143,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
 
const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link);
defer gpa.free(strtab);
try self.strtab.appendSlice(gpa, strtab);
const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link);
defer allocator.free(strtab);
try self.strtab.appendSlice(allocator, strtab);
 
try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
try self.symtab.ensureUnusedCapacity(allocator, symtab.len);
for (symtab) |sym| {
const out_sym = self.symtab.addOneAssumeCapacity();
out_sym.* = sym;
@@ -145,21 +159,6 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
}
}
 
pub fn init(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
const handle = elf_file.fileHandle(self.file_handle);
 
try self.initAtoms(gpa, handle, elf_file);
try self.initSymtab(gpa, elf_file);
 
for (self.shdrs.items, 0..) |shdr, i| {
const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
if (!atom.flags.alive) continue;
if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
}
}
 
fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
const shdrs = self.shdrs.items;
try self.atoms.resize(allocator, shdrs.len);
@@ -782,6 +781,12 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void {
}
}
 
pub fn parseAr(self: *Object, elf_file: *Elf) !void {
const gpa = elf_file.base.comp.gpa;
const handle = elf_file.fileHandle(self.file_handle);
try self.parseCommon(gpa, handle, elf_file);
}
 
pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
 
src/link/Elf/relocatable.zig added: 100, removed: 56, total 44
@@ -7,18 +7,20 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
try positionals.ensureUnusedCapacity(comp.objects.len);
positionals.appendSliceAssumeCapacity(comp.objects);
 
// This is a set of object files emitted by clang in a single `build-exe` invocation.
// For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
// in this set.
for (comp.c_object_table.keys()) |key| {
try positionals.append(.{ .path = key.status.success.object_path });
}
 
if (module_obj_path) |path| try positionals.append(.{ .path = path });
 
if (comp.include_compiler_rt) {
try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });
}
 
for (positionals.items) |obj| {
elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
parsePositional(elf_file, obj.path) catch |err| switch (err) {
error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
error.UnknownFileType => try elf_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
else => |e| try elf_file.reportParseError(
obj.path,
"unexpected error: parsing input file failed with error {s}",
@@ -172,13 +174,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
 
if (comp.link_errors.items.len > 0) return error.FlushFailure;
 
// Init all objects
for (elf_file.objects.items) |index| {
try elf_file.file(index).?.object.init(elf_file);
}
 
if (comp.link_errors.items.len > 0) return error.FlushFailure;
 
// Now, we are ready to resolve the symbols across all input files.
// We will first resolve the files in the ZigObject, next in the parsed
// input Object files.
@@ -214,6 +209,55 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
if (comp.link_errors.items.len > 0) return error.FlushFailure;
}
 
fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
if (try Object.isObject(path)) {
try parseObject(elf_file, path);
} else if (try Archive.isArchive(path)) {
try parseArchive(elf_file, path);
} else return error.UnknownFileType;
// TODO: should we check for LD script?
// Actually, should we even unpack an archive?
}
 
fn parseObject(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
const gpa = elf_file.base.comp.gpa;
const handle = try std.fs.cwd().openFile(path, .{});
const fh = try elf_file.addFileHandle(handle);
 
const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
elf_file.files.set(index, .{ .object = .{
.path = try gpa.dupe(u8, path),
.file_handle = fh,
.index = index,
} });
try elf_file.objects.append(gpa, index);
 
const object = elf_file.file(index).?.object;
try object.parseAr(elf_file);
}
 
fn parseArchive(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
const gpa = elf_file.base.comp.gpa;
const handle = try std.fs.cwd().openFile(path, .{});
const fh = try elf_file.addFileHandle(handle);
 
var archive = Archive{};
defer archive.deinit(gpa);
try archive.parse(elf_file, path, fh);
 
const objects = try archive.objects.toOwnedSlice(gpa);
defer gpa.free(objects);
 
for (objects) |extracted| {
const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
elf_file.files.set(index, .{ .object = extracted });
const object = &elf_file.files.items(.data)[index].object;
object.index = index;
try object.parseAr(elf_file);
try elf_file.objects.append(gpa, index);
}
}
 
fn claimUnresolved(elf_file: *Elf) void {
if (elf_file.zigObjectPtr()) |zig_object| {
zig_object.claimUnresolvedObject(elf_file);
@@ -518,3 +562,4 @@ const Archive = @import("Archive.zig");
const Compilation = @import("../../Compilation.zig");
const Elf = @import("../Elf.zig");
const File = @import("file.zig").File;
const Object = @import("Object.zig");