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(...@@ -38,7 +38,7 @@ strings: std.HashMapUnmanaged(
38 std.hash_map.default_max_load_percentage,38 std.hash_map.default_max_load_percentage,
39),39),
40string_bytes: std.ArrayList(u8),40string_bytes: std.ArrayList(u8),
41section_table: std.ArrayList(Section),41section_table: std.AutoArrayHashMapUnmanaged(String, Section),
42tls_si: Symbol.Index,42tls_si: Symbol.Index,
43pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),43pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),
44object_section_table: std.array_hash_map.Auto(String, Symbol.Index),44object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
...@@ -395,14 +395,40 @@ pub const Member = struct {...@@ -395,14 +395,40 @@ pub const Member = struct {
395 name_slice[name.len] = 0;395 name_slice[name.len] = 0;
396396
397 gop.value_ptr.* = .{397 gop.value_ptr.* = .{
398 .index = old_size,398 .offset = old_size,
399 .len = name.len,399 .len = name.len,
400 };400 };
401 }401 }
402402
403 field[0] = '/';403 break :offset gop.value_ptr.offset;
404 storeHeaderDecimalStr(field[1..], gop.value_ptr.index);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 ..], ' ');
405 }415 }
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");
406 }432 }
407433
408 pub fn storeHeaderDecimalStr(field_ptr: anytype, value: u64) void {434 pub fn storeHeaderDecimalStr(field_ptr: anytype, value: u64) void {
...@@ -422,7 +448,7 @@ pub const LongNamesTable = struct {...@@ -422,7 +448,7 @@ pub const LongNamesTable = struct {
422 entries: std.AutoArrayHashMapUnmanaged(void, Entry),448 entries: std.AutoArrayHashMapUnmanaged(void, Entry),
423449
424 pub const Entry = struct {450 pub const Entry = struct {
425 index: u64,451 offset: u64,
426 len: u64,452 len: u64,
427 };453 };
428454
...@@ -433,7 +459,7 @@ pub const LongNamesTable = struct {...@@ -433,7 +459,7 @@ pub const LongNamesTable = struct {
433 assert(adapter.coff.isArchive()); // TODO: move to helper that uses this459 assert(adapter.coff.isArchive()); // TODO: move to helper that uses this
434 const longnames_slice = Node.known.longnames_member.slice(&adapter.coff.mf);460 const longnames_slice = Node.known.longnames_member.slice(&adapter.coff.mf);
435 const rhs = adapter.coff.long_names_table.entries.values()[rhs_index];461 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);
437 }463 }
438464
439 pub fn hash(_: Adapter, key: []const u8) u32 {465 pub fn hash(_: Adapter, key: []const u8) u32 {
...@@ -463,6 +489,19 @@ pub const SymbolTable = struct {...@@ -463,6 +489,19 @@ pub const SymbolTable = struct {
463 pub const SymbolName = union(enum) {489 pub const SymbolName = union(enum) {
464 short: []const u8,490 short: []const u8,
465 long: StringIndex,491 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 }
466 };505 };
467506
468 // Symbol.Index does not map 1:1 with SymbolTable.Index:507 // Symbol.Index does not map 1:1 with SymbolTable.Index:
...@@ -666,7 +705,7 @@ pub const Symbol = struct {...@@ -666,7 +705,7 @@ pub const Symbol = struct {
666 }705 }
667706
668 pub fn section(sn: SectionNumber, coff: *const Coff) *Section {707 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()];
670 }709 }
671710
672 pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {711 pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
...@@ -693,6 +732,13 @@ pub const Symbol = struct {...@@ -693,6 +732,13 @@ pub const Symbol = struct {
693 return ni;732 return ni;
694 }733 }
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
696 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {742 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
697 const sym = si.get(coff);743 const sym = si.get(coff);
698 sym.rva = coff.computeNodeRva(sym.ni);744 sym.rva = coff.computeNodeRva(sym.ni);
...@@ -1522,16 +1568,16 @@ fn initHeaders(...@@ -1522,16 +1568,16 @@ fn initHeaders(
1522 .sti = .none,1568 .sti = .none,
1523 .gmi = .none,1569 .gmi = .none,
1524 };1570 };
1525 assert(try coff.addSection(".data", .{1571 assert(try coff.addSection(.@".data", .{
1526 .CNT_INITIALIZED_DATA = true,1572 .CNT_INITIALIZED_DATA = true,
1527 .MEM_READ = true,1573 .MEM_READ = true,
1528 .MEM_WRITE = true,1574 .MEM_WRITE = true,
1529 }) == .data);1575 }) == .data);
1530 assert(try coff.addSection(".rdata", .{1576 assert(try coff.addSection(.@".rdata", .{
1531 .CNT_INITIALIZED_DATA = true,1577 .CNT_INITIALIZED_DATA = true,
1532 .MEM_READ = true,1578 .MEM_READ = true,
1533 }) == .rdata);1579 }) == .rdata);
1534 assert(try coff.addSection(".text", .{1580 assert(try coff.addSection(.@".text", .{
1535 .CNT_CODE = true,1581 .CNT_CODE = true,
1536 .MEM_EXECUTE = true,1582 .MEM_EXECUTE = true,
1537 .MEM_READ = true,1583 .MEM_READ = true,
...@@ -1625,7 +1671,7 @@ fn initHeaders(...@@ -1625,7 +1671,7 @@ fn initHeaders(
16251671
1626 if (comp.config.any_non_single_threaded) {1672 if (comp.config.any_non_single_threaded) {
1627 if (!is_image)1673 if (!is_image)
1628 coff.tls_si = try coff.addSection(".tls$", .{1674 coff.tls_si = try coff.addSection(.@".tls$", .{
1629 .CNT_INITIALIZED_DATA = true,1675 .CNT_INITIALIZED_DATA = true,
1630 .MEM_READ = true,1676 .MEM_READ = true,
1631 .MEM_WRITE = true,1677 .MEM_WRITE = true,
...@@ -1637,7 +1683,7 @@ fn initHeaders(...@@ -1637,7 +1683,7 @@ fn initHeaders(
1637 _ = try coff.objectSectionMapIndex(1683 _ = try coff.objectSectionMapIndex(
1638 .@".tls$",1684 .@".tls$",
1639 coff.mf.flags.block_size,1685 coff.mf.flags.block_size,
1640 .{ .read = true, .write = !is_image, .tls = true },1686 .{ .read = true, .write = !is_image },
1641 );1687 );
1642 }1688 }
1643}1689}
...@@ -1877,7 +1923,7 @@ pub fn dataDirectoryPtr(...@@ -1877,7 +1923,7 @@ pub fn dataDirectoryPtr(
18771923
1878pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {1924pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
1879 return @ptrCast(@alignCast(1925 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)],
1881 ));1927 ));
1882}1928}
18831929
...@@ -1958,6 +2004,30 @@ fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {...@@ -1958,6 +2004,30 @@ fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
1958 return (try coff.getOrPutString(string orelse return .none)).toOptional();2004 return (try coff.getOrPutString(string orelse return .none)).toOptional();
1959}2005}
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
1961/// `len` does not include null terminators2031/// `len` does not include null terminators
1962fn ensureUnusedStringCapacity(coff: *Coff, len: usize) !void {2032fn ensureUnusedStringCapacity(coff: *Coff, len: usize) !void {
1963 const gpa = coff.base.comp.gpa;2033 const gpa = coff.base.comp.gpa;
...@@ -2035,7 +2105,7 @@ fn navSection(...@@ -2035,7 +2105,7 @@ fn navSection(
2035 const ip = &zcu.intern_pool;2105 const ip = &zcu.intern_pool;
2036 const default: String, const attributes: ObjectSectionAttributes =2106 const default: String, const attributes: ObjectSectionAttributes =
2037 if (nav_resolved.@"threadlocal" and coff.base.comp.config.any_non_single_threaded) .{2107 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() },
2039 } else if (ip.isFunctionType(nav_resolved.type)) .{2109 } else if (ip.isFunctionType(nav_resolved.type)) .{
2040 .@".text", .{ .read = true, .execute = true },2110 .@".text", .{ .read = true, .execute = true },
2041 } else if (nav_resolved.@"const") .{2111 } else if (nav_resolved.@"const") .{
...@@ -2311,12 +2381,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2311,12 +2381,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
23112381
2312 const entry = coff.symbolTableEntryPtr(sym.sti) orelse entry: {2382 const entry = coff.symbolTableEntryPtr(sym.sti) orelse entry: {
2313 var buf: [15]u8 = undefined;2383 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 =
2315 if (sym.gmi != .none) blk: {2385 if (sym.gmi != .none) blk: {
2316 const gn = sym.gmi.globalName(coff);2386 const gn = sym.gmi.globalName(coff);
2317 break :blk .{2387 break :blk .{
2318 gn.name.toSlice(coff),2388 try coff.getOrPutSymbolName(gn.name.toSlice(coff), gn.name),
2319 gn.name,
2320 0,2389 0,
2321 if (Symbol.Index.text.get(coff).section_number == sym.section_number)2390 if (Symbol.Index.text.get(coff).section_number == sym.section_number)
2322 .FUNCTION2391 .FUNCTION
...@@ -2325,8 +2394,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2325,8 +2394,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2325 };2394 };
2326 } else blk: switch (coff.getNode(sym.ni)) {2395 } else blk: switch (coff.getNode(sym.ni)) {
2327 .image_section => .{2396 .image_section => .{
2328 &sym.section_number.header(coff).name,2397 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),
2329 null,
2330 1,2398 1,
2331 .NULL,2399 .NULL,
2332 },2400 },
...@@ -2335,8 +2403,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2335,8 +2403,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2335 const ip = &zcu.intern_pool;2403 const ip = &zcu.intern_pool;
2336 const nav = ip.getNav(nmi.navIndex(coff));2404 const nav = ip.getNav(nmi.navIndex(coff));
2337 break :blk .{2405 break :blk .{
2338 nav.fqn.toSlice(ip),2406 try coff.getOrPutSymbolName(nav.fqn.toSlice(ip), null),
2339 null,
2340 0,2407 0,
2341 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,2408 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,
2342 };2409 };
...@@ -2344,7 +2411,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2344,7 +2411,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2344 .uav => |umi| {2411 .uav => |umi| {
2345 var w = Io.Writer.fixed(&buf);2412 var w = Io.Writer.fixed(&buf);
2346 w.print("__anon_{x}", .{umi.uavValue(coff)}) catch unreachable;2413 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 };
2348 },2419 },
2349 inline .lazy_code, .lazy_const_data => |mi, tag| {2420 inline .lazy_code, .lazy_const_data => |mi, tag| {
2350 const lazy_sym = mi.lazySymbol(coff);2421 const lazy_sym = mi.lazySymbol(coff);
...@@ -2355,7 +2426,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2355,7 +2426,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2355 defer gpa.free(name);2426 defer gpa.free(name);
23562427
2357 const string = try coff.getOrPutString(name);2428 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 };
2359 },2434 },
2360 else => {2435 else => {
2361 log.err("TODO implement symbol table init for {s} ({d})", .{ @tagName(coff.getNode(sym.ni)), si });2436 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...@@ -2363,22 +2438,6 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2363 },2438 },
2364 };2439 };
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
2382 const old_num_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);2441 const old_num_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
2383 const new_num_symbols = old_num_symbols + 1 + num_aux_symbols;2442 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...@@ -2389,17 +2448,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2389 si.flushSymbolTableIndex(coff);2448 si.flushSymbolTableIndex(coff);
23902449
2391 const entry = coff.symbolTableEntryPtr(sym.sti).?;2450 const entry = coff.symbolTableEntryPtr(sym.sti).?;
2392 switch (symbol_name) {2451 symbol_name.store(coff, &entry.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 }
24032452
2404 entry.section_number = @enumFromInt(@intFromEnum(sym.section_number));2453 entry.section_number = @enumFromInt(@intFromEnum(sym.section_number));
2405 entry.type = .{2454 entry.type = .{
...@@ -2431,7 +2480,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2431,7 +2480,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2431 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });2480 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
2432}2481}
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 {
2435 assert(coff.base.comp.zcu != null);2484 assert(coff.base.comp.zcu != null);
24362485
2437 const gpa = coff.base.comp.gpa;2486 const gpa = coff.base.comp.gpa;
...@@ -2457,7 +2506,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags...@@ -2457,7 +2506,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
2457 });2506 });
24582507
2459 const si = coff.addSymbolAssumeCapacity();2508 const si = coff.addSymbolAssumeCapacity();
2460 coff.section_table.appendAssumeCapacity(.{2509 coff.section_table.putAssumeCapacity(name, .{
2461 .si = si,2510 .si = si,
2462 .relocation_table_ni = .none,2511 .relocation_table_ni = .none,
2463 });2512 });
...@@ -2468,7 +2517,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags...@@ -2468,7 +2517,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
2468 const virtual_size = coff.optionalHeaderField(.section_alignment);2517 const virtual_size = coff.optionalHeaderField(.section_alignment);
2469 const rva: u32 = switch (section_index) {2518 const rva: u32 = switch (section_index) {
2470 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),2519 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 +
2472 coff.targetLoad(&section_table[section_index - 1].virtual_size),2521 coff.targetLoad(&section_table[section_index - 1].virtual_size),
2473 };2522 };
24742523
...@@ -2494,12 +2543,13 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags...@@ -2494,12 +2543,13 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
2494 .number_of_linenumbers = 0,2543 .number_of_linenumbers = 0,
2495 .flags = flags,2544 .flags = flags,
2496 };2545 };
2497 @memcpy(section.name[0..name.len], name);
2498 @memset(section.name[name.len..], 0);
2499 if (coff.targetEndian() != native_endian)2546 if (coff.targetEndian() != native_endian)
2500 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);2547 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
25012548
2549 const name_slice = name.toSlice(coff);
2502 if (coff.isImage()) {2550 if (coff.isImage()) {
2551 @memcpy(section.name[0..name_slice.len], name_slice);
2552 @memset(section.name[name_slice.len..], 0);
2503 switch (coff.optionalHeaderPtr()) {2553 switch (coff.optionalHeaderPtr()) {
2504 inline else => |optional_header| coff.targetStore(2554 inline else => |optional_header| coff.targetStore(
2505 &optional_header.size_of_image,2555 &optional_header.size_of_image,
...@@ -2507,6 +2557,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags...@@ -2507,6 +2557,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
2507 ),2557 ),
2508 }2558 }
2509 } else {2559 } else {
2560 (try coff.getOrPutSymbolName(name_slice, name)).store(coff, &section.name);
2510 try coff.pendingSymbolTableEntry(si);2561 try coff.pendingSymbolTableEntry(si);
2511 }2562 }
25122563
...@@ -2522,7 +2573,34 @@ const ObjectSectionAttributes = packed struct {...@@ -2522,7 +2573,34 @@ const ObjectSectionAttributes = packed struct {
2522 nocache: bool = false,2573 nocache: bool = false,
2523 discard: bool = false,2574 discard: bool = false,
2524 remove: bool = false,2575 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 }
2526};2604};
25272605
2528fn pseudoSectionMapIndex(2606fn pseudoSectionMapIndex(
...@@ -2535,14 +2613,25 @@ fn pseudoSectionMapIndex(...@@ -2535,14 +2613,25 @@ fn pseudoSectionMapIndex(
2535 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);2613 const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name);
2536 const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);2614 const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index);
2537 if (!pseudo_section_gop.found_existing) {2615 if (!pseudo_section_gop.found_existing) {
2538 const parent: Symbol.Index = if (attributes.execute)2616 const default_parent: Symbol.Index = if (attributes.execute)
2539 .text2617 .text
2540 else if (attributes.tls and coff.tls_si != .null)
2541 coff.tls_si
2542 else if (attributes.write)2618 else if (attributes.write)
2543 .data2619 .data
2544 else2620 else
2545 .rdata;2621 .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
2546 try coff.nodes.ensureUnusedCapacity(gpa, 1);2635 try coff.nodes.ensureUnusedCapacity(gpa, 1);
2547 try coff.symbols.ensureUnusedCapacity(gpa, 1);2636 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2548 const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment });2637 const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment });
...@@ -2570,9 +2659,18 @@ fn objectSectionMapIndex(...@@ -2570,9 +2659,18 @@ fn objectSectionMapIndex(
2570 if (!object_section_gop.found_existing) {2659 if (!object_section_gop.found_existing) {
2571 try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);2660 try coff.ensureUnusedStringCapacity(name.toSlice(coff).len);
2572 const name_slice = name.toSlice(coff);2661 const name_slice = name.toSlice(coff);
2573 const parent = (try coff.pseudoSectionMapIndex(coff.getOrPutStringAssumeCapacity(2662 const prefix_index = std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len;
2574 name_slice[0 .. std.mem.indexOfScalar(u8, name_slice, '$') orelse name_slice.len],2663 const parent_name = coff.getOrPutStringAssumeCapacity(if (coff.isImage())
2575 ), alignment, attributes)).symbol(coff);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 );
2576 try coff.nodes.ensureUnusedCapacity(gpa, 1);2674 try coff.nodes.ensureUnusedCapacity(gpa, 1);
2577 try coff.symbols.ensureUnusedCapacity(gpa, 1);2675 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2578 const parent_ni = parent.node(coff);2676 const parent_ni = parent.node(coff);
...@@ -2610,6 +2708,33 @@ fn objectSectionMapIndex(...@@ -2610,6 +2708,33 @@ fn objectSectionMapIndex(
2610 return osmi;2708 return osmi;
2611}2709}
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
2613pub fn addReloc(2738pub fn addReloc(
2614 coff: *Coff,2739 coff: *Coff,
2615 loc_si: Symbol.Index,2740 loc_si: Symbol.Index,
...@@ -2762,6 +2887,7 @@ fn loadObject(...@@ -2762,6 +2887,7 @@ fn loadObject(
2762 const target = &comp.root_mod.resolved_target.result;2887 const target = &comp.root_mod.resolved_target.result;
2763 const target_endian = coff.targetEndian();2888 const target_endian = coff.targetEndian();
2764 const is_archive = coff.isArchive();2889 const is_archive = coff.isArchive();
2890 assert(!coff.isObj());
27652891
2766 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtArchiveNameString(archive_name) });2892 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtArchiveNameString(archive_name) });
2767 const header = try r.peekStruct(std.coff.Header, coff.targetEndian());2893 const header = try r.peekStruct(std.coff.Header, coff.targetEndian());
...@@ -2773,7 +2899,7 @@ fn loadObject(...@@ -2773,7 +2899,7 @@ fn loadObject(
2773 if (header.number_of_sections == 0) return;2899 if (header.number_of_sections == 0) return;
2774 if (@sizeOf(std.coff.Header) + header.number_of_sections * @sizeOf(std.coff.SectionHeader) > fl.size)2900 if (@sizeOf(std.coff.Header) + header.number_of_sections * @sizeOf(std.coff.SectionHeader) > fl.size)
2775 return diags.failParse(path, "invalid section table", .{});2901 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) = &.{
2777 .RELOCS_STRIPPED,2903 .RELOCS_STRIPPED,
2778 .EXECUTABLE_IMAGE,2904 .EXECUTABLE_IMAGE,
2779 .AGGRESSIVE_WS_TRIM,2905 .AGGRESSIVE_WS_TRIM,
...@@ -2782,7 +2908,7 @@ fn loadObject(...@@ -2782,7 +2908,7 @@ fn loadObject(
2782 .DLL,2908 .DLL,
2783 .BYTES_REVERSED_HI,2909 .BYTES_REVERSED_HI,
2784 };2910 };
2785 inline for (unexpected_flags) |flag|2911 inline for (unexpected_header_flags) |flag|
2786 if (@field(header.flags, @tagName(flag)))2912 if (@field(header.flags, @tagName(flag)))
2787 return diags.failParse(path, "unexpected flag set: {t}", .{flag});2913 return diags.failParse(path, "unexpected flag set: {t}", .{flag});
27882914
...@@ -2810,17 +2936,88 @@ fn loadObject(...@@ -2810,17 +2936,88 @@ fn loadObject(
2810 defer gpa.free(string_table);2936 defer gpa.free(string_table);
28112937
2812 try coff.ensureManyUnusedStringCapacity(2938 try coff.ensureManyUnusedStringCapacity(
2813 header.number_of_symbols,2939 header.number_of_sections + header.number_of_symbols,
2814 string_table_len - @sizeOf(u32),2940 string_table_len - @sizeOf(u32),
2815 );2941 );
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
2817 const mi = if (is_archive) mi: {3012 const mi = if (is_archive) mi: {
2818 try coff.nodes.ensureUnusedCapacity(gpa, 2);3013 try coff.nodes.ensureUnusedCapacity(gpa, 2);
2819 try coff.members.ensureUnusedCapacity(gpa, 1);3014 try coff.members.ensureUnusedCapacity(gpa, 1);
3015 const path_str = try path.toString(gpa);
3016 defer gpa.free(path_str);
28203017
2821 const mi = try coff.addMemberAssumeCapacity(.coff, fl.size);3018 const mi = try coff.addMemberAssumeCapacity(.coff, fl.size);
2822 const member = mi.get(coff);3019 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
2825 {3022 {
2826 var nw: MappedFile.Node.Writer = undefined;3023 var nw: MappedFile.Node.Writer = undefined;
...@@ -2861,6 +3058,10 @@ fn loadObject(...@@ -2861,6 +3058,10 @@ fn loadObject(
2861 break :name string_table[index..];3058 break :name string_table[index..];
2862 } else &symbol.name, 0);3059 } 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
2864 if (is_archive) {3065 if (is_archive) {
2865 try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));3066 try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name));
2866 continue;3067 continue;
...@@ -2869,6 +3070,9 @@ fn loadObject(...@@ -2869,6 +3070,9 @@ fn loadObject(
2869 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });3070 const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name });
2870 if (global_gop.found_existing)3071 if (global_gop.found_existing)
2871 return diags.failParse(path, "multiple definitions of '{s}'", .{name});3072 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)
2872 }3076 }
2873}3077}
28743078
...@@ -2880,6 +3084,12 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo...@@ -2880,6 +3084,12 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo
28803084
2881 log.debug("loadArchive({f})", .{path.fmtEscapeString()});3085 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
2883 _ = gpa;3093 _ = gpa;
2884 _ = diags;3094 _ = diags;
2885 _ = r;3095 _ = r;
...@@ -3918,7 +4128,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -3918,7 +4128,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
3918 ),4128 ),
3919 }4129 }
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(
3922 0,4132 0,
3923 std.mem.alignForward(4133 std.mem.alignForward(
3924 u32,4134 u32,
...@@ -4120,7 +4330,7 @@ fn flushExportsSort(coff: *Coff) void {...@@ -4120,7 +4330,7 @@ fn flushExportsSort(coff: *Coff) void {
4120fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {4330fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
4121 var rva = start_rva;4331 var rva = start_rva;
4122 for (4332 for (
4123 coff.section_table.items[start_section_index..],4333 coff.section_table.values()[start_section_index..],
4124 coff.sectionTableSlice()[start_section_index..],4334 coff.sectionTableSlice()[start_section_index..],
4125 ) |*section, *header| {4335 ) |*section, *header| {
4126 const section_sym = section.si.get(coff);4336 const section_sym = section.si.get(coff);