From 50db993119d9b1031700be94b757eaf100f35857 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 31 Aug 2021 23:05:01 +0200 Subject: [PATCH] macho: fix allocating sections within segment when parsing objects --- lib/std/macho.zig | 40 +- src/link/MachO.zig | 852 +++++++++++++++++--------------- src/link/MachO/DebugSymbols.zig | 171 +++---- src/link/MachO/commands.zig | 93 +--- 4 files changed, 552 insertions(+), 604 deletions(-) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 3c41b65522..b4d7964b5e 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -601,35 +601,35 @@ pub const segment_command = extern struct { /// command and their size is reflected in cmdsize. pub const segment_command_64 = extern struct { /// LC_SEGMENT_64 - cmd: u32, + cmd: u32 = LC_SEGMENT_64, /// includes sizeof section_64 structs - cmdsize: u32, + cmdsize: u32 = @sizeOf(segment_command_64), /// segment name segname: [16]u8, /// memory address of this segment - vmaddr: u64, + vmaddr: u64 = 0, /// memory size of this segment - vmsize: u64, + vmsize: u64 = 0, /// file offset of this segment - fileoff: u64, + fileoff: u64 = 0, /// amount to map from the file - filesize: u64, + filesize: u64 = 0, /// maximum VM protection - maxprot: vm_prot_t, + maxprot: vm_prot_t = VM_PROT_NONE, /// initial VM protection - initprot: vm_prot_t, + initprot: vm_prot_t = VM_PROT_NONE, /// number of sections in segment - nsects: u32, - flags: u32, + nsects: u32 = 0, + flags: u32 = 0, }; /// A segment is made up of zero or more sections. Non-MH_OBJECT files have @@ -700,34 +700,34 @@ pub const section_64 = extern struct { segname: [16]u8, /// memory address of this section - addr: u64, + addr: u64 = 0, /// size in bytes of this section - size: u64, + size: u64 = 0, /// file offset of this section - offset: u32, + offset: u32 = 0, /// section alignment (power of 2) - @"align": u32, + @"align": u32 = 0, /// file offset of relocation entries - reloff: u32, + reloff: u32 = 0, /// number of relocation entries - nreloc: u32, + nreloc: u32 = 0, /// flags (section type and attributes - flags: u32, + flags: u32 = S_REGULAR, /// reserved (for offset or index) - reserved1: u32, + reserved1: u32 = 0, /// reserved (for count or sizeof) - reserved2: u32, + reserved2: u32 = 0, /// reserved - reserved3: u32, + reserved3: u32 = 0, }; pub const nlist = extern struct { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 1def32c41a..38446db5a6 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1133,20 +1133,19 @@ pub const MatchingSection = struct { }; pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection { - const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const segname = commands.segmentName(sect); const sectname = commands.sectionName(sect); - - var needs_allocation = false; const res: ?MatchingSection = blk: { switch (commands.sectionType(sect)) { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { if (self.text_const_section_index == null) { - self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__const", .{}); - needs_allocation = true; + self.text_const_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1159,11 +1158,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio // TODO it seems the common values within the sections in objects are deduplicated/merged // on merging the sections' contents. if (self.objc_methname_section_index == null) { - self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__objc_methname", .{ - .flags = macho.S_CSTRING_LITERALS, - }); - needs_allocation = true; + self.objc_methname_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__objc_methname", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1172,11 +1173,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_methtype")) { if (self.objc_methtype_section_index == null) { - self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__objc_methtype", .{ - .flags = macho.S_CSTRING_LITERALS, - }); - needs_allocation = true; + self.objc_methtype_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__objc_methtype", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1185,9 +1188,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_classname")) { if (self.objc_classname_section_index == null) { - self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__objc_classname", .{}); - needs_allocation = true; + self.objc_classname_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__objc_classname", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1197,11 +1204,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio } if (self.cstring_section_index == null) { - self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__cstring", .{ - .flags = macho.S_CSTRING_LITERALS, - }); - needs_allocation = true; + self.cstring_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__cstring", + sect.size, + sect.@"align", + .{ + .flags = macho.S_CSTRING_LITERALS, + }, + ); } break :blk .{ @@ -1212,29 +1223,37 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio macho.S_LITERAL_POINTERS => { if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { if (self.objc_selrefs_section_index == null) { - self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__objc_selrefs", .{ - .flags = macho.S_LITERAL_POINTERS, - }); - needs_allocation = true; + self.objc_selrefs_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__objc_selrefs", + sect.size, + sect.@"align", + .{ + .flags = macho.S_LITERAL_POINTERS, + }, + ); } break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.objc_selrefs_section_index.?, }; + } else { + // TODO investigate + break :blk null; } - - // TODO investigate - break :blk null; }, macho.S_MOD_INIT_FUNC_POINTERS => { if (self.mod_init_func_section_index == null) { - self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__mod_init_func", .{ - .flags = macho.S_MOD_INIT_FUNC_POINTERS, - }); - needs_allocation = true; + self.mod_init_func_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__mod_init_func", + sect.size, + sect.@"align", + .{ + .flags = macho.S_MOD_INIT_FUNC_POINTERS, + }, + ); } break :blk .{ @@ -1244,11 +1263,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }, macho.S_MOD_TERM_FUNC_POINTERS => { if (self.mod_term_func_section_index == null) { - self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__mod_term_func", .{ - .flags = macho.S_MOD_TERM_FUNC_POINTERS, - }); - needs_allocation = true; + self.mod_term_func_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__mod_term_func", + sect.size, + sect.@"align", + .{ + .flags = macho.S_MOD_TERM_FUNC_POINTERS, + }, + ); } break :blk .{ @@ -1258,11 +1281,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }, macho.S_ZEROFILL => { if (self.bss_section_index == null) { - self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__bss", .{ - .flags = macho.S_ZEROFILL, - }); - needs_allocation = true; + self.bss_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__bss", + sect.size, + sect.@"align", + .{ + .flags = macho.S_ZEROFILL, + }, + ); } break :blk .{ @@ -1272,11 +1299,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }, macho.S_THREAD_LOCAL_VARIABLES => { if (self.tlv_section_index == null) { - self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__thread_vars", .{ - .flags = macho.S_THREAD_LOCAL_VARIABLES, - }); - needs_allocation = true; + self.tlv_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__thread_vars", + sect.size, + sect.@"align", + .{ + .flags = macho.S_THREAD_LOCAL_VARIABLES, + }, + ); } break :blk .{ @@ -1286,11 +1317,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }, macho.S_THREAD_LOCAL_REGULAR => { if (self.tlv_data_section_index == null) { - self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__thread_data", .{ - .flags = macho.S_THREAD_LOCAL_REGULAR, - }); - needs_allocation = true; + self.tlv_data_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__thread_data", + sect.size, + sect.@"align", + .{ + .flags = macho.S_THREAD_LOCAL_REGULAR, + }, + ); } break :blk .{ @@ -1300,11 +1335,15 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }, macho.S_THREAD_LOCAL_ZEROFILL => { if (self.tlv_bss_section_index == null) { - self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__thread_bss", .{ - .flags = macho.S_THREAD_LOCAL_ZEROFILL, - }); - needs_allocation = true; + self.tlv_bss_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__thread_bss", + sect.size, + sect.@"align", + .{ + .flags = macho.S_THREAD_LOCAL_ZEROFILL, + }, + ); } break :blk .{ @@ -1317,9 +1356,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio // TODO I believe __eh_frame is currently part of __unwind_info section // in the latest ld64 output. if (self.eh_frame_section_index == null) { - self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__eh_frame", .{}); - needs_allocation = true; + self.eh_frame_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__eh_frame", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1330,9 +1373,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio // TODO audit this: is this the right mapping? if (self.data_const_section_index == null) { - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__const", .{}); - needs_allocation = true; + self.data_const_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1343,11 +1390,17 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio macho.S_REGULAR => { if (commands.sectionIsCode(sect)) { if (self.text_section_index == null) { - self.text_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__text", .{ - .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - }); - needs_allocation = true; + self.text_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__text", + sect.size, + sect.@"align", + .{ + .flags = macho.S_REGULAR | + macho.S_ATTR_PURE_INSTRUCTIONS | + macho.S_ATTR_SOME_INSTRUCTIONS, + }, + ); } break :blk .{ @@ -1368,9 +1421,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio if (mem.eql(u8, segname, "__TEXT")) { if (mem.eql(u8, sectname, "__ustring")) { if (self.ustring_section_index == null) { - self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__ustring", .{}); - needs_allocation = true; + self.ustring_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__ustring", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1379,9 +1436,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { if (self.gcc_except_tab_section_index == null) { - self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__gcc_except_tab", .{}); - needs_allocation = true; + self.gcc_except_tab_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__gcc_except_tab", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1390,9 +1451,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_methlist")) { if (self.objc_methlist_section_index == null) { - self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__objc_methlist", .{}); - needs_allocation = true; + self.objc_methlist_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__objc_methlist", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1406,9 +1471,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio mem.eql(u8, sectname, "__gopclntab")) { if (self.data_const_section_index == null) { - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__const", .{}); - needs_allocation = true; + self.data_const_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1417,9 +1486,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else { if (self.text_const_section_index == null) { - self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); - try text_seg.addSection(self.base.allocator, "__const", .{}); - needs_allocation = true; + self.text_const_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1431,9 +1504,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio if (mem.eql(u8, segname, "__DATA_CONST")) { if (self.data_const_section_index == null) { - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__const", .{}); - needs_allocation = true; + self.data_const_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1445,9 +1522,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio if (mem.eql(u8, segname, "__DATA")) { if (mem.eql(u8, sectname, "__const")) { if (self.data_const_section_index == null) { - self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__const", .{}); - needs_allocation = true; + self.data_const_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1456,9 +1537,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__cfstring")) { if (self.objc_cfstring_section_index == null) { - self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__cfstring", .{}); - needs_allocation = true; + self.objc_cfstring_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__cfstring", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1467,9 +1552,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_classlist")) { if (self.objc_classlist_section_index == null) { - self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__objc_classlist", .{}); - needs_allocation = true; + self.objc_classlist_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__objc_classlist", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1478,9 +1567,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { if (self.objc_imageinfo_section_index == null) { - self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.base.allocator, "__objc_imageinfo", .{}); - needs_allocation = true; + self.objc_imageinfo_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__objc_imageinfo", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1489,9 +1582,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_const")) { if (self.objc_const_section_index == null) { - self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__objc_const", .{}); - needs_allocation = true; + self.objc_const_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__objc_const", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1500,9 +1597,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_classrefs")) { if (self.objc_classrefs_section_index == null) { - self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__objc_classrefs", .{}); - needs_allocation = true; + self.objc_classrefs_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__objc_classrefs", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1511,9 +1612,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else if (mem.eql(u8, sectname, "__objc_data")) { if (self.objc_data_section_index == null) { - self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__objc_data", .{}); - needs_allocation = true; + self.objc_data_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__objc_data", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1522,9 +1627,13 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio }; } else { if (self.data_section_index == null) { - self.data_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.base.allocator, "__data", .{}); - needs_allocation = true; + self.data_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__data", + sect.size, + sect.@"align", + .{}, + ); } break :blk .{ @@ -1545,42 +1654,6 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio else => break :blk null, } }; - - if (res) |match| { - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - _ = try self.block_free_lists.getOrPutValue(self.base.allocator, match, .{}); - - const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; - if (!use_stage1) { - const target_seg = &self.load_commands.items[match.seg].Segment; - const target_sect = &target_seg.sections.items[match.sect]; - - // Update section's alignment - // TODO if sect.@"align" > target_sect.@"align", should we move the entire - // section to match the required alignment? - target_sect.@"align" = math.max(target_sect.@"align", sect.@"align"); - - if (needs_allocation) { - const alignment = try math.powi(u32, 2, target_sect.@"align"); - const needed_size = sect.size; - const off = target_seg.findFreeSpace(needed_size, alignment, self.header_pad); - assert(off + needed_size <= target_seg.inner.fileoff + target_seg.inner.filesize); // TODO expand - - log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{ - segname, - sectname, - off, - off + needed_size, - }); - - target_sect.addr = target_seg.inner.vmaddr + off; - target_sect.size = needed_size; - target_sect.offset = @intCast(u32, off); - self.load_commands_dirty = true; - } - } - } - return res; } @@ -3878,9 +3951,12 @@ 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 = SegmentCommand.empty("__PAGEZERO", .{ - .vmsize = 0x100000000, // size always set to 4GB - }), + .Segment = .{ + .inner = .{ + .segname = makeStaticString("__PAGEZERO"), + .vmsize = 0x100000000, // size always set to 4GB + }, + }, }); self.load_commands_dirty = true; } @@ -3895,51 +3971,39 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty("__TEXT", .{ - .vmaddr = 0x100000000, // always starts at 4GB - .vmsize = needed_size, - .filesize = needed_size, - .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, - .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, - }), + .Segment = .{ + .inner = .{ + .segname = makeStaticString("__TEXT"), + .vmaddr = 0x100000000, // always starts at 4GB + .vmsize = needed_size, + .filesize = needed_size, + .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, + .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, + }, + }, }); self.load_commands_dirty = true; } if (self.text_section_index == null) { - 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 alignment: u2 = switch (self.base.options.target.cpu.arch) { .x86_64 => 0, .aarch64 => 2, else => unreachable, // unhandled architecture type }; const needed_size = self.base.options.program_code_size_hint; - const off = text_segment.findFreeSpace(needed_size, @as(u16, 1) << alignment, self.header_pad); - - log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try text_segment.addSection(self.base.allocator, "__text", .{ - .addr = text_segment.inner.vmaddr + off, - .size = @intCast(u32, needed_size), - .offset = @intCast(u32, off), - .@"align" = alignment, - .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - }); - const match = MatchingSection{ - .seg = self.text_segment_cmd_index.?, - .sect = self.text_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + self.text_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__text", + needed_size, + alignment, + .{ + .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, + }, + ); } if (self.stubs_section_index == null) { - const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - self.stubs_section_index = @intCast(u16, text_segment.sections.items.len); - const alignment: u2 = switch (self.base.options.target.cpu.arch) { .x86_64 => 0, .aarch64 => 2, @@ -3951,32 +4015,19 @@ pub fn populateMissingMetadata(self: *MachO) !void { else => unreachable, // unhandled architecture type }; const needed_size = stub_size * self.base.options.symbol_count_hint; - const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); - assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. - - log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try text_segment.addSection(self.base.allocator, "__stubs", .{ - .addr = text_segment.inner.vmaddr + off, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = alignment, - .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved2 = stub_size, - }); - const match = MatchingSection{ - .seg = self.text_segment_cmd_index.?, - .sect = self.stubs_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + self.stubs_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__stubs", + needed_size, + alignment, + .{ + .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, + .reserved2 = stub_size, + }, + ); } if (self.stub_helper_section_index == null) { - const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - self.stub_helper_section_index = @intCast(u16, text_segment.sections.items.len); - const alignment: u2 = switch (self.base.options.target.cpu.arch) { .x86_64 => 0, .aarch64 => 2, @@ -3993,25 +4044,15 @@ pub fn populateMissingMetadata(self: *MachO) !void { else => unreachable, }; const needed_size = stub_size * self.base.options.symbol_count_hint + preamble_size; - const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); - assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. - - log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try text_segment.addSection(self.base.allocator, "__stub_helper", .{ - .addr = text_segment.inner.vmaddr + off, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = alignment, - .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - }); - const match = MatchingSection{ - .seg = self.text_segment_cmd_index.?, - .sect = self.stub_helper_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + self.stub_helper_section_index = try self.allocateSection( + self.text_segment_cmd_index.?, + "__stub_helper", + needed_size, + alignment, + .{ + .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, + }, + ); } if (self.data_const_segment_cmd_index == null) { @@ -4020,45 +4061,39 @@ pub fn populateMissingMetadata(self: *MachO) !void { const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); - log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); + log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ + address_and_offset.offset, + address_and_offset.offset + needed_size, + }); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty("__DATA_CONST", .{ - .vmaddr = address_and_offset.address, - .vmsize = needed_size, - .fileoff = address_and_offset.offset, - .filesize = needed_size, - .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, - .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, - }), + .Segment = .{ + .inner = .{ + .segname = makeStaticString("__DATA_CONST"), + .vmaddr = address_and_offset.address, + .vmsize = needed_size, + .fileoff = address_and_offset.offset, + .filesize = needed_size, + .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, + .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, + }, + }, }); self.load_commands_dirty = true; } if (self.got_section_index == null) { - const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - self.got_section_index = @intCast(u16, dc_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = dc_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= dc_segment.inner.fileoff + dc_segment.inner.filesize); // TODO Must expand __DATA_CONST segment. - - log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try dc_segment.addSection(self.base.allocator, "__got", .{ - .addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = 3, // 2^3 = @sizeOf(u64) - .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, - }); - const match = MatchingSection{ - .seg = self.data_const_segment_cmd_index.?, - .sect = self.got_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.got_section_index = try self.allocateSection( + self.data_const_segment_cmd_index.?, + "__got", + needed_size, + alignment, + .{ + .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, + }, + ); } if (self.data_segment_cmd_index == null) { @@ -4070,175 +4105,115 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty("__DATA", .{ - .vmaddr = address_and_offset.address, - .vmsize = needed_size, - .fileoff = address_and_offset.offset, - .filesize = needed_size, - .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, - .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, - }), + .Segment = .{ + .inner = .{ + .segname = makeStaticString("__DATA"), + .vmaddr = address_and_offset.address, + .vmsize = needed_size, + .fileoff = address_and_offset.offset, + .filesize = needed_size, + .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, + .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, + }, + }, }); self.load_commands_dirty = true; } if (self.la_symbol_ptr_section_index == null) { - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - self.la_symbol_ptr_section_index = @intCast(u16, data_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. - - log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try data_segment.addSection(self.base.allocator, "__la_symbol_ptr", .{ - .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = 3, // 2^3 = @sizeOf(u64) - .flags = macho.S_LAZY_SYMBOL_POINTERS, - }); - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.la_symbol_ptr_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.la_symbol_ptr_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__la_symbol_ptr", + needed_size, + alignment, + .{ + .flags = macho.S_LAZY_SYMBOL_POINTERS, + }, + ); } if (self.data_section_index == null) { - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - self.data_section_index = @intCast(u16, data_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. - - log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try data_segment.addSection(self.base.allocator, "__data", .{ - .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = 3, // 2^3 = @sizeOf(u64) - }); - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.data_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.data_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__data", + needed_size, + alignment, + .{}, + ); } if (self.tlv_section_index == null) { - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - self.tlv_section_index = @intCast(u16, data_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. - - log.debug("found __thread_vars section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try data_segment.addSection(self.base.allocator, "__thread_vars", .{ - .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = 3, // 2^3 = @sizeOf(u64) - .flags = macho.S_THREAD_LOCAL_VARIABLES, - }); - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.tlv_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.tlv_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__thread_vars", + needed_size, + alignment, + .{ + .flags = macho.S_THREAD_LOCAL_VARIABLES, + }, + ); } if (self.tlv_data_section_index == null) { - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - self.tlv_data_section_index = @intCast(u16, data_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. - - log.debug("found __thread_data section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); - - try data_segment.addSection(self.base.allocator, "__thread_data", .{ - .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, - .size = needed_size, - .offset = @intCast(u32, off), - .@"align" = 3, // 2^3 = @sizeOf(u64) - .flags = macho.S_THREAD_LOCAL_REGULAR, - }); - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.tlv_data_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.tlv_data_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__thread_data", + needed_size, + alignment, + .{ + .flags = macho.S_THREAD_LOCAL_REGULAR, + }, + ); } if (self.tlv_bss_section_index == null) { - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - self.tlv_bss_section_index = @intCast(u16, data_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. - - log.debug("found __thread_bss section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.tlv_bss_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__thread_bss", + needed_size, + alignment, + .{ + .flags = macho.S_THREAD_LOCAL_ZEROFILL, + }, + ); // We keep offset to the section in a separate variable as the actual section is usually pointing at the // beginning of the file. - self.tlv_bss_file_offset = off; - try data_segment.addSection(self.base.allocator, "__thread_bss", .{ - .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, - .size = needed_size, - .@"align" = 3, // 2^3 = @sizeOf(u64) - .flags = macho.S_THREAD_LOCAL_ZEROFILL, - }); - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.tlv_bss_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const out_sect = &seg.sections.items[self.tlv_bss_section_index.?]; + self.tlv_bss_file_offset = out_sect.offset; + out_sect.offset = 0; } if (self.bss_section_index == null) { - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - self.bss_section_index = @intCast(u16, data_segment.sections.items.len); - const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; - const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); - assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. - - log.debug("found __bss section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); + const alignment: u16 = 3; // 2^3 = @sizeOf(u64) + self.bss_section_index = try self.allocateSection( + self.data_segment_cmd_index.?, + "__bss", + needed_size, + alignment, + .{ + .flags = macho.S_ZEROFILL, + }, + ); // We keep offset to the section in a separate variable as the actual section is usually pointing at the // beginning of the file. - self.bss_file_offset = off; - try data_segment.addSection(self.base.allocator, "__bss", .{ - .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, - .size = 0, - .@"align" = 3, // 2^3 = @sizeOf(u64) - .flags = macho.S_ZEROFILL, - }); - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.bss_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); - self.load_commands_dirty = true; + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const out_sect = &seg.sections.items[self.bss_section_index.?]; + self.bss_file_offset = out_sect.offset; + out_sect.offset = 0; } if (self.linkedit_segment_cmd_index == null) { @@ -4248,12 +4223,15 @@ pub fn populateMissingMetadata(self: *MachO) !void { log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset}); try self.load_commands.append(self.base.allocator, .{ - .Segment = SegmentCommand.empty("__LINKEDIT", .{ - .vmaddr = address_and_offset.address, - .fileoff = address_and_offset.offset, - .maxprot = macho.VM_PROT_READ, - .initprot = macho.VM_PROT_READ, - }), + .Segment = .{ + .inner = .{ + .segname = makeStaticString("__LINKEDIT"), + .vmaddr = address_and_offset.address, + .fileoff = address_and_offset.offset, + .maxprot = macho.VM_PROT_READ, + .initprot = macho.VM_PROT_READ, + }, + }, }); self.load_commands_dirty = true; } @@ -4485,6 +4463,67 @@ pub fn populateMissingMetadata(self: *MachO) !void { } } +const AllocateSectionOpts = struct { + flags: u32 = macho.S_REGULAR, + reserved1: u32 = 0, + reserved2: u32 = 0, +}; + +fn allocateSection( + self: *MachO, + segment_id: u16, + sectname: []const u8, + size: u64, + alignment: u32, + opts: AllocateSectionOpts, +) !u16 { + const seg = &self.load_commands.items[segment_id].Segment; + var sect = macho.section_64{ + .sectname = makeStaticString(sectname), + .segname = seg.inner.segname, + .size = @intCast(u32, size), + .@"align" = alignment, + .flags = opts.flags, + .reserved1 = opts.reserved1, + .reserved2 = opts.reserved2, + }; + + const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; + if (!use_stage1) { + const alignment_pow_2 = try math.powi(u32, 2, alignment); + const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null; + const off = seg.findFreeSpace(size, alignment_pow_2, padding); + + assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand + + log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{ + commands.segmentName(sect), + commands.sectionName(sect), + off, + off + size, + }); + + sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff; + sect.offset = @intCast(u32, off); + } + + const index = @intCast(u16, seg.sections.items.len); + try seg.sections.append(self.base.allocator, sect); + seg.inner.cmdsize += @sizeOf(macho.section_64); + seg.inner.nsects += 1; + + const match = MatchingSection{ + .seg = segment_id, + .sect = index, + }; + _ = try self.section_ordinals.getOrPut(self.base.allocator, match); + try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); + + self.load_commands_dirty = true; + + return index; +} + fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const text_section = &text_segment.sections.items[self.text_section_index.?]; @@ -5513,6 +5552,13 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { std.math.maxInt(@TypeOf(actual_size)); } +pub fn makeStaticString(bytes: []const u8) [16]u8 { + var buf = [_]u8{0} ** 16; + assert(bytes.len <= buf.len); + mem.copy(u8, &buf, bytes); + return buf; +} + pub fn makeString(self: *MachO, string: []const u8) !u32 { if (self.strtab_dir.getAdapted(@as([]const u8, string), StringSliceAdapter{ .strtab = &self.strtab })) |off| { log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off }); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 025959793e..450d842134 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -5,23 +5,26 @@ const assert = std.debug.assert; const fs = std.fs; const log = std.log.scoped(.dsym); const macho = std.macho; +const math = std.math; const mem = std.mem; const DW = std.dwarf; const leb = std.leb; const Allocator = mem.Allocator; const build_options = @import("build_options"); +const commands = @import("commands.zig"); const trace = @import("../../tracy.zig").trace; +const LoadCommand = commands.LoadCommand; const Module = @import("../../Module.zig"); const Type = @import("../../type.zig").Type; const link = @import("../../link.zig"); const MachO = @import("../MachO.zig"); -const SrcFn = MachO.SrcFn; const TextBlock = MachO.TextBlock; +const SegmentCommand = commands.SegmentCommand; +const SrcFn = MachO.SrcFn; +const makeStaticString = MachO.makeStaticString; const padToIdeal = MachO.padToIdeal; -usingnamespace @import("commands.zig"); - const page_size: u16 = 0x1000; base: *MachO, @@ -185,107 +188,86 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size }); try self.load_commands.append(allocator, .{ - .Segment = SegmentCommand.empty("__DWARF", .{ - .vmaddr = vmaddr, - .vmsize = needed_size, - .fileoff = off, - .filesize = needed_size, - }), + .Segment = .{ + .inner = .{ + .segname = makeStaticString("__DWARF"), + .vmaddr = vmaddr, + .vmsize = needed_size, + .fileoff = off, + .filesize = needed_size, + }, + }, }); self.load_commands_dirty = true; } if (self.debug_str_section_index == null) { - const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; - self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len); assert(self.debug_string_table.items.len == 0); - - try dwarf_segment.addSection(allocator, "__debug_str", .{ - .addr = dwarf_segment.inner.vmaddr, - .size = @intCast(u32, self.debug_string_table.items.len), - .offset = @intCast(u32, dwarf_segment.inner.fileoff), - .@"align" = 1, - }); - self.load_commands_dirty = true; + self.debug_str_section_index = try self.allocateSection( + "__debug_str", + @intCast(u32, self.debug_string_table.items.len), + 0, + ); self.debug_string_table_dirty = true; } if (self.debug_info_section_index == null) { - const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; - self.debug_info_section_index = @intCast(u16, dwarf_segment.sections.items.len); - - const file_size_hint = 200; - const p_align = 1; - const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); - - log.debug("found __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - - try dwarf_segment.addSection(allocator, "__debug_info", .{ - .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, - .size = file_size_hint, - .offset = @intCast(u32, off), - .@"align" = p_align, - }); - self.load_commands_dirty = true; + self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0); self.debug_info_header_dirty = true; } if (self.debug_abbrev_section_index == null) { - const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; - self.debug_abbrev_section_index = @intCast(u16, dwarf_segment.sections.items.len); - - const file_size_hint = 128; - const p_align = 1; - const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); - - log.debug("found __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - - try dwarf_segment.addSection(allocator, "__debug_abbrev", .{ - .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, - .size = file_size_hint, - .offset = @intCast(u32, off), - .@"align" = p_align, - }); - self.load_commands_dirty = true; + self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0); self.debug_abbrev_section_dirty = true; } if (self.debug_aranges_section_index == null) { - const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; - self.debug_aranges_section_index = @intCast(u16, dwarf_segment.sections.items.len); - - const file_size_hint = 160; - const p_align = 16; - const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); - - log.debug("found __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - - try dwarf_segment.addSection(allocator, "__debug_aranges", .{ - .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, - .size = file_size_hint, - .offset = @intCast(u32, off), - .@"align" = p_align, - }); - self.load_commands_dirty = true; + self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4); self.debug_aranges_section_dirty = true; } if (self.debug_line_section_index == null) { - const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; - self.debug_line_section_index = @intCast(u16, dwarf_segment.sections.items.len); - - const file_size_hint = 250; - const p_align = 1; - const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null); - - log.debug("found __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint }); - - try dwarf_segment.addSection(allocator, "__debug_line", .{ - .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff, - .size = file_size_hint, - .offset = @intCast(u32, off), - .@"align" = p_align, - }); - self.load_commands_dirty = true; + self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0); self.debug_line_header_dirty = true; } } +fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 { + const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; + var sect = macho.section_64{ + .sectname = makeStaticString(sectname), + .segname = seg.inner.segname, + .size = @intCast(u32, size), + .@"align" = alignment, + }; + const alignment_pow_2 = try math.powi(u32, 2, alignment); + const off = seg.findFreeSpace(size, alignment_pow_2, null); + + assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand + + log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{ + commands.segmentName(sect), + commands.sectionName(sect), + off, + off + size, + }); + + sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff; + sect.offset = @intCast(u32, off); + + const index = @intCast(u16, seg.sections.items.len); + try seg.sections.append(self.base.base.allocator, sect); + seg.inner.cmdsize += @sizeOf(macho.section_64); + seg.inner.nsects += 1; + + // TODO + // const match = MatchingSection{ + // .seg = segment_id, + // .sect = index, + // }; + // _ = try self.section_ordinals.getOrPut(self.base.allocator, match); + // try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); + + self.load_commands_dirty = true; + + return index; +} + pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void { // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the // Zig source code. @@ -611,15 +593,18 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void { } fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand { - var cmd = SegmentCommand.empty("", .{ - .cmdsize = base_cmd.inner.cmdsize, - .vmaddr = base_cmd.inner.vmaddr, - .vmsize = base_cmd.inner.vmsize, - .maxprot = base_cmd.inner.maxprot, - .initprot = base_cmd.inner.initprot, - .nsects = base_cmd.inner.nsects, - .flags = base_cmd.inner.flags, - }); + var cmd = SegmentCommand{ + .inner = .{ + .segname = undefined, + .cmdsize = base_cmd.inner.cmdsize, + .vmaddr = base_cmd.inner.vmaddr, + .vmsize = base_cmd.inner.vmsize, + .maxprot = base_cmd.inner.maxprot, + .initprot = base_cmd.inner.initprot, + .nsects = base_cmd.inner.nsects, + .flags = base_cmd.inner.flags, + }, + }; mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname); try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects); @@ -689,7 +674,7 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void { } fn writeHeader(self: *DebugSymbols) !void { - var header = emptyHeader(.{ + var header = commands.emptyHeader(.{ .filetype = macho.MH_DSYM, }); diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index b50ce95acf..25154e2d5b 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -9,6 +9,7 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const MachO = @import("../MachO.zig"); +const makeStaticString = MachO.makeStaticString; const padToIdeal = MachO.padToIdeal; pub const HeaderArgs = struct { @@ -217,75 +218,6 @@ pub const SegmentCommand = struct { inner: macho.segment_command_64, sections: std.ArrayListUnmanaged(macho.section_64) = .{}, - const SegmentOptions = struct { - cmdsize: u32 = @sizeOf(macho.segment_command_64), - vmaddr: u64 = 0, - vmsize: u64 = 0, - fileoff: u64 = 0, - filesize: u64 = 0, - maxprot: macho.vm_prot_t = macho.VM_PROT_NONE, - initprot: macho.vm_prot_t = macho.VM_PROT_NONE, - nsects: u32 = 0, - flags: u32 = 0, - }; - - pub fn empty(comptime segname: []const u8, opts: SegmentOptions) SegmentCommand { - return .{ - .inner = .{ - .cmd = macho.LC_SEGMENT_64, - .cmdsize = opts.cmdsize, - .segname = makeStaticString(segname), - .vmaddr = opts.vmaddr, - .vmsize = opts.vmsize, - .fileoff = opts.fileoff, - .filesize = opts.filesize, - .maxprot = opts.maxprot, - .initprot = opts.initprot, - .nsects = opts.nsects, - .flags = opts.flags, - }, - }; - } - - const SectionOptions = struct { - addr: u64 = 0, - size: u64 = 0, - offset: u32 = 0, - @"align": u32 = 0, - reloff: u32 = 0, - nreloc: u32 = 0, - flags: u32 = macho.S_REGULAR, - reserved1: u32 = 0, - reserved2: u32 = 0, - reserved3: u32 = 0, - }; - - pub fn addSection( - self: *SegmentCommand, - alloc: *Allocator, - comptime sectname: []const u8, - opts: SectionOptions, - ) !void { - var section = macho.section_64{ - .sectname = makeStaticString(sectname), - .segname = undefined, - .addr = opts.addr, - .size = opts.size, - .offset = opts.offset, - .@"align" = opts.@"align", - .reloff = opts.reloff, - .nreloc = opts.nreloc, - .flags = opts.flags, - .reserved1 = opts.reserved1, - .reserved2 = opts.reserved2, - .reserved3 = opts.reserved3, - }; - mem.copy(u8, §ion.segname, &self.inner.segname); - try self.sections.append(alloc, section); - self.inner.cmdsize += @sizeOf(macho.section_64); - self.inner.nsects += 1; - } - pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand { const inner = try reader.readStruct(macho.segment_command_64); var segment = SegmentCommand{ @@ -427,13 +359,6 @@ pub fn createLoadDylibCommand( return dylib_cmd; } -fn makeStaticString(bytes: []const u8) [16]u8 { - var buf = [_]u8{0} ** 16; - assert(bytes.len <= buf.len); - mem.copy(u8, &buf, bytes); - return buf; -} - fn parseName(name: *const [16]u8) []const u8 { const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; return name[0..len]; @@ -513,34 +438,26 @@ test "read-write segment command" { 0x00, 0x00, 0x00, 0x00, // reserved3 }; var cmd = SegmentCommand{ - .inner = .{ - .cmd = macho.LC_SEGMENT_64, + .inner = macho.segment_command_64.new(.{ .cmdsize = 152, .segname = makeStaticString("__TEXT"), .vmaddr = 4294967296, .vmsize = 294912, - .fileoff = 0, .filesize = 294912, .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE, .initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ, .nsects = 1, - .flags = 0, - }, + }), }; - try cmd.sections.append(gpa, .{ + try cmd.sections.append(gpa, macho.section_64.new(.{ .sectname = makeStaticString("__text"), .segname = makeStaticString("__TEXT"), .addr = 4294983680, .size = 448, .offset = 16384, .@"align" = 2, - .reloff = 0, - .nreloc = 0, .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + })); defer cmd.deinit(gpa); try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });