I’m trying to link libc into a wasm project. The library seems to expect main
even though lib.entry
is set to .disabled
. Oh well, just need to stick an empty function in there I guess. If I put the following in a zig file:
export fn main() callconv(.C) c_int {
return 0;
}
The linker pulls in an additional function from __main_void.c
:
// If the user's `main` function expects arguments, the compiler will rename
// it to `__main_argc_argv`, and this version will get linked in, which
// initializes the argument data and calls `__main_argc_argv`.
__attribute__((__weak__, nodebug))
int __main_void(void) {
wasm-objdump indicates that main
has the following signiture:
- type[2] (i32, i32) -> i32
So argc
and argv
are still there somehow. If I add the through a C file instead, I get the correct behavior:
int main(void) {
return 0;
}
1 Like
It’s not clear what lib
is from your description. Is it your wasm artifact? Is it an external library you’re linking your wasm artifact with?
Could you post your full build.zig script for context?
const std = @import("std");
const cfg = @import("./build-cfg.zig");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const arch = if (@hasDecl(@TypeOf(target), "getCpuArch")) target.getCpuArch() else target.result.cpu.arch;
const is_wasm = switch (arch) {
.wasm32, .wasm64 => true,
else => false,
};
const lib = b.addSharedLibrary(.{
.name = cfg.module_name,
.root_source_file = .{ .path = cfg.stub_path },
.target = target,
.optimize = optimize,
});
if (is_wasm) {
lib.rdynamic = true;
}
const imports = .{};
if (@hasDecl(std.Build.Step.Compile, "addModule")) {
// Zig 0.11.0
lib.addModule("exporter", b.createModule(.{
.source_file = .{ .path = cfg.exporter_path },
}));
lib.addModule("module", b.createModule(.{
.source_file = .{ .path = cfg.module_path },
.dependencies = &imports,
}));
} else if (@hasField(std.Build.Step.Compile, "root_module")) {
// Zig 0.12.0
lib.root_module.addImport("exporter", b.createModule(.{
.root_source_file = .{ .path = cfg.exporter_path },
}));
lib.root_module.addImport("module", b.createModule(.{
.root_source_file = .{ .path = cfg.module_path },
.imports = &imports,
}));
if (is_wasm) {
// WASM needs to be compiled as exe
lib.kind = .exe;
lib.linkage = .static;
lib.entry = .disabled;
}
}
if (cfg.use_libc) {
lib.linkLibC();
if (is_wasm) {
// add empty function expected by libc
lib.addCSourceFile(.{ .file = .{ .path = "./main.c" }, .flags = &.{} });
}
}
const wf = b.addWriteFiles();
wf.addCopyFileToSource(lib.getEmittedBin(), cfg.output_path);
wf.step.dependOn(&lib.step);
b.getInstallStep().dependOn(&wf.step);
}
1 Like
If you’re targeting wasm32-wasi
, you can set lib.wasi_exec_model = .reactor
. This will export a small _initialize
function which must be called before any other exported symbols are accessed, but not require your artifact to export any main
symbol.
3 Likes