diff options
Diffstat (limited to 'src/common/impl/binary_linux.c')
| -rw-r--r-- | src/common/impl/binary_linux.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/src/common/impl/binary_linux.c b/src/common/impl/binary_linux.c new file mode 100644 index 0000000..9a24217 --- /dev/null +++ b/src/common/impl/binary_linux.c @@ -0,0 +1,147 @@ +#include "common/binary.h" + +#if defined(FF_HAVE_ELF) || defined(__sun) || (defined(__FreeBSD__) && !defined(__DragonFly__)) || defined(__OpenBSD__) || defined(__NetBSD__) + + #include "common/io.h" + #include "common/library.h" + #include "common/strutil.h" + + #include <libelf.h> // #1254 + #include <fcntl.h> + +/** + * Structure to hold dynamically loaded libelf function pointers + */ +struct FFElfData { + FF_LIBRARY_SYMBOL(elf_version) + FF_LIBRARY_SYMBOL(elf_begin) + FF_LIBRARY_SYMBOL(elf_getshdrstrndx) + FF_LIBRARY_SYMBOL(elf_nextscn) + FF_LIBRARY_SYMBOL(elf64_getshdr) + FF_LIBRARY_SYMBOL(elf32_getshdr) + FF_LIBRARY_SYMBOL(elf_getdata) + FF_LIBRARY_SYMBOL(elf_strptr) + FF_LIBRARY_SYMBOL(elf_end) + + bool inited; +} elfData; + +/** + * Extracts string literals from an ELF (Linux/Unix) binary file + * + * This function loads the libelf library dynamically, opens the ELF file, + * locates the .rodata section (which contains string literals), and + * scans it for valid strings. Each string found is passed to the + * callback function for processing. + * + * The function supports both 32-bit and 64-bit ELF formats. + */ +const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) { + // Initialize libelf if not already done + if (!elfData.inited) { + elfData.inited = true; + FF_LIBRARY_LOAD_MESSAGE(libelf, "libelf" FF_LIBRARY_EXTENSION, 1); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_version) + if (elfData.ffelf_version(EV_CURRENT) == EV_NONE) { + return "elf_version() failed"; + } + + // Load all required libelf functions + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_begin) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_getshdrstrndx) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_nextscn) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf64_getshdr) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf32_getshdr) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_getdata) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_strptr) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_end) + + libelf = NULL; + } + + if (elfData.ffelf_end == NULL) { + return "load libelf failed"; + } + + // Open the ELF file + FF_AUTO_CLOSE_FD int fd = open(elfFile, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return "open() failed"; + } + + Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + return "elf_begin() failed"; + } + + // Get the section header string table index + size_t shstrndx = 0; + if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0) { + elfData.ffelf_end(elf); + return "elf_getshdrstrndx() failed"; + } + + // Iterate through all sections, looking for .rodata which contains string literals + Elf_Scn* scn = NULL; + while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL) { + // Try 64-bit section header first, then 32-bit if that fails + Elf64_Shdr* shdr64 = elfData.ffelf64_getshdr(scn); + Elf32_Shdr* shdr32 = NULL; + if (shdr64 == NULL) { + shdr32 = elfData.ffelf32_getshdr(scn); + if (shdr32 == NULL) { + continue; + } + } + + // Get the section name and check if it's .rodata + const char* name = elfData.ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name); + if (name == NULL || !ffStrEquals(name, ".rodata")) { + continue; + } + + // Get the section data + Elf_Data* data = elfData.ffelf_getdata(scn, NULL); + if (data == NULL) { + continue; + } + + // Scan the section for string literals + for (size_t off = 0; off < data->d_size; ++off) { + const char* p = (const char*) data->d_buf + off; + if (*p == '\0') { + continue; + } + uint32_t len = (uint32_t) strnlen(p, data->d_size - off); + if (len < minLength) { + off += len; + continue; + } + // Only process printable ASCII characters + if (*p >= ' ' && *p <= '~') // Ignore control characters + { + if (!cb(p, len, userdata)) { + break; + } + } + off += len; + } + + break; + } + + elfData.ffelf_end(elf); + return NULL; +} + +#else + +/** + * Fallback implementation when libelf is not available + */ +const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) { + FF_UNUSED(file, cb, userdata, minLength); + return "Fastfetch was built without libelf support"; +} + +#endif |