| ... | ... | @@ -6,6 +6,9 @@ ptr_width: PtrWidth, |
| 6 | 6 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 7 | 7 | llvm_object: ?*LlvmObject = null, |
| 8 | 8 | |
| 9 | /// A list of all input files. |
| 10 | /// Index of each input file also encodes the priority or precedence of one input file |
| 11 | /// over another. |
| 9 | 12 | files: std.MultiArrayList(File.Entry) = .{}, |
| 10 | 13 | zig_module_index: ?File.Index = null, |
| 11 | 14 | linker_defined_index: ?File.Index = null, |
| ... | ... | @@ -47,6 +50,7 @@ shstrtab: StringTable(.strtab) = .{}, |
| 47 | 50 | /// .strtab buffer |
| 48 | 51 | strtab: StringTable(.strtab) = .{}, |
| 49 | 52 | |
| 53 | /// Representation of the GOT table as committed to the file. |
| 50 | 54 | got: GotSection = .{}, |
| 51 | 55 | |
| 52 | 56 | text_section_index: ?u16 = null, |
| ... | ... | @@ -86,10 +90,10 @@ rela_iplt_start_index: ?Symbol.Index = null, |
| 86 | 90 | rela_iplt_end_index: ?Symbol.Index = null, |
| 87 | 91 | start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, |
| 88 | 92 | |
| 93 | /// An array of symbols parsed across all input files. |
| 89 | 94 | symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 90 | 95 | symbols_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 91 | 96 | resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, |
| 92 | | unresolved: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, |
| 93 | 97 | symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 94 | 98 | |
| 95 | 99 | phdr_table_dirty: bool = false, |
| ... | ... | @@ -271,7 +275,6 @@ pub fn deinit(self: *Elf) void { |
| 271 | 275 | self.symbols_free_list.deinit(gpa); |
| 272 | 276 | self.got.deinit(gpa); |
| 273 | 277 | self.resolver.deinit(gpa); |
| 274 | | self.unresolved.deinit(gpa); |
| 275 | 278 | self.start_stop_indexes.deinit(gpa); |
| 276 | 279 | |
| 277 | 280 | { |
| ... | ... | @@ -316,7 +319,7 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. |
| 316 | 319 | const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?; |
| 317 | 320 | try parent_atom.addReloc(self, .{ |
| 318 | 321 | .r_offset = reloc_info.offset, |
| 319 | | .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | elf.R_X86_64_64, |
| 322 | .r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | elf.R_X86_64_64, |
| 320 | 323 | .r_addend = reloc_info.addend, |
| 321 | 324 | }); |
| 322 | 325 | |
| ... | ... | @@ -997,12 +1000,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 997 | 1000 | }; |
| 998 | 1001 | _ = compiler_rt_path; |
| 999 | 1002 | |
| 1000 | | // Parse input files |
| 1003 | // Here we will parse input positional and library files (if referenced). |
| 1004 | // This will roughly match in any linker backend we support. |
| 1001 | 1005 | var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); |
| 1002 | 1006 | defer positionals.deinit(); |
| 1003 | 1007 | try positionals.ensureUnusedCapacity(self.base.options.objects.len); |
| 1004 | 1008 | positionals.appendSliceAssumeCapacity(self.base.options.objects); |
| 1005 | 1009 | |
| 1010 | // This is a set of object files emitted by clang in a single `build-exe` invocation. |
| 1011 | // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up |
| 1012 | // in this set. |
| 1006 | 1013 | for (comp.c_object_table.keys()) |key| { |
| 1007 | 1014 | try positionals.append(.{ .path = key.status.success.object_path }); |
| 1008 | 1015 | } |
| ... | ... | @@ -1016,6 +1023,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1016 | 1023 | try self.handleAndReportParseError(obj.path, err, &parse_ctx); |
| 1017 | 1024 | } |
| 1018 | 1025 | |
| 1026 | // Handle any lazy symbols that were emitted by incremental compilation. |
| 1019 | 1027 | if (self.lazy_syms.getPtr(.none)) |metadata| { |
| 1020 | 1028 | // Most lazy symbols can be updated on first use, but |
| 1021 | 1029 | // anyerror needs to wait for everything to be flushed. |
| ... | ... | @@ -1046,27 +1054,34 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1046 | 1054 | try dw.flushModule(module); |
| 1047 | 1055 | } |
| 1048 | 1056 | |
| 1057 | // If we haven't already, create a linker-generated input file comprising of |
| 1058 | // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. |
| 1049 | 1059 | if (self.linker_defined_index == null) { |
| 1050 | 1060 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 1051 | 1061 | self.files.set(index, .{ .linker_defined = .{ .index = index } }); |
| 1052 | 1062 | self.linker_defined_index = index; |
| 1053 | 1063 | } |
| 1054 | | |
| 1055 | | // Symbol resolution happens here |
| 1056 | 1064 | try self.addLinkerDefinedSymbols(); |
| 1065 | |
| 1066 | // Now, we are ready to resolve the symbols across all input files. |
| 1067 | // We will first resolve the files in the ZigModule, next in the parsed |
| 1068 | // input Object files. |
| 1069 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak |
| 1070 | // symbol for potential resolution at load-time. |
| 1057 | 1071 | self.resolveSymbols(); |
| 1058 | 1072 | self.markImportsExports(); |
| 1059 | 1073 | self.claimUnresolved(); |
| 1060 | 1074 | |
| 1061 | | // Scan and create missing synthetic entries such as GOT indirection |
| 1075 | // Scan and create missing synthetic entries such as GOT indirection. |
| 1062 | 1076 | try self.scanRelocs(); |
| 1063 | 1077 | |
| 1064 | | // Allocate atoms parsed from input object files |
| 1078 | // Allocate atoms parsed from input object files, followed by allocating |
| 1079 | // linker-defined synthetic symbols. |
| 1065 | 1080 | try self.allocateObjects(); |
| 1066 | 1081 | self.allocateLinkerDefinedSymbols(); |
| 1067 | 1082 | |
| 1068 | 1083 | // Beyond this point, everything has been allocated a virtual address and we can resolve |
| 1069 | | // the relocations. |
| 1084 | // the relocations, and commit objects to file. |
| 1070 | 1085 | if (self.zig_module_index) |index| { |
| 1071 | 1086 | for (self.file(index).?.zig_module.atoms.keys()) |atom_index| { |
| 1072 | 1087 | const atom_ptr = self.atom(atom_index).?; |
| ... | ... | @@ -1083,9 +1098,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1083 | 1098 | } |
| 1084 | 1099 | try self.writeObjects(); |
| 1085 | 1100 | |
| 1101 | // Generate and emit the symbol table. |
| 1086 | 1102 | try self.updateSymtabSize(); |
| 1087 | 1103 | try self.writeSymtab(); |
| 1088 | 1104 | |
| 1105 | // Dump the state for easy debugging. |
| 1106 | // State can be dumped via `--debug-log link_state`. |
| 1089 | 1107 | if (build_options.enable_logging) { |
| 1090 | 1108 | state_log.debug("{}", .{self.dumpState()}); |
| 1091 | 1109 | } |
| ... | ... | @@ -1393,17 +1411,32 @@ fn claimUnresolved(self: *Elf) void { |
| 1393 | 1411 | } |
| 1394 | 1412 | } |
| 1395 | 1413 | |
| 1414 | /// In scanRelocs we will go over all live atoms and scan their relocs. |
| 1415 | /// This will help us work out what synthetics to emit, GOT indirection, etc. |
| 1416 | /// This is also the point where we will report undefined symbols for any |
| 1417 | /// alloc sections. |
| 1396 | 1418 | fn scanRelocs(self: *Elf) !void { |
| 1419 | const gpa = self.base.allocator; |
| 1420 | |
| 1421 | var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); |
| 1422 | defer { |
| 1423 | var it = undefs.iterator(); |
| 1424 | while (it.next()) |entry| { |
| 1425 | entry.value_ptr.deinit(); |
| 1426 | } |
| 1427 | undefs.deinit(); |
| 1428 | } |
| 1429 | |
| 1397 | 1430 | if (self.zig_module_index) |index| { |
| 1398 | 1431 | const zig_module = self.file(index).?.zig_module; |
| 1399 | | try zig_module.scanRelocs(self); |
| 1432 | try zig_module.scanRelocs(self, &undefs); |
| 1400 | 1433 | } |
| 1401 | 1434 | for (self.objects.items) |index| { |
| 1402 | 1435 | const object = self.file(index).?.object; |
| 1403 | | try object.scanRelocs(self); |
| 1436 | try object.scanRelocs(self, &undefs); |
| 1404 | 1437 | } |
| 1405 | 1438 | |
| 1406 | | // try self.reportUndefined(); |
| 1439 | try self.reportUndefined(&undefs); |
| 1407 | 1440 | |
| 1408 | 1441 | for (self.symbols.items) |*sym| { |
| 1409 | 1442 | if (sym.flags.needs_got) { |
| ... | ... | @@ -1433,8 +1466,10 @@ fn allocateObjects(self: *Elf) !void { |
| 1433 | 1466 | |
| 1434 | 1467 | for (object.globals()) |global_index| { |
| 1435 | 1468 | const global = self.symbol(global_index); |
| 1469 | const atom_ptr = global.atom(self) orelse continue; |
| 1470 | if (!atom_ptr.alive) continue; |
| 1436 | 1471 | if (global.file_index == index) { |
| 1437 | | global.value = global.atom(self).?.value; |
| 1472 | global.value = atom_ptr.value; |
| 1438 | 1473 | } |
| 1439 | 1474 | } |
| 1440 | 1475 | } |
| ... | ... | @@ -2829,21 +2864,23 @@ pub fn updateDeclExports( |
| 2829 | 2864 | }; |
| 2830 | 2865 | const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info)); |
| 2831 | 2866 | |
| 2867 | const name_off = try self.strtab.insert(gpa, exp_name); |
| 2832 | 2868 | const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { |
| 2833 | 2869 | const sym_index = try zig_module.addGlobalEsym(gpa); |
| 2834 | | _ = try zig_module.global_symbols.addOne(gpa); |
| 2870 | const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, name_off); |
| 2871 | const esym = zig_module.elfSym(sym_index); |
| 2872 | esym.st_name = name_off; |
| 2873 | lookup_gop.value_ptr.* = sym_index; |
| 2835 | 2874 | try decl_metadata.exports.append(gpa, sym_index); |
| 2875 | const gop = try self.getOrPutGlobal(name_off); |
| 2876 | try zig_module.global_symbols.append(gpa, gop.index); |
| 2836 | 2877 | break :blk sym_index; |
| 2837 | 2878 | }; |
| 2838 | | const name_off = try self.strtab.insert(gpa, exp_name); |
| 2839 | | const esym = &zig_module.global_esyms.items[sym_index]; |
| 2879 | const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff]; |
| 2840 | 2880 | esym.st_value = decl_sym.value; |
| 2841 | 2881 | esym.st_shndx = decl_sym.atom_index; |
| 2842 | 2882 | esym.st_info = (stb_bits << 4) | stt_bits; |
| 2843 | 2883 | esym.st_name = name_off; |
| 2844 | | |
| 2845 | | const gop = try self.getOrPutGlobal(name_off); |
| 2846 | | zig_module.global_symbols.items[sym_index] = gop.index; |
| 2847 | 2884 | } |
| 2848 | 2885 | } |
| 2849 | 2886 | |
| ... | ... | @@ -3636,16 +3673,17 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 |
| 3636 | 3673 | _ = lib_name; |
| 3637 | 3674 | const gpa = self.base.allocator; |
| 3638 | 3675 | const off = try self.strtab.insert(gpa, name); |
| 3639 | | const gop = try self.getOrPutGlobal(off); |
| 3640 | 3676 | const zig_module = self.file(self.zig_module_index.?).?.zig_module; |
| 3641 | 3677 | const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off); |
| 3642 | 3678 | if (!lookup_gop.found_existing) { |
| 3643 | 3679 | const esym_index = try zig_module.addGlobalEsym(gpa); |
| 3644 | | const esym = &zig_module.global_esyms.items[esym_index]; |
| 3680 | const esym = zig_module.elfSym(esym_index); |
| 3645 | 3681 | esym.st_name = off; |
| 3646 | 3682 | lookup_gop.value_ptr.* = esym_index; |
| 3683 | const gop = try self.getOrPutGlobal(off); |
| 3684 | try zig_module.global_symbols.append(gpa, gop.index); |
| 3647 | 3685 | } |
| 3648 | | return gop.index; |
| 3686 | return lookup_gop.value_ptr.*; |
| 3649 | 3687 | } |
| 3650 | 3688 | |
| 3651 | 3689 | const GetOrCreateComdatGroupOwnerResult = struct { |
| ... | ... | @@ -3684,89 +3722,39 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO |
| 3684 | 3722 | return &self.comdat_groups_owners.items[index]; |
| 3685 | 3723 | } |
| 3686 | 3724 | |
| 3687 | | fn reportUndefined(self: *Elf) !void { |
| 3725 | fn reportUndefined(self: *Elf, undefs: anytype) !void { |
| 3688 | 3726 | const gpa = self.base.allocator; |
| 3689 | 3727 | const max_notes = 4; |
| 3690 | 3728 | |
| 3691 | | try self.misc_errors.ensureUnusedCapacity(gpa, self.unresolved.keys().len); |
| 3692 | | |
| 3693 | | const CollectStruct = struct { |
| 3694 | | notes: [max_notes]link.File.ErrorMsg = [_]link.File.ErrorMsg{.{ .msg = undefined }} ** max_notes, |
| 3695 | | notes_len: u3 = 0, |
| 3696 | | notes_count: usize = 0, |
| 3697 | | }; |
| 3698 | | |
| 3699 | | const collect: []CollectStruct = try gpa.alloc(CollectStruct, self.unresolved.keys().len); |
| 3700 | | defer gpa.free(collect); |
| 3701 | | @memset(collect, .{}); |
| 3702 | | |
| 3703 | | // Collect all references across all input files |
| 3704 | | if (self.zig_module_index) |index| { |
| 3705 | | const zig_module = self.file(index).?.zig_module; |
| 3706 | | for (zig_module.atoms.keys()) |atom_index| { |
| 3707 | | const atom_ptr = self.atom(atom_index).?; |
| 3708 | | if (!atom_ptr.alive) continue; |
| 3709 | | |
| 3710 | | for (atom_ptr.relocs(self)) |rel| { |
| 3711 | | if (self.unresolved.getIndex(rel.r_sym())) |bin_index| { |
| 3712 | | const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ |
| 3713 | | zig_module.path, |
| 3714 | | atom_ptr.name(self), |
| 3715 | | }); |
| 3716 | | const bin = &collect[bin_index]; |
| 3717 | | if (bin.notes_len < max_notes) { |
| 3718 | | bin.notes[bin.notes_len] = .{ .msg = note }; |
| 3719 | | bin.notes_len += 1; |
| 3720 | | } |
| 3721 | | bin.notes_count += 1; |
| 3722 | | } |
| 3723 | | } |
| 3724 | | } |
| 3725 | | } |
| 3726 | | |
| 3727 | | for (self.objects.items) |index| { |
| 3728 | | const object = self.file(index).?.object; |
| 3729 | | for (object.atoms.items) |atom_index| { |
| 3730 | | const atom_ptr = self.atom(atom_index) orelse continue; |
| 3731 | | if (!atom_ptr.alive) continue; |
| 3729 | try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count()); |
| 3732 | 3730 | |
| 3733 | | for (atom_ptr.relocs(self)) |rel| { |
| 3734 | | const sym_index = object.symbols.items[rel.r_sym()]; |
| 3735 | | if (self.unresolved.getIndex(sym_index)) |bin_index| { |
| 3736 | | const note = try std.fmt.allocPrint(gpa, "referenced by {}:{s}", .{ |
| 3737 | | object.fmtPath(), |
| 3738 | | atom_ptr.name(self), |
| 3739 | | }); |
| 3740 | | const bin = &collect[bin_index]; |
| 3741 | | if (bin.notes_len < max_notes) { |
| 3742 | | bin.notes[bin.notes_len] = .{ .msg = note }; |
| 3743 | | bin.notes_len += 1; |
| 3744 | | } |
| 3745 | | bin.notes_count += 1; |
| 3746 | | } |
| 3747 | | } |
| 3748 | | } |
| 3749 | | } |
| 3750 | | |
| 3751 | | // Generate error notes |
| 3752 | | for (self.unresolved.keys(), 0..) |sym_index, bin_index| { |
| 3753 | | const collected = &collect[bin_index]; |
| 3731 | var it = undefs.iterator(); |
| 3732 | while (it.next()) |entry| { |
| 3733 | const undef_index = entry.key_ptr.*; |
| 3734 | const atoms = entry.value_ptr.*.items; |
| 3735 | const nnotes = @min(atoms.len, max_notes); |
| 3754 | 3736 | |
| 3755 | 3737 | var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1); |
| 3756 | 3738 | defer notes.deinit(); |
| 3757 | 3739 | |
| 3758 | | for (collected.notes[0..collected.notes_len]) |note| { |
| 3759 | | notes.appendAssumeCapacity(note); |
| 3740 | for (atoms[0..nnotes]) |atom_index| { |
| 3741 | const atom_ptr = self.atom(atom_index).?; |
| 3742 | const file_ptr = self.file(atom_ptr.file_index).?; |
| 3743 | const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ |
| 3744 | file_ptr.fmtPath(), |
| 3745 | atom_ptr.name(self), |
| 3746 | }); |
| 3747 | notes.appendAssumeCapacity(.{ .msg = note }); |
| 3760 | 3748 | } |
| 3761 | 3749 | |
| 3762 | | if (collected.notes_count > max_notes) { |
| 3763 | | const remaining = collected.notes_count - max_notes; |
| 3750 | if (atoms.len > max_notes) { |
| 3751 | const remaining = atoms.len - max_notes; |
| 3764 | 3752 | const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining}); |
| 3765 | 3753 | notes.appendAssumeCapacity(.{ .msg = note }); |
| 3766 | 3754 | } |
| 3767 | 3755 | |
| 3768 | 3756 | var err_msg = link.File.ErrorMsg{ |
| 3769 | | .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(sym_index).name(self)}), |
| 3757 | .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)}), |
| 3770 | 3758 | }; |
| 3771 | 3759 | err_msg.notes = try notes.toOwnedSlice(); |
| 3772 | 3760 | |
| ... | ... | @@ -3931,7 +3919,7 @@ const DeclMetadata = struct { |
| 3931 | 3919 | fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { |
| 3932 | 3920 | const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module; |
| 3933 | 3921 | for (m.exports.items) |*exp| { |
| 3934 | | const exp_name = elf_file.strtab.getAssumeExists(zig_module.global_esyms.items[exp.*].st_name); |
| 3922 | const exp_name = elf_file.strtab.getAssumeExists(zig_module.elfSym(exp.*).st_name); |
| 3935 | 3923 | if (mem.eql(u8, name, exp_name)) return exp; |
| 3936 | 3924 | } |
| 3937 | 3925 | return null; |