| ... | @@ -31,12 +31,8 @@ long_names_table: LongNamesTable, | ... | @@ -31,12 +31,8 @@ long_names_table: LongNamesTable, |
| 31 | import_table: ImportTable, | 31 | import_table: ImportTable, |
| 32 | export_table: ExportTable, | 32 | export_table: ExportTable, |
| 33 | symbol_table: SymbolTable, | 33 | symbol_table: SymbolTable, |
| 34 | inputs: std.ArrayList(struct { | 34 | inputs: std.ArrayList(Input), |
| 35 | path: std.Build.Cache.Path, | 35 | input_resolved: std.ArrayList(Symbol.Index), |
| 36 | archive_name: ?[]const u8, | | |
| 37 | first_si: Symbol.Index, | | |
| 38 | last_si: Symbol.Index, | | |
| 39 | }), | | |
| 40 | input_sections: std.ArrayList(struct { | 36 | input_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 | }; |
| 302 | | 302 | |
| 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 | }; |
| 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 | pub const Member = struct { | 399 | pub 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( |
| 2253 | pub fn globalSymbol(coff: *Coff, opts: GlobalOptions) !Symbol.Index { | 2267 | pub 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 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 | 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 | }; |
| 3212 | | 3227 | |
| 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); |
| 3433 | | 3448 | |
| | 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( |
| 3441 | | 3462 | |
| 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 | } |
| 3446 | | 3467 | |
| 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); |
| 3453 | | 3474 | |
| ... | @@ -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 | }); |
| 3492 | | 3513 | |
| ... | @@ -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 input | 3580 | var err = try diags.addErrorWithNotes(2); |
| 3554 | | 3581 | 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 | } |
| 3573 | | 3598 | |
| 3574 | // TODO: Here if we *were* undefined we want to associate this symbol now with this section for | 3599 | if (global_gop.found_existing) { |
| 3575 | // the flushMoved iteration | 3600 | // `input_resolved` allows visting this symbol in this input section's flushMoved, |
| 3576 | | 3601 | // 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 | } |
| 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 | 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 | } |
| 4590 | | 4624 | |
| ... | @@ -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); | | |
| 4707 | | 4740 | |
| 4708 | // TODO: This iteration doesn't visit symbols that were added first | 4741 | { |
| 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 | } |
| 4710 | | 4749 | |
| 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 | }, |