Merge pull request #25558 from jacobly0/elfv2-load-obj

Elf2: start implementing input object loading
This commit is contained in:
Jacob Young 2025-10-30 12:09:13 -04:00 committed by GitHub
commit 5b060ef9d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1390 additions and 400 deletions

View File

@ -596,10 +596,13 @@ pub fn appendZigProcessFlags(
"-target", try target.query.zigTriple(b.allocator), "-target", try target.query.zigTriple(b.allocator),
"-mcpu", try target.query.serializeCpuAlloc(b.allocator), "-mcpu", try target.query.serializeCpuAlloc(b.allocator),
}); });
if (target.query.dynamic_linker) |dynamic_linker| {
if (target.query.dynamic_linker.get()) |dynamic_linker| { if (dynamic_linker.get()) |dynamic_linker_path| {
try zig_args.append("--dynamic-linker"); try zig_args.append("--dynamic-linker");
try zig_args.append(dynamic_linker); try zig_args.append(dynamic_linker_path);
} else {
try zig_args.append("--no-dynamic-linker");
}
} }
} }
} }

View File

@ -434,8 +434,7 @@ pub const Reader = struct {
return err; return err;
}; };
} }
r.interface.seek = 0; r.interface.tossBuffered();
r.interface.end = 0;
}, },
.failure => return r.seek_err.?, .failure => return r.seek_err.?,
} }
@ -467,15 +466,11 @@ pub const Reader = struct {
} }
fn setLogicalPos(r: *Reader, offset: u64) void { fn setLogicalPos(r: *Reader, offset: u64) void {
const logical_pos = logicalPos(r); const logical_pos = r.logicalPos();
if (offset < logical_pos or offset >= r.pos) { if (offset < logical_pos or offset >= r.pos) {
r.interface.seek = 0; r.interface.tossBuffered();
r.interface.end = 0;
r.pos = offset; r.pos = offset;
} else { } else r.interface.toss(@intCast(offset - logical_pos));
const logical_delta: usize = @intCast(offset - logical_pos);
r.interface.seek += logical_delta;
}
} }
/// Number of slices to store on the stack, when trying to send as many byte /// Number of slices to store on the stack, when trying to send as many byte

View File

@ -46,8 +46,9 @@ android_api_level: ?u32 = null,
abi: ?Target.Abi = null, abi: ?Target.Abi = null,
/// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path
/// based on the `os_tag`. /// based on the `os_tag`. When `dynamic_linker` is a non-`null` empty string, no dynamic
dynamic_linker: Target.DynamicLinker = .none, /// linker is used regardless of `os_tag`.
dynamic_linker: ?Target.DynamicLinker = null,
/// `null` means default for the cpu/arch/os combo. /// `null` means default for the cpu/arch/os combo.
ofmt: ?Target.ObjectFormat = null, ofmt: ?Target.ObjectFormat = null,
@ -213,7 +214,7 @@ pub fn parse(args: ParseOptions) !Query {
const diags = args.diagnostics orelse &dummy_diags; const diags = args.diagnostics orelse &dummy_diags;
var result: Query = .{ 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, '-'); 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 { pub fn isNativeOs(self: Query) bool {
return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and 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 { 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 (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false;
if (a.android_api_level != b.android_api_level) return false; if (a.android_api_level != b.android_api_level) return false;
if (a.abi != b.abi) 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; if (a.ofmt != b.ofmt) return false;
return true; return true;
@ -611,6 +612,12 @@ fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool {
return SemanticVersion.order(a.?, b.?) == .eq; 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 { test parse {
const io = std.testing.io; const io = std.testing.io;

View File

@ -7013,7 +7013,8 @@ pub const RTLD = switch (native_os) {
LAZY: bool = false, LAZY: bool = false,
NOW: bool = false, NOW: bool = false,
NOLOAD: bool = false, NOLOAD: bool = false,
_3: u5 = 0, DEEPBIND: bool = false,
_4: u4 = 0,
GLOBAL: bool = false, GLOBAL: bool = false,
_9: u3 = 0, _9: u3 = 0,
NODELETE: bool = false, NODELETE: bool = false,

View File

@ -943,11 +943,30 @@ pub const Elf32 = struct {
unused: u5 = 0, unused: u5 = 0,
}; };
}; };
pub const Rel = extern struct {
offset: Elf32.Addr,
info: Info,
addend: u0 = 0,
pub const Info = packed struct(u32) {
type: u8,
sym: u24,
};
};
pub const Rela = extern struct {
offset: Elf32.Addr,
info: Info,
addend: i32,
pub const Info = Elf32.Rel.Info;
};
comptime { comptime {
assert(@sizeOf(Elf32.Ehdr) == 52); assert(@sizeOf(Elf32.Ehdr) == 52);
assert(@sizeOf(Elf32.Phdr) == 32); assert(@sizeOf(Elf32.Phdr) == 32);
assert(@sizeOf(Elf32.Shdr) == 40); assert(@sizeOf(Elf32.Shdr) == 40);
assert(@sizeOf(Elf32.Sym) == 16); assert(@sizeOf(Elf32.Sym) == 16);
assert(@sizeOf(Elf32.Rel) == 8);
assert(@sizeOf(Elf32.Rela) == 12);
} }
}; };
pub const Elf64 = struct { pub const Elf64 = struct {
@ -1008,11 +1027,30 @@ pub const Elf64 = struct {
pub const Info = Elf32.Sym.Info; pub const Info = Elf32.Sym.Info;
pub const Other = Elf32.Sym.Other; pub const Other = Elf32.Sym.Other;
}; };
pub const Rel = extern struct {
offset: Elf64.Addr,
info: Info,
addend: u0 = 0,
pub const Info = packed struct(u64) {
type: u32,
sym: u32,
};
};
pub const Rela = extern struct {
offset: Elf64.Addr,
info: Info,
addend: i64,
pub const Info = Elf64.Rel.Info;
};
comptime { comptime {
assert(@sizeOf(Elf64.Ehdr) == 64); assert(@sizeOf(Elf64.Ehdr) == 64);
assert(@sizeOf(Elf64.Phdr) == 56); assert(@sizeOf(Elf64.Phdr) == 56);
assert(@sizeOf(Elf64.Shdr) == 64); assert(@sizeOf(Elf64.Shdr) == 64);
assert(@sizeOf(Elf64.Sym) == 24); assert(@sizeOf(Elf64.Sym) == 24);
assert(@sizeOf(Elf64.Rel) == 16);
assert(@sizeOf(Elf64.Rela) == 24);
} }
}; };
pub const ElfN = switch (@sizeOf(usize)) { pub const ElfN = switch (@sizeOf(usize)) {
@ -1428,6 +1466,14 @@ pub const CLASS = enum(u8) {
_, _,
pub const NUM = @typeInfo(CLASS).@"enum".fields.len; pub const NUM = @typeInfo(CLASS).@"enum".fields.len;
pub fn ElfN(comptime class: CLASS) type {
return switch (class) {
.NONE, _ => comptime unreachable,
.@"32" => Elf32,
.@"64" => Elf64,
};
}
}; };
/// Deprecated, use `@intFromEnum(std.elf.DATA.NONE)` /// Deprecated, use `@intFromEnum(std.elf.DATA.NONE)`

View File

@ -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 // 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, // make calls yet on some architectures (e.g. MIPS) *because* they haven't been applied yet,
// so this must be fully inlined. // 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}); @call(.always_inline, std.pie.relocate, .{phdrs});
} }

View File

@ -585,10 +585,10 @@ fn abiAndDynamicLinkerFromFile(
.os = os, .os = os,
.abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag), .abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag),
.ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), .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 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; 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_linux = builtin.target.os.tag == .linux;
const is_illumos = builtin.target.os.tag == .illumos; const is_illumos = builtin.target.os.tag == .illumos;
const is_darwin = builtin.target.os.tag.isDarwin(); 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()); query.abi != null and (!is_linux or query.abi.?.isGnu());
const os_is_non_native = query.os_tag != null; const os_is_non_native = query.os_tag != null;
// The illumos environment is always the same. // The illumos environment is always the same.
@ -1126,10 +1126,7 @@ fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Quer
.os = os, .os = os,
.abi = abi, .abi = abi,
.ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
.dynamic_linker = if (query.dynamic_linker.get() == null) .dynamic_linker = query.dynamic_linker orelse .standard(cpu, os, abi),
Target.DynamicLinker.standard(cpu, os, abi)
else
query.dynamic_linker,
}; };
} }

View File

@ -258,8 +258,6 @@ test_filters: []const []const u8,
link_task_wait_group: WaitGroup = .{}, link_task_wait_group: WaitGroup = .{},
link_prog_node: std.Progress.Node = .none, link_prog_node: std.Progress.Node = .none,
link_const_prog_node: std.Progress.Node = .none,
link_synth_prog_node: std.Progress.Node = .none,
llvm_opt_bisect_limit: c_int, llvm_opt_bisect_limit: c_int,
@ -1991,7 +1989,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu; break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu;
}, },
} }
if (options.config.use_new_linker) break :s .zcu;
} }
if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm
if (is_exe_or_dyn_lib) break :s .lib; if (is_exe_or_dyn_lib) break :s .lib;
@ -3066,35 +3063,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
// we also want it around during `flush`. // we also want it around during `flush`.
if (comp.bin_file) |lf| { if (comp.bin_file) |lf| {
comp.link_prog_node = main_progress_node.start("Linking", 0); comp.link_prog_node = main_progress_node.start("Linking", 0);
if (lf.cast(.elf2)) |elf| { lf.startProgress(comp.link_prog_node);
comp.link_prog_node.increaseEstimatedTotalItems(3);
comp.link_const_prog_node = comp.link_prog_node.start("Constants", 0);
comp.link_synth_prog_node = comp.link_prog_node.start("Synthetics", 0);
elf.mf.update_prog_node = comp.link_prog_node.start("Relocations", elf.mf.updates.items.len);
} else if (lf.cast(.coff2)) |coff| {
comp.link_prog_node.increaseEstimatedTotalItems(3);
comp.link_const_prog_node = comp.link_prog_node.start("Constants", 0);
comp.link_synth_prog_node = comp.link_prog_node.start("Synthetics", 0);
coff.mf.update_prog_node = comp.link_prog_node.start("Relocations", coff.mf.updates.items.len);
}
} }
defer { defer if (comp.bin_file) |lf| {
lf.endProgress();
comp.link_prog_node.end(); comp.link_prog_node.end();
comp.link_prog_node = .none; comp.link_prog_node = .none;
comp.link_const_prog_node.end(); };
comp.link_const_prog_node = .none;
comp.link_synth_prog_node.end();
comp.link_synth_prog_node = .none;
if (comp.bin_file) |lf| {
if (lf.cast(.elf2)) |elf| {
elf.mf.update_prog_node.end();
elf.mf.update_prog_node = .none;
} else if (lf.cast(.coff2)) |coff| {
coff.mf.update_prog_node.end();
coff.mf.update_prog_node = .none;
}
}
}
try comp.performAllTheWork(main_progress_node); try comp.performAllTheWork(main_progress_node);

View File

@ -123,6 +123,7 @@ pub const ResolveError = error{
WasiExecModelRequiresWasi, WasiExecModelRequiresWasi,
SharedMemoryIsWasmOnly, SharedMemoryIsWasmOnly,
ObjectFilesCannotShareMemory, ObjectFilesCannotShareMemory,
ObjectFilesCannotSpecifyDynamicLinker,
SharedMemoryRequiresAtomicsAndBulkMemory, SharedMemoryRequiresAtomicsAndBulkMemory,
ThreadsRequireSharedMemory, ThreadsRequireSharedMemory,
EmittingLlvmModuleRequiresLlvmBackend, EmittingLlvmModuleRequiresLlvmBackend,
@ -131,6 +132,7 @@ pub const ResolveError = error{
EmittingBinaryRequiresLlvmLibrary, EmittingBinaryRequiresLlvmLibrary,
LldIncompatibleObjectFormat, LldIncompatibleObjectFormat,
LldCannotIncrementallyLink, LldCannotIncrementallyLink,
LldCannotSpecifyDynamicLinkerForSharedLibraries,
LtoRequiresLld, LtoRequiresLld,
SanitizeThreadRequiresLibCpp, SanitizeThreadRequiresLibCpp,
LibCRequiresLibUnwind, LibCRequiresLibUnwind,
@ -142,6 +144,7 @@ pub const ResolveError = error{
TargetCannotStaticLinkExecutables, TargetCannotStaticLinkExecutables,
LibCRequiresDynamicLinking, LibCRequiresDynamicLinking,
SharedLibrariesRequireDynamicLinking, SharedLibrariesRequireDynamicLinking,
DynamicLinkingWithLldRequiresSharedLibraries,
ExportMemoryAndDynamicIncompatible, ExportMemoryAndDynamicIncompatible,
DynamicLibraryPrecludesPie, DynamicLibraryPrecludesPie,
TargetRequiresPie, TargetRequiresPie,
@ -274,16 +277,11 @@ pub fn resolve(options: Options) ResolveError!Config {
if (options.link_mode == .static) return error.LibCRequiresDynamicLinking; if (options.link_mode == .static) return error.LibCRequiresDynamicLinking;
break :b .dynamic; 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.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) { if (explicitly_exe_or_dyn_lib and link_libc) {
// When using the native glibc/musl ABI, dynamic linking is usually what people want. // 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())) { 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; 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: { const use_new_linker = b: {
if (use_lld) { if (use_lld) {
if (options.use_new_linker == true) return error.NewLinkerIncompatibleWithLld; if (options.use_new_linker == true) return error.NewLinkerIncompatibleWithLld;

View File

@ -182,6 +182,10 @@ pub fn emitMir(emit: *Emit) Error!void {
try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null) try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{ else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
.name = extern_func.toSlice(&emit.lower.mir).?, .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, .type = .FUNC,
})) else if (emit.bin_file.cast(.macho)) |macho_file| })) else if (emit.bin_file.cast(.macho)) |macho_file|
try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null) try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
@ -217,9 +221,7 @@ pub fn emitMir(emit: *Emit) Error!void {
}, emit.lower.target), reloc_info), }, emit.lower.target), reloc_info),
.mov => try emit.encodeInst(try .new(.none, .mov, &.{ .mov => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0], lowered_inst.ops[0],
.{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{}) },
.base = .{ .reg = .ds },
}) },
}, emit.lower.target), reloc_info), }, emit.lower.target), reloc_info),
else => unreachable, else => unreachable,
} else if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) { } else if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) {
@ -322,10 +324,12 @@ pub fn emitMir(emit: *Emit) Error!void {
}, emit.lower.target), &.{.{ }, emit.lower.target), &.{.{
.op_index = 0, .op_index = 0,
.target = .{ .target = .{
.index = if (emit.bin_file.cast(.elf)) |elf_file| .index = if (emit.bin_file.cast(.elf)) |elf_file| try elf_file.getGlobalSymbol(
try elf_file.getGlobalSymbol("__tls_get_addr", null) "__tls_get_addr",
else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{ if (comp.config.link_libc) "c" else null,
) else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
.name = "__tls_get_addr", .name = "__tls_get_addr",
.lib_name = if (comp.config.link_libc) "c" else null,
.type = .FUNC, .type = .FUNC,
})) else unreachable, })) else unreachable,
.is_extern = true, .is_extern = true,
@ -720,7 +724,7 @@ pub fn emitMir(emit: *Emit) Error!void {
for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{ for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
.r_offset = table_reloc.source_offset, .r_offset = table_reloc.source_offset,
.r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"), .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32S"),
.r_addend = @as(i64, table_offset) + table_reloc.target_offset, .r_addend = @as(i64, table_offset) + table_reloc.target_offset,
}, zo); }, zo);
for (emit.lower.mir.table) |entry| { for (emit.lower.mir.table) |entry| {
@ -738,7 +742,7 @@ pub fn emitMir(emit: *Emit) Error!void {
table_reloc.source_offset, table_reloc.source_offset,
@enumFromInt(emit.atom_index), @enumFromInt(emit.atom_index),
@as(i64, table_offset) + table_reloc.target_offset, @as(i64, table_offset) + table_reloc.target_offset,
.{ .X86_64 = .@"32" }, .{ .X86_64 = .@"32S" },
); );
for (emit.lower.mir.table) |entry| { for (emit.lower.mir.table) |entry| {
try elf.addReloc( try elf.addReloc(
@ -824,7 +828,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (!emit.pic) const r_type: std.elf.R_X86_64 = if (!emit.pic)
.@"32" .@"32S"
else if (reloc.target.is_extern and !reloc.target.force_pcrel_direct) else if (reloc.target.is_extern and !reloc.target.force_pcrel_direct)
.GOTPCREL .GOTPCREL
else else
@ -855,7 +859,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
end_offset - 4, end_offset - 4,
@enumFromInt(reloc.target.index), @enumFromInt(reloc.target.index),
reloc.off, reloc.off,
.{ .X86_64 = .@"32" }, .{ .X86_64 = .@"32S" },
) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc( ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
@enumFromInt(emit.atom_index), @enumFromInt(emit.atom_index),
end_offset - 4, end_offset - 4,

View File

@ -571,6 +571,26 @@ pub const File = struct {
return if (dev.env.supports(tag.devFeature()) and base.tag == tag) @fieldParentPtr("base", base) else null; return if (dev.env.supports(tag.devFeature()) and base.tag == tag) @fieldParentPtr("base", base) else null;
} }
pub fn startProgress(base: *File, prog_node: std.Progress.Node) void {
switch (base.tag) {
else => {},
inline .elf2, .coff2 => |tag| {
dev.check(tag.devFeature());
return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node);
},
}
}
pub fn endProgress(base: *File) void {
switch (base.tag) {
else => {},
inline .elf2, .coff2 => |tag| {
dev.check(tag.devFeature());
return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress();
},
}
}
pub fn makeWritable(base: *File) !void { pub fn makeWritable(base: *File) !void {
dev.check(.make_writable); dev.check(.make_writable);
const comp = base.comp; const comp = base.comp;
@ -620,10 +640,10 @@ pub const File = struct {
&coff.mf &coff.mf
else else
unreachable; unreachable;
mf.file = .adaptFromNewApi(try Io.Dir.openFile(base.emit.root_dir.handle.adaptToNewApi(), io, base.emit.sub_path, .{ mf.file = try base.emit.root_dir.handle.adaptToNewApi().openFile(io, base.emit.sub_path, .{
.mode = .read_write, .mode = .read_write,
})); });
base.file = mf.file; base.file = .adaptFromNewApi(mf.file);
try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1])); try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
}, },
.c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }), .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
@ -648,6 +668,7 @@ pub const File = struct {
pub fn makeExecutable(base: *File) !void { pub fn makeExecutable(base: *File) !void {
dev.check(.make_executable); dev.check(.make_executable);
const comp = base.comp; const comp = base.comp;
const io = comp.io;
switch (comp.config.output_mode) { switch (comp.config.output_mode) {
.Obj => return, .Obj => return,
.Lib => switch (comp.config.link_mode) { .Lib => switch (comp.config.link_mode) {
@ -698,8 +719,8 @@ pub const File = struct {
unreachable; unreachable;
mf.unmap(); mf.unmap();
assert(mf.file.handle == f.handle); assert(mf.file.handle == f.handle);
mf.file.close(io);
mf.file = undefined; mf.file = undefined;
f.close();
base.file = null; base.file = null;
}, },
.c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }), .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
@ -1120,7 +1141,7 @@ pub const File = struct {
pub fn loadInput(base: *File, input: Input) anyerror!void { pub fn loadInput(base: *File, input: Input) anyerror!void {
if (base.tag == .lld) return; if (base.tag == .lld) return;
switch (base.tag) { switch (base.tag) {
inline .elf, .wasm => |tag| { inline .elf, .elf2, .wasm => |tag| {
dev.check(tag.devFeature()); dev.check(tag.devFeature());
return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input); return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
}, },
@ -1281,9 +1302,6 @@ pub const PrelinkTask = union(enum) {
/// Tells the linker to load a shared library, possibly one that is a /// Tells the linker to load a shared library, possibly one that is a
/// GNU ld script. /// GNU ld script.
load_dso: Path, load_dso: Path,
/// Tells the linker to load an input which could be an object file,
/// archive, or shared library.
load_input: Input,
}; };
pub const ZcuTask = union(enum) { pub const ZcuTask = union(enum) {
/// Write the constant value for a Decl to the output file. /// Write the constant value for a Decl to the output file.
@ -1461,20 +1479,6 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}), else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
}; };
}, },
.load_input => |input| {
const prog_node = comp.link_prog_node.start("Parse Input", 0);
defer prog_node.end();
base.loadInput(input) catch |err| switch (err) {
error.LinkFailure => return, // error reported via link_diags
else => |e| {
if (input.path()) |path| {
diags.addParseError(path, "failed to parse linker input: {s}", .{@errorName(e)});
} else {
diags.addError("failed to {s}: {s}", .{ input.taskName(), @errorName(e) });
}
},
};
},
} }
} }
pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {

View File

@ -26,6 +26,8 @@ pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
src_loc: Zcu.LazySrcLoc, src_loc: Zcu.LazySrcLoc,
}), }),
relocs: std.ArrayList(Reloc), relocs: std.ArrayList(Reloc),
const_prog_node: std.Progress.Node,
synth_prog_node: std.Progress.Node,
pub const default_file_alignment: u16 = 0x200; pub const default_file_alignment: u16 = 0x200;
pub const default_size_of_stack_reserve: u32 = 0x1000000; pub const default_size_of_stack_reserve: u32 = 0x1000000;
@ -630,11 +632,11 @@ fn create(
}; };
const coff = try arena.create(Coff); const coff = try arena.create(Coff);
const file = try path.root_dir.handle.createFile(path.sub_path, .{ const file = try path.root_dir.handle.adaptToNewApi().createFile(comp.io, path.sub_path, .{
.read = true, .read = true,
.mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode), .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
}); });
errdefer file.close(); errdefer file.close(comp.io);
coff.* = .{ coff.* = .{
.base = .{ .base = .{
.tag = .coff2, .tag = .coff2,
@ -642,7 +644,7 @@ fn create(
.comp = comp, .comp = comp,
.emit = path, .emit = path,
.file = file, .file = .adaptFromNewApi(file),
.gc_sections = false, .gc_sections = false,
.print_gc_sections = false, .print_gc_sections = false,
.build_id = .none, .build_id = .none,
@ -671,6 +673,8 @@ fn create(
}), }),
.pending_uavs = .empty, .pending_uavs = .empty,
.relocs = .empty, .relocs = .empty,
.const_prog_node = .none,
.synth_prog_node = .none,
}; };
errdefer coff.deinit(); errdefer coff.deinit();
@ -973,6 +977,26 @@ fn initHeaders(
assert(coff.nodes.len == expected_nodes_len); assert(coff.nodes.len == expected_nodes_len);
} }
pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
prog_node.increaseEstimatedTotalItems(3);
coff.const_prog_node = prog_node.start("Constants", coff.pending_uavs.count());
coff.synth_prog_node = prog_node.start("Synthetics", count: {
var count = coff.globals.count() - coff.global_pending_index;
for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
break :count count;
});
coff.mf.update_prog_node = prog_node.start("Relocations", coff.mf.updates.items.len);
}
pub fn endProgress(coff: *Coff) void {
coff.mf.update_prog_node.end();
coff.mf.update_prog_node = .none;
coff.synth_prog_node.end();
coff.synth_prog_node = .none;
coff.const_prog_node.end();
coff.const_prog_node = .none;
}
fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node { fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
return coff.nodes.get(@intFromEnum(ni)); return coff.nodes.get(@intFromEnum(ni));
} }
@ -1172,7 +1196,7 @@ pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbo
}); });
if (!sym_gop.found_existing) { if (!sym_gop.found_existing) {
sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity(); sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1); coff.synth_prog_node.increaseEstimatedTotalItems(1);
} }
return sym_gop.value_ptr.*; return sym_gop.value_ptr.*;
} }
@ -1250,7 +1274,7 @@ pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty); const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
if (!sym_gop.found_existing) { if (!sym_gop.found_existing) {
sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity(); sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity();
coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1); coff.synth_prog_node.increaseEstimatedTotalItems(1);
} }
return sym_gop.value_ptr.*; return sym_gop.value_ptr.*;
} }
@ -1585,7 +1609,7 @@ pub fn lowerUav(
.alignment = uav_align, .alignment = uav_align,
.src_loc = src_loc, .src_loc = src_loc,
}; };
coff.base.comp.link_const_prog_node.increaseEstimatedTotalItems(1); coff.const_prog_node.increaseEstimatedTotalItems(1);
} }
} }
return .{ .sym_index = @intFromEnum(si) }; return .{ .sym_index = @intFromEnum(si) };
@ -1726,17 +1750,16 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
const comp = coff.base.comp; const comp = coff.base.comp;
task: { task: {
while (coff.pending_uavs.pop()) |pending_uav| { while (coff.pending_uavs.pop()) |pending_uav| {
const sub_prog_node = const sub_prog_node = coff.idleProgNode(tid, coff.const_prog_node, .{ .uav = pending_uav.key });
coff.idleProgNode(tid, comp.link_const_prog_node, .{ .uav = pending_uav.key });
defer sub_prog_node.end(); defer sub_prog_node.end();
coff.flushUav( coff.flushUav(
.{ .zcu = coff.base.comp.zcu.?, .tid = tid }, .{ .zcu = comp.zcu.?, .tid = tid },
pending_uav.key, pending_uav.key,
pending_uav.value.alignment, pending_uav.value.alignment,
pending_uav.value.src_loc, pending_uav.value.src_loc,
) catch |err| switch (err) { ) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
else => |e| return coff.base.comp.link_diags.fail( else => |e| return comp.link_diags.fail(
"linker failed to lower constant: {t}", "linker failed to lower constant: {t}",
.{e}, .{e},
), ),
@ -1744,17 +1767,17 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
break :task; break :task;
} }
if (coff.global_pending_index < coff.globals.count()) { if (coff.global_pending_index < coff.globals.count()) {
const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid }; const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };
const gmi: Node.GlobalMapIndex = @enumFromInt(coff.global_pending_index); const gmi: Node.GlobalMapIndex = @enumFromInt(coff.global_pending_index);
coff.global_pending_index += 1; coff.global_pending_index += 1;
const sub_prog_node = comp.link_synth_prog_node.start( const sub_prog_node = coff.synth_prog_node.start(
gmi.globalName(coff).name.toSlice(coff), gmi.globalName(coff).name.toSlice(coff),
0, 0,
); );
defer sub_prog_node.end(); defer sub_prog_node.end();
coff.flushGlobal(pt, gmi) catch |err| switch (err) { coff.flushGlobal(pt, gmi) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
else => |e| return coff.base.comp.link_diags.fail( else => |e| return comp.link_diags.fail(
"linker failed to lower constant: {t}", "linker failed to lower constant: {t}",
.{e}, .{e},
), ),
@ -1763,7 +1786,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
} }
var lazy_it = coff.lazy.iterator(); var lazy_it = coff.lazy.iterator();
while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) { while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid }; const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = tid };
const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index }; const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
lazy.value.pending_index += 1; lazy.value.pending_index += 1;
const kind = switch (lmr.kind) { const kind = switch (lmr.kind) {
@ -1771,7 +1794,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
.const_data => "data", .const_data => "data",
}; };
var name: [std.Progress.Node.max_name_len]u8 = undefined; var name: [std.Progress.Node.max_name_len]u8 = undefined;
const sub_prog_node = comp.link_synth_prog_node.start( const sub_prog_node = coff.synth_prog_node.start(
std.fmt.bufPrint(&name, "lazy {s} for {f}", .{ std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
kind, kind,
Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt), Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt),
@ -1781,7 +1804,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
defer sub_prog_node.end(); defer sub_prog_node.end();
coff.flushLazy(pt, lmr) catch |err| switch (err) { coff.flushLazy(pt, lmr) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory,
else => |e| return coff.base.comp.link_diags.fail( else => |e| return comp.link_diags.fail(
"linker failed to lower lazy {s}: {t}", "linker failed to lower lazy {s}: {t}",
.{ kind, e }, .{ kind, e },
), ),
@ -1802,6 +1825,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
} }
} }
if (coff.pending_uavs.count() > 0) return true; if (coff.pending_uavs.count() > 0) return true;
if (coff.globals.count() > coff.global_pending_index) return true;
for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true; for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
if (coff.mf.updates.items.len > 0) return true; if (coff.mf.updates.items.len > 0) return true;
return false; return false;

View File

@ -1882,17 +1882,13 @@ fn initSyntheticSections(self: *Elf) !void {
const comp = self.base.comp; const comp = self.base.comp;
const target = self.getTarget(); const target = self.getTarget();
const ptr_size = self.ptrWidthBytes(); const ptr_size = self.ptrWidthBytes();
const shared_objects = self.shared_objects.values();
const is_exe_or_dyn_lib = switch (comp.config.output_mode) { const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
.Exe => true, .Exe => true,
.Lib => comp.config.link_mode == .dynamic, .Lib => comp.config.link_mode == .dynamic,
.Obj => false, .Obj => false,
}; };
const have_dynamic_linker = comp.config.link_mode == .dynamic and is_exe_or_dyn_lib and !target.dynamic_linker.eql(.none); const have_dynamic_linker = comp.config.link_mode == .dynamic and is_exe_or_dyn_lib;
const needs_interp = have_dynamic_linker and
(comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker);
const needs_eh_frame = blk: { const needs_eh_frame = blk: {
if (self.zigObjectPtr()) |zo| 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(.{ self.section_indexes.interp = try self.addSection(.{
.name = try self.insertShString(".interp"), .name = try self.insertShString(".interp"),
.type = elf.SHT_PROGBITS, .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) { if (self.section_indexes.dynstrtab == null) {
self.section_indexes.dynstrtab = try self.addSection(.{ self.section_indexes.dynstrtab = try self.addSection(.{
.name = try self.insertShString(".dynstr"), .name = try self.insertShString(".dynstr"),

View File

@ -34,8 +34,6 @@ pub fn parse(
defer strtab.deinit(gpa); defer strtab.deinit(gpa);
while (pos < size) { while (pos < size) {
pos = mem.alignForward(usize, pos, 2);
var hdr: elf.ar_hdr = undefined; var hdr: elf.ar_hdr = undefined;
{ {
const n = try handle.preadAll(mem.asBytes(&hdr), pos); const n = try handle.preadAll(mem.asBytes(&hdr), pos);
@ -50,7 +48,7 @@ pub fn parse(
} }
const obj_size = try hdr.size(); const obj_size = try hdr.size();
defer pos += obj_size; defer pos = std.mem.alignForward(usize, pos + obj_size, 2);
if (hdr.isSymtab() or hdr.isSymtab64()) continue; if (hdr.isSymtab() or hdr.isSymtab64()) continue;
if (hdr.isStrtab()) { if (hdr.isStrtab()) {

File diff suppressed because it is too large Load Diff

View File

@ -808,7 +808,6 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
const link_mode = comp.config.link_mode; const link_mode = comp.config.link_mode;
const is_dyn_lib = link_mode == .dynamic and is_lib; const is_dyn_lib = link_mode == .dynamic and is_lib;
const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; 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 target = &comp.root_mod.resolved_target.result;
const compiler_rt_path: ?Cache.Path = blk: { const compiler_rt_path: ?Cache.Path = blk: {
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; 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 if (output_mode == .Exe and link_mode == .dynamic) {
(comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker))
{
if (target.dynamic_linker.get()) |dynamic_linker| { if (target.dynamic_linker.get()) |dynamic_linker| {
try argv.append("-dynamic-linker"); try argv.append("--dynamic-linker");
try argv.append(dynamic_linker); try argv.append(dynamic_linker);
} else {
try argv.append("--no-dynamic-linker");
} }
} }

View File

@ -1,4 +1,4 @@
file: std.fs.File, file: std.Io.File,
flags: packed struct { flags: packed struct {
block_size: std.mem.Alignment, block_size: std.mem.Alignment,
copy_file_range_unsupported: bool, copy_file_range_unsupported: bool,
@ -24,7 +24,7 @@ pub const Error = std.posix.MMapError || std.posix.MRemapError || std.fs.File.Se
NoSpaceLeft, NoSpaceLeft,
}; };
pub fn init(file: std.fs.File, gpa: std.mem.Allocator) !MappedFile { pub fn init(file: std.Io.File, gpa: std.mem.Allocator) !MappedFile {
var mf: MappedFile = .{ var mf: MappedFile = .{
.file = file, .file = file,
.flags = undefined, .flags = undefined,
@ -144,6 +144,15 @@ pub const Node = extern struct {
} }
}; };
pub const FileLocation = struct {
offset: u64,
size: u64,
pub fn end(fl: FileLocation) u64 {
return fl.offset + fl.size;
}
};
pub const Index = enum(u32) { pub const Index = enum(u32) {
none, none,
_, _,
@ -275,7 +284,7 @@ pub const Node = extern struct {
ni: Node.Index, ni: Node.Index,
mf: *const MappedFile, mf: *const MappedFile,
set_has_content: bool, set_has_content: bool,
) struct { offset: u64, size: u64 } { ) FileLocation {
var offset, const size = ni.location(mf).resolve(mf); var offset, const size = ni.location(mf).resolve(mf);
var parent_ni = ni; var parent_ni = ni;
while (true) { while (true) {
@ -386,7 +395,7 @@ pub const Node = extern struct {
fn sendFile( fn sendFile(
interface: *std.Io.Writer, interface: *std.Io.Writer,
file_reader: *std.fs.File.Reader, file_reader: *std.Io.File.Reader,
limit: std.Io.Limit, limit: std.Io.Limit,
) std.Io.Writer.FileError!usize { ) std.Io.Writer.FileError!usize {
if (limit == .nothing) return 0; if (limit == .nothing) return 0;
@ -397,14 +406,14 @@ pub const Node = extern struct {
switch (file_reader.mode) { switch (file_reader.mode) {
.positional => { .positional => {
const fr_buf = file_reader.interface.buffered(); const fr_buf = file_reader.interface.buffered();
const buf_copy_size = interface.write(fr_buf) catch unreachable; if (fr_buf.len > 0) {
file_reader.interface.toss(buf_copy_size); const n = interface.write(fr_buf) catch unreachable;
if (buf_copy_size < fr_buf.len) return buf_copy_size; file_reader.interface.toss(n);
assert(file_reader.logicalPos() == file_reader.pos); return n;
}
const w: *Writer = @fieldParentPtr("interface", interface); const w: *Writer = @fieldParentPtr("interface", interface);
const copy_size: usize = @intCast(w.mf.copyFileRange( const n: usize = @intCast(w.mf.copyFileRange(
.adaptFromNewApi(file_reader.file), file_reader.file,
file_reader.pos, file_reader.pos,
w.ni.fileLocation(w.mf, true).offset + interface.end, w.ni.fileLocation(w.mf, true).offset + interface.end,
limit.minInt(interface.unusedCapacityLen()), limit.minInt(interface.unusedCapacityLen()),
@ -412,8 +421,10 @@ pub const Node = extern struct {
w.err = err; w.err = err;
return error.WriteFailed; return error.WriteFailed;
}); });
interface.end += copy_size; if (n == 0) return error.Unimplemented;
return copy_size; file_reader.pos += n;
interface.end += n;
return n;
}, },
.streaming, .streaming,
.streaming_reading, .streaming_reading,
@ -614,7 +625,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
// Resize the entire file // Resize the entire file
if (ni == Node.Index.root) { if (ni == Node.Index.root) {
try mf.ensureCapacityForSetLocation(gpa); try mf.ensureCapacityForSetLocation(gpa);
try mf.file.setEndPos(new_size); try std.fs.File.adaptFromNewApi(mf.file).setEndPos(new_size);
try mf.ensureTotalCapacity(@intCast(new_size)); try mf.ensureTotalCapacity(@intCast(new_size));
ni.setLocationAssumeCapacity(mf, old_offset, new_size); ni.setLocationAssumeCapacity(mf, old_offset, new_size);
return; return;
@ -894,7 +905,7 @@ fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
fn copyFileRange( fn copyFileRange(
mf: *MappedFile, mf: *MappedFile,
old_file: std.fs.File, old_file: std.Io.File,
old_file_offset: u64, old_file_offset: u64,
new_file_offset: u64, new_file_offset: u64,
size: u64, size: u64,

View File

@ -558,6 +558,7 @@ const usage_build_generic =
\\ --enable-new-dtags Use the new behavior for dynamic tags (RUNPATH) \\ --enable-new-dtags Use the new behavior for dynamic tags (RUNPATH)
\\ --disable-new-dtags Use the old behavior for dynamic tags (RPATH) \\ --disable-new-dtags Use the old behavior for dynamic tags (RPATH)
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) \\ --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 /) \\ --sysroot [path] Set the system root directory (usually /)
\\ --version [ver] Dynamic library semver \\ --version [ver] Dynamic library semver
\\ -fentry Enable entry point with default symbol name \\ -fentry Enable entry point with default symbol name
@ -1301,6 +1302,8 @@ fn buildOutputType(
mod_opts.optimize_mode = parseOptimizeMode(rest); mod_opts.optimize_mode = parseOptimizeMode(rest);
} else if (mem.eql(u8, arg, "--dynamic-linker")) { } else if (mem.eql(u8, arg, "--dynamic-linker")) {
create_module.dynamic_linker = args_iter.nextOrFatal(); 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")) { } else if (mem.eql(u8, arg, "--sysroot")) {
const next_arg = args_iter.nextOrFatal(); const next_arg = args_iter.nextOrFatal();
create_module.sysroot = next_arg; create_module.sysroot = next_arg;
@ -2418,6 +2421,11 @@ fn buildOutputType(
mem.eql(u8, arg, "-dynamic-linker")) mem.eql(u8, arg, "-dynamic-linker"))
{ {
create_module.dynamic_linker = linker_args_it.nextOrFatal(); 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 } else if (mem.eql(u8, arg, "-E") or
mem.eql(u8, arg, "--export-dynamic") or mem.eql(u8, arg, "--export-dynamic") or
mem.eql(u8, arg, "-export-dynamic")) mem.eql(u8, arg, "-export-dynamic"))
@ -3191,13 +3199,14 @@ fn buildOutputType(
const resolved_soname: ?[]const u8 = switch (soname) { const resolved_soname: ?[]const u8 = switch (soname) {
.yes => |explicit| explicit, .yes => |explicit| explicit,
.no => null, .no => null,
.yes_default_value => switch (target.ofmt) { .yes_default_value => if (create_module.resolved_options.output_mode == .Lib and
.elf => if (have_version) 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 }) try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major })
else else
try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name}), try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name})
else => null, else
}, null,
}; };
const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) { 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})); try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu}));
} }
if (create_module.dynamic_linker) |dl| { 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 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, .result = target,
.is_native_os = target_query.isNativeOs(), .is_native_os = target_query.isNativeOs(),
.is_native_abi = target_query.isNativeAbi(), .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.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
error.SharedMemoryIsWasmOnly => fatal("only WebAssembly CPU targets support shared memory", .{}), error.SharedMemoryIsWasmOnly => fatal("only WebAssembly CPU targets support shared memory", .{}),
error.ObjectFilesCannotShareMemory => fatal("object files cannot share 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.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}),
error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}), error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}),
error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}), 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.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.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.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.LtoRequiresLld => fatal("LTO requires using LLD", .{}),
error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}), 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", .{}), 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.TargetCannotStaticLinkExecutables => fatal("static linking of executables unavailable on the specified target", .{}),
error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}), error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}),
error.SharedLibrariesRequireDynamicLinking => fatal("using shared libraries 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.ExportMemoryAndDynamicIncompatible => fatal("exporting memory is incompatible with dynamic linking", .{}),
error.DynamicLibraryPrecludesPie => fatal("dynamic libraries cannot be position independent executables", .{}), error.DynamicLibraryPrecludesPie => fatal("dynamic libraries cannot be position independent executables", .{}),
error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}), error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}),