authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-12 23:59:19+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-12 23:59:19+01:00
log8bd01eb7a914416a772b365dc75d83890067e26c
tree712c0c17006e6fc20ee158542df0d585a245530b
parent616a8f9853e461400f6f5c49dede3bf3a0ca25b6

elf: refactor archive specific object parsing logic


3 files changed, 97 insertions(+), 53 deletions(-)

src/link/Elf.zig+6-12
......@@ -1075,6 +1075,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
10751075 // --verbose-link
10761076 if (comp.verbose_link) try self.dumpArgv(comp);
10771077
1078 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1079 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
1080 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
1081
10781082 const csu = try CsuObjects.init(arena, comp);
10791083 const compiler_rt_path: ?[]const u8 = blk: {
10801084 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)
10821086 break :blk null;
10831087 };
10841088
1085 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1086 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
1087 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
1088
10891089 // Here we will parse input positional and library files (if referenced).
10901090 // This will roughly match in any linker backend we support.
10911091 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
......@@ -1249,13 +1249,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
12491249
12501250 if (comp.link_errors.items.len > 0) return error.FlushFailure;
12511251
1252 // Init all objects
1253 for (self.objects.items) |index| {
1254 try self.file(index).?.object.init(self);
1255 }
1256
1257 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1258
12591252 // Dedup shared objects
12601253 {
12611254 var seen_dsos = std.StringHashMap(void).init(gpa);
......@@ -1651,7 +1644,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
16511644 Compilation.dump_argv(argv.items);
16521645}
16531646
1654const ParseError = error{
1647pub const ParseError = error{
16551648 MalformedObject,
16561649 MalformedArchive,
16571650 InvalidCpuArch,
......@@ -1662,6 +1655,7 @@ const ParseError = error{
16621655 FileSystem,
16631656 NotSupported,
16641657 InvalidCharacter,
1658 UnknownFileType,
16651659} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
16661660
16671661pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
src/link/Elf/Object.zig+35-30
......@@ -55,12 +55,26 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
5555
5656pub fn parse(self: *Object, elf_file: *Elf) !void {
5757 const gpa = elf_file.base.comp.gpa;
58 const offset = if (self.archive) |ar| ar.offset else 0;
5958 const handle = elf_file.fileHandle(self.file_handle);
59
60 try self.parseCommon(gpa, handle, elf_file);
61 try self.initAtoms(gpa, handle, elf_file);
62 try self.initSymtab(gpa, elf_file);
63
64 for (self.shdrs.items, 0..) |shdr, i| {
65 const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
66 if (!atom.flags.alive) continue;
67 if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
68 try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
69 }
70}
71
72fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
73 const offset = if (self.archive) |ar| ar.offset else 0;
6074 const file_size = (try handle.stat()).size;
6175
62 const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr));
63 defer gpa.free(header_buffer);
76 const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr));
77 defer allocator.free(header_buffer);
6478 self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*;
6579
6680 const target = elf_file.base.comp.root_mod.resolved_target.result;
......@@ -87,10 +101,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
87101 return error.MalformedObject;
88102 }
89103
90 const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize);
91 defer gpa.free(shdrs_buffer);
104 const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize);
105 defer allocator.free(shdrs_buffer);
92106 const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum];
93 try self.shdrs.appendUnalignedSlice(gpa, shdrs);
107 try self.shdrs.appendUnalignedSlice(allocator, shdrs);
94108
95109 for (self.shdrs.items) |shdr| {
96110 if (shdr.sh_type != elf.SHT_NOBITS) {
......@@ -101,15 +115,15 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
101115 }
102116 }
103117
104 const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx);
105 defer gpa.free(shstrtab);
118 const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx);
119 defer allocator.free(shstrtab);
106120 for (self.shdrs.items) |shdr| {
107121 if (shdr.sh_name >= shstrtab.len) {
108122 try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});
109123 return error.MalformedObject;
110124 }
111125 }
112 try self.strtab.appendSlice(gpa, shstrtab);
126 try self.strtab.appendSlice(allocator, shstrtab);
113127
114128 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
115129 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
......@@ -120,8 +134,8 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
120134 const shdr = self.shdrs.items[index];
121135 self.first_global = shdr.sh_info;
122136
123 const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index);
124 defer gpa.free(raw_symtab);
137 const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index);
138 defer allocator.free(raw_symtab);
125139 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
126140 try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});
127141 return error.MalformedObject;
......@@ -129,11 +143,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
129143 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
130144
131145 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
132 const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link);
133 defer gpa.free(strtab);
134 try self.strtab.appendSlice(gpa, strtab);
146 const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link);
147 defer allocator.free(strtab);
148 try self.strtab.appendSlice(allocator, strtab);
135149
136 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
150 try self.symtab.ensureUnusedCapacity(allocator, symtab.len);
137151 for (symtab) |sym| {
138152 const out_sym = self.symtab.addOneAssumeCapacity();
139153 out_sym.* = sym;
......@@ -145,21 +159,6 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
145159 }
146160}
147161
148pub fn init(self: *Object, elf_file: *Elf) !void {
149 const gpa = elf_file.base.comp.gpa;
150 const handle = elf_file.fileHandle(self.file_handle);
151
152 try self.initAtoms(gpa, handle, elf_file);
153 try self.initSymtab(gpa, elf_file);
154
155 for (self.shdrs.items, 0..) |shdr, i| {
156 const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
157 if (!atom.flags.alive) continue;
158 if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
159 try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file);
160 }
161}
162
163162fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void {
164163 const shdrs = self.shdrs.items;
165164 try self.atoms.resize(allocator, shdrs.len);
......@@ -782,6 +781,12 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void {
782781 }
783782}
784783
784pub fn parseAr(self: *Object, elf_file: *Elf) !void {
785 const gpa = elf_file.base.comp.gpa;
786 const handle = elf_file.fileHandle(self.file_handle);
787 try self.parseCommon(gpa, handle, elf_file);
788}
789
785790pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
786791 const comp = elf_file.base.comp;
787792 const gpa = comp.gpa;
src/link/Elf/relocatable.zig+56-11
......@@ -7,18 +7,20 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
77 try positionals.ensureUnusedCapacity(comp.objects.len);
88 positionals.appendSliceAssumeCapacity(comp.objects);
99
10 // This is a set of object files emitted by clang in a single `build-exe` invocation.
11 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
12 // in this set.
1310 for (comp.c_object_table.keys()) |key| {
1411 try positionals.append(.{ .path = key.status.success.object_path });
1512 }
1613
1714 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1815
16 if (comp.include_compiler_rt) {
17 try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path });
18 }
19
1920 for (positionals.items) |obj| {
20 elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
21 parsePositional(elf_file, obj.path) catch |err| switch (err) {
2122 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
23 error.UnknownFileType => try elf_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
2224 else => |e| try elf_file.reportParseError(
2325 obj.path,
2426 "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
172174
173175 if (comp.link_errors.items.len > 0) return error.FlushFailure;
174176
175 // Init all objects
176 for (elf_file.objects.items) |index| {
177 try elf_file.file(index).?.object.init(elf_file);
178 }
179
180 if (comp.link_errors.items.len > 0) return error.FlushFailure;
181
182177 // Now, we are ready to resolve the symbols across all input files.
183178 // We will first resolve the files in the ZigObject, next in the parsed
184179 // input Object files.
......@@ -214,6 +209,55 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
214209 if (comp.link_errors.items.len > 0) return error.FlushFailure;
215210}
216211
212fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
213 if (try Object.isObject(path)) {
214 try parseObject(elf_file, path);
215 } else if (try Archive.isArchive(path)) {
216 try parseArchive(elf_file, path);
217 } else return error.UnknownFileType;
218 // TODO: should we check for LD script?
219 // Actually, should we even unpack an archive?
220}
221
222fn parseObject(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
223 const gpa = elf_file.base.comp.gpa;
224 const handle = try std.fs.cwd().openFile(path, .{});
225 const fh = try elf_file.addFileHandle(handle);
226
227 const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
228 elf_file.files.set(index, .{ .object = .{
229 .path = try gpa.dupe(u8, path),
230 .file_handle = fh,
231 .index = index,
232 } });
233 try elf_file.objects.append(gpa, index);
234
235 const object = elf_file.file(index).?.object;
236 try object.parseAr(elf_file);
237}
238
239fn parseArchive(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
240 const gpa = elf_file.base.comp.gpa;
241 const handle = try std.fs.cwd().openFile(path, .{});
242 const fh = try elf_file.addFileHandle(handle);
243
244 var archive = Archive{};
245 defer archive.deinit(gpa);
246 try archive.parse(elf_file, path, fh);
247
248 const objects = try archive.objects.toOwnedSlice(gpa);
249 defer gpa.free(objects);
250
251 for (objects) |extracted| {
252 const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
253 elf_file.files.set(index, .{ .object = extracted });
254 const object = &elf_file.files.items(.data)[index].object;
255 object.index = index;
256 try object.parseAr(elf_file);
257 try elf_file.objects.append(gpa, index);
258 }
259}
260
217261fn claimUnresolved(elf_file: *Elf) void {
218262 if (elf_file.zigObjectPtr()) |zig_object| {
219263 zig_object.claimUnresolvedObject(elf_file);
......@@ -518,3 +562,4 @@ const Archive = @import("Archive.zig");
518562const Compilation = @import("../../Compilation.zig");
519563const Elf = @import("../Elf.zig");
520564const File = @import("file.zig").File;
565const Object = @import("Object.zig");