authorgravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-01-22 05:30:38-08:00
committergravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-01-22 05:30:38-08:00
log6089ed9ee77ed034a39c2b557f4608cd8d779d3f
tree9eaf9d78ca73d8c2dc1654e1e8beacbed1551988
parentbe4468be371de34e90a86346b0f6da6f2d85bef4
parentc0284e242f7d78955204dc8a627fecd45aa5e521

Merge branch 'master' into crc


37 files changed, 3114 insertions(+), 760 deletions(-)

CMakeLists.txt+2
......@@ -593,10 +593,12 @@ set(ZIG_STAGE2_SOURCES
593593 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
594594 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
595595 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
596597 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
597598 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
598599 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
599600 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
601 "${CMAKE_SOURCE_DIR}/src/link/MachO/eh_frame.zig"
600602 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
601603 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"
602604 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
lib/std/c/freebsd.zig+89-51
......@@ -155,6 +155,8 @@ pub const EAI = enum(c_int) {
155155
156156pub const EAI_MAX = 15;
157157
158pub const IFNAMESIZE = 16;
159
158160pub const AI = struct {
159161 /// get address to use bind()
160162 pub const PASSIVE = 0x00000001;
......@@ -1271,62 +1273,98 @@ pub const siginfo_t = extern struct {
12711273 },
12721274};
12731275
1274pub usingnamespace switch (builtin.cpu.arch) {
1275 .x86_64 => struct {
1276 pub const ucontext_t = extern struct {
1277 sigmask: sigset_t,
1278 mcontext: mcontext_t,
1279 link: ?*ucontext_t,
1280 stack: stack_t,
1281 flags: c_int,
1282 __spare__: [4]c_int,
1283 };
1284
1285 /// XXX x86_64 specific
1286 pub const mcontext_t = extern struct {
1287 onstack: u64,
1288 rdi: u64,
1289 rsi: u64,
1290 rdx: u64,
1291 rcx: u64,
1292 r8: u64,
1293 r9: u64,
1294 rax: u64,
1295 rbx: u64,
1296 rbp: u64,
1297 r10: u64,
1298 r11: u64,
1299 r12: u64,
1300 r13: u64,
1301 r14: u64,
1302 r15: u64,
1303 trapno: u32,
1304 fs: u16,
1305 gs: u16,
1306 addr: u64,
1276pub const mcontext_t = switch (builtin.cpu.arch) {
1277 .x86_64 => extern struct {
1278 onstack: u64,
1279 rdi: u64,
1280 rsi: u64,
1281 rdx: u64,
1282 rcx: u64,
1283 r8: u64,
1284 r9: u64,
1285 rax: u64,
1286 rbx: u64,
1287 rbp: u64,
1288 r10: u64,
1289 r11: u64,
1290 r12: u64,
1291 r13: u64,
1292 r14: u64,
1293 r15: u64,
1294 trapno: u32,
1295 fs: u16,
1296 gs: u16,
1297 addr: u64,
1298 flags: u32,
1299 es: u16,
1300 ds: u16,
1301 err: u64,
1302 rip: u64,
1303 cs: u64,
1304 rflags: u64,
1305 rsp: u64,
1306 ss: u64,
1307 len: u64,
1308 fpformat: u64,
1309 ownedfp: u64,
1310 fpstate: [64]u64 align(16),
1311 fsbase: u64,
1312 gsbase: u64,
1313 xfpustate: u64,
1314 xfpustate_len: u64,
1315 spare: [4]u64,
1316 },
1317 .aarch64 => extern struct {
1318 gpregs: extern struct {
1319 x: [30]u64,
1320 lr: u64,
1321 sp: u64,
1322 elr: u64,
1323 spsr: u32,
1324 _pad: u32,
1325 },
1326 fpregs: extern struct {
1327 q: [32]u128,
1328 sr: u32,
1329 cr: u32,
13071330 flags: u32,
1308 es: u16,
1309 ds: u16,
1310 err: u64,
1311 rip: u64,
1312 cs: u64,
1313 rflags: u64,
1314 rsp: u64,
1315 ss: u64,
1316 len: u64,
1317 fpformat: u64,
1318 ownedfp: u64,
1319 fpstate: [64]u64 align(16),
1320 fsbase: u64,
1321 gsbase: u64,
1322 xfpustate: u64,
1323 xfpustate_len: u64,
1324 spare: [4]u64,
1325 };
1331 _pad: u32,
1332 },
1333 flags: u32,
1334 _pad: u32,
1335 _spare: [8]u64,
1336 },
1337 else => struct {},
1338};
1339
1340pub const REG = switch (builtin.cpu.arch) {
1341 .aarch64 => struct {
1342 pub const FP = 29;
1343 pub const SP = 31;
1344 pub const PC = 32;
1345 },
1346 .arm => struct {
1347 pub const FP = 11;
1348 pub const SP = 13;
1349 pub const PC = 15;
1350 },
1351 .x86_64 => struct {
1352 pub const RBP = 12;
1353 pub const RIP = 21;
1354 pub const RSP = 24;
13261355 },
13271356 else => struct {},
13281357};
13291358
1359pub const ucontext_t = extern struct {
1360 sigmask: sigset_t,
1361 mcontext: mcontext_t,
1362 link: ?*ucontext_t,
1363 stack: stack_t,
1364 flags: c_int,
1365 __spare__: [4]c_int,
1366};
1367
13301368pub const E = enum(u16) {
13311369 /// No error occurred.
13321370 SUCCESS = 0,
lib/std/compress.zig+30
......@@ -4,6 +4,36 @@ pub const deflate = @import("compress/deflate.zig");
44pub const gzip = @import("compress/gzip.zig");
55pub const zlib = @import("compress/zlib.zig");
66
7pub fn HashedReader(
8 comptime ReaderType: anytype,
9 comptime HasherType: anytype,
10) type {
11 return struct {
12 child_reader: ReaderType,
13 hasher: HasherType,
14
15 pub const Error = ReaderType.Error;
16 pub const Reader = std.io.Reader(*@This(), Error, read);
17
18 pub fn read(self: *@This(), buf: []u8) Error!usize {
19 const amt = try self.child_reader.read(buf);
20 self.hasher.update(buf);
21 return amt;
22 }
23
24 pub fn reader(self: *@This()) Reader {
25 return .{ .context = self };
26 }
27 };
28}
29
30pub fn hashedReader(
31 reader: anytype,
32 hasher: anytype,
33) HashedReader(@TypeOf(reader), @TypeOf(hasher)) {
34 return .{ .child_reader = reader, .hasher = hasher };
35}
36
737test {
838 _ = deflate;
939 _ = gzip;
lib/std/compress/gzip.zig+24-8
......@@ -44,8 +44,11 @@ pub fn GzipStream(comptime ReaderType: type) type {
4444 },
4545
4646 fn init(allocator: mem.Allocator, source: ReaderType) !Self {
47 var hasher = std.compress.hashedReader(source, std.hash.Crc32.init());
48 const hashed_reader = hasher.reader();
49
4750 // gzip header format is specified in RFC1952
48 const header = try source.readBytesNoEof(10);
51 const header = try hashed_reader.readBytesNoEof(10);
4952
5053 // Check the ID1/ID2 fields
5154 if (header[0] != 0x1f or header[1] != 0x8b)
......@@ -66,31 +69,31 @@ pub fn GzipStream(comptime ReaderType: type) type {
6669 _ = XFL;
6770
6871 const extra = if (FLG & FEXTRA != 0) blk: {
69 const len = try source.readIntLittle(u16);
72 const len = try hashed_reader.readIntLittle(u16);
7073 const tmp_buf = try allocator.alloc(u8, len);
7174 errdefer allocator.free(tmp_buf);
7275
73 try source.readNoEof(tmp_buf);
76 try hashed_reader.readNoEof(tmp_buf);
7477 break :blk tmp_buf;
7578 } else null;
7679 errdefer if (extra) |p| allocator.free(p);
7780
7881 const filename = if (FLG & FNAME != 0)
79 try source.readUntilDelimiterAlloc(allocator, 0, max_string_len)
82 try hashed_reader.readUntilDelimiterAlloc(allocator, 0, max_string_len)
8083 else
8184 null;
8285 errdefer if (filename) |p| allocator.free(p);
8386
8487 const comment = if (FLG & FCOMMENT != 0)
85 try source.readUntilDelimiterAlloc(allocator, 0, max_string_len)
88 try hashed_reader.readUntilDelimiterAlloc(allocator, 0, max_string_len)
8689 else
8790 null;
8891 errdefer if (comment) |p| allocator.free(p);
8992
9093 if (FLG & FHCRC != 0) {
91 // TODO: Evaluate and check the header checksum. The stdlib has
92 // no CRC16 yet :(
93 _ = try source.readIntLittle(u16);
94 const hash = try source.readIntLittle(u16);
95 if (hash != @truncate(u16, hasher.hasher.final()))
96 return error.WrongChecksum;
9497 }
9598
9699 return Self{
......@@ -230,3 +233,16 @@ test "sanity checks" {
230233 }, ""),
231234 );
232235}
236
237test "header checksum" {
238 try testReader(&[_]u8{
239 // GZIP header
240 0x1f, 0x8b, 0x08, 0x12, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00,
241
242 // header.FHCRC (should cover entire header)
243 0x99, 0xd6,
244
245 // GZIP data
246 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 }, "");
248}
lib/std/debug.zig+2
......@@ -1986,12 +1986,14 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
19861986 const ip = switch (native_os) {
19871987 .macos => @intCast(usize, ctx.mcontext.ss.pc),
19881988 .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.PC]),
1989 .freebsd => @intCast(usize, ctx.mcontext.gpregs.elr),
19891990 else => @intCast(usize, ctx.mcontext.pc),
19901991 };
19911992 // x29 is the ABI-designated frame pointer
19921993 const bp = switch (native_os) {
19931994 .macos => @intCast(usize, ctx.mcontext.ss.fp),
19941995 .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.FP]),
1996 .freebsd => @intCast(usize, ctx.mcontext.gpregs.x[os.REG.FP]),
19951997 else => @intCast(usize, ctx.mcontext.regs[29]),
19961998 };
19971999 dumpStackTraceFromBase(bp, ip);
lib/std/macho.zig+2
......@@ -2011,6 +2011,7 @@ pub const UNWIND_PERSONALITY_MASK: u32 = 0x30000000;
20112011// x86_64
20122012pub const UNWIND_X86_64_MODE_MASK: u32 = 0x0F000000;
20132013pub const UNWIND_X86_64_MODE = enum(u4) {
2014 OLD = 0,
20142015 RBP_FRAME = 1,
20152016 STACK_IMMD = 2,
20162017 STACK_IND = 3,
......@@ -2039,6 +2040,7 @@ pub const UNWIND_X86_64_REG = enum(u3) {
20392040// arm64
20402041pub const UNWIND_ARM64_MODE_MASK: u32 = 0x0F000000;
20412042pub const UNWIND_ARM64_MODE = enum(u4) {
2043 OLD = 0,
20422044 FRAMELESS = 2,
20432045 DWARF = 3,
20442046 FRAME = 4,
src/Sema.zig+26-31
......@@ -3948,6 +3948,7 @@ fn validateArrayInitTy(
39483948 return;
39493949 },
39503950 .Struct => if (ty.isTuple()) {
3951 _ = try sema.resolveTypeFields(ty);
39513952 const array_len = ty.arrayLen();
39523953 if (extra.init_count > array_len) {
39533954 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
......@@ -4642,11 +4643,11 @@ fn failWithBadMemberAccess(
46424643 .Enum => "enum",
46434644 else => unreachable,
46444645 };
4645 if (sema.mod.declIsRoot(agg_ty.getOwnerDecl())) {
4646 if (agg_ty.getOwnerDeclOrNull()) |some| if (sema.mod.declIsRoot(some)) {
46464647 return sema.fail(block, field_src, "root struct of file '{}' has no member named '{s}'", .{
46474648 agg_ty.fmt(sema.mod), field_name,
46484649 });
4649 }
4650 };
46504651 const msg = msg: {
46514652 const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{
46524653 kw_name, agg_ty.fmt(sema.mod), field_name,
......@@ -7514,7 +7515,7 @@ fn resolveGenericInstantiationType(
75147515}
75157516
75167517fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
7517 if (!ty.isSimpleTuple()) return;
7518 if (!ty.isSimpleTupleOrAnonStruct()) return;
75187519 const tuple = ty.tupleFields();
75197520 for (tuple.values) |field_val, i| {
75207521 try sema.resolveTupleLazyValues(block, src, tuple.types[i]);
......@@ -11771,8 +11772,8 @@ fn zirShl(
1177111772 // TODO coerce rhs if air_tag is not shl_sat
1177211773 const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
1177311774
11774 const maybe_lhs_val = try sema.resolveMaybeUndefVal(lhs);
11775 const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs);
11775 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs);
11776 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs);
1177611777
1177711778 if (maybe_rhs_val) |rhs_val| {
1177811779 if (rhs_val.isUndef()) {
......@@ -11842,7 +11843,7 @@ fn zirShl(
1184211843 if (scalar_ty.zigTypeTag() == .ComptimeInt) {
1184311844 break :val shifted.wrapped_result;
1184411845 }
11845 if (shifted.overflow_bit.compareAllWithZero(.eq)) {
11846 if (shifted.overflow_bit.compareAllWithZero(.eq, sema.mod)) {
1184611847 break :val shifted.wrapped_result;
1184711848 }
1184811849 return sema.fail(block, src, "operation caused overflow", .{});
......@@ -11959,8 +11960,8 @@ fn zirShr(
1195911960 const target = sema.mod.getTarget();
1196011961 const scalar_ty = lhs_ty.scalarType();
1196111962
11962 const maybe_lhs_val = try sema.resolveMaybeUndefVal(lhs);
11963 const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs);
11963 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(lhs);
11964 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(rhs);
1196411965
1196511966 const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
1196611967 if (rhs_val.isUndef()) {
......@@ -12799,7 +12800,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1279912800 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1280012801 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1280112802 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
12802 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div);
12803 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1280312804
1280412805 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1280512806 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -12831,7 +12832,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1283112832 const lhs_val = maybe_lhs_val orelse unreachable;
1283212833 const rhs_val = maybe_rhs_val orelse unreachable;
1283312834 const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable;
12834 if (!rem.compareAllWithZero(.eq)) {
12835 if (!rem.compareAllWithZero(.eq, mod)) {
1283512836 return sema.fail(block, src, "ambiguous coercion of division operands '{s}' and '{s}'; non-zero remainder '{}'", .{
1283612837 @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), rem.fmtValue(resolved_type, sema.mod),
1283712838 });
......@@ -12959,7 +12960,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1295912960 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1296012961 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1296112962 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
12962 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_exact);
12963 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1296312964
1296412965 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1296512966 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -13024,7 +13025,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1302413025 if (maybe_rhs_val) |rhs_val| {
1302513026 if (is_int) {
1302613027 const modulus_val = try lhs_val.intMod(rhs_val, resolved_type, sema.arena, mod);
13027 if (!(modulus_val.compareAllWithZero(.eq))) {
13028 if (!(modulus_val.compareAllWithZero(.eq, mod))) {
1302813029 return sema.fail(block, src, "exact division produced remainder", .{});
1302913030 }
1303013031 const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, mod);
......@@ -13035,7 +13036,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1303513036 return sema.addConstant(resolved_type, res);
1303613037 } else {
1303713038 const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, mod);
13038 if (!(modulus_val.compareAllWithZero(.eq))) {
13039 if (!(modulus_val.compareAllWithZero(.eq, mod))) {
1303913040 return sema.fail(block, src, "exact division produced remainder", .{});
1304013041 }
1304113042 return sema.addConstant(
......@@ -13122,7 +13123,7 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1312213123 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1312313124 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1312413125 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13125 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_floor);
13126 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1312613127
1312713128 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1312813129 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -13238,7 +13239,7 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1323813239 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1323913240 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1324013241 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13241 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_trunc);
13242 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1324213243
1324313244 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1324413245 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -13481,7 +13482,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1348113482 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1348213483 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1348313484 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13484 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .mod_rem);
13485 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1348513486
1348613487 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1348713488 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -13664,7 +13665,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1366413665 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1366513666 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1366613667 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13667 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .mod);
13668 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1366813669
1366913670 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1367013671 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -13766,7 +13767,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
1376613767 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
1376713768 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
1376813769 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13769 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .rem);
13770 try sema.checkInvalidPtrArithmetic(block, src, lhs_ty);
1377013771
1377113772 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
1377213773 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
......@@ -14106,12 +14107,7 @@ fn analyzeArithmetic(
1410614107 const air_tag: Air.Inst.Tag = switch (zir_tag) {
1410714108 .add => .ptr_add,
1410814109 .sub => .ptr_sub,
14109 else => return sema.fail(
14110 block,
14111 src,
14112 "invalid pointer arithmetic operand: '{s}''",
14113 .{@tagName(zir_tag)},
14114 ),
14110 else => return sema.fail(block, src, "invalid pointer arithmetic operator", .{}),
1411514111 };
1411614112 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
1411714113 },
......@@ -19697,7 +19693,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1969719693 }
1969819694 }
1969919695
19700 if (try sema.resolveMaybeUndefVal(operand)) |val| {
19696 if (try sema.resolveMaybeUndefValIntable(operand)) |val| {
1970119697 if (val.isUndef()) return sema.addConstUndef(dest_ty);
1970219698 if (!is_vector) {
1970319699 return sema.addConstant(
......@@ -19901,7 +19897,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1990119897 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1990219898 const operand = try sema.resolveInst(inst_data.operand);
1990319899 const operand_ty = sema.typeOf(operand);
19904 _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
19900 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
1990519901
1990619902 if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
1990719903 return sema.addConstant(operand_ty, val);
......@@ -19909,7 +19905,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1990919905
1991019906 const target = sema.mod.getTarget();
1991119907 switch (operand_ty.zigTypeTag()) {
19912 .Int, .ComptimeInt => {
19908 .Int => {
1991319909 const runtime_src = if (try sema.resolveMaybeUndefVal(operand)) |val| {
1991419910 if (val.isUndef()) return sema.addConstUndef(operand_ty);
1991519911 const result_val = try val.bitReverse(operand_ty, target, sema.arena);
......@@ -19929,7 +19925,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1992919925 const elems = try sema.arena.alloc(Value, vec_len);
1993019926 for (elems) |*elem, i| {
1993119927 const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
19932 elem.* = try elem_val.bitReverse(operand_ty, target, sema.arena);
19928 elem.* = try elem_val.bitReverse(scalar_ty, target, sema.arena);
1993319929 }
1993419930 return sema.addConstant(
1993519931 operand_ty,
......@@ -20028,7 +20024,6 @@ fn checkInvalidPtrArithmetic(
2002820024 block: *Block,
2002920025 src: LazySrcLoc,
2003020026 ty: Type,
20031 zir_tag: Zir.Inst.Tag,
2003220027) CompileError!void {
2003320028 switch (try ty.zigTypeTagOrPoison()) {
2003420029 .Pointer => switch (ty.ptrSize()) {
......@@ -20036,8 +20031,8 @@ fn checkInvalidPtrArithmetic(
2003620031 .Many, .C => return sema.fail(
2003720032 block,
2003820033 src,
20039 "invalid pointer arithmetic operand: '{s}''",
20040 .{@tagName(zir_tag)},
20034 "invalid pointer arithmetic operator",
20035 .{},
2004120036 ),
2004220037 },
2004320038 else => return,
src/arch/arm/CodeGen.zig+132-52
......@@ -77,11 +77,11 @@ end_di_column: u32,
7777/// which is a relative jump, based on the address following the reloc.
7878exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
7979
80/// For every argument, we postpone the creation of debug info for
81/// later after all Mir instructions have been generated. Only then we
80/// We postpone the creation of debug info for function args and locals
81/// until after all Mir instructions have been generated. Only then we
8282/// will know saved_regs_stack_space which is necessary in order to
83/// address parameters passed on the stack.
84dbg_arg_relocs: std.ArrayListUnmanaged(DbgArgReloc) = .{},
83/// calculate the right stack offsest with respect to the `.fp` register.
84dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .{},
8585
8686/// Whenever there is a runtime branch, we push a Branch onto this stack,
8787/// and pop it off when the runtime branch joins. This provides an "overlay"
......@@ -243,9 +243,107 @@ const BigTomb = struct {
243243 }
244244};
245245
246const DbgArgReloc = struct {
247 inst: Air.Inst.Index,
248 index: u32,
246const DbgInfoReloc = struct {
247 tag: Air.Inst.Tag,
248 ty: Type,
249 name: [:0]const u8,
250 mcv: MCValue,
251
252 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
253 switch (reloc.tag) {
254 .arg => try reloc.genArgDbgInfo(function),
255
256 .dbg_var_ptr,
257 .dbg_var_val,
258 => try reloc.genVarDbgInfo(function),
259
260 else => unreachable,
261 }
262 }
263
264 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {
265 switch (function.debug_output) {
266 .dwarf => |dw| {
267 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (reloc.mcv) {
268 .register => |reg| .{ .register = reg.dwarfLocOp() },
269 .stack_offset,
270 .stack_argument_offset,
271 => blk: {
272 const adjusted_stack_offset = switch (reloc.mcv) {
273 .stack_offset => |offset| -@intCast(i32, offset),
274 .stack_argument_offset => |offset| @intCast(i32, function.saved_regs_stack_space + offset),
275 else => unreachable,
276 };
277 break :blk .{ .stack = .{
278 .fp_register = DW.OP.breg11,
279 .offset = adjusted_stack_offset,
280 } };
281 },
282 else => unreachable, // not a possible argument
283 };
284
285 try dw.genArgDbgInfo(
286 reloc.name,
287 reloc.ty,
288 function.bin_file.tag,
289 function.mod_fn.owner_decl,
290 loc,
291 );
292 },
293 .plan9 => {},
294 .none => {},
295 }
296 }
297
298 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
299 const is_ptr = switch (reloc.tag) {
300 .dbg_var_ptr => true,
301 .dbg_var_val => false,
302 else => unreachable,
303 };
304
305 switch (function.debug_output) {
306 .dwarf => |dw| {
307 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (reloc.mcv) {
308 .register => |reg| .{ .register = reg.dwarfLocOp() },
309 .ptr_stack_offset,
310 .stack_offset,
311 .stack_argument_offset,
312 => |offset| blk: {
313 const adjusted_offset = switch (reloc.mcv) {
314 .ptr_stack_offset,
315 .stack_offset,
316 => -@intCast(i32, offset),
317 .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset),
318 else => unreachable,
319 };
320 break :blk .{ .stack = .{
321 .fp_register = DW.OP.breg11,
322 .offset = adjusted_offset,
323 } };
324 },
325 .memory => |address| .{ .memory = address },
326 .immediate => |x| .{ .immediate = x },
327 .undef => .undef,
328 .none => .none,
329 else => blk: {
330 log.debug("TODO generate debug info for {}", .{reloc.mcv});
331 break :blk .nop;
332 },
333 };
334 try dw.genVarDbgInfo(
335 reloc.name,
336 reloc.ty,
337 function.bin_file.tag,
338 function.mod_fn.owner_decl,
339 is_ptr,
340 loc,
341 );
342 },
343 .plan9 => {},
344 .none => {},
345 }
346 }
249347};
250348
251349const Self = @This();
......@@ -298,7 +396,7 @@ pub fn generate(
298396 defer function.stack.deinit(bin_file.allocator);
299397 defer function.blocks.deinit(bin_file.allocator);
300398 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
301 defer function.dbg_arg_relocs.deinit(bin_file.allocator);
399 defer function.dbg_info_relocs.deinit(bin_file.allocator);
302400
303401 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
304402 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
......@@ -322,8 +420,8 @@ pub fn generate(
322420 else => |e| return e,
323421 };
324422
325 for (function.dbg_arg_relocs.items) |reloc| {
326 try function.genArgDbgInfo(reloc.inst, reloc.index);
423 for (function.dbg_info_relocs.items) |reloc| {
424 try reloc.genDbgInfo(function);
327425 }
328426
329427 var mir = Mir{
......@@ -896,9 +994,6 @@ fn allocMem(
896994 assert(abi_size > 0);
897995 assert(abi_align > 0);
898996
899 if (abi_align > self.stack_align)
900 self.stack_align = abi_align;
901
902997 // TODO find a free slot instead of always appending
903998 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
904999 self.next_stack_offset = offset;
......@@ -4035,46 +4130,20 @@ fn genInlineMemsetCode(
40354130 // end:
40364131}
40374132
4038fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, arg_index: u32) error{OutOfMemory}!void {
4039 const mcv = self.args[arg_index];
4040 const arg = self.air.instructions.items(.data)[inst].arg;
4041 const ty = self.air.getRefType(arg.ty);
4042 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg.src_index);
4043
4044 switch (self.debug_output) {
4045 .dwarf => |dw| {
4046 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) {
4047 .register => |reg| .{ .register = reg.dwarfLocOp() },
4048 .stack_offset,
4049 .stack_argument_offset,
4050 => blk: {
4051 const adjusted_stack_offset = switch (mcv) {
4052 .stack_offset => |offset| -@intCast(i32, offset),
4053 .stack_argument_offset => |offset| @intCast(i32, self.saved_regs_stack_space + offset),
4054 else => unreachable,
4055 };
4056 break :blk .{ .stack = .{
4057 .fp_register = DW.OP.breg11,
4058 .offset = adjusted_stack_offset,
4059 } };
4060 },
4061 else => unreachable, // not a possible argument
4062
4063 };
4064 try dw.genArgDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, loc);
4065 },
4066 .plan9 => {},
4067 .none => {},
4068 }
4069}
4070
40714133fn airArg(self: *Self, inst: Air.Inst.Index) !void {
40724134 const arg_index = self.arg_index;
40734135 self.arg_index += 1;
40744136
4075 try self.dbg_arg_relocs.append(self.gpa, .{
4076 .inst = inst,
4077 .index = arg_index,
4137 const ty = self.air.typeOfIndex(inst);
4138 const tag = self.air.instructions.items(.tag)[inst];
4139 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
4140 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
4141
4142 try self.dbg_info_relocs.append(self.gpa, .{
4143 .tag = tag,
4144 .ty = ty,
4145 .name = name,
4146 .mcv = self.args[arg_index],
40784147 });
40794148
40804149 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
......@@ -4485,10 +4554,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
44854554
44864555fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
44874556 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4488 const name = self.air.nullTerminatedString(pl_op.payload);
44894557 const operand = pl_op.operand;
4490 // TODO emit debug info for this variable
4491 _ = name;
4558 const tag = self.air.instructions.items(.tag)[inst];
4559 const ty = self.air.typeOf(operand);
4560 const mcv = try self.resolveInst(operand);
4561 const name = self.air.nullTerminatedString(pl_op.payload);
4562
4563 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
4564
4565 try self.dbg_info_relocs.append(self.gpa, .{
4566 .tag = tag,
4567 .ty = ty,
4568 .name = name,
4569 .mcv = mcv,
4570 });
4571
44924572 return self.finishAir(inst, .dead, .{ operand, .none, .none });
44934573}
44944574
src/codegen/llvm.zig+183-164
......@@ -10396,12 +10396,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
1039610396 .mips, .mipsel => return false,
1039710397 .x86_64 => switch (target.os.tag) {
1039810398 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
10399 else => {
10400 const class = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10401 if (class[0] == .memory) return true;
10402 if (class[0] == .x87 and class[2] != .none) return true;
10403 return false;
10404 },
10399 else => return firstParamSRetSystemV(fn_info.return_type, target),
1040510400 },
1040610401 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
1040710402 .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target) == .memory,
......@@ -10413,11 +10408,20 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
1041310408 .riscv32, .riscv64 => return riscv_c_abi.classifyType(fn_info.return_type, target) == .memory,
1041410409 else => return false, // TODO investigate C ABI for other architectures
1041510410 },
10411 .SysV => return firstParamSRetSystemV(fn_info.return_type, target),
10412 .Win64 => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
1041610413 .Stdcall => return !isScalar(fn_info.return_type),
1041710414 else => return false,
1041810415 }
1041910416}
1042010417
10418fn firstParamSRetSystemV(ty: Type, target: std.Target) bool {
10419 const class = x86_64_abi.classifySystemV(ty, target, .ret);
10420 if (class[0] == .memory) return true;
10421 if (class[0] == .x87 and class[2] != .none) return true;
10422 return false;
10423}
10424
1042110425/// In order to support the C calling convention, some return types need to be lowered
1042210426/// completely differently in the function prototype to honor the C ABI, and then
1042310427/// be effectively bitcasted to the actual return type.
......@@ -10442,77 +10446,14 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
1044210446 }
1044310447 },
1044410448 .C => {
10445 const is_scalar = isScalar(fn_info.return_type);
1044610449 switch (target.cpu.arch) {
1044710450 .mips, .mipsel => return dg.lowerType(fn_info.return_type),
1044810451 .x86_64 => switch (target.os.tag) {
10449 .windows => switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) {
10450 .integer => {
10451 if (is_scalar) {
10452 return dg.lowerType(fn_info.return_type);
10453 } else {
10454 const abi_size = fn_info.return_type.abiSize(target);
10455 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10456 }
10457 },
10458 .win_i128 => return dg.context.intType(64).vectorType(2),
10459 .memory => return dg.context.voidType(),
10460 .sse => return dg.lowerType(fn_info.return_type),
10461 else => unreachable,
10462 },
10463 else => {
10464 if (is_scalar) {
10465 return dg.lowerType(fn_info.return_type);
10466 }
10467 const classes = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10468 if (classes[0] == .memory) {
10469 return dg.context.voidType();
10470 }
10471 var llvm_types_buffer: [8]*llvm.Type = undefined;
10472 var llvm_types_index: u32 = 0;
10473 for (classes) |class| {
10474 switch (class) {
10475 .integer => {
10476 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10477 llvm_types_index += 1;
10478 },
10479 .sse, .sseup => {
10480 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10481 llvm_types_index += 1;
10482 },
10483 .float => {
10484 llvm_types_buffer[llvm_types_index] = dg.context.floatType();
10485 llvm_types_index += 1;
10486 },
10487 .float_combine => {
10488 llvm_types_buffer[llvm_types_index] = dg.context.floatType().vectorType(2);
10489 llvm_types_index += 1;
10490 },
10491 .x87 => {
10492 if (llvm_types_index != 0 or classes[2] != .none) {
10493 return dg.context.voidType();
10494 }
10495 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10496 llvm_types_index += 1;
10497 },
10498 .x87up => continue,
10499 .complex_x87 => {
10500 @panic("TODO");
10501 },
10502 .memory => unreachable, // handled above
10503 .win_i128 => unreachable, // windows only
10504 .none => break,
10505 }
10506 }
10507 if (classes[0] == .integer and classes[1] == .none) {
10508 const abi_size = fn_info.return_type.abiSize(target);
10509 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10510 }
10511 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
10512 },
10452 .windows => return lowerWin64FnRetTy(dg, fn_info),
10453 else => return lowerSystemVFnRetTy(dg, fn_info),
1051310454 },
1051410455 .wasm32 => {
10515 if (is_scalar) {
10456 if (isScalar(fn_info.return_type)) {
1051610457 return dg.lowerType(fn_info.return_type);
1051710458 }
1051810459 const classes = wasm_c_abi.classifyType(fn_info.return_type, target);
......@@ -10569,6 +10510,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
1056910510 else => return dg.lowerType(fn_info.return_type),
1057010511 }
1057110512 },
10513 .Win64 => return lowerWin64FnRetTy(dg, fn_info),
10514 .SysV => return lowerSystemVFnRetTy(dg, fn_info),
1057210515 .Stdcall => {
1057310516 if (isScalar(fn_info.return_type)) {
1057410517 return dg.lowerType(fn_info.return_type);
......@@ -10580,6 +10523,76 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
1058010523 }
1058110524}
1058210525
10526fn lowerWin64FnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10527 const target = dg.module.getTarget();
10528 switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) {
10529 .integer => {
10530 if (isScalar(fn_info.return_type)) {
10531 return dg.lowerType(fn_info.return_type);
10532 } else {
10533 const abi_size = fn_info.return_type.abiSize(target);
10534 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10535 }
10536 },
10537 .win_i128 => return dg.context.intType(64).vectorType(2),
10538 .memory => return dg.context.voidType(),
10539 .sse => return dg.lowerType(fn_info.return_type),
10540 else => unreachable,
10541 }
10542}
10543
10544fn lowerSystemVFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10545 if (isScalar(fn_info.return_type)) {
10546 return dg.lowerType(fn_info.return_type);
10547 }
10548 const target = dg.module.getTarget();
10549 const classes = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10550 if (classes[0] == .memory) {
10551 return dg.context.voidType();
10552 }
10553 var llvm_types_buffer: [8]*llvm.Type = undefined;
10554 var llvm_types_index: u32 = 0;
10555 for (classes) |class| {
10556 switch (class) {
10557 .integer => {
10558 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10559 llvm_types_index += 1;
10560 },
10561 .sse, .sseup => {
10562 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10563 llvm_types_index += 1;
10564 },
10565 .float => {
10566 llvm_types_buffer[llvm_types_index] = dg.context.floatType();
10567 llvm_types_index += 1;
10568 },
10569 .float_combine => {
10570 llvm_types_buffer[llvm_types_index] = dg.context.floatType().vectorType(2);
10571 llvm_types_index += 1;
10572 },
10573 .x87 => {
10574 if (llvm_types_index != 0 or classes[2] != .none) {
10575 return dg.context.voidType();
10576 }
10577 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10578 llvm_types_index += 1;
10579 },
10580 .x87up => continue,
10581 .complex_x87 => {
10582 @panic("TODO");
10583 },
10584 .memory => unreachable, // handled above
10585 .win_i128 => unreachable, // windows only
10586 .none => break,
10587 }
10588 }
10589 if (classes[0] == .integer and classes[1] == .none) {
10590 const abi_size = fn_info.return_type.abiSize(target);
10591 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10592 }
10593 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
10594}
10595
1058310596const ParamTypeIterator = struct {
1058410597 dg: *DeclGen,
1058510598 fn_info: Type.Payload.Function.Data,
......@@ -10629,7 +10642,6 @@ const ParamTypeIterator = struct {
1062910642 it.zig_index += 1;
1063010643 return .no_bits;
1063110644 }
10632 const dg = it.dg;
1063310645 switch (it.fn_info.cc) {
1063410646 .Unspecified, .Inline => {
1063510647 it.zig_index += 1;
......@@ -10648,7 +10660,6 @@ const ParamTypeIterator = struct {
1064810660 @panic("TODO implement async function lowering in the LLVM backend");
1064910661 },
1065010662 .C => {
10651 const is_scalar = isScalar(ty);
1065210663 switch (it.target.cpu.arch) {
1065310664 .mips, .mipsel => {
1065410665 it.zig_index += 1;
......@@ -10656,99 +10667,13 @@ const ParamTypeIterator = struct {
1065610667 return .byval;
1065710668 },
1065810669 .x86_64 => switch (it.target.os.tag) {
10659 .windows => switch (x86_64_abi.classifyWindows(ty, it.target)) {
10660 .integer => {
10661 if (is_scalar) {
10662 it.zig_index += 1;
10663 it.llvm_index += 1;
10664 return .byval;
10665 } else {
10666 it.zig_index += 1;
10667 it.llvm_index += 1;
10668 return .abi_sized_int;
10669 }
10670 },
10671 .win_i128 => {
10672 it.zig_index += 1;
10673 it.llvm_index += 1;
10674 return .byref;
10675 },
10676 .memory => {
10677 it.zig_index += 1;
10678 it.llvm_index += 1;
10679 return .byref_mut;
10680 },
10681 .sse => {
10682 it.zig_index += 1;
10683 it.llvm_index += 1;
10684 return .byval;
10685 },
10686 else => unreachable,
10687 },
10688 else => {
10689 const classes = x86_64_abi.classifySystemV(ty, it.target, .arg);
10690 if (classes[0] == .memory) {
10691 it.zig_index += 1;
10692 it.llvm_index += 1;
10693 it.byval_attr = true;
10694 return .byref;
10695 }
10696 if (is_scalar) {
10697 it.zig_index += 1;
10698 it.llvm_index += 1;
10699 return .byval;
10700 }
10701 var llvm_types_buffer: [8]*llvm.Type = undefined;
10702 var llvm_types_index: u32 = 0;
10703 for (classes) |class| {
10704 switch (class) {
10705 .integer => {
10706 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10707 llvm_types_index += 1;
10708 },
10709 .sse, .sseup => {
10710 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10711 llvm_types_index += 1;
10712 },
10713 .float => {
10714 llvm_types_buffer[llvm_types_index] = dg.context.floatType();
10715 llvm_types_index += 1;
10716 },
10717 .float_combine => {
10718 llvm_types_buffer[llvm_types_index] = dg.context.floatType().vectorType(2);
10719 llvm_types_index += 1;
10720 },
10721 .x87 => {
10722 it.zig_index += 1;
10723 it.llvm_index += 1;
10724 it.byval_attr = true;
10725 return .byref;
10726 },
10727 .x87up => unreachable,
10728 .complex_x87 => {
10729 @panic("TODO");
10730 },
10731 .memory => unreachable, // handled above
10732 .win_i128 => unreachable, // windows only
10733 .none => break,
10734 }
10735 }
10736 if (classes[0] == .integer and classes[1] == .none) {
10737 it.zig_index += 1;
10738 it.llvm_index += 1;
10739 return .abi_sized_int;
10740 }
10741 it.llvm_types_buffer = llvm_types_buffer;
10742 it.llvm_types_len = llvm_types_index;
10743 it.llvm_index += llvm_types_index;
10744 it.zig_index += 1;
10745 return .multiple_llvm_types;
10746 },
10670 .windows => return it.nextWin64(ty),
10671 else => return it.nextSystemV(ty),
1074710672 },
1074810673 .wasm32 => {
1074910674 it.zig_index += 1;
1075010675 it.llvm_index += 1;
10751 if (is_scalar) {
10676 if (isScalar(ty)) {
1075210677 return .byval;
1075310678 }
1075410679 const classes = wasm_c_abi.classifyType(ty, it.target);
......@@ -10766,7 +10691,7 @@ const ParamTypeIterator = struct {
1076610691 .byval => return .byval,
1076710692 .integer => {
1076810693 it.llvm_types_len = 1;
10769 it.llvm_types_buffer[0] = dg.context.intType(64);
10694 it.llvm_types_buffer[0] = it.dg.context.intType(64);
1077010695 return .multiple_llvm_types;
1077110696 },
1077210697 .double_integer => return Lowering{ .i64_array = 2 },
......@@ -10806,6 +10731,8 @@ const ParamTypeIterator = struct {
1080610731 },
1080710732 }
1080810733 },
10734 .Win64 => return it.nextWin64(ty),
10735 .SysV => return it.nextSystemV(ty),
1080910736 .Stdcall => {
1081010737 it.zig_index += 1;
1081110738 it.llvm_index += 1;
......@@ -10824,6 +10751,98 @@ const ParamTypeIterator = struct {
1082410751 },
1082510752 }
1082610753 }
10754
10755 fn nextWin64(it: *ParamTypeIterator, ty: Type) ?Lowering {
10756 switch (x86_64_abi.classifyWindows(ty, it.target)) {
10757 .integer => {
10758 if (isScalar(ty)) {
10759 it.zig_index += 1;
10760 it.llvm_index += 1;
10761 return .byval;
10762 } else {
10763 it.zig_index += 1;
10764 it.llvm_index += 1;
10765 return .abi_sized_int;
10766 }
10767 },
10768 .win_i128 => {
10769 it.zig_index += 1;
10770 it.llvm_index += 1;
10771 return .byref;
10772 },
10773 .memory => {
10774 it.zig_index += 1;
10775 it.llvm_index += 1;
10776 return .byref_mut;
10777 },
10778 .sse => {
10779 it.zig_index += 1;
10780 it.llvm_index += 1;
10781 return .byval;
10782 },
10783 else => unreachable,
10784 }
10785 }
10786
10787 fn nextSystemV(it: *ParamTypeIterator, ty: Type) ?Lowering {
10788 const classes = x86_64_abi.classifySystemV(ty, it.target, .arg);
10789 if (classes[0] == .memory) {
10790 it.zig_index += 1;
10791 it.llvm_index += 1;
10792 it.byval_attr = true;
10793 return .byref;
10794 }
10795 if (isScalar(ty)) {
10796 it.zig_index += 1;
10797 it.llvm_index += 1;
10798 return .byval;
10799 }
10800 var llvm_types_buffer: [8]*llvm.Type = undefined;
10801 var llvm_types_index: u32 = 0;
10802 for (classes) |class| {
10803 switch (class) {
10804 .integer => {
10805 llvm_types_buffer[llvm_types_index] = it.dg.context.intType(64);
10806 llvm_types_index += 1;
10807 },
10808 .sse, .sseup => {
10809 llvm_types_buffer[llvm_types_index] = it.dg.context.doubleType();
10810 llvm_types_index += 1;
10811 },
10812 .float => {
10813 llvm_types_buffer[llvm_types_index] = it.dg.context.floatType();
10814 llvm_types_index += 1;
10815 },
10816 .float_combine => {
10817 llvm_types_buffer[llvm_types_index] = it.dg.context.floatType().vectorType(2);
10818 llvm_types_index += 1;
10819 },
10820 .x87 => {
10821 it.zig_index += 1;
10822 it.llvm_index += 1;
10823 it.byval_attr = true;
10824 return .byref;
10825 },
10826 .x87up => unreachable,
10827 .complex_x87 => {
10828 @panic("TODO");
10829 },
10830 .memory => unreachable, // handled above
10831 .win_i128 => unreachable, // windows only
10832 .none => break,
10833 }
10834 }
10835 if (classes[0] == .integer and classes[1] == .none) {
10836 it.zig_index += 1;
10837 it.llvm_index += 1;
10838 return .abi_sized_int;
10839 }
10840 it.llvm_types_buffer = llvm_types_buffer;
10841 it.llvm_types_len = llvm_types_index;
10842 it.llvm_index += llvm_types_index;
10843 it.zig_index += 1;
10844 return .multiple_llvm_types;
10845 }
1082710846};
1082810847
1082910848fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator {
src/crash_report.zig+2
......@@ -238,12 +238,14 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
238238 const ip = switch (native_os) {
239239 .macos => @intCast(usize, ctx.mcontext.ss.pc),
240240 .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.PC]),
241 .freebsd => @intCast(usize, ctx.mcontext.gpregs.elr),
241242 else => @intCast(usize, ctx.mcontext.pc),
242243 };
243244 // x29 is the ABI-designated frame pointer
244245 const bp = switch (native_os) {
245246 .macos => @intCast(usize, ctx.mcontext.ss.fp),
246247 .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.FP]),
248 .freebsd => @intCast(usize, ctx.mcontext.gpregs.x[os.REG.FP]),
247249 else => @intCast(usize, ctx.mcontext.regs[29]),
248250 };
249251 break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } };
src/link.zig+3
......@@ -697,6 +697,7 @@ pub const File = struct {
697697 /// TODO audit this error set. most of these should be collapsed into one error,
698698 /// and ErrorFlags should be updated to convey the meaning to the user.
699699 pub const FlushError = error{
700 BadDwarfCfi,
700701 CacheUnavailable,
701702 CurrentWorkingDirectoryUnlinked,
702703 DivisionByZero,
......@@ -737,6 +738,8 @@ pub const File = struct {
737738 MissingEndForExpression,
738739 /// TODO: this should be removed from the error set in favor of using ErrorFlags
739740 MissingMainEntrypoint,
741 /// TODO: this should be removed from the error set in favor of using ErrorFlags
742 MissingSection,
740743 MissingSymbol,
741744 MissingTableSymbols,
742745 ModuleNameMismatch,
src/link/MachO/Object.zig+432-94
......@@ -8,6 +8,7 @@ const std = @import("std");
88const build_options = @import("build_options");
99const assert = std.debug.assert;
1010const dwarf = std.dwarf;
11const eh_frame = @import("eh_frame.zig");
1112const fs = std.fs;
1213const io = std.io;
1314const log = std.log.scoped(.link);
......@@ -24,6 +25,7 @@ const DwarfInfo = @import("DwarfInfo.zig");
2425const LoadCommandIterator = macho.LoadCommandIterator;
2526const Zld = @import("zld.zig").Zld;
2627const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
28const UnwindInfo = @import("UnwindInfo.zig");
2729
2830name: []const u8,
2931mtime: u64,
......@@ -44,6 +46,8 @@ symtab: []macho.nlist_64 = undefined,
4446/// Can be undefined as set together with in_symtab.
4547source_symtab_lookup: []u32 = undefined,
4648/// Can be undefined as set together with in_symtab.
49reverse_symtab_lookup: []u32 = undefined,
50/// Can be undefined as set together with in_symtab.
4751source_address_lookup: []i64 = undefined,
4852/// Can be undefined as set together with in_symtab.
4953source_section_index_lookup: []i64 = undefined,
......@@ -53,22 +57,49 @@ strtab_lookup: []u32 = undefined,
5357atom_by_index_table: []AtomIndex = undefined,
5458/// Can be undefined as set together with in_symtab.
5559globals_lookup: []i64 = undefined,
60/// Can be undefined as set together with in_symtab.
61relocs_lookup: []RelocEntry = undefined,
5662
5763atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
64exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
65
66eh_frame_sect: ?macho.section_64 = null,
67eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
68eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},
69
70unwind_info_sect: ?macho.section_64 = null,
71unwind_relocs_lookup: []Record = undefined,
72unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
73
74const RelocEntry = struct { start: u32, len: u32 };
75
76const Record = struct {
77 dead: bool,
78 reloc: RelocEntry,
79};
5880
5981pub fn deinit(self: *Object, gpa: Allocator) void {
6082 self.atoms.deinit(gpa);
83 self.exec_atoms.deinit(gpa);
6184 gpa.free(self.name);
6285 gpa.free(self.contents);
6386 if (self.in_symtab) |_| {
6487 gpa.free(self.source_symtab_lookup);
88 gpa.free(self.reverse_symtab_lookup);
6589 gpa.free(self.source_address_lookup);
6690 gpa.free(self.source_section_index_lookup);
6791 gpa.free(self.strtab_lookup);
6892 gpa.free(self.symtab);
6993 gpa.free(self.atom_by_index_table);
7094 gpa.free(self.globals_lookup);
95 gpa.free(self.relocs_lookup);
7196 }
97 self.eh_frame_relocs_lookup.deinit(gpa);
98 self.eh_frame_records_lookup.deinit(gpa);
99 if (self.hasUnwindRecords()) {
100 gpa.free(self.unwind_relocs_lookup);
101 }
102 self.unwind_records_lookup.deinit(gpa);
72103}
73104
74105pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
......@@ -105,76 +136,95 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
105136 .ncmds = self.header.ncmds,
106137 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
107138 };
108 while (it.next()) |cmd| {
109 switch (cmd.cmd()) {
110 .SYMTAB => {
111 const symtab = cmd.cast(macho.symtab_command).?;
112 self.in_symtab = @ptrCast(
113 [*]const macho.nlist_64,
114 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
115 )[0..symtab.nsyms];
116 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
117
118 const nsects = self.getSourceSections().len;
119
120 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
121 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
122 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
123 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
124 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
125 // This is wasteful but we need to be able to lookup source symbol address after stripping and
126 // allocating of sections.
127 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
128 self.source_section_index_lookup = try allocator.alloc(i64, nsects);
129
130 for (self.symtab) |*sym| {
131 sym.* = .{
132 .n_value = 0,
133 .n_sect = 0,
134 .n_desc = 0,
135 .n_strx = 0,
136 .n_type = 0,
137 };
138 }
139 const nsects = self.getSourceSections().len;
140 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
141 .SYMTAB => break cmd.cast(macho.symtab_command).?,
142 else => {},
143 } else return;
144
145 self.in_symtab = @ptrCast(
146 [*]const macho.nlist_64,
147 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
148 )[0..symtab.nsyms];
149 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
150
151 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
152 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
153 self.reverse_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
154 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
155 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
156 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
157 self.relocs_lookup = try allocator.alloc(RelocEntry, self.in_symtab.?.len + nsects);
158 // This is wasteful but we need to be able to lookup source symbol address after stripping and
159 // allocating of sections.
160 self.source_address_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
161 self.source_section_index_lookup = try allocator.alloc(i64, nsects);
162
163 for (self.symtab) |*sym| {
164 sym.* = .{
165 .n_value = 0,
166 .n_sect = 0,
167 .n_desc = 0,
168 .n_strx = 0,
169 .n_type = 0,
170 };
171 }
139172
140 mem.set(i64, self.globals_lookup, -1);
141 mem.set(AtomIndex, self.atom_by_index_table, 0);
142 mem.set(i64, self.source_section_index_lookup, -1);
173 mem.set(i64, self.globals_lookup, -1);
174 mem.set(AtomIndex, self.atom_by_index_table, 0);
175 mem.set(i64, self.source_section_index_lookup, -1);
176 mem.set(RelocEntry, self.relocs_lookup, .{
177 .start = 0,
178 .len = 0,
179 });
143180
144 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
145 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
146 // the GO compiler does not necessarily respect that therefore we sort immediately by type
147 // and address within.
148 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
149 defer sorted_all_syms.deinit();
181 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
182 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
183 // the GO compiler does not necessarily respect that therefore we sort immediately by type
184 // and address within.
185 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
186 defer sorted_all_syms.deinit();
150187
151 for (self.in_symtab.?) |_, index| {
152 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
153 }
188 for (self.in_symtab.?) |_, index| {
189 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
190 }
154191
155 // We sort by type: defined < undefined, and
156 // afterwards by address in each group. Normally, dysymtab should
157 // be enough to guarantee the sort, but turns out not every compiler
158 // is kind enough to specify the symbols in the correct order.
159 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
192 // We sort by type: defined < undefined, and
193 // afterwards by address in each group. Normally, dysymtab should
194 // be enough to guarantee the sort, but turns out not every compiler
195 // is kind enough to specify the symbols in the correct order.
196 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
160197
161 for (sorted_all_syms.items) |sym_id, i| {
162 const sym = sym_id.getSymbol(self);
198 for (sorted_all_syms.items) |sym_id, i| {
199 const sym = sym_id.getSymbol(self);
163200
164 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {
165 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);
166 }
201 if (sym.sect() and self.source_section_index_lookup[sym.n_sect - 1] == -1) {
202 self.source_section_index_lookup[sym.n_sect - 1] = @intCast(i64, i);
203 }
167204
168 self.symtab[i] = sym;
169 self.source_symtab_lookup[i] = sym_id.index;
170 self.source_address_lookup[i] = if (sym.undf()) -1 else @intCast(i64, sym.n_value);
205 self.symtab[i] = sym;
206 self.source_symtab_lookup[i] = sym_id.index;
207 self.reverse_symtab_lookup[sym_id.index] = @intCast(u32, i);
208 self.source_address_lookup[i] = if (sym.undf()) -1 else @intCast(i64, sym.n_value);
171209
172 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;
173 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
174 }
210 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;
211 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
212 }
213
214 // Parse __TEXT,__eh_frame header if one exists
215 self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame");
216
217 // Parse __LD,__compact_unwind header if one exists
218 self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind");
219 if (self.hasUnwindRecords()) {
220 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
221 mem.set(Record, self.unwind_relocs_lookup, .{
222 .dead = true,
223 .reloc = .{
224 .start = 0,
225 .len = 0,
175226 },
176 else => {},
177 }
227 });
178228 }
179229}
180230
......@@ -192,6 +242,17 @@ const SymbolAtIndex = struct {
192242 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.in_strtab.?.ptr + off), 0);
193243 }
194244
245 fn getSymbolSeniority(self: SymbolAtIndex, ctx: Context) u2 {
246 const sym = self.getSymbol(ctx);
247 if (!sym.ext()) {
248 const sym_name = self.getSymbolName(ctx);
249 if (mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L")) return 0;
250 return 1;
251 }
252 if (sym.weakDef() or sym.pext()) return 2;
253 return 3;
254 }
255
195256 /// Performs lexicographic-like check.
196257 /// * lhs and rhs defined
197258 /// * if lhs == rhs
......@@ -206,23 +267,15 @@ const SymbolAtIndex = struct {
206267 if (lhs.sect() and rhs.sect()) {
207268 if (lhs.n_value == rhs.n_value) {
208269 if (lhs.n_sect == rhs.n_sect) {
209 if (lhs.ext() and rhs.ext()) {
210 if ((lhs.pext() or lhs.weakDef()) and (rhs.pext() or rhs.weakDef())) {
211 return false;
212 } else return rhs.pext() or rhs.weakDef();
213 } else {
214 const lhs_name = lhs_index.getSymbolName(ctx);
215 const lhs_temp = mem.startsWith(u8, lhs_name, "l") or mem.startsWith(u8, lhs_name, "L");
216 const rhs_name = rhs_index.getSymbolName(ctx);
217 const rhs_temp = mem.startsWith(u8, rhs_name, "l") or mem.startsWith(u8, rhs_name, "L");
218 if (lhs_temp and rhs_temp) {
219 return false;
220 } else return rhs_temp;
221 }
270 const lhs_senior = lhs_index.getSymbolSeniority(ctx);
271 const rhs_senior = rhs_index.getSymbolSeniority(ctx);
272 if (lhs_senior == rhs_senior) {
273 return lessThanByNStrx(ctx, lhs_index, rhs_index);
274 } else return lhs_senior < rhs_senior;
222275 } else return lhs.n_sect < rhs.n_sect;
223276 } else return lhs.n_value < rhs.n_value;
224277 } else if (lhs.undf() and rhs.undf()) {
225 return false;
278 return lessThanByNStrx(ctx, lhs_index, rhs_index);
226279 } else return rhs.undf();
227280 }
228281
......@@ -295,14 +348,20 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
295348 return lhs.header.addr < rhs.header.addr;
296349}
297350
298/// Splits input sections into Atoms.
351pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
352 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
353
354 try self.splitRegularSections(zld, object_id);
355 try self.parseEhFrameSection(zld, object_id);
356 try self.parseUnwindInfo(zld, object_id);
357}
358
359/// Splits input regular sections into Atoms.
299360/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
300361/// into subsections where each subsection then represents an Atom.
301pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
362pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
302363 const gpa = zld.gpa;
303364
304 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
305
306365 const sections = self.getSourceSections();
307366 for (sections) |sect, id| {
308367 if (sect.isDebug()) continue;
......@@ -418,6 +477,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
418477 sect.@"align",
419478 out_sect_id,
420479 );
480 if (!sect.isZerofill()) {
481 try self.cacheRelocs(zld, atom_index);
482 }
421483 zld.addAtomToSection(atom_index);
422484 }
423485
......@@ -431,7 +493,6 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
431493 const nsyms_trailing = atom_loc.len - 1;
432494 next_sym_index += atom_loc.len;
433495
434 // TODO: We want to bubble up the first externally defined symbol here.
435496 const atom_size = if (next_sym_index < sect_start_index + sect_loc.len)
436497 symtab[next_sym_index].n_value - addr
437498 else
......@@ -461,7 +522,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
461522 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
462523 self.atom_by_index_table[alias_index] = atom_index;
463524 }
464
525 if (!sect.isZerofill()) {
526 try self.cacheRelocs(zld, atom_index);
527 }
465528 zld.addAtomToSection(atom_index);
466529 }
467530 } else {
......@@ -476,6 +539,9 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
476539 sect.@"align",
477540 out_sect_id,
478541 );
542 if (!sect.isZerofill()) {
543 try self.cacheRelocs(zld, atom_index);
544 }
479545 zld.addAtomToSection(atom_index);
480546 }
481547 }
......@@ -484,7 +550,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
484550fn createAtomFromSubsection(
485551 self: *Object,
486552 zld: *Zld,
487 object_id: u31,
553 object_id: u32,
488554 sym_index: u32,
489555 inner_sym_index: u32,
490556 inner_nsyms_trailing: u32,
......@@ -497,7 +563,7 @@ fn createAtomFromSubsection(
497563 const atom = zld.getAtomPtr(atom_index);
498564 atom.inner_sym_index = inner_sym_index;
499565 atom.inner_nsyms_trailing = inner_nsyms_trailing;
500 atom.file = object_id;
566 atom.file = object_id + 1;
501567 self.symtab[sym_index].n_sect = out_sect_id + 1;
502568
503569 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
......@@ -519,9 +585,220 @@ fn createAtomFromSubsection(
519585 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
520586 }
521587
588 const out_sect = zld.sections.items(.header)[out_sect_id];
589 if (out_sect.isCode() and
590 mem.eql(u8, "__TEXT", out_sect.segName()) and
591 mem.eql(u8, "__text", out_sect.sectName()))
592 {
593 // TODO currently assuming a single section for executable machine code
594 try self.exec_atoms.append(gpa, atom_index);
595 }
596
522597 return atom_index;
523598}
524599
600fn filterRelocs(
601 relocs: []align(1) const macho.relocation_info,
602 start_addr: u64,
603 end_addr: u64,
604) RelocEntry {
605 const Predicate = struct {
606 addr: u64,
607
608 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
609 return rel.r_address >= self.addr;
610 }
611 };
612 const LPredicate = struct {
613 addr: u64,
614
615 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
616 return rel.r_address < self.addr;
617 }
618 };
619
620 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
621 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
622
623 return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };
624}
625
626fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
627 const atom = zld.getAtom(atom_index);
628
629 const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
630 const source_sect = self.getSourceSection(source_sym.n_sect - 1);
631 assert(!source_sect.isZerofill());
632 break :blk source_sect;
633 } else blk: {
634 // If there was no matching symbol present in the source symtab, this means
635 // we are dealing with either an entire section, or part of it, but also
636 // starting at the beginning.
637 const nbase = @intCast(u32, self.in_symtab.?.len);
638 const sect_id = @intCast(u16, atom.sym_index - nbase);
639 const source_sect = self.getSourceSection(sect_id);
640 assert(!source_sect.isZerofill());
641 break :blk source_sect;
642 };
643
644 const relocs = self.getRelocs(source_sect);
645
646 self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
647 const offset = source_sym.n_value - source_sect.addr;
648 break :blk filterRelocs(relocs, offset, offset + atom.size);
649 } else filterRelocs(relocs, 0, atom.size);
650}
651
652fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
653 const sect = self.eh_frame_sect orelse return;
654
655 log.debug("parsing __TEXT,__eh_frame section", .{});
656
657 if (zld.getSectionByName("__TEXT", "__eh_frame") == null) {
658 _ = try zld.initSection("__TEXT", "__eh_frame", .{});
659 }
660
661 const gpa = zld.gpa;
662 const cpu_arch = zld.options.target.cpu.arch;
663 const relocs = self.getRelocs(sect);
664
665 var it = self.getEhFrameRecordsIterator();
666 var record_count: u32 = 0;
667 while (try it.next()) |_| {
668 record_count += 1;
669 }
670
671 try self.eh_frame_relocs_lookup.ensureTotalCapacity(gpa, record_count);
672 try self.eh_frame_records_lookup.ensureTotalCapacity(gpa, record_count);
673
674 it.reset();
675
676 while (try it.next()) |record| {
677 const offset = it.pos - record.getSize();
678 const rel_pos = switch (cpu_arch) {
679 .aarch64 => filterRelocs(relocs, offset, offset + record.getSize()),
680 .x86_64 => RelocEntry{ .start = 0, .len = 0 },
681 else => unreachable,
682 };
683 self.eh_frame_relocs_lookup.putAssumeCapacityNoClobber(offset, .{
684 .dead = false,
685 .reloc = rel_pos,
686 });
687
688 if (record.tag == .fde) {
689 const target = blk: {
690 switch (cpu_arch) {
691 .aarch64 => {
692 assert(rel_pos.len > 0); // TODO convert to an error as the FDE eh frame is malformed
693 // Find function symbol that this record describes
694 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
695 const target = UnwindInfo.parseRelocTarget(
696 zld,
697 object_id,
698 rel,
699 it.data[offset..],
700 @intCast(i32, offset),
701 );
702 break :blk target;
703 },
704 .x86_64 => {
705 const target_address = record.getTargetSymbolAddress(.{
706 .base_addr = sect.addr,
707 .base_offset = offset,
708 });
709 const target_sym_index = self.getSymbolByAddress(target_address, null);
710 const target = if (self.getGlobal(target_sym_index)) |global_index|
711 zld.globals.items[global_index]
712 else
713 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
714 break :blk target;
715 },
716 else => unreachable,
717 }
718 };
719 log.debug("FDE at offset {x} tracks {s}", .{ offset, zld.getSymbolName(target) });
720 if (target.getFile() != object_id) {
721 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;
722 } else {
723 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
724 self.eh_frame_records_lookup.putAssumeCapacityNoClobber(atom_index, offset);
725 }
726 }
727 }
728}
729
730fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
731 const sect = self.unwind_info_sect orelse {
732 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
733 // we will try fully synthesising unwind info records to somewhat match Apple ld's
734 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
735 // we still create the output `__TEXT,__unwind_info` section.
736 if (self.eh_frame_sect != null) {
737 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
738 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
739 }
740 }
741 return;
742 };
743
744 log.debug("parsing unwind info in {s}", .{self.name});
745
746 const gpa = zld.gpa;
747 const cpu_arch = zld.options.target.cpu.arch;
748
749 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
750 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
751 }
752
753 try self.unwind_records_lookup.ensureTotalCapacity(gpa, @intCast(u32, self.exec_atoms.items.len));
754
755 const unwind_records = self.getUnwindRecords();
756
757 const needs_eh_frame = for (unwind_records) |record| {
758 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
759 } else false;
760
761 if (needs_eh_frame) {
762 if (self.eh_frame_sect == null) {
763 log.err("missing __TEXT,__eh_frame section", .{});
764 log.err(" in object {s}", .{self.name});
765 return error.MissingSection;
766 }
767 }
768
769 const relocs = self.getRelocs(sect);
770 for (unwind_records) |record, record_id| {
771 const offset = record_id * @sizeOf(macho.compact_unwind_entry);
772 const rel_pos = filterRelocs(
773 relocs,
774 offset,
775 offset + @sizeOf(macho.compact_unwind_entry),
776 );
777 assert(rel_pos.len > 0); // TODO convert to an error as the unwind info is malformed
778 self.unwind_relocs_lookup[record_id] = .{
779 .dead = false,
780 .reloc = rel_pos,
781 };
782
783 // Find function symbol that this record describes
784 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
785 const target = UnwindInfo.parseRelocTarget(
786 zld,
787 object_id,
788 rel,
789 mem.asBytes(&record),
790 @intCast(i32, offset),
791 );
792 log.debug("unwind record {d} tracks {s}", .{ record_id, zld.getSymbolName(target) });
793 if (target.getFile() != object_id) {
794 self.unwind_relocs_lookup[record_id].dead = true;
795 } else {
796 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
797 self.unwind_records_lookup.putAssumeCapacityNoClobber(atom_index, @intCast(u32, record_id));
798 }
799 }
800}
801
525802pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
526803 const symtab = self.in_symtab.?;
527804 if (index >= symtab.len) return null;
......@@ -529,23 +806,28 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
529806 return symtab[mapped_index];
530807}
531808
532/// Expects an arena allocator.
533/// Caller owns memory.
534pub fn createReverseSymbolLookup(self: Object, arena: Allocator) ![]u32 {
535 const symtab = self.in_symtab orelse return &[0]u32{};
536 const lookup = try arena.alloc(u32, symtab.len);
537 for (self.source_symtab_lookup) |source_id, id| {
538 lookup[source_id] = @intCast(u32, id);
539 }
540 return lookup;
541}
542
543809pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
544810 const sections = self.getSourceSections();
545811 assert(index < sections.len);
546812 return sections[index];
547813}
548814
815pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {
816 const sections = self.getSourceSections();
817 for (sections) |sect| {
818 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
819 return sect;
820 } else return null;
821}
822
823pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {
824 const sections = self.getSourceSections();
825 for (sections) |sect, i| {
826 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
827 return @intCast(u8, i + 1);
828 } else return null;
829}
830
549831pub fn getSourceSections(self: Object) []const macho.section_64 {
550832 var it = LoadCommandIterator{
551833 .ncmds = self.header.ncmds,
......@@ -652,8 +934,64 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {
652934 return strtab[start..][0 .. len - 1 :0];
653935}
654936
937pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
938 // Find containing atom
939 const Predicate = struct {
940 addr: i64,
941
942 pub fn predicate(pred: @This(), other: i64) bool {
943 return if (other == -1) true else other > pred.addr;
944 }
945 };
946
947 if (sect_hint) |sect_id| {
948 if (self.source_section_index_lookup[sect_id] > -1) {
949 const first_sym_index = @intCast(usize, self.source_section_index_lookup[sect_id]);
950 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup[first_sym_index..], Predicate{
951 .addr = @intCast(i64, addr),
952 });
953 if (target_sym_index > 0) {
954 return @intCast(u32, first_sym_index + target_sym_index - 1);
955 }
956 }
957 return self.getSectionAliasSymbolIndex(sect_id);
958 }
959
960 const target_sym_index = @import("zld.zig").lsearch(i64, self.source_address_lookup, Predicate{
961 .addr = @intCast(i64, addr),
962 });
963 assert(target_sym_index > 0);
964 return @intCast(u32, target_sym_index - 1);
965}
966
967pub fn getGlobal(self: Object, sym_index: u32) ?u32 {
968 if (self.globals_lookup[sym_index] == -1) return null;
969 return @intCast(u32, self.globals_lookup[sym_index]);
970}
971
655972pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
656973 const atom_index = self.atom_by_index_table[sym_index];
657974 if (atom_index == 0) return null;
658975 return atom_index;
659976}
977
978pub fn hasUnwindRecords(self: Object) bool {
979 return self.unwind_info_sect != null;
980}
981
982pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry {
983 const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{};
984 const data = self.getSectionContents(sect);
985 const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
986 return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];
987}
988
989pub fn hasEhFrameRecords(self: Object) bool {
990 return self.eh_frame_sect != null;
991}
992
993pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
994 const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} };
995 const data = self.getSectionContents(sect);
996 return .{ .data = data };
997}
src/link/MachO/UnwindInfo.zig created+845
......@@ -0,0 +1,845 @@
1const UnwindInfo = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
6const fs = std.fs;
7const leb = std.leb;
8const log = std.log.scoped(.unwind_info);
9const macho = std.macho;
10const math = std.math;
11const mem = std.mem;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15const Atom = @import("ZldAtom.zig");
16const AtomIndex = @import("zld.zig").AtomIndex;
17const EhFrameRecord = eh_frame.EhFrameRecord;
18const Object = @import("Object.zig");
19const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
20const Zld = @import("zld.zig").Zld;
21
22const N_DEAD = @import("zld.zig").N_DEAD;
23
24gpa: Allocator,
25
26/// List of all unwind records gathered from all objects and sorted
27/// by source function address.
28records: std.ArrayListUnmanaged(macho.compact_unwind_entry) = .{},
29records_lookup: std.AutoHashMapUnmanaged(AtomIndex, RecordIndex) = .{},
30
31/// List of all personalities referenced by either unwind info entries
32/// or __eh_frame entries.
33personalities: [max_personalities]SymbolWithLoc = undefined,
34personalities_count: u2 = 0,
35
36/// List of common encodings sorted in descending order with the most common first.
37common_encodings: [max_common_encodings]macho.compact_unwind_encoding_t = undefined,
38common_encodings_count: u7 = 0,
39
40/// List of record indexes containing an LSDA pointer.
41lsdas: std.ArrayListUnmanaged(RecordIndex) = .{},
42lsdas_lookup: std.AutoHashMapUnmanaged(RecordIndex, u32) = .{},
43
44/// List of second level pages.
45pages: std.ArrayListUnmanaged(Page) = .{},
46
47const RecordIndex = u32;
48
49const max_personalities = 3;
50const max_common_encodings = 127;
51const max_compact_encodings = 256;
52
53const second_level_page_bytes = 0x1000;
54const second_level_page_words = second_level_page_bytes / @sizeOf(u32);
55
56const max_regular_second_level_entries =
57 (second_level_page_bytes - @sizeOf(macho.unwind_info_regular_second_level_page_header)) /
58 @sizeOf(macho.unwind_info_regular_second_level_entry);
59
60const max_compressed_second_level_entries =
61 (second_level_page_bytes - @sizeOf(macho.unwind_info_compressed_second_level_page_header)) /
62 @sizeOf(u32);
63
64const compressed_entry_func_offset_mask = ~@as(u24, 0);
65
66const Page = struct {
67 kind: enum { regular, compressed },
68 start: RecordIndex,
69 count: u16,
70 page_encodings: [max_compact_encodings]RecordIndex = undefined,
71 page_encodings_count: u8 = 0,
72
73 fn appendPageEncoding(page: *Page, record_id: RecordIndex) void {
74 assert(page.page_encodings_count <= max_compact_encodings);
75 page.page_encodings[page.page_encodings_count] = record_id;
76 page.page_encodings_count += 1;
77 }
78
79 fn getPageEncoding(
80 page: *const Page,
81 info: *const UnwindInfo,
82 enc: macho.compact_unwind_encoding_t,
83 ) ?u8 {
84 comptime var index: u8 = 0;
85 inline while (index < max_compact_encodings) : (index += 1) {
86 if (index >= page.page_encodings_count) return null;
87 const record_id = page.page_encodings[index];
88 const record = info.records.items[record_id];
89 if (record.compactUnwindEncoding == enc) {
90 return index;
91 }
92 }
93 return null;
94 }
95
96 fn format(
97 page: *const Page,
98 comptime unused_format_string: []const u8,
99 options: std.fmt.FormatOptions,
100 writer: anytype,
101 ) !void {
102 _ = page;
103 _ = unused_format_string;
104 _ = options;
105 _ = writer;
106 @compileError("do not format Page directly; use page.fmtDebug()");
107 }
108
109 const DumpCtx = struct {
110 page: *const Page,
111 info: *const UnwindInfo,
112 };
113
114 fn dump(
115 ctx: DumpCtx,
116 comptime unused_format_string: []const u8,
117 options: std.fmt.FormatOptions,
118 writer: anytype,
119 ) @TypeOf(writer).Error!void {
120 _ = options;
121 comptime assert(unused_format_string.len == 0);
122 try writer.writeAll("Page:\n");
123 try writer.print(" kind: {s}\n", .{@tagName(ctx.page.kind)});
124 try writer.print(" entries: {d} - {d}\n", .{
125 ctx.page.start,
126 ctx.page.start + ctx.page.count,
127 });
128 try writer.print(" encodings (count = {d})\n", .{ctx.page.page_encodings_count});
129 for (ctx.page.page_encodings[0..ctx.page.page_encodings_count]) |record_id, i| {
130 const record = ctx.info.records.items[record_id];
131 const enc = record.compactUnwindEncoding;
132 try writer.print(" {d}: 0x{x:0>8}\n", .{ ctx.info.common_encodings_count + i, enc });
133 }
134 }
135
136 fn fmtDebug(page: *const Page, info: *const UnwindInfo) std.fmt.Formatter(dump) {
137 return .{ .data = .{
138 .page = page,
139 .info = info,
140 } };
141 }
142
143 fn write(page: *const Page, info: *const UnwindInfo, writer: anytype) !void {
144 switch (page.kind) {
145 .regular => {
146 try writer.writeStruct(macho.unwind_info_regular_second_level_page_header{
147 .entryPageOffset = @sizeOf(macho.unwind_info_regular_second_level_page_header),
148 .entryCount = page.count,
149 });
150
151 for (info.records.items[page.start..][0..page.count]) |record| {
152 try writer.writeStruct(macho.unwind_info_regular_second_level_entry{
153 .functionOffset = @intCast(u32, record.rangeStart),
154 .encoding = record.compactUnwindEncoding,
155 });
156 }
157 },
158 .compressed => {
159 const entry_offset = @sizeOf(macho.unwind_info_compressed_second_level_page_header) +
160 @intCast(u16, page.page_encodings_count) * @sizeOf(u32);
161 try writer.writeStruct(macho.unwind_info_compressed_second_level_page_header{
162 .entryPageOffset = entry_offset,
163 .entryCount = page.count,
164 .encodingsPageOffset = @sizeOf(
165 macho.unwind_info_compressed_second_level_page_header,
166 ),
167 .encodingsCount = page.page_encodings_count,
168 });
169
170 for (page.page_encodings[0..page.page_encodings_count]) |record_id| {
171 const enc = info.records.items[record_id].compactUnwindEncoding;
172 try writer.writeIntLittle(u32, enc);
173 }
174
175 assert(page.count > 0);
176 const first_entry = info.records.items[page.start];
177 for (info.records.items[page.start..][0..page.count]) |record| {
178 const enc_index = blk: {
179 if (info.getCommonEncoding(record.compactUnwindEncoding)) |id| {
180 break :blk id;
181 }
182 const ncommon = info.common_encodings_count;
183 break :blk ncommon + page.getPageEncoding(info, record.compactUnwindEncoding).?;
184 };
185 const compressed = macho.UnwindInfoCompressedEntry{
186 .funcOffset = @intCast(u24, record.rangeStart - first_entry.rangeStart),
187 .encodingIndex = @intCast(u8, enc_index),
188 };
189 try writer.writeStruct(compressed);
190 }
191 },
192 }
193 }
194};
195
196pub fn deinit(info: *UnwindInfo) void {
197 info.records.deinit(info.gpa);
198 info.records_lookup.deinit(info.gpa);
199 info.pages.deinit(info.gpa);
200 info.lsdas.deinit(info.gpa);
201 info.lsdas_lookup.deinit(info.gpa);
202}
203
204pub fn scanRelocs(zld: *Zld) !void {
205 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
206
207 const cpu_arch = zld.options.target.cpu.arch;
208 for (zld.objects.items) |*object, object_id| {
209 const unwind_records = object.getUnwindRecords();
210 for (object.exec_atoms.items) |atom_index| {
211 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
212 if (object.unwind_relocs_lookup[record_id].dead) continue;
213 const record = unwind_records[record_id];
214 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
215 if (getPersonalityFunctionReloc(
216 zld,
217 @intCast(u32, object_id),
218 record_id,
219 )) |rel| {
220 // Personality function; add GOT pointer.
221 const target = parseRelocTarget(
222 zld,
223 @intCast(u32, object_id),
224 rel,
225 mem.asBytes(&record),
226 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
227 );
228 try Atom.addGotEntry(zld, target);
229 }
230 }
231 }
232 }
233}
234
235pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
236 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) return;
237
238 const cpu_arch = zld.options.target.cpu.arch;
239
240 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
241 defer records.deinit();
242
243 var atom_indexes = std.ArrayList(AtomIndex).init(info.gpa);
244 defer atom_indexes.deinit();
245
246 // TODO handle dead stripping
247 for (zld.objects.items) |*object, object_id| {
248 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
249 const unwind_records = object.getUnwindRecords();
250
251 // Contents of unwind records does not have to cover all symbol in executable section
252 // so we need insert them ourselves.
253 try records.ensureUnusedCapacity(object.exec_atoms.items.len);
254 try atom_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
255
256 for (object.exec_atoms.items) |atom_index| {
257 var record = if (object.unwind_records_lookup.get(atom_index)) |record_id| blk: {
258 if (object.unwind_relocs_lookup[record_id].dead) continue;
259 var record = unwind_records[record_id];
260
261 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
262 try info.collectPersonalityFromDwarf(zld, @intCast(u32, object_id), atom_index, &record);
263 } else {
264 if (getPersonalityFunctionReloc(
265 zld,
266 @intCast(u32, object_id),
267 record_id,
268 )) |rel| {
269 const target = parseRelocTarget(
270 zld,
271 @intCast(u32, object_id),
272 rel,
273 mem.asBytes(&record),
274 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
275 );
276 const personality_index = info.getPersonalityFunction(target) orelse inner: {
277 const personality_index = info.personalities_count;
278 info.personalities[personality_index] = target;
279 info.personalities_count += 1;
280 break :inner personality_index;
281 };
282
283 record.personalityFunction = personality_index + 1;
284 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
285 }
286
287 if (getLsdaReloc(zld, @intCast(u32, object_id), record_id)) |rel| {
288 const target = parseRelocTarget(
289 zld,
290 @intCast(u32, object_id),
291 rel,
292 mem.asBytes(&record),
293 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
294 );
295 record.lsda = @bitCast(u64, target);
296 }
297 }
298 break :blk record;
299 } else blk: {
300 const atom = zld.getAtom(atom_index);
301 const sym = zld.getSymbol(atom.getSymbolWithLoc());
302 if (sym.n_desc == N_DEAD) continue;
303
304 if (!object.hasUnwindRecords()) {
305 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
306 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
307 var record = nullRecord();
308 try info.collectPersonalityFromDwarf(zld, @intCast(u32, object_id), atom_index, &record);
309 switch (cpu_arch) {
310 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
311 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
312 else => unreachable,
313 }
314 break :blk record;
315 }
316 }
317
318 break :blk nullRecord();
319 };
320
321 const atom = zld.getAtom(atom_index);
322 const sym_loc = atom.getSymbolWithLoc();
323 const sym = zld.getSymbol(sym_loc);
324 assert(sym.n_desc != N_DEAD);
325 record.rangeStart = sym.n_value;
326 record.rangeLength = @intCast(u32, atom.size);
327
328 records.appendAssumeCapacity(record);
329 atom_indexes.appendAssumeCapacity(atom_index);
330 }
331 }
332
333 // Fold records
334 try info.records.ensureTotalCapacity(info.gpa, records.items.len);
335 try info.records_lookup.ensureTotalCapacity(info.gpa, @intCast(u32, atom_indexes.items.len));
336
337 var maybe_prev: ?macho.compact_unwind_entry = null;
338 for (records.items) |record, i| {
339 const record_id = blk: {
340 if (maybe_prev) |prev| {
341 const is_dwarf = UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
342 if (is_dwarf or
343 (prev.compactUnwindEncoding != record.compactUnwindEncoding) or
344 (prev.personalityFunction != record.personalityFunction) or
345 record.lsda > 0)
346 {
347 const record_id = @intCast(RecordIndex, info.records.items.len);
348 info.records.appendAssumeCapacity(record);
349 maybe_prev = record;
350 break :blk record_id;
351 } else {
352 break :blk @intCast(RecordIndex, info.records.items.len - 1);
353 }
354 } else {
355 const record_id = @intCast(RecordIndex, info.records.items.len);
356 info.records.appendAssumeCapacity(record);
357 maybe_prev = record;
358 break :blk record_id;
359 }
360 };
361 info.records_lookup.putAssumeCapacityNoClobber(atom_indexes.items[i], record_id);
362 }
363
364 // Calculate common encodings
365 {
366 const CommonEncWithCount = struct {
367 enc: macho.compact_unwind_encoding_t,
368 count: u32,
369
370 fn greaterThan(ctx: void, lhs: @This(), rhs: @This()) bool {
371 _ = ctx;
372 return lhs.count > rhs.count;
373 }
374 };
375
376 const Context = struct {
377 pub fn hash(ctx: @This(), key: macho.compact_unwind_encoding_t) u32 {
378 _ = ctx;
379 return key;
380 }
381
382 pub fn eql(
383 ctx: @This(),
384 key1: macho.compact_unwind_encoding_t,
385 key2: macho.compact_unwind_encoding_t,
386 b_index: usize,
387 ) bool {
388 _ = ctx;
389 _ = b_index;
390 return key1 == key2;
391 }
392 };
393
394 var common_encodings_counts = std.ArrayHashMap(
395 macho.compact_unwind_encoding_t,
396 CommonEncWithCount,
397 Context,
398 false,
399 ).init(info.gpa);
400 defer common_encodings_counts.deinit();
401
402 for (info.records.items) |record| {
403 assert(!isNull(record));
404 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) continue;
405 const enc = record.compactUnwindEncoding;
406 const gop = try common_encodings_counts.getOrPut(enc);
407 if (!gop.found_existing) {
408 gop.value_ptr.* = .{
409 .enc = enc,
410 .count = 0,
411 };
412 }
413 gop.value_ptr.count += 1;
414 }
415
416 var slice = common_encodings_counts.values();
417 std.sort.sort(CommonEncWithCount, slice, {}, CommonEncWithCount.greaterThan);
418
419 var i: u7 = 0;
420 while (i < slice.len) : (i += 1) {
421 if (i >= max_common_encodings) break;
422 if (slice[i].count < 2) continue;
423 info.appendCommonEncoding(slice[i].enc);
424 log.debug("adding common encoding: {d} => 0x{x:0>8}", .{ i, slice[i].enc });
425 }
426 }
427
428 // Compute page allocations
429 {
430 var i: u32 = 0;
431 while (i < info.records.items.len) {
432 const range_start_max: u64 =
433 info.records.items[i].rangeStart + compressed_entry_func_offset_mask;
434 var encoding_count: u9 = info.common_encodings_count;
435 var space_left: u32 = second_level_page_words -
436 @sizeOf(macho.unwind_info_compressed_second_level_page_header) / @sizeOf(u32);
437 var page = Page{
438 .kind = undefined,
439 .start = i,
440 .count = 0,
441 };
442
443 while (space_left >= 1 and i < info.records.items.len) {
444 const record = info.records.items[i];
445 const enc = record.compactUnwindEncoding;
446 const is_dwarf = UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
447
448 if (record.rangeStart >= range_start_max) {
449 break;
450 } else if (info.getCommonEncoding(enc) != null or
451 page.getPageEncoding(info, enc) != null and !is_dwarf)
452 {
453 i += 1;
454 space_left -= 1;
455 } else if (space_left >= 2 and encoding_count < max_compact_encodings) {
456 page.appendPageEncoding(i);
457 i += 1;
458 space_left -= 2;
459 encoding_count += 1;
460 } else {
461 break;
462 }
463 }
464
465 page.count = @intCast(u16, i - page.start);
466
467 if (i < info.records.items.len and page.count < max_regular_second_level_entries) {
468 page.kind = .regular;
469 page.count = @intCast(u16, @min(
470 max_regular_second_level_entries,
471 info.records.items.len - page.start,
472 ));
473 i = page.start + page.count;
474 } else {
475 page.kind = .compressed;
476 }
477
478 log.debug("{}", .{page.fmtDebug(info)});
479
480 try info.pages.append(info.gpa, page);
481 }
482 }
483
484 // Save indices of records requiring LSDA relocation
485 try info.lsdas_lookup.ensureTotalCapacity(info.gpa, @intCast(u32, info.records.items.len));
486 for (info.records.items) |rec, i| {
487 info.lsdas_lookup.putAssumeCapacityNoClobber(@intCast(RecordIndex, i), @intCast(u32, info.lsdas.items.len));
488 if (rec.lsda == 0) continue;
489 try info.lsdas.append(info.gpa, @intCast(RecordIndex, i));
490 }
491}
492
493fn collectPersonalityFromDwarf(
494 info: *UnwindInfo,
495 zld: *Zld,
496 object_id: u32,
497 atom_index: u32,
498 record: *macho.compact_unwind_entry,
499) !void {
500 const object = &zld.objects.items[object_id];
501 var it = object.getEhFrameRecordsIterator();
502 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
503 it.seekTo(fde_offset);
504 const fde = (try it.next()).?;
505 const cie_ptr = fde.getCiePointer();
506 const cie_offset = fde_offset + 4 - cie_ptr;
507 it.seekTo(cie_offset);
508 const cie = (try it.next()).?;
509
510 if (cie.getPersonalityPointerReloc(
511 zld,
512 @intCast(u32, object_id),
513 cie_offset,
514 )) |target| {
515 const personality_index = info.getPersonalityFunction(target) orelse inner: {
516 const personality_index = info.personalities_count;
517 info.personalities[personality_index] = target;
518 info.personalities_count += 1;
519 break :inner personality_index;
520 };
521
522 record.personalityFunction = personality_index + 1;
523 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
524 }
525}
526
527pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
528 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
529 const sect = &zld.sections.items(.header)[sect_id];
530 sect.@"align" = 2;
531 sect.size = info.calcRequiredSize();
532}
533
534fn calcRequiredSize(info: UnwindInfo) usize {
535 var total_size: usize = 0;
536 total_size += @sizeOf(macho.unwind_info_section_header);
537 total_size +=
538 @intCast(usize, info.common_encodings_count) * @sizeOf(macho.compact_unwind_encoding_t);
539 total_size += @intCast(usize, info.personalities_count) * @sizeOf(u32);
540 total_size += (info.pages.items.len + 1) * @sizeOf(macho.unwind_info_section_header_index_entry);
541 total_size += info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry);
542 total_size += info.pages.items.len * second_level_page_bytes;
543 return total_size;
544}
545
546pub fn write(info: *UnwindInfo, zld: *Zld) !void {
547 const sect_id = zld.getSectionByName("__TEXT", "__unwind_info") orelse return;
548 const sect = &zld.sections.items(.header)[sect_id];
549 const seg_id = zld.sections.items(.segment_index)[sect_id];
550 const seg = zld.segments.items[seg_id];
551
552 const text_sect_id = zld.getSectionByName("__TEXT", "__text").?;
553 const text_sect = zld.sections.items(.header)[text_sect_id];
554
555 var personalities: [max_personalities]u32 = undefined;
556 const cpu_arch = zld.options.target.cpu.arch;
557
558 log.debug("Personalities:", .{});
559 for (info.personalities[0..info.personalities_count]) |target, i| {
560 const atom_index = zld.getGotAtomIndexForSymbol(target).?;
561 const atom = zld.getAtom(atom_index);
562 const sym = zld.getSymbol(atom.getSymbolWithLoc());
563 personalities[i] = @intCast(u32, sym.n_value - seg.vmaddr);
564 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
565 }
566
567 for (info.records.items) |*rec| {
568 // Finalize missing address values
569 rec.rangeStart += text_sect.addr - seg.vmaddr;
570 if (rec.personalityFunction > 0) {
571 const index = math.cast(usize, rec.personalityFunction - 1) orelse return error.Overflow;
572 rec.personalityFunction = personalities[index];
573 }
574
575 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
576 const lsda_target = @bitCast(SymbolWithLoc, rec.lsda);
577 if (lsda_target.getFile()) |_| {
578 const sym = zld.getSymbol(lsda_target);
579 rec.lsda = sym.n_value - seg.vmaddr;
580 }
581 }
582 }
583
584 for (info.records.items) |record, i| {
585 log.debug("Unwind record at offset 0x{x}", .{i * @sizeOf(macho.compact_unwind_entry)});
586 log.debug(" start: 0x{x}", .{record.rangeStart});
587 log.debug(" length: 0x{x}", .{record.rangeLength});
588 log.debug(" compact encoding: 0x{x:0>8}", .{record.compactUnwindEncoding});
589 log.debug(" personality: 0x{x}", .{record.personalityFunction});
590 log.debug(" LSDA: 0x{x}", .{record.lsda});
591 }
592
593 var buffer = std.ArrayList(u8).init(info.gpa);
594 defer buffer.deinit();
595
596 const size = info.calcRequiredSize();
597 try buffer.ensureTotalCapacityPrecise(size);
598
599 var cwriter = std.io.countingWriter(buffer.writer());
600 const writer = cwriter.writer();
601
602 const common_encodings_offset: u32 = @sizeOf(macho.unwind_info_section_header);
603 const common_encodings_count: u32 = info.common_encodings_count;
604 const personalities_offset: u32 = common_encodings_offset + common_encodings_count * @sizeOf(u32);
605 const personalities_count: u32 = info.personalities_count;
606 const indexes_offset: u32 = personalities_offset + personalities_count * @sizeOf(u32);
607 const indexes_count: u32 = @intCast(u32, info.pages.items.len + 1);
608
609 try writer.writeStruct(macho.unwind_info_section_header{
610 .commonEncodingsArraySectionOffset = common_encodings_offset,
611 .commonEncodingsArrayCount = common_encodings_count,
612 .personalityArraySectionOffset = personalities_offset,
613 .personalityArrayCount = personalities_count,
614 .indexSectionOffset = indexes_offset,
615 .indexCount = indexes_count,
616 });
617
618 try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count]));
619 try writer.writeAll(mem.sliceAsBytes(personalities[0..info.personalities_count]));
620
621 const pages_base_offset = @intCast(u32, size - (info.pages.items.len * second_level_page_bytes));
622 const lsda_base_offset = @intCast(u32, pages_base_offset -
623 (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry)));
624 for (info.pages.items) |page, i| {
625 assert(page.count > 0);
626 const first_entry = info.records.items[page.start];
627 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
628 .functionOffset = @intCast(u32, first_entry.rangeStart),
629 .secondLevelPagesSectionOffset = @intCast(u32, pages_base_offset + i * second_level_page_bytes),
630 .lsdaIndexArraySectionOffset = lsda_base_offset +
631 info.lsdas_lookup.get(page.start).? * @sizeOf(macho.unwind_info_section_header_lsda_index_entry),
632 });
633 }
634
635 const last_entry = info.records.items[info.records.items.len - 1];
636 const sentinel_address = @intCast(u32, last_entry.rangeStart + last_entry.rangeLength);
637 try writer.writeStruct(macho.unwind_info_section_header_index_entry{
638 .functionOffset = sentinel_address,
639 .secondLevelPagesSectionOffset = 0,
640 .lsdaIndexArraySectionOffset = lsda_base_offset +
641 @intCast(u32, info.lsdas.items.len) * @sizeOf(macho.unwind_info_section_header_lsda_index_entry),
642 });
643
644 for (info.lsdas.items) |record_id| {
645 const record = info.records.items[record_id];
646 try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{
647 .functionOffset = @intCast(u32, record.rangeStart),
648 .lsdaOffset = @intCast(u32, record.lsda),
649 });
650 }
651
652 for (info.pages.items) |page| {
653 const start = cwriter.bytes_written;
654 try page.write(info, writer);
655 const nwritten = cwriter.bytes_written - start;
656 if (nwritten < second_level_page_bytes) {
657 const offset = math.cast(usize, second_level_page_bytes - nwritten) orelse return error.Overflow;
658 try writer.writeByteNTimes(0, offset);
659 }
660 }
661
662 const padding = buffer.items.len - cwriter.bytes_written;
663 if (padding > 0) {
664 const offset = math.cast(usize, cwriter.bytes_written) orelse return error.Overflow;
665 mem.set(u8, buffer.items[offset..], 0);
666 }
667
668 try zld.file.pwriteAll(buffer.items, sect.offset);
669}
670
671pub fn parseRelocTarget(
672 zld: *Zld,
673 object_id: u32,
674 rel: macho.relocation_info,
675 code: []const u8,
676 base_offset: i32,
677) SymbolWithLoc {
678 const tracy = trace(@src());
679 defer tracy.end();
680
681 const object = &zld.objects.items[object_id];
682
683 const sym_index = if (rel.r_extern == 0) blk: {
684 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
685 const rel_offset = @intCast(u32, rel.r_address - base_offset);
686 assert(rel.r_pcrel == 0 and rel.r_length == 3);
687 const address_in_section = mem.readIntLittle(u64, code[rel_offset..][0..8]);
688 const sym_index = object.getSymbolByAddress(address_in_section, sect_id);
689 break :blk sym_index;
690 } else object.reverse_symtab_lookup[rel.r_symbolnum];
691
692 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
693 const sym = zld.getSymbol(sym_loc);
694
695 if (sym.sect() and !sym.ext()) {
696 // Make sure we are not dealing with a local alias.
697 const atom_index = object.getAtomIndexForSymbol(sym_index) orelse
698 return sym_loc;
699 const atom = zld.getAtom(atom_index);
700 return atom.getSymbolWithLoc();
701 } else if (object.getGlobal(sym_index)) |global_index| {
702 return zld.globals.items[global_index];
703 } else return sym_loc;
704}
705
706fn getRelocs(
707 zld: *Zld,
708 object_id: u32,
709 record_id: usize,
710) []align(1) const macho.relocation_info {
711 const object = &zld.objects.items[object_id];
712 assert(object.hasUnwindRecords());
713 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
714 const relocs = object.getRelocs(object.unwind_info_sect.?);
715 return relocs[rel_pos.start..][0..rel_pos.len];
716}
717
718fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
719 const base_offset = @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry));
720 const rel_offset = rel.r_address - base_offset;
721 return rel_offset == 16;
722}
723
724pub fn getPersonalityFunctionReloc(
725 zld: *Zld,
726 object_id: u32,
727 record_id: usize,
728) ?macho.relocation_info {
729 const relocs = getRelocs(zld, object_id, record_id);
730 for (relocs) |rel| {
731 if (isPersonalityFunction(record_id, rel)) return rel;
732 }
733 return null;
734}
735
736fn getPersonalityFunction(info: UnwindInfo, global_index: SymbolWithLoc) ?u2 {
737 comptime var index: u2 = 0;
738 inline while (index < max_personalities) : (index += 1) {
739 if (index >= info.personalities_count) return null;
740 if (info.personalities[index].eql(global_index)) {
741 return index;
742 }
743 }
744 return null;
745}
746
747fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
748 const base_offset = @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry));
749 const rel_offset = rel.r_address - base_offset;
750 return rel_offset == 24;
751}
752
753pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
754 const relocs = getRelocs(zld, object_id, record_id);
755 for (relocs) |rel| {
756 if (isLsda(record_id, rel)) return rel;
757 }
758 return null;
759}
760
761pub fn isNull(rec: macho.compact_unwind_entry) bool {
762 return rec.rangeStart == 0 and
763 rec.rangeLength == 0 and
764 rec.compactUnwindEncoding == 0 and
765 rec.lsda == 0 and
766 rec.personalityFunction == 0;
767}
768
769inline fn nullRecord() macho.compact_unwind_entry {
770 return .{
771 .rangeStart = 0,
772 .rangeLength = 0,
773 .compactUnwindEncoding = 0,
774 .personalityFunction = 0,
775 .lsda = 0,
776 };
777}
778
779fn appendCommonEncoding(info: *UnwindInfo, enc: macho.compact_unwind_encoding_t) void {
780 assert(info.common_encodings_count <= max_common_encodings);
781 info.common_encodings[info.common_encodings_count] = enc;
782 info.common_encodings_count += 1;
783}
784
785fn getCommonEncoding(info: UnwindInfo, enc: macho.compact_unwind_encoding_t) ?u7 {
786 comptime var index: u7 = 0;
787 inline while (index < max_common_encodings) : (index += 1) {
788 if (index >= info.common_encodings_count) return null;
789 if (info.common_encodings[index] == enc) {
790 return index;
791 }
792 }
793 return null;
794}
795
796pub const UnwindEncoding = struct {
797 pub fn getMode(enc: macho.compact_unwind_encoding_t) u4 {
798 comptime assert(macho.UNWIND_ARM64_MODE_MASK == macho.UNWIND_X86_64_MODE_MASK);
799 return @truncate(u4, (enc & macho.UNWIND_ARM64_MODE_MASK) >> 24);
800 }
801
802 pub fn isDwarf(enc: macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch) bool {
803 const mode = getMode(enc);
804 return switch (cpu_arch) {
805 .aarch64 => @intToEnum(macho.UNWIND_ARM64_MODE, mode) == .DWARF,
806 .x86_64 => @intToEnum(macho.UNWIND_X86_64_MODE, mode) == .DWARF,
807 else => unreachable,
808 };
809 }
810
811 pub fn setMode(enc: *macho.compact_unwind_encoding_t, mode: anytype) void {
812 enc.* |= @intCast(u32, @enumToInt(mode)) << 24;
813 }
814
815 pub fn hasLsda(enc: macho.compact_unwind_encoding_t) bool {
816 const has_lsda = @truncate(u1, (enc & macho.UNWIND_HAS_LSDA) >> 31);
817 return has_lsda == 1;
818 }
819
820 pub fn setHasLsda(enc: *macho.compact_unwind_encoding_t, has_lsda: bool) void {
821 const mask = @intCast(u32, @boolToInt(has_lsda)) << 31;
822 enc.* |= mask;
823 }
824
825 pub fn getPersonalityIndex(enc: macho.compact_unwind_encoding_t) u2 {
826 const index = @truncate(u2, (enc & macho.UNWIND_PERSONALITY_MASK) >> 28);
827 return index;
828 }
829
830 pub fn setPersonalityIndex(enc: *macho.compact_unwind_encoding_t, index: u2) void {
831 const mask = @intCast(u32, index) << 28;
832 enc.* |= mask;
833 }
834
835 pub fn getDwarfSectionOffset(enc: macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch) u24 {
836 assert(isDwarf(enc, cpu_arch));
837 const offset = @truncate(u24, enc);
838 return offset;
839 }
840
841 pub fn setDwarfSectionOffset(enc: *macho.compact_unwind_encoding_t, cpu_arch: std.Target.Cpu.Arch, offset: u24) void {
842 assert(isDwarf(enc.*, cpu_arch));
843 enc.* |= offset;
844 }
845};
src/link/MachO/ZldAtom.zig+78-166
......@@ -29,11 +29,11 @@ const Zld = @import("zld.zig").Zld;
2929/// a stub trampoline, it can be found in the linkers `locals` arraylist.
3030sym_index: u32,
3131
32/// -1 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
33/// Otherwise, it is the index into appropriate object file.
32/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
33/// Otherwise, it is the index into appropriate object file (indexing from 1).
3434/// Prefer using `getFile()` helper to get the file index out rather than using
3535/// the field directly.
36file: i32,
36file: u32,
3737
3838/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
3939/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
......@@ -51,13 +51,6 @@ size: u64,
5151/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
5252alignment: u32,
5353
54/// Cached index and length into the relocations records array that correspond to
55/// this Atom and need to be resolved before the Atom can be committed into the
56/// final linked image.
57/// Do not use these fields directly. Instead, use `getAtomRelocs()` helper.
58cached_relocs_start: i32,
59cached_relocs_len: u32,
60
6154/// Points to the previous and next neighbours
6255next_index: ?AtomIndex,
6356prev_index: ?AtomIndex,
......@@ -66,20 +59,18 @@ pub const empty = Atom{
6659 .sym_index = 0,
6760 .inner_sym_index = 0,
6861 .inner_nsyms_trailing = 0,
69 .file = -1,
62 .file = 0,
7063 .size = 0,
7164 .alignment = 0,
72 .cached_relocs_start = -1,
73 .cached_relocs_len = 0,
7465 .prev_index = null,
7566 .next_index = null,
7667};
7768
7869/// Returns `null` if the Atom is a synthetic Atom.
7970/// Otherwise, returns an index into an array of Objects.
80pub inline fn getFile(self: Atom) ?u31 {
81 if (self.file == -1) return null;
82 return @intCast(u31, self.file);
71pub fn getFile(self: Atom) ?u32 {
72 if (self.file == 0) return null;
73 return self.file - 1;
8374}
8475
8576pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
......@@ -92,7 +83,7 @@ pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
9283const InnerSymIterator = struct {
9384 sym_index: u32,
9485 count: u32,
95 file: i32,
86 file: u32,
9687
9788 pub fn next(it: *@This()) ?SymbolWithLoc {
9889 if (it.count == 0) return null;
......@@ -159,19 +150,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u
159150 return source_sym.n_value - base_addr;
160151}
161152
162pub fn scanAtomRelocs(
163 zld: *Zld,
164 atom_index: AtomIndex,
165 relocs: []align(1) const macho.relocation_info,
166 reverse_lookup: []u32,
167) !void {
153pub fn scanAtomRelocs(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
168154 const arch = zld.options.target.cpu.arch;
169155 const atom = zld.getAtom(atom_index);
170156 assert(atom.getFile() != null); // synthetic atoms do not have relocs
171157
172158 return switch (arch) {
173 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs, reverse_lookup),
174 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs, reverse_lookup),
159 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
160 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
175161 else => unreachable,
176162 };
177163}
......@@ -202,16 +188,11 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
202188 };
203189}
204190
205pub fn parseRelocTarget(
206 zld: *Zld,
207 atom_index: AtomIndex,
208 rel: macho.relocation_info,
209 reverse_lookup: []u32,
210) SymbolWithLoc {
191pub fn parseRelocTarget(zld: *Zld, atom_index: AtomIndex, rel: macho.relocation_info) SymbolWithLoc {
211192 const atom = zld.getAtom(atom_index);
212193 const object = &zld.objects.items[atom.getFile().?];
213194
214 if (rel.r_extern == 0) {
195 const sym_index = if (rel.r_extern == 0) sym_index: {
215196 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
216197 const ctx = getRelocContext(zld, atom_index);
217198 const atom_code = getAtomCode(zld, atom_index);
......@@ -219,9 +200,9 @@ pub fn parseRelocTarget(
219200
220201 const address_in_section = if (rel.r_pcrel == 0) blk: {
221202 break :blk if (rel.r_length == 3)
222 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
203 mem.readIntLittle(u64, atom_code[rel_offset..][0..8])
223204 else
224 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
205 mem.readIntLittle(u32, atom_code[rel_offset..][0..4]);
225206 } else blk: {
226207 const correction: u3 = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
227208 .X86_64_RELOC_SIGNED => 0,
......@@ -232,38 +213,14 @@ pub fn parseRelocTarget(
232213 };
233214 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
234215 const target_address = @intCast(i64, ctx.base_addr) + rel.r_address + 4 + correction + addend;
235 break :blk target_address;
216 break :blk @intCast(u64, target_address);
236217 };
237218
238219 // Find containing atom
239 const Predicate = struct {
240 addr: i64,
241
242 pub fn predicate(pred: @This(), other: i64) bool {
243 return if (other == -1) true else other > pred.addr;
244 }
245 };
246
247 if (object.source_section_index_lookup[sect_id] > -1) {
248 const first_sym_index = @intCast(usize, object.source_section_index_lookup[sect_id]);
249 const target_sym_index = @import("zld.zig").lsearch(i64, object.source_address_lookup[first_sym_index..], Predicate{
250 .addr = address_in_section,
251 });
252
253 if (target_sym_index > 0) {
254 return SymbolWithLoc{
255 .sym_index = @intCast(u32, first_sym_index + target_sym_index - 1),
256 .file = atom.file,
257 };
258 }
259 }
260
261 // Start of section is not contained anywhere, return synthetic atom.
262 const sym_index = object.getSectionAliasSymbolIndex(sect_id);
263 return SymbolWithLoc{ .sym_index = sym_index, .file = atom.file };
264 }
220 const sym_index = object.getSymbolByAddress(address_in_section, sect_id);
221 break :sym_index sym_index;
222 } else object.reverse_symtab_lookup[rel.r_symbolnum];
265223
266 const sym_index = reverse_lookup[rel.r_symbolnum];
267224 const sym_loc = SymbolWithLoc{
268225 .sym_index = sym_index,
269226 .file = atom.file,
......@@ -272,30 +229,12 @@ pub fn parseRelocTarget(
272229
273230 if (sym.sect() and !sym.ext()) {
274231 return sym_loc;
275 } else if (object.globals_lookup[sym_index] > -1) {
276 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
232 } else if (object.getGlobal(sym_index)) |global_index| {
277233 return zld.globals.items[global_index];
278234 } else return sym_loc;
279235}
280236
281pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc) ?AtomIndex {
282 const is_via_got = got: {
283 switch (zld.options.target.cpu.arch) {
284 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
285 .ARM64_RELOC_GOT_LOAD_PAGE21,
286 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
287 .ARM64_RELOC_POINTER_TO_GOT,
288 => true,
289 else => false,
290 },
291 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
292 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
293 else => false,
294 },
295 else => unreachable,
296 }
297 };
298
237pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?AtomIndex {
299238 if (is_via_got) {
300239 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
301240 }
......@@ -314,12 +253,7 @@ pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: Sy
314253 return object.getAtomIndexForSymbol(target.sym_index);
315254}
316255
317fn scanAtomRelocsArm64(
318 zld: *Zld,
319 atom_index: AtomIndex,
320 relocs: []align(1) const macho.relocation_info,
321 reverse_lookup: []u32,
322) !void {
256fn scanAtomRelocsArm64(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
323257 for (relocs) |rel| {
324258 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
325259
......@@ -332,7 +266,7 @@ fn scanAtomRelocsArm64(
332266
333267 const atom = zld.getAtom(atom_index);
334268 const object = &zld.objects.items[atom.getFile().?];
335 const sym_index = reverse_lookup[rel.r_symbolnum];
269 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
336270 const sym_loc = SymbolWithLoc{
337271 .sym_index = sym_index,
338272 .file = atom.file,
......@@ -341,10 +275,10 @@ fn scanAtomRelocsArm64(
341275
342276 if (sym.sect() and !sym.ext()) continue;
343277
344 const target = if (object.globals_lookup[sym_index] > -1) blk: {
345 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
346 break :blk zld.globals.items[global_index];
347 } else sym_loc;
278 const target = if (object.getGlobal(sym_index)) |global_index|
279 zld.globals.items[global_index]
280 else
281 sym_loc;
348282
349283 switch (rel_type) {
350284 .ARM64_RELOC_BRANCH26 => {
......@@ -368,12 +302,7 @@ fn scanAtomRelocsArm64(
368302 }
369303}
370304
371fn scanAtomRelocsX86(
372 zld: *Zld,
373 atom_index: AtomIndex,
374 relocs: []align(1) const macho.relocation_info,
375 reverse_lookup: []u32,
376) !void {
305fn scanAtomRelocsX86(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
377306 for (relocs) |rel| {
378307 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
379308
......@@ -386,7 +315,7 @@ fn scanAtomRelocsX86(
386315
387316 const atom = zld.getAtom(atom_index);
388317 const object = &zld.objects.items[atom.getFile().?];
389 const sym_index = reverse_lookup[rel.r_symbolnum];
318 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
390319 const sym_loc = SymbolWithLoc{
391320 .sym_index = sym_index,
392321 .file = atom.file,
......@@ -395,10 +324,10 @@ fn scanAtomRelocsX86(
395324
396325 if (sym.sect() and !sym.ext()) continue;
397326
398 const target = if (object.globals_lookup[sym_index] > -1) blk: {
399 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
400 break :blk zld.globals.items[global_index];
401 } else sym_loc;
327 const target = if (object.getGlobal(sym_index)) |global_index|
328 zld.globals.items[global_index]
329 else
330 sym_loc;
402331
403332 switch (rel_type) {
404333 .X86_64_RELOC_BRANCH => {
......@@ -432,7 +361,7 @@ fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
432361 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
433362}
434363
435fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
364pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
436365 if (zld.got_table.contains(target)) return;
437366 const gpa = zld.gpa;
438367 const atom_index = try zld.createGotAtom();
......@@ -466,7 +395,6 @@ pub fn resolveRelocs(
466395 atom_index: AtomIndex,
467396 atom_code: []u8,
468397 atom_relocs: []align(1) const macho.relocation_info,
469 reverse_lookup: []u32,
470398) !void {
471399 const arch = zld.options.target.cpu.arch;
472400 const atom = zld.getAtom(atom_index);
......@@ -480,14 +408,14 @@ pub fn resolveRelocs(
480408 const ctx = getRelocContext(zld, atom_index);
481409
482410 return switch (arch) {
483 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
484 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
411 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
412 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
485413 else => unreachable,
486414 };
487415}
488416
489pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc, is_tlv: bool) !u64 {
490 const target_atom_index = getRelocTargetAtomIndex(zld, rel, target) orelse {
417pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
418 const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
491419 // If there is no atom for target, we still need to check for special, atom-less
492420 // symbols such as `___dso_handle`.
493421 const target_name = zld.getSymbolName(target);
......@@ -499,7 +427,7 @@ pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: Symb
499427 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
500428 target_atom.sym_index,
501429 zld.getSymbolName(target_atom.getSymbolWithLoc()),
502 target_atom.file,
430 target_atom.getFile(),
503431 });
504432
505433 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
......@@ -541,7 +469,6 @@ fn resolveRelocsArm64(
541469 atom_index: AtomIndex,
542470 atom_code: []u8,
543471 atom_relocs: []align(1) const macho.relocation_info,
544 reverse_lookup: []u32,
545472 context: RelocContext,
546473) !void {
547474 const atom = zld.getAtom(atom_index);
......@@ -565,20 +492,20 @@ fn resolveRelocsArm64(
565492 .ARM64_RELOC_SUBTRACTOR => {
566493 assert(subtractor == null);
567494
568 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
495 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
569496 @tagName(rel_type),
570497 rel.r_address,
571498 rel.r_symbolnum,
572 atom.file,
499 atom.getFile(),
573500 });
574501
575 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
502 subtractor = parseRelocTarget(zld, atom_index, rel);
576503 continue;
577504 },
578505 else => {},
579506 }
580507
581 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
508 const target = parseRelocTarget(zld, atom_index, rel);
582509 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
583510
584511 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
......@@ -586,19 +513,20 @@ fn resolveRelocsArm64(
586513 rel.r_address,
587514 target.sym_index,
588515 zld.getSymbolName(target),
589 target.file,
516 target.getFile(),
590517 });
591518
592519 const source_addr = blk: {
593520 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
594521 break :blk source_sym.n_value + rel_offset;
595522 };
523 const is_via_got = relocRequiresGot(zld, rel);
596524 const is_tlv = is_tlv: {
597525 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
598526 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
599527 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
600528 };
601 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);
529 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
602530
603531 log.debug(" | source_addr = 0x{x}", .{source_addr});
604532
......@@ -610,9 +538,9 @@ fn resolveRelocsArm64(
610538 } else target;
611539 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
612540 zld.getSymbolName(atom.getSymbolWithLoc()),
613 atom.file,
541 atom.getFile(),
614542 zld.getSymbolName(target),
615 zld.getAtom(getRelocTargetAtomIndex(zld, rel, target).?).file,
543 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
616544 });
617545
618546 const displacement = if (calcPcRelativeDisplacementArm64(
......@@ -628,7 +556,7 @@ fn resolveRelocsArm64(
628556 zld,
629557 actual_target,
630558 ).?);
631 log.debug(" | target_addr = 0x{x}", .{thunk_sym.n_value});
559 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
632560 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
633561 };
634562
......@@ -832,7 +760,6 @@ fn resolveRelocsX86(
832760 atom_index: AtomIndex,
833761 atom_code: []u8,
834762 atom_relocs: []align(1) const macho.relocation_info,
835 reverse_lookup: []u32,
836763 context: RelocContext,
837764) !void {
838765 const atom = zld.getAtom(atom_index);
......@@ -847,33 +774,34 @@ fn resolveRelocsX86(
847774 .X86_64_RELOC_SUBTRACTOR => {
848775 assert(subtractor == null);
849776
850 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
777 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
851778 @tagName(rel_type),
852779 rel.r_address,
853780 rel.r_symbolnum,
854 atom.file,
781 atom.getFile(),
855782 });
856783
857 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
784 subtractor = parseRelocTarget(zld, atom_index, rel);
858785 continue;
859786 },
860787 else => {},
861788 }
862789
863 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
790 const target = parseRelocTarget(zld, atom_index, rel);
864791 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
865792
866793 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{
867794 @tagName(rel_type),
868795 rel.r_address,
869796 target.sym_index,
870 target.file,
797 target.getFile(),
871798 });
872799
873800 const source_addr = blk: {
874801 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
875802 break :blk source_sym.n_value + rel_offset;
876803 };
804 const is_via_got = relocRequiresGot(zld, rel);
877805 const is_tlv = is_tlv: {
878806 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
879807 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
......@@ -882,7 +810,7 @@ fn resolveRelocsX86(
882810
883811 log.debug(" | source_addr = 0x{x}", .{source_addr});
884812
885 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);
813 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
886814
887815 switch (rel_type) {
888816 .X86_64_RELOC_BRANCH => {
......@@ -1016,9 +944,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
1016944}
1017945
1018946pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {
1019 const atom = zld.getAtomPtr(atom_index);
947 const atom = zld.getAtom(atom_index);
1020948 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
1021949 const object = zld.objects.items[atom.getFile().?];
950 const cache = object.relocs_lookup[atom.sym_index];
1022951
1023952 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1024953 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
......@@ -1036,43 +965,7 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.re
1036965 };
1037966
1038967 const relocs = object.getRelocs(source_sect);
1039
1040 if (atom.cached_relocs_start == -1) {
1041 const indexes = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1042 const offset = source_sym.n_value - source_sect.addr;
1043 break :blk filterRelocs(relocs, offset, offset + atom.size);
1044 } else filterRelocs(relocs, 0, atom.size);
1045 atom.cached_relocs_start = indexes.start;
1046 atom.cached_relocs_len = indexes.len;
1047 }
1048
1049 return relocs[@intCast(u32, atom.cached_relocs_start)..][0..atom.cached_relocs_len];
1050}
1051
1052fn filterRelocs(
1053 relocs: []align(1) const macho.relocation_info,
1054 start_addr: u64,
1055 end_addr: u64,
1056) struct { start: i32, len: u32 } {
1057 const Predicate = struct {
1058 addr: u64,
1059
1060 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1061 return rel.r_address >= self.addr;
1062 }
1063 };
1064 const LPredicate = struct {
1065 addr: u64,
1066
1067 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1068 return rel.r_address < self.addr;
1069 }
1070 };
1071
1072 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
1073 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
1074
1075 return .{ .start = @intCast(i32, start), .len = @intCast(u32, len) };
968 return relocs[cache.start..][0..cache.len];
1076969}
1077970
1078971pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
......@@ -1111,3 +1004,22 @@ pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
11111004 .load_store_128 => try math.divExact(u12, narrowed, 16),
11121005 };
11131006}
1007
1008pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
1009 switch (zld.options.target.cpu.arch) {
1010 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
1011 .ARM64_RELOC_GOT_LOAD_PAGE21,
1012 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1013 .ARM64_RELOC_POINTER_TO_GOT,
1014 => return true,
1015 else => return false,
1016 },
1017 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
1018 .X86_64_RELOC_GOT,
1019 .X86_64_RELOC_GOT_LOAD,
1020 => return true,
1021 else => return false,
1022 },
1023 else => unreachable,
1024 }
1025}
src/link/MachO/dead_strip.zig+166-51
......@@ -2,6 +2,7 @@
22
33const std = @import("std");
44const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
56const log = std.log.scoped(.dead_strip);
67const macho = std.macho;
78const math = std.math;
......@@ -11,13 +12,14 @@ const Allocator = mem.Allocator;
1112const AtomIndex = @import("zld.zig").AtomIndex;
1213const Atom = @import("ZldAtom.zig");
1314const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
15const UnwindInfo = @import("UnwindInfo.zig");
1416const Zld = @import("zld.zig").Zld;
1517
1618const N_DEAD = @import("zld.zig").N_DEAD;
1719
1820const AtomTable = std.AutoHashMap(AtomIndex, void);
1921
20pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {
22pub fn gcAtoms(zld: *Zld) !void {
2123 const gpa = zld.gpa;
2224
2325 var arena = std.heap.ArenaAllocator.init(gpa);
......@@ -30,7 +32,7 @@ pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) Allocator.Error!void {
3032 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
3133
3234 try collectRoots(zld, &roots);
33 mark(zld, roots, &alive, reverse_lookups);
35 try mark(zld, roots, &alive);
3436 prune(zld, alive);
3537}
3638
......@@ -45,10 +47,10 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
4547 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
4648 _ = try roots.getOrPut(atom_index);
4749
48 log.debug("root(ATOM({d}, %{d}, {d}))", .{
50 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
4951 atom_index,
5052 zld.getAtom(atom_index).sym_index,
51 zld.getAtom(atom_index).file,
53 zld.getAtom(atom_index).getFile(),
5254 });
5355 },
5456 else => |other| {
......@@ -63,32 +65,15 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
6365 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
6466 _ = try roots.getOrPut(atom_index);
6567
66 log.debug("root(ATOM({d}, %{d}, {d}))", .{
68 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
6769 atom_index,
6870 zld.getAtom(atom_index).sym_index,
69 zld.getAtom(atom_index).file,
71 zld.getAtom(atom_index).getFile(),
7072 });
7173 }
7274 },
7375 }
7476
75 // TODO just a temp until we learn how to parse unwind records
76 for (zld.globals.items) |global| {
77 if (mem.eql(u8, "___gxx_personality_v0", zld.getSymbolName(global))) {
78 const object = zld.objects.items[global.getFile().?];
79 if (object.getAtomIndexForSymbol(global.sym_index)) |atom_index| {
80 _ = try roots.getOrPut(atom_index);
81
82 log.debug("root(ATOM({d}, %{d}, {d}))", .{
83 atom_index,
84 zld.getAtom(atom_index).sym_index,
85 zld.getAtom(atom_index).file,
86 });
87 }
88 break;
89 }
90 }
91
9277 for (zld.objects.items) |object| {
9378 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
9479
......@@ -119,28 +104,23 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
119104 if (is_gc_root) {
120105 try roots.putNoClobber(atom_index, {});
121106
122 log.debug("root(ATOM({d}, %{d}, {d}))", .{
107 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
123108 atom_index,
124109 zld.getAtom(atom_index).sym_index,
125 zld.getAtom(atom_index).file,
110 zld.getAtom(atom_index).getFile(),
126111 });
127112 }
128113 }
129114 }
130115}
131116
132fn markLive(
133 zld: *Zld,
134 atom_index: AtomIndex,
135 alive: *AtomTable,
136 reverse_lookups: [][]u32,
137) void {
117fn markLive(zld: *Zld, atom_index: AtomIndex, alive: *AtomTable) void {
138118 if (alive.contains(atom_index)) return;
139119
140120 const atom = zld.getAtom(atom_index);
141121 const sym_loc = atom.getSymbolWithLoc();
142122
143 log.debug("mark(ATOM({d}, %{d}, {d}))", .{ atom_index, sym_loc.sym_index, sym_loc.file });
123 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
144124
145125 alive.putAssumeCapacityNoClobber(atom_index, {});
146126
......@@ -151,14 +131,13 @@ fn markLive(
151131 if (header.isZerofill()) return;
152132
153133 const relocs = Atom.getAtomRelocs(zld, atom_index);
154 const reverse_lookup = reverse_lookups[atom.getFile().?];
155134 for (relocs) |rel| {
156135 const target = switch (cpu_arch) {
157136 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
158137 .ARM64_RELOC_ADDEND => continue,
159 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
138 else => Atom.parseRelocTarget(zld, atom_index, rel),
160139 },
161 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
140 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel),
162141 else => unreachable,
163142 };
164143 const target_sym = zld.getSymbol(target);
......@@ -174,21 +153,21 @@ fn markLive(
174153
175154 const object = zld.objects.items[target.getFile().?];
176155 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
177 log.debug(" following ATOM({d}, %{d}, {d})", .{
156 log.debug(" following ATOM({d}, %{d}, {?d})", .{
178157 target_atom_index,
179158 zld.getAtom(target_atom_index).sym_index,
180 zld.getAtom(target_atom_index).file,
159 zld.getAtom(target_atom_index).getFile(),
181160 });
182161
183 markLive(zld, target_atom_index, alive, reverse_lookups);
162 markLive(zld, target_atom_index, alive);
184163 }
185164}
186165
187fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookups: [][]u32) bool {
166fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable) bool {
188167 const atom = zld.getAtom(atom_index);
189168 const sym_loc = atom.getSymbolWithLoc();
190169
191 log.debug("refersLive(ATOM({d}, %{d}, {d}))", .{ atom_index, sym_loc.sym_index, sym_loc.file });
170 log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
192171
193172 const cpu_arch = zld.options.target.cpu.arch;
194173
......@@ -197,14 +176,13 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
197176 assert(!header.isZerofill());
198177
199178 const relocs = Atom.getAtomRelocs(zld, atom_index);
200 const reverse_lookup = reverse_lookups[atom.getFile().?];
201179 for (relocs) |rel| {
202180 const target = switch (cpu_arch) {
203181 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
204182 .ARM64_RELOC_ADDEND => continue,
205 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
183 else => Atom.parseRelocTarget(zld, atom_index, rel),
206184 },
207 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
185 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel),
208186 else => unreachable,
209187 };
210188
......@@ -214,10 +192,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
214192 continue;
215193 };
216194 if (alive.contains(target_atom_index)) {
217 log.debug(" refers live ATOM({d}, %{d}, {d})", .{
195 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
218196 target_atom_index,
219197 zld.getAtom(target_atom_index).sym_index,
220 zld.getAtom(target_atom_index).file,
198 zld.getAtom(target_atom_index).getFile(),
221199 });
222200 return true;
223201 }
......@@ -226,10 +204,10 @@ fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookup
226204 return false;
227205}
228206
229fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32) void {
207fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
230208 var it = roots.keyIterator();
231209 while (it.next()) |root| {
232 markLive(zld, root.*, alive, reverse_lookups);
210 markLive(zld, root.*, alive);
233211 }
234212
235213 var loop: bool = true;
......@@ -251,14 +229,151 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32
251229 const source_sect = object.getSourceSection(sect_id);
252230
253231 if (source_sect.isDontDeadStripIfReferencesLive()) {
254 if (refersLive(zld, atom_index, alive.*, reverse_lookups)) {
255 markLive(zld, atom_index, alive, reverse_lookups);
232 if (refersLive(zld, atom_index, alive.*)) {
233 markLive(zld, atom_index, alive);
256234 loop = true;
257235 }
258236 }
259237 }
260238 }
261239 }
240
241 for (zld.objects.items) |_, object_id| {
242 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
243 // marking all references as live.
244 try markUnwindRecords(zld, @intCast(u32, object_id), alive);
245 }
246}
247
248fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
249 const object = &zld.objects.items[object_id];
250 const cpu_arch = zld.options.target.cpu.arch;
251
252 const unwind_records = object.getUnwindRecords();
253
254 for (object.exec_atoms.items) |atom_index| {
255 if (!object.hasUnwindRecords()) {
256 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
257 const ptr = object.eh_frame_relocs_lookup.getPtr(fde_offset).?;
258 if (ptr.dead) continue; // already marked
259 if (!alive.contains(atom_index)) {
260 // Mark dead and continue.
261 ptr.dead = true;
262 } else {
263 // Mark references live and continue.
264 try markEhFrameRecord(zld, object_id, atom_index, alive);
265 }
266 continue;
267 }
268 }
269
270 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
271 if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do
272 if (!alive.contains(atom_index)) {
273 // Mark the record dead and continue.
274 object.unwind_relocs_lookup[record_id].dead = true;
275 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
276 object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true;
277 }
278 continue;
279 }
280
281 const record = unwind_records[record_id];
282 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
283 try markEhFrameRecord(zld, object_id, atom_index, alive);
284 } else {
285 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
286 const target = UnwindInfo.parseRelocTarget(
287 zld,
288 object_id,
289 rel,
290 mem.asBytes(&record),
291 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
292 );
293 const target_sym = zld.getSymbol(target);
294 if (!target_sym.undf()) {
295 const target_object = zld.objects.items[target.getFile().?];
296 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
297 markLive(zld, target_atom_index, alive);
298 }
299 }
300
301 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
302 const target = UnwindInfo.parseRelocTarget(
303 zld,
304 object_id,
305 rel,
306 mem.asBytes(&record),
307 @intCast(i32, record_id * @sizeOf(macho.compact_unwind_entry)),
308 );
309 const target_object = zld.objects.items[target.getFile().?];
310 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
311 markLive(zld, target_atom_index, alive);
312 }
313 }
314 }
315}
316
317fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {
318 const cpu_arch = zld.options.target.cpu.arch;
319 const object = &zld.objects.items[object_id];
320 var it = object.getEhFrameRecordsIterator();
321
322 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
323 it.seekTo(fde_offset);
324 const fde = (try it.next()).?;
325
326 const cie_ptr = fde.getCiePointer();
327 const cie_offset = fde_offset + 4 - cie_ptr;
328 it.seekTo(cie_offset);
329 const cie = (try it.next()).?;
330
331 switch (cpu_arch) {
332 .aarch64 => {
333 // Mark FDE references which should include any referenced LSDA record
334 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
335 for (relocs) |rel| {
336 const target = UnwindInfo.parseRelocTarget(
337 zld,
338 object_id,
339 rel,
340 fde.data,
341 @intCast(i32, fde_offset) + 4,
342 );
343 const target_sym = zld.getSymbol(target);
344 if (!target_sym.undf()) blk: {
345 const target_object = zld.objects.items[target.getFile().?];
346 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
347 break :blk;
348 markLive(zld, target_atom_index, alive);
349 }
350 }
351 },
352 .x86_64 => {
353 const lsda_ptr = try fde.getLsdaPointer(cie, .{
354 .base_addr = object.eh_frame_sect.?.addr,
355 .base_offset = fde_offset,
356 });
357 if (lsda_ptr) |lsda_address| {
358 // Mark LSDA record as live
359 const sym_index = object.getSymbolByAddress(lsda_address, null);
360 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
361 markLive(zld, target_atom_index, alive);
362 }
363 },
364 else => unreachable,
365 }
366
367 // Mark CIE references which should include any referenced personalities
368 // that are defined locally.
369 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
370 const target_sym = zld.getSymbol(target);
371 if (!target_sym.undf()) {
372 const target_object = zld.objects.items[target.getFile().?];
373 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
374 markLive(zld, target_atom_index, alive);
375 }
376 }
262377}
263378
264379fn prune(zld: *Zld, alive: AtomTable) void {
......@@ -275,10 +390,10 @@ fn prune(zld: *Zld, alive: AtomTable) void {
275390 const atom = zld.getAtom(atom_index);
276391 const sym_loc = atom.getSymbolWithLoc();
277392
278 log.debug("prune(ATOM({d}, %{d}, {d}))", .{
393 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
279394 atom_index,
280395 sym_loc.sym_index,
281 sym_loc.file,
396 sym_loc.getFile(),
282397 });
283398 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
284399
src/link/MachO/eh_frame.zig created+625
......@@ -0,0 +1,625 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const macho = std.macho;
4const math = std.math;
5const mem = std.mem;
6const leb = std.leb;
7const log = std.log.scoped(.eh_frame);
8
9const Allocator = mem.Allocator;
10const AtomIndex = @import("zld.zig").AtomIndex;
11const Atom = @import("ZldAtom.zig");
12const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
13const UnwindInfo = @import("UnwindInfo.zig");
14const Zld = @import("zld.zig").Zld;
15
16pub fn scanRelocs(zld: *Zld) !void {
17 const gpa = zld.gpa;
18
19 for (zld.objects.items) |*object, object_id| {
20 var cies = std.AutoHashMap(u32, void).init(gpa);
21 defer cies.deinit();
22
23 var it = object.getEhFrameRecordsIterator();
24
25 for (object.exec_atoms.items) |atom_index| {
26 const fde_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
27 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
28 it.seekTo(fde_offset);
29 const fde = (try it.next()).?;
30
31 const cie_ptr = fde.getCiePointer();
32 const cie_offset = fde_offset + 4 - cie_ptr;
33
34 if (!cies.contains(cie_offset)) {
35 try cies.putNoClobber(cie_offset, {});
36 it.seekTo(cie_offset);
37 const cie = (try it.next()).?;
38 try cie.scanRelocs(zld, @intCast(u32, object_id), cie_offset);
39 }
40 }
41 }
42}
43
44pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
45 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
46 const sect = &zld.sections.items(.header)[sect_id];
47 sect.@"align" = 3;
48 sect.size = 0;
49
50 const cpu_arch = zld.options.target.cpu.arch;
51 const gpa = zld.gpa;
52 var size: u32 = 0;
53
54 for (zld.objects.items) |*object| {
55 var cies = std.AutoHashMap(u32, u32).init(gpa);
56 defer cies.deinit();
57
58 var eh_it = object.getEhFrameRecordsIterator();
59
60 for (object.exec_atoms.items) |atom_index| {
61 const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
62 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
63
64 const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
65 const record = unwind_info.records.items[record_id];
66
67 // TODO skip this check if no __compact_unwind is present
68 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
69 if (!is_dwarf) continue;
70
71 eh_it.seekTo(fde_record_offset);
72 const source_fde_record = (try eh_it.next()).?;
73
74 const cie_ptr = source_fde_record.getCiePointer();
75 const cie_offset = fde_record_offset + 4 - cie_ptr;
76
77 const gop = try cies.getOrPut(cie_offset);
78 if (!gop.found_existing) {
79 eh_it.seekTo(cie_offset);
80 const source_cie_record = (try eh_it.next()).?;
81 gop.value_ptr.* = size;
82 size += source_cie_record.getSize();
83 }
84
85 size += source_fde_record.getSize();
86 }
87 }
88
89 sect.size = size;
90}
91
92pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
93 const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
94 const sect = zld.sections.items(.header)[sect_id];
95 const seg_id = zld.sections.items(.segment_index)[sect_id];
96 const seg = zld.segments.items[seg_id];
97
98 const cpu_arch = zld.options.target.cpu.arch;
99 const gpa = zld.gpa;
100
101 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
102 defer {
103 for (eh_records.values()) |*rec| {
104 rec.deinit(gpa);
105 }
106 eh_records.deinit();
107 }
108
109 var eh_frame_offset: u32 = 0;
110
111 for (zld.objects.items) |*object, object_id| {
112 try eh_records.ensureUnusedCapacity(2 * @intCast(u32, object.exec_atoms.items.len));
113
114 var cies = std.AutoHashMap(u32, u32).init(gpa);
115 defer cies.deinit();
116
117 var eh_it = object.getEhFrameRecordsIterator();
118
119 for (object.exec_atoms.items) |atom_index| {
120 const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
121 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
122
123 const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
124 const record = &unwind_info.records.items[record_id];
125
126 // TODO skip this check if no __compact_unwind is present
127 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
128 if (!is_dwarf) continue;
129
130 eh_it.seekTo(fde_record_offset);
131 const source_fde_record = (try eh_it.next()).?;
132
133 const cie_ptr = source_fde_record.getCiePointer();
134 const cie_offset = fde_record_offset + 4 - cie_ptr;
135
136 const gop = try cies.getOrPut(cie_offset);
137 if (!gop.found_existing) {
138 eh_it.seekTo(cie_offset);
139 const source_cie_record = (try eh_it.next()).?;
140 var cie_record = try source_cie_record.toOwned(gpa);
141 try cie_record.relocate(zld, @intCast(u32, object_id), .{
142 .source_offset = cie_offset,
143 .out_offset = eh_frame_offset,
144 .sect_addr = sect.addr,
145 });
146 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record);
147 gop.value_ptr.* = eh_frame_offset;
148 eh_frame_offset += cie_record.getSize();
149 }
150
151 var fde_record = try source_fde_record.toOwned(gpa);
152 fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*);
153 try fde_record.relocate(zld, @intCast(u32, object_id), .{
154 .source_offset = fde_record_offset,
155 .out_offset = eh_frame_offset,
156 .sect_addr = sect.addr,
157 });
158
159 switch (cpu_arch) {
160 .aarch64 => {}, // relocs take care of LSDA pointers
161 .x86_64 => {
162 // We need to relocate target symbol address ourselves.
163 const atom = zld.getAtom(atom_index);
164 const atom_sym = zld.getSymbol(atom.getSymbolWithLoc());
165 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
166 .base_addr = sect.addr,
167 .base_offset = eh_frame_offset,
168 });
169
170 // We need to parse LSDA pointer and relocate ourselves.
171 const cie_record = eh_records.get(
172 eh_frame_offset + 4 - fde_record.getCiePointer(),
173 ).?;
174 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
175 .base_addr = object.eh_frame_sect.?.addr,
176 .base_offset = fde_record_offset,
177 });
178 if (source_lsda_ptr) |ptr| {
179 const sym_index = object.getSymbolByAddress(ptr, null);
180 const sym = object.symtab[sym_index];
181 try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
182 .base_addr = sect.addr,
183 .base_offset = eh_frame_offset,
184 });
185 }
186 },
187 else => unreachable,
188 }
189
190 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
191
192 UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
193 &record.compactUnwindEncoding,
194 cpu_arch,
195 @intCast(u24, eh_frame_offset),
196 );
197
198 const cie_record = eh_records.get(
199 eh_frame_offset + 4 - fde_record.getCiePointer(),
200 ).?;
201 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
202 .base_addr = sect.addr,
203 .base_offset = eh_frame_offset,
204 });
205 if (lsda_ptr) |ptr| {
206 record.lsda = ptr - seg.vmaddr;
207 }
208
209 eh_frame_offset += fde_record.getSize();
210 }
211 }
212
213 var buffer = std.ArrayList(u8).init(gpa);
214 defer buffer.deinit();
215 const writer = buffer.writer();
216
217 for (eh_records.values()) |record| {
218 try writer.writeIntLittle(u32, record.size);
219 try buffer.appendSlice(record.data);
220 }
221
222 try zld.file.pwriteAll(buffer.items, sect.offset);
223}
224const EhFrameRecordTag = enum { cie, fde };
225
226pub fn EhFrameRecord(comptime is_mutable: bool) type {
227 return struct {
228 tag: EhFrameRecordTag,
229 size: u32,
230 data: if (is_mutable) []u8 else []const u8,
231
232 const Record = @This();
233
234 pub fn deinit(rec: *Record, gpa: Allocator) void {
235 comptime assert(is_mutable);
236 gpa.free(rec.data);
237 }
238
239 pub fn toOwned(rec: Record, gpa: Allocator) Allocator.Error!EhFrameRecord(true) {
240 const data = try gpa.dupe(u8, rec.data);
241 return EhFrameRecord(true){
242 .tag = rec.tag,
243 .size = rec.size,
244 .data = data,
245 };
246 }
247
248 pub inline fn getSize(rec: Record) u32 {
249 return 4 + rec.size;
250 }
251
252 pub fn scanRelocs(
253 rec: Record,
254 zld: *Zld,
255 object_id: u32,
256 source_offset: u32,
257 ) !void {
258 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
259 try Atom.addGotEntry(zld, target);
260 }
261 }
262
263 pub fn getTargetSymbolAddress(rec: Record, ctx: struct {
264 base_addr: u64,
265 base_offset: u64,
266 }) u64 {
267 assert(rec.tag == .fde);
268 const addend = mem.readIntLittle(i64, rec.data[4..][0..8]);
269 return @intCast(u64, @intCast(i64, ctx.base_addr + ctx.base_offset + 8) + addend);
270 }
271
272 pub fn setTargetSymbolAddress(rec: *Record, value: u64, ctx: struct {
273 base_addr: u64,
274 base_offset: u64,
275 }) !void {
276 assert(rec.tag == .fde);
277 const addend = @intCast(i64, value) - @intCast(i64, ctx.base_addr + ctx.base_offset + 8);
278 mem.writeIntLittle(i64, rec.data[4..][0..8], addend);
279 }
280
281 pub fn getPersonalityPointerReloc(
282 rec: Record,
283 zld: *Zld,
284 object_id: u32,
285 source_offset: u32,
286 ) ?SymbolWithLoc {
287 const cpu_arch = zld.options.target.cpu.arch;
288 const relocs = getRelocs(zld, object_id, source_offset);
289 for (relocs) |rel| {
290 switch (cpu_arch) {
291 .aarch64 => {
292 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
293 switch (rel_type) {
294 .ARM64_RELOC_SUBTRACTOR,
295 .ARM64_RELOC_UNSIGNED,
296 => continue,
297 .ARM64_RELOC_POINTER_TO_GOT => {},
298 else => unreachable,
299 }
300 },
301 .x86_64 => {
302 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
303 switch (rel_type) {
304 .X86_64_RELOC_GOT => {},
305 else => unreachable,
306 }
307 },
308 else => unreachable,
309 }
310 const target = UnwindInfo.parseRelocTarget(
311 zld,
312 object_id,
313 rel,
314 rec.data,
315 @intCast(i32, source_offset) + 4,
316 );
317 return target;
318 }
319 return null;
320 }
321
322 pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
323 source_offset: u32,
324 out_offset: u32,
325 sect_addr: u64,
326 }) !void {
327 comptime assert(is_mutable);
328
329 const cpu_arch = zld.options.target.cpu.arch;
330 const relocs = getRelocs(zld, object_id, ctx.source_offset);
331
332 for (relocs) |rel| {
333 const target = UnwindInfo.parseRelocTarget(
334 zld,
335 object_id,
336 rel,
337 rec.data,
338 @intCast(i32, ctx.source_offset) + 4,
339 );
340 const rel_offset = @intCast(u32, rel.r_address - @intCast(i32, ctx.source_offset) - 4);
341 const source_addr = ctx.sect_addr + rel_offset + ctx.out_offset + 4;
342
343 switch (cpu_arch) {
344 .aarch64 => {
345 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
346 switch (rel_type) {
347 .ARM64_RELOC_SUBTRACTOR => {
348 // Address of the __eh_frame in the source object file
349 },
350 .ARM64_RELOC_POINTER_TO_GOT => {
351 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
352 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse
353 return error.Overflow;
354 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
355 },
356 .ARM64_RELOC_UNSIGNED => {
357 assert(rel.r_extern == 1);
358 const target_addr = try Atom.getRelocTargetAddress(zld, target, false, false);
359 const result = @intCast(i64, target_addr) - @intCast(i64, source_addr);
360 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @intCast(i64, result));
361 },
362 else => unreachable,
363 }
364 },
365 .x86_64 => {
366 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
367 switch (rel_type) {
368 .X86_64_RELOC_GOT => {
369 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
370 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
371 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
372 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
373 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
374 },
375 else => unreachable,
376 }
377 },
378 else => unreachable,
379 }
380 }
381 }
382
383 pub fn getCiePointer(rec: Record) u32 {
384 assert(rec.tag == .fde);
385 return mem.readIntLittle(u32, rec.data[0..4]);
386 }
387
388 pub fn setCiePointer(rec: *Record, ptr: u32) void {
389 assert(rec.tag == .fde);
390 mem.writeIntLittle(u32, rec.data[0..4], ptr);
391 }
392
393 pub fn getAugmentationString(rec: Record) []const u8 {
394 assert(rec.tag == .cie);
395 return mem.sliceTo(@ptrCast([*:0]const u8, rec.data.ptr + 5), 0);
396 }
397
398 pub fn getPersonalityPointer(rec: Record, ctx: struct {
399 base_addr: u64,
400 base_offset: u64,
401 }) !?u64 {
402 assert(rec.tag == .cie);
403 const aug_str = rec.getAugmentationString();
404
405 var stream = std.io.fixedBufferStream(rec.data[9 + aug_str.len ..]);
406 var creader = std.io.countingReader(stream.reader());
407 const reader = creader.reader();
408
409 for (aug_str) |ch, i| switch (ch) {
410 'z' => if (i > 0) {
411 return error.BadDwarfCfi;
412 } else {
413 _ = try leb.readULEB128(u64, reader);
414 },
415 'R' => {
416 _ = try reader.readByte();
417 },
418 'P' => {
419 const enc = try reader.readByte();
420 const offset = ctx.base_offset + 13 + aug_str.len + creader.bytes_read;
421 const ptr = try getEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), reader);
422 return ptr;
423 },
424 'L' => {
425 _ = try reader.readByte();
426 },
427 'S', 'B', 'G' => {},
428 else => return error.BadDwarfCfi,
429 };
430
431 return null;
432 }
433
434 pub fn getLsdaPointer(rec: Record, cie: Record, ctx: struct {
435 base_addr: u64,
436 base_offset: u64,
437 }) !?u64 {
438 assert(rec.tag == .fde);
439 const enc = (try cie.getLsdaEncoding()) orelse return null;
440 var stream = std.io.fixedBufferStream(rec.data[20..]);
441 const reader = stream.reader();
442 _ = try reader.readByte();
443 const offset = ctx.base_offset + 25;
444 const ptr = try getEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), reader);
445 return ptr;
446 }
447
448 pub fn setLsdaPointer(rec: *Record, cie: Record, value: u64, ctx: struct {
449 base_addr: u64,
450 base_offset: u64,
451 }) !void {
452 assert(rec.tag == .fde);
453 const enc = (try cie.getLsdaEncoding()) orelse unreachable;
454 var stream = std.io.fixedBufferStream(rec.data[21..]);
455 const writer = stream.writer();
456 const offset = ctx.base_offset + 25;
457 try setEncodedPointer(enc, @intCast(i64, ctx.base_addr + offset), value, writer);
458 }
459
460 fn getLsdaEncoding(rec: Record) !?u8 {
461 assert(rec.tag == .cie);
462 const aug_str = rec.getAugmentationString();
463
464 const base_offset = 9 + aug_str.len;
465 var stream = std.io.fixedBufferStream(rec.data[base_offset..]);
466 var creader = std.io.countingReader(stream.reader());
467 const reader = creader.reader();
468
469 for (aug_str) |ch, i| switch (ch) {
470 'z' => if (i > 0) {
471 return error.BadDwarfCfi;
472 } else {
473 _ = try leb.readULEB128(u64, reader);
474 },
475 'R' => {
476 _ = try reader.readByte();
477 },
478 'P' => {
479 const enc = try reader.readByte();
480 _ = try getEncodedPointer(enc, 0, reader);
481 },
482 'L' => {
483 const enc = try reader.readByte();
484 return enc;
485 },
486 'S', 'B', 'G' => {},
487 else => return error.BadDwarfCfi,
488 };
489
490 return null;
491 }
492
493 fn getEncodedPointer(enc: u8, pcrel_offset: i64, reader: anytype) !?u64 {
494 if (enc == EH_PE.omit) return null;
495
496 var ptr: i64 = switch (enc & 0x0F) {
497 EH_PE.absptr => @bitCast(i64, try reader.readIntLittle(u64)),
498 EH_PE.udata2 => @bitCast(i16, try reader.readIntLittle(u16)),
499 EH_PE.udata4 => @bitCast(i32, try reader.readIntLittle(u32)),
500 EH_PE.udata8 => @bitCast(i64, try reader.readIntLittle(u64)),
501 EH_PE.uleb128 => @bitCast(i64, try leb.readULEB128(u64, reader)),
502 EH_PE.sdata2 => try reader.readIntLittle(i16),
503 EH_PE.sdata4 => try reader.readIntLittle(i32),
504 EH_PE.sdata8 => try reader.readIntLittle(i64),
505 EH_PE.sleb128 => try leb.readILEB128(i64, reader),
506 else => return null,
507 };
508
509 switch (enc & 0x70) {
510 EH_PE.absptr => {},
511 EH_PE.pcrel => ptr += pcrel_offset,
512 EH_PE.datarel,
513 EH_PE.textrel,
514 EH_PE.funcrel,
515 EH_PE.aligned,
516 => return null,
517 else => return null,
518 }
519
520 return @bitCast(u64, ptr);
521 }
522
523 fn setEncodedPointer(enc: u8, pcrel_offset: i64, value: u64, writer: anytype) !void {
524 if (enc == EH_PE.omit) return;
525
526 var actual = @intCast(i64, value);
527
528 switch (enc & 0x70) {
529 EH_PE.absptr => {},
530 EH_PE.pcrel => actual -= pcrel_offset,
531 EH_PE.datarel,
532 EH_PE.textrel,
533 EH_PE.funcrel,
534 EH_PE.aligned,
535 => unreachable,
536 else => unreachable,
537 }
538
539 switch (enc & 0x0F) {
540 EH_PE.absptr => try writer.writeIntLittle(u64, @bitCast(u64, actual)),
541 EH_PE.udata2 => try writer.writeIntLittle(u16, @bitCast(u16, @intCast(i16, actual))),
542 EH_PE.udata4 => try writer.writeIntLittle(u32, @bitCast(u32, @intCast(i32, actual))),
543 EH_PE.udata8 => try writer.writeIntLittle(u64, @bitCast(u64, actual)),
544 EH_PE.uleb128 => try leb.writeULEB128(writer, @bitCast(u64, actual)),
545 EH_PE.sdata2 => try writer.writeIntLittle(i16, @intCast(i16, actual)),
546 EH_PE.sdata4 => try writer.writeIntLittle(i32, @intCast(i32, actual)),
547 EH_PE.sdata8 => try writer.writeIntLittle(i64, actual),
548 EH_PE.sleb128 => try leb.writeILEB128(writer, actual),
549 else => unreachable,
550 }
551 }
552 };
553}
554
555pub fn getRelocs(
556 zld: *Zld,
557 object_id: u32,
558 source_offset: u32,
559) []align(1) const macho.relocation_info {
560 const object = &zld.objects.items[object_id];
561 assert(object.hasEhFrameRecords());
562 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
563 return &[0]macho.relocation_info{};
564 const all_relocs = object.getRelocs(object.eh_frame_sect.?);
565 return all_relocs[urel.reloc.start..][0..urel.reloc.len];
566}
567
568pub const Iterator = struct {
569 data: []const u8,
570 pos: u32 = 0,
571
572 pub fn next(it: *Iterator) !?EhFrameRecord(false) {
573 if (it.pos >= it.data.len) return null;
574
575 var stream = std.io.fixedBufferStream(it.data[it.pos..]);
576 const reader = stream.reader();
577
578 var size = try reader.readIntLittle(u32);
579 if (size == 0xFFFFFFFF) {
580 log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
581 return error.BadDwarfCfi;
582 }
583
584 const id = try reader.readIntLittle(u32);
585 const tag: EhFrameRecordTag = if (id == 0) .cie else .fde;
586 const offset: u32 = 4;
587 const record = EhFrameRecord(false){
588 .tag = tag,
589 .size = size,
590 .data = it.data[it.pos + offset ..][0..size],
591 };
592
593 it.pos += size + offset;
594
595 return record;
596 }
597
598 pub fn reset(it: *Iterator) void {
599 it.pos = 0;
600 }
601
602 pub fn seekTo(it: *Iterator, pos: u32) void {
603 assert(pos >= 0 and pos < it.data.len);
604 it.pos = pos;
605 }
606};
607
608pub const EH_PE = struct {
609 pub const absptr = 0x00;
610 pub const uleb128 = 0x01;
611 pub const udata2 = 0x02;
612 pub const udata4 = 0x03;
613 pub const udata8 = 0x04;
614 pub const sleb128 = 0x09;
615 pub const sdata2 = 0x0A;
616 pub const sdata4 = 0x0B;
617 pub const sdata8 = 0x0C;
618 pub const pcrel = 0x10;
619 pub const textrel = 0x20;
620 pub const datarel = 0x30;
621 pub const funcrel = 0x40;
622 pub const aligned = 0x50;
623 pub const indirect = 0x80;
624 pub const omit = 0xFF;
625};
src/link/MachO/thunks.zig+4-5
......@@ -68,7 +68,7 @@ pub const Thunk = struct {
6868 }
6969};
7070
71pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
71pub fn createThunks(zld: *Zld, sect_id: u8) !void {
7272 const header = &zld.sections.items(.header)[sect_id];
7373 if (header.size == 0) return;
7474
......@@ -140,7 +140,6 @@ pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
140140 try scanRelocs(
141141 zld,
142142 atom_index,
143 reverse_lookups[atom.getFile().?],
144143 allocated,
145144 thunk_index,
146145 group_end,
......@@ -214,7 +213,6 @@ fn allocateThunk(
214213fn scanRelocs(
215214 zld: *Zld,
216215 atom_index: AtomIndex,
217 reverse_lookup: []u32,
218216 allocated: std.AutoHashMap(AtomIndex, void),
219217 thunk_index: ThunkIndex,
220218 group_end: AtomIndex,
......@@ -231,7 +229,7 @@ fn scanRelocs(
231229 for (relocs) |rel| {
232230 if (!relocNeedsThunk(rel)) continue;
233231
234 const target = Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup);
232 const target = Atom.parseRelocTarget(zld, atom_index, rel);
235233 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
236234
237235 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
......@@ -308,7 +306,8 @@ fn isReachable(
308306 if (!allocated.contains(target_atom_index)) return false;
309307
310308 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
311 const target_addr = Atom.getRelocTargetAddress(zld, rel, target, false) catch unreachable;
309 const is_via_got = Atom.relocRequiresGot(zld, rel);
310 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
312311 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
313312 return false;
314313
src/link/MachO/zld.zig+123-104
......@@ -10,6 +10,7 @@ const mem = std.mem;
1010
1111const aarch64 = @import("../../arch/aarch64/bits.zig");
1212const dead_strip = @import("dead_strip.zig");
13const eh_frame = @import("eh_frame.zig");
1314const fat = @import("fat.zig");
1415const link = @import("../../link.zig");
1516const load_commands = @import("load_commands.zig");
......@@ -30,6 +31,7 @@ const LibStub = @import("../tapi.zig").LibStub;
3031const Object = @import("Object.zig");
3132const StringTable = @import("../strtab.zig").StringTable;
3233const Trie = @import("Trie.zig");
34const UnwindInfo = @import("UnwindInfo.zig");
3335
3436const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
3537const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
......@@ -389,6 +391,14 @@ pub const Zld = struct {
389391 break :blk null;
390392 }
391393
394 // We handle unwind info separately.
395 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
396 break :blk null;
397 }
398 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
399 break :blk null;
400 }
401
392402 if (sect.isCode()) {
393403 break :blk self.getSectionByName("__TEXT", "__text") orelse try self.initSection(
394404 "__TEXT",
......@@ -402,12 +412,6 @@ pub const Zld = struct {
402412 }
403413
404414 if (sect.isDebug()) {
405 // TODO debug attributes
406 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
407 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
408 sect.flags, segname, sectname,
409 });
410 }
411415 break :blk null;
412416 }
413417
......@@ -459,13 +463,6 @@ pub const Zld = struct {
459463 );
460464 },
461465 macho.S_COALESCED => {
462 // TODO unwind info
463 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
464 log.debug("TODO eh frame section: type 0x{x}, name '{s},{s}'", .{
465 sect.flags, segname, sectname,
466 });
467 break :blk null;
468 }
469466 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
470467 segname,
471468 sectname,
......@@ -937,7 +934,7 @@ pub const Zld = struct {
937934 }
938935 }
939936
940 fn resolveSymbolsInObject(self: *Zld, object_id: u16, resolver: *SymbolResolver) !void {
937 fn resolveSymbolsInObject(self: *Zld, object_id: u32, resolver: *SymbolResolver) !void {
941938 const object = &self.objects.items[object_id];
942939 const in_symtab = object.in_symtab orelse return;
943940
......@@ -977,7 +974,7 @@ pub const Zld = struct {
977974 continue;
978975 }
979976
980 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
977 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id + 1 };
981978
982979 const global_index = resolver.table.get(sym_name) orelse {
983980 const gpa = self.gpa;
......@@ -1378,7 +1375,7 @@ pub const Zld = struct {
13781375 }
13791376 }
13801377
1381 fn writeAtoms(self: *Zld, reverse_lookups: [][]u32) !void {
1378 fn writeAtoms(self: *Zld) !void {
13821379 const gpa = self.gpa;
13831380 const slice = self.sections.slice();
13841381
......@@ -1386,6 +1383,7 @@ pub const Zld = struct {
13861383 const header = slice.items(.header)[sect_id];
13871384 var atom_index = first_atom_index;
13881385
1386 if (atom_index == 0) continue;
13891387 if (header.isZerofill()) continue;
13901388
13911389 var buffer = std.ArrayList(u8).init(gpa);
......@@ -1407,7 +1405,7 @@ pub const Zld = struct {
14071405 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{
14081406 atom.sym_index,
14091407 self.getSymbolName(atom.getSymbolWithLoc()),
1410 atom.file,
1408 atom.getFile(),
14111409 });
14121410 if (padding_size > 0) {
14131411 log.debug(" (with padding {x})", .{padding_size});
......@@ -1460,7 +1458,6 @@ pub const Zld = struct {
14601458 atom_index,
14611459 buffer.items[offset..][0..size],
14621460 relocs,
1463 reverse_lookups[atom.getFile().?],
14641461 );
14651462 }
14661463
......@@ -1501,9 +1498,10 @@ pub const Zld = struct {
15011498 while (i < slice.len) : (i += 1) {
15021499 const section = self.sections.get(i);
15031500 if (section.header.size == 0) {
1504 log.debug("pruning section {s},{s}", .{
1501 log.debug("pruning section {s},{s} {d}", .{
15051502 section.header.segName(),
15061503 section.header.sectName(),
1504 section.first_atom_index,
15071505 });
15081506 continue;
15091507 }
......@@ -1519,7 +1517,7 @@ pub const Zld = struct {
15191517 }
15201518 }
15211519
1522 fn calcSectionSizes(self: *Zld, reverse_lookups: [][]u32) !void {
1520 fn calcSectionSizes(self: *Zld) !void {
15231521 const slice = self.sections.slice();
15241522 for (slice.items(.header)) |*header, sect_id| {
15251523 if (header.size == 0) continue;
......@@ -1528,6 +1526,8 @@ pub const Zld = struct {
15281526 }
15291527
15301528 var atom_index = slice.items(.first_atom_index)[sect_id];
1529 if (atom_index == 0) continue;
1530
15311531 header.size = 0;
15321532 header.@"align" = 0;
15331533
......@@ -1556,7 +1556,7 @@ pub const Zld = struct {
15561556 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;
15571557
15581558 // Create jump/branch range extenders if needed.
1559 try thunks.createThunks(self, @intCast(u8, sect_id), reverse_lookups);
1559 try thunks.createThunks(self, @intCast(u8, sect_id));
15601560 }
15611561 }
15621562 }
......@@ -1601,8 +1601,6 @@ pub const Zld = struct {
16011601
16021602 const slice = self.sections.slice();
16031603 for (slice.items(.header)[indexes.start..indexes.end]) |*header, sect_id| {
1604 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
1605
16061604 const alignment = try math.powi(u32, 2, header.@"align");
16071605 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
16081606 const n_sect = @intCast(u8, indexes.start + sect_id + 1);
......@@ -1613,48 +1611,51 @@ pub const Zld = struct {
16131611 @intCast(u32, segment.fileoff + start_aligned);
16141612 header.addr = segment.vmaddr + start_aligned;
16151613
1616 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1617 n_sect,
1618 header.segName(),
1619 header.sectName(),
1620 });
1614 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
1615 if (atom_index > 0) {
1616 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1617 n_sect,
1618 header.segName(),
1619 header.sectName(),
1620 });
16211621
1622 while (true) {
1623 const atom = self.getAtom(atom_index);
1624 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1625 sym.n_value += header.addr;
1626 sym.n_sect = n_sect;
1622 while (true) {
1623 const atom = self.getAtom(atom_index);
1624 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1625 sym.n_value += header.addr;
1626 sym.n_sect = n_sect;
16271627
1628 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1629 atom.sym_index,
1630 self.getSymbolName(atom.getSymbolWithLoc()),
1631 sym.n_value,
1632 });
1628 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1629 atom.sym_index,
1630 self.getSymbolName(atom.getSymbolWithLoc()),
1631 sym.n_value,
1632 });
16331633
1634 if (atom.getFile() != null) {
1635 // Update each symbol contained within the atom
1636 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1637 while (it.next()) |sym_loc| {
1638 const inner_sym = self.getSymbolPtr(sym_loc);
1639 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1640 self,
1641 atom_index,
1642 sym_loc.sym_index,
1643 );
1644 inner_sym.n_sect = n_sect;
1645 }
1634 if (atom.getFile() != null) {
1635 // Update each symbol contained within the atom
1636 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1637 while (it.next()) |sym_loc| {
1638 const inner_sym = self.getSymbolPtr(sym_loc);
1639 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1640 self,
1641 atom_index,
1642 sym_loc.sym_index,
1643 );
1644 inner_sym.n_sect = n_sect;
1645 }
16461646
1647 // If there is a section alias, update it now too
1648 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1649 const alias = self.getSymbolPtr(sym_loc);
1650 alias.n_value = sym.n_value;
1651 alias.n_sect = n_sect;
1647 // If there is a section alias, update it now too
1648 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1649 const alias = self.getSymbolPtr(sym_loc);
1650 alias.n_value = sym.n_value;
1651 alias.n_sect = n_sect;
1652 }
16521653 }
1653 }
16541654
1655 if (atom.next_index) |next_index| {
1656 atom_index = next_index;
1657 } else break;
1655 if (atom.next_index) |next_index| {
1656 atom_index = next_index;
1657 } else break;
1658 }
16581659 }
16591660
16601661 start = start_aligned + header.size;
......@@ -1675,7 +1676,7 @@ pub const Zld = struct {
16751676 reserved2: u32 = 0,
16761677 };
16771678
1678 fn initSection(
1679 pub fn initSection(
16791680 self: *Zld,
16801681 segname: []const u8,
16811682 sectname: []const u8,
......@@ -1685,7 +1686,7 @@ pub const Zld = struct {
16851686 log.debug("creating section '{s},{s}'", .{ segname, sectname });
16861687 const index = @intCast(u8, self.sections.slice().len);
16871688 try self.sections.append(gpa, .{
1688 .segment_index = undefined,
1689 .segment_index = undefined, // Segments will be created automatically later down the pipeline
16891690 .header = .{
16901691 .sectname = makeStaticString(sectname),
16911692 .segname = makeStaticString(segname),
......@@ -1693,13 +1694,13 @@ pub const Zld = struct {
16931694 .reserved1 = opts.reserved1,
16941695 .reserved2 = opts.reserved2,
16951696 },
1696 .first_atom_index = undefined,
1697 .last_atom_index = undefined,
1697 .first_atom_index = 0,
1698 .last_atom_index = 0,
16981699 });
16991700 return index;
17001701 }
17011702
1702 inline fn getSegmentPrecedence(segname: []const u8) u4 {
1703 fn getSegmentPrecedence(segname: []const u8) u4 {
17031704 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
17041705 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
17051706 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
......@@ -1708,14 +1709,14 @@ pub const Zld = struct {
17081709 return 0x4;
17091710 }
17101711
1711 inline fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
1712 fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
17121713 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
17131714 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
17141715 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
17151716 return macho.PROT.READ | macho.PROT.WRITE;
17161717 }
17171718
1718 inline fn getSectionPrecedence(header: macho.section_64) u8 {
1719 fn getSectionPrecedence(header: macho.section_64) u8 {
17191720 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
17201721 const section_precedence: u4 = blk: {
17211722 if (header.isCode()) {
......@@ -1732,10 +1733,11 @@ pub const Zld = struct {
17321733 macho.S_ZEROFILL => break :blk 0xf,
17331734 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
17341735 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
1735 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
1736 break :blk 0xf
1737 else
1738 break :blk 0x3,
1736 else => {
1737 if (mem.eql(u8, "__unwind_info", header.sectName())) break :blk 0xe;
1738 if (mem.eql(u8, "__eh_frame", header.sectName())) break :blk 0xf;
1739 break :blk 0x3;
1740 },
17391741 }
17401742 };
17411743 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
......@@ -1768,8 +1770,8 @@ pub const Zld = struct {
17681770 }
17691771 }
17701772
1771 fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void {
1772 try self.writeDyldInfoData(reverse_lookups);
1773 fn writeLinkeditSegmentData(self: *Zld) !void {
1774 try self.writeDyldInfoData();
17731775 try self.writeFunctionStarts();
17741776 try self.writeDataInCode();
17751777 try self.writeSymtabs();
......@@ -1806,7 +1808,7 @@ pub const Zld = struct {
18061808 }
18071809 }
18081810
1809 fn collectRebaseData(self: *Zld, rebase: *Rebase, reverse_lookups: [][]u32) !void {
1811 fn collectRebaseData(self: *Zld, rebase: *Rebase) !void {
18101812 log.debug("collecting rebase data", .{});
18111813
18121814 // First, unpack GOT entries
......@@ -1862,6 +1864,7 @@ pub const Zld = struct {
18621864
18631865 const cpu_arch = self.options.target.cpu.arch;
18641866 var atom_index = slice.items(.first_atom_index)[sect_id];
1867 if (atom_index == 0) continue;
18651868
18661869 while (true) {
18671870 const atom = self.getAtom(atom_index);
......@@ -1899,7 +1902,7 @@ pub const Zld = struct {
18991902 },
19001903 else => unreachable,
19011904 }
1902 const target = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
1905 const target = Atom.parseRelocTarget(self, atom_index, rel);
19031906 const target_sym = self.getSymbol(target);
19041907 if (target_sym.undf()) continue;
19051908
......@@ -1962,7 +1965,10 @@ pub const Zld = struct {
19621965 }
19631966 }
19641967
1965 fn collectBindData(self: *Zld, bind: *Bind, reverse_lookups: [][]u32) !void {
1968 fn collectBindData(
1969 self: *Zld,
1970 bind: *Bind,
1971 ) !void {
19661972 log.debug("collecting bind data", .{});
19671973
19681974 // First, unpack GOT section
......@@ -1993,6 +1999,7 @@ pub const Zld = struct {
19931999
19942000 const cpu_arch = self.options.target.cpu.arch;
19952001 var atom_index = slice.items(.first_atom_index)[sect_id];
2002 if (atom_index == 0) continue;
19962003
19972004 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
19982005
......@@ -2033,7 +2040,7 @@ pub const Zld = struct {
20332040 else => unreachable,
20342041 }
20352042
2036 const global = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
2043 const global = Atom.parseRelocTarget(self, atom_index, rel);
20372044 const bind_sym_name = self.getSymbolName(global);
20382045 const bind_sym = self.getSymbol(global);
20392046 if (!bind_sym.undf()) continue;
......@@ -2164,16 +2171,18 @@ pub const Zld = struct {
21642171 try trie.finalize(gpa);
21652172 }
21662173
2167 fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
2174 fn writeDyldInfoData(
2175 self: *Zld,
2176 ) !void {
21682177 const gpa = self.gpa;
21692178
21702179 var rebase = Rebase{};
21712180 defer rebase.deinit(gpa);
2172 try self.collectRebaseData(&rebase, reverse_lookups);
2181 try self.collectRebaseData(&rebase);
21732182
21742183 var bind = Bind{};
21752184 defer bind.deinit(gpa);
2176 try self.collectBindData(&bind, reverse_lookups);
2185 try self.collectBindData(&bind);
21772186
21782187 var lazy_bind = LazyBind{};
21792188 defer lazy_bind.deinit(gpa);
......@@ -2873,12 +2882,12 @@ pub const Zld = struct {
28732882 return buf;
28742883 }
28752884
2876 pub inline fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
2885 pub fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
28772886 assert(atom_index < self.atoms.items.len);
28782887 return &self.atoms.items[atom_index];
28792888 }
28802889
2881 pub inline fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
2890 pub fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
28822891 assert(atom_index < self.atoms.items.len);
28832892 return self.atoms.items[atom_index];
28842893 }
......@@ -2889,17 +2898,17 @@ pub const Zld = struct {
28892898 } else return null;
28902899 }
28912900
2892 pub inline fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
2901 pub fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
28932902 const index = self.sections.items(.segment_index)[sect_id];
28942903 return self.segments.items[index];
28952904 }
28962905
2897 pub inline fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
2906 pub fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
28982907 const index = self.sections.items(.segment_index)[sect_id];
28992908 return &self.segments.items[index];
29002909 }
29012910
2902 pub inline fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
2911 pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
29032912 assert(self.segments.items.len > 0);
29042913 const seg = &self.segments.items[self.segments.items.len - 1];
29052914 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
......@@ -3384,6 +3393,8 @@ pub const Zld = struct {
33843393 const slice = self.sections.slice();
33853394 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
33863395 var atom_index = first_atom_index;
3396 if (atom_index == 0) continue;
3397
33873398 const header = slice.items(.header)[sect_id];
33883399
33893400 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
......@@ -3412,7 +3423,7 @@ pub const Zld = struct {
34123423 sym.n_value,
34133424 atom.size,
34143425 atom.alignment,
3415 atom.file,
3426 atom.getFile(),
34163427 sym.n_sect,
34173428 });
34183429
......@@ -3475,19 +3486,19 @@ const IndirectPointer = struct {
34753486 }
34763487};
34773488
3478pub const SymbolWithLoc = struct {
3489pub const SymbolWithLoc = extern struct {
34793490 // Index into the respective symbol table.
34803491 sym_index: u32,
34813492
3482 // -1 means it's a synthetic global.
3483 file: i32 = -1,
3493 // 0 means it's a synthetic global.
3494 file: u32 = 0,
34843495
3485 pub inline fn getFile(self: SymbolWithLoc) ?u31 {
3486 if (self.file == -1) return null;
3487 return @intCast(u31, self.file);
3496 pub fn getFile(self: SymbolWithLoc) ?u32 {
3497 if (self.file == 0) return null;
3498 return self.file - 1;
34883499 }
34893500
3490 pub inline fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
3501 pub fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
34913502 return self.file == other.file and self.sym_index == other.sym_index;
34923503 }
34933504};
......@@ -3965,7 +3976,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
39653976 };
39663977
39673978 for (zld.objects.items) |_, object_id| {
3968 try zld.resolveSymbolsInObject(@intCast(u16, object_id), &resolver);
3979 try zld.resolveSymbolsInObject(@intCast(u32, object_id), &resolver);
39693980 }
39703981
39713982 try zld.resolveSymbolsInArchives(&resolver);
......@@ -3995,16 +4006,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
39954006 }
39964007
39974008 for (zld.objects.items) |*object, object_id| {
3998 try object.splitIntoAtoms(&zld, @intCast(u31, object_id));
3999 }
4000
4001 var reverse_lookups: [][]u32 = try arena.alloc([]u32, zld.objects.items.len);
4002 for (zld.objects.items) |object, i| {
4003 reverse_lookups[i] = try object.createReverseSymbolLookup(arena);
4009 try object.splitIntoAtoms(&zld, @intCast(u32, object_id));
40044010 }
40054011
40064012 if (gc_sections) {
4007 try dead_strip.gcAtoms(&zld, reverse_lookups);
4013 try dead_strip.gcAtoms(&zld);
40084014 }
40094015
40104016 try zld.createDyldPrivateAtom();
......@@ -4019,13 +4025,24 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
40194025 if (header.isZerofill()) continue;
40204026
40214027 const relocs = Atom.getAtomRelocs(&zld, atom_index);
4022 try Atom.scanAtomRelocs(&zld, atom_index, relocs, reverse_lookups[atom.getFile().?]);
4028 try Atom.scanAtomRelocs(&zld, atom_index, relocs);
40234029 }
40244030 }
40254031
4032 try eh_frame.scanRelocs(&zld);
4033 try UnwindInfo.scanRelocs(&zld);
4034
40264035 try zld.createDyldStubBinderGotAtom();
40274036
4028 try zld.calcSectionSizes(reverse_lookups);
4037 try zld.calcSectionSizes();
4038
4039 var unwind_info = UnwindInfo{ .gpa = zld.gpa };
4040 defer unwind_info.deinit();
4041 try unwind_info.collect(&zld);
4042
4043 try eh_frame.calcSectionSize(&zld, &unwind_info);
4044 try unwind_info.calcSectionSize(&zld);
4045
40294046 try zld.pruneAndSortSections();
40304047 try zld.createSegments();
40314048 try zld.allocateSegments();
......@@ -4039,8 +4056,10 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
40394056 zld.logAtoms();
40404057 }
40414058
4042 try zld.writeAtoms(reverse_lookups);
4043 try zld.writeLinkeditSegmentData(reverse_lookups);
4059 try zld.writeAtoms();
4060 try eh_frame.write(&zld, &unwind_info);
4061 try unwind_info.write(&zld);
4062 try zld.writeLinkeditSegmentData();
40444063
40454064 // If the last section of __DATA segment is zerofill section, we need to ensure
40464065 // that the free space between the end of the last non-zerofill section of __DATA
src/type.zig+37-9
......@@ -3789,6 +3789,39 @@ pub const Type = extern union {
37893789 }
37903790 }
37913791
3792 /// Returns true if the type's layout is already resolved and it is safe
3793 /// to use `abiSize`, `abiAlignment` and `bitSize` on it.
3794 pub fn layoutIsResolved(ty: Type) bool {
3795 switch (ty.zigTypeTag()) {
3796 .Struct => {
3797 if (ty.castTag(.@"struct")) |struct_ty| {
3798 return struct_ty.data.haveLayout();
3799 }
3800 return true;
3801 },
3802 .Union => {
3803 if (ty.cast(Payload.Union)) |union_ty| {
3804 return union_ty.data.haveLayout();
3805 }
3806 return true;
3807 },
3808 .Array => {
3809 if (ty.arrayLenIncludingSentinel() == 0) return true;
3810 return ty.childType().layoutIsResolved();
3811 },
3812 .Optional => {
3813 var buf: Type.Payload.ElemType = undefined;
3814 const payload_ty = ty.optionalChild(&buf);
3815 return payload_ty.layoutIsResolved();
3816 },
3817 .ErrorUnion => {
3818 const payload_ty = ty.errorUnionPayload();
3819 return payload_ty.layoutIsResolved();
3820 },
3821 else => return true,
3822 }
3823 }
3824
37923825 pub fn isSinglePointer(self: Type) bool {
37933826 return switch (self.tag()) {
37943827 .single_const_pointer,
......@@ -5500,7 +5533,7 @@ pub const Type = extern union {
55005533 }
55015534 const S = struct {
55025535 fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, m: *Module) ?usize {
5503 if (int_val.compareAllWithZero(.lt)) return null;
5536 if (int_val.compareAllWithZero(.lt, m)) return null;
55045537 var end_payload: Value.Payload.U64 = .{
55055538 .base = .{ .tag = .int_u64 },
55065539 .data = end,
......@@ -6498,12 +6531,7 @@ pub const Type = extern union {
64986531 // pointee type needs to be resolved more, that needs to be done before calling
64996532 // this ptr() function.
65006533 if (d.@"align" != 0) canonicalize: {
6501 if (d.pointee_type.castTag(.@"struct")) |struct_ty| {
6502 if (!struct_ty.data.haveLayout()) break :canonicalize;
6503 }
6504 if (d.pointee_type.cast(Payload.Union)) |union_ty| {
6505 if (!union_ty.data.haveLayout()) break :canonicalize;
6506 }
6534 if (!d.pointee_type.layoutIsResolved()) break :canonicalize;
65076535 if (d.@"align" == d.pointee_type.abiAlignment(target)) {
65086536 d.@"align" = 0;
65096537 }
......@@ -6528,12 +6556,12 @@ pub const Type = extern union {
65286556 if (!d.mutable and d.pointee_type.eql(Type.u8, mod)) {
65296557 switch (d.size) {
65306558 .Slice => {
6531 if (sent.compareAllWithZero(.eq)) {
6559 if (sent.compareAllWithZero(.eq, mod)) {
65326560 return Type.initTag(.const_slice_u8_sentinel_0);
65336561 }
65346562 },
65356563 .Many => {
6536 if (sent.compareAllWithZero(.eq)) {
6564 if (sent.compareAllWithZero(.eq, mod)) {
65376565 return Type.initTag(.manyptr_const_u8_sentinel_0);
65386566 }
65396567 },
src/value.zig+29-5
......@@ -2076,13 +2076,22 @@ pub const Value = extern union {
20762076 /// For vectors, returns true if comparison is true for ALL elements.
20772077 ///
20782078 /// Note that `!compareAllWithZero(.eq, ...) != compareAllWithZero(.neq, ...)`
2079 pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator) bool {
2080 return compareAllWithZeroAdvanced(lhs, op, null) catch unreachable;
2079 pub fn compareAllWithZero(lhs: Value, op: std.math.CompareOperator, mod: *Module) bool {
2080 return compareAllWithZeroAdvancedExtra(lhs, op, mod, null) catch unreachable;
20812081 }
20822082
20832083 pub fn compareAllWithZeroAdvanced(
20842084 lhs: Value,
20852085 op: std.math.CompareOperator,
2086 sema: *Sema,
2087 ) Module.CompileError!bool {
2088 return compareAllWithZeroAdvancedExtra(lhs, op, sema.mod, sema);
2089 }
2090
2091 pub fn compareAllWithZeroAdvancedExtra(
2092 lhs: Value,
2093 op: std.math.CompareOperator,
2094 mod: *Module,
20862095 opt_sema: ?*Sema,
20872096 ) Module.CompileError!bool {
20882097 if (lhs.isInf()) {
......@@ -2095,10 +2104,25 @@ pub const Value = extern union {
20952104 }
20962105
20972106 switch (lhs.tag()) {
2098 .repeated => return lhs.castTag(.repeated).?.data.compareAllWithZeroAdvanced(op, opt_sema),
2107 .repeated => return lhs.castTag(.repeated).?.data.compareAllWithZeroAdvancedExtra(op, mod, opt_sema),
20992108 .aggregate => {
21002109 for (lhs.castTag(.aggregate).?.data) |elem_val| {
2101 if (!(try elem_val.compareAllWithZeroAdvanced(op, opt_sema))) return false;
2110 if (!(try elem_val.compareAllWithZeroAdvancedExtra(op, mod, opt_sema))) return false;
2111 }
2112 return true;
2113 },
2114 .str_lit => {
2115 const str_lit = lhs.castTag(.str_lit).?.data;
2116 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
2117 for (bytes) |byte| {
2118 if (!std.math.compare(byte, op, 0)) return false;
2119 }
2120 return true;
2121 },
2122 .bytes => {
2123 const bytes = lhs.castTag(.bytes).?.data;
2124 for (bytes) |byte| {
2125 if (!std.math.compare(byte, op, 0)) return false;
21022126 }
21032127 return true;
21042128 },
......@@ -3103,7 +3127,7 @@ pub const Value = extern union {
31033127 .int_i64,
31043128 .int_big_positive,
31053129 .int_big_negative,
3106 => compareAllWithZero(self, .eq),
3130 => self.orderAgainstZero().compare(.eq),
31073131
31083132 .undef => unreachable,
31093133 .unreachable_value => unreachable,
test/behavior.zig-1
......@@ -106,7 +106,6 @@ test {
106106 _ = @import("behavior/bugs/12430.zig");
107107 _ = @import("behavior/bugs/12450.zig");
108108 _ = @import("behavior/bugs/12486.zig");
109 _ = @import("behavior/bugs/12488.zig");
110109 _ = @import("behavior/bugs/12498.zig");
111110 _ = @import("behavior/bugs/12551.zig");
112111 _ = @import("behavior/bugs/12571.zig");
test/behavior/bugs/12488.zig deleted-13
......@@ -1,13 +0,0 @@
1const expect = @import("std").testing.expect;
2
3const A = struct {
4 a: u32,
5};
6
7fn foo(comptime a: anytype) !void {
8 try expect(a[0][0] == @sizeOf(A));
9}
10
11test {
12 try foo(.{[_]usize{@sizeOf(A)}});
13}
test/behavior/fn.zig+23
......@@ -517,3 +517,26 @@ test "peer type resolution of inferred error set with non-void payload" {
517517 };
518518 try expect(try S.openDataFile(.read) == 1);
519519}
520
521test "lazy values passed to anytype parameter" {
522 const A = struct {
523 a: u32,
524 fn foo(comptime a: anytype) !void {
525 try expect(a[0][0] == @sizeOf(@This()));
526 }
527 };
528 try A.foo(.{[_]usize{@sizeOf(A)}});
529
530 const B = struct {
531 fn foo(comptime a: anytype) !void {
532 try expect(a.x == 0);
533 }
534 };
535 try B.foo(.{ .x = @sizeOf(B) });
536
537 const C = struct {};
538 try expect(@truncate(u32, @sizeOf(C)) == 0);
539
540 const D = struct {};
541 try expect(@sizeOf(D) << 1 == 0);
542}
test/behavior/pointers.zig+16
......@@ -509,6 +509,7 @@ test "ptrCast comptime known slice to C pointer" {
509509test "ptrToInt on a generic function" {
510510 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
511511 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
512 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
512513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
513514 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
514515
......@@ -532,3 +533,18 @@ test "pointer alignment and element type include call expression" {
532533 };
533534 try expect(@alignOf(S.P) > 0);
534535}
536
537test "pointer to array has explicit alignment" {
538 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
539
540 const S = struct {
541 const Base = extern struct { a: u8 };
542 const Base2 = extern struct { a: u8 };
543 fn func(ptr: *[4]Base) *align(1) [4]Base2 {
544 return @alignCast(1, @ptrCast(*[4]Base2, ptr));
545 }
546 };
547 var bases = [_]S.Base{.{ .a = 2 }} ** 4;
548 const casted = S.func(&bases);
549 try expect(casted[0].a == 2);
550}
test/behavior/struct.zig+5
......@@ -1573,3 +1573,8 @@ test "struct fields get automatically reordered" {
15731573 };
15741574 try expect(@sizeOf(S1) == @sizeOf(S2));
15751575}
1576
1577test "directly initiating tuple like struct" {
1578 const a = struct { u8 }{8};
1579 try expect(a[0] == 8);
1580}
test/behavior/vector.zig+11
......@@ -1286,3 +1286,14 @@ test "store to vector in slice" {
12861286 s[i] = s[0];
12871287 try expectEqual(v[1], v[0]);
12881288}
1289
1290test "addition of vectors represented as strings" {
1291 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1292 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1293 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1294
1295 const V = @Vector(3, u8);
1296 const foo: V = "foo".*;
1297 const bar: V = @typeName(u32).*;
1298 try expectEqual(V{ 219, 162, 161 }, foo + bar);
1299}
test/c_abi/cfuncs.c+12
......@@ -1015,3 +1015,15 @@ void __attribute__((stdcall)) stdcall_big_union(union BigUnion x) {
10151015 assert_or_panic(x.a.c == 3);
10161016 assert_or_panic(x.a.d == 4);
10171017}
1018
1019#ifdef __x86_64__
1020struct ByRef __attribute__((ms_abi)) c_explict_win64(struct ByRef in) {
1021 in.val = 42;
1022 return in;
1023}
1024
1025struct ByRef __attribute__((sysv_abi)) c_explict_sys_v(struct ByRef in) {
1026 in.val = 42;
1027 return in;
1028}
1029#endif
test/c_abi/main.zig+16
......@@ -1190,3 +1190,19 @@ test "Stdcall ABI big union" {
11901190 };
11911191 stdcall_big_union(x);
11921192}
1193
1194extern fn c_explict_win64(ByRef) callconv(.Win64) ByRef;
1195test "explicit SysV calling convention" {
1196 if (builtin.cpu.arch != .x86_64) return error.SkipZigTest;
1197
1198 const res = c_explict_win64(.{ .val = 1, .arr = undefined });
1199 try expect(res.val == 42);
1200}
1201
1202extern fn c_explict_sys_v(ByRef) callconv(.SysV) ByRef;
1203test "explicit Win64 calling convention" {
1204 if (builtin.cpu.arch != .x86_64) return error.SkipZigTest;
1205
1206 const res = c_explict_sys_v(.{ .val = 1, .arr = undefined });
1207 try expect(res.val == 42);
1208}
test/cases/compile_errors/bad_member_access_on_tuple.zig created+9
......@@ -0,0 +1,9 @@
1comptime {
2 _ = @TypeOf(.{}).is_optional;
3}
4
5// error
6// backend=stage2
7// target=native
8//
9// :2:21: error: struct '@TypeOf(.{})' has no member named 'is_optional'
test/link.zig+5
......@@ -190,6 +190,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
190190 .requires_symlinks = true,
191191 });
192192
193 cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{
194 .build_modes = true,
195 .requires_symlinks = true,
196 });
197
193198 cases.addBuildFile("test/link/macho/uuid/build.zig", .{
194199 .build_modes = false,
195200 .requires_symlinks = true,
test/link/macho/unwind_info/all.h created+41
......@@ -0,0 +1,41 @@
1#ifndef ALL
2#define ALL
3
4#include <cstddef>
5#include <string>
6#include <stdexcept>
7
8struct SimpleString {
9 SimpleString(size_t max_size);
10 ~SimpleString();
11
12 void print(const char* tag) const;
13 bool append_line(const char* x);
14
15private:
16 size_t max_size;
17 char* buffer;
18 size_t length;
19};
20
21struct SimpleStringOwner {
22 SimpleStringOwner(const char* x);
23 ~SimpleStringOwner();
24
25private:
26 SimpleString string;
27};
28
29class Error: public std::exception {
30public:
31 explicit Error(const char* msg) : msg{ msg } {}
32 virtual ~Error() noexcept {}
33 virtual const char* what() const noexcept {
34 return msg.c_str();
35 }
36
37protected:
38 std::string msg;
39};
40
41#endif
test/link/macho/unwind_info/build.zig created+68
......@@ -0,0 +1,68 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
5
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
8 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
9
10 const test_step = b.step("test", "Test the program");
11
12 testUnwindInfo(b, test_step, mode, target, false);
13 testUnwindInfo(b, test_step, mode, target, true);
14}
15
16fn testUnwindInfo(
17 b: *Builder,
18 test_step: *std.build.Step,
19 mode: std.builtin.Mode,
20 target: std.zig.CrossTarget,
21 dead_strip: bool,
22) void {
23 const exe = createScenario(b, mode, target);
24 exe.link_gc_sections = dead_strip;
25
26 const check = exe.checkObject(.macho);
27 check.checkStart("segname __TEXT");
28 check.checkNext("sectname __gcc_except_tab");
29 check.checkNext("sectname __unwind_info");
30
31 switch (builtin.cpu.arch) {
32 .aarch64 => {
33 check.checkNext("sectname __eh_frame");
34 },
35 .x86_64 => {}, // We do not expect `__eh_frame` section on x86_64 in this case
36 else => unreachable,
37 }
38
39 check.checkInSymtab();
40 check.checkNext("{*} (__TEXT,__text) external ___gxx_personality_v0");
41
42 const run_cmd = check.runAndCompare();
43 run_cmd.expectStdOutEqual(
44 \\Constructed: a
45 \\Constructed: b
46 \\About to destroy: b
47 \\About to destroy: a
48 \\Error: Not enough memory!
49 \\
50 );
51
52 test_step.dependOn(&run_cmd.step);
53}
54
55fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
56 const exe = b.addExecutable("test", null);
57 b.default_step.dependOn(&exe.step);
58 exe.addIncludePath(".");
59 exe.addCSourceFiles(&[_][]const u8{
60 "main.cpp",
61 "simple_string.cpp",
62 "simple_string_owner.cpp",
63 }, &[0][]const u8{});
64 exe.setBuildMode(mode);
65 exe.setTarget(target);
66 exe.linkLibCpp();
67 return exe;
68}
test/link/macho/unwind_info/main.cpp created+24
......@@ -0,0 +1,24 @@
1#include "all.h"
2#include <cstdio>
3
4void fn_c() {
5 SimpleStringOwner c{ "cccccccccc" };
6}
7
8void fn_b() {
9 SimpleStringOwner b{ "b" };
10 fn_c();
11}
12
13int main() {
14 try {
15 SimpleStringOwner a{ "a" };
16 fn_b();
17 SimpleStringOwner d{ "d" };
18 } catch (const Error& e) {
19 printf("Error: %s\n", e.what());
20 } catch(const std::exception& e) {
21 printf("Exception: %s\n", e.what());
22 }
23 return 0;
24}
test/link/macho/unwind_info/simple_string.cpp created+30
......@@ -0,0 +1,30 @@
1#include "all.h"
2#include <cstdio>
3#include <cstring>
4
5SimpleString::SimpleString(size_t max_size)
6: max_size{ max_size }, length{} {
7 if (max_size == 0) {
8 throw Error{ "Max size must be at least 1." };
9 }
10 buffer = new char[max_size];
11 buffer[0] = 0;
12}
13
14SimpleString::~SimpleString() {
15 delete[] buffer;
16}
17
18void SimpleString::print(const char* tag) const {
19 printf("%s: %s", tag, buffer);
20}
21
22bool SimpleString::append_line(const char* x) {
23 const auto x_len = strlen(x);
24 if (x_len + length + 2 > max_size) return false;
25 std::strncpy(buffer + length, x, max_size - length);
26 length += x_len;
27 buffer[length++] = '\n';
28 buffer[length] = 0;
29 return true;
30}
test/link/macho/unwind_info/simple_string_owner.cpp created+12
......@@ -0,0 +1,12 @@
1#include "all.h"
2
3SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } {
4 if (!string.append_line(x)) {
5 throw Error{ "Not enough memory!" };
6 }
7 string.print("Constructed");
8}
9
10SimpleStringOwner::~SimpleStringOwner() {
11 string.print("About to destroy");
12}
test/link/macho/uuid/build.zig+6-6
......@@ -12,18 +12,18 @@ pub fn build(b: *Builder) void {
1212 .os_tag = .macos,
1313 };
1414
15 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
16 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
17 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "af0f4c21a07c30daba59213d80262e45");
15 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
16 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
17 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
1818
1919 const x86_64_macos = std.zig.CrossTarget{
2020 .cpu_arch = .x86_64,
2121 .os_tag = .macos,
2222 };
2323
24 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
25 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "63f47191c7153f5fba48bd63cb2f5f57");
26 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "e7bba66220e33eda9e73ab293ccf93d2");
24 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");
25 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");
26 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "4b58f2583c383169bbe3a716bd240048");
2727}
2828
2929fn testUuid(
test/link/macho/weak_library/build.zig+2
......@@ -31,6 +31,8 @@ pub fn build(b: *Builder) void {
3131
3232 check.checkInSymtab();
3333 check.checkNext("(undefined) weak external _a (from liba)");
34
35 check.checkInSymtab();
3436 check.checkNext("(undefined) weak external _asStr (from liba)");
3537
3638 const run_cmd = check.runAndCompare();