Skip to content

Commit

Permalink
fldd fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
netblue30 committed Oct 6, 2017
1 parent 2ac9b3f commit 50f4a42
Showing 1 changed file with 78 additions and 20 deletions.
98 changes: 78 additions & 20 deletions src/fldd/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>

#ifdef __LP64__
#define Elf_Ehdr Elf64_Ehdr
Expand Down Expand Up @@ -59,7 +60,8 @@ typedef struct storage_t {
struct storage_t *next;
const char *name;
} Storage;
static Storage *libs, *lib_paths;
static Storage *libs = NULL;
static Storage *lib_paths = NULL;

// return 1 if found
static int storage_find(Storage *ptr, const char *name) {
Expand Down Expand Up @@ -94,12 +96,10 @@ static void storage_print(Storage *ptr, int fd) {
}
}

static bool ptr_ok(const void *ptr, const void *base, const void *end, const char *name, const char *exe) {
static bool ptr_ok(const void *ptr, const void *base, const void *end, const char *name) {
bool r;

r = (ptr >= base && ptr <= end);
if (!r && !arg_quiet)
fprintf(stderr, "Warning: fldd: bad pointer %s for %s\n", name, exe);
r = (ptr >= base && ptr < end);
return r;
}

Expand Down Expand Up @@ -130,11 +130,11 @@ static void copy_libs_for_exe(const char *exe) {
}

Elf_Phdr *pbuf = (Elf_Phdr *)(base + sizeof(*ebuf));
while (ebuf->e_phnum-- > 0 && ptr_ok(pbuf, base, end, "pbuf", exe)) {
while (ebuf->e_phnum-- > 0 && ptr_ok(pbuf, base, end, "pbuf")) {
switch (pbuf->p_type) {
case PT_INTERP:
// dynamic loader ld-linux.so
if (!ptr_ok(base + pbuf->p_offset, base, end, "base + pbuf->p_offset", exe))
if (!ptr_ok(base + pbuf->p_offset, base, end, "base + pbuf->p_offset"))
goto close;

storage_add(&libs, base + pbuf->p_offset);
Expand All @@ -144,16 +144,16 @@ static void copy_libs_for_exe(const char *exe) {
}

Elf_Shdr *sbuf = (Elf_Shdr *)(base + ebuf->e_shoff);
if (!ptr_ok(sbuf, base, end, "sbuf", exe))
if (!ptr_ok(sbuf, base, end, "sbuf"))
goto close;

// Find strings section
char *strbase = NULL;
int sections = ebuf->e_shnum;
while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf", exe)) {
while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf")) {
if (sbuf->sh_type == SHT_STRTAB) {
strbase = base + sbuf->sh_offset;
if (!ptr_ok(strbase, base, end, "strbase", exe))
if (!ptr_ok(strbase, base, end, "strbase"))
goto close;
break;
}
Expand All @@ -164,23 +164,26 @@ static void copy_libs_for_exe(const char *exe) {

// Find dynamic section
sections = ebuf->e_shnum;
while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf", exe)) {
while (sections-- > 0 && ptr_ok(sbuf, base, end, "sbuf")) {
// TODO: running fldd on large gui programs (fldd /usr/bin/transmission-qt)
// crash on accessing memory location sbuf->sh_type if sbuf->sh_type in the previous section was 0 (SHT_NULL)
// for now we just exit the while loop - this is probably incorrect
// printf("sbuf %p #%s#, sections %d, type %u\n", sbuf, exe, sections, sbuf->sh_type);
if (!ptr_ok(sbuf, base, end, "sbuf"))
goto close;

if (sbuf->sh_type == SHT_NULL)
break;
if (sbuf->sh_type == SHT_DYNAMIC) {
Elf_Dyn *dbuf = (Elf_Dyn *)(base + sbuf->sh_offset);
if (!ptr_ok(dbuf, base, end, "dbuf", exe))
if (!ptr_ok(dbuf, base, end, "dbuf"))
goto close;
// Find DT_RPATH/DT_RUNPATH tags first
unsigned long size = sbuf->sh_size;
while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf", exe)) {
while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf")) {
if (dbuf->d_tag == DT_RPATH || dbuf->d_tag == DT_RUNPATH) {
const char *searchpath = strbase + dbuf->d_un.d_ptr;
if (!ptr_ok(searchpath, base, end, "searchpath", exe))
if (!ptr_ok(searchpath, base, end, "searchpath"))
goto close;
storage_add(&lib_paths, searchpath);
}
Expand All @@ -190,10 +193,10 @@ static void copy_libs_for_exe(const char *exe) {
// Find DT_NEEDED tags
dbuf = (Elf_Dyn *)(base + sbuf->sh_offset);
size = sbuf->sh_size;
while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf", exe)) {
while (size >= sizeof(*dbuf) && ptr_ok(dbuf, base, end, "dbuf")) {
if (dbuf->d_tag == DT_NEEDED) {
const char *lib = strbase + dbuf->d_un.d_ptr;
if (!ptr_ok(lib, base, end, "lib", exe))
if (!ptr_ok(lib, base, end, "lib"))
goto close;
copy_libs_for_lib(lib);
}
Expand Down Expand Up @@ -243,9 +246,53 @@ static void lib_paths_init(void) {
storage_add(&lib_paths, default_lib_paths[i]);
}


static void walk_directory(const char *dirname) {
assert(dirname);

DIR *dir = opendir(dirname);
if (dir) {
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0)
continue;
if (strcmp(entry->d_name, "..") == 0)
continue;

// build full path
char *path;
if (asprintf(&path, "%s/%s", dirname, entry->d_name) == -1)
errExit("asprintf");

// check regular so library
char *ptr = strstr(entry->d_name, ".so");
if (ptr) {
if (*(ptr + 3) == '\0' || *(ptr + 3) == '.') {
copy_libs_for_exe(path);
free(path);
continue;
}
}

// check directory
// entry->d_type field is supported in glibc since version 2.19 (Feb 2014)
// we'll use stat to check for directories
struct stat s;
if (stat(path, &s) == -1)
errExit("stat");
if (S_ISDIR(s.st_mode))
walk_directory(path);
}
closedir(dir);
}
}



static void usage(void) {
printf("Usage: fldd program [file]\n");
printf("print a list of libraries used by program or store it in the file.\n");
printf("Usage: fldd program_or_directory [file]\n");
printf("Print a list of libraries used by program or store it in the file.\n");
printf("Print a list of libraries used by all .so files in a directory or store it in the file.\n");
}

int main(int argc, char **argv) {
Expand Down Expand Up @@ -296,11 +343,22 @@ printf("\n");
}
}

// initialize local storage
lib_paths_init();
copy_libs_for_exe(argv[1]);

// process files
struct stat s;
if (stat(argv[1], &s) == -1)
errExit("stat");
if (S_ISDIR(s.st_mode))
walk_directory(argv[1]);
else
copy_libs_for_exe(argv[1]);


// print libraries and exit
storage_print(libs, fd);
if (argc == 3)
close(fd);

return 0;
}

0 comments on commit 50f4a42

Please sign in to comment.