Commit 98d6d40cd6
Changed files (2)
src
link
src/link/MachO/load_commands.zig
@@ -18,7 +18,6 @@ fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool
}
pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
- const options = &macho_file.options;
var sizeofcmds: u64 = 0;
// LC_SEGMENT_64
@@ -44,14 +43,14 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
false,
);
// LC_MAIN
- if (!options.dylib) {
+ if (!macho_file.base.isDynLib()) {
sizeofcmds += @sizeOf(macho.entry_point_command);
}
// LC_ID_DYLIB
- if (options.dylib) {
+ if (macho_file.base.isDynLib()) {
sizeofcmds += blk: {
- const emit = options.emit;
- const install_name = options.install_name orelse emit.sub_path;
+ const emit = macho_file.base.emit;
+ const install_name = macho_file.install_name orelse emit.sub_path;
break :blk calcInstallNameLen(
@sizeOf(macho.dylib_command),
install_name,
@@ -61,7 +60,7 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
}
// LC_RPATH
{
- for (options.rpath_list) |rpath| {
+ for (macho_file.rpath_table.keys()) |rpath| {
sizeofcmds += calcInstallNameLen(
@sizeOf(macho.rpath_command),
rpath,
@@ -71,14 +70,12 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) u32 {
}
// LC_SOURCE_VERSION
sizeofcmds += @sizeOf(macho.source_version_command);
- if (options.platform) |platform| {
- if (platform.isBuildVersionCompatible()) {
- // LC_BUILD_VERSION
- sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
- } else {
- // LC_VERSION_MIN_*
- sizeofcmds += @sizeOf(macho.version_min_command);
- }
+ if (macho_file.platform.isBuildVersionCompatible()) {
+ // LC_BUILD_VERSION
+ sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
+ } else {
+ // LC_VERSION_MIN_*
+ sizeofcmds += @sizeOf(macho.version_min_command);
}
// LC_UUID
sizeofcmds += @sizeOf(macho.uuid_command);
@@ -134,11 +131,10 @@ pub fn calcLoadCommandsSizeObject(macho_file: *MachO) u32 {
}
pub fn calcMinHeaderPadSize(macho_file: *MachO) u32 {
- const options = &macho_file.options;
- var padding: u32 = calcLoadCommandsSize(macho_file, false) + (options.headerpad orelse 0);
+ var padding: u32 = calcLoadCommandsSize(macho_file, false) + (macho_file.headerpad_size orelse 0);
log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
- if (options.headerpad_max_install_names) {
+ if (macho_file.headerpad_max_install_names) {
const min_headerpad_size: u32 = calcLoadCommandsSize(macho_file, true);
log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
min_headerpad_size + @sizeOf(macho.mach_header_64),
src/link/MachO.zig
@@ -110,6 +110,8 @@ compatibility_version: ?std.SemanticVersion,
entry_name: ?[]const u8,
platform: Platform,
sdk_version: ?std.SemanticVersion,
+/// Rpath table
+rpath_table: std.StringArrayHashMapUnmanaged(void) = .{},
/// Hot-code swapping state.
hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
@@ -200,6 +202,12 @@ pub fn createEmpty(
.mode = link.File.determineMode(false, output_mode, link_mode),
});
+ // Filter rpaths
+ try self.rpath_table.ensureUnusedCapacity(gpa, self.base.rpath_list.len);
+ for (options.rpath_list) |rpath| {
+ _ = self.rpath_table.putAssumeCapacity(rpath, {});
+ }
+
// Append null file
try self.files.append(gpa, .null);
// Atom at index 0 is reserved as null atom
@@ -317,6 +325,7 @@ pub fn deinit(self: *MachO) void {
}
self.thunks.deinit(gpa);
self.unwind_records.deinit(gpa);
+ self.rpath_table.deinit(gpa);
}
pub 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
if (module_obj_path) |path| try positionals.append(.{ .path = path });
- // rpaths
- var rpath_table = std.StringArrayHashMap(void).init(gpa);
- defer rpath_table.deinit();
- try rpath_table.ensureUnusedCapacity(self.base.rpath_list.len);
-
- for (self.base.rpath_list) |rpath| {
- _ = rpath_table.putAssumeCapacity(rpath, {});
- }
-
for (positionals.items) |obj| {
self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
error.MalformedObject,
@@ -533,6 +533,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
try self.generateUnwindInfo();
try self.initSegments();
+ try self.allocateSections();
+ self.allocateSegments();
+ self.allocateAtoms();
+ self.allocateSyntheticSymbols();
+
state_log.debug("{}", .{self.dumpState()});
@panic("TODO");
@@ -613,7 +618,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
try argv.append(syslibroot);
}
- for (self.base.rpath_list) |rpath| {
+ for (self.rpath_table.keys()) |rpath| {
try argv.append("-rpath");
try argv.append(rpath);
}
@@ -2016,6 +2021,171 @@ fn initSegments(self: *MachO) !void {
self.linkedit_seg_index = self.getSegmentByName("__LINKEDIT").?;
}
+fn allocateSections(self: *MachO) !void {
+ const headerpad = load_commands.calcMinHeaderPadSize(self);
+ var vmaddr: u64 = if (self.pagezero_seg_index) |index|
+ self.segments.items[index].vmaddr + self.segments.items[index].vmsize
+ else
+ 0;
+ vmaddr += headerpad;
+ var fileoff = headerpad;
+
+ const page_size = self.getPageSize();
+ const slice = self.sections.slice();
+
+ var next_seg_id: u8 = if (self.pagezero_seg_index) |index| index + 1 else 0;
+ for (slice.items(.header), slice.items(.segment_id)) |*header, seg_id| {
+ if (seg_id != next_seg_id) {
+ vmaddr = mem.alignForward(u64, vmaddr, page_size);
+ fileoff = mem.alignForward(u32, fileoff, page_size);
+ }
+
+ const alignment = try math.powi(u32, 2, header.@"align");
+
+ vmaddr = mem.alignForward(u64, vmaddr, alignment);
+ header.addr = vmaddr;
+ vmaddr += header.size;
+
+ if (!header.isZerofill()) {
+ fileoff = mem.alignForward(u32, fileoff, alignment);
+ header.offset = fileoff;
+ fileoff += @intCast(header.size);
+ }
+
+ next_seg_id = seg_id;
+ }
+}
+
+fn allocateSegments(self: *MachO) void {
+ const page_size = self.getPageSize();
+ var vmaddr = if (self.pagezero_seg_index) |index|
+ self.segments.items[index].vmaddr + self.segments.items[index].vmsize
+ else
+ 0;
+ var fileoff: u64 = 0;
+ const index = if (self.pagezero_seg_index) |index| index + 1 else 0;
+
+ const slice = self.sections.slice();
+ var next_sect_id: u8 = 0;
+ for (self.segments.items[index..], index..) |*seg, seg_id| {
+ seg.vmaddr = vmaddr;
+ seg.fileoff = fileoff;
+
+ for (
+ slice.items(.header)[next_sect_id..],
+ slice.items(.segment_id)[next_sect_id..],
+ ) |header, sid| {
+ if (seg_id != sid) break;
+
+ vmaddr = header.addr + header.size;
+ if (!header.isZerofill()) {
+ fileoff = header.offset + header.size;
+ }
+
+ next_sect_id += 1;
+ }
+
+ vmaddr = mem.alignForward(u64, vmaddr, page_size);
+ fileoff = mem.alignForward(u64, fileoff, page_size);
+
+ seg.vmsize = vmaddr - seg.vmaddr;
+ seg.filesize = fileoff - seg.fileoff;
+ }
+}
+
+pub fn allocateAtoms(self: *MachO) void {
+ const slice = self.sections.slice();
+ for (slice.items(.header), slice.items(.atoms)) |header, atoms| {
+ if (atoms.items.len == 0) continue;
+ for (atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index).?;
+ assert(atom.flags.alive);
+ atom.value += header.addr;
+ }
+ }
+
+ for (self.thunks.items) |*thunk| {
+ const header = self.sections.items(.header)[thunk.out_n_sect];
+ thunk.value += header.addr;
+ }
+}
+
+fn allocateSyntheticSymbols(self: *MachO) void {
+ const text_seg = self.getTextSegment();
+
+ if (self.mh_execute_header_index) |index| {
+ const global = self.getSymbol(index);
+ global.value = text_seg.vmaddr;
+ }
+
+ if (self.data_sect_index) |idx| {
+ const sect = self.sections.items(.header)[idx];
+ for (&[_]?Symbol.Index{
+ self.dso_handle_index,
+ self.mh_dylib_header_index,
+ self.dyld_private_index,
+ }) |maybe_index| {
+ if (maybe_index) |index| {
+ const global = self.getSymbol(index);
+ global.value = sect.addr;
+ global.out_n_sect = idx;
+ }
+ }
+ }
+
+ for (self.boundary_symbols.items) |sym_index| {
+ const sym = self.getSymbol(sym_index);
+ const name = sym.getName(self);
+
+ sym.flags.@"export" = false;
+ sym.value = text_seg.vmaddr;
+
+ if (mem.startsWith(u8, name, "segment$start$")) {
+ const segname = name["segment$start$".len..];
+ if (self.getSegmentByName(segname)) |seg_id| {
+ const seg = self.segments.items[seg_id];
+ sym.value = seg.vmaddr;
+ }
+ } else if (mem.startsWith(u8, name, "segment$stop$")) {
+ const segname = name["segment$stop$".len..];
+ if (self.getSegmentByName(segname)) |seg_id| {
+ const seg = self.segments.items[seg_id];
+ sym.value = seg.vmaddr + seg.vmsize;
+ }
+ } else if (mem.startsWith(u8, name, "section$start$")) {
+ const actual_name = name["section$start$".len..];
+ const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+ const segname = actual_name[0..sep];
+ const sectname = actual_name[sep + 1 ..];
+ if (self.getSectionByName(segname, sectname)) |sect_id| {
+ const sect = self.sections.items(.header)[sect_id];
+ sym.value = sect.addr;
+ sym.out_n_sect = sect_id;
+ }
+ } else if (mem.startsWith(u8, name, "section$stop$")) {
+ const actual_name = name["section$stop$".len..];
+ const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic
+ const segname = actual_name[0..sep];
+ const sectname = actual_name[sep + 1 ..];
+ if (self.getSectionByName(segname, sectname)) |sect_id| {
+ const sect = self.sections.items(.header)[sect_id];
+ sym.value = sect.addr + sect.size;
+ sym.out_n_sect = sect_id;
+ }
+ } else unreachable;
+ }
+
+ if (self.objc_stubs.symbols.items.len > 0) {
+ const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr;
+
+ for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| {
+ const sym = self.getSymbol(sym_index);
+ sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch);
+ sym.out_n_sect = self.objc_stubs_sect_index.?;
+ }
+ }
+}
+
fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
_ = self;
_ = atom_index;
@@ -2952,8 +3122,8 @@ pub const Platform = struct {
pub fn isBuildVersionCompatible(plat: Platform) bool {
inline for (supported_platforms) |sup_plat| {
- if (sup_plat[0] == plat.platform) {
- return sup_plat[1] <= plat.version.value;
+ if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
+ return sup_plat[2] <= plat.toAppleVersion();
}
}
return false;