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

Adding an option to force compiling a crate as PIE (instead of PIC) #87934

Closed
hlopko opened this issue Aug 11, 2021 · 12 comments
Closed

Adding an option to force compiling a crate as PIE (instead of PIC) #87934

hlopko opened this issue Aug 11, 2021 · 12 comments

Comments

@hlopko
Copy link
Contributor

hlopko commented Aug 11, 2021

Hi all,

AFAIK we always compile crates (--crate-type=lib) in relocation-model=pic as PIC (we call LLVMRustSetModulePICLevel in https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/context.rs#L183). Only when we are building a binary crate we compile using PIE (https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/context.rs#L187).

This is a reasonable default, PIC objects can be used for both executables and shared libraries. PIE objects can only be used for executables, but PIE code can be faster than PIC code.

We would like introduce a way to build crates as PIE in performance-critical scenarios where we know we won't need to produce shared libraries. Even further, cargo could detect that it's only building an executable and it could choose to build crates as PIE by default (but this is outside of the scope of this issue).

An obvious solution is to add a command line flag and check its value in https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/context.rs#L186. Would that be the best way to implement this? Would it be a welcomed change?

Thank you and have a lovely day!

@tmandry
Copy link
Member

tmandry commented Aug 18, 2021

Please consider filing a Major Change Proposal for this. I don't expect it to be controversial.

Adding this as a new relocation-model option makes sense to me. Implementation should be fairly straightforward in that case. The rustc guide is a great resource on the overall contribution flow.

See also: Adding a new option to rustc

@petrochenkov
Copy link
Contributor

petrochenkov commented Aug 25, 2021

I'm not sure and MCP is necessary, a PR with implementation would be enough, this should be a trivial change from the compiler side.
The PR will probably need an FCP poll though because adding new -C relocation-model=pie would be a public facing change.

@darbysauter
Copy link

I would appreciate this change. I think adding -C relocation-model=pie would be a good option and cargo could only allow this if as mentioned above the preliminary crates do not need to be shared libraries.

I am working on a Rust kernel that will relocate itself and this would be much better than translating GOT, vtables, etc. at runtime.

@darbysauter
Copy link

I added -Clink-arg=-pie and now my kernel is possibly PIE. Not sure if this would look any different compared to having all the intermediaries be compiled as PIE. I am in the process of changing my bootloader and kernel to work with the changes. I will still have to translate the GOT, vtables, etc. But it now gives me information about what needs to be relocated allowing me to parse .data.rel.ro. Maybe what I envisioned where the ELF could be placed anywhere and be fine without needing anything to be translated isn't possible with rust, this should be good enough.

I had envisioned PIE not having a GOT, and string and vtables containing relative addresses and every access would be relative to program counter, but maybe my understanding is incorrect.

@hlopko
Copy link
Contributor Author

hlopko commented Sep 10, 2021

Thank you all! I've started the MCP in rust-lang/compiler-team#461 just to be sure. I'll upload a PR shortly.

@darbysauter I don't think -Clink-arg=-pie is enough to make your binary pie, you really need to compile all your objects as pie. Then the code will be relocatable, but it shouldn't need GOT or PLT - all locations will be resolved at link time. Maybe you can try my PR and see if it works :)

@hlopko
Copy link
Contributor Author

hlopko commented Sep 10, 2021

#88820 is the prototype PR.

@darbysauter
Copy link

I finally got around to testing your PR. I used:
rustflags = ["-Crelocation-model=pie","-Clink-arg=-pie","-Clink-arg=--image-base=0x200000"]

The binary is different compared to pic, but the GOT still contains absolute addresses, vtables still contain absolute addresses, etc. I am not sure entirely what is different between the two, but it looks as if I will just need to translate the addresses. Maybe what I am looking for doesn't exist.

Thanks for the help!

@bjorn3
Copy link
Member

bjorn3 commented Sep 17, 2021

rustflags = ["-Crelocation-model=pie"] will only compile your own code and cargo dependencies as PIE. The standard library is still compiled as PIC. For it to become PIE too you need to compile the standard library yourself too using cargo build -Zbuild-std.

@bjorn3
Copy link
Member

bjorn3 commented Sep 17, 2021

Also the GOT will always contain absolute addresses at runtime. PIC/PIE only make the code use relative addresses. All data, including the GOT, still uses absolute addresses.

@darbysauter
Copy link

What is the difference between PIC and PIE I can't seem to find any good explanations online. There also seems to be 2 different factors, compiling as PIC/PIE and linking as PIC/PIE.

What I understand now is that either way PIC or PIE needs to have addresses translated depending where it is placed in memory, so what is the advantage of PIE?

@bjorn3
Copy link
Member

bjorn3 commented Sep 17, 2021

PIE is a performance optimization over PIC. I think it basically puts all data (local or imported) at fixed offsets relative to the executable code, copying data from dylib dependencies and updating their GOT if necessary and avoids the GOT for non-imported functions, thus making them unoverridable by dependencies or LD_PRELOAD. It still uses the GOT for imported functions. Be aware that I am not certain if I correctly described all differences between PIC and PIE.

@hlopko
Copy link
Contributor Author

hlopko commented Oct 31, 2021

Closing since #88820 has been merged, thank you all!

@hlopko hlopko closed this as completed Oct 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants