aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-06-20 12:45:51 +0200
committerJakub Konka <kubkon@jakubkonka.com>2021-06-24 14:45:45 +0200
commitbc78b02c04143b7e1b32aceacdcf44130026a7a7 (patch)
treed0d20c362872c1dea62a50f694e309ca7c685645
parent09b46198ff8d64c9884a0cf13788855b4d4bbe2d (diff)
downloadzig-bc78b02c04143b7e1b32aceacdcf44130026a7a7.tar.gz
zig-bc78b02c04143b7e1b32aceacdcf44130026a7a7.zip
zld: introduce Stub.zig which represents parsed stub file
Instead of trying to fit a stub file into the frame of a Dylib struct, I think it makes more sense to keep them as separate entities with possibly shared interface (which would be added in the future). This cleaned up a lot of logic in Dylib as well as Stub. Also, while here I've made creating actual *Symbols lazy in the sense Dylib and Stub only store hash maps of symbol names that they expose but we defer create and referencing given dylib/stub until link time when a symbol is actually referenced. This should reduce memory usage and speed things up a bit.
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/link/MachO/Archive.zig9
-rw-r--r--src/link/MachO/Dylib.zig119
-rw-r--r--src/link/MachO/Object.zig9
-rw-r--r--src/link/MachO/Stub.zig130
-rw-r--r--src/link/MachO/Symbol.zig16
-rw-r--r--src/link/MachO/Zld.zig286
7 files changed, 345 insertions, 225 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f279990e27..edd24d282e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -579,6 +579,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
+ "${CMAKE_SOURCE_DIR}/src/link/MachO/Stub.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
index 13dadb1dd2..48bc33f0fa 100644
--- a/src/link/MachO/Archive.zig
+++ b/src/link/MachO/Archive.zig
@@ -234,8 +234,11 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
return object;
}
-pub fn isArchive(file: fs.File) bool {
- const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch return false;
- file.seekTo(0) catch return false;
+pub fn isArchive(file: fs.File) !bool {
+ const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch |err| switch (err) {
+ error.EndOfStream => return false,
+ else => |e| return e,
+ };
+ try file.seekTo(0);
return mem.eql(u8, &magic, Archive.ARMAG);
}
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index 3ac1ff5ed2..bcf2433dd7 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -9,7 +9,6 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Symbol = @import("Symbol.zig");
-const LibStub = @import("../tapi.zig").LibStub;
usingnamespace @import("commands.zig");
@@ -29,7 +28,10 @@ id_cmd_index: ?u16 = null,
id: ?Id = null,
-symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
+/// Parsed symbol table represented as hash map of symbols'
+/// names. We can and should defer creating *Symbols until
+/// a symbol is referenced by an object file.
+symbols: std.StringArrayHashMapUnmanaged(void) = .{},
pub const Id = struct {
name: []const u8,
@@ -52,9 +54,8 @@ pub fn deinit(self: *Dylib) void {
}
self.load_commands.deinit(self.allocator);
- for (self.symbols.values()) |value| {
- value.deinit(self.allocator);
- self.allocator.destroy(value);
+ for (self.symbols.keys()) |key| {
+ self.allocator.free(key);
}
self.symbols.deinit(self.allocator);
@@ -171,103 +172,33 @@ pub fn parseSymbols(self: *Dylib) !void {
if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;
const name = try self.allocator.dupe(u8, sym_name);
- const proxy = try self.allocator.create(Symbol.Proxy);
- errdefer self.allocator.destroy(proxy);
-
- proxy.* = .{
- .base = .{
- .@"type" = .proxy,
- .name = name,
- },
- .dylib = self,
- };
-
- try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
+ try self.symbols.putNoClobber(self.allocator, name, {});
}
}
-pub fn isDylib(file: fs.File) bool {
- const header = file.reader().readStruct(macho.mach_header_64) catch return false;
- file.seekTo(0) catch return false;
+pub fn isDylib(file: fs.File) !bool {
+ const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
+ error.EndOfStream => return false,
+ else => |e| return e,
+ };
+ try file.seekTo(0);
return header.filetype == macho.MH_DYLIB;
}
-pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
- assert(lib_stub.inner.len > 0);
+pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol {
+ if (!self.symbols.contains(sym_name)) return null;
- log.debug("parsing shared library from stub '{s}'", .{self.name.?});
+ const name = try self.allocator.dupe(u8, sym_name);
+ const proxy = try self.allocator.create(Symbol.Proxy);
+ errdefer self.allocator.destroy(proxy);
- const umbrella_lib = lib_stub.inner[0];
- self.id = .{
- .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
- // TODO parse from the stub
- .timestamp = 2,
- .current_version = 0,
- .compatibility_version = 0,
- };
-
- const target_string: []const u8 = switch (self.arch.?) {
- .aarch64 => "arm64-macos",
- .x86_64 => "x86_64-macos",
- else => unreachable,
+ proxy.* = .{
+ .base = .{
+ .@"type" = .proxy,
+ .name = name,
+ },
+ .file = .{ .dylib = self },
};
- for (lib_stub.inner) |stub| {
- if (!hasTarget(stub.targets, target_string)) continue;
-
- if (stub.exports) |exports| {
- for (exports) |exp| {
- if (!hasTarget(exp.targets, target_string)) continue;
-
- for (exp.symbols) |sym_name| {
- if (self.symbols.contains(sym_name)) continue;
-
- const name = try self.allocator.dupe(u8, sym_name);
- const proxy = try self.allocator.create(Symbol.Proxy);
- errdefer self.allocator.destroy(proxy);
-
- proxy.* = .{
- .base = .{
- .@"type" = .proxy,
- .name = name,
- },
- .dylib = self,
- };
-
- try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
- }
- }
- }
-
- if (stub.reexports) |reexports| {
- for (reexports) |reexp| {
- if (!hasTarget(reexp.targets, target_string)) continue;
-
- for (reexp.symbols) |sym_name| {
- if (self.symbols.contains(sym_name)) continue;
-
- const name = try self.allocator.dupe(u8, sym_name);
- const proxy = try self.allocator.create(Symbol.Proxy);
- errdefer self.allocator.destroy(proxy);
-
- proxy.* = .{
- .base = .{
- .@"type" = .proxy,
- .name = name,
- },
- .dylib = self,
- };
-
- try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
- }
- }
- }
- }
-}
-
-fn hasTarget(targets: []const []const u8, target: []const u8) bool {
- for (targets) |t| {
- if (mem.eql(u8, t, target)) return true;
- }
- return false;
+ return &proxy.base;
}
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 5b19d81698..64db2fe091 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -534,8 +534,11 @@ pub fn parseDataInCode(self: *Object) !void {
}
}
-pub fn isObject(file: fs.File) bool {
- const header = file.reader().readStruct(macho.mach_header_64) catch return false;
- file.seekTo(0) catch return false;
+pub fn isObject(file: fs.File) !bool {
+ const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
+ error.EndOfStream => return false,
+ else => |e| return e,
+ };
+ try file.seekTo(0);
return header.filetype == macho.MH_OBJECT;
}
diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig
new file mode 100644
index 0000000000..3e1474539d
--- /dev/null
+++ b/src/link/MachO/Stub.zig
@@ -0,0 +1,130 @@
+const Stub = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const fs = std.fs;
+const log = std.log.scoped(.stub);
+const macho = std.macho;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Symbol = @import("Symbol.zig");
+pub const LibStub = @import("../tapi.zig").LibStub;
+
+allocator: *Allocator,
+arch: ?std.Target.Cpu.Arch = null,
+lib_stub: ?LibStub = null,
+name: ?[]const u8 = null,
+
+ordinal: ?u16 = null,
+
+id: ?Id = null,
+
+/// Parsed symbol table represented as hash map of symbols'
+/// names. We can and should defer creating *Symbols until
+/// a symbol is referenced by an object file.
+symbols: std.StringArrayHashMapUnmanaged(void) = .{},
+
+pub const Id = struct {
+ name: []const u8,
+ timestamp: u32,
+ current_version: u32,
+ compatibility_version: u32,
+
+ pub fn deinit(id: *Id, allocator: *Allocator) void {
+ allocator.free(id.name);
+ }
+};
+
+pub fn init(allocator: *Allocator) Stub {
+ return .{ .allocator = allocator };
+}
+
+pub fn deinit(self: *Stub) void {
+ self.symbols.deinit(self.allocator);
+
+ if (self.lib_stub) |*lib_stub| {
+ lib_stub.deinit();
+ }
+
+ if (self.name) |name| {
+ self.allocator.free(name);
+ }
+
+ if (self.id) |*id| {
+ id.deinit(self.allocator);
+ }
+}
+
+pub fn parse(self: *Stub) !void {
+ const lib_stub = self.lib_stub orelse return error.EmptyStubFile;
+ if (lib_stub.inner.len == 0) return error.EmptyStubFile;
+
+ log.debug("parsing shared library from stub '{s}'", .{self.name.?});
+
+ const umbrella_lib = lib_stub.inner[0];
+ self.id = .{
+ .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
+ // TODO parse from the stub
+ .timestamp = 2,
+ .current_version = 0,
+ .compatibility_version = 0,
+ };
+
+ const target_string: []const u8 = switch (self.arch.?) {
+ .aarch64 => "arm64-macos",
+ .x86_64 => "x86_64-macos",
+ else => unreachable,
+ };
+
+ for (lib_stub.inner) |stub| {
+ if (!hasTarget(stub.targets, target_string)) continue;
+
+ if (stub.exports) |exports| {
+ for (exports) |exp| {
+ if (!hasTarget(exp.targets, target_string)) continue;
+
+ for (exp.symbols) |sym_name| {
+ if (self.symbols.contains(sym_name)) continue;
+ try self.symbols.putNoClobber(self.allocator, sym_name, {});
+ }
+ }
+ }
+
+ if (stub.reexports) |reexports| {
+ for (reexports) |reexp| {
+ if (!hasTarget(reexp.targets, target_string)) continue;
+
+ for (reexp.symbols) |sym_name| {
+ if (self.symbols.contains(sym_name)) continue;
+ try self.symbols.putNoClobber(self.allocator, sym_name, {});
+ }
+ }
+ }
+ }
+}
+
+fn hasTarget(targets: []const []const u8, target: []const u8) bool {
+ for (targets) |t| {
+ if (mem.eql(u8, t, target)) return true;
+ }
+ return false;
+}
+
+pub fn createProxy(self: *Stub, sym_name: []const u8) !?*Symbol {
+ if (!self.symbols.contains(sym_name)) return null;
+
+ const name = try self.allocator.dupe(u8, sym_name);
+ const proxy = try self.allocator.create(Symbol.Proxy);
+ errdefer self.allocator.destroy(proxy);
+
+ proxy.* = .{
+ .base = .{
+ .@"type" = .proxy,
+ .name = name,
+ },
+ .file = .{ .stub = self },
+ };
+
+ return &proxy.base;
+}
diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig
index 35656852ea..2286b1ea93 100644
--- a/src/link/MachO/Symbol.zig
+++ b/src/link/MachO/Symbol.zig
@@ -7,6 +7,7 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Dylib = @import("Dylib.zig");
const Object = @import("Object.zig");
+const Stub = @import("Stub.zig");
pub const Type = enum {
regular,
@@ -84,11 +85,22 @@ pub const Regular = struct {
pub const Proxy = struct {
base: Symbol,
- /// Dylib where to locate this symbol.
+ /// Dylib or stub where to locate this symbol.
/// null means self-reference.
- dylib: ?*Dylib = null,
+ file: ?union(enum) {
+ dylib: *Dylib,
+ stub: *Stub,
+ } = null,
pub const base_type: Symbol.Type = .proxy;
+
+ pub fn dylibOrdinal(proxy: *Proxy) u16 {
+ const file = proxy.file orelse return 0;
+ return switch (file) {
+ .dylib => |dylib| dylib.ordinal.?,
+ .stub => |stub| stub.ordinal.?,
+ };
+ }
};
pub const Unresolved = struct {
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig
index 564da387c3..0a9d209c61 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -16,8 +16,8 @@ const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");
const CodeSignature = @import("CodeSignature.zig");
const Dylib = @import("Dylib.zig");
-const LibStub = @import("../tapi.zig").LibStub;
const Object = @import("Object.zig");
+const Stub = @import("Stub.zig");
const Symbol = @import("Symbol.zig");
const Trie = @import("Trie.zig");
@@ -38,6 +38,10 @@ stack_size: u64 = 0,
objects: std.ArrayListUnmanaged(*Object) = .{},
archives: std.ArrayListUnmanaged(*Archive) = .{},
dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
+lib_stubs: std.ArrayListUnmanaged(*Stub) = .{},
+
+libsystem_stub_index: ?u16 = null,
+next_dylib_ordinal: u16 = 1,
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
@@ -153,9 +157,20 @@ pub fn deinit(self: *Zld) void {
}
self.dylibs.deinit(self.allocator);
+ for (self.lib_stubs.items) |stub| {
+ stub.deinit();
+ self.allocator.destroy(stub);
+ }
+ self.lib_stubs.deinit(self.allocator);
+
+ for (self.imports.values()) |proxy| {
+ proxy.deinit(self.allocator);
+ self.allocator.destroy(proxy);
+ }
+ self.imports.deinit(self.allocator);
+
self.tentatives.deinit(self.allocator);
self.globals.deinit(self.allocator);
- self.imports.deinit(self.allocator);
self.unresolved.deinit(self.allocator);
self.strtab.deinit(self.allocator);
@@ -245,9 +260,11 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
dylib,
stub,
},
- file: fs.File,
+ origin: union {
+ file: fs.File,
+ stub: Stub.LibStub,
+ },
name: []const u8,
- stub: ?LibStub = null,
};
var classified = std.ArrayList(Input).init(self.allocator);
defer classified.deinit();
@@ -262,45 +279,45 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
};
try_object: {
- if (!Object.isObject(file)) break :try_object;
+ if (!(try Object.isObject(file))) break :try_object;
try classified.append(.{
.kind = .object,
- .file = file,
+ .origin = .{ .file = file },
.name = full_path,
});
continue;
}
try_archive: {
- if (!Archive.isArchive(file)) break :try_archive;
+ if (!(try Archive.isArchive(file))) break :try_archive;
try classified.append(.{
.kind = .archive,
- .file = file,
+ .origin = .{ .file = file },
.name = full_path,
});
continue;
}
try_dylib: {
- if (!Dylib.isDylib(file)) break :try_dylib;
+ if (!(try Dylib.isDylib(file))) break :try_dylib;
try classified.append(.{
.kind = .dylib,
- .file = file,
+ .origin = .{ .file = file },
.name = full_path,
});
continue;
}
try_stub: {
- var lib_stub = LibStub.loadFromFile(self.allocator, file) catch {
+ var lib_stub = Stub.LibStub.loadFromFile(self.allocator, file) catch {
break :try_stub;
};
try classified.append(.{
.kind = .stub,
- .file = file,
+ .origin = .{ .stub = lib_stub },
.name = full_path,
- .stub = lib_stub,
});
+ file.close();
continue;
}
@@ -318,7 +335,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
object.* = Object.init(self.allocator);
object.arch = self.arch.?;
object.name = input.name;
- object.file = input.file;
+ object.file = input.origin.file;
+
try object.parse();
try self.objects.append(self.allocator, object);
},
@@ -329,40 +347,34 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
archive.* = Archive.init(self.allocator);
archive.arch = self.arch.?;
archive.name = input.name;
- archive.file = input.file;
+ archive.file = input.origin.file;
+
try archive.parse();
try self.archives.append(self.allocator, archive);
},
- .dylib, .stub => {
+ .dylib => {
const dylib = try self.allocator.create(Dylib);
errdefer self.allocator.destroy(dylib);
dylib.* = Dylib.init(self.allocator);
dylib.arch = self.arch.?;
dylib.name = input.name;
- dylib.file = input.file;
- dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
+ dylib.file = input.origin.file;
- // TODO Defer parsing of the dylibs until they are actually needed
- if (input.stub) |stub| {
- try dylib.parseFromStub(stub);
- } else {
- try dylib.parse();
- }
+ try dylib.parse();
try self.dylibs.append(self.allocator, dylib);
+ },
+ .stub => {
+ const stub = try self.allocator.create(Stub);
+ errdefer self.allocator.destroy(stub);
- // Add LC_LOAD_DYLIB command
- const dylib_id = dylib.id orelse unreachable;
- var dylib_cmd = try createLoadDylibCommand(
- self.allocator,
- dylib_id.name,
- dylib_id.timestamp,
- dylib_id.current_version,
- dylib_id.compatibility_version,
- );
- errdefer dylib_cmd.deinit(self.allocator);
-
- try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
+ stub.* = Stub.init(self.allocator);
+ stub.arch = self.arch.?;
+ stub.name = input.name;
+ stub.lib_stub = input.origin.stub;
+
+ try stub.parse();
+ try self.lib_stubs.append(self.allocator, stub);
},
}
}
@@ -372,7 +384,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
for (libs) |lib| {
const file = try fs.cwd().openFile(lib, .{});
- if (Dylib.isDylib(file)) {
+ if (try Dylib.isDylib(file)) {
const dylib = try self.allocator.create(Dylib);
errdefer self.allocator.destroy(dylib);
@@ -380,57 +392,27 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
dylib.arch = self.arch.?;
dylib.name = try self.allocator.dupe(u8, lib);
dylib.file = file;
- dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
- // TODO Defer parsing of the dylibs until they are actually needed
try dylib.parse();
try self.dylibs.append(self.allocator, dylib);
-
- // Add LC_LOAD_DYLIB command
- const dylib_id = dylib.id orelse unreachable;
- var dylib_cmd = try createLoadDylibCommand(
- self.allocator,
- dylib_id.name,
- dylib_id.timestamp,
- dylib_id.current_version,
- dylib_id.compatibility_version,
- );
- errdefer dylib_cmd.deinit(self.allocator);
-
- try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
} else {
// Try tbd stub file next.
- if (LibStub.loadFromFile(self.allocator, file)) |*lib_stub| {
- defer lib_stub.deinit();
-
- const dylib = try self.allocator.create(Dylib);
- errdefer self.allocator.destroy(dylib);
-
- dylib.* = Dylib.init(self.allocator);
- dylib.arch = self.arch.?;
- dylib.name = try self.allocator.dupe(u8, lib);
- dylib.file = file;
- dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
+ if (Stub.LibStub.loadFromFile(self.allocator, file)) |lib_stub| {
+ const stub = try self.allocator.create(Stub);
+ errdefer self.allocator.destroy(stub);
- try dylib.parseFromStub(lib_stub.*);
- try self.dylibs.append(self.allocator, dylib);
+ stub.* = Stub.init(self.allocator);
+ stub.arch = self.arch.?;
+ stub.name = try self.allocator.dupe(u8, lib);
+ stub.lib_stub = lib_stub;
- // Add LC_LOAD_DYLIB command
- const dylib_id = dylib.id orelse unreachable;
- var dylib_cmd = try createLoadDylibCommand(
- self.allocator,
- dylib_id.name,
- dylib_id.timestamp,
- dylib_id.current_version,
- dylib_id.compatibility_version,
- );
- errdefer dylib_cmd.deinit(self.allocator);
-
- try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
+ try stub.parse();
+ try self.lib_stubs.append(self.allocator, stub);
} else |_| {
// TODO this entire logic has to be cleaned up.
try file.seekTo(0);
- if (Archive.isArchive(file)) {
+
+ if (try Archive.isArchive(file)) {
const archive = try self.allocator.create(Archive);
errdefer self.allocator.destroy(archive);
@@ -438,6 +420,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
archive.arch = self.arch.?;
archive.name = try self.allocator.dupe(u8, lib);
archive.file = file;
+
try archive.parse();
try self.archives.append(self.allocator, archive);
} else {
@@ -449,26 +432,28 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
}
}
-fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
- const file = try fs.cwd().openFile(lib_system_path, .{});
+fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
+ const file = try fs.cwd().openFile(libc_stub_path, .{});
+ defer file.close();
+
+ var lib_stub = try Stub.LibStub.loadFromFile(self.allocator, file);
- var lib_stub = try LibStub.loadFromFile(self.allocator, file);
- defer lib_stub.deinit();
+ const stub = try self.allocator.create(Stub);
+ errdefer self.allocator.destroy(stub);
- const dylib = try self.allocator.create(Dylib);
- errdefer self.allocator.destroy(dylib);
+ stub.* = Stub.init(self.allocator);
+ stub.arch = self.arch.?;
+ stub.name = try self.allocator.dupe(u8, libc_stub_path);
+ stub.lib_stub = lib_stub;
- dylib.* = Dylib.init(self.allocator);
- dylib.arch = self.arch.?;
- dylib.name = try self.allocator.dupe(u8, lib_system_path);
- dylib.file = file;
- dylib.ordinal = @intCast(u16, self.dylibs.items.len) + 1;
+ try stub.parse();
- try dylib.parseFromStub(lib_stub);
- try self.dylibs.append(self.allocator, dylib);
+ self.libsystem_stub_index = @intCast(u16, self.lib_stubs.items.len);
+ try self.lib_stubs.append(self.allocator, stub);
- // Add LC_LOAD_DYLIB command
- const dylib_id = dylib.id orelse unreachable;
+ // Add LC_LOAD_DYLIB load command.
+ stub.ordinal = self.next_dylib_ordinal;
+ const dylib_id = stub.id orelse unreachable;
var dylib_cmd = try createLoadDylibCommand(
self.allocator,
dylib_id.name,
@@ -477,8 +462,8 @@ fn parseLibSystem(self: *Zld, lib_system_path: []const u8) !void {
dylib_id.compatibility_version,
);
errdefer dylib_cmd.deinit(self.allocator);
-
try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
+ self.next_dylib_ordinal += 1;
}
fn mapAndUpdateSections(
@@ -1906,21 +1891,34 @@ fn resolveSymbols(self: *Zld) !void {
for (self.unresolved.values()) |value| {
unresolved.appendAssumeCapacity(value);
}
- self.unresolved.clearAndFree(self.allocator);
-
- var has_undefined = false;
- while (unresolved.popOrNull()) |undef| {
- var found = false;
- for (self.dylibs.items) |dylib| {
- const proxy = dylib.symbols.get(undef.name) orelse continue;
- try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
- undef.alias = proxy;
- found = true;
- }
-
- if (!found) {
- if (mem.eql(u8, undef.name, "___dso_handle")) {
- const proxy = self.imports.get(undef.name) orelse blk: {
+ self.unresolved.clearRetainingCapacity();
+
+ var referenced = std.AutoHashMap(union(enum) {
+ dylib: *Dylib,
+ stub: *Stub,
+ }, void).init(self.allocator);
+ defer referenced.deinit();
+
+ loop: while (unresolved.popOrNull()) |undef| {
+ const proxy = self.imports.get(undef.name) orelse outer: {
+ const proxy = inner: {
+ for (self.dylibs.items) |dylib| {
+ const proxy = (try dylib.createProxy(undef.name)) orelse continue;
+ try referenced.put(.{ .dylib = dylib }, {});
+ break :inner proxy;
+ }
+ for (self.lib_stubs.items) |stub, i| {
+ const proxy = (try stub.createProxy(undef.name)) orelse continue;
+ if (self.libsystem_stub_index.? != @intCast(u16, i)) {
+ // LibSystem gets its load command separately.
+ try referenced.put(.{ .stub = stub }, {});
+ }
+ break :inner proxy;
+ }
+ if (mem.eql(u8, undef.name, "___dso_handle")) {
+ // TODO this is just a temp patch until I work out what to actually
+ // do with ___dso_handle and __mh_execute_header symbols which are
+ // synthetically created by the linker on macOS.
const name = try self.allocator.dupe(u8, undef.name);
const proxy = try self.allocator.create(Symbol.Proxy);
errdefer self.allocator.destroy(proxy);
@@ -1929,26 +1927,69 @@ fn resolveSymbols(self: *Zld) !void {
.@"type" = .proxy,
.name = name,
},
+ .file = null,
};
- try self.imports.putNoClobber(self.allocator, name, &proxy.base);
- break :blk &proxy.base;
- };
- undef.alias = proxy;
- continue;
+ break :inner &proxy.base;
+ }
+
+ self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
+ continue :loop;
+ };
+
+ try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
+ break :outer proxy;
+ };
+ undef.alias = proxy;
+ }
+
+ // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
+ var it = referenced.iterator();
+ while (it.next()) |key| {
+ var dylib_cmd = blk: {
+ switch (key.key_ptr.*) {
+ .dylib => |dylib| {
+ dylib.ordinal = self.next_dylib_ordinal;
+ const dylib_id = dylib.id orelse unreachable;
+ break :blk try createLoadDylibCommand(
+ self.allocator,
+ dylib_id.name,
+ dylib_id.timestamp,
+ dylib_id.current_version,
+ dylib_id.compatibility_version,
+ );
+ },
+ .stub => |stub| {
+ stub.ordinal = self.next_dylib_ordinal;
+ const dylib_id = stub.id orelse unreachable;
+ break :blk try createLoadDylibCommand(
+ self.allocator,
+ dylib_id.name,
+ dylib_id.timestamp,
+ dylib_id.current_version,
+ dylib_id.compatibility_version,
+ );
+ },
}
+ };
+ errdefer dylib_cmd.deinit(self.allocator);
+ try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
+ self.next_dylib_ordinal += 1;
+ }
+ if (self.unresolved.count() > 0) {
+ for (self.unresolved.values()) |undef| {
log.err("undefined reference to symbol '{s}'", .{undef.name});
log.err(" | referenced in {s}", .{
undef.cast(Symbol.Unresolved).?.file.name.?,
});
- has_undefined = true;
}
- }
- if (has_undefined) return error.UndefinedSymbolReference;
+ return error.UndefinedSymbolReference;
+ }
// Finally put dyld_stub_binder as an Import
- const proxy = self.dylibs.items[self.dylibs.items.len - 1].symbols.get("dyld_stub_binder") orelse {
+ const libsystem_stub = self.lib_stubs.items[self.libsystem_stub_index.?];
+ const proxy = (try libsystem_stub.createProxy("dyld_stub_binder")) orelse {
log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
return error.UndefinedSymbolReference;
};
@@ -2814,7 +2855,7 @@ fn writeBindInfoTable(self: *Zld) !void {
try pointers.append(.{
.offset = base_offset + proxy.base.got_index.? * @sizeOf(u64),
.segment_id = segment_id,
- .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,
+ .dylib_ordinal = proxy.dylibOrdinal(),
.name = proxy.base.name,
});
}
@@ -2833,7 +2874,7 @@ fn writeBindInfoTable(self: *Zld) !void {
try pointers.append(.{
.offset = base_offset,
.segment_id = segment_id,
- .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,
+ .dylib_ordinal = proxy.dylibOrdinal(),
.name = proxy.base.name,
});
}
@@ -2873,7 +2914,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
pointers.appendAssumeCapacity(.{
.offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
.segment_id = segment_id,
- .dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0,
+ .dylib_ordinal = proxy.dylibOrdinal(),
.name = sym.name,
});
}
@@ -3181,12 +3222,11 @@ fn writeSymbolTable(self: *Zld) !void {
for (self.imports.values()) |sym| {
const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
- const dylib_ordinal = if (proxy.dylib) |dylib| dylib.ordinal.? else 0;
try undefs.append(.{
.n_strx = try self.makeString(sym.name),
.n_type = macho.N_UNDF | macho.N_EXT,
.n_sect = 0,
- .n_desc = (dylib_ordinal * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
+ .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
.n_value = 0,
});
}