zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit f81bd30057d6a1d1d50851b65a644aa86296cd4c (tree)
parent e7d452338c07fd1c8469ae78ed28451ee43089dc
Author: kcbanner <kcbanner@gmail.com>
Date:   Fri,  5 Jun 2026 01:55:34 -0400

Coff: Symbol table output

Diffstat:
Mlib/std/coff.zig | 18+++++++++---------
Msrc/libs/mingw/implib.zig | 2+-
Msrc/link/Coff.zig | 429++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
3 files changed, 393 insertions(+), 56 deletions(-)

diff --git a/lib/std/coff.zig b/lib/std/coff.zig @@ -658,7 +658,7 @@ pub const SectionHeader = extern struct { }; }; -pub const Symbol = struct { +pub const Symbol = extern struct { name: [8]u8, value: u32, section_number: SectionNumber, @@ -683,18 +683,18 @@ pub const Symbol = struct { } }; -pub const SectionNumber = enum(u16) { +pub const SectionNumber = enum(i16) { /// The symbol record is not yet assigned a section. /// A value of zero indicates that a reference to an external symbol is defined elsewhere. /// A value of non-zero is a common symbol with a size that is specified by the value. UNDEFINED = 0, /// The symbol has an absolute (non-relocatable) value and is not an address. - ABSOLUTE = 0xffff, + ABSOLUTE = -1, /// The symbol provides general type or debugging information but does not correspond to a section. /// Microsoft tools use this setting along with .file records (storage class FILE). - DEBUG = 0xfffe, + DEBUG = -2, _, }; @@ -866,7 +866,7 @@ pub const StorageClass = enum(u8) { _, }; -pub const FunctionDefinition = struct { +pub const FunctionDefinition = extern struct { /// The symbol-table index of the corresponding .bf (begin function) symbol record. tag_index: u32, @@ -885,7 +885,7 @@ pub const FunctionDefinition = struct { unused: [2]u8, }; -pub const SectionDefinition = struct { +pub const SectionDefinition = extern struct { /// The size of section data; the same as SizeOfRawData in the section header. length: u32, @@ -907,7 +907,7 @@ pub const SectionDefinition = struct { unused: [3]u8, }; -pub const FileDefinition = struct { +pub const FileDefinition = extern struct { /// An ANSI string that gives the name of the source file. /// This is padded with nulls if it is less than the maximum length. file_name: [18]u8, @@ -918,7 +918,7 @@ pub const FileDefinition = struct { } }; -pub const WeakExternalDefinition = struct { +pub const WeakExternalDefinition = extern struct { /// The symbol-table index of sym2, the symbol to be linked if sym1 is not found. tag_index: u32, @@ -977,7 +977,7 @@ pub const ComdatSelection = enum(u8) { _, }; -pub const DebugInfoDefinition = struct { +pub const DebugInfoDefinition = extern struct { unused_1: [4]u8, /// The actual ordinal line number (1, 2, 3, and so on) within the source file, corresponding to the .bf or .ef record. diff --git a/src/libs/mingw/implib.zig b/src/libs/mingw/implib.zig @@ -1012,7 +1012,7 @@ fn getShortImport( fn writeSymbol(writer: *std.Io.Writer, symbol: std.coff.Symbol) !void { try writer.writeAll(&symbol.name); try writer.writeInt(u32, symbol.value, .little); - try writer.writeInt(u16, @intFromEnum(symbol.section_number), .little); + try writer.writeInt(i16, @intFromEnum(symbol.section_number), .little); try writer.writeInt(u8, @intFromEnum(symbol.type.base_type), .little); try writer.writeInt(u8, @intFromEnum(symbol.type.complex_type), .little); try writer.writeInt(u8, @intFromEnum(symbol.storage_class), .little); diff --git a/src/link/Coff.zig b/src/link/Coff.zig @@ -30,6 +30,7 @@ lib_string_len: u64, long_names_table: LongNamesTable, import_table: ImportTable, export_table: ExportTable, +symbol_table: SymbolTable, strings: std.HashMapUnmanaged( u32, void, @@ -37,10 +38,10 @@ strings: std.HashMapUnmanaged( std.hash_map.default_max_load_percentage, ), string_bytes: std.ArrayList(u8), -image_section_table: std.ArrayList(Symbol.Index), +section_table: std.ArrayList(Symbol.Index), pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index), object_section_table: std.array_hash_map.Auto(String, Symbol.Index), -symbol_table: std.ArrayList(Symbol), +symbols: std.ArrayList(Symbol), globals: std.array_hash_map.Auto(GlobalName, Symbol.Index), global_pending_index: u32, navs: std.array_hash_map.Auto(InternPool.Nav.Index, Symbol.Index), @@ -144,6 +145,7 @@ pub const Node = union(enum) { header, /// Images and archives only. signature, + /// Archives only. archive_member_header: Member.Index, archive_member: Member.Index, @@ -154,15 +156,21 @@ pub const Node = union(enum) { /// Image only data_directories, section_table, - image_section: Symbol.Index, + // Archives and objects only + symbol_table, + symbol_table_entry, + // Archives and objects only + string_table, + + image_section: Symbol.Index, // TODO: image_section -> section - /// Only images contain imports + /// Images only import_directory_table, import_lookup_table: ImportTable.Index, import_address_table: ImportTable.Index, import_hint_name_table: ImportTable.Index, - /// Only images contain exports + /// Images only export_directory_table, export_address_table, export_name_pointer_table, @@ -291,6 +299,8 @@ pub const Node = union(enum) { optional_header, data_directories, section_table, + symbol_table, + string_table, }; var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined; const info = @typeInfo(Known).@"enum"; @@ -436,6 +446,47 @@ pub const LongNamesTable = struct { }; }; +pub const SymbolTable = struct { + string_offsets: std.AutoArrayHashMapUnmanaged(String, StringIndex), + entries: std.AutoArrayHashMapUnmanaged(Symbol.Index, Entry), + + // Adding nodes to the symbol table has the result of accumulating padding + // between the last symbol and the string table, due to the growth factor + // in MappedFile. The spec requires the string table begin immediately + // after the last symbol, so we compact the symbol table node if needed. + pending_shrink: bool, + + pub const Entry = struct { + entry_si: Symbol.Index, + index: Index, + }; + + pub const Add = union(enum) { + section, + global: struct { + import: bool, + }, + }; + + pub const SymbolName = union(enum) { + short: []const u8, + long: StringIndex, + }; + + // Symbol.Index does not map 1:1 with SymbolTable.Index due to auxiliary entries + pub const Index = enum(u32) { + _, + + pub fn get(sti: SymbolTable.Index, coff: *Coff) *Entry { + return &coff.symbol_table.entries.values()[@intFromEnum(sti)]; + } + }; + + pub const StringIndex = enum(u32) { + _, + }; +}; + pub const ExportTable = struct { ni: MappedFile.Node.Index, export_directory_table_ni: MappedFile.Node.Index, @@ -582,7 +633,7 @@ pub const Symbol = struct { } pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index { - return coff.image_section_table.items[sn.toIndex()]; + return coff.section_table.items[sn.toIndex()]; } pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader { @@ -600,7 +651,7 @@ pub const Symbol = struct { const known_count = @typeInfo(Index).@"enum".field_names.len; pub fn get(si: Symbol.Index, coff: *Coff) *Symbol { - return &coff.symbol_table.items[@intFromEnum(si)]; + return &coff.symbols.items[@intFromEnum(si)]; } pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index { @@ -920,12 +971,17 @@ fn create( .name_table_ni = .none, .entries = .empty, }, + .symbol_table = .{ + .string_offsets = .empty, + .entries = .empty, + .pending_shrink = false, + }, .strings = .empty, .string_bytes = .empty, - .image_section_table = .empty, + .section_table = .empty, .pseudo_section_table = .empty, .object_section_table = .empty, - .symbol_table = .empty, + .symbols = .empty, .globals = .empty, .global_pending_index = 0, .navs = .empty, @@ -965,15 +1021,16 @@ pub fn deinit(coff: *Coff) void { const gpa = coff.base.comp.gpa; coff.mf.deinit(gpa); coff.nodes.deinit(gpa); + // TODO: Update this coff.long_names_table.entries.deinit(gpa); coff.import_table.entries.deinit(gpa); coff.export_table.entries.deinit(gpa); coff.strings.deinit(gpa); coff.string_bytes.deinit(gpa); - coff.image_section_table.deinit(gpa); + coff.section_table.deinit(gpa); coff.pseudo_section_table.deinit(gpa); coff.object_section_table.deinit(gpa); - coff.symbol_table.deinit(gpa); + coff.symbols.deinit(gpa); coff.globals.deinit(gpa); coff.navs.deinit(gpa); coff.uavs.deinit(gpa); @@ -1035,8 +1092,13 @@ fn initHeaders( var expected_nodes_len: usize = Node.known_count; if (comp.zcu != null) { + // Section nodes expected_nodes_len += 3; + // Symbol table nodes + if (is_archive) expected_nodes_len += 6; + // Pseudo-sections and import / export table nodes if (is_image) expected_nodes_len += 9; + // TLS section nodes expected_nodes_len += @as(usize, @intFromBool(comp.config.any_non_single_threaded)) * 2; } defer assert(coff.nodes.len == expected_nodes_len); @@ -1294,10 +1356,26 @@ fn initHeaders( })); coff.nodes.appendAssumeCapacity(.section_table); + const symbol_table_ni = Node.known.symbol_table; + assert(symbol_table_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{ + .alignment = .@"4", + .fixed = true, + .moved = true, + })); + coff.nodes.appendAssumeCapacity(.symbol_table); + + const string_table_ni = Node.known.string_table; + assert(string_table_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{ + .size = @sizeOf(u32), + .fixed = true, + .resized = true, + })); + coff.nodes.appendAssumeCapacity(.string_table); + assert(coff.nodes.len == Node.known_count); - try coff.symbol_table.ensureTotalCapacity(gpa, Symbol.Index.known_count); - coff.symbol_table.addOneAssumeCapacity().* = .{ + try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count); + coff.symbols.addOneAssumeCapacity().* = .{ .ni = .none, .rva = 0, .size = 0, @@ -1360,7 +1438,7 @@ fn initHeaders( }); coff.nodes.appendAssumeCapacity(.export_address_table); - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity(); const export_address_table_sym = coff.export_table.export_address_table_si.get(coff); @@ -1451,6 +1529,10 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 { .section_table, .export_name_table, .placeholder, + + .symbol_table, + .symbol_table_entry, + .string_table, => unreachable, .image_section => |si| si, .import_directory_table => break :parent_rva coff.targetLoad( @@ -1632,7 +1714,28 @@ pub fn dataDirectoryPtr( } pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader { - return @ptrCast(@alignCast(Node.known.section_table.slice(&coff.mf))); + return @ptrCast(@alignCast( + Node.known.section_table.slice(&coff.mf)[0 .. coff.section_table.items.len * @sizeOf(std.coff.SectionHeader)], + )); +} + +pub fn symbolTableEntryPtr(coff: *Coff, sti: SymbolTable.Index) *align(2) std.coff.Symbol { + return @ptrCast(@alignCast( + &Node.known.symbol_table.slice(&coff.mf)[@intFromEnum(sti) * std.coff.Symbol.sizeOf()], + )); +} + +pub fn symbolAuxSectionDefinitionPtr(coff: *Coff, si: Symbol.Index) *align(2) std.coff.SectionDefinition { + const sti = coff.symbol_table.entries.get(si).?.index; + + const symbol = coff.symbolTableEntryPtr(sti); + assert(symbol.storage_class == .STATIC and symbol.number_of_aux_symbols == 1); + + return @ptrCast(symbolTableEntryPtr(coff, @enumFromInt(@intFromEnum(sti) + 1))); +} + +pub fn symbolTableStringLenPtr(coff: *Coff) *align(2) u32 { + return @ptrCast(@alignCast(Node.known.string_table.slice(&coff.mf)[0..@sizeOf(u32)])); } pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry { @@ -1662,7 +1765,7 @@ pub fn exportOrdinalTableSlice(coff: *Coff) []std.coff.ExportOrdinalTableEntry { } fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index { - defer coff.symbol_table.addOneAssumeCapacity().* = .{ + defer coff.symbols.addOneAssumeCapacity().* = .{ .ni = .none, .rva = 0, .size = 0, @@ -1670,7 +1773,7 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index { .target_relocs = .none, .section_number = .UNDEFINED, }; - return @enumFromInt(coff.symbol_table.items.len); + return @enumFromInt(coff.symbols.items.len); } fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index { @@ -1715,7 +1818,7 @@ fn getOrPutGlobalSymbol( lib_name: ?[]const u8, ) !std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index).GetOrPutResult { const gpa = coff.base.comp.gpa; - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const sym_gop = try coff.globals.getOrPut(gpa, .{ .name = try coff.getOrPutString(name), .lib_name = try coff.getOrPutOptionalString(lib_name), @@ -1724,6 +1827,7 @@ fn getOrPutGlobalSymbol( sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity(); coff.synth_prog_node.increaseEstimatedTotalItems(1); } + return sym_gop; } @@ -1758,7 +1862,7 @@ fn navSection( } fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { const gpa = zcu.gpa; - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const sym_gop = try coff.navs.getOrPut(gpa, nav_index); if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity(); return @enumFromInt(sym_gop.index); @@ -1776,7 +1880,7 @@ pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbo fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex { const gpa = coff.base.comp.gpa; - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const sym_gop = try coff.uavs.getOrPut(gpa, uav_val); if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity(); return @enumFromInt(sym_gop.index); @@ -1788,7 +1892,7 @@ pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index { pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index { const gpa = coff.base.comp.gpa; - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty); if (!sym_gop.found_existing) { sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity(); @@ -2004,13 +2108,185 @@ fn addMemberSymbol( coff.pending_members.putAssumeCapacity(mi, {}); } +fn addSymbolTableEntry( + coff: *Coff, + name: union(enum) { + bytes: []const u8, + string: String, + }, + si: Symbol.Index, + add: SymbolTable.Add, +) !void { + assert(!coff.isImage()); + const gpa = coff.base.comp.gpa; + + const string, const name_slice = switch (name) { + .bytes => |bytes| .{ try coff.getOrPutString(bytes), bytes }, + .string => |s| .{ s, s.toSlice(coff) }, + }; + + const symbol_name: SymbolTable.SymbolName = if (name_slice.len > 8) index: { + const string_gop = try coff.symbol_table.string_offsets.getOrPut(gpa, string); + if (!string_gop.found_existing) { + const string_index = Node.known.string_table.location(&coff.mf).resolve(&coff.mf)[1]; + string_gop.value_ptr.* = @enumFromInt(string_index); + + try Node.known.string_table.resize(&coff.mf, gpa, string_index + name_slice.len + 1); + const slice = Node.known.string_table.slice(&coff.mf); + @memcpy(slice[string_index..][0..name_slice.len], name_slice); + slice[string_index + name_slice.len] = 0; + } + + break :index .{ .long = string_gop.value_ptr.* }; + } else .{ .short = name_slice }; + + const symbol_index = coff.targetLoad(&coff.headerPtr().number_of_symbols); + const symbols_added: u8 = switch (add) { + .section => count: { + const sym = si.get(coff); + + try coff.nodes.ensureUnusedCapacity(gpa, 2); + _ = try coff.addSymbolTableEntryAssumeCapacity( + symbol_name, + 0, + sym.section_number, + .{ + .complex_type = .NULL, + .base_type = .NULL, + }, + .STATIC, + 1, + ); + + // Aux entry ields are updated by flushMoved / flushResized + + try coff.symbol_table.entries.put(gpa, si, .{ + .entry_si = .null, + .index = @enumFromInt(symbol_index), + }); + + break :count 2; + }, + .global => |global| count: { + const sym = si.get(coff); + + try coff.nodes.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); + + const entry_ni = try coff.addSymbolTableEntryAssumeCapacity( + symbol_name, + if (global.import) 0 else coff.computeNodeSectionOffset(sym.ni), + if (global.import) .UNDEFINED else sym.section_number, + .{ + .base_type = .NULL, + .complex_type = if (global.import or + Symbol.Index.text.get(coff).section_number == sym.section_number) + .FUNCTION + else + .NULL, + }, + .EXTERNAL, + 0, + ); + + const entry_si = coff.addSymbolAssumeCapacity(); + { + const entry_sym = entry_si.get(coff); + entry_sym.ni = entry_ni; + assert(entry_sym.loc_relocs == .none); + entry_sym.loc_relocs = @enumFromInt(coff.relocs.items.len); + entry_sym.section_number = .UNDEFINED; + } + + try coff.addReloc( + entry_si, + @offsetOf(std.coff.Symbol, "value"), + si, + 0, + .{ .AMD64 = .SECREL }, + ); + + try coff.symbol_table.entries.put(gpa, si, .{ + .entry_si = entry_si, + .index = @enumFromInt(symbol_index), + }); + + break :count 1; + }, + }; + + const new_num_symbols = symbol_index + symbols_added; + coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols); + coff.symbol_table.pending_shrink = + Node.known.symbol_table.location(&coff.mf).resolve(&coff.mf)[1] > + new_num_symbols * std.coff.Symbol.sizeOf(); +} + +/// Caller guarantees there is capacity for 1 + number_of_aux_symbols nodes. +/// Auxiliary nodes are zero-initialized. +fn addSymbolTableEntryAssumeCapacity( + coff: *Coff, + name: SymbolTable.SymbolName, + value: u32, + section_number: Symbol.SectionNumber, + @"type": std.coff.SymType, + storage_class: std.coff.StorageClass, + number_of_aux_symbols: u8, +) !MappedFile.Node.Index { + const gpa = coff.base.comp.gpa; + + const entry_ni = try coff.mf.addLastChildNode(gpa, Node.known.symbol_table, .{ + .alignment = .@"2", + .size = std.coff.Symbol.sizeOf(), + .fixed = true, + }); + coff.nodes.appendAssumeCapacity(.symbol_table_entry); + + const entry: *align(2) std.coff.Symbol = @ptrCast(@alignCast(entry_ni.slice(&coff.mf))); + entry.* = .{ + .name = undefined, + .value = value, + .section_number = @enumFromInt(@intFromEnum(section_number)), + .type = @"type", + .storage_class = storage_class, + .number_of_aux_symbols = number_of_aux_symbols, + }; + + switch (name) { + .short => |s| { + @memcpy(entry.name[0..s.len], s); + @memset(entry.name[s.len..], 0); + }, + .long => |l| { + @memset(entry.name[0..4], 0); + const offset_ptr: *align(2) u32 = @ptrCast(entry.name[4..]); + coff.targetStore(offset_ptr, @intFromEnum(l)); + }, + } + + if (coff.targetEndian() != native_endian) + std.mem.byteSwapAllFields(std.coff.SectionHeader, entry.*); + + for (0..number_of_aux_symbols) |_| { + const aux_ni = try coff.mf.addLastChildNode(gpa, Node.known.symbol_table, .{ + .alignment = .@"2", + .size = std.coff.Symbol.sizeOf(), + .fixed = true, + }); + coff.nodes.appendAssumeCapacity(.symbol_table_entry); + @memset(aux_ni.slice(&coff.mf), 0); + } + + return entry_ni; +} + fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index { assert(coff.base.comp.zcu != null); const gpa = coff.base.comp.gpa; try coff.nodes.ensureUnusedCapacity(gpa, 1); - try coff.image_section_table.ensureUnusedCapacity(gpa, 1); - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.section_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const coff_header = coff.headerPtr(); const section_index = coff.targetLoad(&coff_header.number_of_sections); @@ -2022,19 +2298,19 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags @sizeOf(std.coff.SectionHeader) * section_table_len, ); - const parent_ni, const alignment = if (coff.isArchive()) - .{ Node.known.zcu_member, .@"1" } + const parent_ni = if (coff.isArchive()) + Node.known.zcu_member else - .{ Node.known.file, coff.mf.flags.block_size }; + Node.known.file; const ni = try coff.mf.addLastChildNode(gpa, parent_ni, .{ - .alignment = alignment, + .alignment = coff.mf.flags.block_size, .moved = true, .bubbles_moved = false, }); const si = coff.addSymbolAssumeCapacity(); - coff.image_section_table.appendAssumeCapacity(si); + coff.section_table.appendAssumeCapacity(si); coff.nodes.appendAssumeCapacity(.{ .image_section = si }); const section_table = coff.sectionTableSlice(); @@ -2042,7 +2318,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags const virtual_size = coff.optionalHeaderField(.section_alignment); const rva: u32 = switch (section_index) { 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]), - else => coff.image_section_table.items[section_index - 1].get(coff).rva + + else => coff.section_table.items[section_index - 1].get(coff).rva + coff.targetLoad(&section_table[section_index - 1].virtual_size), }; @@ -2080,6 +2356,8 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags @intCast(rva + virtual_size), ), } + } else { + try coff.addSymbolTableEntry(.{ .bytes = name }, si, .section); } return si; @@ -2112,7 +2390,7 @@ fn pseudoSectionMapIndex( else .rdata; try coff.nodes.ensureUnusedCapacity(gpa, 1); - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment }); const si = coff.addSymbolAssumeCapacity(); pseudo_section_gop.value_ptr.* = si; @@ -2142,7 +2420,7 @@ fn objectSectionMapIndex( name_slice[0 .. std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len], ), alignment, attributes)).symbol(coff); try coff.nodes.ensureUnusedCapacity(gpa, 1); - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const parent_ni = parent.node(coff); var prev_ni: MappedFile.Node.Index = .none; var next_it = parent_ni.children(&coff.mf); @@ -2251,6 +2529,8 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde const sym = si.get(coff); sym.ni = ni; sym.section_number = sec_si.get(coff).section_number; + + // TODO: Add symbol table entry }, else => si.deleteLocationRelocs(coff), } @@ -2506,11 +2786,6 @@ pub fn flush( _ = prog_node; while (try coff.idle(tid)) {} - // TODO: Second linker member symbol tables are built here - if (isArchive(coff)) { - //Member.Index.second.get(coff).content_ni; - } - const comp = coff.base.comp; // Implib generation should instead be done via building a MappedFile progressively @@ -2634,6 +2909,26 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { coff.flushExportsSort(); break :task; } + // TODO: This and the above task ideally run only once, as it's wasteful otherwise + if (coff.symbol_table.pending_shrink) { + coff.symbol_table.pending_shrink = false; + // TODO: Prog node + const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols); + Node.known.symbol_table.shrink( + &coff.mf, + comp.gpa, + number_of_symbols * std.coff.Symbol.sizeOf(), + true, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => |e| return comp.link_diags.fail( + "linker failed to shrink symbol table: {t}", + .{e}, + ), + }; + + break :task; + } } if (coff.pending_uavs.count() > 0) return true; if (coff.globals.count() > coff.global_pending_index) return true; @@ -2641,6 +2936,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { if (coff.mf.updates.items.len > 0) return true; if (coff.pending_members.count() > 0) return true; if (coff.export_table.pending_sort) return true; + if (coff.symbol_table.pending_shrink) return true; return false; } @@ -2733,14 +3029,28 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void { const gpa = zcu.gpa; const gn = gmi.globalName(coff); - // TODO: We still need to emit a reloc for the __imp_Name symbol? + if (!coff.isImage()) { + // TODO: What about data imports? + + // const si = gmi.symbol(coff); + // const sym = si.get(coff); + // sym.section_number = Symbol.Index.text.get(coff).section_number; + // assert(sym.loc_relocs == .none); + // sym.loc_relocs = @enumFromInt(coff.relocs.items.len); + // + // try coff.addSymbolTableEntry( + // .{ .bytes = gn.name.toSlice(coff) }, + // si, + // .{ .global = .{ .import = true } }, + // ); - if (!coff.isImage()) return; + return; + } if (gn.lib_name.toSlice(coff)) |lib_name| { const name = gn.name.toSlice(coff); try coff.nodes.ensureUnusedCapacity(gpa, 4); - try coff.symbol_table.ensureUnusedCapacity(gpa, 1); + try coff.symbols.ensureUnusedCapacity(gpa, 1); const target_endian = coff.targetEndian(); const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic); @@ -2749,7 +3059,6 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void { .PE32 => .{ 4, .@"4" }, .@"PE32+" => .{ 8, .@"8" }, }; - const gop = try coff.import_table.entries.getOrPutAdapted( gpa, lib_name, @@ -2959,7 +3268,16 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void { .data_directories, .section_table, .placeholder, + + .symbol_table_entry, + .string_table, => if (!coff.isArchive()) unreachable, + .symbol_table => { + coff.targetStore( + &coff.headerPtr().pointer_to_symbol_table, + @intCast(ni.location(&coff.mf).resolve(&coff.mf)[0]), + ); + }, .archive_member_header => |mi| { const member = mi.get(coff); switch (member.kind) { @@ -3121,7 +3439,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { ), } - if (size > coff.image_section_table.items[0].get(coff).rva) try coff.virtualSlide( + if (size > coff.section_table.items[0].get(coff).rva) try coff.virtualSlide( 0, std.mem.alignForward( u32, @@ -3159,6 +3477,12 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { .data_directories, => unreachable, .section_table => {}, + .symbol_table => assert(!coff.isImage()), + .symbol_table_entry => unreachable, + .string_table => { + assert(!coff.isImage()); + coff.targetStore(coff.symbolTableStringLenPtr(), @intCast(size)); + }, .image_section => |si| { const sym = si.get(coff); const section_index = sym.section_number.toIndex(); @@ -3173,6 +3497,13 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { coff.targetStore(&section.virtual_size, virtual_size); try coff.virtualSlide(section_index + 1, sym.rva + virtual_size); } + + if (coff.isArchive()) { + coff.targetStore( + &coff.symbolAuxSectionDefinitionPtr(si).length, + @intCast(size), + ); + } }, .import_directory_table => coff.targetStore( &coff.dataDirectoryPtr(.IMPORT).size, @@ -3298,7 +3629,7 @@ fn flushExportsSort(coff: *Coff) void { fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { var rva = start_rva; for ( - coff.image_section_table.items[start_section_index..], + coff.section_table.items[start_section_index..], coff.sectionTableSlice()[start_section_index..], ) |section_si, *section| { const section_sym = section_si.get(coff); @@ -3343,7 +3674,7 @@ fn updateExportsInner( Value.fromInterned(uav).fmtValue(pt), }), } - try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len); + try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len); const exported_si: Symbol.Index = switch (exported) { .nav => |nav| try coff.navSymbol(zcu, nav), .uav => |uav| @enumFromInt(@intFromEnum(try coff.lowerUav( @@ -3376,13 +3707,20 @@ fn updateExportsInner( std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory); } - if (coff.isArchive()) + if (coff.isArchive()) { try coff.addMemberSymbol( symbol_gop.key_ptr.*.name, coff.getNode(Node.known.zcu_member).archive_member, export_si, ); + try coff.addSymbolTableEntry( + .{ .bytes = name }, + export_si, + .{ .global = .{ .import = false } }, + ); + } + if (coff.export_table.ni == .none) continue; const entries_ctx = ExportTable.Adapter{ .coff = coff }; @@ -3425,8 +3763,7 @@ fn updateExportsInner( coff.targetStore(&edt.number_of_names, @intCast(export_count)); edt.number_of_entries = edt.number_of_names; - // TODO: If we had an estimate of the total number of exports this could be a lot more efficient - + // TODO: These should all be resized ahead of time to fit all exports (after https://github.com/ziglang/zig/issues/23616) try coff.export_table.export_address_table_si.node(coff).resize( &coff.mf, gpa,