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

Initial .eh_frame_hdr implementation #250

Merged
merged 6 commits into from
Oct 10, 2017
Merged

Conversation

main--
Copy link
Contributor

@main-- main-- commented Oct 4, 2017

No description provided.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 0.0% when pulling 045b44f on main--:eh_frame_hdr into 6a3f391 on gimli-rs:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage remained the same at 0.0% when pulling 045b44f on main--:eh_frame_hdr into 6a3f391 on gimli-rs:master.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

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

This is a great start!

The big thing we need before merging this is tests. We strive for branch coverage in our unit tests:

https://github.com/gimli-rs/gimli/blob/master/CONTRIBUTING.md#coverage

The ideal we aim to reach is having our unit tests exercise every branch in gimli. We allow an exception for branches which propagate errors inside a try!(..) invocation, but we do want to exercise the original error paths.

Pull requests adding new code should ensure that this ideal is met.

We use the test-assembler crate to create test cases for binary data. A little more details and links to examples here.

Here are some scenarios where I think we need test coverage:

  • The successful parsing case where we are given good .eh_frame_hdr data

  • When fde_count_enc is omit but table_enc is not omit, and vice versa. ParsedEhFrameHdr::table should do the right thing in these cases.

  • When each of fde_count_enc, table_enc, and eh_frame_ptr_enc are indirectly encoded.

  • When the version is not 1

  • When the binary search table has LEB encoding

  • When EhHdrTable::lookup is given an address that it does not have an entry for

  • When EhHdrTable::lookup is given an address it does have an entry for

Thanks a ton @main-- !

src/cfi.rs Outdated
/// `EhFrameHdr` contains the information about the `.eh_frame` section.
///
/// A pointer to the start of the `.eh_frame` data, and optionally, a binary
/// search table of pointers to the `.eh_frame` records are found in this section.
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick:

to the .eh_frame records that are found in this section.

src/cfi.rs Outdated
@@ -89,6 +90,149 @@ impl<R: Reader> From<R> for DebugFrame<R> {
}
}

/// `EhFrameHdr` contains the information about the `.eh_frame` section.
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: this introduction sentence should mention that this is the type for the .eh_frame_hdr section.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, also please add this type to the list of sections / API entry points in the top level crate docs in src/lib.rs! Thanks!


/// `ParsedEhFrameHdr` contains the parsed information fron the `.eh_frame` section.
#[derive(Clone, Debug)]
pub struct ParsedEhFrameHdr<R: Reader> {
Copy link
Member

Choose a reason for hiding this comment

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

I don't love this name, but I also don't have any great suggestions :-p

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same

src/cfi.rs Outdated
pub fn parse(&self, bases: &BaseAddresses, addr_size: u8) -> Result<ParsedEhFrameHdr<R>> {
let mut reader = self.0.clone();
let version = reader.read_u8()?;
assert_eq!(version, 1); // FIXME return error
Copy link
Member

Choose a reason for hiding this comment

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

Let's fix this before merging :)

src/cfi.rs Outdated
let fde_count = parse_encoded_pointer(fde_count_enc, bases, addr_size, &self.0, &mut reader)?;
let fde_count = match fde_count {
Pointer::Direct(c) => c,
Pointer::Indirect(_) => unreachable!(),
Copy link
Member

Choose a reason for hiding this comment

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

Is this really unreachable? I would expect there to be some early return if fde_count_enc.is_indirect() in order to make this actually unreachable, and not just a bad .eh_frame_hdr.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this probably should just return an error.

I mean, this isn't even a pointer, it's just a count that uses the pointer-encoding mechanism. I can't imagine any sane (or insane) reason why a compiler would emit an indirect pointer here and supporting this case just makes everything so very awkward (since we definitely can't just deref) - so why bother when this won't ever happen?

Copy link
Member

Choose a reason for hiding this comment

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

I think this probably should just return an error.

Agreed, and we should have test coverage.

src/cfi.rs Outdated
pub fn lookup(&self, address: u64, bases: &BaseAddresses) -> Result<Pointer> {
let size = match self.hdr.table_enc.format() {
constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128
=> return Err(Error::VariableLengthSearchTable),
Copy link
Member

Choose a reason for hiding this comment

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

I don't see anything in https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html that suggests that using LEBs is not valid here, although it does break binary search, as you point out. How about a comment to the effect that it doesn't make sense because of the binary search stuff, but that it isn't strictly prohibited.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, another tricky problem. Just like with fde_count, the compiler could in theory do something completely nonsensical here for no reason at all.

Again, I decided not to bother as that's what other implementations do (llvm, libunwind (lol)).

So I would say it's de facto prohibited.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense. We should leave a comment to this effect here and ... queue broken record ... have test coverage for this case.

libunwind (lol)

Wow...

Pointer::Indirect(_) => return Err(Error::UnsupportedPointerEncoding),
};

match pivot.cmp(&address) {
Copy link
Member

Choose a reason for hiding this comment

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

I guess the reason that we can't read the table into a slice and use slice::binary_search_by is because we would need to repeat that code for all the different sizes?

I'd prefer to use the off-the-shelf binary search implementation if possible.

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps some macro_rules to hide the code repetitition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually originally intended to use std's binary search here. I ended up doing it this way instead when I didn't find a safe way (as in: not transmute) to convert from &[u8] to &[u32] and friends.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I guess that transmute involves making sure that alignment is correct and all that too or else we'll perform unaligned loads and go into UB.

Ok, let's keep this binary search implementation.

@fitzgen
Copy link
Member

fitzgen commented Oct 4, 2017

Oh, and if you have any questions about test-assembler or anything like that, don't hesitate to ask me :)

@coveralls
Copy link

Coverage Status

Coverage remained the same at 0.0% when pulling d79ba6d on main--:eh_frame_hdr into 6a3f391 on gimli-rs:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage remained the same at 0.0% when pulling d79ba6d on main--:eh_frame_hdr into 6a3f391 on gimli-rs:master.

@philipc
Copy link
Collaborator

philipc commented Oct 10, 2017

Is this ready for another round of review?

@main--
Copy link
Contributor Author

main-- commented Oct 10, 2017

Yes

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

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

👍 thanks a ton @main-- !

@fitzgen fitzgen merged commit 0d69019 into gimli-rs:master Oct 10, 2017
@fitzgen
Copy link
Member

fitzgen commented Oct 10, 2017

(For the record, it's always a safe-bet to drop a comment when a PR is ready to be looked at again -- half the time I don't get the "more commits pushed" emails...)

@main-- main-- deleted the eh_frame_hdr branch October 10, 2017 18:17
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

Successfully merging this pull request may close these issues.

None yet

4 participants