Last active
May 15, 2021 13:06
-
-
Save smx-smx/35d3037310f7e5bd9afe3b4900d448a3 to your computer and use it in GitHub Desktop.
ldd with dlopen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Custom ldd that also dlopens | |
* supporting LD_LIBRARY_PATH, basically acts as "dlopen() with dependencies" | |
* without modifying the current process LD_LIBRARY_PATH | |
* | |
* Stefano Moioli <smxdev4@gmail.com>, 2021 | |
**/ | |
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <dlfcn.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
/** for get_interp **/ | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
#include <sys/mman.h> | |
#include <link.h> | |
#define CAST(t, x) ((t)(x)) | |
#define UPTR(x) ((uintptr_t)(x)) | |
#define ID_MATCH(str, id) strncmp(str, id, sizeof(id) - 1) == 0 | |
#define DEBUG | |
struct lib_node { | |
void *handle; | |
char *path; | |
struct lib_node *next; | |
}; | |
char *get_interp(){ | |
int fd = open("/proc/self/exe", O_RDONLY); | |
if(fd <= 0){ | |
return NULL; | |
} | |
char *result = NULL; | |
do { | |
struct stat statBuf; | |
if(fstat(fd, &statBuf) < 0){ | |
break; | |
} | |
void *pMem = mmap(0, statBuf.st_size, PROT_READ, MAP_SHARED, fd, 0); | |
if(pMem == MAP_FAILED){ | |
break; | |
} | |
do { | |
ElfW(Ehdr) *elf_hdr = pMem; | |
if(elf_hdr->e_phnum == 0 || elf_hdr->e_phoff == 0){ | |
break; | |
} | |
ElfW(Phdr) *elf_phdr = CAST(ElfW(Phdr) *, UPTR(pMem) + elf_hdr->e_phoff); | |
for(unsigned i=0; i<elf_hdr->e_phnum; i++){ | |
ElfW(Phdr) *phdr = &elf_phdr[i]; | |
if(phdr->p_type != PT_INTERP) continue; | |
if(phdr->p_paddr == 0 || phdr->p_filesz == 0) break; | |
char *interp_path = CAST(char *, UPTR(pMem) + phdr->p_paddr); | |
result = strdup(interp_path); | |
} | |
} while(0); | |
munmap(pMem, statBuf.st_size); | |
} while(0); | |
close(fd); | |
return result; | |
} | |
#define LD_LIBPATH_TPL "LD_LIBRARY_PATH=" | |
#define MAKE_LD_LIBPATH_SET(result, a) asprintf(&result, LD_LIBPATH_TPL "%s", a) | |
#define MAKE_LD_LIBPATH_APPEND(result, a, b) asprintf(&result, LD_LIBPATH_TPL "%s:%s", a, b) | |
char *make_ldlib_path(char *ld_libpath_extra, int append){ | |
/** | |
* cur | xtra | appnd | |
* ---------------------- | |
* null | null | ? |> null | |
* smth | null | ? |> cur | |
* null | smth | ? |> xtra | |
* smth | smth | 0 |> xtra|cur (prepend) | |
* smth | smth | 1 |> cur|xtra (append) | |
**/ | |
char *cur = getenv("LD_LIBRARY_PATH"); | |
char *xtra = ld_libpath_extra; | |
char *result = NULL; | |
if(xtra == NULL){ | |
if(cur != NULL){ | |
MAKE_LD_LIBPATH_SET(result, cur); | |
} | |
} else { | |
if(cur == NULL){ | |
MAKE_LD_LIBPATH_SET(result, xtra); | |
} else { | |
if(append){ | |
MAKE_LD_LIBPATH_APPEND(result, cur, xtra); | |
} else { | |
MAKE_LD_LIBPATH_APPEND(result, xtra, cur); | |
} | |
} | |
} | |
return result; | |
} | |
int main(int argc, char *argv[]){ | |
char *interp_path = get_interp(); | |
if(interp_path == NULL){ | |
fputs("failed to get interp path\n", stderr); | |
return 1; | |
} | |
char *ld_libpath_extra = NULL; | |
int ld_libpath_append = 1; | |
char *elf_path = argv[1]; | |
if(argc > 2){ | |
ld_libpath_extra = argv[2]; | |
} | |
if(argc > 3){ | |
ld_libpath_append = atoi(argv[3]) == 1; | |
} | |
pid_t pid; | |
int fd[2] = {-1, -1}; | |
pipe(fd); | |
if((pid=fork()) < 0){ | |
perror("fork"); | |
return 1; | |
} else if(pid == 0){ | |
char *env[] = { | |
"LD_TRACE_LOADED_OBJECTS=1", | |
make_ldlib_path(ld_libpath_extra, ld_libpath_append), | |
NULL | |
}; | |
char *run_argv[] = { | |
interp_path, | |
elf_path, | |
NULL | |
}; | |
// close input | |
close(fd[0]); | |
dup2(fd[1], STDOUT_FILENO); | |
dup2(fd[1], STDERR_FILENO); | |
close(fd[1]); | |
execve(run_argv[0], run_argv, env); | |
} else { | |
// close output | |
close(fd[1]); | |
FILE *fh = fdopen(fd[0], "r"); | |
if(!fh){ | |
perror("fdopen"); | |
return 1; | |
} | |
struct lib_node *head = NULL; | |
struct lib_node *prev = NULL; | |
char ident_left[] = " => "; | |
char ident_right[] = " ("; | |
char *line; | |
while (fscanf(fh, "%m[^\n]\n", &line) == 1) { | |
char *left = strchr(line, ' '); | |
if(left == NULL) continue; | |
if(!ID_MATCH(left, ident_left)){ | |
continue; | |
} | |
left += sizeof(ident_left) - 1; | |
char *right = strchr(left, ' '); | |
if(right == NULL) continue; | |
if(!ID_MATCH(right, ident_right)){ | |
continue; | |
} | |
*right = '\0'; | |
head = calloc(1, sizeof(*head)); | |
head->path = strdup(left); | |
// link in reverse | |
head->next = prev; | |
prev = head; | |
free(line); | |
} | |
struct lib_node *top = head; | |
for(head=top; head != NULL; head = head->next){ | |
head->handle = dlopen(head->path, RTLD_NOLOAD); | |
if(head->handle == NULL) { | |
head->handle = dlopen(head->path, RTLD_NOW | RTLD_GLOBAL); | |
} | |
#ifdef DEBUG | |
printf("%s: %p\n", head->path, head->handle); | |
#endif | |
// free here, if we don't need these later anymore | |
free(head->path); | |
free(head); | |
} | |
fclose(fh); | |
close(fd[0]); | |
int status = 0; | |
waitpid(pid, &status, 0); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment