authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-11 23:23:16+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 12:34:39+01:00
log98d6d40cd64b5c52ba2ddd01fbedb5069bf0458d
treeaf55bc60f3e5697721b7d9a9e7b77634d6bfe7c9
parent32ebceea95d22d399bc979370e5ce4cc3ca7d0ef

macho: allocate sections, segments and atoms


2 files changed, 195 insertions(+), 29 deletions(-)

src/link/MachO.zig+182-12
...@@ -110,6 +110,8 @@ compatibility_version: ?std.SemanticVersion,...@@ -110,6 +110,8 @@ compatibility_version: ?std.SemanticVersion,
110entry_name: ?[]const u8,110entry_name: ?[]const u8,
111platform: Platform,111platform: Platform,
112sdk_version: ?std.SemanticVersion,112sdk_version: ?std.SemanticVersion,
113/// Rpath table
114rpath_table: std.StringArrayHashMapUnmanaged(void) = .{},
113115
114/// Hot-code swapping state.116/// Hot-code swapping state.
115hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},117hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
...@@ -200,6 +202,12 @@ pub fn createEmpty(...@@ -200,6 +202,12 @@ pub fn createEmpty(
200 .mode = link.File.determineMode(false, output_mode, link_mode),202 .mode = link.File.determineMode(false, output_mode, link_mode),
201 });203 });
202204
205 // Filter rpaths
206 try self.rpath_table.ensureUnusedCapacity(gpa, self.base.rpath_list.len);
207 for (options.rpath_list) |rpath| {
208 _ = self.rpath_table.putAssumeCapacity(rpath, {});
209 }
210
203 // Append null file211 // Append null file
204 try self.files.append(gpa, .null);212 try self.files.append(gpa, .null);
205 // Atom at index 0 is reserved as null atom213 // Atom at index 0 is reserved as null atom
...@@ -317,6 +325,7 @@ pub fn deinit(self: *MachO) void {...@@ -317,6 +325,7 @@ pub fn deinit(self: *MachO) void {
317 }325 }
318 self.thunks.deinit(gpa);326 self.thunks.deinit(gpa);
319 self.unwind_records.deinit(gpa);327 self.unwind_records.deinit(gpa);
328 self.rpath_table.deinit(gpa);
320}329}
321330
322pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {331pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
...@@ -378,15 +387,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -378,15 +387,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
378387
379 if (module_obj_path) |path| try positionals.append(.{ .path = path });388 if (module_obj_path) |path| try positionals.append(.{ .path = path });
380389
381 // rpaths
382 var rpath_table = std.StringArrayHashMap(void).init(gpa);
383 defer rpath_table.deinit();
384 try rpath_table.ensureUnusedCapacity(self.base.rpath_list.len);
385
386 for (self.base.rpath_list) |rpath| {
387 _ = rpath_table.putAssumeCapacity(rpath, {});
388 }
389
390 for (positionals.items) |obj| {390 for (positionals.items) |obj| {
391 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {391 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
392 error.MalformedObject,392 error.MalformedObject,
...@@ -533,6 +533,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -533,6 +533,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
533 try self.generateUnwindInfo();533 try self.generateUnwindInfo();
534 try self.initSegments();534 try self.initSegments();
535535
536 try self.allocateSections();
537 self.allocateSegments();
538 self.allocateAtoms();
539 self.allocateSyntheticSymbols();
540
536 state_log.debug("{}", .{self.dumpState()});541 state_log.debug("{}", .{self.dumpState()});
537542
538 @panic("TODO");543 @panic("TODO");
...@@ -613,7 +618,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -613,7 +618,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
613 try argv.append(syslibroot);618 try argv.append(syslibroot);
614 }619 }
615620
616 for (self.base.rpath_list) |rpath| {621 for (self.rpath_table.keys()) |rpath| {
617 try argv.append("-rpath");622 try argv.append("-rpath");
618 try argv.append(rpath);623 try argv.append(rpath);
619 }624 }
...@@ -2016,6 +2021,171 @@ fn initSegments(self: *MachO) !void {...@@ -2016,6 +2021,171 @@ fn initSegments(self: *MachO) !void {
2016 self.linkedit_seg_index = self.getSegmentByName("__LINKEDIT").?;2021 self.linkedit_seg_index = self.getSegmentByName("__LINKEDIT").?;
2017}2022}
20182023
2024fn allocateSections(self: *MachO) !void {
2025 const headerpad = load_commands.calcMinHeaderPadSize(self);
2026 var vmaddr: u64 = if (self.pagezero_seg_index) |index|
2027 self.segments.items[index].vmaddr + self.segments.items[index].vmsize
2028 else
2029 0;
2030 vmaddr += headerpad;
2031 var fileoff = headerpad;
2032
2033 const page_size = self.getPageSize();
2034 const slice = self.sections.slice();
2035
2036 var next_seg_id: u8 = if (self.pagezero_seg_index) |index| index + 1 else 0;
2037 for (slice.items(.header), slice.items(.segment_id)) |*header, seg_id| {
2038 if (seg_id != next_seg_id) {
2039 vmaddr = mem.alignForward(u64, vmaddr, page_size);
2040 fileoff = mem.alignForward(u32, fileoff, page_size);
2041 }
2042
2043 const alignment = try math.powi(u32, 2, header.@"align");
2044
2045 vmaddr = mem.alignForward(u64, vmaddr, alignment);
2046 header.addr = vmaddr;
2047 vmaddr += header.size;
2048
2049 if (!header.isZerofill()) {
2050 fileoff = mem.alignForward(u32, fileoff, alignment);
2051 header.offset = fileoff;
2052 fileoff += @intCast(header.size);
2053 }
2054
2055 next_seg_id = seg_id;
2056 }
2057}
2058
2059fn allocateSegments(self: *MachO) void {
2060 const page_size = self.getPageSize();
2061 var vmaddr = if (self.pagezero_seg_index) |index|
2062 self.segments.items[index].vmaddr + self.segments.items[index].vmsize
2063 else
2064 0;
2065 var fileoff: u64 = 0;
2066 const index = if (self.pagezero_seg_index) |index| index + 1 else 0;
2067
2068 const slice = self.sections.slice();
2069 var next_sect_id: u8 = 0;
2070 for (self.segments.items[index..], index..) |*seg, seg_id| {
2071 seg.vmaddr = vmaddr;
2072 seg.fileoff = fileoff;
2073
2074 for (
2075 slice.items(.header)[next_sect_id..],
2076 slice.items(.segment_id)[next_sect_id..],
2077 ) |header, sid| {
2078 if (seg_id != sid) break;
2079
2080 vmaddr = header.addr + header.size;
2081 if (!header.isZerofill()) {
2082 fileoff = header.offset + header.size;
2083 }
2084
2085 next_sect_id += 1;
2086 }
2087
2088 vmaddr = mem.alignForward(u64, vmaddr, page_size);
2089 fileoff = mem.alignForward(u64, fileoff, page_size);
2090
2091 seg.vmsize = vmaddr - seg.vmaddr;
2092 seg.filesize = fileoff - seg.fileoff;
2093 }
2094}
2095
2096pub fn allocateAtoms(self: *MachO) void {
2097 const slice = self.sections.slice();
2098 for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
2099 if (atoms.items.len == 0) continue;
2100 for (atoms.items) |atom_index| {
2101 const atom = self.getAtom(atom_index).?;
2102 assert(atom.flags.alive);
2103 atom.value += header.addr;
2104 }
2105 }
2106
2107 for (self.thunks.items) |*thunk| {
2108 const header = self.sections.items(.header)[thunk.out_n_sect];
2109 thunk.value += header.addr;
2110 }
2111}
2112
2113fn allocateSyntheticSymbols(self: *MachO) void {
2114 const text_seg = self.getTextSegment();
2115
2116 if (self.mh_execute_header_index) |index| {
2117 const global = self.getSymbol(index);
2118 global.value = text_seg.vmaddr;
2119 }
2120
2121 if (self.data_sect_index) |idx| {
2122 const sect = self.sections.items(.header)[idx];
2123 for (&[_]?Symbol.Index{
2124 self.dso_handle_index,
2125 self.mh_dylib_header_index,
2126 self.dyld_private_index,
2127 }) |maybe_index| {
2128 if (maybe_index) |index| {
2129 const global = self.getSymbol(index);
2130 global.value = sect.addr;
2131 global.out_n_sect = idx;
2132 }
2133 }
2134 }
2135
2136 for (self.boundary_symbols.items) |sym_index| {
2137 const sym = self.getSymbol(sym_index);
2138 const name = sym.getName(self);
2139
2140 sym.flags.@"export" = false;
2141 sym.value = text_seg.vmaddr;
2142
2143 if (mem.startsWith(u8, name, "segment$start$")) {
2144 const segname = name["segment$start$".len..];
2145 if (self.getSegmentByName(segname)) |seg_id| {
2146 const seg = self.segments.items[seg_id];
2147 sym.value = seg.vmaddr;
2148 }
2149 } else if (mem.startsWith(u8, name, "segment$stop$")) {
2150 const segname = name["segment$stop$".len..];
2151 if (self.getSegmentByName(segname)) |seg_id| {
2152 const seg = self.segments.items[seg_id];
2153 sym.value = seg.vmaddr + seg.vmsize;
2154 }
2155 } else if (mem.startsWith(u8, name, "section$start$")) {
2156 const actual_name = name["section$start$".len..];
2157 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2158 const segname = actual_name[0..sep];
2159 const sectname = actual_name[sep + 1 ..];
2160 if (self.getSectionByName(segname, sectname)) |sect_id| {
2161 const sect = self.sections.items(.header)[sect_id];
2162 sym.value = sect.addr;
2163 sym.out_n_sect = sect_id;
2164 }
2165 } else if (mem.startsWith(u8, name, "section$stop$")) {
2166 const actual_name = name["section$stop$".len..];
2167 const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
2168 const segname = actual_name[0..sep];
2169 const sectname = actual_name[sep + 1 ..];
2170 if (self.getSectionByName(segname, sectname)) |sect_id| {
2171 const sect = self.sections.items(.header)[sect_id];
2172 sym.value = sect.addr + sect.size;
2173 sym.out_n_sect = sect_id;
2174 }
2175 } else unreachable;
2176 }
2177
2178 if (self.objc_stubs.symbols.items.len > 0) {
2179 const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
2180
2181 for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| {
2182 const sym = self.getSymbol(sym_index);
2183 sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
2184 sym.out_n_sect = self.objc_stubs_sect_index.?;
2185 }
2186 }
2187}
2188
2019fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {2189fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
2020 _ = self;2190 _ = self;
2021 _ = atom_index;2191 _ = atom_index;
...@@ -2952,8 +3122,8 @@ pub const Platform = struct {...@@ -2952,8 +3122,8 @@ pub const Platform = struct {
29523122
2953 pub fn isBuildVersionCompatible(plat: Platform) bool {3123 pub fn isBuildVersionCompatible(plat: Platform) bool {
2954 inline for (supported_platforms) |sup_plat| {3124 inline for (supported_platforms) |sup_plat| {
2955 if (sup_plat[0] == plat.platform) {3125 if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
2956 return sup_plat[1] <= plat.version.value;3126 return sup_plat[2] <= plat.toAppleVersion();
2957 }3127 }
2958 }3128 }
2959 return false;3129 return false;
src/link/MachO/load_commands.zig+13-17
...@@ -18,7 +18,6 @@ fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool...@@ -18,7 +18,6 @@ fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool
18}18}
1919
20pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {20pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
21 const options = &macho_file.options;
22 var sizeofcmds: u64 = 0;21 var sizeofcmds: u64 = 0;
2322
24 // LC_SEGMENT_6423 // LC_SEGMENT_64
...@@ -44,14 +43,14 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {...@@ -44,14 +43,14 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
44 false,43 false,
45 );44 );
46 // LC_MAIN45 // LC_MAIN
47 if (!options.dylib) {46 if (!macho_file.base.isDynLib()) {
48 sizeofcmds += @sizeOf(macho.entry_point_command);47 sizeofcmds += @sizeOf(macho.entry_point_command);
49 }48 }
50 // LC_ID_DYLIB49 // LC_ID_DYLIB
51 if (options.dylib) {50 if (macho_file.base.isDynLib()) {
52 sizeofcmds += blk: {51 sizeofcmds += blk: {
53 const emit = options.emit;52 const emit = macho_file.base.emit;
54 const install_name = options.install_name orelse emit.sub_path;53 const install_name = macho_file.install_name orelse emit.sub_path;
55 break :blk calcInstallNameLen(54 break :blk calcInstallNameLen(
56 @sizeOf(macho.dylib_command),55 @sizeOf(macho.dylib_command),
57 install_name,56 install_name,
...@@ -61,7 +60,7 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {...@@ -61,7 +60,7 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
61 }60 }
62 // LC_RPATH61 // LC_RPATH
63 {62 {
64 for (options.rpath_list) |rpath| {63 for (macho_file.rpath_table.keys()) |rpath| {
65 sizeofcmds += calcInstallNameLen(64 sizeofcmds += calcInstallNameLen(
66 @sizeOf(macho.rpath_command),65 @sizeOf(macho.rpath_command),
67 rpath,66 rpath,
...@@ -71,14 +70,12 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {...@@ -71,14 +70,12 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
71 }70 }
72 // LC_SOURCE_VERSION71 // LC_SOURCE_VERSION
73 sizeofcmds += @sizeOf(macho.source_version_command);72 sizeofcmds += @sizeOf(macho.source_version_command);
74 if (options.platform) |platform| {73 if (macho_file.platform.isBuildVersionCompatible()) {
75 if (platform.isBuildVersionCompatible()) {74 // LC_BUILD_VERSION
76 // LC_BUILD_VERSION75 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
77 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);76 } else {
78 } else {77 // LC_VERSION_MIN_*
79 // LC_VERSION_MIN_*78 sizeofcmds += @sizeOf(macho.version_min_command);
80 sizeofcmds += @sizeOf(macho.version_min_command);
81 }
82 }79 }
83 // LC_UUID80 // LC_UUID
84 sizeofcmds += @sizeOf(macho.uuid_command);81 sizeofcmds += @sizeOf(macho.uuid_command);
...@@ -134,11 +131,10 @@ pub fn calcLoadCommandsSizeObject(macho_file: *MachO) u32 {...@@ -134,11 +131,10 @@ pub fn calcLoadCommandsSizeObject(macho_file: *MachO) u32 {
134}131}
135132
136pub fn calcMinHeaderPadSize(macho_file: *MachO) u32 {133pub fn calcMinHeaderPadSize(macho_file: *MachO) u32 {
137 const options = &macho_file.options;134 var padding: u32 = calcLoadCommandsSize(macho_file, false) + (macho_file.headerpad_size orelse 0);
138 var padding: u32 = calcLoadCommandsSize(macho_file, false) + (options.headerpad orelse 0);
139 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});135 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
140136
141 if (options.headerpad_max_install_names) {137 if (macho_file.headerpad_max_install_names) {
142 const min_headerpad_size: u32 = calcLoadCommandsSize(macho_file, true);138 const min_headerpad_size: u32 = calcLoadCommandsSize(macho_file, true);
143 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{139 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
144 min_headerpad_size + @sizeOf(macho.mach_header_64),140 min_headerpad_size + @sizeOf(macho.mach_header_64),