| ... | ... | @@ -38,7 +38,7 @@ strings: std.HashMapUnmanaged( |
| 38 | 38 | std.hash_map.default_max_load_percentage, |
| 39 | 39 | ), |
| 40 | 40 | string_bytes: std.ArrayList(u8), |
| 41 | | section_table: std.ArrayList(Section), |
| 41 | section_table: std.AutoArrayHashMapUnmanaged(String, Section), |
| 42 | 42 | tls_si: Symbol.Index, |
| 43 | 43 | pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index), |
| 44 | 44 | object_section_table: std.array_hash_map.Auto(String, Symbol.Index), |
| ... | ... | @@ -395,14 +395,40 @@ pub const Member = struct { |
| 395 | 395 | name_slice[name.len] = 0; |
| 396 | 396 | |
| 397 | 397 | gop.value_ptr.* = .{ |
| 398 | | .index = old_size, |
| 398 | .offset = old_size, |
| 399 | 399 | .len = name.len, |
| 400 | 400 | }; |
| 401 | 401 | } |
| 402 | 402 | |
| 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 ..], ' '); |
| 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 | } |
| 407 | 433 | |
| 408 | 434 | pub fn storeHeaderDecimalStr(field_ptr: anytype, value: u64) void { |
| ... | ... | @@ -422,7 +448,7 @@ pub const LongNamesTable = struct { |
| 422 | 448 | entries: std.AutoArrayHashMapUnmanaged(void, Entry), |
| 423 | 449 | |
| 424 | 450 | pub const Entry = struct { |
| 425 | | index: u64, |
| 451 | offset: u64, |
| 426 | 452 | len: u64, |
| 427 | 453 | }; |
| 428 | 454 | |
| ... | ... | @@ -433,7 +459,7 @@ pub const LongNamesTable = struct { |
| 433 | 459 | assert(adapter.coff.isArchive()); // TODO: move to helper that uses this |
| 434 | 460 | const longnames_slice = Node.known.longnames_member.slice(&adapter.coff.mf); |
| 435 | 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 | } |
| 438 | 464 | |
| 439 | 465 | pub fn hash(_: Adapter, key: []const u8) u32 { |
| ... | ... | @@ -463,6 +489,19 @@ pub const SymbolTable = struct { |
| 463 | 489 | pub const SymbolName = union(enum) { |
| 464 | 490 | short: []const u8, |
| 465 | 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 | }; |
| 467 | 506 | |
| 468 | 507 | // Symbol.Index does not map 1:1 with SymbolTable.Index: |
| ... | ... | @@ -666,7 +705,7 @@ pub const Symbol = struct { |
| 666 | 705 | } |
| 667 | 706 | |
| 668 | 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 | } |
| 671 | 710 | |
| 672 | 711 | pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader { |
| ... | ... | @@ -693,6 +732,13 @@ pub const Symbol = struct { |
| 693 | 732 | return ni; |
| 694 | 733 | } |
| 695 | 734 | |
| 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 | 742 | pub fn flushMoved(si: Symbol.Index, coff: *Coff) void { |
| 697 | 743 | const sym = si.get(coff); |
| 698 | 744 | sym.rva = coff.computeNodeRva(sym.ni); |
| ... | ... | @@ -1522,16 +1568,16 @@ fn initHeaders( |
| 1522 | 1568 | .sti = .none, |
| 1523 | 1569 | .gmi = .none, |
| 1524 | 1570 | }; |
| 1525 | | assert(try coff.addSection(".data", .{ |
| 1571 | assert(try coff.addSection(.@".data", .{ |
| 1526 | 1572 | .CNT_INITIALIZED_DATA = true, |
| 1527 | 1573 | .MEM_READ = true, |
| 1528 | 1574 | .MEM_WRITE = true, |
| 1529 | 1575 | }) == .data); |
| 1530 | | assert(try coff.addSection(".rdata", .{ |
| 1576 | assert(try coff.addSection(.@".rdata", .{ |
| 1531 | 1577 | .CNT_INITIALIZED_DATA = true, |
| 1532 | 1578 | .MEM_READ = true, |
| 1533 | 1579 | }) == .rdata); |
| 1534 | | assert(try coff.addSection(".text", .{ |
| 1580 | assert(try coff.addSection(.@".text", .{ |
| 1535 | 1581 | .CNT_CODE = true, |
| 1536 | 1582 | .MEM_EXECUTE = true, |
| 1537 | 1583 | .MEM_READ = true, |
| ... | ... | @@ -1625,7 +1671,7 @@ fn initHeaders( |
| 1625 | 1671 | |
| 1626 | 1672 | if (comp.config.any_non_single_threaded) { |
| 1627 | 1673 | if (!is_image) |
| 1628 | | coff.tls_si = try coff.addSection(".tls$", .{ |
| 1674 | coff.tls_si = try coff.addSection(.@".tls$", .{ |
| 1629 | 1675 | .CNT_INITIALIZED_DATA = true, |
| 1630 | 1676 | .MEM_READ = true, |
| 1631 | 1677 | .MEM_WRITE = true, |
| ... | ... | @@ -1637,7 +1683,7 @@ fn initHeaders( |
| 1637 | 1683 | _ = try coff.objectSectionMapIndex( |
| 1638 | 1684 | .@".tls$", |
| 1639 | 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 | 1923 | |
| 1878 | 1924 | pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader { |
| 1879 | 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 | } |
| 1883 | 1929 | |
| ... | ... | @@ -1958,6 +2004,30 @@ fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional { |
| 1958 | 2004 | return (try coff.getOrPutString(string orelse return .none)).toOptional(); |
| 1959 | 2005 | } |
| 1960 | 2006 | |
| 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`. |
| 2011 | fn 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 | 2031 | /// `len` does not include null terminators |
| 1962 | 2032 | fn ensureUnusedStringCapacity(coff: *Coff, len: usize) !void { |
| 1963 | 2033 | const gpa = coff.base.comp.gpa; |
| ... | ... | @@ -2035,7 +2105,7 @@ fn navSection( |
| 2035 | 2105 | const ip = &zcu.intern_pool; |
| 2036 | 2106 | const default: String, const attributes: ObjectSectionAttributes = |
| 2037 | 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 | 2109 | } else if (ip.isFunctionType(nav_resolved.type)) .{ |
| 2040 | 2110 | .@".text", .{ .read = true, .execute = true }, |
| 2041 | 2111 | } else if (nav_resolved.@"const") .{ |
| ... | ... | @@ -2311,12 +2381,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void |
| 2311 | 2381 | |
| 2312 | 2382 | const entry = coff.symbolTableEntryPtr(sym.sti) orelse entry: { |
| 2313 | 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 | 2385 | if (sym.gmi != .none) blk: { |
| 2316 | 2386 | const gn = sym.gmi.globalName(coff); |
| 2317 | 2387 | break :blk .{ |
| 2318 | | gn.name.toSlice(coff), |
| 2319 | | gn.name, |
| 2388 | try coff.getOrPutSymbolName(gn.name.toSlice(coff), gn.name), |
| 2320 | 2389 | 0, |
| 2321 | 2390 | if (Symbol.Index.text.get(coff).section_number == sym.section_number) |
| 2322 | 2391 | .FUNCTION |
| ... | ... | @@ -2325,8 +2394,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void |
| 2325 | 2394 | }; |
| 2326 | 2395 | } else blk: switch (coff.getNode(sym.ni)) { |
| 2327 | 2396 | .image_section => .{ |
| 2328 | | &sym.section_number.header(coff).name, |
| 2329 | | null, |
| 2397 | try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null), |
| 2330 | 2398 | 1, |
| 2331 | 2399 | .NULL, |
| 2332 | 2400 | }, |
| ... | ... | @@ -2335,8 +2403,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void |
| 2335 | 2403 | const ip = &zcu.intern_pool; |
| 2336 | 2404 | const nav = ip.getNav(nmi.navIndex(coff)); |
| 2337 | 2405 | break :blk .{ |
| 2338 | | nav.fqn.toSlice(ip), |
| 2339 | | null, |
| 2406 | try coff.getOrPutSymbolName(nav.fqn.toSlice(ip), null), |
| 2340 | 2407 | 0, |
| 2341 | 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 | 2411 | .uav => |umi| { |
| 2345 | 2412 | var w = Io.Writer.fixed(&buf); |
| 2346 | 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 | 2420 | inline .lazy_code, .lazy_const_data => |mi, tag| { |
| 2350 | 2421 | const lazy_sym = mi.lazySymbol(coff); |
| ... | ... | @@ -2355,7 +2426,11 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void |
| 2355 | 2426 | defer gpa.free(name); |
| 2356 | 2427 | |
| 2357 | 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 | 2435 | else => { |
| 2361 | 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 | 2438 | }, |
| 2364 | 2439 | }; |
| 2365 | 2440 | |
| 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 | 2441 | const old_num_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols); |
| 2383 | 2442 | const new_num_symbols = old_num_symbols + 1 + num_aux_symbols; |
| 2384 | 2443 | |
| ... | ... | @@ -2389,17 +2448,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void |
| 2389 | 2448 | si.flushSymbolTableIndex(coff); |
| 2390 | 2449 | |
| 2391 | 2450 | 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); |
| 2403 | 2452 | |
| 2404 | 2453 | entry.section_number = @enumFromInt(@intFromEnum(sym.section_number)); |
| 2405 | 2454 | entry.type = .{ |
| ... | ... | @@ -2431,7 +2480,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void |
| 2431 | 2480 | log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti }); |
| 2432 | 2481 | } |
| 2433 | 2482 | |
| 2434 | | fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index { |
| 2483 | fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !Symbol.Index { |
| 2435 | 2484 | assert(coff.base.comp.zcu != null); |
| 2436 | 2485 | |
| 2437 | 2486 | const gpa = coff.base.comp.gpa; |
| ... | ... | @@ -2457,7 +2506,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags |
| 2457 | 2506 | }); |
| 2458 | 2507 | |
| 2459 | 2508 | const si = coff.addSymbolAssumeCapacity(); |
| 2460 | | coff.section_table.appendAssumeCapacity(.{ |
| 2509 | coff.section_table.putAssumeCapacity(name, .{ |
| 2461 | 2510 | .si = si, |
| 2462 | 2511 | .relocation_table_ni = .none, |
| 2463 | 2512 | }); |
| ... | ... | @@ -2468,7 +2517,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags |
| 2468 | 2517 | const virtual_size = coff.optionalHeaderField(.section_alignment); |
| 2469 | 2518 | const rva: u32 = switch (section_index) { |
| 2470 | 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 | 2521 | coff.targetLoad(&section_table[section_index - 1].virtual_size), |
| 2473 | 2522 | }; |
| 2474 | 2523 | |
| ... | ... | @@ -2494,12 +2543,13 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags |
| 2494 | 2543 | .number_of_linenumbers = 0, |
| 2495 | 2544 | .flags = flags, |
| 2496 | 2545 | }; |
| 2497 | | @memcpy(section.name[0..name.len], name); |
| 2498 | | @memset(section.name[name.len..], 0); |
| 2499 | 2546 | if (coff.targetEndian() != native_endian) |
| 2500 | 2547 | std.mem.byteSwapAllFields(std.coff.SectionHeader, section); |
| 2501 | 2548 | |
| 2549 | const name_slice = name.toSlice(coff); |
| 2502 | 2550 | if (coff.isImage()) { |
| 2551 | @memcpy(section.name[0..name_slice.len], name_slice); |
| 2552 | @memset(section.name[name_slice.len..], 0); |
| 2503 | 2553 | switch (coff.optionalHeaderPtr()) { |
| 2504 | 2554 | inline else => |optional_header| coff.targetStore( |
| 2505 | 2555 | &optional_header.size_of_image, |
| ... | ... | @@ -2507,6 +2557,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags |
| 2507 | 2557 | ), |
| 2508 | 2558 | } |
| 2509 | 2559 | } else { |
| 2560 | (try coff.getOrPutSymbolName(name_slice, name)).store(coff, &section.name); |
| 2510 | 2561 | try coff.pendingSymbolTableEntry(si); |
| 2511 | 2562 | } |
| 2512 | 2563 | |
| ... | ... | @@ -2522,7 +2573,34 @@ const ObjectSectionAttributes = packed struct { |
| 2522 | 2573 | nocache: bool = false, |
| 2523 | 2574 | discard: bool = false, |
| 2524 | 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 | }; |
| 2527 | 2605 | |
| 2528 | 2606 | fn pseudoSectionMapIndex( |
| ... | ... | @@ -2535,14 +2613,25 @@ fn pseudoSectionMapIndex( |
| 2535 | 2613 | const pseudo_section_gop = try coff.pseudo_section_table.getOrPut(gpa, name); |
| 2536 | 2614 | const psmi: Node.PseudoSectionMapIndex = @enumFromInt(pseudo_section_gop.index); |
| 2537 | 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 | 2617 | .text |
| 2540 | | else if (attributes.tls and coff.tls_si != .null) |
| 2541 | | coff.tls_si |
| 2542 | 2618 | else if (attributes.write) |
| 2543 | 2619 | .data |
| 2544 | 2620 | else |
| 2545 | 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 | 2635 | try coff.nodes.ensureUnusedCapacity(gpa, 1); |
| 2547 | 2636 | try coff.symbols.ensureUnusedCapacity(gpa, 1); |
| 2548 | 2637 | const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment }); |
| ... | ... | @@ -2570,9 +2659,18 @@ fn objectSectionMapIndex( |
| 2570 | 2659 | if (!object_section_gop.found_existing) { |
| 2571 | 2660 | try coff.ensureUnusedStringCapacity(name.toSlice(coff).len); |
| 2572 | 2661 | 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 | ); |
| 2576 | 2674 | try coff.nodes.ensureUnusedCapacity(gpa, 1); |
| 2577 | 2675 | try coff.symbols.ensureUnusedCapacity(gpa, 1); |
| 2578 | 2676 | const parent_ni = parent.node(coff); |
| ... | ... | @@ -2610,6 +2708,33 @@ fn objectSectionMapIndex( |
| 2610 | 2708 | return osmi; |
| 2611 | 2709 | } |
| 2612 | 2710 | |
| 2711 | fn 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 | |
| 2613 | 2738 | pub fn addReloc( |
| 2614 | 2739 | coff: *Coff, |
| 2615 | 2740 | loc_si: Symbol.Index, |
| ... | ... | @@ -2762,6 +2887,7 @@ fn loadObject( |
| 2762 | 2887 | const target = &comp.root_mod.resolved_target.result; |
| 2763 | 2888 | const target_endian = coff.targetEndian(); |
| 2764 | 2889 | const is_archive = coff.isArchive(); |
| 2890 | assert(!coff.isObj()); |
| 2765 | 2891 | |
| 2766 | 2892 | log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtArchiveNameString(archive_name) }); |
| 2767 | 2893 | const header = try r.peekStruct(std.coff.Header, coff.targetEndian()); |
| ... | ... | @@ -2773,7 +2899,7 @@ fn loadObject( |
| 2773 | 2899 | if (header.number_of_sections == 0) return; |
| 2774 | 2900 | if (@sizeOf(std.coff.Header) + header.number_of_sections * @sizeOf(std.coff.SectionHeader) > fl.size) |
| 2775 | 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 | 2903 | .RELOCS_STRIPPED, |
| 2778 | 2904 | .EXECUTABLE_IMAGE, |
| 2779 | 2905 | .AGGRESSIVE_WS_TRIM, |
| ... | ... | @@ -2782,7 +2908,7 @@ fn loadObject( |
| 2782 | 2908 | .DLL, |
| 2783 | 2909 | .BYTES_REVERSED_HI, |
| 2784 | 2910 | }; |
| 2785 | | inline for (unexpected_flags) |flag| |
| 2911 | inline for (unexpected_header_flags) |flag| |
| 2786 | 2912 | if (@field(header.flags, @tagName(flag))) |
| 2787 | 2913 | return diags.failParse(path, "unexpected flag set: {t}", .{flag}); |
| 2788 | 2914 | |
| ... | ... | @@ -2810,17 +2936,88 @@ fn loadObject( |
| 2810 | 2936 | defer gpa.free(string_table); |
| 2811 | 2937 | |
| 2812 | 2938 | try coff.ensureManyUnusedStringCapacity( |
| 2813 | | header.number_of_symbols, |
| 2939 | header.number_of_sections + header.number_of_symbols, |
| 2814 | 2940 | string_table_len - @sizeOf(u32), |
| 2815 | 2941 | ); |
| 2816 | 2942 | |
| 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 | 3012 | const mi = if (is_archive) mi: { |
| 2818 | 3013 | try coff.nodes.ensureUnusedCapacity(gpa, 2); |
| 2819 | 3014 | try coff.members.ensureUnusedCapacity(gpa, 1); |
| 3015 | const path_str = try path.toString(gpa); |
| 3016 | defer gpa.free(path_str); |
| 2820 | 3017 | |
| 2821 | 3018 | const mi = try coff.addMemberAssumeCapacity(.coff, fl.size); |
| 2822 | 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); |
| 2824 | 3021 | |
| 2825 | 3022 | { |
| 2826 | 3023 | var nw: MappedFile.Node.Writer = undefined; |
| ... | ... | @@ -2861,6 +3058,10 @@ fn loadObject( |
| 2861 | 3058 | break :name string_table[index..]; |
| 2862 | 3059 | } else &symbol.name, 0); |
| 2863 | 3060 | |
| 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 | 3065 | if (is_archive) { |
| 2865 | 3066 | try coff.ensureMemberSymbol(mi, coff.getOrPutStringAssumeCapacity(name)); |
| 2866 | 3067 | continue; |
| ... | ... | @@ -2869,6 +3070,9 @@ fn loadObject( |
| 2869 | 3070 | const global_gop = try coff.getOrPutGlobalSymbol(.{ .name = name }); |
| 2870 | 3071 | if (global_gop.found_existing) |
| 2871 | 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 | } |
| 2874 | 3078 | |
| ... | ... | @@ -2880,6 +3084,12 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo |
| 2880 | 3084 | |
| 2881 | 3085 | log.debug("loadArchive({f})", .{path.fmtEscapeString()}); |
| 2882 | 3086 | |
| 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 | 3093 | _ = gpa; |
| 2884 | 3094 | _ = diags; |
| 2885 | 3095 | _ = r; |
| ... | ... | @@ -3918,7 +4128,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { |
| 3918 | 4128 | ), |
| 3919 | 4129 | } |
| 3920 | 4130 | |
| 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 | 4132 | 0, |
| 3923 | 4133 | std.mem.alignForward( |
| 3924 | 4134 | u32, |
| ... | ... | @@ -4120,7 +4330,7 @@ fn flushExportsSort(coff: *Coff) void { |
| 4120 | 4330 | fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { |
| 4121 | 4331 | var rva = start_rva; |
| 4122 | 4332 | for ( |
| 4123 | | coff.section_table.items[start_section_index..], |
| 4333 | coff.section_table.values()[start_section_index..], |
| 4124 | 4334 | coff.sectionTableSlice()[start_section_index..], |
| 4125 | 4335 | ) |*section, *header| { |
| 4126 | 4336 | const section_sym = section.si.get(coff); |