gimli-rs / gimli

A library for reading and writing the DWARF debugging format
https://docs.rs/gimli/
Apache License 2.0
831 stars 105 forks source link

Guidance on handling of relocations #708

Closed mstange closed 3 months ago

mstange commented 5 months ago

Gimli's documentation currently does not give much information on what to do about relocations, or on the cases where you need to think about them. I would greatly appreciate some guidance on those questions.

In my usage of gimli / addr2line I am not handling relocations anywhere. Should I? In which cases? How can I make examples that show the difference?

Here's my current understanding:

For "symbolication"-related information, I get the sense that I don't need to consider relocations. The debug sections needed for addr2line-type information are only present on the file system. These sections don't get loaded into process memory. What I do need to do is to translate the "lookup address" into an address that makes sense in the space of Stated Virtual Memory Addresses in the file (executable / shared library / object file) that is storing the debug information. But once my lookup address is in this SVMA space, all stored addresses in the debug info in that file should be correct within that SVMA space. If the file is the outcome of linking, then the linker has already rewritten all addresses from debug info in the source object files to be correct in the SVMA space of the linked binary / library. There's one case where I use addr2line with pre-link object files, and that's the macOS OSO "object map" case (example). Here too the only thing I translate is the lookup address, and I assume that the debug info in the object file makes sense in the object file's SVMA space. Is that a founded assumption? I have not run into trouble with this yet.

Do I need to handle relocations when using DWO or DWP files? Given this code in the dwarfdump example it seems that DWO file loading does not need to consider relocations. Am I interpreting this correctly?

For unwind info, when interpreting the contents of a .eh_frame section stored in a file: I am converting my current PC value into an address that makes sense in the file's virtual memory address space. Then I look up an FDE based on this SVMA, and simply assume that everything from here on out makes sense in SVMA space. The addresses I give to BaseAddresses are the SVMAs of the sections in the file. I suppose the one thing that might need to know about Actual Virtual Memory Addresses is the evaluation of DWARF expressions - Operation::Address mentions that an address needs to be relocated (but I'm not sure what this means). But I could imagine that a DWARF expression wants to get the contents of a register and then compare those contents with the start address of a function, for example, and that start address depends on where the dynamic linker placed the library in process memory.

For unwind info, when interpreting the contents of .eh_frame as loaded into the memory of a running process: I'm currently treating this case as if the .eh_frame contents in my process memory were stored byte-for-byte identically in the file. I am translating lookup addresses into SVMAs, just as if I was doing unwinding with a file. The addresses I give to BaseAddresses are SVMAs. (While writing this I first thought I got this wrong, because I am getting the section addresses from the load commands inside process memory, and I would have expected those addresses to have been translated into AVMAs by the dynamic linker, but some local testing confirms that those section addresses are indeed SVMAs.) Is this a recipe for trouble? Will the contents of .eh_frame in my running process be different from the contents of .eh_frame in the file? In what cases would the discrepancies be noticeable?

philipc commented 5 months ago

You only ever need to handle relocations if you are trying to parse a relocatable object file. These will sometimes partially work without handling relocations:

For linked files, SVMA/AVMA translation is enough because the DWARF parser knows which bytes are addresses without needing relocations to tell it. gimli will generally only use SVMA, and it's up to you to translate the addresses it returns.

Mach-O OSO handling is fine because the relocations are all section references with implicit addends and a base SVMA of 0.

DWO/DWP handling is fine because the DWARF is split such that the DWO/DWP only contain the DWARF that doesn't need relocating.

.eh_frame should be the same in memory and file. I don't think that the dynamic loader modifies it. It's correct for BaseAddresses to be using SVMA and not AVMA.

Operation::Address says relocate, but it really means translate SVMA/AVMA if needed. Handling actual relocations wouldn't be possible here because it doesn't provide enough info to do that (you need the address of the value to be relocated). I'm not sure that SVMA/AVMA translation is even needed here; it may be possible to delay the translation until EvaluationResult::RequiresMemory. This isn't something I have experience with.

mstange commented 5 months ago

Thank you so much for this comprehensive reply!

philipc commented 3 months ago

I found that when running dwarfdump on Mach-O object files, it's nice to apply some of the relocations so that the dumped addresses match the symbol addresses (https://github.com/gimli-rs/object/pull/675/files), but for samply's case, the better way is to skip doing the relocations and add the symbol address yourself, as I think you are doing.

I don't think there's any action to take for this issue, but can reopen if needed.