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:41-04:00
log14a7131c4f97895d6ab600b88940b6afb6d6ae4e
treef587546f9ccc89110981fcefd393568adeea7501
parent9bf95b438196f8eef28a37e35976142aea152ae9

Coff: More input loading progress

- Map sections by name, so we can create them on-demand when linking non-images - Verify that parent section attributes match when adding a pseudo / objection section - Support section names with len > 8 in non-images

1 files changed, 278 insertions(+), 68 deletions(-)

src/link/Coff.zig+278-68
......@@ -38,7 +38,7 @@ strings: std.HashMapUnmanaged(
3838 std.hash_map.default_max_load_percentage,
3939),
4040string_bytes: std.ArrayList(u8),
41section_table: std.ArrayList(Section),
41section_table: std.AutoArrayHashMapUnmanaged(String, Section),
4242tls_si: Symbol.Index,
4343pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),
4444object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
......@@ -395,14 +395,40 @@ pub const Member = struct {
395395 name_slice[name.len] = 0;
396396
397397 gop.value_ptr.* = .{
398 .index = old_size,
398 .offset = old_size,
399399 .len = name.len,
400400 };
401401 }
402402
403 field[0] = '/';
404 storeHeaderDecimalStr(field[1..], gop.value_ptr.index);
403 break :offset gop.value_ptr.offset;
404 } else null;
405
406 const header = member.headerPtr(coff);
407 if (opt_name_offset) |name_offset| {
408 header.name[0] = '/';
409 storeHeaderDecimalStr(header.name[1..], name_offset);
410 } else {
411 @memcpy(header.name[0..name.len], name);
412 header.name[name.len] = '/';
413 const padding = max_name_len - name.len - 1;
414 @memset(header.name[max_name_len - padding ..], ' ');
405415 }
416
417 storeHeaderDecimalStr(&header.date, timestamp);
418
419 // Matching the Microsoft behaviour of emitting blanks for these fields
420 header.user_id = @splat(' ');
421 header.group_id = @splat(' ');
422
423 // file_mode is actually octal, but we only ever write 0 to it
424 storeHeaderDecimalStr(&header.file_mode, 0);
425 if (!member.content_ni.hasResized(&coff.mf))
426 storeHeaderDecimalStr(
427 &header.size,
428 member.content_ni.location(&coff.mf).resolve(&coff.mf)[1],
429 );
430
431 @memcpy(&header.end_of_header, "`\n");
406432 }
407433
408434 pub fn storeHeaderDecimalStr(field_ptr: anytype, value: u64) void {
......@@ -422,7 +448,7 @@ pub const LongNamesTable = struct {
422448 entries: std.AutoArrayHashMapUnmanaged(void, Entry),
423449
424450 pub const Entry = struct {
425 index: u64,
451 offset: u64,
426452 len: u64,
427453 };
428454
......@@ -433,7 +459,7 @@ pub const LongNamesTable = struct {
433459 assert(adapter.coff.isArchive()); // TODO: move to helper that uses this
434460 const longnames_slice = Node.known.longnames_member.slice(&adapter.coff.mf);
435461 const rhs = adapter.coff.long_names_table.entries.values()[rhs_index];
436 return std.mem.eql(u8, longnames_slice[rhs.index..][0..rhs.len], lhs_key);
462 return std.mem.eql(u8, longnames_slice[rhs.offset..][0..rhs.len], lhs_key);
437463 }
438464
439465 pub fn hash(_: Adapter, key: []const u8) u32 {
......@@ -463,6 +489,19 @@ pub const SymbolTable = struct {
463489 pub const SymbolName = union(enum) {
464490 short: []const u8,
465491 long: StringIndex,
492
493 pub fn store(name: SymbolName, coff: *const Coff, field: *[8]u8) void {
494 switch (name) {
495 .short => |s| {
496 @memcpy(field[0..s.len], s);
497 @memset(field[s.len..], 0);
498 },
499 .long => |l| {
500 @memset(field[0..4], 0);
501 std.mem.writePackedInt(u32, field[4..], 0, @intFromEnum(l), coff.targetEndian());
502 },
503 }
504 }
466505 };
467506
468507 // Symbol.Index does not map 1:1 with SymbolTable.Index:
......@@ -666,7 +705,7 @@ pub const Symbol = struct {
666705 }
667706
668707 pub fn section(sn: SectionNumber, coff: *const Coff) *Section {
669 return &coff.section_table.items[sn.toIndex()];
708 return &coff.section_table.values()[sn.toIndex()];
670709 }
671710
672711 pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
......@@ -693,6 +732,13 @@ pub const Symbol = struct {
693732 return ni;
694733 }
695734
735 pub fn knownString(si: Symbol.Index) String.Optional {
736 return switch (si) {
737 .null, _ => .none,
738 inline else => |tag| @field(String.Optional, "." ++ @tagName(tag)),
739 };
740 }
741
696742 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
697743 const sym = si.get(coff);
698744 sym.rva = coff.computeNodeRva(sym.ni);
......@@ -1522,16 +1568,16 @@ fn initHeaders(
15221568 .sti = .none,
15231569 .gmi = .none,
15241570 };
1525 assert(try coff.addSection(".data", .{
1571 assert(try coff.addSection(.@".data", .{
15261572 .CNT_INITIALIZED_DATA = true,
15271573 .MEM_READ = true,
15281574 .MEM_WRITE = true,
15291575 }) == .data);
1530 assert(try coff.addSection(".rdata", .{
1576 assert(try coff.addSection(.@".rdata", .{
15311577 .CNT_INITIALIZED_DATA = true,
15321578 .MEM_READ = true,
15331579 }) == .rdata);
1534 assert(try coff.addSection(".text", .{
1580 assert(try coff.addSection(.@".text", .{
15351581 .CNT_CODE = true,
15361582 .MEM_EXECUTE = true,
15371583 .MEM_READ = true,
......@@ -1625,7 +1671,7 @@ fn initHeaders(
16251671
16261672 if (comp.config.any_non_single_threaded) {
16271673 if (!is_image)
1628 coff.tls_si = try coff.addSection(".tls$", .{
1674 coff.tls_si = try coff.addSection(.@".tls$", .{
16291675 .CNT_INITIALIZED_DATA = true,
16301676 .MEM_READ = true,
16311677 .MEM_WRITE = true,
......@@ -1637,7 +1683,7 @@ fn initHeaders(
16371683 _ = try coff.objectSectionMapIndex(
16381684 .@".tls$",
16391685 coff.mf.flags.block_size,
1640 .{ .read = true, .write = !is_image, .tls = true },
1686 .{ .read = true, .write = !is_image },
16411687 );
16421688 }
16431689}
......@@ -1877,7 +1923,7 @@ pub fn dataDirectoryPtr(
18771923
18781924pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
18791925 return @ptrCast(@alignCast(
1880 Node.known.section_table.slice(&coff.mf)[0 .. coff.section_table.items.len * @sizeOf(std.coff.SectionHeader)],
1926 Node.known.section_table.slice(&coff.mf)[0 .. coff.section_table.count() * @sizeOf(std.coff.SectionHeader)],
18811927 ));
18821928}
18831929
......@@ -1958,6 +2004,30 @@ fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
19582004 return (try coff.getOrPutString(string orelse return .none)).toOptional();
19592005}
19602006
2007/// If the name does not fit in the symbol header, adds it to the symbol table string table.
2008/// If the caller knows this name already has a String associated with it, they can avoid
2009/// a redundant call to `getOrPutString` by specifying `opt_string`.
2010/// The lifetime of the return value matches that of `name`.
2011fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName {
2012 assert(!coff.isImage());
2013 const gpa = coff.base.comp.gpa;
2014 return if (name.len > 8) name: {
2015 const string = opt_string orelse try coff.getOrPutString(name);
2016 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
2017 if (!string_gop.found_existing) {
2018 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
2019 string_gop.value_ptr.* = @enumFromInt(string_index);
2020
2021 try coff.symbol_table.strings_ni.resize(&coff.mf, gpa, string_index + name.len + 1);
2022 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
2023 @memcpy(slice[string_index..][0..name.len], name);
2024 slice[string_index + name.len] = 0;
2025 }
2026
2027 break :name .{ .long = string_gop.value_ptr.* };
2028 } else .{ .short = name };
2029}
2030
19612031/// `len` does not include null terminators
19622032fn ensureUnusedStringCapacity(coff: *Coff, len: usize) !void {
19632033 const gpa = coff.base.comp.gpa;
......@@ -2035,7 +2105,7 @@ fn navSection(
20352105 const ip = &zcu.intern_pool;
20362106 const default: String, const attributes: ObjectSectionAttributes =
20372107 if (nav_resolved.@"threadlocal" and coff.base.comp.config.any_non_single_threaded) .{
2038 .@".tls$", .{ .read = true, .write = true, .tls = true },
2108 .@".tls$", .{ .read = true, .write = !coff.isImage() },
20392109 } else if (ip.isFunctionType(nav_resolved.type)) .{
20402110 .@".text", .{ .read = true, .execute = true },
20412111 } else if (nav_resolved.@"const") .{
......@@ -2311,12 +2381,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23112381
23122382 const entry = coff.symbolTableEntryPtr(sym.sti) orelse entry: {
23132383 var buf: [15]u8 = undefined;
2314 const name_slice, const opt_name_string, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
2384 const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
23152385 if (sym.gmi != .none) blk: {
23162386 const gn = sym.gmi.globalName(coff);
23172387 break :blk .{
2318 gn.name.toSlice(coff),
2319 gn.name,
2388 try coff.getOrPutSymbolName(gn.name.toSlice(coff), gn.name),
23202389 0,
23212390 if (Symbol.Index.text.get(coff).section_number == sym.section_number)
23222391 .FUNCTION
......@@ -2325,8 +2394,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23252394 };
23262395 } else blk: switch (coff.getNode(sym.ni)) {
23272396 .image_section => .{
2328 &sym.section_number.header(coff).name,
2329 null,
2397 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),
23302398 1,
23312399 .NULL,
23322400 },
......@@ -2335,8 +2403,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23352403 const ip = &zcu.intern_pool;
23362404 const nav = ip.getNav(nmi.navIndex(coff));
23372405 break :blk .{
2338 nav.fqn.toSlice(ip),
2339 null,
2406 try coff.getOrPutSymbolName(nav.fqn.toSlice(ip), null),
23402407 0,
23412408 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,
23422409 };
......@@ -2344,7 +2411,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23442411 .uav => |umi| {
23452412 var w = Io.Writer.fixed(&buf);
23462413 w.print("__anon_{x}", .{umi.uavValue(coff)}) catch unreachable;
2347 break :blk .{ w.buffered(), null, 0, .NULL };
2414 break :blk .{
2415 try coff.getOrPutSymbolName(w.buffered(), null),
2416 0,
2417 .NULL,
2418 };
23482419 },
23492420 inline .lazy_code, .lazy_const_data => |mi, tag| {
23502421 const lazy_sym = mi.lazySymbol(coff);
......@@ -2355,7 +2426,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23552426 defer gpa.free(name);
23562427
23572428 const string = try coff.getOrPutString(name);
2358 break :blk .{ string.toSlice(coff), string, 0, if (tag == .lazy_code) .FUNCTION else .NULL };
2429 break :blk .{
2430 try coff.getOrPutSymbolName(string.toSlice(coff), string),
2431 0,
2432 if (tag == .lazy_code) .FUNCTION else .NULL,
2433 };
23592434 },
23602435 else => {
23612436 log.err("TODO implement symbol table init for {s} ({d})", .{ @tagName(coff.getNode(sym.ni)), si });
......@@ -2363,22 +2438,6 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23632438 },
23642439 };
23652440
2366 const symbol_name: SymbolTable.SymbolName = if (name_slice.len > 8) name: {
2367 const string = opt_name_string orelse try coff.getOrPutString(name_slice);
2368 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
2369 if (!string_gop.found_existing) {
2370 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
2371 string_gop.value_ptr.* = @enumFromInt(string_index);
2372
2373 try coff.symbol_table.strings_ni.resize(&coff.mf, gpa, string_index + name_slice.len + 1);
2374 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
2375 @memcpy(slice[string_index..][0..name_slice.len], name_slice);
2376 slice[string_index + name_slice.len] = 0;
2377 }
2378
2379 break :name .{ .long = string_gop.value_ptr.* };
2380 } else .{ .short = name_slice };
2381
23822441 const old_num_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
23832442 const new_num_symbols = old_num_symbols + 1 + num_aux_symbols;
23842443
......@@ -2389,17 +2448,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23892448 si.flushSymbolTableIndex(coff);
23902449
23912450 const entry = coff.symbolTableEntryPtr(sym.sti).?;
2392 switch (symbol_name) {
2393 .short => |s| {
2394 @memcpy(entry.name[0..s.len], s);
2395 @memset(entry.name[s.len..], 0);
2396 },
2397 .long => |l| {
2398 @memset(entry.name[0..4], 0);
2399 const offset_ptr: *align(2) u32 = @ptrCast(entry.name[4..]);
2400 coff.targetStore(offset_ptr, @intFromEnum(l));
2401 },
2402 }
2451 symbol_name.store(coff, &entry.name);
24032452
24042453 entry.section_number = @enumFromInt(@intFromEnum(sym.section_number));
24052454 entry.type = .{
......@@ -2431,7 +2480,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
24312480 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
24322481}
24332482
2434fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
2483fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
24352484 assert(coff.base.comp.zcu != null);
24362485
24372486 const gpa = coff.base.comp.gpa;
......@@ -2457,7 +2506,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
24572506 });
24582507
24592508 const si = coff.addSymbolAssumeCapacity();
2460 coff.section_table.appendAssumeCapacity(.{
2509 coff.section_table.putAssumeCapacity(name, .{
24612510 .si = si,
24622511 .relocation_table_ni = .none,
24632512 });
......@@ -2468,7 +2517,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
24682517 const virtual_size = coff.optionalHeaderField(.section_alignment);
24692518 const rva: u32 = switch (section_index) {
24702519 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
2471 else => coff.section_table.items[section_index - 1].si.get(coff).rva +
2520 else => coff.section_table.values()[section_index - 1].si.get(coff).rva +
24722521 coff.targetLoad(&section_table[section_index - 1].virtual_size),
24732522 };
24742523
......@@ -2494,12 +2543,13 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
24942543 .number_of_linenumbers = 0,
24952544 .flags = flags,
24962545 };
2497 @memcpy(section.name[0..name.len], name);
2498 @memset(section.name[name.len..], 0);
24992546 if (coff.targetEndian() != native_endian)
25002547 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
25012548
2549 const name_slice = name.toSlice(coff);
25022550 if (coff.isImage()) {
2551 @memcpy(section.name[0..name_slice.len], name_slice);
2552 @memset(section.name[name_slice.len..], 0);
25032553 switch (coff.optionalHeaderPtr()) {
25042554 inline else => |optional_header| coff.targetStore(
25052555 &optional_header.size_of_image,
......@@ -2507,6 +2557,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
25072557 ),
25082558 }
25092559 } else {
2560 (try coff.getOrPutSymbolName(name_slice, name)).store(coff, &section.name);
25102561 try coff.pendingSymbolTableEntry(si);
25112562 }
25122563
......@@ -2522,7 +2573,34 @@ const ObjectSectionAttributes = packed struct {
25222573 nocache: bool = false,
25232574 discard: bool = false,
25242575 remove: bool = false,
2525 tls: bool = false,
2576
2577 // TODO: Include init / not init flags?
2578
2579 pub fn fromFlags(flags: std.coff.SectionHeader.Flags) ObjectSectionAttributes {
2580 return .{
2581 .read = flags.MEM_READ,
2582 .write = flags.MEM_WRITE,
2583 .execute = flags.MEM_EXECUTE,
2584 .shared = flags.MEM_SHARED,
2585 .nopage = flags.MEM_NOT_PAGED,
2586 .nocache = flags.MEM_NOT_CACHED,
2587 .discard = flags.MEM_DISCARDABLE,
2588 .remove = flags.LNK_REMOVE,
2589 };
2590 }
2591
2592 pub fn asFlags(attr: ObjectSectionAttributes) std.coff.SectionHeader.Flags {
2593 return .{
2594 .MEM_READ = attr.read,
2595 .MEM_WRITE = attr.write,
2596 .MEM_EXECUTE = attr.execute,
2597 .MEM_SHARED = attr.shared,
2598 .MEM_NOT_PAGED = attr.nopage,
2599 .MEM_NOT_CACHED = attr.nocache,
2600 .MEM_DISCARDABLE = attr.discard,
2601 .LNK_REMOVE = attr.remove,
2602 };
2603 }
25262604};
25272605
25282606fn pseudoSectionMapIndex(
......@@ -2535,14 +2613,25 @@ fn pseudoSectionMapIndex(
25352613 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);
25362614 const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);
25372615 if (!pseudo_section_gop.found_existing) {
2538 const parent: Symbol.Index = if (attributes.execute)
2616 const default_parent: Symbol.Index = if (attributes.execute)
25392617 .text
2540 else if (attributes.tls and coff.tls_si != .null)
2541 coff.tls_si
25422618 else if (attributes.write)
25432619 .data
25442620 else
25452621 .rdata;
2622
2623 const parent = if (coff.isImage() or std.mem.eql(
2624 u8,
2625 name.toSlice(coff),
2626 default_parent.knownString().toSlice(coff).?,
2627 ))
2628 default_parent
2629 else if (coff.section_table.get(name)) |section| parent: {
2630 const header = section.si.get(coff).section_number.header(coff);
2631 try coff.verifyParentSectionAttributes(name, name, .fromFlags(header.flags), attributes);
2632 break :parent section.si;
2633 } else try coff.addSection(name, attributes.asFlags());
2634
25462635 try coff.nodes.ensureUnusedCapacity(gpa, 1);
25472636 try coff.symbols.ensureUnusedCapacity(gpa, 1);
25482637 const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment });
......@@ -2570,9 +2659,18 @@ fn objectSectionMapIndex(
25702659 if (!object_section_gop.found_existing) {
25712660 try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);
25722661 const name_slice = name.toSlice(coff);
2573 const parent = (try coff.pseudoSectionMapIndex(coff.getOrPutStringAssumeCapacity(
2574 name_slice[0 .. std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len],
2575 ), alignment, attributes)).symbol(coff);
2662 const prefix_index = std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len;
2663 const parent_name = coff.getOrPutStringAssumeCapacity(if (coff.isImage())
2664 name_slice[0..prefix_index]
2665 else
2666 name_slice[0..@min(prefix_index + 1, name_slice.len)]);
2667 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, attributes)).symbol(coff);
2668 try coff.verifyParentSectionAttributes(
2669 parent_name,
2670 name,
2671 .fromFlags(parent.get(coff).section_number.header(coff).flags),
2672 attributes,
2673 );
25762674 try coff.nodes.ensureUnusedCapacity(gpa, 1);
25772675 try coff.symbols.ensureUnusedCapacity(gpa, 1);
25782676 const parent_ni = parent.node(coff);
......@@ -2610,6 +2708,33 @@ fn objectSectionMapIndex(
26102708 return osmi;
26112709}
26122710
2711fn verifyParentSectionAttributes(
2712 coff: *Coff,
2713 parent_name: String,
2714 child_name: String,
2715 parent_attrs: ObjectSectionAttributes,
2716 child_attrs: ObjectSectionAttributes,
2717) !void {
2718 if (parent_attrs == child_attrs) return;
2719
2720 const fields = std.meta.fields(ObjectSectionAttributes);
2721 var err = try coff.base.comp.link_diags.addErrorWithNotes(fields.len);
2722 try err.addMsg("object '{s}' was placed in parent section '{s}' with mismatched flags", .{
2723 child_name.toSlice(coff),
2724 parent_name.toSlice(coff),
2725 });
2726
2727 inline for (fields) |field| {
2728 err.addNote("{s}: parent = {d} child = {d}", .{
2729 field.name,
2730 @intFromBool(@field(child_attrs, field.name)),
2731 @intFromBool(@field(parent_attrs, field.name)),
2732 });
2733 }
2734
2735 return error.LinkFailure;
2736}
2737
26132738pub fn addReloc(
26142739 coff: *Coff,
26152740 loc_si: Symbol.Index,
......@@ -2762,6 +2887,7 @@ fn loadObject(
27622887 const target = &comp.root_mod.resolved_target.result;
27632888 const target_endian = coff.targetEndian();
27642889 const is_archive = coff.isArchive();
2890 assert(!coff.isObj());
27652891
27662892 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtArchiveNameString(archive_name) });
27672893 const header = try r.peekStruct(std.coff.Header, coff.targetEndian());
......@@ -2773,7 +2899,7 @@ fn loadObject(
27732899 if (header.number_of_sections == 0) return;
27742900 if (@sizeOf(std.coff.Header) + header.number_of_sections * @sizeOf(std.coff.SectionHeader) > fl.size)
27752901 return diags.failParse(path, "invalid section table", .{});
2776 const unexpected_flags: []const std.meta.FieldEnum(std.coff.Header.Flags) = &.{
2902 const unexpected_header_flags: []const std.meta.FieldEnum(std.coff.Header.Flags) = &.{
27772903 .RELOCS_STRIPPED,
27782904 .EXECUTABLE_IMAGE,
27792905 .AGGRESSIVE_WS_TRIM,
......@@ -2782,7 +2908,7 @@ fn loadObject(
27822908 .DLL,
27832909 .BYTES_REVERSED_HI,
27842910 };
2785 inline for (unexpected_flags) |flag|
2911 inline for (unexpected_header_flags) |flag|
27862912 if (@field(header.flags, @tagName(flag)))
27872913 return diags.failParse(path, "unexpected flag set: {t}", .{flag});
27882914
......@@ -2810,17 +2936,88 @@ fn loadObject(
28102936 defer gpa.free(string_table);
28112937
28122938 try coff.ensureManyUnusedStringCapacity(
2813 header.number_of_symbols,
2939 header.number_of_sections + header.number_of_symbols,
28142940 string_table_len - @sizeOf(u32),
28152941 );
28162942
2943 const InputSection = struct {
2944 header: std.coff.SectionHeader,
2945 psmi: Node.PseudoSectionMapIndex,
2946 };
2947
2948 try fr.seekTo(fl.offset + @sizeOf(std.coff.Header));
2949 const sections: []const InputSection = if (coff.isImage()) sections: {
2950 const sections = try gpa.alloc(InputSection, header.number_of_sections);
2951 errdefer gpa.free(sections);
2952
2953 for (sections, 0..) |*section, section_i| {
2954 section.header = try r.takeStruct(std.coff.SectionHeader, target_endian);
2955 if (section.header.flags.LNK_INFO) {
2956 if (std.mem.eql(u8, &section.header.name, ".drectve"))
2957 return diags.failParse(path, "TODO handle arguments in .drectve section", .{});
2958
2959 continue;
2960 }
2961
2962 if (section.header.flags.LNK_REMOVE or
2963 section.header.flags.MEM_DISCARDABLE)
2964 {
2965 // TODO: Merge .debug$* sections and output to PDB
2966 continue;
2967 }
2968
2969 if (section.header.flags.LNK_COMDAT)
2970 // This will be necessary if we do the equivalent of /Gy for compiler-rt
2971 return diags.failParse(path, "TODO handle COMDAT sections in input objects", .{});
2972
2973 const section_name_slice = if (section.header.name[0] == '/') name: {
2974 const offset_str = std.mem.sliceTo(section.header.name[1..], 0);
2975 const name_offset = std.fmt.parseUnsigned(u24, offset_str, 10) catch
2976 return diags.failParse(path, "ill-formed section name in section {d}: '{s}'", .{
2977 section_i,
2978 section.header.name[0 .. offset_str.len + 1],
2979 });
2980
2981 if (name_offset > string_table.len)
2982 return diags.failParse(path, "out-of-bounds section name offset in section {d}: {d}", .{ section_i, name_offset });
2983
2984 break :name std.mem.sliceTo(string_table[name_offset..], 0);
2985 } else std.mem.sliceTo(&section.header.name, 0);
2986
2987 const section_name = coff.getOrPutStringAssumeCapacity(section_name_slice);
2988 const osmi = try coff.objectSectionMapIndex(
2989 section_name,
2990 if (section.header.flags.ALIGN.toByteUnits()) |align_bytes|
2991 .fromByteUnits(align_bytes)
2992 else
2993 .@"1",
2994 .fromFlags(section.header.flags),
2995 );
2996
2997 _ = osmi;
2998
2999 // TODO: Decide to merge this section
3000 // TODO: Map flags (might need to figure out a better tls flag?)
3001
3002 //coff.objectSectionMapIndex(name: String, alignment: Alignment, attributes: ObjectSectionAttributes)
3003
3004 // TODO: Load relocations, update for new offset? Or can just work with the object section parent?
3005
3006 }
3007
3008 break :sections sections;
3009 } else &.{};
3010 defer gpa.free(sections);
3011
28173012 const mi = if (is_archive) mi: {
28183013 try coff.nodes.ensureUnusedCapacity(gpa, 2);
28193014 try coff.members.ensureUnusedCapacity(gpa, 1);
3015 const path_str = try path.toString(gpa);
3016 defer gpa.free(path_str);
28203017
28213018 const mi = try coff.addMemberAssumeCapacity(.coff, fl.size);
28223019 const member = mi.get(coff);
2823 try member.initHeader(coff, path.sub_path, header.time_date_stamp);
3020 try member.initHeader(coff, path_str, header.time_date_stamp);
28243021
28253022 {
28263023 var nw: MappedFile.Node.Writer = undefined;
......@@ -2861,6 +3058,10 @@ fn loadObject(
28613058 break :name string_table[index..];
28623059 } else &symbol.name, 0);
28633060
3061 // Section numbers are 1-based here
3062 if (!is_archive and @intFromEnum(symbol.section_number) > sections.len)
3063 return diags.failParse(path, "bad section number {d} for '{s}'", .{ symbol.section_number, name });
3064
28643065 if (is_archive) {
28653066 try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
28663067 continue;
......@@ -2869,6 +3070,9 @@ fn loadObject(
28693070 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
28703071 if (global_gop.found_existing)
28713072 return diags.failParse(path, "multiple definitions of '{s}'", .{name});
3073
3074 // TODO: Get the sym and set the ni to point to wherever it was copied in the pseudo section
3075 // TODO: May need to cache offsets and determine symbol sizes later (once we can sort by section offset)
28723076 }
28733077}
28743078
......@@ -2880,6 +3084,12 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
28803084
28813085 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
28823086
3087 // TODO: Skip over 1st linker member
3088 // TODO: Build index of symbols -> members from 2nd linker member
3089 // TODO: We don't actually have to load an object unless we need a symbol from it (when linking images)
3090 // TODO: Lazily call loadObject whenever a symbol is need from one of the members.
3091 // Could do that in flushGlobal if we haven't gotten an .ni for the symbol yet (and no lib_name)?
3092
28833093 _ = gpa;
28843094 _ = diags;
28853095 _ = r;
......@@ -3918,7 +4128,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
39184128 ),
39194129 }
39204130
3921 if (size > coff.section_table.items[0].si.get(coff).rva) try coff.virtualSlide(
4131 if (size > coff.section_table.values()[0].si.get(coff).rva) try coff.virtualSlide(
39224132 0,
39234133 std.mem.alignForward(
39244134 u32,
......@@ -4120,7 +4330,7 @@ fn flushExportsSort(coff: *Coff) void {
41204330fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
41214331 var rva = start_rva;
41224332 for (
4123 coff.section_table.items[start_section_index..],
4333 coff.section_table.values()[start_section_index..],
41244334 coff.sectionTableSlice()[start_section_index..],
41254335 ) |*section, *header| {
41264336 const section_sym = section.si.get(coff);