GJDuck / e9patch

A powerful static binary rewriting tool
GNU General Public License v3.0
978 stars 65 forks source link

How are rewritten libraries initialized? #81

Closed kha-dinh closed 1 month ago

kha-dinh commented 1 month ago

Hi, I am using e9patch to rewrite binaries to be loaded on a non-Linux unikernel (Unikraft to be specific). So far, I found that e9init uses Linux-specific features like proc/self/exe. To simplify things, in the kernel's elf loader, I simply rewrite the offset in the entry of the binary file with offset to my kernel's e9init implementation. However, this is more complicated in the case of rewritten libraries since libc's dynamic loader loads them (kernel elf loader only loads the main binaries and ld-linux-x86-64.so.2). So my question is, when and how is e9init invoked for rewritten libraries? Are there better ways to do this, or do I need to make some changes to e9patch?

kha-dinh commented 1 month ago

After some thinking, I think the best course of action is to implement custom system calls for the missing functionalities and a new e9loader that calls it.

GJDuck commented 1 month ago

For executables, e9init() will replace the entry point, and for shared objects, e9init() will replace the first DT_INIT or DT_INIT_ARRAY in the dynamic section. The dynamic loader calls these functions in sequence when the shared object is loaded, meaning that e9init() should be called. I think this method should work on any system that supports ELF and ld.so dynamic loading, provided the loader implements standard features.

Currently E9Patch loader only "officially" supports Linux, as well as proof-of-concept support for Windows. And e9init() does indeed use low-level Linux features, like proc/self/exe and /proc/self/map_files/, to find certain things necessary for the implementation. So if you want to port E9Patch to a new system, this code likely needs to be rewritten (like it was rewritten for Windows). Fortunately, the init code is not overly complicated.

kha-dinh commented 1 month ago

Thanks for the response. I was able to port e9patcher support to my system quickly. I now have another question: Is there any mechanisms that prevent overlapping trampolines. Do I need to ensure that trampolines of every ELF file occupy different address ranges (e.g., using --mem-lb and --mem-ub)?

GJDuck commented 1 month ago

Ensuring that trampolines do not overlap is already handled by E9Patch. Generally, it inspects the ELF file to determine which memory locations are occupied, then allocates trampolines only in that memory. There can be some exceptions, like ASAN with non-PIE, what competes for the same "free" space. For these cases, the --mem-lb/--mem-ub options can help. The trampolines for different ELF binaries should also not overlap, under Linux at least.

So this should already be handled, unless I misunderstood the question.

GJDuck commented 1 month ago

This should be resolved.