summaryrefslogtreecommitdiffstats
path: root/src/common/impl/binary_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/impl/binary_linux.c')
-rw-r--r--src/common/impl/binary_linux.c147
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