| author | |
| committer | |
| log | ac9f72d87e8eeb4a9d0dead80b61420485279ddd |
| tree | 63046c79edfb445d0eef7092d695ab1646655061 |
| parent | 706bdf6512aec9f5d003cc2ec64ba16ac0a202dc |
`-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_go7 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; |
| 451 | pub const LinkObject = struct { | 451 | pub 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 | }; |
| 455 | 460 | ||
| 456 | pub const InitOptions = struct { | 461 | pub 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 that | 2201 | /// to remind the programmer to update multiple related pieces of code that |
| 2197 | /// are in different locations. Bump this number when adding or deleting | 2202 | /// 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. |
| 2199 | pub const link_hash_implementation_version = 8; | 2204 | pub const link_hash_implementation_version = 9; |
| 2200 | 2205 | ||
| 2201 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { | 2206 | fn 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(); |
| 2208 | 2213 | ||
| 2209 | comptime assert(link_hash_implementation_version == 8); | 2214 | comptime assert(link_hash_implementation_version == 9); |
| 2210 | 2215 | ||
| 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 | } |
| 2248 | 2254 | ||
| 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(); |
| 65 | 65 | ||
| 66 | comptime assert(Compilation.link_hash_implementation_version == 8); | 66 | comptime assert(Compilation.link_hash_implementation_version == 9); |
| 67 | 67 | ||
| 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(); |
| 1373 | 1373 | ||
| 1374 | comptime assert(Compilation.link_hash_implementation_version == 8); | 1374 | comptime assert(Compilation.link_hash_implementation_version == 9); |
| 1375 | 1375 | ||
| 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(); |
| 3496 | 3496 | ||
| 3497 | comptime assert(Compilation.link_hash_implementation_version == 8); | 3497 | comptime assert(Compilation.link_hash_implementation_version == 9); |
| 3498 | 3498 | ||
| 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(); |
| 3152 | 3152 | ||
| 3153 | comptime assert(Compilation.link_hash_implementation_version == 8); | 3153 | comptime assert(Compilation.link_hash_implementation_version == 9); |
| 3154 | 3154 | ||
| 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(); |
| 4201 | 4201 | ||
| 4202 | comptime assert(Compilation.link_hash_implementation_version == 8); | 4202 | comptime assert(Compilation.link_hash_implementation_version == 9); |
| 4203 | 4203 | ||
| 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(); |
| 891 | 891 | ||
| 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(); |
| 902 | 894 | ||
| ... | @@ -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++ until | 1619 | // 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 positional | 1622 | // -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 | } |
| 2642 | 2635 | ||
| 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; |
| 2668 | 2637 | ||
| 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)) { |