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
logd8f23c57aaf70f9dcee25620f79c8a6d5512eaae
treee7abc4acfeb05d273aafc0731ffb3b83b723ab21
parented42120d8b739794e884d924f92ef684325a4e34

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

1 files changed, 89 insertions(+), 50 deletions(-)

src/link/Coff.zig+89-50
......@@ -31,12 +31,8 @@ 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}),
34inputs: std.ArrayList(Input),
35input_resolved: std.ArrayList(Symbol.Index),
4036input_sections: std.ArrayList(struct {
4137 ii: Node.InputIndex,
4238 si: Symbol.Index,
......@@ -298,6 +294,10 @@ pub const Node = union(enum) {
298294 pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
299295 return coff.inputs.items[@intFromEnum(ii)].last_si;
300296 }
297
298 pub fn firstResolvedGlobal(ii: InputIndex, coff: *const Coff) Input.ResolvedIndex {
299 return coff.inputs.items[@intFromEnum(ii)].first_iri;
300 }
301301 };
302302
303303 pub const InputSectionIndex = enum(u32) {
......@@ -384,6 +384,18 @@ pub const Node = union(enum) {
384384 }
385385};
386386
387pub const Input = struct {
388 path: std.Build.Cache.Path,
389 archive_name: ?[]const u8,
390 first_si: Symbol.Index,
391 last_si: Symbol.Index,
392 first_iri: ResolvedIndex,
393
394 const ResolvedIndex = enum(u32) {
395 _,
396 };
397};
398
387399pub const Member = struct {
388400 kind: Kind,
389401 header_ni: MappedFile.Node.Index,
......@@ -1270,6 +1282,7 @@ fn create(
12701282 .pending_shrink = false,
12711283 },
12721284 .inputs = .empty,
1285 .input_resolved = .empty,
12731286 .input_sections = .empty,
12741287 .input_section_pending_index = 0,
12751288 .strings = .empty,
......@@ -1330,6 +1343,7 @@ pub fn deinit(coff: *Coff) void {
13301343 coff.symbol_table.strings.deinit(gpa);
13311344 coff.symbol_table.pending.deinit(gpa);
13321345 coff.inputs.deinit(gpa);
1346 coff.input_resolved.deinit(gpa);
13331347 coff.input_sections.deinit(gpa);
13341348 coff.strings.deinit(gpa);
13351349 coff.string_bytes.deinit(gpa);
......@@ -2253,7 +2267,7 @@ fn getOrPutGlobalSymbol(
22532267pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {
22542268 const gop = try coff.getOrPutGlobalSymbol(opts);
22552269 if (gop.found_existing) {
2256 // TODO: Need to know if this is an export or extern, add to opts
2270 // TODO: Need to know if this is an export or extern, in order to decide if this is duplicate, add to opts
22572271 }
22582272
22592273 return gop.value_ptr.*;
......@@ -3208,6 +3222,7 @@ fn loadObject(
32083222 .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null,
32093223 .first_si = .null,
32103224 .last_si = .null,
3225 .first_iri = @enumFromInt(coff.input_resolved.items.len),
32113226 };
32123227
32133228 const string_table = string_table: {
......@@ -3431,9 +3446,15 @@ fn loadObject(
34313446 var symbols: std.ArrayList(Symbol.Index) = .empty;
34323447 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);
34333448
3449 var undefs: std.ArrayList(struct {
3450 symbol_i: u32,
3451 name: String,
3452 size: u32,
3453 }) = .empty;
3454
34343455 const first_si = coff.symbols.items.len;
3435 var symbol_ix: u32 = 0;
3436 while (symbol_ix < header.number_of_symbols) {
3456 var symbol_i: u32 = 0;
3457 while (symbol_i < header.number_of_symbols) {
34373458 var symbol: std.coff.Symbol = undefined;
34383459 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));
34393460 if (target_endian != native_endian)
......@@ -3441,13 +3462,13 @@ fn loadObject(
34413462
34423463 defer {
34433464 r.toss(symbol.number_of_aux_symbols * symbol_size);
3444 symbol_ix += symbol.number_of_aux_symbols + 1;
3465 symbol_i += symbol.number_of_aux_symbols + 1;
34453466 }
34463467
34473468 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
34483469 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);
34493470 if (index >= string_table.len)
3450 return diags.failParse(path, "bad string offset for symbol 0x{x}", .{symbol_ix});
3471 return diags.failParse(path, "bad string offset for symbol 0x{x}", .{symbol_i});
34513472 break :name string_table[index..];
34523473 } else &symbol.name, 0);
34533474
......@@ -3486,7 +3507,7 @@ fn loadObject(
34863507 {
34873508 if (symbol.number_of_aux_symbols > 1)
34883509 return diags.failParse(path, "invalid number of aux symbols for section 0x{x}: {d}", .{
3489 symbol_ix,
3510 symbol_i,
34903511 symbol.number_of_aux_symbols,
34913512 });
34923513
......@@ -3533,6 +3554,13 @@ fn loadObject(
35333554 },
35343555 },
35353556 .EXTERNAL => switch (symbol.section_number) {
3557 .UNDEFINED => {
3558 (try undefs.addOne(gpa)).* = .{
3559 .symbol_i = symbol_i,
3560 .name = try coff.getOrPutString(name),
3561 .size = symbol.value,
3562 };
3563 },
35363564 .ABSOLUTE => return diags.failParse(
35373565 path,
35383566 "TODO unhandled external absolute symbol: '{s}'",
......@@ -3546,38 +3574,36 @@ fn loadObject(
35463574 else => |sn| {
35473575 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
35483576 si_slice[0] = global_gop.value_ptr.*;
3549
35503577 const sym = si_slice[0].get(coff);
3551 if (sn != .UNDEFINED) {
3552 if (global_gop.found_existing and sym.ni != .none) {
3553 // TODO: Need corresponding logic later if we try to make a global already defined by an input
3554
3555 var err = try diags.addErrorWithNotes(2);
3556 try err.addMsg("multiple definitions of '{s}'", .{name});
3557 switch (coff.getNode(sym.ni)) {
3558 .input_section => |isi| {
3559 const other_ii = isi.input(coff);
3560 err.addNote("first seen in input '{f}{f}'", .{
3561 other_ii.path(coff).fmtEscapeString(),
3562 fmtArchiveNameString(other_ii.archiveName(coff)),
3563 });
3564 },
3565 .nav, .uav => err.addNote("first seen in module '{s}'", .{
3566 comp.zcu.?.root_mod.fully_qualified_name,
3567 }),
3568 else => unreachable,
3569 }
3570 err.addNote("defined again in input '{f}'", .{path});
3571 return error.LinkFailure;
3578 if (global_gop.found_existing and sym.ni != .none) {
3579 // TODO: Need corresponding logic later if we try to make a global already defined by an input
3580 var err = try diags.addErrorWithNotes(2);
3581 try err.addMsg("multiple definitions of '{s}'", .{name});
3582 switch (coff.getNode(sym.ni)) {
3583 .input_section => |isi| {
3584 const other_ii = isi.input(coff);
3585 err.addNote("first seen in input '{f}{f}'", .{
3586 other_ii.path(coff).fmtEscapeString(),
3587 fmtArchiveNameString(other_ii.archiveName(coff)),
3588 });
3589 },
3590 .nav, .uav => err.addNote("first seen in module '{s}'", .{
3591 comp.zcu.?.root_mod.fully_qualified_name,
3592 }),
3593 else => unreachable,
35723594 }
3595 err.addNote("defined again in input '{f}'", .{path});
3596 return error.LinkFailure;
3597 }
35733598
3574 // TODO: Here if we *were* undefined we want to associate this symbol now with this section for
3575 // the flushMoved iteration
3576
3577 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);
3578 } else if (!global_gop.found_existing) {
3579 sym.value = .{ .size = symbol.value };
3599 if (global_gop.found_existing) {
3600 // `input_resolved` allows visting this symbol in this input section's flushMoved,
3601 // as the previously undefined global created earlier will not be in our
3602 // contiguous first / last range.
3603 (try coff.input_resolved.addOne(gpa)).* = si_slice[0];
35803604 }
3605
3606 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);
35813607 },
35823608 },
35833609 else => {},
......@@ -3589,6 +3615,18 @@ fn loadObject(
35893615 input.last_si = @enumFromInt(coff.symbols.items.len - 1);
35903616 }
35913617
3618 // These are added after all the defined symbols are created so they are not part of the
3619 // input's symbol range, which should only contain symbols that are actually located in this input.
3620 for (undefs.items) |undef| {
3621 // TODO: Avoid redundant hashing by having name be a union on String / []const u8
3622 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = undef.name.toSlice(coff) });
3623 symbols.items[undef.symbol_i] = global_gop.value_ptr.*;
3624 if (!global_gop.found_existing) {
3625 const sym = symbols.items[undef.symbol_i].get(coff);
3626 sym.value = .{ .size = undef.size };
3627 }
3628 }
3629
35923630 const relocation_size = std.coff.Relocation.sizeOf();
35933631 for (sections) |section| {
35943632 if (section.si == .null) continue;
......@@ -4581,10 +4619,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {
45814619 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
45824620 sym.rva = coff.computeNodeRva(sym.ni);
45834621 si.applyLocationRelocs(coff);
4584 } else {
4585
4586 // 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?
4587
45884622 }
45894623}
45904624
......@@ -4702,14 +4736,19 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
47024736 },
47034737 .input_section => |isi| {
47044738 const ii = isi.input(coff);
4705 var si = ii.firstSymbol(coff);
4706 const last_si = ii.lastSymbol(coff);
4739 isi.symbol(coff).flushMoved(coff);
47074740
4708 // TODO: This iteration doesn't visit symbols that were added first
4709 // in the range of another section (as undef).
4741 {
4742 var si = ii.firstSymbol(coff);
4743 const last_si = ii.lastSymbol(coff);
4744 while (@intFromEnum(si) <= @intFromEnum(last_si)) : (si = si.next()) {
4745 if (si.get(coff).ni != ni) continue;
4746 si.flushMoved(coff);
4747 }
4748 }
47104749
4711 while (@intFromEnum(si) <= @intFromEnum(last_si)) : (si = si.next()) {
4712 if (si.get(coff).ni != ni) continue;
4750 for (coff.input_resolved.items[@intFromEnum(ii.firstResolvedGlobal(coff))..]) |si| {
4751 if (si.get(coff).ni != ni) break;
47134752 si.flushMoved(coff);
47144753 }
47154754 },