Previously, it would forget to load and then just try to get the strings section data
This would work if some other processing on the file had already loaded the string table
but not if this was the first method that wanted to inspect those strings.
This class of bugs is really an integration with the CachedReadBytes interface, so it
can only get caught if we're testing with that impl. These interface tests should be parameterized
for both interfaces.
This is a zero-alloc interface which parses on-demand, which can be desireable if you can't
or don't want to allocate, but is less CPU-efficient if you're doing a lot of look ups on the table.
This more closely matches the datastructure and its use-case. Users are likely
wanting to look up the VerNeeds and VerDefs for a particular symbol in the .dynsym
table, and to do so they'll need to look up the VersionIndex by the Symbol's index
in the .dynsym table. The ParsingTable helps provide that interface with its .get(index)
method.
Alternatively, someone could do a variant of zip(SymbolTable.iter(), VersionTable.iter())
to have the Symbol alongside the VersionIndex, which is also supported by VersionTable.
Also, use this ParsingTable to replace the hand-rolled SymbolTable.
This implements a lazily-parsed table/array of structs that can be parsed
with the ParseAt trait that drives the parsing of our ELF structs.
Elements in the table are lazily parsed on-demand via the .get(index)
method. The table can also optionally be converted into a ParsingIterator
with either iter() or into_iter(), meaning the ParsingTable can be used
like so:
```
let table = ParsingTable<Foo>::new(...)
for foo in Table {
... do things with parsed Foo
}
```
or
```
let table = ParsingTable<Foo>::new(...)
for foo in Table.iter() {
... do things with parsed Foo
}
for foo in Table.iter() {
... do another pass of things with parsed Foo
}
```
Note that the second pattern will re-parse the second time. Useful
if you're wanting to avoid allocations, but otherwise it's probably
more useful to .collect() the iterator into a Vec<Foo>.
I think that ideally this would be able to yield a ParseError in some way,
as it likely indicates corrupted section data. All of our iterators silently
end iteration on ParseErrors currently, though, so this is in line with
existing patterns of behavior.
There was only one implementer of this trait, on &[u8], and the indirection
didn't serve any real purpose. The indirection via the double &&[u8] impl was
more expensive than necessary, and the indirection also added a layer of unnecessary
cognitive overhead to reading the code.
This goes back to having simple parse methods for the endian-aware integer parsing
which operate on byte slices.
These other type of entries are found in the .gnu.version_r section of type SHT_GNU_VERNEED.
This only parses the type, and is not hooked up to anything else yet.
These are one type of entries found in the .gnu.version_r section of type SHT_GNU_VERNEED.
This only parses the type, and is not hooked up to anything else yet.
These other type of entries are found in the .gnu.version_d section of type SHT_GNU_VERDEF.
This only parses the type, and is not hooked up to anything else yet.
These type of entries are found in the .gnu.version_d section of type SHT_GNU_VERDEF,
alongside VerDefAux (not yet defined).
This only parses the type, and is not hooked up to anything else yet.
These are the entries found in the .gnu.version section of type SHT_GNU_VERSYM.
This only parses the entry type, and is not hooked up to anything in File yet.
I don't plan on implementing any actual compression as part of this library and think that
it is reasonable for users to do the decompression themselves based on the description
provided by the parsed CompressionHeader.
Also, deprecate File::section_data_for_header() in favor of section_data()
This header is found at the start of sections whose flags contain SHF_COMPRESSED,
indicating that the section's data in the file is compressed with a given algorithm.
These define possible values for the elf compression header's ch_type field, which
signals the compression algorithm used for compressed sections (shdr.sh_flags & SHF_COMPRESSED)
New Features:
* .note section and segment parsing
* .dynamic section and segment parsing
* .rel and .rela section parsing
* File::section_headers_with_strtab to get both a header iter and strtab concurrently.
This is useful if you want to iterate over shdrs and get their names at the same time.
Interface changes:
The ReadBytesAt trait was changed to be implemented for an owned CachedReadBytes.
This means that File::open_stream now expects to move-own the CachedReadBytes
as opposed to taking a mutable reference.
The implementation panned out where this header is parsed but then
only used to drive the further parsed Note but not included in the result,
so there's no need for pub(crate) here.
Note: (haha) It looks like gcc/clang choose to emit 32-bit note headers
even for 64-bit objects. I discovered this when writing the parse tests
with data from a 64-bit LSB ELF object that I slurped the .note.gnu.build-id
section from. I kept the 64-bit nhdr parsing around in case someone finds
a use-case for it in the future.
These fixed size headers prefix and describe variable-sized note data.
I made it pub(crate) to keep this out of the public interface, since
I plan to have the actual note iterator parse out the name and desc and
have that result be the actual public interface. pub(crate) might still
be more visibility than is needed, but it's a safe place to start.
This allows for a more convenient way of interacting with the SectionHeaders so
that you can concurrently look up the names of the sections as you're iterating
through the SectionHeaderIterator
Note, while this may satisfy some use-cases, it is likely insufficient for those
who want to actually do linking and to concurrently iterate over the returned Rels
while also looking at the sectin's associated SymbolTable and the section to which
they're applied.
I didn't have a strong motivating factor for deciding whether to represent this as I did,
with r_sym and r_type parsed out as data members, or as an alternative approach where
the rust type keeps a private r_info and then parses out those data members as needed with
getter methods. So I arbitrarily chose to parse them out up front.
As suggested in #21
This changes it from being implemented for a &mut CachedReadBytes, which forced
users to keep a separate variable for the reader when passing it to File::open_stream().
There wasn't a specific reason for it to be implemented that way, and it's actually
something that tenuously even desireable, since ultimately CachedReadBytes is
a more an internal wrapper used within the File for lazy parsing than something
that a user of the interface will want to keep around and use themselves for other things.
This centralizes the iter implementation to one place and leverages the new ParseAt
trait which was already functionally being implemented by all the various types
being iteratively parsed.