-
Notifications
You must be signed in to change notification settings - Fork 108
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
Add EhFrameHdr::lookup_and_parse function #316
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like your solution. If you revise the error handling I think this can be merged.
Although since this is based on code I wrote I certainly wouldn't mind another opinion from someone else.
src/cfi.rs
Outdated
|
||
let entry = parse_cfi_entry(bases, frame.clone(), &mut input)?; | ||
let target_fde = match entry { | ||
Some(CieOrFde::Fde(fde)) => Some(fde.parse(|offset| frame.cie_from_offset(bases, offset))?), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be inefficient, one may want to cache CIEs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(which, by the way, is the same reason why fde.parse()
takes a closure)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Should we return PartialFrameDescriptionEntry ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either this or also take a closure parameter (which has the advantage that we can keep the contains
check here which is a potential footgun otherwise).
src/cfi.rs
Outdated
let entry = parse_cfi_entry(bases, frame.clone(), &mut input)?; | ||
let target_fde = match entry { | ||
Some(CieOrFde::Fde(fde)) => Some(fde.parse(|offset| frame.cie_from_offset(bases, offset))?), | ||
Some(CieOrFde::Cie(_)) => unimplemented!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I never was entirely sure about this (hence the unimplemented!()
but I believe this should actually be unreachable.
Either way, none of these cases should panic - returning an error is the correct reaction for the gimli API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be unreachable!() yeah, eh_frame_hdr points to FDEs only, not CIEs (that would make no sense).
I'll check which error makes more sense to return.
src/cfi.rs
Outdated
None => None | ||
}; | ||
match target_fde { | ||
Some(ref fde) if fde.contains(address) => Ok((*fde).clone()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ref
here is required because of the pattern guard, right? You can get around the awkwardness of cloning for no reason by moving this into the match above (since the other arms of that match are dead anyways).
src/cfi.rs
Outdated
|
||
let eh_frame_ptr = match self.hdr.eh_frame_ptr() { | ||
Pointer::Direct(x) => x, | ||
_ => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it makes sense to deal with indirect pointers here. I certainly have never encountered any. Returning an error is probably fine.
I'll add an example in the doc, and then I think this is ready to merge. |
I added a test and an example (and hopefully fixed compilation, woops). If it compiles and the tests pass, this should be ready to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this solution too.
src/cfi.rs
Outdated
let mut input = &mut frame.section().clone(); | ||
input.skip(offset)?; | ||
|
||
let entry = parse_cfi_entry(bases, frame.clone(), &mut input)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unneeded clone.
src/cfi.rs
Outdated
Some(CieOrFde::Fde(fde)) => Ok(fde.parse(cb)?), | ||
Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), | ||
None => Err(Error::NoUnwindInfoForAddress) | ||
}?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: I think I prefer returns in the match arms instead.
src/cfi.rs
Outdated
/// # let bases = BaseAddresses::default() | ||
/// # .set_cfi(address_of_cfi_section_in_memory) | ||
/// # .set_text(address_of_text_section_in_memory) | ||
/// # .set_data(address_of_data_section_in_memory); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simpler to let bases = unimplemented!();
?
All ready. |
where | ||
F: FnMut(EhFrameOffset<R::Offset>) -> Result<CommonInformationEntry<EhFrame<R>, R, R::Offset>> | ||
{ | ||
let fdeptr = self.lookup(address, bases)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for this to be encoded as DW_EH_PE_pcrel
? If so, we'll get this wrong, since bases.cfi
will be for the eh_frame
section.
Currently,
EhFrameHdr::lookup
returns a Pointer, which is an absolute pointer which may (or may not) point to the correct FDE. This API is used by unwind-rs to speed up stack unwinding. Unfortunately, unwind-rs requires patches to gimli (see gimli-rs/unwind-rs#6) in order to make full use of this API. This is because there is no method to parse the returned Pointer into a FrameDescriptionEntry. Unwind makes parse_cfi_entry, and uses it as such:As can be seen here, there is a big fat unsafe in the middle, which sucks. I believe there is a better solution.
The complicated part is that the fdeptr is absolute, and has no length associated to it. It is, however, guaranteed to always point to an eh_frame section. Furthermore, the eh_frame_hdr has a pointer to the start of this section. As such, I propose this solution, which allows us to get rid of the unsafety.
I additionally took the liberty of adding the guarantee that the returned FramePointer will be valid (there is no need to check contains()).
I am in the process of testing this, and the implementation and doc might need a bit of refinement.
CC @main--