zig

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

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

Coff: Fixup input object relocations

- Handle the case of undefined global symbols becoming defined later by another input, and call flushMoved on these via the new input_resolved tracking array
- Fixup not calling flushMoved on the input section symbol itself

Diffstat:
Msrc/link/Coff.zig | 139++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 89 insertions(+), 50 deletions(-)

diff --git a/src/link/Coff.zig b/src/link/Coff.zig @@ -31,12 +31,8 @@ long_names_table: LongNamesTable, import_table: ImportTable, export_table: ExportTable, symbol_table: SymbolTable, -inputs: std.ArrayList(struct { - path: std.Build.Cache.Path, - archive_name: ?[]const u8, - first_si: Symbol.Index, - last_si: Symbol.Index, -}), +inputs: std.ArrayList(Input), +input_resolved: std.ArrayList(Symbol.Index), input_sections: std.ArrayList(struct { ii: Node.InputIndex, si: Symbol.Index, @@ -298,6 +294,10 @@ pub const Node = union(enum) { pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index { return coff.inputs.items[@intFromEnum(ii)].last_si; } + + pub fn firstResolvedGlobal(ii: InputIndex, coff: *const Coff) Input.ResolvedIndex { + return coff.inputs.items[@intFromEnum(ii)].first_iri; + } }; pub const InputSectionIndex = enum(u32) { @@ -384,6 +384,18 @@ pub const Node = union(enum) { } }; +pub const Input = struct { + path: std.Build.Cache.Path, + archive_name: ?[]const u8, + first_si: Symbol.Index, + last_si: Symbol.Index, + first_iri: ResolvedIndex, + + const ResolvedIndex = enum(u32) { + _, + }; +}; + pub const Member = struct { kind: Kind, header_ni: MappedFile.Node.Index, @@ -1270,6 +1282,7 @@ fn create( .pending_shrink = false, }, .inputs = .empty, + .input_resolved = .empty, .input_sections = .empty, .input_section_pending_index = 0, .strings = .empty, @@ -1330,6 +1343,7 @@ pub fn deinit(coff: *Coff) void { coff.symbol_table.strings.deinit(gpa); coff.symbol_table.pending.deinit(gpa); coff.inputs.deinit(gpa); + coff.input_resolved.deinit(gpa); coff.input_sections.deinit(gpa); coff.strings.deinit(gpa); coff.string_bytes.deinit(gpa); @@ -2253,7 +2267,7 @@ fn getOrPutGlobalSymbol( pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index { const gop = try coff.getOrPutGlobalSymbol(opts); if (gop.found_existing) { - // TODO: Need to know if this is an export or extern, add to opts + // TODO: Need to know if this is an export or extern, in order to decide if this is duplicate, add to opts } return gop.value_ptr.*; @@ -3208,6 +3222,7 @@ fn loadObject( .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null, .first_si = .null, .last_si = .null, + .first_iri = @enumFromInt(coff.input_resolved.items.len), }; const string_table = string_table: { @@ -3431,9 +3446,15 @@ fn loadObject( var symbols: std.ArrayList(Symbol.Index) = .empty; try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols); + var undefs: std.ArrayList(struct { + symbol_i: u32, + name: String, + size: u32, + }) = .empty; + const first_si = coff.symbols.items.len; - var symbol_ix: u32 = 0; - while (symbol_ix < header.number_of_symbols) { + var symbol_i: u32 = 0; + while (symbol_i < header.number_of_symbols) { var symbol: std.coff.Symbol = undefined; @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size)); if (target_endian != native_endian) @@ -3441,13 +3462,13 @@ fn loadObject( defer { r.toss(symbol.number_of_aux_symbols * symbol_size); - symbol_ix += symbol.number_of_aux_symbols + 1; + symbol_i += symbol.number_of_aux_symbols + 1; } const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: { const index = std.mem.readInt(u32, symbol.name[4..], target_endian); if (index >= string_table.len) - return diags.failParse(path, "bad string offset for symbol 0x{x}", .{symbol_ix}); + return diags.failParse(path, "bad string offset for symbol 0x{x}", .{symbol_i}); break :name string_table[index..]; } else &symbol.name, 0); @@ -3486,7 +3507,7 @@ fn loadObject( { if (symbol.number_of_aux_symbols > 1) return diags.failParse(path, "invalid number of aux symbols for section 0x{x}: {d}", .{ - symbol_ix, + symbol_i, symbol.number_of_aux_symbols, }); @@ -3533,6 +3554,13 @@ fn loadObject( }, }, .EXTERNAL => switch (symbol.section_number) { + .UNDEFINED => { + (try undefs.addOne(gpa)).* = .{ + .symbol_i = symbol_i, + .name = try coff.getOrPutString(name), + .size = symbol.value, + }; + }, .ABSOLUTE => return diags.failParse( path, "TODO unhandled external absolute symbol: '{s}'", @@ -3546,38 +3574,36 @@ fn loadObject( else => |sn| { const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name }); si_slice[0] = global_gop.value_ptr.*; - const sym = si_slice[0].get(coff); - if (sn != .UNDEFINED) { - if (global_gop.found_existing and sym.ni != .none) { - // TODO: Need corresponding logic later if we try to make a global already defined by an input - - var err = try diags.addErrorWithNotes(2); - try err.addMsg("multiple definitions of '{s}'", .{name}); - switch (coff.getNode(sym.ni)) { - .input_section => |isi| { - const other_ii = isi.input(coff); - err.addNote("first seen in input '{f}{f}'", .{ - other_ii.path(coff).fmtEscapeString(), - fmtArchiveNameString(other_ii.archiveName(coff)), - }); - }, - .nav, .uav => err.addNote("first seen in module '{s}'", .{ - comp.zcu.?.root_mod.fully_qualified_name, - }), - else => unreachable, - } - err.addNote("defined again in input '{f}'", .{path}); - return error.LinkFailure; + if (global_gop.found_existing and sym.ni != .none) { + // TODO: Need corresponding logic later if we try to make a global already defined by an input + var err = try diags.addErrorWithNotes(2); + try err.addMsg("multiple definitions of '{s}'", .{name}); + switch (coff.getNode(sym.ni)) { + .input_section => |isi| { + const other_ii = isi.input(coff); + err.addNote("first seen in input '{f}{f}'", .{ + other_ii.path(coff).fmtEscapeString(), + fmtArchiveNameString(other_ii.archiveName(coff)), + }); + }, + .nav, .uav => err.addNote("first seen in module '{s}'", .{ + comp.zcu.?.root_mod.fully_qualified_name, + }), + else => unreachable, } + err.addNote("defined again in input '{f}'", .{path}); + return error.LinkFailure; + } - // TODO: Here if we *were* undefined we want to associate this symbol now with this section for - // the flushMoved iteration - - coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value); - } else if (!global_gop.found_existing) { - sym.value = .{ .size = symbol.value }; + if (global_gop.found_existing) { + // `input_resolved` allows visting this symbol in this input section's flushMoved, + // as the previously undefined global created earlier will not be in our + // contiguous first / last range. + (try coff.input_resolved.addOne(gpa)).* = si_slice[0]; } + + coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value); }, }, else => {}, @@ -3589,6 +3615,18 @@ fn loadObject( input.last_si = @enumFromInt(coff.symbols.items.len - 1); } + // These are added after all the defined symbols are created so they are not part of the + // input's symbol range, which should only contain symbols that are actually located in this input. + for (undefs.items) |undef| { + // TODO: Avoid redundant hashing by having name be a union on String / []const u8 + const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = undef.name.toSlice(coff) }); + symbols.items[undef.symbol_i] = global_gop.value_ptr.*; + if (!global_gop.found_existing) { + const sym = symbols.items[undef.symbol_i].get(coff); + sym.value = .{ .size = undef.size }; + } + } + const relocation_size = std.coff.Relocation.sizeOf(); for (sections) |section| { if (section.si == .null) continue; @@ -4581,10 +4619,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void { coff.nodes.appendAssumeCapacity(.{ .global = gmi }); sym.rva = coff.computeNodeRva(sym.ni); si.applyLocationRelocs(coff); - } else { - - // TODO: If no .ni, report symbol not found - or should it be right when it's added as a global if we don't know about it? - } } @@ -4702,14 +4736,19 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void { }, .input_section => |isi| { const ii = isi.input(coff); - var si = ii.firstSymbol(coff); - const last_si = ii.lastSymbol(coff); + isi.symbol(coff).flushMoved(coff); - // TODO: This iteration doesn't visit symbols that were added first - // in the range of another section (as undef). + { + var si = ii.firstSymbol(coff); + const last_si = ii.lastSymbol(coff); + while (@intFromEnum(si) <= @intFromEnum(last_si)) : (si = si.next()) { + if (si.get(coff).ni != ni) continue; + si.flushMoved(coff); + } + } - while (@intFromEnum(si) <= @intFromEnum(last_si)) : (si = si.next()) { - if (si.get(coff).ni != ni) continue; + for (coff.input_resolved.items[@intFromEnum(ii.firstResolvedGlobal(coff))..]) |si| { + if (si.get(coff).ni != ni) break; si.flushMoved(coff); } },