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:
LD_PRELOAD
to all programs on the host;The only two “solutions” that I’ve been able to come up with are ugly hacks:
LD_PRELOAD
all programs, all of the time, which seems slow and fragile; orlibfreetype.so.6.12.3
to libxxxxtype.so.6.12.3
; then
libxxxxtype.so.6.12.3
to libxxxxtype.so.6
;libxxxxtype.so.6
; andlibfreetype.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.
A shared object is an indivisible unit that is generated from one or more relocatable objects. Shared objects can be bound with dynamic executables to form a runable process. As their name implies, shared objects can be shared by more than one application.
A shared library or shared object is a file that is intended to be shared by multiple programs. Symbols used by a program are loaded from shared libraries into memory at load time or runtime.
so (short for "shared object"). Shared libraries are the most common way to manage dependencies on Linux systems. These shared resources are loaded into memory before the application starts, and when several processes require the same library, it will be loaded only once on the system.
These shared objects are named with the extension . fso and are stored on the server in a subdirectory of the application that created the shared object. Adobe Media Server creates these directories automatically; you don't have to create a directory for each instance name.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With