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

Any reason FrameDescriptionEntry::write is not public? #648

Open
KJTsanaktsidis opened this issue Mar 6, 2023 · 8 comments
Open

Any reason FrameDescriptionEntry::write is not public? #648

KJTsanaktsidis opened this issue Mar 6, 2023 · 8 comments

Comments

@KJTsanaktsidis
Copy link

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!

@philipc
Copy link
Collaborator

philipc commented Mar 7, 2023

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?

@KJTsanaktsidis
Copy link
Author

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.

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libgcc/unwind-dw2-fde.c;h=7b74c391ced1b70990736161a22c9adcea524a04;hb=HEAD

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.

@bjorn3
Copy link
Contributor

bjorn3 commented Mar 7, 2023

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.

@philipc
Copy link
Collaborator

philipc commented Mar 7, 2023

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?

@bjorn3
Copy link
Contributor

bjorn3 commented Mar 7, 2023

Wasmtime always compiles an entire wasm module at once.

@KJTsanaktsidis
Copy link
Author

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?

@philipc
Copy link
Collaborator

philipc commented Mar 12, 2023

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.

@KJTsanaktsidis
Copy link
Author

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!

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

No branches or pull requests

3 participants