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.
Related
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?
Let's say I have a file called library.c
#include <stdio.h>
void someFunc(int n)
{
printf("%s: %d\n", LIBNAME, n);
}
I compile it into two shared object files, using different macros (so resulting code is different). In this example I provide different LIBNAME:
gcc -DLIBNAME=\"lib1\" -fPIC -shared -g -Og library.c -o library1.so
gcc -DLIBNAME=\"lib2\" -fPIC -shared -g -Og library.c -o library2.so
Then I load both libraries from executable:
#include <dlfcn.h>
#include <stdio.h>
typedef void (*functype)(int);
int callFunc(const char* libname, int n)
{
void* lib = dlopen(libname, RTLD_NOW|RTLD_LOCAL);
if (!lib) {
fprintf(stderr, "%s\n", dlerror());
return -1;
}
functype func = dlsym(lib, "someFunc");
if (!func) {
fprintf(stderr, "%s\n", dlerror());
dlclose(lib);
return -1;
}
func(n);
dlclose(lib);
return 0;
}
int main()
{
int res = callFunc("./library1.so", 42);
if (res == 0)
return callFunc("./library2.so", 13);
else
return res;
}
In gdb I want to debug function from a certain library (e.g. library1.so), while not having to worry about another one (library2.so). I can't set breakpoint by function name, because it's the same in both libraries. I can't do it with sourcefile:linenumber either, since source file is the same for both libraries. How can I tell gdb to set breakpoint only in one library?
In gdb I want to debug function from a certain library (e.g.
library1.so), while not having to worry about another one (library2.so)
You can set pending breakpoint on someFunc and ignore it manually if it was called from library2.so. You can know this from info sharedlibrary output: if someFunc was called from library2.so, library2.so will be loaded and you will see it in info sharedlibrary output.
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0x00007ffff7dd6f60 0x00007ffff7df5030 Yes (*) /lib64/ld-linux-x86-64.so.2
0x00007ffff7bd2ee0 0x00007ffff7bd3bbe Yes (*) /lib64/libdl.so.2
0x00007ffff7834340 0x00007ffff797b27f Yes (*) /lib64/libc.so.6
0x00007ffff7611550 0x00007ffff761162c Yes ./library2.so
(*): Shared library is missing debugging information.
(gdb)
When you see it loaded, you can ignore this breakpoint and continue execution. Though it is not fully automated way of debugging someFunc, this is probably the best you can do in this case.
Why don't you use "break if"? Something like
break library.c:<line_no> if libname == "./library1.so"
In case libname does not work, you can also use the second parameter n.
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.
I have a linux C program that handles request sent to a TCP socket (bound to a particular port). I want to be able to query the internal state of the C program via a request to that port, but I dont want to hard code what global variables can be queried. Thus I want the query to contain the string name of a global and the C code to look that string up in the symbol table to find its address and then send its value back over the TCP socket. Of course the symbol table must not have been stripped. So can the C program even locate its own symbol table, and is there a library interface for looking up symbols given their name? This is an ELF executable C program built with gcc.
This is actually fairly easy. You use dlopen / dlsym to access symbols. In order for this to work, the symbols have to be present in the dynamic symbol table. There are multiple symbol tables!
#include <dlfcn.h>
#include <stdio.h>
__attribute__((visibility("default")))
const char A[] = "Value of A";
__attribute__((visibility("hidden")))
const char B[] = "Value of B";
const char C[] = "Value of C";
int main(int argc, char *argv[])
{
void *hdl;
const char *ptr;
int i;
hdl = dlopen(NULL, 0);
for (i = 1; i < argc; ++i) {
ptr = dlsym(hdl, argv[i]);
printf("%s = %s\n", argv[i], ptr);
}
return 0;
}
In order to add all symbols to the dynamic symbol table, use -Wl,--export-dynamic. If you want to remove most symbols from the symbol table (recommended), set -fvisibility=hidden and then explicitly add the symbols you want with __attribute__((visibility("default"))) or one of the other methods.
~ $ gcc dlopentest.c -Wall -Wextra -ldl
~ $ ./a.out A B C
A = (null)
B = (null)
C = (null)
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,--export-dynamic
~ $ ./a.out A B C
A = Value of A
B = (null)
C = Value of C
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,--export-dynamic -fvisibility=hidden
~ $ ./a.out A B C
A = Value of A
B = (null)
C = (null)
Safety
Notice that there is a lot of room for bad behavior.
$ ./a.out printf
printf = ▯▯▯▯ (garbage)
If you want this to be safe, you should create a whitelist of permissible symbols.
file: reflect.c
#include <stdio.h>
#include "reflect.h"
struct sym_table_t gbl_sym_table[1] __attribute__((weak)) = {{NULL, NULL}};
void * reflect_query_symbol(const char *name)
{
struct sym_table_t *p = &gbl_sym_table[0];
for(; p->name; p++) {
if(strcmp(p->name, name) == 0) {
return p->addr;
}
}
return NULL;
}
file: reflect.h
#include <stdio.h>
struct sym_table_t {
char *name;
void *addr;
};
void * reflect_query_symbol(const char *name);
file: main.c
just #include "reflect.h" and call reflect_query_symbol
example:
#include <stdio.h>
#include "reflect.h"
void foo(void)
{
printf("bar test\n");
}
int uninited_data;
int inited_data = 3;
int main(int argc, char *argv[])
{
int i;
void *addr;
for(i=1; i<argc; i++) {
addr = reflect_query_symbol(argv[i]);
if(addr) {
printf("%s lay at: %p\n", argv[i], addr);
} else {
printf("%s NOT found\n", argv[i], addr);
}
}
return 0;
}
file:Makefile
objs = main.o reflect.o
main: $(objs)
gcc -o $# $^
nm $# | awk 'BEGIN{ print "#include <stdio.h>"; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.c
gcc -c .reflect.real.c -o .reflect.real.o
gcc -o $# $^ .reflect.real.o
nm $# | awk 'BEGIN{ print "#include <stdio.h>"; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.c
gcc -c .reflect.real.c -o .reflect.real.o
gcc -o $# $^ .reflect.real.o
The general term for this sort of feature is "reflection", and it is not part of C.
If this is for debugging purposes, and you want to be able to inspect the entire state of a C program remotely, examine any variable, start and stop its execution, and so on, you might consider GDB remote debugging:
GDB offers a 'remote' mode often used when debugging embedded systems.
Remote operation is when GDB runs on one machine and the program being
debugged runs on another. GDB can communicate to the remote 'stub'
which understands GDB protocol via Serial or TCP/IP. A stub program
can be created by linking to the appropriate stub files provided with
GDB, which implement the target side of the communication
protocol. Alternatively, gdbserver can be used to remotely debug
the program without needing to change it in any way.
This is more focused than my previous Valgrind question; I am trying to narrow down write and read errors when parsing command-line options:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <string.h>
#include <locale.h>
#include <bzlib.h>
#include <zlib.h>
#include "starch.h"
#define BUFMAXLEN 1024
int main(int argc, char **argv) {
if (parseCommandLineInputs( &argc, &argv ) != 0)
exit(EXIT_FAILURE);
return 0;
}
int parseCommandLineInputs(int *argc, char ***argv) {
pid_t pid;
struct utsname uts;
char uniqTag[BUFMAXLEN];
if ((*argc == 1) || (*argc > 4)) {
printUsage();
return -1;
}
if ((pid = getpid()) < 0) {
fprintf(stderr, "\n\t[starch] - Error: Could not obtain process ID\n\n");
return -1;
}
uname( &uts );
sprintf(uniqTag, "pid%d.%s", pid, uts.nodename);
switch (*argc) {
case 2: {
if (strcmp(*argv[1], "-") != 0) {
if (fileExists(*argv[1]) != 0) { /* standard input */
...
}
return 0;
}
int fileExists(char *fn) {
struct stat buf;
int i = stat (fn, &buf);
if (i == 0)
return 0;
return -1;
}
void printUsage() {
fprintf(stderr, "my usage statement\n\n");
}
My makefile is as follows:
CC = gcc
CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1 -DUSE_ZLIB -O3 -Wformat -Wall -pedantic -std=gnu99 -g
BIN = ../bin
all: starch
rm -rf *~
starch: starch.o
mkdir -p $(BIN) && $(CC) ${CFLAGS} starch.o -lbz2 -lz -o ${BIN}/starch
rm -rf *~
clean:
rm -rf *.o *~ ${BIN}/starch
I get the following errors when I run with valgrind:
$ valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes -v ../bin/starch
...
==2675== 1 errors in context 1 of 2:
==2675== Invalid read of size 8
==2675== at 0x3AB4262243: fwrite (in /lib64/libc-2.5.so)
==2675== Address 0x7fedffd68 is on thread 1's stack
==2675==
==2675==
==2675== 1 errors in context 2 of 2:
==2675== Invalid write of size 8
==2675== at 0x401AA6: parseCommandLineInputs (starch.c:217)
==2675== by 0x7FF0000AF: ???
==2675== by 0x401DFA: main (starch.c:46)
==2675== Address 0x7fedffd68 is on thread 1's stack
The first error is not telling me anything I can use, since I am not using fwrite() anywhere.
The second error is tripped up on the fprintf statement in printUsage().
Line 46 is the following line:
if (parseCommandLineInputs( &argc, &argv ) != 0)
Line 217 is the following line:
fprintf(stderr, "my usage statement\n\n");
What is wrong with my application that explains why these errors appear?
Two things that pop up to me right away:
*argv[1] is NOT the same as (*argv)[1] which is what you probably mean. Array subscripting has precedence over pointer dereferencing. This results in an invalid pointer. As many experienced programmers will tell you: "Don't try to remember the precedence of operators - if in doubt use parentheses, if not just use them anyway".
-O3 in the compiler flags is a big fat NO-NO when debugging. The compiler will mangle your code so much that it can make your life impossible. Variables can disappear completely and functions can go mysteriously away as they get inlined. If your code compiles, links and runs with -O0 (IIRC some code containing inline assembly needs -O1 with some (all?) GCC versions) use it, otherwise use -O1 at most.
There is too much missing so it is not easy to tell what is going on. I suppose that pid is pid_t?
The only thing that I then see is this one:
sprintf(uniqTag, "pid%d.%s", pid, uts.nodename);
pid_t is not necessarily an int so sprintf might be on the wrong track when parsing its arguments and messup your stack. But gcc should have have told you if you compile with -Wall, which I suppose.
Try compiling with a different compiler, clang e.g.