mirror of
https://github.com/ziglang/zig.git
synced 2026-01-10 09:25:11 +00:00
Merge pull request #17020 from ziglang/macho-versions
macho: big-ish refactor and report errors to the user using Zig's API
This commit is contained in:
commit
f4c9e19bc3
@ -588,7 +588,6 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
|
||||
|
||||
@ -2609,6 +2609,9 @@ pub fn totalErrorCount(self: *Compilation) u32 {
|
||||
}
|
||||
total += @intFromBool(self.link_error_flags.missing_libc);
|
||||
|
||||
// Misc linker errors
|
||||
total += self.bin_file.miscErrors().len;
|
||||
|
||||
// Compile log errors only count if there are no other errors.
|
||||
if (total == 0) {
|
||||
if (self.bin_file.options.module) |module| {
|
||||
@ -2759,6 +2762,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
|
||||
}));
|
||||
}
|
||||
|
||||
for (self.bin_file.miscErrors()) |link_err| {
|
||||
try bundle.addRootErrorMessage(.{
|
||||
.msg = try bundle.addString(link_err.msg),
|
||||
.notes_len = @intCast(link_err.notes.len),
|
||||
});
|
||||
const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
|
||||
for (link_err.notes, 0..) |note, i| {
|
||||
bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
|
||||
.msg = try bundle.addString(note.msg),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (self.bin_file.options.module) |module| {
|
||||
if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) {
|
||||
const keys = module.compile_log_decls.keys();
|
||||
|
||||
@ -670,7 +670,7 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
|
||||
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
// Add relocation to the decl.
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index }).?;
|
||||
const target = macho_file.getGlobalByIndex(relocation.sym_index);
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = .branch,
|
||||
@ -885,9 +885,9 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const Atom = link.File.MachO.Atom;
|
||||
const Relocation = Atom.Relocation;
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index }).?;
|
||||
try Atom.addRelocations(macho_file, atom_index, &[_]Relocation{ .{
|
||||
.target = .{ .sym_index = data.sym_index, .file = null },
|
||||
.target = .{ .sym_index = data.sym_index },
|
||||
.offset = offset,
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
@ -898,7 +898,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
else => unreachable,
|
||||
},
|
||||
}, .{
|
||||
.target = .{ .sym_index = data.sym_index, .file = null },
|
||||
.target = .{ .sym_index = data.sym_index },
|
||||
.offset = offset + 4,
|
||||
.addend = 0,
|
||||
.pcrel = false,
|
||||
|
||||
@ -43,9 +43,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
}),
|
||||
.linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
// Add relocation to the decl.
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(
|
||||
.{ .sym_index = symbol.atom_index, .file = null },
|
||||
).?;
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
|
||||
const target = macho_file.getGlobalByIndex(symbol.sym_index);
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = .branch,
|
||||
@ -77,10 +75,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
.linker_import,
|
||||
.linker_tlv,
|
||||
=> |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{
|
||||
.sym_index = symbol.atom_index,
|
||||
.file = null,
|
||||
}).?;
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
|
||||
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
|
||||
.type = switch (lowered_relocs[0].target) {
|
||||
.linker_got => .got,
|
||||
@ -88,7 +83,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
.linker_tlv => .tlv,
|
||||
else => unreachable,
|
||||
},
|
||||
.target = .{ .sym_index = symbol.sym_index, .file = null },
|
||||
.target = .{ .sym_index = symbol.sym_index },
|
||||
.offset = @as(u32, @intCast(end_offset - 4)),
|
||||
.addend = 0,
|
||||
.pcrel = true,
|
||||
|
||||
39
src/link.zig
39
src/link.zig
@ -228,7 +228,6 @@ pub const Options = struct {
|
||||
|
||||
version: ?std.SemanticVersion,
|
||||
compatibility_version: ?std.SemanticVersion,
|
||||
darwin_sdk_version: ?std.SemanticVersion = null,
|
||||
libc_installation: ?*const LibCInstallation,
|
||||
|
||||
dwarf_format: ?std.dwarf.Format,
|
||||
@ -694,19 +693,15 @@ pub const File = struct {
|
||||
/// TODO audit this error set. most of these should be collapsed into one error,
|
||||
/// and ErrorFlags should be updated to convey the meaning to the user.
|
||||
pub const FlushError = error{
|
||||
BadDwarfCfi,
|
||||
CacheUnavailable,
|
||||
CurrentWorkingDirectoryUnlinked,
|
||||
DivisionByZero,
|
||||
DllImportLibraryNotFound,
|
||||
EmptyStubFile,
|
||||
ExpectedFuncType,
|
||||
FailedToEmit,
|
||||
FailedToResolveRelocationTarget,
|
||||
FileSystem,
|
||||
FilesOpenedWithWrongFlags,
|
||||
FlushFailure,
|
||||
FrameworkNotFound,
|
||||
FunctionSignatureMismatch,
|
||||
GlobalTypeMismatch,
|
||||
HotSwapUnavailableOnHostOperatingSystem,
|
||||
@ -723,27 +718,19 @@ pub const File = struct {
|
||||
LLD_LinkingIsTODO_ForSpirV,
|
||||
LibCInstallationMissingCRTDir,
|
||||
LibCInstallationNotAvailable,
|
||||
LibraryNotFound,
|
||||
LinkingWithoutZigSourceUnimplemented,
|
||||
MalformedArchive,
|
||||
MalformedDwarf,
|
||||
MalformedSection,
|
||||
MemoryTooBig,
|
||||
MemoryTooSmall,
|
||||
MismatchedCpuArchitecture,
|
||||
MissAlignment,
|
||||
MissingEndForBody,
|
||||
MissingEndForExpression,
|
||||
/// TODO: this should be removed from the error set in favor of using ErrorFlags
|
||||
MissingMainEntrypoint,
|
||||
/// TODO: this should be removed from the error set in favor of using ErrorFlags
|
||||
MissingSection,
|
||||
MissingSymbol,
|
||||
MissingTableSymbols,
|
||||
ModuleNameMismatch,
|
||||
MultipleSymbolDefinitions,
|
||||
NoObjectsToLink,
|
||||
NotObject,
|
||||
NotObjectFile,
|
||||
NotSupported,
|
||||
OutOfMemory,
|
||||
@ -755,21 +742,15 @@ pub const File = struct {
|
||||
SymbolMismatchingType,
|
||||
TODOImplementPlan9Objs,
|
||||
TODOImplementWritingLibFiles,
|
||||
TODOImplementWritingStaticLibFiles,
|
||||
UnableToSpawnSelf,
|
||||
UnableToSpawnWasm,
|
||||
UnableToWriteArchive,
|
||||
UndefinedLocal,
|
||||
/// TODO: merge with UndefinedSymbolReference
|
||||
UndefinedSymbol,
|
||||
/// TODO: merge with UndefinedSymbol
|
||||
UndefinedSymbolReference,
|
||||
Underflow,
|
||||
UnexpectedRemainder,
|
||||
UnexpectedTable,
|
||||
UnexpectedValue,
|
||||
UnhandledDwFormValue,
|
||||
UnhandledSymbolType,
|
||||
UnknownFeature,
|
||||
Unseekable,
|
||||
UnsupportedCpuArchitecture,
|
||||
@ -866,6 +847,13 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn miscErrors(base: *File) []const ErrorMsg {
|
||||
switch (base.tag) {
|
||||
.macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items,
|
||||
else => return &.{},
|
||||
}
|
||||
}
|
||||
|
||||
pub const UpdateDeclExportsError = error{
|
||||
OutOfMemory,
|
||||
AnalysisFail,
|
||||
@ -1129,6 +1117,19 @@ pub const File = struct {
|
||||
missing_libc: bool = false,
|
||||
};
|
||||
|
||||
pub const ErrorMsg = struct {
|
||||
msg: []const u8,
|
||||
notes: []ErrorMsg = &.{},
|
||||
|
||||
pub fn deinit(self: *ErrorMsg, gpa: Allocator) void {
|
||||
for (self.notes) |*note| {
|
||||
note.deinit(gpa);
|
||||
}
|
||||
gpa.free(self.notes);
|
||||
gpa.free(self.msg);
|
||||
}
|
||||
};
|
||||
|
||||
pub const LazySymbol = struct {
|
||||
pub const Kind = enum { code, const_data };
|
||||
|
||||
|
||||
3046
src/link/MachO.zig
3046
src/link/MachO.zig
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,3 @@
|
||||
const Archive = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Object = @import("Object.zig");
|
||||
|
||||
file: fs.File,
|
||||
fat_offset: u64,
|
||||
name: []const u8,
|
||||
@ -87,6 +75,13 @@ const ar_hdr = extern struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn isArchive(file: fs.File, fat_offset: u64) bool {
|
||||
const reader = file.reader();
|
||||
const magic = reader.readBytesNoEof(SARMAG) catch return false;
|
||||
defer file.seekTo(fat_offset) catch {};
|
||||
return mem.eql(u8, &magic, ARMAG);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Archive, allocator: Allocator) void {
|
||||
self.file.close();
|
||||
for (self.toc.keys()) |*key| {
|
||||
@ -100,21 +95,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
|
||||
}
|
||||
|
||||
pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
|
||||
const magic = try reader.readBytesNoEof(SARMAG);
|
||||
if (!mem.eql(u8, &magic, ARMAG)) {
|
||||
log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
|
||||
return error.NotArchive;
|
||||
}
|
||||
|
||||
_ = try reader.readBytesNoEof(SARMAG);
|
||||
self.header = try reader.readStruct(ar_hdr);
|
||||
if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
|
||||
log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
|
||||
ARFMAG,
|
||||
self.header.ar_fmag,
|
||||
});
|
||||
return error.NotArchive;
|
||||
}
|
||||
|
||||
const name_or_length = try self.header.nameOrLength();
|
||||
var embedded_name = try parseName(allocator, name_or_length, reader);
|
||||
log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
|
||||
@ -146,7 +128,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
|
||||
defer allocator.free(symtab);
|
||||
|
||||
reader.readNoEof(symtab) catch {
|
||||
log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{symtab_size});
|
||||
log.debug("incomplete symbol table: expected symbol table of length 0x{x}", .{symtab_size});
|
||||
return error.MalformedArchive;
|
||||
};
|
||||
|
||||
@ -155,7 +137,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
|
||||
defer allocator.free(strtab);
|
||||
|
||||
reader.readNoEof(strtab) catch {
|
||||
log.err("incomplete symbol table: expected string table of length 0x{x}", .{strtab_size});
|
||||
log.debug("incomplete symbol table: expected string table of length 0x{x}", .{strtab_size});
|
||||
return error.MalformedArchive;
|
||||
};
|
||||
|
||||
@ -182,22 +164,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseObject(
|
||||
self: Archive,
|
||||
gpa: Allocator,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
offset: u32,
|
||||
) !Object {
|
||||
pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
|
||||
const reader = self.file.reader();
|
||||
try reader.context.seekTo(self.fat_offset + offset);
|
||||
|
||||
const object_header = try reader.readStruct(ar_hdr);
|
||||
|
||||
if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG)) {
|
||||
log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, object_header.ar_fmag });
|
||||
return error.MalformedArchive;
|
||||
}
|
||||
|
||||
const name_or_length = try object_header.nameOrLength();
|
||||
const object_name = try parseName(gpa, name_or_length, reader);
|
||||
defer gpa.free(object_name);
|
||||
@ -227,7 +199,19 @@ pub fn parseObject(
|
||||
.contents = contents,
|
||||
};
|
||||
|
||||
try object.parse(gpa, cpu_arch);
|
||||
try object.parse(gpa);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
const Archive = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Object = @import("Object.zig");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,175 @@
|
||||
const CodeSignature = @This();
|
||||
page_size: u16,
|
||||
code_directory: CodeDirectory,
|
||||
requirements: ?Requirements = null,
|
||||
entitlements: ?Entitlements = null,
|
||||
signature: ?Signature = null,
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
pub fn init(page_size: u16) CodeSignature {
|
||||
return .{
|
||||
.page_size = page_size,
|
||||
.code_directory = CodeDirectory.init(page_size),
|
||||
};
|
||||
}
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Compilation = @import("../../Compilation.zig");
|
||||
const Hasher = @import("hasher.zig").ParallelHasher;
|
||||
const Sha256 = std.crypto.hash.sha2.Sha256;
|
||||
pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
|
||||
self.code_directory.deinit(allocator);
|
||||
if (self.requirements) |*req| {
|
||||
req.deinit(allocator);
|
||||
}
|
||||
if (self.entitlements) |*ents| {
|
||||
ents.deinit(allocator);
|
||||
}
|
||||
if (self.signature) |*sig| {
|
||||
sig.deinit(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
|
||||
const file = try fs.cwd().openFile(path, .{});
|
||||
defer file.close();
|
||||
const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
|
||||
self.entitlements = .{ .inner = inner };
|
||||
}
|
||||
|
||||
pub const WriteOpts = struct {
|
||||
file: fs.File,
|
||||
exec_seg_base: u64,
|
||||
exec_seg_limit: u64,
|
||||
file_size: u32,
|
||||
output_mode: std.builtin.OutputMode,
|
||||
};
|
||||
|
||||
pub fn writeAdhocSignature(
|
||||
self: *CodeSignature,
|
||||
comp: *const Compilation,
|
||||
opts: WriteOpts,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
const gpa = comp.gpa;
|
||||
|
||||
var header: macho.SuperBlob = .{
|
||||
.magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
|
||||
.length = @sizeOf(macho.SuperBlob),
|
||||
.count = 0,
|
||||
};
|
||||
|
||||
var blobs = std.ArrayList(Blob).init(gpa);
|
||||
defer blobs.deinit();
|
||||
|
||||
self.code_directory.inner.execSegBase = opts.exec_seg_base;
|
||||
self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
|
||||
self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
|
||||
self.code_directory.inner.codeLimit = opts.file_size;
|
||||
|
||||
const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
|
||||
|
||||
try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
|
||||
self.code_directory.code_slots.items.len = total_pages;
|
||||
self.code_directory.inner.nCodeSlots = total_pages;
|
||||
|
||||
// Calculate hash for each page (in file) and write it to the buffer
|
||||
var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
|
||||
try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
|
||||
.chunk_size = self.page_size,
|
||||
.max_file_size = opts.file_size,
|
||||
});
|
||||
|
||||
try blobs.append(.{ .code_directory = &self.code_directory });
|
||||
header.length += @sizeOf(macho.BlobIndex);
|
||||
header.count += 1;
|
||||
|
||||
var hash: [hash_size]u8 = undefined;
|
||||
|
||||
if (self.requirements) |*req| {
|
||||
var buf = std.ArrayList(u8).init(gpa);
|
||||
defer buf.deinit();
|
||||
try req.write(buf.writer());
|
||||
Sha256.hash(buf.items, &hash, .{});
|
||||
self.code_directory.addSpecialHash(req.slotType(), hash);
|
||||
|
||||
try blobs.append(.{ .requirements = req });
|
||||
header.count += 1;
|
||||
header.length += @sizeOf(macho.BlobIndex) + req.size();
|
||||
}
|
||||
|
||||
if (self.entitlements) |*ents| {
|
||||
var buf = std.ArrayList(u8).init(gpa);
|
||||
defer buf.deinit();
|
||||
try ents.write(buf.writer());
|
||||
Sha256.hash(buf.items, &hash, .{});
|
||||
self.code_directory.addSpecialHash(ents.slotType(), hash);
|
||||
|
||||
try blobs.append(.{ .entitlements = ents });
|
||||
header.count += 1;
|
||||
header.length += @sizeOf(macho.BlobIndex) + ents.size();
|
||||
}
|
||||
|
||||
if (self.signature) |*sig| {
|
||||
try blobs.append(.{ .signature = sig });
|
||||
header.count += 1;
|
||||
header.length += @sizeOf(macho.BlobIndex) + sig.size();
|
||||
}
|
||||
|
||||
self.code_directory.inner.hashOffset =
|
||||
@sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
|
||||
self.code_directory.inner.length = self.code_directory.size();
|
||||
header.length += self.code_directory.size();
|
||||
|
||||
try writer.writeIntBig(u32, header.magic);
|
||||
try writer.writeIntBig(u32, header.length);
|
||||
try writer.writeIntBig(u32, header.count);
|
||||
|
||||
var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
|
||||
for (blobs.items) |blob| {
|
||||
try writer.writeIntBig(u32, blob.slotType());
|
||||
try writer.writeIntBig(u32, offset);
|
||||
offset += blob.size();
|
||||
}
|
||||
|
||||
for (blobs.items) |blob| {
|
||||
try blob.write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self: CodeSignature) u32 {
|
||||
var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
|
||||
if (self.requirements) |req| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + req.size();
|
||||
}
|
||||
if (self.entitlements) |ent| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + ent.size();
|
||||
}
|
||||
if (self.signature) |sig| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + sig.size();
|
||||
}
|
||||
return ssize;
|
||||
}
|
||||
|
||||
pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
|
||||
var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
|
||||
// Approx code slots
|
||||
const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
|
||||
ssize += total_pages * hash_size;
|
||||
var n_special_slots: u32 = 0;
|
||||
if (self.requirements) |req| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + req.size();
|
||||
n_special_slots = @max(n_special_slots, req.slotType());
|
||||
}
|
||||
if (self.entitlements) |ent| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
|
||||
n_special_slots = @max(n_special_slots, ent.slotType());
|
||||
}
|
||||
if (self.signature) |sig| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + sig.size();
|
||||
}
|
||||
ssize += n_special_slots * hash_size;
|
||||
return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
|
||||
}
|
||||
|
||||
pub fn clear(self: *CodeSignature, allocator: Allocator) void {
|
||||
self.code_directory.deinit(allocator);
|
||||
self.code_directory = CodeDirectory.init(self.page_size);
|
||||
}
|
||||
|
||||
const hash_size = Sha256.digest_length;
|
||||
|
||||
@ -218,175 +376,17 @@ const Signature = struct {
|
||||
}
|
||||
};
|
||||
|
||||
page_size: u16,
|
||||
code_directory: CodeDirectory,
|
||||
requirements: ?Requirements = null,
|
||||
entitlements: ?Entitlements = null,
|
||||
signature: ?Signature = null,
|
||||
const CodeSignature = @This();
|
||||
|
||||
pub fn init(page_size: u16) CodeSignature {
|
||||
return .{
|
||||
.page_size = page_size,
|
||||
.code_directory = CodeDirectory.init(page_size),
|
||||
};
|
||||
}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
|
||||
pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
|
||||
self.code_directory.deinit(allocator);
|
||||
if (self.requirements) |*req| {
|
||||
req.deinit(allocator);
|
||||
}
|
||||
if (self.entitlements) |*ents| {
|
||||
ents.deinit(allocator);
|
||||
}
|
||||
if (self.signature) |*sig| {
|
||||
sig.deinit(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
|
||||
const file = try fs.cwd().openFile(path, .{});
|
||||
defer file.close();
|
||||
const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
|
||||
self.entitlements = .{ .inner = inner };
|
||||
}
|
||||
|
||||
pub const WriteOpts = struct {
|
||||
file: fs.File,
|
||||
exec_seg_base: u64,
|
||||
exec_seg_limit: u64,
|
||||
file_size: u32,
|
||||
output_mode: std.builtin.OutputMode,
|
||||
};
|
||||
|
||||
pub fn writeAdhocSignature(
|
||||
self: *CodeSignature,
|
||||
comp: *const Compilation,
|
||||
opts: WriteOpts,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
const gpa = comp.gpa;
|
||||
|
||||
var header: macho.SuperBlob = .{
|
||||
.magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
|
||||
.length = @sizeOf(macho.SuperBlob),
|
||||
.count = 0,
|
||||
};
|
||||
|
||||
var blobs = std.ArrayList(Blob).init(gpa);
|
||||
defer blobs.deinit();
|
||||
|
||||
self.code_directory.inner.execSegBase = opts.exec_seg_base;
|
||||
self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
|
||||
self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
|
||||
self.code_directory.inner.codeLimit = opts.file_size;
|
||||
|
||||
const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
|
||||
|
||||
try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
|
||||
self.code_directory.code_slots.items.len = total_pages;
|
||||
self.code_directory.inner.nCodeSlots = total_pages;
|
||||
|
||||
// Calculate hash for each page (in file) and write it to the buffer
|
||||
var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
|
||||
try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
|
||||
.chunk_size = self.page_size,
|
||||
.max_file_size = opts.file_size,
|
||||
});
|
||||
|
||||
try blobs.append(.{ .code_directory = &self.code_directory });
|
||||
header.length += @sizeOf(macho.BlobIndex);
|
||||
header.count += 1;
|
||||
|
||||
var hash: [hash_size]u8 = undefined;
|
||||
|
||||
if (self.requirements) |*req| {
|
||||
var buf = std.ArrayList(u8).init(gpa);
|
||||
defer buf.deinit();
|
||||
try req.write(buf.writer());
|
||||
Sha256.hash(buf.items, &hash, .{});
|
||||
self.code_directory.addSpecialHash(req.slotType(), hash);
|
||||
|
||||
try blobs.append(.{ .requirements = req });
|
||||
header.count += 1;
|
||||
header.length += @sizeOf(macho.BlobIndex) + req.size();
|
||||
}
|
||||
|
||||
if (self.entitlements) |*ents| {
|
||||
var buf = std.ArrayList(u8).init(gpa);
|
||||
defer buf.deinit();
|
||||
try ents.write(buf.writer());
|
||||
Sha256.hash(buf.items, &hash, .{});
|
||||
self.code_directory.addSpecialHash(ents.slotType(), hash);
|
||||
|
||||
try blobs.append(.{ .entitlements = ents });
|
||||
header.count += 1;
|
||||
header.length += @sizeOf(macho.BlobIndex) + ents.size();
|
||||
}
|
||||
|
||||
if (self.signature) |*sig| {
|
||||
try blobs.append(.{ .signature = sig });
|
||||
header.count += 1;
|
||||
header.length += @sizeOf(macho.BlobIndex) + sig.size();
|
||||
}
|
||||
|
||||
self.code_directory.inner.hashOffset =
|
||||
@sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
|
||||
self.code_directory.inner.length = self.code_directory.size();
|
||||
header.length += self.code_directory.size();
|
||||
|
||||
try writer.writeIntBig(u32, header.magic);
|
||||
try writer.writeIntBig(u32, header.length);
|
||||
try writer.writeIntBig(u32, header.count);
|
||||
|
||||
var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
|
||||
for (blobs.items) |blob| {
|
||||
try writer.writeIntBig(u32, blob.slotType());
|
||||
try writer.writeIntBig(u32, offset);
|
||||
offset += blob.size();
|
||||
}
|
||||
|
||||
for (blobs.items) |blob| {
|
||||
try blob.write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self: CodeSignature) u32 {
|
||||
var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
|
||||
if (self.requirements) |req| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + req.size();
|
||||
}
|
||||
if (self.entitlements) |ent| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + ent.size();
|
||||
}
|
||||
if (self.signature) |sig| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + sig.size();
|
||||
}
|
||||
return ssize;
|
||||
}
|
||||
|
||||
pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
|
||||
var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
|
||||
// Approx code slots
|
||||
const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
|
||||
ssize += total_pages * hash_size;
|
||||
var n_special_slots: u32 = 0;
|
||||
if (self.requirements) |req| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + req.size();
|
||||
n_special_slots = @max(n_special_slots, req.slotType());
|
||||
}
|
||||
if (self.entitlements) |ent| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
|
||||
n_special_slots = @max(n_special_slots, ent.slotType());
|
||||
}
|
||||
if (self.signature) |sig| {
|
||||
ssize += @sizeOf(macho.BlobIndex) + sig.size();
|
||||
}
|
||||
ssize += n_special_slots * hash_size;
|
||||
return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
|
||||
}
|
||||
|
||||
pub fn clear(self: *CodeSignature, allocator: Allocator) void {
|
||||
self.code_directory.deinit(allocator);
|
||||
self.code_directory = CodeDirectory.init(self.page_size);
|
||||
}
|
||||
const Allocator = mem.Allocator;
|
||||
const Compilation = @import("../../Compilation.zig");
|
||||
const Hasher = @import("hasher.zig").ParallelHasher;
|
||||
const Sha256 = std.crypto.hash.sha2.Sha256;
|
||||
|
||||
@ -1,30 +1,6 @@
|
||||
const DebugSymbols = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const link = @import("../../link.zig");
|
||||
const load_commands = @import("load_commands.zig");
|
||||
const log = std.log.scoped(.dsym);
|
||||
const macho = std.macho;
|
||||
const makeStaticString = MachO.makeStaticString;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const padToIdeal = MachO.padToIdeal;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Dwarf = @import("../Dwarf.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Module = @import("../../Module.zig");
|
||||
const StringTable = @import("../strtab.zig").StringTable;
|
||||
const Type = @import("../../type.zig").Type;
|
||||
|
||||
allocator: Allocator,
|
||||
dwarf: Dwarf,
|
||||
file: fs.File,
|
||||
page_size: u16,
|
||||
|
||||
symtab_cmd: macho.symtab_command = .{},
|
||||
|
||||
@ -62,13 +38,14 @@ pub const Reloc = struct {
|
||||
|
||||
/// You must call this function *after* `MachO.populateMissingMetadata()`
|
||||
/// has been called to get a viable debug symbols output.
|
||||
pub fn populateMissingMetadata(self: *DebugSymbols) !void {
|
||||
pub fn populateMissingMetadata(self: *DebugSymbols, macho_file: *MachO) !void {
|
||||
if (self.dwarf_segment_cmd_index == null) {
|
||||
self.dwarf_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
|
||||
|
||||
const off = @as(u64, @intCast(self.page_size));
|
||||
const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
|
||||
const off = @as(u64, @intCast(page_size));
|
||||
const ideal_size: u16 = 200 + 128 + 160 + 250;
|
||||
const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), self.page_size);
|
||||
const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), page_size);
|
||||
|
||||
log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
|
||||
|
||||
@ -355,7 +332,8 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
|
||||
file_size = @max(file_size, header.offset + header.size);
|
||||
}
|
||||
|
||||
const aligned_size = mem.alignForward(u64, file_size, self.page_size);
|
||||
const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
|
||||
const aligned_size = mem.alignForward(u64, file_size, page_size);
|
||||
dwarf_segment.vmaddr = base_vmaddr;
|
||||
dwarf_segment.filesize = aligned_size;
|
||||
dwarf_segment.vmsize = aligned_size;
|
||||
@ -364,12 +342,12 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
|
||||
linkedit.vmaddr = mem.alignForward(
|
||||
u64,
|
||||
dwarf_segment.vmaddr + aligned_size,
|
||||
self.page_size,
|
||||
page_size,
|
||||
);
|
||||
linkedit.fileoff = mem.alignForward(
|
||||
u64,
|
||||
dwarf_segment.fileoff + aligned_size,
|
||||
self.page_size,
|
||||
page_size,
|
||||
);
|
||||
log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});
|
||||
}
|
||||
@ -457,8 +435,9 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void {
|
||||
try self.writeSymtab(macho_file);
|
||||
try self.writeStrtab();
|
||||
|
||||
const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
|
||||
const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
|
||||
const aligned_size = mem.alignForward(u64, seg.filesize, self.page_size);
|
||||
const aligned_size = mem.alignForward(u64, seg.filesize, page_size);
|
||||
seg.vmsize = aligned_size;
|
||||
}
|
||||
|
||||
@ -473,7 +452,7 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void {
|
||||
|
||||
for (macho_file.locals.items, 0..) |sym, sym_id| {
|
||||
if (sym.n_strx == 0) continue; // no name, skip
|
||||
const sym_loc = MachO.SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)), .file = null };
|
||||
const sym_loc = MachO.SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)) };
|
||||
if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
|
||||
if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
|
||||
var out_sym = sym;
|
||||
@ -567,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {
|
||||
assert(sect < self.sections.items.len);
|
||||
return self.sections.items[sect];
|
||||
}
|
||||
|
||||
const DebugSymbols = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const link = @import("../../link.zig");
|
||||
const load_commands = @import("load_commands.zig");
|
||||
const log = std.log.scoped(.dsym);
|
||||
const macho = std.macho;
|
||||
const makeStaticString = MachO.makeStaticString;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const padToIdeal = MachO.padToIdeal;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Dwarf = @import("../Dwarf.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Module = @import("../../Module.zig");
|
||||
const StringTable = @import("../strtab.zig").StringTable;
|
||||
const Type = @import("../../type.zig").Type;
|
||||
|
||||
@ -1,17 +1,3 @@
|
||||
const DwarfInfo = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const dwarf = std.dwarf;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.macho);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
|
||||
pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
|
||||
|
||||
debug_info: []const u8,
|
||||
debug_abbrev: []const u8,
|
||||
debug_str: []const u8,
|
||||
@ -458,7 +444,8 @@ fn findFormSize(self: DwarfInfo, form: u64, di_off: usize, cuh: CompileUnit.Head
|
||||
},
|
||||
|
||||
else => {
|
||||
log.err("unhandled DW_FORM_* value with identifier {x}", .{form});
|
||||
// TODO figure out how to handle this
|
||||
log.debug("unhandled DW_FORM_* value with identifier {x}", .{form});
|
||||
return error.UnhandledDwFormValue;
|
||||
},
|
||||
}
|
||||
@ -501,3 +488,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {
|
||||
assert(off < self.debug_str.len);
|
||||
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);
|
||||
}
|
||||
|
||||
const DwarfInfo = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const dwarf = std.dwarf;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.macho);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
|
||||
pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
|
||||
|
||||
@ -1,25 +1,8 @@
|
||||
const Dylib = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const fmt = std.fmt;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const fat = @import("fat.zig");
|
||||
const tapi = @import("../tapi.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
const LibStub = tapi.LibStub;
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Tbd = tapi.Tbd;
|
||||
|
||||
path: []const u8,
|
||||
id: ?Id = null,
|
||||
weak: bool = false,
|
||||
/// Header is only set if Dylib is parsed directly from a binary and not a stub file.
|
||||
header: ?macho.mach_header_64 = null,
|
||||
|
||||
/// Parsed symbol table represented as hash map of symbols'
|
||||
/// names. We can and should defer creating *Symbols until
|
||||
@ -116,7 +99,15 @@ pub const Id = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn isDylib(file: std.fs.File, fat_offset: u64) bool {
|
||||
const reader = file.reader();
|
||||
const hdr = reader.readStruct(macho.mach_header_64) catch return false;
|
||||
defer file.seekTo(fat_offset) catch {};
|
||||
return hdr.filetype == macho.MH_DYLIB;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Dylib, allocator: Allocator) void {
|
||||
allocator.free(self.path);
|
||||
for (self.symbols.keys()) |key| {
|
||||
allocator.free(key);
|
||||
}
|
||||
@ -129,7 +120,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
|
||||
pub fn parseFromBinary(
|
||||
self: *Dylib,
|
||||
allocator: Allocator,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
dylib_id: u16,
|
||||
dependent_libs: anytype,
|
||||
name: []const u8,
|
||||
@ -140,27 +130,12 @@ pub fn parseFromBinary(
|
||||
|
||||
log.debug("parsing shared library '{s}'", .{name});
|
||||
|
||||
const header = try reader.readStruct(macho.mach_header_64);
|
||||
self.header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
if (header.filetype != macho.MH_DYLIB) {
|
||||
log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
|
||||
return error.NotDylib;
|
||||
}
|
||||
|
||||
const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
|
||||
|
||||
if (this_arch != cpu_arch) {
|
||||
log.err("mismatched cpu architecture: expected {s}, found {s}", .{
|
||||
@tagName(cpu_arch),
|
||||
@tagName(this_arch),
|
||||
});
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
|
||||
const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = header.ncmds,
|
||||
.buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
|
||||
.ncmds = self.header.?.ncmds,
|
||||
.buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
@ -205,6 +180,26 @@ pub fn parseFromBinary(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Platform composed from the first encountered build version type load command:
|
||||
/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
|
||||
pub fn getPlatform(self: Dylib, data: []align(@alignOf(u64)) const u8) ?Platform {
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.?.ncmds,
|
||||
.buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.BUILD_VERSION,
|
||||
.VERSION_MIN_MACOSX,
|
||||
.VERSION_MIN_IPHONEOS,
|
||||
.VERSION_MIN_TVOS,
|
||||
.VERSION_MIN_WATCHOS,
|
||||
=> return Platform.fromLoadCommand(cmd),
|
||||
else => {},
|
||||
}
|
||||
} else return null;
|
||||
}
|
||||
|
||||
fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void {
|
||||
const expanded = &[_][]const u8{
|
||||
try std.fmt.allocPrint(allocator, "_OBJC_CLASS_$_{s}", .{sym_name}),
|
||||
@ -239,41 +234,41 @@ fn addWeakSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void
|
||||
try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true);
|
||||
}
|
||||
|
||||
const TargetMatcher = struct {
|
||||
pub const TargetMatcher = struct {
|
||||
allocator: Allocator,
|
||||
target: CrossTarget,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
os_tag: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
target_strings: std.ArrayListUnmanaged([]const u8) = .{},
|
||||
|
||||
fn init(allocator: Allocator, target: CrossTarget) !TargetMatcher {
|
||||
pub fn init(allocator: Allocator, target: std.Target) !TargetMatcher {
|
||||
var self = TargetMatcher{
|
||||
.allocator = allocator,
|
||||
.target = target,
|
||||
.cpu_arch = target.cpu.arch,
|
||||
.os_tag = target.os.tag,
|
||||
.abi = target.abi,
|
||||
};
|
||||
const apple_string = try targetToAppleString(allocator, target);
|
||||
const apple_string = try toAppleTargetTriple(allocator, self.cpu_arch, self.os_tag, self.abi);
|
||||
try self.target_strings.append(allocator, apple_string);
|
||||
|
||||
const abi = target.abi orelse .none;
|
||||
if (abi == .simulator) {
|
||||
if (self.abi == .simulator) {
|
||||
// For Apple simulator targets, linking gets tricky as we need to link against the simulator
|
||||
// hosts dylibs too.
|
||||
const host_target = try targetToAppleString(allocator, .{
|
||||
.cpu_arch = target.cpu_arch.?,
|
||||
.os_tag = .macos,
|
||||
});
|
||||
const host_target = try toAppleTargetTriple(allocator, self.cpu_arch, .macos, .none);
|
||||
try self.target_strings.append(allocator, host_target);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn deinit(self: *TargetMatcher) void {
|
||||
pub fn deinit(self: *TargetMatcher) void {
|
||||
for (self.target_strings.items) |t| {
|
||||
self.allocator.free(t);
|
||||
}
|
||||
self.target_strings.deinit(self.allocator);
|
||||
}
|
||||
|
||||
inline fn cpuArchToAppleString(cpu_arch: std.Target.Cpu.Arch) []const u8 {
|
||||
inline fn fmtCpuArch(cpu_arch: std.Target.Cpu.Arch) []const u8 {
|
||||
return switch (cpu_arch) {
|
||||
.aarch64 => "arm64",
|
||||
.x86_64 => "x86_64",
|
||||
@ -281,7 +276,7 @@ const TargetMatcher = struct {
|
||||
};
|
||||
}
|
||||
|
||||
inline fn abiToAppleString(abi: std.Target.Abi) ?[]const u8 {
|
||||
inline fn fmtAbi(abi: std.Target.Abi) ?[]const u8 {
|
||||
return switch (abi) {
|
||||
.none => null,
|
||||
.simulator => "simulator",
|
||||
@ -290,14 +285,18 @@ const TargetMatcher = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn targetToAppleString(allocator: Allocator, target: CrossTarget) ![]const u8 {
|
||||
const cpu_arch = cpuArchToAppleString(target.cpu_arch.?);
|
||||
const os_tag = @tagName(target.os_tag.?);
|
||||
const target_abi = abiToAppleString(target.abi orelse .none);
|
||||
if (target_abi) |abi| {
|
||||
return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch, os_tag, abi });
|
||||
pub fn toAppleTargetTriple(
|
||||
allocator: Allocator,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
os_tag: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
) ![]const u8 {
|
||||
const cpu_arch_s = fmtCpuArch(cpu_arch);
|
||||
const os_tag_s = @tagName(os_tag);
|
||||
if (fmtAbi(abi)) |abi_s| {
|
||||
return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ cpu_arch_s, os_tag_s, abi_s });
|
||||
}
|
||||
return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch, os_tag });
|
||||
return std.fmt.allocPrint(allocator, "{s}-{s}", .{ cpu_arch_s, os_tag_s });
|
||||
}
|
||||
|
||||
fn hasValue(stack: []const []const u8, needle: []const u8) bool {
|
||||
@ -307,7 +306,7 @@ const TargetMatcher = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
|
||||
pub fn matchesTarget(self: TargetMatcher, targets: []const []const u8) bool {
|
||||
for (self.target_strings.items) |t| {
|
||||
if (hasValue(targets, t)) return true;
|
||||
}
|
||||
@ -315,26 +314,7 @@ const TargetMatcher = struct {
|
||||
}
|
||||
|
||||
fn matchesArch(self: TargetMatcher, archs: []const []const u8) bool {
|
||||
return hasValue(archs, cpuArchToAppleString(self.target.cpu_arch.?));
|
||||
}
|
||||
|
||||
fn matchesTargetTbd(self: TargetMatcher, tbd: Tbd) !bool {
|
||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const targets = switch (tbd) {
|
||||
.v3 => |v3| blk: {
|
||||
var targets = std.ArrayList([]const u8).init(arena.allocator());
|
||||
for (v3.archs) |arch| {
|
||||
const target = try std.fmt.allocPrint(arena.allocator(), "{s}-{s}", .{ arch, v3.platform });
|
||||
try targets.append(target);
|
||||
}
|
||||
break :blk targets.items;
|
||||
},
|
||||
.v4 => |v4| v4.targets,
|
||||
};
|
||||
|
||||
return self.matchesTarget(targets);
|
||||
return hasValue(archs, fmtCpuArch(self.cpu_arch));
|
||||
}
|
||||
};
|
||||
|
||||
@ -347,7 +327,7 @@ pub fn parseFromStub(
|
||||
dependent_libs: anytype,
|
||||
name: []const u8,
|
||||
) !void {
|
||||
if (lib_stub.inner.len == 0) return error.EmptyStubFile;
|
||||
if (lib_stub.inner.len == 0) return error.NotLibStub;
|
||||
|
||||
log.debug("parsing shared library from stub '{s}'", .{name});
|
||||
|
||||
@ -369,15 +349,16 @@ pub fn parseFromStub(
|
||||
|
||||
log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
|
||||
|
||||
var matcher = try TargetMatcher.init(allocator, .{
|
||||
.cpu_arch = target.cpu.arch,
|
||||
.os_tag = target.os.tag,
|
||||
.abi = target.abi,
|
||||
});
|
||||
var matcher = try TargetMatcher.init(allocator, target);
|
||||
defer matcher.deinit();
|
||||
|
||||
for (lib_stub.inner, 0..) |elem, stub_index| {
|
||||
if (!(try matcher.matchesTargetTbd(elem))) continue;
|
||||
const targets = try elem.targets(allocator);
|
||||
defer {
|
||||
for (targets) |t| allocator.free(t);
|
||||
allocator.free(targets);
|
||||
}
|
||||
if (!matcher.matchesTarget(targets)) continue;
|
||||
|
||||
if (stub_index > 0) {
|
||||
// TODO I thought that we could switch on presence of `parent-umbrella` map;
|
||||
@ -553,3 +534,23 @@ pub fn parseFromStub(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Dylib = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const fmt = std.fmt;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const fat = @import("fat.zig");
|
||||
const tapi = @import("../tapi.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const LibStub = tapi.LibStub;
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Platform = @import("load_commands.zig").Platform;
|
||||
const Tbd = tapi.Tbd;
|
||||
|
||||
@ -2,31 +2,6 @@
|
||||
//! Each Object is fully loaded into memory for easier
|
||||
//! access into different data within.
|
||||
|
||||
const Object = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const assert = std.debug.assert;
|
||||
const dwarf = std.dwarf;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const fs = std.fs;
|
||||
const io = std.io;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const sort = std.sort;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const DwarfInfo = @import("DwarfInfo.zig");
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
|
||||
name: []const u8,
|
||||
mtime: u64,
|
||||
contents: []align(@alignOf(u64)) const u8,
|
||||
@ -54,7 +29,7 @@ source_section_index_lookup: []Entry = undefined,
|
||||
/// Can be undefined as set together with in_symtab.
|
||||
strtab_lookup: []u32 = undefined,
|
||||
/// Can be undefined as set together with in_symtab.
|
||||
atom_by_index_table: []AtomIndex = undefined,
|
||||
atom_by_index_table: []?Atom.Index = undefined,
|
||||
/// Can be undefined as set together with in_symtab.
|
||||
globals_lookup: []i64 = undefined,
|
||||
/// Can be undefined as set together with in_symtab.
|
||||
@ -70,8 +45,8 @@ section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},
|
||||
/// Data-in-code records sorted by address.
|
||||
data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
|
||||
|
||||
atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
|
||||
exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
|
||||
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
exec_atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
|
||||
eh_frame_sect_id: ?u8 = null,
|
||||
eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
|
||||
@ -91,6 +66,13 @@ const Record = struct {
|
||||
reloc: Entry,
|
||||
};
|
||||
|
||||
pub fn isObject(file: std.fs.File) bool {
|
||||
const reader = file.reader();
|
||||
const hdr = reader.readStruct(macho.mach_header_64) catch return false;
|
||||
defer file.seekTo(0) catch {};
|
||||
return hdr.filetype == macho.MH_OBJECT;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Object, gpa: Allocator) void {
|
||||
self.atoms.deinit(gpa);
|
||||
self.exec_atoms.deinit(gpa);
|
||||
@ -118,36 +100,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
|
||||
self.data_in_code.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
|
||||
pub fn parse(self: *Object, allocator: Allocator) !void {
|
||||
var stream = std.io.fixedBufferStream(self.contents);
|
||||
const reader = stream.reader();
|
||||
|
||||
self.header = try reader.readStruct(macho.mach_header_64);
|
||||
|
||||
if (self.header.filetype != macho.MH_OBJECT) {
|
||||
log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
|
||||
macho.MH_OBJECT,
|
||||
self.header.filetype,
|
||||
});
|
||||
return error.NotObject;
|
||||
}
|
||||
|
||||
const this_arch: std.Target.Cpu.Arch = switch (self.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 != cpu_arch) {
|
||||
log.err("mismatched cpu architecture: expected {s}, found {s}", .{
|
||||
@tagName(cpu_arch),
|
||||
@tagName(this_arch),
|
||||
});
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
|
||||
@ -172,7 +130,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
|
||||
self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
|
||||
self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
|
||||
self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
|
||||
self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
|
||||
self.atom_by_index_table = try allocator.alloc(?Atom.Index, self.in_symtab.?.len + nsects);
|
||||
self.relocs_lookup = try allocator.alloc(Entry, self.in_symtab.?.len + nsects);
|
||||
// This is wasteful but we need to be able to lookup source symbol address after stripping and
|
||||
// allocating of sections.
|
||||
@ -190,7 +148,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
|
||||
}
|
||||
|
||||
@memset(self.globals_lookup, -1);
|
||||
@memset(self.atom_by_index_table, 0);
|
||||
@memset(self.atom_by_index_table, null);
|
||||
@memset(self.source_section_index_lookup, .{});
|
||||
@memset(self.relocs_lookup, .{});
|
||||
|
||||
@ -331,10 +289,10 @@ fn filterSymbolsBySection(symbols: []macho.nlist_64, n_sect: u8) struct {
|
||||
}
|
||||
};
|
||||
|
||||
const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, FirstMatch{
|
||||
const index = MachO.lsearch(macho.nlist_64, symbols, FirstMatch{
|
||||
.n_sect = n_sect,
|
||||
});
|
||||
const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
|
||||
const len = MachO.lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
|
||||
.n_sect = n_sect,
|
||||
});
|
||||
|
||||
@ -353,10 +311,10 @@ fn filterSymbolsByAddress(symbols: []macho.nlist_64, start_addr: u64, end_addr:
|
||||
}
|
||||
};
|
||||
|
||||
const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, Predicate{
|
||||
const index = MachO.lsearch(macho.nlist_64, symbols, Predicate{
|
||||
.addr = start_addr,
|
||||
});
|
||||
const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], Predicate{
|
||||
const len = MachO.lsearch(macho.nlist_64, symbols[index..], Predicate{
|
||||
.addr = end_addr,
|
||||
});
|
||||
|
||||
@ -376,25 +334,32 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
|
||||
return lhs.header.addr < rhs.header.addr;
|
||||
}
|
||||
|
||||
pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
pub const SplitIntoAtomsError = error{
|
||||
OutOfMemory,
|
||||
EndOfStream,
|
||||
MissingEhFrameSection,
|
||||
BadDwarfCfi,
|
||||
};
|
||||
|
||||
pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) SplitIntoAtomsError!void {
|
||||
log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
|
||||
|
||||
try self.splitRegularSections(zld, object_id);
|
||||
try self.parseEhFrameSection(zld, object_id);
|
||||
try self.parseUnwindInfo(zld, object_id);
|
||||
try self.parseDataInCode(zld.gpa);
|
||||
try self.splitRegularSections(macho_file, object_id);
|
||||
try self.parseEhFrameSection(macho_file, object_id);
|
||||
try self.parseUnwindInfo(macho_file, object_id);
|
||||
try self.parseDataInCode(macho_file.base.allocator);
|
||||
}
|
||||
|
||||
/// Splits input regular sections into Atoms.
|
||||
/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
|
||||
/// into subsections where each subsection then represents an Atom.
|
||||
pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
const gpa = zld.gpa;
|
||||
pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void {
|
||||
const gpa = macho_file.base.allocator;
|
||||
|
||||
const sections = self.getSourceSections();
|
||||
for (sections, 0..) |sect, id| {
|
||||
if (sect.isDebug()) continue;
|
||||
const out_sect_id = (try zld.getOutputSection(sect)) orelse {
|
||||
const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse {
|
||||
log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
|
||||
continue;
|
||||
};
|
||||
@ -414,13 +379,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
if (self.in_symtab == null) {
|
||||
for (sections, 0..) |sect, id| {
|
||||
if (sect.isDebug()) continue;
|
||||
const out_sect_id = (try zld.getOutputSection(sect)) orelse continue;
|
||||
const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
|
||||
if (sect.size == 0) continue;
|
||||
|
||||
const sect_id = @as(u8, @intCast(id));
|
||||
const sym_index = self.getSectionAliasSymbolIndex(sect_id);
|
||||
const atom_index = try self.createAtomFromSubsection(
|
||||
zld,
|
||||
macho_file,
|
||||
object_id,
|
||||
sym_index,
|
||||
sym_index,
|
||||
@ -429,7 +394,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
sect.@"align",
|
||||
out_sect_id,
|
||||
);
|
||||
zld.addAtomToSection(atom_index);
|
||||
macho_file.addAtomToSection(atom_index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -437,7 +402,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
// Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
|
||||
// have to infer the start of undef section in the symtab ourselves.
|
||||
const iundefsym = blk: {
|
||||
const dysymtab = self.parseDysymtab() orelse {
|
||||
const dysymtab = self.getDysymtab() orelse {
|
||||
var iundefsym: usize = self.in_symtab.?.len;
|
||||
while (iundefsym > 0) : (iundefsym -= 1) {
|
||||
const sym = self.symtab[iundefsym - 1];
|
||||
@ -473,17 +438,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
|
||||
|
||||
// Get output segment/section in the final artifact.
|
||||
const out_sect_id = (try zld.getOutputSection(sect)) orelse continue;
|
||||
const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
|
||||
|
||||
log.debug(" output sect({d}, '{s},{s}')", .{
|
||||
out_sect_id + 1,
|
||||
zld.sections.items(.header)[out_sect_id].segName(),
|
||||
zld.sections.items(.header)[out_sect_id].sectName(),
|
||||
macho_file.sections.items(.header)[out_sect_id].segName(),
|
||||
macho_file.sections.items(.header)[out_sect_id].sectName(),
|
||||
});
|
||||
|
||||
try self.parseRelocs(gpa, section.id);
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
|
||||
const sect_start_index = sect_sym_index + sect_loc.index;
|
||||
|
||||
@ -499,7 +464,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
const sym_index = self.getSectionAliasSymbolIndex(sect_id);
|
||||
const atom_size = first_sym.n_value - sect.addr;
|
||||
const atom_index = try self.createAtomFromSubsection(
|
||||
zld,
|
||||
macho_file,
|
||||
object_id,
|
||||
sym_index,
|
||||
sym_index,
|
||||
@ -509,9 +474,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
out_sect_id,
|
||||
);
|
||||
if (!sect.isZerofill()) {
|
||||
try self.cacheRelocs(zld, atom_index);
|
||||
try self.cacheRelocs(macho_file, atom_index);
|
||||
}
|
||||
zld.addAtomToSection(atom_index);
|
||||
macho_file.addAtomToSection(atom_index);
|
||||
}
|
||||
|
||||
var next_sym_index = sect_start_index;
|
||||
@ -535,7 +500,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
sect.@"align";
|
||||
|
||||
const atom_index = try self.createAtomFromSubsection(
|
||||
zld,
|
||||
macho_file,
|
||||
object_id,
|
||||
atom_sym_index,
|
||||
atom_sym_index,
|
||||
@ -554,14 +519,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
self.atom_by_index_table[alias_index] = atom_index;
|
||||
}
|
||||
if (!sect.isZerofill()) {
|
||||
try self.cacheRelocs(zld, atom_index);
|
||||
try self.cacheRelocs(macho_file, atom_index);
|
||||
}
|
||||
zld.addAtomToSection(atom_index);
|
||||
macho_file.addAtomToSection(atom_index);
|
||||
}
|
||||
} else {
|
||||
const alias_index = self.getSectionAliasSymbolIndex(sect_id);
|
||||
const atom_index = try self.createAtomFromSubsection(
|
||||
zld,
|
||||
macho_file,
|
||||
object_id,
|
||||
alias_index,
|
||||
sect_start_index,
|
||||
@ -571,16 +536,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
out_sect_id,
|
||||
);
|
||||
if (!sect.isZerofill()) {
|
||||
try self.cacheRelocs(zld, atom_index);
|
||||
try self.cacheRelocs(macho_file, atom_index);
|
||||
}
|
||||
zld.addAtomToSection(atom_index);
|
||||
macho_file.addAtomToSection(atom_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn createAtomFromSubsection(
|
||||
self: *Object,
|
||||
zld: *Zld,
|
||||
macho_file: *MachO,
|
||||
object_id: u32,
|
||||
sym_index: u32,
|
||||
inner_sym_index: u32,
|
||||
@ -588,10 +553,10 @@ fn createAtomFromSubsection(
|
||||
size: u64,
|
||||
alignment: u32,
|
||||
out_sect_id: u8,
|
||||
) !AtomIndex {
|
||||
const gpa = zld.gpa;
|
||||
const atom_index = try zld.createEmptyAtom(sym_index, size, alignment);
|
||||
const atom = zld.getAtomPtr(atom_index);
|
||||
) !Atom.Index {
|
||||
const gpa = macho_file.base.allocator;
|
||||
const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment });
|
||||
const atom = macho_file.getAtomPtr(atom_index);
|
||||
atom.inner_sym_index = inner_sym_index;
|
||||
atom.inner_nsyms_trailing = inner_nsyms_trailing;
|
||||
atom.file = object_id + 1;
|
||||
@ -601,22 +566,22 @@ fn createAtomFromSubsection(
|
||||
sym_index,
|
||||
self.getSymbolName(sym_index),
|
||||
out_sect_id + 1,
|
||||
zld.sections.items(.header)[out_sect_id].segName(),
|
||||
zld.sections.items(.header)[out_sect_id].sectName(),
|
||||
macho_file.sections.items(.header)[out_sect_id].segName(),
|
||||
macho_file.sections.items(.header)[out_sect_id].sectName(),
|
||||
object_id,
|
||||
});
|
||||
|
||||
try self.atoms.append(gpa, atom_index);
|
||||
self.atom_by_index_table[sym_index] = atom_index;
|
||||
|
||||
var it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
while (it.next()) |sym_loc| {
|
||||
const inner = zld.getSymbolPtr(sym_loc);
|
||||
const inner = macho_file.getSymbolPtr(sym_loc);
|
||||
inner.n_sect = out_sect_id + 1;
|
||||
self.atom_by_index_table[sym_loc.sym_index] = atom_index;
|
||||
}
|
||||
|
||||
const out_sect = zld.sections.items(.header)[out_sect_id];
|
||||
const out_sect = macho_file.sections.items(.header)[out_sect_id];
|
||||
if (out_sect.isCode() and
|
||||
mem.eql(u8, "__TEXT", out_sect.segName()) and
|
||||
mem.eql(u8, "__text", out_sect.sectName()))
|
||||
@ -648,8 +613,8 @@ fn filterRelocs(
|
||||
}
|
||||
};
|
||||
|
||||
const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
|
||||
const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
|
||||
const start = MachO.bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
|
||||
const len = MachO.lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
|
||||
|
||||
return .{ .start = @as(u32, @intCast(start)), .len = @as(u32, @intCast(len)) };
|
||||
}
|
||||
@ -668,8 +633,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
|
||||
self.section_relocs_lookup.items[sect_id] = start;
|
||||
}
|
||||
|
||||
fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void {
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
|
||||
const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
|
||||
break :blk source_sym.n_sect - 1;
|
||||
@ -696,18 +661,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation
|
||||
return lhs.r_address > rhs.r_address;
|
||||
}
|
||||
|
||||
fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void {
|
||||
const sect_id = self.eh_frame_sect_id orelse return;
|
||||
const sect = self.getSourceSection(sect_id);
|
||||
|
||||
log.debug("parsing __TEXT,__eh_frame section", .{});
|
||||
|
||||
if (zld.getSectionByName("__TEXT", "__eh_frame") == null) {
|
||||
_ = try zld.initSection("__TEXT", "__eh_frame", .{});
|
||||
const gpa = macho_file.base.allocator;
|
||||
|
||||
if (macho_file.eh_frame_section_index == null) {
|
||||
macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{});
|
||||
}
|
||||
|
||||
const gpa = zld.gpa;
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
try self.parseRelocs(gpa, sect_id);
|
||||
const relocs = self.getRelocs(sect_id);
|
||||
|
||||
@ -745,7 +711,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)
|
||||
break rel;
|
||||
} else unreachable;
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = it.data[offset..],
|
||||
@ -760,7 +726,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
});
|
||||
const target_sym_index = self.getSymbolByAddress(target_address, null);
|
||||
const target = if (self.getGlobal(target_sym_index)) |global_index|
|
||||
zld.globals.items[global_index]
|
||||
macho_file.globals.items[global_index]
|
||||
else
|
||||
SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
|
||||
break :blk target;
|
||||
@ -786,7 +752,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
};
|
||||
log.debug("FDE at offset {x} tracks {s}", .{
|
||||
offset,
|
||||
zld.getSymbolName(actual_target),
|
||||
macho_file.getSymbolName(actual_target),
|
||||
});
|
||||
try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
|
||||
}
|
||||
@ -795,15 +761,21 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void {
|
||||
const gpa = macho_file.base.allocator;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const sect_id = self.unwind_info_sect_id orelse {
|
||||
// If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
|
||||
// we will try fully synthesising unwind info records to somewhat match Apple ld's
|
||||
// approach. However, we will only synthesise DWARF records and nothing more. For this reason,
|
||||
// we still create the output `__TEXT,__unwind_info` section.
|
||||
if (self.hasEhFrameRecords()) {
|
||||
if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
|
||||
_ = try zld.initSection("__TEXT", "__unwind_info", .{});
|
||||
if (macho_file.unwind_info_section_index == null) {
|
||||
macho_file.unwind_info_section_index = try macho_file.initSection(
|
||||
"__TEXT",
|
||||
"__unwind_info",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -811,11 +783,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
|
||||
log.debug("parsing unwind info in {s}", .{self.name});
|
||||
|
||||
const gpa = zld.gpa;
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
|
||||
if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
|
||||
_ = try zld.initSection("__TEXT", "__unwind_info", .{});
|
||||
if (macho_file.unwind_info_section_index == null) {
|
||||
macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{});
|
||||
}
|
||||
|
||||
const unwind_records = self.getUnwindRecords();
|
||||
@ -826,11 +795,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
|
||||
} else false;
|
||||
|
||||
if (needs_eh_frame and !self.hasEhFrameRecords()) {
|
||||
log.err("missing __TEXT,__eh_frame section", .{});
|
||||
log.err(" in object {s}", .{self.name});
|
||||
return error.MissingSection;
|
||||
}
|
||||
if (needs_eh_frame and !self.hasEhFrameRecords()) return error.MissingEhFrameSection;
|
||||
|
||||
try self.parseRelocs(gpa, sect_id);
|
||||
const relocs = self.getRelocs(sect_id);
|
||||
@ -850,7 +815,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
|
||||
// Find function symbol that this record describes
|
||||
const rel = relocs[rel_pos.start..][rel_pos.len - 1];
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = mem.asBytes(&record),
|
||||
@ -874,7 +839,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
|
||||
};
|
||||
log.debug("unwind record {d} tracks {s}", .{
|
||||
record_id,
|
||||
zld.getSymbolName(actual_target),
|
||||
macho_file.getSymbolName(actual_target),
|
||||
});
|
||||
try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
|
||||
}
|
||||
@ -945,16 +910,14 @@ fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_cod
|
||||
return lhs.offset < rhs.offset;
|
||||
}
|
||||
|
||||
fn parseDysymtab(self: Object) ?macho.dysymtab_command {
|
||||
fn getDysymtab(self: Object) ?macho.dysymtab_command {
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.DYSYMTAB => {
|
||||
return cmd.cast(macho.dysymtab_command).?;
|
||||
},
|
||||
.DYSYMTAB => return cmd.cast(macho.dysymtab_command).?,
|
||||
else => {},
|
||||
}
|
||||
} else return null;
|
||||
@ -980,6 +943,26 @@ pub fn parseDwarfInfo(self: Object) DwarfInfo {
|
||||
return di;
|
||||
}
|
||||
|
||||
/// Returns Platform composed from the first encountered build version type load command:
|
||||
/// either LC_BUILD_VERSION or LC_VERSION_MIN_*.
|
||||
pub fn getPlatform(self: Object) ?Platform {
|
||||
var it = LoadCommandIterator{
|
||||
.ncmds = self.header.ncmds,
|
||||
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
|
||||
};
|
||||
while (it.next()) |cmd| {
|
||||
switch (cmd.cmd()) {
|
||||
.BUILD_VERSION,
|
||||
.VERSION_MIN_MACOSX,
|
||||
.VERSION_MIN_IPHONEOS,
|
||||
.VERSION_MIN_TVOS,
|
||||
.VERSION_MIN_WATCHOS,
|
||||
=> return Platform.fromLoadCommand(cmd),
|
||||
else => {},
|
||||
}
|
||||
} else return null;
|
||||
}
|
||||
|
||||
pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {
|
||||
const size = @as(usize, @intCast(sect.size));
|
||||
return self.contents[sect.offset..][0..size];
|
||||
@ -1050,7 +1033,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
|
||||
if (sect_hint) |sect_id| {
|
||||
if (self.source_section_index_lookup[sect_id].len > 0) {
|
||||
const lookup = self.source_section_index_lookup[sect_id];
|
||||
const target_sym_index = @import("zld.zig").lsearch(
|
||||
const target_sym_index = MachO.lsearch(
|
||||
i64,
|
||||
self.source_address_lookup[lookup.start..][0..lookup.len],
|
||||
Predicate{ .addr = @as(i64, @intCast(addr)) },
|
||||
@ -1065,7 +1048,7 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
|
||||
return self.getSectionAliasSymbolIndex(sect_id);
|
||||
}
|
||||
|
||||
const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup, Predicate{
|
||||
const target_sym_index = MachO.lsearch(i64, self.source_address_lookup, Predicate{
|
||||
.addr = @as(i64, @intCast(addr)),
|
||||
});
|
||||
assert(target_sym_index > 0);
|
||||
@ -1077,10 +1060,8 @@ pub fn getGlobal(self: Object, sym_index: u32) ?u32 {
|
||||
return @as(u32, @intCast(self.globals_lookup[sym_index]));
|
||||
}
|
||||
|
||||
pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
|
||||
const atom_index = self.atom_by_index_table[sym_index];
|
||||
if (atom_index == 0) return null;
|
||||
return atom_index;
|
||||
pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?Atom.Index {
|
||||
return self.atom_by_index_table[sym_index];
|
||||
}
|
||||
|
||||
pub fn hasUnwindRecords(self: Object) bool {
|
||||
@ -1109,3 +1090,28 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
|
||||
pub fn hasDataInCode(self: Object) bool {
|
||||
return self.data_in_code.items.len > 0;
|
||||
}
|
||||
|
||||
const Object = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const assert = std.debug.assert;
|
||||
const dwarf = std.dwarf;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const fs = std.fs;
|
||||
const io = std.io;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const sort = std.sort;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const DwarfInfo = @import("DwarfInfo.zig");
|
||||
const LoadCommandIterator = macho.LoadCommandIterator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Platform = @import("load_commands.zig").Platform;
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
|
||||
@ -62,7 +62,7 @@ pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
|
||||
const index = macho_file.stub_table.lookup.get(self.target) orelse return null;
|
||||
const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
|
||||
return header.addr +
|
||||
index * @import("stubs.zig").calcStubEntrySize(macho_file.base.options.target.cpu.arch);
|
||||
index * @import("stubs.zig").stubSize(macho_file.base.options.target.cpu.arch);
|
||||
}
|
||||
switch (self.type) {
|
||||
.got, .got_page, .got_pageoff => {
|
||||
|
||||
@ -28,248 +28,6 @@
|
||||
//! After the optional exported symbol information is a byte of how many edges (0-255) that
|
||||
//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
|
||||
//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
|
||||
const Trie = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
pub const Node = struct {
|
||||
base: *Trie,
|
||||
|
||||
/// Terminal info associated with this node.
|
||||
/// If this node is not a terminal node, info is null.
|
||||
terminal_info: ?struct {
|
||||
/// Export flags associated with this exported symbol.
|
||||
export_flags: u64,
|
||||
/// VM address offset wrt to the section this symbol is defined against.
|
||||
vmaddr_offset: u64,
|
||||
} = null,
|
||||
|
||||
/// Offset of this node in the trie output byte stream.
|
||||
trie_offset: ?u64 = null,
|
||||
|
||||
/// List of all edges originating from this node.
|
||||
edges: std.ArrayListUnmanaged(Edge) = .{},
|
||||
|
||||
node_dirty: bool = true,
|
||||
|
||||
/// Edge connecting to nodes in the trie.
|
||||
pub const Edge = struct {
|
||||
from: *Node,
|
||||
to: *Node,
|
||||
label: []u8,
|
||||
|
||||
fn deinit(self: *Edge, allocator: Allocator) void {
|
||||
self.to.deinit(allocator);
|
||||
allocator.destroy(self.to);
|
||||
allocator.free(self.label);
|
||||
self.from = undefined;
|
||||
self.to = undefined;
|
||||
self.label = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
fn deinit(self: *Node, allocator: Allocator) void {
|
||||
for (self.edges.items) |*edge| {
|
||||
edge.deinit(allocator);
|
||||
}
|
||||
self.edges.deinit(allocator);
|
||||
}
|
||||
|
||||
/// Inserts a new node starting from `self`.
|
||||
fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
|
||||
// Check for match with edges from this node.
|
||||
for (self.edges.items) |*edge| {
|
||||
const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
|
||||
if (match == 0) continue;
|
||||
if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
|
||||
|
||||
// Found a match, need to splice up nodes.
|
||||
// From: A -> B
|
||||
// To: A -> C -> B
|
||||
const mid = try allocator.create(Node);
|
||||
mid.* = .{ .base = self.base };
|
||||
var to_label = try allocator.dupe(u8, edge.label[match..]);
|
||||
allocator.free(edge.label);
|
||||
const to_node = edge.to;
|
||||
edge.to = mid;
|
||||
edge.label = try allocator.dupe(u8, label[0..match]);
|
||||
self.base.node_count += 1;
|
||||
|
||||
try mid.edges.append(allocator, .{
|
||||
.from = mid,
|
||||
.to = to_node,
|
||||
.label = to_label,
|
||||
});
|
||||
|
||||
return if (match == label.len) mid else mid.put(allocator, label[match..]);
|
||||
}
|
||||
|
||||
// Add a new node.
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{ .base = self.base };
|
||||
self.base.node_count += 1;
|
||||
|
||||
try self.edges.append(allocator, .{
|
||||
.from = self,
|
||||
.to = node,
|
||||
.label = try allocator.dupe(u8, label),
|
||||
});
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// Recursively parses the node from the input byte stream.
|
||||
fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
|
||||
self.node_dirty = true;
|
||||
const trie_offset = try reader.context.getPos();
|
||||
self.trie_offset = trie_offset;
|
||||
|
||||
var nread: usize = 0;
|
||||
|
||||
const node_size = try leb.readULEB128(u64, reader);
|
||||
if (node_size > 0) {
|
||||
const export_flags = try leb.readULEB128(u64, reader);
|
||||
// TODO Parse special flags.
|
||||
assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
|
||||
export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
|
||||
|
||||
const vmaddr_offset = try leb.readULEB128(u64, reader);
|
||||
|
||||
self.terminal_info = .{
|
||||
.export_flags = export_flags,
|
||||
.vmaddr_offset = vmaddr_offset,
|
||||
};
|
||||
}
|
||||
|
||||
const nedges = try reader.readByte();
|
||||
self.base.node_count += nedges;
|
||||
|
||||
nread += (try reader.context.getPos()) - trie_offset;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < nedges) : (i += 1) {
|
||||
const edge_start_pos = try reader.context.getPos();
|
||||
|
||||
const label = blk: {
|
||||
var label_buf = std.ArrayList(u8).init(allocator);
|
||||
while (true) {
|
||||
const next = try reader.readByte();
|
||||
if (next == @as(u8, 0))
|
||||
break;
|
||||
try label_buf.append(next);
|
||||
}
|
||||
break :blk try label_buf.toOwnedSlice();
|
||||
};
|
||||
|
||||
const seek_to = try leb.readULEB128(u64, reader);
|
||||
const return_pos = try reader.context.getPos();
|
||||
|
||||
nread += return_pos - edge_start_pos;
|
||||
try reader.context.seekTo(seek_to);
|
||||
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{ .base = self.base };
|
||||
|
||||
nread += try node.read(allocator, reader);
|
||||
try self.edges.append(allocator, .{
|
||||
.from = self,
|
||||
.to = node,
|
||||
.label = label,
|
||||
});
|
||||
try reader.context.seekTo(return_pos);
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
/// Writes this node to a byte stream.
|
||||
/// The children of this node *are* not written to the byte stream
|
||||
/// recursively. To write all nodes to a byte stream in sequence,
|
||||
/// iterate over `Trie.ordered_nodes` and call this method on each node.
|
||||
/// This is one of the requirements of the MachO.
|
||||
/// Panics if `finalize` was not called before calling this method.
|
||||
fn write(self: Node, writer: anytype) !void {
|
||||
assert(!self.node_dirty);
|
||||
if (self.terminal_info) |info| {
|
||||
// Terminal node info: encode export flags and vmaddr offset of this symbol.
|
||||
var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
|
||||
var info_stream = std.io.fixedBufferStream(&info_buf);
|
||||
// TODO Implement for special flags.
|
||||
assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
|
||||
info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
|
||||
try leb.writeULEB128(info_stream.writer(), info.export_flags);
|
||||
try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
|
||||
|
||||
// Encode the size of the terminal node info.
|
||||
var size_buf: [@sizeOf(u64)]u8 = undefined;
|
||||
var size_stream = std.io.fixedBufferStream(&size_buf);
|
||||
try leb.writeULEB128(size_stream.writer(), info_stream.pos);
|
||||
|
||||
// Now, write them to the output stream.
|
||||
try writer.writeAll(size_buf[0..size_stream.pos]);
|
||||
try writer.writeAll(info_buf[0..info_stream.pos]);
|
||||
} else {
|
||||
// Non-terminal node is delimited by 0 byte.
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
// Write number of edges (max legal number of edges is 256).
|
||||
try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
|
||||
|
||||
for (self.edges.items) |edge| {
|
||||
// Write edge label and offset to next node in trie.
|
||||
try writer.writeAll(edge.label);
|
||||
try writer.writeByte(0);
|
||||
try leb.writeULEB128(writer, edge.to.trie_offset.?);
|
||||
}
|
||||
}
|
||||
|
||||
const FinalizeResult = struct {
|
||||
/// Current size of this node in bytes.
|
||||
node_size: u64,
|
||||
|
||||
/// True if the trie offset of this node in the output byte stream
|
||||
/// would need updating; false otherwise.
|
||||
updated: bool,
|
||||
};
|
||||
|
||||
/// Updates offset of this node in the output byte stream.
|
||||
fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
|
||||
var node_size: u64 = 0;
|
||||
if (self.terminal_info) |info| {
|
||||
try leb.writeULEB128(writer, info.export_flags);
|
||||
try leb.writeULEB128(writer, info.vmaddr_offset);
|
||||
try leb.writeULEB128(writer, stream.bytes_written);
|
||||
} else {
|
||||
node_size += 1; // 0x0 for non-terminal nodes
|
||||
}
|
||||
node_size += 1; // 1 byte for edge count
|
||||
|
||||
for (self.edges.items) |edge| {
|
||||
const next_node_offset = edge.to.trie_offset orelse 0;
|
||||
node_size += edge.label.len + 1;
|
||||
try leb.writeULEB128(writer, next_node_offset);
|
||||
}
|
||||
|
||||
const trie_offset = self.trie_offset orelse 0;
|
||||
const updated = offset_in_trie != trie_offset;
|
||||
self.trie_offset = offset_in_trie;
|
||||
self.node_dirty = false;
|
||||
node_size += stream.bytes_written;
|
||||
|
||||
return FinalizeResult{ .node_size = node_size, .updated = updated };
|
||||
}
|
||||
};
|
||||
|
||||
/// The root node of the trie.
|
||||
root: ?*Node = null,
|
||||
|
||||
@ -611,3 +369,245 @@ test "ordering bug" {
|
||||
_ = try trie.write(stream.writer());
|
||||
try expectEqualHexStrings(&exp_buffer, buffer);
|
||||
}
|
||||
|
||||
pub const Node = struct {
|
||||
base: *Trie,
|
||||
|
||||
/// Terminal info associated with this node.
|
||||
/// If this node is not a terminal node, info is null.
|
||||
terminal_info: ?struct {
|
||||
/// Export flags associated with this exported symbol.
|
||||
export_flags: u64,
|
||||
/// VM address offset wrt to the section this symbol is defined against.
|
||||
vmaddr_offset: u64,
|
||||
} = null,
|
||||
|
||||
/// Offset of this node in the trie output byte stream.
|
||||
trie_offset: ?u64 = null,
|
||||
|
||||
/// List of all edges originating from this node.
|
||||
edges: std.ArrayListUnmanaged(Edge) = .{},
|
||||
|
||||
node_dirty: bool = true,
|
||||
|
||||
/// Edge connecting to nodes in the trie.
|
||||
pub const Edge = struct {
|
||||
from: *Node,
|
||||
to: *Node,
|
||||
label: []u8,
|
||||
|
||||
fn deinit(self: *Edge, allocator: Allocator) void {
|
||||
self.to.deinit(allocator);
|
||||
allocator.destroy(self.to);
|
||||
allocator.free(self.label);
|
||||
self.from = undefined;
|
||||
self.to = undefined;
|
||||
self.label = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
fn deinit(self: *Node, allocator: Allocator) void {
|
||||
for (self.edges.items) |*edge| {
|
||||
edge.deinit(allocator);
|
||||
}
|
||||
self.edges.deinit(allocator);
|
||||
}
|
||||
|
||||
/// Inserts a new node starting from `self`.
|
||||
fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
|
||||
// Check for match with edges from this node.
|
||||
for (self.edges.items) |*edge| {
|
||||
const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
|
||||
if (match == 0) continue;
|
||||
if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
|
||||
|
||||
// Found a match, need to splice up nodes.
|
||||
// From: A -> B
|
||||
// To: A -> C -> B
|
||||
const mid = try allocator.create(Node);
|
||||
mid.* = .{ .base = self.base };
|
||||
var to_label = try allocator.dupe(u8, edge.label[match..]);
|
||||
allocator.free(edge.label);
|
||||
const to_node = edge.to;
|
||||
edge.to = mid;
|
||||
edge.label = try allocator.dupe(u8, label[0..match]);
|
||||
self.base.node_count += 1;
|
||||
|
||||
try mid.edges.append(allocator, .{
|
||||
.from = mid,
|
||||
.to = to_node,
|
||||
.label = to_label,
|
||||
});
|
||||
|
||||
return if (match == label.len) mid else mid.put(allocator, label[match..]);
|
||||
}
|
||||
|
||||
// Add a new node.
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{ .base = self.base };
|
||||
self.base.node_count += 1;
|
||||
|
||||
try self.edges.append(allocator, .{
|
||||
.from = self,
|
||||
.to = node,
|
||||
.label = try allocator.dupe(u8, label),
|
||||
});
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// Recursively parses the node from the input byte stream.
|
||||
fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
|
||||
self.node_dirty = true;
|
||||
const trie_offset = try reader.context.getPos();
|
||||
self.trie_offset = trie_offset;
|
||||
|
||||
var nread: usize = 0;
|
||||
|
||||
const node_size = try leb.readULEB128(u64, reader);
|
||||
if (node_size > 0) {
|
||||
const export_flags = try leb.readULEB128(u64, reader);
|
||||
// TODO Parse special flags.
|
||||
assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
|
||||
export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
|
||||
|
||||
const vmaddr_offset = try leb.readULEB128(u64, reader);
|
||||
|
||||
self.terminal_info = .{
|
||||
.export_flags = export_flags,
|
||||
.vmaddr_offset = vmaddr_offset,
|
||||
};
|
||||
}
|
||||
|
||||
const nedges = try reader.readByte();
|
||||
self.base.node_count += nedges;
|
||||
|
||||
nread += (try reader.context.getPos()) - trie_offset;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < nedges) : (i += 1) {
|
||||
const edge_start_pos = try reader.context.getPos();
|
||||
|
||||
const label = blk: {
|
||||
var label_buf = std.ArrayList(u8).init(allocator);
|
||||
while (true) {
|
||||
const next = try reader.readByte();
|
||||
if (next == @as(u8, 0))
|
||||
break;
|
||||
try label_buf.append(next);
|
||||
}
|
||||
break :blk try label_buf.toOwnedSlice();
|
||||
};
|
||||
|
||||
const seek_to = try leb.readULEB128(u64, reader);
|
||||
const return_pos = try reader.context.getPos();
|
||||
|
||||
nread += return_pos - edge_start_pos;
|
||||
try reader.context.seekTo(seek_to);
|
||||
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{ .base = self.base };
|
||||
|
||||
nread += try node.read(allocator, reader);
|
||||
try self.edges.append(allocator, .{
|
||||
.from = self,
|
||||
.to = node,
|
||||
.label = label,
|
||||
});
|
||||
try reader.context.seekTo(return_pos);
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
/// Writes this node to a byte stream.
|
||||
/// The children of this node *are* not written to the byte stream
|
||||
/// recursively. To write all nodes to a byte stream in sequence,
|
||||
/// iterate over `Trie.ordered_nodes` and call this method on each node.
|
||||
/// This is one of the requirements of the MachO.
|
||||
/// Panics if `finalize` was not called before calling this method.
|
||||
fn write(self: Node, writer: anytype) !void {
|
||||
assert(!self.node_dirty);
|
||||
if (self.terminal_info) |info| {
|
||||
// Terminal node info: encode export flags and vmaddr offset of this symbol.
|
||||
var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
|
||||
var info_stream = std.io.fixedBufferStream(&info_buf);
|
||||
// TODO Implement for special flags.
|
||||
assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
|
||||
info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
|
||||
try leb.writeULEB128(info_stream.writer(), info.export_flags);
|
||||
try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
|
||||
|
||||
// Encode the size of the terminal node info.
|
||||
var size_buf: [@sizeOf(u64)]u8 = undefined;
|
||||
var size_stream = std.io.fixedBufferStream(&size_buf);
|
||||
try leb.writeULEB128(size_stream.writer(), info_stream.pos);
|
||||
|
||||
// Now, write them to the output stream.
|
||||
try writer.writeAll(size_buf[0..size_stream.pos]);
|
||||
try writer.writeAll(info_buf[0..info_stream.pos]);
|
||||
} else {
|
||||
// Non-terminal node is delimited by 0 byte.
|
||||
try writer.writeByte(0);
|
||||
}
|
||||
// Write number of edges (max legal number of edges is 256).
|
||||
try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
|
||||
|
||||
for (self.edges.items) |edge| {
|
||||
// Write edge label and offset to next node in trie.
|
||||
try writer.writeAll(edge.label);
|
||||
try writer.writeByte(0);
|
||||
try leb.writeULEB128(writer, edge.to.trie_offset.?);
|
||||
}
|
||||
}
|
||||
|
||||
const FinalizeResult = struct {
|
||||
/// Current size of this node in bytes.
|
||||
node_size: u64,
|
||||
|
||||
/// True if the trie offset of this node in the output byte stream
|
||||
/// would need updating; false otherwise.
|
||||
updated: bool,
|
||||
};
|
||||
|
||||
/// Updates offset of this node in the output byte stream.
|
||||
fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
|
||||
var stream = std.io.countingWriter(std.io.null_writer);
|
||||
var writer = stream.writer();
|
||||
|
||||
var node_size: u64 = 0;
|
||||
if (self.terminal_info) |info| {
|
||||
try leb.writeULEB128(writer, info.export_flags);
|
||||
try leb.writeULEB128(writer, info.vmaddr_offset);
|
||||
try leb.writeULEB128(writer, stream.bytes_written);
|
||||
} else {
|
||||
node_size += 1; // 0x0 for non-terminal nodes
|
||||
}
|
||||
node_size += 1; // 1 byte for edge count
|
||||
|
||||
for (self.edges.items) |edge| {
|
||||
const next_node_offset = edge.to.trie_offset orelse 0;
|
||||
node_size += edge.label.len + 1;
|
||||
try leb.writeULEB128(writer, next_node_offset);
|
||||
}
|
||||
|
||||
const trie_offset = self.trie_offset orelse 0;
|
||||
const updated = offset_in_trie != trie_offset;
|
||||
self.trie_offset = offset_in_trie;
|
||||
self.node_dirty = false;
|
||||
node_size += stream.bytes_written;
|
||||
|
||||
return FinalizeResult{ .node_size = node_size, .updated = updated };
|
||||
}
|
||||
};
|
||||
|
||||
const Trie = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
@ -1,26 +1,3 @@
|
||||
const UnwindInfo = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const fs = std.fs;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.unwind_info);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const EhFrameRecord = eh_frame.EhFrameRecord;
|
||||
const Object = @import("Object.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
const N_DEAD = @import("zld.zig").N_DEAD;
|
||||
|
||||
gpa: Allocator,
|
||||
|
||||
/// List of all unwind records gathered from all objects and sorted
|
||||
@ -204,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {
|
||||
info.lsdas_lookup.deinit(info.gpa);
|
||||
}
|
||||
|
||||
pub fn scanRelocs(zld: *Zld) !void {
|
||||
if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
|
||||
pub fn scanRelocs(macho_file: *MachO) !void {
|
||||
if (macho_file.unwind_info_section_index == null) return;
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
for (zld.objects.items, 0..) |*object, object_id| {
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
for (macho_file.objects.items, 0..) |*object, object_id| {
|
||||
const unwind_records = object.getUnwindRecords();
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
while (inner_syms_it.next()) |sym| {
|
||||
const record_id = object.unwind_records_lookup.get(sym) orelse continue;
|
||||
if (object.unwind_relocs_lookup[record_id].dead) continue;
|
||||
const record = unwind_records[record_id];
|
||||
if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
|
||||
if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
|
||||
if (getPersonalityFunctionReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
|
||||
// Personality function; add GOT pointer.
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = @as(u32, @intCast(object_id)),
|
||||
.rel = rel,
|
||||
.code = mem.asBytes(&record),
|
||||
.base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
|
||||
});
|
||||
try Atom.addGotEntry(zld, target);
|
||||
try macho_file.addGotEntry(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
|
||||
pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void {
|
||||
if (macho_file.unwind_info_section_index == null) return;
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
|
||||
var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
|
||||
defer records.deinit();
|
||||
@ -245,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
defer sym_indexes.deinit();
|
||||
|
||||
// TODO handle dead stripping
|
||||
for (zld.objects.items, 0..) |*object, object_id| {
|
||||
for (macho_file.objects.items, 0..) |*object, object_id| {
|
||||
log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
|
||||
const unwind_records = object.getUnwindRecords();
|
||||
|
||||
@ -255,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
|
||||
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
var prev_symbol: ?SymbolWithLoc = null;
|
||||
while (inner_syms_it.next()) |symbol| {
|
||||
var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
|
||||
@ -263,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
var record = unwind_records[record_id];
|
||||
|
||||
if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
|
||||
try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
|
||||
info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
|
||||
} else {
|
||||
if (getPersonalityFunctionReloc(
|
||||
zld,
|
||||
macho_file,
|
||||
@as(u32, @intCast(object_id)),
|
||||
record_id,
|
||||
)) |rel| {
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = @as(u32, @intCast(object_id)),
|
||||
.rel = rel,
|
||||
.code = mem.asBytes(&record),
|
||||
@ -287,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
|
||||
}
|
||||
|
||||
if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = @as(u32, @intCast(object_id)),
|
||||
.rel = rel,
|
||||
.code = mem.asBytes(&record),
|
||||
@ -299,8 +276,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
}
|
||||
break :blk record;
|
||||
} else blk: {
|
||||
const sym = zld.getSymbol(symbol);
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
const sym = macho_file.getSymbol(symbol);
|
||||
if (sym.n_desc == MachO.N_DEAD) continue;
|
||||
if (prev_symbol) |prev_sym| {
|
||||
const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
|
||||
const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
|
||||
@ -311,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
|
||||
if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
|
||||
var record = nullRecord();
|
||||
try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
|
||||
info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
|
||||
switch (cpu_arch) {
|
||||
.aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
|
||||
.x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
|
||||
@ -324,9 +301,9 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
break :blk nullRecord();
|
||||
};
|
||||
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const sym = zld.getSymbol(symbol);
|
||||
assert(sym.n_desc != N_DEAD);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sym = macho_file.getSymbol(symbol);
|
||||
assert(sym.n_desc != MachO.N_DEAD);
|
||||
const size = if (inner_syms_it.next()) |next_sym| blk: {
|
||||
// All this trouble to account for symbol aliases.
|
||||
// TODO I think that remodelling the linker so that a Symbol references an Atom
|
||||
@ -337,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
|
||||
const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
|
||||
if (next_addr > curr_addr) break :blk next_addr - curr_addr;
|
||||
break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
|
||||
} else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
|
||||
break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
|
||||
} else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
|
||||
record.rangeStart = sym.n_value;
|
||||
record.rangeLength = @as(u32, @intCast(size));
|
||||
|
||||
@ -519,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
|
||||
|
||||
fn collectPersonalityFromDwarf(
|
||||
info: *UnwindInfo,
|
||||
zld: *Zld,
|
||||
macho_file: *MachO,
|
||||
object_id: u32,
|
||||
sym_loc: SymbolWithLoc,
|
||||
record: *macho.compact_unwind_entry,
|
||||
) !void {
|
||||
const object = &zld.objects.items[object_id];
|
||||
) void {
|
||||
const object = &macho_file.objects.items[object_id];
|
||||
var it = object.getEhFrameRecordsIterator();
|
||||
const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
|
||||
it.seekTo(fde_offset);
|
||||
const fde = (try it.next()).?;
|
||||
const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
|
||||
const fde = (it.next() catch return).?; // We don't care about the error since we already handled it
|
||||
const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
|
||||
const cie_offset = fde_offset + 4 - cie_ptr;
|
||||
it.seekTo(cie_offset);
|
||||
const cie = (try it.next()).?;
|
||||
const cie = (it.next() catch return).?; // We don't care about the error since we already handled it
|
||||
|
||||
if (cie.getPersonalityPointerReloc(
|
||||
zld,
|
||||
macho_file,
|
||||
@as(u32, @intCast(object_id)),
|
||||
cie_offset,
|
||||
)) |target| {
|
||||
@ -551,9 +528,9 @@ fn collectPersonalityFromDwarf(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
|
||||
const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
|
||||
const sect = &zld.sections.items(.header)[sect_id];
|
||||
pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) void {
|
||||
const sect_id = macho_file.unwind_info_section_index orelse return;
|
||||
const sect = &macho_file.sections.items(.header)[sect_id];
|
||||
sect.@"align" = 2;
|
||||
sect.size = info.calcRequiredSize();
|
||||
}
|
||||
@ -570,25 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {
|
||||
return total_size;
|
||||
}
|
||||
|
||||
pub fn write(info: *UnwindInfo, zld: *Zld) !void {
|
||||
const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
|
||||
const sect = &zld.sections.items(.header)[sect_id];
|
||||
const seg_id = zld.sections.items(.segment_index)[sect_id];
|
||||
const seg = zld.segments.items[seg_id];
|
||||
pub fn write(info: *UnwindInfo, macho_file: *MachO) !void {
|
||||
const sect_id = macho_file.unwind_info_section_index orelse return;
|
||||
const sect = &macho_file.sections.items(.header)[sect_id];
|
||||
const seg_id = macho_file.sections.items(.segment_index)[sect_id];
|
||||
const seg = macho_file.segments.items[seg_id];
|
||||
|
||||
const text_sect_id = zld.getSectionByName("__TEXT", "__text").?;
|
||||
const text_sect = zld.sections.items(.header)[text_sect_id];
|
||||
const text_sect_id = macho_file.text_section_index.?;
|
||||
const text_sect = macho_file.sections.items(.header)[text_sect_id];
|
||||
|
||||
var personalities: [max_personalities]u32 = undefined;
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
|
||||
log.debug("Personalities:", .{});
|
||||
for (info.personalities[0..info.personalities_count], 0..) |target, i| {
|
||||
const atom_index = zld.getGotAtomIndexForSymbol(target).?;
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const sym = zld.getSymbol(atom.getSymbolWithLoc());
|
||||
personalities[i] = @as(u32, @intCast(sym.n_value - seg.vmaddr));
|
||||
log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
|
||||
const addr = macho_file.getGotEntryAddress(target).?;
|
||||
personalities[i] = @as(u32, @intCast(addr - seg.vmaddr));
|
||||
log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) });
|
||||
}
|
||||
|
||||
for (info.records.items) |*rec| {
|
||||
@ -602,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
|
||||
if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
|
||||
const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));
|
||||
if (lsda_target.getFile()) |_| {
|
||||
const sym = zld.getSymbol(lsda_target);
|
||||
const sym = macho_file.getSymbol(lsda_target);
|
||||
rec.lsda = sym.n_value - seg.vmaddr;
|
||||
}
|
||||
}
|
||||
@ -692,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
|
||||
@memset(buffer.items[offset..], 0);
|
||||
}
|
||||
|
||||
try zld.file.pwriteAll(buffer.items, sect.offset);
|
||||
try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
|
||||
}
|
||||
|
||||
fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
|
||||
const object = &zld.objects.items[object_id];
|
||||
fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info {
|
||||
const object = &macho_file.objects.items[object_id];
|
||||
assert(object.hasUnwindRecords());
|
||||
const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
|
||||
const relocs = object.getRelocs(object.unwind_info_sect_id.?);
|
||||
@ -710,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
|
||||
}
|
||||
|
||||
pub fn getPersonalityFunctionReloc(
|
||||
zld: *Zld,
|
||||
macho_file: *MachO,
|
||||
object_id: u32,
|
||||
record_id: usize,
|
||||
) ?macho.relocation_info {
|
||||
const relocs = getRelocs(zld, object_id, record_id);
|
||||
const relocs = getRelocs(macho_file, object_id, record_id);
|
||||
for (relocs) |rel| {
|
||||
if (isPersonalityFunction(record_id, rel)) return rel;
|
||||
}
|
||||
@ -738,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
|
||||
return rel_offset == 24;
|
||||
}
|
||||
|
||||
pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
|
||||
const relocs = getRelocs(zld, object_id, record_id);
|
||||
pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info {
|
||||
const relocs = getRelocs(macho_file, object_id, record_id);
|
||||
for (relocs) |rel| {
|
||||
if (isLsda(record_id, rel)) return rel;
|
||||
}
|
||||
@ -831,3 +806,23 @@ pub const UnwindEncoding = struct {
|
||||
enc.* |= offset;
|
||||
}
|
||||
};
|
||||
|
||||
const UnwindInfo = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const fs = std.fs;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.unwind_info);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const EhFrameRecord = eh_frame.EhFrameRecord;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,92 +1,73 @@
|
||||
//! An algorithm for dead stripping of unreferenced Atoms.
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const log = std.log.scoped(.dead_strip);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const SymbolResolver = @import("zld.zig").SymbolResolver;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
const N_DEAD = @import("zld.zig").N_DEAD;
|
||||
|
||||
const AtomTable = std.AutoHashMap(AtomIndex, void);
|
||||
|
||||
pub fn gcAtoms(zld: *Zld, resolver: *const SymbolResolver) !void {
|
||||
const gpa = zld.gpa;
|
||||
pub fn gcAtoms(macho_file: *MachO) !void {
|
||||
const gpa = macho_file.base.allocator;
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena.deinit();
|
||||
|
||||
var roots = AtomTable.init(arena.allocator());
|
||||
try roots.ensureUnusedCapacity(@as(u32, @intCast(zld.globals.items.len)));
|
||||
try roots.ensureUnusedCapacity(@as(u32, @intCast(macho_file.globals.items.len)));
|
||||
|
||||
var alive = AtomTable.init(arena.allocator());
|
||||
try alive.ensureTotalCapacity(@as(u32, @intCast(zld.atoms.items.len)));
|
||||
try alive.ensureTotalCapacity(@as(u32, @intCast(macho_file.atoms.items.len)));
|
||||
|
||||
try collectRoots(zld, &roots, resolver);
|
||||
try mark(zld, roots, &alive);
|
||||
prune(zld, alive);
|
||||
try collectRoots(macho_file, &roots);
|
||||
mark(macho_file, roots, &alive);
|
||||
prune(macho_file, alive);
|
||||
}
|
||||
|
||||
fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
|
||||
const sym = zld.getSymbol(sym_loc);
|
||||
fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
|
||||
const sym = macho_file.getSymbol(sym_loc);
|
||||
assert(!sym.undf());
|
||||
const object = &zld.objects.items[file];
|
||||
const object = &macho_file.objects.items[file];
|
||||
const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
|
||||
log.debug("root(ATOM({d}, %{d}, {d}))", .{
|
||||
atom_index,
|
||||
zld.getAtom(atom_index).sym_index,
|
||||
macho_file.getAtom(atom_index).sym_index,
|
||||
file,
|
||||
});
|
||||
_ = try roots.getOrPut(atom_index);
|
||||
}
|
||||
|
||||
fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !void {
|
||||
fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
|
||||
log.debug("collecting roots", .{});
|
||||
|
||||
switch (zld.options.output_mode) {
|
||||
switch (macho_file.base.options.output_mode) {
|
||||
.Exe => {
|
||||
// Add entrypoint as GC root
|
||||
const global: SymbolWithLoc = zld.getEntryPoint();
|
||||
if (global.getFile()) |file| {
|
||||
try addRoot(zld, roots, file, global);
|
||||
} else {
|
||||
assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
|
||||
if (macho_file.getEntryPoint()) |global| {
|
||||
if (global.getFile()) |file| {
|
||||
try addRoot(macho_file, roots, file, global);
|
||||
} else {
|
||||
assert(macho_file.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
|
||||
}
|
||||
}
|
||||
},
|
||||
else => |other| {
|
||||
assert(other == .Lib);
|
||||
// Add exports as GC roots
|
||||
for (zld.globals.items) |global| {
|
||||
const sym = zld.getSymbol(global);
|
||||
for (macho_file.globals.items) |global| {
|
||||
const sym = macho_file.getSymbol(global);
|
||||
if (sym.undf()) continue;
|
||||
|
||||
if (global.getFile()) |file| {
|
||||
try addRoot(zld, roots, file, global);
|
||||
try addRoot(macho_file, roots, file, global);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Add all symbols force-defined by the user.
|
||||
for (zld.options.force_undefined_symbols.keys()) |sym_name| {
|
||||
const global_index = resolver.table.get(sym_name).?;
|
||||
const global = zld.globals.items[global_index];
|
||||
const sym = zld.getSymbol(global);
|
||||
for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
|
||||
const global_index = macho_file.resolver.get(sym_name).?;
|
||||
const global = macho_file.globals.items[global_index];
|
||||
const sym = macho_file.getSymbol(global);
|
||||
assert(!sym.undf());
|
||||
try addRoot(zld, roots, global.getFile().?, global);
|
||||
try addRoot(macho_file, roots, global.getFile().?, global);
|
||||
}
|
||||
|
||||
for (zld.objects.items) |object| {
|
||||
for (macho_file.objects.items) |object| {
|
||||
const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
|
||||
|
||||
for (object.atoms.items) |atom_index| {
|
||||
@ -95,7 +76,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !
|
||||
// as a root.
|
||||
if (!has_subsections) break :blk true;
|
||||
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
|
||||
source_sym.n_sect - 1
|
||||
else sect_id: {
|
||||
@ -118,39 +99,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable, resolver: *const SymbolResolver) !
|
||||
|
||||
log.debug("root(ATOM({d}, %{d}, {?d}))", .{
|
||||
atom_index,
|
||||
zld.getAtom(atom_index).sym_index,
|
||||
zld.getAtom(atom_index).getFile(),
|
||||
macho_file.getAtom(atom_index).sym_index,
|
||||
macho_file.getAtom(atom_index).getFile(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
|
||||
fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void {
|
||||
if (alive.contains(atom_index)) return;
|
||||
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sym_loc = atom.getSymbolWithLoc();
|
||||
|
||||
log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
|
||||
|
||||
alive.putAssumeCapacityNoClobber(atom_index, {});
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
|
||||
const sym = zld.getSymbol(atom.getSymbolWithLoc());
|
||||
const header = zld.sections.items(.header)[sym.n_sect - 1];
|
||||
const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
|
||||
const header = macho_file.sections.items(.header)[sym.n_sect - 1];
|
||||
if (header.isZerofill()) return;
|
||||
|
||||
const code = Atom.getAtomCode(zld, atom_index);
|
||||
const relocs = Atom.getAtomRelocs(zld, atom_index);
|
||||
const ctx = Atom.getRelocContext(zld, atom_index);
|
||||
const code = Atom.getAtomCode(macho_file, atom_index);
|
||||
const relocs = Atom.getAtomRelocs(macho_file, atom_index);
|
||||
const ctx = Atom.getRelocContext(macho_file, atom_index);
|
||||
|
||||
for (relocs) |rel| {
|
||||
const target = switch (cpu_arch) {
|
||||
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
|
||||
.ARM64_RELOC_ADDEND => continue,
|
||||
else => Atom.parseRelocTarget(zld, .{
|
||||
else => Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = atom.getFile().?,
|
||||
.rel = rel,
|
||||
.code = code,
|
||||
@ -158,7 +139,7 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
|
||||
.base_addr = ctx.base_addr,
|
||||
}),
|
||||
},
|
||||
.x86_64 => Atom.parseRelocTarget(zld, .{
|
||||
.x86_64 => Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = atom.getFile().?,
|
||||
.rel = rel,
|
||||
.code = code,
|
||||
@ -167,50 +148,50 @@ fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
|
||||
}),
|
||||
else => unreachable,
|
||||
};
|
||||
const target_sym = zld.getSymbol(target);
|
||||
const target_sym = macho_file.getSymbol(target);
|
||||
|
||||
if (target_sym.undf()) continue;
|
||||
if (target.getFile() == null) {
|
||||
const target_sym_name = zld.getSymbolName(target);
|
||||
const target_sym_name = macho_file.getSymbolName(target);
|
||||
if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
|
||||
if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
|
||||
|
||||
unreachable; // referenced symbol not found
|
||||
}
|
||||
|
||||
const object = zld.objects.items[target.getFile().?];
|
||||
const object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
|
||||
log.debug(" following ATOM({d}, %{d}, {?d})", .{
|
||||
target_atom_index,
|
||||
zld.getAtom(target_atom_index).sym_index,
|
||||
zld.getAtom(target_atom_index).getFile(),
|
||||
macho_file.getAtom(target_atom_index).sym_index,
|
||||
macho_file.getAtom(target_atom_index).getFile(),
|
||||
});
|
||||
|
||||
markLive(zld, target_atom_index, alive);
|
||||
markLive(macho_file, target_atom_index, alive);
|
||||
}
|
||||
}
|
||||
|
||||
fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool {
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sym_loc = atom.getSymbolWithLoc();
|
||||
|
||||
log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
|
||||
const sym = zld.getSymbol(sym_loc);
|
||||
const header = zld.sections.items(.header)[sym.n_sect - 1];
|
||||
const sym = macho_file.getSymbol(sym_loc);
|
||||
const header = macho_file.sections.items(.header)[sym.n_sect - 1];
|
||||
assert(!header.isZerofill());
|
||||
|
||||
const code = Atom.getAtomCode(zld, atom_index);
|
||||
const relocs = Atom.getAtomRelocs(zld, atom_index);
|
||||
const ctx = Atom.getRelocContext(zld, atom_index);
|
||||
const code = Atom.getAtomCode(macho_file, atom_index);
|
||||
const relocs = Atom.getAtomRelocs(macho_file, atom_index);
|
||||
const ctx = Atom.getRelocContext(macho_file, atom_index);
|
||||
|
||||
for (relocs) |rel| {
|
||||
const target = switch (cpu_arch) {
|
||||
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
|
||||
.ARM64_RELOC_ADDEND => continue,
|
||||
else => Atom.parseRelocTarget(zld, .{
|
||||
else => Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = atom.getFile().?,
|
||||
.rel = rel,
|
||||
.code = code,
|
||||
@ -218,7 +199,7 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
|
||||
.base_addr = ctx.base_addr,
|
||||
}),
|
||||
},
|
||||
.x86_64 => Atom.parseRelocTarget(zld, .{
|
||||
.x86_64 => Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = atom.getFile().?,
|
||||
.rel = rel,
|
||||
.code = code,
|
||||
@ -228,16 +209,16 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const object = zld.objects.items[target.getFile().?];
|
||||
const object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {
|
||||
log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)});
|
||||
log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(target)});
|
||||
continue;
|
||||
};
|
||||
if (alive.contains(target_atom_index)) {
|
||||
log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
|
||||
target_atom_index,
|
||||
zld.getAtom(target_atom_index).sym_index,
|
||||
zld.getAtom(target_atom_index).getFile(),
|
||||
macho_file.getAtom(target_atom_index).sym_index,
|
||||
macho_file.getAtom(target_atom_index).getFile(),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@ -246,21 +227,21 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
|
||||
fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) void {
|
||||
var it = roots.keyIterator();
|
||||
while (it.next()) |root| {
|
||||
markLive(zld, root.*, alive);
|
||||
markLive(macho_file, root.*, alive);
|
||||
}
|
||||
|
||||
var loop: bool = true;
|
||||
while (loop) {
|
||||
loop = false;
|
||||
|
||||
for (zld.objects.items) |object| {
|
||||
for (macho_file.objects.items) |object| {
|
||||
for (object.atoms.items) |atom_index| {
|
||||
if (alive.contains(atom_index)) continue;
|
||||
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
|
||||
source_sym.n_sect - 1
|
||||
else blk: {
|
||||
@ -271,8 +252,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
|
||||
const source_sect = object.getSourceSection(sect_id);
|
||||
|
||||
if (source_sect.isDontDeadStripIfReferencesLive()) {
|
||||
if (refersLive(zld, atom_index, alive.*)) {
|
||||
markLive(zld, atom_index, alive);
|
||||
if (refersLive(macho_file, atom_index, alive.*)) {
|
||||
markLive(macho_file, atom_index, alive);
|
||||
loop = true;
|
||||
}
|
||||
}
|
||||
@ -280,26 +261,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
|
||||
}
|
||||
}
|
||||
|
||||
for (zld.objects.items, 0..) |_, object_id| {
|
||||
for (macho_file.objects.items, 0..) |_, object_id| {
|
||||
// Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
|
||||
// marking all references as live.
|
||||
try markUnwindRecords(zld, @as(u32, @intCast(object_id)), alive);
|
||||
markUnwindRecords(macho_file, @as(u32, @intCast(object_id)), alive);
|
||||
}
|
||||
}
|
||||
|
||||
fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
|
||||
const object = &zld.objects.items[object_id];
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) void {
|
||||
const object = &macho_file.objects.items[object_id];
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
|
||||
const unwind_records = object.getUnwindRecords();
|
||||
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
|
||||
if (!object.hasUnwindRecords()) {
|
||||
if (alive.contains(atom_index)) {
|
||||
// Mark references live and continue.
|
||||
try markEhFrameRecords(zld, object_id, atom_index, alive);
|
||||
markEhFrameRecords(macho_file, object_id, atom_index, alive);
|
||||
} else {
|
||||
while (inner_syms_it.next()) |sym| {
|
||||
if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
|
||||
@ -325,86 +306,86 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
|
||||
|
||||
const record = unwind_records[record_id];
|
||||
if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
|
||||
try markEhFrameRecords(zld, object_id, atom_index, alive);
|
||||
markEhFrameRecords(macho_file, object_id, atom_index, alive);
|
||||
} else {
|
||||
if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| {
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = mem.asBytes(&record),
|
||||
.base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
|
||||
});
|
||||
const target_sym = zld.getSymbol(target);
|
||||
const target_sym = macho_file.getSymbol(target);
|
||||
if (!target_sym.undf()) {
|
||||
const target_object = zld.objects.items[target.getFile().?];
|
||||
const target_object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
|
||||
markLive(zld, target_atom_index, alive);
|
||||
markLive(macho_file, target_atom_index, alive);
|
||||
}
|
||||
}
|
||||
|
||||
if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| {
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = mem.asBytes(&record),
|
||||
.base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
|
||||
});
|
||||
const target_object = zld.objects.items[target.getFile().?];
|
||||
const target_object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
|
||||
markLive(zld, target_atom_index, alive);
|
||||
markLive(macho_file, target_atom_index, alive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const object = &zld.objects.items[object_id];
|
||||
fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) void {
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const object = &macho_file.objects.items[object_id];
|
||||
var it = object.getEhFrameRecordsIterator();
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
|
||||
while (inner_syms_it.next()) |sym| {
|
||||
const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
|
||||
it.seekTo(fde_offset);
|
||||
const fde = (try it.next()).?;
|
||||
const fde = (it.next() catch continue).?; // We don't care about the error at this point since it was already handled
|
||||
|
||||
const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
|
||||
const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
|
||||
const cie_offset = fde_offset + 4 - cie_ptr;
|
||||
it.seekTo(cie_offset);
|
||||
const cie = (try it.next()).?;
|
||||
const cie = (it.next() catch continue).?; // We don't care about the error at this point since it was already handled
|
||||
|
||||
switch (cpu_arch) {
|
||||
.aarch64 => {
|
||||
// Mark FDE references which should include any referenced LSDA record
|
||||
const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
|
||||
const relocs = eh_frame.getRelocs(macho_file, object_id, fde_offset);
|
||||
for (relocs) |rel| {
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = fde.data,
|
||||
.base_offset = @as(i32, @intCast(fde_offset)) + 4,
|
||||
});
|
||||
const target_sym = zld.getSymbol(target);
|
||||
const target_sym = macho_file.getSymbol(target);
|
||||
if (!target_sym.undf()) blk: {
|
||||
const target_object = zld.objects.items[target.getFile().?];
|
||||
const target_object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
|
||||
break :blk;
|
||||
markLive(zld, target_atom_index, alive);
|
||||
markLive(macho_file, target_atom_index, alive);
|
||||
}
|
||||
}
|
||||
},
|
||||
.x86_64 => {
|
||||
const sect = object.getSourceSection(object.eh_frame_sect_id.?);
|
||||
const lsda_ptr = try fde.getLsdaPointer(cie, .{
|
||||
const lsda_ptr = fde.getLsdaPointer(cie, .{
|
||||
.base_addr = sect.addr,
|
||||
.base_offset = fde_offset,
|
||||
});
|
||||
}) catch continue; // We don't care about the error at this point since it was already handled
|
||||
if (lsda_ptr) |lsda_address| {
|
||||
// Mark LSDA record as live
|
||||
const sym_index = object.getSymbolByAddress(lsda_address, null);
|
||||
const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
|
||||
markLive(zld, target_atom_index, alive);
|
||||
markLive(macho_file, target_atom_index, alive);
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
@ -412,20 +393,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *
|
||||
|
||||
// Mark CIE references which should include any referenced personalities
|
||||
// that are defined locally.
|
||||
if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
|
||||
const target_sym = zld.getSymbol(target);
|
||||
if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| {
|
||||
const target_sym = macho_file.getSymbol(target);
|
||||
if (!target_sym.undf()) {
|
||||
const target_object = zld.objects.items[target.getFile().?];
|
||||
const target_object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
|
||||
markLive(zld, target_atom_index, alive);
|
||||
markLive(macho_file, target_atom_index, alive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prune(zld: *Zld, alive: AtomTable) void {
|
||||
fn prune(macho_file: *MachO, alive: AtomTable) void {
|
||||
log.debug("pruning dead atoms", .{});
|
||||
for (zld.objects.items) |*object| {
|
||||
for (macho_file.objects.items) |*object| {
|
||||
var i: usize = 0;
|
||||
while (i < object.atoms.items.len) {
|
||||
const atom_index = object.atoms.items[i];
|
||||
@ -434,7 +415,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
|
||||
continue;
|
||||
}
|
||||
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sym_loc = atom.getSymbolWithLoc();
|
||||
|
||||
log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
|
||||
@ -442,15 +423,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {
|
||||
sym_loc.sym_index,
|
||||
sym_loc.getFile(),
|
||||
});
|
||||
log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
|
||||
log.debug(" {s} in {s}", .{ macho_file.getSymbolName(sym_loc), object.name });
|
||||
|
||||
const sym = zld.getSymbolPtr(sym_loc);
|
||||
const sym = macho_file.getSymbolPtr(sym_loc);
|
||||
const sect_id = sym.n_sect - 1;
|
||||
var section = zld.sections.get(sect_id);
|
||||
var section = macho_file.sections.get(sect_id);
|
||||
section.header.size -= atom.size;
|
||||
|
||||
if (atom.prev_index) |prev_index| {
|
||||
const prev = zld.getAtomPtr(prev_index);
|
||||
const prev = macho_file.getAtomPtr(prev_index);
|
||||
prev.next_index = atom.next_index;
|
||||
} else {
|
||||
if (atom.next_index) |next_index| {
|
||||
@ -458,33 +439,49 @@ fn prune(zld: *Zld, alive: AtomTable) void {
|
||||
}
|
||||
}
|
||||
if (atom.next_index) |next_index| {
|
||||
const next = zld.getAtomPtr(next_index);
|
||||
const next = macho_file.getAtomPtr(next_index);
|
||||
next.prev_index = atom.prev_index;
|
||||
} else {
|
||||
if (atom.prev_index) |prev_index| {
|
||||
section.last_atom_index = prev_index;
|
||||
} else {
|
||||
assert(section.header.size == 0);
|
||||
section.first_atom_index = 0;
|
||||
section.last_atom_index = 0;
|
||||
section.first_atom_index = null;
|
||||
section.last_atom_index = null;
|
||||
}
|
||||
}
|
||||
|
||||
zld.sections.set(sect_id, section);
|
||||
macho_file.sections.set(sect_id, section);
|
||||
_ = object.atoms.swapRemove(i);
|
||||
|
||||
sym.n_desc = N_DEAD;
|
||||
sym.n_desc = MachO.N_DEAD;
|
||||
|
||||
var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_sym_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
while (inner_sym_it.next()) |inner| {
|
||||
const inner_sym = zld.getSymbolPtr(inner);
|
||||
inner_sym.n_desc = N_DEAD;
|
||||
const inner_sym = macho_file.getSymbolPtr(inner);
|
||||
inner_sym.n_desc = MachO.N_DEAD;
|
||||
}
|
||||
|
||||
if (Atom.getSectionAlias(zld, atom_index)) |alias| {
|
||||
const alias_sym = zld.getSymbolPtr(alias);
|
||||
alias_sym.n_desc = N_DEAD;
|
||||
if (Atom.getSectionAlias(macho_file, atom_index)) |alias| {
|
||||
const alias_sym = macho_file.getSymbolPtr(alias);
|
||||
alias_sym.n_desc = MachO.N_DEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const eh_frame = @import("eh_frame.zig");
|
||||
const log = std.log.scoped(.dead_strip);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
|
||||
const AtomTable = std.AutoHashMap(Atom.Index, void);
|
||||
|
||||
@ -1,14 +1,3 @@
|
||||
const Rebase = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.dyld_info);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
buffer: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
@ -572,3 +561,14 @@ test "rebase - composite" {
|
||||
macho.REBASE_OPCODE_DONE,
|
||||
}, rebase.buffer.items);
|
||||
}
|
||||
|
||||
const Rebase = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.dyld_info);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
@ -1,12 +1,3 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.dyld_info);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn Bind(comptime Ctx: type, comptime Target: type) type {
|
||||
return struct {
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
@ -738,3 +729,12 @@ test "lazy bind" {
|
||||
macho.BIND_OPCODE_DONE,
|
||||
}, bind.buffer.items);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.dyld_info);
|
||||
const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
@ -1,68 +1,52 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.eh_frame);
|
||||
pub fn scanRelocs(macho_file: *MachO) !void {
|
||||
const gpa = macho_file.base.allocator;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
pub fn scanRelocs(zld: *Zld) !void {
|
||||
const gpa = zld.gpa;
|
||||
|
||||
for (zld.objects.items, 0..) |*object, object_id| {
|
||||
for (macho_file.objects.items, 0..) |*object, object_id| {
|
||||
var cies = std.AutoHashMap(u32, void).init(gpa);
|
||||
defer cies.deinit();
|
||||
|
||||
var it = object.getEhFrameRecordsIterator();
|
||||
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
while (inner_syms_it.next()) |sym| {
|
||||
const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
|
||||
if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
|
||||
it.seekTo(fde_offset);
|
||||
const fde = (try it.next()).?;
|
||||
const fde = (it.next() catch continue).?; // We don't care about this error since we already handled it
|
||||
|
||||
const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
|
||||
const cie_ptr = fde.getCiePointerSource(@intCast(object_id), macho_file, fde_offset);
|
||||
const cie_offset = fde_offset + 4 - cie_ptr;
|
||||
|
||||
if (!cies.contains(cie_offset)) {
|
||||
try cies.putNoClobber(cie_offset, {});
|
||||
it.seekTo(cie_offset);
|
||||
const cie = (try it.next()).?;
|
||||
try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
|
||||
const cie = (it.next() catch continue).?; // We don't care about this error since we already handled it
|
||||
try cie.scanRelocs(macho_file, @as(u32, @intCast(object_id)), cie_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
|
||||
const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
|
||||
const sect = &zld.sections.items(.header)[sect_id];
|
||||
pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) error{OutOfMemory}!void {
|
||||
const sect_id = macho_file.eh_frame_section_index orelse return;
|
||||
const sect = &macho_file.sections.items(.header)[sect_id];
|
||||
sect.@"align" = 3;
|
||||
sect.size = 0;
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const gpa = zld.gpa;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const gpa = macho_file.base.allocator;
|
||||
var size: u32 = 0;
|
||||
|
||||
for (zld.objects.items, 0..) |*object, object_id| {
|
||||
for (macho_file.objects.items, 0..) |*object, object_id| {
|
||||
var cies = std.AutoHashMap(u32, u32).init(gpa);
|
||||
defer cies.deinit();
|
||||
|
||||
var eh_it = object.getEhFrameRecordsIterator();
|
||||
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
while (inner_syms_it.next()) |sym| {
|
||||
const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
|
||||
if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
|
||||
@ -75,15 +59,15 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
|
||||
if (!is_dwarf) continue;
|
||||
|
||||
eh_it.seekTo(fde_record_offset);
|
||||
const source_fde_record = (try eh_it.next()).?;
|
||||
const source_fde_record = (eh_it.next() catch continue).?; // We already handled this error
|
||||
|
||||
const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
|
||||
const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
|
||||
const cie_offset = fde_record_offset + 4 - cie_ptr;
|
||||
|
||||
const gop = try cies.getOrPut(cie_offset);
|
||||
if (!gop.found_existing) {
|
||||
eh_it.seekTo(cie_offset);
|
||||
const source_cie_record = (try eh_it.next()).?;
|
||||
const source_cie_record = (eh_it.next() catch continue).?; // We already handled this error
|
||||
gop.value_ptr.* = size;
|
||||
size += source_cie_record.getSize();
|
||||
}
|
||||
@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
|
||||
const sect = zld.sections.items(.header)[sect_id];
|
||||
const seg_id = zld.sections.items(.segment_index)[sect_id];
|
||||
const seg = zld.segments.items[seg_id];
|
||||
pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void {
|
||||
const sect_id = macho_file.eh_frame_section_index orelse return;
|
||||
const sect = macho_file.sections.items(.header)[sect_id];
|
||||
const seg_id = macho_file.sections.items(.segment_index)[sect_id];
|
||||
const seg = macho_file.segments.items[seg_id];
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const gpa = zld.gpa;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const gpa = macho_file.base.allocator;
|
||||
|
||||
var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
|
||||
defer {
|
||||
@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
|
||||
var eh_frame_offset: u32 = 0;
|
||||
|
||||
for (zld.objects.items, 0..) |*object, object_id| {
|
||||
for (macho_file.objects.items, 0..) |*object, object_id| {
|
||||
try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
|
||||
|
||||
var cies = std.AutoHashMap(u32, u32).init(gpa);
|
||||
@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
var eh_it = object.getEhFrameRecordsIterator();
|
||||
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
||||
var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
|
||||
while (inner_syms_it.next()) |target| {
|
||||
const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
|
||||
if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
|
||||
@ -137,17 +121,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
if (!is_dwarf) continue;
|
||||
|
||||
eh_it.seekTo(fde_record_offset);
|
||||
const source_fde_record = (try eh_it.next()).?;
|
||||
const source_fde_record = (eh_it.next() catch continue).?; // We already handled this error
|
||||
|
||||
const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
|
||||
const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
|
||||
const cie_offset = fde_record_offset + 4 - cie_ptr;
|
||||
|
||||
const gop = try cies.getOrPut(cie_offset);
|
||||
if (!gop.found_existing) {
|
||||
eh_it.seekTo(cie_offset);
|
||||
const source_cie_record = (try eh_it.next()).?;
|
||||
const source_cie_record = (eh_it.next() catch continue).?; // We already handled this error
|
||||
var cie_record = try source_cie_record.toOwned(gpa);
|
||||
try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
|
||||
try cie_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
|
||||
.source_offset = cie_offset,
|
||||
.out_offset = eh_frame_offset,
|
||||
.sect_addr = sect.addr,
|
||||
@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
}
|
||||
|
||||
var fde_record = try source_fde_record.toOwned(gpa);
|
||||
try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
|
||||
try fde_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
|
||||
.source_offset = fde_record_offset,
|
||||
.out_offset = eh_frame_offset,
|
||||
.sect_addr = sect.addr,
|
||||
@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
.aarch64 => {}, // relocs take care of LSDA pointers
|
||||
.x86_64 => {
|
||||
// We need to relocate target symbol address ourselves.
|
||||
const atom_sym = zld.getSymbol(target);
|
||||
const atom_sym = macho_file.getSymbol(target);
|
||||
try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
|
||||
.base_addr = sect.addr,
|
||||
.base_offset = eh_frame_offset,
|
||||
@ -180,17 +164,17 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
eh_frame_offset + 4 - fde_record.getCiePointer(),
|
||||
).?;
|
||||
const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
|
||||
const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
|
||||
const source_lsda_ptr = fde_record.getLsdaPointer(cie_record, .{
|
||||
.base_addr = eh_frame_sect.addr,
|
||||
.base_offset = fde_record_offset,
|
||||
});
|
||||
}) catch continue; // We already handled this error
|
||||
if (source_lsda_ptr) |ptr| {
|
||||
const sym_index = object.getSymbolByAddress(ptr, null);
|
||||
const sym = object.symtab[sym_index];
|
||||
try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
|
||||
fde_record.setLsdaPointer(cie_record, sym.n_value, .{
|
||||
.base_addr = sect.addr,
|
||||
.base_offset = eh_frame_offset,
|
||||
});
|
||||
}) catch continue; // We already handled this error
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
@ -207,10 +191,10 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
const cie_record = eh_records.get(
|
||||
eh_frame_offset + 4 - fde_record.getCiePointer(),
|
||||
).?;
|
||||
const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
|
||||
const lsda_ptr = fde_record.getLsdaPointer(cie_record, .{
|
||||
.base_addr = sect.addr,
|
||||
.base_offset = eh_frame_offset,
|
||||
});
|
||||
}) catch continue; // We already handled this error
|
||||
if (lsda_ptr) |ptr| {
|
||||
record.lsda = ptr - seg.vmaddr;
|
||||
}
|
||||
@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
||||
try buffer.appendSlice(record.data);
|
||||
}
|
||||
|
||||
try zld.file.pwriteAll(buffer.items, sect.offset);
|
||||
try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
|
||||
}
|
||||
const EhFrameRecordTag = enum { cie, fde };
|
||||
|
||||
@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
|
||||
pub fn scanRelocs(
|
||||
rec: Record,
|
||||
zld: *Zld,
|
||||
macho_file: *MachO,
|
||||
object_id: u32,
|
||||
source_offset: u32,
|
||||
) !void {
|
||||
if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
|
||||
try Atom.addGotEntry(zld, target);
|
||||
if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| {
|
||||
try macho_file.addGotEntry(target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
|
||||
pub fn getPersonalityPointerReloc(
|
||||
rec: Record,
|
||||
zld: *Zld,
|
||||
macho_file: *MachO,
|
||||
object_id: u32,
|
||||
source_offset: u32,
|
||||
) ?SymbolWithLoc {
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const relocs = getRelocs(zld, object_id, source_offset);
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const relocs = getRelocs(macho_file, object_id, source_offset);
|
||||
for (relocs) |rel| {
|
||||
switch (cpu_arch) {
|
||||
.aarch64 => {
|
||||
@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = rec.data,
|
||||
@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
|
||||
pub fn relocate(rec: *Record, macho_file: *MachO, object_id: u32, ctx: struct {
|
||||
source_offset: u32,
|
||||
out_offset: u32,
|
||||
sect_addr: u64,
|
||||
}) !void {
|
||||
comptime assert(is_mutable);
|
||||
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const relocs = getRelocs(zld, object_id, ctx.source_offset);
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const relocs = getRelocs(macho_file, object_id, ctx.source_offset);
|
||||
|
||||
for (relocs) |rel| {
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = object_id,
|
||||
.rel = rel,
|
||||
.code = rec.data,
|
||||
@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
// Address of the __eh_frame in the source object file
|
||||
},
|
||||
.ARM64_RELOC_POINTER_TO_GOT => {
|
||||
const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
|
||||
const target_addr = macho_file.getGotEntryAddress(target).?;
|
||||
const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
|
||||
return error.Overflow;
|
||||
mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
|
||||
},
|
||||
.ARM64_RELOC_UNSIGNED => {
|
||||
assert(rel.r_extern == 1);
|
||||
const target_addr = try Atom.getRelocTargetAddress(zld, target, false, false);
|
||||
const target_addr = Atom.getRelocTargetAddress(macho_file, target, false);
|
||||
const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
|
||||
mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
|
||||
},
|
||||
@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
|
||||
switch (rel_type) {
|
||||
.X86_64_RELOC_GOT => {
|
||||
const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
|
||||
const target_addr = macho_file.getGotEntryAddress(target).?;
|
||||
const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
|
||||
const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 {
|
||||
pub fn getCiePointerSource(rec: Record, object_id: u32, macho_file: *MachO, offset: u32) u32 {
|
||||
assert(rec.tag == .fde);
|
||||
const cpu_arch = zld.options.target.cpu.arch;
|
||||
const cpu_arch = macho_file.base.options.target.cpu.arch;
|
||||
const addend = mem.readIntLittle(u32, rec.data[0..4]);
|
||||
switch (cpu_arch) {
|
||||
.aarch64 => {
|
||||
const relocs = getRelocs(zld, object_id, offset);
|
||||
const relocs = getRelocs(macho_file, object_id, offset);
|
||||
const maybe_rel = for (relocs) |rel| {
|
||||
if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
|
||||
@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
|
||||
break rel;
|
||||
} else null;
|
||||
const rel = maybe_rel orelse return addend;
|
||||
const object = &zld.objects.items[object_id];
|
||||
const object = &macho_file.objects.items[object_id];
|
||||
const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
|
||||
const sect = object.getSourceSection(object.eh_frame_sect_id.?);
|
||||
return @intCast(sect.addr + offset - target_addr + addend);
|
||||
@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
|
||||
const object = &zld.objects.items[object_id];
|
||||
pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info {
|
||||
const object = &macho_file.objects.items[object_id];
|
||||
assert(object.hasEhFrameRecords());
|
||||
const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
|
||||
return &[0]macho.relocation_info{};
|
||||
@ -604,7 +588,7 @@ pub const Iterator = struct {
|
||||
|
||||
var size = try reader.readIntLittle(u32);
|
||||
if (size == 0xFFFFFFFF) {
|
||||
log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
|
||||
log.debug("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
|
||||
return error.BadDwarfCfi;
|
||||
}
|
||||
|
||||
@ -650,3 +634,18 @@ pub const EH_PE = struct {
|
||||
pub const indirect = 0x80;
|
||||
pub const omit = 0xFF;
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.eh_frame);
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
|
||||
@ -1,42 +1,44 @@
|
||||
const std = @import("std");
|
||||
const log = std.log.scoped(.archive);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch {
|
||||
const cpu_arch: std.Target.Cpu.Arch = switch (cputype) {
|
||||
macho.CPU_TYPE_ARM64 => .aarch64,
|
||||
macho.CPU_TYPE_X86_64 => .x86_64,
|
||||
else => {
|
||||
if (logError) {
|
||||
log.err("unsupported cpu architecture 0x{x}", .{cputype});
|
||||
}
|
||||
return error.UnsupportedCpuArchitecture;
|
||||
},
|
||||
};
|
||||
return cpu_arch;
|
||||
pub fn isFatLibrary(file: std.fs.File) bool {
|
||||
const reader = file.reader();
|
||||
const hdr = reader.readStructBig(macho.fat_header) catch return false;
|
||||
defer file.seekTo(0) catch {};
|
||||
return hdr.magic == macho.FAT_MAGIC;
|
||||
}
|
||||
|
||||
pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
|
||||
pub const Arch = struct {
|
||||
tag: std.Target.Cpu.Arch,
|
||||
offset: u64,
|
||||
};
|
||||
|
||||
/// Caller owns the memory.
|
||||
pub fn parseArchs(gpa: Allocator, file: std.fs.File) ![]const Arch {
|
||||
const reader = file.reader();
|
||||
const fat_header = try reader.readStructBig(macho.fat_header);
|
||||
if (fat_header.magic != macho.FAT_MAGIC) return 0;
|
||||
assert(fat_header.magic == macho.FAT_MAGIC);
|
||||
|
||||
var archs = try std.ArrayList(Arch).initCapacity(gpa, fat_header.nfat_arch);
|
||||
defer archs.deinit();
|
||||
|
||||
var fat_arch_index: u32 = 0;
|
||||
while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
|
||||
const fat_arch = try reader.readStructBig(macho.fat_arch);
|
||||
// If we come across an architecture that we do not know how to handle, that's
|
||||
// fine because we can keep looking for one that might match.
|
||||
const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) {
|
||||
error.UnsupportedCpuArchitecture => continue,
|
||||
const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
|
||||
macho.CPU_TYPE_ARM64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_ARM_ALL) .aarch64 else continue,
|
||||
macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
|
||||
else => continue,
|
||||
};
|
||||
if (lib_arch == cpu_arch) {
|
||||
// We have found a matching architecture!
|
||||
return fat_arch.offset;
|
||||
}
|
||||
} else {
|
||||
log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
|
||||
@tagName(cpu_arch),
|
||||
});
|
||||
return error.MismatchedCpuArchitecture;
|
||||
|
||||
archs.appendAssumeCapacity(.{ .tag = arch, .offset = fat_arch.offset });
|
||||
}
|
||||
|
||||
return archs.toOwnedSlice();
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.archive);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
@ -1,12 +1,3 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const ThreadPool = std.Thread.Pool;
|
||||
const WaitGroup = std.Thread.WaitGroup;
|
||||
|
||||
pub fn ParallelHasher(comptime Hasher: type) type {
|
||||
const hash_size = Hasher.digest_length;
|
||||
|
||||
@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {
|
||||
const Self = @This();
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const ThreadPool = std.Thread.Pool;
|
||||
const WaitGroup = std.Thread.WaitGroup;
|
||||
|
||||
@ -1,13 +1,3 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const link = @import("../../link.zig");
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Dylib = @import("Dylib.zig");
|
||||
|
||||
/// Default implicit entrypoint symbol name.
|
||||
pub const default_entry_point: []const u8 = "_main";
|
||||
|
||||
@ -86,8 +76,14 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
|
||||
}
|
||||
// LC_SOURCE_VERSION
|
||||
sizeofcmds += @sizeOf(macho.source_version_command);
|
||||
// LC_BUILD_VERSION
|
||||
sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
|
||||
// LC_BUILD_VERSION or LC_VERSION_MIN_
|
||||
if (Platform.fromTarget(options.target).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);
|
||||
// LC_LOAD_DYLIB
|
||||
@ -101,17 +97,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx
|
||||
);
|
||||
}
|
||||
// LC_CODE_SIGNATURE
|
||||
{
|
||||
const target = options.target;
|
||||
const requires_codesig = blk: {
|
||||
if (options.entitlements) |_| break :blk true;
|
||||
if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
|
||||
break :blk true;
|
||||
break :blk false;
|
||||
};
|
||||
if (requires_codesig) {
|
||||
sizeofcmds += @sizeOf(macho.linkedit_data_command);
|
||||
}
|
||||
if (MachO.requiresCodeSignature(options)) {
|
||||
sizeofcmds += @sizeOf(macho.linkedit_data_command);
|
||||
}
|
||||
|
||||
return @as(u32, @intCast(sizeofcmds));
|
||||
@ -271,33 +258,28 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: an
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void {
|
||||
pub fn writeVersionMinLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
|
||||
const cmd: macho.LC = switch (platform.os_tag) {
|
||||
.macos => .VERSION_MIN_MACOSX,
|
||||
.ios => .VERSION_MIN_IPHONEOS,
|
||||
.tvos => .VERSION_MIN_TVOS,
|
||||
.watchos => .VERSION_MIN_WATCHOS,
|
||||
else => unreachable,
|
||||
};
|
||||
try lc_writer.writeAll(mem.asBytes(&macho.version_min_command{
|
||||
.cmd = cmd,
|
||||
.version = platform.toAppleVersion(),
|
||||
.sdk = if (sdk_version) |ver| semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn writeBuildVersionLC(platform: Platform, sdk_version: ?std.SemanticVersion, lc_writer: anytype) !void {
|
||||
const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
|
||||
const platform_version = blk: {
|
||||
const ver = options.target.os.version_range.semver.min;
|
||||
const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8));
|
||||
break :blk platform_version;
|
||||
};
|
||||
const sdk_version: ?std.SemanticVersion = options.darwin_sdk_version orelse blk: {
|
||||
if (options.sysroot) |path| break :blk inferSdkVersionFromSdkPath(path);
|
||||
break :blk null;
|
||||
};
|
||||
const sdk_version_value: u32 = if (sdk_version) |ver|
|
||||
@intCast(ver.major << 16 | ver.minor << 8)
|
||||
else
|
||||
platform_version;
|
||||
const is_simulator_abi = options.target.abi == .simulator;
|
||||
try lc_writer.writeStruct(macho.build_version_command{
|
||||
.cmdsize = cmdsize,
|
||||
.platform = switch (options.target.os.tag) {
|
||||
.macos => .MACOS,
|
||||
.ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
|
||||
.watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
|
||||
.tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
|
||||
else => unreachable,
|
||||
},
|
||||
.minos = platform_version,
|
||||
.sdk = sdk_version_value,
|
||||
.platform = platform.toApplePlatform(),
|
||||
.minos = platform.toAppleVersion(),
|
||||
.sdk = if (sdk_version) |ver| semanticVersionToAppleVersion(ver) else platform.toAppleVersion(),
|
||||
.ntools = 1,
|
||||
});
|
||||
try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
|
||||
@ -320,7 +302,160 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: an
|
||||
}
|
||||
}
|
||||
|
||||
fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion {
|
||||
pub const Platform = struct {
|
||||
os_tag: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
version: std.SemanticVersion,
|
||||
|
||||
/// Using Apple's ld64 as our blueprint, `min_version` as well as `sdk_version` are set to
|
||||
/// the extracted minimum platform version.
|
||||
pub fn fromLoadCommand(lc: macho.LoadCommandIterator.LoadCommand) Platform {
|
||||
switch (lc.cmd()) {
|
||||
.BUILD_VERSION => {
|
||||
const cmd = lc.cast(macho.build_version_command).?;
|
||||
return .{
|
||||
.os_tag = switch (cmd.platform) {
|
||||
.MACOS => .macos,
|
||||
.IOS, .IOSSIMULATOR => .ios,
|
||||
.TVOS, .TVOSSIMULATOR => .tvos,
|
||||
.WATCHOS, .WATCHOSSIMULATOR => .watchos,
|
||||
else => @panic("TODO"),
|
||||
},
|
||||
.abi = switch (cmd.platform) {
|
||||
.IOSSIMULATOR,
|
||||
.TVOSSIMULATOR,
|
||||
.WATCHOSSIMULATOR,
|
||||
=> .simulator,
|
||||
else => .none,
|
||||
},
|
||||
.version = appleVersionToSemanticVersion(cmd.minos),
|
||||
};
|
||||
},
|
||||
.VERSION_MIN_MACOSX,
|
||||
.VERSION_MIN_IPHONEOS,
|
||||
.VERSION_MIN_TVOS,
|
||||
.VERSION_MIN_WATCHOS,
|
||||
=> {
|
||||
const cmd = lc.cast(macho.version_min_command).?;
|
||||
return .{
|
||||
.os_tag = switch (lc.cmd()) {
|
||||
.VERSION_MIN_MACOSX => .macos,
|
||||
.VERSION_MIN_IPHONEOS => .ios,
|
||||
.VERSION_MIN_TVOS => .tvos,
|
||||
.VERSION_MIN_WATCHOS => .watchos,
|
||||
else => unreachable,
|
||||
},
|
||||
.abi = .none,
|
||||
.version = appleVersionToSemanticVersion(cmd.version),
|
||||
};
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fromTarget(target: std.Target) Platform {
|
||||
return .{
|
||||
.os_tag = target.os.tag,
|
||||
.abi = target.abi,
|
||||
.version = target.os.version_range.semver.min,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toAppleVersion(plat: Platform) u32 {
|
||||
return semanticVersionToAppleVersion(plat.version);
|
||||
}
|
||||
|
||||
pub fn toApplePlatform(plat: Platform) macho.PLATFORM {
|
||||
return switch (plat.os_tag) {
|
||||
.macos => .MACOS,
|
||||
.ios => if (plat.abi == .simulator) .IOSSIMULATOR else .IOS,
|
||||
.tvos => if (plat.abi == .simulator) .TVOSSIMULATOR else .TVOS,
|
||||
.watchos => if (plat.abi == .simulator) .WATCHOSSIMULATOR else .WATCHOS,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isBuildVersionCompatible(plat: Platform) bool {
|
||||
inline for (supported_platforms) |sup_plat| {
|
||||
if (sup_plat[0] == plat.os_tag and sup_plat[1] == plat.abi) {
|
||||
return sup_plat[2] <= plat.toAppleVersion();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn fmtTarget(plat: Platform, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(formatTarget) {
|
||||
return .{ .data = .{ .platform = plat, .cpu_arch = cpu_arch } };
|
||||
}
|
||||
|
||||
const FmtCtx = struct {
|
||||
platform: Platform,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
};
|
||||
|
||||
pub fn formatTarget(
|
||||
ctx: FmtCtx,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
try writer.print("{s}-{s}", .{ @tagName(ctx.cpu_arch), @tagName(ctx.platform.os_tag) });
|
||||
if (ctx.platform.abi != .none) {
|
||||
try writer.print("-{s}", .{@tagName(ctx.platform.abi)});
|
||||
}
|
||||
}
|
||||
|
||||
/// Caller owns the memory.
|
||||
pub fn allocPrintTarget(plat: Platform, gpa: Allocator, cpu_arch: std.Target.Cpu.Arch) error{OutOfMemory}![]u8 {
|
||||
var buffer = std.ArrayList(u8).init(gpa);
|
||||
defer buffer.deinit();
|
||||
try buffer.writer().print("{}", .{plat.fmtTarget(cpu_arch)});
|
||||
return buffer.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn eqlTarget(plat: Platform, other: Platform) bool {
|
||||
return plat.os_tag == other.os_tag and plat.abi == other.abi;
|
||||
}
|
||||
};
|
||||
|
||||
const SupportedPlatforms = struct {
|
||||
std.Target.Os.Tag,
|
||||
std.Target.Abi,
|
||||
u32, // Min platform version for which to emit LC_BUILD_VERSION
|
||||
u32, // Min supported platform version
|
||||
};
|
||||
|
||||
// Source: https://github.com/apple-oss-distributions/ld64/blob/59a99ab60399c5e6c49e6945a9e1049c42b71135/src/ld/PlatformSupport.cpp#L52
|
||||
// zig fmt: off
|
||||
const supported_platforms = [_]SupportedPlatforms{
|
||||
.{ .macos, .none, 0xA0E00, 0xA0800 },
|
||||
.{ .ios, .none, 0xC0000, 0x70000 },
|
||||
.{ .tvos, .none, 0xC0000, 0x70000 },
|
||||
.{ .watchos, .none, 0x50000, 0x20000 },
|
||||
.{ .ios, .simulator, 0xD0000, 0x80000 },
|
||||
.{ .tvos, .simulator, 0xD0000, 0x80000 },
|
||||
.{ .watchos, .simulator, 0x60000, 0x20000 },
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
inline fn semanticVersionToAppleVersion(version: std.SemanticVersion) u32 {
|
||||
const major = version.major;
|
||||
const minor = version.minor;
|
||||
const patch = version.patch;
|
||||
return (@as(u32, @intCast(major)) << 16) | (@as(u32, @intCast(minor)) << 8) | @as(u32, @intCast(patch));
|
||||
}
|
||||
|
||||
pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion {
|
||||
return .{
|
||||
.major = @as(u16, @truncate(version >> 16)),
|
||||
.minor = @as(u8, @truncate(version >> 8)),
|
||||
.patch = @as(u8, @truncate(version)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion {
|
||||
const stem = std.fs.path.stem(path);
|
||||
const start = for (stem, 0..) |c, i| {
|
||||
if (std.ascii.isDigit(c)) break i;
|
||||
@ -374,3 +509,14 @@ test "parseSdkVersion" {
|
||||
|
||||
try expect(parseSdkVersion("11") == null);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const link = @import("../../link.zig");
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Dylib = @import("Dylib.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
const std = @import("std");
|
||||
const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
|
||||
const Relocation = @import("Relocation.zig");
|
||||
|
||||
pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
|
||||
pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 15,
|
||||
.aarch64 => 6 * @sizeOf(u32),
|
||||
@ -11,7 +6,7 @@ pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
pub inline fn stubHelperSize(cpu_arch: std.Target.Cpu.Arch) u8 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
@ -19,7 +14,7 @@ pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
pub inline fn stubSize(cpu_arch: std.Target.Cpu.Arch) u8 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 6,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
@ -27,7 +22,15 @@ pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn calcStubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
pub inline fn stubAlignment(cpu_arch: std.Target.Cpu.Arch) u8 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 1,
|
||||
.aarch64 => 4,
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn stubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u8 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 1,
|
||||
.aarch64 => 2 * @sizeOf(u32),
|
||||
@ -159,3 +162,8 @@ pub fn writeStubCode(args: struct {
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
|
||||
const Relocation = @import("Relocation.zig");
|
||||
|
||||
@ -5,24 +5,6 @@
|
||||
//! The algorithm works pessimistically and assumes that any reference to an Atom in
|
||||
//! another output section is out of range.
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.thunks);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
pub const ThunkIndex = u32;
|
||||
|
||||
/// Branch instruction has 26 bits immediate but 4 byte aligned.
|
||||
const jump_bits = @bitSizeOf(i28);
|
||||
|
||||
@ -35,21 +17,35 @@ const max_distance = (1 << (jump_bits - 1));
|
||||
const max_allowed_distance = max_distance - 0x500_000;
|
||||
|
||||
pub const Thunk = struct {
|
||||
start_index: AtomIndex,
|
||||
start_index: Atom.Index,
|
||||
len: u32,
|
||||
|
||||
lookup: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, AtomIndex) = .{},
|
||||
targets: std.MultiArrayList(Target) = .{},
|
||||
lookup: std.AutoHashMapUnmanaged(Target, u32) = .{},
|
||||
|
||||
pub const Tag = enum {
|
||||
stub,
|
||||
atom,
|
||||
};
|
||||
|
||||
pub const Target = struct {
|
||||
tag: Tag,
|
||||
target: SymbolWithLoc,
|
||||
};
|
||||
|
||||
pub const Index = u32;
|
||||
|
||||
pub fn deinit(self: *Thunk, gpa: Allocator) void {
|
||||
self.targets.deinit(gpa);
|
||||
self.lookup.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn getStartAtomIndex(self: Thunk) AtomIndex {
|
||||
pub fn getStartAtomIndex(self: Thunk) Atom.Index {
|
||||
assert(self.len != 0);
|
||||
return self.start_index;
|
||||
}
|
||||
|
||||
pub fn getEndAtomIndex(self: Thunk) AtomIndex {
|
||||
pub fn getEndAtomIndex(self: Thunk) Atom.Index {
|
||||
assert(self.len != 0);
|
||||
return self.start_index + self.len - 1;
|
||||
}
|
||||
@ -62,19 +58,18 @@ pub const Thunk = struct {
|
||||
return @alignOf(u32);
|
||||
}
|
||||
|
||||
pub fn getTrampolineForSymbol(self: Thunk, zld: *Zld, target: SymbolWithLoc) ?SymbolWithLoc {
|
||||
const atom_index = self.lookup.get(target) orelse return null;
|
||||
const atom = zld.getAtom(atom_index);
|
||||
return atom.getSymbolWithLoc();
|
||||
pub fn getTrampoline(self: Thunk, macho_file: *MachO, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
|
||||
const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null;
|
||||
return macho_file.getAtom(atom_index).getSymbolWithLoc();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
const header = &zld.sections.items(.header)[sect_id];
|
||||
pub fn createThunks(macho_file: *MachO, sect_id: u8) !void {
|
||||
const header = &macho_file.sections.items(.header)[sect_id];
|
||||
if (header.size == 0) return;
|
||||
|
||||
const gpa = zld.gpa;
|
||||
const first_atom_index = zld.sections.items(.first_atom_index)[sect_id];
|
||||
const gpa = macho_file.base.allocator;
|
||||
const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?;
|
||||
|
||||
header.size = 0;
|
||||
header.@"align" = 0;
|
||||
@ -84,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
{
|
||||
var atom_index = first_atom_index;
|
||||
while (true) {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
|
||||
sym.n_value = 0;
|
||||
atom_count += 1;
|
||||
|
||||
@ -95,7 +90,7 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
}
|
||||
}
|
||||
|
||||
var allocated = std.AutoHashMap(AtomIndex, void).init(gpa);
|
||||
var allocated = std.AutoHashMap(Atom.Index, void).init(gpa);
|
||||
defer allocated.deinit();
|
||||
try allocated.ensureTotalCapacity(atom_count);
|
||||
|
||||
@ -104,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
var offset: u64 = 0;
|
||||
|
||||
while (true) {
|
||||
const group_start_atom = zld.getAtom(group_start);
|
||||
const group_start_atom = macho_file.getAtom(group_start);
|
||||
log.debug("GROUP START at {d}", .{group_start});
|
||||
|
||||
while (true) {
|
||||
const atom = zld.getAtom(group_end);
|
||||
const atom = macho_file.getAtom(group_end);
|
||||
offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment));
|
||||
|
||||
const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
|
||||
const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
|
||||
sym.n_value = offset;
|
||||
offset += atom.size;
|
||||
|
||||
zld.logAtom(group_end, log);
|
||||
macho_file.logAtom(group_end, log);
|
||||
|
||||
header.@"align" = @max(header.@"align", atom.alignment);
|
||||
|
||||
allocated.putAssumeCapacityNoClobber(group_end, {});
|
||||
|
||||
const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc());
|
||||
const group_start_sym = macho_file.getSymbol(group_start_atom.getSymbolWithLoc());
|
||||
if (offset - group_start_sym.n_value >= max_allowed_distance) break;
|
||||
|
||||
if (atom.next_index) |next_index| {
|
||||
@ -131,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
log.debug("GROUP END at {d}", .{group_end});
|
||||
|
||||
// Insert thunk at group_end
|
||||
const thunk_index = @as(u32, @intCast(zld.thunks.items.len));
|
||||
try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
|
||||
const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len));
|
||||
try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
|
||||
|
||||
// Scan relocs in the group and create trampolines for any unreachable callsite.
|
||||
var atom_index = group_start;
|
||||
while (true) {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
try scanRelocs(
|
||||
zld,
|
||||
macho_file,
|
||||
atom_index,
|
||||
allocated,
|
||||
thunk_index,
|
||||
@ -154,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
}
|
||||
|
||||
offset = mem.alignForward(u64, offset, Thunk.getAlignment());
|
||||
allocateThunk(zld, thunk_index, offset, header);
|
||||
offset += zld.thunks.items[thunk_index].getSize();
|
||||
allocateThunk(macho_file, thunk_index, offset, header);
|
||||
offset += macho_file.thunks.items[thunk_index].getSize();
|
||||
|
||||
const thunk = zld.thunks.items[thunk_index];
|
||||
const thunk = macho_file.thunks.items[thunk_index];
|
||||
if (thunk.len == 0) {
|
||||
const group_end_atom = zld.getAtom(group_end);
|
||||
const group_end_atom = macho_file.getAtom(group_end);
|
||||
if (group_end_atom.next_index) |next_index| {
|
||||
group_start = next_index;
|
||||
group_end = next_index;
|
||||
} else break;
|
||||
} else {
|
||||
const thunk_end_atom_index = thunk.getEndAtomIndex();
|
||||
const thunk_end_atom = zld.getAtom(thunk_end_atom_index);
|
||||
const thunk_end_atom = macho_file.getAtom(thunk_end_atom_index);
|
||||
if (thunk_end_atom.next_index) |next_index| {
|
||||
group_start = next_index;
|
||||
group_end = next_index;
|
||||
@ -178,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
|
||||
}
|
||||
|
||||
fn allocateThunk(
|
||||
zld: *Zld,
|
||||
thunk_index: ThunkIndex,
|
||||
macho_file: *MachO,
|
||||
thunk_index: Thunk.Index,
|
||||
base_offset: u64,
|
||||
header: *macho.section_64,
|
||||
) void {
|
||||
const thunk = zld.thunks.items[thunk_index];
|
||||
const thunk = macho_file.thunks.items[thunk_index];
|
||||
if (thunk.len == 0) return;
|
||||
|
||||
const first_atom_index = thunk.getStartAtomIndex();
|
||||
@ -192,14 +187,14 @@ fn allocateThunk(
|
||||
var atom_index = first_atom_index;
|
||||
var offset = base_offset;
|
||||
while (true) {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
offset = mem.alignForward(u64, offset, Thunk.getAlignment());
|
||||
|
||||
const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
|
||||
const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
|
||||
sym.n_value = offset;
|
||||
offset += atom.size;
|
||||
|
||||
zld.logAtom(atom_index, log);
|
||||
macho_file.logAtom(atom_index, log);
|
||||
|
||||
header.@"align" = @max(header.@"align", atom.alignment);
|
||||
|
||||
@ -212,162 +207,163 @@ fn allocateThunk(
|
||||
}
|
||||
|
||||
fn scanRelocs(
|
||||
zld: *Zld,
|
||||
atom_index: AtomIndex,
|
||||
allocated: std.AutoHashMap(AtomIndex, void),
|
||||
thunk_index: ThunkIndex,
|
||||
group_end: AtomIndex,
|
||||
macho_file: *MachO,
|
||||
atom_index: Atom.Index,
|
||||
allocated: std.AutoHashMap(Atom.Index, void),
|
||||
thunk_index: Thunk.Index,
|
||||
group_end: Atom.Index,
|
||||
) !void {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const object = zld.objects.items[atom.getFile().?];
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
const object = macho_file.objects.items[atom.getFile().?];
|
||||
|
||||
const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
|
||||
const source_sect = object.getSourceSection(source_sym.n_sect - 1);
|
||||
break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));
|
||||
} else 0;
|
||||
|
||||
const code = Atom.getAtomCode(zld, atom_index);
|
||||
const relocs = Atom.getAtomRelocs(zld, atom_index);
|
||||
const ctx = Atom.getRelocContext(zld, atom_index);
|
||||
const code = Atom.getAtomCode(macho_file, atom_index);
|
||||
const relocs = Atom.getAtomRelocs(macho_file, atom_index);
|
||||
const ctx = Atom.getRelocContext(macho_file, atom_index);
|
||||
|
||||
for (relocs) |rel| {
|
||||
if (!relocNeedsThunk(rel)) continue;
|
||||
|
||||
const target = Atom.parseRelocTarget(zld, .{
|
||||
const target = Atom.parseRelocTarget(macho_file, .{
|
||||
.object_id = atom.getFile().?,
|
||||
.rel = rel,
|
||||
.code = code,
|
||||
.base_offset = ctx.base_offset,
|
||||
.base_addr = ctx.base_addr,
|
||||
});
|
||||
if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
|
||||
if (isReachable(macho_file, atom_index, rel, base_offset, target, allocated)) continue;
|
||||
|
||||
log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
|
||||
rel.r_address - base_offset,
|
||||
zld.getSymbolName(atom.getSymbolWithLoc()),
|
||||
zld.getSymbol(atom.getSymbolWithLoc()).n_value,
|
||||
zld.getSymbolName(target),
|
||||
zld.getSymbol(target).n_value,
|
||||
macho_file.getSymbolName(atom.getSymbolWithLoc()),
|
||||
macho_file.getSymbol(atom.getSymbolWithLoc()).n_value,
|
||||
macho_file.getSymbolName(target),
|
||||
macho_file.getSymbol(target).n_value,
|
||||
});
|
||||
|
||||
const gpa = zld.gpa;
|
||||
const target_sym = zld.getSymbol(target);
|
||||
const gpa = macho_file.base.allocator;
|
||||
const target_sym = macho_file.getSymbol(target);
|
||||
const thunk = &macho_file.thunks.items[thunk_index];
|
||||
|
||||
const actual_target: SymbolWithLoc = if (target_sym.undf()) blk: {
|
||||
const stub_atom_index = zld.getStubsAtomIndexForSymbol(target).?;
|
||||
break :blk .{ .sym_index = zld.getAtom(stub_atom_index).sym_index };
|
||||
} else target;
|
||||
|
||||
const thunk = &zld.thunks.items[thunk_index];
|
||||
const gop = try thunk.lookup.getOrPut(gpa, actual_target);
|
||||
const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;
|
||||
const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };
|
||||
const gop = try thunk.lookup.getOrPut(gpa, thunk_target);
|
||||
if (!gop.found_existing) {
|
||||
const thunk_atom_index = try createThunkAtom(zld);
|
||||
gop.value_ptr.* = thunk_atom_index;
|
||||
|
||||
const thunk_atom = zld.getAtomPtr(thunk_atom_index);
|
||||
const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
|
||||
const end_atom = zld.getAtomPtr(end_atom_index);
|
||||
|
||||
if (end_atom.next_index) |first_after_index| {
|
||||
const first_after_atom = zld.getAtomPtr(first_after_index);
|
||||
first_after_atom.prev_index = thunk_atom_index;
|
||||
thunk_atom.next_index = first_after_index;
|
||||
}
|
||||
|
||||
end_atom.next_index = thunk_atom_index;
|
||||
thunk_atom.prev_index = end_atom_index;
|
||||
|
||||
if (thunk.len == 0) {
|
||||
thunk.start_index = thunk_atom_index;
|
||||
}
|
||||
|
||||
thunk.len += 1;
|
||||
gop.value_ptr.* = try pushThunkAtom(macho_file, thunk, group_end);
|
||||
try thunk.targets.append(gpa, thunk_target);
|
||||
}
|
||||
|
||||
try zld.thunk_table.put(gpa, atom_index, thunk_index);
|
||||
try macho_file.thunk_table.put(gpa, atom_index, thunk_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
|
||||
const thunk_atom_index = try createThunkAtom(macho_file);
|
||||
|
||||
const thunk_atom = macho_file.getAtomPtr(thunk_atom_index);
|
||||
const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
|
||||
const end_atom = macho_file.getAtomPtr(end_atom_index);
|
||||
|
||||
if (end_atom.next_index) |first_after_index| {
|
||||
const first_after_atom = macho_file.getAtomPtr(first_after_index);
|
||||
first_after_atom.prev_index = thunk_atom_index;
|
||||
thunk_atom.next_index = first_after_index;
|
||||
}
|
||||
|
||||
end_atom.next_index = thunk_atom_index;
|
||||
thunk_atom.prev_index = end_atom_index;
|
||||
|
||||
if (thunk.len == 0) {
|
||||
thunk.start_index = thunk_atom_index;
|
||||
}
|
||||
|
||||
thunk.len += 1;
|
||||
|
||||
return thunk_atom_index;
|
||||
}
|
||||
|
||||
inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
|
||||
const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
|
||||
return rel_type == .ARM64_RELOC_BRANCH26;
|
||||
}
|
||||
|
||||
fn isReachable(
|
||||
zld: *Zld,
|
||||
atom_index: AtomIndex,
|
||||
macho_file: *MachO,
|
||||
atom_index: Atom.Index,
|
||||
rel: macho.relocation_info,
|
||||
base_offset: i32,
|
||||
target: SymbolWithLoc,
|
||||
allocated: std.AutoHashMap(AtomIndex, void),
|
||||
allocated: std.AutoHashMap(Atom.Index, void),
|
||||
) bool {
|
||||
if (zld.getStubsAtomIndexForSymbol(target)) |_| return false;
|
||||
if (macho_file.stub_table.lookup.contains(target)) return false;
|
||||
|
||||
const source_atom = zld.getAtom(atom_index);
|
||||
const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());
|
||||
const source_atom = macho_file.getAtom(atom_index);
|
||||
const source_sym = macho_file.getSymbol(source_atom.getSymbolWithLoc());
|
||||
|
||||
const target_object = zld.objects.items[target.getFile().?];
|
||||
const target_object = macho_file.objects.items[target.getFile().?];
|
||||
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
|
||||
const target_atom = zld.getAtom(target_atom_index);
|
||||
const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
|
||||
const target_atom = macho_file.getAtom(target_atom_index);
|
||||
const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
|
||||
|
||||
if (source_sym.n_sect != target_sym.n_sect) return false;
|
||||
|
||||
if (!allocated.contains(target_atom_index)) return false;
|
||||
|
||||
const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));
|
||||
const is_via_got = Atom.relocRequiresGot(zld, rel);
|
||||
const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
|
||||
const target_addr = if (Atom.relocRequiresGot(macho_file, rel))
|
||||
macho_file.getGotEntryAddress(target).?
|
||||
else
|
||||
Atom.getRelocTargetAddress(macho_file, target, false);
|
||||
_ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn createThunkAtom(zld: *Zld) !AtomIndex {
|
||||
const sym_index = try zld.allocateSymbol();
|
||||
const atom_index = try zld.createEmptyAtom(sym_index, @sizeOf(u32) * 3, 2);
|
||||
const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });
|
||||
fn createThunkAtom(macho_file: *MachO) !Atom.Index {
|
||||
const sym_index = try macho_file.allocateSymbol();
|
||||
const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
|
||||
const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index });
|
||||
sym.n_type = macho.N_SECT;
|
||||
|
||||
const sect_id = zld.getSectionByName("__TEXT", "__text") orelse unreachable;
|
||||
sym.n_sect = sect_id + 1;
|
||||
|
||||
sym.n_sect = macho_file.text_section_index.? + 1;
|
||||
return atom_index;
|
||||
}
|
||||
|
||||
fn getThunkIndex(zld: *Zld, atom_index: AtomIndex) ?ThunkIndex {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const sym = zld.getSymbol(atom.getSymbolWithLoc());
|
||||
for (zld.thunks.items, 0..) |thunk, i| {
|
||||
if (thunk.len == 0) continue;
|
||||
|
||||
const thunk_atom_index = thunk.getStartAtomIndex();
|
||||
const thunk_atom = zld.getAtom(thunk_atom_index);
|
||||
const thunk_sym = zld.getSymbol(thunk_atom.getSymbolWithLoc());
|
||||
const start_addr = thunk_sym.n_value;
|
||||
const end_addr = start_addr + thunk.getSize();
|
||||
|
||||
if (start_addr <= sym.n_value and sym.n_value < end_addr) {
|
||||
return @as(u32, @intCast(i));
|
||||
}
|
||||
pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void {
|
||||
const slice = thunk.targets.slice();
|
||||
for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {
|
||||
const atom = macho_file.getAtom(@intCast(atom_index));
|
||||
const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
|
||||
const source_addr = sym.n_value;
|
||||
const tag = slice.items(.tag)[target_index];
|
||||
const target = slice.items(.target)[target_index];
|
||||
const target_addr = switch (tag) {
|
||||
.stub => macho_file.getStubsEntryAddress(target).?,
|
||||
.atom => macho_file.getSymbol(target).n_value,
|
||||
};
|
||||
const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
const sym = zld.getSymbol(atom.getSymbolWithLoc());
|
||||
const source_addr = sym.n_value;
|
||||
const thunk = zld.thunks.items[getThunkIndex(zld, atom_index).?];
|
||||
const target_addr = for (thunk.lookup.keys()) |target| {
|
||||
const target_atom_index = thunk.lookup.get(target).?;
|
||||
if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
|
||||
} else unreachable;
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.thunks);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
|
||||
const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
}
|
||||
const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = MachO.SymbolWithLoc;
|
||||
|
||||
@ -1,12 +1,3 @@
|
||||
const std = @import("std");
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Compilation = @import("../../Compilation.zig");
|
||||
const Md5 = std.crypto.hash.Md5;
|
||||
const Hasher = @import("hasher.zig").ParallelHasher;
|
||||
|
||||
/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce
|
||||
/// the final digest.
|
||||
/// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD
|
||||
@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
|
||||
out[6] = (out[6] & 0x0F) | (3 << 4);
|
||||
out[8] = (out[8] & 0x3F) | 0x80;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Compilation = @import("../../Compilation.zig");
|
||||
const Md5 = std.crypto.hash.Md5;
|
||||
const Hasher = @import("hasher.zig").ParallelHasher;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,9 +2,10 @@ const std = @import("std");
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
const log = std.log.scoped(.tapi);
|
||||
const yaml = @import("tapi/yaml.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Yaml = @import("tapi/yaml.zig").Yaml;
|
||||
const Yaml = yaml.Yaml;
|
||||
|
||||
const VersionField = union(enum) {
|
||||
string: []const u8,
|
||||
@ -80,6 +81,30 @@ pub const Tbd = union(enum) {
|
||||
v3: TbdV3,
|
||||
v4: TbdV4,
|
||||
|
||||
/// Caller owns memory.
|
||||
pub fn targets(self: Tbd, gpa: Allocator) error{OutOfMemory}![]const []const u8 {
|
||||
var out = std.ArrayList([]const u8).init(gpa);
|
||||
defer out.deinit();
|
||||
|
||||
switch (self) {
|
||||
.v3 => |v3| {
|
||||
try out.ensureTotalCapacityPrecise(v3.archs.len);
|
||||
for (v3.archs) |arch| {
|
||||
const target = try std.fmt.allocPrint(gpa, "{s}-{s}", .{ arch, v3.platform });
|
||||
out.appendAssumeCapacity(target);
|
||||
}
|
||||
},
|
||||
.v4 => |v4| {
|
||||
try out.ensureTotalCapacityPrecise(v4.targets.len);
|
||||
for (v4.targets) |t| {
|
||||
out.appendAssumeCapacity(try gpa.dupe(u8, t));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return out.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn currentVersion(self: Tbd) ?VersionField {
|
||||
return switch (self) {
|
||||
.v3 => |v3| v3.current_version,
|
||||
@ -102,6 +127,11 @@ pub const Tbd = union(enum) {
|
||||
}
|
||||
};
|
||||
|
||||
pub const TapiError = error{
|
||||
NotLibStub,
|
||||
FileTooBig,
|
||||
} || yaml.YamlError || std.fs.File.ReadError;
|
||||
|
||||
pub const LibStub = struct {
|
||||
/// Underlying memory for stub's contents.
|
||||
yaml: Yaml,
|
||||
@ -109,7 +139,7 @@ pub const LibStub = struct {
|
||||
/// Typed contents of the tbd file.
|
||||
inner: []Tbd,
|
||||
|
||||
pub fn loadFromFile(allocator: Allocator, file: fs.File) !LibStub {
|
||||
pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub {
|
||||
const source = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
|
||||
defer allocator.free(source);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user