| author | |
| committer | |
| log | c5fb245f60039565f7a5a4de412da815f037ab15 |
| tree | 0a473e618a9982cd9a0209bc053028eef8497cce |
| parent | 428f7452704d1a7b3d9da8e7d4d408db4cbff81f |
| parent | ad634bca9f12a9de641270478a6abf9495b9846f |
| signature |
38 files changed, 1137 insertions(+), 1114 deletions(-)
doc/docgen.zig+1| ... | ... | @@ -1424,6 +1424,7 @@ fn genHtml( |
| 1424 | 1424 | const result = try ChildProcess.exec(.{ |
| 1425 | 1425 | .allocator = allocator, |
| 1426 | 1426 | .argv = build_args.items, |
| 1427 | .cwd = tmp_dir_name, | |
| 1427 | 1428 | .env_map = &env_map, |
| 1428 | 1429 | .max_output_bytes = max_doc_file_size, |
| 1429 | 1430 | }); |
lib/docs/main.js+21-6| ... | ... | @@ -2198,13 +2198,26 @@ const NAV_MODES = { |
| 2198 | 2198 | if (opts.addParensIfFnSignature && fnObj.src == 0) { |
| 2199 | 2199 | payloadHtml += "("; |
| 2200 | 2200 | } |
| 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 "; | |
| 2204 | 2206 | } |
| 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 | } | |
| 2207 | 2215 | } |
| 2216 | } | |
| 2217 | if (fnObj.has_lib_name) { | |
| 2218 | payloadHtml += '"' + fnObj.lib_name + '" '; | |
| 2219 | } | |
| 2220 | if (opts.wantHtml) { | |
| 2208 | 2221 | payloadHtml += '<span class="tok-kw">fn </span>'; |
| 2209 | 2222 | if (fnDecl) { |
| 2210 | 2223 | payloadHtml += '<span class="tok-fn">'; |
| ... | ... | @@ -2324,7 +2337,9 @@ const NAV_MODES = { |
| 2324 | 2337 | if (fnObj.has_cc) { |
| 2325 | 2338 | let cc = zigAnalysis.exprs[fnObj.cc]; |
| 2326 | 2339 | if (cc) { |
| 2327 | payloadHtml += "callconv(." + cc.enumLiteral + ") "; | |
| 2340 | if (cc.enumLiteral !== "Inline") { | |
| 2341 | payloadHtml += "callconv(" + exprName(cc, opts) + ") "; | |
| 2342 | } | |
| 2328 | 2343 | } |
| 2329 | 2344 | } |
| 2330 | 2345 |
lib/std/array_hash_map.zig+2-2| ... | ... | @@ -54,7 +54,7 @@ pub fn hashString(s: []const u8) u32 { |
| 54 | 54 | |
| 55 | 55 | /// Insertion order is preserved. |
| 56 | 56 | /// 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 | |
| 58 | 58 | /// the (well defined) behavior when mixing insertions and deletions with iteration. |
| 59 | 59 | /// For a hash map that can be initialized directly that does not store an Allocator |
| 60 | 60 | /// field, see `ArrayHashMapUnmanaged`. |
| ... | ... | @@ -448,7 +448,7 @@ pub fn ArrayHashMap( |
| 448 | 448 | /// General purpose hash table. |
| 449 | 449 | /// Insertion order is preserved. |
| 450 | 450 | /// 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 | |
| 452 | 452 | /// the (well defined) behavior when mixing insertions and deletions with iteration. |
| 453 | 453 | /// This type does not store an Allocator field - the Allocator must be passed in |
| 454 | 454 | /// 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 { |
| 31 | 31 | return struct { |
| 32 | 32 | const Self = @This(); |
| 33 | 33 | /// 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 | |
| 35 | 35 | /// operation explicitly either: (1) states otherwise or (2) lists the |
| 36 | 36 | /// invalidated pointers. |
| 37 | 37 | /// |
| ... | ... | @@ -527,7 +527,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 527 | 527 | return struct { |
| 528 | 528 | const Self = @This(); |
| 529 | 529 | /// 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 | |
| 531 | 531 | /// operation explicitly either: (1) states otherwise or (2) lists the |
| 532 | 532 | /// invalidated pointers. |
| 533 | 533 | /// |
lib/std/buf_map.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ const mem = std.mem; |
| 4 | 4 | const Allocator = mem.Allocator; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | |
| 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 | |
| 8 | 8 | /// frees them when they get removed. |
| 9 | 9 | pub const BufMap = struct { |
| 10 | 10 | hash_map: BufMapHashMap, |
lib/std/c/freebsd.zig+13| ... | ... | @@ -13,6 +13,19 @@ pub const cpulevel_t = c_int; |
| 13 | 13 | pub const cpuwhich_t = c_int; |
| 14 | 14 | pub const id_t = i64; |
| 15 | 15 | |
| 16 | pub const CPU_LEVEL_ROOT: cpulevel_t = 1; | |
| 17 | pub const CPU_LEVEL_CPUSET: cpulevel_t = 2; | |
| 18 | pub const CPU_LEVEL_WHICH: cpulevel_t = 3; | |
| 19 | pub const CPU_WHICH_TID: cpuwhich_t = 1; | |
| 20 | pub const CPU_WHICH_PID: cpuwhich_t = 2; | |
| 21 | pub const CPU_WHICH_CPUSET: cpuwhich_t = 3; | |
| 22 | pub const CPU_WHICH_IRQ: cpuwhich_t = 4; | |
| 23 | pub const CPU_WHICH_JAIL: cpuwhich_t = 5; | |
| 24 | pub const CPU_WHICH_DOMAIN: cpuwhich_t = 6; | |
| 25 | pub const CPU_WHICH_INTRHANDLER: cpuwhich_t = 7; | |
| 26 | pub const CPU_WHICH_ITHREAD: cpuwhich_t = 8; | |
| 27 | pub const CPU_WHICH_TIDPID: cpuwhich_t = 8; | |
| 28 | ||
| 16 | 29 | extern "c" fn __error() *c_int; |
| 17 | 30 | pub const _errno = __error; |
| 18 | 31 |
lib/std/c/netbsd.zig+12| ... | ... | @@ -121,9 +121,21 @@ pub const pthread_attr_t = extern struct { |
| 121 | 121 | }; |
| 122 | 122 | |
| 123 | 123 | pub const sem_t = ?*opaque {}; |
| 124 | pub const cpuset_t = opaque {}; | |
| 125 | pub const cpuid_t = c_ulong; | |
| 124 | 126 | |
| 125 | 127 | pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*anyopaque) E; |
| 126 | 128 | pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E; |
| 129 | pub extern "c" fn pthread_setaffinity_np(thread: std.c.pthread_t, size: usize, set: ?*cpuset_t) c_int; | |
| 130 | pub extern "c" fn pthread_getaffinity_np(thread: std.c.pthread_t, size: usize, set: ?*cpuset_t) c_int; | |
| 131 | ||
| 132 | pub extern "c" fn cpuset_create() ?*cpuset_t; | |
| 133 | pub extern "c" fn cpuset_destroy(set: ?*cpuset_t) void; | |
| 134 | pub extern "c" fn cpuset_zero(set: ?*cpuset_t) void; | |
| 135 | pub extern "c" fn cpuset_set(cpu: cpuid_t, set: ?*cpuset_t) c_int; | |
| 136 | pub extern "c" fn cpuset_clr(cpu: cpuid_t, set: ?*cpuset_t) c_int; | |
| 137 | pub extern "c" fn cpuset_isset(cpu: cpuid_t, set: ?*const cpuset_t) c_int; | |
| 138 | pub extern "c" fn cpuset_size(set: ?*cpuset_t) usize; | |
| 127 | 139 | |
| 128 | 140 | pub const blkcnt_t = i64; |
| 129 | 141 | pub 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 { |
| 50 | 50 | }; |
| 51 | 51 | |
| 52 | 52 | return struct { |
| 53 | /// Array of `struct { key: []const u8, value: V }` where `value` is `void{}` if `V` is `void`. | |
| 54 | /// Sorted by `key` length. | |
| 53 | 55 | pub const kvs = precomputed.sorted_kvs; |
| 54 | 56 | |
| 57 | /// Checks if the map has a value for the key. | |
| 55 | 58 | pub fn has(str: []const u8) bool { |
| 56 | 59 | return get(str) != null; |
| 57 | 60 | } |
| 58 | 61 | |
| 62 | /// Returns the value for the key if any, else null. | |
| 59 | 63 | pub fn get(str: []const u8) ?V { |
| 60 | 64 | if (str.len < precomputed.min_len or str.len > precomputed.max_len) |
| 61 | 65 | return null; |
lib/std/hash_map.zig+1-1| ... | ... | @@ -350,7 +350,7 @@ pub fn verifyContext( |
| 350 | 350 | /// General purpose hash table. |
| 351 | 351 | /// No order is guaranteed and any modification invalidates live iterators. |
| 352 | 352 | /// 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. | |
| 354 | 354 | /// For a hash map that can be initialized directly that does not store an Allocator |
| 355 | 355 | /// field, see `HashMapUnmanaged`. |
| 356 | 356 | /// 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 |
| 16 | 16 | pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator; |
| 17 | 17 | pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; |
| 18 | 18 | pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator; |
| 19 | pub const Check = @import("heap/general_purpose_allocator.zig").Check; | |
| 19 | 20 | pub const WasmAllocator = @import("heap/WasmAllocator.zig"); |
| 20 | 21 | pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); |
| 21 | 22 | pub const PageAllocator = @import("heap/PageAllocator.zig"); |
lib/std/heap/general_purpose_allocator.zig+23-21| ... | ... | @@ -155,6 +155,8 @@ pub const Config = struct { |
| 155 | 155 | verbose_log: bool = false, |
| 156 | 156 | }; |
| 157 | 157 | |
| 158 | pub const Check = enum { ok, leak }; | |
| 159 | ||
| 158 | 160 | pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 159 | 161 | return struct { |
| 160 | 162 | backing_allocator: Allocator = std.heap.page_allocator, |
| ... | ... | @@ -431,7 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 431 | 433 | } else struct {}; |
| 432 | 434 | |
| 433 | 435 | /// Returns true if there were leaks; false otherwise. |
| 434 | pub fn deinit(self: *Self) bool { | |
| 436 | pub fn deinit(self: *Self) Check { | |
| 435 | 437 | const leaks = if (config.safety) self.detectLeaks() else false; |
| 436 | 438 | if (config.retain_metadata) { |
| 437 | 439 | self.freeRetainedMetadata(); |
| ... | ... | @@ -441,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 441 | 443 | self.small_allocations.deinit(self.backing_allocator); |
| 442 | 444 | } |
| 443 | 445 | self.* = undefined; |
| 444 | return leaks; | |
| 446 | return @intToEnum(Check, @boolToInt(leaks)); | |
| 445 | 447 | } |
| 446 | 448 | |
| 447 | 449 | fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void { |
| ... | ... | @@ -1024,7 +1026,7 @@ const test_config = Config{}; |
| 1024 | 1026 | |
| 1025 | 1027 | test "small allocations - free in same order" { |
| 1026 | 1028 | 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"); | |
| 1028 | 1030 | const allocator = gpa.allocator(); |
| 1029 | 1031 | |
| 1030 | 1032 | var list = std.ArrayList(*u64).init(std.testing.allocator); |
| ... | ... | @@ -1043,7 +1045,7 @@ test "small allocations - free in same order" { |
| 1043 | 1045 | |
| 1044 | 1046 | test "small allocations - free in reverse order" { |
| 1045 | 1047 | 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"); | |
| 1047 | 1049 | const allocator = gpa.allocator(); |
| 1048 | 1050 | |
| 1049 | 1051 | var list = std.ArrayList(*u64).init(std.testing.allocator); |
| ... | ... | @@ -1062,7 +1064,7 @@ test "small allocations - free in reverse order" { |
| 1062 | 1064 | |
| 1063 | 1065 | test "large allocations" { |
| 1064 | 1066 | 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"); | |
| 1066 | 1068 | const allocator = gpa.allocator(); |
| 1067 | 1069 | |
| 1068 | 1070 | const ptr1 = try allocator.alloc(u64, 42768); |
| ... | ... | @@ -1075,7 +1077,7 @@ test "large allocations" { |
| 1075 | 1077 | |
| 1076 | 1078 | test "very large allocation" { |
| 1077 | 1079 | 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"); | |
| 1079 | 1081 | const allocator = gpa.allocator(); |
| 1080 | 1082 | |
| 1081 | 1083 | try std.testing.expectError(error.OutOfMemory, allocator.alloc(u8, math.maxInt(usize))); |
| ... | ... | @@ -1083,7 +1085,7 @@ test "very large allocation" { |
| 1083 | 1085 | |
| 1084 | 1086 | test "realloc" { |
| 1085 | 1087 | 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"); | |
| 1087 | 1089 | const allocator = gpa.allocator(); |
| 1088 | 1090 | |
| 1089 | 1091 | var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1); |
| ... | ... | @@ -1105,7 +1107,7 @@ test "realloc" { |
| 1105 | 1107 | |
| 1106 | 1108 | test "shrink" { |
| 1107 | 1109 | 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"); | |
| 1109 | 1111 | const allocator = gpa.allocator(); |
| 1110 | 1112 | |
| 1111 | 1113 | var slice = try allocator.alloc(u8, 20); |
| ... | ... | @@ -1130,7 +1132,7 @@ test "shrink" { |
| 1130 | 1132 | |
| 1131 | 1133 | test "large object - grow" { |
| 1132 | 1134 | 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"); | |
| 1134 | 1136 | const allocator = gpa.allocator(); |
| 1135 | 1137 | |
| 1136 | 1138 | var slice1 = try allocator.alloc(u8, page_size * 2 - 20); |
| ... | ... | @@ -1148,7 +1150,7 @@ test "large object - grow" { |
| 1148 | 1150 | |
| 1149 | 1151 | test "realloc small object to large object" { |
| 1150 | 1152 | 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"); | |
| 1152 | 1154 | const allocator = gpa.allocator(); |
| 1153 | 1155 | |
| 1154 | 1156 | var slice = try allocator.alloc(u8, 70); |
| ... | ... | @@ -1165,7 +1167,7 @@ test "realloc small object to large object" { |
| 1165 | 1167 | |
| 1166 | 1168 | test "shrink large object to large object" { |
| 1167 | 1169 | 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"); | |
| 1169 | 1171 | const allocator = gpa.allocator(); |
| 1170 | 1172 | |
| 1171 | 1173 | var slice = try allocator.alloc(u8, page_size * 2 + 50); |
| ... | ... | @@ -1190,7 +1192,7 @@ test "shrink large object to large object" { |
| 1190 | 1192 | |
| 1191 | 1193 | test "shrink large object to large object with larger alignment" { |
| 1192 | 1194 | 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"); | |
| 1194 | 1196 | const allocator = gpa.allocator(); |
| 1195 | 1197 | |
| 1196 | 1198 | var debug_buffer: [1000]u8 = undefined; |
| ... | ... | @@ -1226,7 +1228,7 @@ test "shrink large object to large object with larger alignment" { |
| 1226 | 1228 | |
| 1227 | 1229 | test "realloc large object to small object" { |
| 1228 | 1230 | 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"); | |
| 1230 | 1232 | const allocator = gpa.allocator(); |
| 1231 | 1233 | |
| 1232 | 1234 | var slice = try allocator.alloc(u8, page_size * 2 + 50); |
| ... | ... | @@ -1244,7 +1246,7 @@ test "overrideable mutexes" { |
| 1244 | 1246 | .backing_allocator = std.testing.allocator, |
| 1245 | 1247 | .mutex = std.Thread.Mutex{}, |
| 1246 | 1248 | }; |
| 1247 | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); | |
| 1249 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | |
| 1248 | 1250 | const allocator = gpa.allocator(); |
| 1249 | 1251 | |
| 1250 | 1252 | const ptr = try allocator.create(i32); |
| ... | ... | @@ -1253,7 +1255,7 @@ test "overrideable mutexes" { |
| 1253 | 1255 | |
| 1254 | 1256 | test "non-page-allocator backing allocator" { |
| 1255 | 1257 | 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"); | |
| 1257 | 1259 | const allocator = gpa.allocator(); |
| 1258 | 1260 | |
| 1259 | 1261 | const ptr = try allocator.create(i32); |
| ... | ... | @@ -1262,7 +1264,7 @@ test "non-page-allocator backing allocator" { |
| 1262 | 1264 | |
| 1263 | 1265 | test "realloc large object to larger alignment" { |
| 1264 | 1266 | 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"); | |
| 1266 | 1268 | const allocator = gpa.allocator(); |
| 1267 | 1269 | |
| 1268 | 1270 | var debug_buffer: [1000]u8 = undefined; |
| ... | ... | @@ -1304,7 +1306,7 @@ test "realloc large object to larger alignment" { |
| 1304 | 1306 | test "large object shrinks to small but allocation fails during shrink" { |
| 1305 | 1307 | var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3); |
| 1306 | 1308 | 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"); | |
| 1308 | 1310 | const allocator = gpa.allocator(); |
| 1309 | 1311 | |
| 1310 | 1312 | 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" { |
| 1322 | 1324 | |
| 1323 | 1325 | test "objects of size 1024 and 2048" { |
| 1324 | 1326 | 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"); | |
| 1326 | 1328 | const allocator = gpa.allocator(); |
| 1327 | 1329 | |
| 1328 | 1330 | const slice = try allocator.alloc(u8, 1025); |
| ... | ... | @@ -1334,7 +1336,7 @@ test "objects of size 1024 and 2048" { |
| 1334 | 1336 | |
| 1335 | 1337 | test "setting a memory cap" { |
| 1336 | 1338 | 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"); | |
| 1338 | 1340 | const allocator = gpa.allocator(); |
| 1339 | 1341 | |
| 1340 | 1342 | gpa.setRequestedMemoryLimit(1010); |
| ... | ... | @@ -1361,11 +1363,11 @@ test "setting a memory cap" { |
| 1361 | 1363 | test "double frees" { |
| 1362 | 1364 | // use a GPA to back a GPA to check for leaks of the latter's metadata |
| 1363 | 1365 | 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"); | |
| 1365 | 1367 | |
| 1366 | 1368 | const GPA = GeneralPurposeAllocator(.{ .safety = true, .never_unmap = true, .retain_metadata = true }); |
| 1367 | 1369 | 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"); | |
| 1369 | 1371 | const allocator = gpa.allocator(); |
| 1370 | 1372 | |
| 1371 | 1373 | // 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; |
| 141 | 141 | pub const blkcnt_t = system.blkcnt_t; |
| 142 | 142 | pub const blksize_t = system.blksize_t; |
| 143 | 143 | pub const clock_t = system.clock_t; |
| 144 | pub const cpu_set_t = system.cpu_set_t; | |
| 144 | pub const cpu_set_t = if (builtin.os.tag == .linux) | |
| 145 | system.cpu_set_t | |
| 146 | else if (builtin.os.tag == .freebsd) | |
| 147 | freebsd.cpuset_t | |
| 148 | else | |
| 149 | u32; | |
| 145 | 150 | pub const dev_t = system.dev_t; |
| 146 | 151 | pub const dl_phdr_info = system.dl_phdr_info; |
| 147 | 152 | pub const empty_sigset = system.empty_sigset; |
| ... | ... | @@ -5518,13 +5523,27 @@ pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; |
| 5518 | 5523 | |
| 5519 | 5524 | pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { |
| 5520 | 5525 | 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"); | |
| 5528 | 5547 | } |
| 5529 | 5548 | } |
| 5530 | 5549 |
lib/std/os/linux/seccomp.zig+2-2| ... | ... | @@ -29,8 +29,8 @@ |
| 29 | 29 | //! which is dependant on the ABI. Since BPF programs execute in a 32-bit |
| 30 | 30 | //! machine, validation of 64-bit arguments necessitates two load-and-compare |
| 31 | 31 | //! 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 | |
| 34 | 34 | //! compiled on a little-endian machine will not work on a big-endian one, |
| 35 | 35 | //! and vice-versa. For example: Checking the upper 32-bits of `data.arg1` |
| 36 | 36 | //! 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 { |
| 182 | 182 | |
| 183 | 183 | /// Creates a bit-packed array of `Int`. Non-byte-multiple integers |
| 184 | 184 | /// 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 | |
| 186 | 186 | /// meta data. PackedArray(i3, 8) will occupy exactly 3 bytes |
| 187 | 187 | /// of memory. |
| 188 | 188 | pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type { |
| ... | ... | @@ -261,7 +261,7 @@ pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: Endian, comptim |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | 263 | /// 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 | |
| 265 | 265 | /// within the array's `Int`'s total bits. |
| 266 | 266 | pub fn sliceCastEndian(self: *Self, comptime NewInt: type, comptime new_endian: Endian) PackedIntSliceEndian(NewInt, new_endian) { |
| 267 | 267 | 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 { |
| 336 | 336 | } |
| 337 | 337 | |
| 338 | 338 | /// 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 | |
| 340 | 340 | /// within the slice's `Int`'s total bits. |
| 341 | 341 | pub fn sliceCastEndian(self: Self, comptime NewInt: type, comptime new_endian: Endian) PackedIntSliceEndian(NewInt, new_endian) { |
| 342 | 342 | 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 { |
| 1162 | 1162 | .linux => { |
| 1163 | 1163 | return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory; |
| 1164 | 1164 | }, |
| 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 | }, | |
| 1165 | 1174 | .windows => { |
| 1166 | 1175 | var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined; |
| 1167 | 1176 | const rc = std.os.windows.ntdll.NtQuerySystemInformation( |
lib/test_runner.zig+3-3| ... | ... | @@ -56,7 +56,7 @@ fn mainServer() !void { |
| 56 | 56 | }, |
| 57 | 57 | .query_test_metadata => { |
| 58 | 58 | std.testing.allocator_instance = .{}; |
| 59 | defer if (std.testing.allocator_instance.deinit()) { | |
| 59 | defer if (std.testing.allocator_instance.deinit() == .leak) { | |
| 60 | 60 | @panic("internal test runner memory leak"); |
| 61 | 61 | }; |
| 62 | 62 | |
| ... | ... | @@ -108,7 +108,7 @@ fn mainServer() !void { |
| 108 | 108 | } |
| 109 | 109 | }, |
| 110 | 110 | }; |
| 111 | leak = std.testing.allocator_instance.deinit(); | |
| 111 | leak = std.testing.allocator_instance.deinit() == .leak; | |
| 112 | 112 | try server.serveTestResults(.{ |
| 113 | 113 | .index = index, |
| 114 | 114 | .flags = .{ |
| ... | ... | @@ -148,7 +148,7 @@ fn mainTerminal() void { |
| 148 | 148 | for (test_fn_list, 0..) |test_fn, i| { |
| 149 | 149 | std.testing.allocator_instance = .{}; |
| 150 | 150 | defer { |
| 151 | if (std.testing.allocator_instance.deinit()) { | |
| 151 | if (std.testing.allocator_instance.deinit() == .leak) { | |
| 152 | 152 | leaks += 1; |
| 153 | 153 | } |
| 154 | 154 | } |
src/Autodoc.zig+26-18| ... | ... | @@ -112,7 +112,7 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 112 | 112 | .ComptimeExpr = .{ .name = "ComptimeExpr" }, |
| 113 | 113 | }); |
| 114 | 114 | |
| 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 | |
| 116 | 116 | var i: u32 = 1; |
| 117 | 117 | while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) { |
| 118 | 118 | var tmpbuf = std.ArrayList(u8).init(self.arena); |
| ... | ... | @@ -196,8 +196,10 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 196 | 196 | .anyerror_type => .{ |
| 197 | 197 | .ErrorSet = .{ .name = try tmpbuf.toOwnedSlice() }, |
| 198 | 198 | }, |
| 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() }, | |
| 201 | 203 | }, |
| 202 | 204 | }, |
| 203 | 205 | ); |
| ... | ... | @@ -4010,17 +4012,27 @@ fn analyzeFancyFunction( |
| 4010 | 4012 | } |
| 4011 | 4013 | |
| 4012 | 4014 | 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) { | |
| 4014 | 4016 | 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 | ||
| 4015 | 4019 | 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 | ||
| 4017 | 4022 | extra_index += 1; |
| 4018 | 4023 | } else if (extra.data.bits.has_cc_body) { |
| 4019 | 4024 | const cc_body_len = file.zir.extra[extra_index]; |
| 4020 | 4025 | 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 | ||
| 4024 | 4036 | extra_index += cc_body_len; |
| 4025 | 4037 | } else { |
| 4026 | 4038 | // auto calling convention |
| ... | ... | @@ -4565,26 +4577,22 @@ fn walkRef( |
| 4565 | 4577 | .expr = .{ .int = .{ .value = 1 } }, |
| 4566 | 4578 | }; |
| 4567 | 4579 | }, |
| 4568 | // TODO: dunno what to do with those | |
| 4569 | 4580 | .calling_convention_type => { |
| 4570 | 4581 | 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) }, | |
| 4574 | 4584 | }; |
| 4575 | 4585 | }, |
| 4576 | 4586 | .calling_convention_c => { |
| 4577 | 4587 | 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" }, | |
| 4581 | 4590 | }; |
| 4582 | 4591 | }, |
| 4583 | 4592 | .calling_convention_inline => { |
| 4584 | 4593 | 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" }, | |
| 4588 | 4596 | }; |
| 4589 | 4597 | }, |
| 4590 | 4598 | // .generic_poison => { |
src/arch/aarch64/CodeGen.zig+1| ... | ... | @@ -4290,6 +4290,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4290 | 4290 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4291 | 4291 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4292 | 4292 | const atom = elf_file.getAtom(atom_index); |
| 4293 | _ = try atom.getOrCreateOffsetTableEntry(elf_file); | |
| 4293 | 4294 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 4294 | 4295 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); |
| 4295 | 4296 | } 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 |
| 4270 | 4270 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4271 | 4271 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4272 | 4272 | const atom = elf_file.getAtom(atom_index); |
| 4273 | _ = try atom.getOrCreateOffsetTableEntry(elf_file); | |
| 4273 | 4274 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 4274 | 4275 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); |
| 4275 | 4276 | } 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 |
| 1734 | 1734 | const func = func_payload.data; |
| 1735 | 1735 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 1736 | 1736 | const atom = elf_file.getAtom(atom_index); |
| 1737 | _ = try atom.getOrCreateOffsetTableEntry(elf_file); | |
| 1737 | 1738 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 1738 | 1739 | try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); |
| 1739 | 1740 | _ = 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 |
| 1254 | 1254 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { |
| 1255 | 1255 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 1256 | 1256 | const atom = elf_file.getAtom(atom_index); |
| 1257 | _ = try atom.getOrCreateOffsetTableEntry(elf_file); | |
| 1257 | 1258 | break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 1258 | 1259 | } else unreachable; |
| 1259 | 1260 |
src/arch/x86_64/CodeGen.zig+10-4| ... | ... | @@ -5624,7 +5624,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 5624 | 5624 | |
| 5625 | 5625 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 5626 | 5626 | 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); | |
| 5628 | 5630 | try self.asmMemory(.call, Memory.sib(.qword, .{ |
| 5629 | 5631 | .base = .ds, |
| 5630 | 5632 | .disp = @intCast(i32, got_addr), |
| ... | ... | @@ -5853,7 +5855,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { |
| 5853 | 5855 | .{ .kind = .const_data, .ty = Type.anyerror }, |
| 5854 | 5856 | 4, // dword alignment |
| 5855 | 5857 | ); |
| 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); | |
| 5857 | 5861 | try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ |
| 5858 | 5862 | .base = .ds, |
| 5859 | 5863 | .disp = @intCast(i32, got_addr), |
| ... | ... | @@ -7574,7 +7578,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 7574 | 7578 | const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl); |
| 7575 | 7579 | if (self.bin_file.cast(link.File.MachO)) |_| { |
| 7576 | 7580 | _ = try self.addInst(.{ |
| 7577 | .tag = .mov_linker, | |
| 7581 | .tag = .lea_linker, | |
| 7578 | 7582 | .ops = .tlv_reloc, |
| 7579 | 7583 | .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{ |
| 7580 | 7584 | .reg = @enumToInt(Register.rdi), |
| ... | ... | @@ -8230,7 +8234,9 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { |
| 8230 | 8234 | .{ .kind = .const_data, .ty = Type.anyerror }, |
| 8231 | 8235 | 4, // dword alignment |
| 8232 | 8236 | ); |
| 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); | |
| 8234 | 8240 | try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ |
| 8235 | 8241 | .base = .ds, |
| 8236 | 8242 | .disp = @intCast(i32, got_addr), |
src/codegen.zig+1| ... | ... | @@ -1006,6 +1006,7 @@ fn genDeclRef( |
| 1006 | 1006 | if (bin_file.cast(link.File.Elf)) |elf_file| { |
| 1007 | 1007 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); |
| 1008 | 1008 | const atom = elf_file.getAtom(atom_index); |
| 1009 | _ = try atom.getOrCreateOffsetTableEntry(elf_file); | |
| 1009 | 1010 | return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) }); |
| 1010 | 1011 | } else if (bin_file.cast(link.File.MachO)) |macho_file| { |
| 1011 | 1012 | const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); |
src/link/Coff.zig+167-155| ... | ... | @@ -37,13 +37,14 @@ strtab_offset: ?u32 = null, |
| 37 | 37 | |
| 38 | 38 | temp_strtab: StringTable(.temp_strtab) = .{}, |
| 39 | 39 | |
| 40 | got_entries: std.ArrayListUnmanaged(Entry) = .{}, | |
| 41 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 42 | got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | |
| 40 | got_table: TableSection(SymbolWithLoc) = .{}, | |
| 43 | 41 | |
| 44 | 42 | /// A table of ImportTables partitioned by the library name. |
| 45 | 43 | /// Key is an offset into the interning string table `temp_strtab`. |
| 46 | 44 | import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{}, |
| 45 | ||
| 46 | got_table_count_dirty: bool = true, | |
| 47 | got_table_contents_dirty: bool = true, | |
| 47 | 48 | imports_count_dirty: bool = true, |
| 48 | 49 | |
| 49 | 50 | /// Virtual address of the entry point procedure relative to image base. |
| ... | ... | @@ -106,12 +107,6 @@ const HotUpdateState = struct { |
| 106 | 107 | loaded_base_address: ?std.os.windows.HMODULE = null, |
| 107 | 108 | }; |
| 108 | 109 | |
| 109 | const Entry = struct { | |
| 110 | target: SymbolWithLoc, | |
| 111 | // Index into the synthetic symbol table (i.e., file == null). | |
| 112 | sym_index: u32, | |
| 113 | }; | |
| 114 | ||
| 115 | 110 | const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); |
| 116 | 111 | const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); |
| 117 | 112 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); |
| ... | ... | @@ -188,7 +183,8 @@ pub const PtrWidth = enum { |
| 188 | 183 | p32, |
| 189 | 184 | p64, |
| 190 | 185 | |
| 191 | fn abiSize(pw: PtrWidth) u4 { | |
| 186 | /// Size in bytes. | |
| 187 | pub fn size(pw: PtrWidth) u4 { | |
| 192 | 188 | return switch (pw) { |
| 193 | 189 | .p32 => 4, |
| 194 | 190 | .p64 => 8, |
| ... | ... | @@ -310,9 +306,7 @@ pub fn deinit(self: *Coff) void { |
| 310 | 306 | self.globals_free_list.deinit(gpa); |
| 311 | 307 | self.strtab.deinit(gpa); |
| 312 | 308 | 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); | |
| 316 | 310 | |
| 317 | 311 | for (self.import_tables.values()) |*itab| { |
| 318 | 312 | itab.deinit(gpa); |
| ... | ... | @@ -371,7 +365,7 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 371 | 365 | } |
| 372 | 366 | |
| 373 | 367 | 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(); | |
| 375 | 369 | self.got_section_index = try self.allocateSection(".got", file_size, .{ |
| 376 | 370 | .CNT_INITIALIZED_DATA = 1, |
| 377 | 371 | .MEM_READ = 1, |
| ... | ... | @@ -396,7 +390,7 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 396 | 390 | } |
| 397 | 391 | |
| 398 | 392 | 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(); | |
| 400 | 394 | self.idata_section_index = try self.allocateSection(".idata", file_size, .{ |
| 401 | 395 | .CNT_INITIALIZED_DATA = 1, |
| 402 | 396 | .MEM_READ = 1, |
| ... | ... | @@ -498,8 +492,8 @@ fn growSection(self: *Coff, sect_id: u32, needed_size: u32) !void { |
| 498 | 492 | |
| 499 | 493 | const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address); |
| 500 | 494 | if (needed_size > sect_vm_capacity) { |
| 501 | try self.growSectionVirtualMemory(sect_id, needed_size); | |
| 502 | 495 | self.markRelocsDirtyByAddress(header.virtual_address + needed_size); |
| 496 | try self.growSectionVirtualMemory(sect_id, needed_size); | |
| 503 | 497 | } |
| 504 | 498 | |
| 505 | 499 | header.virtual_size = @max(header.virtual_size, needed_size); |
| ... | ... | @@ -698,26 +692,12 @@ fn allocateGlobal(self: *Coff) !u32 { |
| 698 | 692 | return index; |
| 699 | 693 | } |
| 700 | 694 | |
| 701 | pub 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; | |
| 695 | fn 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); | |
| 721 | 701 | } |
| 722 | 702 | |
| 723 | 703 | pub fn createAtom(self: *Coff) !Atom.Index { |
| ... | ... | @@ -737,37 +717,6 @@ pub fn createAtom(self: *Coff) !Atom.Index { |
| 737 | 717 | return atom_index; |
| 738 | 718 | } |
| 739 | 719 | |
| 740 | fn 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 | ||
| 771 | 720 | fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { |
| 772 | 721 | const atom = self.getAtom(atom_index); |
| 773 | 722 | const sym = atom.getSymbol(self); |
| ... | ... | @@ -873,17 +822,75 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: [] |
| 873 | 822 | if (amt != code.len) return error.InputOutput; |
| 874 | 823 | } |
| 875 | 824 | |
| 876 | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { | |
| 825 | fn 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 | ||
| 877 | 843 | switch (self.ptr_width) { |
| 878 | 844 | .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); | |
| 881 | 848 | }, |
| 882 | 849 | .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); | |
| 885 | 853 | }, |
| 886 | 854 | } |
| 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 | } | |
| 887 | 894 | } |
| 888 | 895 | |
| 889 | 896 | fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { |
| ... | ... | @@ -904,6 +911,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 904 | 911 | reloc.dirty = true; |
| 905 | 912 | } |
| 906 | 913 | } |
| 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 | } | |
| 907 | 923 | } |
| 908 | 924 | |
| 909 | 925 | fn 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 { |
| 994 | 1010 | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 995 | 1011 | |
| 996 | 1012 | // 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 }); | |
| 1008 | 1014 | |
| 1009 | 1015 | self.locals.items[sym_index].section_number = .UNDEFINED; |
| 1010 | 1016 | _ = self.atom_by_index_table.remove(sym_index); |
| ... | ... | @@ -1243,14 +1249,7 @@ fn updateLazySymbolAtom( |
| 1243 | 1249 | atom.size = code_len; |
| 1244 | 1250 | symbol.value = vaddr; |
| 1245 | 1251 | |
| 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 }); | |
| 1254 | 1253 | try self.writeAtom(atom_index, code); |
| 1255 | 1254 | } |
| 1256 | 1255 | |
| ... | ... | @@ -1321,6 +1320,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple |
| 1321 | 1320 | const decl_metadata = self.decls.get(decl_index).?; |
| 1322 | 1321 | const atom_index = decl_metadata.atom; |
| 1323 | 1322 | const atom = self.getAtom(atom_index); |
| 1323 | const sym_index = atom.getSymbolIndex().?; | |
| 1324 | 1324 | const sect_index = decl_metadata.section; |
| 1325 | 1325 | const code_len = @intCast(u32, code.len); |
| 1326 | 1326 | |
| ... | ... | @@ -1340,10 +1340,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple |
| 1340 | 1340 | if (vaddr != sym.value) { |
| 1341 | 1341 | sym.value = vaddr; |
| 1342 | 1342 | 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 }); | |
| 1347 | 1346 | } |
| 1348 | 1347 | } else if (code_len < atom.size) { |
| 1349 | 1348 | self.shrinkAtom(atom_index, code_len); |
| ... | ... | @@ -1361,15 +1360,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple |
| 1361 | 1360 | self.getAtomPtr(atom_index).size = code_len; |
| 1362 | 1361 | sym.value = vaddr; |
| 1363 | 1362 | |
| 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 }); | |
| 1370 | 1364 | } |
| 1371 | 1365 | |
| 1372 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); | |
| 1373 | 1366 | try self.writeAtom(atom_index, code); |
| 1374 | 1367 | } |
| 1375 | 1368 | |
| ... | ... | @@ -1651,6 +1644,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1651 | 1644 | try self.writeAtom(atom_index, code.items); |
| 1652 | 1645 | } |
| 1653 | 1646 | |
| 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 | ||
| 1654 | 1657 | try self.writeBaseRelocations(); |
| 1655 | 1658 | |
| 1656 | 1659 | if (self.getEntryPoint()) |entry_sym_loc| { |
| ... | ... | @@ -1739,48 +1742,82 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Dec |
| 1739 | 1742 | fn writeBaseRelocations(self: *Coff) !void { |
| 1740 | 1743 | const gpa = self.base.allocator; |
| 1741 | 1744 | |
| 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); | |
| 1743 | 1746 | defer { |
| 1744 | var it = pages.valueIterator(); | |
| 1747 | var it = page_table.valueIterator(); | |
| 1745 | 1748 | while (it.next()) |inner| { |
| 1746 | 1749 | inner.deinit(); |
| 1747 | 1750 | } |
| 1748 | pages.deinit(); | |
| 1751 | page_table.deinit(); | |
| 1749 | 1752 | } |
| 1750 | 1753 | |
| 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); | |
| 1758 | 1759 | 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 | }); | |
| 1764 | 1794 | } |
| 1765 | try gop.value_ptr.append(.{ | |
| 1766 | .offset = @intCast(u12, rva - page), | |
| 1767 | .type = .DIR64, | |
| 1768 | }); | |
| 1769 | 1795 | } |
| 1770 | 1796 | } |
| 1771 | 1797 | |
| 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 | ||
| 1772 | 1809 | var buffer = std.ArrayList(u8).init(gpa); |
| 1773 | 1810 | defer buffer.deinit(); |
| 1774 | 1811 | |
| 1775 | var pages_it = pages.iterator(); | |
| 1776 | while (pages_it.next()) |entry| { | |
| 1812 | for (pages.items) |page| { | |
| 1813 | const entries = page_table.getPtr(page).?; | |
| 1777 | 1814 | // Pad to required 4byte alignment |
| 1778 | 1815 | if (!mem.isAlignedGeneric( |
| 1779 | 1816 | usize, |
| 1780 | entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation), | |
| 1817 | entries.items.len * @sizeOf(coff.BaseRelocation), | |
| 1781 | 1818 | @sizeOf(u32), |
| 1782 | 1819 | )) { |
| 1783 | try entry.value_ptr.append(.{ | |
| 1820 | try entries.append(.{ | |
| 1784 | 1821 | .offset = 0, |
| 1785 | 1822 | .type = .ABSOLUTE, |
| 1786 | 1823 | }); |
| ... | ... | @@ -1788,14 +1825,14 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1788 | 1825 | |
| 1789 | 1826 | const block_size = @intCast( |
| 1790 | 1827 | u32, |
| 1791 | entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry), | |
| 1828 | entries.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry), | |
| 1792 | 1829 | ); |
| 1793 | 1830 | try buffer.ensureUnusedCapacity(block_size); |
| 1794 | 1831 | buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{ |
| 1795 | .page_rva = entry.key_ptr.*, | |
| 1832 | .page_rva = page, | |
| 1796 | 1833 | .block_size = block_size, |
| 1797 | 1834 | })); |
| 1798 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entry.value_ptr.items)); | |
| 1835 | buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entries.items)); | |
| 1799 | 1836 | } |
| 1800 | 1837 | |
| 1801 | 1838 | 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 |
| 2315 | 2352 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 2316 | 2353 | } |
| 2317 | 2354 | |
| 2318 | /// Returns GOT atom that references `sym_loc` if one exists. | |
| 2319 | /// Returns null otherwise. | |
| 2320 | pub 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 | ||
| 2326 | 2355 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
| 2327 | 2356 | if (name.len <= 8) { |
| 2328 | 2357 | mem.copy(u8, &header.name, name); |
| ... | ... | @@ -2410,25 +2439,7 @@ fn logSymtab(self: *Coff) void { |
| 2410 | 2439 | } |
| 2411 | 2440 | |
| 2412 | 2441 | 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}); | |
| 2432 | 2443 | } |
| 2433 | 2444 | |
| 2434 | 2445 | fn logSections(self: *Coff) void { |
| ... | ... | @@ -2484,6 +2495,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 2484 | 2495 | const Module = @import("../Module.zig"); |
| 2485 | 2496 | const Object = @import("Coff/Object.zig"); |
| 2486 | 2497 | const Relocation = @import("Coff/Relocation.zig"); |
| 2498 | const TableSection = @import("table_section.zig").TableSection; | |
| 2487 | 2499 | const StringTable = @import("strtab.zig").StringTable; |
| 2488 | 2500 | const Type = @import("../type.zig").Type; |
| 2489 | 2501 | const TypedValue = @import("../TypedValue.zig"); |
src/link/Coff/Relocation.zig+9-9| ... | ... | @@ -48,17 +48,16 @@ dirty: bool = true, |
| 48 | 48 | /// Returns address of the target if any. |
| 49 | 49 | pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 { |
| 50 | 50 | 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; | |
| 58 | 58 | const target_atom = coff_file.getAtom(target_atom_index); |
| 59 | 59 | return target_atom.getSymbol(coff_file).value; |
| 60 | 60 | }, |
| 61 | ||
| 62 | 61 | .import, .import_page, .import_pageoff => { |
| 63 | 62 | const sym = coff_file.getSymbol(self.target); |
| 64 | 63 | 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 { |
| 74 | 73 | |
| 75 | 74 | /// Returns true if and only if the reloc is dirty AND the target address is available. |
| 76 | 75 | pub 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; | |
| 78 | 78 | return self.dirty; |
| 79 | 79 | } |
| 80 | 80 |
src/link/Elf.zig+25-46| ... | ... | @@ -30,6 +30,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 30 | 30 | const Module = @import("../Module.zig"); |
| 31 | 31 | const Package = @import("../Package.zig"); |
| 32 | 32 | const StringTable = @import("strtab.zig").StringTable; |
| 33 | const TableSection = @import("table_section.zig").TableSection; | |
| 33 | 34 | const Type = @import("../type.zig").Type; |
| 34 | 35 | const TypedValue = @import("../TypedValue.zig"); |
| 35 | 36 | const Value = @import("../value.zig").Value; |
| ... | ... | @@ -148,17 +149,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, |
| 148 | 149 | |
| 149 | 150 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 150 | 151 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 151 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 152 | 152 | |
| 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. | |
| 156 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | |
| 153 | got_table: TableSection(u32) = .{}, | |
| 157 | 154 | |
| 158 | 155 | phdr_table_dirty: bool = false, |
| 159 | 156 | shdr_table_dirty: bool = false, |
| 160 | 157 | shstrtab_dirty: bool = false, |
| 161 | offset_table_count_dirty: bool = false, | |
| 158 | got_table_count_dirty: bool = false, | |
| 162 | 159 | |
| 163 | 160 | debug_strtab_dirty: bool = false, |
| 164 | 161 | debug_abbrev_section_dirty: bool = false, |
| ... | ... | @@ -329,8 +326,7 @@ pub fn deinit(self: *Elf) void { |
| 329 | 326 | self.global_symbols.deinit(gpa); |
| 330 | 327 | self.global_symbol_free_list.deinit(gpa); |
| 331 | 328 | 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); | |
| 334 | 330 | |
| 335 | 331 | { |
| 336 | 332 | var it = self.decls.iterator(); |
| ... | ... | @@ -1289,6 +1285,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1289 | 1285 | assert(!self.shdr_table_dirty); |
| 1290 | 1286 | assert(!self.shstrtab_dirty); |
| 1291 | 1287 | assert(!self.debug_strtab_dirty); |
| 1288 | assert(!self.got_table_count_dirty); | |
| 1292 | 1289 | } |
| 1293 | 1290 | |
| 1294 | 1291 | fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | ... | @@ -2168,7 +2165,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { |
| 2168 | 2165 | _ = self.atom_by_index_table.remove(local_sym_index); |
| 2169 | 2166 | self.getAtomPtr(atom_index).local_sym_index = 0; |
| 2170 | 2167 | |
| 2171 | self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {}; | |
| 2168 | self.got_table.freeEntry(gpa, local_sym_index); | |
| 2172 | 2169 | } |
| 2173 | 2170 | |
| 2174 | 2171 | fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { |
| ... | ... | @@ -2191,11 +2188,9 @@ pub fn createAtom(self: *Elf) !Atom.Index { |
| 2191 | 2188 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); |
| 2192 | 2189 | const atom = try self.atoms.addOne(gpa); |
| 2193 | 2190 | const local_sym_index = try self.allocateLocalSymbol(); |
| 2194 | const offset_table_index = try self.allocateGotOffset(); | |
| 2195 | 2191 | try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index); |
| 2196 | 2192 | atom.* = .{ |
| 2197 | 2193 | .local_sym_index = local_sym_index, |
| 2198 | .offset_table_index = offset_table_index, | |
| 2199 | 2194 | .prev_index = null, |
| 2200 | 2195 | .next_index = null, |
| 2201 | 2196 | }; |
| ... | ... | @@ -2352,26 +2347,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 { |
| 2352 | 2347 | return index; |
| 2353 | 2348 | } |
| 2354 | 2349 | |
| 2355 | pub 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 | ||
| 2375 | 2350 | fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { |
| 2376 | 2351 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 2377 | 2352 | for (unnamed_consts.items) |atom| { |
| ... | ... | @@ -2465,6 +2440,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2465 | 2440 | const decl_metadata = self.decls.get(decl_index).?; |
| 2466 | 2441 | const atom_index = decl_metadata.atom; |
| 2467 | 2442 | const atom = self.getAtom(atom_index); |
| 2443 | const local_sym_index = atom.getSymbolIndex().?; | |
| 2468 | 2444 | |
| 2469 | 2445 | const shdr_index = decl_metadata.shdr; |
| 2470 | 2446 | 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 |
| 2485 | 2461 | local_sym.st_value = vaddr; |
| 2486 | 2462 | |
| 2487 | 2463 | 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); | |
| 2490 | 2467 | } |
| 2491 | 2468 | } else if (code.len < local_sym.st_size) { |
| 2492 | 2469 | self.shrinkAtom(atom_index, code.len); |
| ... | ... | @@ -2494,7 +2471,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2494 | 2471 | local_sym.st_size = code.len; |
| 2495 | 2472 | |
| 2496 | 2473 | // 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); | |
| 2498 | 2475 | } else { |
| 2499 | 2476 | const local_sym = atom.getSymbolPtr(self); |
| 2500 | 2477 | local_sym.* = .{ |
| ... | ... | @@ -2509,12 +2486,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2509 | 2486 | errdefer self.freeAtom(atom_index); |
| 2510 | 2487 | log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); |
| 2511 | 2488 | |
| 2512 | self.offset_table.items[atom.offset_table_index] = vaddr; | |
| 2513 | 2489 | local_sym.st_value = vaddr; |
| 2514 | 2490 | local_sym.st_size = code.len; |
| 2515 | 2491 | |
| 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); | |
| 2518 | 2495 | } |
| 2519 | 2496 | |
| 2520 | 2497 | const local_sym = atom.getSymbolPtr(self); |
| ... | ... | @@ -2755,12 +2732,12 @@ fn updateLazySymbolAtom( |
| 2755 | 2732 | errdefer self.freeAtom(atom_index); |
| 2756 | 2733 | log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); |
| 2757 | 2734 | |
| 2758 | self.offset_table.items[atom.offset_table_index] = vaddr; | |
| 2759 | 2735 | local_sym.st_value = vaddr; |
| 2760 | 2736 | local_sym.st_size = code.len; |
| 2761 | 2737 | |
| 2762 | 2738 | 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); | |
| 2764 | 2741 | |
| 2765 | 2742 | const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; |
| 2766 | 2743 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; |
| ... | ... | @@ -2989,32 +2966,34 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2989 | 2966 | } |
| 2990 | 2967 | } |
| 2991 | 2968 | |
| 2992 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | |
| 2969 | fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void { | |
| 2993 | 2970 | 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; | |
| 2996 | 2973 | try self.growAllocSection(self.got_section_index.?, needed_size); |
| 2997 | self.offset_table_count_dirty = false; | |
| 2974 | self.got_table_count_dirty = false; | |
| 2998 | 2975 | } |
| 2999 | 2976 | const endian = self.base.options.target.cpu.arch.endian(); |
| 3000 | 2977 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; |
| 3001 | 2978 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 3002 | 2979 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| 3003 | 2980 | 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; | |
| 3004 | 2983 | switch (entry_size) { |
| 3005 | 2984 | 2 => { |
| 3006 | 2985 | 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); | |
| 3008 | 2987 | try self.base.file.?.pwriteAll(&buf, off); |
| 3009 | 2988 | }, |
| 3010 | 2989 | 4 => { |
| 3011 | 2990 | 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); | |
| 3013 | 2992 | try self.base.file.?.pwriteAll(&buf, off); |
| 3014 | 2993 | }, |
| 3015 | 2994 | 8 => { |
| 3016 | 2995 | var buf: [8]u8 = undefined; |
| 3017 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | |
| 2996 | mem.writeInt(u64, &buf, got_value, endian); | |
| 3018 | 2997 | try self.base.file.?.pwriteAll(&buf, off); |
| 3019 | 2998 | |
| 3020 | 2999 | if (self.base.child_pid) |pid| { |
src/link/Elf/Atom.zig+13-5| ... | ... | @@ -14,9 +14,6 @@ const Elf = @import("../Elf.zig"); |
| 14 | 14 | /// offset table entry. |
| 15 | 15 | local_sym_index: u32, |
| 16 | 16 | |
| 17 | /// This field is undefined for symbols with size = 0. | |
| 18 | offset_table_index: u32, | |
| 19 | ||
| 20 | 17 | /// Points to the previous and next neighbors, based on the `text_offset`. |
| 21 | 18 | /// This can be used to find, for example, the capacity of this `TextBlock`. |
| 22 | 19 | prev_index: ?Index, |
| ... | ... | @@ -48,13 +45,24 @@ pub fn getName(self: Atom, elf_file: *const Elf) []const u8 { |
| 48 | 45 | return elf_file.getSymbolName(self.getSymbolIndex().?); |
| 49 | 46 | } |
| 50 | 47 | |
| 48 | /// If entry already exists, returns index to it. | |
| 49 | /// Otherwise, creates a new entry in the Global Offset Table for this Atom. | |
| 50 | pub 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 | ||
| 51 | 58 | pub 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).?; | |
| 53 | 61 | const target = elf_file.base.options.target; |
| 54 | 62 | const ptr_bits = target.cpu.arch.ptrBitWidth(); |
| 55 | 63 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 56 | 64 | 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; | |
| 58 | 66 | } |
| 59 | 67 | |
| 60 | 68 | /// 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"); |
| 19 | 19 | const link = @import("../link.zig"); |
| 20 | 20 | const llvm_backend = @import("../codegen/llvm.zig"); |
| 21 | 21 | const load_commands = @import("MachO/load_commands.zig"); |
| 22 | const stubs = @import("MachO/stubs.zig"); | |
| 22 | 23 | const target_util = @import("../target.zig"); |
| 23 | 24 | const trace = @import("../tracy.zig").trace; |
| 24 | 25 | const zld = @import("MachO/zld.zig"); |
| ... | ... | @@ -41,6 +42,7 @@ const Md5 = std.crypto.hash.Md5; |
| 41 | 42 | const Module = @import("../Module.zig"); |
| 42 | 43 | const Relocation = @import("MachO/Relocation.zig"); |
| 43 | 44 | const StringTable = @import("strtab.zig").StringTable; |
| 45 | const TableSection = @import("table_section.zig").TableSection; | |
| 44 | 46 | const Trie = @import("MachO/Trie.zig"); |
| 45 | 47 | const Type = @import("../type.zig").Type; |
| 46 | 48 | const TypedValue = @import("../TypedValue.zig"); |
| ... | ... | @@ -150,17 +152,20 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 150 | 152 | |
| 151 | 153 | dyld_stub_binder_index: ?u32 = null, |
| 152 | 154 | dyld_private_atom_index: ?Atom.Index = null, |
| 153 | stub_helper_preamble_atom_index: ?Atom.Index = null, | |
| 154 | 155 | |
| 155 | 156 | strtab: StringTable(.strtab) = .{}, |
| 156 | 157 | |
| 157 | got_table: SectionTable = .{}, | |
| 158 | stubs_table: SectionTable = .{}, | |
| 159 | tlv_table: SectionTable = .{}, | |
| 158 | got_table: TableSection(SymbolWithLoc) = .{}, | |
| 159 | stub_table: TableSection(SymbolWithLoc) = .{}, | |
| 160 | 160 | |
| 161 | 161 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 162 | 162 | |
| 163 | 163 | segment_table_dirty: bool = false, |
| 164 | got_table_count_dirty: bool = false, | |
| 165 | got_table_contents_dirty: bool = false, | |
| 166 | stub_table_count_dirty: bool = false, | |
| 167 | stub_table_contents_dirty: bool = false, | |
| 168 | stub_helper_preamble_allocated: bool = false, | |
| 164 | 169 | |
| 165 | 170 | /// A helper var to indicate if we are at the start of the incremental updates, or |
| 166 | 171 | /// already somewhere further along the update-and-run chain. |
| ... | ... | @@ -210,17 +215,16 @@ rebases: RebaseTable = .{}, |
| 210 | 215 | /// this will be a table indexed by index into the list of Atoms. |
| 211 | 216 | bindings: BindingTable = .{}, |
| 212 | 217 | |
| 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. | |
| 216 | lazy_bindings: BindingTable = .{}, | |
| 217 | ||
| 218 | 218 | /// Table of tracked LazySymbols. |
| 219 | 219 | lazy_syms: LazySymbolTable = .{}, |
| 220 | 220 | |
| 221 | 221 | /// Table of tracked Decls. |
| 222 | 222 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, |
| 223 | 223 | |
| 224 | /// Table of threadlocal variables descriptors. | |
| 225 | /// They are emitted in the `__thread_vars` section. | |
| 226 | tlv_table: TlvSymbolTable = .{}, | |
| 227 | ||
| 224 | 228 | /// Hot-code swapping state. |
| 225 | 229 | hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, |
| 226 | 230 | |
| ... | ... | @@ -237,6 +241,8 @@ const LazySymbolMetadata = struct { |
| 237 | 241 | alignment: u32, |
| 238 | 242 | }; |
| 239 | 243 | |
| 244 | const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index); | |
| 245 | ||
| 240 | 246 | const DeclMetadata = struct { |
| 241 | 247 | atom: Atom.Index, |
| 242 | 248 | section: u8, |
| ... | ... | @@ -265,122 +271,6 @@ const DeclMetadata = struct { |
| 265 | 271 | } |
| 266 | 272 | }; |
| 267 | 273 | |
| 268 | const 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 | ||
| 384 | 274 | const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); |
| 385 | 275 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); |
| 386 | 276 | const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); |
| ... | ... | @@ -722,15 +612,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 722 | 612 | return error.UndefinedSymbolReference; |
| 723 | 613 | } |
| 724 | 614 | |
| 725 | try self.createDyldPrivateAtom(); | |
| 726 | try self.createStubHelperPreambleAtom(); | |
| 727 | ||
| 728 | 615 | for (actions.items) |action| switch (action.kind) { |
| 729 | 616 | .none => {}, |
| 730 | 617 | .add_got => try self.addGotEntry(action.target), |
| 731 | 618 | .add_stub => try self.addStubEntry(action.target), |
| 732 | 619 | }; |
| 733 | 620 | |
| 621 | try self.createDyldPrivateAtom(); | |
| 622 | try self.writeStubHelperPreamble(); | |
| 623 | ||
| 734 | 624 | try self.allocateSpecialSymbols(); |
| 735 | 625 | |
| 736 | 626 | for (self.relocs.keys()) |atom_index| { |
| ... | ... | @@ -756,6 +646,27 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 756 | 646 | try self.writeAtom(atom_index, code.items); |
| 757 | 647 | } |
| 758 | 648 | |
| 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 | ||
| 759 | 670 | if (build_options.enable_logging) { |
| 760 | 671 | self.logSymtab(); |
| 761 | 672 | self.logSections(); |
| ... | ... | @@ -1239,19 +1150,16 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { |
| 1239 | 1150 | } |
| 1240 | 1151 | } |
| 1241 | 1152 | |
| 1153 | Atom.resolveRelocations(self, atom_index, relocs.items, code); | |
| 1154 | ||
| 1242 | 1155 | 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| { | |
| 1249 | 1158 | log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)}); |
| 1250 | 1159 | }; |
| 1251 | 1160 | } |
| 1252 | 1161 | } |
| 1253 | 1162 | |
| 1254 | Atom.resolveRelocations(self, atom_index, relocs.items, code); | |
| 1255 | 1163 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1256 | 1164 | |
| 1257 | 1165 | // Now we can mark the relocs as resolved. |
| ... | ... | @@ -1260,7 +1168,7 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { |
| 1260 | 1168 | } |
| 1261 | 1169 | } |
| 1262 | 1170 | |
| 1263 | fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { | |
| 1171 | fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { | |
| 1264 | 1172 | const segment = self.segments.items[segment_index]; |
| 1265 | 1173 | const cpu_arch = self.base.options.target.cpu.arch; |
| 1266 | 1174 | const nwritten = if (!segment.isWriteable()) |
| ... | ... | @@ -1270,9 +1178,145 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: |
| 1270 | 1178 | if (nwritten != code.len) return error.InputOutput; |
| 1271 | 1179 | } |
| 1272 | 1180 | |
| 1273 | fn 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); | |
| 1181 | fn 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 | ||
| 1213 | fn 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 | ||
| 1242 | fn 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 | } | |
| 1276 | 1320 | } |
| 1277 | 1321 | |
| 1278 | 1322 | fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| ... | ... | @@ -1290,13 +1334,28 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { |
| 1290 | 1334 | log.debug("marking relocs dirty by address: {x}", .{addr}); |
| 1291 | 1335 | for (self.relocs.values()) |*relocs| { |
| 1292 | 1336 | 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; | |
| 1297 | 1339 | reloc.dirty = true; |
| 1298 | 1340 | } |
| 1299 | 1341 | } |
| 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 | } | |
| 1300 | 1359 | } |
| 1301 | 1360 | |
| 1302 | 1361 | pub fn allocateSpecialSymbols(self: *MachO) !void { |
| ... | ... | @@ -1335,40 +1394,6 @@ pub fn createAtom(self: *MachO) !Atom.Index { |
| 1335 | 1394 | return atom_index; |
| 1336 | 1395 | } |
| 1337 | 1396 | |
| 1338 | pub 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 | ||
| 1372 | 1397 | fn createDyldPrivateAtom(self: *MachO) !void { |
| 1373 | 1398 | if (self.dyld_private_atom_index != null) return; |
| 1374 | 1399 | |
| ... | ... | @@ -1383,339 +1408,17 @@ fn createDyldPrivateAtom(self: *MachO) !void { |
| 1383 | 1408 | |
| 1384 | 1409 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); |
| 1385 | 1410 | log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); |
| 1386 | try self.writePtrWidthAtom(atom_index); | |
| 1387 | } | |
| 1388 | ||
| 1389 | fn 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 | ||
| 1513 | fn 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 | ||
| 1594 | fn 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 | ||
| 1624 | fn 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); | |
| 1705 | 1413 | } |
| 1706 | 1414 | |
| 1707 | fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { | |
| 1415 | fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index { | |
| 1708 | 1416 | const gpa = self.base.allocator; |
| 1709 | 1417 | const size = 3 * @sizeOf(u64); |
| 1710 | 1418 | const required_alignment: u32 = 1; |
| 1711 | 1419 | const atom_index = try self.createAtom(); |
| 1712 | 1420 | self.getAtomPtr(atom_index).size = size; |
| 1713 | 1421 | |
| 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 | ||
| 1719 | 1422 | const sym = self.getAtom(atom_index).getSymbolPtr(self); |
| 1720 | 1423 | sym.n_type = macho.N_SECT; |
| 1721 | 1424 | sym.n_sect = self.thread_vars_section_index.? + 1; |
| ... | ... | @@ -1889,8 +1592,7 @@ pub fn deinit(self: *MachO) void { |
| 1889 | 1592 | } |
| 1890 | 1593 | |
| 1891 | 1594 | self.got_table.deinit(gpa); |
| 1892 | self.stubs_table.deinit(gpa); | |
| 1893 | self.tlv_table.deinit(gpa); | |
| 1595 | self.stub_table.deinit(gpa); | |
| 1894 | 1596 | self.strtab.deinit(gpa); |
| 1895 | 1597 | |
| 1896 | 1598 | self.locals.deinit(gpa); |
| ... | ... | @@ -1923,14 +1625,12 @@ pub fn deinit(self: *MachO) void { |
| 1923 | 1625 | |
| 1924 | 1626 | self.atoms.deinit(gpa); |
| 1925 | 1627 | |
| 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); | |
| 1933 | 1630 | } |
| 1631 | self.decls.deinit(gpa); | |
| 1632 | self.lazy_syms.deinit(gpa); | |
| 1633 | self.tlv_table.deinit(gpa); | |
| 1934 | 1634 | |
| 1935 | 1635 | for (self.unnamed_const_atoms.values()) |*atoms| { |
| 1936 | 1636 | atoms.deinit(gpa); |
| ... | ... | @@ -1953,11 +1653,6 @@ pub fn deinit(self: *MachO) void { |
| 1953 | 1653 | bindings.deinit(gpa); |
| 1954 | 1654 | } |
| 1955 | 1655 | self.bindings.deinit(gpa); |
| 1956 | ||
| 1957 | for (self.lazy_bindings.values()) |*bindings| { | |
| 1958 | bindings.deinit(gpa); | |
| 1959 | } | |
| 1960 | self.lazy_bindings.deinit(gpa); | |
| 1961 | 1656 | } |
| 1962 | 1657 | |
| 1963 | 1658 | fn freeAtom(self: *MachO, atom_index: Atom.Index) void { |
| ... | ... | @@ -2104,31 +1799,16 @@ fn allocateGlobal(self: *MachO) !u32 { |
| 2104 | 1799 | fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { |
| 2105 | 1800 | if (self.got_table.lookup.contains(target)) return; |
| 2106 | 1801 | 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; | |
| 2110 | 1804 | self.markRelocsDirtyByTarget(target); |
| 2111 | 1805 | } |
| 2112 | 1806 | |
| 2113 | 1807 | fn 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 | ||
| 2126 | fn 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; | |
| 2132 | 1812 | self.markRelocsDirtyByTarget(target); |
| 2133 | 1813 | } |
| 2134 | 1814 | |
| ... | ... | @@ -2278,6 +1958,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2278 | 1958 | } |
| 2279 | 1959 | } |
| 2280 | 1960 | |
| 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 | ||
| 2281 | 1967 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 2282 | 1968 | const sym_index = self.getAtom(atom_index).getSymbolIndex().?; |
| 2283 | 1969 | Atom.freeRelocations(self, atom_index); |
| ... | ... | @@ -2426,6 +2112,101 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol, alignmen |
| 2426 | 2112 | return atom.*.?; |
| 2427 | 2113 | } |
| 2428 | 2114 | |
| 2115 | fn 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 | ||
| 2429 | 2210 | pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index { |
| 2430 | 2211 | const gop = try self.decls.getOrPut(self.base.allocator, decl_index); |
| 2431 | 2212 | if (!gop.found_existing) { |
| ... | ... | @@ -2493,21 +2274,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 |
| 2493 | 2274 | const sect_id = decl_metadata.section; |
| 2494 | 2275 | const header = &self.sections.items(.header)[sect_id]; |
| 2495 | 2276 | 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; | |
| 2500 | 2277 | const code_len = code.len; |
| 2501 | 2278 | |
| 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 | ||
| 2508 | 2279 | if (atom.size != 0) { |
| 2509 | 2280 | 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); | |
| 2511 | 2282 | sym.n_type = macho.N_SECT; |
| 2512 | 2283 | sym.n_sect = sect_id + 1; |
| 2513 | 2284 | sym.n_desc = 0; |
| ... | ... | @@ -2517,23 +2288,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 |
| 2517 | 2288 | |
| 2518 | 2289 | if (need_realloc) { |
| 2519 | 2290 | 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 }); | |
| 2521 | 2292 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 2522 | 2293 | |
| 2523 | 2294 | if (vaddr != sym.n_value) { |
| 2524 | 2295 | 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); | |
| 2534 | 2296 | 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 }); | |
| 2537 | 2300 | } |
| 2538 | 2301 | } else if (code_len < atom.size) { |
| 2539 | 2302 | self.shrinkAtom(atom_index, code_len); |
| ... | ... | @@ -2544,7 +2307,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 |
| 2544 | 2307 | self.getAtomPtr(atom_index).size = code_len; |
| 2545 | 2308 | } else { |
| 2546 | 2309 | 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); | |
| 2548 | 2311 | sym.n_type = macho.N_SECT; |
| 2549 | 2312 | sym.n_sect = sect_id + 1; |
| 2550 | 2313 | sym.n_desc = 0; |
| ... | ... | @@ -2552,21 +2315,13 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 |
| 2552 | 2315 | const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); |
| 2553 | 2316 | errdefer self.freeAtom(atom_index); |
| 2554 | 2317 | |
| 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 }); | |
| 2556 | 2319 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 2557 | 2320 | |
| 2558 | 2321 | self.getAtomPtr(atom_index).size = code_len; |
| 2559 | 2322 | sym.n_value = vaddr; |
| 2560 | 2323 | |
| 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 }); | |
| 2570 | 2325 | } |
| 2571 | 2326 | |
| 2572 | 2327 | try self.writeAtom(atom_index, code); |
| ... | ... | @@ -2828,11 +2583,7 @@ fn populateMissingMetadata(self: *MachO) !void { |
| 2828 | 2583 | } |
| 2829 | 2584 | |
| 2830 | 2585 | 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); | |
| 2836 | 2587 | self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{ |
| 2837 | 2588 | .size = stub_size, |
| 2838 | 2589 | .alignment = switch (cpu_arch) { |
| ... | ... | @@ -3021,7 +2772,7 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void { |
| 3021 | 2772 | const last_atom = self.getAtom(last_atom_index); |
| 3022 | 2773 | const sym = last_atom.getSymbol(self); |
| 3023 | 2774 | break :blk (sym.n_value + last_atom.size) - segment.vmaddr; |
| 3024 | } else 0; | |
| 2775 | } else header.size; | |
| 3025 | 2776 | |
| 3026 | 2777 | log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{ |
| 3027 | 2778 | header.segName(), |
| ... | ... | @@ -3249,6 +3000,28 @@ fn writeLinkeditSegmentData(self: *MachO) !void { |
| 3249 | 3000 | seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size); |
| 3250 | 3001 | } |
| 3251 | 3002 | |
| 3003 | fn 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 | ||
| 3252 | 3025 | fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { |
| 3253 | 3026 | const gpa = self.base.allocator; |
| 3254 | 3027 | const slice = self.sections.slice(); |
| ... | ... | @@ -3276,9 +3049,42 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { |
| 3276 | 3049 | } |
| 3277 | 3050 | } |
| 3278 | 3051 | |
| 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 | ||
| 3279 | 3055 | try rebase.finalize(gpa); |
| 3280 | 3056 | } |
| 3281 | 3057 | |
| 3058 | fn 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 | ||
| 3282 | 3088 | fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { |
| 3283 | 3089 | const gpa = self.base.allocator; |
| 3284 | 3090 | const slice = self.sections.slice(); |
| ... | ... | @@ -3320,9 +3126,16 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { |
| 3320 | 3126 | } |
| 3321 | 3127 | } |
| 3322 | 3128 | |
| 3129 | // Gather GOT pointers | |
| 3130 | try self.collectBindDataFromTableSection(self.got_section_index.?, bind, self.got_table); | |
| 3323 | 3131 | try bind.finalize(gpa, self); |
| 3324 | 3132 | } |
| 3325 | 3133 | |
| 3134 | fn 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 | ||
| 3326 | 3139 | fn collectExportData(self: *MachO, trie: *Trie) !void { |
| 3327 | 3140 | const gpa = self.base.allocator; |
| 3328 | 3141 | |
| ... | ... | @@ -3366,7 +3179,7 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 3366 | 3179 | |
| 3367 | 3180 | var lazy_bind = LazyBind{}; |
| 3368 | 3181 | defer lazy_bind.deinit(gpa); |
| 3369 | try self.collectBindData(&lazy_bind, self.lazy_bindings); | |
| 3182 | try self.collectLazyBindData(&lazy_bind); | |
| 3370 | 3183 | |
| 3371 | 3184 | var trie: Trie = .{}; |
| 3372 | 3185 | defer trie.deinit(gpa); |
| ... | ... | @@ -3442,34 +3255,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3442 | 3255 | if (lazy_bind.size() == 0) return; |
| 3443 | 3256 | |
| 3444 | 3257 | 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); | |
| 3446 | 3259 | |
| 3447 | const section = self.sections.get(stub_helper_section_index); | |
| 3260 | const header = self.sections.items(.header)[stub_helper_section_index]; | |
| 3448 | 3261 | |
| 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; | |
| 3456 | 3267 | |
| 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; | |
| 3463 | 3270 | |
| 3464 | 3271 | log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{ |
| 3465 | 3272 | bind_offset, |
| 3466 | self.getSymbolName(lazy_bind.entries.items[index - 1].target), | |
| 3273 | self.getSymbolName(lazy_bind.entries.items[index].target), | |
| 3467 | 3274 | file_offset, |
| 3468 | 3275 | }); |
| 3469 | 3276 | |
| 3470 | 3277 | try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); |
| 3471 | ||
| 3472 | atom_index = atom.prev_index.?; | |
| 3473 | 3278 | } |
| 3474 | 3279 | } |
| 3475 | 3280 | |
| ... | ... | @@ -3585,7 +3390,7 @@ const SymtabCtx = struct { |
| 3585 | 3390 | |
| 3586 | 3391 | fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3587 | 3392 | 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()); | |
| 3589 | 3394 | const ngot_entries = @intCast(u32, self.got_table.lookup.count()); |
| 3590 | 3395 | const nindirectsyms = nstubs * 2 + ngot_entries; |
| 3591 | 3396 | const iextdefsym = ctx.nlocalsym; |
| ... | ... | @@ -3606,13 +3411,13 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3606 | 3411 | const writer = buf.writer(); |
| 3607 | 3412 | |
| 3608 | 3413 | 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); | |
| 3614 | 3419 | 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).?); | |
| 3616 | 3421 | } |
| 3617 | 3422 | } |
| 3618 | 3423 | |
| ... | ... | @@ -3620,10 +3425,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3620 | 3425 | const got = &self.sections.items(.header)[sect_id]; |
| 3621 | 3426 | got.reserved1 = nstubs; |
| 3622 | 3427 | 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); | |
| 3625 | 3430 | 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).?); | |
| 3627 | 3432 | } else { |
| 3628 | 3433 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); |
| 3629 | 3434 | } |
| ... | ... | @@ -3633,11 +3438,11 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { |
| 3633 | 3438 | if (self.la_symbol_ptr_section_index) |sect_id| { |
| 3634 | 3439 | const la_symbol_ptr = &self.sections.items(.header)[sect_id]; |
| 3635 | 3440 | 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); | |
| 3639 | 3444 | 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).?); | |
| 3641 | 3446 | } |
| 3642 | 3447 | } |
| 3643 | 3448 | |
| ... | ... | @@ -4321,13 +4126,13 @@ pub fn logSymtab(self: *MachO) void { |
| 4321 | 4126 | } |
| 4322 | 4127 | |
| 4323 | 4128 | log.debug("GOT entries:", .{}); |
| 4324 | log.debug("{}", .{self.got_table.fmtDebug(self)}); | |
| 4129 | log.debug("{}", .{self.got_table}); | |
| 4325 | 4130 | |
| 4326 | 4131 | log.debug("stubs entries:", .{}); |
| 4327 | log.debug("{}", .{self.stubs_table.fmtDebug(self)}); | |
| 4132 | log.debug("{}", .{self.stub_table}); | |
| 4328 | 4133 | |
| 4329 | log.debug("threadlocal entries:", .{}); | |
| 4330 | log.debug("{}", .{self.tlv_table.fmtDebug(self)}); | |
| 4134 | // log.debug("threadlocal entries:", .{}); | |
| 4135 | // log.debug("{}", .{self.tlv_table}); | |
| 4331 | 4136 | } |
| 4332 | 4137 | |
| 4333 | 4138 | pub 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 |
| 158 | 158 | try gop.value_ptr.append(gpa, binding); |
| 159 | 159 | } |
| 160 | 160 | |
| 161 | pub 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 | ||
| 176 | 161 | pub fn resolveRelocations( |
| 177 | 162 | macho_file: *MachO, |
| 178 | 163 | atom_index: Index, |
| ... | ... | @@ -193,6 +178,4 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void { |
| 193 | 178 | if (removed_rebases) |*rebases| rebases.value.deinit(gpa); |
| 194 | 179 | var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index); |
| 195 | 180 | 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); | |
| 198 | 181 | } |
src/link/MachO/DebugSymbols.zig+2-2| ... | ... | @@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { |
| 230 | 230 | .got_load => blk: { |
| 231 | 231 | const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?; |
| 232 | 232 | 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); | |
| 234 | 234 | }, |
| 235 | 235 | }; |
| 236 | 236 | if (sym.n_value == reloc.prev_vaddr) continue; |
| ... | ... | @@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { |
| 240 | 240 | .got_load => blk: { |
| 241 | 241 | const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?; |
| 242 | 242 | 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); | |
| 244 | 244 | }, |
| 245 | 245 | }; |
| 246 | 246 | 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 { |
| 15 | 15 | got, |
| 16 | 16 | /// RIP-relative displacement |
| 17 | 17 | signed, |
| 18 | /// RIP-relative displacement to GOT pointer to TLV thunk | |
| 18 | /// RIP-relative displacement to a TLV thunk | |
| 19 | 19 | tlv, |
| 20 | 20 | |
| 21 | 21 | // aarch64 |
| ... | ... | @@ -39,25 +39,35 @@ pub const Type = enum { |
| 39 | 39 | |
| 40 | 40 | /// Returns true if and only if the reloc is dirty AND the target address is available. |
| 41 | 41 | pub 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; | |
| 43 | 44 | return self.dirty; |
| 44 | 45 | } |
| 45 | 46 | |
| 46 | pub 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), | |
| 47 | pub 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 | }, | |
| 49 | 54 | .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; | |
| 54 | 58 | }, |
| 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 | } | |
| 61 | 71 | } |
| 62 | 72 | |
| 63 | 73 | pub 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 |
| 66 | 76 | const source_sym = atom.getSymbol(macho_file); |
| 67 | 77 | const source_addr = source_sym.n_value + self.offset; |
| 68 | 78 | |
| 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(). | |
| 72 | 80 | const target_addr: i64 = switch (self.type) { |
| 73 | 81 | .tlv_initializer => blk: { |
| 74 | 82 | assert(self.addend == 0); // Addend here makes no sense. |
| 75 | 83 | 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); | |
| 78 | 85 | }, |
| 79 | else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend, | |
| 86 | else => @intCast(i64, target_base_addr) + self.addend, | |
| 80 | 87 | }; |
| 81 | 88 | |
| 82 | 89 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ |
| ... | ... | @@ -189,11 +196,48 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8 |
| 189 | 196 | } |
| 190 | 197 | } |
| 191 | 198 | |
| 192 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 199 | pub inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 193 | 200 | const group_decode = @truncate(u5, inst[3]); |
| 194 | 201 | return ((group_decode >> 2) == 4); |
| 195 | 202 | } |
| 196 | 203 | |
| 204 | pub 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 | ||
| 209 | pub 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 | ||
| 214 | pub 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 | ||
| 221 | pub 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 | ||
| 230 | pub 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 | ||
| 197 | 241 | const Relocation = @This(); |
| 198 | 242 | |
| 199 | 243 | const std = @import("std"); |
src/link/MachO/ZldAtom.zig+17-58| ... | ... | @@ -21,6 +21,7 @@ const Allocator = mem.Allocator; |
| 21 | 21 | const Arch = std.Target.Cpu.Arch; |
| 22 | 22 | const AtomIndex = @import("zld.zig").AtomIndex; |
| 23 | 23 | const Object = @import("Object.zig"); |
| 24 | const Relocation = @import("Relocation.zig"); | |
| 24 | 25 | const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; |
| 25 | 26 | const Zld = @import("zld.zig").Zld; |
| 26 | 27 | |
| ... | ... | @@ -571,7 +572,7 @@ fn resolveRelocsArm64( |
| 571 | 572 | zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(), |
| 572 | 573 | }); |
| 573 | 574 | |
| 574 | const displacement = if (calcPcRelativeDisplacementArm64( | |
| 575 | const displacement = if (Relocation.calcPcRelativeDisplacementArm64( | |
| 575 | 576 | source_addr, |
| 576 | 577 | zld.getSymbol(actual_target).n_value, |
| 577 | 578 | )) |disp| blk: { |
| ... | ... | @@ -585,7 +586,7 @@ fn resolveRelocsArm64( |
| 585 | 586 | actual_target, |
| 586 | 587 | ).?); |
| 587 | 588 | 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); | |
| 589 | 590 | }; |
| 590 | 591 | |
| 591 | 592 | const code = atom_code[rel_offset..][0..4]; |
| ... | ... | @@ -607,7 +608,7 @@ fn resolveRelocsArm64( |
| 607 | 608 | |
| 608 | 609 | log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); |
| 609 | 610 | |
| 610 | const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr)); | |
| 611 | const pages = @bitCast(u21, Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)); | |
| 611 | 612 | const code = atom_code[rel_offset..][0..4]; |
| 612 | 613 | var inst = aarch64.Instruction{ |
| 613 | 614 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( |
| ... | ... | @@ -627,8 +628,8 @@ fn resolveRelocsArm64( |
| 627 | 628 | log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); |
| 628 | 629 | |
| 629 | 630 | 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); | |
| 632 | 633 | var inst = aarch64.Instruction{ |
| 633 | 634 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( |
| 634 | 635 | aarch64.Instruction, |
| ... | ... | @@ -644,11 +645,11 @@ fn resolveRelocsArm64( |
| 644 | 645 | aarch64.Instruction.load_store_register, |
| 645 | 646 | ), code), |
| 646 | 647 | }; |
| 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) { | |
| 648 | 649 | 0 => if (inst.load_store_register.v == 1) |
| 649 | PageOffsetInstKind.load_store_128 | |
| 650 | Relocation.PageOffsetInstKind.load_store_128 | |
| 650 | 651 | else |
| 651 | PageOffsetInstKind.load_store_8, | |
| 652 | Relocation.PageOffsetInstKind.load_store_8, | |
| 652 | 653 | 1 => .load_store_16, |
| 653 | 654 | 2 => .load_store_32, |
| 654 | 655 | 3 => .load_store_64, |
| ... | ... | @@ -665,7 +666,7 @@ fn resolveRelocsArm64( |
| 665 | 666 | |
| 666 | 667 | log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); |
| 667 | 668 | |
| 668 | const off = try calcPageOffset(adjusted_target_addr, .load_store_64); | |
| 669 | const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64); | |
| 669 | 670 | var inst: aarch64.Instruction = .{ |
| 670 | 671 | .load_store_register = mem.bytesToValue(meta.TagPayload( |
| 671 | 672 | aarch64.Instruction, |
| ... | ... | @@ -689,7 +690,7 @@ fn resolveRelocsArm64( |
| 689 | 690 | size: u2, |
| 690 | 691 | }; |
| 691 | 692 | const reg_info: RegInfo = blk: { |
| 692 | if (isArithmeticOp(code)) { | |
| 693 | if (Relocation.isArithmeticOp(code)) { | |
| 693 | 694 | const inst = mem.bytesToValue(meta.TagPayload( |
| 694 | 695 | aarch64.Instruction, |
| 695 | 696 | aarch64.Instruction.add_subtract_immediate, |
| ... | ... | @@ -716,7 +717,7 @@ fn resolveRelocsArm64( |
| 716 | 717 | .load_store_register = .{ |
| 717 | 718 | .rt = reg_info.rd, |
| 718 | 719 | .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), | |
| 720 | 721 | .opc = 0b01, |
| 721 | 722 | .op1 = 0b01, |
| 722 | 723 | .v = 0, |
| ... | ... | @@ -726,7 +727,7 @@ fn resolveRelocsArm64( |
| 726 | 727 | .add_subtract_immediate = .{ |
| 727 | 728 | .rd = reg_info.rd, |
| 728 | 729 | .rn = reg_info.rn, |
| 729 | .imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic), | |
| 730 | .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic), | |
| 730 | 731 | .sh = 0, |
| 731 | 732 | .s = 0, |
| 732 | 733 | .op = 0, |
| ... | ... | @@ -858,7 +859,7 @@ fn resolveRelocsX86( |
| 858 | 859 | const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); |
| 859 | 860 | const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); |
| 860 | 861 | 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); | |
| 862 | 863 | mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); |
| 863 | 864 | }, |
| 864 | 865 | |
| ... | ... | @@ -868,7 +869,7 @@ fn resolveRelocsX86( |
| 868 | 869 | const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); |
| 869 | 870 | const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); |
| 870 | 871 | 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); | |
| 872 | 873 | mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); |
| 873 | 874 | }, |
| 874 | 875 | |
| ... | ... | @@ -876,7 +877,7 @@ fn resolveRelocsX86( |
| 876 | 877 | const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]); |
| 877 | 878 | const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend); |
| 878 | 879 | 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); | |
| 880 | 881 | |
| 881 | 882 | if (zld.tlv_ptr_table.get(target) == null) { |
| 882 | 883 | // We need to rewrite the opcode from movq to leaq. |
| ... | ... | @@ -913,7 +914,7 @@ fn resolveRelocsX86( |
| 913 | 914 | |
| 914 | 915 | log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr}); |
| 915 | 916 | |
| 916 | const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction); | |
| 917 | const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction); | |
| 917 | 918 | mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp); |
| 918 | 919 | }, |
| 919 | 920 | |
| ... | ... | @@ -955,11 +956,6 @@ fn resolveRelocsX86( |
| 955 | 956 | } |
| 956 | 957 | } |
| 957 | 958 | |
| 958 | inline fn isArithmeticOp(inst: *const [4]u8) bool { | |
| 959 | const group_decode = @truncate(u5, inst[3]); | |
| 960 | return ((group_decode >> 2) == 4); | |
| 961 | } | |
| 962 | ||
| 963 | 959 | pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 { |
| 964 | 960 | const atom = zld.getAtom(atom_index); |
| 965 | 961 | 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_ |
| 1006 | 1002 | return relocs[cache.start..][0..cache.len]; |
| 1007 | 1003 | } |
| 1008 | 1004 | |
| 1009 | pub 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 | ||
| 1014 | pub 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 | ||
| 1019 | pub 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 | ||
| 1026 | const 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 | ||
| 1035 | pub 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 | ||
| 1046 | 1005 | pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool { |
| 1047 | 1006 | switch (zld.options.target.cpu.arch) { |
| 1048 | 1007 | .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); |
| 9 | 9 | const Allocator = mem.Allocator; |
| 10 | 10 | const AtomIndex = @import("zld.zig").AtomIndex; |
| 11 | 11 | const Atom = @import("ZldAtom.zig"); |
| 12 | const Relocation = @import("Relocation.zig"); | |
| 12 | 13 | const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; |
| 13 | 14 | const UnwindInfo = @import("UnwindInfo.zig"); |
| 14 | 15 | const Zld = @import("zld.zig").Zld; |
| ... | ... | @@ -368,7 +369,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 368 | 369 | const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false); |
| 369 | 370 | const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]); |
| 370 | 371 | 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); | |
| 372 | 373 | mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp); |
| 373 | 374 | }, |
| 374 | 375 | else => unreachable, |
src/link/MachO/stubs.zig created+161| ... | ... | @@ -0,0 +1,161 @@ |
| 1 | const std = @import("std"); | |
| 2 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | |
| 3 | ||
| 4 | const Relocation = @import("Relocation.zig"); | |
| 5 | ||
| 6 | pub 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 | ||
| 14 | pub 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 | ||
| 22 | pub 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 | ||
| 30 | pub 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 | ||
| 38 | pub 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 | ||
| 98 | pub 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 | ||
| 131 | pub 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"); |
| 17 | 17 | const Allocator = mem.Allocator; |
| 18 | 18 | const Atom = @import("ZldAtom.zig"); |
| 19 | 19 | const AtomIndex = @import("zld.zig").AtomIndex; |
| 20 | const Relocation = @import("Relocation.zig"); | |
| 20 | 21 | const SymbolWithLoc = @import("zld.zig").SymbolWithLoc; |
| 21 | 22 | const Zld = @import("zld.zig").Zld; |
| 22 | 23 | |
| ... | ... | @@ -317,7 +318,7 @@ fn isReachable( |
| 317 | 318 | const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset); |
| 318 | 319 | const is_via_got = Atom.relocRequiresGot(zld, rel); |
| 319 | 320 | 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 | |
| 321 | 322 | return false; |
| 322 | 323 | |
| 323 | 324 | return true; |
| ... | ... | @@ -364,9 +365,9 @@ pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void { |
| 364 | 365 | if (atom_index == target_atom_index) break zld.getSymbol(target).n_value; |
| 365 | 366 | } else unreachable; |
| 366 | 367 | |
| 367 | const pages = Atom.calcNumberOfPages(source_addr, target_addr); | |
| 368 | const pages = Relocation.calcNumberOfPages(source_addr, target_addr); | |
| 368 | 369 | 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); | |
| 370 | 371 | try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32()); |
| 371 | 372 | try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32()); |
| 372 | 373 | } |
src/link/MachO/zld.zig+20-110| ... | ... | @@ -16,6 +16,7 @@ const link = @import("../../link.zig"); |
| 16 | 16 | const load_commands = @import("load_commands.zig"); |
| 17 | 17 | const thunks = @import("thunks.zig"); |
| 18 | 18 | const trace = @import("../../tracy.zig").trace; |
| 19 | const stub_helpers = @import("stubs.zig"); | |
| 19 | 20 | |
| 20 | 21 | const Allocator = mem.Allocator; |
| 21 | 22 | const Archive = @import("Archive.zig"); |
| ... | ... | @@ -666,59 +667,17 @@ pub const Zld = struct { |
| 666 | 667 | const entry = self.got_entries.items[index]; |
| 667 | 668 | break :blk entry.getAtomSymbol(self).n_value; |
| 668 | 669 | }; |
| 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); | |
| 713 | 676 | } |
| 714 | 677 | |
| 715 | 678 | pub fn createStubHelperAtom(self: *Zld) !AtomIndex { |
| 716 | 679 | 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); | |
| 722 | 681 | const alignment: u2 = switch (cpu_arch) { |
| 723 | 682 | .x86_64 => 0, |
| 724 | 683 | .aarch64 => 2, |
| ... | ... | @@ -749,32 +708,11 @@ pub const Zld = struct { |
| 749 | 708 | const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? }); |
| 750 | 709 | break :blk sym.n_value; |
| 751 | 710 | }; |
| 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); | |
| 778 | 716 | } |
| 779 | 717 | |
| 780 | 718 | pub fn createLazyPointerAtom(self: *Zld) !AtomIndex { |
| ... | ... | @@ -819,11 +757,7 @@ pub const Zld = struct { |
| 819 | 757 | .aarch64 => 2, |
| 820 | 758 | else => unreachable, // unhandled architecture type |
| 821 | 759 | }; |
| 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); | |
| 827 | 761 | const sym_index = try self.allocateSymbol(); |
| 828 | 762 | const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment); |
| 829 | 763 | const sym = self.getSymbolPtr(.{ .sym_index = sym_index }); |
| ... | ... | @@ -863,31 +797,11 @@ pub const Zld = struct { |
| 863 | 797 | const sym = self.getSymbol(atom.getSymbolWithLoc()); |
| 864 | 798 | break :blk sym.n_value; |
| 865 | 799 | }; |
| 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); | |
| 891 | 805 | } |
| 892 | 806 | |
| 893 | 807 | fn createTentativeDefAtoms(self: *Zld) !void { |
| ... | ... | @@ -2267,11 +2181,7 @@ pub const Zld = struct { |
| 2267 | 2181 | assert(self.stub_helper_preamble_sym_index != null); |
| 2268 | 2182 | |
| 2269 | 2183 | 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); | |
| 2275 | 2185 | const header = section.header; |
| 2276 | 2186 | var atom_index = section.first_atom_index; |
| 2277 | 2187 | atom_index = self.getAtom(atom_index).next_index.?; // skip preamble |
src/link/table_section.zig created+65| ... | ... | @@ -0,0 +1,65 @@ |
| 1 | pub 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 | ||
| 61 | const std = @import("std"); | |
| 62 | const assert = std.debug.assert; | |
| 63 | const log = std.log.scoped(.link); | |
| 64 | ||
| 65 | const Allocator = std.mem.Allocator; |
test/standalone/windows_spawn/main.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral; |
| 4 | 4 | |
| 5 | 5 | pub fn main() anyerror!void { |
| 6 | 6 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
| 7 | defer if (gpa.deinit()) @panic("found memory leaks"); | |
| 7 | defer if (gpa.deinit() == .leak) @panic("found memory leaks"); | |
| 8 | 8 | const allocator = gpa.allocator(); |
| 9 | 9 | |
| 10 | 10 | var it = try std.process.argsWithAllocator(allocator); |