| ... | ... | @@ -31,12 +31,8 @@ long_names_table: LongNamesTable, |
| 31 | 31 | import_table: ImportTable, |
| 32 | 32 | export_table: ExportTable, |
| 33 | 33 | symbol_table: SymbolTable, |
| 34 | | inputs: 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 | | }), |
| 34 | inputs: std.ArrayList(Input), |
| 35 | input_resolved: std.ArrayList(Symbol.Index), |
| 40 | 36 | input_sections: std.ArrayList(struct { |
| 41 | 37 | ii: Node.InputIndex, |
| 42 | 38 | si: Symbol.Index, |
| ... | ... | @@ -298,6 +294,10 @@ pub const Node = union(enum) { |
| 298 | 294 | pub fn lastSymbol(ii: InputIndex, coff: *const Coff) Symbol.Index { |
| 299 | 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 | }; |
| 302 | 302 | |
| 303 | 303 | pub const InputSectionIndex = enum(u32) { |
| ... | ... | @@ -384,6 +384,18 @@ pub const Node = union(enum) { |
| 384 | 384 | } |
| 385 | 385 | }; |
| 386 | 386 | |
| 387 | pub 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 | |
| 387 | 399 | pub const Member = struct { |
| 388 | 400 | kind: Kind, |
| 389 | 401 | header_ni: MappedFile.Node.Index, |
| ... | ... | @@ -1270,6 +1282,7 @@ fn create( |
| 1270 | 1282 | .pending_shrink = false, |
| 1271 | 1283 | }, |
| 1272 | 1284 | .inputs = .empty, |
| 1285 | .input_resolved = .empty, |
| 1273 | 1286 | .input_sections = .empty, |
| 1274 | 1287 | .input_section_pending_index = 0, |
| 1275 | 1288 | .strings = .empty, |
| ... | ... | @@ -1330,6 +1343,7 @@ pub fn deinit(coff: *Coff) void { |
| 1330 | 1343 | coff.symbol_table.strings.deinit(gpa); |
| 1331 | 1344 | coff.symbol_table.pending.deinit(gpa); |
| 1332 | 1345 | coff.inputs.deinit(gpa); |
| 1346 | coff.input_resolved.deinit(gpa); |
| 1333 | 1347 | coff.input_sections.deinit(gpa); |
| 1334 | 1348 | coff.strings.deinit(gpa); |
| 1335 | 1349 | coff.string_bytes.deinit(gpa); |
| ... | ... | @@ -2253,7 +2267,7 @@ fn getOrPutGlobalSymbol( |
| 2253 | 2267 | pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index { |
| 2254 | 2268 | const gop = try coff.getOrPutGlobalSymbol(opts); |
| 2255 | 2269 | 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 |
| 2257 | 2271 | } |
| 2258 | 2272 | |
| 2259 | 2273 | return gop.value_ptr.*; |
| ... | ... | @@ -3208,6 +3222,7 @@ fn loadObject( |
| 3208 | 3222 | .archive_name = if (archive_name) |m| try gpa.dupe(u8, m) else null, |
| 3209 | 3223 | .first_si = .null, |
| 3210 | 3224 | .last_si = .null, |
| 3225 | .first_iri = @enumFromInt(coff.input_resolved.items.len), |
| 3211 | 3226 | }; |
| 3212 | 3227 | |
| 3213 | 3228 | const string_table = string_table: { |
| ... | ... | @@ -3431,9 +3446,15 @@ fn loadObject( |
| 3431 | 3446 | var symbols: std.ArrayList(Symbol.Index) = .empty; |
| 3432 | 3447 | try symbols.ensureUnusedCapacity(gpa, header.number_of_symbols); |
| 3433 | 3448 | |
| 3449 | var undefs: std.ArrayList(struct { |
| 3450 | symbol_i: u32, |
| 3451 | name: String, |
| 3452 | size: u32, |
| 3453 | }) = .empty; |
| 3454 | |
| 3434 | 3455 | 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) { |
| 3437 | 3458 | var symbol: std.coff.Symbol = undefined; |
| 3438 | 3459 | @memcpy(std.mem.asBytes(&symbol)[0..symbol_size], try r.take(symbol_size)); |
| 3439 | 3460 | if (target_endian != native_endian) |
| ... | ... | @@ -3441,13 +3462,13 @@ fn loadObject( |
| 3441 | 3462 | |
| 3442 | 3463 | defer { |
| 3443 | 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 | } |
| 3446 | 3467 | |
| 3447 | 3468 | const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: { |
| 3448 | 3469 | const index = std.mem.readInt(u32, symbol.name[4..], target_endian); |
| 3449 | 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 | 3472 | break :name string_table[index..]; |
| 3452 | 3473 | } else &symbol.name, 0); |
| 3453 | 3474 | |
| ... | ... | @@ -3486,7 +3507,7 @@ fn loadObject( |
| 3486 | 3507 | { |
| 3487 | 3508 | if (symbol.number_of_aux_symbols > 1) |
| 3488 | 3509 | return diags.failParse(path, "invalid number of aux symbols for section 0x{x}: {d}", .{ |
| 3489 | | symbol_ix, |
| 3510 | symbol_i, |
| 3490 | 3511 | symbol.number_of_aux_symbols, |
| 3491 | 3512 | }); |
| 3492 | 3513 | |
| ... | ... | @@ -3533,6 +3554,13 @@ fn loadObject( |
| 3533 | 3554 | }, |
| 3534 | 3555 | }, |
| 3535 | 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 | 3564 | .ABSOLUTE => return diags.failParse( |
| 3537 | 3565 | path, |
| 3538 | 3566 | "TODO unhandled external absolute symbol: '{s}'", |
| ... | ... | @@ -3546,38 +3574,36 @@ fn loadObject( |
| 3546 | 3574 | else => |sn| { |
| 3547 | 3575 | const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name }); |
| 3548 | 3576 | si_slice[0] = global_gop.value_ptr.*; |
| 3549 | | |
| 3550 | 3577 | 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, |
| 3572 | 3594 | } |
| 3595 | err.addNote("defined again in input '{f}'", .{path}); |
| 3596 | return error.LinkFailure; |
| 3597 | } |
| 3573 | 3598 | |
| 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]; |
| 3580 | 3604 | } |
| 3605 | |
| 3606 | coff.initInputSectionSymbol(sym, sections[@intCast(@intFromEnum(sn) - 1)].si, symbol.value); |
| 3581 | 3607 | }, |
| 3582 | 3608 | }, |
| 3583 | 3609 | else => {}, |
| ... | ... | @@ -3589,6 +3615,18 @@ fn loadObject( |
| 3589 | 3615 | input.last_si = @enumFromInt(coff.symbols.items.len - 1); |
| 3590 | 3616 | } |
| 3591 | 3617 | |
| 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 | 3630 | const relocation_size = std.coff.Relocation.sizeOf(); |
| 3593 | 3631 | for (sections) |section| { |
| 3594 | 3632 | if (section.si == .null) continue; |
| ... | ... | @@ -4581,10 +4619,6 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void { |
| 4581 | 4619 | coff.nodes.appendAssumeCapacity(.{ .global = gmi }); |
| 4582 | 4620 | sym.rva = coff.computeNodeRva(sym.ni); |
| 4583 | 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 | } |
| 4590 | 4624 | |
| ... | ... | @@ -4702,14 +4736,19 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void { |
| 4702 | 4736 | }, |
| 4703 | 4737 | .input_section => |isi| { |
| 4704 | 4738 | const ii = isi.input(coff); |
| 4705 | | var si = ii.firstSymbol(coff); |
| 4706 | | const last_si = ii.lastSymbol(coff); |
| 4739 | isi.symbol(coff).flushMoved(coff); |
| 4707 | 4740 | |
| 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 | } |
| 4710 | 4749 | |
| 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; |
| 4713 | 4752 | si.flushMoved(coff); |
| 4714 | 4753 | } |
| 4715 | 4754 | }, |