Make ELF loading respect program header virtual addresses for non-PIE binaries - #1530
Make ELF loading respect program header virtual addresses for non-PIE binaries#1530cshung wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR adds support for running and testing non-PIE Rust guest binaries, including updating snapshot virtual-address mapping so non-PIE guests execute at their declared ELF virtual addresses.
Changes:
- Add a helper in
hyperlight_testingto locate the non-PIEsimpleguestbinary. - Update snapshot mapping/entrypoint calculation to support non-identity VA mappings for non-PIE code regions.
- Add a new integration test and build automation (Justfile) to produce and run a non-PIE guest.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/hyperlight_testing/src/lib.rs | Adds path helper(s) for locating non-PIE Rust guest binaries. |
| src/hyperlight_host/tests/integration_test.rs | Adds an integration test that boots and calls into a non-PIE guest. |
| src/hyperlight_host/src/sandbox/snapshot/mod.rs | Adjusts snapshot mappings and entrypoint VA calculation to support non-PIE guests. |
| Justfile | Adds tasks to build and stage non-PIE Rust guest artifacts. |
7468e06 to
40c43be
Compare
|
Addressed Copilot review feedback:
|
3fa110a to
35fef7b
Compare
ludfjig
left a comment
There was a problem hiding this comment.
LGTM. @syntactically could you have a look too
syntactically
left a comment
There was a problem hiding this comment.
Thanks for doing this! It looks like it is moving in a good direction. I have a couple of minor suggestions/comments, as well as a bit of an alternative design that you could take (but totally up to you on that one!)
ludfjig
left a comment
There was a problem hiding this comment.
LGTM but will once again defer to @syntactically for final review
syntactically
left a comment
There was a problem hiding this comment.
This looks good to me, thank you and sorry for the delay reviewing!
I do think that there is one minor move that would make the code a little bit cleaner / better fitting with existing conventions, which I've commented on inline. Feel free to push back with a reason why this code belongs here, though!
52a8d4f to
b53a7c5
Compare
|
All review comments have been addressed. The suggestion to move conflict-check logic into \layout.rs\ (from the Jul 15 review) is implemented in the follow-up PR #1655 via the \code_virt_base()\ method which handles both VA selection and overlap validation. @syntactically — ready for re-review when you get a chance! |
syntactically
left a comment
There was a problem hiding this comment.
If you've already done that, I think it would be slightly nice to get it cherry-picked here without the ASLR changes, since I'm not totally sure we are decided on doing ASLR---it has pretty limited benefits for the modal hyperlight use case because one can't (generally) re-slide an image after a snapshot is taken, and in practice almost all images of a given binary are expected to descend from one snapshot.
As I said before I don't feel incredibly strongly though---I will leave the decision up to you.
b3d461d to
dfed7ca
Compare
1799d75 to
623a290
Compare
Add support for running non-PIE (ET_EXEC) guest binaries by mapping code at the ELF's declared virtual address rather than assuming identity mapping (physical == virtual). Changes: - Add is_pie() and base_va() methods to ExeInfo/ElfInfo to detect ET_DYN vs ET_EXEC binaries and extract the base virtual address - Add SandboxMemoryLayout::code_virt_base() to compute the correct virtual base for the code region and validate it doesn't conflict with other memory regions - Update snapshot creation to use non-identity virtual mapping for non-PIE code regions - Add non-PIE guest build step to CI (cargo hyperlight with -C relocation-model=static -C link-args=--no-pie) - Add integration test verifying non-PIE guest execution - Add test helper for locating non-PIE guest binaries Signed-off-by: cshung <3410332+cshung@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6f20a05d-6bee-4e2e-b320-12f8d9759bbc Signed-off-by: cshung <3410332+cshung@users.noreply.github.com>
623a290 to
e630b8d
Compare
| let mut regions = self.get_memory_regions_::<GuestMemoryRegion>(())?; | ||
|
|
||
| if !is_pie { | ||
| let code_virt_end = code_virt_base + loaded_size; |
There was a problem hiding this comment.
This will wrap and panic if the elf file is mal formed
|
Thanks for all the great work here & putting up with so many rounds of iteration @cshung! The addition of As you probably noticed, we have several different variants of
Adding another field for GVA to the base struct is not ideal, then, because it's semantically confusing for the other variants (one of which even already contains GVAs!). I think what this PR really needs is for The obvious way to fix this is just by changing If you wanted to make another change to rename that type and fields to something more sensible (either as another PR using stacked PRs to keep this on top, or as an early commit in this PR which we must keep without squashing when merging, since it's a noisy, large, and semantically distinct change), that would be great, but is by no means required/expected. If you did do that, taking the opportunity to (again, as distinct commits that we must not squash on merge) to also remove one of the redundant size fields in the structure (so changing from host range/guest range to host base/guest base/size) would be great. A couple more minor nits:
|
Summary
For non-PIE (ET_EXEC) ELF binaries, the guest page table now maps the code region at the ELF's declared virtual address rather than identity-mapping it at the GPA. This allows statically-linked binaries with a fixed load address (e.g.,
--image-base=0x200000) to execute correctly.Problem
Previously, Hyperlight assumed code GVA == code GPA (identity mapping). Non-PIE binaries that declare a non-zero base virtual address (via program header
p_vaddr) would triple-fault because the guest CPU jumped to the ELF's declared entrypoint VA, which wasn't mapped in the page tables.Solution
code_virt_basefrom the ELF's lowest LOAD segmentp_vaddrbase_va > 0): map code at the declared VA in the guest page tablesbase_va == 0): preserve existing identity mapping behavior (with assertion to guard the invariant)code_virt_base + (entrypoint_va - base_va)The fix leverages the existing
Mappingstruct's support forphys_base != virt_base— no changes to the page table code itself.Testing
non_pie_guest_hello_worldintegration test exercises full guest lifecycle (init, COW, function call, return value) with a non-PIE simpleguest built at--image-base=0x200000Build infrastructure
build-rust-guests-non-pieJustfile targetsguestsrecipe to avoid clobbering normal guest binariessimple_guest_non_pie_as_string()test helperContributes to: #1408