Tool for modifying dynamic section of an ELF binary - c

Is there a tool for modifying the shared library entries in the dynamic section of an ELF binary? I would like to explicitly modify the shared library dependencies in my binary (i.e. replace path to existing library with a custom path)

replace path to existing library with a custom path
If this is your own library, then you probably linking it like that:
$ cc -o prog1 -l/full/path/to/libABC.so prog1.o
instead of the proper:
$ cc -o prog1 -L/full/path/to/ -lABC prog1.o
The first approach tells Linux linker that application needs precisely that library, only that library and no override should be possible. Second approach tells that application needs the library which would be installed somewhere on the system, either in the default library path or one pointed by the $LD_LIBRARY_PATH (would be looked up during run-time). -L is used only during link-time.
Otherwise, instead of patching the ELF, first check if you can substitute the library using a symlink. This is the usual trick: it is hard to modify executable afterward, but it is very easy to change where to the symlink points.

patchelf is what you want
$ patchelf --replace-needed LIB_ORIGIN LIB_NEW ELF_FILE
To see the effect
$ readelf -d ELF_FILE
Install the tools is easy:
$ sudo apt-get install patchelf readelf

You may want to check the LD_LIBRARY_PATH environment variable.

If you look at the .dynsym section in Linux via readelf, you'll just see something like:
1: 0000000000000000 163 FUNC GLOBAL DEFAULT UND fseek#GLIBC_2.2.5 (2)
which just contains a symbolic name of the library. However, if you include the dynamic loader info, you get:
libc.so.6 => /lib/libc.so.6 (0x00002ba11da4a000)
/lib64/ld-linux-x86-64.so.2 (0x00002ba11d82a000)
So as mentioned, the absolute easiest thing to do (assuming you're doing this for debugging, and not forever) would just be to create a new session, export your custom path in front of the existing LD_LIBRARY_PATH, and go from there.

Related

How to debug "cannot open shared object file: No such file or directory"?

On our project at work, where I use a build system managed by another team, I am running into an issue where I am able to compile and link the binary, but then when executing it, it complains about being unable to load a shared object file, it should never have been linked against:
/home/me/build/bin/executable: error while loading shared libraries: libicui18n.so.52.1: cannot open shared object file: No such file or directory
The curious part is, that the only instance of the file name libicui18n.so.52.1 I can even find on my machine are /opt/onlyoffice/desktopeditors/libicui18n.so.52.1 and /home/me/.local/opt/foxitsoftware/foxitreader/lib/libicui18n.so.52.1, which I definitely don't link against, at least according to linker command executed by the build system:
It does not occur as any of the explicit /absolute/path/to/libsomething.so.42.3 files.
Nothing related is given as -L option.
Nothing related is given as /absolute/path/to/libsomething.a file.
Nothing related is in $LD_LIBRARY_PATH or $LIBRARY_PATH.
The Intel Fortran compiler has no reason to include those in the default paths.
So now I am wondering, how else the libicui18n.so.52.1 may be linked against, and how I may go about debugging such an issue.
how else the libicui18n.so.52.1 may be linked against
ELF libraries can specify "what name should be used at runtime" independently of the name of the library itself. Example:
gcc -fPIC -shared -o libt.so t.c -Wl,--soname=libfoo.so.1.2.3
gcc main.c -L. -lt
./a.out
./a.out: error while loading shared libraries: libfoo.so.1.2.3: cannot open shared object file: No such file or directory
Here, the library is built in a way which requires libfoo.so.1.2.3 at runtime, although no such library exists. It is up to the packaging system to provide such library (or a symlink).
You can examine a library to see what name it expects to be used at runtime like so:
readelf -d libt.so | grep SONAME
0x000000000000000e (SONAME) Library soname: [libfoo.so.1.2.3]
You can examine the list of libraries a.out requires at runtime like so:
readelf -d ./a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libfoo.so.1.2.3]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
how I may go about debugging such an issue.
As above. Your next question is probably "how can I fix this issue?".
Since libicui18n.so.52.1 is installed, but not installed into a directory which the dynamic linker will search by default, all you have to do is tell your binary to add the non-standard /opt/onlyoffice/desktopeditors directory the list of directories to search.
You do that by adding -Wl,-rpath=/opt/onlyoffice/desktopeditors to your executable's link line.
You can also add /opt/onlyoffice/desktopeditors to LD_LIBRARY_PATH, but using -Wl,-rpath is preferred.

Can't link dynamic library when running C language with root privileges?

I have a C language file named testFunc.c that uses the dynamic library libCfunc.so. This library is placed under the path /home/cuiyujie/workspace/library/lib.
I added this path to library path export LD_LIBRARY_PATH=/home/cuiyujie/workspace/library/lib:$LD_LIBRARY_PATH
When I use the following command to compile, it can be compiled normally.
gcc testFunc.c -lCfunc -lm -O0 -g -o testFunc
But when I run it, if I use ./testFunc, it can run normally.
But if I use sudo ./testFunc, he will get the following error.
./testFunc: error while loading shared libraries: libCfunc.so: cannot
open shared object file: No such file or directory
I found on Google that when root is used, the value of the LD_LIBRARY_PATH variable is ignored.
I used the following command to recompile. Specify the library path when compiling.
gcc testFunc.c -L/home/cuiyujie/workspace/library/lib -lCfunc -lm -O0 -g -o testFunc
When I continue to run with the sudo ./testFunc command, the same error still appears.
The reason why I need to execute with root is because I need to read some inquiries that only root privileges can read. I want to get the physical address of certain variables, so I need to read the mapping file of the process, which requires root privileges.
The linker flag -L just tells the linker where to look for the library (or a library stub, if such is used) at link time. It does not influence the library search path at runtime.
For a system wide installed library you'd place the library in a place that's been configured in the global linker search path, set through /etc/ld.so.conf and files in /etc/ld.so.conf.d.
However it is perfectly possible to specify additional search paths specific to certain binaries by means of the so callled rpath. The rpath is set using the (you guessed it) rpath extra linker flag -Wl,-rpath.
Linking the program with
gcc -o … -Wl,-rpath='${ORIGIN}' …
would make the ELF interpreter (the piece of code that loads ELF binaries and does dynamic linkage) to also look for additional libraries right next to the program binary. You can read up on the details of rpaths in the ld.so manpage.
Be aware that rpaths invoke certain security considerations.
LD_LIBRARY_PATH is an environment variable, and all environment variables exist separately for each user.
When you export it under your regular user, but then run the executable as root using sudo, the export does not exist for the new process.
You can preserve the environment of your user with the -E parameter:
sudo -E ./testFunc
or you can specifically preserve the LD_LIBRARY_PATH variable like this:
sudo LD_LIBRARY_PATH=/home/cuiyujie/workspace/library/lib:$LD_LIBRARY_PATH ./testFunc

How can I set an executable's rpath and check its value after building it?

I'm building the following file:
int main()
{
return 0;
}
with the following flags:
g++ -o main -Wl,-rpath,'$ORIGIN' main.cpp
but the rpath flag is doing nothing. When I execute the following commands:
objdump -x main | grep -i rpath
readelf -a main | grep -i rpath
I obtain nothing (RPATH is not defined).
What I'm doing wrong?
EDIT
I have tried to do the above with a different binary using the following cmake flags:
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
I have moved the executable to a different machine, and placed a dynamic library that it needs to 'dlopen' in the same folder. It has worked (and I'm 100% sure this is because rpath, since before applying the above cmake flags the executable didn't worked).
Still, using the above two commands to check rpath (objdump and readelf) I still don't see anything.
If I didnt miss something here, you are not linking any libs in your build command.
Lets say you want to link libusb.so shared library, which is located in libusb sub-folder of your current folder where is main.cpp.
I will not take any details here, about soname, linkname of lib etc, just to make clear about rpath.
rpath will provide runtime linker path to library, not for linktime, cause even shared library need to be present(accessible) in compile/link time. So, to provide your application loader with possibility to look for needed library in start time, relatively to your app folder, there is $ORIGIN variable, you can see it with readelf but only if you link some library with $ORIGIN in rpath.
Here is example based on your question:
g++ main.cpp -o main -L./libusb -Wl,-rpath,'$ORIGIN/libusb' -lusb
As you see, you need to provide -L directory for compile/link time search, and rpath for runtime linker. Now you will be able to examin all needed libs for your app using readelf and location for search.

How to load C library functions into assembly, and use assembly library functions in another C or assembly project

Currently I am building a foo.h and foo.c with:
$ clang -I . -dynamiclib \
-undefined dynamic_lookup \
-o foo.dylib foo.c
I am able to use this in other C libraries like this:
clang -I . -dynamiclib \
-undefined dynamic_lookup \
-o bar.dylib bar.c foo.dylib
I would like to use this library in an assembly project.
$ nasm -f macho64 test.asm \
&& ld -e start -macosx_version_min 10.13.0 -static -o test test.o foo.dylib
$ ./test
ld: warning: foo.dylib, ignoring unexpected dylib file
Wondering how I link together the C -> asm system to get the C functions working in asm. Then I would like to go further and use that compiled asm to use in either a C or asm project, so wondering how to do that.
When using the assembly in C, I would like for you to basically get functions and import #include "myassembly.h" or something like that, so it feels like a real library. Then you have a function like myfunc which is defined in assembly, but you can use it in c as myfunc(1, 2, 3); sort of thing.
If I change it from static to dynamic linking with the -lSystem flag (and removing -static), I get this:
dyld: Library not loaded: foo.dylib
Referenced from: ./test
Reason: image not found
make: *** [...] Abort trap: 6
You're specifying -static which means:
-static Produces a mach-o file that does not use the dyld. Only used
building the kernel.
dyld is the dynamic loader. If you're not using the dynamic loader, you can't use dynamic libraries.
Update for edited question:
When a dylib is created, it gets an "install name". When an executable is linked to that dylib, the executable stores the install name of the dylib in its reference to it. (Note, it does not store the link-time path of the dylib file it linked against.) When the executable is loaded, the dynamic loader looks for the dylib using the install name it recorded, by default.
You can specify the install name using the -install_name <name> option to the linker. It could be the absolute path to where you expect the library to be installed (e.g. /usr/local/lib/foo.dylib), if you expect it to be installed in a fixed location. Often, though, that's not useful. You want a more flexible means for the dynamic loader to find the dylib.
The dynamic loader understands certain special path prefixes on install names to support such flexibility. See the dyld(1) man page. For example, if you specify an install name of #executable_path/foo.dylib then, at load time, the loader will look next to the executable for the library.
You can see the install name of a dylib by using otool -D foo.dylib. Your dylib may not have an install name, in which case its effective install name is just its file name with no path.
If the loader doesn't find the library by using its install name, it has a search strategy. By default, it looks in ~/lib:/usr/local/lib:/lib:/usr/lib. You can use some environment variables to alter the search strategy. For example, you can set DYLD_FALLBACK_LIBRARY_PATH to a colon-delimited list of directories to search, instead. These environment variables are also listed in the dyld(1) man page.

Is there a way to unhide hidden-visibility symbols with GNU binutils?

I'm working on a script to make uClibc usable on an existing glibc-targetted gcc/binutils toolchain, and the one problem I'm left with is that pthread_cancel needs to dlopen libgcc_s.so.1. The version supplied with the host gcc is linked to depend on glibc, so I'm instead using ld's -u option to pull in the needed symbols (and their dependencies) from libgcc_eh.a to make a replacement libgcc_s.so.1:
gcc -specs uclibc.specs -Wl,-u,_Unwind_Resume -Wl,-u,__gcc_personality_v0 \
-Wl,-u,_Unwind_ForcedUnwind -Wl,-u,_Unwind_GetCFA -shared -o libgcc_s.so.1
In principle I would be done, but all the symbols in libgcc_eh.a have their visibility set to hidden, so in the output .so file, they all become local and don't get added to the .dynsym symbol table.
I'm looking for a way to use binutils (perhaps objcopy? or a linker script?) on either the .so file or the original .o files in libgcc_eh.a to un-hide these symbols. Is this possible?
objcopy doesn't seem to have this feature, but you can do it with the ELFkickers rebind tool:
rebind --visibility default file.o SYMBOLS...
This must be done on the original .o files. If you try to do it on the .so it'll be too late, because the hidden symbols will have been omitted from the .dynsym section.
I think you should be able to use --globalize-symbol in objcopy.
e.g.
$ nm /usr/lib/gcc/i686-redhat-linux/4.6.3/libgcc_eh.a | grep emutls_alloc
00000000 t emutls_alloc
$ objcopy --globalize-symbol=emutls_alloc /usr/lib/gcc/i686-redhat-linux/4.6.3/libgcc_eh.a /tmp/libgcc_eh.a
$ nm /tmp/libgcc_eh.a |grep emutls_alloc
00000000 T emutls_alloc
You can provide --globalize-symbol several times to objcopy, but you'll need to explicitly mention the full symbol name of all the symbols you want to globalize.
Though I'm not sure what kind of breakage could occur turning libgcc_eh.a into a shared object, as libgcc_eh.a is presumably compiled without -fpic/-fPIC. Turns out libgcc_eh.a is compiled as position independent code.

Resources