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

Coverage is not generated when using lld as linker #71233

Closed
maurer opened this issue Apr 17, 2020 · 8 comments · Fixed by #71234
Closed

Coverage is not generated when using lld as linker #71233

maurer opened this issue Apr 17, 2020 · 8 comments · Fixed by #71234
Labels
A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@maurer
Copy link
Contributor

maurer commented Apr 17, 2020

Attempting to build a coverage Rust binary with

rustc -C link-args="--coverage -fuse-ld=lld" -C linker=clang -Z profile hello.rs -g -C codegen-units=1
./hello

will not actually generate any .gcda files.

Both of

rustc -C link-args="--coverage -fuse-ld=bfd" -C linker=clang -Z profile hello.rs -g -C codegen-units=1
./hello
rustc -C link-args="--coverage -fuse-ld=gold" -C linker=clang -Z profile hello.rs -g -C codegen-units=1
./hello

will produce appropriate output.

I was able to trace this down to an issue with .ctors / .init_array incompatibility. Specifically, ld.bfd and ld.gold will munge sections to deal with the presence of both .ctors and .init_array in the same binary; lld will not.

Rust is currently implicitly using the deprecated .ctors section in its output format, because we don't explicitly set Options.UseInitArray = true;. Clang does this via some autodetect logic because it supports legacy platforms for which their other compilers do not have .init_array support (gcc < 4.7), but looking through Rust's supported platforms it doesn't appear that we have any platforms with compilers that old, so we can likely just set this true.

I'll upload a patch shortly which sets us to use .init_array unconditionally. If we find any platforms we support that may want to use .ctors, we can add this as a platform attribute similar to other arguments to LLVMRustCreateTargetMachine, and possibly pass -fno-use-init-array if clang is in use to profile_builtins's build.rs for those platforms which don't support .init_array (this shouldn't be needed if clang's autodetect is working correctly).

Linking #39915 to this issue in case someone has been having similar problems.

@maurer maurer added the C-bug Category: This is a bug. label Apr 17, 2020
@cuviper
Copy link
Member

cuviper commented Apr 17, 2020

Clang does this via some autodetect logic because it supports legacy platforms for which their other compilers do not have .init_array support (gcc < 4.7), but looking through Rust's supported platforms it doesn't appear that we have any platforms with compilers that old, so we can likely just set this true.

The platform support page currently says Linux 2.6.18 -- roughly RHEL5, which shipped gcc 4.1.2. Even RHEL6 only shipped 4.4.7, and then RHEL7 would meet your threshold with gcc 4.8.5.

@maurer
Copy link
Contributor Author

maurer commented Apr 17, 2020

I thought the kernel revision was in terms of what system calls we needed, not the user space software. Unfortunately, we don't have access to clang internals directly so we can't just re-use their auto-detection algorithm.

Some possible options:

  • Declare we require .init_array support from our runtime (use the patch I uploaded). As support for this option, Clang is dropping autodetect support in the next release.
  • Code a check similar to what clang uses. Note that this is not master, it's a commit from early December to be old enough to still have the check.
  • Add an field toTarget that allow them to declare they are .ctors targets, but default it to .init_array. This would allow special legacy targets to be created should any users exist.

Do you have a preference?

@jonas-schievink jonas-schievink added A-linkage Area: linking into static, shared libraries and binaries T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 17, 2020
@cuviper
Copy link
Member

cuviper commented Apr 17, 2020

I thought the kernel revision was in terms of what system calls we needed, not the user space software.

That kind of goes hand in hand. There's not a unified release for this stuff like in the BSDs, but looking at RHEL5 at least gives a reference of contemporary versions.

legacy platforms for which their other compilers do not have .init_array support (gcc < 4.7),

Are you sure that version is about support? There are much older references to .init_array in the GCC sources. I did find bug 46770, "Replace .ctors/.dtors with .init_array/.fini_array on targets supporting them", which targeted 4.7.0, but I think it may have still worked even before that. It's ultimately a feature of the loader, and as that bug mentions, glibc has supported this since 1999!

@maurer
Copy link
Contributor Author

maurer commented Apr 17, 2020

So, the core problem here is mixing .ctors and .init_array. The loader has supported it for a long time, so either alone would work. However, you want to be able to link the code generated on your system together, which is why clang was detecting gcc versions. If you have a gcc < 4.7, you may generate .ctors, which will then cause bad behavior like we're seeing if you link it with .init_array code.

If we're only considering Rust code, and not considering the profiler runtime (which gets built by the host toolchain and uses a C++ constructor), this will still work on those old systems. It's when you try to mix C/C++ code built with gcc < 4.7 that you'd encounter surprises.

@cuviper
Copy link
Member

cuviper commented Apr 17, 2020

So, the core problem here is mixing .ctors and .init_array.

Ah, I see. If bfd and gold are able to merge these, but lld can't/won't, then can we change behavior based on the linker flavor? I'm not sure if that choice is visible to all codegen, or just the final link.
(I don't see -Clinker-flavor in your options, but you might want that...)

Declare we require .init_array support from our runtime (use the patch I uploaded).

I guess this too is a more nuanced statement, about what we're able to support mixed when using lld. Maybe that's OK, but better if there's at least some option for going back to .ctors.

Code a check similar to what clang uses. Note that this is not master, it's a commit from early December to be old enough to still have the check.

I think this may be the prudent choice, as long as we're still supporting older platforms. It also looks like some Clang platforms may use ctors by default even now, like NetBSD < 9.

@mati865
Copy link
Contributor

mati865 commented Apr 17, 2020

-Clinker-flavor in this case is set to Gcc because LLD is chosen by passing -fuse-ld=lld flag to GCC/Clang. This is comfortable since GCC/Clang knows location of all system libraries and how to link them.
For Lld flavor I think you not only have to add all system libraries paths but also link the libraries in the right order by adding -lc -lgcc ... somewhere.

@maurer
Copy link
Contributor Author

maurer commented Apr 17, 2020

I think that controlling this behavior based on linker-flavor is probably not a great idea because:

  • You can try to use lld to link legacy object files, which would want .ctors output.
  • Even with linkers that can deal with a mix of the two sections, this is still a legacy format which we should only be emitting when needed, not by default.

@MaskRay
Copy link
Contributor

MaskRay commented Apr 17, 2020

You may read dlang/dmd#10562 about the support of .init_array in libc. For a platform with .init_array support, .ctors is entirely unnecessary.

RalfJung added a commit to RalfJung/rust that referenced this issue May 9, 2020
rustllvm: Use .init_array rather than .ctors

LLVM TargetMachines default to using the (now-legacy) .ctors
representation of init functions. Mixing .ctors and .init_array
representations can cause issues when linking with lld.

This happens in practice for:

* Our profiling runtime which is currently implicitly built with
  .init_array since it is built by clang, which sets this field.
* External C/C++ code that may be linked into the same process.

Fixes: rust-lang#71233
@bors bors closed this as completed in 0e7d5be May 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants