| author | |
| committer | |
| log | 67d458370dfc87753e9938e7af94cb86e3bc98cc |
| tree | 1c6a86449cc968bc28f74d59743cea792c423c5e |
| parent | 3df58a95835fe2f9ba16e9ff9e0d03c39d263bf3 |
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, |
| 9 | files: std.MultiArrayList(File.Entry) = .{}, | 9 | files: std.MultiArrayList(File.Entry) = .{}, |
| 10 | zig_module_index: ?File.Index = null, | 10 | zig_module_index: ?File.Index = null, |
| 11 | linker_defined_index: ?File.Index = null, | 11 | linker_defined_index: ?File.Index = null, |
| 12 | objects: std.ArrayListUnmanaged(File.Index) = .{}, | ||
| 12 | 13 | ||
| 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 = .{}, |
| 51 | text_section_index: ?u16 = null, | 52 | text_section_index: ?u16 = null, |
| 52 | rodata_section_index: ?u16 = null, | 53 | rodata_section_index: ?u16 = null, |
| 53 | data_section_index: ?u16 = null, | 54 | data_section_index: ?u16 = null, |
| 55 | eh_frame_section_index: ?u16 = null, | ||
| 56 | eh_frame_hdr_section_index: ?u16 = null, | ||
| 54 | dynamic_section_index: ?u16 = null, | 57 | dynamic_section_index: ?u16 = null, |
| 55 | got_section_index: ?u16 = null, | 58 | got_section_index: ?u16 = null, |
| 56 | got_plt_section_index: ?u16 = null, | 59 | got_plt_section_index: ?u16 = null, |
| 57 | plt_section_index: ?u16 = null, | 60 | plt_section_index: ?u16 = null, |
| 58 | eh_frame_hdr_section_index: ?u16 = null, | ||
| 59 | rela_dyn_section_index: ?u16 = null, | 61 | rela_dyn_section_index: ?u16 = null, |
| 60 | debug_info_section_index: ?u16 = null, | 62 | debug_info_section_index: ?u16 = null, |
| 61 | debug_abbrev_section_index: ?u16 = null, | 63 | debug_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`. |
| 137 | unnamed_consts: UnnamedConstTable = .{}, | 139 | unnamed_consts: UnnamedConstTable = .{}, |
| 138 | 140 | ||
| 141 | comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{}, | ||
| 142 | comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{}, | ||
| 143 | comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{}, | ||
| 144 | |||
| 139 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); | 145 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); |
| 140 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); | 146 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); |
| 141 | 147 | ||
| ... | @@ -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); | ||
| 256 | 263 | ||
| 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 | } |
| 296 | 303 | ||
| 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 | } |
| 299 | 309 | ||
| 300 | pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { | 310 | pub 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; | ||
| 1285 | 1290 | ||
| 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 | |||
| 1296 | fn 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 | } |
| 1288 | 1319 | ||
| 1289 | fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 1320 | fn 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); |
| 2230 | 2262 | ||
| 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); |
| 2235 | 2267 | ||
| 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; |
| 2240 | 2272 | ||
| ... | @@ -2447,6 +2479,7 @@ pub fn updateDecl( | ... | @@ -2447,6 +2479,7 @@ pub fn updateDecl( |
| 2447 | fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void { | 2479 | fn 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; | ||
| 2450 | 2483 | ||
| 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)); |
| 2652 | 2685 | ||
| 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 dominant | 2733 | 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 | } |
| 3342 | 3377 | ||
| ... | @@ -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 | } |
| 3444 | 3479 | ||
| 3480 | const GetOrCreateComdatGroupOwnerResult = struct { | ||
| 3481 | found_existing: bool, | ||
| 3482 | index: ComdatGroupOwner.Index, | ||
| 3483 | }; | ||
| 3484 | |||
| 3485 | pub 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 | |||
| 3500 | pub 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 | |||
| 3506 | pub 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 | |||
| 3511 | pub 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 | |||
| 3445 | fn reportUndefined(self: *Elf) !void { | 3516 | fn 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 | } |
| 3562 | 3648 | ||
| 3649 | /// Binary search | ||
| 3650 | pub 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 | ||
| 3669 | pub 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 | |||
| 3563 | const default_entry_addr = 0x8000000; | 3680 | const default_entry_addr = 0x8000000; |
| 3564 | 3681 | ||
| 3565 | pub const base_tag: link.File.Tag = .elf; | 3682 | pub 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 | }; |
| 3609 | 3726 | ||
| 3727 | const ComdatGroupOwner = struct { | ||
| 3728 | file: File.Index = 0, | ||
| 3729 | const Index = u32; | ||
| 3730 | }; | ||
| 3731 | |||
| 3732 | pub const ComdatGroup = struct { | ||
| 3733 | owner: ComdatGroupOwner.Index, | ||
| 3734 | shndx: u16, | ||
| 3735 | pub const Index = u32; | ||
| 3736 | }; | ||
| 3737 | |||
| 3610 | pub const SymtabSize = struct { | 3738 | pub 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"); |
| 3654 | const Liveness = @import("../Liveness.zig"); | 3782 | const Liveness = @import("../Liveness.zig"); |
| 3655 | const LlvmObject = @import("../codegen/llvm.zig").Object; | 3783 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 3656 | const Module = @import("../Module.zig"); | 3784 | const Module = @import("../Module.zig"); |
| 3785 | const Object = @import("Elf/Object.zig"); | ||
| 3657 | const InternPool = @import("../InternPool.zig"); | 3786 | const InternPool = @import("../InternPool.zig"); |
| 3658 | const Package = @import("../Package.zig"); | 3787 | const Package = @import("../Package.zig"); |
| 3659 | const Symbol = @import("Elf/Symbol.zig"); | 3788 | const 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 | } |
| 48 | 48 | ||
| 49 | pub 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 | |||
| 54 | pub 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. | ||
| 61 | pub 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 | |||
| 84 | pub 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 in | 90 | /// 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 | } |
| 249 | 289 | ||
| 250 | pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { | 290 | pub 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 | } |
| 256 | 297 | ||
| 257 | pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void { | 298 | pub 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 @@ |
| 1 | index: File.Index, | 1 | index: File.Index, |
| 2 | symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, | 2 | symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, |
| 3 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | 3 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 4 | alive: bool = true, | ||
| 5 | 4 | ||
| 6 | output_symtab_size: Elf.SymtabSize = .{}, | 5 | output_symtab_size: Elf.SymtabSize = .{}, |
| 7 | 6 | ||
| ... | @@ -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 | } |
| 78 | 77 | ||
| 79 | pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym { | ||
| 80 | return &self.symtab.items[symbol_index]; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn globals(self: *LinkerDefined) []const Symbol.Index { | 78 | pub 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 @@ | ||
| 1 | archive: ?[]const u8 = null, | ||
| 2 | path: []const u8, | ||
| 3 | data: []const u8, | ||
| 4 | index: File.Index, | ||
| 5 | |||
| 6 | header: ?elf.Elf64_Ehdr = null, | ||
| 7 | shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, | ||
| 8 | strings: StringTable(.object_strings) = .{}, | ||
| 9 | symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, | ||
| 10 | strtab: []const u8 = &[0]u8{}, | ||
| 11 | first_global: ?u32 = null, | ||
| 12 | |||
| 13 | symbols: std.ArrayListUnmanaged(u32) = .{}, | ||
| 14 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, | ||
| 15 | comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, | ||
| 16 | |||
| 17 | fdes: std.ArrayListUnmanaged(Fde) = .{}, | ||
| 18 | cies: std.ArrayListUnmanaged(Cie) = .{}, | ||
| 19 | |||
| 20 | needs_exec_stack: bool = false, | ||
| 21 | alive: bool = true, | ||
| 22 | num_dynrelocs: u32 = 0, | ||
| 23 | |||
| 24 | output_symtab_size: Elf.SymtabSize = .{}, | ||
| 25 | |||
| 26 | pub 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 | |||
| 37 | pub 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 | |||
| 48 | pub 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 | |||
| 91 | fn 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 | |||
| 183 | fn 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 | |||
| 203 | fn 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 | |||
| 217 | fn 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 | |||
| 250 | fn 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 | |||
| 332 | fn 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 | |||
| 358 | pub 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 | |||
| 383 | pub 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 | |||
| 416 | pub 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 | |||
| 425 | pub 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 | |||
| 443 | pub 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. | ||
| 472 | pub 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 | |||
| 531 | pub 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 | |||
| 561 | pub 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 | |||
| 591 | pub 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 | |||
| 596 | pub 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 | |||
| 601 | pub 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 | |||
| 607 | fn 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 | |||
| 612 | pub 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 | |||
| 619 | pub fn asFile(self: *Object) File { | ||
| 620 | return .{ .object = self }; | ||
| 621 | } | ||
| 622 | |||
| 623 | pub 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 | |||
| 629 | pub 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 | |||
| 642 | pub 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 | |||
| 649 | const FormatContext = struct { | ||
| 650 | object: *Object, | ||
| 651 | elf_file: *Elf, | ||
| 652 | }; | ||
| 653 | |||
| 654 | fn 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 | |||
| 675 | pub 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 | |||
| 682 | fn 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 | |||
| 698 | pub 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 | |||
| 705 | fn 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 | |||
| 720 | pub 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 | |||
| 727 | fn 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 | |||
| 742 | pub 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 | |||
| 749 | fn 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 | |||
| 772 | pub fn fmtPath(self: *Object) std.fmt.Formatter(formatPath) { | ||
| 773 | return .{ .data = self }; | ||
| 774 | } | ||
| 775 | |||
| 776 | fn 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 | |||
| 792 | const Object = @This(); | ||
| 793 | |||
| 794 | const std = @import("std"); | ||
| 795 | const assert = std.debug.assert; | ||
| 796 | const eh_frame = @import("eh_frame.zig"); | ||
| 797 | const elf = std.elf; | ||
| 798 | const fs = std.fs; | ||
| 799 | const log = std.log.scoped(.link); | ||
| 800 | const math = std.math; | ||
| 801 | const mem = std.mem; | ||
| 802 | |||
| 803 | const Allocator = mem.Allocator; | ||
| 804 | const Atom = @import("Atom.zig"); | ||
| 805 | const Cie = eh_frame.Cie; | ||
| 806 | const Elf = @import("../Elf.zig"); | ||
| 807 | const Fde = eh_frame.Fde; | ||
| 808 | const File = @import("file.zig").File; | ||
| 809 | const StringTable = @import("../strtab.zig").StringTable; | ||
| 810 | const 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 | } |
| 68 | 68 | ||
| 69 | pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym { | 69 | pub 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 | } |
| 76 | 77 | ||
| 77 | pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { | 78 | pub 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 | } |
| 86 | 87 | ||
| 87 | pub fn address(symbol: Symbol, opts: struct { | 88 | pub 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) = .{}, |
| 11 | atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, | 11 | atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, |
| 12 | relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, | 12 | relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, |
| 13 | 13 | ||
| 14 | alive: bool = true, | ||
| 15 | |||
| 16 | output_symtab_size: Elf.SymtabSize = .{}, | 14 | output_symtab_size: Elf.SymtabSize = .{}, |
| 17 | 15 | ||
| 18 | pub fn deinit(self: *ZigModule, allocator: Allocator) void { | 16 | pub 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 |
| 82 | pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { | 80 | pub 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 @@ | ||
| 1 | pub 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 | |||
| 101 | pub 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 | |||
| 202 | pub 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 | |||
| 233 | pub 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 | |||
| 272 | pub 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 | |||
| 283 | fn 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 | |||
| 307 | pub 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 | |||
| 357 | pub 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 | |||
| 415 | const eh_frame_hdr_header_size: u64 = 12; | ||
| 416 | |||
| 417 | const 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 | |||
| 436 | const std = @import("std"); | ||
| 437 | const assert = std.debug.assert; | ||
| 438 | const elf = std.elf; | ||
| 439 | const relocs_log = std.log.scoped(.link_relocs); | ||
| 440 | |||
| 441 | const Allocator = std.mem.Allocator; | ||
| 442 | const Atom = @import("Atom.zig"); | ||
| 443 | const Elf = @import("../Elf.zig"); | ||
| 444 | const Object = @import("Object.zig"); | ||
| 445 | const Symbol = @import("Symbol.zig"); | ||
src/link/Elf/file.zig+4-4| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | pub const File = union(enum) { | 1 | pub 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, |
| 6 | 6 | ||
| 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; |
| 106 | const Allocator = std.mem.Allocator; | 106 | const Allocator = std.mem.Allocator; |
| 107 | const Elf = @import("../Elf.zig"); | 107 | const Elf = @import("../Elf.zig"); |
| 108 | const LinkerDefined = @import("LinkerDefined.zig"); | 108 | const LinkerDefined = @import("LinkerDefined.zig"); |
| 109 | // const Object = @import("Object.zig"); | 109 | const Object = @import("Object.zig"); |
| 110 | // const SharedObject = @import("SharedObject.zig"); | 110 | // const SharedObject = @import("SharedObject.zig"); |
| 111 | const Symbol = @import("Symbol.zig"); | 111 | const Symbol = @import("Symbol.zig"); |
| 112 | const ZigModule = @import("ZigModule.zig"); | 112 | const ZigModule = @import("ZigModule.zig"); |