authorgravatar for motiejus@jakstys.ltMotiejus Jakštys <motiejus@jakstys.lt> 2023-05-23 10:50:15+03:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-05-30 08:18:10+02:00
logac9f72d87e8eeb4a9d0dead80b61420485279ddd
tree63046c79edfb445d0eef7092d695ab1646655061
parent706bdf6512aec9f5d003cc2ec64ba16ac0a202dc

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

7 files changed, 28 insertions(+), 44 deletions(-)

src/Compilation.zig+8-2
...@@ -451,6 +451,11 @@ pub const CacheMode = link.CacheMode;...@@ -451,6 +451,11 @@ pub const CacheMode = link.CacheMode;
451pub const LinkObject = struct {451pub const LinkObject = struct {
452 path: []const u8,452 path: []const u8,
453 must_link: bool = false,453 must_link: bool = false,
454 // When the library is passed via a positional argument, it will be
455 // added as a full path. If it's `-l<lib>`, then just the basename.
456 //
457 // Consistent with `withLOption` variable name in lld ELF driver.
458 loption: bool = false,
454};459};
455460
456pub const InitOptions = struct {461pub const InitOptions = struct {
...@@ -2196,7 +2201,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo...@@ -2196,7 +2201,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
2196/// to remind the programmer to update multiple related pieces of code that2201/// to remind the programmer to update multiple related pieces of code that
2197/// are in different locations. Bump this number when adding or deleting2202/// are in different locations. Bump this number when adding or deleting
2198/// anything from the link cache manifest.2203/// anything from the link cache manifest.
2199pub const link_hash_implementation_version = 8;2204pub const link_hash_implementation_version = 9;
22002205
2201fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {2206fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
2202 const gpa = comp.gpa;2207 const gpa = comp.gpa;
...@@ -2206,7 +2211,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2206,7 +2211,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2206 defer arena_allocator.deinit();2211 defer arena_allocator.deinit();
2207 const arena = arena_allocator.allocator();2212 const arena = arena_allocator.allocator();
22082213
2209 comptime assert(link_hash_implementation_version == 8);2214 comptime assert(link_hash_implementation_version == 9);
22102215
2211 if (comp.bin_file.options.module) |mod| {2216 if (comp.bin_file.options.module) |mod| {
2212 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{2217 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...@@ -2244,6 +2249,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2244 for (comp.bin_file.options.objects) |obj| {2249 for (comp.bin_file.options.objects) |obj| {
2245 _ = try man.addFile(obj.path, null);2250 _ = try man.addFile(obj.path, null);
2246 man.hash.add(obj.must_link);2251 man.hash.add(obj.must_link);
2252 man.hash.add(obj.loption);
2247 }2253 }
22482254
2249 for (comp.c_object_table.keys()) |key| {2255 for (comp.c_object_table.keys()) |key| {
src/link.zig+1
...@@ -1020,6 +1020,7 @@ pub const File = struct {...@@ -1020,6 +1020,7 @@ pub const File = struct {
1020 for (base.options.objects) |obj| {1020 for (base.options.objects) |obj| {
1021 _ = try man.addFile(obj.path, null);1021 _ = try man.addFile(obj.path, null);
1022 man.hash.add(obj.must_link);1022 man.hash.add(obj.must_link);
1023 man.hash.add(obj.loption);
1023 }1024 }
1024 for (comp.c_object_table.keys()) |key| {1025 for (comp.c_object_table.keys()) |key| {
1025 _ = try man.addFile(key.status.success.object_path, null);1026 _ = try man.addFile(key.status.success.object_path, null);
src/link/Coff/lld.zig+1-1
...@@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -63,7 +63,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
63 man = comp.cache_parent.obtain();63 man = comp.cache_parent.obtain();
64 self.base.releaseLock();64 self.base.releaseLock();
6565
66 comptime assert(Compilation.link_hash_implementation_version == 8);66 comptime assert(Compilation.link_hash_implementation_version == 9);
6767
68 for (self.base.options.objects) |obj| {68 for (self.base.options.objects) |obj| {
69 _ = try man.addFile(obj.path, null);69 _ = try man.addFile(obj.path, null);
src/link/Elf.zig+9-1
...@@ -1371,13 +1371,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1371,13 +1371,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1371 // We are about to obtain this lock, so here we give other processes a chance first.1371 // We are about to obtain this lock, so here we give other processes a chance first.
1372 self.base.releaseLock();1372 self.base.releaseLock();
13731373
1374 comptime assert(Compilation.link_hash_implementation_version == 8);1374 comptime assert(Compilation.link_hash_implementation_version == 9);
13751375
1376 try man.addOptionalFile(self.base.options.linker_script);1376 try man.addOptionalFile(self.base.options.linker_script);
1377 try man.addOptionalFile(self.base.options.version_script);1377 try man.addOptionalFile(self.base.options.version_script);
1378 for (self.base.options.objects) |obj| {1378 for (self.base.options.objects) |obj| {
1379 _ = try man.addFile(obj.path, null);1379 _ = try man.addFile(obj.path, null);
1380 man.hash.add(obj.must_link);1380 man.hash.add(obj.must_link);
1381 man.hash.add(obj.loption);
1381 }1382 }
1382 for (comp.c_object_table.keys()) |key| {1383 for (comp.c_object_table.keys()) |key| {
1383 _ = try man.addFile(key.status.success.object_path, null);1384 _ = 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...@@ -1719,6 +1720,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1719 for (self.base.options.objects) |obj| {1720 for (self.base.options.objects) |obj| {
1720 if (Compilation.classifyFileExt(obj.path) == .shared_library) {1721 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
1721 const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;1722 const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
1723 if (obj.loption) continue;
1724
1722 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {1725 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
1723 try argv.append("-rpath");1726 try argv.append("-rpath");
1724 try argv.append(lib_dir_path);1727 try argv.append(lib_dir_path);
...@@ -1767,6 +1770,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1767,6 +1770,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1767 try argv.append("-no-whole-archive");1770 try argv.append("-no-whole-archive");
1768 whole_archive = false;1771 whole_archive = false;
1769 }1772 }
1773
1774 if (obj.loption) {
1775 assert(obj.path[0] == ':');
1776 try argv.append("-l");
1777 }
1770 try argv.append(obj.path);1778 try argv.append(obj.path);
1771 }1779 }
1772 if (whole_archive) {1780 if (whole_archive) {
src/link/MachO/zld.zig+1-1
...@@ -3494,7 +3494,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3494,7 +3494,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3494 // We are about to obtain this lock, so here we give other processes a chance first.3494 // We are about to obtain this lock, so here we give other processes a chance first.
3495 macho_file.base.releaseLock();3495 macho_file.base.releaseLock();
34963496
3497 comptime assert(Compilation.link_hash_implementation_version == 8);3497 comptime assert(Compilation.link_hash_implementation_version == 9);
34983498
3499 for (options.objects) |obj| {3499 for (options.objects) |obj| {
3500 _ = try man.addFile(obj.path, null);3500 _ = try man.addFile(obj.path, null);
src/link/Wasm.zig+2-2
...@@ -3150,7 +3150,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3150,7 +3150,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3150 // We are about to obtain this lock, so here we give other processes a chance first.3150 // We are about to obtain this lock, so here we give other processes a chance first.
3151 wasm.base.releaseLock();3151 wasm.base.releaseLock();
31523152
3153 comptime assert(Compilation.link_hash_implementation_version == 8);3153 comptime assert(Compilation.link_hash_implementation_version == 9);
31543154
3155 for (options.objects) |obj| {3155 for (options.objects) |obj| {
3156 _ = try man.addFile(obj.path, null);3156 _ = try man.addFile(obj.path, null);
...@@ -4199,7 +4199,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -4199,7 +4199,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
4199 // We are about to obtain this lock, so here we give other processes a chance first.4199 // We are about to obtain this lock, so here we give other processes a chance first.
4200 wasm.base.releaseLock();4200 wasm.base.releaseLock();
42014201
4202 comptime assert(Compilation.link_hash_implementation_version == 8);4202 comptime assert(Compilation.link_hash_implementation_version == 9);
42034203
4204 for (wasm.base.options.objects) |obj| {4204 for (wasm.base.options.objects) |obj| {
4205 _ = try man.addFile(obj.path, null);4205 _ = try man.addFile(obj.path, null);
src/main.zig+6-37
...@@ -889,14 +889,6 @@ fn buildOutputType(...@@ -889,14 +889,6 @@ fn buildOutputType(
889 var link_objects = std.ArrayList(Compilation.LinkObject).init(gpa);889 var link_objects = std.ArrayList(Compilation.LinkObject).init(gpa);
890 defer link_objects.deinit();890 defer link_objects.deinit();
891891
892 // This map is a flag per link_objects item, used to represent the
893 // `-l :file.so` syntax from gcc/clang.
894 // This is only exposed from the `zig cc` interface. It means that the `path`
895 // field from the corresponding `link_objects` element is a suffix, and is
896 // to be tried against each library path as a prefix until an existing file is found.
897 // This map remains empty for the main CLI.
898 var link_objects_lib_search_paths: std.AutoHashMapUnmanaged(u32, void) = .{};
899
900 var framework_dirs = std.ArrayList([]const u8).init(gpa);892 var framework_dirs = std.ArrayList([]const u8).init(gpa);
901 defer framework_dirs.deinit();893 defer framework_dirs.deinit();
902894
...@@ -1627,14 +1619,15 @@ fn buildOutputType(...@@ -1627,14 +1619,15 @@ fn buildOutputType(
1627 // We don't know whether this library is part of libc or libc++ until1619 // We don't know whether this library is part of libc or libc++ until
1628 // we resolve the target, so we simply append to the list for now.1620 // we resolve the target, so we simply append to the list for now.
1629 if (mem.startsWith(u8, it.only_arg, ":")) {1621 if (mem.startsWith(u8, it.only_arg, ":")) {
1630 // This "feature" of gcc/clang means to treat this as a positional1622 // -l :path/to/filename is used when callers need
1631 // link object, but using the library search directories as a prefix.1623 // more control over what's in the resulting
1624 // binary: no extra rpaths and DSO filename exactly
1625 // as provided. Hello, Go.
1632 try link_objects.append(.{1626 try link_objects.append(.{
1633 .path = it.only_arg[1..],1627 .path = it.only_arg,
1634 .must_link = must_link,1628 .must_link = must_link,
1629 .loption = true,
1635 });1630 });
1636 const index = @intCast(u32, link_objects.items.len - 1);
1637 try link_objects_lib_search_paths.put(arena, index, {});
1638 } else if (force_static_libs) {1631 } else if (force_static_libs) {
1639 try static_libs.append(it.only_arg);1632 try static_libs.append(it.only_arg);
1640 } else {1633 } else {
...@@ -2640,30 +2633,6 @@ fn buildOutputType(...@@ -2640,30 +2633,6 @@ fn buildOutputType(
2640 }2633 }
2641 }2634 }
26422635
2643 // Resolve `-l :file.so` syntax from `zig cc`. We use a separate map for this data
2644 // since this is an uncommon case.
2645 {
2646 var it = link_objects_lib_search_paths.iterator();
2647 while (it.next()) |item| {
2648 const link_object_i = item.key_ptr.*;
2649 const suffix = link_objects.items[link_object_i].path;
2650
2651 for (lib_dirs.items) |lib_dir_path| {
2652 const test_path = try fs.path.join(arena, &.{ lib_dir_path, suffix });
2653 fs.cwd().access(test_path, .{}) catch |err| switch (err) {
2654 error.FileNotFound => continue,
2655 else => |e| fatal("unable to search for library '{s}': {s}", .{
2656 test_path, @errorName(e),
2657 }),
2658 };
2659 link_objects.items[link_object_i].path = test_path;
2660 break;
2661 } else {
2662 fatal("library '{s}' not found", .{suffix});
2663 }
2664 }
2665 }
2666
2667 const object_format = target_info.target.ofmt;2636 const object_format = target_info.target.ofmt;
26682637
2669 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {2638 if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {