Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/std/Build/Step/Compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ linker_dynamicbase: bool = true,

linker_allow_shlib_undefined: ?bool = null,

/// Allow version scripts to refer to undefined symbols.
linker_allow_undefined_version: ?bool = null,

/// Permit read-only relocations in read-only segments. Disallowed by default.
link_z_notext: bool = false,

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

if (self.kind == .@"test") {
if (self.exec_cmd_args) |exec_cmd_args| {
Expand Down
7 changes: 5 additions & 2 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,7 @@ pub const CreateOptions = struct {
link_emit_relocs: bool = false,
linker_script: ?[]const u8 = null,
version_script: ?[]const u8 = null,
linker_allow_undefined_version: bool = false,
soname: ?[]const u8 = null,
linker_gc_sections: ?bool = null,
linker_allow_shlib_undefined: ?bool = null,
Expand Down Expand Up @@ -1572,6 +1573,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.stack_size = options.stack_size,
.image_base = options.image_base,
.version_script = options.version_script,
.allow_undefined_version = options.linker_allow_undefined_version,
.gc_sections = options.linker_gc_sections,
.emit_relocs = options.link_emit_relocs,
.soname = options.soname,
Expand Down Expand Up @@ -2458,7 +2460,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
/// to remind the programmer to update multiple related pieces of code that
/// are in different locations. Bump this number when adding or deleting
/// anything from the link cache manifest.
pub const link_hash_implementation_version = 10;
pub const link_hash_implementation_version = 11;

fn addNonIncrementalStuffToCacheManifest(
comp: *Compilation,
Expand All @@ -2467,7 +2469,7 @@ fn addNonIncrementalStuffToCacheManifest(
) !void {
const gpa = comp.gpa;

comptime assert(link_hash_implementation_version == 10);
comptime assert(link_hash_implementation_version == 11);

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

try man.addOptionalFile(opts.linker_script);
try man.addOptionalFile(opts.version_script);
man.hash.add(opts.allow_undefined_version);

man.hash.addOptional(opts.stack_size);
man.hash.addOptional(opts.image_base);
Expand Down
1 change: 1 addition & 0 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub const File = struct {
minor_subsystem_version: ?u16,
gc_sections: ?bool,
allow_shlib_undefined: ?bool,
allow_undefined_version: bool,
subsystem: ?std.Target.SubSystem,
linker_script: ?[]const u8,
version_script: ?[]const u8,
Expand Down
2 changes: 1 addition & 1 deletion src/link/Coff/lld.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)
man = comp.cache_parent.obtain();
self.base.releaseLock();

comptime assert(Compilation.link_hash_implementation_version == 10);
comptime assert(Compilation.link_hash_implementation_version == 11);

for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
Expand Down
10 changes: 9 additions & 1 deletion src/link/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ soname: ?[]const u8,
bind_global_refs_locally: bool,
linker_script: ?[]const u8,
version_script: ?[]const u8,
allow_undefined_version: bool,
print_icf_sections: bool,
print_map: bool,
entry_name: ?[]const u8,
Expand Down Expand Up @@ -325,6 +326,7 @@ pub fn createEmpty(
.bind_global_refs_locally = options.bind_global_refs_locally,
.linker_script = options.linker_script,
.version_script = options.version_script,
.allow_undefined_version = options.allow_undefined_version,
.print_icf_sections = options.print_icf_sections,
.print_map = options.print_map,
};
Expand Down Expand Up @@ -2410,10 +2412,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
// We are about to obtain this lock, so here we give other processes a chance first.
self.base.releaseLock();

comptime assert(Compilation.link_hash_implementation_version == 10);
comptime assert(Compilation.link_hash_implementation_version == 11);

try man.addOptionalFile(self.linker_script);
try man.addOptionalFile(self.version_script);
man.hash.add(self.allow_undefined_version);
for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
man.hash.add(obj.must_link);
Expand Down Expand Up @@ -2789,6 +2792,11 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
try argv.append("-version-script");
try argv.append(version_script);
}
if (self.allow_undefined_version) {
try argv.append("--undefined-version");
} else {
try argv.append("--no-undefined-version");
}
}

// Positional arguments to the linker such as object files.
Expand Down
2 changes: 1 addition & 1 deletion src/link/MachO/zld.zig
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn linkWithZld(
// We are about to obtain this lock, so here we give other processes a chance first.
macho_file.base.releaseLock();

comptime assert(Compilation.link_hash_implementation_version == 10);
comptime assert(Compilation.link_hash_implementation_version == 11);

for (objects) |obj| {
_ = try man.addFile(obj.path, null);
Expand Down
4 changes: 2 additions & 2 deletions src/link/Wasm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3547,7 +3547,7 @@ fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) lin
// We are about to obtain this lock, so here we give other processes a chance first.
wasm.base.releaseLock();

comptime assert(Compilation.link_hash_implementation_version == 10);
comptime assert(Compilation.link_hash_implementation_version == 11);

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

comptime assert(Compilation.link_hash_implementation_version == 10);
comptime assert(Compilation.link_hash_implementation_version == 11);

for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
Expand Down
12 changes: 12 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ const usage_build_generic =
\\Global Link Options:
\\ -T[script], --script [script] Use a custom linker script
\\ --version-script [path] Provide a version .map file
\\ --undefined-version Allow version scripts to refer to undefined symbols
\\ --no-undefined-version (default) Disallow version scripts from referring to undefined symbols
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
\\ --sysroot [path] Set the system root directory (usually /)
\\ --version [ver] Dynamic library semver
Expand Down Expand Up @@ -826,6 +828,7 @@ fn buildOutputType(
var want_compiler_rt: ?bool = null;
var linker_script: ?[]const u8 = null;
var version_script: ?[]const u8 = null;
var linker_allow_undefined_version: bool = false;
var disable_c_depfile = false;
var linker_sort_section: ?link.File.Elf.SortSection = null;
var linker_gc_sections: ?bool = null;
Expand Down Expand Up @@ -1200,6 +1203,10 @@ fn buildOutputType(
linker_script = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
version_script = args_iter.nextOrFatal();
} else if (mem.eql(u8, arg, "--undefined-version")) {
linker_allow_undefined_version = true;
} else if (mem.eql(u8, arg, "--no-undefined-version")) {
linker_allow_undefined_version = false;
} else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
// We don't know whether this library is part of libc
// or libc++ until we resolve the target, so we append
Expand Down Expand Up @@ -2153,6 +2160,10 @@ fn buildOutputType(
create_module.opts.rdynamic = true;
} else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
version_script = linker_args_it.nextOrFatal();
} else if (mem.eql(u8, arg, "--undefined-version")) {
linker_allow_undefined_version = true;
} else if (mem.eql(u8, arg, "--no-undefined-version")) {
linker_allow_undefined_version = false;
} else if (mem.eql(u8, arg, "-O")) {
linker_optimization = linker_args_it.nextOrFatal();
} else if (mem.startsWith(u8, arg, "-O")) {
Expand Down Expand Up @@ -3166,6 +3177,7 @@ fn buildOutputType(
.hash_style = hash_style,
.linker_script = linker_script,
.version_script = version_script,
.linker_allow_undefined_version = linker_allow_undefined_version,
.disable_c_depfile = disable_c_depfile,
.soname = resolved_soname,
.linker_sort_section = linker_sort_section,
Expand Down
63 changes: 63 additions & 0 deletions test/link/elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ pub fn testAll(b: *Build) *Step {
elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));
elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));
elf_step.dependOn(testLdScriptPathError(b, .{ .target = glibc_target }));
elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));
elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));
elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));
// https://github.com/ziglang/zig/issues/17451
// elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));
Expand Down Expand Up @@ -2008,6 +2010,67 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step {
return test_step;
}

fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ld-script-allow-undefined-version", opts);

const so = addSharedLibrary(b, opts, .{
.name = "add",
.zig_source_bytes =
\\export fn add(a: i32, b: i32) i32 {
\\ return a + b;
\\}
,
});
const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
so.setLinkerScript(ld);
so.linker_allow_undefined_version = true;

const exe = addExecutable(b, opts, .{
.name = "main",
.zig_source_bytes =
\\const std = @import("std");
\\extern fn add(a: i32, b: i32) i32;
\\pub fn main() void {
\\ std.debug.print("{d}\n", .{add(1, 2)});
\\}
,
});
exe.linkLibrary(so);
exe.linkLibC();

const run = addRunArtifact(exe);
run.expectStdErrEqual("3\n");
test_step.dependOn(&run.step);

return test_step;
}

fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ld-script-disallow-undefined-version", opts);

const so = addSharedLibrary(b, opts, .{
.name = "add",
.zig_source_bytes =
\\export fn add(a: i32, b: i32) i32 {
\\ return a + b;
\\}
,
});
const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
so.setLinkerScript(ld);
so.linker_allow_undefined_version = false;

expectLinkErrors(
so,
test_step,
.{
.contains = "error: ld.lld: version script assignment of 'ADD_1.0' to symbol 'sub' failed: symbol not defined",
},
);

return test_step;
}

fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);

Expand Down