authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:34-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:22:42-04:00
logb6192adfb22da64973c961297b60a74a5b6c9b23
treeb680901e7b2545dacb9fed22b022455ee4b4d46f
parent14a7131c4f97895d6ab600b88940b6afb6d6ae4e

Coff: Linking input objects

- Input object validation - Load sections, symbols, and relocs from input objects - Load reloc addends from the reloc locations in input objects - Flush input sections into the output

3 files changed, 669 insertions(+), 139 deletions(-)

lib/std/coff.zig+12-9
......@@ -666,7 +666,7 @@ pub const Symbol = extern struct {
666666 storage_class: StorageClass,
667667 number_of_aux_symbols: u8,
668668
669 pub fn sizeOf() usize {
669 pub fn sizeOf() comptime_int {
670670 return 18;
671671 }
672672
......@@ -929,7 +929,7 @@ pub const WeakExternalDefinition = extern struct {
929929
930930 unused: [10]u8,
931931
932 pub fn sizeOf() usize {
932 pub fn sizeOf() comptime_int {
933933 return 18;
934934 }
935935};
......@@ -1393,7 +1393,7 @@ pub const Relocation = extern struct {
13931393 symbol_table_index: u32,
13941394 type: u16,
13951395
1396 pub fn sizeOf() usize {
1396 pub fn sizeOf() comptime_int {
13971397 return 10;
13981398 }
13991399};
......@@ -1986,11 +1986,14 @@ pub const ArchiveMemberHeader = extern struct {
19861986 end_of_header: [2]u8,
19871987};
19881988
1989pub const FirstLinkerMemberHeader = extern struct {
1990 /// Big-endian symbol count
1991 number_of_symbols: u32,
1992};
1989pub const LineNumber = extern struct {
1990 type: extern union {
1991 symbol_table_index: u32,
1992 virtual_address: u32,
1993 },
1994 line_number: u16,
19931995
1994pub const SecondLinkerMemberHeader = extern struct {
1995 number_of_members: u32,
1996 pub fn sizeOf() comptime_int {
1997 return 6;
1998 }
19961999};
src/codegen/x86_64/Emit.zig+3-3
......@@ -816,7 +816,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
816816 @enumFromInt(@intFromEnum(emit.atom_id)),
817817 end_offset - 4,
818818 @enumFromInt(@intFromEnum(target.symbol)),
819 reloc.off,
819 .{ .known = reloc.off },
820820 .{ .AMD64 = .REL32 },
821821 ) else unreachable,
822822 .branch => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
......@@ -854,7 +854,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
854854 @enumFromInt(@intFromEnum(emit.atom_id)),
855855 end_offset - 4,
856856 @enumFromInt(@intFromEnum(target.symbol)),
857 reloc.off,
857 .{ .known = reloc.off },
858858 .{ .AMD64 = .REL32 },
859859 ) else return emit.fail("TODO implement {s} reloc for {s}", .{
860860 @tagName(reloc.target), @tagName(emit.bin_file.tag),
......@@ -912,7 +912,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
912912 @enumFromInt(@intFromEnum(emit.atom_id)),
913913 end_offset - 4,
914914 @enumFromInt(@intFromEnum(target.symbol)),
915 reloc.off,
915 .{ .known = reloc.off },
916916 .{ .AMD64 = .SECREL },
917917 ) else return emit.fail("TODO implement {s} reloc for {s}", .{
918918 @tagName(reloc.target), @tagName(emit.bin_file.tag),
src/link/Coff.zig+654-127
......@@ -31,6 +31,18 @@ long_names_table: LongNamesTable,
3131import_table: ImportTable,
3232export_table: ExportTable,
3333symbol_table: SymbolTable,
34inputs: std.ArrayList(struct {
35 path: std.Build.Cache.Path,
36 archive_name: ?[]const u8,
37 first_si: Symbol.Index,
38 last_si: Symbol.Index,
39}),
40input_sections: std.ArrayList(struct {
41 ii: Node.InputIndex,
42 si: Symbol.Index,
43 file_location: MappedFile.Node.FileLocation,
44}),
45input_section_pending_index: u32,
3446strings: std.HashMapUnmanaged(
3547 u32,
3648 void,
......@@ -59,6 +71,7 @@ const_prog_node: std.Progress.Node,
5971synth_prog_node: std.Progress.Node,
6072symbol_prog_node: std.Progress.Node,
6173member_prog_node: std.Progress.Node,
74input_prog_node: std.Progress.Node,
6275dump_snapshot: bool,
6376
6477pub const default_file_alignment: u16 = 0x200;
......@@ -185,6 +198,7 @@ pub const Node = union(enum) {
185198
186199 pseudo_section: PseudoSectionMapIndex,
187200 object_section: ObjectSectionMapIndex,
201 input_section: InputSectionIndex,
188202 global: GlobalMapIndex,
189203 nav: NavMapIndex,
190204 uav: UavMapIndex,
......@@ -266,6 +280,46 @@ pub const Node = union(enum) {
266280 }
267281 };
268282
283 pub const InputIndex = enum(u32) {
284 _,
285
286 pub fn path(ii: InputIndex, coff: *const Coff) std.Build.Cache.Path {
287 return coff.inputs.items[@intFromEnum(ii)].path;
288 }
289
290 pub fn archiveName(ii: InputIndex, coff: *const Coff) ?[]const u8 {
291 return coff.inputs.items[@intFromEnum(ii)].archive_name;
292 }
293
294 pub fn firstSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
295 return coff.inputs.items[@intFromEnum(ii)].first_si;
296 }
297
298 pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
299 return coff.inputs.items[@intFromEnum(ii)].last_si;
300 }
301 };
302
303 pub const InputSectionIndex = enum(u32) {
304 _,
305
306 pub fn input(isi: InputSectionIndex, coff: *const Coff) InputIndex {
307 return coff.input_sections.items[@intFromEnum(isi)].ii;
308 }
309
310 pub fn fileLocation(isi: InputSectionIndex, coff: *const Coff) MappedFile.Node.FileLocation {
311 return coff.input_sections.items[@intFromEnum(isi)].file_location;
312 }
313
314 pub fn symbol(isi: InputSectionIndex, coff: *const Coff) Symbol.Index {
315 return coff.input_sections.items[@intFromEnum(isi)].si;
316 }
317
318 pub fn lastSymbol(isi: InputSectionIndex, coff: *const Coff) Symbol.Index {
319 return coff.input_sections.items[@intFromEnum(isi)].last_si;
320 }
321 };
322
269323 pub const LazyMapRef = struct {
270324 kind: link.File.LazySymbol.Kind,
271325 index: u32,
......@@ -648,15 +702,15 @@ pub const Section = struct {
648702 si: Symbol.Index,
649703 relocation_table_ni: MappedFile.Node.Index,
650704
651 pub const RelocationIndex = enum(u32) {
705 pub const RelocationIndex = enum(u16) {
652706 none,
653707 _,
654708
655 pub fn wrap(i: ?u32) RelocationIndex {
709 pub fn wrap(i: ?u16) RelocationIndex {
656710 return @enumFromInt((i orelse return .none) + 1);
657711 }
658712
659 pub fn unwrap(sri: RelocationIndex) ?u32 {
713 pub fn unwrap(sri: RelocationIndex) ?u16 {
660714 return switch (sri) {
661715 .none => null,
662716 _ => @intFromEnum(sri) - 1,
......@@ -680,7 +734,12 @@ pub const GlobalName = struct { name: String, lib_name: String.Optional };
680734pub const Symbol = struct {
681735 ni: MappedFile.Node.Index,
682736 rva: u32,
683 size: u32,
737 value: union {
738 /// For generated symbols, this is their size
739 size: u32,
740 /// For globals from input sections, this is the offset within the input section
741 input_offset: u32,
742 },
684743 /// Relocations contained within this symbol
685744 loc_relocs: Reloc.Index,
686745 /// Relocations targeting this symbol
......@@ -704,6 +763,10 @@ pub const Symbol = struct {
704763 return sn.section(coff).si;
705764 }
706765
766 pub fn name(sn: SectionNumber, coff: *const Coff) String {
767 return coff.section_table.keys()[sn.toIndex()];
768 }
769
707770 pub fn section(sn: SectionNumber, coff: *const Coff) *Section {
708771 return &coff.section_table.values()[sn.toIndex()];
709772 }
......@@ -732,6 +795,10 @@ pub const Symbol = struct {
732795 return ni;
733796 }
734797
798 pub fn next(si: Symbol.Index) Symbol.Index {
799 return @enumFromInt(@intFromEnum(si) + 1);
800 }
801
735802 pub fn knownString(si: Symbol.Index) String.Optional {
736803 return switch (si) {
737804 .null, _ => .none,
......@@ -742,6 +809,10 @@ pub const Symbol = struct {
742809 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
743810 const sym = si.get(coff);
744811 sym.rva = coff.computeNodeRva(sym.ni);
812 if (sym.gmi != .none and coff.getNode(sym.ni) == .input_section) {
813 // Symbols in input sections share a ni with their section
814 sym.rva += sym.value.input_offset;
815 }
745816 si.applyLocationRelocs(coff);
746817 si.applyTargetRelocs(coff);
747818 }
......@@ -761,13 +832,18 @@ pub const Symbol = struct {
761832
762833 pub fn applyLocationRelocs(si: Symbol.Index, coff: *Coff) void {
763834 const sym = si.get(coff);
764 for (coff.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
765 if (reloc.loc != si) break;
766 if (reloc.sri.entry(coff, sym.section_number)) |entry| coff.targetStore(
767 &entry.virtual_address,
768 @intCast(coff.computeNodeSectionOffset(sym.ni) + reloc.offset),
769 );
770 reloc.apply(coff);
835 switch (sym.loc_relocs) {
836 .none => {},
837 else => |loc_relocs| {
838 for (coff.relocs.items[@intFromEnum(loc_relocs)..]) |*reloc| {
839 if (reloc.loc != si) break;
840 if (reloc.sri.entry(coff, sym.section_number)) |entry| coff.targetStore(
841 &entry.virtual_address,
842 @intCast(coff.computeSymbolSectionOffset(sym) + reloc.offset),
843 );
844 reloc.apply(coff);
845 }
846 },
771847 }
772848 }
773849
......@@ -783,11 +859,16 @@ pub const Symbol = struct {
783859
784860 pub fn deleteLocationRelocs(si: Symbol.Index, coff: *Coff) void {
785861 const sym = si.get(coff);
786 for (coff.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
787 if (reloc.loc != si) break;
788 reloc.delete(coff);
862 switch (sym.loc_relocs) {
863 .none => {},
864 else => |loc_relocs| {
865 for (coff.relocs.items[@intFromEnum(loc_relocs)..]) |*reloc| {
866 if (reloc.loc != si) break;
867 reloc.delete(coff);
868 }
869 sym.loc_relocs = .none;
870 },
789871 }
790 sym.loc_relocs = .none;
791872 }
792873 };
793874
......@@ -797,14 +878,20 @@ pub const Symbol = struct {
797878};
798879
799880pub const Reloc = extern struct {
881 offset: u64,
882 addend: i64,
800883 type: Reloc.Type,
884 sri: Section.RelocationIndex,
801885 prev: Reloc.Index,
802886 next: Reloc.Index,
803887 loc: Symbol.Index,
804888 target: Symbol.Index,
805 sri: Section.RelocationIndex,
806 offset: u64,
807 addend: i64,
889 flags: packed struct(u8) {
890 // Indicates the addend is not known and should be recovered from the location itself.
891 // COFF relocation tables don't encode the addend, only the location.
892 recover_addend: bool,
893 _: u7 = 0,
894 },
808895
809896 pub const Type = extern union {
810897 AMD64: std.coff.IMAGE.REL.AMD64,
......@@ -827,7 +914,7 @@ pub const Reloc = extern struct {
827914 }
828915 };
829916
830 pub fn apply(reloc: *const Reloc, coff: *Coff) void {
917 pub fn apply(reloc: *Reloc, coff: *Coff) void {
831918 const loc_sym = reloc.loc.get(coff);
832919 switch (loc_sym.ni) {
833920 .none => return,
......@@ -836,9 +923,11 @@ pub const Reloc = extern struct {
836923
837924 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
838925 const target_endian = coff.targetEndian();
926 const target_machine = coff.targetLoad(&coff.headerPtr().machine);
839927
840928 if (!coff.isImage()) {
841 switch (coff.targetLoad(&coff.headerPtr().machine)) {
929 assert(!reloc.flags.recover_addend);
930 switch (target_machine) {
842931 else => |machine| @panic(@tagName(machine)),
843932 .AMD64 => switch (reloc.type.AMD64) {
844933 else => |kind| @panic(@tagName(kind)),
......@@ -890,6 +979,54 @@ pub const Reloc = extern struct {
890979 }
891980
892981 return;
982 } else if (reloc.flags.recover_addend) {
983 reloc.flags.recover_addend = false;
984 reloc.addend = switch (target_machine) {
985 else => |machine| @panic(@tagName(machine)),
986 .AMD64 => switch (reloc.type.AMD64) {
987 else => |kind| @panic(@tagName(kind)),
988 .ABSOLUTE => 0,
989 .ADDR64 => @bitCast(std.mem.readInt(
990 u64,
991 loc_slice[0..8],
992 target_endian,
993 )),
994 .ADDR32,
995 .ADDR32NB,
996 .REL32,
997 .REL32_1,
998 .REL32_2,
999 .REL32_3,
1000 .REL32_4,
1001 .REL32_5,
1002 .SECREL,
1003 => std.mem.readInt(
1004 u32,
1005 loc_slice[0..4],
1006 target_endian,
1007 ),
1008 },
1009 .I386 => switch (reloc.type.I386) {
1010 else => |kind| @panic(@tagName(kind)),
1011 .ABSOLUTE => 0,
1012 .DIR16,
1013 .REL16,
1014 => std.mem.readInt(
1015 u16,
1016 loc_slice[0..2],
1017 target_endian,
1018 ),
1019 .DIR32,
1020 .DIR32NB,
1021 .REL32,
1022 .SECREL,
1023 => std.mem.readInt(
1024 u32,
1025 loc_slice[0..4],
1026 target_endian,
1027 ),
1028 },
1029 };
8931030 }
8941031
8951032 const target_sym = reloc.target.get(coff);
......@@ -899,8 +1036,7 @@ pub const Reloc = extern struct {
8991036 }
9001037
9011038 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
902
903 switch (coff.targetLoad(&coff.headerPtr().machine)) {
1039 switch (target_machine) {
9041040 else => |machine| @panic(@tagName(machine)),
9051041 .AMD64 => switch (reloc.type.AMD64) {
9061042 else => |kind| @panic(@tagName(kind)),
......@@ -962,7 +1098,7 @@ pub const Reloc = extern struct {
9621098 .SECREL => std.mem.writeInt(
9631099 u32,
9641100 loc_slice[0..4],
965 @intCast(coff.computeNodeSectionOffset(target_sym.ni) + reloc.addend),
1101 @intCast(coff.computeSymbolSectionOffset(target_sym) + reloc.addend),
9661102 target_endian,
9671103 ),
9681104 },
......@@ -1002,7 +1138,7 @@ pub const Reloc = extern struct {
10021138 .SECREL => std.mem.writeInt(
10031139 u32,
10041140 loc_slice[0..4],
1005 @intCast(coff.computeNodeSectionOffset(target_sym.ni) + reloc.addend),
1141 @intCast(coff.computeSymbolSectionOffset(target_sym) + reloc.addend),
10061142 target_endian,
10071143 ),
10081144 },
......@@ -1133,6 +1269,9 @@ fn create(
11331269 .pending = .empty,
11341270 .pending_shrink = false,
11351271 },
1272 .inputs = .empty,
1273 .input_sections = .empty,
1274 .input_section_pending_index = 0,
11361275 .strings = .empty,
11371276 .string_bytes = .empty,
11381277 .section_table = .empty,
......@@ -1154,6 +1293,7 @@ fn create(
11541293 .synth_prog_node = .none,
11551294 .symbol_prog_node = .none,
11561295 .member_prog_node = .none,
1296 .input_prog_node = .none,
11571297 .dump_snapshot = options.enable_link_snapshots,
11581298 };
11591299 errdefer coff.deinit();
......@@ -1561,7 +1701,7 @@ fn initHeaders(
15611701 coff.symbols.addOneAssumeCapacity().* = .{
15621702 .ni = .none,
15631703 .rva = 0,
1564 .size = 0,
1704 .value = .{ .size = 0 },
15651705 .loc_relocs = .none,
15661706 .target_relocs = .none,
15671707 .section_number = .UNDEFINED,
......@@ -1701,12 +1841,18 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
17011841 coff.symbol_prog_node = prog_node.start("Symbols", coff.symbol_table.pending.count());
17021842 coff.member_prog_node = prog_node.start("Members", coff.pending_members.count());
17031843 }
1844 coff.input_prog_node = prog_node.start(
1845 "Inputs",
1846 coff.input_sections.items.len - coff.input_section_pending_index,
1847 );
17041848 coff.mf.update_prog_node = prog_node.start("Relocations", coff.mf.updates.items.len);
17051849}
17061850
17071851pub fn endProgress(coff: *Coff) void {
17081852 coff.mf.update_prog_node.end();
17091853 coff.mf.update_prog_node = .none;
1854 coff.input_prog_node.end();
1855 coff.input_prog_node = .none;
17101856 if (!isImage(coff)) {
17111857 coff.member_prog_node.end();
17121858 coff.member_prog_node = .none;
......@@ -1736,11 +1882,11 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
17361882 .section_table,
17371883 .export_name_table,
17381884 .placeholder,
1739
17401885 .symbol_table,
17411886 .string_table,
17421887 .relocation_table,
17431888 .relocation_table_entry,
1889 .input_section,
17441890 => unreachable,
17451891 .image_section => |si| si,
17461892 .import_directory_table => break :parent_rva coff.targetLoad(
......@@ -1781,9 +1927,12 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
17811927 const offset, _ = ni.location(&coff.mf).resolve(&coff.mf);
17821928 return @intCast(parent_rva + offset);
17831929}
1784fn computeNodeSectionOffset(coff: *Coff, ni: MappedFile.Node.Index) u32 {
1785 var section_offset: u32 = 0;
1786 var parent_ni = ni;
1930fn computeSymbolSectionOffset(coff: *Coff, sym: *const Symbol) u32 {
1931 var section_offset: u32 = if (sym.gmi != .none and coff.getNode(sym.ni) == .input_section)
1932 sym.value.input_offset
1933 else
1934 0;
1935 var parent_ni = sym.ni;
17871936 while (true) {
17881937 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
17891938 section_offset += @intCast(offset);
......@@ -1981,7 +2130,7 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
19812130 defer coff.symbols.addOneAssumeCapacity().* = .{
19822131 .ni = .none,
19832132 .rva = 0,
1984 .size = 0,
2133 .value = .{ .size = 0 },
19852134 .loc_relocs = .none,
19862135 .target_relocs = .none,
19872136 .section_number = .UNDEFINED,
......@@ -2003,6 +2152,15 @@ fn getOrPutString(coff: *Coff, string: []const u8) !String {
20032152fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
20042153 return (try coff.getOrPutString(string orelse return .none)).toOptional();
20052154}
2155fn getString(coff: *Coff, string: []const u8) ?String {
2156 if (coff.strings.getKeyAdapted(
2157 string,
2158 std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
2159 )) |key|
2160 return @enumFromInt(key)
2161 else
2162 return null;
2163}
20062164
20072165/// If the name does not fit in the symbol header, adds it to the symbol table string table.
20082166/// If the caller knows this name already has a String associated with it, they can avoid
......@@ -2105,7 +2263,7 @@ fn navSection(
21052263 const ip = &zcu.intern_pool;
21062264 const default: String, const attributes: ObjectSectionAttributes =
21072265 if (nav_resolved.@"threadlocal" and coff.base.comp.config.any_non_single_threaded) .{
2108 .@".tls$", .{ .read = true, .write = !coff.isImage() },
2266 .@".tls$", .{ .read = true, .write = true },
21092267 } else if (ip.isFunctionType(nav_resolved.type)) .{
21102268 .@".text", .{ .read = true, .execute = true },
21112269 } else if (nav_resolved.@"const") .{
......@@ -2189,7 +2347,7 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.
21892347 @enumFromInt(@intFromEnum(reloc_info.parent.atom_index)),
21902348 reloc_info.offset,
21912349 target_si,
2192 reloc_info.addend,
2350 .{ .known = reloc_info.addend },
21932351 switch (coff.targetLoad(&coff.headerPtr().machine)) {
21942352 else => unreachable,
21952353 .AMD64 => .{ .AMD64 = .ADDR64 },
......@@ -2467,19 +2625,40 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
24672625 };
24682626
24692627 coff.targetStore(&entry.value, switch (sym.section_number) {
2470 .UNDEFINED => sym.size,
2628 .UNDEFINED => sym.value.size,
24712629 .ABSOLUTE,
24722630 .DEBUG,
24732631 => unreachable,
24742632 else => switch (coff.getNode(sym.ni)) {
24752633 .image_section => 0,
2476 else => coff.computeNodeSectionOffset(sym.ni),
2634 else => coff.computeSymbolSectionOffset(sym),
24772635 },
24782636 });
24792637
24802638 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
24812639}
24822640
2641fn flushInputSection(coff: *Coff, isi: Node.InputSectionIndex) !void {
2642 const file_loc = isi.fileLocation(coff);
2643 if (file_loc.size == 0) return;
2644 const comp = coff.base.comp;
2645 const io = comp.io;
2646 const gpa = comp.gpa;
2647 const ii = isi.input(coff);
2648 const path = ii.path(coff);
2649 const file = try path.root_dir.handle.openFile(io, path.sub_path, .{});
2650 defer file.close(io);
2651 var fr = file.reader(io, &.{});
2652 try fr.seekTo(file_loc.offset);
2653 var nw: MappedFile.Node.Writer = undefined;
2654 const si = isi.symbol(coff);
2655 si.node(coff).writer(&coff.mf, gpa, &nw);
2656 defer nw.deinit();
2657 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
2658 return error.EndOfStream;
2659 si.applyLocationRelocs(coff);
2660}
2661
24832662fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
24842663 assert(coff.base.comp.zcu != null);
24852664
......@@ -2612,7 +2791,7 @@ fn pseudoSectionMapIndex(
26122791 const gpa = coff.base.comp.gpa;
26132792 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);
26142793 const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);
2615 if (!pseudo_section_gop.found_existing) {
2794 const sn = if (!pseudo_section_gop.found_existing) sn: {
26162795 const default_parent: Symbol.Index = if (attributes.execute)
26172796 .text
26182797 else if (attributes.write)
......@@ -2626,11 +2805,10 @@ fn pseudoSectionMapIndex(
26262805 default_parent.knownString().toSlice(coff).?,
26272806 ))
26282807 default_parent
2629 else if (coff.section_table.get(name)) |section| parent: {
2630 const header = section.si.get(coff).section_number.header(coff);
2631 try coff.verifyParentSectionAttributes(name, name, .fromFlags(header.flags), attributes);
2632 break :parent section.si;
2633 } else try coff.addSection(name, attributes.asFlags());
2808 else if (coff.section_table.get(name)) |section|
2809 section.si
2810 else
2811 try coff.addSection(name, attributes.asFlags());
26342812
26352813 try coff.nodes.ensureUnusedCapacity(gpa, 1);
26362814 try coff.symbols.ensureUnusedCapacity(gpa, 1);
......@@ -2644,9 +2822,30 @@ fn pseudoSectionMapIndex(
26442822 assert(sym.loc_relocs == .none);
26452823 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
26462824 coff.nodes.appendAssumeCapacity(.{ .pseudo_section = psmi });
2647 }
2825 break :sn sym.section_number;
2826 } else pseudo_section_gop.value_ptr.get(coff).section_number;
2827
2828 try coff.verifyParentSectionAttributes(
2829 .pseudo,
2830 sn.name(coff),
2831 name,
2832 .fromFlags(sn.header(coff).flags),
2833 attributes,
2834 );
2835
26482836 return psmi;
26492837}
2838
2839fn objectSectionParentName(coff: *Coff, name: []const u8) []const u8 {
2840 // In images we want to sort object sections into the final root section name.
2841 // Otherwise, we want to keep the full name so that this sort can occur correctly when
2842 // the object is finally linked into an image.
2843 return if (coff.isImage())
2844 name[0 .. std.mem.indexOfScalar(u8, name, '$') orelse name.len]
2845 else
2846 name;
2847}
2848
26502849fn objectSectionMapIndex(
26512850 coff: *Coff,
26522851 name: String,
......@@ -2654,23 +2853,20 @@ fn objectSectionMapIndex(
26542853 attributes: ObjectSectionAttributes,
26552854) !Node.ObjectSectionMapIndex {
26562855 const gpa = coff.base.comp.gpa;
2856 const effective_attributes = if (coff.isImage() and std.mem.startsWith(u8, name.toSlice(coff), ".tls")) attr: {
2857 // In images, the .tls section is a read-only template
2858 var attr = attributes;
2859 attr.write = false;
2860 break :attr attr;
2861 } else attributes;
2862
26572863 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
26582864 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
2659 if (!object_section_gop.found_existing) {
2865 const sn = if (!object_section_gop.found_existing) sn: {
26602866 try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);
26612867 const name_slice = name.toSlice(coff);
2662 const prefix_index = std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len;
2663 const parent_name = coff.getOrPutStringAssumeCapacity(if (coff.isImage())
2664 name_slice[0..prefix_index]
2665 else
2666 name_slice[0..@min(prefix_index + 1, name_slice.len)]);
2667 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, attributes)).symbol(coff);
2668 try coff.verifyParentSectionAttributes(
2669 parent_name,
2670 name,
2671 .fromFlags(parent.get(coff).section_number.header(coff).flags),
2672 attributes,
2673 );
2868 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
2869 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
26742870 try coff.nodes.ensureUnusedCapacity(gpa, 1);
26752871 try coff.symbols.ensureUnusedCapacity(gpa, 1);
26762872 const parent_ni = parent.node(coff);
......@@ -2704,12 +2900,23 @@ fn objectSectionMapIndex(
27042900 assert(sym.loc_relocs == .none);
27052901 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
27062902 coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
2707 }
2903 break :sn sym.section_number;
2904 } else object_section_gop.value_ptr.get(coff).section_number;
2905
2906 try coff.verifyParentSectionAttributes(
2907 .object,
2908 sn.name(coff),
2909 name,
2910 .fromFlags(sn.header(coff).flags),
2911 effective_attributes,
2912 );
2913
27082914 return osmi;
27092915}
27102916
27112917fn verifyParentSectionAttributes(
27122918 coff: *Coff,
2919 kind: enum { pseudo, object },
27132920 parent_name: String,
27142921 child_name: String,
27152922 parent_attrs: ObjectSectionAttributes,
......@@ -2718,18 +2925,25 @@ fn verifyParentSectionAttributes(
27182925 if (parent_attrs == child_attrs) return;
27192926
27202927 const fields = std.meta.fields(ObjectSectionAttributes);
2721 var err = try coff.base.comp.link_diags.addErrorWithNotes(fields.len);
2722 try err.addMsg("object '{s}' was placed in parent section '{s}' with mismatched flags", .{
2928 const BackingT = @typeInfo(ObjectSectionAttributes).@"struct".backing_integer.?;
2929 const num_notes = @popCount(@as(BackingT, @bitCast(parent_attrs)) ^ @as(BackingT, @bitCast(child_attrs)));
2930 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
2931 try err.addMsg("{t} section '{s}' was placed in parent section '{s}' with mismatched flags", .{
2932 kind,
27232933 child_name.toSlice(coff),
27242934 parent_name.toSlice(coff),
27252935 });
27262936
27272937 inline for (fields) |field| {
2728 err.addNote("{s}: parent = {d} child = {d}", .{
2729 field.name,
2730 @intFromBool(@field(child_attrs, field.name)),
2731 @intFromBool(@field(parent_attrs, field.name)),
2732 });
2938 if (@field(child_attrs, field.name) != @field(parent_attrs, field.name)) {
2939 err.addNote("flags.{s} was {d} in {s}, but {d} in {s}", .{
2940 field.name,
2941 @intFromBool(@field(child_attrs, field.name)),
2942 child_name.toSlice(coff),
2943 @intFromBool(@field(parent_attrs, field.name)),
2944 parent_name.toSlice(coff),
2945 });
2946 }
27332947 }
27342948
27352949 return error.LinkFailure;
......@@ -2740,13 +2954,24 @@ pub fn addReloc(
27402954 loc_si: Symbol.Index,
27412955 offset: u64,
27422956 target_si: Symbol.Index,
2743 addend: i64,
2957 addend: union(enum) {
2958 known: i64,
2959 pending: void,
2960 },
27442961 @"type": Reloc.Type,
27452962) !void {
27462963 const gpa = coff.base.comp.gpa;
27472964 const target = target_si.get(coff);
27482965
2749 log.debug("addReloc({d}@{d}+{d} -> {d}@{d}+{d})", .{ loc_si, loc_si.get(coff).section_number, offset, target_si, target_si.get(coff).section_number, addend });
2966 log.debug("addReloc({d}@{d}+{d} -> {d}@{d}+{d}{s})", .{
2967 loc_si,
2968 loc_si.get(coff).section_number,
2969 offset,
2970 target_si,
2971 target_si.get(coff).section_number,
2972 if (addend == .pending) 0 else addend.known,
2973 if (addend == .pending) "p" else "k",
2974 });
27502975
27512976 try coff.relocs.ensureUnusedCapacity(gpa, 1);
27522977
......@@ -2817,7 +3042,10 @@ pub fn addReloc(
28173042 .target = target_si,
28183043 .sri = sri,
28193044 .offset = offset,
2820 .addend = addend,
3045 .addend = if (addend == .pending) 0 else addend.known,
3046 .flags = .{
3047 .recover_addend = addend == .pending,
3048 },
28213049 };
28223050 switch (target.target_relocs) {
28233051 .none => {},
......@@ -2869,10 +3097,34 @@ pub fn loadInput(coff: *Coff, input: link.Input) (Io.File.Reader.SizeError ||
28693097fn fmtArchiveNameString(archiveName: ?[]const u8) std.fmt.Alt(?[]const u8, archiveNameStringEscape) {
28703098 return .{ .data = archiveName };
28713099}
3100
28723101fn archiveNameStringEscape(archiveName: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
28733102 try w.print("({f})", .{std.zig.fmtString(archiveName orelse return)});
28743103}
28753104
3105fn inputSectionHeaderNameSlice(
3106 coff: *Coff,
3107 header: *const std.coff.SectionHeader,
3108 string_table: []const u8,
3109 path: std.Build.Cache.Path,
3110 section_i: usize,
3111) ![]const u8 {
3112 const diags = &coff.base.comp.link_diags;
3113 return if (header.name[0] == '/') name: {
3114 const offset_str = std.mem.sliceTo(header.name[1..], 0);
3115 const name_offset = std.fmt.parseUnsigned(u24, offset_str, 10) catch
3116 return diags.failParse(path, "ill-formed section name in section {d}: '{s}'", .{
3117 section_i,
3118 header.name[0 .. offset_str.len + 1],
3119 });
3120
3121 if (name_offset > string_table.len)
3122 return diags.failParse(path, "out-of-bounds section name offset in section {d}: {d}", .{ section_i, name_offset });
3123
3124 break :name std.mem.sliceTo(string_table[name_offset..], 0);
3125 } else std.mem.sliceTo(&header.name, 0);
3126}
3127
28763128fn loadObject(
28773129 coff: *Coff,
28783130 path: std.Build.Cache.Path,
......@@ -2890,6 +3142,7 @@ fn loadObject(
28903142 assert(!coff.isObj());
28913143
28923144 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtArchiveNameString(archive_name) });
3145
28933146 const header = try r.peekStruct(std.coff.Header, coff.targetEndian());
28943147 if (header.machine != target.toCoffMachine())
28953148 return diags.failParse(path, "machine mismatch: expected {t}, found {t}", .{
......@@ -2927,6 +3180,16 @@ fn loadObject(
29273180 symbol_table_end + string_table_len > fl.size)
29283181 return diags.failParse(path, "bad string table", .{});
29293182
3183 const ii: Node.InputIndex = @enumFromInt(coff.inputs.items.len);
3184 try coff.inputs.ensureUnusedCapacity(gpa, 1);
3185 const input = coff.inputs.addOneAssumeCapacity();
3186 input.* = .{
3187 .path = path,
3188 .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null,
3189 .first_si = .null,
3190 .last_si = .null,
3191 };
3192
29303193 const string_table = string_table: {
29313194 const string_table = try gpa.alloc(u8, string_table_len);
29323195 errdefer gpa.free(string_table);
......@@ -2942,38 +3205,34 @@ fn loadObject(
29423205
29433206 const InputSection = struct {
29443207 header: std.coff.SectionHeader,
2945 psmi: Node.PseudoSectionMapIndex,
3208 name: String,
3209 si: Symbol.Index,
29463210 };
29473211
2948 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));
29493212 const sections: []const InputSection = if (coff.isImage()) sections: {
29503213 const sections = try gpa.alloc(InputSection, header.number_of_sections);
29513214 errdefer gpa.free(sections);
29523215
2953 for (sections, 0..) |*section, section_i| {
2954 section.header = try r.takeStruct(std.coff.SectionHeader, target_endian);
2955 if (section.header.flags.LNK_INFO) {
2956 if (std.mem.eql(u8, &section.header.name, ".drectve"))
2957 return diags.failParse(path, "TODO handle arguments in .drectve section", .{});
3216 var num_input_sections: u16 = 0;
3217 var reqd_object_sections: std.AutoArrayHashMapUnmanaged(String, void) = .empty;
3218 defer reqd_object_sections.deinit(gpa);
3219 var reqd_pseudo_sections: std.StringArrayHashMapUnmanaged(void) = .empty;
3220 defer reqd_pseudo_sections.deinit(gpa);
3221 try reqd_object_sections.ensureUnusedCapacity(gpa, sections.len);
3222 try reqd_pseudo_sections.ensureUnusedCapacity(gpa, sections.len);
29583223
2959 continue;
2960 }
2961
2962 if (section.header.flags.LNK_REMOVE or
2963 section.header.flags.MEM_DISCARDABLE)
2964 {
2965 // TODO: Merge .debug$* sections and output to PDB
2966 continue;
2967 }
2968
2969 if (section.header.flags.LNK_COMDAT)
2970 // This will be necessary if we do the equivalent of /Gy for compiler-rt
2971 return diags.failParse(path, "TODO handle COMDAT sections in input objects", .{});
3224 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));
3225 for (sections, 0..) |*section, section_i| {
3226 section.* = .{
3227 .header = try r.takeStruct(std.coff.SectionHeader, target_endian),
3228 .name = undefined,
3229 .si = .null,
3230 };
29723231
29733232 const section_name_slice = if (section.header.name[0] == '/') name: {
29743233 const offset_str = std.mem.sliceTo(section.header.name[1..], 0);
29753234 const name_offset = std.fmt.parseUnsigned(u24, offset_str, 10) catch
2976 return diags.failParse(path, "ill-formed section name in section {d}: '{s}'", .{
3235 return diags.failParse(path, "ill-formed section name offset in section {d}: '{s}'", .{
29773236 section_i,
29783237 section.header.name[0 .. offset_str.len + 1],
29793238 });
......@@ -2983,26 +3242,137 @@ fn loadObject(
29833242
29843243 break :name std.mem.sliceTo(string_table[name_offset..], 0);
29853244 } else std.mem.sliceTo(&section.header.name, 0);
3245 section.name = coff.getOrPutStringAssumeCapacity(section_name_slice);
29863246
2987 const section_name = coff.getOrPutStringAssumeCapacity(section_name_slice);
2988 const osmi = try coff.objectSectionMapIndex(
2989 section_name,
2990 if (section.header.flags.ALIGN.toByteUnits()) |align_bytes|
2991 .fromByteUnits(align_bytes)
2992 else
2993 .@"1",
2994 .fromFlags(section.header.flags),
3247 if (section.header.pointer_to_linenumbers +
3248 section.header.number_of_linenumbers * std.coff.LineNumber.sizeOf() > fl.size)
3249 return diags.failParse(path, "bad line numbers location in section {d} `{s}`", .{
3250 section_i,
3251 section_name_slice,
3252 });
3253
3254 if (section.header.pointer_to_relocations +
3255 section.header.number_of_relocations * std.coff.Relocation.sizeOf() > fl.size)
3256 return diags.failParse(path, "bad relocations location in section {d} `{s}`", .{
3257 section_i,
3258 section_name_slice,
3259 });
3260
3261 if (section.header.pointer_to_raw_data + section.header.size_of_raw_data > fl.size)
3262 return diags.failParse(path, "bad raw data location in section {d} `{s}`", .{
3263 section_i,
3264 section_name_slice,
3265 });
3266
3267 if (section.header.flags.LNK_REMOVE or
3268 section.header.flags.MEM_DISCARDABLE)
3269 {
3270 // TODO: Merge .debug$* sections and output to PDB
3271 continue;
3272 }
3273
3274 num_input_sections += 1;
3275 _ = reqd_object_sections.getOrPutAssumeCapacity(section.name);
3276 _ = reqd_pseudo_sections.getOrPutAssumeCapacity(
3277 coff.objectSectionParentName(section.name.toSlice(coff)),
29953278 );
3279 }
3280
3281 var symbol_capacity: u16 = num_input_sections;
3282 var node_capacity: u16 = 0;
3283 {
3284 var iter = reqd_object_sections.count();
3285 while (iter > 0) {
3286 iter -= 1;
3287 if (coff.object_section_table.contains(reqd_object_sections.keys()[iter]))
3288 reqd_object_sections.swapRemoveAt(iter);
3289 }
29963290
2997 _ = osmi;
3291 node_capacity += @intCast(reqd_object_sections.count());
3292 symbol_capacity += @intCast(reqd_object_sections.count());
3293 }
3294
3295 {
3296 var iter = reqd_pseudo_sections.count();
3297 while (iter > 0) {
3298 // TODO: Track the extra number of strings and their length and reserve? These have not been reserved as
3299 // part of the ensureManyUnusedStringCapacity call above
3300 iter -= 1;
3301 const name = coff.getString(reqd_pseudo_sections.keys()[iter]) orelse continue;
3302 if (coff.pseudo_section_table.contains(name))
3303 reqd_pseudo_sections.swapRemoveAt(iter);
3304 }
3305
3306 node_capacity += @intCast(reqd_pseudo_sections.count());
3307 symbol_capacity += @intCast(reqd_pseudo_sections.count());
3308 }
3309
3310 try coff.nodes.ensureUnusedCapacity(gpa, node_capacity);
3311 try coff.symbols.ensureUnusedCapacity(gpa, symbol_capacity + num_input_sections);
3312 try coff.input_sections.ensureUnusedCapacity(gpa, num_input_sections);
3313
3314 for (sections) |*section| {
3315 if (section.header.flags.LNK_INFO) {
3316 if (std.mem.eql(u8, &section.header.name, ".drectve")) {
3317 try fr.seekTo(fl.offset + section.header.pointer_to_raw_data);
3318 var buf: [128]u8 = undefined;
3319 var section_r = r.limited(.limited(section.header.size_of_raw_data), &buf);
3320 while (section_r.interface.takeDelimiter(' ') catch |err| switch (err) {
3321 error.StreamTooLong => return diags.failParse(path, "unexpectedly long .drectve argument", .{}),
3322 else => |e| return e,
3323 }) |arg| {
3324 // Microsoft tools emit 3 space characters into this section even with /Zl
3325 if (arg.len > 0)
3326 return diags.failParse(path, "unsupported argument in .drectve section: `{s}`", .{arg});
3327 }
3328 }
3329
3330 continue;
3331 }
29983332
2999 // TODO: Decide to merge this section
3000 // TODO: Map flags (might need to figure out a better tls flag?)
3333 if (section.header.flags.LNK_REMOVE or
3334 section.header.flags.MEM_DISCARDABLE)
3335 {
3336 continue;
3337 }
30013338
3002 //coff.objectSectionMapIndex(name: String, alignment: Alignment, attributes: ObjectSectionAttributes)
3339 if (section.header.flags.LNK_COMDAT)
3340 // This will be necessary if we do the equivalent of /Gy for compiler-rt
3341 return diags.failParse(path, "TODO handle COMDAT sections in input objects", .{});
30033342
3004 // TODO: Load relocations, update for new offset? Or can just work with the object section parent?
3343 log.debug("loadInputSection({s})", .{section.name.toSlice(coff)});
30053344
3345 const parent_osmi = try coff.objectSectionMapIndex(
3346 section.name,
3347 coff.mf.flags.block_size,
3348 .fromFlags(section.header.flags),
3349 );
3350 const parent_si = parent_osmi.symbol(coff);
3351 const ni = try coff.mf.addLastChildNode(gpa, parent_si.node(coff), .{
3352 .size = section.header.size_of_raw_data,
3353 .alignment = if (section.header.flags.ALIGN.toByteUnits()) |align_bytes|
3354 .fromByteUnits(align_bytes)
3355 else
3356 .@"1",
3357 .moved = true,
3358 });
3359 coff.nodes.appendAssumeCapacity(.{ .input_section = @enumFromInt(coff.input_sections.items.len) });
3360
3361 section.si = coff.addSymbolAssumeCapacity();
3362 const sym = section.si.get(coff);
3363 sym.ni = ni;
3364 sym.section_number = parent_si.get(coff).section_number;
3365
3366 coff.input_sections.addOneAssumeCapacity().* = .{
3367 .ii = ii,
3368 .si = section.si,
3369 .file_location = .{
3370 .offset = fl.offset + section.header.pointer_to_raw_data,
3371 .size = section.header.size_of_raw_data,
3372 },
3373 };
3374
3375 coff.synth_prog_node.increaseEstimatedTotalItems(1);
30063376 }
30073377
30083378 break :sections sections;
......@@ -3019,38 +3389,42 @@ fn loadObject(
30193389 const member = mi.get(coff);
30203390 try member.initHeader(coff, path_str, header.time_date_stamp);
30213391
3392 // TODO: This could be deferred to an idle task?
3393
30223394 {
30233395 var nw: MappedFile.Node.Writer = undefined;
30243396 member.content_ni.writer(&coff.mf, gpa, &nw);
30253397 defer nw.deinit();
30263398
30273399 try fr.seekTo(fl.offset);
3028 try r.streamExact(&nw.interface, fl.size);
3400 if (try nw.interface.sendFileAll(fr, .limited64(fl.size)) != fl.size)
3401 return error.EndOfStream;
30293402 }
30303403
30313404 break :mi mi;
30323405 } else undefined;
30333406
3407 // TODO: Also reserve memory for the symbols / globals / relocs within each section
3408
30343409 try fr.seekTo(fl.offset + header.pointer_to_symbol_table);
3035 const symbol_size = std.coff.Symbol.sizeOf();
3410 const symbol_size = comptime std.coff.Symbol.sizeOf();
30363411
3412 var symbols: std.ArrayList(Symbol.Index) = .empty;
3413 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
3414
3415 const first_si = coff.symbols.items.len;
30373416 var symbol_ix: u32 = 0;
30383417 while (symbol_ix < header.number_of_symbols) {
3039 const symbol: *align(2) std.coff.Symbol = @ptrCast(@alignCast(try r.take(symbol_size)));
3418 var symbol: std.coff.Symbol = undefined;
3419 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));
3420 if (target_endian != native_endian)
3421 std.mem.byteSwapAllFields(std.coff.Symbol, &symbol);
3422
30403423 defer {
30413424 r.toss(symbol.number_of_aux_symbols * symbol_size);
30423425 symbol_ix += symbol.number_of_aux_symbols + 1;
30433426 }
30443427
3045 switch (symbol.section_number) {
3046 .UNDEFINED, .ABSOLUTE, .DEBUG => continue,
3047 else => switch (symbol.storage_class) {
3048 .STATIC => if (symbol.value == 0) continue,
3049 .EXTERNAL => {},
3050 else => continue,
3051 },
3052 }
3053
30543428 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
30553429 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);
30563430 if (index >= string_table.len)
......@@ -3058,21 +3432,125 @@ fn loadObject(
30583432 break :name string_table[index..];
30593433 } else &symbol.name, 0);
30603434
3061 // Section numbers are 1-based here
3062 if (!is_archive and @intFromEnum(symbol.section_number) > sections.len)
3063 return diags.failParse(path, "bad section number {d} for '{s}'", .{ symbol.section_number, name });
3435 const si = symbols.addOneAssumeCapacity();
3436 si.* = .null;
3437
3438 switch (symbol.section_number) {
3439 .UNDEFINED, .ABSOLUTE, .DEBUG => continue,
3440 else => switch (symbol.storage_class) {
3441 .STATIC => if (symbol.value == 0 and symbol.type == std.coff.SymType{
3442 .complex_type = .NULL,
3443 .base_type = .NULL,
3444 }) {
3445 if (symbol.number_of_aux_symbols != 1)
3446 return diags.failParse(path, "invalid number of aux symbols for section {d}: {d}", .{
3447 symbol_ix,
3448 symbol.number_of_aux_symbols,
3449 });
3450
3451 var section_def: std.coff.SectionDefinition = undefined;
3452 @memcpy(std.mem.asBytes(&section_def)[0..symbol_size], try r.peek(symbol_size));
3453 if (target_endian != native_endian)
3454 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def);
3455
3456 // TODO: Extract the COMDAT section info
3457
3458 if (section_def.number > sections.len)
3459 return diags.failParse(
3460 path,
3461 "section symbol for '{s}' contained an out of bounds section number: {d}",
3462 .{ name, section_def.number },
3463 );
3464
3465 // It's valid for this to not match the symbol's section number (ie. .drectve sets this)
3466 if (section_def.number == 0)
3467 continue;
3468
3469 const section = &sections[section_def.number - 1];
3470 if (section_def.number_of_relocations != section.header.number_of_relocations)
3471 return diags.failParse(
3472 path,
3473 "section symbol for '{s}' relocation count did not match section header: {d} vs {d}",
3474 .{ name, section_def.number_of_relocations, section.header.number_of_relocations },
3475 );
3476
3477 if (section_def.number_of_linenumbers != section.header.number_of_linenumbers)
3478 return diags.failParse(
3479 path,
3480 "section symbol for '{s}' line number count did not match section header: {d} vs {d}",
3481 .{ name, section_def.number_of_linenumbers, section.header.number_of_linenumbers },
3482 );
3483
3484 si.* = section.si;
3485 continue;
3486 },
3487 .EXTERNAL => {},
3488 else => continue,
3489 },
3490 }
30643491
30653492 if (is_archive) {
30663493 try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
30673494 continue;
30683495 }
30693496
3497 // Section numbers are 1-based here
3498 if (@intFromEnum(symbol.section_number) <= 0 or @intFromEnum(symbol.section_number) > sections.len)
3499 return diags.failParse(path, "bad section number {d} for '{s}'", .{ symbol.section_number, name });
3500
30703501 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
3502 // TODO: Support weak symbols
30713503 if (global_gop.found_existing)
30723504 return diags.failParse(path, "multiple definitions of '{s}'", .{name});
3505 si.* = global_gop.value_ptr.*;
3506
3507 const section = sections[@intCast(@intFromEnum(symbol.section_number) - 1)];
3508 const section_sym = section.si.get(coff);
30733509
3074 // TODO: Get the sym and set the ni to point to wherever it was copied in the pseudo section
3075 // TODO: May need to cache offsets and determine symbol sizes later (once we can sort by section offset)
3510 const sym = si.get(coff);
3511 sym.ni = section_sym.ni;
3512 sym.value = .{ .input_offset = symbol.value };
3513 sym.section_number = section_sym.section_number;
3514 }
3515
3516 if (coff.symbols.items.len > first_si) {
3517 input.first_si = @enumFromInt(first_si);
3518 input.last_si = @enumFromInt(coff.symbols.items.len - 1);
3519 }
3520
3521 const relocation_size = std.coff.Relocation.sizeOf();
3522 for (sections) |section| {
3523 if (section.si == .null) continue;
3524
3525 const loc_sym = section.si.get(coff);
3526 assert(loc_sym.loc_relocs == .none);
3527 loc_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
3528
3529 if (section.header.number_of_relocations == 0) continue;
3530
3531 try coff.relocs.ensureUnusedCapacity(gpa, section.header.number_of_relocations);
3532 try fr.seekTo(fl.offset + section.header.pointer_to_relocations);
3533 for (0..section.header.number_of_relocations) |reloc_i| {
3534 var reloc: std.coff.Relocation = undefined;
3535 @memcpy(std.mem.asBytes(&reloc)[0..relocation_size], try r.take(relocation_size));
3536 if (target_endian != native_endian)
3537 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);
3538
3539 if (reloc.symbol_table_index >= symbols.items.len)
3540 return diags.failParse(
3541 path,
3542 "relocation {d} in section '{s}' targets invalid symbol index {d}",
3543 .{ reloc_i, section.name.toSlice(coff), reloc.symbol_table_index },
3544 );
3545
3546 try coff.addReloc(
3547 section.si,
3548 reloc.virtual_address - section.header.virtual_address,
3549 symbols.items[reloc.symbol_table_index],
3550 .pending,
3551 @bitCast(reloc.type), // TODO: Checks on this cast?
3552 );
3553 }
30763554 }
30773555}
30783556
......@@ -3184,13 +3662,13 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
31843662 error.WriteFailed => return nw.err.?,
31853663 else => |e| return e,
31863664 };
3187 si.get(coff).size = @intCast(nw.interface.end);
3665 si.get(coff).value.size = @intCast(nw.interface.end);
31883666 si.applyLocationRelocs(coff);
31893667 }
31903668
31913669 // TODO: Did my MappedFile resize change affect this?
31923670 if (nav.resolved.?.@"linksection".unwrap()) |_| {
3193 try ni.resize(&coff.mf, gpa, si.get(coff).size);
3671 try ni.resize(&coff.mf, gpa, si.get(coff).value.size);
31943672 var parent_ni = ni;
31953673 while (true) {
31963674 parent_ni = parent_ni.parent(&coff.mf);
......@@ -3318,7 +3796,7 @@ fn updateFuncInner(
33183796 error.WriteFailed => return nw.err.?,
33193797 else => |e| return e,
33203798 };
3321 si.get(coff).size = @intCast(nw.interface.end);
3799 si.get(coff).value.size = @intCast(nw.interface.end);
33223800 si.applyLocationRelocs(coff);
33233801}
33243802
......@@ -3543,6 +4021,28 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
35434021 };
35444022 break :task;
35454023 }
4024 // TODO: Idle task for flushing obj into lib?
4025 if (coff.input_section_pending_index < coff.input_sections.items.len) {
4026 const isi: Node.InputSectionIndex = @enumFromInt(coff.input_section_pending_index);
4027 coff.input_section_pending_index += 1;
4028 const sub_prog_node = coff.idleProgNode(tid, coff.input_prog_node, coff.getNode(isi.symbol(coff).node(coff)));
4029 defer sub_prog_node.end();
4030 coff.flushInputSection(isi) catch |err| switch (err) {
4031 else => |e| {
4032 const ii = isi.input(coff);
4033 return comp.link_diags.fail(
4034 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
4035 .{
4036 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
4037 ii.path(coff).fmtEscapeString(),
4038 fmtArchiveNameString(ii.archiveName(coff)),
4039 e,
4040 },
4041 );
4042 },
4043 };
4044 break :task;
4045 }
35464046 while (coff.mf.updates.pop()) |ni| {
35474047 const clean_moved = ni.cleanMoved(&coff.mf);
35484048 const clean_resized = ni.cleanResized(&coff.mf);
......@@ -3608,6 +4108,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
36084108 if (coff.globals.count() > coff.global_pending_index) return true;
36094109 if (coff.symbol_table.pending.count() > 0) return true;
36104110 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
4111 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
36114112 if (coff.mf.updates.items.len > 0) return true;
36124113 if (coff.pending_members.count() > 0) return true;
36134114 if (coff.export_table.pending_sort) return true;
......@@ -3626,6 +4127,14 @@ fn idleProgNode(
36264127 else => |tag| @tagName(tag),
36274128 .image_section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
36284129 inline .pseudo_section, .object_section => |smi| smi.name(coff).toSlice(coff),
4130 .input_section => |isi| {
4131 const ii = isi.input(coff);
4132 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
4133 ii.path(coff).fmtEscapeString(),
4134 fmtArchiveNameString(ii.archiveName(coff)),
4135 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
4136 }) catch &name;
4137 },
36294138 .global => |gmi| gmi.globalName(coff).name.toSlice(coff),
36304139 .nav => |nmi| {
36314140 const ip = &coff.base.comp.zcu.?.intern_pool;
......@@ -3699,7 +4208,7 @@ fn flushUav(
36994208 error.WriteFailed => return nw.err.?,
37004209 else => |e| return e,
37014210 };
3702 si.get(coff).size = @intCast(nw.interface.end);
4211 si.get(coff).value.size = @intCast(nw.interface.end);
37034212 si.applyLocationRelocs(coff);
37044213}
37054214
......@@ -3864,12 +4373,12 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {
38644373 });
38654374 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
38664375 sym.ni = ni;
3867 sym.size = init.len;
4376 sym.value.size = init.len;
38684377 try coff.addReloc(
38694378 si,
38704379 init.len - 4,
38714380 gop.value_ptr.import_address_table_si,
3872 @intCast(addr_size * import_symbol_index),
4381 .{ .known = @intCast(addr_size * import_symbol_index) },
38734382 .{ .AMD64 = .REL32 },
38744383 );
38754384 },
......@@ -3929,7 +4438,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
39294438 error.WriteFailed => return nw.err.?,
39304439 else => |e| return e,
39314440 };
3932 si.get(coff).size = @intCast(nw.interface.end);
4441 si.get(coff).value.size = @intCast(nw.interface.end);
39334442 si.applyLocationRelocs(coff);
39344443}
39354444
......@@ -3992,6 +4501,15 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
39924501 @intCast(file_offset),
39934502 );
39944503 },
4504 .input_section => |isi| {
4505 const ii = isi.input(coff);
4506 var si = ii.firstSymbol(coff);
4507 const last_si = ii.lastSymbol(coff);
4508 while (@intFromEnum(si) <= @intFromEnum(last_si)) : (si = si.next()) {
4509 if (si.get(coff).ni != ni) continue;
4510 si.flushMoved(coff);
4511 }
4512 },
39954513 .import_directory_table => coff.targetStore(
39964514 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
39974515 coff.computeNodeRva(ni),
......@@ -4204,6 +4722,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
42044722 );
42054723 }
42064724 },
4725 .input_section => {},
42074726 .import_directory_table => coff.targetStore(
42084727 &coff.dataDirectoryPtr(.IMPORT).size,
42094728 @intCast(size),
......@@ -4228,7 +4747,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
42284747 );
42294748 }
42304749
4231 smi.symbol(coff).get(coff).size = @intCast(size);
4750 smi.symbol(coff).get(coff).value.size = @intCast(size);
42324751 },
42334752 .global,
42344753 .nav,
......@@ -4398,7 +4917,7 @@ fn updateExportsInner(
43984917 const export_sym = export_si.get(coff);
43994918 export_sym.ni = exported_ni;
44004919 export_sym.rva = exported_sym.rva;
4401 export_sym.size = exported_sym.size;
4920 export_sym.value.size = exported_sym.value.size;
44024921 export_sym.section_number = exported_sym.section_number;
44034922 defer export_si.applyTargetRelocs(coff);
44044923
......@@ -4407,7 +4926,7 @@ fn updateExportsInner(
44074926 coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
44084927 } else if (@"export".opts.name.eqlSlice("_tls_used", ip)) {
44094928 const tls_directory = coff.dataDirectoryPtr(.TLS);
4410 tls_directory.* = .{ .virtual_address = exported_sym.rva, .size = exported_sym.size };
4929 tls_directory.* = .{ .virtual_address = exported_sym.rva, .size = exported_sym.value.size };
44114930 if (coff.targetEndian() != native_endian)
44124931 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
44134932 }
......@@ -4492,7 +5011,7 @@ fn updateExportsInner(
44925011 coff.export_table.export_address_table_si,
44935012 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),
44945013 export_si,
4495 0,
5014 .{ .known = 0 },
44965015 .{ .AMD64 = .ADDR32NB },
44975016 );
44985017 } else {
......@@ -4541,6 +5060,14 @@ pub fn printNode(
45415060 .image_section => |si| try w.print("({s})", .{
45425061 std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
45435062 }),
5063 .input_section => |isi| {
5064 const ii = isi.input(coff);
5065 try w.print("({f}{f}, {s})", .{
5066 ii.path(coff).fmtEscapeString(),
5067 fmtArchiveNameString(ii.archiveName(coff)),
5068 isi.symbol(coff).get(coff).section_number.name(coff).toSlice(coff),
5069 });
5070 },
45445071 .import_lookup_table,
45455072 .import_address_table,
45465073 .import_hint_name_table,