From 12efefeba5a8128e0a79b4b04fad0d538c382e47 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 Nov 2020 13:09:30 +0100 Subject: [PATCH 1/4] stage2 elf: make -soname an opt-in and not opt-out As discussed with LemonBoy, it makes more sense to have the `-soname` an opt-in option rather than opt-out, which is especially true if we are to align `zig cc` with `cc` to a greater degree. --- src/link/Elf.zig | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9747eced41..dbd41bc4d6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1512,13 +1512,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } if (is_dyn_lib) { - const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver| - try std.fmt.allocPrint(arena, "lib{}.so.{}", .{ self.base.options.root_name, ver.major }) - else - try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name}); - try argv.append("-soname"); - try argv.append(soname); - + if (self.base.options.override_soname) |soname| { + try argv.append("-soname"); + try argv.append(soname); + } if (self.base.options.version_script) |version_script| { try argv.append("-version-script"); try argv.append(version_script); From 375bab8460517f5d5ee02c161dc65ff1694132d7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 Nov 2020 15:27:17 +0100 Subject: [PATCH 2/4] stage2 elf: refactor override_soname to soname --- src/Compilation.zig | 4 ++-- src/glibc.zig | 4 ++-- src/link.zig | 2 +- src/link/Elf.zig | 4 ++-- src/main.zig | 24 ++++++++++++------------ 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index b813182e30..a66df1db7b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -364,7 +364,7 @@ pub const InitOptions = struct { link_emit_relocs: bool = false, linker_script: ?[]const u8 = null, version_script: ?[]const u8 = null, - override_soname: ?[]const u8 = null, + soname: ?[]const u8 = null, linker_gc_sections: ?bool = null, linker_allow_shlib_undefined: ?bool = null, linker_bind_global_refs_locally: ?bool = null, @@ -828,7 +828,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .emit_relocs = options.link_emit_relocs, .rdynamic = options.rdynamic, .extra_lld_args = options.lld_argv, - .override_soname = options.override_soname, + .soname = options.soname, .version = options.version, .libc_installation = libc_dirs.libc_installation, .pic = pic, diff --git a/src/glibc.zig b/src/glibc.zig index 108aeb6f77..9a00144d7d 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -917,7 +917,7 @@ fn buildSharedLib( }; const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); - const override_soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null; + const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null; const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename }); const c_source_files = [1]Compilation.CSourceFile{ .{ @@ -955,7 +955,7 @@ fn buildSharedLib( .clang_passthrough_mode = comp.clang_passthrough_mode, .version = version, .version_script = map_file_path, - .override_soname = override_soname, + .soname = soname, .c_source_files = &c_source_files, .is_compiler_rt_or_libc = true, }); diff --git a/src/link.zig b/src/link.zig index e8a9f3d730..21022a760a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -88,7 +88,7 @@ pub const Options = struct { subsystem: ?std.Target.SubSystem, linker_script: ?[]const u8, version_script: ?[]const u8, - override_soname: ?[]const u8, + soname: ?[]const u8, llvm_cpu_features: ?[*:0]const u8, /// Extra args passed directly to LLD. Ignored when not linking with LLD. extra_lld_args: []const []const u8, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dbd41bc4d6..5adf909656 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1314,7 +1314,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } } if (is_dyn_lib) { - man.hash.addOptionalBytes(self.base.options.override_soname); + man.hash.addOptionalBytes(self.base.options.soname); man.hash.addOptional(self.base.options.version); } man.hash.addStringSet(self.base.options.system_libs); @@ -1512,7 +1512,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } if (is_dyn_lib) { - if (self.base.options.override_soname) |soname| { + if (self.base.options.soname) |soname| { try argv.append("-soname"); try argv.append(soname); } diff --git a/src/main.zig b/src/main.zig index 8de94e1abd..9a189d043b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -467,7 +467,7 @@ fn buildOutputType( var linker_script: ?[]const u8 = null; var version_script: ?[]const u8 = null; var disable_c_depfile = false; - var override_soname: ?[]const u8 = null; + var soname: ?[]const u8 = null; var linker_gc_sections: ?bool = null; var linker_allow_shlib_undefined: ?bool = null; var linker_bind_global_refs_locally: ?bool = null; @@ -1108,33 +1108,33 @@ fn buildOutputType( if (i >= linker_args.items.len) { fatal("expected linker arg after '{}'", .{arg}); } - const soname = linker_args.items[i]; - override_soname = soname; + const name = linker_args.items[i]; + soname = name; // Use it as --name. // Example: libsoundio.so.2 var prefix: usize = 0; - if (mem.startsWith(u8, soname, "lib")) { + if (mem.startsWith(u8, name, "lib")) { prefix = 3; } - var end: usize = soname.len; - if (mem.endsWith(u8, soname, ".so")) { + var end: usize = name.len; + if (mem.endsWith(u8, name, ".so")) { end -= 3; } else { var found_digit = false; - while (end > 0 and std.ascii.isDigit(soname[end - 1])) { + while (end > 0 and std.ascii.isDigit(name[end - 1])) { found_digit = true; end -= 1; } - if (found_digit and end > 0 and soname[end - 1] == '.') { + if (found_digit and end > 0 and name[end - 1] == '.') { end -= 1; } else { - end = soname.len; + end = name.len; } - if (mem.endsWith(u8, soname[prefix..end], ".so")) { + if (mem.endsWith(u8, name[prefix..end], ".so")) { end -= 3; } } - provided_name = soname[prefix..end]; + provided_name = name[prefix..end]; } else if (mem.eql(u8, arg, "-rpath")) { i += 1; if (i >= linker_args.items.len) { @@ -1660,7 +1660,7 @@ fn buildOutputType( .linker_script = linker_script, .version_script = version_script, .disable_c_depfile = disable_c_depfile, - .override_soname = override_soname, + .soname = soname, .linker_gc_sections = linker_gc_sections, .linker_allow_shlib_undefined = linker_allow_shlib_undefined, .linker_bind_global_refs_locally = linker_bind_global_refs_locally, From dd522c0c977cb2ce4d1bb1d059e1755d7c94cef5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 Nov 2020 18:12:55 +0100 Subject: [PATCH 3/4] stage2 elf: fix glibc to always specify soname --- src/glibc.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/glibc.zig b/src/glibc.zig index 9a00144d7d..3d63f5d3de 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -911,13 +911,14 @@ fn buildSharedLib( const tracy = trace(@src()); defer tracy.end(); + const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }); const emit_bin = Compilation.EmitLoc{ .directory = bin_directory, - .basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }), + .basename = basename, }; const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); - const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null; + const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename; const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename }); const c_source_files = [1]Compilation.CSourceFile{ .{ From c3b0182f31c874eb9f1bb21397debdfdbf6161d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Nov 2020 17:49:55 -0700 Subject: [PATCH 4/4] restore sonames by default when using zig cli Before this commit, the branch regressed this case: zig build-lib foo.zig -dynamic readelf -d libfoo.so x000000000000000e (SONAME) Library soname: [libfoo.so] Now it works again, but it preserves the property that using `zig cc` has the SONAME off by default to match C compilers. --- src/link/Elf.zig | 6 ++---- src/main.zig | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 5adf909656..82c3df35ea 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1313,10 +1313,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { man.hash.addOptionalBytes(self.base.options.dynamic_linker); } } - if (is_dyn_lib) { - man.hash.addOptionalBytes(self.base.options.soname); - man.hash.addOptional(self.base.options.version); - } + man.hash.addOptionalBytes(self.base.options.soname); + man.hash.addOptional(self.base.options.version); man.hash.addStringSet(self.base.options.system_libs); man.hash.add(allow_shlib_undefined); man.hash.add(self.base.options.bind_global_refs_locally); diff --git a/src/main.zig b/src/main.zig index 9a189d043b..3939ed7b37 100644 --- a/src/main.zig +++ b/src/main.zig @@ -306,6 +306,8 @@ const usage_build_generic = \\ --version-script [path] Provide a version .map file \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) \\ --version [ver] Dynamic library semver + \\ -fsoname[=name] (linux) Override the default SONAME value + \\ -fno-soname (linux) Disable emitting a SONAME \\ -rdynamic Add all symbols to the dynamic symbol table \\ -rpath [path] Add directory to the runtime library search path \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library @@ -350,6 +352,12 @@ const repl_help = \\ ; +const SOName = union(enum) { + no, + yes_default_value, + yes: []const u8, +}; + const Emit = union(enum) { no, yes_default_path, @@ -452,6 +460,7 @@ fn buildOutputType( var target_ofmt: ?[]const u8 = null; var output_mode: std.builtin.OutputMode = undefined; var emit_h: Emit = undefined; + var soname: SOName = undefined; var ensure_libc_on_non_freestanding = false; var ensure_libcpp_on_non_freestanding = false; var link_libc = false; @@ -467,7 +476,6 @@ fn buildOutputType( var linker_script: ?[]const u8 = null; var version_script: ?[]const u8 = null; var disable_c_depfile = false; - var soname: ?[]const u8 = null; var linker_gc_sections: ?bool = null; var linker_allow_shlib_undefined: ?bool = null; var linker_bind_global_refs_locally: ?bool = null; @@ -564,6 +572,8 @@ fn buildOutputType( // .translate_c, .zig_test, .run => emit_h = .no, // else => unreachable, //} + + soname = .yes_default_value; const args = all_args[2..]; var i: usize = 0; args_loop: while (i < args.len) : (i += 1) { @@ -828,6 +838,12 @@ fn buildOutputType( use_clang = false; } else if (mem.eql(u8, arg, "-rdynamic")) { rdynamic = true; + } else if (mem.eql(u8, arg, "-fsoname")) { + soname = .yes_default_value; + } else if (mem.startsWith(u8, arg, "-fsoname=")) { + soname = .{ .yes = arg["-fsoname=".len..] }; + } else if (mem.eql(u8, arg, "-fno-soname")) { + soname = .no; } else if (mem.eql(u8, arg, "-femit-bin")) { emit_bin = .yes_default_path; } else if (mem.startsWith(u8, arg, "-femit-bin=")) { @@ -955,6 +971,7 @@ fn buildOutputType( }, .cc, .cpp => { emit_h = .no; + soname = .no; strip = true; ensure_libc_on_non_freestanding = true; ensure_libcpp_on_non_freestanding = arg_mode == .cpp; @@ -1109,7 +1126,7 @@ fn buildOutputType( fatal("expected linker arg after '{}'", .{arg}); } const name = linker_args.items[i]; - soname = name; + soname = .{ .yes = name }; // Use it as --name. // Example: libsoundio.so.2 var prefix: usize = 0; @@ -1433,6 +1450,18 @@ fn buildOutputType( const have_enable_cache = enable_cache orelse false; const optional_version = if (have_version) version else null; + const resolved_soname: ?[]const u8 = switch (soname) { + .yes => |explicit| explicit, + .no => null, + .yes_default_value => switch (object_format) { + .elf => if (have_version) + try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major }) + else + try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name}), + else => null, + }, + }; + const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) { .no => null, .yes_default_path => Compilation.EmitLoc{ @@ -1660,7 +1689,7 @@ fn buildOutputType( .linker_script = linker_script, .version_script = version_script, .disable_c_depfile = disable_c_depfile, - .soname = soname, + .soname = resolved_soname, .linker_gc_sections = linker_gc_sections, .linker_allow_shlib_undefined = linker_allow_shlib_undefined, .linker_bind_global_refs_locally = linker_bind_global_refs_locally,