| 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 | 9 | files: std.MultiArrayList(File.Entry) = .{}, |
| 10 | 10 | zig_module_index: ?File.Index = null, |
| 11 | 11 | linker_defined_index: ?File.Index = null, |
| 12 | objects: std.ArrayListUnmanaged(File.Index) = .{}, | |
| 12 | 13 | |
| 13 | 14 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 14 | 15 | /// Same order as in the file. |
| ... | ... | @@ -51,11 +52,12 @@ got: GotSection = .{}, |
| 51 | 52 | text_section_index: ?u16 = null, |
| 52 | 53 | rodata_section_index: ?u16 = null, |
| 53 | 54 | data_section_index: ?u16 = null, |
| 55 | eh_frame_section_index: ?u16 = null, | |
| 56 | eh_frame_hdr_section_index: ?u16 = null, | |
| 54 | 57 | dynamic_section_index: ?u16 = null, |
| 55 | 58 | got_section_index: ?u16 = null, |
| 56 | 59 | got_plt_section_index: ?u16 = null, |
| 57 | 60 | plt_section_index: ?u16 = null, |
| 58 | eh_frame_hdr_section_index: ?u16 = null, | |
| 59 | 61 | rela_dyn_section_index: ?u16 = null, |
| 60 | 62 | debug_info_section_index: ?u16 = null, |
| 61 | 63 | debug_abbrev_section_index: ?u16 = null, |
| ... | ... | @@ -136,6 +138,10 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre |
| 136 | 138 | /// with `Decl` `main`, and lives as long as that `Decl`. |
| 137 | 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 | 145 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); |
| 140 | 146 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); |
| 141 | 147 | |
| ... | ... | @@ -249,10 +255,11 @@ pub fn deinit(self: *Elf) void { |
| 249 | 255 | .null => {}, |
| 250 | 256 | .zig_module => data.zig_module.deinit(gpa), |
| 251 | 257 | .linker_defined => data.linker_defined.deinit(gpa), |
| 252 | // .object => data.object.deinit(gpa), | |
| 258 | .object => data.object.deinit(gpa), | |
| 253 | 259 | // .shared_object => data.shared_object.deinit(gpa), |
| 254 | 260 | }; |
| 255 | 261 | self.files.deinit(gpa); |
| 262 | self.objects.deinit(gpa); | |
| 256 | 263 | |
| 257 | 264 | self.shdrs.deinit(gpa); |
| 258 | 265 | self.phdr_to_shdr_table.deinit(gpa); |
| ... | ... | @@ -295,6 +302,9 @@ pub fn deinit(self: *Elf) void { |
| 295 | 302 | } |
| 296 | 303 | |
| 297 | 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 | 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 | 838 | const zig_module = self.file(index).?.zig_module; |
| 829 | 839 | const sym_index = try zig_module.addLocal(self); |
| 830 | 840 | const sym = self.symbol(sym_index); |
| 831 | const esym = sym.sourceSymbol(self); | |
| 841 | const esym = zig_module.sourceSymbol(sym_index, self); | |
| 832 | 842 | const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path)); |
| 833 | 843 | sym.name_offset = name_off; |
| 834 | 844 | esym.st_name = name_off; |
| ... | ... | @@ -1276,14 +1286,35 @@ fn parsePositional( |
| 1276 | 1286 | ) ParseError!void { |
| 1277 | 1287 | const tracy = trace(@src()); |
| 1278 | 1288 | defer tracy.end(); |
| 1279 | ||
| 1280 | _ = self; | |
| 1281 | _ = in_file; | |
| 1282 | _ = path; | |
| 1283 | 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 | 1320 | fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | ... | @@ -2226,6 +2257,7 @@ fn updateDeclCode( |
| 2226 | 2257 | ) !void { |
| 2227 | 2258 | const gpa = self.base.allocator; |
| 2228 | 2259 | const mod = self.base.options.module.?; |
| 2260 | const zig_module = self.file(self.zig_module_index.?).?.zig_module; | |
| 2229 | 2261 | const decl = mod.declPtr(decl_index); |
| 2230 | 2262 | |
| 2231 | 2263 | const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); |
| ... | ... | @@ -2234,7 +2266,7 @@ fn updateDeclCode( |
| 2234 | 2266 | const required_alignment = decl.getAlignment(mod); |
| 2235 | 2267 | |
| 2236 | 2268 | const sym = self.symbol(sym_index); |
| 2237 | const esym = sym.sourceSymbol(self); | |
| 2269 | const esym = zig_module.sourceSymbol(sym_index, self); | |
| 2238 | 2270 | const atom_ptr = sym.atom(self).?; |
| 2239 | 2271 | const shdr_index = sym.output_section_index; |
| 2240 | 2272 | |
| ... | ... | @@ -2447,6 +2479,7 @@ pub fn updateDecl( |
| 2447 | 2479 | fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void { |
| 2448 | 2480 | const gpa = self.base.allocator; |
| 2449 | 2481 | const mod = self.base.options.module.?; |
| 2482 | const zig_module = self.file(self.zig_module_index.?).?.zig_module; | |
| 2450 | 2483 | |
| 2451 | 2484 | var required_alignment: u32 = undefined; |
| 2452 | 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 | 2523 | const local_sym = self.symbol(symbol_index); |
| 2491 | 2524 | const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?; |
| 2492 | 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 | 2527 | local_esym.st_name = name_str_index; |
| 2495 | 2528 | local_esym.st_info |= elf.STT_OBJECT; |
| 2496 | 2529 | local_esym.st_size = code.len; |
| ... | ... | @@ -2566,7 +2599,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module |
| 2566 | 2599 | const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; |
| 2567 | 2600 | const local_sym = self.symbol(sym_index); |
| 2568 | 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 | 2603 | local_esym.st_name = name_str_index; |
| 2571 | 2604 | local_esym.st_info |= elf.STT_OBJECT; |
| 2572 | 2605 | local_esym.st_size = code.len; |
| ... | ... | @@ -2650,8 +2683,8 @@ pub fn updateDeclExports( |
| 2650 | 2683 | }; |
| 2651 | 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 | 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 | 2688 | const sym_index = try zig_module.addGlobal(exp_name, self); |
| 2656 | 2689 | try decl_metadata.exports.append(gpa, sym_index); |
| 2657 | 2690 | break :blk sym_index; |
| ... | ... | @@ -2660,8 +2693,8 @@ pub fn updateDeclExports( |
| 2660 | 2693 | sym.value = decl_sym.value; |
| 2661 | 2694 | sym.atom_index = decl_sym.atom_index; |
| 2662 | 2695 | sym.output_section_index = decl_sym.output_section_index; |
| 2663 | const esym = sym.sourceSymbol(self); | |
| 2664 | esym.* = decl_esym.*; | |
| 2696 | const esym = zig_module.sourceSymbol(sym_index, self); | |
| 2697 | esym.* = decl_esym; | |
| 2665 | 2698 | esym.st_info = (stb_bits << 4) | stt_bits; |
| 2666 | 2699 | } |
| 2667 | 2700 | } |
| ... | ... | @@ -2691,11 +2724,12 @@ pub fn deleteDeclExport( |
| 2691 | 2724 | const metadata = self.decls.getPtr(decl_index) orelse return; |
| 2692 | 2725 | const gpa = self.base.allocator; |
| 2693 | 2726 | const mod = self.base.options.module.?; |
| 2727 | const zig_module = self.file(self.zig_module_index.?).?.zig_module; | |
| 2694 | 2728 | const exp_name = mod.intern_pool.stringToSlice(name); |
| 2695 | 2729 | const sym_index = metadata.@"export"(self, exp_name) orelse return; |
| 2696 | 2730 | log.debug("deleting export '{s}'", .{exp_name}); |
| 2697 | 2731 | const sym = self.symbol(sym_index.*); |
| 2698 | const esym = sym.sourceSymbol(self); | |
| 2732 | const esym = zig_module.sourceSymbol(sym_index.*, self); | |
| 2699 | 2733 | assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant |
| 2700 | 2734 | sym.* = .{}; |
| 2701 | 2735 | // TODO free list for esym! |
| ... | ... | @@ -3337,6 +3371,7 @@ pub fn file(self: *Elf, index: File.Index) ?File { |
| 3337 | 3371 | .null => null, |
| 3338 | 3372 | .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined }, |
| 3339 | 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 | 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 | 3516 | fn reportUndefined(self: *Elf) !void { |
| 3446 | 3517 | const gpa = self.base.allocator; |
| 3447 | 3518 | const max_notes = 4; |
| ... | ... | @@ -3552,6 +3623,21 @@ fn fmtDumpState( |
| 3552 | 3623 | try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path }); |
| 3553 | 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 | 3641 | if (self.linker_defined_index) |index| { |
| 3556 | 3642 | const linker_defined = self.file(index).?.linker_defined; |
| 3557 | 3643 | try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); |
| ... | ... | @@ -3560,6 +3646,37 @@ fn fmtDumpState( |
| 3560 | 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 | 3680 | const default_entry_addr = 0x8000000; |
| 3564 | 3681 | |
| 3565 | 3682 | pub const base_tag: link.File.Tag = .elf; |
| ... | ... | @@ -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 | 3738 | pub const SymtabSize = struct { |
| 3611 | 3739 | nlocals: u32 = 0, |
| 3612 | 3740 | nglobals: u32 = 0, |
| ... | ... | @@ -3654,6 +3782,7 @@ const LinkerDefined = @import("Elf/LinkerDefined.zig"); |
| 3654 | 3782 | const Liveness = @import("../Liveness.zig"); |
| 3655 | 3783 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 3656 | 3784 | const Module = @import("../Module.zig"); |
| 3785 | const Object = @import("Elf/Object.zig"); | |
| 3657 | 3786 | const InternPool = @import("../InternPool.zig"); |
| 3658 | 3787 | const Package = @import("../Package.zig"); |
| 3659 | 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 | 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 | 89 | /// Returns how much room there is to grow in virtual address space. |
| 50 | 90 | /// File offset relocation happens transparently, so it is not included in |
| 51 | 91 | /// this calculation. |
| ... | ... | @@ -247,11 +287,12 @@ pub fn free(self: *Atom, elf_file: *Elf) void { |
| 247 | 287 | self.* = .{}; |
| 248 | 288 | } |
| 249 | 289 | |
| 250 | pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { | |
| 251 | const file_ptr = elf_file.file(self.file_index).?; | |
| 252 | if (file_ptr != .zig_module) @panic("TODO"); | |
| 253 | const zig_module = file_ptr.zig_module; | |
| 254 | return zig_module.relocs.items[self.relocs_section_index].items; | |
| 290 | pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { | |
| 291 | return switch (elf_file.file(self.file_index).?) { | |
| 292 | .zig_module => |x| x.relocs.items[self.relocs_section_index].items, | |
| 293 | .object => |x| x.getRelocs(self.relocs_section_index), | |
| 294 | else => unreachable, | |
| 295 | }; | |
| 255 | 296 | } |
| 256 | 297 | |
| 257 | 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 | 1 | index: File.Index, |
| 2 | 2 | symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, |
| 3 | 3 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 4 | alive: bool = true, | |
| 5 | 4 | |
| 6 | 5 | output_symtab_size: Elf.SymtabSize = .{}, |
| 7 | 6 | |
| ... | ... | @@ -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 | 78 | pub fn globals(self: *LinkerDefined) []const Symbol.Index { |
| 84 | 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 | 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 | 70 | const file_ptr = symbol.file(elf_file).?; |
| 71 | 71 | switch (file_ptr) { |
| 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), | |
| 72 | .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file).*, | |
| 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 | 78 | pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { |
| 78 | 79 | const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32); |
| 79 | 80 | const sym = symbol.sourceSymbol(elf_file); |
| 80 | const in_archive = switch (file) { | |
| 81 | // .object => |x| !x.alive, | |
| 81 | const in_archive = switch (file_ptr) { | |
| 82 | .object => |x| !x.alive, | |
| 82 | 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 | 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 | 11 | atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, |
| 12 | 12 | relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, |
| 13 | 13 | |
| 14 | alive: bool = true, | |
| 15 | ||
| 16 | 14 | output_symtab_size: Elf.SymtabSize = .{}, |
| 17 | 15 | |
| 18 | 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 | 35 | const symbol_ptr = elf_file.symbol(symbol_index); |
| 38 | 36 | symbol_ptr.atom_index = atom_index; |
| 39 | 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 | 39 | local_esym.st_shndx = output_section_index; |
| 42 | 40 | const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); |
| 43 | 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 | 80 | pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { |
| 83 | 81 | for (self.locals()) |local_index| { |
| 84 | 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 | 84 | switch (esym.st_type()) { |
| 87 | 85 | elf.STT_SECTION, elf.STT_NOTYPE => { |
| 88 | 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 | 1 | pub const File = union(enum) { |
| 2 | 2 | zig_module: *ZigModule, |
| 3 | 3 | linker_defined: *LinkerDefined, |
| 4 | // object: *Object, | |
| 4 | object: *Object, | |
| 5 | 5 | // shared_object: *SharedObject, |
| 6 | 6 | |
| 7 | 7 | pub fn index(file: File) Index { |
| ... | ... | @@ -25,7 +25,7 @@ pub const File = union(enum) { |
| 25 | 25 | switch (file) { |
| 26 | 26 | .zig_module => try writer.writeAll("(zig module)"), |
| 27 | 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 | 29 | // .shared_object => |x| try writer.writeAll(x.path), |
| 30 | 30 | } |
| 31 | 31 | } |
| ... | ... | @@ -95,7 +95,7 @@ pub const File = union(enum) { |
| 95 | 95 | null: void, |
| 96 | 96 | zig_module: ZigModule, |
| 97 | 97 | linker_defined: LinkerDefined, |
| 98 | // object: Object, | |
| 98 | object: Object, | |
| 99 | 99 | // shared_object: SharedObject, |
| 100 | 100 | }; |
| 101 | 101 | }; |
| ... | ... | @@ -106,7 +106,7 @@ const elf = std.elf; |
| 106 | 106 | const Allocator = std.mem.Allocator; |
| 107 | 107 | const Elf = @import("../Elf.zig"); |
| 108 | 108 | const LinkerDefined = @import("LinkerDefined.zig"); |
| 109 | // const Object = @import("Object.zig"); | |
| 109 | const Object = @import("Object.zig"); | |
| 110 | 110 | // const SharedObject = @import("SharedObject.zig"); |
| 111 | 111 | const Symbol = @import("Symbol.zig"); |
| 112 | 112 | const ZigModule = @import("ZigModule.zig"); |