mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
Merge branch 'master' into autodoc-src-files-dirs
This commit is contained in:
commit
c5fb245f60
@ -1424,6 +1424,7 @@ fn genHtml(
|
||||
const result = try ChildProcess.exec(.{
|
||||
.allocator = allocator,
|
||||
.argv = build_args.items,
|
||||
.cwd = tmp_dir_name,
|
||||
.env_map = &env_map,
|
||||
.max_output_bytes = max_doc_file_size,
|
||||
});
|
||||
|
||||
@ -2198,13 +2198,26 @@ const NAV_MODES = {
|
||||
if (opts.addParensIfFnSignature && fnObj.src == 0) {
|
||||
payloadHtml += "(";
|
||||
}
|
||||
if (fnObj.is_extern) {
|
||||
if (opts.wantHtml) {
|
||||
payloadHtml += '<span class="tok-kw">extern </span>';
|
||||
} else {
|
||||
payloadHtml += "extern ";
|
||||
}
|
||||
} else if (fnObj.has_cc) {
|
||||
let cc_expr = zigAnalysis.exprs[fnObj.cc];
|
||||
if (cc_expr.enumLiteral === "Inline") {
|
||||
if(opts.wantHtml) {
|
||||
payloadHtml += '<span class="tok-kw">inline </span>'
|
||||
} else {
|
||||
payloadHtml += "inline "
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnObj.has_lib_name) {
|
||||
payloadHtml += '"' + fnObj.lib_name + '" ';
|
||||
}
|
||||
if (opts.wantHtml) {
|
||||
if (fnObj.is_extern) {
|
||||
payloadHtml += "pub extern ";
|
||||
}
|
||||
if (fnObj.has_lib_name) {
|
||||
payloadHtml += '"' + fnObj.lib_name + '" ';
|
||||
}
|
||||
payloadHtml += '<span class="tok-kw">fn </span>';
|
||||
if (fnDecl) {
|
||||
payloadHtml += '<span class="tok-fn">';
|
||||
@ -2324,7 +2337,9 @@ const NAV_MODES = {
|
||||
if (fnObj.has_cc) {
|
||||
let cc = zigAnalysis.exprs[fnObj.cc];
|
||||
if (cc) {
|
||||
payloadHtml += "callconv(." + cc.enumLiteral + ") ";
|
||||
if (cc.enumLiteral !== "Inline") {
|
||||
payloadHtml += "callconv(" + exprName(cc, opts) + ") ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ pub fn hashString(s: []const u8) u32 {
|
||||
|
||||
/// Insertion order is preserved.
|
||||
/// Deletions perform a "swap removal" on the entries list.
|
||||
/// Modifying the hash map while iterating is allowed, however one must understand
|
||||
/// Modifying the hash map while iterating is allowed, however, one must understand
|
||||
/// the (well defined) behavior when mixing insertions and deletions with iteration.
|
||||
/// For a hash map that can be initialized directly that does not store an Allocator
|
||||
/// field, see `ArrayHashMapUnmanaged`.
|
||||
@ -448,7 +448,7 @@ pub fn ArrayHashMap(
|
||||
/// General purpose hash table.
|
||||
/// Insertion order is preserved.
|
||||
/// Deletions perform a "swap removal" on the entries list.
|
||||
/// Modifying the hash map while iterating is allowed, however one must understand
|
||||
/// Modifying the hash map while iterating is allowed, however, one must understand
|
||||
/// the (well defined) behavior when mixing insertions and deletions with iteration.
|
||||
/// This type does not store an Allocator field - the Allocator must be passed in
|
||||
/// with each function call that requires it. See `ArrayHashMap` for a type that stores
|
||||
|
||||
@ -31,7 +31,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
/// Contents of the list. Pointers to elements in this slice are
|
||||
/// **invalid after resizing operations** on the ArrayList, unless the
|
||||
/// **invalid after resizing operations** on the ArrayList unless the
|
||||
/// operation explicitly either: (1) states otherwise or (2) lists the
|
||||
/// invalidated pointers.
|
||||
///
|
||||
@ -527,7 +527,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
return struct {
|
||||
const Self = @This();
|
||||
/// Contents of the list. Pointers to elements in this slice are
|
||||
/// **invalid after resizing operations** on the ArrayList, unless the
|
||||
/// **invalid after resizing operations** on the ArrayList unless the
|
||||
/// operation explicitly either: (1) states otherwise or (2) lists the
|
||||
/// invalidated pointers.
|
||||
///
|
||||
|
||||
@ -4,7 +4,7 @@ const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const testing = std.testing;
|
||||
|
||||
/// BufMap copies keys and values before they go into the map, and
|
||||
/// BufMap copies keys and values before they go into the map and
|
||||
/// frees them when they get removed.
|
||||
pub const BufMap = struct {
|
||||
hash_map: BufMapHashMap,
|
||||
|
||||
@ -13,6 +13,19 @@ pub const cpulevel_t = c_int;
|
||||
pub const cpuwhich_t = c_int;
|
||||
pub const id_t = i64;
|
||||
|
||||
pub const CPU_LEVEL_ROOT: cpulevel_t = 1;
|
||||
pub const CPU_LEVEL_CPUSET: cpulevel_t = 2;
|
||||
pub const CPU_LEVEL_WHICH: cpulevel_t = 3;
|
||||
pub const CPU_WHICH_TID: cpuwhich_t = 1;
|
||||
pub const CPU_WHICH_PID: cpuwhich_t = 2;
|
||||
pub const CPU_WHICH_CPUSET: cpuwhich_t = 3;
|
||||
pub const CPU_WHICH_IRQ: cpuwhich_t = 4;
|
||||
pub const CPU_WHICH_JAIL: cpuwhich_t = 5;
|
||||
pub const CPU_WHICH_DOMAIN: cpuwhich_t = 6;
|
||||
pub const CPU_WHICH_INTRHANDLER: cpuwhich_t = 7;
|
||||
pub const CPU_WHICH_ITHREAD: cpuwhich_t = 8;
|
||||
pub const CPU_WHICH_TIDPID: cpuwhich_t = 8;
|
||||
|
||||
extern "c" fn __error() *c_int;
|
||||
pub const _errno = __error;
|
||||
|
||||
|
||||
@ -121,9 +121,21 @@ pub const pthread_attr_t = extern struct {
|
||||
};
|
||||
|
||||
pub const sem_t = ?*opaque {};
|
||||
pub const cpuset_t = opaque {};
|
||||
pub const cpuid_t = c_ulong;
|
||||
|
||||
pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*anyopaque) E;
|
||||
pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
|
||||
pub extern "c" fn pthread_setaffinity_np(thread: std.c.pthread_t, size: usize, set: ?*cpuset_t) c_int;
|
||||
pub extern "c" fn pthread_getaffinity_np(thread: std.c.pthread_t, size: usize, set: ?*cpuset_t) c_int;
|
||||
|
||||
pub extern "c" fn cpuset_create() ?*cpuset_t;
|
||||
pub extern "c" fn cpuset_destroy(set: ?*cpuset_t) void;
|
||||
pub extern "c" fn cpuset_zero(set: ?*cpuset_t) void;
|
||||
pub extern "c" fn cpuset_set(cpu: cpuid_t, set: ?*cpuset_t) c_int;
|
||||
pub extern "c" fn cpuset_clr(cpu: cpuid_t, set: ?*cpuset_t) c_int;
|
||||
pub extern "c" fn cpuset_isset(cpu: cpuid_t, set: ?*const cpuset_t) c_int;
|
||||
pub extern "c" fn cpuset_size(set: ?*cpuset_t) usize;
|
||||
|
||||
pub const blkcnt_t = i64;
|
||||
pub const blksize_t = i32;
|
||||
|
||||
@ -50,12 +50,16 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
|
||||
};
|
||||
|
||||
return struct {
|
||||
/// Array of `struct { key: []const u8, value: V }` where `value` is `void{}` if `V` is `void`.
|
||||
/// Sorted by `key` length.
|
||||
pub const kvs = precomputed.sorted_kvs;
|
||||
|
||||
/// Checks if the map has a value for the key.
|
||||
pub fn has(str: []const u8) bool {
|
||||
return get(str) != null;
|
||||
}
|
||||
|
||||
/// Returns the value for the key if any, else null.
|
||||
pub fn get(str: []const u8) ?V {
|
||||
if (str.len < precomputed.min_len or str.len > precomputed.max_len)
|
||||
return null;
|
||||
|
||||
@ -350,7 +350,7 @@ pub fn verifyContext(
|
||||
/// General purpose hash table.
|
||||
/// No order is guaranteed and any modification invalidates live iterators.
|
||||
/// It provides fast operations (lookup, insertion, deletion) with quite high
|
||||
/// load factors (up to 80% by default) for a low memory usage.
|
||||
/// load factors (up to 80% by default) for low memory usage.
|
||||
/// For a hash map that can be initialized directly that does not store an Allocator
|
||||
/// field, see `HashMapUnmanaged`.
|
||||
/// If iterating over the table entries is a strong usecase and needs to be fast,
|
||||
|
||||
@ -16,6 +16,7 @@ pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").Log
|
||||
pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
|
||||
pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
|
||||
pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
|
||||
pub const Check = @import("heap/general_purpose_allocator.zig").Check;
|
||||
pub const WasmAllocator = @import("heap/WasmAllocator.zig");
|
||||
pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");
|
||||
pub const PageAllocator = @import("heap/PageAllocator.zig");
|
||||
|
||||
@ -155,6 +155,8 @@ pub const Config = struct {
|
||||
verbose_log: bool = false,
|
||||
};
|
||||
|
||||
pub const Check = enum { ok, leak };
|
||||
|
||||
pub fn GeneralPurposeAllocator(comptime config: Config) type {
|
||||
return struct {
|
||||
backing_allocator: Allocator = std.heap.page_allocator,
|
||||
@ -431,7 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
|
||||
} else struct {};
|
||||
|
||||
/// Returns true if there were leaks; false otherwise.
|
||||
pub fn deinit(self: *Self) bool {
|
||||
pub fn deinit(self: *Self) Check {
|
||||
const leaks = if (config.safety) self.detectLeaks() else false;
|
||||
if (config.retain_metadata) {
|
||||
self.freeRetainedMetadata();
|
||||
@ -441,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
|
||||
self.small_allocations.deinit(self.backing_allocator);
|
||||
}
|
||||
self.* = undefined;
|
||||
return leaks;
|
||||
return @intToEnum(Check, @boolToInt(leaks));
|
||||
}
|
||||
|
||||
fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void {
|
||||
@ -1024,7 +1026,7 @@ const test_config = Config{};
|
||||
|
||||
test "small allocations - free in same order" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var list = std.ArrayList(*u64).init(std.testing.allocator);
|
||||
@ -1043,7 +1045,7 @@ test "small allocations - free in same order" {
|
||||
|
||||
test "small allocations - free in reverse order" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var list = std.ArrayList(*u64).init(std.testing.allocator);
|
||||
@ -1062,7 +1064,7 @@ test "small allocations - free in reverse order" {
|
||||
|
||||
test "large allocations" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const ptr1 = try allocator.alloc(u64, 42768);
|
||||
@ -1075,7 +1077,7 @@ test "large allocations" {
|
||||
|
||||
test "very large allocation" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
try std.testing.expectError(error.OutOfMemory, allocator.alloc(u8, math.maxInt(usize)));
|
||||
@ -1083,7 +1085,7 @@ test "very large allocation" {
|
||||
|
||||
test "realloc" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1);
|
||||
@ -1105,7 +1107,7 @@ test "realloc" {
|
||||
|
||||
test "shrink" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var slice = try allocator.alloc(u8, 20);
|
||||
@ -1130,7 +1132,7 @@ test "shrink" {
|
||||
|
||||
test "large object - grow" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var slice1 = try allocator.alloc(u8, page_size * 2 - 20);
|
||||
@ -1148,7 +1150,7 @@ test "large object - grow" {
|
||||
|
||||
test "realloc small object to large object" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var slice = try allocator.alloc(u8, 70);
|
||||
@ -1165,7 +1167,7 @@ test "realloc small object to large object" {
|
||||
|
||||
test "shrink large object to large object" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var slice = try allocator.alloc(u8, page_size * 2 + 50);
|
||||
@ -1190,7 +1192,7 @@ test "shrink large object to large object" {
|
||||
|
||||
test "shrink large object to large object with larger alignment" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var debug_buffer: [1000]u8 = undefined;
|
||||
@ -1226,7 +1228,7 @@ test "shrink large object to large object with larger alignment" {
|
||||
|
||||
test "realloc large object to small object" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var slice = try allocator.alloc(u8, page_size * 2 + 50);
|
||||
@ -1244,7 +1246,7 @@ test "overrideable mutexes" {
|
||||
.backing_allocator = std.testing.allocator,
|
||||
.mutex = std.Thread.Mutex{},
|
||||
};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const ptr = try allocator.create(i32);
|
||||
@ -1253,7 +1255,7 @@ test "overrideable mutexes" {
|
||||
|
||||
test "non-page-allocator backing allocator" {
|
||||
var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = std.testing.allocator };
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const ptr = try allocator.create(i32);
|
||||
@ -1262,7 +1264,7 @@ test "non-page-allocator backing allocator" {
|
||||
|
||||
test "realloc large object to larger alignment" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var debug_buffer: [1000]u8 = undefined;
|
||||
@ -1304,7 +1306,7 @@ test "realloc large object to larger alignment" {
|
||||
test "large object shrinks to small but allocation fails during shrink" {
|
||||
var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3);
|
||||
var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = failing_allocator.allocator() };
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
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" {
|
||||
|
||||
test "objects of size 1024 and 2048" {
|
||||
var gpa = GeneralPurposeAllocator(test_config){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const slice = try allocator.alloc(u8, 1025);
|
||||
@ -1334,7 +1336,7 @@ test "objects of size 1024 and 2048" {
|
||||
|
||||
test "setting a memory cap" {
|
||||
var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
gpa.setRequestedMemoryLimit(1010);
|
||||
@ -1361,11 +1363,11 @@ test "setting a memory cap" {
|
||||
test "double frees" {
|
||||
// use a GPA to back a GPA to check for leaks of the latter's metadata
|
||||
var backing_gpa = GeneralPurposeAllocator(.{ .safety = true }){};
|
||||
defer std.testing.expect(!backing_gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(backing_gpa.deinit() == .ok) catch @panic("leak");
|
||||
|
||||
const GPA = GeneralPurposeAllocator(.{ .safety = true, .never_unmap = true, .retain_metadata = true });
|
||||
var gpa = GPA{ .backing_allocator = backing_gpa.allocator() };
|
||||
defer std.testing.expect(!gpa.deinit()) catch @panic("leak");
|
||||
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// detect a small allocation double free, even though bucket is emptied
|
||||
|
||||
@ -141,7 +141,12 @@ pub const addrinfo = system.addrinfo;
|
||||
pub const blkcnt_t = system.blkcnt_t;
|
||||
pub const blksize_t = system.blksize_t;
|
||||
pub const clock_t = system.clock_t;
|
||||
pub const cpu_set_t = system.cpu_set_t;
|
||||
pub const cpu_set_t = if (builtin.os.tag == .linux)
|
||||
system.cpu_set_t
|
||||
else if (builtin.os.tag == .freebsd)
|
||||
freebsd.cpuset_t
|
||||
else
|
||||
u32;
|
||||
pub const dev_t = system.dev_t;
|
||||
pub const dl_phdr_info = system.dl_phdr_info;
|
||||
pub const empty_sigset = system.empty_sigset;
|
||||
@ -5518,13 +5523,27 @@ pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
|
||||
|
||||
pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
|
||||
var set: cpu_set_t = undefined;
|
||||
switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
|
||||
.SUCCESS => return set,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
.PERM => return error.PermissionDenied,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
if (builtin.os.tag == .linux) {
|
||||
switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
|
||||
.SUCCESS => return set,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
.PERM => return error.PermissionDenied,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
} else if (builtin.os.tag == .freebsd) {
|
||||
switch (errno(freebsd.cpuset_getaffinity(freebsd.CPU_LEVEL_WHICH, freebsd.CPU_WHICH_PID, pid, @sizeOf(cpu_set_t), &set))) {
|
||||
.SUCCESS => return set,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
.EDEADLK => unreachable,
|
||||
.PERM => return error.PermissionDenied,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
} else {
|
||||
@compileError("unsupported platform");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,8 +29,8 @@
|
||||
//! which is dependant on the ABI. Since BPF programs execute in a 32-bit
|
||||
//! machine, validation of 64-bit arguments necessitates two load-and-compare
|
||||
//! instructions for the upper and lower words.
|
||||
//! 3. A further wrinkle to the above is endianess. Unlike network packets,
|
||||
//! syscall data shares the endianess of the target machine. A filter
|
||||
//! 3. A further wrinkle to the above is endianness. Unlike network packets,
|
||||
//! syscall data shares the endianness of the target machine. A filter
|
||||
//! compiled on a little-endian machine will not work on a big-endian one,
|
||||
//! and vice-versa. For example: Checking the upper 32-bits of `data.arg1`
|
||||
//! requires a load at `@offsetOf(data, "arg1") + 4` on big-endian systems
|
||||
|
||||
@ -182,7 +182,7 @@ pub fn PackedIntIo(comptime Int: type, comptime endian: Endian) type {
|
||||
|
||||
/// Creates a bit-packed array of `Int`. Non-byte-multiple integers
|
||||
/// will take up less memory in PackedIntArray than in a normal array.
|
||||
/// Elements are packed using native endianess and without storing any
|
||||
/// Elements are packed using native endianness and without storing any
|
||||
/// meta data. PackedArray(i3, 8) will occupy exactly 3 bytes
|
||||
/// of memory.
|
||||
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
|
||||
}
|
||||
|
||||
/// Create a PackedIntSliceEndian of the array using `NewInt` as the integer type
|
||||
/// and `new_endian` as the new endianess. `NewInt`'s bit width must fit evenly
|
||||
/// and `new_endian` as the new endianness. `NewInt`'s bit width must fit evenly
|
||||
/// within the array's `Int`'s total bits.
|
||||
pub fn sliceCastEndian(self: *Self, comptime NewInt: type, comptime new_endian: Endian) PackedIntSliceEndian(NewInt, new_endian) {
|
||||
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 {
|
||||
}
|
||||
|
||||
/// Create a PackedIntSliceEndian of the slice using `NewInt` as the integer type
|
||||
/// and `new_endian` as the new endianess. `NewInt`'s bit width must fit evenly
|
||||
/// and `new_endian` as the new endianness. `NewInt`'s bit width must fit evenly
|
||||
/// within the slice's `Int`'s total bits.
|
||||
pub fn sliceCastEndian(self: Self, comptime NewInt: type, comptime new_endian: Endian) PackedIntSliceEndian(NewInt, new_endian) {
|
||||
return Io.sliceCast(self.bytes, NewInt, new_endian, self.bit_offset, self.len);
|
||||
|
||||
@ -1162,6 +1162,15 @@ pub fn totalSystemMemory() TotalSystemMemoryError!usize {
|
||||
.linux => {
|
||||
return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory;
|
||||
},
|
||||
.freebsd => {
|
||||
var physmem: c_ulong = undefined;
|
||||
var len: usize = @sizeOf(c_ulong);
|
||||
os.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) {
|
||||
error.NameTooLong, error.UnknownName => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
return @intCast(usize, physmem);
|
||||
},
|
||||
.windows => {
|
||||
var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined;
|
||||
const rc = std.os.windows.ntdll.NtQuerySystemInformation(
|
||||
|
||||
@ -56,7 +56,7 @@ fn mainServer() !void {
|
||||
},
|
||||
.query_test_metadata => {
|
||||
std.testing.allocator_instance = .{};
|
||||
defer if (std.testing.allocator_instance.deinit()) {
|
||||
defer if (std.testing.allocator_instance.deinit() == .leak) {
|
||||
@panic("internal test runner memory leak");
|
||||
};
|
||||
|
||||
@ -108,7 +108,7 @@ fn mainServer() !void {
|
||||
}
|
||||
},
|
||||
};
|
||||
leak = std.testing.allocator_instance.deinit();
|
||||
leak = std.testing.allocator_instance.deinit() == .leak;
|
||||
try server.serveTestResults(.{
|
||||
.index = index,
|
||||
.flags = .{
|
||||
@ -148,7 +148,7 @@ fn mainTerminal() void {
|
||||
for (test_fn_list, 0..) |test_fn, i| {
|
||||
std.testing.allocator_instance = .{};
|
||||
defer {
|
||||
if (std.testing.allocator_instance.deinit()) {
|
||||
if (std.testing.allocator_instance.deinit() == .leak) {
|
||||
leaks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ pub fn generateZirData(self: *Autodoc) !void {
|
||||
.ComptimeExpr = .{ .name = "ComptimeExpr" },
|
||||
});
|
||||
|
||||
// this skipts Ref.none but it's ok becuse we replaced it with ComptimeExpr
|
||||
// this skips Ref.none but it's ok becuse we replaced it with ComptimeExpr
|
||||
var i: u32 = 1;
|
||||
while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) {
|
||||
var tmpbuf = std.ArrayList(u8).init(self.arena);
|
||||
@ -196,8 +196,10 @@ pub fn generateZirData(self: *Autodoc) !void {
|
||||
.anyerror_type => .{
|
||||
.ErrorSet = .{ .name = try tmpbuf.toOwnedSlice() },
|
||||
},
|
||||
.calling_convention_inline, .calling_convention_c, .calling_convention_type => .{
|
||||
.EnumLiteral = .{ .name = try tmpbuf.toOwnedSlice() },
|
||||
// should be an Enum but if we don't analyze std we don't get the ast node
|
||||
// since it's std.builtin.CallingConvention
|
||||
.calling_convention_type => .{
|
||||
.Type = .{ .name = try tmpbuf.toOwnedSlice() },
|
||||
},
|
||||
},
|
||||
);
|
||||
@ -4010,17 +4012,27 @@ fn analyzeFancyFunction(
|
||||
}
|
||||
|
||||
var cc_index: ?usize = null;
|
||||
if (extra.data.bits.has_cc_ref) {
|
||||
if (extra.data.bits.has_cc_ref and !extra.data.bits.has_cc_body) {
|
||||
const cc_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
|
||||
const cc_expr = try self.walkRef(file, scope, parent_src, cc_ref, false);
|
||||
|
||||
cc_index = self.exprs.items.len;
|
||||
_ = try self.walkRef(file, scope, parent_src, cc_ref, false);
|
||||
try self.exprs.append(self.arena, cc_expr.expr);
|
||||
|
||||
extra_index += 1;
|
||||
} else if (extra.data.bits.has_cc_body) {
|
||||
const cc_body_len = file.zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const cc_body = file.zir.extra[extra_index .. extra_index + cc_body_len];
|
||||
_ = cc_body;
|
||||
// TODO: analyze the block (or bail with a comptimeExpr)
|
||||
const cc_body = file.zir.extra[extra_index..][0..cc_body_len];
|
||||
|
||||
// We assume the body ends with a break_inline
|
||||
const break_index = cc_body[cc_body.len - 1];
|
||||
const break_operand = data[break_index].@"break".operand;
|
||||
const cc_expr = try self.walkRef(file, scope, parent_src, break_operand, false);
|
||||
|
||||
cc_index = self.exprs.items.len;
|
||||
try self.exprs.append(self.arena, cc_expr.expr);
|
||||
|
||||
extra_index += cc_body_len;
|
||||
} else {
|
||||
// auto calling convention
|
||||
@ -4565,26 +4577,22 @@ fn walkRef(
|
||||
.expr = .{ .int = .{ .value = 1 } },
|
||||
};
|
||||
},
|
||||
// TODO: dunno what to do with those
|
||||
.calling_convention_type => {
|
||||
return DocData.WalkResult{
|
||||
.typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) },
|
||||
// .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
|
||||
.expr = .{ .int = .{ .value = 1 } },
|
||||
.typeRef = .{ .type = @enumToInt(Ref.type_type) },
|
||||
.expr = .{ .type = @enumToInt(Ref.calling_convention_type) },
|
||||
};
|
||||
},
|
||||
.calling_convention_c => {
|
||||
return DocData.WalkResult{
|
||||
.typeRef = .{ .type = @enumToInt(Ref.calling_convention_c) },
|
||||
// .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
|
||||
.expr = .{ .int = .{ .value = 1 } },
|
||||
.typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) },
|
||||
.expr = .{ .enumLiteral = "C" },
|
||||
};
|
||||
},
|
||||
.calling_convention_inline => {
|
||||
return DocData.WalkResult{
|
||||
.typeRef = .{ .type = @enumToInt(Ref.calling_convention_inline) },
|
||||
// .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
|
||||
.expr = .{ .int = .{ .value = 1 } },
|
||||
.typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) },
|
||||
.expr = .{ .enumLiteral = "Inline" },
|
||||
};
|
||||
},
|
||||
// .generic_poison => {
|
||||
|
||||
@ -4290,6 +4290,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
|
||||
try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
|
||||
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
|
||||
@ -4270,6 +4270,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
|
||||
try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
|
||||
} else if (self.bin_file.cast(link.File.MachO)) |_| {
|
||||
|
||||
@ -1734,6 +1734,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
const func = func_payload.data;
|
||||
const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
|
||||
try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
|
||||
_ = try self.addInst(.{
|
||||
|
||||
@ -1254,6 +1254,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
|
||||
const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));
|
||||
} else unreachable;
|
||||
|
||||
|
||||
@ -5624,7 +5624,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
|
||||
const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
const got_addr = atom.getOffsetTableAddress(elf_file);
|
||||
try self.asmMemory(.call, Memory.sib(.qword, .{
|
||||
.base = .ds,
|
||||
.disp = @intCast(i32, got_addr),
|
||||
@ -5853,7 +5855,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
|
||||
.{ .kind = .const_data, .ty = Type.anyerror },
|
||||
4, // dword alignment
|
||||
);
|
||||
const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
const got_addr = atom.getOffsetTableAddress(elf_file);
|
||||
try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
|
||||
.base = .ds,
|
||||
.disp = @intCast(i32, got_addr),
|
||||
@ -7574,7 +7578,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl);
|
||||
if (self.bin_file.cast(link.File.MachO)) |_| {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mov_linker,
|
||||
.tag = .lea_linker,
|
||||
.ops = .tlv_reloc,
|
||||
.data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
|
||||
.reg = @enumToInt(Register.rdi),
|
||||
@ -8230,7 +8234,9 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
|
||||
.{ .kind = .const_data, .ty = Type.anyerror },
|
||||
4, // dword alignment
|
||||
);
|
||||
const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
const got_addr = atom.getOffsetTableAddress(elf_file);
|
||||
try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
|
||||
.base = .ds,
|
||||
.disp = @intCast(i32, got_addr),
|
||||
|
||||
@ -1006,6 +1006,7 @@ fn genDeclRef(
|
||||
if (bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
|
||||
const atom = elf_file.getAtom(atom_index);
|
||||
_ = try atom.getOrCreateOffsetTableEntry(elf_file);
|
||||
return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });
|
||||
} else if (bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
|
||||
|
||||
@ -37,13 +37,14 @@ strtab_offset: ?u32 = null,
|
||||
|
||||
temp_strtab: StringTable(.temp_strtab) = .{},
|
||||
|
||||
got_entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
|
||||
got_table: TableSection(SymbolWithLoc) = .{},
|
||||
|
||||
/// A table of ImportTables partitioned by the library name.
|
||||
/// Key is an offset into the interning string table `temp_strtab`.
|
||||
import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{},
|
||||
|
||||
got_table_count_dirty: bool = true,
|
||||
got_table_contents_dirty: bool = true,
|
||||
imports_count_dirty: bool = true,
|
||||
|
||||
/// Virtual address of the entry point procedure relative to image base.
|
||||
@ -106,12 +107,6 @@ const HotUpdateState = struct {
|
||||
loaded_base_address: ?std.os.windows.HMODULE = null,
|
||||
};
|
||||
|
||||
const Entry = struct {
|
||||
target: SymbolWithLoc,
|
||||
// Index into the synthetic symbol table (i.e., file == null).
|
||||
sym_index: u32,
|
||||
};
|
||||
|
||||
const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
|
||||
const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
|
||||
const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
|
||||
@ -188,7 +183,8 @@ pub const PtrWidth = enum {
|
||||
p32,
|
||||
p64,
|
||||
|
||||
fn abiSize(pw: PtrWidth) u4 {
|
||||
/// Size in bytes.
|
||||
pub fn size(pw: PtrWidth) u4 {
|
||||
return switch (pw) {
|
||||
.p32 => 4,
|
||||
.p64 => 8,
|
||||
@ -310,9 +306,7 @@ pub fn deinit(self: *Coff) void {
|
||||
self.globals_free_list.deinit(gpa);
|
||||
self.strtab.deinit(gpa);
|
||||
self.temp_strtab.deinit(gpa);
|
||||
self.got_entries.deinit(gpa);
|
||||
self.got_entries_free_list.deinit(gpa);
|
||||
self.got_entries_table.deinit(gpa);
|
||||
self.got_table.deinit(gpa);
|
||||
|
||||
for (self.import_tables.values()) |*itab| {
|
||||
itab.deinit(gpa);
|
||||
@ -371,7 +365,7 @@ fn populateMissingMetadata(self: *Coff) !void {
|
||||
}
|
||||
|
||||
if (self.got_section_index == null) {
|
||||
const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
|
||||
const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size();
|
||||
self.got_section_index = try self.allocateSection(".got", file_size, .{
|
||||
.CNT_INITIALIZED_DATA = 1,
|
||||
.MEM_READ = 1,
|
||||
@ -396,7 +390,7 @@ fn populateMissingMetadata(self: *Coff) !void {
|
||||
}
|
||||
|
||||
if (self.idata_section_index == null) {
|
||||
const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();
|
||||
const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size();
|
||||
self.idata_section_index = try self.allocateSection(".idata", file_size, .{
|
||||
.CNT_INITIALIZED_DATA = 1,
|
||||
.MEM_READ = 1,
|
||||
@ -498,8 +492,8 @@ fn growSection(self: *Coff, sect_id: u32, needed_size: u32) !void {
|
||||
|
||||
const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);
|
||||
if (needed_size > sect_vm_capacity) {
|
||||
try self.growSectionVirtualMemory(sect_id, needed_size);
|
||||
self.markRelocsDirtyByAddress(header.virtual_address + needed_size);
|
||||
try self.growSectionVirtualMemory(sect_id, needed_size);
|
||||
}
|
||||
|
||||
header.virtual_size = @max(header.virtual_size, needed_size);
|
||||
@ -698,26 +692,12 @@ fn allocateGlobal(self: *Coff) !u32 {
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
|
||||
const gpa = self.base.allocator;
|
||||
try self.got_entries.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
const index: u32 = blk: {
|
||||
if (self.got_entries_free_list.popOrNull()) |index| {
|
||||
log.debug(" (reusing GOT entry index {d})", .{index});
|
||||
break :blk index;
|
||||
} else {
|
||||
log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len});
|
||||
const index = @intCast(u32, self.got_entries.items.len);
|
||||
_ = self.got_entries.addOneAssumeCapacity();
|
||||
break :blk index;
|
||||
}
|
||||
};
|
||||
|
||||
self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
|
||||
try self.got_entries_table.putNoClobber(gpa, target, index);
|
||||
|
||||
return index;
|
||||
fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void {
|
||||
if (self.got_table.lookup.contains(target)) return;
|
||||
const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
|
||||
try self.writeOffsetTableEntry(got_index);
|
||||
self.got_table_count_dirty = true;
|
||||
self.markRelocsDirtyByTarget(target);
|
||||
}
|
||||
|
||||
pub fn createAtom(self: *Coff) !Atom.Index {
|
||||
@ -737,37 +717,6 @@ pub fn createAtom(self: *Coff) !Atom.Index {
|
||||
return atom_index;
|
||||
}
|
||||
|
||||
fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index {
|
||||
const atom_index = try self.createAtom();
|
||||
const atom = self.getAtomPtr(atom_index);
|
||||
atom.size = @sizeOf(u64);
|
||||
|
||||
const sym = atom.getSymbolPtr(self);
|
||||
sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
|
||||
sym.value = try self.allocateAtom(atom_index, atom.size, @sizeOf(u64));
|
||||
|
||||
log.debug("allocated GOT atom at 0x{x}", .{sym.value});
|
||||
|
||||
try Atom.addRelocation(self, atom_index, .{
|
||||
.type = .direct,
|
||||
.target = target,
|
||||
.offset = 0,
|
||||
.addend = 0,
|
||||
.pcrel = false,
|
||||
.length = 3,
|
||||
});
|
||||
|
||||
const target_sym = self.getSymbol(target);
|
||||
switch (target_sym.section_number) {
|
||||
.UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
|
||||
.ABSOLUTE => {},
|
||||
.DEBUG => unreachable, // not possible
|
||||
else => try Atom.addBaseRelocation(self, atom_index, 0),
|
||||
}
|
||||
|
||||
return atom_index;
|
||||
}
|
||||
|
||||
fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = atom.getSymbol(self);
|
||||
@ -873,17 +822,75 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []
|
||||
if (amt != code.len) return error.InputOutput;
|
||||
}
|
||||
|
||||
fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
|
||||
fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
|
||||
const sect_id = self.got_section_index.?;
|
||||
|
||||
if (self.got_table_count_dirty) {
|
||||
const needed_size = @intCast(u32, self.got_table.entries.items.len * self.ptr_width.size());
|
||||
try self.growSection(sect_id, needed_size);
|
||||
self.got_table_count_dirty = false;
|
||||
}
|
||||
|
||||
const header = &self.sections.items(.header)[sect_id];
|
||||
const entry = self.got_table.entries.items[index];
|
||||
const entry_value = self.getSymbol(entry).value;
|
||||
const entry_offset = index * self.ptr_width.size();
|
||||
const file_offset = header.pointer_to_raw_data + entry_offset;
|
||||
const vmaddr = header.virtual_address + entry_offset;
|
||||
|
||||
log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value + self.getImageBase() });
|
||||
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
|
||||
try self.writeAtom(atom_index, &buffer);
|
||||
var buf: [4]u8 = undefined;
|
||||
mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + self.getImageBase()));
|
||||
try self.base.file.?.pwriteAll(&buf, file_offset);
|
||||
},
|
||||
.p64 => {
|
||||
var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
|
||||
try self.writeAtom(atom_index, &buffer);
|
||||
var buf: [8]u8 = undefined;
|
||||
mem.writeIntLittle(u64, &buf, entry_value + self.getImageBase());
|
||||
try self.base.file.?.pwriteAll(&buf, file_offset);
|
||||
},
|
||||
}
|
||||
|
||||
if (is_hot_update_compatible) {
|
||||
if (self.base.child_pid) |handle| {
|
||||
const gpa = self.base.allocator;
|
||||
const slide = @ptrToInt(self.hot_state.loaded_base_address.?);
|
||||
const actual_vmaddr = vmaddr + slide;
|
||||
const pvaddr = @intToPtr(*anyopaque, actual_vmaddr);
|
||||
log.debug("writing GOT entry to memory at address {x}", .{actual_vmaddr});
|
||||
if (build_options.enable_logging) {
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
var buf: [4]u8 = undefined;
|
||||
try debugMem(gpa, handle, pvaddr, &buf);
|
||||
},
|
||||
.p64 => {
|
||||
var buf: [8]u8 = undefined;
|
||||
try debugMem(gpa, handle, pvaddr, &buf);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
var buf: [4]u8 = undefined;
|
||||
mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + slide));
|
||||
writeMem(handle, pvaddr, &buf) catch |err| {
|
||||
log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
.p64 => {
|
||||
var buf: [8]u8 = undefined;
|
||||
mem.writeIntLittle(u64, &buf, entry_value + slide);
|
||||
writeMem(handle, pvaddr, &buf) catch |err| {
|
||||
log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
|
||||
@ -904,6 +911,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
|
||||
reloc.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: dirty only really affected GOT cells
|
||||
for (self.got_table.entries.items) |entry| {
|
||||
const target_addr = self.getSymbol(entry).value;
|
||||
if (target_addr >= addr) {
|
||||
self.got_table_contents_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
self.locals_free_list.append(gpa, sym_index) catch {};
|
||||
|
||||
// Try freeing GOT atom if this decl had one
|
||||
const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
|
||||
if (self.got_entries_table.get(got_target)) |got_index| {
|
||||
self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
|
||||
self.got_entries.items[got_index] = .{
|
||||
.target = .{ .sym_index = 0, .file = null },
|
||||
.sym_index = 0,
|
||||
};
|
||||
_ = self.got_entries_table.remove(got_target);
|
||||
|
||||
log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
|
||||
}
|
||||
self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
|
||||
|
||||
self.locals.items[sym_index].section_number = .UNDEFINED;
|
||||
_ = self.atom_by_index_table.remove(sym_index);
|
||||
@ -1243,14 +1249,7 @@ fn updateLazySymbolAtom(
|
||||
atom.size = code_len;
|
||||
symbol.value = vaddr;
|
||||
|
||||
const got_target = SymbolWithLoc{ .sym_index = local_sym_index, .file = null };
|
||||
const got_index = try self.allocateGotEntry(got_target);
|
||||
const got_atom_index = try self.createGotAtom(got_target);
|
||||
const got_atom = self.getAtom(got_atom_index);
|
||||
self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
|
||||
try self.writePtrWidthAtom(got_atom_index);
|
||||
|
||||
self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
|
||||
try self.addGotEntry(.{ .sym_index = local_sym_index });
|
||||
try self.writeAtom(atom_index, code);
|
||||
}
|
||||
|
||||
@ -1321,6 +1320,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
|
||||
const decl_metadata = self.decls.get(decl_index).?;
|
||||
const atom_index = decl_metadata.atom;
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym_index = atom.getSymbolIndex().?;
|
||||
const sect_index = decl_metadata.section;
|
||||
const code_len = @intCast(u32, code.len);
|
||||
|
||||
@ -1340,10 +1340,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
|
||||
if (vaddr != sym.value) {
|
||||
sym.value = vaddr;
|
||||
log.debug(" (updating GOT entry)", .{});
|
||||
const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
|
||||
const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
|
||||
self.markRelocsDirtyByTarget(got_target);
|
||||
try self.writePtrWidthAtom(got_atom_index);
|
||||
const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?;
|
||||
try self.writeOffsetTableEntry(got_entry_index);
|
||||
self.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
|
||||
}
|
||||
} else if (code_len < atom.size) {
|
||||
self.shrinkAtom(atom_index, code_len);
|
||||
@ -1361,15 +1360,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
|
||||
self.getAtomPtr(atom_index).size = code_len;
|
||||
sym.value = vaddr;
|
||||
|
||||
const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
|
||||
const got_index = try self.allocateGotEntry(got_target);
|
||||
const got_atom_index = try self.createGotAtom(got_target);
|
||||
const got_atom = self.getAtom(got_atom_index);
|
||||
self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
|
||||
try self.writePtrWidthAtom(got_atom_index);
|
||||
try self.addGotEntry(.{ .sym_index = sym_index });
|
||||
}
|
||||
|
||||
self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
|
||||
try self.writeAtom(atom_index, code);
|
||||
}
|
||||
|
||||
@ -1651,6 +1644,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
try self.writeAtom(atom_index, code.items);
|
||||
}
|
||||
|
||||
// Update GOT if it got moved in memory.
|
||||
if (self.got_table_contents_dirty) {
|
||||
for (self.got_table.entries.items, 0..) |entry, i| {
|
||||
if (!self.got_table.lookup.contains(entry)) continue;
|
||||
// TODO: write all in one go rather than incrementally.
|
||||
try self.writeOffsetTableEntry(i);
|
||||
}
|
||||
self.got_table_contents_dirty = false;
|
||||
}
|
||||
|
||||
try self.writeBaseRelocations();
|
||||
|
||||
if (self.getEntryPoint()) |entry_sym_loc| {
|
||||
@ -1739,48 +1742,82 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Dec
|
||||
fn writeBaseRelocations(self: *Coff) !void {
|
||||
const gpa = self.base.allocator;
|
||||
|
||||
var pages = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);
|
||||
var page_table = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);
|
||||
defer {
|
||||
var it = pages.valueIterator();
|
||||
var it = page_table.valueIterator();
|
||||
while (it.next()) |inner| {
|
||||
inner.deinit();
|
||||
}
|
||||
pages.deinit();
|
||||
page_table.deinit();
|
||||
}
|
||||
|
||||
var it = self.base_relocs.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const atom_index = entry.key_ptr.*;
|
||||
const atom = self.getAtom(atom_index);
|
||||
const offsets = entry.value_ptr.*;
|
||||
|
||||
for (offsets.items) |offset| {
|
||||
{
|
||||
var it = self.base_relocs.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const atom_index = entry.key_ptr.*;
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = atom.getSymbol(self);
|
||||
const rva = sym.value + offset;
|
||||
const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
|
||||
const gop = try pages.getOrPut(page);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
|
||||
const offsets = entry.value_ptr.*;
|
||||
|
||||
for (offsets.items) |offset| {
|
||||
const rva = sym.value + offset;
|
||||
const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
|
||||
const gop = try page_table.getOrPut(page);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
|
||||
}
|
||||
try gop.value_ptr.append(.{
|
||||
.offset = @intCast(u12, rva - page),
|
||||
.type = .DIR64,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const header = &self.sections.items(.header)[self.got_section_index.?];
|
||||
for (self.got_table.entries.items, 0..) |entry, index| {
|
||||
if (!self.got_table.lookup.contains(entry)) continue;
|
||||
|
||||
const sym = self.getSymbol(entry);
|
||||
if (sym.section_number == .UNDEFINED) continue;
|
||||
|
||||
const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size());
|
||||
const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
|
||||
const gop = try page_table.getOrPut(page);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
|
||||
}
|
||||
try gop.value_ptr.append(.{
|
||||
.offset = @intCast(u12, rva - page),
|
||||
.type = .DIR64,
|
||||
});
|
||||
}
|
||||
try gop.value_ptr.append(.{
|
||||
.offset = @intCast(u12, rva - page),
|
||||
.type = .DIR64,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort pages by address.
|
||||
var pages = try std.ArrayList(u32).initCapacity(gpa, page_table.count());
|
||||
defer pages.deinit();
|
||||
{
|
||||
var it = page_table.keyIterator();
|
||||
while (it.next()) |page| {
|
||||
pages.appendAssumeCapacity(page.*);
|
||||
}
|
||||
}
|
||||
std.sort.sort(u32, pages.items, {}, std.sort.asc(u32));
|
||||
|
||||
var buffer = std.ArrayList(u8).init(gpa);
|
||||
defer buffer.deinit();
|
||||
|
||||
var pages_it = pages.iterator();
|
||||
while (pages_it.next()) |entry| {
|
||||
for (pages.items) |page| {
|
||||
const entries = page_table.getPtr(page).?;
|
||||
// Pad to required 4byte alignment
|
||||
if (!mem.isAlignedGeneric(
|
||||
usize,
|
||||
entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation),
|
||||
entries.items.len * @sizeOf(coff.BaseRelocation),
|
||||
@sizeOf(u32),
|
||||
)) {
|
||||
try entry.value_ptr.append(.{
|
||||
try entries.append(.{
|
||||
.offset = 0,
|
||||
.type = .ABSOLUTE,
|
||||
});
|
||||
@ -1788,14 +1825,14 @@ fn writeBaseRelocations(self: *Coff) !void {
|
||||
|
||||
const block_size = @intCast(
|
||||
u32,
|
||||
entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry),
|
||||
entries.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry),
|
||||
);
|
||||
try buffer.ensureUnusedCapacity(block_size);
|
||||
buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{
|
||||
.page_rva = entry.key_ptr.*,
|
||||
.page_rva = page,
|
||||
.block_size = block_size,
|
||||
}));
|
||||
buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entry.value_ptr.items));
|
||||
buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entries.items));
|
||||
}
|
||||
|
||||
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
|
||||
return self.atom_by_index_table.get(sym_loc.sym_index);
|
||||
}
|
||||
|
||||
/// Returns GOT atom that references `sym_loc` if one exists.
|
||||
/// Returns null otherwise.
|
||||
pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
|
||||
const got_index = self.got_entries_table.get(sym_loc) orelse return null;
|
||||
const got_entry = self.got_entries.items[got_index];
|
||||
return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
|
||||
}
|
||||
|
||||
fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
|
||||
if (name.len <= 8) {
|
||||
mem.copy(u8, &header.name, name);
|
||||
@ -2410,25 +2439,7 @@ fn logSymtab(self: *Coff) void {
|
||||
}
|
||||
|
||||
log.debug("GOT entries:", .{});
|
||||
for (self.got_entries.items, 0..) |entry, i| {
|
||||
const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
|
||||
const target_sym = self.getSymbol(entry.target);
|
||||
if (target_sym.section_number == .UNDEFINED) {
|
||||
log.debug(" {d}@{x} => import('{s}')", .{
|
||||
i,
|
||||
got_sym.value,
|
||||
self.getSymbolName(entry.target),
|
||||
});
|
||||
} else {
|
||||
log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
|
||||
i,
|
||||
got_sym.value,
|
||||
entry.target.sym_index,
|
||||
entry.target.file,
|
||||
logSymAttributes(target_sym, &buf),
|
||||
});
|
||||
}
|
||||
}
|
||||
log.debug("{}", .{self.got_table});
|
||||
}
|
||||
|
||||
fn logSections(self: *Coff) void {
|
||||
@ -2484,6 +2495,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
const Module = @import("../Module.zig");
|
||||
const Object = @import("Coff/Object.zig");
|
||||
const Relocation = @import("Coff/Relocation.zig");
|
||||
const TableSection = @import("table_section.zig").TableSection;
|
||||
const StringTable = @import("strtab.zig").StringTable;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const TypedValue = @import("../TypedValue.zig");
|
||||
|
||||
@ -48,17 +48,16 @@ dirty: bool = true,
|
||||
/// Returns address of the target if any.
|
||||
pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
|
||||
switch (self.type) {
|
||||
.got, .got_page, .got_pageoff, .direct, .page, .pageoff => {
|
||||
const maybe_target_atom_index = switch (self.type) {
|
||||
.got, .got_page, .got_pageoff => coff_file.getGotAtomIndexForSymbol(self.target),
|
||||
.direct, .page, .pageoff => coff_file.getAtomIndexForSymbol(self.target),
|
||||
else => unreachable,
|
||||
};
|
||||
const target_atom_index = maybe_target_atom_index orelse return null;
|
||||
.got, .got_page, .got_pageoff => {
|
||||
const got_index = coff_file.got_table.lookup.get(self.target) orelse return null;
|
||||
const header = coff_file.sections.items(.header)[coff_file.got_section_index.?];
|
||||
return header.virtual_address + got_index * coff_file.ptr_width.size();
|
||||
},
|
||||
.direct, .page, .pageoff => {
|
||||
const target_atom_index = coff_file.getAtomIndexForSymbol(self.target) orelse return null;
|
||||
const target_atom = coff_file.getAtom(target_atom_index);
|
||||
return target_atom.getSymbol(coff_file).value;
|
||||
},
|
||||
|
||||
.import, .import_page, .import_pageoff => {
|
||||
const sym = coff_file.getSymbol(self.target);
|
||||
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 {
|
||||
|
||||
/// Returns true if and only if the reloc is dirty AND the target address is available.
|
||||
pub fn isResolvable(self: Relocation, coff_file: *Coff) bool {
|
||||
_ = self.getTargetAddress(coff_file) orelse return false;
|
||||
const addr = self.getTargetAddress(coff_file) orelse return false;
|
||||
if (addr == 0) return false;
|
||||
return self.dirty;
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
const Module = @import("../Module.zig");
|
||||
const Package = @import("../Package.zig");
|
||||
const StringTable = @import("strtab.zig").StringTable;
|
||||
const TableSection = @import("table_section.zig").TableSection;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const TypedValue = @import("../TypedValue.zig");
|
||||
const Value = @import("../value.zig").Value;
|
||||
@ -148,17 +149,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
|
||||
|
||||
local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
/// Same order as in the file. The value is the absolute vaddr value.
|
||||
/// If the vaddr of the executable program header changes, the entire
|
||||
/// offset table needs to be rewritten.
|
||||
offset_table: std.ArrayListUnmanaged(u64) = .{},
|
||||
got_table: TableSection(u32) = .{},
|
||||
|
||||
phdr_table_dirty: bool = false,
|
||||
shdr_table_dirty: bool = false,
|
||||
shstrtab_dirty: bool = false,
|
||||
offset_table_count_dirty: bool = false,
|
||||
got_table_count_dirty: bool = false,
|
||||
|
||||
debug_strtab_dirty: bool = false,
|
||||
debug_abbrev_section_dirty: bool = false,
|
||||
@ -329,8 +326,7 @@ pub fn deinit(self: *Elf) void {
|
||||
self.global_symbols.deinit(gpa);
|
||||
self.global_symbol_free_list.deinit(gpa);
|
||||
self.local_symbol_free_list.deinit(gpa);
|
||||
self.offset_table_free_list.deinit(gpa);
|
||||
self.offset_table.deinit(gpa);
|
||||
self.got_table.deinit(gpa);
|
||||
|
||||
{
|
||||
var it = self.decls.iterator();
|
||||
@ -1289,6 +1285,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
|
||||
assert(!self.shdr_table_dirty);
|
||||
assert(!self.shstrtab_dirty);
|
||||
assert(!self.debug_strtab_dirty);
|
||||
assert(!self.got_table_count_dirty);
|
||||
}
|
||||
|
||||
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 {
|
||||
_ = self.atom_by_index_table.remove(local_sym_index);
|
||||
self.getAtomPtr(atom_index).local_sym_index = 0;
|
||||
|
||||
self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
|
||||
self.got_table.freeEntry(gpa, local_sym_index);
|
||||
}
|
||||
|
||||
fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
|
||||
@ -2191,11 +2188,9 @@ pub fn createAtom(self: *Elf) !Atom.Index {
|
||||
const atom_index = @intCast(Atom.Index, self.atoms.items.len);
|
||||
const atom = try self.atoms.addOne(gpa);
|
||||
const local_sym_index = try self.allocateLocalSymbol();
|
||||
const offset_table_index = try self.allocateGotOffset();
|
||||
try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
|
||||
atom.* = .{
|
||||
.local_sym_index = local_sym_index,
|
||||
.offset_table_index = offset_table_index,
|
||||
.prev_index = null,
|
||||
.next_index = null,
|
||||
};
|
||||
@ -2352,26 +2347,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 {
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn allocateGotOffset(self: *Elf) !u32 {
|
||||
try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
|
||||
|
||||
const index = blk: {
|
||||
if (self.offset_table_free_list.popOrNull()) |index| {
|
||||
log.debug(" (reusing GOT offset at index {d})", .{index});
|
||||
break :blk index;
|
||||
} else {
|
||||
log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len});
|
||||
const index = @intCast(u32, self.offset_table.items.len);
|
||||
_ = self.offset_table.addOneAssumeCapacity();
|
||||
self.offset_table_count_dirty = true;
|
||||
break :blk index;
|
||||
}
|
||||
};
|
||||
|
||||
self.offset_table.items[index] = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
|
||||
const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
|
||||
for (unnamed_consts.items) |atom| {
|
||||
@ -2465,6 +2440,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
|
||||
const decl_metadata = self.decls.get(decl_index).?;
|
||||
const atom_index = decl_metadata.atom;
|
||||
const atom = self.getAtom(atom_index);
|
||||
const local_sym_index = atom.getSymbolIndex().?;
|
||||
|
||||
const shdr_index = decl_metadata.shdr;
|
||||
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
|
||||
local_sym.st_value = vaddr;
|
||||
|
||||
log.debug(" (writing new offset table entry)", .{});
|
||||
self.offset_table.items[atom.offset_table_index] = vaddr;
|
||||
try self.writeOffsetTableEntry(atom.offset_table_index);
|
||||
const got_entry_index = self.got_table.lookup.get(local_sym_index).?;
|
||||
self.got_table.entries.items[got_entry_index] = local_sym_index;
|
||||
try self.writeOffsetTableEntry(got_entry_index);
|
||||
}
|
||||
} else if (code.len < local_sym.st_size) {
|
||||
self.shrinkAtom(atom_index, code.len);
|
||||
@ -2494,7 +2471,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
|
||||
local_sym.st_size = code.len;
|
||||
|
||||
// TODO this write could be avoided if no fields of the symbol were changed.
|
||||
try self.writeSymbol(atom.getSymbolIndex().?);
|
||||
try self.writeSymbol(local_sym_index);
|
||||
} else {
|
||||
const local_sym = atom.getSymbolPtr(self);
|
||||
local_sym.* = .{
|
||||
@ -2509,12 +2486,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
|
||||
errdefer self.freeAtom(atom_index);
|
||||
log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
|
||||
|
||||
self.offset_table.items[atom.offset_table_index] = vaddr;
|
||||
local_sym.st_value = vaddr;
|
||||
local_sym.st_size = code.len;
|
||||
|
||||
try self.writeSymbol(atom.getSymbolIndex().?);
|
||||
try self.writeOffsetTableEntry(atom.offset_table_index);
|
||||
try self.writeSymbol(local_sym_index);
|
||||
const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);
|
||||
try self.writeOffsetTableEntry(got_entry_index);
|
||||
}
|
||||
|
||||
const local_sym = atom.getSymbolPtr(self);
|
||||
@ -2755,12 +2732,12 @@ fn updateLazySymbolAtom(
|
||||
errdefer self.freeAtom(atom_index);
|
||||
log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });
|
||||
|
||||
self.offset_table.items[atom.offset_table_index] = vaddr;
|
||||
local_sym.st_value = vaddr;
|
||||
local_sym.st_size = code.len;
|
||||
|
||||
try self.writeSymbol(local_sym_index);
|
||||
try self.writeOffsetTableEntry(atom.offset_table_index);
|
||||
const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);
|
||||
try self.writeOffsetTableEntry(got_entry_index);
|
||||
|
||||
const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr;
|
||||
const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
|
||||
@ -2989,32 +2966,34 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
|
||||
fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void {
|
||||
const entry_size: u16 = self.archPtrWidthBytes();
|
||||
if (self.offset_table_count_dirty) {
|
||||
const needed_size = self.offset_table.items.len * entry_size;
|
||||
if (self.got_table_count_dirty) {
|
||||
const needed_size = self.got_table.entries.items.len * entry_size;
|
||||
try self.growAllocSection(self.got_section_index.?, needed_size);
|
||||
self.offset_table_count_dirty = false;
|
||||
self.got_table_count_dirty = false;
|
||||
}
|
||||
const endian = self.base.options.target.cpu.arch.endian();
|
||||
const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
|
||||
const off = shdr.sh_offset + @as(u64, entry_size) * index;
|
||||
const phdr = &self.program_headers.items[self.phdr_got_index.?];
|
||||
const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;
|
||||
const got_entry = self.got_table.entries.items[index];
|
||||
const got_value = self.getSymbol(got_entry).st_value;
|
||||
switch (entry_size) {
|
||||
2 => {
|
||||
var buf: [2]u8 = undefined;
|
||||
mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
|
||||
mem.writeInt(u16, &buf, @intCast(u16, got_value), endian);
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
},
|
||||
4 => {
|
||||
var buf: [4]u8 = undefined;
|
||||
mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
|
||||
mem.writeInt(u32, &buf, @intCast(u32, got_value), endian);
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
},
|
||||
8 => {
|
||||
var buf: [8]u8 = undefined;
|
||||
mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
|
||||
mem.writeInt(u64, &buf, got_value, endian);
|
||||
try self.base.file.?.pwriteAll(&buf, off);
|
||||
|
||||
if (self.base.child_pid) |pid| {
|
||||
|
||||
@ -14,9 +14,6 @@ const Elf = @import("../Elf.zig");
|
||||
/// offset table entry.
|
||||
local_sym_index: u32,
|
||||
|
||||
/// This field is undefined for symbols with size = 0.
|
||||
offset_table_index: u32,
|
||||
|
||||
/// Points to the previous and next neighbors, based on the `text_offset`.
|
||||
/// This can be used to find, for example, the capacity of this `TextBlock`.
|
||||
prev_index: ?Index,
|
||||
@ -48,13 +45,24 @@ pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
|
||||
return elf_file.getSymbolName(self.getSymbolIndex().?);
|
||||
}
|
||||
|
||||
/// If entry already exists, returns index to it.
|
||||
/// Otherwise, creates a new entry in the Global Offset Table for this Atom.
|
||||
pub fn getOrCreateOffsetTableEntry(self: Atom, elf_file: *Elf) !u32 {
|
||||
const sym_index = self.getSymbolIndex().?;
|
||||
if (elf_file.got_table.lookup.get(sym_index)) |index| return index;
|
||||
const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index);
|
||||
elf_file.got_table_count_dirty = true;
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
|
||||
assert(self.getSymbolIndex() != null);
|
||||
const sym_index = self.getSymbolIndex().?;
|
||||
const got_entry_index = elf_file.got_table.lookup.get(sym_index).?;
|
||||
const target = elf_file.base.options.target;
|
||||
const ptr_bits = target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
const got = elf_file.program_headers.items[elf_file.phdr_got_index.?];
|
||||
return got.p_vaddr + self.offset_table_index * ptr_bytes;
|
||||
return got.p_vaddr + got_entry_index * ptr_bytes;
|
||||
}
|
||||
|
||||
/// Returns how much room there is to grow in virtual address space.
|
||||
|
||||
1033
src/link/MachO.zig
1033
src/link/MachO.zig
File diff suppressed because it is too large
Load Diff
@ -158,21 +158,6 @@ pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void
|
||||
try gop.value_ptr.append(gpa, binding);
|
||||
}
|
||||
|
||||
pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
|
||||
const gpa = macho_file.base.allocator;
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{
|
||||
macho_file.getSymbolName(binding.target),
|
||||
binding.offset,
|
||||
atom.getSymbolIndex(),
|
||||
});
|
||||
const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
try gop.value_ptr.append(gpa, binding);
|
||||
}
|
||||
|
||||
pub fn resolveRelocations(
|
||||
macho_file: *MachO,
|
||||
atom_index: Index,
|
||||
@ -193,6 +178,4 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
|
||||
if (removed_rebases) |*rebases| rebases.value.deinit(gpa);
|
||||
var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
|
||||
if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
|
||||
var removed_lazy_bindings = macho_file.lazy_bindings.fetchOrderedRemove(atom_index);
|
||||
if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(gpa);
|
||||
}
|
||||
|
||||
@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
|
||||
.got_load => blk: {
|
||||
const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
|
||||
const got_entry = macho_file.got_table.entries.items[got_index];
|
||||
break :blk got_entry.getSymbol(macho_file);
|
||||
break :blk macho_file.getSymbol(got_entry);
|
||||
},
|
||||
};
|
||||
if (sym.n_value == reloc.prev_vaddr) continue;
|
||||
@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
|
||||
.got_load => blk: {
|
||||
const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
|
||||
const got_entry = macho_file.got_table.entries.items[got_index];
|
||||
break :blk got_entry.getName(macho_file);
|
||||
break :blk macho_file.getSymbolName(got_entry);
|
||||
},
|
||||
};
|
||||
const sect = &self.sections.items[self.debug_info_section_index.?];
|
||||
|
||||
@ -15,7 +15,7 @@ pub const Type = enum {
|
||||
got,
|
||||
/// RIP-relative displacement
|
||||
signed,
|
||||
/// RIP-relative displacement to GOT pointer to TLV thunk
|
||||
/// RIP-relative displacement to a TLV thunk
|
||||
tlv,
|
||||
|
||||
// aarch64
|
||||
@ -39,25 +39,35 @@ pub const Type = enum {
|
||||
|
||||
/// Returns true if and only if the reloc is dirty AND the target address is available.
|
||||
pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {
|
||||
_ = self.getTargetAtomIndex(macho_file) orelse return false;
|
||||
const addr = self.getTargetBaseAddress(macho_file) orelse return false;
|
||||
if (addr == 0) return false;
|
||||
return self.dirty;
|
||||
}
|
||||
|
||||
pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
|
||||
return switch (self.type) {
|
||||
.got, .got_page, .got_pageoff => macho_file.got_table.getAtomIndex(macho_file, self.target),
|
||||
.tlv => {
|
||||
const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse
|
||||
return null;
|
||||
const thunk_atom = macho_file.getAtom(thunk_atom_index);
|
||||
return macho_file.got_table.getAtomIndex(macho_file, thunk_atom.getSymbolWithLoc());
|
||||
pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
|
||||
switch (self.type) {
|
||||
.got, .got_page, .got_pageoff => {
|
||||
const got_index = macho_file.got_table.lookup.get(self.target) orelse return null;
|
||||
const header = macho_file.sections.items(.header)[macho_file.got_section_index.?];
|
||||
return header.addr + got_index * @sizeOf(u64);
|
||||
},
|
||||
.branch => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index|
|
||||
index
|
||||
else
|
||||
macho_file.getAtomIndexForSymbol(self.target),
|
||||
else => macho_file.getAtomIndexForSymbol(self.target),
|
||||
};
|
||||
.tlv => {
|
||||
const atom_index = macho_file.tlv_table.get(self.target) orelse return null;
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
return atom.getSymbol(macho_file).n_value;
|
||||
},
|
||||
.branch => {
|
||||
if (macho_file.stub_table.lookup.get(self.target)) |index| {
|
||||
const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
|
||||
return header.addr +
|
||||
index * @import("stubs.zig").calcStubEntrySize(macho_file.base.options.target.cpu.arch);
|
||||
}
|
||||
const atom_index = macho_file.getAtomIndexForSymbol(self.target) orelse return null;
|
||||
const atom = macho_file.getAtom(atom_index);
|
||||
return atom.getSymbol(macho_file).n_value;
|
||||
},
|
||||
else => return macho_file.getSymbol(self.target).n_value,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
const source_sym = atom.getSymbol(macho_file);
|
||||
const source_addr = source_sym.n_value + self.offset;
|
||||
|
||||
const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
|
||||
const target_atom = macho_file.getAtom(target_atom_index);
|
||||
|
||||
const target_base_addr = self.getTargetBaseAddress(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
|
||||
const target_addr: i64 = switch (self.type) {
|
||||
.tlv_initializer => blk: {
|
||||
assert(self.addend == 0); // Addend here makes no sense.
|
||||
const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?];
|
||||
const target_sym = target_atom.getSymbol(macho_file);
|
||||
break :blk @intCast(i64, target_sym.n_value - header.addr);
|
||||
break :blk @intCast(i64, target_base_addr - header.addr);
|
||||
},
|
||||
else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend,
|
||||
else => @intCast(i64, target_base_addr) + self.addend,
|
||||
};
|
||||
|
||||
log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
|
||||
@ -189,11 +196,48 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8
|
||||
}
|
||||
}
|
||||
|
||||
inline fn isArithmeticOp(inst: *const [4]u8) bool {
|
||||
pub inline fn isArithmeticOp(inst: *const [4]u8) bool {
|
||||
const group_decode = @truncate(u5, inst[3]);
|
||||
return ((group_decode >> 2) == 4);
|
||||
}
|
||||
|
||||
pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
|
||||
const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
|
||||
return math.cast(i32, disp) orelse error.Overflow;
|
||||
}
|
||||
|
||||
pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
|
||||
const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
|
||||
return math.cast(i28, disp) orelse error.Overflow;
|
||||
}
|
||||
|
||||
pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
|
||||
const source_page = @intCast(i32, source_addr >> 12);
|
||||
const target_page = @intCast(i32, target_addr >> 12);
|
||||
const pages = @intCast(i21, target_page - source_page);
|
||||
return pages;
|
||||
}
|
||||
|
||||
pub const PageOffsetInstKind = enum {
|
||||
arithmetic,
|
||||
load_store_8,
|
||||
load_store_16,
|
||||
load_store_32,
|
||||
load_store_64,
|
||||
load_store_128,
|
||||
};
|
||||
|
||||
pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
|
||||
const narrowed = @truncate(u12, target_addr);
|
||||
return switch (kind) {
|
||||
.arithmetic, .load_store_8 => narrowed,
|
||||
.load_store_16 => try math.divExact(u12, narrowed, 2),
|
||||
.load_store_32 => try math.divExact(u12, narrowed, 4),
|
||||
.load_store_64 => try math.divExact(u12, narrowed, 8),
|
||||
.load_store_128 => try math.divExact(u12, narrowed, 16),
|
||||
};
|
||||
}
|
||||
|
||||
const Relocation = @This();
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
@ -21,6 +21,7 @@ const Allocator = mem.Allocator;
|
||||
const Arch = std.Target.Cpu.Arch;
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Object = @import("Object.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
@ -571,7 +572,7 @@ fn resolveRelocsArm64(
|
||||
zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
|
||||
});
|
||||
|
||||
const displacement = if (calcPcRelativeDisplacementArm64(
|
||||
const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
|
||||
source_addr,
|
||||
zld.getSymbol(actual_target).n_value,
|
||||
)) |disp| blk: {
|
||||
@ -585,7 +586,7 @@ fn resolveRelocsArm64(
|
||||
actual_target,
|
||||
).?);
|
||||
log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
|
||||
break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
|
||||
break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
|
||||
};
|
||||
|
||||
const code = atom_code[rel_offset..][0..4];
|
||||
@ -607,7 +608,7 @@ fn resolveRelocsArm64(
|
||||
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
|
||||
const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr));
|
||||
const pages = @bitCast(u21, Relocation.calcNumberOfPages(source_addr, adjusted_target_addr));
|
||||
const code = atom_code[rel_offset..][0..4];
|
||||
var inst = aarch64.Instruction{
|
||||
.pc_relative_address = mem.bytesToValue(meta.TagPayload(
|
||||
@ -627,8 +628,8 @@ fn resolveRelocsArm64(
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
|
||||
const code = atom_code[rel_offset..][0..4];
|
||||
if (isArithmeticOp(code)) {
|
||||
const off = try calcPageOffset(adjusted_target_addr, .arithmetic);
|
||||
if (Relocation.isArithmeticOp(code)) {
|
||||
const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
|
||||
var inst = aarch64.Instruction{
|
||||
.add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
@ -644,11 +645,11 @@ fn resolveRelocsArm64(
|
||||
aarch64.Instruction.load_store_register,
|
||||
), code),
|
||||
};
|
||||
const off = try calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
|
||||
const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
|
||||
0 => if (inst.load_store_register.v == 1)
|
||||
PageOffsetInstKind.load_store_128
|
||||
Relocation.PageOffsetInstKind.load_store_128
|
||||
else
|
||||
PageOffsetInstKind.load_store_8,
|
||||
Relocation.PageOffsetInstKind.load_store_8,
|
||||
1 => .load_store_16,
|
||||
2 => .load_store_32,
|
||||
3 => .load_store_64,
|
||||
@ -665,7 +666,7 @@ fn resolveRelocsArm64(
|
||||
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
|
||||
const off = try calcPageOffset(adjusted_target_addr, .load_store_64);
|
||||
const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
|
||||
var inst: aarch64.Instruction = .{
|
||||
.load_store_register = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
@ -689,7 +690,7 @@ fn resolveRelocsArm64(
|
||||
size: u2,
|
||||
};
|
||||
const reg_info: RegInfo = blk: {
|
||||
if (isArithmeticOp(code)) {
|
||||
if (Relocation.isArithmeticOp(code)) {
|
||||
const inst = mem.bytesToValue(meta.TagPayload(
|
||||
aarch64.Instruction,
|
||||
aarch64.Instruction.add_subtract_immediate,
|
||||
@ -716,7 +717,7 @@ fn resolveRelocsArm64(
|
||||
.load_store_register = .{
|
||||
.rt = reg_info.rd,
|
||||
.rn = reg_info.rn,
|
||||
.offset = try calcPageOffset(adjusted_target_addr, .load_store_64),
|
||||
.offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
|
||||
.opc = 0b01,
|
||||
.op1 = 0b01,
|
||||
.v = 0,
|
||||
@ -726,7 +727,7 @@ fn resolveRelocsArm64(
|
||||
.add_subtract_immediate = .{
|
||||
.rd = reg_info.rd,
|
||||
.rn = reg_info.rn,
|
||||
.imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic),
|
||||
.imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
|
||||
.sh = 0,
|
||||
.s = 0,
|
||||
.op = 0,
|
||||
@ -858,7 +859,7 @@ fn resolveRelocsX86(
|
||||
const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
|
||||
const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
|
||||
},
|
||||
|
||||
@ -868,7 +869,7 @@ fn resolveRelocsX86(
|
||||
const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
|
||||
const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
|
||||
},
|
||||
|
||||
@ -876,7 +877,7 @@ fn resolveRelocsX86(
|
||||
const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
|
||||
const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
|
||||
if (zld.tlv_ptr_table.get(target) == null) {
|
||||
// We need to rewrite the opcode from movq to leaq.
|
||||
@ -913,7 +914,7 @@ fn resolveRelocsX86(
|
||||
|
||||
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
|
||||
|
||||
const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
|
||||
mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
|
||||
},
|
||||
|
||||
@ -955,11 +956,6 @@ fn resolveRelocsX86(
|
||||
}
|
||||
}
|
||||
|
||||
inline fn isArithmeticOp(inst: *const [4]u8) bool {
|
||||
const group_decode = @truncate(u5, inst[3]);
|
||||
return ((group_decode >> 2) == 4);
|
||||
}
|
||||
|
||||
pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
|
||||
const atom = zld.getAtom(atom_index);
|
||||
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_
|
||||
return relocs[cache.start..][0..cache.len];
|
||||
}
|
||||
|
||||
pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
|
||||
const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
|
||||
return math.cast(i32, disp) orelse error.Overflow;
|
||||
}
|
||||
|
||||
pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
|
||||
const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
|
||||
return math.cast(i28, disp) orelse error.Overflow;
|
||||
}
|
||||
|
||||
pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
|
||||
const source_page = @intCast(i32, source_addr >> 12);
|
||||
const target_page = @intCast(i32, target_addr >> 12);
|
||||
const pages = @intCast(i21, target_page - source_page);
|
||||
return pages;
|
||||
}
|
||||
|
||||
const PageOffsetInstKind = enum {
|
||||
arithmetic,
|
||||
load_store_8,
|
||||
load_store_16,
|
||||
load_store_32,
|
||||
load_store_64,
|
||||
load_store_128,
|
||||
};
|
||||
|
||||
pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
|
||||
const narrowed = @truncate(u12, target_addr);
|
||||
return switch (kind) {
|
||||
.arithmetic, .load_store_8 => narrowed,
|
||||
.load_store_16 => try math.divExact(u12, narrowed, 2),
|
||||
.load_store_32 => try math.divExact(u12, narrowed, 4),
|
||||
.load_store_64 => try math.divExact(u12, narrowed, 8),
|
||||
.load_store_128 => try math.divExact(u12, narrowed, 16),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
|
||||
switch (zld.options.target.cpu.arch) {
|
||||
.aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
|
||||
|
||||
@ -9,6 +9,7 @@ const log = std.log.scoped(.eh_frame);
|
||||
const Allocator = mem.Allocator;
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
@ -368,7 +369,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
||||
const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
|
||||
const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
|
||||
const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
|
||||
const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
||||
mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
|
||||
},
|
||||
else => unreachable,
|
||||
|
||||
161
src/link/MachO/stubs.zig
Normal file
161
src/link/MachO/stubs.zig
Normal file
@ -0,0 +1,161 @@
|
||||
const std = @import("std");
|
||||
const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
|
||||
const Relocation = @import("Relocation.zig");
|
||||
|
||||
pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 15,
|
||||
.aarch64 => 6 * @sizeOf(u32),
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 6,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn calcStubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u4 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => 1,
|
||||
.aarch64 => 2 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeStubHelperPreambleCode(args: struct {
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
source_addr: u64,
|
||||
dyld_private_addr: u64,
|
||||
dyld_stub_binder_got_addr: u64,
|
||||
}, writer: anytype) !void {
|
||||
switch (args.cpu_arch) {
|
||||
.x86_64 => {
|
||||
try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
|
||||
{
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(
|
||||
args.source_addr + 3,
|
||||
args.dyld_private_addr,
|
||||
0,
|
||||
);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
|
||||
{
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(
|
||||
args.source_addr + 11,
|
||||
args.dyld_stub_binder_got_addr,
|
||||
0,
|
||||
);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
{
|
||||
const pages = Relocation.calcNumberOfPages(args.source_addr, args.dyld_private_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
|
||||
}
|
||||
{
|
||||
const off = try Relocation.calcPageOffset(args.dyld_private_addr, .arithmetic);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
|
||||
}
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.stp(
|
||||
.x16,
|
||||
.x17,
|
||||
aarch64.Register.sp,
|
||||
aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
|
||||
).toU32());
|
||||
{
|
||||
const pages = Relocation.calcNumberOfPages(args.source_addr + 12, args.dyld_stub_binder_got_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
}
|
||||
{
|
||||
const off = try Relocation.calcPageOffset(args.dyld_stub_binder_got_addr, .load_store_64);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
|
||||
.x16,
|
||||
.x16,
|
||||
aarch64.Instruction.LoadStoreOffset.imm(off),
|
||||
).toU32());
|
||||
}
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeStubHelperCode(args: struct {
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
source_addr: u64,
|
||||
target_addr: u64,
|
||||
}, writer: anytype) !void {
|
||||
switch (args.cpu_arch) {
|
||||
.x86_64 => {
|
||||
try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
|
||||
{
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 6, args.target_addr, 0);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
const stub_size: u4 = 3 * @sizeOf(u32);
|
||||
const literal = blk: {
|
||||
const div_res = try std.math.divExact(u64, stub_size - @sizeOf(u32), 4);
|
||||
break :blk std.math.cast(u18, div_res) orelse return error.Overflow;
|
||||
};
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
|
||||
.w16,
|
||||
literal,
|
||||
).toU32());
|
||||
{
|
||||
const disp = try Relocation.calcPcRelativeDisplacementArm64(args.source_addr + 4, args.target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
|
||||
}
|
||||
try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeStubCode(args: struct {
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
source_addr: u64,
|
||||
target_addr: u64,
|
||||
}, writer: anytype) !void {
|
||||
switch (args.cpu_arch) {
|
||||
.x86_64 => {
|
||||
try writer.writeAll(&.{ 0xff, 0x25 });
|
||||
{
|
||||
const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 2, args.target_addr, 0);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
{
|
||||
const pages = Relocation.calcNumberOfPages(args.source_addr, args.target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
}
|
||||
{
|
||||
const off = try Relocation.calcPageOffset(args.target_addr, .load_store_64);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
|
||||
.x16,
|
||||
.x16,
|
||||
aarch64.Instruction.LoadStoreOffset.imm(off),
|
||||
).toU32());
|
||||
}
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ const aarch64 = @import("../../arch/aarch64/bits.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("ZldAtom.zig");
|
||||
const AtomIndex = @import("zld.zig").AtomIndex;
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
|
||||
const Zld = @import("zld.zig").Zld;
|
||||
|
||||
@ -317,7 +318,7 @@ fn isReachable(
|
||||
const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
|
||||
const is_via_got = Atom.relocRequiresGot(zld, rel);
|
||||
const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
|
||||
_ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
|
||||
_ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -364,9 +365,9 @@ pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
|
||||
if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
|
||||
} else unreachable;
|
||||
|
||||
const pages = Atom.calcNumberOfPages(source_addr, target_addr);
|
||||
const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
const off = try Atom.calcPageOffset(target_addr, .arithmetic);
|
||||
const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ const link = @import("../../link.zig");
|
||||
const load_commands = @import("load_commands.zig");
|
||||
const thunks = @import("thunks.zig");
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
const stub_helpers = @import("stubs.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Archive = @import("Archive.zig");
|
||||
@ -666,59 +667,17 @@ pub const Zld = struct {
|
||||
const entry = self.got_entries.items[index];
|
||||
break :blk entry.getAtomSymbol(self).n_value;
|
||||
};
|
||||
switch (cpu_arch) {
|
||||
.x86_64 => {
|
||||
try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
|
||||
{
|
||||
const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 3, dyld_private_addr, 0);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
|
||||
{
|
||||
const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 11, dyld_stub_binder_got_addr, 0);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
{
|
||||
const pages = Atom.calcNumberOfPages(source_addr, dyld_private_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
|
||||
}
|
||||
{
|
||||
const off = try Atom.calcPageOffset(dyld_private_addr, .arithmetic);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
|
||||
}
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.stp(
|
||||
.x16,
|
||||
.x17,
|
||||
aarch64.Register.sp,
|
||||
aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
|
||||
).toU32());
|
||||
{
|
||||
const pages = Atom.calcNumberOfPages(source_addr + 12, dyld_stub_binder_got_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
}
|
||||
{
|
||||
const off = try Atom.calcPageOffset(dyld_stub_binder_got_addr, .load_store_64);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
|
||||
.x16,
|
||||
.x16,
|
||||
aarch64.Instruction.LoadStoreOffset.imm(off),
|
||||
).toU32());
|
||||
}
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
try stub_helpers.writeStubHelperPreambleCode(.{
|
||||
.cpu_arch = cpu_arch,
|
||||
.source_addr = source_addr,
|
||||
.dyld_private_addr = dyld_private_addr,
|
||||
.dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
|
||||
}, writer);
|
||||
}
|
||||
|
||||
pub fn createStubHelperAtom(self: *Zld) !AtomIndex {
|
||||
const cpu_arch = self.options.target.cpu.arch;
|
||||
const stub_size: u4 = switch (cpu_arch) {
|
||||
.x86_64 => 10,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
};
|
||||
const stub_size = stub_helpers.calcStubHelperEntrySize(cpu_arch);
|
||||
const alignment: u2 = switch (cpu_arch) {
|
||||
.x86_64 => 0,
|
||||
.aarch64 => 2,
|
||||
@ -749,32 +708,11 @@ pub const Zld = struct {
|
||||
const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
|
||||
break :blk sym.n_value;
|
||||
};
|
||||
switch (cpu_arch) {
|
||||
.x86_64 => {
|
||||
try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
|
||||
{
|
||||
const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 6, target_addr, 0);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
const stub_size: u4 = 3 * @sizeOf(u32);
|
||||
const literal = blk: {
|
||||
const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
|
||||
break :blk math.cast(u18, div_res) orelse return error.Overflow;
|
||||
};
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
|
||||
.w16,
|
||||
literal,
|
||||
).toU32());
|
||||
{
|
||||
const disp = try Atom.calcPcRelativeDisplacementArm64(source_addr + 4, target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
|
||||
}
|
||||
try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
try stub_helpers.writeStubHelperCode(.{
|
||||
.cpu_arch = cpu_arch,
|
||||
.source_addr = source_addr,
|
||||
.target_addr = target_addr,
|
||||
}, writer);
|
||||
}
|
||||
|
||||
pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {
|
||||
@ -819,11 +757,7 @@ pub const Zld = struct {
|
||||
.aarch64 => 2,
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
const stub_size: u4 = switch (cpu_arch) {
|
||||
.x86_64 => 6,
|
||||
.aarch64 => 3 * @sizeOf(u32),
|
||||
else => unreachable, // unhandled architecture type
|
||||
};
|
||||
const stub_size = stub_helpers.calcStubEntrySize(cpu_arch);
|
||||
const sym_index = try self.allocateSymbol();
|
||||
const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
|
||||
const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
|
||||
@ -863,31 +797,11 @@ pub const Zld = struct {
|
||||
const sym = self.getSymbol(atom.getSymbolWithLoc());
|
||||
break :blk sym.n_value;
|
||||
};
|
||||
switch (cpu_arch) {
|
||||
.x86_64 => {
|
||||
try writer.writeAll(&.{ 0xff, 0x25 });
|
||||
{
|
||||
const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 2, target_addr, 0);
|
||||
try writer.writeIntLittle(i32, disp);
|
||||
}
|
||||
},
|
||||
.aarch64 => {
|
||||
{
|
||||
const pages = Atom.calcNumberOfPages(source_addr, target_addr);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
|
||||
}
|
||||
{
|
||||
const off = try Atom.calcPageOffset(target_addr, .load_store_64);
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
|
||||
.x16,
|
||||
.x16,
|
||||
aarch64.Instruction.LoadStoreOffset.imm(off),
|
||||
).toU32());
|
||||
}
|
||||
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
try stub_helpers.writeStubCode(.{
|
||||
.cpu_arch = cpu_arch,
|
||||
.source_addr = source_addr,
|
||||
.target_addr = target_addr,
|
||||
}, writer);
|
||||
}
|
||||
|
||||
fn createTentativeDefAtoms(self: *Zld) !void {
|
||||
@ -2267,11 +2181,7 @@ pub const Zld = struct {
|
||||
assert(self.stub_helper_preamble_sym_index != null);
|
||||
|
||||
const section = self.sections.get(stub_helper_section_index);
|
||||
const stub_offset: u4 = switch (self.options.target.cpu.arch) {
|
||||
.x86_64 => 1,
|
||||
.aarch64 => 2 * @sizeOf(u32),
|
||||
else => unreachable,
|
||||
};
|
||||
const stub_offset = stub_helpers.calcStubOffsetInStubHelper(self.options.target.cpu.arch);
|
||||
const header = section.header;
|
||||
var atom_index = section.first_atom_index;
|
||||
atom_index = self.getAtom(atom_index).next_index.?; // skip preamble
|
||||
|
||||
65
src/link/table_section.zig
Normal file
65
src/link/table_section.zig
Normal file
@ -0,0 +1,65 @@
|
||||
pub fn TableSection(comptime Entry: type) type {
|
||||
return struct {
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
free_list: std.ArrayListUnmanaged(Index) = .{},
|
||||
lookup: std.AutoHashMapUnmanaged(Entry, Index) = .{},
|
||||
|
||||
pub fn deinit(self: *Self, allocator: Allocator) void {
|
||||
self.entries.deinit(allocator);
|
||||
self.free_list.deinit(allocator);
|
||||
self.lookup.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn allocateEntry(self: *Self, allocator: Allocator, entry: Entry) Allocator.Error!Index {
|
||||
try self.entries.ensureUnusedCapacity(allocator, 1);
|
||||
const index = blk: {
|
||||
if (self.free_list.popOrNull()) |index| {
|
||||
log.debug(" (reusing entry index {d})", .{index});
|
||||
break :blk index;
|
||||
} else {
|
||||
log.debug(" (allocating entry at index {d})", .{self.entries.items.len});
|
||||
const index = @intCast(u32, self.entries.items.len);
|
||||
_ = self.entries.addOneAssumeCapacity();
|
||||
break :blk index;
|
||||
}
|
||||
};
|
||||
self.entries.items[index] = entry;
|
||||
try self.lookup.putNoClobber(allocator, entry, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn freeEntry(self: *Self, allocator: Allocator, entry: Entry) void {
|
||||
const index = self.lookup.get(entry) orelse return;
|
||||
self.free_list.append(allocator, index) catch {};
|
||||
self.entries.items[index] = undefined;
|
||||
_ = self.lookup.remove(entry);
|
||||
}
|
||||
|
||||
pub fn count(self: Self) usize {
|
||||
return self.entries.items.len;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: Self,
|
||||
comptime unused_format_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = options;
|
||||
comptime assert(unused_format_string.len == 0);
|
||||
try writer.writeAll("TableSection:\n");
|
||||
for (self.entries.items, 0..) |entry, i| {
|
||||
try writer.print(" {d} => {}\n", .{ i, entry });
|
||||
}
|
||||
}
|
||||
|
||||
const Self = @This();
|
||||
pub const Index = u32;
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.link);
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
@ -4,7 +4,7 @@ const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer if (gpa.deinit()) @panic("found memory leaks");
|
||||
defer if (gpa.deinit() == .leak) @panic("found memory leaks");
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var it = try std.process.argsWithAllocator(allocator);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user