Proposal: Add linkname in addition to linksection
Right now, exported and imported symbols in Zig use verbatim the name that is given when using export fn/extern fn or use a much more complex scheme when using @export/@extern.
Problem
So if i want to export (or import) a function with another name than i want to have it in Zig, i have two options:
// import
extern fn SDL_CreateWindow(…) …;
pub const createWindow = SDL_CreateWindow;
// export
export fn SDL_CreateWindow(…) … {}
pub const createWindow = SDL_CreateWindow;
or
// import
pub const createWindow = @extern(
*const fn(…) callconv(.C) …,
.{ .name = "SDL_CreateWindow" },
});
// export
pub fn createWindow() callconv(.) … { … }
comptime {
@export(
createWindow,
.{ .name = "SDL_CreateWindow" },
);
}
Both options feel clunky when i only want to change the name of the emitted symbol, so a C library will have a nice prefixed variant that doesn't clash names, and the Zig variant feels way more ziggy.
Proposal
Thus, i propose introduction of a new keyword named linkname which is valid whereever linksection is valid which changes the way a function symbol is emitted internally:
// import
pub extern fn createWindow(…) linkname("SDL_CreateWindow") …;
// export
pub export fn createWindow(…) linkname("SDL_CreateWindow") … { }
Use Case
Ashet OS
Ashet OS uses dynamic linking as the primary kernel interface, and i keep my symbols nicely namespaced and grouped:
ashet.kernel.process.debug.write_log
which sadly leads to Zig code looking likes this:
pub extern fn @"ashet.kernel.process.debug.write_log"(…) void;
which doesn't really read well, and renaming all symbols manually is tedious and error prone
C Library Wrappers
Using wrappers in Zig is common and having a simple wrapper that would export SDL functions without SDL_ prefix would make the code much nicer to read:
- const window = sdl.SDL_CreateWindow(…);
+ const window = sdl.CreateWindow(…);
apigen
apigen will gain support for something like global_prefix SDL_ which adds this prefix to all exported/imported symbols.
This would allow making nice generated Zig "Headers" which do not expose this prefix on the API surface in Zig, making the usage and the reading/writing way more convenient.
Additional potential drive-by proposal by myself:
Replace extern "libname" fn func () T with extern fn func() linklibrary("libname") T, the extern "libname" always felt out-of-language
Replace
extern "libname" fn func () Twithextern fn func() linklibrary("libname") T, theextern "libname"always felt out-of-language
Could you make that a separate, easy to accept, proposal? That syntax often gets confused with C++'s extern "C".
Replace
extern "libname" fn func () Twithextern fn func() linklibrary("libname") T, theextern "libname"always felt out-of-languageCould you make that a separate, easy to accept, proposal? That syntax often gets confused with C++'s
extern "C".
sure!
What does it boil down to on Windows? What will the linker see?
EDIT: actually, I should probably expand the question to "what happen on each platform". Do we get two exports, one being an alias for the other? Or is the primary definition a local, while linkname emits the global name? Or in MachO lingo, is the primary a private external visible only to the linkage unit?
What does it boil down to on (whatever)? What will the linker see?
Basically what you do is that you have a Zig symbol name and a linker symbol name. Right now, these are the same unless @export is used.
The linker will see whatever is written in linkname() while the programmer sees the other name.
What does it boil down to on (whatever)? What will the linker see?
Basically what you do is that you have a Zig symbol name and a linker symbol name. Right now, these are the same unless
@exportis used.The linker will see whatever is written in
linkname()while the programmer sees the other name.
Nice, ok, so there's no aliasing. In that case I am in favour of this change.
I'm assuming this is similar to how Hare does linking?
export @symbol("glfwInit") fn init() void;
which would link the init function to the glfwInit symbol from GLFW and expose it for use, similarly with this proposal
pub extern fn init() linkname("glfwInit") void;
would do the same, I imagine? I feel like this syntax would be a lot easier to understand than the status-quo.
I'm assuming this is similar to how Hare does linking? </snip> would do the same, I imagine? I feel like this syntax would be a lot easier to understand than the status-quo.
Yep! Exactly!
In AshetOS it would be very nice to have this feature:
pub const syscalls = struct {
/// All syscalls related to generic resource management.
pub const resources = struct {
/// Returns the type of the system resource.
pub const get_type = @extern(*const fn (
SystemResource,
) SystemResource.Type, .{ .name = "ashet_syscalls_resources_get_type" });
/// Returns the current owner of this resource.
pub const get_owners = @extern(*const fn (
SystemResource,
owners_ptr: ?[*]Process,
owners_len: usize,
) usize, .{ .name = "ashet_syscalls_resources_get_owners" });
/// Adds the process to the owners of this resource, so the process
pub const send_to_process = @extern(*const fn (
SystemResource,
Process,
) void, .{ .name = "ashet_syscalls_resources_send_to_process" });
/// Drops the ownership of the resource for the current process.
pub const release = @extern(*const fn (
SystemResource,
) void, .{ .name = "ashet_syscalls_resources_release" });
};
};
It makes the code hard to read using @extern for basically all functions on the system boundary
the llvm backend in the compiler would also a prime benefactor from this pattern https://github.com/ziglang/zig/blob/0.13.0/src/codegen/llvm/bindings.zig#L27
I think it would be nice to consider if this proposal could somehow be made to subsume #9575.