[llvm] r179051 - Template the MachO types over the word size.

Rafael EspĂ­ndola rafael.espindola at gmail.com
Tue Apr 16 16:55:49 PDT 2013


> I'm having problems coming up with a solution that doesn't involve
> reading anything in as a batch operating, parsing it, filling out
> structures and _then_ having it for access rather than lib/Object
> having pointers to everything.
>
> Another thought is to clean and layer the code a little better so that
> an individual class is abstracted out a bit both up and down the
> layers. Rafael, any ideas here?

Some. Sorry if this sounds like a brain dump (it is):

Any implementation that supports handling files of different
endianness will have to have checks for mismatched endianness and will
have to flip bytes sometimes. The first design options are when those
things happen:

* Check the endianness only once when opening the file.
* Check the endianness when looking at a struct (a RelocationEntry for example)
* Check the endianness when looking at a field (an offset for example)

A fully templated implementation implements the first option.
Something like InMemoryStruct implements the
second one and a wrapper around a pointer with getters for individual
fields implements the third.

As for flipping the bytes, the last two options are available. Both
the template and field getter flip the bytes only when accessing a
field. An InMemoryStruct like solution flips them when looking at a
struct.

A solution that always uses a getter for individual fields would be
too slow for users like lld. A fully templated solution is considered
too complex by some and the code duplication is probably not worth it
for tools like llvm-readobj.

So why not the option of "parsing" one struct at a time? An
implementation that would always copy could have methods that return
structs by value. This would work, but is slower than the templated
case. In particular in the matching endianness case since the copy is
unnecessary.

What InMemoryStruct does is try to avoid the copy in the case of
matching endianness. If they match operator* and operator-> give
access to the raw object data. If they don't match, the struct is
parsed and kept in an internal buffer. There are still some problems
with this solution:

* Stack space is still allocated for the flipped bytes even if the
endianness matches.
* We flip more than what we need. Even if the client only wants a
field of a struct, the entire struct is flipped.
* The difference in lifetime when the endian mismatches makes it
*very* easy to write code that works on the same endian case and
accesses dead memory otherwise. I started this by removing uses of
InMemoryStruct after having to debug in a ppc qemu vm why some tests
would fail only when the host was big endian.

My current idea for a better compromise is to have the templated
implementation that can be used by performance critical code but
provide getters in the base class so non performance critical code can
avoid using the templates. For example, in templated code asking for a
relocation entry returns a raw pointer whose type encodes the
endianness. In non templated code, the method in the base class
returns a wrapper struct with a PointerIntPair that records the actual
pointer and the endianness. The wrapper then provides getters like
getAddress() and getPCRel().

All of this is of course on top of moving things like
getRelocationValueString back to the c++ file. They are never
performance critical and can use run time checks.

> -eric

Cheers,
Rafael



More information about the llvm-commits mailing list