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,...@@ -31,12 +31,8 @@ long_names_table: LongNamesTable,
31import_table: ImportTable,31import_table: ImportTable,
32export_table: ExportTable,32export_table: ExportTable,
33symbol_table: SymbolTable,33symbol_table: SymbolTable,
34inputs: std.ArrayList(struct {34inputs: std.ArrayList(Input),
35 path: std.Build.Cache.Path,35input_resolved: std.ArrayList(Symbol.Index),
36 archive_name: ?[]const u8,
37 first_si: Symbol.Index,
38 last_si: Symbol.Index,
39}),
40input_sections: std.ArrayList(struct {36input_sections: std.ArrayList(struct {
41 ii: Node.InputIndex,37 ii: Node.InputIndex,
42 si: Symbol.Index,38 si: Symbol.Index,
...@@ -298,6 +294,10 @@ pub const Node = union(enum) {...@@ -298,6 +294,10 @@ pub const Node = union(enum) {
298 pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {294 pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index {
299 return coff.inputs.items[@intFromEnum(ii)].last_si;295 return coff.inputs.items[@intFromEnum(ii)].last_si;
300 }296 }
297
298 pub fn firstResolvedGlobal(ii: InputIndex, coff: *const Coff) Input.ResolvedIndex {
299 return coff.inputs.items[@intFromEnum(ii)].first_iri;
300 }
301 };301 };
302302
303 pub const InputSectionIndex = enum(u32) {303 pub const InputSectionIndex = enum(u32) {
...@@ -384,6 +384,18 @@ pub const Node = union(enum) {...@@ -384,6 +384,18 @@ pub const Node = union(enum) {
384 }384 }
385};385};
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
387pub const Member = struct {399pub const Member = struct {
388 kind: Kind,400 kind: Kind,
389 header_ni: MappedFile.Node.Index,401 header_ni: MappedFile.Node.Index,
...@@ -1270,6 +1282,7 @@ fn create(...@@ -1270,6 +1282,7 @@ fn create(
1270 .pending_shrink = false,1282 .pending_shrink = false,
1271 },1283 },
1272 .inputs = .empty,1284 .inputs = .empty,
1285 .input_resolved = .empty,
1273 .input_sections = .empty,1286 .input_sections = .empty,
1274 .input_section_pending_index = 0,1287 .input_section_pending_index = 0,
1275 .strings = .empty,1288 .strings = .empty,
...@@ -1330,6 +1343,7 @@ pub fn deinit(coff: *Coff) void {...@@ -1330,6 +1343,7 @@ pub fn deinit(coff: *Coff) void {
1330 coff.symbol_table.strings.deinit(gpa);1343 coff.symbol_table.strings.deinit(gpa);
1331 coff.symbol_table.pending.deinit(gpa);1344 coff.symbol_table.pending.deinit(gpa);
1332 coff.inputs.deinit(gpa);1345 coff.inputs.deinit(gpa);
1346 coff.input_resolved.deinit(gpa);
1333 coff.input_sections.deinit(gpa);1347 coff.input_sections.deinit(gpa);
1334 coff.strings.deinit(gpa);1348 coff.strings.deinit(gpa);
1335 coff.string_bytes.deinit(gpa);1349 coff.string_bytes.deinit(gpa);
...@@ -2253,7 +2267,7 @@ fn getOrPutGlobalSymbol(...@@ -2253,7 +2267,7 @@ fn getOrPutGlobalSymbol(
2253pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {2267pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index {
2254 const gop = try coff.getOrPutGlobalSymbol(opts);2268 const gop = try coff.getOrPutGlobalSymbol(opts);
2255 if (gop.found_existing) {2269 if (gop.found_existing) {
2256 // TODO: Need to know if this is an export or extern, add to opts2270 // TODO: Need to know if this is an export or extern, in order to decide if this is duplicate, add to opts
2257 }2271 }
22582272
2259 return gop.value_ptr.*;2273 return gop.value_ptr.*;
...@@ -3208,6 +3222,7 @@ fn loadObject(...@@ -3208,6 +3222,7 @@ fn loadObject(
3208 .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null,3222 .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null,
3209 .first_si = .null,3223 .first_si = .null,
3210 .last_si = .null,3224 .last_si = .null,
3225 .first_iri = @enumFromInt(coff.input_resolved.items.len),
3211 };3226 };
32123227
3213 const string_table = string_table: {3228 const string_table = string_table: {
...@@ -3431,9 +3446,15 @@ fn loadObject(...@@ -3431,9 +3446,15 @@ fn loadObject(
3431 var symbols: std.ArrayList(Symbol.Index) = .empty;3446 var symbols: std.ArrayList(Symbol.Index) = .empty;
3432 try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols);3447 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
3434 const first_si = coff.symbols.items.len;3455 const first_si = coff.symbols.items.len;
3435 var symbol_ix: u32 = 0;3456 var symbol_i: u32 = 0;
3436 while (symbol_ix < header.number_of_symbols) {3457 while (symbol_i < header.number_of_symbols) {
3437 var symbol: std.coff.Symbol = undefined;3458 var symbol: std.coff.Symbol = undefined;
3438 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));3459 @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size));
3439 if (target_endian != native_endian)3460 if (target_endian != native_endian)
...@@ -3441,13 +3462,13 @@ fn loadObject(...@@ -3441,13 +3462,13 @@ fn loadObject(
34413462
3442 defer {3463 defer {
3443 r.toss(symbol.number_of_aux_symbols * symbol_size);3464 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;
3445 }3466 }
34463467
3447 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {3468 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
3448 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);3469 const index = std.mem.readInt(u32, symbol.name[4..], target_endian);
3449 if (index >= string_table.len)3470 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});
3451 break :name string_table[index..];3472 break :name string_table[index..];
3452 } else &symbol.name, 0);3473 } else &symbol.name, 0);
34533474
...@@ -3486,7 +3507,7 @@ fn loadObject(...@@ -3486,7 +3507,7 @@ fn loadObject(
3486 {3507 {
3487 if (symbol.number_of_aux_symbols > 1)3508 if (symbol.number_of_aux_symbols > 1)
3488 return diags.failParse(path, "invalid number of aux symbols for section 0x{x}: {d}", .{3509 return diags.failParse(path, "invalid number of aux symbols for section 0x{x}: {d}", .{
3489 symbol_ix,3510 symbol_i,
3490 symbol.number_of_aux_symbols,3511 symbol.number_of_aux_symbols,
3491 });3512 });
34923513
...@@ -3533,6 +3554,13 @@ fn loadObject(...@@ -3533,6 +3554,13 @@ fn loadObject(
3533 },3554 },
3534 },3555 },
3535 .EXTERNAL => switch (symbol.section_number) {3556 .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 },
3536 .ABSOLUTE => return diags.failParse(3564 .ABSOLUTE => return diags.failParse(
3537 path,3565 path,
3538 "TODO unhandled external absolute symbol: '{s}'",3566 "TODO unhandled external absolute symbol: '{s}'",
...@@ -3546,38 +3574,36 @@ fn loadObject(...@@ -3546,38 +3574,36 @@ fn loadObject(
3546 else => |sn| {3574 else => |sn| {
3547 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });3575 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
3548 si_slice[0] = global_gop.value_ptr.*;3576 si_slice[0] = global_gop.value_ptr.*;
3549
3550 const sym = si_slice[0].get(coff);3577 const sym = si_slice[0].get(coff);
3551 if (sn != .UNDEFINED) {3578 if (global_gop.found_existing and sym.ni != .none) {
3552 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
3553 // TODO: Need corresponding logic later if we try to make a global already defined by an input3580 var err = try diags.addErrorWithNotes(2);
35543581 try err.addMsg("multiple definitions of '{s}'", .{name});
3555 var err = try diags.addErrorWithNotes(2);3582 switch (coff.getNode(sym.ni)) {
3556 try err.addMsg("multiple definitions of '{s}'", .{name});3583 .input_section => |isi| {
3557 switch (coff.getNode(sym.ni)) {3584 const other_ii = isi.input(coff);
3558 .input_section => |isi| {3585 err.addNote("first seen in input '{f}{f}'", .{
3559 const other_ii = isi.input(coff);3586 other_ii.path(coff).fmtEscapeString(),
3560 err.addNote("first seen in input '{f}{f}'", .{3587 fmtArchiveNameString(other_ii.archiveName(coff)),
3561 other_ii.path(coff).fmtEscapeString(),3588 });
3562 fmtArchiveNameString(other_ii.archiveName(coff)),3589 },
3563 });3590 .nav, .uav => err.addNote("first seen in module '{s}'", .{
3564 },3591 comp.zcu.?.root_mod.fully_qualified_name,
3565 .nav, .uav => err.addNote("first seen in module '{s}'", .{3592 }),
3566 comp.zcu.?.root_mod.fully_qualified_name,3593 else => unreachable,
3567 }),
3568 else => unreachable,
3569 }
3570 err.addNote("defined again in input '{f}'", .{path});
3571 return error.LinkFailure;
3572 }3594 }
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 for3599 if (global_gop.found_existing) {
3575 // the flushMoved iteration3600 // `input_resolved` allows visting this symbol in this input section's flushMoved,
35763601 // as the previously undefined global created earlier will not be in our
3577 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);3602 // contiguous first / last range.
3578 } else if (!global_gop.found_existing) {3603 (try coff.input_resolved.addOne(gpa)).* = si_slice[0];
3579 sym.value = .{ .size = symbol.value };
3580 }3604 }
3605
3606 coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value);
3581 },3607 },
3582 },3608 },
3583 else => {},3609 else => {},
...@@ -3589,6 +3615,18 @@ fn loadObject(...@@ -3589,6 +3615,18 @@ fn loadObject(
3589 input.last_si = @enumFromInt(coff.symbols.items.len - 1);3615 input.last_si = @enumFromInt(coff.symbols.items.len - 1);
3590 }3616 }
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
3592 const relocation_size = std.coff.Relocation.sizeOf();3630 const relocation_size = std.coff.Relocation.sizeOf();
3593 for (sections) |section| {3631 for (sections) |section| {
3594 if (section.si == .null) continue;3632 if (section.si == .null) continue;
...@@ -4581,10 +4619,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {...@@ -4581,10 +4619,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void {
4581 coff.nodes.appendAssumeCapacity(.{ .global = gmi });4619 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
4582 sym.rva = coff.computeNodeRva(sym.ni);4620 sym.rva = coff.computeNodeRva(sym.ni);
4583 si.applyLocationRelocs(coff);4621 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
4588 }4622 }
4589}4623}
45904624
...@@ -4702,14 +4736,19 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -4702,14 +4736,19 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
4702 },4736 },
4703 .input_section => |isi| {4737 .input_section => |isi| {
4704 const ii = isi.input(coff);4738 const ii = isi.input(coff);
4705 var si = ii.firstSymbol(coff);4739 isi.symbol(coff).flushMoved(coff);
4706 const last_si = ii.lastSymbol(coff);
47074740
4708 // TODO: This iteration doesn't visit symbols that were added first4741 {
4709 // in the range of another section (as undef).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()) {4750 for (coff.input_resolved.items[@intFromEnum(ii.firstResolvedGlobal(coff))..]) |si| {
4712 if (si.get(coff).ni != ni) continue;4751 if (si.get(coff).ni != ni) break;
4713 si.flushMoved(coff);4752 si.flushMoved(coff);
4714 }4753 }
4715 },4754 },