mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Elf2: start implementing dynamic linking
This commit is contained in:
parent
40901440a6
commit
0834e696f7
@ -596,10 +596,13 @@ pub fn appendZigProcessFlags(
|
||||
"-target", try target.query.zigTriple(b.allocator),
|
||||
"-mcpu", try target.query.serializeCpuAlloc(b.allocator),
|
||||
});
|
||||
|
||||
if (target.query.dynamic_linker.get()) |dynamic_linker| {
|
||||
try zig_args.append("--dynamic-linker");
|
||||
try zig_args.append(dynamic_linker);
|
||||
if (target.query.dynamic_linker) |dynamic_linker| {
|
||||
if (dynamic_linker.get()) |dynamic_linker_path| {
|
||||
try zig_args.append("--dynamic-linker");
|
||||
try zig_args.append(dynamic_linker_path);
|
||||
} else {
|
||||
try zig_args.append("--no-dynamic-linker");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,8 +46,9 @@ android_api_level: ?u32 = null,
|
||||
abi: ?Target.Abi = null,
|
||||
|
||||
/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
|
||||
/// based on the `os_tag`.
|
||||
dynamic_linker: Target.DynamicLinker = .none,
|
||||
/// based on the `os_tag`. When `dynamic_linker` is a non-`null` empty string, no dynamic
|
||||
/// linker is used regardless of `os_tag`.
|
||||
dynamic_linker: ?Target.DynamicLinker = null,
|
||||
|
||||
/// `null` means default for the cpu/arch/os combo.
|
||||
ofmt: ?Target.ObjectFormat = null,
|
||||
@ -213,7 +214,7 @@ pub fn parse(args: ParseOptions) !Query {
|
||||
const diags = args.diagnostics orelse &dummy_diags;
|
||||
|
||||
var result: Query = .{
|
||||
.dynamic_linker = Target.DynamicLinker.init(args.dynamic_linker),
|
||||
.dynamic_linker = if (args.dynamic_linker) |dynamic_linker| .init(dynamic_linker) else null,
|
||||
};
|
||||
|
||||
var it = mem.splitScalar(u8, args.arch_os_abi, '-');
|
||||
@ -381,7 +382,7 @@ pub fn isNativeCpu(self: Query) bool {
|
||||
|
||||
pub fn isNativeOs(self: Query) bool {
|
||||
return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
|
||||
self.dynamic_linker.get() == null and self.glibc_version == null and self.android_api_level == null;
|
||||
self.dynamic_linker == null and self.glibc_version == null and self.android_api_level == null;
|
||||
}
|
||||
|
||||
pub fn isNativeAbi(self: Query) bool {
|
||||
@ -599,7 +600,7 @@ pub fn eql(a: Query, b: Query) bool {
|
||||
if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;
|
||||
if (a.android_api_level != b.android_api_level) return false;
|
||||
if (a.abi != b.abi) return false;
|
||||
if (!a.dynamic_linker.eql(b.dynamic_linker)) return false;
|
||||
if (!dynamicLinkerEqualOpt(a.dynamic_linker, b.dynamic_linker)) return false;
|
||||
if (a.ofmt != b.ofmt) return false;
|
||||
|
||||
return true;
|
||||
@ -611,6 +612,12 @@ fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {
|
||||
return SemanticVersion.order(a.?, b.?) == .eq;
|
||||
}
|
||||
|
||||
fn dynamicLinkerEqualOpt(a: ?Target.DynamicLinker, b: ?Target.DynamicLinker) bool {
|
||||
if (a == null and b == null) return true;
|
||||
if (a == null or b == null) return false;
|
||||
return a.?.eql(b.?);
|
||||
}
|
||||
|
||||
test parse {
|
||||
const io = std.testing.io;
|
||||
|
||||
|
||||
@ -7013,7 +7013,8 @@ pub const RTLD = switch (native_os) {
|
||||
LAZY: bool = false,
|
||||
NOW: bool = false,
|
||||
NOLOAD: bool = false,
|
||||
_3: u5 = 0,
|
||||
DEEPBIND: bool = false,
|
||||
_4: u4 = 0,
|
||||
GLOBAL: bool = false,
|
||||
_9: u3 = 0,
|
||||
NODELETE: bool = false,
|
||||
|
||||
@ -562,7 +562,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
|
||||
// Apply the initial relocations as early as possible in the startup process. We cannot
|
||||
// make calls yet on some architectures (e.g. MIPS) *because* they haven't been applied yet,
|
||||
// so this must be fully inlined.
|
||||
if (builtin.position_independent_executable) {
|
||||
if (builtin.link_mode == .static and builtin.position_independent_executable) {
|
||||
@call(.always_inline, std.pie.relocate, .{phdrs});
|
||||
}
|
||||
|
||||
|
||||
@ -585,10 +585,10 @@ fn abiAndDynamicLinkerFromFile(
|
||||
.os = os,
|
||||
.abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag),
|
||||
.ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
|
||||
.dynamic_linker = query.dynamic_linker,
|
||||
.dynamic_linker = query.dynamic_linker orelse .none,
|
||||
};
|
||||
var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC
|
||||
const look_for_ld = query.dynamic_linker.get() == null;
|
||||
const look_for_ld = query.dynamic_linker == null;
|
||||
|
||||
var got_dyn_section: bool = false;
|
||||
{
|
||||
@ -938,7 +938,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
|
||||
const is_linux = builtin.target.os.tag == .linux;
|
||||
const is_illumos = builtin.target.os.tag == .illumos;
|
||||
const is_darwin = builtin.target.os.tag.isDarwin();
|
||||
const have_all_info = query.dynamic_linker.get() != null and
|
||||
const have_all_info = query.dynamic_linker != null and
|
||||
query.abi != null and (!is_linux or query.abi.?.isGnu());
|
||||
const os_is_non_native = query.os_tag != null;
|
||||
// The illumos environment is always the same.
|
||||
@ -1126,10 +1126,7 @@ fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Quer
|
||||
.os = os,
|
||||
.abi = abi,
|
||||
.ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
|
||||
.dynamic_linker = if (query.dynamic_linker.get() == null)
|
||||
Target.DynamicLinker.standard(cpu, os, abi)
|
||||
else
|
||||
query.dynamic_linker,
|
||||
.dynamic_linker = query.dynamic_linker orelse .standard(cpu, os, abi),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -123,6 +123,7 @@ pub const ResolveError = error{
|
||||
WasiExecModelRequiresWasi,
|
||||
SharedMemoryIsWasmOnly,
|
||||
ObjectFilesCannotShareMemory,
|
||||
ObjectFilesCannotSpecifyDynamicLinker,
|
||||
SharedMemoryRequiresAtomicsAndBulkMemory,
|
||||
ThreadsRequireSharedMemory,
|
||||
EmittingLlvmModuleRequiresLlvmBackend,
|
||||
@ -131,6 +132,7 @@ pub const ResolveError = error{
|
||||
EmittingBinaryRequiresLlvmLibrary,
|
||||
LldIncompatibleObjectFormat,
|
||||
LldCannotIncrementallyLink,
|
||||
LldCannotSpecifyDynamicLinkerForSharedLibraries,
|
||||
LtoRequiresLld,
|
||||
SanitizeThreadRequiresLibCpp,
|
||||
LibCRequiresLibUnwind,
|
||||
@ -142,6 +144,7 @@ pub const ResolveError = error{
|
||||
TargetCannotStaticLinkExecutables,
|
||||
LibCRequiresDynamicLinking,
|
||||
SharedLibrariesRequireDynamicLinking,
|
||||
DynamicLinkingWithLldRequiresSharedLibraries,
|
||||
ExportMemoryAndDynamicIncompatible,
|
||||
DynamicLibraryPrecludesPie,
|
||||
TargetRequiresPie,
|
||||
@ -274,16 +277,11 @@ pub fn resolve(options: Options) ResolveError!Config {
|
||||
if (options.link_mode == .static) return error.LibCRequiresDynamicLinking;
|
||||
break :b .dynamic;
|
||||
}
|
||||
// When creating a executable that links to system libraries, we
|
||||
// require dynamic linking, but we must not link static libraries
|
||||
// or object files dynamically!
|
||||
if (options.any_dyn_libs and options.output_mode == .Exe) {
|
||||
if (options.link_mode == .static) return error.SharedLibrariesRequireDynamicLinking;
|
||||
break :b .dynamic;
|
||||
}
|
||||
|
||||
if (options.link_mode) |link_mode| break :b link_mode;
|
||||
|
||||
if (options.any_dyn_libs) break :b .dynamic;
|
||||
|
||||
if (explicitly_exe_or_dyn_lib and link_libc) {
|
||||
// When using the native glibc/musl ABI, dynamic linking is usually what people want.
|
||||
if (options.resolved_target.is_native_abi and (target.isGnuLibC() or target.isMuslLibC())) {
|
||||
@ -425,6 +423,25 @@ pub fn resolve(options: Options) ResolveError!Config {
|
||||
break :b use_llvm;
|
||||
};
|
||||
|
||||
switch (options.output_mode) {
|
||||
.Exe => if (options.any_dyn_libs) {
|
||||
// When creating a executable that links to system libraries, we
|
||||
// require dynamic linking, but we must not link static libraries
|
||||
// or object files dynamically!
|
||||
if (link_mode == .static) return error.SharedLibrariesRequireDynamicLinking;
|
||||
} else if (use_lld and !link_libc and !link_libcpp and !link_libunwind) {
|
||||
// Lld does not support creating dynamic executables when not
|
||||
// linking to any shared libraries.
|
||||
if (link_mode == .dynamic) return error.DynamicLinkingWithLldRequiresSharedLibraries;
|
||||
},
|
||||
.Lib => if (use_lld and options.resolved_target.is_explicit_dynamic_linker) {
|
||||
return error.LldCannotSpecifyDynamicLinkerForSharedLibraries;
|
||||
},
|
||||
.Obj => if (options.resolved_target.is_explicit_dynamic_linker) {
|
||||
return error.ObjectFilesCannotSpecifyDynamicLinker;
|
||||
},
|
||||
}
|
||||
|
||||
const use_new_linker = b: {
|
||||
if (use_lld) {
|
||||
if (options.use_new_linker == true) return error.NewLinkerIncompatibleWithLld;
|
||||
|
||||
@ -182,6 +182,10 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
|
||||
else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
|
||||
.name = extern_func.toSlice(&emit.lower.mir).?,
|
||||
.lib_name = switch (comp.compiler_rt_strat) {
|
||||
.none, .lib, .obj, .zcu => null,
|
||||
.dyn_lib => "compiler_rt",
|
||||
},
|
||||
.type = .FUNC,
|
||||
})) else if (emit.bin_file.cast(.macho)) |macho_file|
|
||||
try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
|
||||
@ -320,10 +324,12 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
}, emit.lower.target), &.{.{
|
||||
.op_index = 0,
|
||||
.target = .{
|
||||
.index = if (emit.bin_file.cast(.elf)) |elf_file|
|
||||
try elf_file.getGlobalSymbol("__tls_get_addr", null)
|
||||
else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
|
||||
.index = if (emit.bin_file.cast(.elf)) |elf_file| try elf_file.getGlobalSymbol(
|
||||
"__tls_get_addr",
|
||||
if (comp.config.link_libc) "c" else null,
|
||||
) else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
|
||||
.name = "__tls_get_addr",
|
||||
.lib_name = if (comp.config.link_libc) "c" else null,
|
||||
.type = .FUNC,
|
||||
})) else unreachable,
|
||||
.is_extern = true,
|
||||
|
||||
@ -1882,17 +1882,13 @@ fn initSyntheticSections(self: *Elf) !void {
|
||||
const comp = self.base.comp;
|
||||
const target = self.getTarget();
|
||||
const ptr_size = self.ptrWidthBytes();
|
||||
const shared_objects = self.shared_objects.values();
|
||||
|
||||
const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
|
||||
.Exe => true,
|
||||
.Lib => comp.config.link_mode == .dynamic,
|
||||
.Obj => false,
|
||||
};
|
||||
const have_dynamic_linker = comp.config.link_mode == .dynamic and is_exe_or_dyn_lib and !target.dynamic_linker.eql(.none);
|
||||
|
||||
const needs_interp = have_dynamic_linker and
|
||||
(comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker);
|
||||
const have_dynamic_linker = comp.config.link_mode == .dynamic and is_exe_or_dyn_lib;
|
||||
|
||||
const needs_eh_frame = blk: {
|
||||
if (self.zigObjectPtr()) |zo|
|
||||
@ -2004,7 +2000,15 @@ fn initSyntheticSections(self: *Elf) !void {
|
||||
});
|
||||
}
|
||||
|
||||
if (needs_interp and self.section_indexes.interp == null) {
|
||||
if (needs_interp: {
|
||||
if (comp.config.link_mode == .static) break :needs_interp false;
|
||||
if (target.dynamic_linker.get() == null) break :needs_interp false;
|
||||
break :needs_interp switch (comp.config.output_mode) {
|
||||
.Exe => true,
|
||||
.Lib => comp.root_mod.resolved_target.is_explicit_dynamic_linker,
|
||||
.Obj => false,
|
||||
};
|
||||
} and self.section_indexes.interp == null) {
|
||||
self.section_indexes.interp = try self.addSection(.{
|
||||
.name = try self.insertShString(".interp"),
|
||||
.type = elf.SHT_PROGBITS,
|
||||
@ -2013,7 +2017,7 @@ fn initSyntheticSections(self: *Elf) !void {
|
||||
});
|
||||
}
|
||||
|
||||
if (self.isEffectivelyDynLib() or shared_objects.len > 0 or comp.config.pie) {
|
||||
if (have_dynamic_linker or comp.config.pie or self.isEffectivelyDynLib()) {
|
||||
if (self.section_indexes.dynstrtab == null) {
|
||||
self.section_indexes.dynstrtab = try self.addSection(.{
|
||||
.name = try self.insertShString(".dynstr"),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -808,7 +808,6 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
|
||||
const link_mode = comp.config.link_mode;
|
||||
const is_dyn_lib = link_mode == .dynamic and is_lib;
|
||||
const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
|
||||
const have_dynamic_linker = link_mode == .dynamic and is_exe_or_dyn_lib;
|
||||
const target = &comp.root_mod.resolved_target.result;
|
||||
const compiler_rt_path: ?Cache.Path = blk: {
|
||||
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
|
||||
@ -1070,12 +1069,12 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
|
||||
}
|
||||
}
|
||||
|
||||
if (have_dynamic_linker and
|
||||
(comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker))
|
||||
{
|
||||
if (output_mode == .Exe and link_mode == .dynamic) {
|
||||
if (target.dynamic_linker.get()) |dynamic_linker| {
|
||||
try argv.append("-dynamic-linker");
|
||||
try argv.append("--dynamic-linker");
|
||||
try argv.append(dynamic_linker);
|
||||
} else {
|
||||
try argv.append("--no-dynamic-linker");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
src/main.zig
30
src/main.zig
@ -558,6 +558,7 @@ const usage_build_generic =
|
||||
\\ --enable-new-dtags Use the new behavior for dynamic tags (RUNPATH)
|
||||
\\ --disable-new-dtags Use the old behavior for dynamic tags (RPATH)
|
||||
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
|
||||
\\ --no-dynamic-linker Do not set any dynamic interpreter path
|
||||
\\ --sysroot [path] Set the system root directory (usually /)
|
||||
\\ --version [ver] Dynamic library semver
|
||||
\\ -fentry Enable entry point with default symbol name
|
||||
@ -1301,6 +1302,8 @@ fn buildOutputType(
|
||||
mod_opts.optimize_mode = parseOptimizeMode(rest);
|
||||
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
|
||||
create_module.dynamic_linker = args_iter.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "--no-dynamic-linker")) {
|
||||
create_module.dynamic_linker = "";
|
||||
} else if (mem.eql(u8, arg, "--sysroot")) {
|
||||
const next_arg = args_iter.nextOrFatal();
|
||||
create_module.sysroot = next_arg;
|
||||
@ -2418,6 +2421,11 @@ fn buildOutputType(
|
||||
mem.eql(u8, arg, "-dynamic-linker"))
|
||||
{
|
||||
create_module.dynamic_linker = linker_args_it.nextOrFatal();
|
||||
} else if (mem.eql(u8, arg, "-I") or
|
||||
mem.eql(u8, arg, "--no-dynamic-linker") or
|
||||
mem.eql(u8, arg, "-no-dynamic-linker"))
|
||||
{
|
||||
create_module.dynamic_linker = "";
|
||||
} else if (mem.eql(u8, arg, "-E") or
|
||||
mem.eql(u8, arg, "--export-dynamic") or
|
||||
mem.eql(u8, arg, "-export-dynamic"))
|
||||
@ -3191,13 +3199,14 @@ fn buildOutputType(
|
||||
const resolved_soname: ?[]const u8 = switch (soname) {
|
||||
.yes => |explicit| explicit,
|
||||
.no => null,
|
||||
.yes_default_value => switch (target.ofmt) {
|
||||
.elf => if (have_version)
|
||||
.yes_default_value => if (create_module.resolved_options.output_mode == .Lib and
|
||||
create_module.resolved_options.link_mode == .dynamic and target.ofmt == .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,
|
||||
},
|
||||
try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name})
|
||||
else
|
||||
null,
|
||||
};
|
||||
|
||||
const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) {
|
||||
@ -3646,7 +3655,11 @@ fn buildOutputType(
|
||||
try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu}));
|
||||
}
|
||||
if (create_module.dynamic_linker) |dl| {
|
||||
try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });
|
||||
if (dl.len > 0) {
|
||||
try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });
|
||||
} else {
|
||||
try test_exec_args.append(arena, "--no-dynamic-linker");
|
||||
}
|
||||
}
|
||||
try test_exec_args.append(arena, null); // placeholder for the path of the emitted C source file
|
||||
}
|
||||
@ -3793,7 +3806,7 @@ fn createModule(
|
||||
.result = target,
|
||||
.is_native_os = target_query.isNativeOs(),
|
||||
.is_native_abi = target_query.isNativeAbi(),
|
||||
.is_explicit_dynamic_linker = !target_query.dynamic_linker.eql(.none),
|
||||
.is_explicit_dynamic_linker = target_query.dynamic_linker != null,
|
||||
};
|
||||
};
|
||||
|
||||
@ -3965,6 +3978,7 @@ fn createModule(
|
||||
error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
|
||||
error.SharedMemoryIsWasmOnly => fatal("only WebAssembly CPU targets support shared memory", .{}),
|
||||
error.ObjectFilesCannotShareMemory => fatal("object files cannot share memory", .{}),
|
||||
error.ObjectFilesCannotSpecifyDynamicLinker => fatal("object files cannot specify --dynamic-linker", .{}),
|
||||
error.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}),
|
||||
error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}),
|
||||
error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}),
|
||||
@ -3973,6 +3987,7 @@ fn createModule(
|
||||
error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}),
|
||||
error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}),
|
||||
error.LldCannotIncrementallyLink => fatal("self-hosted backends do not support linking with LLD", .{}),
|
||||
error.LldCannotSpecifyDynamicLinkerForSharedLibraries => fatal("LLD does not support --dynamic-linker on shared libraries", .{}),
|
||||
error.LtoRequiresLld => fatal("LTO requires using LLD", .{}),
|
||||
error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}),
|
||||
error.LibCRequiresLibUnwind => fatal("libc of the specified target requires linking libunwind", .{}),
|
||||
@ -3984,6 +3999,7 @@ fn createModule(
|
||||
error.TargetCannotStaticLinkExecutables => fatal("static linking of executables unavailable on the specified target", .{}),
|
||||
error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}),
|
||||
error.SharedLibrariesRequireDynamicLinking => fatal("using shared libraries requires dynamic linking", .{}),
|
||||
error.DynamicLinkingWithLldRequiresSharedLibraries => fatal("dynamic linking with lld requires at least one shared library", .{}),
|
||||
error.ExportMemoryAndDynamicIncompatible => fatal("exporting memory is incompatible with dynamic linking", .{}),
|
||||
error.DynamicLibraryPrecludesPie => fatal("dynamic libraries cannot be position independent executables", .{}),
|
||||
error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user