Interposing part of a shared object by soname - linux

I’ve written a shared object that modifies the arguments to FreeType’s FT_Load_Glyph and FT_Render_Glyph functions, currently by interposing it with LD_PRELOAD and dlsym.
This works fine, but I’m curious to know whether or not there’s a way to make these changes:
to all programs that use FreeType on a given host (running e.g. Debian);
without clobbering any programs that aren’t actually linked to FreeType;
without simply applying an LD_PRELOAD to all programs on the host;
without requiring any maintenance unless FreeType’s soname is changed; and
without modifying any of FreeType’s files, nor those of any programs on the host.
The only two “solutions” that I’ve been able to come up with are ugly hacks:
to LD_PRELOAD all programs, all of the time, which seems slow and fragile; or
to copy e.g. libfreetype.so.6.12.3 to libxxxxtype.so.6.12.3; then
patch the soname in libxxxxtype.so.6.12.3 to libxxxxtype.so.6;
link the interposing shared object against libxxxxtype.so.6; and
install the shared object as e.g. libfreetype.so.6.999.
I’d essentially like to transparently patch a couple of functions in a shared object, while letting the remaining functions through, without necessarily having access to the source of the shared object or the programs that use it, but if I make a fake shared object with the soname libfreetype.so.6, I can’t see a clean way to link it to (or dlopen) the real libfreetype.so.6.
This is my first real experiment with shared libraries, so please bear with me if this question makes some incorrect assumptions, or just makes no sense.

Can you try to use uprobes to dynamically steal control from some functions?
Check http://www.brendangregg.com/blog/2015-06-28/linux-ftrace-uprobe.html
uprobes: user-level dynamic tracing, which was added to Linux 3.5 and improved in Linux 3.14. It lets you trace user-level functions; for example, the return of the readline() function from all running bash shells, with the returned string:
# ./uprobe 'r:bash:readline +0($retval):string'
Tracing uprobe readline (r:readline /bin/bash:0x8db60 +0($retval):string). Ctrl-C to end.
bash-11886 [003] d... 19601837.001935: readline: (0x41e876 <- 0x48db60) arg1="ls -l"
bash-11886 [002] d... 19601851.008409: readline: (0x41e876 <- 0x48db60) arg1="echo "hello world""
bash-11886 [002] d... 19601854.099730: readline: (0x41e876 <- 0x48db60) arg1="df -h"
bash-11886 [002] d... 19601858.805740: readline: (0x41e876 <- 0x48db60) arg1="cd .."
bash-11886 [003] d... 19601898.378753: readline: (0x41e876 <- 0x48db60) arg1="foo bar"
^C
Ending tracing...
And http://www.brendangregg.com/blog/2015-07-03/hacking-linux-usdt-ftrace.html
There were also other solutions of tracing user-space functions, like ftrace, systemtap, dtrace, lttng. Some of them need recompilation and defining tracing points statically in the program; and uprobes are "user-level dynamic tracing".
Some links about uprobes:
https://events.linuxfoundation.org/slides/lfcs2010_keniston.pdf Uprobes: User-Space Probes (2010)
https://www.kernel.org/doc/Documentation/trace/uprobetracer.txt
https://lwn.net/Articles/499190/ "Uprobes in 3.5", Jonathan Corbet (2012)
There is handler of uprobes which has pt_regs. As said in last link: "Uprobes thus implements a mechanism by which a kernel function can be invoked whenever a process executes a specific instruction location." and it suggests that uprobes may replace some ptrace/gdb based solutions; so there is a possibility to change execution of any program hitting active uprobe, by changing its eip/rip (PC) register.
You may try some other dynamic instrumentation tools, like pin or dyninst; but they are designed for per-process usage.

Another solution would be to make system wide "overlay" for lib, with custom libfreetype and then proxying unmodified methods to real lib.
You have to make custom lib compatible with real one. You can to that by using dlopen with absolute path (eg. dlopen("/usr/lib64/libfreetype.so.6")),
copying definitions of real, exported functions and proxying them with dlsym.
It think that for ease of maintenance you could event replace proxied argument types with simple void*. You would only need to make changes when freetype functions change (arguments count, function names).
To create lib "overlay", you could install custom lib into eg. "/opt/myapp/lib64/libfreetype.so.6", then add this path to dynamic linker run time paths.
You may have to create symlinks for other versions or compile new custom lib if original implementation changes. Whatever is needed to shadow real lib and keep other apps working :)
Google says that to change run time loading paths on Debian you have to simply edit /etc/ld.so.conf. Add /opt/myapp/lib64 path at the beginning so it will be checked first.
Now any app searching for freetype should load your lib, you can check it with ldd <path to app>.
I can think of just one case when this solution will not work: if app is loading bundled libfreetype or loading it by full path, not by name.

to LD_PRELOAD all programs, all of the time, which seems slow and fragile
That's a good solution (for what you want). I don't see a better one.
It's not fragile. It provides information to the runtime linker in a documented way. You're not bonking on anything, pretending something isn't what it is. You're just altering the preference hierarchy for function-name resolution.
It's not slow. The linker has to do something sometime. It's got to check if LD_PRELOAD is defined, which in any case is a user-space operation. So it will follow that path, and load your library before doing a bunch of other work. I'd be astonished if the time was even measurable under normal circumstances.
There are two concerns I'd have, but they're orthogonal to the technique. The code actually has to work in all cases, and you have to dig into the process-creation framework a bit to make sure LD_PRELOAD really is defined everywhere. Other than that, ld.so defines its environment variables precisely for your intended use. Who's to argue?

Related

Passively inject shared object to a specific executable

I'm interested on injecting my own shared object to any future to run instance of a specific executable.
This executable gives me hard time since it is executed a lot and quite frequently which makes me reluctant to inject my code actively (using ptrace()).
The best option I thought of is to use some kind of ELF patcher (maybe https://github.com/NixOS/patchelf ?) in order to make the executable depends on my code. This option discourages me since I'm afraid any bug in the code will lead to an executable corruption.
Any other suggestion?
Rules:
Root permissions are granted
I can't load a kernel module
The injection must be inline - meaning, before the executable entry point was called (main())

How to link kernel functions to user-space program?

I have a user-space program (Capstone). I would like to use it in FreeBSD kernel. I believe most of them have the same function name, semantics and arguments (in FreeBSD, the kernel printf is also named printf). First I built it as libcapstone.a library, and link my program with it. Since the include files are different between Linux user-space and FreeBSD kernel, the library cannot find symbols like sprintf, memset and vsnprintf. How could I make these symbols (from FreeBSD kernel) visible to libcapstone.a?
If directly including header files like <sys/systm.h> in Linux user-space source code, the errors would be like undefined type u_int or u_char, even if I add -D_BSD_SOURCE to CFLAGS.
Additionally, any other better ways to do this?
You also need ; take a look at kernel man pages, eg "man 9 printf". They list required includes at the top.
Note, however, that you're trying to do something really hard. Some basic functions (eg printf) might be there; others differ completely (eg malloc(9)), and most POSIX APIs are simply not there. You won't be able to use open(2), socket(2), or fork(2).

Is a core dump executable by itself?

The Wikipedia page on Core dump says
In Unix-like systems, core dumps generally use the standard executable
image-format:
a.out in older versions of Unix,
ELF in modern Linux, System V, Solaris, and BSD systems,
Mach-O in OS X, etc.
Does this mean a core dump is executable by itself? If not, why not?
Edit: Since #WumpusQ.Wumbley mentions a coredump_filter in a comment, perhaps the above question should be: can a core dump be produced such that it is executable by itself?
In older unix variants it was the default to include the text as well as data in the core dump but it was also given in the a.out format and not ELF. Today's default behavior (in Linux for sure, not 100% sure about BSD variants, Solaris etc.) is to have the core dump in ELF format without the text sections but that behavior can be changed.
However, a core dump cannot be executed directly in any case without some help. The reason for that is that there are two things missing from a simple core file. One is the entry point, the other is code to restore the CPU state to the state at or just before the dump occurred (by default also the text sections are missing).
In AIX there used to be a utility called undump but I have no idea what happened to it. It doesn't exist in any standard Linux distribution I know of. As mentioned above (#WumpusQ) there's also an attempt at a similar project for Linux mentioned in above comments, however this project is not complete and doesn't restore the CPU state to the original state. It is, however, still good enough in some specific debugging cases.
It is also worth mentioning that there exist other ELF formatted files that cannot be executes as well which are not core files. Such as object files (compiler output) and .so (shared object) files. Those require a linking stage before being run to resolve external addresses.
I emailed this question the creator of the undump utility for his expertise, and got the following reply:
As mentioned in some of the answers there, it is possible to include
the code sections by setting the coredump_filter, but it's not the
default for Linux (and I'm not entirely sure about BSD variants and
Solaris). If the various code sections are saved in the original
core-dump, there is really nothing missing in order to create the new
executable. It does, however, require some changes in the original
core file (such as including an entry point and pointing that entry
point to code that will restore CPU registers). If the core file is
modified in this way it will become an executable and you'll be able
to run it. Unfortunately, though, some of the states are not going to
be saved so the new executable will not be able to run directly. Open
files, sockets, pips, etc are not going to be open and may even point
to other FDs (which could cause all sorts of weird things). However,
it will most probably be enough for most debugging tasks such running
small functions from gdb (so that you don't get a "not running an
executable" stuff).
As other guys said, I don't think you can execute a core dump file without the original binary.
In case you're interested to debug the binary (and it has debugging symbols included, in other words it is not stripped) then you can run gdb binary core.
Inside gdb you can use bt command (backtrace) to get the stack trace when the application crashed.

Which libraries appear in /proc/$PID/pmaps?

On Linux you can inspect /proc/$PID/pmaps to see the libraries loaded by a particular program, and a program can open /proc/self/pmaps to examine the libraries it itself has loaded.
I know pmaps will only contain dynamic libraries, and obviously the kernel can't predict which libraries we might dlopen at a later point, so I expect those aren't included in /proc/self/maps. But I'm unsure of a few other other scenarios:
Are libraries that have been linked at build time but we haven't called any function in yet included? My understanding is the Linux delays linking symbols until the first time they are used, so I'm not sure if they'll show up.
Does pmaps contain all the libraries used recursively? E.g. if I look at each library in pmaps and run ldd on it, and then run ldd on those, ad nauseum, I shouldn't find any new libraries that weren't in the original pmaps? I tried this on a couple binaries and it appears to be so but maybe I'm getting lucky.
Are libraries that have been linked at build time but we haven't called any function in yet included?
Yes: the runtime loader will mmap every library that your executable directly depends on, before your program starts running.
You can find the list of such libraries by running
readelf -d a.out | grep NEEDED
Does pmaps contain all the libraries used recursively?
Yes: if a library that you directly depend on itself depends on some other library, the runtime loader will mmap the recursive dependencies as well.
My understanding is the Linux delays linking symbols until the first time they are used
That is mosty correct for function symbols, but false for data symbols, which can't be resolved lazily.
Also, whether the symbols are resolved lazily or not depends on LD_BIND_NOW environment variable, and on an equivalent setting in the executable dynamic section, controlled by -znow linker flag.
None of that changes the mmap pciture though; if you have a DT_NEEDED entry for foo.so in your dynamic section, then foo.so will be mmaped (and will show in /proc/self/*map*) independent of lazy or non-lazy resolution.
/proc/$pid/maps is not only going to list libraries that are loaded, but also ALL other mapped memory segments.
Read this thread and the article in there:
Understanding Linux /proc/id/maps

Why does a shared object fail if it has extra symbols compared to the original

I have a stripped ld.so that I want to replace with the unstripped version (so that valgrind works). I have ensured that I have the same version of glib and the cross compiler.
I have compiled the shared object, calling 'file' on it shows that it is compiled correctly (the only difference with the original being the 'unstripped' and being about 15% bigger). Unfortunately, it then causes a kernel panic (unable to init) on start up. Stripping the newly compiled .so, readelf-ing it and diff-ing it with the original, shows that there were extra symbols in the new version of the .so . All of the old symbols were still present, so what I don't understand is why the kernel panics with those extra symbols there.
I would expect the extra symbols to have no affect on the kernel start up, as they should never be called, so why do I get a kernel panic?
NB: To be clear - I will still need to investigate why there are extra symbols, but my question is about why these unused symbols cause problems.
The kernel (assuming Linux) doesn't depend on or use ld.so in any way, shape or form. The reason it panics is most likely that it can't exec any of the user-level programs (such as /bin/init and /bin/sh), which do use ld.so.
As to why your init doesn't like your new ld.so, it's hard to tell. One common mistake is to try to replace ld.so with the contents of /usr/lib/debug/ld-X.Y.so. While that file looks like it's not much different from the original /lib/ld-X.Y.so, it is in fact very different, and can't be used to replace the original, only to debug the original (/usr/lib/debug/ld-X.Y.so usually contains only debug sections, but none of code and data sections of /lib/ld-X.Y.so, and so attempts to run it usually cause immediate SIGSEGV).
Perhaps you can set up a chroot, mimicking your embedded environment, and run /bin/ls in it? The error (or a core dump) this will produce will likely tell you what's wrong with your ld.so.

Resources