authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:35-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:55-04:00
logae1130ab2090ce25cd709c89749da2e3cd161d2f
tree89539967389655e1b22bf236197d7b793f5ab704
parent587030e75440d26a52cc65e58d14a66a4f459096

Coff: stub out merging and handle special case sections

- Parse / flush /MERGE arguments, impl is incomplete - Fixup recovering addends not sign extending - Add .fptable section when linking msvc libc - this needs to be a separate section as it gets marked read-only at runtime - Pseudo sections prefer to use the exact section name if it exists already (to support .fptable)

1 files changed, 193 insertions(+), 55 deletions(-)

src/link/Coff.zig+193-55
...@@ -60,6 +60,8 @@ string_bytes: std.ArrayList(u8),...@@ -60,6 +60,8 @@ string_bytes: std.ArrayList(u8),
60section_table: std.AutoArrayHashMapUnmanaged(String, Section),60section_table: std.AutoArrayHashMapUnmanaged(String, Section),
61pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),61pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),
62object_section_table: std.array_hash_map.Auto(String, Symbol.Index),62object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
63section_merges: std.AutoArrayHashMapUnmanaged(String, String),
64section_merge_pending_index: u32,
63symbols: std.ArrayList(Symbol),65symbols: std.ArrayList(Symbol),
64globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),66globals: std.array_hash_map.Auto(GlobalName, Symbol.Index),
65global_pending_index: u32,67global_pending_index: u32,
...@@ -93,6 +95,8 @@ pub const archive_end_of_header = "`\n";...@@ -93,6 +95,8 @@ pub const archive_end_of_header = "`\n";
9395
94pub const imp_prefix = "__imp_";96pub const imp_prefix = "__imp_";
9597
98const header_name_max_len = @typeInfo(@FieldType(std.coff.SectionHeader, "name")).array.len;
99
96/// This is the start of a Portable Executable (PE) file.100/// This is the start of a Portable Executable (PE) file.
97/// It starts with a MS-DOS header followed by a MS-DOS stub program.101/// It starts with a MS-DOS header followed by a MS-DOS stub program.
98/// This data does not change so we include it as follows in all binaries.102/// This data does not change so we include it as follows in all binaries.
...@@ -797,6 +801,7 @@ pub const String = enum(u32) {...@@ -797,6 +801,7 @@ pub const String = enum(u32) {
797 @".dtors" = 57,801 @".dtors" = 57,
798 @".dtors$ZZZ" = 64,802 @".dtors$ZZZ" = 64,
799 @".bss" = 75,803 @".bss" = 75,
804 @".fptable" = 80,
800 _,805 _,
801806
802 pub const Optional = enum(u32) {807 pub const Optional = enum(u32) {
...@@ -811,6 +816,7 @@ pub const String = enum(u32) {...@@ -811,6 +816,7 @@ pub const String = enum(u32) {
811 @".dtors" = @intFromEnum(String.@".dtors"),816 @".dtors" = @intFromEnum(String.@".dtors"),
812 @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"),817 @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"),
813 @".bss" = @intFromEnum(String.@".bss"),818 @".bss" = @intFromEnum(String.@".bss"),
819 @".fptable" = @intFromEnum(String.@".fptable"),
814 none = std.math.maxInt(u32),820 none = std.math.maxInt(u32),
815 _,821 _,
816822
...@@ -1242,11 +1248,6 @@ pub const Reloc = extern struct {...@@ -1242,11 +1248,6 @@ pub const Reloc = extern struct {
1242 .ADDR32,1248 .ADDR32,
1243 .ADDR32NB,1249 .ADDR32NB,
1244 .SECREL,1250 .SECREL,
1245 => std.mem.readInt(
1246 u32,
1247 loc_slice[0..4],
1248 target_endian,
1249 ),
1250 .REL32,1251 .REL32,
1251 .REL32_1,1252 .REL32_1,
1252 .REL32_2,1253 .REL32_2,
...@@ -1263,11 +1264,6 @@ pub const Reloc = extern struct {...@@ -1263,11 +1264,6 @@ pub const Reloc = extern struct {
1263 else => |kind| @panic(@tagName(kind)),1264 else => |kind| @panic(@tagName(kind)),
1264 .ABSOLUTE => 0,1265 .ABSOLUTE => 0,
1265 .DIR16,1266 .DIR16,
1266 => std.mem.readInt(
1267 u16,
1268 loc_slice[0..2],
1269 target_endian,
1270 ),
1271 .REL16,1267 .REL16,
1272 => std.mem.readInt(1268 => std.mem.readInt(
1273 i16,1269 i16,
...@@ -1277,11 +1273,6 @@ pub const Reloc = extern struct {...@@ -1277,11 +1273,6 @@ pub const Reloc = extern struct {
1277 .DIR32,1273 .DIR32,
1278 .DIR32NB,1274 .DIR32NB,
1279 .SECREL,1275 .SECREL,
1280 => std.mem.readInt(
1281 u32,
1282 loc_slice[0..4],
1283 target_endian,
1284 ),
1285 .REL32,1276 .REL32,
1286 => std.mem.readInt(1277 => std.mem.readInt(
1287 i32,1278 i32,
...@@ -1553,6 +1544,8 @@ fn create(...@@ -1553,6 +1544,8 @@ fn create(
1553 .section_table = .empty,1544 .section_table = .empty,
1554 .pseudo_section_table = .empty,1545 .pseudo_section_table = .empty,
1555 .object_section_table = .empty,1546 .object_section_table = .empty,
1547 .section_merges = .empty,
1548 .section_merge_pending_index = 0,
1556 .symbols = .empty,1549 .symbols = .empty,
1557 .globals = .empty,1550 .globals = .empty,
1558 .global_pending_index = 0,1551 .global_pending_index = 0,
...@@ -1698,7 +1691,7 @@ fn initHeaders(...@@ -1698,7 +1691,7 @@ fn initHeaders(
1698 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);1691 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
1699 const is_image = coff.isImage();1692 const is_image = coff.isImage();
1700 const is_archive = coff.isArchive();1693 const is_archive = coff.isArchive();
17011694 const target = &comp.root_mod.resolved_target.result;
1702 const optional_header_size: u16 = if (is_image) switch (magic) {1695 const optional_header_size: u16 = if (is_image) switch (magic) {
1703 _ => unreachable,1696 _ => unreachable,
1704 inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),1697 inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
...@@ -1713,12 +1706,14 @@ fn initHeaders(...@@ -1713,12 +1706,14 @@ fn initHeaders(
1713 // Sections1706 // Sections
1714 expected_nodes_len += 4;1707 expected_nodes_len += 4;
17151708
1716 if (is_image)1709 if (is_image) {
1717 // Pseudo-sections and import / export table1710 // Pseudo-sections and import / export table
1718 expected_nodes_len += 91711 expected_nodes_len += 9;
1719 else1712 if (comp.config.link_libc and target.abi == .msvc)
1720 // Symbol table1713 expected_nodes_len += 1;
1721 expected_nodes_len += 2;1714 } else
1715 // Symbol table
1716 expected_nodes_len += 2;
17221717
1723 // TLS section1718 // TLS section
1724 if (comp.config.any_non_single_threaded) {1719 if (comp.config.any_non_single_threaded) {
...@@ -2004,9 +1999,10 @@ fn initHeaders(...@@ -2004,9 +1999,10 @@ fn initHeaders(
20041999
2005 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);2000 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);
2006 assert(coff.addSymbolAssumeCapacity() == .null);2001 assert(coff.addSymbolAssumeCapacity() == .null);
2002
2007 // TODO: How do we tell MappedFile not to allocate physical space for these?2003 // TODO: How do we tell MappedFile not to allocate physical space for these?
2008 // TODO: Could have a node flag 'virtual' that can never have slice* called on it or fileLocation2004 // TODO: Could have a node flag 'virtual' that can never have slice* called on it or fileLocation
20092005 // TODO: Instead of it's own section, we can place .bss as a pseudo-section at the end of .text in the extra space
2010 assert(try coff.addSection(.@".bss", .{2006 assert(try coff.addSection(.@".bss", .{
2011 .CNT_UNINITIALIZED_DATA = true,2007 .CNT_UNINITIALIZED_DATA = true,
2012 .MEM_READ = true,2008 .MEM_READ = true,
...@@ -2028,6 +2024,18 @@ fn initHeaders(...@@ -2028,6 +2024,18 @@ fn initHeaders(
2028 }) == .text);2024 }) == .text);
20292025
2030 if (is_image) {2026 if (is_image) {
2027 if (comp.config.link_libc and target.abi == .msvc) {
2028 // This section contains a function pointer table used by control flow guard:
2029 // https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
2030 // The page containing it is set to PAGE_READONLY during startup, so this can't
2031 // be merged into .data this protection would overlap writable memory.
2032 _ = try coff.addSection(.@".fptable", .{
2033 .CNT_INITIALIZED_DATA = true,
2034 .MEM_READ = true,
2035 .MEM_WRITE = true,
2036 });
2037 }
2038
2031 coff.import_table.ni = try coff.mf.addLastChildNode(2039 coff.import_table.ni = try coff.mf.addLastChildNode(
2032 gpa,2040 gpa,
2033 (try coff.objectSectionMapIndex(2041 (try coff.objectSectionMapIndex(
...@@ -2199,7 +2207,8 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {...@@ -2199,7 +2207,8 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
2199 coff.synth_prog_node = prog_node.start("Synthetics", count: {2207 coff.synth_prog_node = prog_node.start("Synthetics", count: {
2200 var count =2208 var count =
2201 coff.globals.count() - coff.global_pending_index +2209 coff.globals.count() - coff.global_pending_index +
2202 coff.late_globals.items.len - coff.late_globals_pending_index;2210 coff.late_globals.items.len - coff.late_globals_pending_index +
2211 coff.section_merges.count() - coff.section_merge_pending_index;
22032212
2204 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;2213 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
2205 break :count count;2214 break :count count;
...@@ -2562,7 +2571,8 @@ fn getString(coff: *Coff, string: []const u8) String.Optional {...@@ -2562,7 +2571,8 @@ fn getString(coff: *Coff, string: []const u8) String.Optional {
2562fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName {2571fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName {
2563 assert(!coff.isImage());2572 assert(!coff.isImage());
2564 const gpa = coff.base.comp.gpa;2573 const gpa = coff.base.comp.gpa;
2565 return if (name.len > 8) name: {2574
2575 return if (name.len > header_name_max_len) name: {
2566 const string = opt_string orelse try coff.getOrPutString(name);2576 const string = opt_string orelse try coff.getOrPutString(name);
2567 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);2577 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
2568 if (!string_gop.found_existing) {2578 if (!string_gop.found_existing) {
...@@ -3202,8 +3212,6 @@ const ObjectSectionAttributes = packed struct {...@@ -3202,8 +3212,6 @@ const ObjectSectionAttributes = packed struct {
3202 initialized: bool = false,3212 initialized: bool = false,
3203 uninitialized: bool = false,3213 uninitialized: bool = false,
32043214
3205 // TODO: Include init / not init flags?
3206
3207 pub fn fromFlags(flags: std.coff.SectionHeader.Flags) ObjectSectionAttributes {3215 pub fn fromFlags(flags: std.coff.SectionHeader.Flags) ObjectSectionAttributes {
3208 return .{3216 return .{
3209 .read = flags.MEM_READ,3217 .read = flags.MEM_READ,
...@@ -3244,26 +3252,22 @@ fn pseudoSectionMapIndex(...@@ -3244,26 +3252,22 @@ fn pseudoSectionMapIndex(
3244 const gpa = coff.base.comp.gpa;3252 const gpa = coff.base.comp.gpa;
3245 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);3253 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);
3246 const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);3254 const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);
3247 const sn = if (!pseudo_section_gop.found_existing) sn: {3255 const parent_sn = if (!pseudo_section_gop.found_existing) sn: {
3248 const default_parent: Symbol.Index = if (attributes.uninitialized)3256 const effective_name = coff.section_merges.get(name) orelse name;
3249 .bss3257 const parent = if (coff.section_table.get(effective_name)) |existing_sec|
3250 else if (attributes.execute)3258 existing_sec.si
3251 .text3259 else if (coff.isImage()) parent: {
3252 else if (attributes.write)3260 const parent: Symbol.Index = if (attributes.uninitialized)
3253 .data3261 .bss
3254 else3262 else if (attributes.execute)
3255 .rdata;3263 .text
3264 else if (attributes.write)
3265 .data
3266 else
3267 .rdata;
32563268
3257 const parent = if (coff.isImage() or std.mem.eql(3269 break :parent parent;
3258 u8,3270 } else try coff.addSection(effective_name, attributes.asFlags());
3259 name.toSlice(coff),
3260 default_parent.knownString().toSlice(coff).?,
3261 ))
3262 default_parent
3263 else if (coff.section_table.get(name)) |section|
3264 section.si
3265 else
3266 try coff.addSection(name, attributes.asFlags());
32673271
3268 try coff.nodes.ensureUnusedCapacity(gpa, 1);3272 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3269 try coff.symbols.ensureUnusedCapacity(gpa, 1);3273 try coff.symbols.ensureUnusedCapacity(gpa, 1);
...@@ -3282,9 +3286,9 @@ fn pseudoSectionMapIndex(...@@ -3282,9 +3286,9 @@ fn pseudoSectionMapIndex(
32823286
3283 try coff.verifyParentSectionAttributes(3287 try coff.verifyParentSectionAttributes(
3284 .pseudo,3288 .pseudo,
3285 sn.name(coff),3289 parent_sn.name(coff),
3286 name,3290 name,
3287 .fromFlags(sn.header(coff).flags),3291 .fromFlags(parent_sn.header(coff).flags),
3288 attributes,3292 attributes,
3289 );3293 );
32903294
...@@ -3614,6 +3618,8 @@ fn loadObject(...@@ -3614,6 +3618,8 @@ fn loadObject(
3614 const target_endian = coff.targetEndian();3618 const target_endian = coff.targetEndian();
3615 const is_archive = coff.isArchive();3619 const is_archive = coff.isArchive();
3616 assert(!coff.isObj());3620 assert(!coff.isObj());
3621 // We want to evaluate new merges as we see them in .drectve sections to avoid redundant work
3622 assert(coff.section_merge_pending_index == coff.section_merges.count());
36173623
3618 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberNameString(member_name) });3624 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberNameString(member_name) });
36193625
...@@ -3826,10 +3832,15 @@ fn loadObject(...@@ -3826,10 +3832,15 @@ fn loadObject(
3826 var num_global_symbols: u32 = 0;3832 var num_global_symbols: u32 = 0;
3827 var pending_symbols: std.AutoArrayHashMapUnmanaged(u32, PendingSymbol) = .empty;3833 var pending_symbols: std.AutoArrayHashMapUnmanaged(u32, PendingSymbol) = .empty;
3828 defer pending_symbols.deinit(gpa);3834 defer pending_symbols.deinit(gpa);
3829
3830 if (!is_archive)3835 if (!is_archive)
3831 try pending_symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);3836 try pending_symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
38323837
3838 var section_merges: std.ArrayList(struct {
3839 from: String,
3840 to: String,
3841 }) = .empty;
3842 defer section_merges.deinit(gpa);
3843
3833 // Discover symbol names and COMDAT symbol mappings3844 // Discover symbol names and COMDAT symbol mappings
3834 var symbol_i: u32 = 0;3845 var symbol_i: u32 = 0;
3835 while (symbol_i < header.number_of_symbols) {3846 while (symbol_i < header.number_of_symbols) {
...@@ -4081,15 +4092,48 @@ fn loadObject(...@@ -4081,15 +4092,48 @@ fn loadObject(
4081 );4092 );
4082 } else if (std.ascii.startsWithIgnoreCase(arg, "/guardsym:")) {4093 } else if (std.ascii.startsWithIgnoreCase(arg, "/guardsym:")) {
4083 // TODO: https://learn.microsoft.com/en-us/windows/win32/secbp/pe-metadata4094 // TODO: https://learn.microsoft.com/en-us/windows/win32/secbp/pe-metadata
4084 } else if (std.ascii.startsWithIgnoreCase(arg, "/merge:")) {4095 } else if (std.ascii.startsWithIgnoreCase(arg, "/merge:")) merge: {
4085 var split = std.mem.splitScalar(u8, arg["/merge:".len..], '=');4096 var split = std.mem.splitScalar(u8, arg["/merge:".len..], '=');
4086 const from = split.first();4097 const from = split.first();
4087 const to = split.next() orelse4098 const to = split.next() orelse
4088 return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg});4099 return diags.failParse(path, "malformed .drectve argument: '{s}'", .{arg});
4100 if (to.len > header_name_max_len)
4101 return diags.failParse(
4102 path,
4103 "/merge .drectve target exceeds max length of {d}: '{s}'",
4104 .{ header_name_max_len, arg },
4105 );
4106 if (std.mem.eql(u8, from, to)) break :merge;
4107
4108 try coff.ensureManyUnusedStringCapacity(2, from.len + to.len + 2);
4109 const from_str = coff.getOrPutStringAssumeCapacity(from);
4110 const to_str = coff.getOrPutStringAssumeCapacity(to);
4111
4112 {
4113 var iter = to_str;
4114 while (coff.section_merges.get(iter)) |next_to| {
4115 if (next_to == from_str)
4116 return diags.failParse(
4117 path,
4118 "/merge .drectve argument would create a cycle: {s}={s} leads to {s}={s}",
4119 .{ from, to, iter.toSlice(coff), to },
4120 );
4121
4122 iter = next_to;
4123 }
4124 }
40894125
4090 // TODO: Override the parent selection for generated sections below4126 try coff.section_merges.ensureUnusedCapacity(gpa, 1);
4091 _ = from;4127 const gop = coff.section_merges.getOrPutAssumeCapacity(from_str);
4092 _ = to;4128 if (!gop.found_existing) {
4129 coff.synth_prog_node.increaseEstimatedTotalItems(1);
4130 gop.value_ptr.* = to_str;
4131 } else if (gop.value_ptr.* != to_str)
4132 return diags.failParse(
4133 path,
4134 "conflicting /merge .drectve arguments: first seen as {s}={s}, now seen as {s}={s}",
4135 .{ from, gop.value_ptr.toSlice(coff), from, to },
4136 );
4093 } else if (std.ascii.startsWithIgnoreCase(arg, "/disallowlib:")) {4137 } else if (std.ascii.startsWithIgnoreCase(arg, "/disallowlib:")) {
4094 const lib_name = arg["/disallowlib:".len..];4138 const lib_name = arg["/disallowlib:".len..];
4095 // TODO: Track these and issue error in prelink if any match4139 // TODO: Track these and issue error in prelink if any match
...@@ -4250,6 +4294,9 @@ fn loadObject(...@@ -4250,6 +4294,9 @@ fn loadObject(
4250 };4294 };
4251 }4295 }
42524296
4297 while (coff.section_merge_pending_index < coff.section_merges.count()) : (coff.section_merge_pending_index += 1)
4298 try coff.flushSectionMerge(coff.section_merge_pending_index);
4299
4253 // Resolve pending associations, create parent sections4300 // Resolve pending associations, create parent sections
4254 var num_included_sections: u16 = 0;4301 var num_included_sections: u16 = 0;
4255 var num_included_symbols: u32 = 0;4302 var num_included_symbols: u32 = 0;
...@@ -4276,6 +4323,11 @@ fn loadObject(...@@ -4276,6 +4323,11 @@ fn loadObject(
4276 .pending => unreachable,4323 .pending => unreachable,
4277 }4324 }
42784325
4326 // TODO: Until we support sorting .pdata, we shouldn't merge these in, the result would be invalid
4327 const section_name = section.name.toSlice(coff);
4328 if (std.mem.startsWith(u8, section_name, ".pdata"))
4329 continue;
4330
4279 num_included_sections += 1;4331 num_included_sections += 1;
4280 num_included_symbols += section.num_symbols;4332 num_included_symbols += section.num_symbols;
4281 num_included_relocs += section.header.number_of_relocations;4333 num_included_relocs += section.header.number_of_relocations;
...@@ -4293,7 +4345,7 @@ fn loadObject(...@@ -4293,7 +4345,7 @@ fn loadObject(
4293 try coff.input_sections.ensureUnusedCapacity(gpa, num_included_sections);4345 try coff.input_sections.ensureUnusedCapacity(gpa, num_included_sections);
42944346
4295 for (sections) |*section| {4347 for (sections) |*section| {
4296 if (section.comdat_result != .include) continue;4348 if (section.parent_si == .null) continue;
42974349
4298 const ni = try coff.mf.addLastChildNode(gpa, section.parent_si.node(coff), .{4350 const ni = try coff.mf.addLastChildNode(gpa, section.parent_si.node(coff), .{
4299 .size = section.header.size_of_raw_data,4351 .size = section.header.size_of_raw_data,
...@@ -4457,7 +4509,7 @@ fn loadObject(...@@ -4457,7 +4509,7 @@ fn loadObject(
44574509
4458 const relocation_size = std.coff.Relocation.sizeOf();4510 const relocation_size = std.coff.Relocation.sizeOf();
4459 for (sections) |section| {4511 for (sections) |section| {
4460 if (section.comdat_result != .include) continue;4512 if (section.si == .null) continue;
44614513
4462 const loc_sym = section.si.get(coff);4514 const loc_sym = section.si.get(coff);
4463 assert(loc_sym.loc_relocs == .none);4515 assert(loc_sym.loc_relocs == .none);
...@@ -5481,6 +5533,26 @@ pub fn flush(...@@ -5481,6 +5533,26 @@ pub fn flush(
5481pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {5533pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5482 const comp = coff.base.comp;5534 const comp = coff.base.comp;
5483 task: {5535 task: {
5536 while (coff.section_merge_pending_index < coff.section_merges.count()) {
5537 defer coff.section_merge_pending_index += 1;
5538 const sub_prog_node = coff.synth_prog_node.start(
5539 coff.section_merges.keys()[coff.section_merge_pending_index].toSlice(coff),
5540 0,
5541 );
5542 defer sub_prog_node.end();
5543 coff.flushSectionMerge(coff.section_merge_pending_index) catch |err| switch (err) {
5544 //error.OutOfMemory => |e| return e,
5545 else => |e| return comp.link_diags.fail(
5546 "linker failed to merge section {s} into {s}: {t}",
5547 .{
5548 coff.section_merges.keys()[coff.section_merge_pending_index].toSlice(coff),
5549 coff.section_merges.values()[coff.section_merge_pending_index].toSlice(coff),
5550 e,
5551 },
5552 ),
5553 };
5554 break :task;
5555 }
5484 while (coff.pending_uavs.pop()) |pending_uav| {5556 while (coff.pending_uavs.pop()) |pending_uav| {
5485 const sub_prog_node = coff.idleProgNode(tid, coff.const_prog_node, .{ .uav = pending_uav.key });5557 const sub_prog_node = coff.idleProgNode(tid, coff.const_prog_node, .{ .uav = pending_uav.key });
5486 defer sub_prog_node.end();5558 defer sub_prog_node.end();
...@@ -5655,7 +5727,8 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5655,7 +5727,8 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5655 try coff.flushMember(pending_mi.key);5727 try coff.flushMember(pending_mi.key);
5656 break :task;5728 break :task;
5657 }5729 }
5658 // TODO: This and the next task ideally only run once, as it's wasteful otherwise5730 // TODO: All the sort / shrink tasks ideally run only once - otherwise it's wasteful
5731 // Defer until exports_complete?
5659 if (coff.export_table.pending_sort) {5732 if (coff.export_table.pending_sort) {
5660 defer coff.export_table.pending_sort = false;5733 defer coff.export_table.pending_sort = false;
5661 const sub_prog_node = coff.idleProgNode(5734 const sub_prog_node = coff.idleProgNode(
...@@ -5694,6 +5767,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5694,6 +5767,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5694 break :task;5767 break :task;
5695 }5768 }
5696 }5769 }
5770 if (coff.section_merge_pending_index < coff.section_merges.count()) return true;
5697 if (coff.pending_uavs.count() > 0) return true;5771 if (coff.pending_uavs.count() > 0) return true;
5698 if (coff.pending_input != null) return true;5772 if (coff.pending_input != null) return true;
5699 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;5773 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
...@@ -6805,6 +6879,70 @@ fn flushExportsSort(coff: *Coff) void {...@@ -6805,6 +6879,70 @@ fn flushExportsSort(coff: *Coff) void {
6805 });6879 });
6806}6880}
68076881
6882fn flushSectionMerge(coff: *Coff, index: u32) !void {
6883 assert(coff.isImage());
6884 const from = coff.section_merges.keys()[index];
6885 const to = coff.section_merges.values()[index];
6886 assert(from != to);
6887
6888 log.debug("flushSectionMerge({s}->{s})", .{ from.toSlice(coff), to.toSlice(coff) });
6889
6890 const opt_to_sec = coff.section_table.getPtr(to);
6891 if (coff.section_table.getPtr(from)) |from_sec| {
6892 const from_sym = from_sec.si.get(coff);
6893 if (opt_to_sec) |to_sec| {
6894 const to_sym = to_sec.si.get(coff);
6895
6896 // TODO: Create a pseudo-section named `from` in `to`, copy `from_sec` ni into that pseudo section
6897 // TODO: Update .section_number for all contained syms
6898 // TODO: Remove `from_sec` from section table (set size = 0 and can do it in flushResized?).
6899 // This is non-trivial as we can't leave holes in the section table.
6900 // TODO: Merge section flags
6901 _ = to_sym;
6902
6903 return coff.base.comp.link_diags.fail("TODO implement section to section merge", .{});
6904 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
6905 const to_sym = to_ps_si.get(coff);
6906 if (from_sym.section_number == to_sym.section_number)
6907 return;
6908
6909 // TODO: Same as above, except place `from` into a node in `to_psmi`'s parent
6910 return coff.base.comp.link_diags.fail("TODO implement section to pseudosection merge", .{});
6911 }
6912
6913 // If `to` doesn't exist, /MERGE is defined as renaming `from` to `to`.
6914 // No other path will create image-level sections, so we can safely rename this now
6915 const from_name = &from_sec.si.get(coff).section_number.header(coff).name;
6916 const to_slice = to.toSlice(coff);
6917 @memcpy(from_name[0..to_slice.len], to_slice);
6918 @memset(from_name[to_slice.len..], 0);
6919 } else if (coff.pseudo_section_table.getIndex(from)) |from_index| {
6920 const from_psmi: Node.PseudoSectionMapIndex = @enumFromInt(from_index);
6921 const from_sym = from_psmi.symbol(coff).get(coff);
6922 if (opt_to_sec) |to_sec| {
6923 const to_sym = to_sec.si.get(coff);
6924 if (from_sym.section_number == to_sym.section_number)
6925 return;
6926
6927 // TODO: Move from_psmi's node into to_sec
6928 // TODO: Update .section_number for all contained syms
6929 // TODO: Merge section flags
6930
6931 return coff.base.comp.link_diags.fail("TODO implement pseudosection to section merge", .{});
6932 } else if (coff.pseudo_section_table.get(to)) |to_ps_si| {
6933 const to_sym = to_ps_si.get(coff);
6934 if (from_sym.section_number == to_sym.section_number)
6935 return;
6936
6937 // TODO: Same as above, but move from_psmi's node after to_psmi's node in its parent
6938
6939 return coff.base.comp.link_diags.fail("TODO implement pseudosection to pseudosection merge", .{});
6940 }
6941
6942 // Renaming pseudo-sections have no effect on the output, so this is a no-op.
6943 }
6944}
6945
6808fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {6946fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
6809 var rva = start_rva;6947 var rva = start_rva;
6810 for (6948 for (