diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 64db6eeadc..1b4b5556df 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -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), diff --git a/lib/std/macho.zig b/lib/std/macho.zig index a77603c9ef..d0cd238fa3 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -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 { /// and . 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, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 43d6733132..d7457ec549 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -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) }); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 71183f4157..a2f1b385dc 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -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, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 8babb5d647..cda5077528 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -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, diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 31a8b357c2..7593593cbc 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -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); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 7f4a49d0d5..12ca463760 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -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; }, diff --git a/test/standalone.zig b/test/standalone.zig index ec35b89c40..4081e5aff3 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -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", .{}); diff --git a/test/standalone/link_common_symbols_alignment/a.c b/test/standalone/link_common_symbols_alignment/a.c new file mode 100644 index 0000000000..adff9d15f3 --- /dev/null +++ b/test/standalone/link_common_symbols_alignment/a.c @@ -0,0 +1,2 @@ +int foo; +__attribute__((aligned(4096))) int bar; diff --git a/test/standalone/link_common_symbols_alignment/build.zig b/test/standalone/link_common_symbols_alignment/build.zig new file mode 100644 index 0000000000..a62d86af4f --- /dev/null +++ b/test/standalone/link_common_symbols_alignment/build.zig @@ -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); +} diff --git a/test/standalone/link_common_symbols_alignment/main.zig b/test/standalone/link_common_symbols_alignment/main.zig new file mode 100644 index 0000000000..3d3457c764 --- /dev/null +++ b/test/standalone/link_common_symbols_alignment/main.zig @@ -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); +} diff --git a/test/standalone/link_import_tls_dylib/a.c b/test/standalone/link_import_tls_dylib/a.c new file mode 100644 index 0000000000..5c5aa5bae4 --- /dev/null +++ b/test/standalone/link_import_tls_dylib/a.c @@ -0,0 +1 @@ +_Thread_local int a; diff --git a/test/standalone/link_import_tls_dylib/build.zig b/test/standalone/link_import_tls_dylib/build.zig new file mode 100644 index 0000000000..332173fbb6 --- /dev/null +++ b/test/standalone/link_import_tls_dylib/build.zig @@ -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); +} diff --git a/test/standalone/link_import_tls_dylib/main.zig b/test/standalone/link_import_tls_dylib/main.zig new file mode 100644 index 0000000000..354c6f545e --- /dev/null +++ b/test/standalone/link_import_tls_dylib/main.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +extern threadlocal var a: i32; + +test { + try std.testing.expect(a == 0); +}