authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-12-26 21:39:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-01 17:51:21-07:00
log57562c8d507667b6fefcb7fbc7a305fbd610b5dd
treef0e494bdace7302f7cce8ad8c9c7d3227bd81d64
parentc9fe43679f8d8f0db6250cc881b59cc68daaf128

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.

11 files changed, 124 insertions(+), 99 deletions(-)

src/Compilation.zig+6-3
...@@ -924,7 +924,7 @@ pub const LinkObject = struct {...@@ -924,7 +924,7 @@ pub const LinkObject = struct {
924 loption: bool = false,924 loption: bool = false,
925};925};
926926
927pub const InitOptions = struct {927pub const CreateOptions = struct {
928 zig_lib_directory: Directory,928 zig_lib_directory: Directory,
929 local_cache_directory: Directory,929 local_cache_directory: Directory,
930 global_cache_directory: Directory,930 global_cache_directory: Directory,
...@@ -1054,7 +1054,7 @@ pub const InitOptions = struct {...@@ -1054,7 +1054,7 @@ pub const InitOptions = struct {
1054 /// infinite recursion.1054 /// infinite recursion.
1055 skip_linker_dependencies: bool = false,1055 skip_linker_dependencies: bool = false,
1056 hash_style: link.File.Elf.HashStyle = .both,1056 hash_style: link.File.Elf.HashStyle = .both,
1057 entry: ?[]const u8 = null,1057 entry: Entry = .default,
1058 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{},1058 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{},
1059 stack_size: ?u64 = null,1059 stack_size: ?u64 = null,
1060 image_base: ?u64 = null,1060 image_base: ?u64 = null,
...@@ -1089,6 +1089,8 @@ pub const InitOptions = struct {...@@ -1089,6 +1089,8 @@ pub const InitOptions = struct {
1089 /// (Windows) PDB output path1089 /// (Windows) PDB output path
1090 pdb_out_path: ?[]const u8 = null,1090 pdb_out_path: ?[]const u8 = null,
1091 error_limit: ?Compilation.Module.ErrorInt = null,1091 error_limit: ?Compilation.Module.ErrorInt = null,
1092
1093 pub const Entry = link.File.OpenOptions.Entry;
1092};1094};
10931095
1094fn addModuleTableToCacheHash(1096fn addModuleTableToCacheHash(
...@@ -1159,7 +1161,7 @@ fn addModuleTableToCacheHash(...@@ -1159,7 +1161,7 @@ fn addModuleTableToCacheHash(
1159 }1161 }
1160}1162}
11611163
1162pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {1164pub fn create(gpa: Allocator, options: CreateOptions) !*Compilation {
1163 const output_mode = options.config.output_mode;1165 const output_mode = options.config.output_mode;
1164 const is_dyn_lib = switch (output_mode) {1166 const is_dyn_lib = switch (output_mode) {
1165 .Obj, .Exe => false,1167 .Obj, .Exe => false,
...@@ -1543,6 +1545,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1543,6 +1545,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1543 .dynamicbase = options.linker_dynamicbase,1545 .dynamicbase = options.linker_dynamicbase,
1544 .major_subsystem_version = options.major_subsystem_version,1546 .major_subsystem_version = options.major_subsystem_version,
1545 .minor_subsystem_version = options.minor_subsystem_version,1547 .minor_subsystem_version = options.minor_subsystem_version,
1548 .entry = options.entry,
1546 .stack_size = options.stack_size,1549 .stack_size = options.stack_size,
1547 .image_base = options.image_base,1550 .image_base = options.image_base,
1548 .version_script = options.version_script,1551 .version_script = options.version_script,
src/Compilation/Config.zig-29
...@@ -55,7 +55,6 @@ export_memory: bool,...@@ -55,7 +55,6 @@ export_memory: bool,
55shared_memory: bool,55shared_memory: bool,
56is_test: bool,56is_test: bool,
57test_evented_io: bool,57test_evented_io: bool,
58entry: ?[]const u8,
59debug_format: DebugFormat,58debug_format: DebugFormat,
60root_strip: bool,59root_strip: bool,
61root_error_tracing: bool,60root_error_tracing: bool,
...@@ -100,12 +99,6 @@ pub const Options = struct {...@@ -100,12 +99,6 @@ pub const Options = struct {
100 use_lld: ?bool = null,99 use_lld: ?bool = null,
101 use_clang: ?bool = null,100 use_clang: ?bool = null,
102 lto: ?bool = null,101 lto: ?bool = null,
103 entry: union(enum) {
104 default,
105 disabled,
106 enabled,
107 named: []const u8,
108 } = .default,
109 /// WASI-only. Type of WASI execution model ("command" or "reactor").102 /// WASI-only. Type of WASI execution model ("command" or "reactor").
110 wasi_exec_model: ?std.builtin.WasiExecModel = null,103 wasi_exec_model: ?std.builtin.WasiExecModel = null,
111 import_memory: ?bool = null,104 import_memory: ?bool = null,
...@@ -123,8 +116,6 @@ pub const ResolveError = error{...@@ -123,8 +116,6 @@ pub const ResolveError = error{
123 ObjectFilesCannotShareMemory,116 ObjectFilesCannotShareMemory,
124 SharedMemoryRequiresAtomicsAndBulkMemory,117 SharedMemoryRequiresAtomicsAndBulkMemory,
125 ThreadsRequireSharedMemory,118 ThreadsRequireSharedMemory,
126 UnknownTargetEntryPoint,
127 NonExecutableEntryPoint,
128 EmittingLlvmModuleRequiresLlvmBackend,119 EmittingLlvmModuleRequiresLlvmBackend,
129 LlvmLacksTargetSupport,120 LlvmLacksTargetSupport,
130 ZigLacksTargetSupport,121 ZigLacksTargetSupport,
...@@ -352,25 +343,6 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -352,25 +343,6 @@ pub fn resolve(options: Options) ResolveError!Config {
352 break :b false;343 break :b false;
353 };344 };
354345
355 const entry: ?[]const u8 = switch (options.entry) {
356 .disabled => null,
357 .default => b: {
358 if (options.output_mode != .Exe) break :b null;
359
360 // When producing C source code, the decision of entry point is made
361 // when compiling the C code, not when producing the C code.
362 if (target.ofmt == .c) break :b null;
363
364 break :b target_util.defaultEntrySymbolName(target, wasi_exec_model) orelse
365 return error.UnknownTargetEntryPoint;
366 },
367 .enabled => target_util.defaultEntrySymbolName(target, wasi_exec_model) orelse
368 return error.UnknownTargetEntryPoint,
369 .named => |name| name,
370 };
371 if (entry != null and options.output_mode != .Exe)
372 return error.NonExecutableEntryPoint;
373
374 const any_unwind_tables = options.any_unwind_tables or346 const any_unwind_tables = options.any_unwind_tables or
375 link_libunwind or target_util.needUnwindTables(target);347 link_libunwind or target_util.needUnwindTables(target);
376348
...@@ -519,7 +491,6 @@ pub fn resolve(options: Options) ResolveError!Config {...@@ -519,7 +491,6 @@ pub fn resolve(options: Options) ResolveError!Config {
519 .use_llvm = use_llvm,491 .use_llvm = use_llvm,
520 .use_lib_llvm = use_lib_llvm,492 .use_lib_llvm = use_lib_llvm,
521 .use_lld = use_lld,493 .use_lld = use_lld,
522 .entry = entry,
523 .wasi_exec_model = wasi_exec_model,494 .wasi_exec_model = wasi_exec_model,
524 .debug_format = debug_format,495 .debug_format = debug_format,
525 .root_strip = root_strip,496 .root_strip = root_strip,
src/link.zig+9
...@@ -83,6 +83,8 @@ pub const File = struct {...@@ -83,6 +83,8 @@ pub const File = struct {
83 symbol_count_hint: u64 = 32,83 symbol_count_hint: u64 = 32,
84 program_code_size_hint: u64 = 256 * 1024,84 program_code_size_hint: u64 = 256 * 1024,
8585
86 /// This may depend on what symbols are found during the linking process.
87 entry: Entry,
86 /// Virtual address of the entry point procedure relative to image base.88 /// Virtual address of the entry point procedure relative to image base.
87 entry_addr: ?u64,89 entry_addr: ?u64,
88 stack_size: ?u64,90 stack_size: ?u64,
...@@ -169,6 +171,13 @@ pub const File = struct {...@@ -169,6 +171,13 @@ pub const File = struct {
169 module_definition_file: ?[]const u8,171 module_definition_file: ?[]const u8,
170172
171 wasi_emulated_libs: []const wasi_libc.CRTFile,173 wasi_emulated_libs: []const wasi_libc.CRTFile,
174
175 pub const Entry = union(enum) {
176 default,
177 disabled,
178 enabled,
179 named: []const u8,
180 };
172 };181 };
173182
174 /// Attempts incremental linking, if the file already exists. If183 /// Attempts incremental linking, if the file already exists. If
src/link/Coff.zig+20-1
...@@ -17,6 +17,7 @@ dynamicbase: bool,...@@ -17,6 +17,7 @@ dynamicbase: bool,
17major_subsystem_version: u16,17major_subsystem_version: u16,
18minor_subsystem_version: u16,18minor_subsystem_version: u16,
19lib_dirs: []const []const u8,19lib_dirs: []const []const u8,
20entry: link.File.OpenOptions.Entry,
20entry_addr: ?u32,21entry_addr: ?u32,
21module_definition_file: ?[]const u8,22module_definition_file: ?[]const u8,
22pdb_out_path: ?[]const u8,23pdb_out_path: ?[]const u8,
...@@ -303,7 +304,12 @@ pub fn createEmpty(...@@ -303,7 +304,12 @@ pub fn createEmpty(
303 .Obj => 0,304 .Obj => 0,
304 },305 },
305306
307 // Subsystem depends on the set of public symbol names from linked objects.
308 // See LinkerDriver::inferSubsystem from the LLD project for the flow chart.
306 .subsystem = options.subsystem,309 .subsystem = options.subsystem,
310
311 .entry = options.entry,
312
307 .tsaware = options.tsaware,313 .tsaware = options.tsaware,
308 .nxcompat = options.nxcompat,314 .nxcompat = options.nxcompat,
309 .dynamicbase = options.dynamicbase,315 .dynamicbase = options.dynamicbase,
...@@ -2498,7 +2504,20 @@ inline fn getSizeOfImage(self: Coff) u32 {...@@ -2498,7 +2504,20 @@ inline fn getSizeOfImage(self: Coff) u32 {
2498/// Returns symbol location corresponding to the set entrypoint (if any).2504/// Returns symbol location corresponding to the set entrypoint (if any).
2499pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {2505pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {
2500 const comp = self.base.comp;2506 const comp = self.base.comp;
2501 const entry_name = comp.config.entry orelse return null;2507
2508 // TODO This is incomplete.
2509 // The entry symbol name depends on the subsystem as well as the set of
2510 // public symbol names from linked objects.
2511 // See LinkerDriver::findDefaultEntry from the LLD project for the flow chart.
2512 const entry_name = switch (self.entry) {
2513 .disabled => return null,
2514 .default => switch (comp.config.output_mode) {
2515 .Exe => "wWinMainCRTStartup",
2516 .Obj, .Lib => return null,
2517 },
2518 .enabled => "wWinMainCRTStartup",
2519 .named => |name| name,
2520 };
2502 const global_index = self.resolver.get(entry_name) orelse return null;2521 const global_index = self.resolver.get(entry_name) orelse return null;
2503 return self.globals.items[global_index];2522 return self.globals.items[global_index];
2504}2523}
src/link/Coff/lld.zig+11-4
...@@ -52,6 +52,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -52,6 +52,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
52 const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;52 const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
53 const target = comp.root_mod.resolved_target.result;53 const target = comp.root_mod.resolved_target.result;
54 const optimize_mode = comp.root_mod.optimize_mode;54 const optimize_mode = comp.root_mod.optimize_mode;
55 const entry_name: ?[]const u8 = switch (self.entry) {
56 // This logic isn't quite right for disabled or enabled. No point in fixing it
57 // when the goal is to eliminate dependency on LLD anyway.
58 // https://github.com/ziglang/zig/issues/17751
59 .disabled, .default, .enabled => null,
60 .named => |name| name,
61 };
5562
56 // See link/Elf.zig for comments on how this mechanism works.63 // See link/Elf.zig for comments on how this mechanism works.
57 const id_symlink_basename = "lld.id";64 const id_symlink_basename = "lld.id";
...@@ -80,7 +87,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -80,7 +87,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
80 }87 }
81 }88 }
82 try man.addOptionalFile(module_obj_path);89 try man.addOptionalFile(module_obj_path);
83 man.hash.addOptionalBytes(comp.config.entry);90 man.hash.addOptionalBytes(entry_name);
84 man.hash.add(self.base.stack_size);91 man.hash.add(self.base.stack_size);
85 man.hash.add(self.image_base);92 man.hash.add(self.image_base);
86 man.hash.addListOfBytes(self.lib_dirs);93 man.hash.addListOfBytes(self.lib_dirs);
...@@ -218,8 +225,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -218,8 +225,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
218 try argv.append("-DLL");225 try argv.append("-DLL");
219 }226 }
220227
221 if (comp.config.entry) |entry| {228 if (entry_name) |name| {
222 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));229 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name}));
223 }230 }
224231
225 if (self.tsaware) {232 if (self.tsaware) {
...@@ -441,7 +448,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -441,7 +448,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
441 }448 }
442 } else {449 } else {
443 try argv.append("-NODEFAULTLIB");450 try argv.append("-NODEFAULTLIB");
444 if (!is_lib and comp.config.entry == null) {451 if (!is_lib and entry_name == null) {
445 if (comp.module) |module| {452 if (comp.module) |module| {
446 if (module.stage1_flags.have_winmain_crt_startup) {453 if (module.stage1_flags.have_winmain_crt_startup) {
447 try argv.append("-ENTRY:WinMainCRTStartup");454 try argv.append("-ENTRY:WinMainCRTStartup");
src/link/Elf.zig+21-8
...@@ -25,6 +25,7 @@ linker_script: ?[]const u8,...@@ -25,6 +25,7 @@ linker_script: ?[]const u8,
25version_script: ?[]const u8,25version_script: ?[]const u8,
26print_icf_sections: bool,26print_icf_sections: bool,
27print_map: bool,27print_map: bool,
28entry_name: ?[]const u8,
2829
29ptr_width: PtrWidth,30ptr_width: PtrWidth,
3031
...@@ -290,6 +291,13 @@ pub fn createEmpty(...@@ -290,6 +291,13 @@ pub fn createEmpty(
290 .page_size = page_size,291 .page_size = page_size,
291 .default_sym_version = default_sym_version,292 .default_sym_version = default_sym_version,
292293
294 .entry_name = switch (options.entry) {
295 .disabled => null,
296 .default => if (output_mode != .Exe) null else defaultEntrySymbolName(target.cpu.arch),
297 .enabled => defaultEntrySymbolName(target.cpu.arch),
298 .named => |name| name,
299 },
300
293 .image_base = b: {301 .image_base = b: {
294 if (is_dyn_lib) break :b 0;302 if (is_dyn_lib) break :b 0;
295 if (output_mode == .Exe and comp.config.pie) break :b 0;303 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...@@ -1305,7 +1313,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13051313
1306 // Look for entry address in objects if not set by the incremental compiler.1314 // Look for entry address in objects if not set by the incremental compiler.
1307 if (self.entry_index == null) {1315 if (self.entry_index == null) {
1308 if (comp.config.entry) |name| {1316 if (self.entry_name) |name| {
1309 self.entry_index = self.globalByName(name);1317 self.entry_index = self.globalByName(name);
1310 }1318 }
1311 }1319 }
...@@ -1679,9 +1687,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1679,9 +1687,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1679 }1687 }
1680 }1688 }
16811689
1682 if (comp.config.entry) |entry| {1690 if (self.entry_name) |name| {
1683 try argv.append("--entry");1691 try argv.appendSlice(&.{ "--entry", name });
1684 try argv.append(entry);
1685 }1692 }
16861693
1687 for (self.base.rpath_list) |rpath| {1694 for (self.base.rpath_list) |rpath| {
...@@ -2427,7 +2434,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -2427,7 +2434,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
24272434
2428 // We can skip hashing libc and libc++ components that we are in charge of building from Zig2435 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
2429 // installation sources because they are always a product of the compiler version + target information.2436 // installation sources because they are always a product of the compiler version + target information.
2430 man.hash.addOptionalBytes(comp.config.entry);2437 man.hash.addOptionalBytes(self.entry_name);
2431 man.hash.add(self.image_base);2438 man.hash.add(self.image_base);
2432 man.hash.add(self.base.gc_sections);2439 man.hash.add(self.base.gc_sections);
2433 man.hash.addOptional(self.sort_section);2440 man.hash.addOptional(self.sort_section);
...@@ -2563,9 +2570,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -2563,9 +2570,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
2563 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),2570 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
2564 }2571 }
25652572
2566 if (comp.config.entry) |entry| {2573 if (self.entry_name) |name| {
2567 try argv.append("--entry");2574 try argv.appendSlice(&.{ "--entry", name });
2568 try argv.append(entry);
2569 }2575 }
25702576
2571 for (self.base.force_undefined_symbols.keys()) |sym| {2577 for (self.base.force_undefined_symbols.keys()) |sym| {
...@@ -6512,6 +6518,13 @@ const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection);...@@ -6512,6 +6518,13 @@ const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection);
6512pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;6518pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1;
6513pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;6519pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2;
65146520
6521fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
6522 return switch (cpu_arch) {
6523 .mips, .mipsel, .mips64, .mips64el => "__start",
6524 else => "_start",
6525 };
6526}
6527
6515const std = @import("std");6528const std = @import("std");
6516const build_options = @import("build_options");6529const build_options = @import("build_options");
6517const builtin = @import("builtin");6530const builtin = @import("builtin");
src/link/MachO.zig+13-4
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1base: File,1base: File,
2entry_name: ?[]const u8,
23
3/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.4/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
4llvm_object: ?*LlvmObject = null,5llvm_object: ?*LlvmObject = null,
...@@ -231,6 +232,12 @@ pub fn createEmpty(...@@ -231,6 +232,12 @@ pub fn createEmpty(
231 .install_name = options.install_name,232 .install_name = options.install_name,
232 .entitlements = options.entitlements,233 .entitlements = options.entitlements,
233 .compatibility_version = options.compatibility_version,234 .compatibility_version = options.compatibility_version,
235 .entry_name = switch (options.entry) {
236 .disabled => null,
237 .default => if (output_mode != .Exe) null else default_entry_symbol_name,
238 .enabled => default_entry_symbol_name,
239 .named => |name| name,
240 },
234 };241 };
235 if (use_llvm and comp.config.have_zcu) {242 if (use_llvm and comp.config.have_zcu) {
236 self.llvm_object = try LlvmObject.create(arena, comp);243 self.llvm_object = try LlvmObject.create(arena, comp);
...@@ -1629,8 +1636,9 @@ pub fn resolveSymbols(self: *MachO) !void {...@@ -1629,8 +1636,9 @@ pub fn resolveSymbols(self: *MachO) !void {
1629 // we search for it in libraries should there be no object files specified1636 // we search for it in libraries should there be no object files specified
1630 // on the linker line.1637 // on the linker line.
1631 if (output_mode == .Exe) {1638 if (output_mode == .Exe) {
1632 const entry_name = comp.config.entry.?;1639 if (self.entry_name) |entry_name| {
1633 _ = try self.addUndefined(entry_name, .{});1640 _ = try self.addUndefined(entry_name, .{});
1641 }
1634 }1642 }
16351643
1636 // Force resolution of any symbols requested by the user.1644 // Force resolution of any symbols requested by the user.
...@@ -5085,8 +5093,7 @@ pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {...@@ -5085,8 +5093,7 @@ pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 {
5085/// Returns symbol location corresponding to the set entrypoint if any.5093/// Returns symbol location corresponding to the set entrypoint if any.
5086/// Asserts output mode is executable.5094/// Asserts output mode is executable.
5087pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {5095pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {
5088 const comp = self.base.comp;5096 const entry_name = self.entry_name orelse return null;
5089 const entry_name = comp.config.entry orelse return null;
5090 const global = self.getGlobal(entry_name) orelse return null;5097 const global = self.getGlobal(entry_name) orelse return null;
5091 return global;5098 return global;
5092}5099}
...@@ -5645,6 +5652,8 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {...@@ -5645,6 +5652,8 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
5645 }5652 }
5646}5653}
56475654
5655const default_entry_symbol_name = "_main";
5656
5648pub const base_tag: File.Tag = File.Tag.macho;5657pub const base_tag: File.Tag = File.Tag.macho;
5649pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));5658pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
5650pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));5659pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
src/link/MachO/zld.zig+2-3
...@@ -276,9 +276,8 @@ pub fn linkWithZld(...@@ -276,9 +276,8 @@ pub fn linkWithZld(
276 try argv.append("-dead_strip_dylibs");276 try argv.append("-dead_strip_dylibs");
277 }277 }
278278
279 if (comp.config.entry) |entry| {279 if (macho_file.entry_name) |entry_name| {
280 try argv.append("-e");280 try argv.appendSlice(&.{ "-e", entry_name });
281 try argv.append(entry);
282 }281 }
283282
284 for (objects) |obj| {283 for (objects) |obj| {
src/link/Wasm.zig+33-18
...@@ -37,6 +37,7 @@ pub const Relocation = types.Relocation;...@@ -37,6 +37,7 @@ pub const Relocation = types.Relocation;
37pub const base_tag: link.File.Tag = .wasm;37pub const base_tag: link.File.Tag = .wasm;
3838
39base: link.File,39base: link.File,
40entry_name: ?[]const u8,
40import_symbols: bool,41import_symbols: bool,
41export_symbol_names: []const []const u8,42export_symbol_names: []const []const u8,
42global_base: ?u64,43global_base: ?u64,
...@@ -397,6 +398,7 @@ pub fn createEmpty(...@@ -397,6 +398,7 @@ pub fn createEmpty(
397 const use_llvm = comp.config.use_llvm;398 const use_llvm = comp.config.use_llvm;
398 const output_mode = comp.config.output_mode;399 const output_mode = comp.config.output_mode;
399 const shared_memory = comp.config.shared_memory;400 const shared_memory = comp.config.shared_memory;
401 const wasi_exec_model = comp.config.wasi_exec_model;
400402
401 // If using LLD to link, this code should produce an object file so that it403 // If using LLD to link, this code should produce an object file so that it
402 // can be passed to LLD.404 // can be passed to LLD.
...@@ -434,6 +436,13 @@ pub fn createEmpty(...@@ -434,6 +436,13 @@ pub fn createEmpty(
434 .initial_memory = options.initial_memory,436 .initial_memory = options.initial_memory,
435 .max_memory = options.max_memory,437 .max_memory = options.max_memory,
436 .wasi_emulated_libs = options.wasi_emulated_libs,438 .wasi_emulated_libs = options.wasi_emulated_libs,
439
440 .entry_name = switch (options.entry) {
441 .disabled => null,
442 .default => if (output_mode != .Exe) null else defaultEntrySymbolName(wasi_exec_model),
443 .enabled => defaultEntrySymbolName(wasi_exec_model),
444 .named => |name| name,
445 },
437 };446 };
438 if (use_llvm and comp.config.have_zcu) {447 if (use_llvm and comp.config.have_zcu) {
439 wasm.llvm_object = try LlvmObject.create(arena, comp);448 wasm.llvm_object = try LlvmObject.create(arena, comp);
...@@ -3042,7 +3051,7 @@ fn setupExports(wasm: *Wasm) !void {...@@ -3042,7 +3051,7 @@ fn setupExports(wasm: *Wasm) !void {
3042fn setupStart(wasm: *Wasm) !void {3051fn setupStart(wasm: *Wasm) !void {
3043 const comp = wasm.base.comp;3052 const comp = wasm.base.comp;
3044 // do not export entry point if user set none or no default was set.3053 // do not export entry point if user set none or no default was set.
3045 const entry_name = comp.config.entry orelse return;3054 const entry_name = wasm.entry_name orelse return;
30463055
3047 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {3056 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
3048 log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});3057 log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
...@@ -3475,8 +3484,8 @@ fn resetState(wasm: *Wasm) void {...@@ -3475,8 +3484,8 @@ fn resetState(wasm: *Wasm) void {
3475}3484}
34763485
3477pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {3486pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
3478 const use_lld = build_options.have_llvm and wasm.base.comp.config.use_lld;3487 const use_lld = build_options.have_llvm and comp.config.use_lld;
3479 const use_llvm = wasm.base.comp.config.use_llvm;3488 const use_llvm = comp.config.use_llvm;
34803489
3481 if (use_lld) {3490 if (use_lld) {
3482 return wasm.linkWithLLD(comp, prog_node);3491 return wasm.linkWithLLD(comp, prog_node);
...@@ -3492,7 +3501,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3492,7 +3501,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3492 const tracy = trace(@src());3501 const tracy = trace(@src());
3493 defer tracy.end();3502 defer tracy.end();
34943503
3495 const gpa = wasm.base.comp.gpa;3504 const gpa = comp.gpa;
3496 const shared_memory = comp.config.shared_memory;3505 const shared_memory = comp.config.shared_memory;
3497 const import_memory = comp.config.import_memory;3506 const import_memory = comp.config.import_memory;
34983507
...@@ -3503,8 +3512,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3503,8 +3512,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
35033512
3504 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.3513 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
3505 const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});3514 const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path});
3506 const opt_zcu = wasm.base.comp.module;3515 const opt_zcu = comp.module;
3507 const use_llvm = wasm.base.comp.config.use_llvm;3516 const use_llvm = comp.config.use_llvm;
35083517
3509 // If there is no Zig code to compile, then we should skip flushing the output file because it3518 // If there is no Zig code to compile, then we should skip flushing the output file because it
3510 // will not be part of the linker line anyway.3519 // 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...@@ -3535,7 +3544,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3535 defer if (!wasm.base.disable_lld_caching) man.deinit();3544 defer if (!wasm.base.disable_lld_caching) man.deinit();
3536 var digest: [Cache.hex_digest_len]u8 = undefined;3545 var digest: [Cache.hex_digest_len]u8 = undefined;
35373546
3538 const objects = wasm.base.comp.objects;3547 const objects = comp.objects;
35393548
3540 // NOTE: The following section must be maintained to be equal3549 // NOTE: The following section must be maintained to be equal
3541 // as the section defined in `linkWithLLD`3550 // as the section defined in `linkWithLLD`
...@@ -3556,7 +3565,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3556,7 +3565,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3556 }3565 }
3557 try man.addOptionalFile(module_obj_path);3566 try man.addOptionalFile(module_obj_path);
3558 try man.addOptionalFile(compiler_rt_path);3567 try man.addOptionalFile(compiler_rt_path);
3559 man.hash.addOptionalBytes(wasm.base.comp.config.entry);3568 man.hash.addOptionalBytes(wasm.entry_name);
3560 man.hash.add(wasm.base.stack_size);3569 man.hash.add(wasm.base.stack_size);
3561 man.hash.add(wasm.base.build_id);3570 man.hash.add(wasm.base.build_id);
3562 man.hash.add(import_memory);3571 man.hash.add(import_memory);
...@@ -3605,12 +3614,12 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3605,12 +3614,12 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3605 var positionals = std.ArrayList([]const u8).init(arena);3614 var positionals = std.ArrayList([]const u8).init(arena);
3606 try positionals.ensureUnusedCapacity(objects.len);3615 try positionals.ensureUnusedCapacity(objects.len);
36073616
3608 const target = wasm.base.comp.root_mod.resolved_target.result;3617 const target = comp.root_mod.resolved_target.result;
3609 const output_mode = wasm.base.comp.config.output_mode;3618 const output_mode = comp.config.output_mode;
3610 const link_mode = wasm.base.comp.config.link_mode;3619 const link_mode = comp.config.link_mode;
3611 const link_libc = wasm.base.comp.config.link_libc;3620 const link_libc = comp.config.link_libc;
3612 const link_libcpp = wasm.base.comp.config.link_libcpp;3621 const link_libcpp = comp.config.link_libcpp;
3613 const wasi_exec_model = wasm.base.comp.config.wasi_exec_model;3622 const wasi_exec_model = comp.config.wasi_exec_model;
36143623
3615 // When the target os is WASI, we allow linking with WASI-LIBC3624 // When the target os is WASI, we allow linking with WASI-LIBC
3616 if (target.os.tag == .wasi) {3625 if (target.os.tag == .wasi) {
...@@ -4648,7 +4657,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -4648,7 +4657,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
4648 }4657 }
4649 try man.addOptionalFile(module_obj_path);4658 try man.addOptionalFile(module_obj_path);
4650 try man.addOptionalFile(compiler_rt_path);4659 try man.addOptionalFile(compiler_rt_path);
4651 man.hash.addOptionalBytes(wasm.base.comp.config.entry);4660 man.hash.addOptionalBytes(wasm.entry_name);
4652 man.hash.add(wasm.base.stack_size);4661 man.hash.add(wasm.base.stack_size);
4653 man.hash.add(wasm.base.build_id);4662 man.hash.add(wasm.base.build_id);
4654 man.hash.add(import_memory);4663 man.hash.add(import_memory);
...@@ -4799,9 +4808,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -4799,9 +4808,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
4799 try argv.append("--export-dynamic");4808 try argv.append("--export-dynamic");
4800 }4809 }
48014810
4802 if (comp.config.entry) |entry| {4811 if (wasm.entry_name) |entry_name| {
4803 try argv.append("--entry");4812 try argv.appendSlice(&.{ "--entry", entry_name });
4804 try argv.append(entry);
4805 } else {4813 } else {
4806 try argv.append("--no-entry");4814 try argv.append("--no-entry");
4807 }4815 }
...@@ -5347,3 +5355,10 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {...@@ -5347,3 +5355,10 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
5347 try wasm.mark(target_loc.finalLoc(wasm));5355 try wasm.mark(target_loc.finalLoc(wasm));
5348 }5356 }
5349}5357}
5358
5359fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 {
5360 return switch (wasi_exec_model) {
5361 .reactor => "_initialize",
5362 .command => "_start",
5363 };
5364}
src/main.zig+9-9
...@@ -857,6 +857,7 @@ fn buildOutputType(...@@ -857,6 +857,7 @@ fn buildOutputType(
857 var linker_optimization: ?[]const u8 = null;857 var linker_optimization: ?[]const u8 = null;
858 var linker_module_definition_file: ?[]const u8 = null;858 var linker_module_definition_file: ?[]const u8 = null;
859 var test_no_exec = false;859 var test_no_exec = false;
860 var entry: Compilation.CreateOptions.Entry = .default;
860 var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{};861 var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{};
861 var stack_size: ?u64 = null;862 var stack_size: ?u64 = null;
862 var image_base: ?u64 = null;863 var image_base: ?u64 = null;
...@@ -1129,7 +1130,7 @@ fn buildOutputType(...@@ -1129,7 +1130,7 @@ fn buildOutputType(
1129 } else if (mem.eql(u8, arg, "-O")) {1130 } else if (mem.eql(u8, arg, "-O")) {
1130 mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());1131 mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());
1131 } else if (mem.startsWith(u8, arg, "-fentry=")) {1132 } else if (mem.startsWith(u8, arg, "-fentry=")) {
1132 create_module.opts.entry = .{ .named = arg["-fentry=".len..] };1133 entry = .{ .named = arg["-fentry=".len..] };
1133 } else if (mem.eql(u8, arg, "--force_undefined")) {1134 } else if (mem.eql(u8, arg, "--force_undefined")) {
1134 try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});1135 try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});
1135 } else if (mem.eql(u8, arg, "--stack")) {1136 } else if (mem.eql(u8, arg, "--stack")) {
...@@ -1556,12 +1557,12 @@ fn buildOutputType(...@@ -1556,12 +1557,12 @@ fn buildOutputType(
1556 } else if (mem.eql(u8, arg, "--import-memory")) {1557 } else if (mem.eql(u8, arg, "--import-memory")) {
1557 create_module.opts.import_memory = true;1558 create_module.opts.import_memory = true;
1558 } else if (mem.eql(u8, arg, "-fentry")) {1559 } else if (mem.eql(u8, arg, "-fentry")) {
1559 switch (create_module.opts.entry) {1560 switch (entry) {
1560 .default, .disabled => create_module.opts.entry = .enabled,1561 .default, .disabled => entry = .enabled,
1561 .enabled, .named => {},1562 .enabled, .named => {},
1562 }1563 }
1563 } else if (mem.eql(u8, arg, "-fno-entry")) {1564 } else if (mem.eql(u8, arg, "-fno-entry")) {
1564 create_module.opts.entry = .disabled;1565 entry = .disabled;
1565 } else if (mem.eql(u8, arg, "--export-memory")) {1566 } else if (mem.eql(u8, arg, "--export-memory")) {
1566 create_module.opts.export_memory = true;1567 create_module.opts.export_memory = true;
1567 } else if (mem.eql(u8, arg, "--import-symbols")) {1568 } else if (mem.eql(u8, arg, "--import-symbols")) {
...@@ -2065,7 +2066,7 @@ fn buildOutputType(...@@ -2065,7 +2066,7 @@ fn buildOutputType(
2065 create_module.sysroot = it.only_arg;2066 create_module.sysroot = it.only_arg;
2066 },2067 },
2067 .entry => {2068 .entry => {
2068 create_module.opts.entry = .{ .named = it.only_arg };2069 entry = .{ .named = it.only_arg };
2069 },2070 },
2070 .force_undefined_symbol => {2071 .force_undefined_symbol => {
2071 try force_undefined_symbols.put(arena, it.only_arg, {});2072 try force_undefined_symbols.put(arena, it.only_arg, {});
...@@ -2210,7 +2211,7 @@ fn buildOutputType(...@@ -2210,7 +2211,7 @@ fn buildOutputType(
2210 } else if (mem.eql(u8, arg, "--export-table")) {2211 } else if (mem.eql(u8, arg, "--export-table")) {
2211 linker_export_table = true;2212 linker_export_table = true;
2212 } else if (mem.eql(u8, arg, "--no-entry")) {2213 } else if (mem.eql(u8, arg, "--no-entry")) {
2213 create_module.opts.entry = .disabled;2214 entry = .disabled;
2214 } else if (mem.eql(u8, arg, "--initial-memory")) {2215 } else if (mem.eql(u8, arg, "--initial-memory")) {
2215 const next_arg = linker_args_it.nextOrFatal();2216 const next_arg = linker_args_it.nextOrFatal();
2216 linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {2217 linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
...@@ -2284,7 +2285,7 @@ fn buildOutputType(...@@ -2284,7 +2285,7 @@ fn buildOutputType(
2284 };2285 };
2285 have_version = true;2286 have_version = true;
2286 } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {2287 } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
2287 create_module.opts.entry = .{ .named = linker_args_it.nextOrFatal() };2288 entry = .{ .named = linker_args_it.nextOrFatal() };
2288 } else if (mem.eql(u8, arg, "-u")) {2289 } else if (mem.eql(u8, arg, "-u")) {
2289 try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {});2290 try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {});
2290 } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {2291 } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
...@@ -3200,6 +3201,7 @@ fn buildOutputType(...@@ -3200,6 +3201,7 @@ fn buildOutputType(
3200 .minor_subsystem_version = minor_subsystem_version,3201 .minor_subsystem_version = minor_subsystem_version,
3201 .link_eh_frame_hdr = link_eh_frame_hdr,3202 .link_eh_frame_hdr = link_eh_frame_hdr,
3202 .link_emit_relocs = link_emit_relocs,3203 .link_emit_relocs = link_emit_relocs,
3204 .entry = entry,
3203 .force_undefined_symbols = force_undefined_symbols,3205 .force_undefined_symbols = force_undefined_symbols,
3204 .stack_size = stack_size,3206 .stack_size = stack_size,
3205 .image_base = image_base,3207 .image_base = image_base,
...@@ -3845,8 +3847,6 @@ fn createModule(...@@ -3845,8 +3847,6 @@ fn createModule(
3845 error.ObjectFilesCannotShareMemory => fatal("object files cannot share memory", .{}),3847 error.ObjectFilesCannotShareMemory => fatal("object files cannot share memory", .{}),
3846 error.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}),3848 error.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}),
3847 error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}),3849 error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}),
3848 error.UnknownTargetEntryPoint => fatal("unknown target entry point", .{}),
3849 error.NonExecutableEntryPoint => fatal("entry points only allowed for executables", .{}),
3850 error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}),3850 error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}),
3851 error.LlvmLacksTargetSupport => fatal("LLVM lacks support for the specified target", .{}),3851 error.LlvmLacksTargetSupport => fatal("LLVM lacks support for the specified target", .{}),
3852 error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}),3852 error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}),
src/target.zig-20
...@@ -683,23 +683,3 @@ pub fn backendSupportsFeature(...@@ -683,23 +683,3 @@ pub fn backendSupportsFeature(
683 .safety_checked_instructions => use_llvm,683 .safety_checked_instructions => use_llvm,
684 };684 };
685}685}
686
687pub fn defaultEntrySymbolName(
688 target: std.Target,
689 /// May be `undefined` when `target` is not WASI.
690 wasi_exec_model: std.builtin.WasiExecModel,
691) ?[]const u8 {
692 return switch (target.ofmt) {
693 .coff => "wWinMainCRTStartup",
694 .macho => "_main",
695 .elf, .plan9 => switch (target.cpu.arch) {
696 .mips, .mipsel, .mips64, .mips64el => "__start",
697 else => "_start",
698 },
699 .wasm => switch (wasi_exec_model) {
700 .reactor => "_initialize",
701 .command => "_start",
702 },
703 else => null,
704 };
705}