zig
zig copied to clipboard
Allow exporting symbols from executable
At least it is possible in linux, windows... (see ld.so)
But zig just ignore the export and @export when build-exe
That's not true. @export works in every output mode, you're probably using it wrong.
@LemonBoy
export fn t() void {}
comptime {
@export(t, .{ .name="tx"});
}
pub fn main() void {}
command:
$ zig build-exe x.zig
$ nm -D x
nm: x: no symbols
$ objdump -T x
x: file format elf64-x86-64
objdump: x: not a dynamic object
DYNAMIC SYMBOL TABLE:
no symbols
same thing happens in windows target
Image Size: 48000 Data dir: 00000000 00000000 (Export) Data dir: 0003E977 0000003C (Import) Data dir: 00000000 00000000 (Resource)
PS: It works if i export symbols from c (dllexport)
@codehz you created a static executable; use zig build-exe -dynamic to create a dynamic executable.
Edit: uhh; is -dynamic broken when not linking against anything?
use
zig build-exe -dynamicto create a dynamic executable.
no effect at all, and in windows, there is no dynamic executable
no effect at all
yes, see my edit above: possible workaround is linking libc -lc as well.
possible workaround is linking libc
-lcas well.
nope, I can build it without -lc, and adding -lc has not make t and tx exported
x: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 Base __gmon_start__
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __errno_location
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.4 __stack_chk_fail
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 __tls_get_addr
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 abort
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 close
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dl_iterate_phdr
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 environ
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 flock
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getenv
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 isatty
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memset
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 mmap
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 munmap
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.4 openat
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 read
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sched_yield
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigaction
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 write
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __fxstat
Here's your exported symbol:
zig build-exe x.zig
readelf -s foo |& grep tx
If you want to have your symbols exported in the dynamic symbol table:
zig build-exe -rdynamic x.zig
objdump -T foo | grep tx
You're welcome.
exported in the dynamic symbol table:
Unfortunately, the binary produced by this method cannot be loaded by dlopen
./x: cannot dynamically load executable
and I also tried this for target x86_64-windows-gnu, not working either
Unfortunately, the binary produced by this method cannot be loaded by dlopen
Because it's a binary and not a shared library? This is starting to sound a lot like a XY problem, do you mind explaining what are you trying to do and what have you tried?
@LemonBoy Ok, now the origin problem is I want to do this on windows: let plugin (dll) to load symbol from exe, it has been tested in a plain c project
- start Main.exe
- main.exe load plugin dll (at runtime, so no circular dependency)
- plugin can call exported function from main exe
(Yes, technically speaking, this is not the best method, the "correct" method is let the main program initialize plugin and provides an interface.)
Heya @codehz!
I did this as well at work and i now figured that i'm in devils kitchen. This approach is easy and convenient, but will make your life horrible as soon as you ever want to rename your executable.
Because then you have to recompile all plugins, every time you change the executable name. And you cannot create or deploy staging/testing builds, as you always have to overwrite your production executable (Imagine you have System.exe in production, you want to test if your changes to an API were successful, so you deploy System.fixed.exe. But your plugins will still load and call System.exe instead.)
But still, this should be a possible use case to compile LoadLibrary-loadable executables
@codehz it sounds like you're looking for the zig equivalent of -Wl,-E; I think that's only a feature of dynamic linked ELF executables?
I'm not sure if PE executables can do that: the ld flag --export-all-symbols seems to only be supported for DLLs, not executables?
I'm not sure if PE executables can do that: the ld flag
--export-all-symbolsseems to only be supported for DLLs, not executables?
I just simply add __declspec(dllexport) in c source, and it work like a charm..
I just simply add __declspec(dllexport) in c source, and it work like a charm..
Then -fdll-export-fns is your friend, it marks all the exported declarations as dllexport.
I'm no Windows expert but this all or nothing option isn't granular enough, what about an extra field in the @export options?
What is the actual issue here? I've read through the discussion and I'm honestly quite confused.
I think this is because zig does not support -Wl,-E. In C language, the executable file compiled with -Wl,-E flag contains symbols.
The -rdynamic C compiler flag, as mentioned by @LemonBoy above, passes -E to the linker - i.e. it is equivalent to -Wl,-E.
I try test this. https://github.com/brickBrackBro/lua/blob/main/build.zig
zig build -Dshared=true will generate lua and lua.so file. the symbols are in lua.so file.
zig build will only generate one lua file and no symbols.
zig build -Dshared=truewill generateluaandlua.sofile. the symbols are inlua.sofile.
zig buildwill only generate oneluafile and no symbols.
Well, yeah, I don't see you passing -rdynamic anywhere in that build script. So the behavior is as expected.
zig build -Dshared=truewill generateluaandlua.sofile. the symbols are inlua.sofile.zig buildwill only generate oneluafile and no symbols.Well, yeah, I don't see you passing
-rdynamicanywhere in that build script. So the behavior is as expected.
I don't know where to set -rdynamic in build.zig file. Can you give me some suggest. Thanks.
std.Build.Step.Compile has an rdynamic field that you can set.
std.Build.Step.Compilehas anrdynamicfield that you can set.
Nice job! Your solution is great and solved my problem. Thank you very much for your help.
I'm going to close this as I don't think there's a Zig bug here, but it's honestly a bit confusing to decode this issue.
If I'm wrong, please comment with a clear explanation and I'll reopen.