diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b19e728d2..690e2e35b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -561,6 +561,7 @@ set(ZIG_STAGE2_SOURCES src/libs/libunwind.zig src/link.zig src/link/C.zig + src/link/Coff.zig src/link/Dwarf.zig src/link/Elf.zig src/link/Elf/Archive.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index 6ae57f3492..7c5f699d83 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2718,35 +2718,17 @@ pub fn destroy(comp: *Compilation) void { } comp.crt_files.deinit(gpa); } - - if (comp.libunwind_static_lib) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.libcxx_static_lib) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.libcxxabi_static_lib) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.compiler_rt_lib) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.compiler_rt_obj) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.ubsan_rt_lib) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.ubsan_rt_obj) |*crt_file| { - crt_file.deinit(gpa); - } - if (comp.fuzzer_lib) |*crt_file| { - crt_file.deinit(gpa); - } - - if (comp.zigc_static_lib) |*crt_file| { - crt_file.deinit(gpa); - } + if (comp.libcxx_static_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.libcxxabi_static_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.libunwind_static_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.tsan_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.ubsan_rt_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.ubsan_rt_obj) |*crt_file| crt_file.deinit(gpa); + if (comp.zigc_static_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.compiler_rt_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.compiler_rt_obj) |*crt_file| crt_file.deinit(gpa); + if (comp.compiler_rt_dyn_lib) |*crt_file| crt_file.deinit(gpa); + if (comp.fuzzer_lib) |*crt_file| crt_file.deinit(gpa); if (comp.glibc_so_files) |*glibc_file| { glibc_file.deinit(gpa); diff --git a/src/link.zig b/src/link.zig index e876e8677a..1cf54f531c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1265,7 +1265,7 @@ pub const File = struct { pub const Lld = @import("link/Lld.zig"); pub const C = @import("link/C.zig"); - pub const Coff2 = @import("link/Coff2.zig"); + pub const Coff2 = @import("link/Coff.zig"); pub const Elf = @import("link/Elf.zig"); pub const Elf2 = @import("link/Elf2.zig"); pub const MachO = @import("link/MachO.zig"); diff --git a/src/link/Coff2.zig b/src/link/Coff.zig similarity index 92% rename from src/link/Coff2.zig rename to src/link/Coff.zig index 79d4b17505..8f9ec49f78 100644 --- a/src/link/Coff2.zig +++ b/src/link/Coff.zig @@ -1,5 +1,4 @@ base: link.File, -endian: std.builtin.Endian, mf: MappedFile, nodes: std.MultiArrayList(Node), import_table: ImportTable, @@ -105,9 +104,9 @@ pub const Node = union(enum) { section_table, section: Symbol.Index, import_directory_table, - import_lookup_table: u32, - import_address_table: u32, - import_hint_name_table: u32, + import_lookup_table: ImportTable.Index, + import_address_table: ImportTable.Index, + import_hint_name_table: ImportTable.Index, global: GlobalMapIndex, nav: NavMapIndex, uav: UavMapIndex, @@ -222,13 +221,15 @@ pub const DataDirectory = enum { delay_import_descriptor, clr_runtime_header, reserved, + + pub const len = @typeInfo(DataDirectory).@"enum".fields.len; }; pub const ImportTable = struct { - directory_table_ni: MappedFile.Node.Index, - dlls: std.AutoArrayHashMapUnmanaged(void, Dll), + ni: MappedFile.Node.Index, + entries: std.AutoArrayHashMapUnmanaged(void, Entry), - pub const Dll = struct { + pub const Entry = struct { import_lookup_table_ni: MappedFile.Node.Index, import_address_table_si: Symbol.Index, import_hint_name_table_ni: MappedFile.Node.Index, @@ -241,7 +242,7 @@ pub const ImportTable = struct { pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { const coff = adapter.coff; - const dll_name = coff.import_table.dlls.values()[rhs_index] + const dll_name = coff.import_table.entries.values()[rhs_index] .import_hint_name_table_ni.sliceConst(&coff.mf); return std.mem.startsWith(u8, dll_name, lhs_key) and std.mem.startsWith(u8, dll_name[lhs_key.len..], ".dll\x00"); @@ -252,6 +253,14 @@ pub const ImportTable = struct { return std.array_hash_map.hashString(key); } }; + + pub const Index = enum(u32) { + _, + + pub fn get(import_index: ImportTable.Index, coff: *Coff) *Entry { + return &coff.import_table.entries.values()[@intFromEnum(import_index)]; + } + }; }; pub const String = enum(u32) { @@ -294,9 +303,9 @@ pub const Symbol = struct { /// Relocations targeting this symbol target_relocs: Reloc.Index, section_number: SectionNumber, - data_directory: ?DataDirectory, unused0: u32 = 0, unused1: u32 = 0, + unused2: u16 = 0, pub const SectionNumber = enum(i16) { UNDEFINED = 0, @@ -340,8 +349,10 @@ pub const Symbol = struct { pub fn flushMoved(si: Symbol.Index, coff: *Coff) void { const sym = si.get(coff); sym.rva = coff.computeNodeRva(sym.ni); - if (si == coff.entry_hack) + if (si == coff.entry_hack) { + @branchHint(.unlikely); coff.targetStore(&coff.optionalHeaderStandardPtr().address_of_entry_point, sym.rva); + } si.applyLocationRelocs(coff); si.applyTargetRelocs(coff); } @@ -565,6 +576,8 @@ fn create( ) !*Coff { const target = &comp.root_mod.resolved_target.result; assert(target.ofmt == .coff); + if (target.cpu.arch.endian() != comptime targetEndian(undefined)) + return error.UnsupportedCOFFArchitecture; const is_image = switch (comp.config.output_mode) { .Exe => true, .Lib => switch (comp.config.link_mode) { @@ -613,12 +626,11 @@ fn create( .allow_shlib_undefined = false, .stack_size = 0, }, - .endian = target.cpu.arch.endian(), .mf = try .init(file, comp.gpa), .nodes = .empty, .import_table = .{ - .directory_table_ni = .none, - .dlls = .empty, + .ni = .none, + .entries = .empty, }, .strings = .empty, .string_bytes = .empty, @@ -654,7 +666,7 @@ pub fn deinit(coff: *Coff) void { const gpa = coff.base.comp.gpa; coff.mf.deinit(gpa); coff.nodes.deinit(gpa); - coff.import_table.dlls.deinit(gpa); + coff.import_table.entries.deinit(gpa); coff.strings.deinit(gpa); coff.string_bytes.deinit(gpa); coff.section_table.deinit(gpa); @@ -687,9 +699,8 @@ fn initHeaders( _ => unreachable, inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))), } else 0; - const data_directories_len = @typeInfo(DataDirectory).@"enum".fields.len; const data_directories_size: u16 = if (is_image) - @sizeOf(std.coff.ImageDataDirectory) * data_directories_len + @sizeOf(std.coff.ImageDataDirectory) * DataDirectory.len else 0; @@ -724,7 +735,7 @@ fn initHeaders( })); coff.nodes.appendAssumeCapacity(.coff_header); { - const coff_header: *std.coff.Header = @ptrCast(@alignCast(coff_header_ni.slice(&coff.mf))); + const coff_header = coff.headerPtr(); coff_header.* = .{ .machine = machine, .number_of_sections = 0, @@ -751,11 +762,9 @@ fn initHeaders( .fixed = true, })); coff.nodes.appendAssumeCapacity(.optional_header); - if (is_image) switch (magic) { - _ => unreachable, - .PE32 => { - const optional_header: *std.coff.OptionalHeader.PE32 = - @ptrCast(@alignCast(optional_header_ni.slice(&coff.mf))); + coff.targetStore(&coff.optionalHeaderStandardPtr().magic, magic); + if (is_image) switch (coff.optionalHeaderPtr()) { + .PE32 => |optional_header| { optional_header.* = .{ .standard = .{ .magic = .PE32, @@ -800,15 +809,13 @@ fn initHeaders( .size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_commit = default_size_of_heap_commit, .loader_flags = 0, - .number_of_rva_and_sizes = data_directories_len, + .number_of_rva_and_sizes = DataDirectory.len, }; if (target_endian != native_endian) std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header); }, - .@"PE32+" => { - const header: *std.coff.OptionalHeader.@"PE32+" = - @ptrCast(@alignCast(optional_header_ni.slice(&coff.mf))); - header.* = .{ + .@"PE32+" => |optional_header| { + optional_header.* = .{ .standard = .{ .magic = .@"PE32+", .major_linker_version = 0, @@ -851,10 +858,10 @@ fn initHeaders( .size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_commit = default_size_of_heap_commit, .loader_flags = 0, - .number_of_rva_and_sizes = data_directories_len, + .number_of_rva_and_sizes = DataDirectory.len, }; if (target_endian != native_endian) - std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", header); + std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", optional_header); }, }; @@ -866,11 +873,10 @@ fn initHeaders( })); coff.nodes.appendAssumeCapacity(.data_directories); { - const data_directories: *[data_directories_len]std.coff.ImageDataDirectory = - @ptrCast(@alignCast(data_directories_ni.slice(&coff.mf))); + const data_directories = coff.dataDirectorySlice(); @memset(data_directories, .{ .virtual_address = 0, .size = 0 }); - if (target_endian != native_endian) for (data_directories) |*data_directory| - std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, data_directory); + if (target_endian != native_endian) + std.mem.byteSwapAllFields([DataDirectory.len]std.coff.ImageDataDirectory, data_directories); } const section_table_ni = Node.known.section_table; @@ -890,33 +896,29 @@ fn initHeaders( .loc_relocs = .none, .target_relocs = .none, .section_number = .UNDEFINED, - .data_directory = null, }; - assert(try coff.addSection(".data", null, .{ + assert(try coff.addSection(".data", .{ .CNT_INITIALIZED_DATA = true, .MEM_READ = true, .MEM_WRITE = true, }) == .data); - assert(try coff.addSection(".idata", .import_table, .{ + assert(try coff.addSection(".idata", .{ .CNT_INITIALIZED_DATA = true, .MEM_READ = true, }) == .idata); - assert(try coff.addSection(".rdata", null, .{ + assert(try coff.addSection(".rdata", .{ .CNT_INITIALIZED_DATA = true, .MEM_READ = true, }) == .rdata); - assert(try coff.addSection(".text", null, .{ + assert(try coff.addSection(".text", .{ .CNT_CODE = true, .MEM_EXECUTE = true, .MEM_READ = true, }) == .text); - coff.import_table.directory_table_ni = try coff.mf.addLastChildNode( + coff.import_table.ni = try coff.mf.addLastChildNode( gpa, Symbol.Index.idata.node(coff), - .{ - .alignment = .@"4", - .fixed = true, - }, + .{ .alignment = .@"4" }, ); coff.nodes.appendAssumeCapacity(.import_directory_table); assert(coff.symbol_table.items.len == Symbol.Index.known_count); @@ -926,22 +928,37 @@ fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node { return coff.nodes.get(@intFromEnum(ni)); } fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 { - var section_offset: u32 = 0; - var parent_ni = ni; - while (true) { - assert(parent_ni != .none); - switch (coff.getNode(parent_ni)) { - else => {}, - .section => |si| return si.get(coff).rva + section_offset, - } - const parent_offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf); - section_offset += @intCast(parent_offset); - parent_ni = parent_ni.parent(&coff.mf); - } + const parent_rva = parent_rva: { + const parent_si = switch (coff.getNode(ni.parent(&coff.mf))) { + .file, + .header, + .signature, + .coff_header, + .optional_header, + .data_directories, + .section_table, + => unreachable, + .section => |si| si, + .import_directory_table => unreachable, + .import_lookup_table => |import_index| break :parent_rva coff.targetLoad( + &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva, + ), + .import_address_table => |import_index| break :parent_rva coff.targetLoad( + &coff.importDirectoryEntryPtr(import_index).import_address_table_rva, + ), + .import_hint_name_table => |import_index| break :parent_rva coff.targetLoad( + &coff.importDirectoryEntryPtr(import_index).name_rva, + ), + inline .global, .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(coff), + }; + break :parent_rva parent_si.get(coff).rva; + }; + const offset, _ = ni.location(&coff.mf).resolve(&coff.mf); + return @intCast(parent_rva + offset); } -pub inline fn targetEndian(coff: *const Coff) std.builtin.Endian { - return coff.endian; +pub inline fn targetEndian(_: *const Coff) std.builtin.Endian { + return .little; } fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { const Child = @typeInfo(@TypeOf(ptr)).pointer.child; @@ -1004,14 +1021,27 @@ pub fn optionalHeaderField( }; } -pub fn dataDirectoriesSlice(coff: *Coff) []std.coff.ImageDataDirectory { +pub fn dataDirectorySlice(coff: *Coff) *[DataDirectory.len]std.coff.ImageDataDirectory { return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf))); } +pub fn dataDirectoryPtr(coff: *Coff, data_directory: DataDirectory) *std.coff.ImageDataDirectory { + return &coff.dataDirectorySlice()[@intFromEnum(data_directory)]; +} pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader { return @ptrCast(@alignCast(Node.known.section_table.slice(&coff.mf))); } +pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry { + return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf))); +} +pub fn importDirectoryEntryPtr( + coff: *Coff, + import_index: ImportTable.Index, +) *std.coff.ImportDirectoryEntry { + return &coff.importDirectoryTableSlice()[@intFromEnum(import_index)]; +} + fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index { defer coff.symbol_table.addOneAssumeCapacity().* = .{ .ni = .none, @@ -1020,7 +1050,6 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index { .loc_relocs = .none, .target_relocs = .none, .section_number = .UNDEFINED, - .data_directory = null, }; return @enumFromInt(coff.symbol_table.items.len); } @@ -1139,12 +1168,7 @@ pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol. return coff.optionalHeaderField(.image_base) + target_si.get(coff).rva; } -fn addSection( - coff: *Coff, - name: []const u8, - maybe_data_directory: ?DataDirectory, - flags: std.coff.SectionHeader.Flags, -) !Symbol.Index { +fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index { const gpa = coff.base.comp.gpa; try coff.nodes.ensureUnusedCapacity(gpa, 1); try coff.section_table.ensureUnusedCapacity(gpa, 1); @@ -1179,7 +1203,6 @@ fn addSection( sym.ni = ni; sym.rva = rva; sym.section_number = @enumFromInt(section_table_len); - sym.data_directory = maybe_data_directory; } const section = §ion_table[section_index]; section.* = .{ @@ -1198,11 +1221,6 @@ fn addSection( @memset(section.name[name.len..], 0); if (coff.targetEndian() != native_endian) std.mem.byteSwapAllFields(std.coff.SectionHeader, section); - if (maybe_data_directory) |data_directory| - coff.dataDirectoriesSlice()[@intFromEnum(data_directory)] = .{ - .virtual_address = section.virtual_address, - .size = section.virtual_size, - }; switch (coff.optionalHeaderPtr()) { inline else => |optional_header| coff.targetStore( &optional_header.size_of_image, @@ -1651,15 +1669,15 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void { .@"PE32+" => .{ 8, .@"8" }, }; - const gop = try coff.import_table.dlls.getOrPutAdapted( + const gop = try coff.import_table.entries.getOrPutAdapted( gpa, lib_name, ImportTable.Adapter{ .coff = coff }, ); const import_hint_name_align: std.mem.Alignment = .@"2"; if (!gop.found_existing) { - errdefer _ = coff.import_table.dlls.pop(); - try coff.import_table.directory_table_ni.resize( + errdefer _ = coff.import_table.entries.pop(); + try coff.import_table.ni.resize( &coff.mf, gpa, @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2), @@ -1701,13 +1719,12 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void { @memcpy(import_hint_name_slice[0..lib_name.len], lib_name); @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll"); @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0); - coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @intCast(gop.index) }); - coff.nodes.appendAssumeCapacity(.{ .import_address_table = @intCast(gop.index) }); - coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @intCast(gop.index) }); + coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @enumFromInt(gop.index) }); + coff.nodes.appendAssumeCapacity(.{ .import_address_table = @enumFromInt(gop.index) }); + coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @enumFromInt(gop.index) }); - const import_directory_table: []std.coff.ImportDirectoryEntry = - @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf))); - import_directory_table[gop.index..][0..2].* = .{ .{ + const import_directory_entries = coff.importDirectoryTableSlice()[gop.index..][0..2]; + import_directory_entries.* = .{ .{ .import_lookup_table_rva = coff.computeNodeRva(import_lookup_table_ni), .time_date_stamp = 0, .forwarder_chain = 0, @@ -1720,6 +1737,8 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void { .name_rva = 0, .import_address_table_rva = 0, } }; + if (target_endian != native_endian) + std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries); } const import_symbol_index = gop.value_ptr.len; gop.value_ptr.len = import_symbol_index + 1; @@ -1845,42 +1864,44 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { } fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void { - const node = coff.getNode(ni); - switch (node) { - else => |tag| @panic(@tagName(tag)), + switch (coff.getNode(ni)) { + .file, + .header, + .signature, + .coff_header, + .optional_header, + .data_directories, + .section_table, + => unreachable, .section => |si| return coff.targetStore( &si.get(coff).section_number.header(coff).pointer_to_raw_data, @intCast(ni.fileLocation(&coff.mf, false).offset), ), - .import_directory_table => {}, - .import_lookup_table => |import_directory_table_index| { - const import_directory_table: []std.coff.ImportDirectoryEntry = - @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf))); - const import_directory_entry = &import_directory_table[import_directory_table_index]; - coff.targetStore(&import_directory_entry.import_lookup_table_rva, coff.computeNodeRva(ni)); - }, - .import_address_table => |import_directory_table_index| { - const import_directory_table: []std.coff.ImportDirectoryEntry = - @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf))); - const import_directory_entry = &import_directory_table[import_directory_table_index]; - coff.targetStore(&import_directory_entry.import_lookup_table_rva, coff.computeNodeRva(ni)); - const import_address_table_si = - coff.import_table.dlls.values()[import_directory_table_index].import_address_table_si; + .import_directory_table => coff.targetStore( + &coff.dataDirectoryPtr(.import_table).virtual_address, + coff.computeNodeRva(ni), + ), + .import_lookup_table => |import_index| coff.targetStore( + &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva, + coff.computeNodeRva(ni), + ), + .import_address_table => |import_index| { + const import_address_table_si = import_index.get(coff).import_address_table_si; import_address_table_si.flushMoved(coff); coff.targetStore( - &import_directory_entry.import_address_table_rva, + &coff.importDirectoryEntryPtr(import_index).import_address_table_rva, import_address_table_si.get(coff).rva, ); }, - .import_hint_name_table => |import_directory_table_index| { + .import_hint_name_table => |import_index| { const target_endian = coff.targetEndian(); const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic); - const import_directory_table: []std.coff.ImportDirectoryEntry = - @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf))); - const import_directory_entry = &import_directory_table[import_directory_table_index]; const import_hint_name_rva = coff.computeNodeRva(ni); - coff.targetStore(&import_directory_entry.name_rva, import_hint_name_rva); - const import_entry = &coff.import_table.dlls.values()[import_directory_table_index]; + coff.targetStore( + &coff.importDirectoryEntryPtr(import_index).name_rva, + import_hint_name_rva, + ); + const import_entry = import_index.get(coff); const import_lookup_slice = import_entry.import_lookup_table_ni.slice(&coff.mf); const import_address_slice = import_entry.import_address_table_si.node(coff).slice(&coff.mf); @@ -1930,9 +1951,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void { fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { _, const size = ni.location(&coff.mf).resolve(&coff.mf); - const node = coff.getNode(ni); - switch (node) { - else => |tag| @panic(@tagName(tag)), + switch (coff.getNode(ni)) { .file => {}, .header => { switch (coff.optionalHeaderPtr()) { @@ -1950,12 +1969,12 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { ), ); }, + .signature, .coff_header, .optional_header, .data_directories => unreachable, .section_table => {}, .section => |si| { const sym = si.get(coff); - const section_table = coff.sectionTableSlice(); const section_index = sym.section_number.toIndex(); - const section = §ion_table[section_index]; + const section = &coff.sectionTableSlice()[section_index]; coff.targetStore(§ion.size_of_raw_data, @intCast(size)); if (size > coff.targetLoad(§ion.virtual_size)) { const virtual_size = std.mem.alignForward( @@ -1964,13 +1983,13 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { coff.optionalHeaderField(.section_alignment), ); coff.targetStore(§ion.virtual_size, virtual_size); - if (sym.data_directory) |data_directory| - coff.dataDirectoriesSlice()[@intFromEnum(data_directory)].size = - section.virtual_size; try coff.virtualSlide(section_index + 1, sym.rva + virtual_size); } }, - .import_directory_table, + .import_directory_table => coff.targetStore( + &coff.dataDirectoryPtr(.import_table).size, + @intCast(size), + ), .import_lookup_table, .import_address_table, .import_hint_name_table, @@ -1982,20 +2001,15 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void { => {}, } } - fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void { - const section_table = coff.sectionTableSlice(); var rva = start_rva; for ( coff.section_table.items[start_section_index..], - section_table[start_section_index..], + coff.sectionTableSlice()[start_section_index..], ) |section_si, *section| { const section_sym = section_si.get(coff); section_sym.rva = rva; coff.targetStore(§ion.virtual_address, rva); - if (section_sym.data_directory) |data_directory| - coff.dataDirectoriesSlice()[@intFromEnum(data_directory)].virtual_address = - section.virtual_address; try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf); rva += coff.targetLoad(§ion.virtual_size); } @@ -2100,9 +2114,8 @@ pub fn printNode( .import_lookup_table, .import_address_table, .import_hint_name_table, - => |import_directory_table_index| try w.print("({s})", .{ - std.mem.sliceTo(coff.import_table.dlls.values()[import_directory_table_index] - .import_hint_name_table_ni.sliceConst(&coff.mf), 0), + => |import_index| try w.print("({s})", .{ + std.mem.sliceTo(import_index.get(coff).import_hint_name_table_ni.sliceConst(&coff.mf), 0), }), .global => |gmi| { const gn = gmi.globalName(coff); diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 90e7dfbc67..5abc2621bc 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -1,6 +1,8 @@ base: link.File, mf: MappedFile, +known: Node.Known, nodes: std.MultiArrayList(Node), +phdrs: std.ArrayList(MappedFile.Node.Index), symtab: std.ArrayList(Symbol), shstrtab: StringTable, strtab: StringTable, @@ -85,23 +87,15 @@ pub const Node = union(enum) { } }; - pub const Tag = @typeInfo(Node).@"union".tag_type.?; + pub const Known = struct { + pub const rodata: MappedFile.Node.Index = @enumFromInt(1); + pub const ehdr: MappedFile.Node.Index = @enumFromInt(2); + pub const phdr: MappedFile.Node.Index = @enumFromInt(3); + pub const shdr: MappedFile.Node.Index = @enumFromInt(4); + pub const text: MappedFile.Node.Index = @enumFromInt(5); + pub const data: MappedFile.Node.Index = @enumFromInt(6); - const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len; - const known = known: { - const Known = enum { - file, - seg_rodata, - ehdr, - phdr, - shdr, - seg_text, - seg_data, - }; - var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined; - for (@typeInfo(Known).@"enum".fields) |field| - @field(mut_known, field.name) = @enumFromInt(field.value); - break :known mut_known; + tls: MappedFile.Node.Index, }; comptime { @@ -231,6 +225,21 @@ pub const Symbol = struct { } } + pub fn flushMoved(si: Symbol.Index, elf: *Elf) void { + const value = elf.computeNodeVAddr(si.node(elf)); + switch (elf.symPtr(si)) { + inline else => |sym, class| { + elf.targetStore(&sym.value, @intCast(value)); + if (si == elf.entry_hack) { + @branchHint(.unlikely); + @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value; + } + }, + } + si.applyLocationRelocs(elf); + si.applyTargetRelocs(elf); + } + pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void { for (elf.relocs.items[@intFromEnum(si.get(elf).loc_relocs)..]) |*reloc| { if (reloc.loc != si) break; @@ -290,8 +299,8 @@ pub const Reloc = extern struct { }; pub fn apply(reloc: *const Reloc, elf: *Elf) void { - const target_endian = elf.targetEndian(); - switch (reloc.loc.get(elf).ni) { + const loc_ni = reloc.loc.get(elf).ni; + switch (loc_ni) { .none => return, else => |ni| if (ni.hasMoved(&elf.mf)) return, } @@ -299,68 +308,47 @@ pub const Reloc = extern struct { .none => return, else => |ni| if (ni.hasMoved(&elf.mf)) return, } - switch (elf.shdrSlice()) { - inline else => |shdr, class| { - const sym = @field(elf.symSlice(), @tagName(class)); - const loc_sym = &sym[@intFromEnum(reloc.loc)]; - const loc_shndx = - std.mem.toNative(@TypeOf(loc_sym.shndx), loc_sym.shndx, target_endian); + const loc_slice = loc_ni.slice(&elf.mf)[@intCast(reloc.offset)..]; + const target_endian = elf.targetEndian(); + switch (elf.symtabSlice()) { + inline else => |symtab, class| { + const loc_sym = &symtab[@intFromEnum(reloc.loc)]; + const loc_shndx = elf.targetLoad(&loc_sym.shndx); assert(loc_shndx != std.elf.SHN_UNDEF); - const loc_sh = &shdr[loc_shndx]; - const loc_value = std.mem.toNative( - @TypeOf(loc_sym.value), - loc_sym.value, - target_endian, - ) + reloc.offset; - const loc_sh_addr = - std.mem.toNative(@TypeOf(loc_sh.addr), loc_sh.addr, target_endian); - const loc_sh_offset = - std.mem.toNative(@TypeOf(loc_sh.offset), loc_sh.offset, target_endian); - const loc_file_offset: usize = @intCast(loc_value - loc_sh_addr + loc_sh_offset); - const target_sym = &sym[@intFromEnum(reloc.target)]; - const target_value = std.mem.toNative( - @TypeOf(target_sym.value), - target_sym.value, - target_endian, - ) +% @as(u64, @bitCast(reloc.addend)); + const loc_value = elf.targetLoad(&loc_sym.value) + reloc.offset; + const target_sym = &symtab[@intFromEnum(reloc.target)]; + const target_value = + elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend)); switch (elf.ehdrField(.machine)) { else => |machine| @panic(@tagName(machine)), .X86_64 => switch (reloc.type.X86_64) { else => |kind| @panic(@tagName(kind)), .@"64" => std.mem.writeInt( u64, - elf.mf.contents[loc_file_offset..][0..8], + loc_slice[0..8], target_value, target_endian, ), .PC32 => std.mem.writeInt( i32, - elf.mf.contents[loc_file_offset..][0..4], + loc_slice[0..4], @intCast(@as(i64, @bitCast(target_value -% loc_value))), target_endian, ), .@"32" => std.mem.writeInt( u32, - elf.mf.contents[loc_file_offset..][0..4], + loc_slice[0..4], @intCast(target_value), target_endian, ), .TPOFF32 => { const phdr = @field(elf.phdrSlice(), @tagName(class)); - const ph = &phdr[4]; - assert(std.mem.toNative( - @TypeOf(ph.type), - ph.type, - target_endian, - ) == std.elf.PT_TLS); + const ph = &phdr[elf.getNode(elf.known.tls).segment]; + assert(elf.targetLoad(&ph.type) == std.elf.PT_TLS); std.mem.writeInt( i32, - elf.mf.contents[loc_file_offset..][0..4], - @intCast(@as(i64, @bitCast(target_value -% std.mem.toNative( - @TypeOf(ph.memsz), - ph.memsz, - target_endian, - )))), + loc_slice[0..4], + @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))), target_endian, ); }, @@ -475,7 +463,11 @@ fn create( .stack_size = 0, }, .mf = try .init(file, comp.gpa), + .known = .{ + .tls = .none, + }, .nodes = .empty, + .phdrs = .empty, .symtab = .empty, .shstrtab = .{ .map = .empty, @@ -488,7 +480,7 @@ fn create( .globals = .empty, .navs = .empty, .uavs = .empty, - .lazy = .initFill(.{ + .lazy = comptime .initFill(.{ .map = .empty, .pending_index = 0, }), @@ -498,18 +490,7 @@ fn create( }; errdefer elf.deinit(); - switch (class) { - .NONE, _ => unreachable, - inline else => |ct_class| try elf.initHeaders( - ct_class, - data, - osabi, - @"type", - machine, - maybe_interp, - ), - } - + try elf.initHeaders(class, data, osabi, @"type", machine, maybe_interp); return elf; } @@ -517,6 +498,7 @@ pub fn deinit(elf: *Elf) void { const gpa = elf.base.comp.gpa; elf.mf.deinit(gpa); elf.nodes.deinit(gpa); + elf.phdrs.deinit(gpa); elf.symtab.deinit(gpa); elf.shstrtab.map.deinit(gpa); elf.strtab.map.deinit(gpa); @@ -531,7 +513,7 @@ pub fn deinit(elf: *Elf) void { fn initHeaders( elf: *Elf, - comptime class: std.elf.CLASS, + class: std.elf.CLASS, data: std.elf.DATA, osabi: std.elf.OSABI, @"type": std.elf.ET, @@ -540,16 +522,10 @@ fn initHeaders( ) !void { const comp = elf.base.comp; const gpa = comp.gpa; - const ElfN = switch (class) { - .NONE, _ => comptime unreachable, - .@"32" => std.elf.Elf32, - .@"64" => std.elf.Elf64, - }; - const addr_align: std.mem.Alignment = comptime .fromByteUnits(@sizeOf(ElfN.Addr)); - const target_endian: std.builtin.Endian = switch (data) { + const addr_align: std.mem.Alignment = switch (class) { .NONE, _ => unreachable, - .@"2LSB" => .little, - .@"2MSB" => .big, + .@"32" => .@"4", + .@"64" => .@"8", }; var phnum: u32 = 0; @@ -570,218 +546,257 @@ fn initHeaders( break :phndx phnum; } else undefined; - try elf.nodes.ensureTotalCapacity(gpa, Node.known_count); + const expected_nodes_len = 15; + try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len); + try elf.phdrs.resize(gpa, phnum); elf.nodes.appendAssumeCapacity(.file); - const seg_rodata_ni = Node.known.seg_rodata; - assert(seg_rodata_ni == try elf.mf.addOnlyChildNode(gpa, .root, .{ + assert(Node.Known.rodata == try elf.mf.addOnlyChildNode(gpa, .root, .{ .alignment = elf.mf.flags.block_size, .fixed = true, .moved = true, + .bubbles_moved = false, })); elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx }); + elf.phdrs.items[rodata_phndx] = Node.Known.rodata; - const ehdr_ni = Node.known.ehdr; - assert(ehdr_ni == try elf.mf.addOnlyChildNode(gpa, seg_rodata_ni, .{ - .size = @sizeOf(ElfN.Ehdr), - .alignment = addr_align, - .fixed = true, - })); - elf.nodes.appendAssumeCapacity(.ehdr); - { - const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(ehdr_ni.slice(&elf.mf))); - const EI = std.elf.EI; - @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC); - ehdr.ident[EI.CLASS] = @intFromEnum(class); - ehdr.ident[EI.DATA] = @intFromEnum(data); - ehdr.ident[EI.VERSION] = 1; - ehdr.ident[EI.OSABI] = @intFromEnum(osabi); - ehdr.ident[EI.ABIVERSION] = 0; - @memset(ehdr.ident[EI.PAD..], 0); - ehdr.type = @"type"; - ehdr.machine = machine; - ehdr.version = 1; - ehdr.entry = 0; - ehdr.phoff = 0; - ehdr.shoff = 0; - ehdr.flags = 0; - ehdr.ehsize = @sizeOf(ElfN.Ehdr); - ehdr.phentsize = @sizeOf(ElfN.Phdr); - ehdr.phnum = @min(phnum, std.elf.PN_XNUM); - ehdr.shentsize = @sizeOf(ElfN.Shdr); - ehdr.shnum = 1; - ehdr.shstrndx = 0; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); + switch (class) { + .NONE, _ => unreachable, + inline else => |ct_class| { + const ElfN = switch (ct_class) { + .NONE, _ => comptime unreachable, + .@"32" => std.elf.Elf32, + .@"64" => std.elf.Elf64, + }; + + assert(Node.Known.ehdr == try elf.mf.addOnlyChildNode(gpa, Node.Known.rodata, .{ + .size = @sizeOf(ElfN.Ehdr), + .alignment = addr_align, + .fixed = true, + })); + elf.nodes.appendAssumeCapacity(.ehdr); + + const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(Node.Known.ehdr.slice(&elf.mf))); + const EI = std.elf.EI; + @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC); + ehdr.ident[EI.CLASS] = @intFromEnum(class); + ehdr.ident[EI.DATA] = @intFromEnum(data); + ehdr.ident[EI.VERSION] = 1; + ehdr.ident[EI.OSABI] = @intFromEnum(osabi); + ehdr.ident[EI.ABIVERSION] = 0; + @memset(ehdr.ident[EI.PAD..], 0); + ehdr.type = @"type"; + ehdr.machine = machine; + ehdr.version = 1; + ehdr.entry = 0; + ehdr.phoff = 0; + ehdr.shoff = 0; + ehdr.flags = 0; + ehdr.ehsize = @sizeOf(ElfN.Ehdr); + ehdr.phentsize = @sizeOf(ElfN.Phdr); + ehdr.phnum = @min(phnum, std.elf.PN_XNUM); + ehdr.shentsize = @sizeOf(ElfN.Shdr); + ehdr.shnum = 1; + ehdr.shstrndx = std.elf.SHN_UNDEF; + if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); + }, } - const phdr_ni = Node.known.phdr; - assert(phdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{ - .size = @sizeOf(ElfN.Phdr) * phnum, + assert(Node.Known.phdr == try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{ + .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum), .alignment = addr_align, .moved = true, .resized = true, + .bubbles_moved = false, })); elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx }); + elf.phdrs.items[phdr_phndx] = Node.Known.phdr; - const shdr_ni = Node.known.shdr; - assert(shdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{ - .size = @sizeOf(ElfN.Shdr), + assert(Node.Known.shdr == try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{ + .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum), .alignment = addr_align, })); elf.nodes.appendAssumeCapacity(.shdr); - const seg_text_ni = Node.known.seg_text; - assert(seg_text_ni == try elf.mf.addLastChildNode(gpa, .root, .{ + assert(Node.Known.text == try elf.mf.addLastChildNode(gpa, .root, .{ .alignment = elf.mf.flags.block_size, .moved = true, + .bubbles_moved = false, })); elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx }); + elf.phdrs.items[text_phndx] = Node.Known.text; - const seg_data_ni = Node.known.seg_data; - assert(seg_data_ni == try elf.mf.addLastChildNode(gpa, .root, .{ + assert(Node.Known.data == try elf.mf.addLastChildNode(gpa, .root, .{ .alignment = elf.mf.flags.block_size, .moved = true, + .bubbles_moved = false, })); elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx }); + elf.phdrs.items[data_phndx] = Node.Known.data; - assert(elf.nodes.len == Node.known_count); + var ph_vaddr: u32 = switch (elf.ehdrField(.type)) { + else => 0, + .EXEC => switch (elf.ehdrField(.machine)) { + .@"386" => 0x400000, + .AARCH64, .X86_64 => 0x200000, + .PPC, .PPC64 => 0x10000000, + .S390, .S390_OLD => 0x1000000, + .OLD_SPARCV9, .SPARCV9 => 0x100000, + else => 0x10000, + }, + }; + switch (class) { + .NONE, _ => unreachable, + inline else => |ct_class| { + const ElfN = switch (ct_class) { + .NONE, _ => comptime unreachable, + .@"32" => std.elf.Elf32, + .@"64" => std.elf.Elf64, + }; + const target_endian = elf.targetEndian(); - { - const phdr: []ElfN.Phdr = @ptrCast(@alignCast(phdr_ni.slice(&elf.mf))); - const ph_phdr = &phdr[phdr_phndx]; - ph_phdr.* = .{ - .type = std.elf.PT_PHDR, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = @intCast(phdr_ni.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr); - - if (maybe_interp) |_| { - const ph_interp = &phdr[interp_phndx]; - ph_interp.* = .{ - .type = std.elf.PT_INTERP, + const phdr: []ElfN.Phdr = @ptrCast(@alignCast(Node.Known.phdr.slice(&elf.mf))); + const ph_phdr = &phdr[phdr_phndx]; + ph_phdr.* = .{ + .type = std.elf.PT_PHDR, .offset = 0, .vaddr = 0, .paddr = 0, .filesz = 0, .memsz = 0, .flags = .{ .R = true }, - .@"align" = 1, + .@"align" = @intCast(Node.Known.phdr.alignment(&elf.mf).toByteUnits()), }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp); - } + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr); - const ph_rodata = &phdr[rodata_phndx]; - ph_rodata.* = .{ - .type = std.elf.PT_NULL, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true }, - .@"align" = @intCast(seg_rodata_ni.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata); + if (maybe_interp) |_| { + const ph_interp = &phdr[interp_phndx]; + ph_interp.* = .{ + .type = std.elf.PT_INTERP, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = 1, + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp); + } - const ph_text = &phdr[text_phndx]; - ph_text.* = .{ - .type = std.elf.PT_NULL, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .X = true }, - .@"align" = @intCast(seg_text_ni.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text); - - const ph_data = &phdr[data_phndx]; - ph_data.* = .{ - .type = std.elf.PT_NULL, - .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, - .flags = .{ .R = true, .W = true }, - .@"align" = @intCast(seg_data_ni.alignment(&elf.mf).toByteUnits()), - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data); - - if (comp.config.any_non_single_threaded) { - const ph_tls = &phdr[tls_phndx]; - ph_tls.* = .{ - .type = std.elf.PT_TLS, + _, const rodata_size = Node.Known.rodata.location(&elf.mf).resolve(&elf.mf); + const ph_rodata = &phdr[rodata_phndx]; + ph_rodata.* = .{ + .type = std.elf.PT_NULL, .offset = 0, - .vaddr = 0, - .paddr = 0, - .filesz = 0, - .memsz = 0, + .vaddr = ph_vaddr, + .paddr = ph_vaddr, + .filesz = @intCast(rodata_size), + .memsz = @intCast(rodata_size), .flags = .{ .R = true }, - .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()), + .@"align" = @intCast(Node.Known.rodata.alignment(&elf.mf).toByteUnits()), }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls); - } + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata); + ph_vaddr += @intCast(rodata_size); - const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(shdr_ni.slice(&elf.mf))); - sh_null.* = .{ - .name = try elf.string(.shstrtab, ""), - .type = std.elf.SHT_NULL, - .flags = .{ .shf = .{} }, - .addr = 0, - .offset = 0, - .size = 0, - .link = 0, - .info = if (phnum >= std.elf.PN_XNUM) phnum else 0, - .addralign = 0, - .entsize = 0, - }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null); - } + _, const text_size = Node.Known.text.location(&elf.mf).resolve(&elf.mf); + const ph_text = &phdr[text_phndx]; + ph_text.* = .{ + .type = std.elf.PT_NULL, + .offset = 0, + .vaddr = ph_vaddr, + .paddr = ph_vaddr, + .filesz = @intCast(text_size), + .memsz = @intCast(text_size), + .flags = .{ .R = true, .X = true }, + .@"align" = @intCast(Node.Known.text.alignment(&elf.mf).toByteUnits()), + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text); + ph_vaddr += @intCast(text_size); - try elf.symtab.ensureTotalCapacity(gpa, 1); - elf.symtab.addOneAssumeCapacity().* = .{ - .ni = .none, - .loc_relocs = .none, - .target_relocs = .none, - .unused = 0, - }; - assert(try elf.addSection(seg_rodata_ni, .{ - .type = std.elf.SHT_SYMTAB, - .addralign = addr_align, - .entsize = @sizeOf(ElfN.Sym), - }) == .symtab); - const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf))); - symtab.* = .{ - .name = try elf.string(.strtab, ""), - .value = 0, - .size = 0, - .info = .{ - .type = .NOTYPE, - .bind = .LOCAL, + _, const data_size = Node.Known.data.location(&elf.mf).resolve(&elf.mf); + const ph_data = &phdr[data_phndx]; + ph_data.* = .{ + .type = std.elf.PT_NULL, + .offset = 0, + .vaddr = ph_vaddr, + .paddr = ph_vaddr, + .filesz = @intCast(data_size), + .memsz = @intCast(data_size), + .flags = .{ .R = true, .W = true }, + .@"align" = @intCast(Node.Known.data.alignment(&elf.mf).toByteUnits()), + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data); + ph_vaddr += @intCast(data_size); + + if (comp.config.any_non_single_threaded) { + const ph_tls = &phdr[tls_phndx]; + ph_tls.* = .{ + .type = std.elf.PT_TLS, + .offset = 0, + .vaddr = 0, + .paddr = 0, + .filesz = 0, + .memsz = 0, + .flags = .{ .R = true }, + .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()), + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls); + } + + const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(Node.Known.shdr.slice(&elf.mf))); + sh_null.* = .{ + .name = try elf.string(.shstrtab, ""), + .type = std.elf.SHT_NULL, + .flags = .{ .shf = .{} }, + .addr = 0, + .offset = 0, + .size = 0, + .link = 0, + .info = if (phnum >= std.elf.PN_XNUM) phnum else 0, + .addralign = 0, + .entsize = 0, + }; + if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null); + + try elf.symtab.ensureTotalCapacity(gpa, 1); + elf.symtab.addOneAssumeCapacity().* = .{ + .ni = .none, + .loc_relocs = .none, + .target_relocs = .none, + .unused = 0, + }; + assert(try elf.addSection(Node.Known.rodata, .{ + .type = std.elf.SHT_SYMTAB, + .addralign = addr_align, + .entsize = @sizeOf(ElfN.Sym), + }) == .symtab); + + const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf))); + symtab.* = .{ + .name = try elf.string(.strtab, ""), + .value = 0, + .size = 0, + .info = .{ + .type = .NOTYPE, + .bind = .LOCAL, + }, + .other = .{ + .visibility = .DEFAULT, + }, + .shndx = std.elf.SHN_UNDEF, + }; + + const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class)); + ehdr.shstrndx = ehdr.shnum; }, - .other = .{ - .visibility = .DEFAULT, - }, - .shndx = std.elf.SHN_UNDEF, - }; - { - const ehdr = @field(elf.ehdrPtr(), @tagName(class)); - ehdr.shstrndx = ehdr.shnum; } - assert(try elf.addSection(seg_rodata_ni, .{ + assert(try elf.addSection(Node.Known.rodata, .{ .type = std.elf.SHT_STRTAB, .addralign = elf.mf.flags.block_size, .entsize = 1, }) == .shstrtab); - assert(try elf.addSection(seg_rodata_ni, .{ + assert(try elf.addSection(Node.Known.rodata, .{ .type = std.elf.SHT_STRTAB, .addralign = elf.mf.flags.block_size, .entsize = 1, @@ -793,45 +808,17 @@ fn initHeaders( Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0; Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0; - assert(try elf.addSection(seg_rodata_ni, .{ - .name = ".rodata", - .flags = .{ .ALLOC = true }, - .addralign = elf.mf.flags.block_size, - }) == .rodata); - assert(try elf.addSection(seg_text_ni, .{ - .name = ".text", - .flags = .{ .ALLOC = true, .EXECINSTR = true }, - .addralign = elf.mf.flags.block_size, - }) == .text); - assert(try elf.addSection(seg_data_ni, .{ - .name = ".data", - .flags = .{ .WRITE = true, .ALLOC = true }, - .addralign = elf.mf.flags.block_size, - }) == .data); - if (comp.config.any_non_single_threaded) { - try elf.nodes.ensureUnusedCapacity(gpa, 1); - const seg_tls_ni = try elf.mf.addLastChildNode(gpa, seg_data_ni, .{ - .alignment = elf.mf.flags.block_size, - .moved = true, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); - - assert(try elf.addSection(seg_tls_ni, .{ - .name = ".tdata", - .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, - .addralign = elf.mf.flags.block_size, - }) == .tdata); - } if (maybe_interp) |interp| { try elf.nodes.ensureUnusedCapacity(gpa, 1); - const seg_interp_ni = try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{ + const interp_ni = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{ .size = interp.len + 1, .moved = true, .resized = true, }); elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx }); + elf.phdrs.items[interp_phndx] = interp_ni; - const sec_interp_si = try elf.addSection(seg_interp_ni, .{ + const sec_interp_si = try elf.addSection(interp_ni, .{ .name = ".interp", .size = @intCast(interp.len + 1), .flags = .{ .ALLOC = true }, @@ -840,28 +827,99 @@ fn initHeaders( @memcpy(sec_interp[0..interp.len], interp); sec_interp[interp.len] = 0; } + assert(try elf.addSection(Node.Known.rodata, .{ + .name = ".rodata", + .flags = .{ .ALLOC = true }, + .addralign = elf.mf.flags.block_size, + }) == .rodata); + assert(try elf.addSection(Node.Known.text, .{ + .name = ".text", + .flags = .{ .ALLOC = true, .EXECINSTR = true }, + .addralign = elf.mf.flags.block_size, + }) == .text); + assert(try elf.addSection(Node.Known.data, .{ + .name = ".data", + .flags = .{ .WRITE = true, .ALLOC = true }, + .addralign = elf.mf.flags.block_size, + }) == .data); + if (comp.config.any_non_single_threaded) { + try elf.nodes.ensureUnusedCapacity(gpa, 1); + elf.known.tls = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{ + .alignment = elf.mf.flags.block_size, + .moved = true, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); + elf.phdrs.items[tls_phndx] = elf.known.tls; + + assert(try elf.addSection(elf.known.tls, .{ + .name = ".tdata", + .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, + .addralign = elf.mf.flags.block_size, + }) == .tdata); + } + assert(elf.nodes.len == expected_nodes_len); } -fn getNode(elf: *Elf, ni: MappedFile.Node.Index) Node { +fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node { return elf.nodes.get(@intFromEnum(ni)); } +fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { + const parent_vaddr = parent_vaddr: { + const parent_si = switch (elf.getNode(ni.parent(&elf.mf))) { + .file, .ehdr, .shdr => unreachable, + .segment => |phndx| break :parent_vaddr switch (elf.phdrSlice()) { + inline else => |ph| elf.targetLoad(&ph[phndx].vaddr), + }, + .section => |si| si, + inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf), + }; + break :parent_vaddr switch (elf.symPtr(parent_si)) { + inline else => |sym| elf.targetLoad(&sym.value), + }; + }; + const offset, _ = ni.location(&elf.mf).resolve(&elf.mf); + return parent_vaddr + offset; +} -pub fn identClass(elf: *Elf) std.elf.CLASS { +pub fn identClass(elf: *const Elf) std.elf.CLASS { return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]); } - -pub fn identData(elf: *Elf) std.elf.DATA { +pub fn identData(elf: *const Elf) std.elf.DATA { return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]); } -fn endianForData(data: std.elf.DATA) std.builtin.Endian { - return switch (data) { + +pub fn targetEndian(elf: *const Elf) std.builtin.Endian { + return switch (elf.identData()) { .NONE, _ => unreachable, .@"2LSB" => .little, .@"2MSB" => .big, }; } -pub fn targetEndian(elf: *Elf) std.builtin.Endian { - return endianForData(elf.identData()); +fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { + const Child = @typeInfo(@TypeOf(ptr)).pointer.child; + return switch (@typeInfo(Child)) { + else => @compileError(@typeName(Child)), + .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()), + .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))), + .@"struct" => |@"struct"| @bitCast( + elf.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))), + ), + }; +} +fn targetStore(elf: *const Elf, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void { + const Child = @typeInfo(@TypeOf(ptr)).pointer.child; + return switch (@typeInfo(Child)) { + else => @compileError(@typeName(Child)), + .int => ptr.* = std.mem.nativeTo(Child, val, elf.targetEndian()), + .@"enum" => |@"enum"| elf.targetStore( + @as(*@"enum".tag_type, @ptrCast(ptr)), + @intFromEnum(val), + ), + .@"struct" => |@"struct"| elf.targetStore( + @as(*@"struct".backing_integer.?, @ptrCast(ptr)), + @bitCast(val), + ), + }; } pub const EhdrPtr = union(std.elf.CLASS) { @@ -870,7 +928,7 @@ pub const EhdrPtr = union(std.elf.CLASS) { @"64": *std.elf.Elf64.Ehdr, }; pub fn ehdrPtr(elf: *Elf) EhdrPtr { - const slice = Node.known.ehdr.slice(&elf.mf); + const slice = Node.Known.ehdr.slice(&elf.mf); return switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| @unionInit( @@ -882,26 +940,12 @@ pub fn ehdrPtr(elf: *Elf) EhdrPtr { } pub fn ehdrField( elf: *Elf, - comptime field: enum { type, machine }, -) @FieldType(std.elf.Elf32.Ehdr, @tagName(field)) { - return @enumFromInt(std.mem.toNative( - @typeInfo(@FieldType(std.elf.Elf32.Ehdr, @tagName(field))).@"enum".tag_type, - @intFromEnum(switch (elf.ehdrPtr()) { - inline else => |ehdr| @field(ehdr, @tagName(field)), - }), - elf.targetEndian(), - )); -} - -fn baseAddrForType(@"type": std.elf.ET) u64 { - return switch (@"type") { - else => 0, - .EXEC => 0x1000000, + comptime field: std.meta.FieldEnum(std.elf.Elf64.Ehdr), +) @FieldType(std.elf.Elf64.Ehdr, @tagName(field)) { + return switch (elf.ehdrPtr()) { + inline else => |ehdr| elf.targetLoad(&@field(ehdr, @tagName(field))), }; } -pub fn baseAddr(elf: *Elf) u64 { - return baseAddrForType(elf.ehdrField(.type)); -} pub const PhdrSlice = union(std.elf.CLASS) { NONE: noreturn, @@ -909,7 +953,7 @@ pub const PhdrSlice = union(std.elf.CLASS) { @"64": []std.elf.Elf64.Phdr, }; pub fn phdrSlice(elf: *Elf) PhdrSlice { - const slice = Node.known.phdr.slice(&elf.mf); + const slice = Node.Known.phdr.slice(&elf.mf); return switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| @unionInit( @@ -926,7 +970,7 @@ pub const ShdrSlice = union(std.elf.CLASS) { @"64": []std.elf.Elf64.Shdr, }; pub fn shdrSlice(elf: *Elf) ShdrSlice { - const slice = Node.known.shdr.slice(&elf.mf); + const slice = Node.Known.shdr.slice(&elf.mf); return switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| @unionInit( @@ -937,17 +981,17 @@ pub fn shdrSlice(elf: *Elf) ShdrSlice { }; } -pub const SymSlice = union(std.elf.CLASS) { +pub const SymtabSlice = union(std.elf.CLASS) { NONE: noreturn, @"32": []std.elf.Elf32.Sym, @"64": []std.elf.Elf64.Sym, }; -pub fn symSlice(elf: *Elf) SymSlice { +pub fn symtabSlice(elf: *Elf) SymtabSlice { const slice = Symbol.Index.symtab.node(elf).slice(&elf.mf); return switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| @unionInit( - SymSlice, + SymtabSlice, @tagName(class), @ptrCast(@alignCast(slice)), ), @@ -960,7 +1004,7 @@ pub const SymPtr = union(std.elf.CLASS) { @"64": *std.elf.Elf64.Sym, }; pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr { - return switch (elf.symSlice()) { + return switch (elf.symtabSlice()) { inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]), }; } @@ -1123,7 +1167,9 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.In } }, }, ); - return 0; + return switch (elf.symPtr(target_si)) { + inline else => |sym| elf.targetLoad(&sym.value), + }; } fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { @@ -1135,21 +1181,19 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { entsize: std.elf.Word = 0, }) !Symbol.Index { const gpa = elf.base.comp.gpa; - const target_endian = elf.targetEndian(); try elf.nodes.ensureUnusedCapacity(gpa, 1); try elf.symtab.ensureUnusedCapacity(gpa, 1); const shstrtab_entry = try elf.string(.shstrtab, opts.name); const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) { inline else => |ehdr| { - const shentsize = std.mem.toNative(@TypeOf(ehdr.shentsize), ehdr.shentsize, target_endian); - const shndx = std.mem.toNative(@TypeOf(ehdr.shnum), ehdr.shnum, target_endian); + const shndx = elf.targetLoad(&ehdr.shnum); const shnum = shndx + 1; - ehdr.shnum = std.mem.nativeTo(@TypeOf(ehdr.shnum), shnum, target_endian); - break :shndx .{ shndx, shentsize * shnum }; + elf.targetStore(&ehdr.shnum, shnum); + break :shndx .{ shndx, elf.targetLoad(&ehdr.shentsize) * shnum }; }, }; - try Node.known.shdr.resize(&elf.mf, gpa, shdr_size); + try Node.Known.shdr.resize(&elf.mf, gpa, shdr_size); const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{ .alignment = opts.addralign, .size = opts.size, @@ -1179,7 +1223,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { .addralign = @intCast(opts.addralign.toByteUnits()), .entsize = opts.entsize, }; - if (target_endian != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh); + if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh); }, } return si; @@ -1188,37 +1232,29 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void { const strtab_entry = try elf.string(.strtab, name); const shstrtab_entry = try elf.string(.shstrtab, name); - const target_endian = elf.targetEndian(); switch (elf.shdrSlice()) { inline else => |shdr, class| { const sym = @field(elf.symPtr(si), @tagName(class)); - sym.name = std.mem.nativeTo(@TypeOf(sym.name), strtab_entry, target_endian); - const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian); - const sh = &shdr[shndx]; - sh.name = std.mem.nativeTo(@TypeOf(sh.name), shstrtab_entry, target_endian); + elf.targetStore(&sym.name, strtab_entry); + const sh = &shdr[elf.targetLoad(&sym.shndx)]; + elf.targetStore(&sh.name, shstrtab_entry); }, } } fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void { - const target_endian = elf.targetEndian(); switch (elf.shdrSlice()) { - inline else => |shdr, class| { - const sym = @field(elf.symPtr(si), @tagName(class)); - const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian); - shdr[shndx].link = @field(elf.symPtr(link_si), @tagName(class)).shndx; - }, + inline else => |shdr, class| shdr[ + elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx) + ].link = @field(elf.symPtr(link_si), @tagName(class)).shndx, } } fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 { - const target_endian = elf.targetEndian(); - const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[name: switch (elf.shdrSlice()) { - inline else => |shndx, class| { - const sym = @field(elf.symPtr(si), @tagName(class)); - const sh = &shndx[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)]; - break :name std.mem.toNative(@TypeOf(sh.name), sh.name, target_endian); - }, + const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrSlice()) { + inline else => |shndx, class| elf.targetLoad( + &shndx[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name, + ), }..]; return name[0..std.mem.indexOfScalar(u8, name, 0).? :0]; } @@ -1243,7 +1279,7 @@ pub fn addReloc( ) !void { const gpa = elf.base.comp.gpa; const target = target_si.get(elf); - const ri: link.File.Elf2.Reloc.Index = @enumFromInt(elf.relocs.items.len); + const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); (try elf.relocs.addOne(gpa)).* = .{ .type = @"type", .prev = .none, @@ -1332,10 +1368,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error.WriteFailed => return error.OutOfMemory, else => |e| return e, }; - const target_endian = elf.targetEndian(); switch (elf.symPtr(si)) { - inline else => |sym| sym.size = - std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian), + inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), } si.applyLocationRelocs(elf); } @@ -1463,10 +1497,8 @@ fn updateFuncInner( error.WriteFailed => return nw.err.?, else => |e| return e, }; - const target_endian = elf.targetEndian(); switch (elf.symPtr(si)) { - inline else => |sym| sym.size = - std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian), + inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), } si.applyLocationRelocs(elf); } @@ -1634,10 +1666,8 @@ fn flushUav( error.WriteFailed => return error.OutOfMemory, else => |e| return e, }; - const target_endian = elf.targetEndian(); switch (elf.symPtr(si)) { - inline else => |sym| sym.size = - std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian), + inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), } si.applyLocationRelocs(elf); } @@ -1689,167 +1719,121 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { .none, .{ .atom_index = @intFromEnum(si) }, ); - const target_endian = elf.targetEndian(); switch (elf.symPtr(si)) { - inline else => |sym| sym.size = - std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian), + inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), } si.applyLocationRelocs(elf); } fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { - const target_endian = elf.targetEndian(); - const file_offset = ni.fileLocation(&elf.mf, false).offset; - const node = elf.getNode(ni); - switch (node) { - else => |tag| @panic(@tagName(tag)), - .ehdr => assert(file_offset == 0), + switch (elf.getNode(ni)) { + .file => unreachable, + .ehdr => assert(ni.fileLocation(&elf.mf, false).offset == 0), .shdr => switch (elf.ehdrPtr()) { - inline else => |ehdr| ehdr.shoff = - std.mem.nativeTo(@TypeOf(ehdr.shoff), @intCast(file_offset), target_endian), + inline else => |ehdr| elf.targetStore( + &ehdr.shoff, + @intCast(ni.fileLocation(&elf.mf, false).offset), + ), }, .segment => |phndx| switch (elf.phdrSlice()) { inline else => |phdr, class| { const ph = &phdr[phndx]; - switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) { + elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset)); + switch (elf.targetLoad(&ph.type)) { else => unreachable, - std.elf.PT_NULL, std.elf.PT_LOAD, std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {}, - std.elf.PT_PHDR => { - const ehdr = @field(elf.ehdrPtr(), @tagName(class)); - ehdr.phoff = - std.mem.nativeTo(@TypeOf(ehdr.phoff), @intCast(file_offset), target_endian); - }, + std.elf.PT_NULL, std.elf.PT_LOAD => return, + std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {}, + std.elf.PT_PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset, std.elf.PT_TLS => {}, } - ph.offset = std.mem.nativeTo(@TypeOf(ph.offset), @intCast(file_offset), target_endian); - ph.vaddr = std.mem.nativeTo( - @TypeOf(ph.vaddr), - @intCast(elf.baseAddr() + file_offset), - target_endian, - ); + elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni))); ph.paddr = ph.vaddr; }, }, .section => |si| switch (elf.shdrSlice()) { inline else => |shdr, class| { const sym = @field(elf.symPtr(si), @tagName(class)); - const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian); - const sh = &shdr[shndx]; - const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative( - @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?, - @bitCast(sh.flags), - target_endian, - )); - if (flags.shf.ALLOC) { - sym.value = std.mem.nativeTo( - @TypeOf(sym.value), - @intCast(elf.baseAddr() + file_offset), - target_endian, - ); - sh.addr = sym.value; + const sh = &shdr[elf.targetLoad(&sym.shndx)]; + elf.targetStore(&sh.offset, @intCast(ni.fileLocation(&elf.mf, false).offset)); + const flags = elf.targetLoad(&sh.flags).shf; + if (flags.ALLOC) { + elf.targetStore(&sh.addr, @intCast(elf.computeNodeVAddr(ni))); + if (!flags.TLS) sym.value = sh.addr; } - sh.offset = std.mem.nativeTo(@TypeOf(sh.offset), @intCast(file_offset), target_endian); }, }, - .nav, .uav, .lazy_code, .lazy_const_data => { - const si = switch (node) { - else => unreachable, - inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf), - }; - switch (elf.shdrSlice()) { - inline else => |shdr, class| { - const sym = @field(elf.symPtr(si), @tagName(class)); - const sh = &shdr[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)]; - const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative( - @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?, - @bitCast(sh.flags), - target_endian, - )); - const sh_addr = if (flags.shf.TLS) - 0 - else - std.mem.toNative(@TypeOf(sh.addr), sh.addr, target_endian); - const sh_offset = std.mem.toNative(@TypeOf(sh.offset), sh.offset, target_endian); - sym.value = std.mem.nativeTo( - @TypeOf(sym.value), - @intCast(file_offset - sh_offset + sh_addr), - target_endian, - ); - if (si == elf.entry_hack) @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value; - }, - } - si.applyLocationRelocs(elf); - si.applyTargetRelocs(elf); - }, + inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf).flushMoved(elf), } try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); } fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { - const target_endian = elf.targetEndian(); _, const size = ni.location(&elf.mf).resolve(&elf.mf); - const node = elf.getNode(ni); - switch (node) { - else => |tag| @panic(@tagName(tag)), - .file, .shdr => {}, + switch (elf.getNode(ni)) { + .file => {}, + .ehdr => unreachable, + .shdr => {}, .segment => |phndx| switch (elf.phdrSlice()) { inline else => |phdr| { + assert(elf.phdrs.items[phndx] == ni); const ph = &phdr[phndx]; - ph.filesz = std.mem.nativeTo(@TypeOf(ph.filesz), @intCast(size), target_endian); - ph.memsz = ph.filesz; - switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) { - else => unreachable, - std.elf.PT_NULL => { - if (size > 0) ph.type = std.mem.nativeTo( - @TypeOf(ph.type), - std.elf.PT_LOAD, - target_endian, - ); - }, - std.elf.PT_LOAD => { - if (size == 0) ph.type = std.mem.nativeTo( - @TypeOf(ph.type), - std.elf.PT_NULL, - target_endian, - ); - }, - std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => {}, - std.elf.PT_TLS => try ni.childrenMoved(elf.base.comp.gpa, &elf.mf), + elf.targetStore(&ph.filesz, @intCast(size)); + if (size > elf.targetLoad(&ph.memsz)) { + const memsz = ni.alignment(&elf.mf).forward(@intCast(size * 4)); + elf.targetStore(&ph.memsz, @intCast(memsz)); + switch (elf.targetLoad(&ph.type)) { + else => unreachable, + std.elf.PT_NULL => if (size > 0) elf.targetStore(&ph.type, std.elf.PT_LOAD), + std.elf.PT_LOAD => if (size == 0) elf.targetStore(&ph.type, std.elf.PT_NULL), + std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => return, + std.elf.PT_TLS => return ni.childrenMoved(elf.base.comp.gpa, &elf.mf), + } + var vaddr = elf.targetLoad(&ph.vaddr); + var new_phndx = phndx; + for (phdr[phndx + 1 ..], phndx + 1..) |*next_ph, next_phndx| { + switch (elf.targetLoad(&next_ph.type)) { + else => unreachable, + std.elf.PT_NULL, std.elf.PT_LOAD => {}, + std.elf.PT_DYNAMIC, + std.elf.PT_INTERP, + std.elf.PT_PHDR, + std.elf.PT_TLS, + => break, + } + const next_vaddr = elf.targetLoad(&next_ph.vaddr); + if (vaddr + memsz <= next_vaddr) break; + vaddr = next_vaddr + elf.targetLoad(&next_ph.memsz); + std.mem.swap(@TypeOf(ph.*), &phdr[new_phndx], next_ph); + const next_ni = elf.phdrs.items[next_phndx]; + elf.phdrs.items[new_phndx] = next_ni; + elf.nodes.items(.data)[@intFromEnum(next_ni)] = .{ .segment = new_phndx }; + new_phndx = @intCast(next_phndx); + } + if (new_phndx != phndx) { + const new_ph = &phdr[new_phndx]; + elf.targetStore(&new_ph.vaddr, vaddr); + new_ph.paddr = new_ph.vaddr; + elf.phdrs.items[new_phndx] = ni; + elf.nodes.items(.data)[@intFromEnum(ni)] = .{ .segment = new_phndx }; + try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); + } } }, }, - .section => |si| switch (elf.shdrSlice()) { - inline else => |shdr, class| { - const sym = @field(elf.symPtr(si), @tagName(class)); - const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian); - const sh = &shdr[shndx]; - switch (std.mem.toNative(@TypeOf(sh.type), sh.type, target_endian)) { + .section => |si| switch (elf.symPtr(si)) { + inline else => |sym, class| { + const sh = &@field(elf.shdrSlice(), @tagName(class))[elf.targetLoad(&sym.shndx)]; + elf.targetStore(&sh.size, @intCast(size)); + switch (elf.targetLoad(&sh.type)) { else => unreachable, - std.elf.SHT_NULL => { - if (size > 0) sh.type = std.mem.nativeTo( - @TypeOf(sh.type), - std.elf.SHT_PROGBITS, - target_endian, - ); - }, - std.elf.SHT_PROGBITS => { - if (size == 0) sh.type = std.mem.nativeTo( - @TypeOf(sh.type), - std.elf.SHT_NULL, - target_endian, - ); - }, - std.elf.SHT_SYMTAB => sh.info = std.mem.nativeTo( - @TypeOf(sh.info), - @intCast(@divExact( - size, - std.mem.toNative(@TypeOf(sh.entsize), sh.entsize, target_endian), - )), - target_endian, + std.elf.SHT_NULL => if (size > 0) elf.targetStore(&sh.type, std.elf.SHT_PROGBITS), + std.elf.SHT_PROGBITS => if (size == 0) elf.targetStore(&sh.type, std.elf.SHT_NULL), + std.elf.SHT_SYMTAB => elf.targetStore( + &sh.info, + @intCast(@divExact(size, elf.targetLoad(&sh.entsize))), ), std.elf.SHT_STRTAB => {}, } - sh.size = std.mem.nativeTo(@TypeOf(sh.size), @intCast(size), target_endian); }, }, .nav, .uav, .lazy_code, .lazy_const_data => {},