elf icon indicating copy to clipboard operation
elf copied to clipboard

License?

Open pfalcon opened this issue 5 years ago • 8 comments

Thanks for this cute hack! Can you please clarify its license?

pfalcon avatar Mar 06 '20 22:03 pfalcon

It is under MIT License.

MikhailProg avatar Mar 10 '20 05:03 MikhailProg

It is under MIT License.

Thanks for reply! Would you mind dropping a LICENSE file into the repo with your copyright and license text, to avoid any future confusion?

Just to explain my interest, I was looking for a solution to a proverbial "do dlopen() from a static executable" problem (for a context, dlopen() is usually disabled in this case). I followed many ideas and deadends with various ELF loaders out there on github, and in the end, one of ideas applied against your code worked. So, I'd be looking at publishing that as a separate project, and would like to ensure that license and credit to you are properly given. Thanks!

pfalcon avatar Mar 10 '20 09:03 pfalcon

I've uploaded LICENSE file. I want to clarify one important thing. Usually such standalone loaders need to relocate itself before loading requested binaries. This loader is written the way to avoid any type of relocation. As an example errno is static variable and is addressed from other functions as a macro (z_errno()) which deferences a pointer of static variable. If it were a simple shared int value it would produce an unnecessary reloc. The other case is outptr = outbuf under init guard in kdoprnt(). If outptr was assigned with outbuf address in outptr declaration at the beginning of the file it would also produce a reloc. All described cases are relevant only if the loader is standalone so probably it's not your case but just warn you in advance. So when porting as standalone check the loader with readelf/objdump for created relocs.

MikhailProg avatar Mar 10 '20 12:03 MikhailProg

Usually such standalone loaders need to relocate itself before loading requested binaries. This loader is written the way to avoid any type of relocation.

Thanks for this info. That's not something I had problems with, or even gave a thought to. I guess, you mean a completely "bare metal" environment, where loader itself not loader by a system ELF loader, like would be the case when run under Linux.

Anyway, my usecase is kinda opposite - I'm not interested in "baremetal" setting (-nostdlib), but actually be built against a libc (and be part of a larger application). Where interest lies is to load ELF .so built against a "foreign" libc, which may not match libc the application was built with. E.g., loading a glibc dynamically linked .so in a static application built against musl. But I appreciate ingenuity you put into implementing this to be as baremetal as possible, and preserve that config in my changes too.

I intend to submit a couple of non-invasive fixes as PR, first one already went: https://github.com/MikhailProg/elf/pull/2 . Btw, I understand that this is an old project project of yours, and you may be not interested in patches, let me know then. (But I wanted to mention, because due to vanity matters, I intend to set up my fork not as a github-level fork, so people interested in your project might have harder time discovering it.)

pfalcon avatar Mar 11 '20 09:03 pfalcon

Btw, I understand that this is an old project project of yours, and you may be not interested in patches, let me know then.

The project is even 3+ years older than the initial commit but PRs are always welcomed.

Where interest lies is to load ELF .so built against a "foreign" libc, which may not match libc the application was built with. E.g., loading a glibc dynamically linked .so in a static application built against musl.

Am I right that except your .so you also manually load glibc and resolve all symbols extracting relocation info from ELF dynamic section?

MikhailProg avatar Mar 11 '20 10:03 MikhailProg

Am I right that except your .so you also manually load glibc and resolve all symbols extracting relocation info from ELF dynamic section?

That was the initial idea, yeah - if we want to load dynamic lib set, then we need to load the entire set ourselves, right? I started trying to separate dynamic loader out of musl libc, but fount it to be relatively tightly coupled with the rest of libc. Next, I spent enough time with https://github.com/GregorR/gelfload, just to find out that it segfaults trying to load an executable doing anything beyond purely trivial. Then I looked at https://github.com/robgjansen/elf-loader , and saw the problem: glibc and its ld.so are even more tightly coupled than musl. ld.so, being executed at its entry point, initialized a lot of data structures, and glibc may access them at any time with seemingly unrelated functions and crash. The last project does crazy things to work around that, effectively trying to mirror layout of such ld.so structures, but they're of course glibc version dependent. So, "load everything on your own" turned out to be a dead-end.

It's clear that there's no way around using the original dynamic loader intended for use with a particular binary, and trying your project, which does just that, showed that all segfaults I saw were indeed gone. The obvious problem is that ld.so loads executables, whereas I'm interested in loading shared libs. ld.so unconditionally starts executable, which then unconditionally calls syscall to terminate. Pondering about it, a crazy/dirty idea of using setjmp/longjmp to "return" from executable came up. And indeed, it worked!

Of course, that's still not ideal, because I can't just drop a prebuilt static executable on any system, and start loading that system's shlibs, I first actually need to build a proxy executable against shlibs of that system. But given the way ld.so's appear to be implemented in such systems, that's the only scalable ("blackbox") way to deal with it, the only other alternative is to patch every ld.so case.

pfalcon avatar Mar 14 '20 16:03 pfalcon

Thank you for your detailed reply.

Pondering about it, a crazy/dirty idea of using setjmp/longjmp to "return" from executable came up. And indeed, it worked!

As I can imagine you use argv to pass jmp_buf pointer to the proxy and to set some return values (e.g dl* function pointers) just before longjmp.

Of course, that's still not ideal, because I can't just drop a prebuilt static executable on any system, and start loading that system's shlibs.

Since your initial executable is static it sounds like your proxy executable is compiled as PIE to avoid images address overlapping.

Thanks again that you continue to reply in the thread.

MikhailProg avatar Mar 16 '20 05:03 MikhailProg

Since your initial executable is static it sounds like your proxy executable is compiled as PIE to avoid images address overlapping.

Good catch, I actually didn't give a thought to it, but my Ubuntu's gcc indeed produces PIE executables by default. Doing that trick on a random embedded Linux system (which is ultimate, but remote goal) would take more hassle.

FWIW, I posted my code at https://github.com/pfalcon/foreign-dlopen .

pfalcon avatar Mar 22 '20 11:03 pfalcon