Skip to content

Direct access external data#158409

Open
ilovepi wants to merge 2 commits into
rust-lang:mainfrom
ilovepi:direct_access_external_data
Open

Direct access external data#158409
ilovepi wants to merge 2 commits into
rust-lang:mainfrom
ilovepi:direct_access_external_data

Conversation

@ilovepi

@ilovepi ilovepi commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Today, when using -Zdirect-access-external-data we've seen an issue when compiling code with ThinLTO or when codegen-units > 1 (due to the use of ThinLTO), that causes GOT accesses to occur in cases where they shouldn't. This is due to two reasons. First, unlike clang, rustc does not emit the direct-access-external-data module flag in the LLVM IR. When compiling with ThinLTO, the missing module flag will cause the compiler to drop the dso_local from external data, leading to GOT slots being used. This breaks Rust usage in the Fuchsia kernel, and for embedded code that cannot use GOT indirection. The Clang example can be found here: https://github.com/llvm/llvm-project/blob/dbab3f71775f6edd11546385aeddb9d0b053a2b5/clang/lib/CodeGen/CodeGenModule.cpp#L1664

The second issue is that without setting the PIC/PIELevel consistently, several things in the LTO pipelines stop working, including direct-access-external-data, incorrect TLS models being selected, or the wrong relocation type being emitted for them in the backend. In the case of direct-access-external-data, if the relocation model is not set correctly, the indirection will always happen because that is the only thing possible (e.g. you're compiling PIC for a shared lib and not PIE). Ideally, we'd unify and simplify things on the LLVM side to make this very hard for frontends to get wrong, or at least supply more consistent APIs for them to consume. This PR works around that by emitting the module flag, and setting the module's PICLevel and PIELevel consistent with the way these are set in context.rs in lto.rs.

When compiling with ThinLTO, the missing module flag will cause the
compiler to drop the `dso_local` from external data, leading to GOT
slots being used. This breaks Rust usage in the Fuchsia kernel, and for
embedded code that cannot use GOT indirection whenever ThinLTO is at
play due to incorrect setting of the relocation model in the LLVM module
when they are merged. Handling the relocation model will be done
separately, as this module flag is a pre-requisite of for that change to
affect direct-access-external-data.

This patch ensures we emit the right module flag for the option. This
matches clang's behavior. See
https://github.com/llvm/llvm-project/blob/dbab3f71775f6edd11546385aeddb9d0b053a2b5/clang/lib/CodeGen/CodeGenModule.cpp#L1664
@rustbot

rustbot commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

These commits modify compiler targets.
(See the Target Tier Policy.)

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-run-make Area: port run-make Makefiles to rmake.rs S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 25, 2026
@rustbot

rustbot commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

r? @nnethercote

rustbot has assigned @nnethercote.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 18 candidates

@ilovepi

ilovepi commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Note: I've included two commits, since I'm trying to solve a single problem, but its fine if these should be split up somehow.

@ilovepi

ilovepi commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

@nikic do you have thoughts here on how we can/should solve this on the llvm side (or if i've done something silly in terms of rustc)?

@rust-log-analyzer

This comment has been minimized.

Without setting the PIC/PIELevel consistently, several things in the LTO
pipelines stop working, direct-access-external-data specifically, but
this can also lead to incorrect TLS models being used or the wrong
relocation type being emitted for them in the backend. Ideally, we'd
unify and simplify things on the LLVM side to make this very hard for
frontends to get wrong. This PR works around that by setting the
module's PICLevel and PIELevel consistent with the way these are set in
context.rs in lto.rs.
@ilovepi ilovepi force-pushed the direct_access_external_data branch from b46d076 to 7f4494e Compare June 25, 2026 18:22
Comment on lines 1115 to 1127
extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) {
unwrap(M)->setPICLevel(PICLevel::Level::BigPIC);
Module *Mod = unwrap(M);
if (!Mod->getModuleFlag("PIC Level")) {
Mod->setPICLevel(PICLevel::Level::BigPIC);
}
}

extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) {
unwrap(M)->setPIELevel(PIELevel::Level::Large);
Module *Mod = unwrap(M);
if (!Mod->getModuleFlag("PIE Level")) {
Mod->setPIELevel(PIELevel::Level::Large);
}
}

@ilovepi ilovepi Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not totally convinced this is 100% the right approach. Alternatively, we could set the merge strategy and do it that way, but I figured its probably better to only replace if it doesn't exist. Happy to amend the strategy here.

View changes since the review

let llmod = module.module_llvm.llmod();

let reloc_model = cgcx.relocation_model;
llvm::set_module_pic_and_pie_levels(llmod, reloc_model, &cgcx.crate_types);

@nikic nikic Jun 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm confused by why this is necessary. Aren't we just loading a previously serialized module here? Shouldn't the PIC/PIE flags have been set at the time the module was created?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I was surprised too. I believe the main contributor is that core & std libs end up w/o the PIE levels set at all, since they aren't being used in an executable. I can't recall if we have other examples or not, but its my vague recollection that we triggered this issue w/o use of core at all when codegen-units>1.

For our kernel specifically, this becomes an issue as we'd really like to use code from core. However, that would be a hard sell if we cannot prevent GOT indirection when accessing globals that would normally be safe to directly access (and have been accessed that way from C++ code for a very long time).

Do you think there's a better way to address this? Maybe somewhere in the lto.rs code?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So the problem is that libcore/libstd are compiled as PIC but you want them to be PIE? And you're solving this by adding the PIE metadata during LTO? I don't think that's the right way to fix this.

Wouldn't the correct solution here to use build-std and enable PIE for libstd?

@nnethercote nnethercote left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't know this stuff at all, I will defer to an expert.

View changes since this review

@nnethercote

Copy link
Copy Markdown
Contributor

r? @nikic

@rustbot rustbot assigned nikic and unassigned nnethercote Jun 29, 2026
@rust-bors

rust-bors Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-run-make Area: port run-make Makefiles to rmake.rs S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants