mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
wasm-linker: implement -fno-entry flag
This adds support for the `-fno-entry` and `-fentry` flags respectively, for
zig build-{exe/lib} and the build system. For `zig cc` we use the `--no-entry`
flag to be compatible with clang and existing tooling.
In `start.zig` we now make the main function optional when the target is
WebAssembly, as to allow for the build-exe command in combination with
`-fno-entry`.
When the execution model is set, and is set to 'reactor', we now verify
when an entry name is given it matches what is expected. When no entry
point is given, we set it to `_initialize` by default. This means the user
will also be met with an error when they use the reactor model, but did
not provide the correct function.
This commit is contained in:
parent
94cee4fb27
commit
1cb7a01b25
@ -64,6 +64,8 @@ initial_memory: ?u64 = null,
|
|||||||
max_memory: ?u64 = null,
|
max_memory: ?u64 = null,
|
||||||
shared_memory: bool = false,
|
shared_memory: bool = false,
|
||||||
global_base: ?u64 = null,
|
global_base: ?u64 = null,
|
||||||
|
/// For WebAssembly only. Tells the linker to not output an entry point.
|
||||||
|
no_entry: ?bool = null,
|
||||||
c_std: std.Build.CStd,
|
c_std: std.Build.CStd,
|
||||||
/// Set via options; intended to be read-only after that.
|
/// Set via options; intended to be read-only after that.
|
||||||
zig_lib_dir: ?LazyPath,
|
zig_lib_dir: ?LazyPath,
|
||||||
@ -1851,6 +1853,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
|||||||
if (self.global_base) |global_base| {
|
if (self.global_base) |global_base| {
|
||||||
try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
|
try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
|
||||||
}
|
}
|
||||||
|
try addFlag(&zig_args, "entry", self.no_entry);
|
||||||
|
|
||||||
if (self.code_model != .default) {
|
if (self.code_model != .default) {
|
||||||
try zig_args.append("-mcmodel");
|
try zig_args.append("-mcmodel");
|
||||||
|
|||||||
@ -82,11 +82,15 @@ comptime {
|
|||||||
.reactor => "_initialize",
|
.reactor => "_initialize",
|
||||||
.command => "_start",
|
.command => "_start",
|
||||||
};
|
};
|
||||||
if (!@hasDecl(root, wasm_start_sym)) {
|
if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) {
|
||||||
|
// Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
|
||||||
|
// case it's not required to provide an entrypoint such as main.
|
||||||
@export(wasi_start, .{ .name = wasm_start_sym });
|
@export(wasi_start, .{ .name = wasm_start_sym });
|
||||||
}
|
}
|
||||||
} else if (native_arch.isWasm() and native_os == .freestanding) {
|
} else if (native_arch.isWasm() and native_os == .freestanding) {
|
||||||
if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
|
// Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
|
||||||
|
// case it's not required to provide an entrypoint such as main.
|
||||||
|
if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(wasm_freestanding_start, .{ .name = start_sym_name });
|
||||||
} else if (native_os != .other and native_os != .freestanding) {
|
} else if (native_os != .other and native_os != .freestanding) {
|
||||||
if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
|
if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -643,6 +643,7 @@ pub const InitOptions = struct {
|
|||||||
linker_import_symbols: bool = false,
|
linker_import_symbols: bool = false,
|
||||||
linker_import_table: bool = false,
|
linker_import_table: bool = false,
|
||||||
linker_export_table: bool = false,
|
linker_export_table: bool = false,
|
||||||
|
linker_no_entry: bool = false,
|
||||||
linker_initial_memory: ?u64 = null,
|
linker_initial_memory: ?u64 = null,
|
||||||
linker_max_memory: ?u64 = null,
|
linker_max_memory: ?u64 = null,
|
||||||
linker_shared_memory: bool = false,
|
linker_shared_memory: bool = false,
|
||||||
@ -1614,6 +1615,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
|
|||||||
.import_symbols = options.linker_import_symbols,
|
.import_symbols = options.linker_import_symbols,
|
||||||
.import_table = options.linker_import_table,
|
.import_table = options.linker_import_table,
|
||||||
.export_table = options.linker_export_table,
|
.export_table = options.linker_export_table,
|
||||||
|
.no_entry = options.linker_no_entry,
|
||||||
.initial_memory = options.linker_initial_memory,
|
.initial_memory = options.linker_initial_memory,
|
||||||
.max_memory = options.linker_max_memory,
|
.max_memory = options.linker_max_memory,
|
||||||
.shared_memory = options.linker_shared_memory,
|
.shared_memory = options.linker_shared_memory,
|
||||||
@ -2577,6 +2579,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
|||||||
man.hash.addOptional(comp.bin_file.options.max_memory);
|
man.hash.addOptional(comp.bin_file.options.max_memory);
|
||||||
man.hash.add(comp.bin_file.options.shared_memory);
|
man.hash.add(comp.bin_file.options.shared_memory);
|
||||||
man.hash.addOptional(comp.bin_file.options.global_base);
|
man.hash.addOptional(comp.bin_file.options.global_base);
|
||||||
|
man.hash.add(comp.bin_file.options.no_entry);
|
||||||
|
|
||||||
// Mach-O specific stuff
|
// Mach-O specific stuff
|
||||||
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
|
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
|
||||||
|
|||||||
@ -166,6 +166,7 @@ pub const Options = struct {
|
|||||||
export_table: bool,
|
export_table: bool,
|
||||||
initial_memory: ?u64,
|
initial_memory: ?u64,
|
||||||
max_memory: ?u64,
|
max_memory: ?u64,
|
||||||
|
no_entry: bool,
|
||||||
shared_memory: bool,
|
shared_memory: bool,
|
||||||
export_symbol_names: []const []const u8,
|
export_symbol_names: []const []const u8,
|
||||||
global_base: ?u64,
|
global_base: ?u64,
|
||||||
|
|||||||
@ -2817,14 +2817,10 @@ fn setupExports(wasm: *Wasm) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setupStart(wasm: *Wasm) !void {
|
fn setupStart(wasm: *Wasm) !void {
|
||||||
|
if (wasm.base.options.no_entry) return;
|
||||||
const entry_name = wasm.base.options.entry orelse "_start";
|
const entry_name = wasm.base.options.entry orelse "_start";
|
||||||
|
|
||||||
const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
|
const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
|
||||||
if (wasm.base.options.output_mode == .Exe) {
|
|
||||||
if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors
|
|
||||||
} else {
|
|
||||||
return; // No entry point needed for non-executable wasm files
|
|
||||||
}
|
|
||||||
log.err("Entry symbol '{s}' missing", .{entry_name});
|
log.err("Entry symbol '{s}' missing", .{entry_name});
|
||||||
return error.MissingSymbol;
|
return error.MissingSymbol;
|
||||||
};
|
};
|
||||||
@ -4544,24 +4540,14 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
|
|||||||
const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
|
const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
|
||||||
try argv.append(arg);
|
try argv.append(arg);
|
||||||
|
|
||||||
if (wasm.base.options.output_mode == .Exe) {
|
|
||||||
if (wasm.base.options.wasi_exec_model == .reactor) {
|
|
||||||
// Reactor execution model does not have _start so lld doesn't look for it.
|
|
||||||
try argv.append("--no-entry");
|
|
||||||
// Make sure "_initialize" and other used-defined functions are exported if this is WASI reactor.
|
|
||||||
// If rdynamic is true, it will already be appended, so only verify if the user did not specify
|
|
||||||
// the flag in which case, we ensure `--export-dynamic` is called.
|
|
||||||
if (!wasm.base.options.rdynamic) {
|
|
||||||
try argv.append("--export-dynamic");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (wasm.base.options.entry == null) {
|
|
||||||
try argv.append("--no-entry"); // So lld doesn't look for _start.
|
|
||||||
}
|
|
||||||
if (wasm.base.options.import_symbols) {
|
if (wasm.base.options.import_symbols) {
|
||||||
try argv.append("--allow-undefined");
|
try argv.append("--allow-undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wasm.base.options.no_entry) {
|
||||||
|
try argv.append("--no-entry");
|
||||||
|
}
|
||||||
|
|
||||||
// XXX - TODO: add when wasm-ld supports --build-id.
|
// XXX - TODO: add when wasm-ld supports --build-id.
|
||||||
// if (wasm.base.options.build_id) {
|
// if (wasm.base.options.build_id) {
|
||||||
// try argv.append("--build-id=tree");
|
// try argv.append("--build-id=tree");
|
||||||
|
|||||||
29
src/main.zig
29
src/main.zig
@ -577,6 +577,8 @@ const usage_build_generic =
|
|||||||
\\ --shared-memory (WebAssembly) use shared linear memory
|
\\ --shared-memory (WebAssembly) use shared linear memory
|
||||||
\\ --global-base=[addr] (WebAssembly) where to start to place global data
|
\\ --global-base=[addr] (WebAssembly) where to start to place global data
|
||||||
\\ --export=[value] (WebAssembly) Force a symbol to be exported
|
\\ --export=[value] (WebAssembly) Force a symbol to be exported
|
||||||
|
\\ -fentry (WebAssembly) Force output an entry point
|
||||||
|
\\ -fno-entry (WebAssembly) Do not output any entry point
|
||||||
\\
|
\\
|
||||||
\\Test Options:
|
\\Test Options:
|
||||||
\\ --test-filter [text] Skip tests that do not match filter
|
\\ --test-filter [text] Skip tests that do not match filter
|
||||||
@ -835,6 +837,7 @@ fn buildOutputType(
|
|||||||
var linker_import_symbols: bool = false;
|
var linker_import_symbols: bool = false;
|
||||||
var linker_import_table: bool = false;
|
var linker_import_table: bool = false;
|
||||||
var linker_export_table: bool = false;
|
var linker_export_table: bool = false;
|
||||||
|
var linker_no_entry: ?bool = null;
|
||||||
var linker_initial_memory: ?u64 = null;
|
var linker_initial_memory: ?u64 = null;
|
||||||
var linker_max_memory: ?u64 = null;
|
var linker_max_memory: ?u64 = null;
|
||||||
var linker_shared_memory: bool = false;
|
var linker_shared_memory: bool = false;
|
||||||
@ -1503,6 +1506,10 @@ fn buildOutputType(
|
|||||||
}
|
}
|
||||||
} else if (mem.eql(u8, arg, "--import-memory")) {
|
} else if (mem.eql(u8, arg, "--import-memory")) {
|
||||||
linker_import_memory = true;
|
linker_import_memory = true;
|
||||||
|
} else if (mem.eql(u8, arg, "-fentry")) {
|
||||||
|
linker_no_entry = false;
|
||||||
|
} else if (mem.eql(u8, arg, "-fno-entry")) {
|
||||||
|
linker_no_entry = true;
|
||||||
} else if (mem.eql(u8, arg, "--export-memory")) {
|
} else if (mem.eql(u8, arg, "--export-memory")) {
|
||||||
linker_export_memory = true;
|
linker_export_memory = true;
|
||||||
} else if (mem.eql(u8, arg, "--import-symbols")) {
|
} else if (mem.eql(u8, arg, "--import-symbols")) {
|
||||||
@ -2134,6 +2141,8 @@ fn buildOutputType(
|
|||||||
linker_import_table = true;
|
linker_import_table = true;
|
||||||
} else if (mem.eql(u8, arg, "--export-table")) {
|
} else if (mem.eql(u8, arg, "--export-table")) {
|
||||||
linker_export_table = true;
|
linker_export_table = true;
|
||||||
|
} else if (mem.eql(u8, arg, "--no-entry")) {
|
||||||
|
linker_no_entry = true;
|
||||||
} else if (mem.eql(u8, arg, "--initial-memory")) {
|
} else if (mem.eql(u8, arg, "--initial-memory")) {
|
||||||
const next_arg = linker_args_it.nextOrFatal();
|
const next_arg = linker_args_it.nextOrFatal();
|
||||||
linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
|
linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
|
||||||
@ -2618,6 +2627,25 @@ fn buildOutputType(
|
|||||||
if (single_threaded == null) {
|
if (single_threaded == null) {
|
||||||
single_threaded = true;
|
single_threaded = true;
|
||||||
}
|
}
|
||||||
|
if (wasi_exec_model) |model| {
|
||||||
|
if (model == .reactor) {
|
||||||
|
if (linker_no_entry != null and !linker_no_entry.?) {
|
||||||
|
fatal("WASI exucution model 'reactor' incompatible with flag '-fentry'. Reactor execution model has no entry point", .{});
|
||||||
|
}
|
||||||
|
if (entry) |entry_name| {
|
||||||
|
if (!mem.eql(u8, "_initialize", entry_name)) {
|
||||||
|
fatal("the entry symbol of the reactor model must be '_initialize', but found '{s}'", .{entry_name});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entry = "_initialize";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (linker_no_entry) |no_entry| {
|
||||||
|
if (no_entry and entry != null) {
|
||||||
|
fatal("combination of '--entry' and `-fno-entry` are incompatible", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (linker_shared_memory) {
|
if (linker_shared_memory) {
|
||||||
if (output_mode == .Obj) {
|
if (output_mode == .Obj) {
|
||||||
fatal("shared memory is not allowed in object files", .{});
|
fatal("shared memory is not allowed in object files", .{});
|
||||||
@ -3465,6 +3493,7 @@ fn buildOutputType(
|
|||||||
.linker_import_symbols = linker_import_symbols,
|
.linker_import_symbols = linker_import_symbols,
|
||||||
.linker_import_table = linker_import_table,
|
.linker_import_table = linker_import_table,
|
||||||
.linker_export_table = linker_export_table,
|
.linker_export_table = linker_export_table,
|
||||||
|
.linker_no_entry = linker_no_entry orelse false,
|
||||||
.linker_initial_memory = linker_initial_memory,
|
.linker_initial_memory = linker_initial_memory,
|
||||||
.linker_max_memory = linker_max_memory,
|
.linker_max_memory = linker_max_memory,
|
||||||
.linker_shared_memory = linker_shared_memory,
|
.linker_shared_memory = linker_shared_memory,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user