Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the correct crt*.o files when linking musl targets. #50105

Merged
merged 6 commits into from
May 11, 2018

Conversation

mixi
Copy link
Contributor

@mixi mixi commented Apr 20, 2018

This is supposed to support optionally using the system copy of musl
libc instead of the included one if supported. This currently only
affects the start files, which is enough to allow building rustc on musl
targets.

Most of the changes are analogous to crt-static.

Excluding the start files is something musl based distributions usually patch into their copy of rustc:

For third-party distributions that not yet carry those patches it would be nice if it was supported without the need to patch upstream sources.

Reasons

What breaks?

Some start files were missed when originally writing the logic to swap in musl start files (gcc comes with its own start files, which are suppressed by -nostdlib, but not manually included later on). This caused #36710, which also affects rustc with the internal llvm copy or any other system libraries that need crtbegin/crtend.

How is it fixed?

The system linker already has all the logic to decide which start files to include, so we can just defer to it (except of course if it doesn't target musl).

Why is it optional?

In #40113 it was first tried to remove the start files, which broke compiling musl-targeting static binaries with a glibc-targeting compiler. This is why it eventually landed without removing the start files. Being an option side-steps the issue.

Why are the start files still installed?

This has the nice side-effect, that the produced rust-std-* binaries can still be used by on a glibc-targeting system with a rustc built against glibc.

Does it work?

With the following build script (using musl-cross-make): https://shadowice.org/~mixi/rust-musl/build.sh, I was able to cross-compile a musl-host musl-targeting rustc on a glibc-based system. The resulting binaries are at https://shadowice.org/~mixi/rust-musl/binaries/. This also requires #50103 and #50104 (which are also applied to the branch the build script uses).

@pietroalbini pietroalbini added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Apr 20, 2018
@pietroalbini
Copy link
Member

Thanks for this PR! Assigned a reviewer to it.

@TimNN
Copy link
Contributor

TimNN commented Apr 24, 2018

Ping from triage @Mark-Simulacrum ! This PR needs your review.

@mixi
Copy link
Contributor Author

mixi commented Apr 25, 2018

I noticed (some days ago, but pushed some minutes ago, in case github using the commit date confused anyone) that bootstrapping with host = build was not possible from the binaries I produced from a glibc system, which the two additional commits fix. (I incuded them in this pull request because they depend on this change)

I also updated the build script I used for testing to work on systems with http_parser.h in /usr/include while cross-compiling.

@@ -586,6 +586,29 @@ impl<'a> Builder<'a> {
extra_args.push_str(&s);
}

let mut target_features = Vec::new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep this logic inside rustc.rs instead of through RUSTFLAGS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted in the commit message some tools are built with a compiler requirement of stage = 0 and build = host, which causes them not to go through rustc.rs:

pub fn rustc(&self, compiler: Compiler) -> PathBuf {
if compiler.is_snapshot(self) {
self.initial_rustc.clone()
} else {
self.sysroot(compiler).join("bin").join(exe("rustc", &compiler.host))
}
}

rust/src/bootstrap/lib.rs

Lines 1201 to 1203 in 25749ad

pub fn is_snapshot(&self, build: &Build) -> bool {
self.stage == 0 && self.host == build.build
}

When deciding how to fix it (I could as well try to make it go through rustc.rs even for stage = 0 and build = host, though my first attempt at it broke the build completely) I was going by the comment at the start of rustc.rs that suggests this is the way forward for some parts of rustc.rs:

//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe all invocations of rustc are intended to go through rustc.rs -- the code you point to doesn't quite indicate to me that rustc is being invoked directly. I personally prefer using the rustc shim over rustflags when possible since it's easier to read and interpret (at least for me).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems I was misreading the code and you are right. The result of the function I linked seems to mainly be used to set the environment variable RUSTC_REAL, while setting RUSTC to the shim.

This leaves only the logic in rustc.rs that disables most of the flag passing if --target wasn't passed as culprit for my build failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done properly now in the newest version this pull request.

@Mark-Simulacrum
Copy link
Member

r? @alexcrichton

Not sure who should review this, but not familiar enough with our management of CRTs on various platforms myself.

@bors
Copy link
Contributor

bors commented Apr 26, 2018

☔ The latest upstream changes (presumably #50228) made this pull request unmergeable. Please resolve the merge conflicts.

@alexcrichton
Copy link
Member

Thanks for the PR @mixi!

I wonder, is a new feature needed here? Could we avoid passing the objects if crt-static is turned off? If you're dynamically linking the CRT you're typically using a full sysroot anyway that has all the startup objects and such

@mixi
Copy link
Contributor Author

mixi commented Apr 28, 2018

The problem (#36710) also affects static linking. See my example poc with -crt-static:

$ cargo build
   Compiling cc v1.0.13
   Compiling test-crtbegin v0.1.0 (file:///home/mixi/data/installfiles/rust/test-crtbegin)
error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-Wl,--eh-frame-hdr" "-Wl,-(" "-m64" "-nostdlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/crt1.o" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/crti.o" "-L" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.1dugthth7hgw6f8k.rcgu.o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.1y16o1qfye96o7m0.rcgu.o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.3rngp6bm2u2q5z0y.rcgu.o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.4oc10dk278mpk1vy.rcgu.o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.4xq48u46a1pwiqn7.rcgu.o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.8xzrsc1ux72v29j.rcgu.o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.oa3rad818d8sgn4.rcgu.o" "-o" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac.crate.allocator.rcgu.o" "-Wl,--gc-sections" "-no-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "-L" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps" "-L" "/home/mixi/data/installfiles/rust/test-crtbegin/target/debug/build/test-crtbegin-41b077543b1e2255/out" "-L" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib" "-Wl,-Bstatic" "-Wl,--whole-archive" "-l" "test" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "-l"
"stdc++" "-Wl,--no-whole-archive" "-Wl,--start-group" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/libstd-d2de561f734e7180.rlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/libpanic_unwind-8b065522657ddcab.rlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/libunwind-812c97ffb046a117.rlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/liballoc_system-65736dae08b20ddb.rlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/liblibc-9ae9616519ec780e.rlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/liballoc-c91b138db9486771.rlib" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/libcore-ac22a995c23de41b.rlib" "-Wl,--end-group" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/libcompiler_builtins-eecc1bbba99ad142.rlib" "-static" "-Wl,-Bdynamic" "/home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/crtn.o" "-Wl,-)"
  = note: /usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../lib/libstdc++.a(system_error.o): In function `_GLOBAL__sub_I_system_error.cc':
          /home/buildozer/aports/main/gcc/src/gcc-6.4.0/libstdc++-v3/src/c++11/system_error.cc:70: undefined reference to `__dso_handle'
          /home/buildozer/aports/main/gcc/src/gcc-6.4.0/libstdc++-v3/src/c++11/system_error.cc:71: undefined reference to `__dso_handle'
          /home/mixi/data/installfiles/rust/test-crtbegin/target/debug/build/test-crtbegin-41b077543b1e2255/out/libtest.a(test.o): In function `__static_initialization_and_destruction_0(int, int)':
          /usr/include/c++/6.4.0/iostream:74: undefined reference to `__dso_handle'
          /home/mixi/data/applications/rust/nightly/lib/rustlib/x86_64-unknown-linux-musl/lib/libstd-d2de561f734e7180.rlib(std-d2de561f734e7180.std4-e9907248abb27c2ab1ea0b1a18ff25a0.rs.rcgu.o):(.data._rust_extern_with_linkage___dso_handle.llvm.1663720274966436130+0x0): undefined reference to `__dso_handle'
          /usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../x86_64-alpine-linux-musl/bin/ld: /home/mixi/data/installfiles/rust/test-crtbegin/target/debug/deps/test_crtbegin-009798609cf383ac: hidden symbol `__dso_handle' isn't defined
          /usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../x86_64-alpine-linux-musl/bin/ld: final link failed: Bad value
          collect2: error: ld returned 1 exit status
$ RUSTFLAGS="-C target-feature=-crt-included" cargo build
   Compiling cc v1.0.13
   Compiling test-crtbegin v0.1.0 (file:///home/mixi/data/installfiles/rust/test-crtbegin)
    Finished dev [unoptimized + debuginfo] target(s) in 2.95 secs
$ file ./target/debug/test-crtbegin
./target/debug/test-crtbegin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
$ ./target/debug/test-crtbegin
N = 123

One thing that could be done on top of this is to default to -crt-included with -crt-static (as -crt-static means we'll require the linker to know of the target libc at any rate).

Another option would be to completely fix the included start files, including crtbegin.o and crtend.o (or crtbeginS.o and crtendS.o depending on whether we are producing a static-pie). These wouldn't even need to be copied when compiling as gcc's should be compatible. This would mean including and testing a lot more logic (see https://git.musl-libc.org/cgit/musl/tree/tools/musl-gcc.specs.sh for a more-or-less complete list of startfiles to include).

A problem musl-gcc's startfile logic has is that it can't know whether the distributions toolchain defaults to pie, so it'll always assume non-pie except if explicitly passed -pie. This means compilation usually fails on default-pie toolchains except if either -pie or -no-pie is passed. It may be possible to fix it in rustc's logic though as a compiler should already know whether it's required to produce pie/pic objects.

I'm not aware of any other problems with just replacing the startfiles, but that doesn't mean there aren't any. Musl's upstream usually recommends against using the musl-gcc wrapper for anything serious (obviously if there are depedencies or C++ code, but I'm not sure these are the only cases where it breaks), so I'm not certain swapping out startfiles is enough.

@alexcrichton
Copy link
Member

@mixi hm true but so one point is that adding another stable feature like crt-included is quite a signficant step and can't be taken lightly. It sounds like not passing the object files with -crt-static will solve the issue above in the case where you pass -C target-feature=-crt-static but it won't solve the issue by default, right? Is that "good enough" for now?

@bors
Copy link
Contributor

bors commented Apr 29, 2018

☔ The latest upstream changes (presumably #50188) made this pull request unmergeable. Please resolve the merge conflicts.

Bootstrap requires serde_derive, which needs proc-macro crate types, so
it won't work with crt-static.
This fixes rust-lang#36710 with +crt-static. We only need to add crtbegin.o and
crtend.o as we only do static linking with the bundled start files and
there is no static-pie support in rustc yet.
@mixi
Copy link
Contributor Author

mixi commented May 1, 2018

The latest commits now do a combination of the proposed alternatives:

  • For -crt-static the native linker is used (For the shared objects, which may be produced with -crt-static because of pie or making shared libraries, the included startfiles were wrong anyway, and some other things as putting the correct loader path into the executable didn't work without a native linker).
  • For +crt-static we include the native compiler's crtbegin.o/crtend.o (not crtbeginS.o/crtendS.o as we only ever link static binaries in this case and link.rs disables pie with crt-static for now). This is the same behaviour as musl-gcc, so it should work as well as musl-gcc does on non-musl linkers.

With this it should be "good enough" for now, even though I'd like to some day support using the system-provided libc.a and startfiles even when linking statically.

@mixi mixi changed the title Add target-feature=[+-]crt-included. Use the correct crt*.o files when linking musl targets. May 1, 2018
@alexcrichton
Copy link
Member

@mixi could we avoid passing objects entirely if -crt-static is active? In that case it's probably best to let the native linker completely take care of linking in the startup objects as it's likely to know best compared to us

@mixi
Copy link
Contributor Author

mixi commented May 1, 2018

@alexcrichton that is exactly what this patch does. For -crt-static it doesn't pass any crt*.o objects or -nostdlib to the linker anymore (in link.rs all the *_crt_* variables only take effect if sess.crt_static() is true). The last commit fixing crt{begin,end}.o-issues only affects +crt-static (as I wanted to fix the bug for static linking too).

@alexcrichton
Copy link
Member

Ah ok I see! If the extra code here is fixing bugs, can tests be added for the fixes?

@mixi
Copy link
Contributor Author

mixi commented May 5, 2018

Ah ok I see! If the extra code here is fixing bugs, can tests be added for the fixes?

Yes, of course. Though it was a bit difficult to decide how to test for it without compiling C++ code. The current test checks for the internal symbol __dso_handle, which, as far as I could find out, all but msvc targets use. I only tested it on x86_64 musl and glibc though.

@alexcrichton
Copy link
Member

Looking over this again, won't this run a risk of breaking the distributed musl target? We're not shipping crtend/crtbegin files, so by default those targets are going to start requiring them, right?

@mixi
Copy link
Contributor Author

mixi commented May 7, 2018

Yes, they are required, but both all glibc-targeting and all musl-targeting compilers already ship their own crtend/crtbegin files (e.g. here) and they are compatible. So as long as there is a compiler for the target (which is already required, it's just not required to be a musl-targeting compiler) these files exist and the compiler will be able to link in its copy of crtbegin.o/crtend.o.

This means it shouldn't™ break any case that currently works.

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels May 10, 2018
@kennytm
Copy link
Member

kennytm commented May 10, 2018

"LNK1181 Cannot open input file" in the test run-make-fulldeps\issue-36710 on x86_64-msvc. Not sure if legit. The warning D9035 (option 'o' has been deprecated) may be relevant.

---- [run-make] run-make-fulldeps\issue-36710 stdout ----
	
error: make failed
status: exit code: 2
command: "make"
stdout:
------------------------------------------
'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\cl.exe' foo.cpp -c -o /c/projects/rust/build/x86_64-pc-windows-msvc/test/run-make-fulldeps/issue-36710.stage2-x86_64-pc-windows-msvc/libfoo.o
foo.cpp
'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\lib.exe' -nologo -out:`cygpath -w /c/projects/rust/build/x86_64-pc-windows-msvc/test/run-make-fulldeps/issue-36710.stage2-x86_64-pc-windows-msvc/foo.lib` /c/projects/rust/build/x86_64-pc-windows-msvc/test/run-make-fulldeps/issue-36710.stage2-x86_64-pc-windows-msvc/libfoo.o
LINK : fatal error LNK1181: cannot open input file 'C:/projects/rust/build/x86_64-pc-windows-msvc/test/run-make-fulldeps/issue-36710.stage2-x86_64-pc-windows-msvc/libfoo.o'
------------------------------------------
stderr:
------------------------------------------
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
cl : Command line warning D9035 : option 'o' has been deprecated and will be removed in a future release
make: *** [../tools.mk:115: /c/projects/rust/build/x86_64-pc-windows-msvc/test/run-make-fulldeps/issue-36710.stage2-x86_64-pc-windows-msvc/foo.lib] Error 157
------------------------------------------
thread '[run-make] run-make-fulldeps\issue-36710' panicked at 'explicit panic', tools\compiletest\src\runtest.rs:3033:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

@kennytm kennytm added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 10, 2018
@mixi
Copy link
Contributor Author

mixi commented May 11, 2018

It was an incompatibility between GCC and MSVC CLI option syntax (and of course me just assuming GCC syntax in the test). I dusted off an old windows to actually test it and the test should now be fixed on MSVC for good.

@alexcrichton
Copy link
Member

@bors: r+

@bors
Copy link
Contributor

bors commented May 11, 2018

📌 Commit 490d050 has been approved by alexcrichton

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels May 11, 2018
@bors
Copy link
Contributor

bors commented May 11, 2018

⌛ Testing commit 490d050 with merge 0cd4650...

bors added a commit that referenced this pull request May 11, 2018
Use the correct crt*.o files when linking musl targets.

This is supposed to support optionally using the system copy of musl
libc instead of the included one if supported. This currently only
affects the start files, which is enough to allow building rustc on musl
targets.

Most of the changes are analogous to crt-static.

Excluding the start files is something musl based distributions usually patch into their copy of rustc:
  - https://github.com/alpinelinux/aports/blob/eb064c8/community/rust/musl-fix-linux_musl_base.patch
  - https://github.com/voidlinux/void-packages/blob/77400fc/srcpkgs/rust/patches/link-musl-dynamically.patch

For third-party distributions that not yet carry those patches it would be nice if it was supported without the need to patch upstream sources.

## Reasons
### What breaks?
Some start files were missed when originally writing the logic to swap in musl start files (gcc comes with its own start files, which are suppressed by -nostdlib, but not manually included later on). This caused #36710, which also affects rustc with the internal llvm copy or any other system libraries that need crtbegin/crtend.

### How is it fixed?
The system linker already has all the logic to decide which start files to include, so we can just defer to it (except of course if it doesn't target musl).

### Why is it optional?
In #40113 it was first tried to remove the start files, which broke compiling musl-targeting static binaries with a glibc-targeting compiler. This is why it eventually landed without removing the start files. Being an option side-steps the issue.

### Why are the start files still installed?
This has the nice side-effect, that the produced rust-std-* binaries can still be used by on a glibc-targeting system with a rustc built against glibc.

## Does it work?
With the following build script (using [musl-cross-make](https://github.com/richfelker/musl-cross-make)): https://shadowice.org/~mixi/rust-musl/build.sh, I was able to cross-compile a musl-host musl-targeting rustc on a glibc-based system. The resulting binaries are at https://shadowice.org/~mixi/rust-musl/binaries/. This also requires #50103 and #50104 (which are also applied to the branch the build script uses).
@bors
Copy link
Contributor

bors commented May 11, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: alexcrichton
Pushing 0cd4650 to master...

@bors bors merged commit 490d050 into rust-lang:master May 11, 2018
@BurntSushi
Copy link
Member

BurntSushi commented May 13, 2018

I don't have a ton of context on this change, but my i686 nightly musl build started failing last night: https://travis-ci.org/BurntSushi/ripgrep/jobs/378266594 --- Note that my x86_64 musl build is still working fine.

@alexcrichton
Copy link
Member

Oops sorry @BurntSushi! I've posted a revert of this at #50709

The issue I think is that crtbegin/crtend aren't present for i686 (when cross-compiling), so the new arguments to the linker here are causing the compilation to fail.

@BurntSushi
Copy link
Member

Ah awesome, thanks! And no worries. This is why I test on nightly CI. :-) It happens!

bors added a commit that referenced this pull request May 19, 2018
Revert #50105 until regression is fixed

Discovered at #50105 (comment) it looks like this caused a regression with i686 musl, so let's revert in the meantime while a fix is worked out
bors added a commit that referenced this pull request Jun 2, 2018
Fix building rustc on and for musl hosts.

This fixes all problems I had when trying to compile rustc on a musl-based distribution (with `crt-static = false` in `config.toml`).

This is a fixed version of what ended up being #50105, making it possible to compile rustc on musl targets.

The differences to the old (now merged and subsequently reverted) pull request are:
 - The commit (6d9154a) that caused the regression for which the original commits were reverted in #50709 is left out. This means the corresponding bug #36710 is still not fixed with `+crt-static`.
 - The test for issue 36710 is skipped for musl targets (until the issue is properly fixed).
 - Building cargo-vendor if `crt-static = false` is needed was broken (cargo-vendor links to some shared libraries if they exist on the system and this produces broken binaries with `+crt-static`)

CC @alexcrichton
djrenren pushed a commit to djrenren/compiletest that referenced this pull request Aug 26, 2019
Revert #50105 until regression is fixed

Discovered at rust-lang/rust#50105 (comment) it looks like this caused a regression with i686 musl, so let's revert in the meantime while a fix is worked out
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants