shared libraries and visibility to user's memory - c

When I use a shared library via dlopen, can the library code "see" memory of my process that calls dlopen? For example, I would like to pass a pointer to memory allocated by my application to the library API.
I'm on Linux/x86 if it is important.

The answer is yes, it can. Here is a simple quick example for illustration purposes.
The library code (in file myso.c):
void setInt( int * i )
{
*i = 12345;
}
The library can be built as follows:
gcc -c -fPIC myso.c
gcc -shared -Wl,-soname,libmy.so -o libmy.so myso.o -lc
Here is the client code (main.c):
#include <stdio.h>
#include <dlfcn.h>
typedef void (*setint_t)( int * );
int main()
{
void * h = dlopen("./libmy.so", RTLD_NOW);
if (h)
{
puts("Loaded library.");
setint_t setInt = dlsym( h, "setInt" );
if (setInt) {
puts("Symbol found");
int k;
setInt(&k);
printf("The int is %d\n", k);
}
}
return 0;
}
Now build and run the code. Make sure main.c and the library are in the same directory, in which we execute the following:
user#fedora-21 ~]$ gcc main.c -ldl
[user#fedora-21 ~]$ ./a.out
Loaded library.
Symbol found
The int is 12345
As one can see, the library was able to write to the memory of the integer k.

Related

Where do Linux shells look for interpreters for ELF binaries? [duplicate]

So everyone probably knows that glibc's /lib/libc.so.6 can be executed in the shell like a normal executable in which cases it prints its version information and exits. This is done via defining an entry point in the .so. For some cases it could be interesting to use this for other projects too. Unfortunately, the low-level entry point you can set by ld's -e option is a bit too low-level: the dynamic loader is not available so you cannot call any proper library functions. glibc for this reason implements the write() system call via a naked system call in this entry point.
My question now is, can anyone think of a nice way how one could bootstrap a full dynamic linker from that entry point so that one could access functions from other .so's?
Update 2: see Andrew G Morgan's slightly more complicated solution which does work for any GLIBC (that solution is also used in libc.so.6 itself (since forever), which is why you can run it as ./libc.so.6 (it prints version info when invoked that way)).
Update 1: this no longer works with newer GLIBC versions:
./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable
Original answer from 2009:
Building your shared library with -pie option appears to give you everything you want:
/* pie.c */
#include <stdio.h>
int foo()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return 42;
}
int main()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return foo();
}
/* main.c */
#include <stdio.h>
extern int foo(void);
int main()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return foo();
}
$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so
$ ./pie.so
in main pie.c:9
in foo pie.c:4
$ ./a.out
in main main.c:6
in foo pie.c:4
$
P.S. glibc implements write(3) via system call because it doesn't have anywhere else to call (it is the lowest level already). This has nothing to do with being able to execute libc.so.6.
I have been looking to add support for this to pam_cap.so, and found this question. As #EmployedRussian notes in a follow-up to their own post, the accepted answer stopped working at some point. It took a while to figure out how to make this work again, so here is a worked example.
This worked example involves 5 files to show how things work with some corresponding tests.
First, consider this trivial program (call it empty.c):
int main(int argc, char **argv) { return 0; }
Compiling it, we can see how it resolves the dynamic symbols on my system as follows:
$ gcc -o empty empty.c
$ objcopy --dump-section .interp=/dev/stdout empty ; echo
/lib64/ld-linux-x86-64.so.2
$ DL_LOADER=/lib64/ld-linux-x86-64.so.2
That last line sets a shell variable for use later.
Here are the two files that build my example shared library:
/* multi.h */
void multi_main(void);
void multi(const char *caller);
and
/* multi.c */
#include <stdio.h>
#include <stdlib.h>
#include "multi.h"
void multi(const char *caller) {
printf("called from %s\n", caller);
}
__attribute__((force_align_arg_pointer))
void multi_main(void) {
multi(__FILE__);
exit(42);
}
const char dl_loader[] __attribute__((section(".interp"))) =
DL_LOADER ;
(Update 2021-11-13: The forced alignment is to help __i386__ code be SSE compatible - without it we get hard to debug glibc SIGSEGV crashes.)
We can compile and run it as follows:
$ gcc -fPIC -shared -o multi.so -DDL_LOADER="\"${DL_LOADER}\"" multi.c -Wl,-e,multi_main
$ ./multi.so
called from multi.c
$ echo $?
42
So, this is a .so that can be executed as a stand alone binary. Next, we validate that it can be loaded as shared object.
/* opener.c */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
void *handle = dlopen("./multi.so", RTLD_NOW);
if (handle == NULL) {
perror("no multi.so load");
exit(1);
}
void (*multi)(const char *) = dlsym(handle, "multi");
multi(__FILE__);
}
That is we dynamically load the shared-object and run a function from it:
$ gcc -o opener opener.c -ldl
$ ./opener
called from opener.c
Finally, we link against this shared object:
/* main.c */
#include "multi.h"
int main(int argc, char **argv) {
multi(__FILE__);
}
Where we compile and run it as follows:
$ gcc main.c -o main multi.so
$ LD_LIBRARY_PATH=./ ./main
called from main.c
(Note, because multi.so isn't in a standard system library location, we need to override where the runtime looks for the shared object file with the LD_LIBRARY_PATH environment variable.)
I suppose you'd have your ld -e point to an entry point which would then use the dlopen() family of functions to find and bootstrap the rest of the dynamic linker. Of course you'd have to ensure that dlopen() itself was either statically linked or you might have to implement enough of your own linker stub to get at it (using system call interfaces such as mmap() just as libc itself is doing.
None of that sounds "nice" to me. In fact just the thought of reading the glibc sources (and the ld-linux source code, as one example) enough to assess the size of the job sounds pretty hoary to me. It might also be a portability nightmare. There may be major differences between how Linux implements ld-linux and how the linkages are done under OpenSolaris, FreeBSD, and so on. (I don't know).

Is it possible for an LD_PRELOAD to only affect the main executable?

The Actual Problem
I have an executable that by default uses EGL and SDL 1.2 to handle graphics and user input respectively. Using LD_PRELOAD, I have replaced both with GLFW.
This works normally unless the user has installed the Wayland version of GLFW, which depends on EGL itself. Because all the EGL calls are either stubbed to do nothing or call GLFW equivalents, it doesn't work (ie. eglSwapBuffers calls glfwSwapBuffers which calls eglSwapBuffers and so on). I can't remove the EGL stubs because then it would call both EGL and GLFW and the main executable is closed-source so I can't modify that.
Is there any way to make LD_PRELOAD affect the main executable but not GLFW? Or any other solution to obtain the same effect?
The Simplified Problem
I made a simplified example to demonstrate the problem.
Main Executable:
#include <stdio.h>
extern void do_something();
int main() {
do_something();
fputs("testing B\n", stderr);
}
Shared Library:
#include <stdio.h>
void do_something() {
fputs("testing A\n", stderr);
}
Preloaded Library:
#include <stdio.h>
int fputs(const char *str, FILE *file) {
// Do Nothing
return 0;
}
When the preloaded library isn't used, the output is:
testing A
testing B
When it is used, the output is nothing.
I'm looking for a way to make the preloaded library only affect the main executable, that the output would be:
testing A
Thank you!
You can check if the return address is in the executable or the library, and then call either the "real" function or do your stub code, like this:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
static struct {
ElfW(Addr) start, end;
} *segments;
static int n;
static int (*real_fputs)(const char *, FILE *);
static int callback(struct dl_phdr_info *info, size_t size, void *data) {
n = info->dlpi_phnum;
segments = malloc(n * sizeof *segments);
for(int i = 0; i < n; ++i) {
segments[i].start = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
segments[i].end = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
}
return 1;
}
__attribute__((__constructor__))
static void setup(void) {
real_fputs = dlsym(RTLD_NEXT, "fputs");
dl_iterate_phdr(callback, NULL);
}
__attribute__((__destructor__))
static void teardown(void) {
free(segments);
}
__attribute__((__noinline__))
int fputs(const char *str, FILE *file) {
ElfW(Addr) addr = (ElfW(Addr))__builtin_extract_return_addr(__builtin_return_address(0));
for(int i = 0; i < n; ++i) {
if(addr >= segments[i].start && addr < segments[i].end) {
// Do Nothing
return 0;
}
}
return real_fputs(str, file);
}
This has some caveats, though. For example, if your executable calls a library function that tail-calls a function you're hooking, then this will incorrectly consider that library call an executable call. (You could mitigate this problem by adding wrappers for those library functions too, that unconditionally forward to the "real" function, and compiling the wrapper code with -fno-optimize-sibling-calls.) Also, there's no way to distinguish whether anonymous executable memory (e.g., JITted code) originally came from the executable or a library.
To test this, save my code as hook_fputs.c, your main executable as main.c, and your shared library as libfoo.c. Then run these commands:
clang -fPIC -shared hook_fputs.c -ldl -o hook_fputs.so
clang -fPIC -shared libfoo.c -o libfoo.so
clang main.c ./libfoo.so
LD_PRELOAD=./hook_fputs.so ./a.out
Implement the interposing library separately for the two cases.
Create a wrapper script or program that uses ldd to find out the exact EGL library version and their paths the target binary is dynamically linked against; then, using ldd on the the GLFW library, to find out whether it is linked against EGL or not. Finally, have it execute the target binary with the path to the appropriate interposing library in LD_PRELOAD environment variable.

How call and compile function from elf to my binary?

I have a binary file (ELF) that I don't write, but I want to use 1 function from this binary (I know the address/offset of the function), that function not exported from the binary.
My goal is to call this function from my C code that I write and compile this function statically in my binary (I compile with gcc).
How can I do that please?
I am going to answer the
call to this function from my c code that I write
part.
The below works under certain assumptions, like dynamic linking and position independent code. I haven't thought for too long about what happens if they are broken (let's experiment/discuss, if there's interest).
$ cat lib.c
int data = 42;
static int foo () { return data; }
gcc -fpic -shared lib.c -o lib.so
$ nm lib.so | grep foo
00000000000010e9 t foo
The above reproduces having the address that you know. The address we know now is 0x10e9. It is the virtual address of foo before relocation. We'll model the relocation the dynamic loader does by hand by simply adding the base address at which lib.so gets loaded.
$ cat 1.c
#define _GNU_SOURCE
#include <stdio.h>
#include <link.h>
#include <string.h>
#include <elf.h>
#define FOO_VADDR 0x10e9
typedef int(*func_t)();
int callback(struct dl_phdr_info *info, size_t size, void *data)
{
if (!(strstr(info->dlpi_name, "lib.so")))
return 0;
Elf64_Addr addr = info->dlpi_addr + FOO_VADDR;
func_t f = (func_t)addr;
int res = f();
printf("res = %d\n", res);
return 0;
}
int main()
{
void *handle = dlopen("./lib.so", RTLD_LAZY);
if (!handle) {
puts("failed to load");
return 1;
}
dl_iterate_phdr(&callback, NULL);
dlclose(handle);
return 0;
}
And now...
$ gcc 1.c -ldl && ./a.out
res = 42
Voila -- it worked! That was fun.
Credit: this was helpful.
If you have questions, feel free to read the man and ask in the comments.
As for
compile this function statically in my binary
I don't know off the bat. This would be trickier. Why do you want that? Also, do you know whether the function depends on some data (or maybe it calls other functions) in the original ELF file, like in the example above?

How to properly debug a shared library written in C?

I am currently writing a shared library that takes a UNIX username and returns a string with all of the groups that user belongs to in [group1, group2, group3...] format.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <utmp.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
int num_groups = 0;
struct passwd *pwd;
gid_t *groups;
struct group *grp;
FILE *stream;
char *buff;
size_t length;
char *printGroups(char *arg)
{
stream = open_memstream(&buff, &length);
pwd = getpwnam(arg);
getgrouplist(arg, pwd->pw_gid, groups, &num_groups);
groups = malloc(num_groups * sizeof(gid_t));
if (groups == NULL){
perror("malloc");
exit(EXIT_FAILURE);
}
getgrouplist(arg, pwd->pw_gid, groups, &num_groups);
fprintf(stream, " [");
for (int i = 0; i < num_groups; ++i){
grp = getgrgid(groups[i]);
if (i == num_groups - 1)
fprintf(stream, "%s", grp->gr_name);
else
fprintf(stream, "%s ", grp->gr_name);
}
free(groups);
fprintf(stream, "]");
fclose(stream);
return buff;
}
This is main function in my shared library that returns the string. I verified that the function is indeed correct - the same logic works in a standalone program using printf instead of open_memstream stringstream.
The library however segfaults and I can't pinpoint why. Valgrind does not output anything useful:
gcc -shared -fpic -g -Wall lib.c
valgrind ./a.out
==9916== Process terminating with default action of signal 11 (SIGSEGV)
==9916== Access not within mapped region at address 0x0
==9916== at 0x1: ???
==9916== by 0xFFF000672: ???
Same goes for gdb backtrace:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000001 in ?? () (gdb) backtrace
#0 0x0000000000000001 in ?? ()
#1 0x00007fffffffe6e9 in ?? ()
#2 0x0000000000000000 in ?? ()
I am out of ideas. Could somebody point me to a solution, ethier an error in the .so source or the reason why both Valgrind and gdb print ??? despite using the -g flag when compiling?
It looks like you're attempting to run the shared library directly. That's not how shared libraries work. They're referenced by other programs that use them.
For example, this code would use your library:
#include <stdio.h>
#include <stdlib.h>
char *printGroups(char *);
int main()
{
char *groups = printGroups("root");
printf("groups: %s\n", groups);
free(groups);
return 0;
}
If you first compile your library like this:
gcc -shared -fpic -g -Wall lib.c -o libmylib.so
Then, assuming this library lives in the same directory as the the above test code, you compile the test code like this:
gcc -g -Wall -Wextra -L. -o mytest mytest.c -lmylib
Then set an environment variable to find your library:
export LD_LIBRARY_PATH=.
You can then run the test program which will use your library.

gcc shared library with header in the same library

I'm trying to compile a shared library (.so) with the following code:
libreceive.h:
#include <stddef.h>
int receive(int sockfd, void *buf, size_t len, int flags);
libreceive.c
#include <stddef.h>
#include <libreceive/libreceive.h>
int receive(int sockfd, void *buf, size_t len, int flags){
return recv(sockfd, buf, len, flags);
}
the problem here is that I'm trying to include the .h in the library that I'm building and using it in the same time from the same library in the .c .
I know that what I'm trying to do is possible, but I can't manage to do it.
How can I do that please.
the code I'm trying is:
gcc -o libreceive.o -c -include libreceive.h libreceive.c
I get the following error:
fatal error: libreceive/libreceive.h: No such file or directory
compilation terminated.
the problem here is that I'm trying to include the .h in the library that I'm building and using it in the same time from the same library in the .c .
I know that what I'm trying to do is possible, but I can't manage to do it.
How can I do that please.
Since libreceive.h and libreceive.c appear to be in the same directory (judging from your compiler call), the normal way is
#include "libreceive.h"
In order to use
#include <libreceive/libreceive.h>
libreceive.h would have to lie in a directory called libreceive, and that directory would have to be part of the include path. It is possible to achieve this, but I believe it is neither necessary nor useful here.
You are missing out a few steps here.
Consider the following setup.
File: add.c
#include "header.h"
int add(int a, int b)
{
printf("SIZE: %d\n", SIZE);
return a+b;
}
File: sub.c
#include "header.h"
int sub(int a, int b)
{
printf("SIZE: %d\n", SIZE);
return a-b;
}
File: header.h, located in directory called include.
#include <stdio.h>
#define SIZE 100
int add(int a, int b);
int sub(int a, int b);
So to step by step build a .so file.
/* Build `.o` files first */
$ gcc -fPIC -c sub.c -I path/to/include/
$ gcc -fPIC -c add.c -I path/to/include/
/* Build shared library called libsample.so */
$ gcc -shared -o libsample.so add.o sub.o
The above command will build a .so by name libsample.so.
Where all definition from .c(like functions) and .h(like #defines) will get included in your library.
How to use this in your code:
Consider the file
File: main.c
#include <stdio.h>
int main()
{
int a = 3, b = 4;
printf("Return : %d\n", add(a, b));
return 0;
}
To make use of your library libsample.so.
$ export LD_LIBRARY_PATH=/path/to/direc/containing/.so/file
$ gcc -o exe main.c -lsample -L/path/to/direc/containing/.so/file
The above command should create a binary called exe.
$./exe
SIZE : 100 /* SIZE Defined in .h file */
Return : 7 /* Defined in add.c */
You can refer this guide : http://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
Finaly I decided to use #include "libreceive.h" as suggested by the guys. the probleme I had is that the compiler was looking for my so in /usr/lib wich is the default when id do sudo gcc and my usr had the $LD_LIBRARY_PATH at /usr/local/lib and therefore gcc coudn't find my library at compile time
another problem was that the program that call thos .so was looking fro the .h in some folder that doesn't exist and I had to add it.
thanks guys for you answers

Resources