diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2020-11-30 23:57:36 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2020-12-01 10:49:31 +0100 |
| commit | 1f7fb560ab5a3a82af24ab7abe11dce8f877c7fe (patch) | |
| tree | 876dfc3329b12c8619a8e4412cbab9fdabb7abef /src | |
| parent | de66b65677f8207b2bc10997031b90c06b738dc8 (diff) | |
| download | zig-1f7fb560ab5a3a82af24ab7abe11dce8f877c7fe.tar.gz zig-1f7fb560ab5a3a82af24ab7abe11dce8f877c7fe.zip | |
lld: use commands.LoadCommand in self-hosted linker
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen.zig | 6 | ||||
| -rw-r--r-- | src/link/MachO.zig | 316 | ||||
| -rw-r--r-- | src/link/MachO/CodeSignature.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/commands.zig | 25 |
4 files changed, 133 insertions, 218 deletions
diff --git a/src/codegen.zig b/src/codegen.zig index ee3f0ee4a8..2bc5381975 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1790,7 +1790,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.func.cast(ir.Inst.Constant)) |func_inst| { if (func_inst.val.cast(Value.Payload.Function)) |func_val| { const func = func_val.func; - const got = &macho_file.sections.items[macho_file.got_section_index.?]; + const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment; + const got = &text_segment.sections.items[macho_file.got_section_index.?]; const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); switch (arch) { .x86_64 => { @@ -3191,7 +3192,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const decl = payload.decl; - const got = &macho_file.sections.items[macho_file.got_section_index.?]; + const text_segment = &macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment; + const got = &text_segment.sections.items[macho_file.got_section_index.?]; const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e8b8e40f37..020faa88b3 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -25,55 +25,9 @@ const Trie = @import("MachO/Trie.zig"); const CodeSignature = @import("MachO/CodeSignature.zig"); const Parser = @import("MachO/Parser.zig"); -pub const base_tag: File.Tag = File.Tag.macho; - -const LoadCommand = union(enum) { - Segment: macho.segment_command_64, - LinkeditData: macho.linkedit_data_command, - Symtab: macho.symtab_command, - Dysymtab: macho.dysymtab_command, - DyldInfo: macho.dyld_info_command, - Dylinker: macho.dylinker_command, - Dylib: macho.dylib_command, - EntryPoint: macho.entry_point_command, - MinVersion: macho.version_min_command, - SourceVersion: macho.source_version_command, - - pub fn cmdsize(self: LoadCommand) u32 { - return switch (self) { - .Segment => |x| x.cmdsize, - .LinkeditData => |x| x.cmdsize, - .Symtab => |x| x.cmdsize, - .Dysymtab => |x| x.cmdsize, - .DyldInfo => |x| x.cmdsize, - .Dylinker => |x| x.cmdsize, - .Dylib => |x| x.cmdsize, - .EntryPoint => |x| x.cmdsize, - .MinVersion => |x| x.cmdsize, - .SourceVersion => |x| x.cmdsize, - }; - } - - pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void { - return switch (self) { - .Segment => |cmd| writeGeneric(cmd, file, offset), - .LinkeditData => |cmd| writeGeneric(cmd, file, offset), - .Symtab => |cmd| writeGeneric(cmd, file, offset), - .Dysymtab => |cmd| writeGeneric(cmd, file, offset), - .DyldInfo => |cmd| writeGeneric(cmd, file, offset), - .Dylinker => |cmd| writeGeneric(cmd, file, offset), - .Dylib => |cmd| writeGeneric(cmd, file, offset), - .EntryPoint => |cmd| writeGeneric(cmd, file, offset), - .MinVersion => |cmd| writeGeneric(cmd, file, offset), - .SourceVersion => |cmd| writeGeneric(cmd, file, offset), - }; - } +usingnamespace @import("MachO/commands.zig"); - fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void { - const slice = [1]@TypeOf(cmd){cmd}; - return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset); - } -}; +pub const base_tag: File.Tag = File.Tag.macho; base: File, @@ -116,18 +70,14 @@ source_version_cmd_index: ?u16 = null, /// Code signature code_signature_cmd_index: ?u16 = null, -/// Table of all sections -sections: std.ArrayListUnmanaged(macho.section_64) = .{}, - -/// __TEXT,__text section +/// Index into __TEXT,__text section. text_section_index: ?u16 = null, - -/// __DATA,__got section +/// Index into __TEXT,__got section. got_section_index: ?u16 = null, - +/// The absolute address of the entry point. entry_addr: ?u64 = null, -// TODO move this into each Segment aggregator +/// TODO move this into each Segment aggregator linkedit_segment_next_offset: ?u32 = null, /// Table of all local symbols @@ -356,40 +306,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { if (self.entry_addr) |addr| { // Update LC_MAIN with entry offset. const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; - main_cmd.entryoff = addr - text_segment.vmaddr; - } - if (self.dylinker_cmd_dirty) { - // Write path to dyld loader. - var off: usize = @sizeOf(macho.mach_header_64); - for (self.load_commands.items) |cmd| { - if (cmd == .Dylinker) break; - off += cmd.cmdsize(); - } - const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker; - off += cmd.name; - log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); - try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); - self.dylinker_cmd_dirty = false; + const main_cmd = &self.load_commands.items[self.main_cmd_index.?].Main; + main_cmd.entryoff = addr - text_segment.inner.vmaddr; } - if (self.libsystem_cmd_dirty) { - // Write path to libSystem. - var off: usize = @sizeOf(macho.mach_header_64); - for (self.load_commands.items) |cmd| { - if (cmd == .Dylib) break; - off += cmd.cmdsize(); - } - const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib; - off += cmd.dylib.name; - log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); - try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); - self.libsystem_cmd_dirty = false; - } - try self.writeExportTrie(); try self.writeSymbolTable(); try self.writeStringTable(); - // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. @@ -402,7 +324,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } if (self.cmd_table_dirty) { - try self.writeCmdHeaders(); + try self.writeLoadCommands(); try self.writeMachOHeader(); self.cmd_table_dirty = false; } @@ -416,8 +338,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } assert(!self.cmd_table_dirty); - assert(!self.dylinker_cmd_dirty); - assert(!self.libsystem_cmd_dirty); switch (self.base.options.output_mode) { .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last @@ -921,7 +841,9 @@ pub fn deinit(self: *MachO) void { self.global_symbol_free_list.deinit(self.base.allocator); self.local_symbols.deinit(self.base.allocator); self.local_symbol_free_list.deinit(self.base.allocator); - self.sections.deinit(self.base.allocator); + for (self.load_commands.items) |*lc| { + lc.deinit(self.base.allocator); + } self.load_commands.deinit(self.base.allocator); } @@ -1075,7 +997,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { } // Perform PIE fixups (if any) - const got_section = self.sections.items[self.got_section_index.?]; + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const got_section = text_segment.sections.items[self.got_section_index.?]; while (self.pie_fixups.popOrNull()) |fixup| { const target_addr = fixup.address; const this_addr = symbol.n_value + fixup.start; @@ -1094,7 +1017,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { } } - const text_section = self.sections.items[self.text_section_index.?]; + const text_section = text_segment.sections.items[self.text_section_index.?]; const section_offset = symbol.n_value - text_section.addr; const file_offset = text_section.offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); @@ -1212,7 +1135,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.pagezero_segment_cmd_index == null) { self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ - .Segment = .{ + .Segment = SegmentCommand.empty(.{ .cmd = macho.LC_SEGMENT_64, .cmdsize = @sizeOf(macho.segment_command_64), .segname = makeStaticString("__PAGEZERO"), @@ -1224,7 +1147,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { .initprot = 0, .nsects = 0, .flags = 0, - }, + }), }); self.cmd_table_dirty = true; } @@ -1233,7 +1156,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; const initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; try self.load_commands.append(self.base.allocator, .{ - .Segment = .{ + .Segment = SegmentCommand.empty(.{ .cmd = macho.LC_SEGMENT_64, .cmdsize = @sizeOf(macho.segment_command_64), .segname = makeStaticString("__TEXT"), @@ -1245,45 +1168,45 @@ pub fn populateMissingMetadata(self: *MachO) !void { .initprot = initprot, .nsects = 0, .flags = 0, - }, + }), }); self.cmd_table_dirty = true; } if (self.text_section_index == null) { - self.text_section_index = @intCast(u16, self.sections.items.len); const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + self.text_section_index = @intCast(u16, text_segment.sections.items.len); const program_code_size_hint = self.base.options.program_code_size_hint; const file_size = mem.alignForwardGeneric(u64, program_code_size_hint, self.page_size); const off = @intCast(u32, self.findFreeSpace(file_size, self.page_size)); // TODO maybe findFreeSpace should return u32 directly? - const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); - try self.sections.append(self.base.allocator, .{ + try text_segment.sections.append(self.base.allocator, .{ .sectname = makeStaticString("__text"), .segname = makeStaticString("__TEXT"), - .addr = text_segment.vmaddr + off, + .addr = text_segment.inner.vmaddr + off, .size = file_size, .offset = off, .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, // 2^2 for aarch64, 2^0 for x86_64 .reloff = 0, .nreloc = 0, - .flags = flags, + .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, .reserved1 = 0, .reserved2 = 0, .reserved3 = 0, }); - text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. - text_segment.filesize = file_size + off; - text_segment.cmdsize += @sizeOf(macho.section_64); - text_segment.nsects += 1; + text_segment.inner.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. + text_segment.inner.filesize = file_size + off; + text_segment.inner.cmdsize += @sizeOf(macho.section_64); + text_segment.inner.nsects += 1; self.cmd_table_dirty = true; } if (self.got_section_index == null) { - self.got_section_index = @intCast(u16, self.sections.items.len); - const text_section = &self.sections.items[self.text_section_index.?]; + const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const text_section = &text_segment.sections.items[self.text_section_index.?]; + self.got_section_index = @intCast(u16, text_segment.sections.items.len); const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; // TODO looking for free space should be done *within* a segment it belongs to @@ -1291,7 +1214,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); - try self.sections.append(self.base.allocator, .{ + try text_segment.sections.append(self.base.allocator, .{ .sectname = makeStaticString("__got"), .segname = makeStaticString("__TEXT"), .addr = text_section.addr + text_section.size, @@ -1300,18 +1223,17 @@ pub fn populateMissingMetadata(self: *MachO) !void { .@"align" = if (self.base.options.target.cpu.arch == .aarch64) 2 else 0, .reloff = 0, .nreloc = 0, - .flags = macho.S_REGULAR, + .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, .reserved1 = 0, .reserved2 = 0, .reserved3 = 0, }); - const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const added_size = mem.alignForwardGeneric(u64, file_size, self.page_size); - text_segment.vmsize += added_size; - text_segment.filesize += added_size; - text_segment.cmdsize += @sizeOf(macho.section_64); - text_segment.nsects += 1; + text_segment.inner.vmsize += added_size; + text_segment.inner.filesize += added_size; + text_segment.inner.cmdsize += @sizeOf(macho.section_64); + text_segment.inner.nsects += 1; self.cmd_table_dirty = true; } if (self.linkedit_segment_cmd_index == null) { @@ -1319,13 +1241,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; const initprot = macho.VM_PROT_READ; - const off = text_segment.fileoff + text_segment.filesize; + const off = text_segment.inner.fileoff + text_segment.inner.filesize; try self.load_commands.append(self.base.allocator, .{ - .Segment = .{ + .Segment = SegmentCommand.empty(.{ .cmd = macho.LC_SEGMENT_64, .cmdsize = @sizeOf(macho.segment_command_64), .segname = makeStaticString("__LINKEDIT"), - .vmaddr = text_segment.vmaddr + text_segment.vmsize, + .vmaddr = text_segment.inner.vmaddr + text_segment.inner.vmsize, .vmsize = 0, .fileoff = off, .filesize = 0, @@ -1333,7 +1255,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { .initprot = initprot, .nsects = 0, .flags = 0, - }, + }), }); self.linkedit_segment_next_offset = @intCast(u32, off); self.cmd_table_dirty = true; @@ -1341,7 +1263,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.dyld_info_cmd_index == null) { self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ - .DyldInfo = .{ + .DyldInfoOnly = .{ .cmd = macho.LC_DYLD_INFO_ONLY, .cmdsize = @sizeOf(macho.dyld_info_command), .rebase_off = 0, @@ -1403,15 +1325,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.dylinker_cmd_index == null) { self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); - try self.load_commands.append(self.base.allocator, .{ - .Dylinker = .{ - .cmd = macho.LC_LOAD_DYLINKER, - .cmdsize = @intCast(u32, cmdsize), - .name = @sizeOf(macho.dylinker_command), - }, + var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ + .cmd = macho.LC_LOAD_DYLINKER, + .cmdsize = @intCast(u32, cmdsize), + .name = @sizeOf(macho.dylinker_command), }); + dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); + mem.set(u8, dylinker_cmd.data, 0); + mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); + try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); self.cmd_table_dirty = true; - self.dylinker_cmd_dirty = true; } if (self.libsystem_cmd_index == null) { self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); @@ -1419,26 +1342,26 @@ pub fn populateMissingMetadata(self: *MachO) !void { // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. const min_version = 0x0; - const dylib = .{ - .name = @sizeOf(macho.dylib_command), - .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files - .current_version = min_version, - .compatibility_version = min_version, - }; - try self.load_commands.append(self.base.allocator, .{ - .Dylib = .{ - .cmd = macho.LC_LOAD_DYLIB, - .cmdsize = @intCast(u32, cmdsize), - .dylib = dylib, + var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ + .cmd = macho.LC_LOAD_DYLIB, + .cmdsize = @intCast(u32, cmdsize), + .dylib = .{ + .name = @sizeOf(macho.dylib_command), + .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files + .current_version = min_version, + .compatibility_version = min_version, }, }); + dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); + mem.set(u8, dylib_cmd.data, 0); + mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); + try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); self.cmd_table_dirty = true; - self.libsystem_cmd_dirty = true; } if (self.main_cmd_index == null) { self.main_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ - .EntryPoint = .{ + .Main = .{ .cmd = macho.LC_MAIN, .cmdsize = @sizeOf(macho.entry_point_command), .entryoff = 0x0, @@ -1459,7 +1382,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { const ver = self.base.options.target.os.version_range.semver.min; const version = ver.major << 16 | ver.minor << 8 | ver.patch; try self.load_commands.append(self.base.allocator, .{ - .MinVersion = .{ + .VersionMin = .{ .cmd = cmd, .cmdsize = @sizeOf(macho.version_min_command), .version = version, @@ -1502,7 +1425,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { } fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { - const text_section = &self.sections.items[self.text_section_index.?]; + const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const text_section = &text_segment.sections.items[self.text_section_index.?]; const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; // We use these to indicate our intention to update metadata, placing the new block, @@ -1644,15 +1568,18 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { return test_end; } } - for (self.sections.items) |section| { - const increased_size = satMul(section.size, alloc_num) / alloc_den; - const test_end = section.offset + increased_size; - if (end > section.offset and start < test_end) { - return test_end; + if (self.text_segment_cmd_index) |text_index| { + const text_segment = self.load_commands.items[text_index].Segment; + for (text_segment.sections.items) |section| { + const increased_size = satMul(section.size, alloc_num) / alloc_den; + const test_end = section.offset + increased_size; + if (end > section.offset and start < test_end) { + return test_end; + } } } if (self.dyld_info_cmd_index) |dyld_info_index| { - const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; + const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; const tight_size = dyld_info.export_size; const increased_size = satMul(tight_size, alloc_num) / alloc_den; const test_end = dyld_info.export_off + increased_size; @@ -1689,12 +1616,15 @@ fn allocatedSize(self: *MachO, start: u64) u64 { const off = @sizeOf(macho.mach_header_64); if (off > start and off < min_pos) min_pos = off; } - for (self.sections.items) |section| { - if (section.offset <= start) continue; - if (section.offset < min_pos) min_pos = section.offset; + if (self.text_segment_cmd_index) |text_index| { + const text_segment = self.load_commands.items[text_index].Segment; + for (text_segment.sections.items) |section| { + if (section.offset <= start) continue; + if (section.offset < min_pos) min_pos = section.offset; + } } if (self.dyld_info_cmd_index) |dyld_info_index| { - const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; + const dyld_info = self.load_commands.items[dyld_info_index].DyldInfoOnly; if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; } if (self.symtab_cmd_index) |symtab_index| { @@ -1714,7 +1644,8 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 { } fn writeOffsetTableEntry(self: *MachO, index: usize) !void { - const sect = &self.sections.items[self.got_section_index.?]; + const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const sect = &text_semgent.sections.items[self.got_section_index.?]; const off = sect.offset + @sizeOf(u64) * index; const vmaddr = sect.addr + @sizeOf(u64) * index; @@ -1782,9 +1713,9 @@ fn writeSymbolTable(self: *MachO) !void { // Advance size of __LINKEDIT segment const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - linkedit.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); - if (linkedit.vmsize < linkedit.filesize) { - linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); + linkedit.inner.filesize += symtab.nsyms * @sizeOf(macho.nlist_64); + if (linkedit.inner.vmsize < linkedit.inner.filesize) { + linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); } self.cmd_table_dirty = true; } @@ -1799,9 +1730,9 @@ fn writeCodeSignaturePadding(self: *MachO) !void { self.linkedit_segment_next_offset = fileoff + datasize; // Advance size of __LINKEDIT segment const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - linkedit.filesize += datasize; - if (linkedit.vmsize < linkedit.filesize) { - linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); + linkedit.inner.filesize += datasize; + if (linkedit.inner.vmsize < linkedit.inner.filesize) { + linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); } log.debug("writing code signature padding from 0x{x} to 0x{x}\n", .{ fileoff, fileoff + datasize }); // Pad out the space. We need to do this to calculate valid hashes for everything in the file @@ -1836,10 +1767,10 @@ fn writeExportTrie(self: *MachO) !void { for (self.global_symbols.items) |symbol| { // TODO figure out if we should put all global symbols into the export trie const name = self.getString(symbol.n_strx); - assert(symbol.n_value >= text_segment.vmaddr); + assert(symbol.n_value >= text_segment.inner.vmaddr); try trie.put(self.base.allocator, .{ .name = name, - .vmaddr_offset = symbol.n_value - text_segment.vmaddr, + .vmaddr_offset = symbol.n_value - text_segment.inner.vmaddr, .export_flags = 0, // TODO workout creation of export flags }); } @@ -1849,7 +1780,7 @@ fn writeExportTrie(self: *MachO) !void { try trie.writeULEB128Mem(self.base.allocator, &buffer); - const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; + const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; const export_size = @intCast(u32, mem.alignForward(buffer.items.len, @sizeOf(u64))); dyld_info.export_off = self.linkedit_segment_next_offset.?; dyld_info.export_size = export_size; @@ -1865,9 +1796,9 @@ fn writeExportTrie(self: *MachO) !void { self.linkedit_segment_next_offset = dyld_info.export_off + dyld_info.export_size; // Advance size of __LINKEDIT segment const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - linkedit.filesize += dyld_info.export_size; - if (linkedit.vmsize < linkedit.filesize) { - linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); + linkedit.inner.filesize += dyld_info.export_size; + if (linkedit.inner.vmsize < linkedit.inner.filesize) { + linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); } self.cmd_table_dirty = true; } @@ -1890,49 +1821,31 @@ fn writeStringTable(self: *MachO) !void { self.linkedit_segment_next_offset = symtab.stroff + symtab.strsize; // Advance size of __LINKEDIT segment const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - linkedit.filesize += symtab.strsize; - if (linkedit.vmsize < linkedit.filesize) { - linkedit.vmsize = mem.alignForwardGeneric(u64, linkedit.filesize, self.page_size); + linkedit.inner.filesize += symtab.strsize; + if (linkedit.inner.vmsize < linkedit.inner.filesize) { + linkedit.inner.vmsize = mem.alignForwardGeneric(u64, linkedit.inner.filesize, self.page_size); } self.cmd_table_dirty = true; } -fn writeCmdHeaders(self: *MachO) !void { - assert(self.cmd_table_dirty); - - // Write all load command headers first. - // Since command sizes are up-to-date and accurate, we will correctly - // leave space for any section headers that any of the segment load - // commands might consist of. - var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); - for (self.load_commands.items) |cmd| { - try cmd.write(&self.base.file.?, last_cmd_offset); - last_cmd_offset += cmd.cmdsize(); +/// Writes all load commands and section headers. +fn writeLoadCommands(self: *MachO) !void { + var sizeofcmds: usize = 0; + for (self.load_commands.items) |lc| { + sizeofcmds += lc.cmdsize(); } - { - const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { - var i: usize = 0; - var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); - while (i < text_segment_index) : (i += 1) { - cmdsize += self.load_commands.items[i].cmdsize(); - } - break :blk cmdsize; - } else { - // If we've landed in here, we are building a MachO object file, so we have - // only one, noname segment to append this section header to. - return error.TODOImplementWritingObjFiles; - }; - // write sections belonging to __TEXT segment - // TODO section indices should belong to each Segment, and we should iterate dynamically. - const id = self.text_section_index.?; - log.debug("writing __TEXT section headers at 0x{x}\n", .{off}); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[id .. id + 2]), off); + + var buffer = try self.base.allocator.alloc(u8, sizeofcmds); + defer self.base.allocator.free(buffer); + var writer = std.io.fixedBufferStream(buffer).writer(); + for (self.load_commands.items) |lc| { + try lc.write(writer); } + + try self.base.file.?.pwriteAll(buffer, @sizeOf(macho.mach_header_64)); } /// Writes Mach-O file header. -/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping -/// variables. fn writeMachOHeader(self: *MachO) !void { var hdr: macho.mach_header_64 = undefined; hdr.magic = macho.MH_MAGIC_64; @@ -1994,10 +1907,3 @@ fn satMul(a: anytype, b: anytype) @TypeOf(a, b) { const T = @TypeOf(a, b); return std.math.mul(T, a, b) catch std.math.maxInt(T); } - -test "" { - // TODO surprisingly this causes a linking error: - // _linkWithLLD symbol missing for arch - // _ = std.testing.refAllDecls(@This()); - _ = std.testing.refAllDecls(@import("MachO/commands.zig")); -} diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 1cef7803f5..98189ae219 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -146,8 +146,8 @@ pub fn calcAdhocSignature(self: *CodeSignature, bin_file: *const MachO) !void { const text_segment = bin_file.load_commands.items[bin_file.text_segment_cmd_index.?].Segment; const code_sig_cmd = bin_file.load_commands.items[bin_file.code_signature_cmd_index.?].LinkeditData; - const execSegBase: u64 = text_segment.fileoff; - const execSegLimit: u64 = text_segment.filesize; + const execSegBase: u64 = text_segment.inner.fileoff; + const execSegLimit: u64 = text_segment.inner.filesize; const execSegFlags: u64 = if (bin_file.base.options.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; const file_size = code_sig_cmd.dataoff; var cdir = CodeDirectory{ diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index 7be72d9745..b9745a2c07 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -154,7 +154,11 @@ pub const LoadCommand = union(enum) { pub const SegmentCommand = struct { inner: macho.segment_command_64, - sections: std.StringArrayHashMapUnmanaged(macho.section_64) = .{}, + sections: std.ArrayListUnmanaged(macho.section_64) = .{}, + + pub fn empty(inner: macho.segment_command_64) SegmentCommand { + return .{ .inner = inner }; + } pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand { const inner = try reader.readStruct(macho.segment_command_64); @@ -166,7 +170,7 @@ pub const SegmentCommand = struct { var i: usize = 0; while (i < inner.nsects) : (i += 1) { const section = try reader.readStruct(macho.section_64); - segment.sections.putAssumeCapacityNoClobber(mem.trimRight(u8, section.sectname[0..], &[_]u8{0}), section); + segment.sections.appendAssumeCapacity(section); } return segment; @@ -176,8 +180,8 @@ pub const SegmentCommand = struct { const cmd = [1]macho.segment_command_64{self.inner}; try writer.writeAll(mem.sliceAsBytes(cmd[0..1])); - for (self.sections.items()) |entry| { - const section = [1]macho.section_64{entry.value}; + for (self.sections.items) |sect| { + const section = [1]macho.section_64{sect}; try writer.writeAll(mem.sliceAsBytes(section[0..1])); } } @@ -188,17 +192,20 @@ pub const SegmentCommand = struct { fn eql(self: SegmentCommand, other: SegmentCommand) bool { if (!mem.eql(u8, mem.asBytes(&self.inner), mem.asBytes(&other.inner))) return false; - const lhs = self.sections.items(); - const rhs = other.sections.items(); + const lhs = self.sections.items; + const rhs = other.sections.items; var i: usize = 0; while (i < self.inner.nsects) : (i += 1) { - if (!mem.eql(u8, lhs[i].key, rhs[i].key)) return false; - if (!mem.eql(u8, mem.asBytes(&lhs[i].value), mem.asBytes(&rhs[i].value))) return false; + if (!mem.eql(u8, mem.asBytes(&lhs[i]), mem.asBytes(&rhs[i]))) return false; } return true; } }; +pub fn emptyGenericCommandWithData(cmd: anytype) GenericCommandWithData(@TypeOf(cmd)) { + return .{ .inner = cmd }; +} + pub fn GenericCommandWithData(comptime Cmd: type) type { return struct { inner: Cmd, @@ -290,7 +297,7 @@ test "read-write segment command" { .flags = 0, }, }; - try cmd.sections.putNoClobber(gpa, "__text", .{ + try cmd.sections.append(gpa, .{ .sectname = makeName("__text"), .segname = makeName("__TEXT"), .addr = 4294983680, |
