criblio / appscope

Gain observability into any Linux command or application with no code modification
https://appscope.dev
Apache License 2.0
266 stars 33 forks source link

4.2.0 Add support for uclibc/mulibc/Alpine #143

Closed monikamarko closed 3 years ago

monikamarko commented 3 years ago

REL: 4.2.0 Ref: C-3957

coccyx commented 3 years ago

@iapaddler @jrcheli to spike on effort

iapaddler commented 3 years ago

Go Dynamic Execs on ld-musl.so

Go dynamic apps lod and run without apparent issue. However, the pre-loaded library constructor is never called. We can interpose, but we aren't emitting any detail.

The root cause for this behavior is a subtle difference between the ld-musl.so and ld-linux.so (glibc) dynamic linker behavior. glibc seems to call shared object constructors in dynamic linker context (_dl_start_user), while musl libc executes constructors in libc_start_main called by crt1.c. The musl libc dynamic linker loads the application along with preloaded and dependent shared objects. Then it jumps to the application entry point. Typically, the application entry point is code defined in crt1.c, when linked with a musl compatible tool chain. But, in the case when the application has been linked with the internal Go linker, libc_start_main is never invoked. The internal Go linker does not link against crt1.o and just calls the main function directly from the startup code in the executable.

iapaddler commented 3 years ago

Go Dynamic Execs

Given that the library is loaded and preload is possible, an approach could be to interpose a function that the Go runtime calls during it's init and call the library constructor directly.

With experimentation, it can be seen that the Go runtime calls pthread_create() as part of init. Interposing pthread_create() and calling the constructor, ensuring that it's only called once, appears to work. We see Go functions interposed and data is emitted.

As part of library init we get the process name. Both glibc and libmusl use the global variable _program_invocation_shortname. However, since we are early in the init procedure or because lib constructors weren't called, the variable isn't initialized when we need it. So, we drop back to getting the exe path and basename() the string to get the process name.

iapaddler commented 3 years ago

Go Static Execs on Musl

Go static execs segfault when started with ldscope or preloaded.

It turns out that init of the function pointer object g_fn fails. The use of dlsym() with the handle RTLD_NEXT has been the default for all cases. However, the libmusl dlsym() doesn't resolve symbols using RTLD_NEXT when the lib is loaded from dlopen(); the case for Ge static execs. It sort of makes sense in that the handle indicates the use of the next objects symbol table. It's actually not clear in the man page how this is supposed to work. It appears that glibc interprets RTLD_NEXT behavior more broadly than libmusl.

The use of RTLD_DEFAULT works. The symbols for the function pointer object are resolved in the Go static case on libmusl. Added a get handle function to determine which handle to use when populating the functions. Changed several places where we were using internal function pointers to use the common function pointer object.

iapaddler commented 3 years ago

Libmusl Doesn't Support Direct Execution

A glibc shared lib can be executed from the command line. It's a bit of hackery to get this to work. It's a nice option. It's used in libscope to emit help and config messages. Not all loaders support this model. ld-musl.so does not.

Might need to move the help and config output to ldscope.

iapaddler commented 3 years ago

No libresolv in a Musl Distro

We use libresolve to parse DNS answer packets. Looks like this will need to be modified without the use of libresolve.

iapaddler commented 3 years ago

Console I/O and libmusl

On glibc we had to hook an internal function in order to ensure we emitted all console I/O. The same function doesn't exist in libmusl. Depending on the public interfaces is better in libmusl, but it appears that there is missing console detail. Will need to look into libmusl code.

iapaddler commented 3 years ago