Skip to content

Commit 7320b24

Browse files
authored
Merge pull request #18486 from castholm/undefined-version
Add support for `--(no-)undefined-version`
2 parents acca16c + 3cd6468 commit 7320b24

File tree

9 files changed

+100
-7
lines changed

9 files changed

+100
-7
lines changed

lib/std/Build/Step/Compile.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ linker_dynamicbase: bool = true,
110110

111111
linker_allow_shlib_undefined: ?bool = null,
112112

113+
/// Allow version scripts to refer to undefined symbols.
114+
linker_allow_undefined_version: ?bool = null,
115+
113116
/// Permit read-only relocations in read-only segments. Disallowed by default.
114117
link_z_notext: bool = false,
115118

@@ -1451,6 +1454,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
14511454
try zig_args.append("--version-script");
14521455
try zig_args.append(version_script.getPath(b));
14531456
}
1457+
if (self.linker_allow_undefined_version) |x| {
1458+
try zig_args.append(if (x) "--undefined-version" else "--no-undefined-version");
1459+
}
14541460

14551461
if (self.kind == .@"test") {
14561462
if (self.exec_cmd_args) |exec_cmd_args| {

src/Compilation.zig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,7 @@ pub const CreateOptions = struct {
10331033
link_emit_relocs: bool = false,
10341034
linker_script: ?[]const u8 = null,
10351035
version_script: ?[]const u8 = null,
1036+
linker_allow_undefined_version: bool = false,
10361037
soname: ?[]const u8 = null,
10371038
linker_gc_sections: ?bool = null,
10381039
linker_allow_shlib_undefined: ?bool = null,
@@ -1572,6 +1573,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15721573
.stack_size = options.stack_size,
15731574
.image_base = options.image_base,
15741575
.version_script = options.version_script,
1576+
.allow_undefined_version = options.linker_allow_undefined_version,
15751577
.gc_sections = options.linker_gc_sections,
15761578
.emit_relocs = options.link_emit_relocs,
15771579
.soname = options.soname,
@@ -2458,7 +2460,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
24582460
/// to remind the programmer to update multiple related pieces of code that
24592461
/// are in different locations. Bump this number when adding or deleting
24602462
/// anything from the link cache manifest.
2461-
pub const link_hash_implementation_version = 10;
2463+
pub const link_hash_implementation_version = 11;
24622464

24632465
fn addNonIncrementalStuffToCacheManifest(
24642466
comp: *Compilation,
@@ -2467,7 +2469,7 @@ fn addNonIncrementalStuffToCacheManifest(
24672469
) !void {
24682470
const gpa = comp.gpa;
24692471

2470-
comptime assert(link_hash_implementation_version == 10);
2472+
comptime assert(link_hash_implementation_version == 11);
24712473

24722474
if (comp.module) |mod| {
24732475
const main_zig_file = try mod.main_mod.root.joinString(arena, mod.main_mod.root_src_path);
@@ -2541,6 +2543,7 @@ fn addNonIncrementalStuffToCacheManifest(
25412543

25422544
try man.addOptionalFile(opts.linker_script);
25432545
try man.addOptionalFile(opts.version_script);
2546+
man.hash.add(opts.allow_undefined_version);
25442547

25452548
man.hash.addOptional(opts.stack_size);
25462549
man.hash.addOptional(opts.image_base);

src/link.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub const File = struct {
113113
minor_subsystem_version: ?u16,
114114
gc_sections: ?bool,
115115
allow_shlib_undefined: ?bool,
116+
allow_undefined_version: bool,
116117
subsystem: ?std.Target.SubSystem,
117118
linker_script: ?[]const u8,
118119
version_script: ?[]const u8,

src/link/Coff/lld.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)
7070
man = comp.cache_parent.obtain();
7171
self.base.releaseLock();
7272

73-
comptime assert(Compilation.link_hash_implementation_version == 10);
73+
comptime assert(Compilation.link_hash_implementation_version == 11);
7474

7575
for (comp.objects) |obj| {
7676
_ = try man.addFile(obj.path, null);

src/link/Elf.zig

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ soname: ?[]const u8,
2222
bind_global_refs_locally: bool,
2323
linker_script: ?[]const u8,
2424
version_script: ?[]const u8,
25+
allow_undefined_version: bool,
2526
print_icf_sections: bool,
2627
print_map: bool,
2728
entry_name: ?[]const u8,
@@ -325,6 +326,7 @@ pub fn createEmpty(
325326
.bind_global_refs_locally = options.bind_global_refs_locally,
326327
.linker_script = options.linker_script,
327328
.version_script = options.version_script,
329+
.allow_undefined_version = options.allow_undefined_version,
328330
.print_icf_sections = options.print_icf_sections,
329331
.print_map = options.print_map,
330332
};
@@ -2410,10 +2412,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
24102412
// We are about to obtain this lock, so here we give other processes a chance first.
24112413
self.base.releaseLock();
24122414

2413-
comptime assert(Compilation.link_hash_implementation_version == 10);
2415+
comptime assert(Compilation.link_hash_implementation_version == 11);
24142416

24152417
try man.addOptionalFile(self.linker_script);
24162418
try man.addOptionalFile(self.version_script);
2419+
man.hash.add(self.allow_undefined_version);
24172420
for (comp.objects) |obj| {
24182421
_ = try man.addFile(obj.path, null);
24192422
man.hash.add(obj.must_link);
@@ -2789,6 +2792,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
27892792
try argv.append("-version-script");
27902793
try argv.append(version_script);
27912794
}
2795+
if (self.allow_undefined_version) {
2796+
try argv.append("--undefined-version");
2797+
} else {
2798+
try argv.append("--no-undefined-version");
2799+
}
27922800
}
27932801

27942802
// Positional arguments to the linker such as object files.

src/link/MachO/zld.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub fn linkWithZld(
5555
// We are about to obtain this lock, so here we give other processes a chance first.
5656
macho_file.base.releaseLock();
5757

58-
comptime assert(Compilation.link_hash_implementation_version == 10);
58+
comptime assert(Compilation.link_hash_implementation_version == 11);
5959

6060
for (objects) |obj| {
6161
_ = try man.addFile(obj.path, null);

src/link/Wasm.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3547,7 +3547,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
35473547
// We are about to obtain this lock, so here we give other processes a chance first.
35483548
wasm.base.releaseLock();
35493549

3550-
comptime assert(Compilation.link_hash_implementation_version == 10);
3550+
comptime assert(Compilation.link_hash_implementation_version == 11);
35513551

35523552
for (objects) |obj| {
35533553
_ = try man.addFile(obj.path, null);
@@ -4633,7 +4633,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo
46334633
// We are about to obtain this lock, so here we give other processes a chance first.
46344634
wasm.base.releaseLock();
46354635

4636-
comptime assert(Compilation.link_hash_implementation_version == 10);
4636+
comptime assert(Compilation.link_hash_implementation_version == 11);
46374637

46384638
for (comp.objects) |obj| {
46394639
_ = try man.addFile(obj.path, null);

src/main.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ const usage_build_generic =
500500
\\Global Link Options:
501501
\\ -T[script], --script [script] Use a custom linker script
502502
\\ --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
503505
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
504506
\\ --sysroot [path] Set the system root directory (usually /)
505507
\\ --version [ver] Dynamic library semver
@@ -826,6 +828,7 @@ fn buildOutputType(
826828
var want_compiler_rt: ?bool = null;
827829
var linker_script: ?[]const u8 = null;
828830
var version_script: ?[]const u8 = null;
831+
var linker_allow_undefined_version: bool = false;
829832
var disable_c_depfile = false;
830833
var linker_sort_section: ?link.File.Elf.SortSection = null;
831834
var linker_gc_sections: ?bool = null;
@@ -1200,6 +1203,10 @@ fn buildOutputType(
12001203
linker_script = args_iter.nextOrFatal();
12011204
} else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
12021205
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;
12031210
} else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
12041211
// We don't know whether this library is part of libc
12051212
// or libc++ until we resolve the target, so we append
@@ -2153,6 +2160,10 @@ fn buildOutputType(
21532160
create_module.opts.rdynamic = true;
21542161
} else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
21552162
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;
21562167
} else if (mem.eql(u8, arg, "-O")) {
21572168
linker_optimization = linker_args_it.nextOrFatal();
21582169
} else if (mem.startsWith(u8, arg, "-O")) {
@@ -3166,6 +3177,7 @@ fn buildOutputType(
31663177
.hash_style = hash_style,
31673178
.linker_script = linker_script,
31683179
.version_script = version_script,
3180+
.linker_allow_undefined_version = linker_allow_undefined_version,
31693181
.disable_c_depfile = disable_c_depfile,
31703182
.soname = resolved_soname,
31713183
.linker_sort_section = linker_sort_section,

test/link/elf.zig

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pub fn testAll(b: *Build) *Step {
9494
elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));
9595
elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));
9696
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 }));
9799
elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));
98100
// https://github.com/ziglang/zig/issues/17451
99101
// elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));
@@ -2008,6 +2010,67 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
20082010
return test_step;
20092011
}
20102012

2013+
fn 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+
2048+
fn 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+
20112074
fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
20122075
const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);
20132076

0 commit comments

Comments
 (0)