authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-11 22:49:42+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-11 22:49:42+02:00
log67d458370dfc87753e9938e7af94cb86e3bc98cc
tree1c6a86449cc968bc28f74d59743cea792c423c5e
parent3df58a95835fe2f9ba16e9ff9e0d03c39d263bf3

elf: add prelim impl of Object parsing


8 files changed, 1459 insertions(+), 40 deletions(-)

src/link/Elf.zig+145-16
...@@ -9,6 +9,7 @@ llvm_object: ?*LlvmObject = null,...@@ -9,6 +9,7 @@ llvm_object: ?*LlvmObject = null,
9files: std.MultiArrayList(File.Entry) = .{},9files: std.MultiArrayList(File.Entry) = .{},
10zig_module_index: ?File.Index = null,10zig_module_index: ?File.Index = null,
11linker_defined_index: ?File.Index = null,11linker_defined_index: ?File.Index = null,
12objects: std.ArrayListUnmanaged(File.Index) = .{},
1213
13/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.14/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
14/// Same order as in the file.15/// Same order as in the file.
...@@ -51,11 +52,12 @@ got: GotSection = .{},...@@ -51,11 +52,12 @@ got: GotSection = .{},
51text_section_index: ?u16 = null,52text_section_index: ?u16 = null,
52rodata_section_index: ?u16 = null,53rodata_section_index: ?u16 = null,
53data_section_index: ?u16 = null,54data_section_index: ?u16 = null,
55eh_frame_section_index: ?u16 = null,
56eh_frame_hdr_section_index: ?u16 = null,
54dynamic_section_index: ?u16 = null,57dynamic_section_index: ?u16 = null,
55got_section_index: ?u16 = null,58got_section_index: ?u16 = null,
56got_plt_section_index: ?u16 = null,59got_plt_section_index: ?u16 = null,
57plt_section_index: ?u16 = null,60plt_section_index: ?u16 = null,
58eh_frame_hdr_section_index: ?u16 = null,
59rela_dyn_section_index: ?u16 = null,61rela_dyn_section_index: ?u16 = null,
60debug_info_section_index: ?u16 = null,62debug_info_section_index: ?u16 = null,
61debug_abbrev_section_index: ?u16 = null,63debug_abbrev_section_index: ?u16 = null,
...@@ -136,6 +138,10 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre...@@ -136,6 +138,10 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre
136/// with `Decl` `main`, and lives as long as that `Decl`.138/// with `Decl` `main`, and lives as long as that `Decl`.
137unnamed_consts: UnnamedConstTable = .{},139unnamed_consts: UnnamedConstTable = .{},
138140
141comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{},
142comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{},
143comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{},
144
139const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));145const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
140const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);146const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
141147
...@@ -249,10 +255,11 @@ pub fn deinit(self: *Elf) void {...@@ -249,10 +255,11 @@ pub fn deinit(self: *Elf) void {
249 .null => {},255 .null => {},
250 .zig_module => data.zig_module.deinit(gpa),256 .zig_module => data.zig_module.deinit(gpa),
251 .linker_defined => data.linker_defined.deinit(gpa),257 .linker_defined => data.linker_defined.deinit(gpa),
252 // .object => data.object.deinit(gpa),258 .object => data.object.deinit(gpa),
253 // .shared_object => data.shared_object.deinit(gpa),259 // .shared_object => data.shared_object.deinit(gpa),
254 };260 };
255 self.files.deinit(gpa);261 self.files.deinit(gpa);
262 self.objects.deinit(gpa);
256263
257 self.shdrs.deinit(gpa);264 self.shdrs.deinit(gpa);
258 self.phdr_to_shdr_table.deinit(gpa);265 self.phdr_to_shdr_table.deinit(gpa);
...@@ -295,6 +302,9 @@ pub fn deinit(self: *Elf) void {...@@ -295,6 +302,9 @@ pub fn deinit(self: *Elf) void {
295 }302 }
296303
297 self.misc_errors.deinit(gpa);304 self.misc_errors.deinit(gpa);
305 self.comdat_groups.deinit(gpa);
306 self.comdat_groups_owners.deinit(gpa);
307 self.comdat_groups_table.deinit(gpa);
298}308}
299309
300pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {310pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
...@@ -828,7 +838,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -828,7 +838,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
828 const zig_module = self.file(index).?.zig_module;838 const zig_module = self.file(index).?.zig_module;
829 const sym_index = try zig_module.addLocal(self);839 const sym_index = try zig_module.addLocal(self);
830 const sym = self.symbol(sym_index);840 const sym = self.symbol(sym_index);
831 const esym = sym.sourceSymbol(self);841 const esym = zig_module.sourceSymbol(sym_index, self);
832 const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path));842 const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path));
833 sym.name_offset = name_off;843 sym.name_offset = name_off;
834 esym.st_name = name_off;844 esym.st_name = name_off;
...@@ -1276,14 +1286,35 @@ fn parsePositional(...@@ -1276,14 +1286,35 @@ fn parsePositional(
1276) ParseError!void {1286) ParseError!void {
1277 const tracy = trace(@src());1287 const tracy = trace(@src());
1278 defer tracy.end();1288 defer tracy.end();
1279
1280 _ = self;
1281 _ = in_file;
1282 _ = path;
1283 _ = must_link;1289 _ = must_link;
1284 _ = ctx;
12851290
1286 return error.UnknownFileType;1291 if (Object.isObject(in_file)) {
1292 try self.parseObject(in_file, path, ctx);
1293 } else return error.UnknownFileType;
1294}
1295
1296fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {
1297 const tracy = trace(@src());
1298 defer tracy.end();
1299
1300 const gpa = self.base.allocator;
1301 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1302 const index = @as(File.Index, @intCast(self.files.slice().len));
1303
1304 var object = Object{
1305 .path = path,
1306 .data = data,
1307 .index = index,
1308 };
1309 errdefer object.deinit(gpa);
1310 try object.parse(self);
1311
1312 ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
1313 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1314
1315 _ = try self.files.addOne(gpa);
1316 self.files.set(index, .{ .object = object });
1317 try self.objects.append(gpa, index);
1287}1318}
12881319
1289fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {1320fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -2226,6 +2257,7 @@ fn updateDeclCode(...@@ -2226,6 +2257,7 @@ fn updateDeclCode(
2226) !void {2257) !void {
2227 const gpa = self.base.allocator;2258 const gpa = self.base.allocator;
2228 const mod = self.base.options.module.?;2259 const mod = self.base.options.module.?;
2260 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2229 const decl = mod.declPtr(decl_index);2261 const decl = mod.declPtr(decl_index);
22302262
2231 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));2263 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
...@@ -2234,7 +2266,7 @@ fn updateDeclCode(...@@ -2234,7 +2266,7 @@ fn updateDeclCode(
2234 const required_alignment = decl.getAlignment(mod);2266 const required_alignment = decl.getAlignment(mod);
22352267
2236 const sym = self.symbol(sym_index);2268 const sym = self.symbol(sym_index);
2237 const esym = sym.sourceSymbol(self);2269 const esym = zig_module.sourceSymbol(sym_index, self);
2238 const atom_ptr = sym.atom(self).?;2270 const atom_ptr = sym.atom(self).?;
2239 const shdr_index = sym.output_section_index;2271 const shdr_index = sym.output_section_index;
22402272
...@@ -2447,6 +2479,7 @@ pub fn updateDecl(...@@ -2447,6 +2479,7 @@ pub fn updateDecl(
2447fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {2479fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {
2448 const gpa = self.base.allocator;2480 const gpa = self.base.allocator;
2449 const mod = self.base.options.module.?;2481 const mod = self.base.options.module.?;
2482 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
24502483
2451 var required_alignment: u32 = undefined;2484 var required_alignment: u32 = undefined;
2452 var code_buffer = std.ArrayList(u8).init(gpa);2485 var code_buffer = std.ArrayList(u8).init(gpa);
...@@ -2490,7 +2523,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol....@@ -2490,7 +2523,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
2490 const local_sym = self.symbol(symbol_index);2523 const local_sym = self.symbol(symbol_index);
2491 const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?;2524 const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?;
2492 local_sym.name_offset = name_str_index;2525 local_sym.name_offset = name_str_index;
2493 const local_esym = local_sym.sourceSymbol(self);2526 const local_esym = zig_module.sourceSymbol(symbol_index, self);
2494 local_esym.st_name = name_str_index;2527 local_esym.st_name = name_str_index;
2495 local_esym.st_info |= elf.STT_OBJECT;2528 local_esym.st_info |= elf.STT_OBJECT;
2496 local_esym.st_size = code.len;2529 local_esym.st_size = code.len;
...@@ -2566,7 +2599,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2566,7 +2599,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2566 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;2599 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
2567 const local_sym = self.symbol(sym_index);2600 const local_sym = self.symbol(sym_index);
2568 local_sym.name_offset = name_str_index;2601 local_sym.name_offset = name_str_index;
2569 const local_esym = local_sym.sourceSymbol(self);2602 const local_esym = zig_module.sourceSymbol(sym_index, self);
2570 local_esym.st_name = name_str_index;2603 local_esym.st_name = name_str_index;
2571 local_esym.st_info |= elf.STT_OBJECT;2604 local_esym.st_info |= elf.STT_OBJECT;
2572 local_esym.st_size = code.len;2605 local_esym.st_size = code.len;
...@@ -2650,8 +2683,8 @@ pub fn updateDeclExports(...@@ -2650,8 +2683,8 @@ pub fn updateDeclExports(
2650 };2683 };
2651 const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info));2684 const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info));
26522685
2686 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2653 const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: {2687 const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: {
2654 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2655 const sym_index = try zig_module.addGlobal(exp_name, self);2688 const sym_index = try zig_module.addGlobal(exp_name, self);
2656 try decl_metadata.exports.append(gpa, sym_index);2689 try decl_metadata.exports.append(gpa, sym_index);
2657 break :blk sym_index;2690 break :blk sym_index;
...@@ -2660,8 +2693,8 @@ pub fn updateDeclExports(...@@ -2660,8 +2693,8 @@ pub fn updateDeclExports(
2660 sym.value = decl_sym.value;2693 sym.value = decl_sym.value;
2661 sym.atom_index = decl_sym.atom_index;2694 sym.atom_index = decl_sym.atom_index;
2662 sym.output_section_index = decl_sym.output_section_index;2695 sym.output_section_index = decl_sym.output_section_index;
2663 const esym = sym.sourceSymbol(self);2696 const esym = zig_module.sourceSymbol(sym_index, self);
2664 esym.* = decl_esym.*;2697 esym.* = decl_esym;
2665 esym.st_info = (stb_bits << 4) | stt_bits;2698 esym.st_info = (stb_bits << 4) | stt_bits;
2666 }2699 }
2667}2700}
...@@ -2691,11 +2724,12 @@ pub fn deleteDeclExport(...@@ -2691,11 +2724,12 @@ pub fn deleteDeclExport(
2691 const metadata = self.decls.getPtr(decl_index) orelse return;2724 const metadata = self.decls.getPtr(decl_index) orelse return;
2692 const gpa = self.base.allocator;2725 const gpa = self.base.allocator;
2693 const mod = self.base.options.module.?;2726 const mod = self.base.options.module.?;
2727 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
2694 const exp_name = mod.intern_pool.stringToSlice(name);2728 const exp_name = mod.intern_pool.stringToSlice(name);
2695 const sym_index = metadata.@"export"(self, exp_name) orelse return;2729 const sym_index = metadata.@"export"(self, exp_name) orelse return;
2696 log.debug("deleting export '{s}'", .{exp_name});2730 log.debug("deleting export '{s}'", .{exp_name});
2697 const sym = self.symbol(sym_index.*);2731 const sym = self.symbol(sym_index.*);
2698 const esym = sym.sourceSymbol(self);2732 const esym = zig_module.sourceSymbol(sym_index.*, self);
2699 assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant2733 assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant
2700 sym.* = .{};2734 sym.* = .{};
2701 // TODO free list for esym!2735 // TODO free list for esym!
...@@ -3337,6 +3371,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {...@@ -3337,6 +3371,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {
3337 .null => null,3371 .null => null,
3338 .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },3372 .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },
3339 .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },3373 .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module },
3374 .object => .{ .object = &self.files.items(.data)[index].object },
3340 };3375 };
3341}3376}
33423377
...@@ -3442,6 +3477,42 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32...@@ -3442,6 +3477,42 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32
3442 return gop.index;3477 return gop.index;
3443}3478}
34443479
3480const GetOrCreateComdatGroupOwnerResult = struct {
3481 found_existing: bool,
3482 index: ComdatGroupOwner.Index,
3483};
3484
3485pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult {
3486 const gpa = self.base.allocator;
3487 const gop = try self.comdat_groups_table.getOrPut(gpa, off);
3488 if (!gop.found_existing) {
3489 const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len));
3490 const owner = try self.comdat_groups_owners.addOne(gpa);
3491 owner.* = .{};
3492 gop.value_ptr.* = index;
3493 }
3494 return .{
3495 .found_existing = gop.found_existing,
3496 .index = gop.value_ptr.*,
3497 };
3498}
3499
3500pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index {
3501 const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len));
3502 _ = try self.comdat_groups.addOne(self.base.allocator);
3503 return index;
3504}
3505
3506pub fn comdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup {
3507 assert(index < self.comdat_groups.items.len);
3508 return &self.comdat_groups.items[index];
3509}
3510
3511pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner {
3512 assert(index < self.comdat_groups_owners.items.len);
3513 return &self.comdat_groups_owners.items[index];
3514}
3515
3445fn reportUndefined(self: *Elf) !void {3516fn reportUndefined(self: *Elf) !void {
3446 const gpa = self.base.allocator;3517 const gpa = self.base.allocator;
3447 const max_notes = 4;3518 const max_notes = 4;
...@@ -3552,6 +3623,21 @@ fn fmtDumpState(...@@ -3552,6 +3623,21 @@ fn fmtDumpState(
3552 try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });3623 try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path });
3553 try writer.print("{}\n", .{zig_module.fmtSymtab(self)});3624 try writer.print("{}\n", .{zig_module.fmtSymtab(self)});
3554 }3625 }
3626
3627 for (self.objects.items) |index| {
3628 const object = self.file(index).?.object;
3629 try writer.print("object({d}) : {}", .{ index, object.fmtPath() });
3630 if (!object.alive) try writer.writeAll(" : [*]");
3631 try writer.writeByte('\n');
3632 try writer.print("{}{}{}{}{}\n", .{
3633 object.fmtAtoms(self),
3634 object.fmtCies(self),
3635 object.fmtFdes(self),
3636 object.fmtSymtab(self),
3637 object.fmtComdatGroups(self),
3638 });
3639 }
3640
3555 if (self.linker_defined_index) |index| {3641 if (self.linker_defined_index) |index| {
3556 const linker_defined = self.file(index).?.linker_defined;3642 const linker_defined = self.file(index).?.linker_defined;
3557 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});3643 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
...@@ -3560,6 +3646,37 @@ fn fmtDumpState(...@@ -3560,6 +3646,37 @@ fn fmtDumpState(
3560 try writer.print("{}\n", .{self.got.fmt(self)});3646 try writer.print("{}\n", .{self.got.fmt(self)});
3561}3647}
35623648
3649/// Binary search
3650pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
3651 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3652 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
3653
3654 var min: usize = 0;
3655 var max: usize = haystack.len;
3656 while (min < max) {
3657 const index = (min + max) / 2;
3658 const curr = haystack[index];
3659 if (predicate.predicate(curr)) {
3660 min = index + 1;
3661 } else {
3662 max = index;
3663 }
3664 }
3665 return min;
3666}
3667
3668/// Linear search
3669pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
3670 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3671 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
3672
3673 var i: usize = 0;
3674 while (i < haystack.len) : (i += 1) {
3675 if (predicate.predicate(haystack[i])) break;
3676 }
3677 return i;
3678}
3679
3563const default_entry_addr = 0x8000000;3680const default_entry_addr = 0x8000000;
35643681
3565pub const base_tag: link.File.Tag = .elf;3682pub const base_tag: link.File.Tag = .elf;
...@@ -3607,6 +3724,17 @@ const DeclMetadata = struct {...@@ -3607,6 +3724,17 @@ const DeclMetadata = struct {
3607 }3724 }
3608};3725};
36093726
3727const ComdatGroupOwner = struct {
3728 file: File.Index = 0,
3729 const Index = u32;
3730};
3731
3732pub const ComdatGroup = struct {
3733 owner: ComdatGroupOwner.Index,
3734 shndx: u16,
3735 pub const Index = u32;
3736};
3737
3610pub const SymtabSize = struct {3738pub const SymtabSize = struct {
3611 nlocals: u32 = 0,3739 nlocals: u32 = 0,
3612 nglobals: u32 = 0,3740 nglobals: u32 = 0,
...@@ -3654,6 +3782,7 @@ const LinkerDefined = @import("Elf/LinkerDefined.zig");...@@ -3654,6 +3782,7 @@ const LinkerDefined = @import("Elf/LinkerDefined.zig");
3654const Liveness = @import("../Liveness.zig");3782const Liveness = @import("../Liveness.zig");
3655const LlvmObject = @import("../codegen/llvm.zig").Object;3783const LlvmObject = @import("../codegen/llvm.zig").Object;
3656const Module = @import("../Module.zig");3784const Module = @import("../Module.zig");
3785const Object = @import("Elf/Object.zig");
3657const InternPool = @import("../InternPool.zig");3786const InternPool = @import("../InternPool.zig");
3658const Package = @import("../Package.zig");3787const Package = @import("../Package.zig");
3659const Symbol = @import("Elf/Symbol.zig");3788const Symbol = @import("Elf/Symbol.zig");
src/link/Elf/Atom.zig+46-5
...@@ -46,6 +46,46 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 {...@@ -46,6 +46,46 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 {
46 return elf_file.strtab.getAssumeExists(self.name_offset);46 return elf_file.strtab.getAssumeExists(self.name_offset);
47}47}
4848
49pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
50 const object = elf_file.file(self.file_index).?.object;
51 return object.shdrs.items[self.input_section_index];
52}
53
54pub fn codeInObject(self: Atom, elf_file: *Elf) []const u8 {
55 const object = elf_file.file(self.file_index).?.object;
56 return object.shdrContents(self.input_section_index);
57}
58
59/// Returns atom's code and optionally uncompresses data if required (for compressed sections).
60/// Caller owns the memory.
61pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 {
62 const gpa = elf_file.base.allocator;
63 const data = self.codeInObject(elf_file);
64 const shdr = self.inputShdr(elf_file);
65 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
66 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
67 switch (chdr.ch_type) {
68 .ZLIB => {
69 var stream = std.io.fixedBufferStream(data[@sizeOf(elf.Elf64_Chdr)..]);
70 var zlib_stream = try std.compress.zlib.decompressStream(gpa, stream.reader());
71 defer zlib_stream.deinit();
72 const decomp = try gpa.alloc(u8, chdr.ch_size);
73 const nread = try zlib_stream.reader().readAll(decomp);
74 if (nread != decomp.len) {
75 return error.Io;
76 }
77 return decomp;
78 },
79 else => @panic("TODO unhandled compression scheme"),
80 }
81 } else return gpa.dupe(u8, data);
82}
83
84pub fn priority(self: Atom, elf_file: *Elf) u64 {
85 const index = elf_file.file(self.file_index).?.index();
86 return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index));
87}
88
49/// Returns how much room there is to grow in virtual address space.89/// Returns how much room there is to grow in virtual address space.
50/// File offset relocation happens transparently, so it is not included in90/// File offset relocation happens transparently, so it is not included in
51/// this calculation.91/// this calculation.
...@@ -247,11 +287,12 @@ pub fn free(self: *Atom, elf_file: *Elf) void {...@@ -247,11 +287,12 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
247 self.* = .{};287 self.* = .{};
248}288}
249289
250pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {290pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
251 const file_ptr = elf_file.file(self.file_index).?;291 return switch (elf_file.file(self.file_index).?) {
252 if (file_ptr != .zig_module) @panic("TODO");292 .zig_module => |x| x.relocs.items[self.relocs_section_index].items,
253 const zig_module = file_ptr.zig_module;293 .object => |x| x.getRelocs(self.relocs_section_index),
254 return zig_module.relocs.items[self.relocs_section_index].items;294 else => unreachable,
295 };
255}296}
256297
257pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {298pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void {
src/link/Elf/LinkerDefined.zig-5
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1index: File.Index,1index: File.Index,
2symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},2symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
3symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},3symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
4alive: bool = true,
54
6output_symtab_size: Elf.SymtabSize = .{},5output_symtab_size: Elf.SymtabSize = .{},
76
...@@ -76,10 +75,6 @@ pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void {...@@ -76,10 +75,6 @@ pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void {
76 }75 }
77}76}
7877
79pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym {
80 return &self.symtab.items[symbol_index];
81}
82
83pub fn globals(self: *LinkerDefined) []const Symbol.Index {78pub fn globals(self: *LinkerDefined) []const Symbol.Index {
84 return self.symbols.items;79 return self.symbols.items;
85}80}
src/link/Elf/Object.zig created+810
...@@ -0,0 +1,810 @@
1archive: ?[]const u8 = null,
2path: []const u8,
3data: []const u8,
4index: File.Index,
5
6header: ?elf.Elf64_Ehdr = null,
7shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{},
8strings: StringTable(.object_strings) = .{},
9symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{},
10strtab: []const u8 = &[0]u8{},
11first_global: ?u32 = null,
12
13symbols: std.ArrayListUnmanaged(u32) = .{},
14atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
15comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{},
16
17fdes: std.ArrayListUnmanaged(Fde) = .{},
18cies: std.ArrayListUnmanaged(Cie) = .{},
19
20needs_exec_stack: bool = false,
21alive: bool = true,
22num_dynrelocs: u32 = 0,
23
24output_symtab_size: Elf.SymtabSize = .{},
25
26pub fn isObject(file: std.fs.File) bool {
27 const reader = file.reader();
28 const header = reader.readStruct(elf.Elf64_Ehdr) catch return false;
29 defer file.seekTo(0) catch {};
30 if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false;
31 if (header.e_ident[elf.EI_VERSION] != 1) return false;
32 if (header.e_type != elf.ET.REL) return false;
33 if (header.e_version != 1) return false;
34 return true;
35}
36
37pub fn deinit(self: *Object, allocator: Allocator) void {
38 allocator.free(self.data);
39 self.shdrs.deinit(allocator);
40 self.strings.deinit(allocator);
41 self.symbols.deinit(allocator);
42 self.atoms.deinit(allocator);
43 self.comdat_groups.deinit(allocator);
44 self.fdes.deinit(allocator);
45 self.cies.deinit(allocator);
46}
47
48pub fn parse(self: *Object, elf_file: *Elf) !void {
49 var stream = std.io.fixedBufferStream(self.data);
50 const reader = stream.reader();
51
52 self.header = try reader.readStruct(elf.Elf64_Ehdr);
53
54 if (self.header.?.e_shnum == 0) return;
55
56 const gpa = elf_file.base.allocator;
57
58 const shdrs = @as(
59 [*]align(1) const elf.Elf64_Shdr,
60 @ptrCast(self.data.ptr + self.header.?.e_shoff),
61 )[0..self.header.?.e_shnum];
62 try self.shdrs.appendUnalignedSlice(gpa, shdrs);
63 try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));
64
65 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
66 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
67 else => {},
68 } else null;
69
70 if (symtab_index) |index| {
71 const shdr = shdrs[index];
72 self.first_global = shdr.sh_info;
73
74 const symtab = self.shdrContents(index);
75 const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym));
76 self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms];
77 self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link)));
78 }
79
80 try self.initAtoms(elf_file);
81 try self.initSymtab(elf_file);
82
83 for (self.shdrs.items, 0..) |shdr, i| {
84 const atom = elf_file.atom(self.atoms.items[i]) orelse continue;
85 if (!atom.alive) continue;
86 if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame"))
87 try self.parseEhFrame(@as(u16, @intCast(i)), elf_file);
88 }
89}
90
91fn initAtoms(self: *Object, elf_file: *Elf) !void {
92 const shdrs = self.shdrs.items;
93 try self.atoms.resize(elf_file.base.allocator, shdrs.len);
94 @memset(self.atoms.items, 0);
95
96 for (shdrs, 0..) |shdr, i| {
97 if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and
98 shdr.sh_flags & elf.SHF_ALLOC == 0 and
99 shdr.sh_type != elf.SHT_LLVM_ADDRSIG) continue;
100
101 switch (shdr.sh_type) {
102 elf.SHT_GROUP => {
103 if (shdr.sh_info >= self.symtab.len) {
104 // TODO convert into an error
105 log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()});
106 continue;
107 }
108 const group_info_sym = self.symtab[shdr.sh_info];
109 const group_signature = blk: {
110 if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) {
111 const sym_shdr = shdrs[group_info_sym.st_shndx];
112 break :blk self.strings.getAssumeExists(sym_shdr.sh_name);
113 }
114 break :blk self.getString(group_info_sym.st_name);
115 };
116
117 const shndx = @as(u16, @intCast(i));
118 const group_raw_data = self.shdrContents(shndx);
119 const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32));
120 const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers];
121
122 if (group_members[0] != 0x1) { // GRP_COMDAT
123 // TODO convert into an error
124 log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()});
125 continue;
126 }
127
128 const group_signature_off = try self.strings.insert(elf_file.base.allocator, group_signature);
129 const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off);
130 const comdat_group_index = try elf_file.addComdatGroup();
131 const comdat_group = elf_file.comdatGroup(comdat_group_index);
132 comdat_group.* = .{
133 .owner = gop.index,
134 .shndx = shndx,
135 };
136 try self.comdat_groups.append(elf_file.base.allocator, comdat_group_index);
137 },
138
139 elf.SHT_SYMTAB_SHNDX => @panic("TODO"),
140
141 elf.SHT_NULL,
142 elf.SHT_REL,
143 elf.SHT_RELA,
144 elf.SHT_SYMTAB,
145 elf.SHT_STRTAB,
146 => {},
147
148 else => {
149 const name = self.strings.getAssumeExists(shdr.sh_name);
150 const shndx = @as(u16, @intCast(i));
151
152 // if (mem.eql(u8, ".note.GNU-stack", name)) {
153 // if (shdr.sh_flags & elf.SHF_EXECINSTR != 0) {
154 // if (!elf_file.options.z_execstack or !elf_file.options.z_execstack_if_needed) {
155 // elf_file.base.warn(
156 // "{}: may cause segmentation fault as this file requested executable stack",
157 // .{self.fmtPath()},
158 // );
159 // }
160 // self.needs_exec_stack = true;
161 // }
162 // continue;
163 // }
164
165 if (self.skipShdr(shndx, elf_file)) continue;
166 try self.addAtom(shdr, shndx, name, elf_file);
167 },
168 }
169 }
170
171 // Parse relocs sections if any.
172 for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
173 elf.SHT_REL, elf.SHT_RELA => {
174 const atom_index = self.atoms.items[shdr.sh_info];
175 if (elf_file.atom(atom_index)) |atom| {
176 atom.relocs_section_index = @as(u16, @intCast(i));
177 }
178 },
179 else => {},
180 };
181}
182
183fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, elf_file: *Elf) !void {
184 const atom_index = try elf_file.addAtom();
185 const atom = elf_file.atom(atom_index).?;
186 atom.atom_index = atom_index;
187 atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
188 atom.file_index = self.index;
189 atom.input_section_index = shndx;
190 self.atoms.items[shndx] = atom_index;
191
192 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
193 const data = self.shdrContents(shndx);
194 const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*;
195 atom.size = chdr.ch_size;
196 atom.alignment = math.log2_int(u64, chdr.ch_addralign);
197 } else {
198 atom.size = shdr.sh_size;
199 atom.alignment = math.log2_int(u64, shdr.sh_addralign);
200 }
201}
202
203fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
204 const shdr = self.shdrs.items[index];
205 const name = self.strings.getAssumeExists(shdr.sh_name);
206 const ignore = blk: {
207 if (mem.startsWith(u8, name, ".note")) break :blk true;
208 if (mem.startsWith(u8, name, ".comment")) break :blk true;
209 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
210 if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
211 mem.startsWith(u8, name, ".debug")) break :blk true;
212 break :blk false;
213 };
214 return ignore;
215}
216
217fn initSymtab(self: *Object, elf_file: *Elf) !void {
218 const gpa = elf_file.base.allocator;
219 const first_global = self.first_global orelse self.symtab.len;
220 const shdrs = self.shdrs.items;
221
222 try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len);
223
224 for (self.symtab[0..first_global], 0..) |sym, i| {
225 const index = try elf_file.addSymbol();
226 self.symbols.appendAssumeCapacity(index);
227 const sym_ptr = elf_file.symbol(index);
228 const name = blk: {
229 if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) {
230 const shdr = shdrs[sym.st_shndx];
231 break :blk self.strings.getAssumeExists(shdr.sh_name);
232 }
233 break :blk self.getString(sym.st_name);
234 };
235 sym_ptr.value = sym.st_value;
236 sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name);
237 sym_ptr.esym_index = @as(u32, @intCast(i));
238 sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx];
239 sym_ptr.file_index = self.index;
240 }
241
242 for (self.symtab[first_global..]) |sym| {
243 const name = self.getString(sym.st_name);
244 const off = try elf_file.strtab.insert(gpa, name);
245 const gop = try elf_file.getOrPutGlobal(off);
246 self.symbols.addOneAssumeCapacity().* = gop.index;
247 }
248}
249
250fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void {
251 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
252 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u16, @intCast(i)),
253 else => {},
254 } else {
255 log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
256 return;
257 };
258
259 const gpa = elf_file.base.allocator;
260 const raw = self.shdrContents(shndx);
261 const relocs = self.getRelocs(relocs_shndx);
262 const fdes_start = self.fdes.items.len;
263 const cies_start = self.cies.items.len;
264
265 var it = eh_frame.Iterator{ .data = raw };
266 while (try it.next()) |rec| {
267 const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4);
268 switch (rec.tag) {
269 .cie => try self.cies.append(gpa, .{
270 .offset = rec.offset,
271 .size = rec.size,
272 .rel_index = @as(u32, @intCast(rel_range.start)),
273 .rel_num = @as(u32, @intCast(rel_range.len)),
274 .rel_section_index = relocs_shndx,
275 .input_section_index = shndx,
276 .file_index = self.index,
277 }),
278 .fde => try self.fdes.append(gpa, .{
279 .offset = rec.offset,
280 .size = rec.size,
281 .cie_index = undefined,
282 .rel_index = @as(u32, @intCast(rel_range.start)),
283 .rel_num = @as(u32, @intCast(rel_range.len)),
284 .rel_section_index = relocs_shndx,
285 .input_section_index = shndx,
286 .file_index = self.index,
287 }),
288 }
289 }
290
291 // Tie each FDE to its CIE
292 for (self.fdes.items[fdes_start..]) |*fde| {
293 const cie_ptr = fde.offset + 4 - fde.ciePointer(elf_file);
294 const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| {
295 if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index));
296 } else {
297 // TODO convert into an error
298 log.debug("{s}: no matching CIE found for FDE at offset {x}", .{
299 self.fmtPath(),
300 fde.offset,
301 });
302 continue;
303 };
304 fde.cie_index = cie_index;
305 }
306
307 // Tie each FDE record to its matching atom
308 const SortFdes = struct {
309 pub fn lessThan(ctx: *Elf, lhs: Fde, rhs: Fde) bool {
310 const lhs_atom = lhs.atom(ctx);
311 const rhs_atom = rhs.atom(ctx);
312 return lhs_atom.priority(ctx) < rhs_atom.priority(ctx);
313 }
314 };
315 mem.sort(Fde, self.fdes.items[fdes_start..], elf_file, SortFdes.lessThan);
316
317 // Create a back-link from atom to FDEs
318 var i: u32 = @as(u32, @intCast(fdes_start));
319 while (i < self.fdes.items.len) {
320 const fde = self.fdes.items[i];
321 const atom = fde.atom(elf_file);
322 atom.fde_start = i;
323 i += 1;
324 while (i < self.fdes.items.len) : (i += 1) {
325 const next_fde = self.fdes.items[i];
326 if (atom.atom_index != next_fde.atom(elf_file).atom_index) break;
327 }
328 atom.fde_end = i;
329 }
330}
331
332fn filterRelocs(
333 relocs: []align(1) const elf.Elf64_Rela,
334 start: u64,
335 len: u64,
336) struct { start: u64, len: u64 } {
337 const Predicate = struct {
338 value: u64,
339
340 pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
341 return rel.r_offset < self.value;
342 }
343 };
344 const LPredicate = struct {
345 value: u64,
346
347 pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool {
348 return rel.r_offset >= self.value;
349 }
350 };
351
352 const f_start = Elf.bsearch(elf.Elf64_Rela, relocs, Predicate{ .value = start });
353 const f_len = Elf.lsearch(elf.Elf64_Rela, relocs[f_start..], LPredicate{ .value = start + len });
354
355 return .{ .start = f_start, .len = f_len };
356}
357
358pub fn scanRelocs(self: *Object, elf_file: *Elf) !void {
359 for (self.atoms.items) |atom_index| {
360 const atom = elf_file.atom(atom_index) orelse continue;
361 if (!atom.alive) continue;
362 const shdr = atom.inputShdr(elf_file);
363 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
364 if (shdr.sh_type == elf.SHT_NOBITS) continue;
365 try atom.scanRelocs(elf_file);
366 }
367
368 for (self.cies.items) |cie| {
369 for (cie.getRelocs(elf_file)) |rel| {
370 const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]);
371 if (sym.flags.import) {
372 if (sym.getType(elf_file) != elf.STT_FUNC)
373 elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{
374 self.fmtPath(),
375 sym.getName(elf_file),
376 });
377 sym.flags.plt = true;
378 }
379 }
380 }
381}
382
383pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
384 const first_global = self.first_global orelse return;
385 for (self.globals(), 0..) |index, i| {
386 const sym_idx = @as(u32, @intCast(first_global + i));
387 const this_sym = self.symtab[sym_idx];
388
389 if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
390
391 if (this_sym.st_shndx != elf.SHN_ABS and this_sym.st_shndx != elf.SHN_COMMON) {
392 const atom_index = self.atoms.items[this_sym.st_shndx];
393 const atom = elf_file.atom(atom_index) orelse continue;
394 if (!atom.alive) continue;
395 }
396
397 const global = elf_file.symbol(index);
398 if (self.asFile().symbolRank(this_sym, !self.alive) < global.symbolRank(elf_file)) {
399 const atom = switch (this_sym.st_shndx) {
400 elf.SHN_ABS, elf.SHN_COMMON => 0,
401 else => self.atoms.items[this_sym.st_shndx],
402 };
403 global.* = .{
404 .value = this_sym.st_value,
405 .name = global.name,
406 .atom = atom,
407 .sym_idx = sym_idx,
408 .file = self.index,
409 .ver_idx = elf_file.default_sym_version,
410 };
411 if (this_sym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
412 }
413 }
414}
415
416pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
417 for (self.globals()) |index| {
418 const global = elf_file.symbol(index);
419 const name = global.name;
420 global.* = .{};
421 global.name = name;
422 }
423}
424
425pub fn markLive(self: *Object, elf_file: *Elf) void {
426 const first_global = self.first_global orelse return;
427 for (self.globals(), 0..) |index, i| {
428 const sym_idx = first_global + i;
429 const sym = self.symtab[sym_idx];
430 if (sym.st_bind() == elf.STB_WEAK) continue;
431
432 const global = elf_file.symbol(index);
433 const file = global.getFile(elf_file) orelse continue;
434 const should_keep = sym.st_shndx == elf.SHN_UNDEF or
435 (sym.st_shndx == elf.SHN_COMMON and global.sourceSymbol(elf_file).st_shndx != elf.SHN_COMMON);
436 if (should_keep and !file.isAlive()) {
437 file.setAlive();
438 file.markLive(elf_file);
439 }
440 }
441}
442
443pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
444 const first_global = self.first_global orelse return;
445 for (self.globals(), 0..) |index, i| {
446 const sym_idx = @as(u32, @intCast(first_global + i));
447 const this_sym = self.symtab[sym_idx];
448 const global = elf_file.symbol(index);
449 const global_file = global.getFile(elf_file) orelse continue;
450
451 if (self.index == global_file.getIndex() or
452 this_sym.st_shndx == elf.SHN_UNDEF or
453 this_sym.st_bind() == elf.STB_WEAK or
454 this_sym.st_shndx == elf.SHN_COMMON) continue;
455
456 if (this_sym.st_shndx != elf.SHN_ABS) {
457 const atom_index = self.atoms.items[this_sym.st_shndx];
458 const atom = elf_file.atom(atom_index) orelse continue;
459 if (!atom.alive) continue;
460 }
461
462 elf_file.base.fatal("multiple definition: {}: {}: {s}", .{
463 self.fmtPath(),
464 global_file.fmtPath(),
465 global.getName(elf_file),
466 });
467 }
468}
469
470/// We will create dummy shdrs per each resolved common symbols to make it
471/// play nicely with the rest of the system.
472pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
473 const first_global = self.first_global orelse return;
474 for (self.globals(), 0..) |index, i| {
475 const sym_idx = @as(u32, @intCast(first_global + i));
476 const this_sym = self.symtab[sym_idx];
477 if (this_sym.st_shndx != elf.SHN_COMMON) continue;
478
479 const global = elf_file.symbol(index);
480 const global_file = global.getFile(elf_file).?;
481 if (global_file.getIndex() != self.index) {
482 if (elf_file.options.warn_common) {
483 elf_file.base.warn("{}: multiple common symbols: {s}", .{
484 self.fmtPath(),
485 global.getName(elf_file),
486 });
487 }
488 continue;
489 }
490
491 const gpa = elf_file.base.allocator;
492
493 const atom_index = try elf_file.addAtom();
494 try self.atoms.append(gpa, atom_index);
495
496 const is_tls = global.getType(elf_file) == elf.STT_TLS;
497 const name = if (is_tls) ".tls_common" else ".common";
498
499 const atom = elf_file.atom(atom_index).?;
500 atom.atom_index = atom_index;
501 atom.name = try elf_file.strtab.insert(gpa, name);
502 atom.file = self.index;
503 atom.size = this_sym.st_size;
504 const alignment = this_sym.st_value;
505 atom.alignment = math.log2_int(u64, alignment);
506
507 var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE;
508 if (is_tls) sh_flags |= elf.SHF_TLS;
509 const shndx = @as(u16, @intCast(self.shdrs.items.len));
510 const shdr = try self.shdrs.addOne(gpa);
511 shdr.* = .{
512 .sh_name = try self.strings.insert(gpa, name),
513 .sh_type = elf.SHT_NOBITS,
514 .sh_flags = sh_flags,
515 .sh_addr = 0,
516 .sh_offset = 0,
517 .sh_size = this_sym.st_size,
518 .sh_link = 0,
519 .sh_info = 0,
520 .sh_addralign = alignment,
521 .sh_entsize = 0,
522 };
523 atom.shndx = shndx;
524
525 global.value = 0;
526 global.atom = atom_index;
527 global.flags.weak = false;
528 }
529}
530
531pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void {
532 if (elf_file.options.strip_all) return;
533
534 for (self.locals()) |local_index| {
535 const local = elf_file.symbol(local_index);
536 if (local.atom(elf_file)) |atom| if (!atom.alive) continue;
537 const s_sym = local.getSourceSymbol(elf_file);
538 switch (s_sym.st_type()) {
539 elf.STT_SECTION, elf.STT_NOTYPE => continue,
540 else => {},
541 }
542 local.flags.output_symtab = true;
543 self.output_symtab_size.nlocals += 1;
544 self.output_symtab_size.strsize += @as(u32, @intCast(local.getName(elf_file).len + 1));
545 }
546
547 for (self.globals()) |global_index| {
548 const global = elf_file.symbol(global_index);
549 if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue;
550 if (global.atom(elf_file)) |atom| if (!atom.alive) continue;
551 global.flags.output_symtab = true;
552 if (global.isLocal()) {
553 self.output_symtab_size.nlocals += 1;
554 } else {
555 self.output_symtab_size.nglobals += 1;
556 }
557 self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1));
558 }
559}
560
561pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void {
562 if (elf_file.options.strip_all) return;
563
564 const gpa = elf_file.base.allocator;
565
566 var ilocal = ctx.ilocal;
567 for (self.locals()) |local_index| {
568 const local = elf_file.symbol(local_index);
569 if (!local.flags.output_symtab) continue;
570 const st_name = try ctx.strtab.insert(gpa, local.getName(elf_file));
571 ctx.symtab[ilocal] = local.asElfSym(st_name, elf_file);
572 ilocal += 1;
573 }
574
575 var iglobal = ctx.iglobal;
576 for (self.globals()) |global_index| {
577 const global = elf_file.symbol(global_index);
578 if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue;
579 if (!global.flags.output_symtab) continue;
580 const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file));
581 if (global.isLocal()) {
582 ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file);
583 ilocal += 1;
584 } else {
585 ctx.symtab[iglobal] = global.asElfSym(st_name, elf_file);
586 iglobal += 1;
587 }
588 }
589}
590
591pub fn locals(self: *Object) []const u32 {
592 const end = self.first_global orelse self.symbols.items.len;
593 return self.symbols.items[0..end];
594}
595
596pub fn globals(self: *Object) []const u32 {
597 const start = self.first_global orelse self.symbols.items.len;
598 return self.symbols.items[start..];
599}
600
601pub inline fn shdrContents(self: *Object, index: u32) []const u8 {
602 assert(index < self.shdrs.items.len);
603 const shdr = self.shdrs.items[index];
604 return self.data[shdr.sh_offset..][0..shdr.sh_size];
605}
606
607fn getString(self: *Object, off: u32) [:0]const u8 {
608 assert(off < self.strtab.len);
609 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
610}
611
612pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 {
613 const raw = self.shdrContents(index);
614 const nmembers = @divExact(raw.len, @sizeOf(u32));
615 const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers];
616 return members;
617}
618
619pub fn asFile(self: *Object) File {
620 return .{ .object = self };
621}
622
623pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela {
624 const raw = self.shdrContents(shndx);
625 const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela));
626 return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num];
627}
628
629pub fn format(
630 self: *Object,
631 comptime unused_fmt_string: []const u8,
632 options: std.fmt.FormatOptions,
633 writer: anytype,
634) !void {
635 _ = self;
636 _ = unused_fmt_string;
637 _ = options;
638 _ = writer;
639 @compileError("do not format objects directly");
640}
641
642pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
643 return .{ .data = .{
644 .object = self,
645 .elf_file = elf_file,
646 } };
647}
648
649const FormatContext = struct {
650 object: *Object,
651 elf_file: *Elf,
652};
653
654fn formatSymtab(
655 ctx: FormatContext,
656 comptime unused_fmt_string: []const u8,
657 options: std.fmt.FormatOptions,
658 writer: anytype,
659) !void {
660 _ = unused_fmt_string;
661 _ = options;
662 const object = ctx.object;
663 try writer.writeAll(" locals\n");
664 for (object.locals()) |index| {
665 const local = ctx.elf_file.symbol(index);
666 try writer.print(" {}\n", .{local.fmt(ctx.elf_file)});
667 }
668 try writer.writeAll(" globals\n");
669 for (object.globals()) |index| {
670 const global = ctx.elf_file.symbol(index);
671 try writer.print(" {}\n", .{global.fmt(ctx.elf_file)});
672 }
673}
674
675pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) {
676 return .{ .data = .{
677 .object = self,
678 .elf_file = elf_file,
679 } };
680}
681
682fn formatAtoms(
683 ctx: FormatContext,
684 comptime unused_fmt_string: []const u8,
685 options: std.fmt.FormatOptions,
686 writer: anytype,
687) !void {
688 _ = unused_fmt_string;
689 _ = options;
690 const object = ctx.object;
691 try writer.writeAll(" atoms\n");
692 for (object.atoms.items) |atom_index| {
693 const atom = ctx.elf_file.atom(atom_index) orelse continue;
694 try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)});
695 }
696}
697
698pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) {
699 return .{ .data = .{
700 .object = self,
701 .elf_file = elf_file,
702 } };
703}
704
705fn formatCies(
706 ctx: FormatContext,
707 comptime unused_fmt_string: []const u8,
708 options: std.fmt.FormatOptions,
709 writer: anytype,
710) !void {
711 _ = unused_fmt_string;
712 _ = options;
713 const object = ctx.object;
714 try writer.writeAll(" cies\n");
715 for (object.cies.items, 0..) |cie, i| {
716 try writer.print(" cie({d}) : {}\n", .{ i, cie.fmt(ctx.elf_file) });
717 }
718}
719
720pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) {
721 return .{ .data = .{
722 .object = self,
723 .elf_file = elf_file,
724 } };
725}
726
727fn formatFdes(
728 ctx: FormatContext,
729 comptime unused_fmt_string: []const u8,
730 options: std.fmt.FormatOptions,
731 writer: anytype,
732) !void {
733 _ = unused_fmt_string;
734 _ = options;
735 const object = ctx.object;
736 try writer.writeAll(" fdes\n");
737 for (object.fdes.items, 0..) |fde, i| {
738 try writer.print(" fde({d}) : {}\n", .{ i, fde.fmt(ctx.elf_file) });
739 }
740}
741
742pub fn fmtComdatGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatComdatGroups) {
743 return .{ .data = .{
744 .object = self,
745 .elf_file = elf_file,
746 } };
747}
748
749fn formatComdatGroups(
750 ctx: FormatContext,
751 comptime unused_fmt_string: []const u8,
752 options: std.fmt.FormatOptions,
753 writer: anytype,
754) !void {
755 _ = unused_fmt_string;
756 _ = options;
757 const object = ctx.object;
758 const elf_file = ctx.elf_file;
759 try writer.writeAll(" comdat groups\n");
760 for (object.comdat_groups.items) |cg_index| {
761 const cg = elf_file.comdatGroup(cg_index);
762 const cg_owner = elf_file.comdatGroupOwner(cg.owner);
763 if (cg_owner.file != object.index) continue;
764 for (object.comdatGroupMembers(cg.shndx)) |shndx| {
765 const atom_index = object.atoms.items[shndx];
766 const atom = elf_file.atom(atom_index) orelse continue;
767 try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) });
768 }
769 }
770}
771
772pub fn fmtPath(self: *Object) std.fmt.Formatter(formatPath) {
773 return .{ .data = self };
774}
775
776fn formatPath(
777 object: *Object,
778 comptime unused_fmt_string: []const u8,
779 options: std.fmt.FormatOptions,
780 writer: anytype,
781) !void {
782 _ = unused_fmt_string;
783 _ = options;
784 if (object.archive) |path| {
785 try writer.writeAll(path);
786 try writer.writeByte('(');
787 try writer.writeAll(object.path);
788 try writer.writeByte(')');
789 } else try writer.writeAll(object.path);
790}
791
792const Object = @This();
793
794const std = @import("std");
795const assert = std.debug.assert;
796const eh_frame = @import("eh_frame.zig");
797const elf = std.elf;
798const fs = std.fs;
799const log = std.log.scoped(.link);
800const math = std.math;
801const mem = std.mem;
802
803const Allocator = mem.Allocator;
804const Atom = @import("Atom.zig");
805const Cie = eh_frame.Cie;
806const Elf = @import("../Elf.zig");
807const Fde = eh_frame.Fde;
808const File = @import("file.zig").File;
809const StringTable = @import("../strtab.zig").StringTable;
810const Symbol = @import("Symbol.zig");
src/link/Elf/Symbol.zig+7-6
...@@ -66,22 +66,23 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {...@@ -66,22 +66,23 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
66 return elf_file.file(symbol.file_index);66 return elf_file.file(symbol.file_index);
67}67}
6868
69pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym {69pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
70 const file_ptr = symbol.file(elf_file).?;70 const file_ptr = symbol.file(elf_file).?;
71 switch (file_ptr) {71 switch (file_ptr) {
72 .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file),72 .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file).*,
73 .linker_defined => return file_ptr.linker_defined.sourceSymbol(symbol.esym_index),73 .linker_defined => return file_ptr.linker_defined.symtab.items[symbol.esym_index],
74 .object => return file_ptr.object.symtab[symbol.esym_index],
74 }75 }
75}76}
7677
77pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {78pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
78 const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32);79 const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32);
79 const sym = symbol.sourceSymbol(elf_file);80 const sym = symbol.sourceSymbol(elf_file);
80 const in_archive = switch (file) {81 const in_archive = switch (file_ptr) {
81 // .object => |x| !x.alive,82 .object => |x| !x.alive,
82 else => false,83 else => false,
83 };84 };
84 return file_ptr.symbolRank(sym.*, in_archive);85 return file_ptr.symbolRank(sym, in_archive);
85}86}
8687
87pub fn address(symbol: Symbol, opts: struct {88pub fn address(symbol: Symbol, opts: struct {
src/link/Elf/ZigModule.zig+2-4
...@@ -11,8 +11,6 @@ global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},...@@ -11,8 +11,6 @@ global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
11atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{},11atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{},
12relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},12relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{},
1313
14alive: bool = true,
15
16output_symtab_size: Elf.SymtabSize = .{},14output_symtab_size: Elf.SymtabSize = .{},
1715
18pub fn deinit(self: *ZigModule, allocator: Allocator) void {16pub fn deinit(self: *ZigModule, allocator: Allocator) void {
...@@ -37,7 +35,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !...@@ -37,7 +35,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !
37 const symbol_ptr = elf_file.symbol(symbol_index);35 const symbol_ptr = elf_file.symbol(symbol_index);
38 symbol_ptr.atom_index = atom_index;36 symbol_ptr.atom_index = atom_index;
39 symbol_ptr.output_section_index = output_section_index;37 symbol_ptr.output_section_index = output_section_index;
40 const local_esym = symbol_ptr.sourceSymbol(elf_file);38 const local_esym = self.sourceSymbol(symbol_ptr.index, elf_file);
41 local_esym.st_shndx = output_section_index;39 local_esym.st_shndx = output_section_index;
42 const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len));40 const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len));
43 const relocs = try self.relocs.addOne(gpa);41 const relocs = try self.relocs.addOne(gpa);
...@@ -82,7 +80,7 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I...@@ -82,7 +80,7 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I
82pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {80pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
83 for (self.locals()) |local_index| {81 for (self.locals()) |local_index| {
84 const local = elf_file.symbol(local_index);82 const local = elf_file.symbol(local_index);
85 const esym = self.sourceSymbol(local_index, elf_file);83 const esym = local.sourceSymbol(elf_file);
86 switch (esym.st_type()) {84 switch (esym.st_type()) {
87 elf.STT_SECTION, elf.STT_NOTYPE => {85 elf.STT_SECTION, elf.STT_NOTYPE => {
88 local.flags.output_symtab = false;86 local.flags.output_symtab = false;
src/link/Elf/eh_frame.zig created+445
...@@ -0,0 +1,445 @@
1pub const Fde = struct {
2 /// Includes 4byte size cell.
3 offset: u64,
4 size: u64,
5 cie_index: u32,
6 rel_index: u32 = 0,
7 rel_num: u32 = 0,
8 rel_section_index: u32 = 0,
9 input_section_index: u32 = 0,
10 file_index: u32 = 0,
11 alive: bool = true,
12 /// Includes 4byte size cell.
13 out_offset: u64 = 0,
14
15 pub fn address(fde: Fde, elf_file: *Elf) u64 {
16 const base: u64 = if (elf_file.eh_frame_section_index) |shndx|
17 elf_file.shdrs.items[shndx].sh_addr
18 else
19 0;
20 return base + fde.out_offset;
21 }
22
23 pub fn data(fde: Fde, elf_file: *Elf) []const u8 {
24 const object = elf_file.file(fde.file_index).?.object;
25 const contents = object.shdrContents(fde.input_section_index);
26 return contents[fde.offset..][0..fde.calcSize()];
27 }
28
29 pub fn cie(fde: Fde, elf_file: *Elf) Cie {
30 const object = elf_file.file(fde.file_index).?.object;
31 return object.cies.items[fde.cie_index];
32 }
33
34 pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 {
35 return std.mem.readIntLittle(u32, fde.data(elf_file)[4..8]);
36 }
37
38 pub fn calcSize(fde: Fde) u64 {
39 return fde.size + 4;
40 }
41
42 pub fn atom(fde: Fde, elf_file: *Elf) *Atom {
43 const object = elf_file.file(fde.file_index).?.object;
44 const rel = fde.relocs(elf_file)[0];
45 const sym = object.symtab[rel.r_sym()];
46 const atom_index = object.atoms.items[sym.st_shndx];
47 return elf_file.atom(atom_index).?;
48 }
49
50 pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
51 const object = elf_file.file(fde.file_index).?.object;
52 return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num];
53 }
54
55 pub fn format(
56 fde: Fde,
57 comptime unused_fmt_string: []const u8,
58 options: std.fmt.FormatOptions,
59 writer: anytype,
60 ) !void {
61 _ = fde;
62 _ = unused_fmt_string;
63 _ = options;
64 _ = writer;
65 @compileError("do not format FDEs directly");
66 }
67
68 pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) {
69 return .{ .data = .{
70 .fde = fde,
71 .elf_file = elf_file,
72 } };
73 }
74
75 const FdeFormatContext = struct {
76 fde: Fde,
77 elf_file: *Elf,
78 };
79
80 fn format2(
81 ctx: FdeFormatContext,
82 comptime unused_fmt_string: []const u8,
83 options: std.fmt.FormatOptions,
84 writer: anytype,
85 ) !void {
86 _ = unused_fmt_string;
87 _ = options;
88 const fde = ctx.fde;
89 const elf_file = ctx.elf_file;
90 const base_addr = fde.address(elf_file);
91 try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{
92 base_addr + fde.out_offset,
93 fde.calcSize(),
94 fde.cie_index,
95 fde.atom(elf_file).name(elf_file),
96 });
97 if (!fde.alive) try writer.writeAll(" : [*]");
98 }
99};
100
101pub const Cie = struct {
102 /// Includes 4byte size cell.
103 offset: u64,
104 size: u64,
105 rel_index: u32 = 0,
106 rel_num: u32 = 0,
107 rel_section_index: u32 = 0,
108 input_section_index: u32 = 0,
109 file_index: u32 = 0,
110 /// Includes 4byte size cell.
111 out_offset: u64 = 0,
112 alive: bool = false,
113
114 pub fn address(cie: Cie, elf_file: *Elf) u64 {
115 const base: u64 = if (elf_file.eh_frame_section_index) |shndx|
116 elf_file.shdrs.items[shndx].sh_addr
117 else
118 0;
119 return base + cie.out_offset;
120 }
121
122 pub fn data(cie: Cie, elf_file: *Elf) []const u8 {
123 const object = elf_file.file(cie.file_index).?.object;
124 const contents = object.shdrContents(cie.input_section_index);
125 return contents[cie.offset..][0..cie.calcSize()];
126 }
127
128 pub fn calcSize(cie: Cie) u64 {
129 return cie.size + 4;
130 }
131
132 pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
133 const object = elf_file.file(cie.file_index).?.object;
134 return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num];
135 }
136
137 pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool {
138 if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false;
139
140 const cie_relocs = cie.relocs(elf_file);
141 const other_relocs = other.relocs(elf_file);
142 if (cie_relocs.len != other_relocs.len) return false;
143
144 for (cie_relocs, other_relocs) |cie_rel, other_rel| {
145 if (cie_rel.r_offset - cie.offset != other_rel.r_offset - other.offset) return false;
146 if (cie_rel.r_type() != other_rel.r_type()) return false;
147 if (cie_rel.r_addend != other_rel.r_addend) return false;
148
149 const cie_object = elf_file.file(cie.file_index).?.object;
150 const other_object = elf_file.file(other.file_index).?.object;
151 const cie_sym = cie_object.symbol(cie_rel.r_sym(), elf_file);
152 const other_sym = other_object.symbol(other_rel.r_sym(), elf_file);
153 if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false;
154 }
155 return true;
156 }
157
158 pub fn format(
159 cie: Cie,
160 comptime unused_fmt_string: []const u8,
161 options: std.fmt.FormatOptions,
162 writer: anytype,
163 ) !void {
164 _ = cie;
165 _ = unused_fmt_string;
166 _ = options;
167 _ = writer;
168 @compileError("do not format CIEs directly");
169 }
170
171 pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) {
172 return .{ .data = .{
173 .cie = cie,
174 .elf_file = elf_file,
175 } };
176 }
177
178 const CieFormatContext = struct {
179 cie: Cie,
180 elf_file: *Elf,
181 };
182
183 fn format2(
184 ctx: CieFormatContext,
185 comptime unused_fmt_string: []const u8,
186 options: std.fmt.FormatOptions,
187 writer: anytype,
188 ) !void {
189 _ = unused_fmt_string;
190 _ = options;
191 const cie = ctx.cie;
192 const elf_file = ctx.elf_file;
193 const base_addr = cie.address(elf_file);
194 try writer.print("@{x} : size({x})", .{
195 base_addr + cie.out_offset,
196 cie.calcSize(),
197 });
198 if (!cie.alive) try writer.writeAll(" : [*]");
199 }
200};
201
202pub const Iterator = struct {
203 data: []const u8,
204 pos: u64 = 0,
205
206 pub const Record = struct {
207 tag: enum { fde, cie },
208 offset: u64,
209 size: u64,
210 };
211
212 pub fn next(it: *Iterator) !?Record {
213 if (it.pos >= it.data.len) return null;
214
215 var stream = std.io.fixedBufferStream(it.data[it.pos..]);
216 const reader = stream.reader();
217
218 var size = try reader.readIntLittle(u32);
219 if (size == 0xFFFFFFFF) @panic("TODO");
220
221 const id = try reader.readIntLittle(u32);
222 const record = Record{
223 .tag = if (id == 0) .cie else .fde,
224 .offset = it.pos,
225 .size = size,
226 };
227 it.pos += size + 4;
228
229 return record;
230 }
231};
232
233pub fn calcEhFrameSize(elf_file: *Elf) !usize {
234 var offset: u64 = 0;
235
236 var cies = std.ArrayList(Cie).init(elf_file.base.allocator);
237 defer cies.deinit();
238
239 for (elf_file.objects.items) |index| {
240 const object = elf_file.file(index).?.object;
241
242 outer: for (object.cies.items) |*cie| {
243 for (cies.items) |other| {
244 if (other.eql(cie.*, elf_file)) {
245 // We already have a CIE record that has the exact same contents, so instead of
246 // duplicating them, we mark this one dead and set its output offset to be
247 // equal to that of the alive record. This way, we won't have to rewrite
248 // Fde.cie_index field when committing the records to file.
249 cie.out_offset = other.out_offset;
250 continue :outer;
251 }
252 }
253 cie.alive = true;
254 cie.out_offset = offset;
255 offset += cie.calcSize();
256 try cies.append(cie.*);
257 }
258 }
259
260 for (elf_file.objects.items) |index| {
261 const object = elf_file.file(index).?.object;
262 for (object.fdes.items) |*fde| {
263 if (!fde.alive) continue;
264 fde.out_offset = offset;
265 offset += fde.calcSize();
266 }
267 }
268
269 return offset + 4; // NULL terminator
270}
271
272pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
273 var count: usize = 0;
274 for (elf_file.objects.items) |index| {
275 for (elf_file.file(index).?.object.fdes.items) |fde| {
276 if (!fde.alive) continue;
277 count += 1;
278 }
279 }
280 return eh_frame_hdr_header_size + count * 8;
281}
282
283fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void {
284 const offset = rel.r_offset - rec.offset;
285 const P = @as(i64, @intCast(rec.address(elf_file) + offset));
286 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
287 const A = rel.r_addend;
288
289 relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{
290 Atom.fmtRelocType(rel.r_type()),
291 offset,
292 P,
293 S + A,
294 sym.name(elf_file),
295 });
296
297 var where = contents[offset..];
298 switch (rel.r_type()) {
299 elf.R_X86_64_32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @truncate(S + A))),
300 elf.R_X86_64_64 => std.mem.writeIntLittle(i64, where[0..8], S + A),
301 elf.R_X86_64_PC32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @intCast(S - P + A))),
302 elf.R_X86_64_PC64 => std.mem.writeIntLittle(i64, where[0..8], S - P + A),
303 else => unreachable,
304 }
305}
306
307pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
308 const gpa = elf_file.base.allocator;
309
310 relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});
311
312 for (elf_file.objects.items) |index| {
313 const object = elf_file.file(index).?.object;
314
315 for (object.cies.items) |cie| {
316 if (!cie.alive) continue;
317
318 const contents = try gpa.dupe(u8, cie.data(elf_file));
319 defer gpa.free(contents);
320
321 for (cie.relocs(elf_file)) |rel| {
322 const sym = object.symbol(rel.r_sym(), elf_file);
323 try resolveReloc(cie, sym, rel, elf_file, contents);
324 }
325
326 try writer.writeAll(contents);
327 }
328 }
329
330 for (elf_file.objects.items) |index| {
331 const object = elf_file.file(index).?.object;
332
333 for (object.fdes.items) |fde| {
334 if (!fde.alive) continue;
335
336 const contents = try gpa.dupe(u8, fde.data(elf_file));
337 defer gpa.free(contents);
338
339 std.mem.writeIntLittle(
340 i32,
341 contents[4..8],
342 @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))),
343 );
344
345 for (fde.relocs(elf_file)) |rel| {
346 const sym = object.symbol(rel.r_sym(), elf_file);
347 try resolveReloc(fde, sym, rel, elf_file, contents);
348 }
349
350 try writer.writeAll(contents);
351 }
352 }
353
354 try writer.writeIntLittle(u32, 0);
355}
356
357pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
358 try writer.writeByte(1); // version
359 try writer.writeByte(EH_PE.pcrel | EH_PE.sdata4);
360 try writer.writeByte(EH_PE.udata4);
361 try writer.writeByte(EH_PE.datarel | EH_PE.sdata4);
362
363 const eh_frame_shdr = elf_file.shdrs.items[elf_file.eh_frame_section_index.?];
364 const eh_frame_hdr_shdr = elf_file.shdrs.items[elf_file.eh_frame_hdr_section_index.?];
365 const num_fdes = @as(u32, @intCast(@divExact(eh_frame_hdr_shdr.sh_size - eh_frame_hdr_header_size, 8)));
366 try writer.writeIntLittle(
367 u32,
368 @as(u32, @bitCast(@as(
369 i32,
370 @truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4),
371 ))),
372 );
373 try writer.writeIntLittle(u32, num_fdes);
374
375 const Entry = struct {
376 init_addr: u32,
377 fde_addr: u32,
378
379 pub fn lessThan(ctx: void, lhs: @This(), rhs: @This()) bool {
380 _ = ctx;
381 return lhs.init_addr < rhs.init_addr;
382 }
383 };
384
385 var entries = std.ArrayList(Entry).init(elf_file.base.allocator);
386 defer entries.deinit();
387 try entries.ensureTotalCapacityPrecise(num_fdes);
388
389 for (elf_file.objects.items) |index| {
390 const object = elf_file.file(index).?.object;
391 for (object.fdes.items) |fde| {
392 if (!fde.alive) continue;
393
394 const relocs = fde.relocs(elf_file);
395 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
396 const rel = relocs[0];
397 const sym = object.symbol(rel.r_sym(), elf_file);
398 const P = @as(i64, @intCast(fde.address(elf_file)));
399 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
400 const A = rel.r_addend;
401 entries.appendAssumeCapacity(.{
402 .init_addr = @as(u32, @bitCast(@as(i32, @truncate(S + A - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)))))),
403 .fde_addr = @as(
404 u32,
405 @bitCast(@as(i32, @truncate(P - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr))))),
406 ),
407 });
408 }
409 }
410
411 std.mem.sort(Entry, entries.items, {}, Entry.lessThan);
412 try writer.writeAll(std.mem.sliceAsBytes(entries.items));
413}
414
415const eh_frame_hdr_header_size: u64 = 12;
416
417const EH_PE = struct {
418 pub const absptr = 0x00;
419 pub const uleb128 = 0x01;
420 pub const udata2 = 0x02;
421 pub const udata4 = 0x03;
422 pub const udata8 = 0x04;
423 pub const sleb128 = 0x09;
424 pub const sdata2 = 0x0A;
425 pub const sdata4 = 0x0B;
426 pub const sdata8 = 0x0C;
427 pub const pcrel = 0x10;
428 pub const textrel = 0x20;
429 pub const datarel = 0x30;
430 pub const funcrel = 0x40;
431 pub const aligned = 0x50;
432 pub const indirect = 0x80;
433 pub const omit = 0xFF;
434};
435
436const std = @import("std");
437const assert = std.debug.assert;
438const elf = std.elf;
439const relocs_log = std.log.scoped(.link_relocs);
440
441const Allocator = std.mem.Allocator;
442const Atom = @import("Atom.zig");
443const Elf = @import("../Elf.zig");
444const Object = @import("Object.zig");
445const Symbol = @import("Symbol.zig");
src/link/Elf/file.zig+4-4
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1pub const File = union(enum) {1pub const File = union(enum) {
2 zig_module: *ZigModule,2 zig_module: *ZigModule,
3 linker_defined: *LinkerDefined,3 linker_defined: *LinkerDefined,
4 // object: *Object,4 object: *Object,
5 // shared_object: *SharedObject,5 // shared_object: *SharedObject,
66
7 pub fn index(file: File) Index {7 pub fn index(file: File) Index {
...@@ -25,7 +25,7 @@ pub const File = union(enum) {...@@ -25,7 +25,7 @@ pub const File = union(enum) {
25 switch (file) {25 switch (file) {
26 .zig_module => try writer.writeAll("(zig module)"),26 .zig_module => try writer.writeAll("(zig module)"),
27 .linker_defined => try writer.writeAll("(linker defined)"),27 .linker_defined => try writer.writeAll("(linker defined)"),
28 // .object => |x| try writer.print("{}", .{x.fmtPath()}),28 .object => |x| try writer.print("{}", .{x.fmtPath()}),
29 // .shared_object => |x| try writer.writeAll(x.path),29 // .shared_object => |x| try writer.writeAll(x.path),
30 }30 }
31 }31 }
...@@ -95,7 +95,7 @@ pub const File = union(enum) {...@@ -95,7 +95,7 @@ pub const File = union(enum) {
95 null: void,95 null: void,
96 zig_module: ZigModule,96 zig_module: ZigModule,
97 linker_defined: LinkerDefined,97 linker_defined: LinkerDefined,
98 // object: Object,98 object: Object,
99 // shared_object: SharedObject,99 // shared_object: SharedObject,
100 };100 };
101};101};
...@@ -106,7 +106,7 @@ const elf = std.elf;...@@ -106,7 +106,7 @@ const elf = std.elf;
106const Allocator = std.mem.Allocator;106const Allocator = std.mem.Allocator;
107const Elf = @import("../Elf.zig");107const Elf = @import("../Elf.zig");
108const LinkerDefined = @import("LinkerDefined.zig");108const LinkerDefined = @import("LinkerDefined.zig");
109// const Object = @import("Object.zig");109const Object = @import("Object.zig");
110// const SharedObject = @import("SharedObject.zig");110// const SharedObject = @import("SharedObject.zig");
111const Symbol = @import("Symbol.zig");111const Symbol = @import("Symbol.zig");
112const ZigModule = @import("ZigModule.zig");112const ZigModule = @import("ZigModule.zig");