Any reason FrameDescriptionEntry::write is not public?
KJTsanaktsidis opened this issue · comments
I'm looking at generating .eh_frame data for JIT'd code with gimli. When the code generator makes code for a function, I want to use gimli to generate a Frame Description Entry for it, so I can immediately register it with libgcc's __register_frame
API (which expects a pointer to a single FDE or CIE).
Unfortunately I can't use gimli to generate a single Frame Description Entry or Common Information Entry, because the write function for those are private. Instead, only an entire .eh_frame
section can be generated. Using this interface would require me to generate a .eh_frame section and hence a CIE for each generated function; however, I only really need one CIE for the whole program otherwise (I think) so this seems very wasteful.
Instead, what I'd like to do is use gimli to generate FDE's and CIEs, and handle registering them with libgcc on an individual basis. I understand that this would mean generating the CIE first, getting its address, registering it with libgcc, and then passing that address to the write
function of each FDE generated (so the offset to the CIE could be computed) - but this doesn't seem like a huge problem to me.
Is there any reason that FrameDescriptionEntry::write
and CommonInformationEntry::write
could not be made public for this use-case?
Thanks!
It's not public because it didn't seem useful, and I didn't want to expose implementation details. I'm not yet convinced that it would be useful. My understanding is that __register_frame
expects a pointer to an entire .eh_frame
section, not a single FDE or CIE. Can you point to any other software that uses the approach you are suggesting?
My understanding is that __register_frame expects a pointer to an entire .eh_frame section, not a single FDE or CIE
Hard to know what this stuff expects, it's all tremendously badly documented. I don't know for sure what I'm trying to do will work, I only just started implementing it...
My reading of the source seems to suggest that passing a single FDE to __register_frame will work, it will construct a single struct object
containing just that FDE. The CIE is accessed by simply doing &f->CIE_delta - f->CIE_delta;
so there's no requirement it's actually passed to explicitly to libgcc at all AFAICT.
But yeah I have absolutely no idea if this will actually work. I guess i'll roll it by hand and report back here if it worked or not.
On macOS you need to pass a single FDE. Wasmtime handles this by creating a full .eh_frame section and then walking the FDE in it.
So the relevant code is https://github.com/bytecodealliance/wasmtime/blob/d3fdb5fc2c02c043e716f4aedf4854bb92705c7c/crates/jit/src/unwind/systemv.rs#L37-L68 which is called for an ELF file that is built up in memory, with a complete .eh_frame
section.
Does Wasmtime do JIT for individual functions? If so it must build an ELF for each one?
Wasmtime always compiles an entire wasm module at once.
So yes libgcc does want a full eh_frame section, because it advances through the provided list of FDE's until it sees a zero-length one (here: https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libgcc/unwind-dw2-fde.c;h=7b74c391ced1b70990736161a22c9adcea524a04;hb=HEAD#l719)
It might work to pass an individual FDE with a four-byte NULL at the end, but each such FDE will be added as a separate struct object
. AFAICT libgcc does a linear scan of objects looking for the one with the right address when unwinding, and only does a O(logN) sorted search for the FDEs within each struct object
, so it's probably going to be very slow to use it this way.
That said, I did actually find having the FDE/CIE ::write
methods exposed was useful for another reason - I made my debuginfo generator keep a continuously growing buffer of FDE's, so I didn't need to re-generate the entire section every time I added a new code block - see here: https://github.com/KJTsanaktsidis/ruby/blob/61417db84da63f4442df0450c728b7200e2fb658/yjit/src/backend/unwind.rs#L142
Maybe this kind of streaming construction of eh_frame sections would be a useful thing to add to gimli?
That does sound useful. I'll be happy to apply patches that expose the write methods. Perhaps adding write_eh_frame
methods would be better than exposing the eh_frame
boolean parameter. A streaming writer might be useful too, but I haven't given much thought to what the API would look like.
OK - let me keep banging my unwinder generator into shape, and when I figure out a good idea for the interface I'd like in gimli, I'll open up a PR. Thanks!