authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-26 07:03:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-26 07:03:58+02:00
log982ab7df6cd61a874e98ef99e923a98e02cf7487
treef3ce7f09f3284ad88183e7edfddbfd5870ac0556
parent3e24e958922806379f9f609e36954a489eb20831
parent55dc81ba2a86519a2fd5bfd985d2d6465ccf4966
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6161 from kubkon/macho-text

Add (empty) __TEXT segment load command

2 files changed, 117 insertions(+), 77 deletions(-)

lib/std/macho.zig+13
...@@ -797,3 +797,16 @@ pub const CPU_SUBTYPE_X86_64_ALL: cpu_subtype_t = 0x3;...@@ -797,3 +797,16 @@ pub const CPU_SUBTYPE_X86_64_ALL: cpu_subtype_t = 0x3;
797797
798/// All ARM-based Macs798/// All ARM-based Macs
799pub const CPU_SUBTYPE_ARM_ALL: cpu_subtype_t = 0x0;799pub const CPU_SUBTYPE_ARM_ALL: cpu_subtype_t = 0x0;
800
801// Protection values defined as bits within the vm_prot_t type
802/// No VM protection
803pub const VM_PROT_NONE: vm_prot_t = 0x0;
804
805/// VM read permission
806pub const VM_PROT_READ: vm_prot_t = 0x1;
807
808/// VM write permission
809pub const VM_PROT_WRITE: vm_prot_t = 0x2;
810
811/// VM execute permission
812pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;
src-self-hosted/link/MachO.zig+104-77
...@@ -16,8 +16,6 @@ const Module = @import("../Module.zig");...@@ -16,8 +16,6 @@ const Module = @import("../Module.zig");
16const link = @import("../link.zig");16const link = @import("../link.zig");
17const File = link.File;17const File = link.File;
1818
19const is_darwin = std.Target.current.os.tag.isDarwin();
20
21pub const base_tag: File.Tag = File.Tag.macho;19pub const base_tag: File.Tag = File.Tag.macho;
2220
23base: File,21base: File,
...@@ -30,24 +28,26 @@ command_file_offset: ?u64 = null,...@@ -30,24 +28,26 @@ command_file_offset: ?u64 = null,
30/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.28/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
31/// Same order as in the file.29/// Same order as in the file.
32segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},30segments: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},
31/// Section (headers) *always* follow segment (load commands) directly!
33sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){},32sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){},
34segment_table_offset: ?u64 = null,33
34/// Offset (index) into __TEXT segment load command.
35text_segment_offset: ?u64 = null,
36/// Offset (index) into __LINKEDIT segment load command.
37linkedit_segment_offset: ?u664 = null,
3538
36/// Entry point load command39/// Entry point load command
37entry_point_cmd: ?macho.entry_point_command = null,40entry_point_cmd: ?macho.entry_point_command = null,
38entry_addr: ?u64 = null,41entry_addr: ?u64 = null,
3942
40/// Default VM start address set at 4GB43/// The first 4GB of process' memory is reserved for the null (__PAGEZERO) segment.
44/// This is also the start address for our binary.
41vm_start_address: u64 = 0x100000000,45vm_start_address: u64 = 0x100000000,
4246
43seg_table_dirty: bool = false,47seg_table_dirty: bool = false,
4448
45error_flags: File.ErrorFlags = File.ErrorFlags{},49error_flags: File.ErrorFlags = File.ErrorFlags{},
4650
47/// TODO ultimately this will be propagated down from main() and set (in this form or another)
48/// when user links against system lib.
49link_against_system: bool = false,
50
51/// `alloc_num / alloc_den` is the factor of padding when allocating.51/// `alloc_num / alloc_den` is the factor of padding when allocating.
52const alloc_num = 4;52const alloc_num = 4;
53const alloc_den = 3;53const alloc_den = 3;
...@@ -138,8 +138,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach...@@ -138,8 +138,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
138 .vmsize = self.vm_start_address,138 .vmsize = self.vm_start_address,
139 .fileoff = 0,139 .fileoff = 0,
140 .filesize = 0,140 .filesize = 0,
141 .maxprot = 0,141 .maxprot = macho.VM_PROT_NONE,
142 .initprot = 0,142 .initprot = macho.VM_PROT_NONE,
143 .nsects = 0,143 .nsects = 0,
144 .flags = 0,144 .flags = 0,
145 };145 };
...@@ -225,67 +225,61 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -225,67 +225,61 @@ pub fn flush(self: *MachO, module: *Module) !void {
225225
226 switch (self.base.options.output_mode) {226 switch (self.base.options.output_mode) {
227 .Exe => {227 .Exe => {
228 if (self.link_against_system) {228 {
229 if (is_darwin) {229 // Specify path to dynamic linker dyld
230 {230 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
231 // Specify path to dynamic linker dyld231 const load_dylinker = [1]macho.dylinker_command{
232 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)));232 .{
233 const load_dylinker = [1]macho.dylinker_command{233 .cmd = macho.LC_LOAD_DYLINKER,
234 .{234 .cmdsize = cmdsize,
235 .cmd = macho.LC_LOAD_DYLINKER,235 .name = @sizeOf(macho.dylinker_command),
236 .cmdsize = cmdsize,236 },
237 .name = @sizeOf(macho.dylinker_command),237 };
238 },238 try self.commands.append(self.base.allocator, .{
239 };239 .cmd = macho.LC_LOAD_DYLINKER,
240 try self.commands.append(self.base.allocator, .{240 .cmdsize = cmdsize,
241 .cmd = macho.LC_LOAD_DYLINKER,241 });
242 .cmdsize = cmdsize,242
243 });243 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);
244244
245 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);245 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);
246246 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
247 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);247
248 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);248 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
249249 self.command_file_offset.? += cmdsize;
250 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);250 }
251 self.command_file_offset.? += cmdsize;251
252 }252 {
253253 // Link against libSystem
254 {254 const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
255 // Link against libSystem255 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
256 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)));256 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
257 // According to Apple's manual, we should obtain current libSystem version using libc call257 const min_version = 0x10000;
258 // NSVersionOfRunTimeLibrary.258 const dylib = .{
259 const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME);259 .name = @sizeOf(macho.dylib_command),
260 const dylib = .{260 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
261 .name = @sizeOf(macho.dylib_command),261 .current_version = min_version,
262 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files262 .compatibility_version = min_version,
263 .current_version = version,263 };
264 .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering264 const load_dylib = [1]macho.dylib_command{
265 };265 .{
266 const load_dylib = [1]macho.dylib_command{266 .cmd = macho.LC_LOAD_DYLIB,
267 .{267 .cmdsize = cmdsize,
268 .cmd = macho.LC_LOAD_DYLIB,268 .dylib = dylib,
269 .cmdsize = cmdsize,269 },
270 .dylib = dylib,270 };
271 },271 try self.commands.append(self.base.allocator, .{
272 };272 .cmd = macho.LC_LOAD_DYLIB,
273 try self.commands.append(self.base.allocator, .{273 .cmdsize = cmdsize,
274 .cmd = macho.LC_LOAD_DYLIB,274 });
275 .cmdsize = cmdsize,275
276 });276 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);
277277
278 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);278 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);
279279 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
280 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);280
281 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);281 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
282282 self.command_file_offset.? += cmdsize;
283 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
284 self.command_file_offset.? += cmdsize;
285 }
286 } else {
287 @panic("linking against libSystem on non-native target is unsupported");
288 }
289 }283 }
290 },284 },
291 .Obj => return error.TODOImplementWritingObjFiles,285 .Obj => return error.TODOImplementWritingObjFiles,
...@@ -327,20 +321,53 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {...@@ -327,20 +321,53 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
327 @panic("TODO implement getDeclVAddr for MachO");321 @panic("TODO implement getDeclVAddr for MachO");
328}322}
329323
330pub fn populateMissingMetadata(self: *MachO) !void {}324pub fn populateMissingMetadata(self: *MachO) !void {
325 if (self.text_segment_offset == null) {
326 self.text_segment_offset = @intCast(u64, self.segments.items.len);
327 const file_size = alignSize(u64, self.base.options.program_code_size_hint, 0x1000);
328 log.debug("vmsize/filesize = {}", .{file_size});
329 const file_offset = 0;
330 const vm_address = self.vm_start_address; // the end of __PAGEZERO segment in VM
331 const protection = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
332 const cmdsize = commandSize(@sizeOf(macho.segment_command_64));
333 const text_segment = .{
334 .cmd = macho.LC_SEGMENT_64,
335 .cmdsize = cmdsize,
336 .segname = makeString("__TEXT"),
337 .vmaddr = vm_address,
338 .vmsize = file_size,
339 .fileoff = 0, // __TEXT segment *always* starts at 0 file offset
340 .filesize = 0, //file_size,
341 .maxprot = protection,
342 .initprot = protection,
343 .nsects = 0,
344 .flags = 0,
345 };
346 try self.commands.append(self.base.allocator, .{
347 .cmd = macho.LC_SEGMENT_64,
348 .cmdsize = cmdsize,
349 });
350 try self.segments.append(self.base.allocator, text_segment);
351 }
352}
331353
332fn makeString(comptime bytes: []const u8) [16]u8 {354fn makeString(comptime bytes: []const u8) [16]u8 {
333 var buf: [16]u8 = undefined;355 var buf = [_]u8{0} ** 16;
334 if (bytes.len > buf.len) @compileError("MachO segment/section name too long");356 if (bytes.len > buf.len) @compileError("MachO segment/section name too long");
335 mem.copy(u8, buf[0..], bytes);357 mem.copy(u8, buf[0..], bytes);
336 return buf;358 return buf;
337}359}
338360
339fn commandSize(min_size: u32) u32 {361fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
340 if (min_size % @sizeOf(u64) == 0) return min_size;362 const size = @intCast(Int, min_size);
363 if (size % alignment == 0) return size;
364
365 const div = size / alignment;
366 return (div + 1) * alignment;
367}
341368
342 const div = min_size / @sizeOf(u64);369fn commandSize(min_size: anytype) u32 {
343 return (div + 1) * @sizeOf(u64);370 return alignSize(u32, min_size, @sizeOf(u64));
344}371}
345372
346fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {373fn addPadding(self: *MachO, size: u32, file_offset: u64) !void {