This is an archive of the discontinued LLVM Phabricator instance.

[lldb][ObjectFile] Relocate sections for in-memory objects (e.g. received via JITLoaderGDB)
ClosedPublic

Authored by sgraenitz on Nov 4 2020, 8:45 AM.

Details

Summary

Part 2 of a fix for JITed code debugging. This has been a regression from 5.0 to 6.0 and it's still reproducible on current master: https://bugs.llvm.org/show_bug.cgi?id=36209 Part 1 was D61611 a while ago.

The in-memory object files we obtain from JITLoaderGDB are not yet relocated. It looks like this used to happen on the LLDB side and my guess is that it broke with D38142. (However, it's hard to tell because the whole thing was broken already due to the bug in part 1.) The patch moved relocation resolution to a later point in time and didn't apply it to in-memory objects. I am not aware of any reason why we wouldn't resolve relocations per-se, so I made it unconditional here. On Debian, it fixes the bug for me and all tests in check-lldb are still fine.

Diff Detail

Event Timeline

sgraenitz created this revision.Nov 4 2020, 8:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 4 2020, 8:45 AM
sgraenitz requested review of this revision.Nov 4 2020, 8:45 AM
labath accepted this revision.Nov 5 2020, 1:04 AM

Seems reasonable to me.

I do wonder though if the jit could be changed to avoid relocation. I don't know what's the behavior of other JITs, but given that the jitted object is not going to get "linked" in the normal sense of the word, wasting memory on debug info relocations seems suboptimal...

This revision is now accepted and ready to land.Nov 5 2020, 1:04 AM

Thanks for having a look!

The JIT implementations in LLVM operate on relocatable object files, so someone needs to resolve them. MCJIT has a flag ProcessAllSections to control which sections will be loaded and relocated. As it is off by default, it will usually ignore sections that it doesn't need for execution. LLDB does it the other way around in ObjectFileELF::RelocateSection(), it only processes sections with the ".debug" prefix. This seems to be a reasonable distribution of tasks. In general, relocations are resolved in place, so it doesn't rise memory consumption.

I do wonder though if the jit could be changed to avoid relocation.

Well, I don't think we are anywhere close to optimizations like this, but it would be nice indeed. If we compile from bitcode on the JIT side, we could lookup external symbols at compile-time and don't produce relocations for them in the first place. I guess it would heavily reduce the number of relocations and potentially save time. On the other hand, thinking about concurrent compile jobs and cross-dependencies.. I can imagine it gets hairy quickly. Plus: the way it is now, we can cache the object files and reuse them thanks to position-independent code.

Thanks for having a look!

The JIT implementations in LLVM operate on relocatable object files, so someone needs to resolve them. MCJIT has a flag ProcessAllSections to control which sections will be loaded and relocated. As it is off by default, it will usually ignore sections that it doesn't need for execution. LLDB does it the other way around in ObjectFileELF::RelocateSection(), it only processes sections with the ".debug" prefix. This seems to be a reasonable distribution of tasks. In general, relocations are resolved in place, so it doesn't rise memory consumption.

But you still have to allocate memory to store the relocations. And that memory is not freed after the relocations are resolved, right?

I do wonder though if the jit could be changed to avoid relocation.

Well, I don't think we are anywhere close to optimizations like this, but it would be nice indeed. If we compile from bitcode on the JIT side, we could lookup external symbols at compile-time and don't produce relocations for them in the first place. I guess it would heavily reduce the number of relocations and potentially save time. On the other hand, thinking about concurrent compile jobs and cross-dependencies.. I can imagine it gets hairy quickly. Plus: the way it is now, we can cache the object files and reuse them thanks to position-independent code.

In a "normal" compilation, there are two kinds of relocations. The ones that are resolved by the normal (static) linker, and the ones that are resolved by the dynamic linker (loader). to make a module relocatable and have it interface with external code, you only need the second kind. The debug info relocations are all of the first kind, so they could be resolved (or, not emitted in the first place -- like what happens with MachO object files) without impacting this ability.

But that's for another discussion...

That's right, but I don't see how even the static ones could be resolved. Wouldn't it hard-wire the objects together? In the next session we want to load some object files from cache while others have changed.. Maybe one day I should check how ccache and the likes handle it.

IIRC OSO entries in MachO files point to the object file on disk, which allows LLDB to load them lazily? The DWARF parsing for ELF should follow the same laziness: each section is only relocated once we need it. It's just not loading the object lazily right? However, we couldn't do that even with OSO-style entries, because there is no "pull mechanism" in JITLoaderGDB :) Well, for now it just works.

I will leave the review here for another day or so, in case there's more feedback.

This revision was landed with ongoing or failed builds.Nov 10 2020, 2:39 AM
This revision was automatically updated to reflect the committed changes.