sunfishcode / mustang

Rust programs written entirely in Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Linking with C libraries

sunfishcode opened this issue · comments

It's currently not safe to link with C libraries, because Mustang doesn't initialize the C runtime. I've now added a point about this to the README.md. But is there a way Mustang could make this work?

__libc_start_main appears to be a relatively stable interface, that looks like it might work in both glibc and musl. Mustang could call it and pass a no-op main to let libc initialize itself, but the tricky part is, it calls exit when it's done.

Since mustang defines its own exit, one option here would be for mustang to call __libc_start_main and "catch" the exit. But then, what should it do? GLIBC's __libc_start_main is annotated with __attribute__ ((noreturn), and the C interface to exit uses __attribute__((noreturn)), so we can't have exit return. Another option would be longjmp, but we can't currently do setjmp in Rust. Another would be to panic and use catch_unwind, but a panic across an FFI boundary is UB.

Another idea is to create a new thread, call __libc_start_main on it, and then terminate the thread once it calls exit. But, Mustang doesn't support threads yet. It's tempting to just call libc's pthread_create et al in that case, however we can't safely call C code until the C runtime is initialized, which is what we're trying to do :-}.

I'm thinking longjmp may be the best option for now. A C-library compatibility mode would be all about having C code in the process, so having a small amount of extra C code to call setjmp would be fine.

__libc_start_main appears to be a relatively stable interface

__libc_start_main is part of the Linux Standard Base specification: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib---libc-start-main-.html This means that you can rely on the current signature.

Mustang could call it and pass a no-op main to let libc initialize itself, but the tricky part is, it calls exit when it's done.

Can't it just pass the real main to libc? If you aren't using libc, c-scape could provide it's own __libc_start_main that calls all .init_array functions and then main.

__libc_start_main appears to be a relatively stable interface

__libc_start_main is part of the Linux Standard Base specification: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib---libc-start-main-.html This means that you can rely on the current signature.

Great, thanks for finding that!

Mustang could call it and pass a no-op main to let libc initialize itself, but the tricky part is, it calls exit when it's done.

Can't it just pass the real main to libc? If you aren't using libc, c-scape could provide it's own __libc_start_main that calls all .init_array functions and then main.

That would work, but I'd prefer mustang avoid calling through C interfaces when it's mustang's own Rust code on both sides. I'm expecting the setjmp approach will work well in practice, and it'll mean that initializing the C runtime is something we can include as an option, and it doesn't change the organization of the code.