aboutsummaryrefslogtreecommitdiff
path: root/src/link
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-12-26 21:39:39 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-01-01 17:51:21 -0700
commit57562c8d507667b6fefcb7fbc7a305fbd610b5dd (patch)
treef0e494bdace7302f7cce8ad8c9c7d3227bd81d64 /src/link
parentc9fe43679f8d8f0db6250cc881b59cc68daaf128 (diff)
downloadzig-57562c8d507667b6fefcb7fbc7a305fbd610b5dd.tar.gz
zig-57562c8d507667b6fefcb7fbc7a305fbd610b5dd.zip
compiler: push entry symbol name resolution into the linker
This is necessary because on COFF, the entry symbol name is not known until the linker has looked at the set of global symbol names to determine which of the four possible main entry points is present.
Diffstat (limited to 'src/link')
-rw-r--r--src/link/Coff.zig21
-rw-r--r--src/link/Coff/lld.zig15
-rw-r--r--src/link/Elf.zig29
-rw-r--r--src/link/MachO.zig17
-rw-r--r--src/link/MachO/zld.zig5
-rw-r--r--src/link/Wasm.zig51
6 files changed, 100 insertions, 38 deletions
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
index 861ec030b3..53f999b965 100644
--- a/src/link/Coff.zig
+++ b/src/link/Coff.zig
@@ -17,6 +17,7 @@ dynamicbase: bool,
major_subsystem_version: u16,
minor_subsystem_version: u16,
lib_dirs: []const []const u8,
+entry: link.File.OpenOptions.Entry,
entry_addr: ?u32,
module_definition_file: ?[]const u8,
pdb_out_path: ?[]const u8,
@@ -303,7 +304,12 @@ pub fn createEmpty(
.Obj => 0,
},
+ // Subsystem depends on the set of public symbol names from linked objects.
+ // See LinkerDriver::inferSubsystem from the LLD project for the flow chart.
.subsystem = options.subsystem,
+
+ .entry = options.entry,
+
.tsaware = options.tsaware,
.nxcompat = options.nxcompat,
.dynamicbase = options.dynamicbase,
@@ -2498,7 +2504,20 @@ inline fn getSizeOfImage(self: Coff) u32 {
/// Returns symbol location corresponding to the set entrypoint (if any).
pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {
const comp = self.base.comp;
- const entry_name = comp.config.entry orelse return null;
+
+ // TODO This is incomplete.
+ // The entry symbol name depends on the subsystem as well as the set of
+ // public symbol names from linked objects.
+ // See LinkerDriver::findDefaultEntry from the LLD project for the flow chart.
+ const entry_name = switch (self.entry) {
+ .disabled => return null,
+ .default => switch (comp.config.output_mode) {
+ .Exe => "wWinMainCRTStartup",
+ .Obj, .Lib => return null,
+ },
+ .enabled => "wWinMainCRTStartup",
+ .named => |name| name,
+ };
const global_index = self.resolver.get(entry_name) orelse return null;
return self.globals.items[global_index];
}
diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig
index 5b01f63645..6afe741f66 100644
--- a/src/link/Coff/lld.zig
+++ b/src/link/Coff/lld.zig
@@ -52,6 +52,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
const target = comp.root_mod.resolved_target.result;
const optimize_mode = comp.root_mod.optimize_mode;
+ const entry_name: ?[]const u8 = switch (self.entry) {
+ // This logic isn't quite right for disabled or enabled. No point in fixing it
+ // when the goal is to eliminate dependency on LLD anyway.
+ // https://github.com/ziglang/zig/issues/17751
+ .disabled, .default, .enabled => null,
+ .named => |name| name,
+ };
// See link/Elf.zig for comments on how this mechanism works.
const id_symlink_basename = "lld.id";
@@ -80,7 +87,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
}
}
try man.addOptionalFile(module_obj_path);
- man.hash.addOptionalBytes(comp.config.entry);
+ man.hash.addOptionalBytes(entry_name);
man.hash.add(self.base.stack_size);
man.hash.add(self.image_base);
man.hash.addListOfBytes(self.lib_dirs);
@@ -218,8 +225,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
try argv.append("-DLL");
}
- if (comp.config.entry) |entry| {
- try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
+ if (entry_name) |name| {
+ try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name}));
}
if (self.tsaware) {
@@ -441,7 +448,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
}
} else {
try argv.append("-NODEFAULTLIB");
- if (!is_lib and comp.config.entry == null) {
+ if (!is_lib and entry_name == null) {
if (comp.module) |module| {
if (module.stage1_flags.have_winmain_crt_startup) {
try argv.append("-ENTRY:WinMainCRTStartup");
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index ea8454490c..65fc0ff5a3 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -25,6 +25,7 @@ linker_script: ?[]const u8,
version_script: ?[]const u8,
print_icf_sections: bool,
print_map: bool,
+entry_name: ?[]const u8,
ptr_width: PtrWidth,
@@ -290,6 +291,13 @@ pub fn createEmpty(
.page_size = page_size,
.default_sym_version = default_sym_version,
+ .entry_name = switch (options.entry) {
+ .disabled => null,
+ .default => if (output_mode != .Exe) null else defaultEntrySymbolName(target.cpu.arch),
+ .enabled => defaultEntrySymbolName(target.cpu.arch),
+ .named => |name| name,
+ },
+
.image_base = b: {
if (is_dyn_lib) break :b 0;
if (output_mode == .Exe and comp.config.pie) break :b 0;
@@ -1305,7 +1313,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
// Look for entry address in objects if not set by the incremental compiler.
if (self.entry_index == null) {
- if (comp.config.entry) |name| {
+ if (self.entry_name) |name| {
self.entry_index = self.globalByName(name);
}
}
@@ -1679,9 +1687,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
}
- if (comp.config.entry) |entry| {
- try argv.append("--entry");
- try argv.append(entry);
+ if (self.entry_name) |name| {
+ try argv.appendSlice(&.{ "--entry", name });
}
for (self.base.rpath_list) |rpath| {
@@ -2427,7 +2434,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
// We can skip hashing libc and libc++ components that we are in charge of building from Zig
// installation sources because they are always a product of the compiler version + target information.
- man.hash.addOptionalBytes(comp.config.entry);
+ man.hash.addOptionalBytes(self.entry_name);
man.hash.add(self.image_base);
man.hash.add(self.base.gc_sections);
man.hash.addOptional(self.sort_section);
@@ -2563,9 +2570,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
.ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
}
- if (comp.config.entry) |entry| {
- try argv.append("--entry");
- try argv.append(entry);
+ if (self.entry_name) |name| {
+ try argv.appendSlice(&.{ "--entry", name });
}
for (self.base.force_undefined_symbols.keys()) |sym| {
@@ -6512,6 +6518,13 @@ const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection);
pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;
pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;
+fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
+ return switch (cpu_arch) {
+ .mips, .mipsel, .mips64, .mips64el => "__start",
+ else => "_start",
+ };
+}
+
const std = @import("std");
const build_options = @import("build_options");
const builtin = @import("builtin");
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index d8fc71c0f0..8c0874e3cb 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -1,4 +1,5 @@
base: File,
+entry_name: ?[]const u8,
/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
llvm_object: ?*LlvmObject = null,
@@ -231,6 +232,12 @@ pub fn createEmpty(
.install_name = options.install_name,
.entitlements = options.entitlements,
.compatibility_version = options.compatibility_version,
+ .entry_name = switch (options.entry) {
+ .disabled => null,
+ .default => if (output_mode != .Exe) null else default_entry_symbol_name,
+ .enabled => default_entry_symbol_name,
+ .named => |name| name,
+ },
};
if (use_llvm and comp.config.have_zcu) {
self.llvm_object = try LlvmObject.create(arena, comp);
@@ -1629,8 +1636,9 @@ pub fn resolveSymbols(self: *MachO) !void {
// we search for it in libraries should there be no object files specified
// on the linker line.
if (output_mode == .Exe) {
- const entry_name = comp.config.entry.?;
- _ = try self.addUndefined(entry_name, .{});
+ if (self.entry_name) |entry_name| {
+ _ = try self.addUndefined(entry_name, .{});
+ }
}
// Force resolution of any symbols requested by the user.
@@ -5085,8 +5093,7 @@ pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {
/// Returns symbol location corresponding to the set entrypoint if any.
/// Asserts output mode is executable.
pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {
- const comp = self.base.comp;
- const entry_name = comp.config.entry orelse return null;
+ const entry_name = self.entry_name orelse return null;
const global = self.getGlobal(entry_name) orelse return null;
return global;
}
@@ -5645,6 +5652,8 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
}
}
+const default_entry_symbol_name = "_main";
+
pub const base_tag: File.Tag = File.Tag.macho;
pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index fa6aa152e4..05fa8e671a 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -276,9 +276,8 @@ pub fn linkWithZld(
try argv.append("-dead_strip_dylibs");
}
- if (comp.config.entry) |entry| {
- try argv.append("-e");
- try argv.append(entry);
+ if (macho_file.entry_name) |entry_name| {
+ try argv.appendSlice(&.{ "-e", entry_name });
}
for (objects) |obj| {
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index b4b0f16d4a..91653ce641 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -37,6 +37,7 @@ pub const Relocation = types.Relocation;
pub const base_tag: link.File.Tag = .wasm;
base: link.File,
+entry_name: ?[]const u8,
import_symbols: bool,
export_symbol_names: []const []const u8,
global_base: ?u64,
@@ -397,6 +398,7 @@ pub fn createEmpty(
const use_llvm = comp.config.use_llvm;
const output_mode = comp.config.output_mode;
const shared_memory = comp.config.shared_memory;
+ const wasi_exec_model = comp.config.wasi_exec_model;
// If using LLD to link, this code should produce an object file so that it
// can be passed to LLD.
@@ -434,6 +436,13 @@ pub fn createEmpty(
.initial_memory = options.initial_memory,
.max_memory = options.max_memory,
.wasi_emulated_libs = options.wasi_emulated_libs,
+
+ .entry_name = switch (options.entry) {
+ .disabled => null,
+ .default => if (output_mode != .Exe) null else defaultEntrySymbolName(wasi_exec_model),
+ .enabled => defaultEntrySymbolName(wasi_exec_model),
+ .named => |name| name,
+ },
};
if (use_llvm and comp.config.have_zcu) {
wasm.llvm_object = try LlvmObject.create(arena, comp);
@@ -3042,7 +3051,7 @@ fn setupExports(wasm: *Wasm) !void {
fn setupStart(wasm: *Wasm) !void {
const comp = wasm.base.comp;
// do not export entry point if user set none or no default was set.
- const entry_name = comp.config.entry orelse return;
+ const entry_name = wasm.entry_name orelse return;
const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
@@ -3475,8 +3484,8 @@ fn resetState(wasm: *Wasm) void {
}
pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
- const use_lld = build_options.have_llvm and wasm.base.comp.config.use_lld;
- const use_llvm = wasm.base.comp.config.use_llvm;
+ const use_lld = build_options.have_llvm and comp.config.use_lld;
+ const use_llvm = comp.config.use_llvm;
if (use_lld) {
return wasm.linkWithLLD(comp, prog_node);
@@ -3492,7 +3501,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
const tracy = trace(@src());
defer tracy.end();
- const gpa = wasm.base.comp.gpa;
+ const gpa = comp.gpa;
const shared_memory = comp.config.shared_memory;
const import_memory = comp.config.import_memory;
@@ -3503,8 +3512,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
- const opt_zcu = wasm.base.comp.module;
- const use_llvm = wasm.base.comp.config.use_llvm;
+ const opt_zcu = comp.module;
+ const use_llvm = comp.config.use_llvm;
// If there is no Zig code to compile, then we should skip flushing the output file because it
// will not be part of the linker line anyway.
@@ -3535,7 +3544,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
defer if (!wasm.base.disable_lld_caching) man.deinit();
var digest: [Cache.hex_digest_len]u8 = undefined;
- const objects = wasm.base.comp.objects;
+ const objects = comp.objects;
// NOTE: The following section must be maintained to be equal
// as the section defined in `linkWithLLD`
@@ -3556,7 +3565,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
}
try man.addOptionalFile(module_obj_path);
try man.addOptionalFile(compiler_rt_path);
- man.hash.addOptionalBytes(wasm.base.comp.config.entry);
+ man.hash.addOptionalBytes(wasm.entry_name);
man.hash.add(wasm.base.stack_size);
man.hash.add(wasm.base.build_id);
man.hash.add(import_memory);
@@ -3605,12 +3614,12 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
var positionals = std.ArrayList([]const u8).init(arena);
try positionals.ensureUnusedCapacity(objects.len);
- const target = wasm.base.comp.root_mod.resolved_target.result;
- const output_mode = wasm.base.comp.config.output_mode;
- const link_mode = wasm.base.comp.config.link_mode;
- const link_libc = wasm.base.comp.config.link_libc;
- const link_libcpp = wasm.base.comp.config.link_libcpp;
- const wasi_exec_model = wasm.base.comp.config.wasi_exec_model;
+ const target = comp.root_mod.resolved_target.result;
+ const output_mode = comp.config.output_mode;
+ const link_mode = comp.config.link_mode;
+ const link_libc = comp.config.link_libc;
+ const link_libcpp = comp.config.link_libcpp;
+ const wasi_exec_model = comp.config.wasi_exec_model;
// When the target os is WASI, we allow linking with WASI-LIBC
if (target.os.tag == .wasi) {
@@ -4648,7 +4657,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
}
try man.addOptionalFile(module_obj_path);
try man.addOptionalFile(compiler_rt_path);
- man.hash.addOptionalBytes(wasm.base.comp.config.entry);
+ man.hash.addOptionalBytes(wasm.entry_name);
man.hash.add(wasm.base.stack_size);
man.hash.add(wasm.base.build_id);
man.hash.add(import_memory);
@@ -4799,9 +4808,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
try argv.append("--export-dynamic");
}
- if (comp.config.entry) |entry| {
- try argv.append("--entry");
- try argv.append(entry);
+ if (wasm.entry_name) |entry_name| {
+ try argv.appendSlice(&.{ "--entry", entry_name });
} else {
try argv.append("--no-entry");
}
@@ -5347,3 +5355,10 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
try wasm.mark(target_loc.finalLoc(wasm));
}
}
+
+fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 {
+ return switch (wasi_exec_model) {
+ .reactor => "_initialize",
+ .command => "_start",
+ };
+}