Change:
Hi, I am trying to build a binary written in rust static linked with a c++ library. I follow the instructions in , but end up with an error Following is a simple example: I build the static library using following command: And build the binary using following command: It failed with error: I can build the binary successfully when linking dynamically. Am I missing any thing here? Any help is appreciated.
You are required to define two functions and one symbol in order to use global objects in your C++ files: symbol, and functions. These are part of Itanium C++ ABI and thus rustc has no responsibility of providing any of the symbols or calling any of the functions. As an aside, that C++ snippet probably does not do what you expect it to do.
I don't get it, does it mean I can't static link to libraries that is written in C++ and uses global variables? Or is there an extra library that I should link to? This is just a simple example to reproduce the failure. And it actually works.
Not without extra effort on your part, no. Remember that you’re mixing the GNU C runtime (which defines the _dsohandle in an object file somewhere at ) and MUSL runtime which probably has its own internal symbols and stuff to do that. Anything related to linking more libraries would likely not work as it would introduce symbol conflict between runtimes. Instead, either build your C++ object targetting MUSL, or use the target on the rust side.
Ah yes indeed, if is a GNU gcc then linking those object files into a Rust musl program won't work (as you're seeing with weird symbol errors), unfortunately.
From what I can tell, this isn't a bug in rustc. Closing.
After keeping my CPU warm for a while by compiling: musl binutils libunwind g++ (targeting musl) I now have in my hands a musl-compatible and and, by compiling a modified rustc (only to stage2 thankfully ) and making the link command be: I am able to create a static binary containing Rust and C++ code (in my case I was trying to compile the example code from which uses a C++ driver). So, this is possible ! Now, not everyone is going to go to the trouble I did above, but there are good use cases for static binaries (for one, they actually run everywhere, unlike "mostly static" binaries that are still glibc version dependent), and it would be nice to be able to modify the "early objects" and "late objects" lists that get passed to the linker. or aren't sufficient since they don't control the order the extra libraries are to the link command (also I'm not sure they work for files but I could easily make a if this was going to work).
is it possible you could share this modified rustc? I am currently working on a cross compiling toolchain and I have the crt object files you mention... but of course rust fails on a C++ library.
Not from this PC, but the only change I made (I think ) was to modify to add the and at the appropriate points (and to copy those files to the same path rustc installs it's copy of etc). There are a few alternatives I've considered since: Rust brings it's own C++ runtime objects (as it does with C runtime on musl) Seems dangerous, I doubt libc++ from GNU and from LLVM have compatible run-time objects Rust allows the linker to add the run-time objects (as it does with the target) I'm not sure it's possible to ask the linker to add the run-time objects and not add the rest of the standard libraries It's not clear how to work out whether to include the C++ run-time, maybe include it unilaterally? Add config flags to (and Cargo's config - ) to specify extra run-time objects to be included in the link commands Seems like a decent solution - given that I don't think rustc can assume the provenance of the like it can with the musl (though even that is a little dubious in my mind) Check if simply creating from and then adding is sufficient If this works it's a pretty good workaround, but doesn't feel like a good long-term solution (what if you link in two crates that both link to C++ libraries... you only want the runtime objects once) Use a custom linker wrapper script which adds these objects to the objects rustc asks for Sensible for a cross compiler, not so nice for a native compiler - I'd quite like to help resolve this, who should I be working with/talking to? Do we need an RFC?
er sorry this is pretty out of cache for me, but if you've got some linking questions or whatnot feel free to hit me up on IRC!
Fair enough, you were the last name I recognised as a core dev to comment. I'll shout in IRC at some point when I've got some time.
For whom it may concern, I used the last suggestion of creating a custom linker script. After some troubleshooting, this is what I came up with that works: The first if removes Bdynamic args, which is for some reason still included in static linking. Then, it adds after it sees , and similar for . For some reason a bunch of compiler symbols were still missing after I those options, so I also to ensure other libraries that were missing were included. It might be a bit sloppy, but that is what worked for me.
Wild speculation, but were the missing symbols related to and similar? If so, you might consider using LLVM's libunwind, which is what the rustc build uses on musl platforms. You'll need libc since libc++ depends on it, but you shouldn't (need to) be dependent on any gcc libraries. You could also link directly to rather than using if you prefer.
Actually, the missing symbols were like which required for sure. TBH, I do not even know if I needed , but I noticed it was always included with so I figured I would just include it because I was tired of waiting an hour too see if it would succeed :)
Did you add support to your musl build? I used to add self-contained atomics (rather than depend on GCC's ones). Yeah, I know the feeling about the multiple hour wait for bootstrap compilations :grinning:...
hmm... that the library you proposed does look like a good alternative especially considering that that was basically all I was missing after fixing crtbegin/crtend issues. I think I will give it a try within the next weeks--its a great suggestion.
Since for this issue has , should this really be closed? With nightly () I still have to use a linker script workaround as .
AFAIK it's currently not possible to make it work when linking statically but it's resolved when CRT linking dynamically (). Also you probably wanted to ping
Oops I didn't realize bors was a bot. Hmm ok maybe can confirm.
The original merge request fixed this problem even for static linking, but was subsequently reverted in (which didn't reopen this bug) because it regressed for toolchains that previously worked (using on a 64-bit gcc without having the proper gcc startfiles installed). The code that landed in the end through merge request only fixes the dynamic linking case because there we can be sure the linker is a proper compiler targeting musl libc or it already didn't work before. It should be noted that your linked C++ code most likely still needs to be built with a musl-targeting compiler, not either a hack like or a straight out glibc targeting compiler, to work when linked into a musl-based binary. So in summary: Yes this bug should still be open.
Stumbled upon this issue when trying something similar myself, and here are some things I learned. I was about to recompile gcc and libstdc++ against musl, but was reminded that alpine uses musl. In fact you can find a libstdc++.a (and the .so if you need) after installing g++ on a alpine container. I also used this container to compile a c++ library because g++ with musl. I'm still fighting to make it work, but this surely helped.
You'll need to find a version of alpine where the musl version (the one underlying the libc++) is compatible with (likely "equal to") the version that your rustc's stdlib was compiled against. If you can get that right, then using alpine's C++ toolchain should work - good luck in your investigations!
I've managed to compile C++ library as part of my workspace using the answer in the second comment. I used with musl target and defined these functions in the main file of the C++ code. To my understanding these are needed to be able to define global variables which have to be initialised before the main function and cleaned after main function. Leaving these functions empty will not properly free global resources but can be used as a quick hack. Use at your own risk.
Reopening as per (feel free to ping me or another team member if bugs need to be reopened!)
This seems to work fine now, as long as is used (or autodetected), so I've opened to reenable the test.
I think that means that a self-contained link will still not work for a C++ binary (even if the C++ bit is built with musl libc)? Is that just inevitable?
For now, yes, that still won't work.
CRT objects are not guaranteed to be compatible between different compilers, so the answer is yes.

Discussion:
set_compiler(&mut cfg, Language::CPlusPlus, target, config, build); true } else { <del> false </del> <ins> // Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars). cfg.try_get_compiler().is_ok() </ins> }; // for VxWorks, record CXX compiler which will be used in lib.rs:linker()

// Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. <del> if !builder.config.dry_run && suite == "run-make-fulldeps" { </del> <ins> if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { </ins> cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx")

<del> -include ../tools.mk # ignore-musl all: foo $(call RUN,foo) foo: foo.rs $(call NATIVE_STATICLIB,foo) $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) $(TMPDIR)/libfoo.o: foo.cpp $(call COMPILE_OBJ_CXX,$@,$<) </del>

<del> #include <stdint.h> struct A { A() { v = 1234; } ~A() { v = 1; } uint32_t v; }; A a; extern "C" { uint32_t get() { return a.v; } } </del>

<del> // Tests that linking to C++ code with global destructors works. // For linking libstdc++ on MinGW #![cfg_attr(all(windows, target_env = "gnu"), feature(static_nobundle))] extern { fn get() -> u32; } fn main() { let i = unsafe { get() }; assert_eq!(i, 1234); } </del>

<ins> include ../../run-make-fulldeps/tools.mk # ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) all: foo $(call RUN,foo) foo: foo.rs $(call NATIVE_STATICLIB,foo) $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) $(TMPDIR)/libfoo.o: foo.cpp $(call COMPILE_OBJ_CXX,$@,$<) </ins>

<ins> #include <stdint.h> struct A { A() { v = 1234; } ~A() { v = 1; } uint32_t v; }; A a; extern "C" { uint32_t get() { return a.v; } } </ins>

<ins> // Tests that linking to C++ code with global destructors works. // For linking libstdc++ on MinGW #![cfg_attr(all(windows, target_env = "gnu"), feature(static_nobundle))] extern { fn get() -> u32; } fn main() { let i = unsafe { get() }; assert_eq!(i, 1234); } </ins>