Linking errors when the C standard library itself contains Rust code using std (Redox)


#1

Redox’s C library currently contains a mix of C and Rust (it uses newlib, which is C, but the platform code is in Rust). Currently it uses a no_std build since there were issues with using std.

Using std in libc.a worked for C programs, but broke when trying to build a Rust program and link against libc.a. Specifically, some Rust symbols from libc.a cause conflicts, namely rust_eh_personality and several symbols beginning in __rdl_.

Is it possible to prevent these symbols from being exposed, or otherwise deal with the link errors? lto=true seems to prevent some others, like rust_begin_unwind, from being exposed, but these still exist.

Admittedly this is a rather unusual thing to do, but hopefully someone has an idea.


#2

I don’t think there is such thing as “library-private” symbol.

You could probably whip up a script that unpacks the library, renames offending symbols using objcopy --redefine-syms=…, then puts objects back in a library.


#3

A trick I’ve used in the past is to link the intermediate library relocatably. Basically you’re taking all the object files and making one big object file out of them. Since this merges all the code sections it might result in unused code showing up in the final binary.

ld -r --whole-archive libc.a -o libc.o
objcopy --localize-hidden libc.o
ar rcs libc.a libc.o

This assumes the symbols you don’t want have hidden visibility.


#4

Nice trick, but here it seems the symbols have “default” visibility.

There may be some technical reason for this. I know there’s some awkward technical reason why certain internal Rust symbols (like these) need to be no_mangle, but I don’t know what it is.


#5

Yeah, unfortunately, Rust doesn’t have a good story for visiblity of symbols: https://github.com/rust-lang/rust/issues/31509


#6

Relevant issue: https://github.com/rust-lang/rust/issues/34493

That appears to be closed, with a commit fixing it…


#7

It’s possible it’s only fixed in the cdylib case, not the staticlib case.


#8

Thanks for the report! This is something that hasn’t gotten much use in the past so our story isn’t currently great, but I’d very much like to improve it!

I think there’s two ways to attack this. First and foremost if you create a *.so with Rust code inside of it then Rust functions should never get exported (unless you defined them). This shouldn’t have any mangled symbols, allocator, personality, or etc related symbols. I think today there’s bugs w/ allocator symbols, requiring LTO for some unwinding symbols, and the personality symbol as well. I’ll get to that in a sec.

If you’re working with a *.a though then unfortunately it’s much trickier. I think you’ll be forced to use a solution like @jethrogb mentioned because there’s not a lot of control over symbols exposed from a staticlib. By using @jethrogb’s solution though you’ll emulate “hidden symbols are only available for this one library” which is precisely what you’ll want in this case, avoiding the archive from exposing other symbols like libcore’s mangled symbols.

So with that in mind I think the bug to fix here is getting more symbols over to the “hidden” visibility where possible. Do you have a comprehensive list of symbols that are duplicated? The ones I know about are:

  • Allocator-related symbols. We’re almost in a position to define these as hidden symbols but the way dynamic libraries are used in rustc I think prevents us from having a trivial patch. Hopefully isn’t too hard to fix though!
  • rust_begin_unwind - we should make this hidden, probably just using some unstable mechanism the compiler recognizes. I don’t think there’s any catch to making this hidden.
  • rust_eh_personality - We should make this hidden, but like the allocator-related symbols it’s a little tricky. Due to the way dynamic libraries in rustc work it’s basically a similar situation where it’s sort of tricky. Would be a great bug to fix though!

#9

Redox currently has to use a static library since it doesn’t yet support dynamic linking.

__rdl_alloc, __rdl_alloc_excess, __rdl_alloc_zeroed, __rdl_dealloc, __rdl_grow_in_place, __rdl_oom, __rdl_realloc, __rdl_realloc_excess, __rdl_shrink_in_place, __rdl_usable_size, and rust_eh_personality result in symbol conflicts; rust_begin_unwind is also an issue without lto=true.


#10

Oh, it’s also exporting the compiler-rt symbols like __absvdi2. Those may be harder to deal with, since they are declared in a C library.

This is causing symbol conflicts (with the current no_std libc) when I try to rebuild the native rustc for Redox (a rustc that runs under Redox, which is cross-compiled under Linux). That wasn’t happening a while ago… I also need to rebase the rust fork that is using.