mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
commit
17c066e925
@ -564,7 +564,14 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
|
||||
|
@ -250,24 +250,6 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
|
||||
resetSegfaultHandler();
|
||||
}
|
||||
|
||||
if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64)
|
||||
nosuspend {
|
||||
// As a workaround for not having threadlocal variable support in LLD for this target,
|
||||
// we have a simpler panic implementation that does not use threadlocal variables.
|
||||
// TODO https://github.com/ziglang/zig/issues/7527
|
||||
const stderr = io.getStdErr().writer();
|
||||
if (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst) == 0) {
|
||||
stderr.print("panic: " ++ format ++ "\n", args) catch os.abort();
|
||||
if (trace) |t| {
|
||||
dumpStackTrace(t.*);
|
||||
}
|
||||
dumpCurrentStackTrace(first_trace_addr);
|
||||
} else {
|
||||
stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
|
||||
}
|
||||
os.abort();
|
||||
};
|
||||
|
||||
nosuspend switch (panic_stage) {
|
||||
0 => {
|
||||
panic_stage = 1;
|
||||
|
@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200;
|
||||
/// section has local relocation entries
|
||||
pub const S_ATTR_LOC_RELOC = 0x100;
|
||||
|
||||
/// template of initial values for TLVs
|
||||
pub const S_THREAD_LOCAL_REGULAR = 0x11;
|
||||
|
||||
/// template of initial values for TLVs
|
||||
pub const S_THREAD_LOCAL_ZEROFILL = 0x12;
|
||||
|
||||
/// TLV descriptors
|
||||
pub const S_THREAD_LOCAL_VARIABLES = 0x13;
|
||||
|
||||
/// pointers to TLV descriptors
|
||||
pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
|
||||
|
||||
/// functions to call to initialize TLV values
|
||||
pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
|
||||
|
||||
/// 32-bit offsets to initializers
|
||||
pub const S_INIT_FUNC_OFFSETS = 0x16;
|
||||
|
||||
pub const cpu_type_t = integer_t;
|
||||
pub const cpu_subtype_t = integer_t;
|
||||
pub const integer_t = c_int;
|
||||
@ -1597,3 +1615,17 @@ pub const GenericBlob = extern struct {
|
||||
/// Total length of blob
|
||||
length: u32,
|
||||
};
|
||||
|
||||
/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
|
||||
/// to point to an array of data_in_code_entry entries. Each entry
|
||||
/// describes a range of data in a code section.
|
||||
pub const data_in_code_entry = extern struct {
|
||||
/// From mach_header to start of data range.
|
||||
offset: u32,
|
||||
|
||||
/// Number of bytes in data range.
|
||||
length: u16,
|
||||
|
||||
/// A DICE_KIND value.
|
||||
kind: u16,
|
||||
};
|
||||
|
@ -221,7 +221,8 @@ pub const Instruction = union(enum) {
|
||||
offset: u12,
|
||||
opc: u2,
|
||||
op1: u2,
|
||||
fixed: u4 = 0b111_0,
|
||||
v: u1,
|
||||
fixed: u3 = 0b111,
|
||||
size: u2,
|
||||
},
|
||||
LoadStorePairOfRegisters: packed struct {
|
||||
@ -505,6 +506,7 @@ pub const Instruction = union(enum) {
|
||||
.offset = offset.toU12(),
|
||||
.opc = opc,
|
||||
.op1 = op1,
|
||||
.v = 0,
|
||||
.size = 0b10,
|
||||
},
|
||||
};
|
||||
@ -517,6 +519,7 @@ pub const Instruction = union(enum) {
|
||||
.offset = offset.toU12(),
|
||||
.opc = opc,
|
||||
.op1 = op1,
|
||||
.v = 0,
|
||||
.size = 0b11,
|
||||
},
|
||||
};
|
||||
|
@ -26,6 +26,7 @@ const target_util = @import("../target.zig");
|
||||
const DebugSymbols = @import("MachO/DebugSymbols.zig");
|
||||
const Trie = @import("MachO/Trie.zig");
|
||||
const CodeSignature = @import("MachO/CodeSignature.zig");
|
||||
const Zld = @import("MachO/Zld.zig");
|
||||
|
||||
usingnamespace @import("MachO/commands.zig");
|
||||
|
||||
@ -632,7 +633,74 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
if (!mem.eql(u8, the_object_path, full_out_path)) {
|
||||
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
|
||||
}
|
||||
} else {
|
||||
} else outer: {
|
||||
const use_zld = blk: {
|
||||
if (self.base.options.is_native_os and self.base.options.system_linker_hack) {
|
||||
// If the user forces the use of ld64, make sure we are running native!
|
||||
break :blk false;
|
||||
}
|
||||
|
||||
if (self.base.options.target.cpu.arch == .aarch64) {
|
||||
// On aarch64, always use zld.
|
||||
break :blk true;
|
||||
}
|
||||
|
||||
if (self.base.options.link_libcpp or
|
||||
self.base.options.output_mode == .Lib or
|
||||
self.base.options.linker_script != null)
|
||||
{
|
||||
// Fallback to LLD in this handful of cases on x86_64 only.
|
||||
break :blk false;
|
||||
}
|
||||
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
if (use_zld) {
|
||||
var zld = Zld.init(self.base.allocator);
|
||||
defer zld.deinit();
|
||||
zld.arch = target.cpu.arch;
|
||||
|
||||
var input_files = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer input_files.deinit();
|
||||
// Positional arguments to the linker such as object files.
|
||||
try input_files.appendSlice(self.base.options.objects);
|
||||
for (comp.c_object_table.items()) |entry| {
|
||||
try input_files.append(entry.key.status.success.object_path);
|
||||
}
|
||||
if (module_obj_path) |p| {
|
||||
try input_files.append(p);
|
||||
}
|
||||
try input_files.append(comp.compiler_rt_static_lib.?.full_object_path);
|
||||
// libc++ dep
|
||||
if (self.base.options.link_libcpp) {
|
||||
try input_files.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
try input_files.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
if (self.base.options.verbose_link) {
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
try argv.append("zig");
|
||||
try argv.append("ld");
|
||||
|
||||
try argv.ensureCapacity(input_files.items.len);
|
||||
for (input_files.items) |f| {
|
||||
argv.appendAssumeCapacity(f);
|
||||
}
|
||||
|
||||
try argv.append("-o");
|
||||
try argv.append(full_out_path);
|
||||
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
try zld.link(input_files.items, full_out_path);
|
||||
|
||||
break :outer;
|
||||
}
|
||||
|
||||
// Create an LLD command line and invoke it.
|
||||
var argv = std.ArrayList([]const u8).init(self.base.allocator);
|
||||
defer argv.deinit();
|
||||
@ -903,119 +971,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
log.warn("unexpected LLD stderr:\n{s}", .{stderr});
|
||||
}
|
||||
}
|
||||
|
||||
// At this stage, LLD has done its job. It is time to patch the resultant
|
||||
// binaries up!
|
||||
const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true });
|
||||
try self.parseFromFile(out_file);
|
||||
|
||||
if (self.libsystem_cmd_index == null and self.header.?.filetype == macho.MH_EXECUTE) {
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const text_section = text_segment.sections.items[self.text_section_index.?];
|
||||
const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
|
||||
const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
|
||||
|
||||
if (needed_size + after_last_cmd_offset > text_section.offset) {
|
||||
log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
|
||||
log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
|
||||
log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
|
||||
return error.NotEnoughPadding;
|
||||
}
|
||||
|
||||
// Calculate next available dylib ordinal.
|
||||
const next_ordinal = blk: {
|
||||
var ordinal: u32 = 1;
|
||||
for (self.load_commands.items) |cmd| {
|
||||
switch (cmd) {
|
||||
.Dylib => ordinal += 1,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
break :blk ordinal;
|
||||
};
|
||||
|
||||
// Add load dylib load command
|
||||
self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
|
||||
u64,
|
||||
@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH),
|
||||
@sizeOf(u64),
|
||||
));
|
||||
// TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
|
||||
// In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
|
||||
const min_version = 0x0;
|
||||
var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
|
||||
.cmd = macho.LC_LOAD_DYLIB,
|
||||
.cmdsize = cmdsize,
|
||||
.dylib = .{
|
||||
.name = @sizeOf(macho.dylib_command),
|
||||
.timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
|
||||
.current_version = min_version,
|
||||
.compatibility_version = min_version,
|
||||
},
|
||||
});
|
||||
dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
|
||||
mem.set(u8, dylib_cmd.data, 0);
|
||||
mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
|
||||
try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
|
||||
if (self.symtab_cmd_index == null or self.dysymtab_cmd_index == null) {
|
||||
log.err("Incomplete Mach-O binary: no LC_SYMTAB or LC_DYSYMTAB load command found!", .{});
|
||||
log.err("Without the symbol table, it is not possible to patch up the binary for cross-compilation.", .{});
|
||||
return error.NoSymbolTableFound;
|
||||
}
|
||||
|
||||
// Patch dyld info
|
||||
try self.fixupBindInfo(next_ordinal);
|
||||
try self.fixupLazyBindInfo(next_ordinal);
|
||||
|
||||
// Write updated load commands and the header
|
||||
try self.writeLoadCommands();
|
||||
try self.writeHeader();
|
||||
|
||||
assert(!self.header_dirty);
|
||||
assert(!self.load_commands_dirty);
|
||||
}
|
||||
if (self.code_signature_cmd_index == null) outer: {
|
||||
if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets.
|
||||
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const text_section = text_segment.sections.items[self.text_section_index.?];
|
||||
const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
|
||||
const needed_size = padToIdeal(@sizeOf(macho.linkedit_data_command));
|
||||
|
||||
if (needed_size + after_last_cmd_offset > text_section.offset) {
|
||||
log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
|
||||
log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
|
||||
log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
|
||||
return error.NotEnoughPadding;
|
||||
}
|
||||
|
||||
// Add code signature load command
|
||||
self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
try self.load_commands.append(self.base.allocator, .{
|
||||
.LinkeditData = .{
|
||||
.cmd = macho.LC_CODE_SIGNATURE,
|
||||
.cmdsize = @sizeOf(macho.linkedit_data_command),
|
||||
.dataoff = 0,
|
||||
.datasize = 0,
|
||||
},
|
||||
});
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
|
||||
// Pad out space for code signature
|
||||
try self.writeCodeSignaturePadding();
|
||||
// Write updated load commands and the header
|
||||
try self.writeLoadCommands();
|
||||
try self.writeHeader();
|
||||
// Generate adhoc code signature
|
||||
try self.writeCodeSignature();
|
||||
|
||||
assert(!self.header_dirty);
|
||||
assert(!self.load_commands_dirty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3331,177 +3286,6 @@ fn writeHeader(self: *MachO) !void {
|
||||
self.header_dirty = false;
|
||||
}
|
||||
|
||||
/// Parse MachO contents from existing binary file.
|
||||
fn parseFromFile(self: *MachO, file: fs.File) !void {
|
||||
self.base.file = file;
|
||||
var reader = file.reader();
|
||||
const header = try reader.readStruct(macho.mach_header_64);
|
||||
try self.load_commands.ensureCapacity(self.base.allocator, header.ncmds);
|
||||
var i: u16 = 0;
|
||||
while (i < header.ncmds) : (i += 1) {
|
||||
const cmd = try LoadCommand.read(self.base.allocator, reader);
|
||||
switch (cmd.cmd()) {
|
||||
macho.LC_SEGMENT_64 => {
|
||||
const x = cmd.Segment;
|
||||
if (parseAndCmpName(&x.inner.segname, "__PAGEZERO")) {
|
||||
self.pagezero_segment_cmd_index = i;
|
||||
} else if (parseAndCmpName(&x.inner.segname, "__LINKEDIT")) {
|
||||
self.linkedit_segment_cmd_index = i;
|
||||
} else if (parseAndCmpName(&x.inner.segname, "__TEXT")) {
|
||||
self.text_segment_cmd_index = i;
|
||||
for (x.sections.items) |sect, j| {
|
||||
if (parseAndCmpName(§.sectname, "__text")) {
|
||||
self.text_section_index = @intCast(u16, j);
|
||||
}
|
||||
}
|
||||
} else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
|
||||
self.data_segment_cmd_index = i;
|
||||
} else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) {
|
||||
self.data_const_segment_cmd_index = i;
|
||||
}
|
||||
},
|
||||
macho.LC_DYLD_INFO_ONLY => {
|
||||
self.dyld_info_cmd_index = i;
|
||||
},
|
||||
macho.LC_SYMTAB => {
|
||||
self.symtab_cmd_index = i;
|
||||
},
|
||||
macho.LC_DYSYMTAB => {
|
||||
self.dysymtab_cmd_index = i;
|
||||
},
|
||||
macho.LC_LOAD_DYLINKER => {
|
||||
self.dylinker_cmd_index = i;
|
||||
},
|
||||
macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => {
|
||||
self.version_min_cmd_index = i;
|
||||
},
|
||||
macho.LC_SOURCE_VERSION => {
|
||||
self.source_version_cmd_index = i;
|
||||
},
|
||||
macho.LC_UUID => {
|
||||
self.uuid_cmd_index = i;
|
||||
},
|
||||
macho.LC_MAIN => {
|
||||
self.main_cmd_index = i;
|
||||
},
|
||||
macho.LC_LOAD_DYLIB => {
|
||||
const x = cmd.Dylib;
|
||||
if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) {
|
||||
self.libsystem_cmd_index = i;
|
||||
}
|
||||
},
|
||||
macho.LC_FUNCTION_STARTS => {
|
||||
self.function_starts_cmd_index = i;
|
||||
},
|
||||
macho.LC_DATA_IN_CODE => {
|
||||
self.data_in_code_cmd_index = i;
|
||||
},
|
||||
macho.LC_CODE_SIGNATURE => {
|
||||
self.code_signature_cmd_index = i;
|
||||
},
|
||||
else => {
|
||||
log.warn("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
|
||||
},
|
||||
}
|
||||
self.load_commands.appendAssumeCapacity(cmd);
|
||||
}
|
||||
self.header = header;
|
||||
}
|
||||
|
||||
fn parseAndCmpName(name: []const u8, needle: []const u8) bool {
|
||||
const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
|
||||
return mem.eql(u8, name[0..len], needle);
|
||||
}
|
||||
|
||||
fn parseSymbolTable(self: *MachO) !void {
|
||||
const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
|
||||
|
||||
var buffer = try self.base.allocator.alloc(macho.nlist_64, symtab.nsyms);
|
||||
defer self.base.allocator.free(buffer);
|
||||
const nread = try self.base.file.?.preadAll(@ptrCast([*]u8, buffer)[0 .. symtab.nsyms * @sizeOf(macho.nlist_64)], symtab.symoff);
|
||||
assert(@divExact(nread, @sizeOf(macho.nlist_64)) == buffer.len);
|
||||
|
||||
try self.locals.ensureCapacity(self.base.allocator, dysymtab.nlocalsym);
|
||||
try self.globals.ensureCapacity(self.base.allocator, dysymtab.nextdefsym);
|
||||
try self.undef_symbols.ensureCapacity(self.base.allocator, dysymtab.nundefsym);
|
||||
|
||||
self.locals.appendSliceAssumeCapacity(buffer[dysymtab.ilocalsym .. dysymtab.ilocalsym + dysymtab.nlocalsym]);
|
||||
self.globals.appendSliceAssumeCapacity(buffer[dysymtab.iextdefsym .. dysymtab.iextdefsym + dysymtab.nextdefsym]);
|
||||
self.undef_symbols.appendSliceAssumeCapacity(buffer[dysymtab.iundefsym .. dysymtab.iundefsym + dysymtab.nundefsym]);
|
||||
}
|
||||
|
||||
fn parseStringTable(self: *MachO) !void {
|
||||
const symtab = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
|
||||
var buffer = try self.base.allocator.alloc(u8, symtab.strsize);
|
||||
defer self.base.allocator.free(buffer);
|
||||
const nread = try self.base.file.?.preadAll(buffer, symtab.stroff);
|
||||
assert(nread == buffer.len);
|
||||
|
||||
try self.string_table.ensureCapacity(self.base.allocator, symtab.strsize);
|
||||
self.string_table.appendSliceAssumeCapacity(buffer);
|
||||
}
|
||||
|
||||
fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void {
|
||||
const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
|
||||
defer self.base.allocator.free(buffer);
|
||||
const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
|
||||
assert(nread == buffer.len);
|
||||
try self.fixupInfoCommon(buffer, dylib_ordinal);
|
||||
try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
|
||||
}
|
||||
|
||||
fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void {
|
||||
const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
|
||||
var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
|
||||
defer self.base.allocator.free(buffer);
|
||||
const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
|
||||
assert(nread == buffer.len);
|
||||
try self.fixupInfoCommon(buffer, dylib_ordinal);
|
||||
try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
|
||||
}
|
||||
|
||||
fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
var reader = stream.reader();
|
||||
|
||||
while (true) {
|
||||
const inst = reader.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => break,
|
||||
else => return err,
|
||||
};
|
||||
const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
|
||||
const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
|
||||
|
||||
switch (opcode) {
|
||||
macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
|
||||
var next = try reader.readByte();
|
||||
while (next != @as(u8, 0)) {
|
||||
next = try reader.readByte();
|
||||
}
|
||||
},
|
||||
macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
|
||||
_ = try std.leb.readULEB128(u64, reader);
|
||||
},
|
||||
macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
|
||||
// Perform the fixup.
|
||||
try stream.seekBy(-1);
|
||||
var writer = stream.writer();
|
||||
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal));
|
||||
},
|
||||
macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
|
||||
_ = try std.leb.readULEB128(u64, reader);
|
||||
},
|
||||
macho.BIND_OPCODE_SET_ADDEND_SLEB => {
|
||||
_ = try std.leb.readILEB128(i64, reader);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
|
||||
// TODO https://github.com/ziglang/zig/issues/1284
|
||||
return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
|
||||
|
256
src/link/MachO/Archive.zig
Normal file
256
src/link/MachO/Archive.zig
Normal file
@ -0,0 +1,256 @@
|
||||
const Archive = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.archive);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Object = @import("Object.zig");
|
||||
const parseName = @import("Zld.zig").parseName;
|
||||
|
||||
usingnamespace @import("commands.zig");
|
||||
|
||||
allocator: *Allocator,
|
||||
file: fs.File,
|
||||
header: ar_hdr,
|
||||
name: []u8,
|
||||
|
||||
objects: std.ArrayListUnmanaged(Object) = .{},
|
||||
|
||||
// Archive files start with the ARMAG identifying string. Then follows a
|
||||
// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
|
||||
// member indicates, for each member file.
|
||||
/// String that begins an archive file.
|
||||
const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
|
||||
/// Size of that string.
|
||||
const SARMAG: u4 = 8;
|
||||
|
||||
/// String in ar_fmag at the end of each header.
|
||||
const ARFMAG: *const [2:0]u8 = "`\n";
|
||||
|
||||
const ar_hdr = extern struct {
|
||||
/// Member file name, sometimes / terminated.
|
||||
ar_name: [16]u8,
|
||||
|
||||
/// File date, decimal seconds since Epoch.
|
||||
ar_date: [12]u8,
|
||||
|
||||
/// User ID, in ASCII format.
|
||||
ar_uid: [6]u8,
|
||||
|
||||
/// Group ID, in ASCII format.
|
||||
ar_gid: [6]u8,
|
||||
|
||||
/// File mode, in ASCII octal.
|
||||
ar_mode: [8]u8,
|
||||
|
||||
/// File size, in ASCII decimal.
|
||||
ar_size: [10]u8,
|
||||
|
||||
/// Always contains ARFMAG.
|
||||
ar_fmag: [2]u8,
|
||||
|
||||
const NameOrLength = union(enum) {
|
||||
Name: []const u8,
|
||||
Length: u64,
|
||||
};
|
||||
pub fn nameOrLength(self: ar_hdr) !NameOrLength {
|
||||
const value = getValue(&self.ar_name);
|
||||
const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive;
|
||||
const len = value.len;
|
||||
if (slash_index == len - 1) {
|
||||
// Name stored directly
|
||||
return NameOrLength{ .Name = value };
|
||||
} else {
|
||||
// Name follows the header directly and its length is encoded in
|
||||
// the name field.
|
||||
const length = try std.fmt.parseInt(u64, value[slash_index + 1 ..], 10);
|
||||
return NameOrLength{ .Length = length };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self: ar_hdr) !u64 {
|
||||
const value = getValue(&self.ar_size);
|
||||
return std.fmt.parseInt(u64, value, 10);
|
||||
}
|
||||
|
||||
fn getValue(raw: []const u8) []const u8 {
|
||||
return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)});
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Archive) void {
|
||||
self.allocator.free(self.name);
|
||||
for (self.objects.items) |*object| {
|
||||
object.deinit();
|
||||
}
|
||||
self.objects.deinit(self.allocator);
|
||||
self.file.close();
|
||||
}
|
||||
|
||||
/// Caller owns the returned Archive instance and is responsible for calling
|
||||
/// `deinit` to free allocated memory.
|
||||
pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: []const u8, file: fs.File) !Archive {
|
||||
var reader = file.reader();
|
||||
var magic = try readMagic(allocator, reader);
|
||||
defer allocator.free(magic);
|
||||
|
||||
if (!mem.eql(u8, magic, ARMAG)) {
|
||||
// Reset file cursor.
|
||||
try file.seekTo(0);
|
||||
return error.NotArchive;
|
||||
}
|
||||
|
||||
const header = try reader.readStruct(ar_hdr);
|
||||
|
||||
if (!mem.eql(u8, &header.ar_fmag, ARFMAG))
|
||||
return error.MalformedArchive;
|
||||
|
||||
var embedded_name = try getName(allocator, header, reader);
|
||||
log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, ar_name });
|
||||
defer allocator.free(embedded_name);
|
||||
|
||||
var name = try allocator.dupe(u8, ar_name);
|
||||
var self = Archive{
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.header = header,
|
||||
.name = name,
|
||||
};
|
||||
|
||||
var object_offsets = try self.readTableOfContents(reader);
|
||||
defer self.allocator.free(object_offsets);
|
||||
|
||||
var i: usize = 1;
|
||||
while (i < object_offsets.len) : (i += 1) {
|
||||
const offset = object_offsets[i];
|
||||
try reader.context.seekTo(offset);
|
||||
try self.readObject(arch, ar_name, reader);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
|
||||
const symtab_size = try reader.readIntLittle(u32);
|
||||
var symtab = try self.allocator.alloc(u8, symtab_size);
|
||||
defer self.allocator.free(symtab);
|
||||
try reader.readNoEof(symtab);
|
||||
|
||||
const strtab_size = try reader.readIntLittle(u32);
|
||||
var strtab = try self.allocator.alloc(u8, strtab_size);
|
||||
defer self.allocator.free(strtab);
|
||||
try reader.readNoEof(strtab);
|
||||
|
||||
var symtab_stream = std.io.fixedBufferStream(symtab);
|
||||
var symtab_reader = symtab_stream.reader();
|
||||
|
||||
var object_offsets = std.ArrayList(u32).init(self.allocator);
|
||||
try object_offsets.append(0);
|
||||
var last: usize = 0;
|
||||
|
||||
while (true) {
|
||||
const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
|
||||
error.EndOfStream => break,
|
||||
else => |e| return e,
|
||||
};
|
||||
const object_offset = try symtab_reader.readIntLittle(u32);
|
||||
|
||||
// TODO Store the table of contents for later reuse.
|
||||
|
||||
// Here, we assume that symbols are NOT sorted in any way, and
|
||||
// they point to objects in sequence.
|
||||
if (object_offsets.items[last] != object_offset) {
|
||||
try object_offsets.append(object_offset);
|
||||
last += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return object_offsets.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {
|
||||
const object_header = try reader.readStruct(ar_hdr);
|
||||
|
||||
if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG))
|
||||
return error.MalformedArchive;
|
||||
|
||||
var object_name = try getName(self.allocator, object_header, reader);
|
||||
log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
|
||||
|
||||
const offset = @intCast(u32, try reader.context.getPos());
|
||||
const header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
|
||||
macho.CPU_TYPE_ARM64 => .aarch64,
|
||||
macho.CPU_TYPE_X86_64 => .x86_64,
|
||||
else => |value| {
|
||||
log.err("unsupported cpu architecture 0x{x}", .{value});
|
||||
return error.UnsupportedCpuArchitecture;
|
||||
},
|
||||
};
|
||||
if (this_arch != arch) {
|
||||
log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
// TODO Implement std.fs.File.clone() or similar.
|
||||
var new_file = try fs.cwd().openFile(ar_name, .{});
|
||||
var object = Object{
|
||||
.allocator = self.allocator,
|
||||
.name = object_name,
|
||||
.ar_name = try mem.dupe(self.allocator, u8, ar_name),
|
||||
.file = new_file,
|
||||
.header = header,
|
||||
};
|
||||
|
||||
try object.readLoadCommands(reader, .{ .offset = offset });
|
||||
|
||||
if (object.symtab_cmd_index != null) {
|
||||
try object.readSymtab();
|
||||
try object.readStrtab();
|
||||
}
|
||||
|
||||
if (object.data_in_code_cmd_index != null) try object.readDataInCode();
|
||||
|
||||
log.debug("\n\n", .{});
|
||||
log.debug("{s} defines symbols", .{object.name});
|
||||
for (object.symtab.items) |sym| {
|
||||
const symname = object.getString(sym.n_strx);
|
||||
log.debug("'{s}': {}", .{ symname, sym });
|
||||
}
|
||||
|
||||
try self.objects.append(self.allocator, object);
|
||||
}
|
||||
|
||||
fn readMagic(allocator: *Allocator, reader: anytype) ![]u8 {
|
||||
var magic = std.ArrayList(u8).init(allocator);
|
||||
try magic.ensureCapacity(SARMAG);
|
||||
var i: usize = 0;
|
||||
while (i < SARMAG) : (i += 1) {
|
||||
const next = try reader.readByte();
|
||||
magic.appendAssumeCapacity(next);
|
||||
}
|
||||
return magic.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
|
||||
const name_or_length = try header.nameOrLength();
|
||||
var name: []u8 = undefined;
|
||||
switch (name_or_length) {
|
||||
.Name => |n| {
|
||||
name = try allocator.dupe(u8, n);
|
||||
},
|
||||
.Length => |len| {
|
||||
var n = try allocator.alloc(u8, len);
|
||||
defer allocator.free(n);
|
||||
try reader.readNoEof(n);
|
||||
const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0));
|
||||
name = try allocator.dupe(u8, n[0..actual_len.?]);
|
||||
},
|
||||
}
|
||||
return name;
|
||||
}
|
228
src/link/MachO/Object.zig
Normal file
228
src/link/MachO/Object.zig
Normal file
@ -0,0 +1,228 @@
|
||||
const Object = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const io = std.io;
|
||||
const log = std.log.scoped(.object);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const parseName = @import("Zld.zig").parseName;
|
||||
|
||||
usingnamespace @import("commands.zig");
|
||||
|
||||
allocator: *Allocator,
|
||||
file: fs.File,
|
||||
name: []u8,
|
||||
ar_name: ?[]u8 = null,
|
||||
|
||||
header: macho.mach_header_64,
|
||||
|
||||
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
|
||||
|
||||
segment_cmd_index: ?u16 = null,
|
||||
symtab_cmd_index: ?u16 = null,
|
||||
dysymtab_cmd_index: ?u16 = null,
|
||||
build_version_cmd_index: ?u16 = null,
|
||||
data_in_code_cmd_index: ?u16 = null,
|
||||
text_section_index: ?u16 = null,
|
||||
|
||||
// __DWARF segment sections
|
||||
dwarf_debug_info_index: ?u16 = null,
|
||||
dwarf_debug_abbrev_index: ?u16 = null,
|
||||
dwarf_debug_str_index: ?u16 = null,
|
||||
dwarf_debug_line_index: ?u16 = null,
|
||||
dwarf_debug_ranges_index: ?u16 = null,
|
||||
|
||||
symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
|
||||
strtab: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
|
||||
|
||||
pub fn deinit(self: *Object) void {
|
||||
for (self.load_commands.items) |*lc| {
|
||||
lc.deinit(self.allocator);
|
||||
}
|
||||
self.load_commands.deinit(self.allocator);
|
||||
self.symtab.deinit(self.allocator);
|
||||
self.strtab.deinit(self.allocator);
|
||||
self.data_in_code_entries.deinit(self.allocator);
|
||||
self.allocator.free(self.name);
|
||||
if (self.ar_name) |v| {
|
||||
self.allocator.free(v);
|
||||
}
|
||||
self.file.close();
|
||||
}
|
||||
|
||||
/// Caller owns the returned Object instance and is responsible for calling
|
||||
/// `deinit` to free allocated memory.
|
||||
pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []const u8, file: fs.File) !Object {
|
||||
var reader = file.reader();
|
||||
const header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
if (header.filetype != macho.MH_OBJECT) {
|
||||
// Reset file cursor.
|
||||
try file.seekTo(0);
|
||||
return error.NotObject;
|
||||
}
|
||||
|
||||
const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {
|
||||
macho.CPU_TYPE_ARM64 => .aarch64,
|
||||
macho.CPU_TYPE_X86_64 => .x86_64,
|
||||
else => |value| {
|
||||
log.err("unsupported cpu architecture 0x{x}", .{value});
|
||||
return error.UnsupportedCpuArchitecture;
|
||||
},
|
||||
};
|
||||
if (this_arch != arch) {
|
||||
log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch });
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
var self = Object{
|
||||
.allocator = allocator,
|
||||
.name = try allocator.dupe(u8, name),
|
||||
.file = file,
|
||||
.header = header,
|
||||
};
|
||||
|
||||
try self.readLoadCommands(reader, .{});
|
||||
|
||||
if (self.symtab_cmd_index != null) {
|
||||
try self.readSymtab();
|
||||
try self.readStrtab();
|
||||
}
|
||||
|
||||
if (self.data_in_code_cmd_index != null) try self.readDataInCode();
|
||||
|
||||
log.debug("\n\n", .{});
|
||||
log.debug("{s} defines symbols", .{self.name});
|
||||
for (self.symtab.items) |sym| {
|
||||
const symname = self.getString(sym.n_strx);
|
||||
log.debug("'{s}': {}", .{ symname, sym });
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub const ReadOffset = struct {
|
||||
offset: ?u32 = null,
|
||||
};
|
||||
|
||||
pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !void {
|
||||
const offset_mod = offset.offset orelse 0;
|
||||
try self.load_commands.ensureCapacity(self.allocator, self.header.ncmds);
|
||||
|
||||
var i: u16 = 0;
|
||||
while (i < self.header.ncmds) : (i += 1) {
|
||||
var cmd = try LoadCommand.read(self.allocator, reader);
|
||||
switch (cmd.cmd()) {
|
||||
macho.LC_SEGMENT_64 => {
|
||||
self.segment_cmd_index = i;
|
||||
var seg = cmd.Segment;
|
||||
for (seg.sections.items) |*sect, j| {
|
||||
const index = @intCast(u16, j);
|
||||
const segname = parseName(§.segname);
|
||||
const sectname = parseName(§.sectname);
|
||||
if (mem.eql(u8, segname, "__DWARF")) {
|
||||
if (mem.eql(u8, sectname, "__debug_info")) {
|
||||
self.dwarf_debug_info_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_abbrev")) {
|
||||
self.dwarf_debug_abbrev_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_str")) {
|
||||
self.dwarf_debug_str_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_line")) {
|
||||
self.dwarf_debug_line_index = index;
|
||||
} else if (mem.eql(u8, sectname, "__debug_ranges")) {
|
||||
self.dwarf_debug_ranges_index = index;
|
||||
}
|
||||
} else if (mem.eql(u8, segname, "__TEXT")) {
|
||||
if (mem.eql(u8, sectname, "__text")) {
|
||||
self.text_section_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
sect.offset += offset_mod;
|
||||
if (sect.reloff > 0)
|
||||
sect.reloff += offset_mod;
|
||||
}
|
||||
|
||||
seg.inner.fileoff += offset_mod;
|
||||
},
|
||||
macho.LC_SYMTAB => {
|
||||
self.symtab_cmd_index = i;
|
||||
cmd.Symtab.symoff += offset_mod;
|
||||
cmd.Symtab.stroff += offset_mod;
|
||||
},
|
||||
macho.LC_DYSYMTAB => {
|
||||
self.dysymtab_cmd_index = i;
|
||||
},
|
||||
macho.LC_BUILD_VERSION => {
|
||||
self.build_version_cmd_index = i;
|
||||
},
|
||||
macho.LC_DATA_IN_CODE => {
|
||||
self.data_in_code_cmd_index = i;
|
||||
},
|
||||
else => {
|
||||
log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
|
||||
},
|
||||
}
|
||||
self.load_commands.appendAssumeCapacity(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readSymtab(self: *Object) !void {
|
||||
const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
var buffer = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
|
||||
defer self.allocator.free(buffer);
|
||||
_ = try self.file.preadAll(buffer, symtab_cmd.symoff);
|
||||
try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms);
|
||||
// TODO this align case should not be needed.
|
||||
// Probably a bug in stage1.
|
||||
const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, buffer));
|
||||
self.symtab.appendSliceAssumeCapacity(slice);
|
||||
}
|
||||
|
||||
pub fn readStrtab(self: *Object) !void {
|
||||
const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
var buffer = try self.allocator.alloc(u8, symtab_cmd.strsize);
|
||||
defer self.allocator.free(buffer);
|
||||
_ = try self.file.preadAll(buffer, symtab_cmd.stroff);
|
||||
try self.strtab.ensureCapacity(self.allocator, symtab_cmd.strsize);
|
||||
self.strtab.appendSliceAssumeCapacity(buffer);
|
||||
}
|
||||
|
||||
pub fn getString(self: *const Object, str_off: u32) []const u8 {
|
||||
assert(str_off < self.strtab.items.len);
|
||||
return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
|
||||
}
|
||||
|
||||
pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
|
||||
const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
|
||||
const sect = seg.sections.items[index];
|
||||
var buffer = try allocator.alloc(u8, sect.size);
|
||||
_ = try self.file.preadAll(buffer, sect.offset);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pub fn readDataInCode(self: *Object) !void {
|
||||
const index = self.data_in_code_cmd_index orelse return;
|
||||
const data_in_code = self.load_commands.items[index].LinkeditData;
|
||||
|
||||
var buffer = try self.allocator.alloc(u8, data_in_code.datasize);
|
||||
defer self.allocator.free(buffer);
|
||||
|
||||
_ = try self.file.preadAll(buffer, data_in_code.dataoff);
|
||||
|
||||
var stream = io.fixedBufferStream(buffer);
|
||||
var reader = stream.reader();
|
||||
while (true) {
|
||||
const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) {
|
||||
error.EndOfStream => break,
|
||||
else => |e| return e,
|
||||
};
|
||||
try self.data_in_code_entries.append(self.allocator, dice);
|
||||
}
|
||||
}
|
3192
src/link/MachO/Zld.zig
Normal file
3192
src/link/MachO/Zld.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -3281,7 +3281,8 @@ pub const ClangArgIterator = struct {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
break :find_clang_arg;
|
||||
},
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
fatal("Unknown Clang option: '{s}'", .{arg});
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
cases.add("test/standalone/main_return_error/error_u8.zig");
|
||||
cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
|
||||
cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
|
||||
cases.addBuildFile("test/standalone/shared_library/build.zig");
|
||||
if (std.Target.current.os.tag != .macos) {
|
||||
// TODO zld cannot link shared libraries yet.
|
||||
cases.addBuildFile("test/standalone/shared_library/build.zig");
|
||||
}
|
||||
cases.addBuildFile("test/standalone/mix_o_files/build.zig");
|
||||
cases.addBuildFile("test/standalone/global_linkage/build.zig");
|
||||
cases.addBuildFile("test/standalone/static_c_lib/build.zig");
|
||||
|
Loading…
Reference in New Issue
Block a user