Skip to content

WIP Attempt to rebase PR #19818 #24457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
11 changes: 11 additions & 0 deletions lib/std/Build/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions),
link_objects: std.ArrayListUnmanaged(LinkObject),

strip: ?bool,
soname: ?std.zig.SoName,
unwind_tables: ?std.builtin.UnwindTables,
single_threaded: ?bool,
stack_protector: ?bool,
Expand Down Expand Up @@ -238,6 +239,7 @@ pub const CreateOptions = struct {
link_libcpp: ?bool = null,
single_threaded: ?bool = null,
strip: ?bool = null,
soname: ?std.zig.SoName = null,
unwind_tables: ?std.builtin.UnwindTables = null,
dwarf_format: ?std.dwarf.Format = null,
code_model: std.builtin.CodeModel = .default,
Expand Down Expand Up @@ -288,6 +290,7 @@ pub fn init(
.frameworks = .{},
.link_objects = .{},
.strip = options.strip,
.soname = options.soname,
.unwind_tables = options.unwind_tables,
.single_threaded = options.single_threaded,
.stack_protector = options.stack_protector,
Expand Down Expand Up @@ -561,6 +564,14 @@ pub fn appendZigProcessFlags(
.full => try zig_args.append("-fsanitize-c=full"),
};

if (m.soname) |soname| {
try zig_args.append(switch (soname) {
.no => "-fno-soname",
.yes_default_value => "-fsoname",
.yes => |value| b.fmt("-fsoname={s}", .{value}),
});
}

if (m.dwarf_format) |dwarf_format| {
try zig_args.append(switch (dwarf_format) {
.@"32" => "-gdwarf32",
Expand Down
6 changes: 6 additions & 0 deletions lib/std/zig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ pub const BuildId = union(enum) {

pub const LtoMode = enum { none, full, thin };

pub const SoName = union(enum) {
no,
yes_default_value,
yes: []const u8,
};

/// Renders a `std.Target.Cpu` value into a textual representation that can be parsed
/// via the `-mcpu` flag passed to the Zig compiler.
/// Appends the result to `buffer`.
Expand Down
30 changes: 21 additions & 9 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,8 @@ pub const Input = union(enum) {
needed: bool,
weak: bool,
reexport: bool,
name: ?[]const u8,
lib_directory: Directory,
};

pub const DsoExact = struct {
Expand Down Expand Up @@ -1661,6 +1663,8 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
man.hash.add(dso.needed);
man.hash.add(dso.weak);
man.hash.add(dso.reexport);
man.hash.addOptionalBytes(dso.name);
man.hash.addOptionalBytes(dso.lib_directory.path);
},
.dso_exact => |dso_exact| {
man.hash.addBytes(dso_exact.name);
Expand Down Expand Up @@ -1936,7 +1940,7 @@ fn resolveLibInput(
else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }),
};
errdefer file.close();
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query, name_query.name, lib_directory);
}

{
Expand All @@ -1950,10 +1954,10 @@ fn resolveLibInput(
}),
};
try checked_paths.writer(gpa).print("\n {f}", .{test_path});
switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{
switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, lib_directory, target, .{
.path = test_path,
.query = name_query.query,
}, link_mode, color)) {
}, link_mode, color, name_query.name)) {
.no_match => {},
.ok => return .ok,
}
Expand All @@ -1974,7 +1978,7 @@ fn resolveLibInput(
}),
};
errdefer file.close();
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query, name_query.name, lib_directory);
}

// In the case of MinGW, the main check will be .lib but we also need to
Expand All @@ -1990,7 +1994,7 @@ fn resolveLibInput(
else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }),
};
errdefer file.close();
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query, name_query.name, lib_directory);
}

return .no_match;
Expand All @@ -2002,6 +2006,8 @@ fn finishResolveLibInput(
file: std.fs.File,
link_mode: std.builtin.LinkMode,
query: UnresolvedInput.Query,
name: ?[]const u8,
lib_directory: Directory,
) ResolveLibInputResult {
switch (link_mode) {
.static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{
Expand All @@ -2016,6 +2022,8 @@ fn finishResolveLibInput(
.needed = query.needed,
.weak = query.weak,
.reexport = query.reexport,
.name = name,
.lib_directory = lib_directory,
} }),
}
return .ok;
Expand All @@ -2035,8 +2043,8 @@ fn resolvePathInput(
color: std.zig.Color,
) Allocator.Error!?ResolveLibInputResult {
switch (Compilation.classifyFileExt(pq.path.sub_path)) {
.static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
.shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
.static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, Directory.cwd(), target, pq, .static, color, null),
.shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, Directory.cwd(), target, pq, .dynamic, color, null),
.object => {
var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) });
Expand Down Expand Up @@ -2072,10 +2080,12 @@ fn resolvePathInputLib(
resolved_inputs: *std.ArrayListUnmanaged(Input),
/// Allocated via `gpa`.
ld_script_bytes: *std.ArrayListUnmanaged(u8),
lib_directory: Directory,
target: *const std.Target,
pq: UnresolvedInput.PathQuery,
link_mode: std.builtin.LinkMode,
color: std.zig.Color,
name: ?[]const u8,
) Allocator.Error!ResolveLibInputResult {
try resolved_inputs.ensureUnusedCapacity(gpa, 1);

Expand All @@ -2100,7 +2110,7 @@ fn resolvePathInputLib(
const buf = ld_script_bytes.items[0..n];
if (mem.startsWith(u8, buf, std.elf.MAGIC) or mem.startsWith(u8, buf, std.elf.ARMAG)) {
// Appears to be an ELF or archive file.
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query, name, lib_directory);
}
const stat = file.stat() catch |err|
fatal("failed to stat {f}: {s}", .{ test_path, @errorName(err) });
Expand Down Expand Up @@ -2166,7 +2176,7 @@ fn resolvePathInputLib(
}),
};
errdefer file.close();
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query, name, lib_directory);
}

pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object {
Expand All @@ -2189,6 +2199,8 @@ pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso
.needed = needed,
.weak = weak,
.reexport = reexport,
.name = null,
.lib_directory = Directory.cwd(),
};
}

Expand Down
15 changes: 11 additions & 4 deletions src/link/Lld.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1179,10 +1179,17 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
}

// By this time, we depend on these libs being dynamically linked
// libraries and not static libraries (the check for that needs to be earlier),
// but they could be full paths to .so files, in which case we
// want to avoid prepending "-l".
argv.appendAssumeCapacity(try dso.path.toString(arena));
// libraries and not static libraries (the check for that needs to be earlier).
if (dso.name) |name| {
if (dso.lib_directory.path) |path| {
argv.appendAssumeCapacity("-L");
argv.appendAssumeCapacity(path);
}
argv.appendAssumeCapacity(if (dso.weak) "-weak-l" else "-l");
argv.appendAssumeCapacity(name);
} else {
argv.appendAssumeCapacity(try dso.path.toString(arena));
}
},
};

Expand Down
9 changes: 2 additions & 7 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const native_os = builtin.os.tag;
const Cache = std.Build.Cache;
const Path = std.Build.Cache.Path;
const Directory = std.Build.Cache.Directory;
const SoName = std.zig.SoName;
const EnvVar = std.zig.EnvVar;
const LibCInstallation = std.zig.LibCInstallation;
const AstGen = std.zig.AstGen;
Expand Down Expand Up @@ -685,12 +686,6 @@ const usage_build_generic =
\\
;

const SOName = union(enum) {
no,
yes_default_value,
yes: []const u8,
};

const EmitBin = union(enum) {
no,
yes_default_path,
Expand Down Expand Up @@ -837,7 +832,7 @@ fn buildOutputType(
var target_arch_os_abi: ?[]const u8 = null;
var target_mcpu: ?[]const u8 = null;
var emit_h: Emit = .no;
var soname: SOName = undefined;
var soname: SoName = undefined;
var want_compiler_rt: ?bool = null;
var want_ubsan_rt: ?bool = null;
var linker_script: ?[]const u8 = null;
Expand Down
49 changes: 49 additions & 0 deletions test/link/elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {

// glibc tests
elf_step.dependOn(testAsNeeded(b, .{ .target = gnu_target }));
elf_step.dependOn(testLibraryPathsCompatibility(b, .{ .target = gnu_target, .use_lld = true }));
elf_step.dependOn(testLibraryPathsCompatibility(b, .{ .target = gnu_target, .use_lld = false }));
// https://github.com/ziglang/zig/issues/17430
// elf_step.dependOn(testCanonicalPlt(b, .{ .target = gnu_target }));
elf_step.dependOn(testCommentString(b, .{ .target = gnu_target }));
Expand Down Expand Up @@ -309,6 +311,53 @@ fn testAsNeeded(b: *Build, opts: Options) *Step {
return test_step;
}

fn testLibraryPathsCompatibility(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "library-paths-compatibility", opts);

const main_o = addObject(b, opts, .{
.name = "main",
.c_source_bytes =
\\#include <stdio.h>
\\int foo();
\\int main() {
\\ printf("%d\n", foo());
\\ return 0;
\\}
\\
,
});
main_o.linkLibC();

const libfoo = addSharedLibrary(b, opts, .{ .name = "foo", .soname = .no });
addCSourceBytes(libfoo, "int foo() { return 42; }", &.{});

{
const scripts = WriteFile.create(b);
const path = scripts.addCopyFile(libfoo.getEmittedBin(), "foo/libfoo.so");

const exe = addExecutable(b, opts, .{ .name = "test" });
exe.addObject(main_o);

exe.addLibraryPath(.{ .generated = .{ .file = &scripts.generated_directory, .sub_path = "foo" } });
exe.addRPath(path.dirname());

exe.linkSystemLibrary2("foo", .{ .needed = false });
exe.linkLibC();

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

const check = exe.checkObject();
check.checkInDynamicSection();
check.checkExact("NEEDED libfoo.so");
check.checkNotPresent("NEEDED foo/libfoo.so");
test_step.dependOn(&check.step);
}

return test_step;
}

fn testCanonicalPlt(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "canonical-plt", opts);

Expand Down
2 changes: 2 additions & 0 deletions test/link/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const OverlayOptions = struct {
zig_source_bytes: ?[]const u8 = null,
pic: ?bool = null,
strip: ?bool = null,
soname: ?std.zig.SoName = null,
};

pub fn addExecutable(b: *std.Build, base: Options, overlay: OverlayOptions) *Compile {
Expand Down Expand Up @@ -97,6 +98,7 @@ fn createModule(b: *Build, base: Options, overlay: OverlayOptions) *Build.Module
},
.pic = overlay.pic,
.strip = if (base.strip) |s| s else overlay.strip,
.soname = overlay.soname,
});

if (overlay.objcpp_source_bytes) |bytes| {
Expand Down