From 50f4a425047bca1c99ed9e86a46bd0d7a74bf500 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Fri, 6 Oct 2017 08:56:47 -0400 Subject: fldd fixes --- src/fldd/main.c | 98 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 20 deletions(-) (limited to 'src/fldd') diff --git a/src/fldd/main.c b/src/fldd/main.c index 6127a4a87..c04daa0ed 100644 --- a/src/fldd/main.c +++ b/src/fldd/main.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef __LP64__ #define Elf_Ehdr Elf64_Ehdr @@ -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) { @@ -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; } @@ -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); @@ -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; } @@ -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); } @@ -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); } @@ -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) { @@ -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; } -- cgit v1.2.3-70-g09d2