authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-09 14:23:31-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-01-09 14:23:31-08:00
log7320b24e0ea768cf8dffaaeaa058db8e9134b444
tree66b14e8b17373b4578529f30540ffc6414668e1f
parentacca16c8cee4529bcb11c150c2c99b7de32ce21f
parent3cd646869b1791b8833134294d3f415cb6fddcf7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18486 from castholm/undefined-version

Add support for `--(no-)undefined-version`

9 files changed, 100 insertions(+), 7 deletions(-)

lib/std/Build/Step/Compile.zig+6
...@@ -110,6 +110,9 @@ linker_dynamicbase: bool = true,...@@ -110,6 +110,9 @@ linker_dynamicbase: bool = true,
110110
111linker_allow_shlib_undefined: ?bool = null,111linker_allow_shlib_undefined: ?bool = null,
112112
113/// Allow version scripts to refer to undefined symbols.
114linker_allow_undefined_version: ?bool = null,
115
113/// Permit read-only relocations in read-only segments. Disallowed by default.116/// Permit read-only relocations in read-only segments. Disallowed by default.
114link_z_notext: bool = false,117link_z_notext: bool = false,
115118
...@@ -1451,6 +1454,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1451,6 +1454,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1451 try zig_args.append("--version-script");1454 try zig_args.append("--version-script");
1452 try zig_args.append(version_script.getPath(b));1455 try zig_args.append(version_script.getPath(b));
1453 }1456 }
1457 if (self.linker_allow_undefined_version) |x| {
1458 try zig_args.append(if (x) "--undefined-version" else "--no-undefined-version");
1459 }
14541460
1455 if (self.kind == .@"test") {1461 if (self.kind == .@"test") {
1456 if (self.exec_cmd_args) |exec_cmd_args| {1462 if (self.exec_cmd_args) |exec_cmd_args| {
src/Compilation.zig+5-2
...@@ -1033,6 +1033,7 @@ pub const CreateOptions = struct {...@@ -1033,6 +1033,7 @@ pub const CreateOptions = struct {
1033 link_emit_relocs: bool = false,1033 link_emit_relocs: bool = false,
1034 linker_script: ?[]const u8 = null,1034 linker_script: ?[]const u8 = null,
1035 version_script: ?[]const u8 = null,1035 version_script: ?[]const u8 = null,
1036 linker_allow_undefined_version: bool = false,
1036 soname: ?[]const u8 = null,1037 soname: ?[]const u8 = null,
1037 linker_gc_sections: ?bool = null,1038 linker_gc_sections: ?bool = null,
1038 linker_allow_shlib_undefined: ?bool = null,1039 linker_allow_shlib_undefined: ?bool = null,
...@@ -1572,6 +1573,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1572,6 +1573,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1572 .stack_size = options.stack_size,1573 .stack_size = options.stack_size,
1573 .image_base = options.image_base,1574 .image_base = options.image_base,
1574 .version_script = options.version_script,1575 .version_script = options.version_script,
1576 .allow_undefined_version = options.linker_allow_undefined_version,
1575 .gc_sections = options.linker_gc_sections,1577 .gc_sections = options.linker_gc_sections,
1576 .emit_relocs = options.link_emit_relocs,1578 .emit_relocs = options.link_emit_relocs,
1577 .soname = options.soname,1579 .soname = options.soname,
...@@ -2458,7 +2460,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo...@@ -2458,7 +2460,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
2458/// to remind the programmer to update multiple related pieces of code that2460/// to remind the programmer to update multiple related pieces of code that
2459/// are in different locations. Bump this number when adding or deleting2461/// are in different locations. Bump this number when adding or deleting
2460/// anything from the link cache manifest.2462/// anything from the link cache manifest.
2461pub const link_hash_implementation_version = 10;2463pub const link_hash_implementation_version = 11;
24622464
2463fn addNonIncrementalStuffToCacheManifest(2465fn addNonIncrementalStuffToCacheManifest(
2464 comp: *Compilation,2466 comp: *Compilation,
...@@ -2467,7 +2469,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2467,7 +2469,7 @@ fn addNonIncrementalStuffToCacheManifest(
2467) !void {2469) !void {
2468 const gpa = comp.gpa;2470 const gpa = comp.gpa;
24692471
2470 comptime assert(link_hash_implementation_version == 10);2472 comptime assert(link_hash_implementation_version == 11);
24712473
2472 if (comp.module) |mod| {2474 if (comp.module) |mod| {
2473 const main_zig_file = try mod.main_mod.root.joinString(arena, mod.main_mod.root_src_path);2475 const main_zig_file = try mod.main_mod.root.joinString(arena, mod.main_mod.root_src_path);
...@@ -2541,6 +2543,7 @@ fn addNonIncrementalStuffToCacheManifest(...@@ -2541,6 +2543,7 @@ fn addNonIncrementalStuffToCacheManifest(
25412543
2542 try man.addOptionalFile(opts.linker_script);2544 try man.addOptionalFile(opts.linker_script);
2543 try man.addOptionalFile(opts.version_script);2545 try man.addOptionalFile(opts.version_script);
2546 man.hash.add(opts.allow_undefined_version);
25442547
2545 man.hash.addOptional(opts.stack_size);2548 man.hash.addOptional(opts.stack_size);
2546 man.hash.addOptional(opts.image_base);2549 man.hash.addOptional(opts.image_base);
src/link.zig+1
...@@ -113,6 +113,7 @@ pub const File = struct {...@@ -113,6 +113,7 @@ pub const File = struct {
113 minor_subsystem_version: ?u16,113 minor_subsystem_version: ?u16,
114 gc_sections: ?bool,114 gc_sections: ?bool,
115 allow_shlib_undefined: ?bool,115 allow_shlib_undefined: ?bool,
116 allow_undefined_version: bool,
116 subsystem: ?std.Target.SubSystem,117 subsystem: ?std.Target.SubSystem,
117 linker_script: ?[]const u8,118 linker_script: ?[]const u8,
118 version_script: ?[]const u8,119 version_script: ?[]const u8,
src/link/Coff/lld.zig+1-1
...@@ -70,7 +70,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)...@@ -70,7 +70,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)
70 man = comp.cache_parent.obtain();70 man = comp.cache_parent.obtain();
71 self.base.releaseLock();71 self.base.releaseLock();
7272
73 comptime assert(Compilation.link_hash_implementation_version == 10);73 comptime assert(Compilation.link_hash_implementation_version == 11);
7474
75 for (comp.objects) |obj| {75 for (comp.objects) |obj| {
76 _ = try man.addFile(obj.path, null);76 _ = try man.addFile(obj.path, null);
src/link/Elf.zig+9-1
...@@ -22,6 +22,7 @@ soname: ?[]const u8,...@@ -22,6 +22,7 @@ soname: ?[]const u8,
22bind_global_refs_locally: bool,22bind_global_refs_locally: bool,
23linker_script: ?[]const u8,23linker_script: ?[]const u8,
24version_script: ?[]const u8,24version_script: ?[]const u8,
25allow_undefined_version: bool,
25print_icf_sections: bool,26print_icf_sections: bool,
26print_map: bool,27print_map: bool,
27entry_name: ?[]const u8,28entry_name: ?[]const u8,
...@@ -325,6 +326,7 @@ pub fn createEmpty(...@@ -325,6 +326,7 @@ pub fn createEmpty(
325 .bind_global_refs_locally = options.bind_global_refs_locally,326 .bind_global_refs_locally = options.bind_global_refs_locally,
326 .linker_script = options.linker_script,327 .linker_script = options.linker_script,
327 .version_script = options.version_script,328 .version_script = options.version_script,
329 .allow_undefined_version = options.allow_undefined_version,
328 .print_icf_sections = options.print_icf_sections,330 .print_icf_sections = options.print_icf_sections,
329 .print_map = options.print_map,331 .print_map = options.print_map,
330 };332 };
...@@ -2410,10 +2412,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi...@@ -2410,10 +2412,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
2410 // We are about to obtain this lock, so here we give other processes a chance first.2412 // We are about to obtain this lock, so here we give other processes a chance first.
2411 self.base.releaseLock();2413 self.base.releaseLock();
24122414
2413 comptime assert(Compilation.link_hash_implementation_version == 10);2415 comptime assert(Compilation.link_hash_implementation_version == 11);
24142416
2415 try man.addOptionalFile(self.linker_script);2417 try man.addOptionalFile(self.linker_script);
2416 try man.addOptionalFile(self.version_script);2418 try man.addOptionalFile(self.version_script);
2419 man.hash.add(self.allow_undefined_version);
2417 for (comp.objects) |obj| {2420 for (comp.objects) |obj| {
2418 _ = try man.addFile(obj.path, null);2421 _ = try man.addFile(obj.path, null);
2419 man.hash.add(obj.must_link);2422 man.hash.add(obj.must_link);
...@@ -2789,6 +2792,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi...@@ -2789,6 +2792,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
2789 try argv.append("-version-script");2792 try argv.append("-version-script");
2790 try argv.append(version_script);2793 try argv.append(version_script);
2791 }2794 }
2795 if (self.allow_undefined_version) {
2796 try argv.append("--undefined-version");
2797 } else {
2798 try argv.append("--no-undefined-version");
2799 }
2792 }2800 }
27932801
2794 // Positional arguments to the linker such as object files.2802 // Positional arguments to the linker such as object files.
src/link/MachO/zld.zig+1-1
...@@ -55,7 +55,7 @@ pub fn linkWithZld(...@@ -55,7 +55,7 @@ pub fn linkWithZld(
55 // We are about to obtain this lock, so here we give other processes a chance first.55 // We are about to obtain this lock, so here we give other processes a chance first.
56 macho_file.base.releaseLock();56 macho_file.base.releaseLock();
5757
58 comptime assert(Compilation.link_hash_implementation_version == 10);58 comptime assert(Compilation.link_hash_implementation_version == 11);
5959
60 for (objects) |obj| {60 for (objects) |obj| {
61 _ = try man.addFile(obj.path, null);61 _ = try man.addFile(obj.path, null);
src/link/Wasm.zig+2-2
...@@ -3547,7 +3547,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin...@@ -3547,7 +3547,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
3547 // We are about to obtain this lock, so here we give other processes a chance first.3547 // We are about to obtain this lock, so here we give other processes a chance first.
3548 wasm.base.releaseLock();3548 wasm.base.releaseLock();
35493549
3550 comptime assert(Compilation.link_hash_implementation_version == 10);3550 comptime assert(Compilation.link_hash_implementation_version == 11);
35513551
3552 for (objects) |obj| {3552 for (objects) |obj| {
3553 _ = try man.addFile(obj.path, null);3553 _ = try man.addFile(obj.path, null);
...@@ -4633,7 +4633,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo...@@ -4633,7 +4633,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo
4633 // We are about to obtain this lock, so here we give other processes a chance first.4633 // We are about to obtain this lock, so here we give other processes a chance first.
4634 wasm.base.releaseLock();4634 wasm.base.releaseLock();
46354635
4636 comptime assert(Compilation.link_hash_implementation_version == 10);4636 comptime assert(Compilation.link_hash_implementation_version == 11);
46374637
4638 for (comp.objects) |obj| {4638 for (comp.objects) |obj| {
4639 _ = try man.addFile(obj.path, null);4639 _ = try man.addFile(obj.path, null);
src/main.zig+12
...@@ -500,6 +500,8 @@ const usage_build_generic =...@@ -500,6 +500,8 @@ const usage_build_generic =
500 \\Global Link Options:500 \\Global Link Options:
501 \\ -T[script], --script [script] Use a custom linker script501 \\ -T[script], --script [script] Use a custom linker script
502 \\ --version-script [path] Provide a version .map file502 \\ --version-script [path] Provide a version .map file
503 \\ --undefined-version Allow version scripts to refer to undefined symbols
504 \\ --no-undefined-version (default) Disallow version scripts from referring to undefined symbols
503 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)505 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
504 \\ --sysroot [path] Set the system root directory (usually /)506 \\ --sysroot [path] Set the system root directory (usually /)
505 \\ --version [ver] Dynamic library semver507 \\ --version [ver] Dynamic library semver
...@@ -826,6 +828,7 @@ fn buildOutputType(...@@ -826,6 +828,7 @@ fn buildOutputType(
826 var want_compiler_rt: ?bool = null;828 var want_compiler_rt: ?bool = null;
827 var linker_script: ?[]const u8 = null;829 var linker_script: ?[]const u8 = null;
828 var version_script: ?[]const u8 = null;830 var version_script: ?[]const u8 = null;
831 var linker_allow_undefined_version: bool = false;
829 var disable_c_depfile = false;832 var disable_c_depfile = false;
830 var linker_sort_section: ?link.File.Elf.SortSection = null;833 var linker_sort_section: ?link.File.Elf.SortSection = null;
831 var linker_gc_sections: ?bool = null;834 var linker_gc_sections: ?bool = null;
...@@ -1200,6 +1203,10 @@ fn buildOutputType(...@@ -1200,6 +1203,10 @@ fn buildOutputType(
1200 linker_script = args_iter.nextOrFatal();1203 linker_script = args_iter.nextOrFatal();
1201 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {1204 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
1202 version_script = args_iter.nextOrFatal();1205 version_script = args_iter.nextOrFatal();
1206 } else if (mem.eql(u8, arg, "--undefined-version")) {
1207 linker_allow_undefined_version = true;
1208 } else if (mem.eql(u8, arg, "--no-undefined-version")) {
1209 linker_allow_undefined_version = false;
1203 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {1210 } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
1204 // We don't know whether this library is part of libc1211 // We don't know whether this library is part of libc
1205 // or libc++ until we resolve the target, so we append1212 // or libc++ until we resolve the target, so we append
...@@ -2153,6 +2160,10 @@ fn buildOutputType(...@@ -2153,6 +2160,10 @@ fn buildOutputType(
2153 create_module.opts.rdynamic = true;2160 create_module.opts.rdynamic = true;
2154 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {2161 } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
2155 version_script = linker_args_it.nextOrFatal();2162 version_script = linker_args_it.nextOrFatal();
2163 } else if (mem.eql(u8, arg, "--undefined-version")) {
2164 linker_allow_undefined_version = true;
2165 } else if (mem.eql(u8, arg, "--no-undefined-version")) {
2166 linker_allow_undefined_version = false;
2156 } else if (mem.eql(u8, arg, "-O")) {2167 } else if (mem.eql(u8, arg, "-O")) {
2157 linker_optimization = linker_args_it.nextOrFatal();2168 linker_optimization = linker_args_it.nextOrFatal();
2158 } else if (mem.startsWith(u8, arg, "-O")) {2169 } else if (mem.startsWith(u8, arg, "-O")) {
...@@ -3166,6 +3177,7 @@ fn buildOutputType(...@@ -3166,6 +3177,7 @@ fn buildOutputType(
3166 .hash_style = hash_style,3177 .hash_style = hash_style,
3167 .linker_script = linker_script,3178 .linker_script = linker_script,
3168 .version_script = version_script,3179 .version_script = version_script,
3180 .linker_allow_undefined_version = linker_allow_undefined_version,
3169 .disable_c_depfile = disable_c_depfile,3181 .disable_c_depfile = disable_c_depfile,
3170 .soname = resolved_soname,3182 .soname = resolved_soname,
3171 .linker_sort_section = linker_sort_section,3183 .linker_sort_section = linker_sort_section,
test/link/elf.zig+63
...@@ -94,6 +94,8 @@ pub fn testAll(b: *Build) *Step {...@@ -94,6 +94,8 @@ pub fn testAll(b: *Build) *Step {
94 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));94 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));
95 elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));95 elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));
96 elf_step.dependOn(testLdScriptPathError(b, .{ .target = glibc_target }));96 elf_step.dependOn(testLdScriptPathError(b, .{ .target = glibc_target }));
97 elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));
98 elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));
97 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));99 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));
98 // https://github.com/ziglang/zig/issues/17451100 // https://github.com/ziglang/zig/issues/17451
99 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));101 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));
...@@ -2008,6 +2010,67 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {...@@ -2008,6 +2010,67 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
2008 return test_step;2010 return test_step;
2009}2011}
20102012
2013fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
2014 const test_step = addTestStep(b, "ld-script-allow-undefined-version", opts);
2015
2016 const so = addSharedLibrary(b, opts, .{
2017 .name = "add",
2018 .zig_source_bytes =
2019 \\export fn add(a: i32, b: i32) i32 {
2020 \\ return a + b;
2021 \\}
2022 ,
2023 });
2024 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2025 so.setLinkerScript(ld);
2026 so.linker_allow_undefined_version = true;
2027
2028 const exe = addExecutable(b, opts, .{
2029 .name = "main",
2030 .zig_source_bytes =
2031 \\const std = @import("std");
2032 \\extern fn add(a: i32, b: i32) i32;
2033 \\pub fn main() void {
2034 \\ std.debug.print("{d}\n", .{add(1, 2)});
2035 \\}
2036 ,
2037 });
2038 exe.linkLibrary(so);
2039 exe.linkLibC();
2040
2041 const run = addRunArtifact(exe);
2042 run.expectStdErrEqual("3\n");
2043 test_step.dependOn(&run.step);
2044
2045 return test_step;
2046}
2047
2048fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {
2049 const test_step = addTestStep(b, "ld-script-disallow-undefined-version", opts);
2050
2051 const so = addSharedLibrary(b, opts, .{
2052 .name = "add",
2053 .zig_source_bytes =
2054 \\export fn add(a: i32, b: i32) i32 {
2055 \\ return a + b;
2056 \\}
2057 ,
2058 });
2059 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2060 so.setLinkerScript(ld);
2061 so.linker_allow_undefined_version = false;
2062
2063 expectLinkErrors(
2064 so,
2065 test_step,
2066 .{
2067 .contains = "error: ld.lld: version script assignment of 'ADD_1.0' to symbol 'sub' failed: symbol not defined",
2068 },
2069 );
2070
2071 return test_step;
2072}
2073
2011fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {2074fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
2012 const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);2075 const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);
20132076