Skip to content

Instantly share code, notes, and snippets.

@smx-smx
Last active May 15, 2021 13:06
Show Gist options
  • Save smx-smx/35d3037310f7e5bd9afe3b4900d448a3 to your computer and use it in GitHub Desktop.
Save smx-smx/35d3037310f7e5bd9afe3b4900d448a3 to your computer and use it in GitHub Desktop.
ldd with dlopen
/**
* 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