Merge pull request #10338 from ziglang/macho-missing-feats

macho: refactor consts in std.macho, and fix two bugs in MachO linker backend
This commit is contained in:
Jakub Konka 2021-12-15 15:54:48 +01:00 committed by GitHub
commit 87b843ef08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 408 additions and 207 deletions

View File

@ -843,7 +843,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
const symtab = while (ncmd != 0) : (ncmd -= 1) {
const lc = @ptrCast(*const std.macho.load_command, ptr);
switch (lc.cmd) {
std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr),
.SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr),
else => {},
}
ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
@ -1072,7 +1072,7 @@ pub const DebugInfo = struct {
@alignCast(@alignOf(macho.load_command), cmd_ptr),
);
cmd_ptr += lc.cmdsize;
if (lc.cmd != macho.LC_SEGMENT_64) continue;
if (lc.cmd != .SEGMENT_64) continue;
const segment_cmd = @ptrCast(
*const std.macho.segment_command_64,
@ -1303,14 +1303,14 @@ pub const ModuleDebugInfo = switch (native_os) {
while (ncmd != 0) : (ncmd -= 1) {
const lc = @ptrCast(*const std.macho.load_command, ptr);
switch (lc.cmd) {
std.macho.LC_SEGMENT_64 => {
.SEGMENT_64 => {
segcmd = @ptrCast(
*const std.macho.segment_command_64,
@alignCast(@alignOf(std.macho.segment_command_64), ptr),
);
segptr = ptr;
},
std.macho.LC_SYMTAB => {
.SYMTAB => {
symtabcmd = @ptrCast(
*const std.macho.symtab_command,
@alignCast(@alignOf(std.macho.symtab_command), ptr),

View File

@ -43,7 +43,7 @@ pub const fat_arch = extern struct {
};
pub const load_command = extern struct {
cmd: u32,
cmd: LC,
cmdsize: u32,
};
@ -51,7 +51,7 @@ pub const load_command = extern struct {
/// identifies an object produced by the static link editor.
pub const uuid_command = extern struct {
/// LC_UUID
cmd: u32,
cmd: LC = .UUID,
/// sizeof(struct uuid_command)
cmdsize: u32,
@ -64,7 +64,7 @@ pub const uuid_command = extern struct {
/// binary was built to run.
pub const version_min_command = extern struct {
/// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS or LC_VERSION_MIN_WATCHOS or LC_VERSION_MIN_TVOS
cmd: u32,
cmd: LC,
/// sizeof(struct version_min_command)
cmdsize: u32,
@ -80,7 +80,7 @@ pub const version_min_command = extern struct {
/// the version of the sources used to build the binary.
pub const source_version_command = extern struct {
/// LC_SOURCE_VERSION
cmd: u32,
cmd: LC = .SOURCE_VERSION,
/// sizeof(source_version_command)
cmdsize: u32,
@ -94,14 +94,14 @@ pub const source_version_command = extern struct {
/// tool values following it.
pub const build_version_command = extern struct {
/// LC_BUILD_VERSION
cmd: u32,
cmd: LC = .BUILD_VERSION,
/// sizeof(struct build_version_command) plus
/// ntools * sizeof(struct build_version_command)
cmdsize: u32,
/// platform
platform: u32,
platform: PLATFORM,
/// X.Y.Z is encoded in nibbles xxxx.yy.zz
minos: u32,
@ -115,26 +115,32 @@ pub const build_version_command = extern struct {
pub const build_tool_version = extern struct {
/// enum for the tool
tool: u32,
tool: TOOL,
/// version number of the tool
version: u32,
};
pub const PLATFORM_MACOS: u32 = 0x1;
pub const PLATFORM_IOS: u32 = 0x2;
pub const PLATFORM_TVOS: u32 = 0x3;
pub const PLATFORM_WATCHOS: u32 = 0x4;
pub const PLATFORM_BRIDGEOS: u32 = 0x5;
pub const PLATFORM_MACCATALYST: u32 = 0x6;
pub const PLATFORM_IOSSIMULATOR: u32 = 0x7;
pub const PLATFORM_TVOSSIMULATOR: u32 = 0x8;
pub const PLATFORM_WATCHOSSIMULATOR: u32 = 0x9;
pub const PLATFORM_DRIVERKIT: u32 = 0x10;
pub const PLATFORM = enum(u32) {
MACOS = 0x1,
IOS = 0x2,
TVOS = 0x3,
WATCHOS = 0x4,
BRIDGEOS = 0x5,
MACCATALYST = 0x6,
IOSSIMULATOR = 0x7,
TVOSSIMULATOR = 0x8,
WATCHOSSIMULATOR = 0x9,
DRIVERKIT = 0x10,
_,
};
pub const TOOL_CLANG: u32 = 0x1;
pub const TOOL_SWIFT: u32 = 0x2;
pub const TOOL_LD: u32 = 0x3;
pub const TOOL = enum(u32) {
CLANG = 0x1,
SWIFT = 0x2,
LD = 0x3,
_,
};
/// The entry_point_command is a replacement for thread_command.
/// It is used for main executables to specify the location (file offset)
@ -142,7 +148,7 @@ pub const TOOL_LD: u32 = 0x3;
/// field will contain the stack size needed for the main thread.
pub const entry_point_command = extern struct {
/// LC_MAIN only used in MH_EXECUTE filetypes
cmd: u32,
cmd: LC = .MAIN,
/// sizeof(struct entry_point_command)
cmdsize: u32,
@ -159,7 +165,7 @@ pub const entry_point_command = extern struct {
/// <nlist.h> and <stab.h>.
pub const symtab_command = extern struct {
/// LC_SYMTAB
cmd: u32,
cmd: LC = .SYMTAB,
/// sizeof(struct symtab_command)
cmdsize: u32,
@ -217,7 +223,7 @@ pub const symtab_command = extern struct {
/// off the section structures.
pub const dysymtab_command = extern struct {
/// LC_DYSYMTAB
cmd: u32,
cmd: LC = .DYSYMTAB,
/// sizeof(struct dysymtab_command)
cmdsize: u32,
@ -357,7 +363,7 @@ pub const dysymtab_command = extern struct {
/// of data in the __LINKEDIT segment.
pub const linkedit_data_command = extern struct {
/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
cmd: u32,
cmd: LC,
/// sizeof(struct linkedit_data_command)
cmdsize: u32,
@ -377,7 +383,7 @@ pub const linkedit_data_command = extern struct {
/// to interpret it.
pub const dyld_info_command = extern struct {
/// LC_DYLD_INFO or LC_DYLD_INFO_ONLY
cmd: u32,
cmd: LC,
/// sizeof(struct dyld_info_command)
cmdsize: u32,
@ -498,7 +504,7 @@ pub const dyld_info_command = extern struct {
/// string for dyld to treat like an environment variable.
pub const dylinker_command = extern struct {
/// LC_ID_DYLINKER, LC_LOAD_DYLINKER, or LC_DYLD_ENVIRONMENT
cmd: u32,
cmd: LC,
/// includes pathname string
cmdsize: u32,
@ -519,7 +525,7 @@ pub const dylinker_command = extern struct {
/// LC_REEXPORT_DYLIB) for each library it uses.
pub const dylib_command = extern struct {
/// LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB
cmd: u32,
cmd: LC,
/// includes pathname string
cmdsize: u32,
@ -553,7 +559,7 @@ pub const dylib = extern struct {
/// run path used to find @rpath prefixed dylibs.
pub const rpath_command = extern struct {
/// LC_RPATH
cmd: u32,
cmd: LC = .RPATH,
/// includes string
cmdsize: u32,
@ -574,7 +580,7 @@ pub const rpath_command = extern struct {
/// reflected in cmdsize.
pub const segment_command = extern struct {
/// LC_SEGMENT
cmd: u32,
cmd: LC = .SEGMENT,
/// includes sizeof section structs
cmdsize: u32,
@ -611,7 +617,7 @@ pub const segment_command = extern struct {
/// command and their size is reflected in cmdsize.
pub const segment_command_64 = extern struct {
/// LC_SEGMENT_64
cmd: u32 = LC_SEGMENT_64,
cmd: LC = .SEGMENT_64,
/// includes sizeof section_64 structs
cmdsize: u32 = @sizeOf(segment_command_64),
@ -882,159 +888,163 @@ pub const relocation_info = packed struct {
/// simply be ignored.
pub const LC_REQ_DYLD = 0x80000000;
/// segment of this file to be mapped
pub const LC_SEGMENT = 0x1;
pub const LC = enum(u32) {
/// segment of this file to be mapped
SEGMENT = 0x1,
/// link-edit stab symbol table info
pub const LC_SYMTAB = 0x2;
/// link-edit stab symbol table info
SYMTAB = 0x2,
/// link-edit gdb symbol table info (obsolete)
pub const LC_SYMSEG = 0x3;
/// link-edit gdb symbol table info (obsolete)
SYMSEG = 0x3,
/// thread
pub const LC_THREAD = 0x4;
/// thread
THREAD = 0x4,
/// unix thread (includes a stack)
pub const LC_UNIXTHREAD = 0x5;
/// unix thread (includes a stack)
UNIXTHREAD = 0x5,
/// load a specified fixed VM shared library
pub const LC_LOADFVMLIB = 0x6;
/// load a specified fixed VM shared library
LOADFVMLIB = 0x6,
/// fixed VM shared library identification
pub const LC_IDFVMLIB = 0x7;
/// fixed VM shared library identification
IDFVMLIB = 0x7,
/// object identification info (obsolete)
pub const LC_IDENT = 0x8;
/// object identification info (obsolete)
IDENT = 0x8,
/// fixed VM file inclusion (internal use)
pub const LC_FVMFILE = 0x9;
/// fixed VM file inclusion (internal use)
FVMFILE = 0x9,
/// prepage command (internal use)
pub const LC_PREPAGE = 0xa;
/// prepage command (internal use)
PREPAGE = 0xa,
/// dynamic link-edit symbol table info
pub const LC_DYSYMTAB = 0xb;
/// dynamic link-edit symbol table info
DYSYMTAB = 0xb,
/// load a dynamically linked shared library
pub const LC_LOAD_DYLIB = 0xc;
/// load a dynamically linked shared library
LOAD_DYLIB = 0xc,
/// dynamically linked shared lib ident
pub const LC_ID_DYLIB = 0xd;
/// dynamically linked shared lib ident
ID_DYLIB = 0xd,
/// load a dynamic linker
pub const LC_LOAD_DYLINKER = 0xe;
/// load a dynamic linker
LOAD_DYLINKER = 0xe,
/// dynamic linker identification
pub const LC_ID_DYLINKER = 0xf;
/// dynamic linker identification
ID_DYLINKER = 0xf,
/// modules prebound for a dynamically
pub const LC_PREBOUND_DYLIB = 0x10;
/// modules prebound for a dynamically
PREBOUND_DYLIB = 0x10,
/// image routines
pub const LC_ROUTINES = 0x11;
/// image routines
ROUTINES = 0x11,
/// sub framework
pub const LC_SUB_FRAMEWORK = 0x12;
/// sub framework
SUB_FRAMEWORK = 0x12,
/// sub umbrella
pub const LC_SUB_UMBRELLA = 0x13;
/// sub umbrella
SUB_UMBRELLA = 0x13,
/// sub client
pub const LC_SUB_CLIENT = 0x14;
/// sub client
SUB_CLIENT = 0x14,
/// sub library
pub const LC_SUB_LIBRARY = 0x15;
/// sub library
SUB_LIBRARY = 0x15,
/// two-level namespace lookup hints
pub const LC_TWOLEVEL_HINTS = 0x16;
/// two-level namespace lookup hints
TWOLEVEL_HINTS = 0x16,
/// prebind checksum
pub const LC_PREBIND_CKSUM = 0x17;
/// prebind checksum
PREBIND_CKSUM = 0x17,
/// load a dynamically linked shared library that is allowed to be missing
/// (all symbols are weak imported).
pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
/// load a dynamically linked shared library that is allowed to be missing
/// (all symbols are weak imported).
LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD),
/// 64-bit segment of this file to be mapped
pub const LC_SEGMENT_64 = 0x19;
/// 64-bit segment of this file to be mapped
SEGMENT_64 = 0x19,
/// 64-bit image routines
pub const LC_ROUTINES_64 = 0x1a;
/// 64-bit image routines
ROUTINES_64 = 0x1a,
/// the uuid
pub const LC_UUID = 0x1b;
/// the uuid
UUID = 0x1b,
/// runpath additions
pub const LC_RPATH = (0x1c | LC_REQ_DYLD);
/// runpath additions
RPATH = (0x1c | LC_REQ_DYLD),
/// local of code signature
pub const LC_CODE_SIGNATURE = 0x1d;
/// local of code signature
CODE_SIGNATURE = 0x1d,
/// local of info to split segments
pub const LC_SEGMENT_SPLIT_INFO = 0x1e;
/// local of info to split segments
SEGMENT_SPLIT_INFO = 0x1e,
/// load and re-export dylib
pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD);
/// load and re-export dylib
REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD),
/// delay load of dylib until first use
pub const LC_LAZY_LOAD_DYLIB = 0x20;
/// delay load of dylib until first use
LAZY_LOAD_DYLIB = 0x20,
/// encrypted segment information
pub const LC_ENCRYPTION_INFO = 0x21;
/// encrypted segment information
ENCRYPTION_INFO = 0x21,
/// compressed dyld information
pub const LC_DYLD_INFO = 0x22;
/// compressed dyld information
DYLD_INFO = 0x22,
/// compressed dyld information only
pub const LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD);
/// compressed dyld information only
DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD),
/// load upward dylib
pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD);
/// load upward dylib
LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD),
/// build for MacOSX min OS version
pub const LC_VERSION_MIN_MACOSX = 0x24;
/// build for MacOSX min OS version
VERSION_MIN_MACOSX = 0x24,
/// build for iPhoneOS min OS version
pub const LC_VERSION_MIN_IPHONEOS = 0x25;
/// build for iPhoneOS min OS version
VERSION_MIN_IPHONEOS = 0x25,
/// compressed table of function start addresses
pub const LC_FUNCTION_STARTS = 0x26;
/// compressed table of function start addresses
FUNCTION_STARTS = 0x26,
/// string for dyld to treat like environment variable
pub const LC_DYLD_ENVIRONMENT = 0x27;
/// string for dyld to treat like environment variable
DYLD_ENVIRONMENT = 0x27,
/// replacement for LC_UNIXTHREAD
pub const LC_MAIN = (0x28 | LC_REQ_DYLD);
/// replacement for LC_UNIXTHREAD
MAIN = (0x28 | LC_REQ_DYLD),
/// table of non-instructions in __text
pub const LC_DATA_IN_CODE = 0x29;
/// table of non-instructions in __text
DATA_IN_CODE = 0x29,
/// source version used to build binary
pub const LC_SOURCE_VERSION = 0x2A;
/// source version used to build binary
SOURCE_VERSION = 0x2A,
/// Code signing DRs copied from linked dylibs
pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B;
/// Code signing DRs copied from linked dylibs
DYLIB_CODE_SIGN_DRS = 0x2B,
/// 64-bit encrypted segment information
pub const LC_ENCRYPTION_INFO_64 = 0x2C;
/// 64-bit encrypted segment information
ENCRYPTION_INFO_64 = 0x2C,
/// linker options in MH_OBJECT files
pub const LC_LINKER_OPTION = 0x2D;
/// linker options in MH_OBJECT files
LINKER_OPTION = 0x2D,
/// optimization hints in MH_OBJECT files
pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E;
/// optimization hints in MH_OBJECT files
LINKER_OPTIMIZATION_HINT = 0x2E,
/// build for AppleTV min OS version
pub const LC_VERSION_MIN_TVOS = 0x2F;
/// build for AppleTV min OS version
VERSION_MIN_TVOS = 0x2F,
/// build for Watch min OS version
pub const LC_VERSION_MIN_WATCHOS = 0x30;
/// build for Watch min OS version
VERSION_MIN_WATCHOS = 0x30,
/// arbitrary data included within a Mach-O file
pub const LC_NOTE = 0x31;
/// arbitrary data included within a Mach-O file
NOTE = 0x31,
/// build for platform min OS version
pub const LC_BUILD_VERSION = 0x32;
/// build for platform min OS version
BUILD_VERSION = 0x32,
_,
};
/// the mach magic number
pub const MH_MAGIC = 0xfeedface;
@ -1537,7 +1547,7 @@ pub const reloc_type_x86_64 = enum(u4) {
pub const reloc_type_arm64 = enum(u4) {
/// For pointers.
ARM64_RELOC_UNSIGNED,
ARM64_RELOC_UNSIGNED = 0,
/// Must be followed by a ARM64_RELOC_UNSIGNED.
ARM64_RELOC_SUBTRACTOR,
@ -1840,43 +1850,43 @@ pub const LoadCommand = union(enum) {
var stream = io.fixedBufferStream(buffer);
return switch (header.cmd) {
LC_SEGMENT_64 => LoadCommand{
.SEGMENT_64 => LoadCommand{
.segment = try SegmentCommand.read(allocator, stream.reader()),
},
LC_DYLD_INFO, LC_DYLD_INFO_ONLY => LoadCommand{
.DYLD_INFO, .DYLD_INFO_ONLY => LoadCommand{
.dyld_info_only = try stream.reader().readStruct(dyld_info_command),
},
LC_SYMTAB => LoadCommand{
.SYMTAB => LoadCommand{
.symtab = try stream.reader().readStruct(symtab_command),
},
LC_DYSYMTAB => LoadCommand{
.DYSYMTAB => LoadCommand{
.dysymtab = try stream.reader().readStruct(dysymtab_command),
},
LC_ID_DYLINKER, LC_LOAD_DYLINKER, LC_DYLD_ENVIRONMENT => LoadCommand{
.ID_DYLINKER, .LOAD_DYLINKER, .DYLD_ENVIRONMENT => LoadCommand{
.dylinker = try GenericCommandWithData(dylinker_command).read(allocator, stream.reader()),
},
LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB => LoadCommand{
.ID_DYLIB, .LOAD_WEAK_DYLIB, .LOAD_DYLIB, .REEXPORT_DYLIB => LoadCommand{
.dylib = try GenericCommandWithData(dylib_command).read(allocator, stream.reader()),
},
LC_MAIN => LoadCommand{
.MAIN => LoadCommand{
.main = try stream.reader().readStruct(entry_point_command),
},
LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS => LoadCommand{
.VERSION_MIN_MACOSX, .VERSION_MIN_IPHONEOS, .VERSION_MIN_WATCHOS, .VERSION_MIN_TVOS => LoadCommand{
.version_min = try stream.reader().readStruct(version_min_command),
},
LC_SOURCE_VERSION => LoadCommand{
.SOURCE_VERSION => LoadCommand{
.source_version = try stream.reader().readStruct(source_version_command),
},
LC_BUILD_VERSION => LoadCommand{
.BUILD_VERSION => LoadCommand{
.build_version = try GenericCommandWithData(build_version_command).read(allocator, stream.reader()),
},
LC_UUID => LoadCommand{
.UUID => LoadCommand{
.uuid = try stream.reader().readStruct(uuid_command),
},
LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_CODE_SIGNATURE => LoadCommand{
.FUNCTION_STARTS, .DATA_IN_CODE, .CODE_SIGNATURE => LoadCommand{
.linkedit_data = try stream.reader().readStruct(linkedit_data_command),
},
LC_RPATH => LoadCommand{
.RPATH => LoadCommand{
.rpath = try GenericCommandWithData(rpath_command).read(allocator, stream.reader()),
},
else => LoadCommand{
@ -1904,7 +1914,7 @@ pub const LoadCommand = union(enum) {
};
}
pub fn cmd(self: LoadCommand) u32 {
pub fn cmd(self: LoadCommand) LC {
return switch (self) {
.dyld_info_only => |x| x.cmd,
.symtab => |x| x.cmd,
@ -2078,7 +2088,7 @@ pub fn createLoadDylibCommand(
));
var dylib_cmd = emptyGenericCommandWithData(dylib_command{
.cmd = LC_LOAD_DYLIB,
.cmd = .LOAD_DYLIB,
.cmdsize = cmdsize,
.dylib = .{
.name = @sizeOf(dylib_command),
@ -2189,7 +2199,7 @@ test "read-write generic command with data" {
};
var cmd = GenericCommandWithData(dylib_command){
.inner = .{
.cmd = LC_LOAD_DYLIB,
.cmd = .LOAD_DYLIB,
.cmdsize = 32,
.dylib = .{
.name = 24,
@ -2227,7 +2237,7 @@ test "read-write C struct command" {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // stacksize
};
const cmd = .{
.cmd = LC_MAIN,
.cmd = .MAIN,
.cmdsize = 24,
.entryoff = 16644,
.stacksize = 0,

View File

@ -130,6 +130,7 @@ objc_imageinfo_section_index: ?u16 = null,
tlv_section_index: ?u16 = null,
tlv_data_section_index: ?u16 = null,
tlv_bss_section_index: ?u16 = null,
tlv_ptrs_section_index: ?u16 = null,
la_symbol_ptr_section_index: ?u16 = null,
data_section_index: ?u16 = null,
bss_section_index: ?u16 = null,
@ -164,6 +165,9 @@ stub_helper_preamble_atom: ?*Atom = null,
strtab: std.ArrayListUnmanaged(u8) = .{},
strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
tlv_ptr_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
tlv_ptr_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},
got_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
got_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},
@ -783,7 +787,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
@sizeOf(u64),
));
var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
.cmd = macho.LC_RPATH,
.cmdsize = cmdsize,
.path = @sizeOf(macho.rpath_command),
});
@ -1526,6 +1529,24 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
.sect = self.tlv_section_index.?,
};
},
macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
if (self.tlv_ptrs_section_index == null) {
self.tlv_ptrs_section_index = try self.initSection(
self.data_segment_cmd_index.?,
"__thread_ptrs",
sect.size,
sect.@"align",
.{
.flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
},
);
}
break :blk .{
.seg = self.data_segment_cmd_index.?,
.sect = self.tlv_ptrs_section_index.?,
};
},
macho.S_THREAD_LOCAL_REGULAR => {
if (self.tlv_data_section_index == null) {
self.tlv_data_section_index = try self.initSection(
@ -2143,6 +2164,24 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
return atom;
}
pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
const local_sym_index = @intCast(u32, self.locals.items.len);
try self.locals.append(self.base.allocator, .{
.n_strx = 0,
.n_type = macho.N_SECT,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
assert(target == .global);
try atom.bindings.append(self.base.allocator, .{
.n_strx = target.global,
.offset = 0,
});
return atom;
}
fn createDyldPrivateAtom(self: *MachO) !void {
if (self.dyld_private_atom != null) return;
const local_sym_index = @intCast(u32, self.locals.items.len);
@ -2164,7 +2203,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
sym.n_value = vaddr;
} else try self.addAtomAndBumpSectionSize(atom, match);
} else try self.addAtomToSection(atom, match);
sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
}
@ -2298,7 +2337,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
const vaddr = try self.allocateAtom(atom, atom.size, alignment_pow_2, match);
log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
sym.n_value = vaddr;
} else try self.addAtomAndBumpSectionSize(atom, match);
} else try self.addAtomToSection(atom, match);
sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
}
@ -2514,7 +2553,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
const vaddr = try self.allocateAtom(atom, size, alignment_pow_2, match);
local_sym.n_value = vaddr;
global_sym.n_value = vaddr;
} else try self.addAtomAndBumpSectionSize(atom, match);
} else try self.addAtomToSection(atom, match);
}
}
@ -2567,7 +2606,7 @@ fn createDsoHandleAtom(self: *MachO) !void {
const sym = &self.locals.items[local_sym_index];
const vaddr = try self.allocateAtom(atom, 0, 1, match);
sym.n_value = vaddr;
} else try self.addAtomAndBumpSectionSize(atom, match);
} else try self.addAtomToSection(atom, match);
}
}
@ -2913,7 +2952,7 @@ fn createMhExecuteHeaderAtom(self: *MachO) !void {
const sym = &self.locals.items[local_sym_index];
const vaddr = try self.allocateAtom(atom, 0, 1, match);
sym.n_value = vaddr;
} else try self.addAtomAndBumpSectionSize(atom, match);
} else try self.addAtomToSection(atom, match);
self.mh_execute_header_index = local_sym_index;
}
@ -2973,7 +3012,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {
const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
atom_sym.n_value = vaddr;
} else try self.addAtomAndBumpSectionSize(atom, match);
} else try self.addAtomToSection(atom, match);
atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
}
@ -3174,7 +3213,7 @@ fn addCodeSignatureLC(self: *MachO) !void {
self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.linkedit_data = .{
.cmd = macho.LC_CODE_SIGNATURE,
.cmd = .CODE_SIGNATURE,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
@ -3215,6 +3254,8 @@ pub fn deinit(self: *MachO) void {
}
self.section_ordinals.deinit(self.base.allocator);
self.tlv_ptr_entries_map.deinit(self.base.allocator);
self.tlv_ptr_entries_map_free_list.deinit(self.base.allocator);
self.got_entries_map.deinit(self.base.allocator);
self.got_entries_map_free_list.deinit(self.base.allocator);
self.stubs_map.deinit(self.base.allocator);
@ -4203,7 +4244,7 @@ fn populateMissingMetadata(self: *MachO) !void {
self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.dyld_info_only = .{
.cmd = macho.LC_DYLD_INFO_ONLY,
.cmd = .DYLD_INFO_ONLY,
.cmdsize = @sizeOf(macho.dyld_info_command),
.rebase_off = 0,
.rebase_size = 0,
@ -4224,7 +4265,6 @@ fn populateMissingMetadata(self: *MachO) !void {
self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.symtab = .{
.cmd = macho.LC_SYMTAB,
.cmdsize = @sizeOf(macho.symtab_command),
.symoff = 0,
.nsyms = 0,
@ -4239,7 +4279,6 @@ fn populateMissingMetadata(self: *MachO) !void {
self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.dysymtab = .{
.cmd = macho.LC_DYSYMTAB,
.cmdsize = @sizeOf(macho.dysymtab_command),
.ilocalsym = 0,
.nlocalsym = 0,
@ -4272,7 +4311,7 @@ fn populateMissingMetadata(self: *MachO) !void {
@sizeOf(u64),
));
var dylinker_cmd = macho.emptyGenericCommandWithData(macho.dylinker_command{
.cmd = macho.LC_LOAD_DYLINKER,
.cmd = .LOAD_DYLINKER,
.cmdsize = cmdsize,
.name = @sizeOf(macho.dylinker_command),
});
@ -4287,7 +4326,6 @@ fn populateMissingMetadata(self: *MachO) !void {
self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.main = .{
.cmd = macho.LC_MAIN,
.cmdsize = @sizeOf(macho.entry_point_command),
.entryoff = 0x0,
.stacksize = 0,
@ -4314,7 +4352,7 @@ fn populateMissingMetadata(self: *MachO) !void {
compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
);
errdefer dylib_cmd.deinit(self.base.allocator);
dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
dylib_cmd.inner.cmd = .ID_DYLIB;
try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
self.load_commands_dirty = true;
}
@ -4323,7 +4361,6 @@ fn populateMissingMetadata(self: *MachO) !void {
self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.source_version = .{
.cmd = macho.LC_SOURCE_VERSION,
.cmdsize = @sizeOf(macho.source_version_command),
.version = 0x0,
},
@ -4350,13 +4387,12 @@ fn populateMissingMetadata(self: *MachO) !void {
} else platform_version;
const is_simulator_abi = self.base.options.target.abi == .simulator;
var cmd = macho.emptyGenericCommandWithData(macho.build_version_command{
.cmd = macho.LC_BUILD_VERSION,
.cmdsize = cmdsize,
.platform = switch (self.base.options.target.os.tag) {
.macos => macho.PLATFORM_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,
.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,
@ -4364,7 +4400,7 @@ fn populateMissingMetadata(self: *MachO) !void {
.ntools = 1,
});
const ld_ver = macho.build_tool_version{
.tool = macho.TOOL_LD,
.tool = .LD,
.version = 0x0,
};
cmd.data = try self.base.allocator.alloc(u8, cmdsize - @sizeOf(macho.build_version_command));
@ -4377,7 +4413,6 @@ fn populateMissingMetadata(self: *MachO) !void {
if (self.uuid_cmd_index == null) {
self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
var uuid_cmd: macho.uuid_command = .{
.cmd = macho.LC_UUID,
.cmdsize = @sizeOf(macho.uuid_command),
.uuid = undefined,
};
@ -4390,7 +4425,7 @@ fn populateMissingMetadata(self: *MachO) !void {
self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.linkedit_data = .{
.cmd = macho.LC_FUNCTION_STARTS,
.cmd = .FUNCTION_STARTS,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
@ -4403,7 +4438,7 @@ fn populateMissingMetadata(self: *MachO) !void {
self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.linkedit_data = .{
.cmd = macho.LC_DATA_IN_CODE,
.cmd = .DATA_IN_CODE,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
@ -4480,13 +4515,36 @@ fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
// Allocate the sections according to their alignment at the beginning of the segment.
var start: u64 = offset;
for (seg.sections.items) |*sect| {
for (seg.sections.items) |*sect, sect_id| {
const alignment = try math.powi(u32, 2, sect.@"align");
const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
const end = start_aligned + sect.size;
sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
sect.addr = seg.inner.vmaddr + start_aligned;
start = end;
// Recalculate section size given the allocated start address
sect.size = if (self.atoms.get(.{
.seg = index,
.sect = @intCast(u16, sect_id),
})) |last_atom| blk: {
var atom = last_atom;
while (atom.prev) |prev| {
atom = prev;
}
var base_addr = sect.addr;
while (true) {
const atom_alignment = try math.powi(u32, 2, atom.alignment);
base_addr = mem.alignForwardGeneric(u64, base_addr, atom_alignment) + atom.size;
if (atom.next) |next| {
atom = next;
} else break;
}
break :blk base_addr - sect.addr;
} else 0;
start = start_aligned + sect.size;
}
const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size);
@ -4834,12 +4892,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
return vaddr;
}
fn addAtomAndBumpSectionSize(self: *MachO, atom: *Atom, match: MatchingSection) !void {
const seg = &self.load_commands.items[match.seg].segment;
const sect = &seg.sections.items[match.sect];
const alignment = try math.powi(u32, 2, atom.alignment);
sect.size = mem.alignForwardGeneric(u64, sect.size, alignment) + atom.size;
fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
if (self.atoms.getPtr(match)) |last| {
last.*.next = atom;
atom.prev = last.*;
@ -4978,6 +5031,7 @@ fn sortSections(self: *MachO) !void {
&self.objc_data_section_index,
&self.data_section_index,
&self.tlv_section_index,
&self.tlv_ptrs_section_index,
&self.tlv_data_section_index,
&self.tlv_bss_section_index,
&self.bss_section_index,
@ -6205,6 +6259,14 @@ fn logSymtab(self: MachO) void {
}
}
log.debug("__thread_ptrs entries:", .{});
for (self.tlv_ptr_entries_map.keys()) |key| {
switch (key) {
.local => unreachable,
.global => |n_strx| log.debug(" {} => {s}", .{ key, self.getString(n_strx) }),
}
}
log.debug("stubs:", .{});
for (self.stubs_map.keys()) |key| {
log.debug(" {} => {s}", .{ key, self.getString(key) });

View File

@ -403,6 +403,13 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
}
try self.addPtrBindingOrRebase(rel, target, context);
},
.ARM64_RELOC_TLVP_LOAD_PAGE21,
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
=> {
if (target == .global) {
try addTlvPtrEntry(target, context);
}
},
else => {},
}
},
@ -452,6 +459,11 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
@intCast(i64, target_sect_base_addr);
}
},
.X86_64_RELOC_TLV => {
if (target == .global) {
try addTlvPtrEntry(target, context);
}
},
else => {},
}
},
@ -531,6 +543,47 @@ fn addPtrBindingOrRebase(
}
}
fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void {
if (context.macho_file.tlv_ptr_entries_map.contains(target)) return;
const value_ptr = blk: {
if (context.macho_file.tlv_ptr_entries_map_free_list.popOrNull()) |i| {
log.debug("reusing __thread_ptrs entry index {d} for {}", .{ i, target });
context.macho_file.tlv_ptr_entries_map.keys()[i] = target;
const value_ptr = context.macho_file.tlv_ptr_entries_map.getPtr(target).?;
break :blk value_ptr;
} else {
const res = try context.macho_file.tlv_ptr_entries_map.getOrPut(
context.macho_file.base.allocator,
target,
);
log.debug("creating new __thread_ptrs entry at index {d} for {}", .{
context.macho_file.tlv_ptr_entries_map.getIndex(target).?,
target,
});
break :blk res.value_ptr;
}
};
const atom = try context.macho_file.createTlvPtrAtom(target);
value_ptr.* = atom;
const match = (try context.macho_file.getMatchingSection(.{
.segname = MachO.makeStaticString("__DATA"),
.sectname = MachO.makeStaticString("__thread_ptrs"),
.flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
})).?;
if (!context.object.start_atoms.contains(match)) {
try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
}
if (context.object.end_atoms.getPtr(match)) |last| {
last.*.next = atom;
atom.prev = last.*;
last.* = atom;
} else {
try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
}
}
fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {
if (context.macho_file.got_entries_map.contains(target)) return;
@ -667,6 +720,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
const sym = macho_file.locals.items[self.local_sym_index];
break :blk sym.n_value + rel.offset;
};
var is_via_thread_ptrs: bool = false;
const target_addr = blk: {
const is_via_got = got: {
switch (arch) {
@ -742,8 +796,13 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
.undef => {
break :blk if (macho_file.stubs_map.get(n_strx)) |atom|
macho_file.locals.items[atom.local_sym_index].n_value
else
0;
else inner: {
if (macho_file.tlv_ptr_entries_map.get(rel.target)) |atom| {
is_via_thread_ptrs = true;
break :inner macho_file.locals.items[atom.local_sym_index].n_value;
}
break :inner 0;
};
},
}
},
@ -854,10 +913,12 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
},
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
const code = self.code.items[rel.offset..][0..4];
const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
const RegInfo = struct {
rd: u5,
rn: u5,
size: u1,
size: u2,
};
const reg_info: RegInfo = blk: {
if (isArithmeticOp(code)) {
@ -878,13 +939,25 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
break :blk .{
.rd = inst.rt,
.rn = inst.rn,
.size = @truncate(u1, inst.size),
.size = inst.size,
};
}
};
const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
var inst = aarch64.Instruction{
var inst = if (is_via_thread_ptrs) blk: {
const offset = try math.divExact(u12, narrowed, 8);
break :blk aarch64.Instruction{
.load_store_register = .{
.rt = reg_info.rd,
.rn = reg_info.rn,
.offset = offset,
.opc = 0b01,
.op1 = 0b01,
.v = 0,
.size = reg_info.size,
},
};
} else aarch64.Instruction{
.add_subtract_immediate = .{
.rd = reg_info.rd,
.rn = reg_info.rn,
@ -892,7 +965,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
.sh = 0,
.s = 0,
.op = 0,
.sf = reg_info.size,
.sf = @truncate(u1, reg_info.size),
},
};
mem.writeIntLittle(u32, code, inst.toU32());
@ -942,8 +1015,10 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
},
.X86_64_RELOC_TLV => {
// We need to rewrite the opcode from movq to leaq.
self.code.items[rel.offset - 2] = 0x8d;
if (!is_via_thread_ptrs) {
// We need to rewrite the opcode from movq to leaq.
self.code.items[rel.offset - 2] = 0x8d;
}
const displacement = try math.cast(
i32,
@intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,

View File

@ -122,7 +122,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
try self.load_commands.append(allocator, .{
.symtab = .{
.cmd = macho.LC_SYMTAB,
.cmdsize = @sizeOf(macho.symtab_command),
.symoff = @intCast(u32, symtab_off),
.nsyms = base_cmd.nsyms,

View File

@ -177,16 +177,16 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
while (i < self.header.?.ncmds) : (i += 1) {
var cmd = try macho.LoadCommand.read(allocator, reader);
switch (cmd.cmd()) {
macho.LC_SYMTAB => {
.SYMTAB => {
self.symtab_cmd_index = i;
},
macho.LC_DYSYMTAB => {
.DYSYMTAB => {
self.dysymtab_cmd_index = i;
},
macho.LC_ID_DYLIB => {
.ID_DYLIB => {
self.id_cmd_index = i;
},
macho.LC_REEXPORT_DYLIB => {
.REEXPORT_DYLIB => {
if (should_lookup_reexports) {
// Parse install_name to dependent dylib.
var id = try Id.fromLoadCommand(allocator, cmd.dylib);

View File

@ -269,7 +269,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
while (i < header.ncmds) : (i += 1) {
var cmd = try macho.LoadCommand.read(allocator, reader);
switch (cmd.cmd()) {
macho.LC_SEGMENT_64 => {
.SEGMENT_64 => {
self.segment_cmd_index = i;
var seg = cmd.segment;
for (seg.sections.items) |*sect, j| {
@ -302,18 +302,18 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
seg.inner.fileoff += offset;
},
macho.LC_SYMTAB => {
.SYMTAB => {
self.symtab_cmd_index = i;
cmd.symtab.symoff += offset;
cmd.symtab.stroff += offset;
},
macho.LC_DYSYMTAB => {
.DYSYMTAB => {
self.dysymtab_cmd_index = i;
},
macho.LC_BUILD_VERSION => {
.BUILD_VERSION => {
self.build_version_cmd_index = i;
},
macho.LC_DATA_IN_CODE => {
.DATA_IN_CODE => {
self.data_in_code_cmd_index = i;
cmd.linkedit_data.dataoff += offset;
},

View File

@ -34,6 +34,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
cases.addBuildFile("test/standalone/link_frameworks/build.zig", .{
.requires_macos_sdk = true,
});
cases.addBuildFile("test/standalone/link_common_symbols_alignment/build.zig", .{});
if (builtin.os.tag == .macos) {
cases.addBuildFile("test/standalone/link_import_tls_dylib/build.zig", .{});
}
cases.addBuildFile("test/standalone/issue_339/build.zig", .{});
cases.addBuildFile("test/standalone/issue_8550/build.zig", .{});
cases.addBuildFile("test/standalone/issue_794/build.zig", .{});

View File

@ -0,0 +1,2 @@
int foo;
__attribute__((aligned(4096))) int bar;

View File

@ -0,0 +1,16 @@
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const lib_a = b.addStaticLibrary("a", null);
lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});
lib_a.setBuildMode(mode);
const test_exe = b.addTest("main.zig");
test_exe.setBuildMode(mode);
test_exe.linkLibrary(lib_a);
const test_step = b.step("test", "Test it");
test_step.dependOn(&test_exe.step);
}

View File

@ -0,0 +1,9 @@
const std = @import("std");
extern var foo: i32;
extern var bar: i32;
test {
try std.testing.expect(@ptrToInt(&foo) % 4 == 0);
try std.testing.expect(@ptrToInt(&bar) % 4096 == 0);
}

View File

@ -0,0 +1 @@
_Thread_local int a;

View File

@ -0,0 +1,16 @@
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
lib.setBuildMode(mode);
lib.addCSourceFile("a.c", &.{});
const test_exe = b.addTest("main.zig");
test_exe.setBuildMode(mode);
test_exe.linkLibrary(lib);
const test_step = b.step("test", "Test it");
test_step.dependOn(&test_exe.step);
}

View File

@ -0,0 +1,7 @@
const std = @import("std");
extern threadlocal var a: i32;
test {
try std.testing.expect(a == 0);
}