From 22e25d0c7834759fe21e407e194aa660ea55d37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 21 Aug 2024 15:31:47 +0800 Subject: [PATCH] Binary: support macOS --- CMakeLists.txt | 12 +- src/detection/editor/editor.c | 4 - src/util/binary_apple.c | 186 ++++++++++++++++++++++++++ src/util/{binary.c => binary_linux.c} | 0 4 files changed, 192 insertions(+), 10 deletions(-) create mode 100644 src/util/binary_apple.c rename src/util/{binary.c => binary_linux.c} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02db14ef3..023c51919 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -482,7 +482,7 @@ if(LINUX) src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_linux.c src/util/platform/FFPlatform_unix.c - src/util/binary.c + src/util/binary_linux.c ) elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC @@ -544,7 +544,7 @@ elseif(ANDROID) src/detection/wmtheme/wmtheme_nosupport.c src/detection/camera/camera_android.c src/util/platform/FFPlatform_unix.c - src/util/binary.c + src/util/binary_linux.c ) elseif(FreeBSD) list(APPEND LIBFASTFETCH_SRC @@ -623,7 +623,7 @@ elseif(FreeBSD) src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_linux.c src/util/platform/FFPlatform_unix.c - src/util/binary.c + src/util/binary_linux.c ) elseif(APPLE) list(APPEND LIBFASTFETCH_SRC @@ -689,7 +689,7 @@ elseif(APPLE) src/util/apple/cf_helpers.c src/util/apple/osascript.m src/util/platform/FFPlatform_unix.c - src/util/binary.c + src/util/binary_apple.c ) elseif(WIN32) list(APPEND LIBFASTFETCH_SRC @@ -756,7 +756,7 @@ elseif(WIN32) src/util/windows/unicode.c src/util/windows/wmi.cpp src/util/platform/FFPlatform_windows.c - src/util/binary.c + src/util/binary_linux.c ) elseif(SunOS) list(APPEND LIBFASTFETCH_SRC @@ -833,7 +833,7 @@ elseif(SunOS) src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_nosupport.c src/util/platform/FFPlatform_unix.c - src/util/binary.c + src/util/binary_linux.c ) endif() diff --git a/src/detection/editor/editor.c b/src/detection/editor/editor.c index a37accb0d..269f567b8 100644 --- a/src/detection/editor/editor.c +++ b/src/detection/editor/editor.c @@ -13,7 +13,6 @@ static inline char* realpath(const char* restrict file_name, char* restrict reso } #endif -#if __linux__ || __FreeBSD__ static bool extractNvimVersion(const char* str, uint32_t len, void* userdata) { if (len < strlen("NVIM v0.0.0")) return true; @@ -38,7 +37,6 @@ static bool extractNanoVersion(const char* str, uint32_t len, void* userdata) ffStrbufSetS((FFstrbuf*) userdata, str + strlen("GNU nano ")); return false; } -#endif const char* ffDetectEditor(FFEditorResult* result) { @@ -90,7 +88,6 @@ const char* ffDetectEditor(FFEditorResult* result) #endif } - #if __linux__ || __FreeBSD__ if (ffStrbufEqualS(&result->exe, "nvim")) ffBinaryExtractStrings(buf, extractNvimVersion, &result->version); else if (ffStrbufEqualS(&result->exe, "vim")) @@ -99,7 +96,6 @@ const char* ffDetectEditor(FFEditorResult* result) ffBinaryExtractStrings(buf, extractNanoVersion, &result->version); if (result->version.length > 0) return NULL; - #endif const char* param = NULL; if ( diff --git a/src/util/binary_apple.c b/src/util/binary_apple.c new file mode 100644 index 000000000..940228af8 --- /dev/null +++ b/src/util/binary_apple.c @@ -0,0 +1,186 @@ +#include "binary.h" +#include "common/io/io.h" +#include "util/stringUtils.h" +#include "util/mallocHelper.h" + +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // swap_fat_arch + +// Ref: https://github.com/AlexDenisov/segment_dumper/blob/master/main.c + +static inline bool readData(FILE *objFile, void *buf, size_t size, off_t offset) +{ + fseek(objFile, offset, SEEK_SET); + return fread(buf, 1, size, objFile) == size; +} + +static bool handleMachSection(FILE *objFile, const char *name, off_t offset, size_t size, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + if (!ffStrEquals(name, "__cstring")) return true; + + FF_AUTO_FREE char* data = (char*) malloc(size); + if (!readData(objFile, data, size, offset)) + return true; + + for (size_t off = 0; off < size; ++off) + { + const char* p = (const char*) data + off; + if (*p == '\0') continue; + uint32_t len = (uint32_t) strlen(p); + if (*p >= ' ' && *p <= '~') // Ignore control characters + { + if (!cb(p, len, userdata)) return false; + } + off += len; + } + return true; +} + +static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + uint32_t ncmds; + off_t loadCommandsOffset = offset; + + if (is_64) + { + struct mach_header_64 header; + if (!readData(objFile, &header, sizeof(header), offset)) + return "read mach header failed"; + + ncmds = header.ncmds; + loadCommandsOffset += sizeof(header); + } + else + { + struct mach_header header; + if (!readData(objFile, &header, sizeof(header), offset)) + return "read mach header failed"; + + ncmds = header.ncmds; + loadCommandsOffset += sizeof(header); + } + + off_t commandOffset = loadCommandsOffset; + struct load_command cmd = {}; + for (uint32_t i = 0U; i < ncmds; i++, commandOffset += cmd.cmdsize) + { + if (!readData(objFile, &cmd, sizeof(cmd), commandOffset)) + continue; + + if (cmd.cmd == LC_SEGMENT_64) + { + struct segment_command_64 segment; + if (!readData(objFile, &segment, sizeof(segment), commandOffset)) + continue; + + if (!ffStrEquals(segment.segname, "__TEXT")) continue; + + for (uint32_t j = 0U; j < segment.nsects; j++) + { + struct section_64 section; + if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section)))) + continue; + + if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata)) + return NULL; + } + } + else if (cmd.cmd == LC_SEGMENT) + { + struct segment_command segment; + if (!readData(objFile, &segment, sizeof(segment), commandOffset)) + continue; + + if (!ffStrEquals(segment.segname, "__TEXT")) continue; + + for (uint32_t j = 0; j < segment.nsects; j++) + { + struct section section; + if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section)))) + continue; + + if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata)) + return NULL; + } + } + + return NULL; + } + + return NULL; +} + +static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + struct fat_header header; + if (!readData(objFile, &header, sizeof(header), 0)) + return "read fat header failed"; + + bool needSwap = header.magic == FAT_CIGAM || header.magic == FAT_CIGAM_64; + + if (needSwap) swap_fat_header(&header, NX_UnknownByteOrder); + + for (uint32_t i = 0U; i < header.nfat_arch; i++) + { + off_t machHeaderOffset = 0; + if (header.magic == FAT_MAGIC) + { + struct fat_arch arch; + if (!readData(objFile, &arch, sizeof(arch), (off_t) (sizeof(header) + i * sizeof(arch)))) + continue; + + if (needSwap) + swap_fat_arch(&arch, 1, NX_UnknownByteOrder); + machHeaderOffset = (off_t)arch.offset; + } + else + { + struct fat_arch_64 arch; + if (!readData(objFile, &arch, sizeof(arch), (off_t) (sizeof(header) + i * sizeof(arch)))) + continue; + + if (needSwap) + swap_fat_arch_64(&arch, 1, NX_UnknownByteOrder); + + machHeaderOffset = (off_t)arch.offset; + } + + uint32_t magic; + if (!readData(objFile, &magic, sizeof(magic), machHeaderOffset)) + continue; + + if (magic == MH_MAGIC_64 || magic == MH_MAGIC) + { + dumpMachHeader(objFile, machHeaderOffset, magic == MH_MAGIC_64, cb, userdata); + return NULL; + } + } + return "Unsupported fat header"; +} + +const char *ffBinaryExtractStrings(const char *machoFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + FF_AUTO_CLOSE_FILE FILE *objFile = fopen(machoFile, "rb"); + if (objFile == NULL) + return "File could not be opened"; + + uint32_t magic; + if (!readData(objFile, &magic, sizeof(magic), 0)) + return "read magic number failed"; + + // MH_CIGAM and MH_CIGAM_64 seem to be no longer used, as `swap_mach_header` is marked as deprecated. + // However FAT_CIGAM and FAT_CIGAM_64 are still used (/usr/bin/vim). + if (magic != MH_MAGIC && magic != MH_MAGIC_64 && magic != FAT_CIGAM && magic != FAT_CIGAM_64 && magic != FAT_MAGIC && magic != FAT_MAGIC_64) + return "Unsupported format or big endian mach-o file"; + + if (magic == FAT_MAGIC || magic == FAT_MAGIC_64 || magic == FAT_CIGAM || magic == FAT_CIGAM_64) + return dumpFatHeader(objFile, cb, userdata); + else + return dumpMachHeader(objFile, 0, magic == MH_MAGIC_64, cb, userdata); +} diff --git a/src/util/binary.c b/src/util/binary_linux.c similarity index 100% rename from src/util/binary.c rename to src/util/binary_linux.c