mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
zig ld: handle --library :path/to/lib.so
`-l :path/to/lib.so` behavior on gcc/clang is: - the path is recorded as-is: no paths, exact filename (`libX.so.Y`). - no rpaths. The previous version removed the `:` and pretended it's a positional argument to the linker. That works in almost all cases, except in how rules_go[1] does things (the Bazel wrapper for Go). Test case in #15743, output: gcc rpath: 0x0000000000000001 (NEEDED) Shared library: [libversioned.so.2] 0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/x] gcc plain: 0x0000000000000001 (NEEDED) Shared library: [libversioned.so.2] zig cc rpath: 0x0000000000000001 (NEEDED) Shared library: [libversioned.so.2] 0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/x] zig cc plain: 0x0000000000000001 (NEEDED) Shared library: [libversioned.so.2] Fixes #15743 [1]: https://github.com/bazelbuild/rules_go
This commit is contained in:
parent
706bdf6512
commit
ac9f72d87e
@ -451,6 +451,11 @@ pub const CacheMode = link.CacheMode;
|
||||
pub const LinkObject = struct {
|
||||
path: []const u8,
|
||||
must_link: bool = false,
|
||||
// When the library is passed via a positional argument, it will be
|
||||
// added as a full path. If it's `-l<lib>`, then just the basename.
|
||||
//
|
||||
// Consistent with `withLOption` variable name in lld ELF driver.
|
||||
loption: bool = false,
|
||||
};
|
||||
|
||||
pub const InitOptions = struct {
|
||||
@ -2196,7 +2201,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
|
||||
/// to remind the programmer to update multiple related pieces of code that
|
||||
/// are in different locations. Bump this number when adding or deleting
|
||||
/// anything from the link cache manifest.
|
||||
pub const link_hash_implementation_version = 8;
|
||||
pub const link_hash_implementation_version = 9;
|
||||
|
||||
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
|
||||
const gpa = comp.gpa;
|
||||
@ -2206,7 +2211,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
comptime assert(link_hash_implementation_version == 8);
|
||||
comptime assert(link_hash_implementation_version == 9);
|
||||
|
||||
if (comp.bin_file.options.module) |mod| {
|
||||
const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
|
||||
@ -2244,6 +2249,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
||||
for (comp.bin_file.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
man.hash.add(obj.must_link);
|
||||
man.hash.add(obj.loption);
|
||||
}
|
||||
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
|
||||
@ -1020,6 +1020,7 @@ pub const File = struct {
|
||||
for (base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
man.hash.add(obj.must_link);
|
||||
man.hash.add(obj.loption);
|
||||
}
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
_ = try man.addFile(key.status.success.object_path, null);
|
||||
|
||||
@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
man = comp.cache_parent.obtain();
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
@ -1371,13 +1371,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
|
||||
try man.addOptionalFile(self.base.options.linker_script);
|
||||
try man.addOptionalFile(self.base.options.version_script);
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
man.hash.add(obj.must_link);
|
||||
man.hash.add(obj.loption);
|
||||
}
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
_ = try man.addFile(key.status.success.object_path, null);
|
||||
@ -1719,6 +1720,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
for (self.base.options.objects) |obj| {
|
||||
if (Compilation.classifyFileExt(obj.path) == .shared_library) {
|
||||
const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
|
||||
if (obj.loption) continue;
|
||||
|
||||
if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(lib_dir_path);
|
||||
@ -1767,6 +1770,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
try argv.append("-no-whole-archive");
|
||||
whole_archive = false;
|
||||
}
|
||||
|
||||
if (obj.loption) {
|
||||
assert(obj.path[0] == ':');
|
||||
try argv.append("-l");
|
||||
}
|
||||
try argv.append(obj.path);
|
||||
}
|
||||
if (whole_archive) {
|
||||
|
||||
@ -3494,7 +3494,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
macho_file.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
@ -3150,7 +3150,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
@ -4199,7 +4199,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 8);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 9);
|
||||
|
||||
for (wasm.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
43
src/main.zig
43
src/main.zig
@ -889,14 +889,6 @@ fn buildOutputType(
|
||||
var link_objects = std.ArrayList(Compilation.LinkObject).init(gpa);
|
||||
defer link_objects.deinit();
|
||||
|
||||
// This map is a flag per link_objects item, used to represent the
|
||||
// `-l :file.so` syntax from gcc/clang.
|
||||
// This is only exposed from the `zig cc` interface. It means that the `path`
|
||||
// field from the corresponding `link_objects` element is a suffix, and is
|
||||
// to be tried against each library path as a prefix until an existing file is found.
|
||||
// This map remains empty for the main CLI.
|
||||
var link_objects_lib_search_paths: std.AutoHashMapUnmanaged(u32, void) = .{};
|
||||
|
||||
var framework_dirs = std.ArrayList([]const u8).init(gpa);
|
||||
defer framework_dirs.deinit();
|
||||
|
||||
@ -1627,14 +1619,15 @@ fn buildOutputType(
|
||||
// We don't know whether this library is part of libc or libc++ until
|
||||
// we resolve the target, so we simply append to the list for now.
|
||||
if (mem.startsWith(u8, it.only_arg, ":")) {
|
||||
// This "feature" of gcc/clang means to treat this as a positional
|
||||
// link object, but using the library search directories as a prefix.
|
||||
// -l :path/to/filename is used when callers need
|
||||
// more control over what's in the resulting
|
||||
// binary: no extra rpaths and DSO filename exactly
|
||||
// as provided. Hello, Go.
|
||||
try link_objects.append(.{
|
||||
.path = it.only_arg[1..],
|
||||
.path = it.only_arg,
|
||||
.must_link = must_link,
|
||||
.loption = true,
|
||||
});
|
||||
const index = @intCast(u32, link_objects.items.len - 1);
|
||||
try link_objects_lib_search_paths.put(arena, index, {});
|
||||
} else if (force_static_libs) {
|
||||
try static_libs.append(it.only_arg);
|
||||
} else {
|
||||
@ -2640,30 +2633,6 @@ fn buildOutputType(
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve `-l :file.so` syntax from `zig cc`. We use a separate map for this data
|
||||
// since this is an uncommon case.
|
||||
{
|
||||
var it = link_objects_lib_search_paths.iterator();
|
||||
while (it.next()) |item| {
|
||||
const link_object_i = item.key_ptr.*;
|
||||
const suffix = link_objects.items[link_object_i].path;
|
||||
|
||||
for (lib_dirs.items) |lib_dir_path| {
|
||||
const test_path = try fs.path.join(arena, &.{ lib_dir_path, suffix });
|
||||
fs.cwd().access(test_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| fatal("unable to search for library '{s}': {s}", .{
|
||||
test_path, @errorName(e),
|
||||
}),
|
||||
};
|
||||
link_objects.items[link_object_i].path = test_path;
|
||||
break;
|
||||
} else {
|
||||
fatal("library '{s}' not found", .{suffix});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const object_format = target_info.target.ofmt;
|
||||
|
||||
if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user