authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:33-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:21:00-04:00
loga87fc47bc17ae28a8a0a90da4c64751a071bac44
tree956120d93f7e0dcd5b0c24ea8ab028352c012b41
parentc8328cb57b9b23c474ab0c55ca5caa0e90bc5f80

- Move the export table logic into updateExportsInner

- Set up relocs for exported symbols in the export address table - Move the sort to `idle`, and set it up to only occur when necessary - Handle the name table being moved

1 files changed, 191 insertions(+), 145 deletions(-)

src/link/Coff.zig+191-145
......@@ -279,15 +279,17 @@ pub const Node = union(enum) {
279279
280280pub const ExportTable = struct {
281281 ni: MappedFile.Node.Index,
282 export_address_table_ni: MappedFile.Node.Index,
282 export_address_table_si: Symbol.Index,
283283 name_pointer_table_ni: MappedFile.Node.Index,
284284 ordinal_table_ni: MappedFile.Node.Index,
285285 name_table_ni: MappedFile.Node.Index,
286286 entries: std.AutoArrayHashMapUnmanaged(void, Entry),
287 pending_sort: bool = false,
287288
288289 pub const Entry = struct {
289290 name_index: u32,
290291 name_len: u32,
292 export_address_table_ri: Reloc.Index,
291293 };
292294
293295 const Adapter = struct {
......@@ -306,10 +308,10 @@ pub const ExportTable = struct {
306308 }
307309 };
308310
309 pub const Index = enum(u32) {
311 pub const Ordinal = enum(u16) {
310312 _,
311313
312 pub fn get(export_index: ExportTable.Index, coff: *Coff) *Entry {
314 pub fn get(export_index: ExportTable.Ordinal, coff: *Coff) *Entry {
313315 return &coff.export_table.entries.values()[@intFromEnum(export_index)];
314316 }
315317 };
......@@ -744,7 +746,7 @@ fn create(
744746 },
745747 .export_table = .{
746748 .ni = .none,
747 .export_address_table_ni = .none,
749 .export_address_table_si = .null,
748750 .name_pointer_table_ni = .none,
749751 .ordinal_table_ni = .none,
750752 .name_table_ni = .none,
......@@ -1083,10 +1085,21 @@ fn initHeaders(
10831085 );
10841086 @memcpy(coff.export_table.ni.slice(&coff.mf)[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
10851087
1086 coff.export_table.export_address_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
1088 const export_address_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
10871089 .alignment = .of(u32),
10881090 .moved = true,
10891091 });
1092
1093 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1094 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();
1095
1096 const export_address_table_sym = coff.export_table.export_address_table_si.get(coff);
1097 export_address_table_sym.ni = export_address_table_ni;
1098 assert(export_address_table_sym.loc_relocs == .none);
1099 export_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1100 export_address_table_sym.section_number =
1101 coff.getNode(edata_section_ni).pseudo_section.symbol(coff).get(coff).section_number;
1102
10901103 coff.export_table.name_pointer_table_ni = try coff.mf.addLastChildNode(gpa, edata_section_ni, .{
10911104 .alignment = .of(u32),
10921105 .moved = true,
......@@ -1120,6 +1133,8 @@ fn initHeaders(
11201133 .name_pointer_table_rva = 0,
11211134 .ordinal_table_rva = 0,
11221135 };
1136 if (target_endian != native_endian)
1137 std.mem.byteSwapAllFields(std.coff.ExportDirectoryTable, export_directory_table);
11231138 }
11241139
11251140 // While tls variables allocated at runtime are writable, the template itself is not
......@@ -1315,10 +1330,6 @@ pub fn exportDirectoryTable(coff: *Coff) *std.coff.ExportDirectoryTable {
13151330 return @ptrCast(@alignCast(coff.export_table.ni.slice(&coff.mf)));
13161331}
13171332
1318pub fn exportAddressTableSlice(coff: *Coff) []std.coff.ExportAddressTableEntry {
1319 return @ptrCast(@alignCast(coff.export_table.export_address_table_ni.slice(&coff.mf)));
1320}
1321
13221333pub fn exportNamePointerTableSlice(coff: *Coff) []std.coff.ExportNamePointerTableEntry {
13231334 return @ptrCast(@alignCast(coff.export_table.name_pointer_table_ni.slice(&coff.mf)));
13241335}
......@@ -1875,82 +1886,6 @@ pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
18751886 };
18761887}
18771888
1878fn flushExports(coff: *Coff, tid: Zcu.PerThread.Id) !void {
1879 const export_count = coff.export_table.entries.count();
1880 if (export_count == 0) return;
1881
1882 const gpa = coff.base.comp.zcu.?.gpa;
1883 const edt = coff.exportDirectoryTable();
1884 edt.number_of_names = @intCast(export_count);
1885 edt.number_of_entries = @intCast(export_count);
1886
1887 try coff.export_table.name_pointer_table_ni.resize(
1888 &coff.mf,
1889 gpa,
1890 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
1891 );
1892
1893 try coff.export_table.ordinal_table_ni.resize(
1894 &coff.mf,
1895 gpa,
1896 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
1897 );
1898
1899 while (try coff.idle(tid)) {}
1900 if (coff.targetEndian() != native_endian)
1901 std.mem.byteSwapAllFields(std.coff.ExportDirectoryTable, edt);
1902
1903 const name_table_rva = coff.computeNodeRva(coff.export_table.name_table_ni);
1904 for (
1905 coff.exportNamePointerTableSlice(),
1906 coff.exportOrdinalTableSlice(),
1907 coff.export_table.entries.values(),
1908 0..,
1909 ) |*np, *ord, entry, entry_i| {
1910 np.name_rva = name_table_rva + entry.name_index;
1911 if (coff.targetEndian() != native_endian)
1912 std.mem.byteSwapAllFields(std.coff.ExportNamePointerTableEntry, np);
1913
1914 ord.unbiased_ordinal = @intCast(entry_i);
1915 if (coff.targetEndian() != native_endian)
1916 std.mem.byteSwapAllFields(std.coff.ExportOrdinalTableEntry, &ord);
1917 }
1918
1919 const Context = struct {
1920 np: []std.coff.ExportNamePointerTableEntry,
1921 ord: []std.coff.ExportOrdinalTableEntry,
1922 entries: []ExportTable.Entry,
1923 names: []const u8,
1924
1925 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
1926 const lhs_entry = &ctx.entries[lhs];
1927 const rhs_entry = &ctx.entries[rhs];
1928 return std.mem.lessThan(
1929 u8,
1930 ctx.names[lhs_entry.name_index..][0..lhs_entry.name_len],
1931 ctx.names[rhs_entry.name_index..][0..rhs_entry.name_len],
1932 );
1933 }
1934
1935 pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void {
1936 std.mem.swap(std.coff.ExportNamePointerTableEntry, &ctx.np[lhs], &ctx.np[rhs]);
1937 std.mem.swap(std.coff.ExportOrdinalTableEntry, &ctx.ord[lhs], &ctx.ord[rhs]);
1938 std.mem.swap(ExportTable.Entry, &ctx.entries[lhs], &ctx.entries[rhs]);
1939 }
1940 };
1941
1942 std.sort.pdqContext(0, export_count, Context{
1943 .np = coff.exportNamePointerTableSlice(),
1944 .ord = coff.exportOrdinalTableSlice(),
1945 .entries = coff.export_table.entries.values(),
1946 .names = coff.export_table.name_table_ni.slice(&coff.mf),
1947 });
1948
1949 // TODO: Is there a way to know if this is the last flush? We could skip doing this if so.
1950 // TODO: Need to reindex with adaptor?
1951 //try coff.export_table.entries.reIndexContext(gpa, ExportTable.Adapter{ .coff = coff });
1952}
1953
19541889pub fn flush(
19551890 coff: *Coff,
19561891 arena: std.mem.Allocator,
......@@ -1961,9 +1896,6 @@ pub fn flush(
19611896 _ = prog_node;
19621897 while (try coff.idle(tid)) {}
19631898
1964 coff.flushExports(tid) catch |err|
1965 return coff.base.comp.link_diags.fail("linker failed to flush exports: {t}", .{err});
1966
19671899 // hack for stage2_x86_64 + coff
19681900 const comp = coff.base.comp;
19691901 if (comp.compiler_rt_dyn_lib) |crt_file| {
......@@ -2061,11 +1993,17 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
20611993 break :task;
20621994 } else coff.mf.update_prog_node.completeOne();
20631995 }
1996 if (coff.export_table.pending_sort) {
1997 coff.export_table.pending_sort = false;
1998 coff.flushExportsSort();
1999 break :task;
2000 }
20642001 }
20652002 if (coff.pending_uavs.count() > 0) return true;
20662003 if (coff.globals.count() > coff.global_pending_index) return true;
20672004 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
20682005 if (coff.mf.updates.items.len > 0) return true;
2006 if (coff.export_table.pending_sort) return true;
20692007 return false;
20702008}
20712009
......@@ -2158,19 +2096,19 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
21582096 const gpa = zcu.gpa;
21592097 const gn = gmi.globalName(coff);
21602098
2161 const target_endian = coff.targetEndian();
2162 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
2163 const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
2164 _ => unreachable,
2165 .PE32 => .{ 4, .@"4" },
2166 .@"PE32+" => .{ 8, .@"8" },
2167 };
2168
2169 const name = gn.name.toSlice(coff);
21702099 if (gn.lib_name.toSlice(coff)) |lib_name| {
2100 const name = gn.name.toSlice(coff);
21712101 try coff.nodes.ensureUnusedCapacity(gpa, 4);
21722102 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
21732103
2104 const target_endian = coff.targetEndian();
2105 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
2106 const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
2107 _ => unreachable,
2108 .PE32 => .{ 4, .@"4" },
2109 .@"PE32+" => .{ 8, .@"8" },
2110 };
2111
21742112 const gop = try coff.import_table.entries.getOrPutAdapted(
21752113 gpa,
21762114 lib_name,
......@@ -2315,47 +2253,6 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
23152253 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
23162254 sym.rva = coff.computeNodeRva(sym.ni);
23172255 si.applyLocationRelocs(coff);
2318 } else {
2319 const entries_ctx = ExportTable.Adapter{ .coff = coff };
2320 const gop = try coff.export_table.entries.getOrPutAdapted(
2321 gpa,
2322 name,
2323 entries_ctx,
2324 );
2325
2326 if (!gop.found_existing) {
2327 errdefer _ = coff.export_table.entries.pop();
2328 if (coff.export_table.entries.count() > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
2329 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
2330
2331 const name_index = coff.export_table.name_table_ni.fileLocation(&coff.mf, true).size;
2332 const new_name_table_size = name_index + name.len + 1;
2333 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
2334 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
2335
2336 try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size);
2337
2338 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
2339 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
2340
2341 gop.value_ptr.* = .{
2342 .name_index = @intCast(name_index),
2343 .name_len = @intCast(name.len),
2344 };
2345
2346 const si = gmi.symbol(coff);
2347 const sym = si.get(coff);
2348
2349 try coff.export_table.export_address_table_ni.resize(
2350 &coff.mf,
2351 gpa,
2352 coff.export_table.entries.count() * @sizeOf(std.coff.ExportAddressTableEntry),
2353 );
2354 const ea = &coff.exportAddressTableSlice()[gop.index];
2355 ea.export_or_forwarder_rva = sym.rva;
2356 if (coff.targetEndian() != native_endian)
2357 std.mem.byteSwapAllFields(std.coff.ExportAddressTableEntry, &ea);
2358 }
23592256 }
23602257}
23612258
......@@ -2490,10 +2387,19 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
24902387 coff.targetStore(&coff.dataDirectoryPtr(.EXPORT).virtual_address, rva);
24912388 coff.targetStore(&coff.exportDirectoryTable().name_rva, rva + @sizeOf(std.coff.ExportDirectoryTable));
24922389 },
2493 .export_address_table => coff.targetStore(
2494 &coff.exportDirectoryTable().export_address_table_rva,
2495 coff.computeNodeRva(ni),
2496 ),
2390 .export_address_table => {
2391 coff.export_table.export_address_table_si.flushMoved(coff);
2392
2393 // These relocs are applied directly here instead of via the above flushMoved call as
2394 // they are non-contiguous, and not tracked under export_address_table_si.
2395 for (coff.export_table.entries.values()) |entry|
2396 entry.export_address_table_ri.get(coff).apply(coff);
2397
2398 coff.targetStore(
2399 &coff.exportDirectoryTable().export_address_table_rva,
2400 coff.computeNodeRva(ni),
2401 );
2402 },
24972403 .export_name_pointer_table => coff.targetStore(
24982404 &coff.exportDirectoryTable().name_pointer_table_rva,
24992405 coff.computeNodeRva(ni),
......@@ -2503,8 +2409,18 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
25032409 coff.computeNodeRva(ni),
25042410 ),
25052411 .export_name_table => {
2506 // .export_name_pointer_table entries are updated in flush
2507 log.warn("flushMoved export_name_table unhandled", .{});
2412 const name_table_rva = coff.computeNodeRva(coff.export_table.name_table_ni);
2413 for (
2414 coff.exportNamePointerTableSlice(),
2415 coff.exportOrdinalTableSlice(),
2416 ) |*np, target_ord| {
2417 const ord: ExportTable.Ordinal = @enumFromInt(coff.targetLoad(&target_ord.unbiased_ordinal));
2418 const entry = ord.get(coff);
2419 coff.targetStore(
2420 &np.name_rva,
2421 @intCast(name_table_rva + entry.name_index),
2422 );
2423 }
25082424 },
25092425 inline .pseudo_section,
25102426 .object_section,
......@@ -2571,6 +2487,40 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
25712487 .global, .nav, .uav, .lazy_code, .lazy_const_data => {},
25722488 }
25732489}
2490
2491fn flushExportsSort(coff: *Coff) void {
2492 const Context = struct {
2493 coff: *Coff,
2494 np: []std.coff.ExportNamePointerTableEntry,
2495 ord: []std.coff.ExportOrdinalTableEntry,
2496 entries: []ExportTable.Entry,
2497 nt: []const u8,
2498
2499 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
2500 const lhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[lhs].unbiased_ordinal)];
2501 const rhs_entry = &ctx.entries[ctx.coff.targetLoad(&ctx.ord[rhs].unbiased_ordinal)];
2502 return std.mem.lessThan(
2503 u8,
2504 ctx.nt[lhs_entry.name_index..][0..lhs_entry.name_len],
2505 ctx.nt[rhs_entry.name_index..][0..rhs_entry.name_len],
2506 );
2507 }
2508
2509 pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void {
2510 std.mem.swap(std.coff.ExportNamePointerTableEntry, &ctx.np[lhs], &ctx.np[rhs]);
2511 std.mem.swap(std.coff.ExportOrdinalTableEntry, &ctx.ord[lhs], &ctx.ord[rhs]);
2512 }
2513 };
2514
2515 std.sort.pdqContext(0, coff.export_table.entries.count(), Context{
2516 .coff = coff,
2517 .np = coff.exportNamePointerTableSlice(),
2518 .ord = coff.exportOrdinalTableSlice(),
2519 .entries = coff.export_table.entries.values(),
2520 .nt = coff.export_table.name_table_ni.slice(&coff.mf),
2521 });
2522}
2523
25742524fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
25752525 var rva = start_rva;
25762526 for (
......@@ -2596,6 +2546,17 @@ pub fn updateExports(
25962546 pt: Zcu.PerThread,
25972547 exported: Zcu.Exported,
25982548 export_indices: []const Zcu.Export.Index,
2549) !void {
2550 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
2551 error.OutOfMemory => error.OutOfMemory,
2552 else => |e| coff.base.comp.link_diags.fail("updateExports failed {t}", .{e}) catch error.AnalysisFail,
2553 };
2554}
2555fn updateExportsInner(
2556 coff: *Coff,
2557 pt: Zcu.PerThread,
2558 exported: Zcu.Exported,
2559 export_indices: []const Zcu.Export.Index,
25992560) !void {
26002561 const zcu = pt.zcu;
26012562 const gpa = zcu.gpa;
......@@ -2622,7 +2583,8 @@ pub fn updateExports(
26222583 const exported_sym = exported_si.get(coff);
26232584 for (export_indices) |export_index| {
26242585 const @"export" = export_index.ptr(zcu);
2625 const export_si = try coff.globalSymbol(@"export".opts.name.toSlice(ip), null);
2586 const name = @"export".opts.name.toSlice(ip);
2587 const export_si = try coff.globalSymbol(name, null);
26262588 const export_sym = export_si.get(coff);
26272589 export_sym.ni = exported_ni;
26282590 export_sym.rva = exported_sym.rva;
......@@ -2637,6 +2599,90 @@ pub fn updateExports(
26372599 if (coff.targetEndian() != native_endian)
26382600 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
26392601 }
2602
2603 const entries_ctx = ExportTable.Adapter{ .coff = coff };
2604 const gop = try coff.export_table.entries.getOrPutAdapted(
2605 gpa,
2606 name,
2607 entries_ctx,
2608 );
2609
2610 if (!gop.found_existing) {
2611 errdefer _ = coff.export_table.entries.pop();
2612
2613 const export_count = coff.export_table.entries.count();
2614 if (export_count > std.math.maxInt(@FieldType(std.coff.ExportDirectoryTable, "number_of_entries")))
2615 return coff.base.comp.link_diags.fail("exceeded maximum number of exports", .{});
2616
2617 const name_index = coff.export_table.name_table_ni.fileLocation(&coff.mf, true).size;
2618 const new_name_table_size = name_index + name.len + 1;
2619 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
2620 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
2621
2622 try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size);
2623
2624 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
2625 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
2626
2627 // If the new name sorts after the current tail of the sorted list, we don't need to re-sort
2628 const ordinal_table_slice = coff.exportOrdinalTableSlice();
2629 if (ordinal_table_slice.len > 0 and !coff.export_table.pending_sort) {
2630 const tail_index: ExportTable.Ordinal =
2631 @enumFromInt(ordinal_table_slice[ordinal_table_slice.len - 1].unbiased_ordinal);
2632 const tail_entry = tail_index.get(coff);
2633 const tail_name = name_table_slice[tail_entry.name_index..][0..tail_entry.name_len];
2634 coff.export_table.pending_sort = std.mem.lessThan(u8, name, tail_name);
2635 }
2636
2637 const edt = coff.exportDirectoryTable();
2638 coff.targetStore(&edt.number_of_names, @intCast(export_count));
2639 edt.number_of_entries = edt.number_of_names;
2640
2641 try coff.export_table.export_address_table_si.node(coff).resize(
2642 &coff.mf,
2643 gpa,
2644 export_count * @sizeOf(std.coff.ExportAddressTableEntry),
2645 );
2646
2647 try coff.export_table.name_pointer_table_ni.resize(
2648 &coff.mf,
2649 gpa,
2650 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
2651 );
2652
2653 try coff.export_table.ordinal_table_ni.resize(
2654 &coff.mf,
2655 gpa,
2656 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
2657 );
2658
2659 coff.targetStore(
2660 &coff.exportNamePointerTableSlice()[gop.index].name_rva,
2661 @intCast(coff.computeNodeRva(coff.export_table.name_table_ni) + name_index),
2662 );
2663 coff.targetStore(
2664 &coff.exportOrdinalTableSlice()[gop.index].unbiased_ordinal,
2665 @intCast(gop.index),
2666 );
2667
2668 gop.value_ptr.* = .{
2669 .name_index = @intCast(name_index),
2670 .name_len = @intCast(name.len),
2671 .export_address_table_ri = @enumFromInt(coff.relocs.items.len),
2672 };
2673
2674 try coff.addReloc(
2675 coff.export_table.export_address_table_si,
2676 @intCast(@sizeOf(std.coff.ExportAddressTableEntry) * gop.index),
2677 export_si,
2678 0,
2679 .{ .AMD64 = .ADDR32NB },
2680 );
2681 } else {
2682 const reloc = gop.value_ptr.*.export_address_table_ri.get(coff);
2683 reloc.target = export_si;
2684 export_si.applyTargetRelocs(coff);
2685 }
26402686 }
26412687}
26422688