authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-24 09:48:00+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-24 17:59:44+02:00
log9745e7b5126a5429da0be67899d89803e769498c
treea8ac1011498798d4dc9458d8d51f141d24375f7b
parent1698e6d7a7af7fa2b92f11c1c58581f297bc44cb

Clean up draft for merging into upstream

Signed-off-by: Jakub Konka <kubkon@jakubkonka.com>

3 files changed, 91 insertions(+), 259 deletions(-)

lib/std/macho.zig+5
...@@ -119,6 +119,11 @@ pub const dylinker_command = extern struct {...@@ -119,6 +119,11 @@ pub const dylinker_command = extern struct {
119 name: u32,119 name: u32,
120};120};
121121
122/// A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
123/// contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
124/// An object that uses a dynamically linked shared library also contains a
125/// dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
126/// LC_REEXPORT_DYLIB) for each library it uses.
122pub const dylib_command = extern struct {127pub const dylib_command = extern struct {
123 /// LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB128 /// LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB
124 cmd: u32,129 cmd: u32,
src-self-hosted/codegen.zig-51
...@@ -1428,57 +1428,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1428,57 +1428,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1428 }1428 }
1429 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {1429 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
1430 switch (arch) {1430 switch (arch) {
1431 // .x86_64 => {
1432 // for (info.args) |mc_arg, arg_i| {
1433 // const arg = inst.args[arg_i];
1434 // const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1435 // // Here we do not use setRegOrMem even though the logic is similar, because
1436 // // the function call will move the stack pointer, so the offsets are different.
1437 // switch (mc_arg) {
1438 // .none => continue,
1439 // .register => |reg| {
1440 // try self.genSetReg(arg.src, reg, arg_mcv);
1441 // // TODO interact with the register allocator to mark the instruction as moved.
1442 // },
1443 // .stack_offset => {
1444 // // Here we need to emit instructions like this:
1445 // // mov qword ptr [rsp + stack_offset], x
1446 // return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1447 // },
1448 // .ptr_stack_offset => {
1449 // return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1450 // },
1451 // .ptr_embedded_in_code => {
1452 // return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1453 // },
1454 // .undef => unreachable,
1455 // .immediate => unreachable,
1456 // .unreach => unreachable,
1457 // .dead => unreachable,
1458 // .embedded_in_code => unreachable,
1459 // .memory => unreachable,
1460 // .compare_flags_signed => unreachable,
1461 // .compare_flags_unsigned => unreachable,
1462 // }
1463 // }
1464
1465 // if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1466 // if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1467 // const func = func_val.func;
1468 // const got = &macho_file.segment_cmds.items[macho_file.seg_got_index.?];
1469 // const ptr_bytes: u64 = 8;
1470 // const got_addr = @intCast(u32, got.vmaddrs + func.owner_decl.link.macho.offset_table_index * ptr_bytes);
1471 // // 01 xx xx xx xx call [addr]
1472 // try self.code.ensureCapacity(self.code.items.len + 5);
1473 // self.code.appendSliceAssumeCapacity(&[1]u8{ 0x1 });
1474 // mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
1475 // } else {
1476 // return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1477 // }
1478 // } else {
1479 // return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1480 // }
1481 // },
1482 .x86_64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for x86_64 arch", .{}),1431 .x86_64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for x86_64 arch", .{}),
1483 .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),1432 .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
1484 else => unreachable,1433 else => unreachable,
src-self-hosted/link/MachO.zig+86-208
...@@ -16,6 +16,8 @@ const Module = @import("../Module.zig");...@@ -16,6 +16,8 @@ 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
19pub const base_tag: File.Tag = File.Tag.macho;21pub const base_tag: File.Tag = File.Tag.macho;
2022
21base: File,23base: File,
...@@ -42,16 +44,27 @@ seg_table_dirty: bool = false,...@@ -42,16 +44,27 @@ seg_table_dirty: bool = false,
4244
43error_flags: File.ErrorFlags = File.ErrorFlags{},45error_flags: File.ErrorFlags = File.ErrorFlags{},
4446
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
45/// `alloc_num / alloc_den` is the factor of padding when allocating.51/// `alloc_num / alloc_den` is the factor of padding when allocating.
46const alloc_num = 4;52const alloc_num = 4;
47const alloc_den = 3;53const alloc_den = 3;
4854
49/// Default path to dyld55/// Default path to dyld
56/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
57/// instead but this will do for now.
50const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";58const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
5159
52/// We always have to link against libSystem since macOS Catalina (TODO link)60/// Default lib search path
61/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
62/// instead but this will do for now.
63const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
64
53const LIB_SYSTEM_NAME: [*:0]const u8 = "System";65const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
54const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib";66/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
67const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
5568
56pub const TextBlock = struct {69pub const TextBlock = struct {
57 pub const empty = TextBlock{};70 pub const empty = TextBlock{};
...@@ -212,74 +225,81 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -212,74 +225,81 @@ pub fn flush(self: *MachO, module: *Module) !void {
212225
213 switch (self.base.options.output_mode) {226 switch (self.base.options.output_mode) {
214 .Exe => {227 .Exe => {
215 {228 if (self.link_against_system) {
216 // We need to add LC_LOAD_DYLINKER and LC_LOAD_DYLIB since we always229 if (is_darwin) {
217 // have to link against libSystem.dylib230 {
218 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)));231 // Specify path to dynamic linker dyld
219 const load_dylinker = [1]macho.dylinker_command{232 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)));
220 .{233 const load_dylinker = [1]macho.dylinker_command{
221 .cmd = macho.LC_LOAD_DYLINKER,234 .{
222 .cmdsize = cmdsize,235 .cmd = macho.LC_LOAD_DYLINKER,
223 .name = @sizeOf(macho.dylinker_command),236 .cmdsize = cmdsize,
224 },237 .name = @sizeOf(macho.dylinker_command),
225 };238 },
226 try self.commands.append(self.base.allocator, .{239 };
227 .cmd = macho.LC_LOAD_DYLINKER,240 try self.commands.append(self.base.allocator, .{
228 .cmdsize = cmdsize,241 .cmd = macho.LC_LOAD_DYLINKER,
229 });242 .cmdsize = cmdsize,
230243 });
231 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);244
232245 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);
233 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);246
234 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);247 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);
235248 try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
236 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);249
237 self.command_file_offset.? += cmdsize;250 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
238 }251 self.command_file_offset.? += cmdsize;
239252 }
240 {253
241 // Link against libSystem254 {
242 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)));255 // Link against libSystem
243 const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME);256 const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)));
244 const dylib = .{257 // According to Apple's manual, we should obtain current libSystem version using libc call
245 .name = @sizeOf(macho.dylib_command),258 // NSVersionOfRunTimeLibrary.
246 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files259 const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME);
247 .current_version = version,260 const dylib = .{
248 .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering261 .name = @sizeOf(macho.dylib_command),
249 };262 .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
250 const load_dylib = [1]macho.dylib_command{263 .current_version = version,
251 .{264 .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering
252 .cmd = macho.LC_LOAD_DYLIB,265 };
253 .cmdsize = cmdsize,266 const load_dylib = [1]macho.dylib_command{
254 .dylib = dylib,267 .{
255 },268 .cmd = macho.LC_LOAD_DYLIB,
256 };269 .cmdsize = cmdsize,
257 try self.commands.append(self.base.allocator, .{270 .dylib = dylib,
258 .cmd = macho.LC_LOAD_DYLIB,271 },
259 .cmdsize = cmdsize,272 };
260 });273 try self.commands.append(self.base.allocator, .{
261274 .cmd = macho.LC_LOAD_DYLIB,
262 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);275 .cmdsize = cmdsize,
263276 });
264 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);277
265 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);278 try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);
266279
267 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);280 const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);
268 self.command_file_offset.? += cmdsize;281 try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
282
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 }
269 }289 }
270 },290 },
271 .Obj => return error.TODOImplementWritingObjFiles,291 .Obj => return error.TODOImplementWritingObjFiles,
272 .Lib => return error.TODOImplementWritingLibFiles,292 .Lib => return error.TODOImplementWritingLibFiles,
273 }293 }
274294
275 // if (self.entry_addr == null and self.base.options.output_mode == .Exe) {295 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
276 // log.debug("flushing. no_entry_point_found = true\n", .{});296 log.debug("flushing. no_entry_point_found = true\n", .{});
277 // self.error_flags.no_entry_point_found = true;297 self.error_flags.no_entry_point_found = true;
278 // } else {298 } else {
279 log.debug("flushing. no_entry_point_found = false\n", .{});299 log.debug("flushing. no_entry_point_found = false\n", .{});
280 self.error_flags.no_entry_point_found = false;300 self.error_flags.no_entry_point_found = false;
281 try self.writeMachOHeader();301 try self.writeMachOHeader();
282 // }302 }
283}303}
284304
285pub fn deinit(self: *MachO) void {305pub fn deinit(self: *MachO) void {
...@@ -290,51 +310,7 @@ pub fn deinit(self: *MachO) void {...@@ -290,51 +310,7 @@ pub fn deinit(self: *MachO) void {
290310
291pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {}311pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {}
292312
293pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {313pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {}
294 // const tracy = trace(@src());
295 // defer tracy.end();
296
297 // var code_buffer = std.ArrayList(u8).init(self.base.allocator);
298 // defer code_buffer.deinit();
299
300 // var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
301 // defer dbg_line_buffer.deinit();
302
303 // var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
304 // defer dbg_info_buffer.deinit();
305
306 // var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
307 // defer {
308 // for (dbg_info_type_relocs.items()) |*entry| {
309 // entry.value.relocs.deinit(self.base.allocator);
310 // }
311 // dbg_info_type_relocs.deinit(self.base.allocator);
312 // }
313
314 // const typed_value = decl.typed_value.most_recent.typed_value;
315 // log.debug("typed_value = {}", .{typed_value});
316
317 // const res = try codegen.generateSymbol(
318 // &self.base,
319 // decl.src(),
320 // typed_value,
321 // &code_buffer,
322 // &dbg_line_buffer,
323 // &dbg_info_buffer,
324 // &dbg_info_type_relocs,
325 // );
326 // log.debug("res = {}", .{res});
327
328 // const code = switch (res) {
329 // .externally_managed => |x| x,
330 // .appended => code_buffer.items,
331 // .fail => |em| {
332 // decl.analysis = .codegen_failure;
333 // try module.failed_decls.put(module.gpa, decl, em);
334 // return;
335 // },
336 // };
337}
338314
339pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}315pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
340316
...@@ -351,105 +327,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {...@@ -351,105 +327,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
351 @panic("TODO implement getDeclVAddr for MachO");327 @panic("TODO implement getDeclVAddr for MachO");
352}328}
353329
354pub fn populateMissingMetadata(self: *MachO) !void {330pub fn populateMissingMetadata(self: *MachO) !void {}
355 // if (self.seg_load_re_index == null) {
356 // self.seg_load_re_index = @intCast(u16, self.segment_cmds.items.len);
357 // const file_size = self.base.options.program_code_size_hint;
358 // const p_align = 0x1000;
359 // const off = self.findFreeSpace(file_size, p_align);
360 // log.debug("found LC_SEGMENT_64 free space 0x{x} to 0x{x}", .{ off, off + file_size });
361 // try self.segment_cmds.append(self.base.allocator, .{});
362 // self.entry_addr = null;
363 // self.seg_table_dirty = true;
364 // }
365 // if (self.seg_got_index == null) {
366 // self.seg_got_index = @intCast(u16, self.segment_cmds.items.len);
367 // const file_size = 8 * self.base.options.symbol_count_hint;
368 // // Apple recommends to page align for better performance.
369 // // TODO This is not necessarily true for MH_OBJECT which means we
370 // // could potentially shave off a couple of bytes when generating
371 // // only object files.
372 // const p_align = 0x1000;
373 // const off = self.findFreeSpace(file_size, p_align);
374 // log.debug("found LC_SEGMENT_64 free space 0x{x} to 0x{x}", .{ off, off + file_size });
375 // const default_vmaddr = 0x4000000;
376 // try self.segment_cmds.append(self.base.allocator, .{
377 // .cmd = macho.LC_SEGMENT_64,
378 // .cmdsize = @sizeOf(macho.segment_command_64),
379 // .segname = self.makeString("__TEXT"),
380 // .vmaddr = default_vmaddr,
381 // .vmsize = file_size,
382 // .fileoff = off,
383 // .filesize = file_size,
384 // .maxprot = 0x5,
385 // .initprot = 0x5,
386 // .nsects = 0,
387 // .flags = 0,
388 // });
389 // self.seg_table_dirty = true;
390 // }
391}
392
393/// Returns end pos of collision, if any.
394fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
395 const header_size: u64 = @sizeOf(macho.mach_header_64);
396 if (start < header_size)
397 return header_size;
398
399 const end = start + satMul(size, alloc_num) / alloc_den;
400
401 // if (self.sec_table_offset) |off| {
402 // const section_size: u64 = @sizeOf(macho.section_64);
403 // const tight_size = self.sections.items.len * section_size;
404 // const increased_size = satMul(tight_size, alloc_num) / alloc_den;
405 // const test_end = off + increased_size;
406 // if (end > off and start < test_end) {
407 // return test_end;
408 // }
409 // }
410
411 // if (self.seg_table_offset) |off| {
412 // const segment_size: u64 = @sizeOf(macho.segment_command_64);
413 // const tight_size = self.segment_cmds.items.len * segment_size;
414 // const increased_size = satMul(tight_size, alloc_num) / alloc_den;
415 // const test_end = off + increased_size;
416 // if (end > off and start < test_end) {
417 // return test_end;
418 // }
419 // }
420
421 // for (self.sections.items) |section| {
422 // const increased_size = satMul(section.size, alloc_num) / alloc_den;
423 // const test_end = section.offset + increased_size;
424 // if (end > section.offset and start < test_end) {
425 // return test_end;
426 // }
427 // }
428
429 for (self.segments.items) |segment| {
430 const increased_size = satMul(segment.filesize, alloc_num) / alloc_den;
431 const test_end = segment_cmd.fileoff + increased_size;
432 if (end > segment_cmd.fileoff and start < test_end) {
433 return test_end;
434 }
435 }
436
437 return null;
438}
439
440fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
441 var start: u64 = 0;
442 while (self.detectAllocCollision(start, object_size)) |item_end| {
443 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
444 }
445 return start;
446}
447
448/// Saturating multiplication
449fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
450 const T = @TypeOf(a, b);
451 return std.math.mul(T, a, b) catch std.math.maxInt(T);
452}
453331
454fn makeString(comptime bytes: []const u8) [16]u8 {332fn makeString(comptime bytes: []const u8) [16]u8 {
455 var buf: [16]u8 = undefined;333 var buf: [16]u8 = undefined;