zld: parse framework dirs and names

This commit is contained in:
Jakub Konka 2021-06-20 19:21:15 +02:00
parent 8216ce6789
commit 72f2f68938
3 changed files with 165 additions and 87 deletions

View File

@ -514,6 +514,119 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
}
}
fn resolvePaths(
arena: *Allocator,
resolved_paths: *std.ArrayList([]const u8),
syslibroot: ?[]const u8,
search_dirs: []const []const u8,
lib_names: []const []const u8,
kind: enum { lib, framework },
) !void {
var resolved_dirs = std.ArrayList([]const u8).init(arena);
for (search_dirs) |dir| {
if (fs.path.isAbsolute(dir)) {
var candidates = std.ArrayList([]const u8).init(arena);
if (syslibroot) |root| {
const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir });
try candidates.append(full_path);
}
try candidates.append(dir);
var found = false;
for (candidates.items) |candidate| {
// Verify that search path actually exists
var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
defer tmp.close();
try resolved_dirs.append(candidate);
found = true;
break;
}
if (!found) {
switch (kind) {
.lib => log.warn("directory not found for '-L{s}'", .{dir}),
.framework => log.warn("directory not found for '-F{s}'", .{dir}),
}
}
} else {
// Verify that search path actually exists
var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) {
error.FileNotFound => {
switch (kind) {
.lib => log.warn("directory not found for '-L{s}'", .{dir}),
.framework => log.warn("directory not found for '-F{s}'", .{dir}),
}
continue;
},
else => |e| return e,
};
defer tmp.close();
try resolved_dirs.append(dir);
}
}
// Assume ld64 default: -search_paths_first
// Look in each directory for a dylib (next, tbd), and then for archive
// TODO implement alternative: -search_dylibs_first
const exts = switch (kind) {
.lib => &[_][]const u8{ "dylib", "tbd", "a" },
.framework => &[_][]const u8{ "dylib", "tbd" },
};
for (lib_names) |lib_name| {
var found = false;
ext: for (exts) |ext| {
const lib_name_ext = blk: {
switch (kind) {
.lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }),
.framework => {
const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name});
const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext });
break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn });
},
}
};
for (resolved_dirs.items) |dir| {
const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });
// Check if the lib file exists.
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
defer tmp.close();
try resolved_paths.append(full_path);
found = true;
break :ext;
}
}
if (!found) {
switch (kind) {
.lib => {
log.warn("library not found for '-l{s}'", .{lib_name});
log.warn("Library search paths:", .{});
},
.framework => {
log.warn("framework not found for '-f{s}'", .{lib_name});
log.warn("Framework search paths:", .{});
},
}
for (resolved_dirs.items) |dir| {
log.warn(" {s}", .{dir});
}
}
}
}
fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
@ -700,7 +813,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
}
// Shared and static libraries passed via `-l` flag.
var libs = std.ArrayList([]const u8).init(arena);
var search_lib_names = std.ArrayList([]const u8).init(arena);
const system_libs = self.base.options.system_libs.keys();
@ -716,84 +828,15 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
try search_lib_names.append(link_lib);
}
var search_lib_dirs = std.ArrayList([]const u8).init(arena);
for (self.base.options.lib_dirs) |path| {
if (fs.path.isAbsolute(path)) {
var candidates = std.ArrayList([]const u8).init(arena);
if (self.base.options.syslibroot) |syslibroot| {
const full_path = try fs.path.join(arena, &[_][]const u8{ syslibroot, path });
try candidates.append(full_path);
}
try candidates.append(path);
var found = false;
for (candidates.items) |candidate| {
// Verify that search path actually exists
var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
defer tmp.close();
try search_lib_dirs.append(candidate);
found = true;
break;
}
if (!found) {
log.warn("directory not found for '-L{s}'", .{path});
}
} else {
// Verify that search path actually exists
var tmp = fs.cwd().openDir(path, .{}) catch |err| switch (err) {
error.FileNotFound => {
log.warn("directory not found for '-L{s}'", .{path});
continue;
},
else => |e| return e,
};
defer tmp.close();
try search_lib_dirs.append(path);
}
}
// Assume ld64 default: -search_paths_first
// Look in each directory for a dylib (next, tbd), and then for archive
// TODO implement alternative: -search_dylibs_first
const exts = &[_][]const u8{ "dylib", "tbd", "a" };
for (search_lib_names.items) |l_name| {
var found = false;
ext: for (exts) |ext| {
const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ l_name, ext });
for (search_lib_dirs.items) |lib_dir| {
const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext });
// Check if the lib file exists.
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
};
defer tmp.close();
try libs.append(full_path);
found = true;
break :ext;
}
}
if (!found) {
log.warn("library not found for '-l{s}'", .{l_name});
log.warn("Library search paths:", .{});
for (search_lib_dirs.items) |lib_dir| {
log.warn(" {s}", .{lib_dir});
}
}
}
var libs = std.ArrayList([]const u8).init(arena);
try resolvePaths(
arena,
&libs,
self.base.options.syslibroot,
self.base.options.lib_dirs,
search_lib_names.items,
.lib,
);
// rpaths
var rpath_table = std.StringArrayHashMap(void).init(arena);
@ -809,9 +852,14 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
}
// frameworks
for (self.base.options.frameworks) |framework| {
log.warn("frameworks not yet supported for '-framework {s}'", .{framework});
}
try resolvePaths(
arena,
&libs,
self.base.options.syslibroot,
self.base.options.framework_dirs,
self.base.options.frameworks,
.framework,
);
if (self.base.options.verbose_link) {
var argv = std.ArrayList([]const u8).init(arena);

View File

@ -60,7 +60,7 @@ 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.?});
log.warn("parsing shared library from stub '{s}'", .{self.name.?});
const umbrella_lib = lib_stub.inner[0];
self.id = .{
@ -84,9 +84,24 @@ pub fn parse(self: *Stub) !void {
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 (exp.symbols) |symbols| {
for (symbols) |sym_name| {
if (self.symbols.contains(sym_name)) continue;
try self.symbols.putNoClobber(self.allocator, sym_name, {});
}
}
if (exp.objc_classes) |classes| {
for (classes) |sym_name| {
log.warn(" | {s}", .{sym_name});
const actual_sym_name = try std.fmt.allocPrint(
self.allocator,
"_OBJC_CLASS_$_{s}",
.{sym_name},
);
if (self.symbols.contains(actual_sym_name)) continue;
try self.symbols.putNoClobber(self.allocator, actual_sym_name, {});
}
}
}
}
@ -101,6 +116,20 @@ pub fn parse(self: *Stub) !void {
}
}
}
if (stub.objc_classes) |classes| {
log.warn(" | objc_classes", .{});
for (classes) |sym_name| {
log.warn(" | {s}", .{sym_name});
const actual_sym_name = try std.fmt.allocPrint(
self.allocator,
"_OBJC_METACLASS_$_{s}",
.{sym_name},
);
if (self.symbols.contains(actual_sym_name)) continue;
try self.symbols.putNoClobber(self.allocator, actual_sym_name, {});
}
}
}
}

View File

@ -36,7 +36,8 @@ pub const LibStub = struct {
},
exports: ?[]const struct {
targets: []const []const u8,
symbols: []const []const u8,
symbols: ?[]const []const u8,
objc_classes: ?[]const []const u8,
},
reexports: ?[]const struct {
targets: []const []const u8,