authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2023-04-22 19:10:23+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-22 19:10:23+02:00
logc5fb245f60039565f7a5a4de412da815f037ab15
tree0a473e618a9982cd9a0209bc053028eef8497cce
parent428f7452704d1a7b3d9da8e7d4d408db4cbff81f
parentad634bca9f12a9de641270478a6abf9495b9846f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge branch 'master' into autodoc-src-files-dirs


38 files changed, 1137 insertions(+), 1114 deletions(-)

doc/docgen.zig+1
......@@ -1424,6 +1424,7 @@ fn genHtml(
14241424 const result = try ChildProcess.exec(.{
14251425 .allocator = allocator,
14261426 .argv = build_args.items,
1427 .cwd = tmp_dir_name,
14271428 .env_map = &env_map,
14281429 .max_output_bytes = max_doc_file_size,
14291430 });
lib/docs/main.js+21-6
......@@ -2198,13 +2198,26 @@ const NAV_MODES = {
21982198 if (opts.addParensIfFnSignature && fnObj.src == 0) {
21992199 payloadHtml += "(";
22002200 }
2201 if (opts.wantHtml) {
2202 if (fnObj.is_extern) {
2203 payloadHtml += "pub extern ";
2201 if (fnObj.is_extern) {
2202 if (opts.wantHtml) {
2203 payloadHtml += '<span class="tok-kw">extern </span>';
2204 } else {
2205 payloadHtml += "extern ";
22042206 }
2205 if (fnObj.has_lib_name) {
2206 payloadHtml += '"' + fnObj.lib_name + '" ';
2207 } else if (fnObj.has_cc) {
2208 let cc_expr = zigAnalysis.exprs[fnObj.cc];
2209 if (cc_expr.enumLiteral === "Inline") {
2210 if(opts.wantHtml) {
2211 payloadHtml += '<span class="tok-kw">inline </span>'
2212 } else {
2213 payloadHtml += "inline "
2214 }
22072215 }
2216 }
2217 if (fnObj.has_lib_name) {
2218 payloadHtml += '"' + fnObj.lib_name + '" ';
2219 }
2220 if (opts.wantHtml) {
22082221 payloadHtml += '<span class="tok-kw">fn </span>';
22092222 if (fnDecl) {
22102223 payloadHtml += '<span class="tok-fn">';
......@@ -2324,7 +2337,9 @@ const NAV_MODES = {
23242337 if (fnObj.has_cc) {
23252338 let cc = zigAnalysis.exprs[fnObj.cc];
23262339 if (cc) {
2327 payloadHtml += "callconv(." + cc.enumLiteral + ") ";
2340 if (cc.enumLiteral !== "Inline") {
2341 payloadHtml += "callconv(" + exprName(cc, opts) + ") ";
2342 }
23282343 }
23292344 }
23302345
lib/std/array_hash_map.zig+2-2
......@@ -54,7 +54,7 @@ pub fn hashString(s: []const u8) u32 {
5454
5555/// Insertion order is preserved.
5656/// Deletions perform a "swap removal" on the entries list.
57/// Modifying the hash map while iterating is allowed, however one must understand
57/// Modifying the hash map while iterating is allowed, however, one must understand
5858/// the (well defined) behavior when mixing insertions and deletions with iteration.
5959/// For a hash map that can be initialized directly that does not store an Allocator
6060/// field, see `ArrayHashMapUnmanaged`.
......@@ -448,7 +448,7 @@ pub fn ArrayHashMap(
448448/// General purpose hash table.
449449/// Insertion order is preserved.
450450/// Deletions perform a "swap removal" on the entries list.
451/// Modifying the hash map while iterating is allowed, however one must understand
451/// Modifying the hash map while iterating is allowed, however, one must understand
452452/// the (well defined) behavior when mixing insertions and deletions with iteration.
453453/// This type does not store an Allocator field - the Allocator must be passed in
454454/// with each function call that requires it. See `ArrayHashMap` for a type that stores
lib/std/array_list.zig+2-2
......@@ -31,7 +31,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
3131 return struct {
3232 const Self = @This();
3333 /// Contents of the list. Pointers to elements in this slice are
34 /// **invalid after resizing operations** on the ArrayList, unless the
34 /// **invalid after resizing operations** on the ArrayList unless the
3535 /// operation explicitly either: (1) states otherwise or (2) lists the
3636 /// invalidated pointers.
3737 ///
......@@ -527,7 +527,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
527527 return struct {
528528 const Self = @This();
529529 /// Contents of the list. Pointers to elements in this slice are
530 /// **invalid after resizing operations** on the ArrayList, unless the
530 /// **invalid after resizing operations** on the ArrayList unless the
531531 /// operation explicitly either: (1) states otherwise or (2) lists the
532532 /// invalidated pointers.
533533 ///
lib/std/buf_map.zig+1-1
......@@ -4,7 +4,7 @@ const mem = std.mem;
44const Allocator = mem.Allocator;
55const testing = std.testing;
66
7/// BufMap copies keys and values before they go into the map, and
7/// BufMap copies keys and values before they go into the map and
88/// frees them when they get removed.
99pub const BufMap = struct {
1010 hash_map: BufMapHashMap,
lib/std/c/freebsd.zig+13
......@@ -13,6 +13,19 @@ pub const cpulevel_t = c_int;
1313pub const cpuwhich_t = c_int;
1414pub const id_t = i64;
1515
16pub const CPU_LEVEL_ROOT: cpulevel_t = 1;
17pub const CPU_LEVEL_CPUSET: cpulevel_t = 2;
18pub const CPU_LEVEL_WHICH: cpulevel_t = 3;
19pub const CPU_WHICH_TID: cpuwhich_t = 1;
20pub const CPU_WHICH_PID: cpuwhich_t = 2;
21pub const CPU_WHICH_CPUSET: cpuwhich_t = 3;
22pub const CPU_WHICH_IRQ: cpuwhich_t = 4;
23pub const CPU_WHICH_JAIL: cpuwhich_t = 5;
24pub const CPU_WHICH_DOMAIN: cpuwhich_t = 6;
25pub const CPU_WHICH_INTRHANDLER: cpuwhich_t = 7;
26pub const CPU_WHICH_ITHREAD: cpuwhich_t = 8;
27pub const CPU_WHICH_TIDPID: cpuwhich_t = 8;
28
1629extern "c" fn __error() *c_int;
1730pub const _errno = __error;
1831
lib/std/c/netbsd.zig+12
......@@ -121,9 +121,21 @@ pub const pthread_attr_t = extern struct {
121121};
122122
123123pub const sem_t = ?*opaque {};
124pub const cpuset_t = opaque {};
125pub const cpuid_t = c_ulong;
124126
125127pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*anyopaque) E;
126128pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
129pub extern "c" fn pthread_setaffinity_np(thread: std.c.pthread_t, size: usize, set: ?*cpuset_t) c_int;
130pub extern "c" fn pthread_getaffinity_np(thread: std.c.pthread_t, size: usize, set: ?*cpuset_t) c_int;
131
132pub extern "c" fn cpuset_create() ?*cpuset_t;
133pub extern "c" fn cpuset_destroy(set: ?*cpuset_t) void;
134pub extern "c" fn cpuset_zero(set: ?*cpuset_t) void;
135pub extern "c" fn cpuset_set(cpu: cpuid_t, set: ?*cpuset_t) c_int;
136pub extern "c" fn cpuset_clr(cpu: cpuid_t, set: ?*cpuset_t) c_int;
137pub extern "c" fn cpuset_isset(cpu: cpuid_t, set: ?*const cpuset_t) c_int;
138pub extern "c" fn cpuset_size(set: ?*cpuset_t) usize;
127139
128140pub const blkcnt_t = i64;
129141pub const blksize_t = i32;
lib/std/comptime_string_map.zig+4
......@@ -50,12 +50,16 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
5050 };
5151
5252 return struct {
53 /// Array of `struct { key: []const u8, value: V }` where `value` is `void{}` if `V` is `void`.
54 /// Sorted by `key` length.
5355 pub const kvs = precomputed.sorted_kvs;
5456
57 /// Checks if the map has a value for the key.
5558 pub fn has(str: []const u8) bool {
5659 return get(str) != null;
5760 }
5861
62 /// Returns the value for the key if any, else null.
5963 pub fn get(str: []const u8) ?V {
6064 if (str.len < precomputed.min_len or str.len > precomputed.max_len)
6165 return null;
lib/std/hash_map.zig+1-1
......@@ -350,7 +350,7 @@ pub fn verifyContext(
350350/// General purpose hash table.
351351/// No order is guaranteed and any modification invalidates live iterators.
352352/// It provides fast operations (lookup, insertion, deletion) with quite high
353/// load factors (up to 80% by default) for a low memory usage.
353/// load factors (up to 80% by default) for low memory usage.
354354/// For a hash map that can be initialized directly that does not store an Allocator
355355/// field, see `HashMapUnmanaged`.
356356/// If iterating over the table entries is a strong usecase and needs to be fast,
lib/std/heap.zig+1
......@@ -16,6 +16,7 @@ pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").Log
1616pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
1717pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
1818pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
19pub const Check = @import("heap/general_purpose_allocator.zig").Check;
1920pub const WasmAllocator = @import("heap/WasmAllocator.zig");
2021pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");
2122pub const PageAllocator = @import("heap/PageAllocator.zig");
lib/std/heap/general_purpose_allocator.zig+23-21
......@@ -155,6 +155,8 @@ pub const Config = struct {
155155 verbose_log: bool = false,
156156};
157157
158pub const Check = enum { ok, leak };
159
158160pub fn GeneralPurposeAllocator(comptime config: Config) type {
159161 return struct {
160162 backing_allocator: Allocator = std.heap.page_allocator,
......@@ -431,7 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
431433 } else struct {};
432434
433435 /// Returns true if there were leaks; false otherwise.
434 pub fn deinit(self: *Self) bool {
436 pub fn deinit(self: *Self) Check {
435437 const leaks = if (config.safety) self.detectLeaks() else false;
436438 if (config.retain_metadata) {
437439 self.freeRetainedMetadata();
......@@ -441,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
441443 self.small_allocations.deinit(self.backing_allocator);
442444 }
443445 self.* = undefined;
444 return leaks;
446 return @intToEnum(Check, @boolToInt(leaks));
445447 }
446448
447449 fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void {
......@@ -1024,7 +1026,7 @@ const test_config = Config{};
10241026
10251027test "small allocations - free in same order" {
10261028 var gpa = GeneralPurposeAllocator(test_config){};
1027 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1029 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
10281030 const allocator = gpa.allocator();
10291031
10301032 var list = std.ArrayList(*u64).init(std.testing.allocator);
......@@ -1043,7 +1045,7 @@ test "small allocations - free in same order" {
10431045
10441046test "small allocations - free in reverse order" {
10451047 var gpa = GeneralPurposeAllocator(test_config){};
1046 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1048 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
10471049 const allocator = gpa.allocator();
10481050
10491051 var list = std.ArrayList(*u64).init(std.testing.allocator);
......@@ -1062,7 +1064,7 @@ test "small allocations - free in reverse order" {
10621064
10631065test "large allocations" {
10641066 var gpa = GeneralPurposeAllocator(test_config){};
1065 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1067 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
10661068 const allocator = gpa.allocator();
10671069
10681070 const ptr1 = try allocator.alloc(u64, 42768);
......@@ -1075,7 +1077,7 @@ test "large allocations" {
10751077
10761078test "very large allocation" {
10771079 var gpa = GeneralPurposeAllocator(test_config){};
1078 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1080 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
10791081 const allocator = gpa.allocator();
10801082
10811083 try std.testing.expectError(error.OutOfMemory, allocator.alloc(u8, math.maxInt(usize)));
......@@ -1083,7 +1085,7 @@ test "very large allocation" {
10831085
10841086test "realloc" {
10851087 var gpa = GeneralPurposeAllocator(test_config){};
1086 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1088 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
10871089 const allocator = gpa.allocator();
10881090
10891091 var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1);
......@@ -1105,7 +1107,7 @@ test "realloc" {
11051107
11061108test "shrink" {
11071109 var gpa = GeneralPurposeAllocator(test_config){};
1108 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1110 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11091111 const allocator = gpa.allocator();
11101112
11111113 var slice = try allocator.alloc(u8, 20);
......@@ -1130,7 +1132,7 @@ test "shrink" {
11301132
11311133test "large object - grow" {
11321134 var gpa = GeneralPurposeAllocator(test_config){};
1133 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1135 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11341136 const allocator = gpa.allocator();
11351137
11361138 var slice1 = try allocator.alloc(u8, page_size * 2 - 20);
......@@ -1148,7 +1150,7 @@ test "large object - grow" {
11481150
11491151test "realloc small object to large object" {
11501152 var gpa = GeneralPurposeAllocator(test_config){};
1151 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1153 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11521154 const allocator = gpa.allocator();
11531155
11541156 var slice = try allocator.alloc(u8, 70);
......@@ -1165,7 +1167,7 @@ test "realloc small object to large object" {
11651167
11661168test "shrink large object to large object" {
11671169 var gpa = GeneralPurposeAllocator(test_config){};
1168 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1170 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11691171 const allocator = gpa.allocator();
11701172
11711173 var slice = try allocator.alloc(u8, page_size * 2 + 50);
......@@ -1190,7 +1192,7 @@ test "shrink large object to large object" {
11901192
11911193test "shrink large object to large object with larger alignment" {
11921194 var gpa = GeneralPurposeAllocator(test_config){};
1193 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1195 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11941196 const allocator = gpa.allocator();
11951197
11961198 var debug_buffer: [1000]u8 = undefined;
......@@ -1226,7 +1228,7 @@ test "shrink large object to large object with larger alignment" {
12261228
12271229test "realloc large object to small object" {
12281230 var gpa = GeneralPurposeAllocator(test_config){};
1229 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1231 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12301232 const allocator = gpa.allocator();
12311233
12321234 var slice = try allocator.alloc(u8, page_size * 2 + 50);
......@@ -1244,7 +1246,7 @@ test "overrideable mutexes" {
12441246 .backing_allocator = std.testing.allocator,
12451247 .mutex = std.Thread.Mutex{},
12461248 };
1247 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1249 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12481250 const allocator = gpa.allocator();
12491251
12501252 const ptr = try allocator.create(i32);
......@@ -1253,7 +1255,7 @@ test "overrideable mutexes" {
12531255
12541256test "non-page-allocator backing allocator" {
12551257 var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = std.testing.allocator };
1256 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1258 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12571259 const allocator = gpa.allocator();
12581260
12591261 const ptr = try allocator.create(i32);
......@@ -1262,7 +1264,7 @@ test "non-page-allocator backing allocator" {
12621264
12631265test "realloc large object to larger alignment" {
12641266 var gpa = GeneralPurposeAllocator(test_config){};
1265 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1267 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12661268 const allocator = gpa.allocator();
12671269
12681270 var debug_buffer: [1000]u8 = undefined;
......@@ -1304,7 +1306,7 @@ test "realloc large object to larger alignment" {
13041306test "large object shrinks to small but allocation fails during shrink" {
13051307 var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3);
13061308 var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = failing_allocator.allocator() };
1307 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1309 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13081310 const allocator = gpa.allocator();
13091311
13101312 var slice = try allocator.alloc(u8, page_size * 2 + 50);
......@@ -1322,7 +1324,7 @@ test "large object shrinks to small but allocation fails during shrink" {
13221324
13231325test "objects of size 1024 and 2048" {
13241326 var gpa = GeneralPurposeAllocator(test_config){};
1325 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1327 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13261328 const allocator = gpa.allocator();
13271329
13281330 const slice = try allocator.alloc(u8, 1025);
......@@ -1334,7 +1336,7 @@ test "objects of size 1024 and 2048" {
13341336
13351337test "setting a memory cap" {
13361338 var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};
1337 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1339 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13381340 const allocator = gpa.allocator();
13391341
13401342 gpa.setRequestedMemoryLimit(1010);
......@@ -1361,11 +1363,11 @@ test "setting a memory cap" {
13611363test "double frees" {
13621364 // use a GPA to back a GPA to check for leaks of the latter's metadata
13631365 var backing_gpa = GeneralPurposeAllocator(.{ .safety = true }){};
1364 defer std.testing.expect(!backing_gpa.deinit()) catch @panic("leak");
1366 defer std.testing.expect(backing_gpa.deinit() == .ok) catch @panic("leak");
13651367
13661368 const GPA = GeneralPurposeAllocator(.{ .safety = true, .never_unmap = true, .retain_metadata = true });
13671369 var gpa = GPA{ .backing_allocator = backing_gpa.allocator() };
1368 defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
1370 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13691371 const allocator = gpa.allocator();
13701372
13711373 // detect a small allocation double free, even though bucket is emptied
lib/std/os.zig+27-8
......@@ -141,7 +141,12 @@ pub const addrinfo = system.addrinfo;
141141pub const blkcnt_t = system.blkcnt_t;
142142pub const blksize_t = system.blksize_t;
143143pub const clock_t = system.clock_t;
144pub const cpu_set_t = system.cpu_set_t;
144pub const cpu_set_t = if (builtin.os.tag == .linux)
145 system.cpu_set_t
146else if (builtin.os.tag == .freebsd)
147 freebsd.cpuset_t
148else
149 u32;
145150pub const dev_t = system.dev_t;
146151pub const dl_phdr_info = system.dl_phdr_info;
147152pub const empty_sigset = system.empty_sigset;
......@@ -5518,13 +5523,27 @@ pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
55185523
55195524pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
55205525 var set: cpu_set_t = undefined;
5521 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
5522 .SUCCESS => return set,
5523 .FAULT => unreachable,
5524 .INVAL => unreachable,
5525 .SRCH => unreachable,
5526 .PERM => return error.PermissionDenied,
5527 else => |err| return unexpectedErrno(err),
5526 if (builtin.os.tag == .linux) {
5527 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
5528 .SUCCESS => return set,
5529 .FAULT => unreachable,
5530 .INVAL => unreachable,
5531 .SRCH => unreachable,
5532 .PERM => return error.PermissionDenied,
5533 else => |err| return unexpectedErrno(err),
5534 }
5535 } else if (builtin.os.tag == .freebsd) {
5536 switch (errno(freebsd.cpuset_getaffinity(freebsd.CPU_LEVEL_WHICH, freebsd.CPU_WHICH_PID, pid, @sizeOf(cpu_set_t), &set))) {
5537 .SUCCESS => return set,
5538 .FAULT => unreachable,
5539 .INVAL => unreachable,
5540 .SRCH => unreachable,
5541 .EDEADLK => unreachable,
5542 .PERM => return error.PermissionDenied,
5543 else => |err| return unexpectedErrno(err),
5544 }
5545 } else {
5546 @compileError("unsupported platform");
55285547 }
55295548}
55305549
lib/std/os/linux/seccomp.zig+2-2
......@@ -29,8 +29,8 @@
2929//! which is dependant on the ABI. Since BPF programs execute in a 32-bit
3030//! machine, validation of 64-bit arguments necessitates two load-and-compare
3131//! instructions for the upper and lower words.
32//! 3. A further wrinkle to the above is endianess. Unlike network packets,
33//! syscall data shares the endianess of the target machine. A filter
32//! 3. A further wrinkle to the above is endianness. Unlike network packets,
33//! syscall data shares the endianness of the target machine. A filter
3434//! compiled on a little-endian machine will not work on a big-endian one,
3535//! and vice-versa. For example: Checking the upper 32-bits of `data.arg1`
3636//! requires a load at `@offsetOf(data, "arg1") + 4` on big-endian systems
lib/std/packed_int_array.zig+3-3
......@@ -182,7 +182,7 @@ pub fn PackedIntIo(comptime Int: type, comptime endian: Endian) type {
182182
183183/// Creates a bit-packed array of `Int`. Non-byte-multiple integers
184184/// will take up less memory in PackedIntArray than in a normal array.
185/// Elements are packed using native endianess and without storing any
185/// Elements are packed using native endianness and without storing any
186186/// meta data. PackedArray(i3, 8) will occupy exactly 3 bytes
187187/// of memory.
188188pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type {
......@@ -261,7 +261,7 @@ pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: Endian, comptim
261261 }
262262
263263 /// Create a PackedIntSliceEndian of the array using `NewInt` as the integer type
264 /// and `new_endian` as the new endianess. `NewInt`'s bit width must fit evenly
264 /// and `new_endian` as the new endianness. `NewInt`'s bit width must fit evenly
265265 /// within the array's `Int`'s total bits.
266266 pub fn sliceCastEndian(self: *Self, comptime NewInt: type, comptime new_endian: Endian) PackedIntSliceEndian(NewInt, new_endian) {
267267 return Io.sliceCast(&self.bytes, NewInt, new_endian, 0, int_count);
......@@ -336,7 +336,7 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: Endian) type {
336336 }
337337
338338 /// Create a PackedIntSliceEndian of the slice using `NewInt` as the integer type
339 /// and `new_endian` as the new endianess. `NewInt`'s bit width must fit evenly
339 /// and `new_endian` as the new endianness. `NewInt`'s bit width must fit evenly
340340 /// within the slice's `Int`'s total bits.
341341 pub fn sliceCastEndian(self: Self, comptime NewInt: type, comptime new_endian: Endian) PackedIntSliceEndian(NewInt, new_endian) {
342342 return Io.sliceCast(self.bytes, NewInt, new_endian, self.bit_offset, self.len);
lib/std/process.zig+9
......@@ -1162,6 +1162,15 @@ pub fn totalSystemMemory() TotalSystemMemoryError!usize {
11621162 .linux => {
11631163 return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory;
11641164 },
1165 .freebsd => {
1166 var physmem: c_ulong = undefined;
1167 var len: usize = @sizeOf(c_ulong);
1168 os.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) {
1169 error.NameTooLong, error.UnknownName => unreachable,
1170 else => |e| return e,
1171 };
1172 return @intCast(usize, physmem);
1173 },
11651174 .windows => {
11661175 var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined;
11671176 const rc = std.os.windows.ntdll.NtQuerySystemInformation(
lib/test_runner.zig+3-3
......@@ -56,7 +56,7 @@ fn mainServer() !void {
5656 },
5757 .query_test_metadata => {
5858 std.testing.allocator_instance = .{};
59 defer if (std.testing.allocator_instance.deinit()) {
59 defer if (std.testing.allocator_instance.deinit() == .leak) {
6060 @panic("internal test runner memory leak");
6161 };
6262
......@@ -108,7 +108,7 @@ fn mainServer() !void {
108108 }
109109 },
110110 };
111 leak = std.testing.allocator_instance.deinit();
111 leak = std.testing.allocator_instance.deinit() == .leak;
112112 try server.serveTestResults(.{
113113 .index = index,
114114 .flags = .{
......@@ -148,7 +148,7 @@ fn mainTerminal() void {
148148 for (test_fn_list, 0..) |test_fn, i| {
149149 std.testing.allocator_instance = .{};
150150 defer {
151 if (std.testing.allocator_instance.deinit()) {
151 if (std.testing.allocator_instance.deinit() == .leak) {
152152 leaks += 1;
153153 }
154154 }
src/Autodoc.zig+26-18
......@@ -112,7 +112,7 @@ pub fn generateZirData(self: *Autodoc) !void {
112112 .ComptimeExpr = .{ .name = "ComptimeExpr" },
113113 });
114114
115 // this skipts Ref.none but it's ok becuse we replaced it with ComptimeExpr
115 // this skips Ref.none but it's ok becuse we replaced it with ComptimeExpr
116116 var i: u32 = 1;
117117 while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) {
118118 var tmpbuf = std.ArrayList(u8).init(self.arena);
......@@ -196,8 +196,10 @@ pub fn generateZirData(self: *Autodoc) !void {
196196 .anyerror_type => .{
197197 .ErrorSet = .{ .name = try tmpbuf.toOwnedSlice() },
198198 },
199 .calling_convention_inline, .calling_convention_c, .calling_convention_type => .{
200 .EnumLiteral = .{ .name = try tmpbuf.toOwnedSlice() },
199 // should be an Enum but if we don't analyze std we don't get the ast node
200 // since it's std.builtin.CallingConvention
201 .calling_convention_type => .{
202 .Type = .{ .name = try tmpbuf.toOwnedSlice() },
201203 },
202204 },
203205 );
......@@ -4010,17 +4012,27 @@ fn analyzeFancyFunction(
40104012 }
40114013
40124014 var cc_index: ?usize = null;
4013 if (extra.data.bits.has_cc_ref) {
4015 if (extra.data.bits.has_cc_ref and !extra.data.bits.has_cc_body) {
40144016 const cc_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
4017 const cc_expr = try self.walkRef(file, scope, parent_src, cc_ref, false);
4018
40154019 cc_index = self.exprs.items.len;
4016 _ = try self.walkRef(file, scope, parent_src, cc_ref, false);
4020 try self.exprs.append(self.arena, cc_expr.expr);
4021
40174022 extra_index += 1;
40184023 } else if (extra.data.bits.has_cc_body) {
40194024 const cc_body_len = file.zir.extra[extra_index];
40204025 extra_index += 1;
4021 const cc_body = file.zir.extra[extra_index .. extra_index + cc_body_len];
4022 _ = cc_body;
4023 // TODO: analyze the block (or bail with a comptimeExpr)
4026 const cc_body = file.zir.extra[extra_index..][0..cc_body_len];
4027
4028 // We assume the body ends with a break_inline
4029 const break_index = cc_body[cc_body.len - 1];
4030 const break_operand = data[break_index].@"break".operand;
4031 const cc_expr = try self.walkRef(file, scope, parent_src, break_operand, false);
4032
4033 cc_index = self.exprs.items.len;
4034 try self.exprs.append(self.arena, cc_expr.expr);
4035
40244036 extra_index += cc_body_len;
40254037 } else {
40264038 // auto calling convention
......@@ -4565,26 +4577,22 @@ fn walkRef(
45654577 .expr = .{ .int = .{ .value = 1 } },
45664578 };
45674579 },
4568 // TODO: dunno what to do with those
45694580 .calling_convention_type => {
45704581 return DocData.WalkResult{
4571 .typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) },
4572 // .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
4573 .expr = .{ .int = .{ .value = 1 } },
4582 .typeRef = .{ .type = @enumToInt(Ref.type_type) },
4583 .expr = .{ .type = @enumToInt(Ref.calling_convention_type) },
45744584 };
45754585 },
45764586 .calling_convention_c => {
45774587 return DocData.WalkResult{
4578 .typeRef = .{ .type = @enumToInt(Ref.calling_convention_c) },
4579 // .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
4580 .expr = .{ .int = .{ .value = 1 } },
4588 .typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) },
4589 .expr = .{ .enumLiteral = "C" },
45814590 };
45824591 },
45834592 .calling_convention_inline => {
45844593 return DocData.WalkResult{
4585 .typeRef = .{ .type = @enumToInt(Ref.calling_convention_inline) },
4586 // .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
4587 .expr = .{ .int = .{ .value = 1 } },
4594 .typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) },
4595 .expr = .{ .enumLiteral = "Inline" },
45884596 };
45894597 },
45904598 // .generic_poison => {
src/arch/aarch64/CodeGen.zig+1
......@@ -4290,6 +4290,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
42904290 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
42914291 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
42924292 const atom = elf_file.getAtom(atom_index);
4293 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
42934294 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
42944295 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
42954296 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
src/arch/arm/CodeGen.zig+1
......@@ -4270,6 +4270,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
42704270 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
42714271 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
42724272 const atom = elf_file.getAtom(atom_index);
4273 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
42734274 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
42744275 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
42754276 } else if (self.bin_file.cast(link.File.MachO)) |_| {
src/arch/riscv64/CodeGen.zig+1
......@@ -1734,6 +1734,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
17341734 const func = func_payload.data;
17351735 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
17361736 const atom = elf_file.getAtom(atom_index);
1737 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
17371738 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
17381739 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
17391740 _ = try self.addInst(.{
src/arch/sparc64/CodeGen.zig+1
......@@ -1254,6 +1254,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
12541254 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
12551255 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
12561256 const atom = elf_file.getAtom(atom_index);
1257 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
12571258 break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));
12581259 } else unreachable;
12591260
src/arch/x86_64/CodeGen.zig+10-4
......@@ -5624,7 +5624,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
56245624
56255625 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
56265626 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
5627 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
5627 const atom = elf_file.getAtom(atom_index);
5628 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
5629 const got_addr = atom.getOffsetTableAddress(elf_file);
56285630 try self.asmMemory(.call, Memory.sib(.qword, .{
56295631 .base = .ds,
56305632 .disp = @intCast(i32, got_addr),
......@@ -5853,7 +5855,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
58535855 .{ .kind = .const_data, .ty = Type.anyerror },
58545856 4, // dword alignment
58555857 );
5856 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
5858 const atom = elf_file.getAtom(atom_index);
5859 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
5860 const got_addr = atom.getOffsetTableAddress(elf_file);
58575861 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
58585862 .base = .ds,
58595863 .disp = @intCast(i32, got_addr),
......@@ -7574,7 +7578,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
75747578 const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl);
75757579 if (self.bin_file.cast(link.File.MachO)) |_| {
75767580 _ = try self.addInst(.{
7577 .tag = .mov_linker,
7581 .tag = .lea_linker,
75787582 .ops = .tlv_reloc,
75797583 .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
75807584 .reg = @enumToInt(Register.rdi),
......@@ -8230,7 +8234,9 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
82308234 .{ .kind = .const_data, .ty = Type.anyerror },
82318235 4, // dword alignment
82328236 );
8233 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
8237 const atom = elf_file.getAtom(atom_index);
8238 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
8239 const got_addr = atom.getOffsetTableAddress(elf_file);
82348240 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
82358241 .base = .ds,
82368242 .disp = @intCast(i32, got_addr),
src/codegen.zig+1
......@@ -1006,6 +1006,7 @@ fn genDeclRef(
10061006 if (bin_file.cast(link.File.Elf)) |elf_file| {
10071007 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
10081008 const atom = elf_file.getAtom(atom_index);
1009 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
10091010 return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });
10101011 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
10111012 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
src/link/Coff.zig+167-155
......@@ -37,13 +37,14 @@ strtab_offset: ?u32 = null,
3737
3838temp_strtab: StringTable(.temp_strtab) = .{},
3939
40got_entries: std.ArrayListUnmanaged(Entry) = .{},
41got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
42got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
40got_table: TableSection(SymbolWithLoc) = .{},
4341
4442/// A table of ImportTables partitioned by the library name.
4543/// Key is an offset into the interning string table `temp_strtab`.
4644import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{},
45
46got_table_count_dirty: bool = true,
47got_table_contents_dirty: bool = true,
4748imports_count_dirty: bool = true,
4849
4950/// Virtual address of the entry point procedure relative to image base.
......@@ -106,12 +107,6 @@ const HotUpdateState = struct {
106107 loaded_base_address: ?std.os.windows.HMODULE = null,
107108};
108109
109const Entry = struct {
110 target: SymbolWithLoc,
111 // Index into the synthetic symbol table (i.e., file == null).
112 sym_index: u32,
113};
114
115110const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
116111const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
117112const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
......@@ -188,7 +183,8 @@ pub const PtrWidth = enum {
188183 p32,
189184 p64,
190185
191 fn abiSize(pw: PtrWidth) u4 {
186 /// Size in bytes.
187 pub fn size(pw: PtrWidth) u4 {
192188 return switch (pw) {
193189 .p32 => 4,
194190 .p64 => 8,
......@@ -310,9 +306,7 @@ pub fn deinit(self: *Coff) void {
310306 self.globals_free_list.deinit(gpa);
311307 self.strtab.deinit(gpa);
312308 self.temp_strtab.deinit(gpa);
313 self.got_entries.deinit(gpa);
314 self.got_entries_free_list.deinit(gpa);
315 self.got_entries_table.deinit(gpa);
309 self.got_table.deinit(gpa);
316310
317311 for (self.import_tables.values()) |*itab| {
318312 itab.deinit(gpa);
......@@ -371,7 +365,7 @@ fn populateMissingMetadata(self: *Coff) !void {
371365 }
372366
373367 if (self.got_section_index == null) {
374 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
368 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size();
375369 self.got_section_index = try self.allocateSection(".got", file_size, .{
376370 .CNT_INITIALIZED_DATA = 1,
377371 .MEM_READ = 1,
......@@ -396,7 +390,7 @@ fn populateMissingMetadata(self: *Coff) !void {
396390 }
397391
398392 if (self.idata_section_index == null) {
399 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
393 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size();
400394 self.idata_section_index = try self.allocateSection(".idata", file_size, .{
401395 .CNT_INITIALIZED_DATA = 1,
402396 .MEM_READ = 1,
......@@ -498,8 +492,8 @@ fn growSection(self: *Coff, sect_id: u32, needed_size: u32) !void {
498492
499493 const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);
500494 if (needed_size > sect_vm_capacity) {
501 try self.growSectionVirtualMemory(sect_id, needed_size);
502495 self.markRelocsDirtyByAddress(header.virtual_address + needed_size);
496 try self.growSectionVirtualMemory(sect_id, needed_size);
503497 }
504498
505499 header.virtual_size = @max(header.virtual_size, needed_size);
......@@ -698,26 +692,12 @@ fn allocateGlobal(self: *Coff) !u32 {
698692 return index;
699693}
700694
701pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
702 const gpa = self.base.allocator;
703 try self.got_entries.ensureUnusedCapacity(gpa, 1);
704
705 const index: u32 = blk: {
706 if (self.got_entries_free_list.popOrNull()) |index| {
707 log.debug(" (reusing GOT entry index {d})", .{index});
708 break :blk index;
709 } else {
710 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len});
711 const index = @intCast(u32, self.got_entries.items.len);
712 _ = self.got_entries.addOneAssumeCapacity();
713 break :blk index;
714 }
715 };
716
717 self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
718 try self.got_entries_table.putNoClobber(gpa, target, index);
719
720 return index;
695fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void {
696 if (self.got_table.lookup.contains(target)) return;
697 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
698 try self.writeOffsetTableEntry(got_index);
699 self.got_table_count_dirty = true;
700 self.markRelocsDirtyByTarget(target);
721701}
722702
723703pub fn createAtom(self: *Coff) !Atom.Index {
......@@ -737,37 +717,6 @@ pub fn createAtom(self: *Coff) !Atom.Index {
737717 return atom_index;
738718}
739719
740fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index {
741 const atom_index = try self.createAtom();
742 const atom = self.getAtomPtr(atom_index);
743 atom.size = @sizeOf(u64);
744
745 const sym = atom.getSymbolPtr(self);
746 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
747 sym.value = try self.allocateAtom(atom_index, atom.size, @sizeOf(u64));
748
749 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
750
751 try Atom.addRelocation(self, atom_index, .{
752 .type = .direct,
753 .target = target,
754 .offset = 0,
755 .addend = 0,
756 .pcrel = false,
757 .length = 3,
758 });
759
760 const target_sym = self.getSymbol(target);
761 switch (target_sym.section_number) {
762 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
763 .ABSOLUTE => {},
764 .DEBUG => unreachable, // not possible
765 else => try Atom.addBaseRelocation(self, atom_index, 0),
766 }
767
768 return atom_index;
769}
770
771720fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
772721 const atom = self.getAtom(atom_index);
773722 const sym = atom.getSymbol(self);
......@@ -873,17 +822,75 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []
873822 if (amt != code.len) return error.InputOutput;
874823}
875824
876fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
825fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
826 const sect_id = self.got_section_index.?;
827
828 if (self.got_table_count_dirty) {
829 const needed_size = @intCast(u32, self.got_table.entries.items.len * self.ptr_width.size());
830 try self.growSection(sect_id, needed_size);
831 self.got_table_count_dirty = false;
832 }
833
834 const header = &self.sections.items(.header)[sect_id];
835 const entry = self.got_table.entries.items[index];
836 const entry_value = self.getSymbol(entry).value;
837 const entry_offset = index * self.ptr_width.size();
838 const file_offset = header.pointer_to_raw_data + entry_offset;
839 const vmaddr = header.virtual_address + entry_offset;
840
841 log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value + self.getImageBase() });
842
877843 switch (self.ptr_width) {
878844 .p32 => {
879 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
880 try self.writeAtom(atom_index, &buffer);
845 var buf: [4]u8 = undefined;
846 mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + self.getImageBase()));
847 try self.base.file.?.pwriteAll(&buf, file_offset);
881848 },
882849 .p64 => {
883 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
884 try self.writeAtom(atom_index, &buffer);
850 var buf: [8]u8 = undefined;
851 mem.writeIntLittle(u64, &buf, entry_value + self.getImageBase());
852 try self.base.file.?.pwriteAll(&buf, file_offset);
885853 },
886854 }
855
856 if (is_hot_update_compatible) {
857 if (self.base.child_pid) |handle| {
858 const gpa = self.base.allocator;
859 const slide = @ptrToInt(self.hot_state.loaded_base_address.?);
860 const actual_vmaddr = vmaddr + slide;
861 const pvaddr = @intToPtr(*anyopaque, actual_vmaddr);
862 log.debug("writing GOT entry to memory at address {x}", .{actual_vmaddr});
863 if (build_options.enable_logging) {
864 switch (self.ptr_width) {
865 .p32 => {
866 var buf: [4]u8 = undefined;
867 try debugMem(gpa, handle, pvaddr, &buf);
868 },
869 .p64 => {
870 var buf: [8]u8 = undefined;
871 try debugMem(gpa, handle, pvaddr, &buf);
872 },
873 }
874 }
875
876 switch (self.ptr_width) {
877 .p32 => {
878 var buf: [4]u8 = undefined;
879 mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + slide));
880 writeMem(handle, pvaddr, &buf) catch |err| {
881 log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
882 };
883 },
884 .p64 => {
885 var buf: [8]u8 = undefined;
886 mem.writeIntLittle(u64, &buf, entry_value + slide);
887 writeMem(handle, pvaddr, &buf) catch |err| {
888 log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
889 };
890 },
891 }
892 }
893 }
887894}
888895
889896fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
......@@ -904,6 +911,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
904911 reloc.dirty = true;
905912 }
906913 }
914
915 // TODO: dirty only really affected GOT cells
916 for (self.got_table.entries.items) |entry| {
917 const target_addr = self.getSymbol(entry).value;
918 if (target_addr >= addr) {
919 self.got_table_contents_dirty = true;
920 break;
921 }
922 }
907923}
908924
909925fn resolveRelocs(self: *Coff, atom_index: Atom.Index, relocs: []*const Relocation, code: []u8, image_base: u64) void {
......@@ -994,17 +1010,7 @@ fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
9941010 self.locals_free_list.append(gpa, sym_index) catch {};
9951011
9961012 // Try freeing GOT atom if this decl had one
997 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
998 if (self.got_entries_table.get(got_target)) |got_index| {
999 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1000 self.got_entries.items[got_index] = .{
1001 .target = .{ .sym_index = 0, .file = null },
1002 .sym_index = 0,
1003 };
1004 _ = self.got_entries_table.remove(got_target);
1005
1006 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1007 }
1013 self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
10081014
10091015 self.locals.items[sym_index].section_number = .UNDEFINED;
10101016 _ = self.atom_by_index_table.remove(sym_index);
......@@ -1243,14 +1249,7 @@ fn updateLazySymbolAtom(
12431249 atom.size = code_len;
12441250 symbol.value = vaddr;
12451251
1246 const got_target = SymbolWithLoc{ .sym_index = local_sym_index, .file = null };
1247 const got_index = try self.allocateGotEntry(got_target);
1248 const got_atom_index = try self.createGotAtom(got_target);
1249 const got_atom = self.getAtom(got_atom_index);
1250 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1251 try self.writePtrWidthAtom(got_atom_index);
1252
1253 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1252 try self.addGotEntry(.{ .sym_index = local_sym_index });
12541253 try self.writeAtom(atom_index, code);
12551254}
12561255
......@@ -1321,6 +1320,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
13211320 const decl_metadata = self.decls.get(decl_index).?;
13221321 const atom_index = decl_metadata.atom;
13231322 const atom = self.getAtom(atom_index);
1323 const sym_index = atom.getSymbolIndex().?;
13241324 const sect_index = decl_metadata.section;
13251325 const code_len = @intCast(u32, code.len);
13261326
......@@ -1340,10 +1340,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
13401340 if (vaddr != sym.value) {
13411341 sym.value = vaddr;
13421342 log.debug(" (updating GOT entry)", .{});
1343 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1344 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
1345 self.markRelocsDirtyByTarget(got_target);
1346 try self.writePtrWidthAtom(got_atom_index);
1343 const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?;
1344 try self.writeOffsetTableEntry(got_entry_index);
1345 self.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
13471346 }
13481347 } else if (code_len < atom.size) {
13491348 self.shrinkAtom(atom_index, code_len);
......@@ -1361,15 +1360,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
13611360 self.getAtomPtr(atom_index).size = code_len;
13621361 sym.value = vaddr;
13631362
1364 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1365 const got_index = try self.allocateGotEntry(got_target);
1366 const got_atom_index = try self.createGotAtom(got_target);
1367 const got_atom = self.getAtom(got_atom_index);
1368 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1369 try self.writePtrWidthAtom(got_atom_index);
1363 try self.addGotEntry(.{ .sym_index = sym_index });
13701364 }
13711365
1372 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
13731366 try self.writeAtom(atom_index, code);
13741367}
13751368
......@@ -1651,6 +1644,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
16511644 try self.writeAtom(atom_index, code.items);
16521645 }
16531646
1647 // Update GOT if it got moved in memory.
1648 if (self.got_table_contents_dirty) {
1649 for (self.got_table.entries.items, 0..) |entry, i| {
1650 if (!self.got_table.lookup.contains(entry)) continue;
1651 // TODO: write all in one go rather than incrementally.
1652 try self.writeOffsetTableEntry(i);
1653 }
1654 self.got_table_contents_dirty = false;
1655 }
1656
16541657 try self.writeBaseRelocations();
16551658
16561659 if (self.getEntryPoint()) |entry_sym_loc| {
......@@ -1739,48 +1742,82 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Dec
17391742fn writeBaseRelocations(self: *Coff) !void {
17401743 const gpa = self.base.allocator;
17411744
1742 var pages = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);
1745 var page_table = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);
17431746 defer {
1744 var it = pages.valueIterator();
1747 var it = page_table.valueIterator();
17451748 while (it.next()) |inner| {
17461749 inner.deinit();
17471750 }
1748 pages.deinit();
1751 page_table.deinit();
17491752 }
17501753
1751 var it = self.base_relocs.iterator();
1752 while (it.next()) |entry| {
1753 const atom_index = entry.key_ptr.*;
1754 const atom = self.getAtom(atom_index);
1755 const offsets = entry.value_ptr.*;
1756
1757 for (offsets.items) |offset| {
1754 {
1755 var it = self.base_relocs.iterator();
1756 while (it.next()) |entry| {
1757 const atom_index = entry.key_ptr.*;
1758 const atom = self.getAtom(atom_index);
17581759 const sym = atom.getSymbol(self);
1759 const rva = sym.value + offset;
1760 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
1761 const gop = try pages.getOrPut(page);
1762 if (!gop.found_existing) {
1763 gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
1760 const offsets = entry.value_ptr.*;
1761
1762 for (offsets.items) |offset| {
1763 const rva = sym.value + offset;
1764 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
1765 const gop = try page_table.getOrPut(page);
1766 if (!gop.found_existing) {
1767 gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
1768 }
1769 try gop.value_ptr.append(.{
1770 .offset = @intCast(u12, rva - page),
1771 .type = .DIR64,
1772 });
1773 }
1774 }
1775
1776 {
1777 const header = &self.sections.items(.header)[self.got_section_index.?];
1778 for (self.got_table.entries.items, 0..) |entry, index| {
1779 if (!self.got_table.lookup.contains(entry)) continue;
1780
1781 const sym = self.getSymbol(entry);
1782 if (sym.section_number == .UNDEFINED) continue;
1783
1784 const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size());
1785 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
1786 const gop = try page_table.getOrPut(page);
1787 if (!gop.found_existing) {
1788 gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
1789 }
1790 try gop.value_ptr.append(.{
1791 .offset = @intCast(u12, rva - page),
1792 .type = .DIR64,
1793 });
17641794 }
1765 try gop.value_ptr.append(.{
1766 .offset = @intCast(u12, rva - page),
1767 .type = .DIR64,
1768 });
17691795 }
17701796 }
17711797
1798 // Sort pages by address.
1799 var pages = try std.ArrayList(u32).initCapacity(gpa, page_table.count());
1800 defer pages.deinit();
1801 {
1802 var it = page_table.keyIterator();
1803 while (it.next()) |page| {
1804 pages.appendAssumeCapacity(page.*);
1805 }
1806 }
1807 std.sort.sort(u32, pages.items, {}, std.sort.asc(u32));
1808
17721809 var buffer = std.ArrayList(u8).init(gpa);
17731810 defer buffer.deinit();
17741811
1775 var pages_it = pages.iterator();
1776 while (pages_it.next()) |entry| {
1812 for (pages.items) |page| {
1813 const entries = page_table.getPtr(page).?;
17771814 // Pad to required 4byte alignment
17781815 if (!mem.isAlignedGeneric(
17791816 usize,
1780 entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation),
1817 entries.items.len * @sizeOf(coff.BaseRelocation),
17811818 @sizeOf(u32),
17821819 )) {
1783 try entry.value_ptr.append(.{
1820 try entries.append(.{
17841821 .offset = 0,
17851822 .type = .ABSOLUTE,
17861823 });
......@@ -1788,14 +1825,14 @@ fn writeBaseRelocations(self: *Coff) !void {
17881825
17891826 const block_size = @intCast(
17901827 u32,
1791 entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry),
1828 entries.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry),
17921829 );
17931830 try buffer.ensureUnusedCapacity(block_size);
17941831 buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{
1795 .page_rva = entry.key_ptr.*,
1832 .page_rva = page,
17961833 .block_size = block_size,
17971834 }));
1798 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entry.value_ptr.items));
1835 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entries.items));
17991836 }
18001837
18011838 const header = &self.sections.items(.header)[self.reloc_section_index.?];
......@@ -2315,14 +2352,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In
23152352 return self.atom_by_index_table.get(sym_loc.sym_index);
23162353}
23172354
2318/// Returns GOT atom that references `sym_loc` if one exists.
2319/// Returns null otherwise.
2320pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2321 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
2322 const got_entry = self.got_entries.items[got_index];
2323 return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2324}
2325
23262355fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
23272356 if (name.len <= 8) {
23282357 mem.copy(u8, &header.name, name);
......@@ -2410,25 +2439,7 @@ fn logSymtab(self: *Coff) void {
24102439 }
24112440
24122441 log.debug("GOT entries:", .{});
2413 for (self.got_entries.items, 0..) |entry, i| {
2414 const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
2415 const target_sym = self.getSymbol(entry.target);
2416 if (target_sym.section_number == .UNDEFINED) {
2417 log.debug(" {d}@{x} => import('{s}')", .{
2418 i,
2419 got_sym.value,
2420 self.getSymbolName(entry.target),
2421 });
2422 } else {
2423 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
2424 i,
2425 got_sym.value,
2426 entry.target.sym_index,
2427 entry.target.file,
2428 logSymAttributes(target_sym, &buf),
2429 });
2430 }
2431 }
2442 log.debug("{}", .{self.got_table});
24322443}
24332444
24342445fn logSections(self: *Coff) void {
......@@ -2484,6 +2495,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
24842495const Module = @import("../Module.zig");
24852496const Object = @import("Coff/Object.zig");
24862497const Relocation = @import("Coff/Relocation.zig");
2498const TableSection = @import("table_section.zig").TableSection;
24872499const StringTable = @import("strtab.zig").StringTable;
24882500const Type = @import("../type.zig").Type;
24892501const TypedValue = @import("../TypedValue.zig");
src/link/Coff/Relocation.zig+9-9
......@@ -48,17 +48,16 @@ dirty: bool = true,
4848/// Returns address of the target if any.
4949pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
5050 switch (self.type) {
51 .got, .got_page, .got_pageoff, .direct, .page, .pageoff => {
52 const maybe_target_atom_index = switch (self.type) {
53 .got, .got_page, .got_pageoff => coff_file.getGotAtomIndexForSymbol(self.target),
54 .direct, .page, .pageoff => coff_file.getAtomIndexForSymbol(self.target),
55 else => unreachable,
56 };
57 const target_atom_index = maybe_target_atom_index orelse return null;
51 .got, .got_page, .got_pageoff => {
52 const got_index = coff_file.got_table.lookup.get(self.target) orelse return null;
53 const header = coff_file.sections.items(.header)[coff_file.got_section_index.?];
54 return header.virtual_address + got_index * coff_file.ptr_width.size();
55 },
56 .direct, .page, .pageoff => {
57 const target_atom_index = coff_file.getAtomIndexForSymbol(self.target) orelse return null;
5858 const target_atom = coff_file.getAtom(target_atom_index);
5959 return target_atom.getSymbol(coff_file).value;
6060 },
61
6261 .import, .import_page, .import_pageoff => {
6362 const sym = coff_file.getSymbol(self.target);
6463 const index = coff_file.import_tables.getIndex(sym.value) orelse return null;
......@@ -74,7 +73,8 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
7473
7574/// Returns true if and only if the reloc is dirty AND the target address is available.
7675pub fn isResolvable(self: Relocation, coff_file: *Coff) bool {
77 _ = self.getTargetAddress(coff_file) orelse return false;
76 const addr = self.getTargetAddress(coff_file) orelse return false;
77 if (addr == 0) return false;
7878 return self.dirty;
7979}
8080
src/link/Elf.zig+25-46
......@@ -30,6 +30,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
3030const Module = @import("../Module.zig");
3131const Package = @import("../Package.zig");
3232const StringTable = @import("strtab.zig").StringTable;
33const TableSection = @import("table_section.zig").TableSection;
3334const Type = @import("../type.zig").Type;
3435const TypedValue = @import("../TypedValue.zig");
3536const Value = @import("../value.zig").Value;
......@@ -148,17 +149,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
148149
149150local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
150151global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
151offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
152152
153/// Same order as in the file. The value is the absolute vaddr value.
154/// If the vaddr of the executable program header changes, the entire
155/// offset table needs to be rewritten.
156offset_table: std.ArrayListUnmanaged(u64) = .{},
153got_table: TableSection(u32) = .{},
157154
158155phdr_table_dirty: bool = false,
159156shdr_table_dirty: bool = false,
160157shstrtab_dirty: bool = false,
161offset_table_count_dirty: bool = false,
158got_table_count_dirty: bool = false,
162159
163160debug_strtab_dirty: bool = false,
164161debug_abbrev_section_dirty: bool = false,
......@@ -329,8 +326,7 @@ pub fn deinit(self: *Elf) void {
329326 self.global_symbols.deinit(gpa);
330327 self.global_symbol_free_list.deinit(gpa);
331328 self.local_symbol_free_list.deinit(gpa);
332 self.offset_table_free_list.deinit(gpa);
333 self.offset_table.deinit(gpa);
329 self.got_table.deinit(gpa);
334330
335331 {
336332 var it = self.decls.iterator();
......@@ -1289,6 +1285,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12891285 assert(!self.shdr_table_dirty);
12901286 assert(!self.shstrtab_dirty);
12911287 assert(!self.debug_strtab_dirty);
1288 assert(!self.got_table_count_dirty);
12921289}
12931290
12941291fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
......@@ -2168,7 +2165,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
21682165 _ = self.atom_by_index_table.remove(local_sym_index);
21692166 self.getAtomPtr(atom_index).local_sym_index = 0;
21702167
2171 self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
2168 self.got_table.freeEntry(gpa, local_sym_index);
21722169}
21732170
21742171fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
......@@ -2191,11 +2188,9 @@ pub fn createAtom(self: *Elf) !Atom.Index {
21912188 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
21922189 const atom = try self.atoms.addOne(gpa);
21932190 const local_sym_index = try self.allocateLocalSymbol();
2194 const offset_table_index = try self.allocateGotOffset();
21952191 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
21962192 atom.* = .{
21972193 .local_sym_index = local_sym_index,
2198 .offset_table_index = offset_table_index,
21992194 .prev_index = null,
22002195 .next_index = null,
22012196 };
......@@ -2352,26 +2347,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 {
23522347 return index;
23532348}
23542349
2355pub fn allocateGotOffset(self: *Elf) !u32 {
2356 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
2357
2358 const index = blk: {
2359 if (self.offset_table_free_list.popOrNull()) |index| {
2360 log.debug(" (reusing GOT offset at index {d})", .{index});
2361 break :blk index;
2362 } else {
2363 log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len});
2364 const index = @intCast(u32, self.offset_table.items.len);
2365 _ = self.offset_table.addOneAssumeCapacity();
2366 self.offset_table_count_dirty = true;
2367 break :blk index;
2368 }
2369 };
2370
2371 self.offset_table.items[index] = 0;
2372 return index;
2373}
2374
23752350fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
23762351 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
23772352 for (unnamed_consts.items) |atom| {
......@@ -2465,6 +2440,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
24652440 const decl_metadata = self.decls.get(decl_index).?;
24662441 const atom_index = decl_metadata.atom;
24672442 const atom = self.getAtom(atom_index);
2443 const local_sym_index = atom.getSymbolIndex().?;
24682444
24692445 const shdr_index = decl_metadata.shdr;
24702446 if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) {
......@@ -2485,8 +2461,9 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
24852461 local_sym.st_value = vaddr;
24862462
24872463 log.debug(" (writing new offset table entry)", .{});
2488 self.offset_table.items[atom.offset_table_index] = vaddr;
2489 try self.writeOffsetTableEntry(atom.offset_table_index);
2464 const got_entry_index = self.got_table.lookup.get(local_sym_index).?;
2465 self.got_table.entries.items[got_entry_index] = local_sym_index;
2466 try self.writeOffsetTableEntry(got_entry_index);
24902467 }
24912468 } else if (code.len < local_sym.st_size) {
24922469 self.shrinkAtom(atom_index, code.len);
......@@ -2494,7 +2471,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
24942471 local_sym.st_size = code.len;
24952472
24962473 // TODO this write could be avoided if no fields of the symbol were changed.
2497 try self.writeSymbol(atom.getSymbolIndex().?);
2474 try self.writeSymbol(local_sym_index);
24982475 } else {
24992476 const local_sym = atom.getSymbolPtr(self);
25002477 local_sym.* = .{
......@@ -2509,12 +2486,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
25092486 errdefer self.freeAtom(atom_index);
25102487 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
25112488
2512 self.offset_table.items[atom.offset_table_index] = vaddr;
25132489 local_sym.st_value = vaddr;
25142490 local_sym.st_size = code.len;
25152491
2516 try self.writeSymbol(atom.getSymbolIndex().?);
2517 try self.writeOffsetTableEntry(atom.offset_table_index);
2492 try self.writeSymbol(local_sym_index);
2493 const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);
2494 try self.writeOffsetTableEntry(got_entry_index);
25182495 }
25192496
25202497 const local_sym = atom.getSymbolPtr(self);
......@@ -2755,12 +2732,12 @@ fn updateLazySymbolAtom(
27552732 errdefer self.freeAtom(atom_index);
27562733 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });
27572734
2758 self.offset_table.items[atom.offset_table_index] = vaddr;
27592735 local_sym.st_value = vaddr;
27602736 local_sym.st_size = code.len;
27612737
27622738 try self.writeSymbol(local_sym_index);
2763 try self.writeOffsetTableEntry(atom.offset_table_index);
2739 const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);
2740 try self.writeOffsetTableEntry(got_entry_index);
27642741
27652742 const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr;
27662743 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
......@@ -2989,32 +2966,34 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
29892966 }
29902967}
29912968
2992fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2969fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void {
29932970 const entry_size: u16 = self.archPtrWidthBytes();
2994 if (self.offset_table_count_dirty) {
2995 const needed_size = self.offset_table.items.len * entry_size;
2971 if (self.got_table_count_dirty) {
2972 const needed_size = self.got_table.entries.items.len * entry_size;
29962973 try self.growAllocSection(self.got_section_index.?, needed_size);
2997 self.offset_table_count_dirty = false;
2974 self.got_table_count_dirty = false;
29982975 }
29992976 const endian = self.base.options.target.cpu.arch.endian();
30002977 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
30012978 const off = shdr.sh_offset + @as(u64, entry_size) * index;
30022979 const phdr = &self.program_headers.items[self.phdr_got_index.?];
30032980 const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;
2981 const got_entry = self.got_table.entries.items[index];
2982 const got_value = self.getSymbol(got_entry).st_value;
30042983 switch (entry_size) {
30052984 2 => {
30062985 var buf: [2]u8 = undefined;
3007 mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
2986 mem.writeInt(u16, &buf, @intCast(u16, got_value), endian);
30082987 try self.base.file.?.pwriteAll(&buf, off);
30092988 },
30102989 4 => {
30112990 var buf: [4]u8 = undefined;
3012 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
2991 mem.writeInt(u32, &buf, @intCast(u32, got_value), endian);
30132992 try self.base.file.?.pwriteAll(&buf, off);
30142993 },
30152994 8 => {
30162995 var buf: [8]u8 = undefined;
3017 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
2996 mem.writeInt(u64, &buf, got_value, endian);
30182997 try self.base.file.?.pwriteAll(&buf, off);
30192998
30202999 if (self.base.child_pid) |pid| {
src/link/Elf/Atom.zig+13-5
......@@ -14,9 +14,6 @@ const Elf = @import("../Elf.zig");
1414/// offset table entry.
1515local_sym_index: u32,
1616
17/// This field is undefined for symbols with size = 0.
18offset_table_index: u32,
19
2017/// Points to the previous and next neighbors, based on the `text_offset`.
2118/// This can be used to find, for example, the capacity of this `TextBlock`.
2219prev_index: ?Index,
......@@ -48,13 +45,24 @@ pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
4845 return elf_file.getSymbolName(self.getSymbolIndex().?);
4946}
5047
48/// If entry already exists, returns index to it.
49/// Otherwise, creates a new entry in the Global Offset Table for this Atom.
50pub fn getOrCreateOffsetTableEntry(self: Atom, elf_file: *Elf) !u32 {
51 const sym_index = self.getSymbolIndex().?;
52 if (elf_file.got_table.lookup.get(sym_index)) |index| return index;
53 const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index);
54 elf_file.got_table_count_dirty = true;
55 return index;
56}
57
5158pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
52 assert(self.getSymbolIndex() != null);
59 const sym_index = self.getSymbolIndex().?;
60 const got_entry_index = elf_file.got_table.lookup.get(sym_index).?;
5361 const target = elf_file.base.options.target;
5462 const ptr_bits = target.cpu.arch.ptrBitWidth();
5563 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
5664 const got = elf_file.program_headers.items[elf_file.phdr_got_index.?];
57 return got.p_vaddr + self.offset_table_index * ptr_bytes;
65 return got.p_vaddr + got_entry_index * ptr_bytes;
5866}
5967
6068/// Returns how much room there is to grow in virtual address space.
src/link/MachO.zig+419-614
......@@ -19,6 +19,7 @@ const fat = @import("MachO/fat.zig");
1919const link = @import("../link.zig");
2020const llvm_backend = @import("../codegen/llvm.zig");
2121const load_commands = @import("MachO/load_commands.zig");
22const stubs = @import("MachO/stubs.zig");
2223const target_util = @import("../target.zig");
2324const trace = @import("../tracy.zig").trace;
2425const zld = @import("MachO/zld.zig");
......@@ -41,6 +42,7 @@ const Md5 = std.crypto.hash.Md5;
4142const Module = @import("../Module.zig");
4243const Relocation = @import("MachO/Relocation.zig");
4344const StringTable = @import("strtab.zig").StringTable;
45const TableSection = @import("table_section.zig").TableSection;
4446const Trie = @import("MachO/Trie.zig");
4547const Type = @import("../type.zig").Type;
4648const TypedValue = @import("../TypedValue.zig");
......@@ -150,17 +152,20 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{},
150152
151153dyld_stub_binder_index: ?u32 = null,
152154dyld_private_atom_index: ?Atom.Index = null,
153stub_helper_preamble_atom_index: ?Atom.Index = null,
154155
155156strtab: StringTable(.strtab) = .{},
156157
157got_table: SectionTable = .{},
158stubs_table: SectionTable = .{},
159tlv_table: SectionTable = .{},
158got_table: TableSection(SymbolWithLoc) = .{},
159stub_table: TableSection(SymbolWithLoc) = .{},
160160
161161error_flags: File.ErrorFlags = File.ErrorFlags{},
162162
163163segment_table_dirty: bool = false,
164got_table_count_dirty: bool = false,
165got_table_contents_dirty: bool = false,
166stub_table_count_dirty: bool = false,
167stub_table_contents_dirty: bool = false,
168stub_helper_preamble_allocated: bool = false,
164169
165170/// A helper var to indicate if we are at the start of the incremental updates, or
166171/// already somewhere further along the update-and-run chain.
......@@ -210,17 +215,16 @@ rebases: RebaseTable = .{},
210215/// this will be a table indexed by index into the list of Atoms.
211216bindings: BindingTable = .{},
212217
213/// A table of lazy bindings indexed by the owning them `Atom`.
214/// Note that once we refactor `Atom`'s lifetime and ownership rules,
215/// this will be a table indexed by index into the list of Atoms.
216lazy_bindings: BindingTable = .{},
217
218218/// Table of tracked LazySymbols.
219219lazy_syms: LazySymbolTable = .{},
220220
221221/// Table of tracked Decls.
222222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
223223
224/// Table of threadlocal variables descriptors.
225/// They are emitted in the `__thread_vars` section.
226tlv_table: TlvSymbolTable = .{},
227
224228/// Hot-code swapping state.
225229hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
226230
......@@ -237,6 +241,8 @@ const LazySymbolMetadata = struct {
237241 alignment: u32,
238242};
239243
244const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index);
245
240246const DeclMetadata = struct {
241247 atom: Atom.Index,
242248 section: u8,
......@@ -265,122 +271,6 @@ const DeclMetadata = struct {
265271 }
266272};
267273
268const SectionTable = struct {
269 entries: std.ArrayListUnmanaged(Entry) = .{},
270 free_list: std.ArrayListUnmanaged(u32) = .{},
271 lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
272
273 pub fn deinit(st: *ST, allocator: Allocator) void {
274 st.entries.deinit(allocator);
275 st.free_list.deinit(allocator);
276 st.lookup.deinit(allocator);
277 }
278
279 pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) !u32 {
280 try st.entries.ensureUnusedCapacity(allocator, 1);
281 const index = blk: {
282 if (st.free_list.popOrNull()) |index| {
283 log.debug(" (reusing entry index {d})", .{index});
284 break :blk index;
285 } else {
286 log.debug(" (allocating entry at index {d})", .{st.entries.items.len});
287 const index = @intCast(u32, st.entries.items.len);
288 _ = st.entries.addOneAssumeCapacity();
289 break :blk index;
290 }
291 };
292 st.entries.items[index] = .{ .target = target, .sym_index = 0 };
293 try st.lookup.putNoClobber(allocator, target, index);
294 return index;
295 }
296
297 pub fn freeEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) void {
298 const index = st.lookup.get(target) orelse return;
299 st.free_list.append(allocator, index) catch {};
300 st.entries.items[index] = .{
301 .target = .{ .sym_index = 0 },
302 .sym_index = 0,
303 };
304 _ = st.lookup.remove(target);
305 }
306
307 pub fn getAtomIndex(st: *const ST, macho_file: *MachO, target: SymbolWithLoc) ?Atom.Index {
308 const index = st.lookup.get(target) orelse return null;
309 return st.entries.items[index].getAtomIndex(macho_file);
310 }
311
312 const FormatContext = struct {
313 macho_file: *MachO,
314 st: *const ST,
315 };
316
317 fn fmt(
318 ctx: FormatContext,
319 comptime unused_format_string: []const u8,
320 options: std.fmt.FormatOptions,
321 writer: anytype,
322 ) @TypeOf(writer).Error!void {
323 _ = options;
324 comptime assert(unused_format_string.len == 0);
325 try writer.writeAll("SectionTable:\n");
326 for (ctx.st.entries.items, 0..) |entry, i| {
327 const atom_sym = entry.getSymbol(ctx.macho_file);
328 const target_sym = ctx.macho_file.getSymbol(entry.target);
329 try writer.print(" {d}@{x} => ", .{ i, atom_sym.n_value });
330 if (target_sym.undf()) {
331 try writer.print("import('{s}')", .{
332 ctx.macho_file.getSymbolName(entry.target),
333 });
334 } else {
335 try writer.print("local(%{d}) in object({?d})", .{
336 entry.target.sym_index,
337 entry.target.file,
338 });
339 }
340 try writer.writeByte('\n');
341 }
342 }
343
344 fn format(st: *const ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
345 _ = st;
346 _ = unused_format_string;
347 _ = options;
348 _ = writer;
349 @compileError("do not format SectionTable directly; use st.fmtDebug()");
350 }
351
352 pub fn fmtDebug(st: *const ST, macho_file: *MachO) std.fmt.Formatter(fmt) {
353 return .{ .data = .{
354 .macho_file = macho_file,
355 .st = st,
356 } };
357 }
358
359 const ST = @This();
360
361 const Entry = struct {
362 target: SymbolWithLoc,
363 // Index into the synthetic symbol table (i.e., file == null).
364 sym_index: u32,
365
366 pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 {
367 return macho_file.getSymbol(.{ .sym_index = entry.sym_index });
368 }
369
370 pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 {
371 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index });
372 }
373
374 pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index {
375 return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index });
376 }
377
378 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
379 return macho_file.getSymbolName(.{ .sym_index = entry.sym_index });
380 }
381 };
382};
383
384274const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
385275const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
386276const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
......@@ -722,15 +612,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
722612 return error.UndefinedSymbolReference;
723613 }
724614
725 try self.createDyldPrivateAtom();
726 try self.createStubHelperPreambleAtom();
727
728615 for (actions.items) |action| switch (action.kind) {
729616 .none => {},
730617 .add_got => try self.addGotEntry(action.target),
731618 .add_stub => try self.addStubEntry(action.target),
732619 };
733620
621 try self.createDyldPrivateAtom();
622 try self.writeStubHelperPreamble();
623
734624 try self.allocateSpecialSymbols();
735625
736626 for (self.relocs.keys()) |atom_index| {
......@@ -756,6 +646,27 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
756646 try self.writeAtom(atom_index, code.items);
757647 }
758648
649 // Update GOT if it got moved in memory.
650 if (self.got_table_contents_dirty) {
651 for (self.got_table.entries.items, 0..) |entry, i| {
652 if (!self.got_table.lookup.contains(entry)) continue;
653 // TODO: write all in one go rather than incrementally.
654 try self.writeOffsetTableEntry(i);
655 }
656 self.got_table_contents_dirty = false;
657 }
658
659 // Update stubs if we moved any section in memory.
660 // TODO: we probably don't need to update all sections if only one got moved.
661 if (self.stub_table_contents_dirty) {
662 for (self.stub_table.entries.items, 0..) |entry, i| {
663 if (!self.stub_table.lookup.contains(entry)) continue;
664 // TODO: write all in one go rather than incrementally.
665 try self.writeStubTableEntry(i);
666 }
667 self.stub_table_contents_dirty = false;
668 }
669
759670 if (build_options.enable_logging) {
760671 self.logSymtab();
761672 self.logSections();
......@@ -1239,19 +1150,16 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
12391150 }
12401151 }
12411152
1153 Atom.resolveRelocations(self, atom_index, relocs.items, code);
1154
12421155 if (is_hot_update_compatible) {
1243 if (self.base.child_pid) |pid| blk: {
1244 const task = self.hot_state.mach_task orelse {
1245 log.warn("cannot hot swap: no Mach task acquired for child process with pid {d}", .{pid});
1246 break :blk;
1247 };
1248 self.updateAtomInMemory(task, section.segment_index, sym.n_value, code) catch |err| {
1156 if (self.hot_state.mach_task) |task| {
1157 self.writeToMemory(task, section.segment_index, sym.n_value, code) catch |err| {
12491158 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
12501159 };
12511160 }
12521161 }
12531162
1254 Atom.resolveRelocations(self, atom_index, relocs.items, code);
12551163 try self.base.file.?.pwriteAll(code, file_offset);
12561164
12571165 // Now we can mark the relocs as resolved.
......@@ -1260,7 +1168,7 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
12601168 }
12611169}
12621170
1263fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {
1171fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {
12641172 const segment = self.segments.items[segment_index];
12651173 const cpu_arch = self.base.options.target.cpu.arch;
12661174 const nwritten = if (!segment.isWriteable())
......@@ -1270,9 +1178,145 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index:
12701178 if (nwritten != code.len) return error.InputOutput;
12711179}
12721180
1273fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
1274 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1275 try self.writeAtom(atom_index, &buffer);
1181fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1182 const sect_id = self.got_section_index.?;
1183
1184 if (self.got_table_count_dirty) {
1185 const needed_size = self.got_table.entries.items.len * @sizeOf(u64);
1186 try self.growSection(sect_id, needed_size);
1187 self.got_table_count_dirty = false;
1188 }
1189
1190 const header = &self.sections.items(.header)[sect_id];
1191 const segment_index = self.sections.items(.segment_index)[sect_id];
1192 const entry = self.got_table.entries.items[index];
1193 const entry_value = self.getSymbol(entry).n_value;
1194 const entry_offset = index * @sizeOf(u64);
1195 const file_offset = header.offset + entry_offset;
1196 const vmaddr = header.addr + entry_offset;
1197
1198 log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value });
1199
1200 var buf: [@sizeOf(u64)]u8 = undefined;
1201 mem.writeIntLittle(u64, &buf, entry_value);
1202 try self.base.file.?.pwriteAll(&buf, file_offset);
1203
1204 if (is_hot_update_compatible) {
1205 if (self.hot_state.mach_task) |task| {
1206 self.writeToMemory(task, segment_index, vmaddr, &buf) catch |err| {
1207 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
1208 };
1209 }
1210 }
1211}
1212
1213fn writeStubHelperPreamble(self: *MachO) !void {
1214 if (self.stub_helper_preamble_allocated) return;
1215
1216 const gpa = self.base.allocator;
1217 const cpu_arch = self.base.options.target.cpu.arch;
1218 const size = stubs.calcStubHelperPreambleSize(cpu_arch);
1219
1220 var buf = try std.ArrayList(u8).initCapacity(gpa, size);
1221 defer buf.deinit();
1222
1223 const dyld_private_addr = self.getAtom(self.dyld_private_atom_index.?).getSymbol(self).n_value;
1224 const dyld_stub_binder_got_addr = blk: {
1225 const index = self.got_table.lookup.get(self.getGlobalByIndex(self.dyld_stub_binder_index.?)).?;
1226 const header = self.sections.items(.header)[self.got_section_index.?];
1227 break :blk header.addr + @sizeOf(u64) * index;
1228 };
1229 const header = self.sections.items(.header)[self.stub_helper_section_index.?];
1230
1231 try stubs.writeStubHelperPreambleCode(.{
1232 .cpu_arch = cpu_arch,
1233 .source_addr = header.addr,
1234 .dyld_private_addr = dyld_private_addr,
1235 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
1236 }, buf.writer());
1237 try self.base.file.?.pwriteAll(buf.items, header.offset);
1238
1239 self.stub_helper_preamble_allocated = true;
1240}
1241
1242fn writeStubTableEntry(self: *MachO, index: usize) !void {
1243 const stubs_sect_id = self.stubs_section_index.?;
1244 const stub_helper_sect_id = self.stub_helper_section_index.?;
1245 const laptr_sect_id = self.la_symbol_ptr_section_index.?;
1246
1247 const cpu_arch = self.base.options.target.cpu.arch;
1248 const stub_entry_size = stubs.calcStubEntrySize(cpu_arch);
1249 const stub_helper_entry_size = stubs.calcStubHelperEntrySize(cpu_arch);
1250 const stub_helper_preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);
1251
1252 if (self.stub_table_count_dirty) {
1253 // We grow all 3 sections one by one.
1254 {
1255 const needed_size = stub_entry_size * self.stub_table.entries.items.len;
1256 try self.growSection(stubs_sect_id, needed_size);
1257 }
1258 {
1259 const needed_size = stub_helper_preamble_size + stub_helper_entry_size * self.stub_table.entries.items.len;
1260 try self.growSection(stub_helper_sect_id, needed_size);
1261 }
1262 {
1263 const needed_size = @sizeOf(u64) * self.stub_table.entries.items.len;
1264 try self.growSection(laptr_sect_id, needed_size);
1265 }
1266 self.stub_table_count_dirty = false;
1267 }
1268
1269 const gpa = self.base.allocator;
1270
1271 const stubs_header = self.sections.items(.header)[stubs_sect_id];
1272 const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id];
1273 const laptr_header = self.sections.items(.header)[laptr_sect_id];
1274
1275 const entry = self.stub_table.entries.items[index];
1276 const stub_addr: u64 = stubs_header.addr + stub_entry_size * index;
1277 const stub_helper_addr: u64 = stub_helper_header.addr + stub_helper_preamble_size + stub_helper_entry_size * index;
1278 const laptr_addr: u64 = laptr_header.addr + @sizeOf(u64) * index;
1279
1280 log.debug("writing stub entry {d}: @{x} => '{s}'", .{ index, stub_addr, self.getSymbolName(entry) });
1281
1282 {
1283 var buf = try std.ArrayList(u8).initCapacity(gpa, stub_entry_size);
1284 defer buf.deinit();
1285 try stubs.writeStubCode(.{
1286 .cpu_arch = cpu_arch,
1287 .source_addr = stub_addr,
1288 .target_addr = laptr_addr,
1289 }, buf.writer());
1290 const off = stubs_header.offset + stub_entry_size * index;
1291 try self.base.file.?.pwriteAll(buf.items, off);
1292 }
1293
1294 {
1295 var buf = try std.ArrayList(u8).initCapacity(gpa, stub_helper_entry_size);
1296 defer buf.deinit();
1297 try stubs.writeStubHelperCode(.{
1298 .cpu_arch = cpu_arch,
1299 .source_addr = stub_helper_addr,
1300 .target_addr = stub_helper_header.addr,
1301 }, buf.writer());
1302 const off = stub_helper_header.offset + stub_helper_preamble_size + stub_helper_entry_size * index;
1303 try self.base.file.?.pwriteAll(buf.items, off);
1304 }
1305
1306 {
1307 var buf: [@sizeOf(u64)]u8 = undefined;
1308 mem.writeIntLittle(u64, &buf, stub_helper_addr);
1309 const off = laptr_header.offset + @sizeOf(u64) * index;
1310 try self.base.file.?.pwriteAll(&buf, off);
1311 }
1312
1313 // TODO: generating new stub entry will require pulling the address of the symbol from the
1314 // target dylib when updating directly in memory.
1315 if (is_hot_update_compatible) {
1316 if (self.hot_state.mach_task) |_| {
1317 @panic("TODO: update a stub entry in memory");
1318 }
1319 }
12761320}
12771321
12781322fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
......@@ -1290,13 +1334,28 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
12901334 log.debug("marking relocs dirty by address: {x}", .{addr});
12911335 for (self.relocs.values()) |*relocs| {
12921336 for (relocs.items) |*reloc| {
1293 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;
1294 const target_atom = self.getAtom(target_atom_index);
1295 const target_sym = target_atom.getSymbol(self);
1296 if (target_sym.n_value < addr) continue;
1337 const target_addr = reloc.getTargetBaseAddress(self) orelse continue;
1338 if (target_addr < addr) continue;
12971339 reloc.dirty = true;
12981340 }
12991341 }
1342
1343 // TODO: dirty only really affected GOT cells
1344 for (self.got_table.entries.items) |entry| {
1345 const target_addr = self.getSymbol(entry).n_value;
1346 if (target_addr >= addr) {
1347 self.got_table_contents_dirty = true;
1348 break;
1349 }
1350 }
1351
1352 {
1353 const stubs_addr = self.getSegment(self.stubs_section_index.?).vmaddr;
1354 const stub_helper_addr = self.getSegment(self.stub_helper_section_index.?).vmaddr;
1355 const laptr_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr;
1356 if (stubs_addr >= addr or stub_helper_addr >= addr or laptr_addr >= addr)
1357 self.stub_table_contents_dirty = true;
1358 }
13001359}
13011360
13021361pub fn allocateSpecialSymbols(self: *MachO) !void {
......@@ -1335,40 +1394,6 @@ pub fn createAtom(self: *MachO) !Atom.Index {
13351394 return atom_index;
13361395}
13371396
1338pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1339 const atom_index = try self.createAtom();
1340 self.getAtomPtr(atom_index).size = @sizeOf(u64);
1341
1342 const sym = self.getAtom(atom_index).getSymbolPtr(self);
1343 sym.n_type = macho.N_SECT;
1344 sym.n_sect = self.got_section_index.? + 1;
1345 sym.n_value = try self.allocateAtom(atom_index, @sizeOf(u64), @alignOf(u64));
1346
1347 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1348
1349 try Atom.addRelocation(self, atom_index, .{
1350 .type = .unsigned,
1351 .target = target,
1352 .offset = 0,
1353 .addend = 0,
1354 .pcrel = false,
1355 .length = 3,
1356 });
1357
1358 const target_sym = self.getSymbol(target);
1359 if (target_sym.undf()) {
1360 try Atom.addBinding(self, atom_index, .{
1361 .target = self.getGlobal(self.getSymbolName(target)).?,
1362 .offset = 0,
1363 });
1364 } else {
1365 try Atom.addRebase(self, atom_index, 0);
1366 }
1367 try self.writePtrWidthAtom(atom_index);
1368
1369 return atom_index;
1370}
1371
13721397fn createDyldPrivateAtom(self: *MachO) !void {
13731398 if (self.dyld_private_atom_index != null) return;
13741399
......@@ -1383,339 +1408,17 @@ fn createDyldPrivateAtom(self: *MachO) !void {
13831408
13841409 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
13851410 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1386 try self.writePtrWidthAtom(atom_index);
1387}
1388
1389fn createStubHelperPreambleAtom(self: *MachO) !void {
1390 if (self.stub_helper_preamble_atom_index != null) return;
1391
1392 const gpa = self.base.allocator;
1393 const arch = self.base.options.target.cpu.arch;
1394 const size: u5 = switch (arch) {
1395 .x86_64 => 15,
1396 .aarch64 => 6 * @sizeOf(u32),
1397 else => unreachable,
1398 };
1399 const atom_index = try self.createAtom();
1400 const atom = self.getAtomPtr(atom_index);
1401 atom.size = size;
1402
1403 const required_alignment: u32 = switch (arch) {
1404 .x86_64 => 1,
1405 .aarch64 => @alignOf(u32),
1406 else => unreachable,
1407 };
1408
1409 const sym = atom.getSymbolPtr(self);
1410 sym.n_type = macho.N_SECT;
1411 sym.n_sect = self.stub_helper_section_index.? + 1;
1412
1413 const dyld_private = self.getAtom(self.dyld_private_atom_index.?).getSymbolWithLoc();
1414 const dyld_stub_binder = self.globals.items[self.dyld_stub_binder_index.?];
1415
1416 const code = try gpa.alloc(u8, size);
1417 defer gpa.free(code);
1418 mem.set(u8, code, 0);
1419
1420 switch (arch) {
1421 .x86_64 => {
1422 // lea %r11, [rip + disp]
1423 code[0] = 0x4c;
1424 code[1] = 0x8d;
1425 code[2] = 0x1d;
1426 // push %r11
1427 code[7] = 0x41;
1428 code[8] = 0x53;
1429 // jmp [rip + disp]
1430 code[9] = 0xff;
1431 code[10] = 0x25;
1432
1433 try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{
1434 .type = .signed,
1435 .target = dyld_private,
1436 .offset = 3,
1437 .addend = 0,
1438 .pcrel = true,
1439 .length = 2,
1440 }, .{
1441 .type = .got,
1442 .target = dyld_stub_binder,
1443 .offset = 11,
1444 .addend = 0,
1445 .pcrel = true,
1446 .length = 2,
1447 } });
1448 },
1449
1450 .aarch64 => {
1451 // adrp x17, 0
1452 mem.writeIntLittle(u32, code[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
1453 // add x17, x17, 0
1454 mem.writeIntLittle(u32, code[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
1455 // stp x16, x17, [sp, #-16]!
1456 mem.writeIntLittle(u32, code[8..][0..4], aarch64.Instruction.stp(
1457 .x16,
1458 .x17,
1459 aarch64.Register.sp,
1460 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
1461 ).toU32());
1462 // adrp x16, 0
1463 mem.writeIntLittle(u32, code[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1464 // ldr x16, [x16, 0]
1465 mem.writeIntLittle(u32, code[16..][0..4], aarch64.Instruction.ldr(
1466 .x16,
1467 .x16,
1468 aarch64.Instruction.LoadStoreOffset.imm(0),
1469 ).toU32());
1470 // br x16
1471 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
1472
1473 try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{
1474 .type = .page,
1475 .target = dyld_private,
1476 .offset = 0,
1477 .addend = 0,
1478 .pcrel = true,
1479 .length = 2,
1480 }, .{
1481 .type = .pageoff,
1482 .target = dyld_private,
1483 .offset = 4,
1484 .addend = 0,
1485 .pcrel = false,
1486 .length = 2,
1487 }, .{
1488 .type = .got_page,
1489 .target = dyld_stub_binder,
1490 .offset = 12,
1491 .addend = 0,
1492 .pcrel = true,
1493 .length = 2,
1494 }, .{
1495 .type = .got_pageoff,
1496 .target = dyld_stub_binder,
1497 .offset = 16,
1498 .addend = 0,
1499 .pcrel = false,
1500 .length = 2,
1501 } });
1502 },
1503
1504 else => unreachable,
1505 }
1506 self.stub_helper_preamble_atom_index = atom_index;
1507
1508 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1509 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1510 try self.writeAtom(atom_index, code);
1511}
1512
1513fn createStubHelperAtom(self: *MachO) !Atom.Index {
1514 const gpa = self.base.allocator;
1515 const arch = self.base.options.target.cpu.arch;
1516 const size: u4 = switch (arch) {
1517 .x86_64 => 10,
1518 .aarch64 => 3 * @sizeOf(u32),
1519 else => unreachable,
1520 };
1521 const atom_index = try self.createAtom();
1522 const atom = self.getAtomPtr(atom_index);
1523 atom.size = size;
1524
1525 const required_alignment: u32 = switch (arch) {
1526 .x86_64 => 1,
1527 .aarch64 => @alignOf(u32),
1528 else => unreachable,
1529 };
1530
1531 const sym = atom.getSymbolPtr(self);
1532 sym.n_type = macho.N_SECT;
1533 sym.n_sect = self.stub_helper_section_index.? + 1;
1534
1535 const code = try gpa.alloc(u8, size);
1536 defer gpa.free(code);
1537 mem.set(u8, code, 0);
1538
1539 const stub_helper_preamble_atom_sym_index = if (self.stub_helper_preamble_atom_index) |stub_index|
1540 self.getAtom(stub_index).getSymbolIndex().?
1541 else
1542 unreachable;
1543
1544 switch (arch) {
1545 .x86_64 => {
1546 // pushq
1547 code[0] = 0x68;
1548 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1549 // jmpq
1550 code[5] = 0xe9;
1551
1552 try Atom.addRelocation(self, atom_index, .{
1553 .type = .branch,
1554 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index },
1555 .offset = 6,
1556 .addend = 0,
1557 .pcrel = true,
1558 .length = 2,
1559 });
1560 },
1561 .aarch64 => {
1562 const literal = blk: {
1563 const div_res = try math.divExact(u64, size - @sizeOf(u32), 4);
1564 break :blk math.cast(u18, div_res) orelse return error.Overflow;
1565 };
1566 // ldr w16, literal
1567 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldrLiteral(
1568 .w16,
1569 literal,
1570 ).toU32());
1571 // b disp
1572 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
1573 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1574
1575 try Atom.addRelocation(self, atom_index, .{
1576 .type = .branch,
1577 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index },
1578 .offset = 4,
1579 .addend = 0,
1580 .pcrel = true,
1581 .length = 2,
1582 });
1583 },
1584 else => unreachable,
1585 }
1586
1587 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1588 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1589 try self.writeAtom(atom_index, code);
1590
1591 return atom_index;
1592}
1593
1594fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index {
1595 const atom_index = try self.createAtom();
1596 const atom = self.getAtomPtr(atom_index);
1597 atom.size = @sizeOf(u64);
1598
1599 const sym = atom.getSymbolPtr(self);
1600 sym.n_type = macho.N_SECT;
1601 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
1602
1603 try Atom.addRelocation(self, atom_index, .{
1604 .type = .unsigned,
1605 .target = .{ .sym_index = stub_sym_index },
1606 .offset = 0,
1607 .addend = 0,
1608 .pcrel = false,
1609 .length = 3,
1610 });
1611 try Atom.addRebase(self, atom_index, 0);
1612 try Atom.addLazyBinding(self, atom_index, .{
1613 .target = self.getGlobal(self.getSymbolName(target)).?,
1614 .offset = 0,
1615 });
1616
1617 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1618 log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) });
1619 try self.writePtrWidthAtom(atom_index);
1620
1621 return atom_index;
1622}
1623
1624fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index {
1625 const gpa = self.base.allocator;
1626 const arch = self.base.options.target.cpu.arch;
1627 const size: u4 = switch (arch) {
1628 .x86_64 => 6,
1629 .aarch64 => 3 * @sizeOf(u32),
1630 else => unreachable, // unhandled architecture type
1631 };
1632 const atom_index = try self.createAtom();
1633 const atom = self.getAtomPtr(atom_index);
1634 atom.size = size;
1635
1636 const required_alignment: u32 = switch (arch) {
1637 .x86_64 => 1,
1638 .aarch64 => @alignOf(u32),
1639 else => unreachable, // unhandled architecture type
1640
1641 };
1642
1643 const sym = atom.getSymbolPtr(self);
1644 sym.n_type = macho.N_SECT;
1645 sym.n_sect = self.stubs_section_index.? + 1;
1646
1647 const code = try gpa.alloc(u8, size);
1648 defer gpa.free(code);
1649 mem.set(u8, code, 0);
1650
1651 switch (arch) {
1652 .x86_64 => {
1653 // jmp
1654 code[0] = 0xff;
1655 code[1] = 0x25;
1656
1657 try Atom.addRelocation(self, atom_index, .{
1658 .type = .branch,
1659 .target = .{ .sym_index = laptr_sym_index },
1660 .offset = 2,
1661 .addend = 0,
1662 .pcrel = true,
1663 .length = 2,
1664 });
1665 },
1666 .aarch64 => {
1667 // adrp x16, pages
1668 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1669 // ldr x16, x16, offset
1670 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(
1671 .x16,
1672 .x16,
1673 aarch64.Instruction.LoadStoreOffset.imm(0),
1674 ).toU32());
1675 // br x16
1676 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1677
1678 try Atom.addRelocations(self, atom_index, &[_]Relocation{
1679 .{
1680 .type = .page,
1681 .target = .{ .sym_index = laptr_sym_index },
1682 .offset = 0,
1683 .addend = 0,
1684 .pcrel = true,
1685 .length = 2,
1686 },
1687 .{
1688 .type = .pageoff,
1689 .target = .{ .sym_index = laptr_sym_index },
1690 .offset = 4,
1691 .addend = 0,
1692 .pcrel = false,
1693 .length = 2,
1694 },
1695 });
1696 },
1697 else => unreachable,
1698 }
1699
1700 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1701 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1702 try self.writeAtom(atom_index, code);
1703
1704 return atom_index;
1411 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1412 try self.writeAtom(atom_index, &buffer);
17051413}
17061414
1707fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1415fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
17081416 const gpa = self.base.allocator;
17091417 const size = 3 * @sizeOf(u64);
17101418 const required_alignment: u32 = 1;
17111419 const atom_index = try self.createAtom();
17121420 self.getAtomPtr(atom_index).size = size;
17131421
1714 const target_sym_name = self.getSymbolName(target);
1715 const name_delimiter = mem.indexOf(u8, target_sym_name, "$").?;
1716 const sym_name = try gpa.dupe(u8, target_sym_name[0..name_delimiter]);
1717 defer gpa.free(sym_name);
1718
17191422 const sym = self.getAtom(atom_index).getSymbolPtr(self);
17201423 sym.n_type = macho.N_SECT;
17211424 sym.n_sect = self.thread_vars_section_index.? + 1;
......@@ -1889,8 +1592,7 @@ pub fn deinit(self: *MachO) void {
18891592 }
18901593
18911594 self.got_table.deinit(gpa);
1892 self.stubs_table.deinit(gpa);
1893 self.tlv_table.deinit(gpa);
1595 self.stub_table.deinit(gpa);
18941596 self.strtab.deinit(gpa);
18951597
18961598 self.locals.deinit(gpa);
......@@ -1923,14 +1625,12 @@ pub fn deinit(self: *MachO) void {
19231625
19241626 self.atoms.deinit(gpa);
19251627
1926 if (self.base.options.module) |_| {
1927 for (self.decls.values()) |*m| {
1928 m.exports.deinit(gpa);
1929 }
1930 self.decls.deinit(gpa);
1931 } else {
1932 assert(self.decls.count() == 0);
1628 for (self.decls.values()) |*m| {
1629 m.exports.deinit(gpa);
19331630 }
1631 self.decls.deinit(gpa);
1632 self.lazy_syms.deinit(gpa);
1633 self.tlv_table.deinit(gpa);
19341634
19351635 for (self.unnamed_const_atoms.values()) |*atoms| {
19361636 atoms.deinit(gpa);
......@@ -1953,11 +1653,6 @@ pub fn deinit(self: *MachO) void {
19531653 bindings.deinit(gpa);
19541654 }
19551655 self.bindings.deinit(gpa);
1956
1957 for (self.lazy_bindings.values()) |*bindings| {
1958 bindings.deinit(gpa);
1959 }
1960 self.lazy_bindings.deinit(gpa);
19611656}
19621657
19631658fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
......@@ -2104,31 +1799,16 @@ fn allocateGlobal(self: *MachO) !u32 {
21041799fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
21051800 if (self.got_table.lookup.contains(target)) return;
21061801 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
2107 const got_atom_index = try self.createGotAtom(target);
2108 const got_atom = self.getAtom(got_atom_index);
2109 self.got_table.entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1802 try self.writeOffsetTableEntry(got_index);
1803 self.got_table_count_dirty = true;
21101804 self.markRelocsDirtyByTarget(target);
21111805}
21121806
21131807fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
2114 if (self.stubs_table.lookup.contains(target)) return;
2115 const stub_index = try self.stubs_table.allocateEntry(self.base.allocator, target);
2116 const stub_helper_atom_index = try self.createStubHelperAtom();
2117 const stub_helper_atom = self.getAtom(stub_helper_atom_index);
2118 const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, target);
2119 const laptr_atom = self.getAtom(laptr_atom_index);
2120 const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
2121 const stub_atom = self.getAtom(stub_atom_index);
2122 self.stubs_table.entries.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
2123 self.markRelocsDirtyByTarget(target);
2124}
2125
2126fn addTlvEntry(self: *MachO, target: SymbolWithLoc) !void {
2127 if (self.tlv_table.lookup.contains(target)) return;
2128 const tlv_index = try self.tlv_table.allocateEntry(self.base.allocator, target);
2129 const tlv_atom_index = try self.createThreadLocalDescriptorAtom(target);
2130 const tlv_atom = self.getAtom(tlv_atom_index);
2131 self.tlv_table.entries.items[tlv_index].sym_index = tlv_atom.getSymbolIndex().?;
1808 if (self.stub_table.lookup.contains(target)) return;
1809 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
1810 try self.writeStubTableEntry(stub_index);
1811 self.stub_table_count_dirty = true;
21321812 self.markRelocsDirtyByTarget(target);
21331813}
21341814
......@@ -2278,6 +1958,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
22781958 }
22791959 }
22801960
1961 const is_threadlocal = if (decl.val.castTag(.variable)) |payload|
1962 payload.data.is_threadlocal and !self.base.options.single_threaded
1963 else
1964 false;
1965 if (is_threadlocal) return self.updateThreadlocalVariable(module, decl_index);
1966
22811967 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
22821968 const sym_index = self.getAtom(atom_index).getSymbolIndex().?;
22831969 Atom.freeRelocations(self, atom_index);
......@@ -2426,6 +2112,101 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol, alignmen
24262112 return atom.*.?;
24272113}
24282114
2115fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
2116 // Lowering a TLV on macOS involves two stages:
2117 // 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss)
2118 // 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars
2119
2120 // 1. Lower the initializer value.
2121 const init_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2122 const init_atom = self.getAtomPtr(init_atom_index);
2123 const init_sym_index = init_atom.getSymbolIndex().?;
2124 Atom.freeRelocations(self, init_atom_index);
2125
2126 const gpa = self.base.allocator;
2127
2128 var code_buffer = std.ArrayList(u8).init(gpa);
2129 defer code_buffer.deinit();
2130
2131 var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
2132 try d_sym.dwarf.initDeclState(module, decl_index)
2133 else
2134 null;
2135 defer if (decl_state) |*ds| ds.deinit();
2136
2137 const decl = module.declPtr(decl_index);
2138 const decl_metadata = self.decls.get(decl_index).?;
2139 const decl_val = decl.val.castTag(.variable).?.data.init;
2140 const res = if (decl_state) |*ds|
2141 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2142 .ty = decl.ty,
2143 .val = decl_val,
2144 }, &code_buffer, .{
2145 .dwarf = ds,
2146 }, .{
2147 .parent_atom_index = init_sym_index,
2148 })
2149 else
2150 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2151 .ty = decl.ty,
2152 .val = decl_val,
2153 }, &code_buffer, .none, .{
2154 .parent_atom_index = init_sym_index,
2155 });
2156
2157 var code = switch (res) {
2158 .ok => code_buffer.items,
2159 .fail => |em| {
2160 decl.analysis = .codegen_failure;
2161 try module.failed_decls.put(module.gpa, decl_index, em);
2162 return;
2163 },
2164 };
2165
2166 const required_alignment = decl.getAlignment(self.base.options.target);
2167
2168 const decl_name = try decl.getFullyQualifiedName(module);
2169 defer gpa.free(decl_name);
2170
2171 const init_sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name});
2172 defer gpa.free(init_sym_name);
2173
2174 const sect_id = decl_metadata.section;
2175 const init_sym = init_atom.getSymbolPtr(self);
2176 init_sym.n_strx = try self.strtab.insert(gpa, init_sym_name);
2177 init_sym.n_type = macho.N_SECT;
2178 init_sym.n_sect = sect_id + 1;
2179 init_sym.n_desc = 0;
2180 init_atom.size = code.len;
2181
2182 init_sym.n_value = try self.allocateAtom(init_atom_index, code.len, required_alignment);
2183 errdefer self.freeAtom(init_atom_index);
2184
2185 log.debug("allocated atom for {s} at 0x{x}", .{ init_sym_name, init_sym.n_value });
2186 log.debug(" (required alignment 0x{x})", .{required_alignment});
2187
2188 try self.writeAtom(init_atom_index, code);
2189
2190 if (decl_state) |*ds| {
2191 try self.d_sym.?.dwarf.commitDeclState(
2192 module,
2193 decl_index,
2194 init_sym.n_value,
2195 self.getAtom(init_atom_index).size,
2196 ds,
2197 );
2198 }
2199
2200 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2201
2202 // 2. Create a TLV descriptor.
2203 const init_atom_sym_loc = init_atom.getSymbolWithLoc();
2204 const gop = try self.tlv_table.getOrPut(gpa, init_atom_sym_loc);
2205 assert(!gop.found_existing);
2206 gop.value_ptr.* = try self.createThreadLocalDescriptorAtom(decl_name, init_atom_sym_loc);
2207 self.markRelocsDirtyByTarget(init_atom_sym_loc);
2208}
2209
24292210pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {
24302211 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
24312212 if (!gop.found_existing) {
......@@ -2493,21 +2274,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
24932274 const sect_id = decl_metadata.section;
24942275 const header = &self.sections.items(.header)[sect_id];
24952276 const segment = self.getSegment(sect_id);
2496 const is_threadlocal = if (!self.base.options.single_threaded)
2497 header.flags == macho.S_THREAD_LOCAL_REGULAR or header.flags == macho.S_THREAD_LOCAL_ZEROFILL
2498 else
2499 false;
25002277 const code_len = code.len;
25012278
2502 const sym_name = if (is_threadlocal)
2503 try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name})
2504 else
2505 decl_name;
2506 defer if (is_threadlocal) gpa.free(sym_name);
2507
25082279 if (atom.size != 0) {
25092280 const sym = atom.getSymbolPtr(self);
2510 sym.n_strx = try self.strtab.insert(gpa, sym_name);
2281 sym.n_strx = try self.strtab.insert(gpa, decl_name);
25112282 sym.n_type = macho.N_SECT;
25122283 sym.n_sect = sect_id + 1;
25132284 sym.n_desc = 0;
......@@ -2517,23 +2288,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
25172288
25182289 if (need_realloc) {
25192290 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
2520 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
2291 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl_name, sym.n_value, vaddr });
25212292 log.debug(" (required alignment 0x{x})", .{required_alignment});
25222293
25232294 if (vaddr != sym.n_value) {
25242295 sym.n_value = vaddr;
2525 // TODO: I think we should update the offset to the initializer here too.
2526 const target: SymbolWithLoc = if (is_threadlocal) blk: {
2527 const tlv_atom_index = self.tlv_table.getAtomIndex(self, .{
2528 .sym_index = sym_index,
2529 }).?;
2530 const tlv_atom = self.getAtom(tlv_atom_index);
2531 break :blk tlv_atom.getSymbolWithLoc();
2532 } else .{ .sym_index = sym_index };
2533 self.markRelocsDirtyByTarget(target);
25342296 log.debug(" (updating GOT entry)", .{});
2535 const got_atom_index = self.got_table.getAtomIndex(self, target).?;
2536 try self.writePtrWidthAtom(got_atom_index);
2297 const got_atom_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?;
2298 try self.writeOffsetTableEntry(got_atom_index);
2299 self.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
25372300 }
25382301 } else if (code_len < atom.size) {
25392302 self.shrinkAtom(atom_index, code_len);
......@@ -2544,7 +2307,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
25442307 self.getAtomPtr(atom_index).size = code_len;
25452308 } else {
25462309 const sym = atom.getSymbolPtr(self);
2547 sym.n_strx = try self.strtab.insert(gpa, sym_name);
2310 sym.n_strx = try self.strtab.insert(gpa, decl_name);
25482311 sym.n_type = macho.N_SECT;
25492312 sym.n_sect = sect_id + 1;
25502313 sym.n_desc = 0;
......@@ -2552,21 +2315,13 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
25522315 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
25532316 errdefer self.freeAtom(atom_index);
25542317
2555 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
2318 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
25562319 log.debug(" (required alignment 0x{x})", .{required_alignment});
25572320
25582321 self.getAtomPtr(atom_index).size = code_len;
25592322 sym.n_value = vaddr;
25602323
2561 if (is_threadlocal) {
2562 try self.addTlvEntry(.{ .sym_index = sym_index });
2563 }
2564 const target: SymbolWithLoc = if (is_threadlocal) blk: {
2565 const tlv_atom_index = self.tlv_table.getAtomIndex(self, .{ .sym_index = sym_index }).?;
2566 const tlv_atom = self.getAtom(tlv_atom_index);
2567 break :blk tlv_atom.getSymbolWithLoc();
2568 } else .{ .sym_index = sym_index };
2569 try self.addGotEntry(target);
2324 try self.addGotEntry(.{ .sym_index = sym_index });
25702325 }
25712326
25722327 try self.writeAtom(atom_index, code);
......@@ -2828,11 +2583,7 @@ fn populateMissingMetadata(self: *MachO) !void {
28282583 }
28292584
28302585 if (self.stubs_section_index == null) {
2831 const stub_size: u32 = switch (cpu_arch) {
2832 .x86_64 => 6,
2833 .aarch64 => 3 * @sizeOf(u32),
2834 else => unreachable, // unhandled architecture type
2835 };
2586 const stub_size = stubs.calcStubEntrySize(cpu_arch);
28362587 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
28372588 .size = stub_size,
28382589 .alignment = switch (cpu_arch) {
......@@ -3021,7 +2772,7 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
30212772 const last_atom = self.getAtom(last_atom_index);
30222773 const sym = last_atom.getSymbol(self);
30232774 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
3024 } else 0;
2775 } else header.size;
30252776
30262777 log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{
30272778 header.segName(),
......@@ -3249,6 +3000,28 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
32493000 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
32503001}
32513002
3003fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3004 const header = self.sections.items(.header)[sect_id];
3005 const segment_index = self.sections.items(.segment_index)[sect_id];
3006 const segment = self.segments.items[segment_index];
3007 const base_offset = header.addr - segment.vmaddr;
3008 const is_got = if (self.got_section_index) |index| index == sect_id else false;
3009
3010 try rebase.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len);
3011
3012 for (table.entries.items, 0..) |entry, i| {
3013 if (!table.lookup.contains(entry)) continue;
3014 const sym = self.getSymbol(entry);
3015 if (is_got and sym.undf()) continue;
3016 const offset = i * @sizeOf(u64);
3017 log.debug(" | rebase at {x}", .{base_offset + offset});
3018 rebase.entries.appendAssumeCapacity(.{
3019 .offset = base_offset + offset,
3020 .segment_id = segment_index,
3021 });
3022 }
3023}
3024
32523025fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
32533026 const gpa = self.base.allocator;
32543027 const slice = self.sections.slice();
......@@ -3276,9 +3049,42 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
32763049 }
32773050 }
32783051
3052 try self.collectRebaseDataFromTableSection(self.got_section_index.?, rebase, self.got_table);
3053 try self.collectRebaseDataFromTableSection(self.la_symbol_ptr_section_index.?, rebase, self.stub_table);
3054
32793055 try rebase.finalize(gpa);
32803056}
32813057
3058fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3059 const header = self.sections.items(.header)[sect_id];
3060 const segment_index = self.sections.items(.segment_index)[sect_id];
3061 const segment = self.segments.items[segment_index];
3062 const base_offset = header.addr - segment.vmaddr;
3063
3064 try bind.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len);
3065
3066 for (table.entries.items, 0..) |entry, i| {
3067 if (!table.lookup.contains(entry)) continue;
3068 const bind_sym = self.getSymbol(entry);
3069 if (!bind_sym.undf()) continue;
3070 const offset = i * @sizeOf(u64);
3071 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3072 base_offset + offset,
3073 self.getSymbolName(entry),
3074 @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
3075 });
3076 if (bind_sym.weakRef()) {
3077 log.debug(" | marking as weak ref ", .{});
3078 }
3079 bind.entries.appendAssumeCapacity(.{
3080 .target = entry,
3081 .offset = base_offset + offset,
3082 .segment_id = segment_index,
3083 .addend = 0,
3084 });
3085 }
3086}
3087
32823088fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
32833089 const gpa = self.base.allocator;
32843090 const slice = self.sections.slice();
......@@ -3320,9 +3126,16 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
33203126 }
33213127 }
33223128
3129 // Gather GOT pointers
3130 try self.collectBindDataFromTableSection(self.got_section_index.?, bind, self.got_table);
33233131 try bind.finalize(gpa, self);
33243132}
33253133
3134fn collectLazyBindData(self: *MachO, bind: anytype) !void {
3135 try self.collectBindDataFromTableSection(self.la_symbol_ptr_section_index.?, bind, self.stub_table);
3136 try bind.finalize(self.base.allocator, self);
3137}
3138
33263139fn collectExportData(self: *MachO, trie: *Trie) !void {
33273140 const gpa = self.base.allocator;
33283141
......@@ -3366,7 +3179,7 @@ fn writeDyldInfoData(self: *MachO) !void {
33663179
33673180 var lazy_bind = LazyBind{};
33683181 defer lazy_bind.deinit(gpa);
3369 try self.collectBindData(&lazy_bind, self.lazy_bindings);
3182 try self.collectLazyBindData(&lazy_bind);
33703183
33713184 var trie: Trie = .{};
33723185 defer trie.deinit(gpa);
......@@ -3442,34 +3255,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
34423255 if (lazy_bind.size() == 0) return;
34433256
34443257 const stub_helper_section_index = self.stub_helper_section_index.?;
3445 assert(self.stub_helper_preamble_atom_index != null);
3258 assert(self.stub_helper_preamble_allocated);
34463259
3447 const section = self.sections.get(stub_helper_section_index);
3260 const header = self.sections.items(.header)[stub_helper_section_index];
34483261
3449 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
3450 .x86_64 => 1,
3451 .aarch64 => 2 * @sizeOf(u32),
3452 else => unreachable,
3453 };
3454 const header = section.header;
3455 var atom_index = section.last_atom_index.?;
3262 const cpu_arch = self.base.options.target.cpu.arch;
3263 const preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);
3264 const stub_size = stubs.calcStubHelperEntrySize(cpu_arch);
3265 const stub_offset = stubs.calcStubOffsetInStubHelper(cpu_arch);
3266 const base_offset = header.offset + preamble_size;
34563267
3457 var index: usize = lazy_bind.offsets.items.len;
3458 while (index > 0) : (index -= 1) {
3459 const atom = self.getAtom(atom_index);
3460 const sym = atom.getSymbol(self);
3461 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
3462 const bind_offset = lazy_bind.offsets.items[index - 1];
3268 for (lazy_bind.offsets.items, 0..) |bind_offset, index| {
3269 const file_offset = base_offset + index * stub_size + stub_offset;
34633270
34643271 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{
34653272 bind_offset,
3466 self.getSymbolName(lazy_bind.entries.items[index - 1].target),
3273 self.getSymbolName(lazy_bind.entries.items[index].target),
34673274 file_offset,
34683275 });
34693276
34703277 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
3471
3472 atom_index = atom.prev_index.?;
34733278 }
34743279}
34753280
......@@ -3585,7 +3390,7 @@ const SymtabCtx = struct {
35853390
35863391fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
35873392 const gpa = self.base.allocator;
3588 const nstubs = @intCast(u32, self.stubs_table.lookup.count());
3393 const nstubs = @intCast(u32, self.stub_table.lookup.count());
35893394 const ngot_entries = @intCast(u32, self.got_table.lookup.count());
35903395 const nindirectsyms = nstubs * 2 + ngot_entries;
35913396 const iextdefsym = ctx.nlocalsym;
......@@ -3606,13 +3411,13 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
36063411 const writer = buf.writer();
36073412
36083413 if (self.stubs_section_index) |sect_id| {
3609 const stubs = &self.sections.items(.header)[sect_id];
3610 stubs.reserved1 = 0;
3611 for (self.stubs_table.entries.items) |entry| {
3612 if (entry.sym_index == 0) continue;
3613 const target_sym = self.getSymbol(entry.target);
3414 const stubs_header = &self.sections.items(.header)[sect_id];
3415 stubs_header.reserved1 = 0;
3416 for (self.stub_table.entries.items) |entry| {
3417 if (!self.stub_table.lookup.contains(entry)) continue;
3418 const target_sym = self.getSymbol(entry);
36143419 assert(target_sym.undf());
3615 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
3420 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
36163421 }
36173422 }
36183423
......@@ -3620,10 +3425,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
36203425 const got = &self.sections.items(.header)[sect_id];
36213426 got.reserved1 = nstubs;
36223427 for (self.got_table.entries.items) |entry| {
3623 if (entry.sym_index == 0) continue;
3624 const target_sym = self.getSymbol(entry.target);
3428 if (!self.got_table.lookup.contains(entry)) continue;
3429 const target_sym = self.getSymbol(entry);
36253430 if (target_sym.undf()) {
3626 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
3431 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
36273432 } else {
36283433 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
36293434 }
......@@ -3633,11 +3438,11 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
36333438 if (self.la_symbol_ptr_section_index) |sect_id| {
36343439 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
36353440 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
3636 for (self.stubs_table.entries.items) |entry| {
3637 if (entry.sym_index == 0) continue;
3638 const target_sym = self.getSymbol(entry.target);
3441 for (self.stub_table.entries.items) |entry| {
3442 if (!self.stub_table.lookup.contains(entry)) continue;
3443 const target_sym = self.getSymbol(entry);
36393444 assert(target_sym.undf());
3640 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
3445 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
36413446 }
36423447 }
36433448
......@@ -4321,13 +4126,13 @@ pub fn logSymtab(self: *MachO) void {
43214126 }
43224127
43234128 log.debug("GOT entries:", .{});
4324 log.debug("{}", .{self.got_table.fmtDebug(self)});
4129 log.debug("{}", .{self.got_table});
43254130
43264131 log.debug("stubs entries:", .{});
4327 log.debug("{}", .{self.stubs_table.fmtDebug(self)});
4132 log.debug("{}", .{self.stub_table});
43284133
4329 log.debug("threadlocal entries:", .{});
4330 log.debug("{}", .{self.tlv_table.fmtDebug(self)});
4134 // log.debug("threadlocal entries:", .{});
4135 // log.debug("{}", .{self.tlv_table});
43314136}
43324137
43334138pub fn logAtoms(self: *MachO) void {
src/link/MachO/Atom.zig-17
......@@ -158,21 +158,6 @@ pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void
158158 try gop.value_ptr.append(gpa, binding);
159159}
160160
161pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
162 const gpa = macho_file.base.allocator;
163 const atom = macho_file.getAtom(atom_index);
164 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{
165 macho_file.getSymbolName(binding.target),
166 binding.offset,
167 atom.getSymbolIndex(),
168 });
169 const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
170 if (!gop.found_existing) {
171 gop.value_ptr.* = .{};
172 }
173 try gop.value_ptr.append(gpa, binding);
174}
175
176161pub fn resolveRelocations(
177162 macho_file: *MachO,
178163 atom_index: Index,
......@@ -193,6 +178,4 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
193178 if (removed_rebases) |*rebases| rebases.value.deinit(gpa);
194179 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
195180 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
196 var removed_lazy_bindings = macho_file.lazy_bindings.fetchOrderedRemove(atom_index);
197 if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(gpa);
198181}
src/link/MachO/DebugSymbols.zig+2-2
......@@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
230230 .got_load => blk: {
231231 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
232232 const got_entry = macho_file.got_table.entries.items[got_index];
233 break :blk got_entry.getSymbol(macho_file);
233 break :blk macho_file.getSymbol(got_entry);
234234 },
235235 };
236236 if (sym.n_value == reloc.prev_vaddr) continue;
......@@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
240240 .got_load => blk: {
241241 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
242242 const got_entry = macho_file.got_table.entries.items[got_index];
243 break :blk got_entry.getName(macho_file);
243 break :blk macho_file.getSymbolName(got_entry);
244244 },
245245 };
246246 const sect = &self.sections.items[self.debug_info_section_index.?];
src/link/MachO/Relocation.zig+66-22
......@@ -15,7 +15,7 @@ pub const Type = enum {
1515 got,
1616 /// RIP-relative displacement
1717 signed,
18 /// RIP-relative displacement to GOT pointer to TLV thunk
18 /// RIP-relative displacement to a TLV thunk
1919 tlv,
2020
2121 // aarch64
......@@ -39,25 +39,35 @@ pub const Type = enum {
3939
4040/// Returns true if and only if the reloc is dirty AND the target address is available.
4141pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {
42 _ = self.getTargetAtomIndex(macho_file) orelse return false;
42 const addr = self.getTargetBaseAddress(macho_file) orelse return false;
43 if (addr == 0) return false;
4344 return self.dirty;
4445}
4546
46pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
47 return switch (self.type) {
48 .got, .got_page, .got_pageoff => macho_file.got_table.getAtomIndex(macho_file, self.target),
47pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
48 switch (self.type) {
49 .got, .got_page, .got_pageoff => {
50 const got_index = macho_file.got_table.lookup.get(self.target) orelse return null;
51 const header = macho_file.sections.items(.header)[macho_file.got_section_index.?];
52 return header.addr + got_index * @sizeOf(u64);
53 },
4954 .tlv => {
50 const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse
51 return null;
52 const thunk_atom = macho_file.getAtom(thunk_atom_index);
53 return macho_file.got_table.getAtomIndex(macho_file, thunk_atom.getSymbolWithLoc());
55 const atom_index = macho_file.tlv_table.get(self.target) orelse return null;
56 const atom = macho_file.getAtom(atom_index);
57 return atom.getSymbol(macho_file).n_value;
5458 },
55 .branch => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index|
56 index
57 else
58 macho_file.getAtomIndexForSymbol(self.target),
59 else => macho_file.getAtomIndexForSymbol(self.target),
60 };
59 .branch => {
60 if (macho_file.stub_table.lookup.get(self.target)) |index| {
61 const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
62 return header.addr +
63 index * @import("stubs.zig").calcStubEntrySize(macho_file.base.options.target.cpu.arch);
64 }
65 const atom_index = macho_file.getAtomIndexForSymbol(self.target) orelse return null;
66 const atom = macho_file.getAtom(atom_index);
67 return atom.getSymbol(macho_file).n_value;
68 },
69 else => return macho_file.getSymbol(self.target).n_value,
70 }
6171}
6272
6373pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void {
......@@ -66,17 +76,14 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod
6676 const source_sym = atom.getSymbol(macho_file);
6777 const source_addr = source_sym.n_value + self.offset;
6878
69 const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
70 const target_atom = macho_file.getAtom(target_atom_index);
71
79 const target_base_addr = self.getTargetBaseAddress(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
7280 const target_addr: i64 = switch (self.type) {
7381 .tlv_initializer => blk: {
7482 assert(self.addend == 0); // Addend here makes no sense.
7583 const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?];
76 const target_sym = target_atom.getSymbol(macho_file);
77 break :blk @intCast(i64, target_sym.n_value - header.addr);
84 break :blk @intCast(i64, target_base_addr - header.addr);
7885 },
79 else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend,
86 else => @intCast(i64, target_base_addr) + self.addend,
8087 };
8188
8289 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
......@@ -189,11 +196,48 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8
189196 }
190197}
191198
192inline fn isArithmeticOp(inst: *const [4]u8) bool {
199pub inline fn isArithmeticOp(inst: *const [4]u8) bool {
193200 const group_decode = @truncate(u5, inst[3]);
194201 return ((group_decode >> 2) == 4);
195202}
196203
204pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
205 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
206 return math.cast(i32, disp) orelse error.Overflow;
207}
208
209pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
210 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
211 return math.cast(i28, disp) orelse error.Overflow;
212}
213
214pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
215 const source_page = @intCast(i32, source_addr >> 12);
216 const target_page = @intCast(i32, target_addr >> 12);
217 const pages = @intCast(i21, target_page - source_page);
218 return pages;
219}
220
221pub const PageOffsetInstKind = enum {
222 arithmetic,
223 load_store_8,
224 load_store_16,
225 load_store_32,
226 load_store_64,
227 load_store_128,
228};
229
230pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
231 const narrowed = @truncate(u12, target_addr);
232 return switch (kind) {
233 .arithmetic, .load_store_8 => narrowed,
234 .load_store_16 => try math.divExact(u12, narrowed, 2),
235 .load_store_32 => try math.divExact(u12, narrowed, 4),
236 .load_store_64 => try math.divExact(u12, narrowed, 8),
237 .load_store_128 => try math.divExact(u12, narrowed, 16),
238 };
239}
240
197241const Relocation = @This();
198242
199243const std = @import("std");
src/link/MachO/ZldAtom.zig+17-58
......@@ -21,6 +21,7 @@ const Allocator = mem.Allocator;
2121const Arch = std.Target.Cpu.Arch;
2222const AtomIndex = @import("zld.zig").AtomIndex;
2323const Object = @import("Object.zig");
24const Relocation = @import("Relocation.zig");
2425const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
2526const Zld = @import("zld.zig").Zld;
2627
......@@ -571,7 +572,7 @@ fn resolveRelocsArm64(
571572 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
572573 });
573574
574 const displacement = if (calcPcRelativeDisplacementArm64(
575 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
575576 source_addr,
576577 zld.getSymbol(actual_target).n_value,
577578 )) |disp| blk: {
......@@ -585,7 +586,7 @@ fn resolveRelocsArm64(
585586 actual_target,
586587 ).?);
587588 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
588 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
589 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
589590 };
590591
591592 const code = atom_code[rel_offset..][0..4];
......@@ -607,7 +608,7 @@ fn resolveRelocsArm64(
607608
608609 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
609610
610 const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr));
611 const pages = @bitCast(u21, Relocation.calcNumberOfPages(source_addr, adjusted_target_addr));
611612 const code = atom_code[rel_offset..][0..4];
612613 var inst = aarch64.Instruction{
613614 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
......@@ -627,8 +628,8 @@ fn resolveRelocsArm64(
627628 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
628629
629630 const code = atom_code[rel_offset..][0..4];
630 if (isArithmeticOp(code)) {
631 const off = try calcPageOffset(adjusted_target_addr, .arithmetic);
631 if (Relocation.isArithmeticOp(code)) {
632 const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
632633 var inst = aarch64.Instruction{
633634 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
634635 aarch64.Instruction,
......@@ -644,11 +645,11 @@ fn resolveRelocsArm64(
644645 aarch64.Instruction.load_store_register,
645646 ), code),
646647 };
647 const off = try calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
648 const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
648649 0 => if (inst.load_store_register.v == 1)
649 PageOffsetInstKind.load_store_128
650 Relocation.PageOffsetInstKind.load_store_128
650651 else
651 PageOffsetInstKind.load_store_8,
652 Relocation.PageOffsetInstKind.load_store_8,
652653 1 => .load_store_16,
653654 2 => .load_store_32,
654655 3 => .load_store_64,
......@@ -665,7 +666,7 @@ fn resolveRelocsArm64(
665666
666667 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
667668
668 const off = try calcPageOffset(adjusted_target_addr, .load_store_64);
669 const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
669670 var inst: aarch64.Instruction = .{
670671 .load_store_register = mem.bytesToValue(meta.TagPayload(
671672 aarch64.Instruction,
......@@ -689,7 +690,7 @@ fn resolveRelocsArm64(
689690 size: u2,
690691 };
691692 const reg_info: RegInfo = blk: {
692 if (isArithmeticOp(code)) {
693 if (Relocation.isArithmeticOp(code)) {
693694 const inst = mem.bytesToValue(meta.TagPayload(
694695 aarch64.Instruction,
695696 aarch64.Instruction.add_subtract_immediate,
......@@ -716,7 +717,7 @@ fn resolveRelocsArm64(
716717 .load_store_register = .{
717718 .rt = reg_info.rd,
718719 .rn = reg_info.rn,
719 .offset = try calcPageOffset(adjusted_target_addr, .load_store_64),
720 .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
720721 .opc = 0b01,
721722 .op1 = 0b01,
722723 .v = 0,
......@@ -726,7 +727,7 @@ fn resolveRelocsArm64(
726727 .add_subtract_immediate = .{
727728 .rd = reg_info.rd,
728729 .rn = reg_info.rn,
729 .imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic),
730 .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
730731 .sh = 0,
731732 .s = 0,
732733 .op = 0,
......@@ -858,7 +859,7 @@ fn resolveRelocsX86(
858859 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
859860 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
860861 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
861 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
862 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
862863 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
863864 },
864865
......@@ -868,7 +869,7 @@ fn resolveRelocsX86(
868869 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
869870 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
870871 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
871 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
872 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
872873 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
873874 },
874875
......@@ -876,7 +877,7 @@ fn resolveRelocsX86(
876877 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
877878 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
878879 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
879 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
880 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
880881
881882 if (zld.tlv_ptr_table.get(target) == null) {
882883 // We need to rewrite the opcode from movq to leaq.
......@@ -913,7 +914,7 @@ fn resolveRelocsX86(
913914
914915 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
915916
916 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
917 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
917918 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
918919 },
919920
......@@ -955,11 +956,6 @@ fn resolveRelocsX86(
955956 }
956957}
957958
958inline fn isArithmeticOp(inst: *const [4]u8) bool {
959 const group_decode = @truncate(u5, inst[3]);
960 return ((group_decode >> 2) == 4);
961}
962
963959pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
964960 const atom = zld.getAtom(atom_index);
965961 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
......@@ -1006,43 +1002,6 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_
10061002 return relocs[cache.start..][0..cache.len];
10071003}
10081004
1009pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
1010 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
1011 return math.cast(i32, disp) orelse error.Overflow;
1012}
1013
1014pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
1015 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
1016 return math.cast(i28, disp) orelse error.Overflow;
1017}
1018
1019pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
1020 const source_page = @intCast(i32, source_addr >> 12);
1021 const target_page = @intCast(i32, target_addr >> 12);
1022 const pages = @intCast(i21, target_page - source_page);
1023 return pages;
1024}
1025
1026const PageOffsetInstKind = enum {
1027 arithmetic,
1028 load_store_8,
1029 load_store_16,
1030 load_store_32,
1031 load_store_64,
1032 load_store_128,
1033};
1034
1035pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
1036 const narrowed = @truncate(u12, target_addr);
1037 return switch (kind) {
1038 .arithmetic, .load_store_8 => narrowed,
1039 .load_store_16 => try math.divExact(u12, narrowed, 2),
1040 .load_store_32 => try math.divExact(u12, narrowed, 4),
1041 .load_store_64 => try math.divExact(u12, narrowed, 8),
1042 .load_store_128 => try math.divExact(u12, narrowed, 16),
1043 };
1044}
1045
10461005pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
10471006 switch (zld.options.target.cpu.arch) {
10481007 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
src/link/MachO/eh_frame.zig+2-1
......@@ -9,6 +9,7 @@ const log = std.log.scoped(.eh_frame);
99const Allocator = mem.Allocator;
1010const AtomIndex = @import("zld.zig").AtomIndex;
1111const Atom = @import("ZldAtom.zig");
12const Relocation = @import("Relocation.zig");
1213const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
1314const UnwindInfo = @import("UnwindInfo.zig");
1415const Zld = @import("zld.zig").Zld;
......@@ -368,7 +369,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
368369 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
369370 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
370371 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
371 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
372 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
372373 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
373374 },
374375 else => unreachable,
src/link/MachO/stubs.zig created+161
......@@ -0,0 +1,161 @@
1const std = @import("std");
2const aarch64 = @import("../../arch/aarch64/bits.zig");
3
4const Relocation = @import("Relocation.zig");
5
6pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
7 return switch (cpu_arch) {
8 .x86_64 => 15,
9 .aarch64 => 6 * @sizeOf(u32),
10 else => unreachable, // unhandled architecture type
11 };
12}
13
14pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
15 return switch (cpu_arch) {
16 .x86_64 => 10,
17 .aarch64 => 3 * @sizeOf(u32),
18 else => unreachable, // unhandled architecture type
19 };
20}
21
22pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
23 return switch (cpu_arch) {
24 .x86_64 => 6,
25 .aarch64 => 3 * @sizeOf(u32),
26 else => unreachable, // unhandled architecture type
27 };
28}
29
30pub inline fn calcStubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u4 {
31 return switch (cpu_arch) {
32 .x86_64 => 1,
33 .aarch64 => 2 * @sizeOf(u32),
34 else => unreachable,
35 };
36}
37
38pub fn writeStubHelperPreambleCode(args: struct {
39 cpu_arch: std.Target.Cpu.Arch,
40 source_addr: u64,
41 dyld_private_addr: u64,
42 dyld_stub_binder_got_addr: u64,
43}, writer: anytype) !void {
44 switch (args.cpu_arch) {
45 .x86_64 => {
46 try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
47 {
48 const disp = try Relocation.calcPcRelativeDisplacementX86(
49 args.source_addr + 3,
50 args.dyld_private_addr,
51 0,
52 );
53 try writer.writeIntLittle(i32, disp);
54 }
55 try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
56 {
57 const disp = try Relocation.calcPcRelativeDisplacementX86(
58 args.source_addr + 11,
59 args.dyld_stub_binder_got_addr,
60 0,
61 );
62 try writer.writeIntLittle(i32, disp);
63 }
64 },
65 .aarch64 => {
66 {
67 const pages = Relocation.calcNumberOfPages(args.source_addr, args.dyld_private_addr);
68 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
69 }
70 {
71 const off = try Relocation.calcPageOffset(args.dyld_private_addr, .arithmetic);
72 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
73 }
74 try writer.writeIntLittle(u32, aarch64.Instruction.stp(
75 .x16,
76 .x17,
77 aarch64.Register.sp,
78 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
79 ).toU32());
80 {
81 const pages = Relocation.calcNumberOfPages(args.source_addr + 12, args.dyld_stub_binder_got_addr);
82 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
83 }
84 {
85 const off = try Relocation.calcPageOffset(args.dyld_stub_binder_got_addr, .load_store_64);
86 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
87 .x16,
88 .x16,
89 aarch64.Instruction.LoadStoreOffset.imm(off),
90 ).toU32());
91 }
92 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
93 },
94 else => unreachable,
95 }
96}
97
98pub fn writeStubHelperCode(args: struct {
99 cpu_arch: std.Target.Cpu.Arch,
100 source_addr: u64,
101 target_addr: u64,
102}, writer: anytype) !void {
103 switch (args.cpu_arch) {
104 .x86_64 => {
105 try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
106 {
107 const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 6, args.target_addr, 0);
108 try writer.writeIntLittle(i32, disp);
109 }
110 },
111 .aarch64 => {
112 const stub_size: u4 = 3 * @sizeOf(u32);
113 const literal = blk: {
114 const div_res = try std.math.divExact(u64, stub_size - @sizeOf(u32), 4);
115 break :blk std.math.cast(u18, div_res) orelse return error.Overflow;
116 };
117 try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
118 .w16,
119 literal,
120 ).toU32());
121 {
122 const disp = try Relocation.calcPcRelativeDisplacementArm64(args.source_addr + 4, args.target_addr);
123 try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
124 }
125 try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
126 },
127 else => unreachable,
128 }
129}
130
131pub fn writeStubCode(args: struct {
132 cpu_arch: std.Target.Cpu.Arch,
133 source_addr: u64,
134 target_addr: u64,
135}, writer: anytype) !void {
136 switch (args.cpu_arch) {
137 .x86_64 => {
138 try writer.writeAll(&.{ 0xff, 0x25 });
139 {
140 const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 2, args.target_addr, 0);
141 try writer.writeIntLittle(i32, disp);
142 }
143 },
144 .aarch64 => {
145 {
146 const pages = Relocation.calcNumberOfPages(args.source_addr, args.target_addr);
147 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
148 }
149 {
150 const off = try Relocation.calcPageOffset(args.target_addr, .load_store_64);
151 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
152 .x16,
153 .x16,
154 aarch64.Instruction.LoadStoreOffset.imm(off),
155 ).toU32());
156 }
157 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
158 },
159 else => unreachable,
160 }
161}
src/link/MachO/thunks.zig+4-3
......@@ -17,6 +17,7 @@ const aarch64 = @import("../../arch/aarch64/bits.zig");
1717const Allocator = mem.Allocator;
1818const Atom = @import("ZldAtom.zig");
1919const AtomIndex = @import("zld.zig").AtomIndex;
20const Relocation = @import("Relocation.zig");
2021const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
2122const Zld = @import("zld.zig").Zld;
2223
......@@ -317,7 +318,7 @@ fn isReachable(
317318 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
318319 const is_via_got = Atom.relocRequiresGot(zld, rel);
319320 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
320 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
321322 return false;
322323
323324 return true;
......@@ -364,9 +365,9 @@ pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
364365 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
365366 } else unreachable;
366367
367 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
368 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
368369 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
369 const off = try Atom.calcPageOffset(target_addr, .arithmetic);
370 const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
370371 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
371372 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
372373}
src/link/MachO/zld.zig+20-110
......@@ -16,6 +16,7 @@ const link = @import("../../link.zig");
1616const load_commands = @import("load_commands.zig");
1717const thunks = @import("thunks.zig");
1818const trace = @import("../../tracy.zig").trace;
19const stub_helpers = @import("stubs.zig");
1920
2021const Allocator = mem.Allocator;
2122const Archive = @import("Archive.zig");
......@@ -666,59 +667,17 @@ pub const Zld = struct {
666667 const entry = self.got_entries.items[index];
667668 break :blk entry.getAtomSymbol(self).n_value;
668669 };
669 switch (cpu_arch) {
670 .x86_64 => {
671 try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
672 {
673 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 3, dyld_private_addr, 0);
674 try writer.writeIntLittle(i32, disp);
675 }
676 try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
677 {
678 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 11, dyld_stub_binder_got_addr, 0);
679 try writer.writeIntLittle(i32, disp);
680 }
681 },
682 .aarch64 => {
683 {
684 const pages = Atom.calcNumberOfPages(source_addr, dyld_private_addr);
685 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
686 }
687 {
688 const off = try Atom.calcPageOffset(dyld_private_addr, .arithmetic);
689 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
690 }
691 try writer.writeIntLittle(u32, aarch64.Instruction.stp(
692 .x16,
693 .x17,
694 aarch64.Register.sp,
695 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
696 ).toU32());
697 {
698 const pages = Atom.calcNumberOfPages(source_addr + 12, dyld_stub_binder_got_addr);
699 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
700 }
701 {
702 const off = try Atom.calcPageOffset(dyld_stub_binder_got_addr, .load_store_64);
703 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
704 .x16,
705 .x16,
706 aarch64.Instruction.LoadStoreOffset.imm(off),
707 ).toU32());
708 }
709 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
710 },
711 else => unreachable,
712 }
670 try stub_helpers.writeStubHelperPreambleCode(.{
671 .cpu_arch = cpu_arch,
672 .source_addr = source_addr,
673 .dyld_private_addr = dyld_private_addr,
674 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
675 }, writer);
713676 }
714677
715678 pub fn createStubHelperAtom(self: *Zld) !AtomIndex {
716679 const cpu_arch = self.options.target.cpu.arch;
717 const stub_size: u4 = switch (cpu_arch) {
718 .x86_64 => 10,
719 .aarch64 => 3 * @sizeOf(u32),
720 else => unreachable,
721 };
680 const stub_size = stub_helpers.calcStubHelperEntrySize(cpu_arch);
722681 const alignment: u2 = switch (cpu_arch) {
723682 .x86_64 => 0,
724683 .aarch64 => 2,
......@@ -749,32 +708,11 @@ pub const Zld = struct {
749708 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
750709 break :blk sym.n_value;
751710 };
752 switch (cpu_arch) {
753 .x86_64 => {
754 try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
755 {
756 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 6, target_addr, 0);
757 try writer.writeIntLittle(i32, disp);
758 }
759 },
760 .aarch64 => {
761 const stub_size: u4 = 3 * @sizeOf(u32);
762 const literal = blk: {
763 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
764 break :blk math.cast(u18, div_res) orelse return error.Overflow;
765 };
766 try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
767 .w16,
768 literal,
769 ).toU32());
770 {
771 const disp = try Atom.calcPcRelativeDisplacementArm64(source_addr + 4, target_addr);
772 try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
773 }
774 try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
775 },
776 else => unreachable,
777 }
711 try stub_helpers.writeStubHelperCode(.{
712 .cpu_arch = cpu_arch,
713 .source_addr = source_addr,
714 .target_addr = target_addr,
715 }, writer);
778716 }
779717
780718 pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {
......@@ -819,11 +757,7 @@ pub const Zld = struct {
819757 .aarch64 => 2,
820758 else => unreachable, // unhandled architecture type
821759 };
822 const stub_size: u4 = switch (cpu_arch) {
823 .x86_64 => 6,
824 .aarch64 => 3 * @sizeOf(u32),
825 else => unreachable, // unhandled architecture type
826 };
760 const stub_size = stub_helpers.calcStubEntrySize(cpu_arch);
827761 const sym_index = try self.allocateSymbol();
828762 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
829763 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
......@@ -863,31 +797,11 @@ pub const Zld = struct {
863797 const sym = self.getSymbol(atom.getSymbolWithLoc());
864798 break :blk sym.n_value;
865799 };
866 switch (cpu_arch) {
867 .x86_64 => {
868 try writer.writeAll(&.{ 0xff, 0x25 });
869 {
870 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 2, target_addr, 0);
871 try writer.writeIntLittle(i32, disp);
872 }
873 },
874 .aarch64 => {
875 {
876 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
877 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
878 }
879 {
880 const off = try Atom.calcPageOffset(target_addr, .load_store_64);
881 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
882 .x16,
883 .x16,
884 aarch64.Instruction.LoadStoreOffset.imm(off),
885 ).toU32());
886 }
887 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
888 },
889 else => unreachable,
890 }
800 try stub_helpers.writeStubCode(.{
801 .cpu_arch = cpu_arch,
802 .source_addr = source_addr,
803 .target_addr = target_addr,
804 }, writer);
891805 }
892806
893807 fn createTentativeDefAtoms(self: *Zld) !void {
......@@ -2267,11 +2181,7 @@ pub const Zld = struct {
22672181 assert(self.stub_helper_preamble_sym_index != null);
22682182
22692183 const section = self.sections.get(stub_helper_section_index);
2270 const stub_offset: u4 = switch (self.options.target.cpu.arch) {
2271 .x86_64 => 1,
2272 .aarch64 => 2 * @sizeOf(u32),
2273 else => unreachable,
2274 };
2184 const stub_offset = stub_helpers.calcStubOffsetInStubHelper(self.options.target.cpu.arch);
22752185 const header = section.header;
22762186 var atom_index = section.first_atom_index;
22772187 atom_index = self.getAtom(atom_index).next_index.?; // skip preamble
src/link/table_section.zig created+65
......@@ -0,0 +1,65 @@
1pub fn TableSection(comptime Entry: type) type {
2 return struct {
3 entries: std.ArrayListUnmanaged(Entry) = .{},
4 free_list: std.ArrayListUnmanaged(Index) = .{},
5 lookup: std.AutoHashMapUnmanaged(Entry, Index) = .{},
6
7 pub fn deinit(self: *Self, allocator: Allocator) void {
8 self.entries.deinit(allocator);
9 self.free_list.deinit(allocator);
10 self.lookup.deinit(allocator);
11 }
12
13 pub fn allocateEntry(self: *Self, allocator: Allocator, entry: Entry) Allocator.Error!Index {
14 try self.entries.ensureUnusedCapacity(allocator, 1);
15 const index = blk: {
16 if (self.free_list.popOrNull()) |index| {
17 log.debug(" (reusing entry index {d})", .{index});
18 break :blk index;
19 } else {
20 log.debug(" (allocating entry at index {d})", .{self.entries.items.len});
21 const index = @intCast(u32, self.entries.items.len);
22 _ = self.entries.addOneAssumeCapacity();
23 break :blk index;
24 }
25 };
26 self.entries.items[index] = entry;
27 try self.lookup.putNoClobber(allocator, entry, index);
28 return index;
29 }
30
31 pub fn freeEntry(self: *Self, allocator: Allocator, entry: Entry) void {
32 const index = self.lookup.get(entry) orelse return;
33 self.free_list.append(allocator, index) catch {};
34 self.entries.items[index] = undefined;
35 _ = self.lookup.remove(entry);
36 }
37
38 pub fn count(self: Self) usize {
39 return self.entries.items.len;
40 }
41
42 pub fn format(
43 self: Self,
44 comptime unused_format_string: []const u8,
45 options: std.fmt.FormatOptions,
46 writer: anytype,
47 ) !void {
48 _ = options;
49 comptime assert(unused_format_string.len == 0);
50 try writer.writeAll("TableSection:\n");
51 for (self.entries.items, 0..) |entry, i| {
52 try writer.print(" {d} => {}\n", .{ i, entry });
53 }
54 }
55
56 const Self = @This();
57 pub const Index = u32;
58 };
59}
60
61const std = @import("std");
62const assert = std.debug.assert;
63const log = std.log.scoped(.link);
64
65const Allocator = std.mem.Allocator;
test/standalone/windows_spawn/main.zig+1-1
......@@ -4,7 +4,7 @@ const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
44
55pub fn main() anyerror!void {
66 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
7 defer if (gpa.deinit()) @panic("found memory leaks");
7 defer if (gpa.deinit() == .leak) @panic("found memory leaks");
88 const allocator = gpa.allocator();
99
1010 var it = try std.process.argsWithAllocator(allocator);