diff --git a/.travis.yml b/.travis.yml index b8a6f25..a4f29bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -133,6 +133,7 @@ script: -Wno-dev -DCMAKE_BUILD_TYPE=${CONFIGURATION} -DCMAKE_INSTALL_PREFIX=mimick-${TRAVIS_TAG} + -DCMAKE_C_FLAGS="-Werror" ${CMAKE_OPTS} .. - TERM=dumb cmake --build . -- -j4 diff --git a/CMakeLists.txt b/CMakeLists.txt index ac52ff0..72888db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,11 @@ else () set (MMK_ABI "win") else () set (MMK_ABI "systemv") + if (CMAKE_C_COMPILER_ID MATCHES "GNU") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcf-protection") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-zibt -Wl,-zshstk") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-zibt -Wl,-zshstk") + endif () endif () set (MMK_ARCH "x86_64") set (MMK_BITS 64) @@ -92,6 +97,9 @@ else () else () message (FATAL_ERROR "Architecture '${_ARCH}' is not supported.") endif () + if (APPLE) + set (MMK_MANGLING "leading-underscore") + endif() set (ASM_EXTENSION ".S") endif () diff --git a/appveyor.yml b/appveyor.yml index 7b724ef..6b5101e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,6 +61,7 @@ install: -DCMAKE_INSTALL_PREFIX="mimick-%RELEASE_NAME%" -DCMAKE_BUILD_TYPE="%CONFIGURATION%" -DCMAKE_SYSTEM_PROCESSOR="%PLATFORM%" + -DCMAKE_C_FLAGS="-Werror" %CMAKE_OPTS% -G "%GENERATOR%" .. diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index f637b75..85745f9 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -1,5 +1,5 @@ if (NOT MSVC) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Werror") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic") endif () add_subdirectory (strdup) diff --git a/src/asm/trampoline-aarch64.S b/src/asm/trampoline-aarch64.S index 68e1a96..651ad55 100644 --- a/src/asm/trampoline-aarch64.S +++ b/src/asm/trampoline-aarch64.S @@ -23,7 +23,7 @@ */ #include "mangling.h" -.align 4 +.align 8 .globl MANGLE(mmk_trampoline) MANGLE(mmk_trampoline): adr x16, . @@ -72,7 +72,7 @@ ret_ctx: ldp x16, x30, [sp], #16 ret -.align 4 +.align 8 .globl MANGLE(mmk_trampoline_end) MANGLE(mmk_trampoline_end): nop diff --git a/src/asm/trampoline-x86_64-systemv.S b/src/asm/trampoline-x86_64-systemv.S index b74db90..774d6a8 100644 --- a/src/asm/trampoline-x86_64-systemv.S +++ b/src/asm/trampoline-x86_64-systemv.S @@ -31,11 +31,19 @@ movdqu (%rsp), Reg; \ add $0x10, %rsp +# if defined(__CET__) +# define mmk_cet endbr64 +# else +# define mmk_cet +# endif + .globl MANGLE(mmk_trampoline) MANGLE(mmk_trampoline): start: + mmk_cet call next // Retrieve IP next: + mmk_cet pop %r11 push %r11 // Setup mock context diff --git a/src/plt-elf.c b/src/plt-elf.c index 953f820..ac1443c 100644 --- a/src/plt-elf.c +++ b/src/plt-elf.c @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include +#include #include #include "mimick/assert.h" @@ -37,17 +37,17 @@ #if MMK_BITS == 32 typedef Elf32_Word ElfWord; typedef Elf32_Sword ElfSWord; -# ifndef ELF_R_SYM -# define ELF_R_SYM(i) ELF32_R_SYM(i) -# endif +#ifndef ELF_R_SYM +#define ELF_R_SYM(i) ELF32_R_SYM(i) +#endif #elif MMK_BITS == 64 typedef Elf64_Xword ElfWord; typedef Elf64_Sxword ElfSWord; -# ifndef ELF_R_SYM -# define ELF_R_SYM(i) ELF64_R_SYM(i) -# endif +#ifndef ELF_R_SYM +#define ELF_R_SYM(i) ELF64_R_SYM(i) +#endif #else -# error Unsupported architecture +#error Unsupported architecture #endif #if defined HAVE_ELF_AUXV_T @@ -55,401 +55,375 @@ typedef ElfW(auxv_t) ElfAux; #elif defined HAVE_ELF_AUXINFO typedef ElfW(Auxinfo) ElfAux; #else -# error Unsupported platform +#error Unsupported platform #endif -# if defined __clang__ +#if defined __clang__ void __clear_cache(void *, void *); -# endif +#endif extern char **environ; -static size_t get_offsets(plt_ctx ctx, plt_lib lib, const char *name, plt_offset ** offsets); - -static void *lib_dt_lookup(plt_lib lib, ElfSWord tag) -{ - ElfW(Addr) base =(ElfW(Addr)) lib->l_addr; - for (const ElfW(Dyn) *dyn = lib->l_ld; dyn->d_tag != DT_NULL; ++dyn) { - if (dyn->d_tag == tag) { - if (dyn->d_un.d_ptr >= base - && (dyn->d_un.d_ptr >> (MMK_BITS - 8)) ^ 0xff) - return (void*) dyn->d_un.d_ptr; - else - return (char*) base + dyn->d_un.d_ptr; - } +static size_t get_offsets(plt_ctx ctx, plt_lib lib, const char *name, + plt_offset **offsets); + +static void *lib_dt_lookup(plt_lib lib, ElfSWord tag) { + ElfW(Addr) base = (ElfW(Addr))lib->l_addr; + for (const ElfW(Dyn) *dyn = lib->l_ld; dyn->d_tag != DT_NULL; ++dyn) { + if (dyn->d_tag == tag) { + if (dyn->d_un.d_ptr >= base && (dyn->d_un.d_ptr >> (MMK_BITS - 8)) ^ 0xff) + return (void *)dyn->d_un.d_ptr; + else + return (char *)base + dyn->d_un.d_ptr; } - return NULL; + } + return NULL; } -static ElfWord lib_dt_lookup_val(plt_lib lib, ElfSWord tag) -{ - for (const ElfW(Dyn) *dyn = lib->l_ld; dyn->d_tag != DT_NULL; ++dyn) { - if (dyn->d_tag == tag) { - return dyn->d_un.d_val; - } +static ElfWord lib_dt_lookup_val(plt_lib lib, ElfSWord tag) { + for (const ElfW(Dyn) *dyn = lib->l_ld; dyn->d_tag != DT_NULL; ++dyn) { + if (dyn->d_tag == tag) { + return dyn->d_un.d_val; } - return 0; + } + return 0; } #if !defined HAVE__R_DEBUG -static int find_dynamic(struct dl_phdr_info *info, size_t size, void *data) -{ - ElfAddr *ctx = data; - - for (ElfOff i = 0; i < info->dlpi_phnum; ++i) { - if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) { - *ctx = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; - return 1; - } +static int find_dynamic(struct dl_phdr_info *info, size_t size, void *data) { + ElfW(Addr) *ctx = data; + + for (ElfW(Off) i = 0; i < info->dlpi_phnum; ++i) { + if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) { + *ctx = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; + return 1; } - return -1; + } + return -1; } -static struct r_debug *r_debug_from_dynamic(void *dynamic) -{ - for (const ElfW(Dyn) *dyn = dynamic; dyn->d_tag != DT_NULL; ++dyn) { - if (dyn->d_tag == DT_DEBUG) - return (struct r_debug *) dyn->d_un.d_ptr; - } - return NULL; +static struct r_debug *r_debug_from_dynamic(void *dynamic) { + for (const ElfW(Dyn) *dyn = dynamic; dyn->d_tag != DT_NULL; ++dyn) { + if (dyn->d_tag == DT_DEBUG) + return (struct r_debug *)dyn->d_un.d_ptr; + } + return NULL; } #endif -static struct r_debug *get_r_debug(void) -{ - // Find our own r_debug - struct r_debug *dbg = NULL; +static struct r_debug *get_r_debug(void) { + // Find our own r_debug + struct r_debug *dbg = NULL; - // First use some known shortcuts + // First use some known shortcuts #if defined HAVE__R_DEBUG - dbg = &_r_debug; + dbg = &_r_debug; #elif defined HAVE__DYNAMIC - dbg = r_debug_from_dynamic(_DYNAMIC); + dbg = r_debug_from_dynamic(_DYNAMIC); #endif #if !defined HAVE__R_DEBUG - // If there are no available shortcuts, we manually query our own phdrs -# if defined HAVE__DYNAMIC - if (!dbg) { -# endif - ElfAddr dynamic; - if (dl_iterate_phdr(find_dynamic, &dynamic) > 0) - dbg = r_debug_from_dynamic((void *) dynamic); -# if defined HAVE__DYNAMIC - } -# endif + // If there are no available shortcuts, we manually query our own phdrs +#if defined HAVE__DYNAMIC + if (!dbg) { +#endif + ElfW(Addr) dynamic; + if (dl_iterate_phdr(find_dynamic, &dynamic) > 0) + dbg = r_debug_from_dynamic((void *)dynamic); +#if defined HAVE__DYNAMIC + } +#endif #endif - return dbg; + return dbg; } -plt_ctx plt_init_ctx(void) -{ - static struct r_debug *dbg = (void*) -1; - if (dbg == (void*) -1) - dbg = get_r_debug(); - return dbg; +plt_ctx plt_init_ctx(void) { + static struct r_debug *dbg = (void *)-1; + if (dbg == (void *)-1) + dbg = get_r_debug(); + return dbg; } -static const char *get_lib_name(plt_ctx ctx, plt_lib lib) -{ - /* The name of the main shared object is the empty string, - we return something to be consistent with the eglibc weirdity */ - if (lib == ctx->r_map) - return "self"; +static const char *get_lib_name(plt_ctx ctx, plt_lib lib) { + /* The name of the main shared object is the empty string, + we return something to be consistent with the eglibc weirdity */ + if (lib == ctx->r_map) + return "self"; - /* Somewhy, eglibc always set l_name to the empty string. */ - if (lib->l_name[0]) - return lib->l_name; + /* Somewhy, eglibc always set l_name to the empty string. */ + if (lib->l_name[0]) + return lib->l_name; - const char *strtab = lib_dt_lookup(lib, DT_STRTAB); - ElfWord soname_off = lib_dt_lookup_val(lib, DT_SONAME); - if (!strtab || soname_off == (ElfWord) - 1) - return NULL; + const char *strtab = lib_dt_lookup(lib, DT_STRTAB); + ElfWord soname_off = lib_dt_lookup_val(lib, DT_SONAME); + if (!strtab || soname_off == (ElfWord)-1) + return NULL; - return &strtab[soname_off]; + return &strtab[soname_off]; } -plt_lib plt_get_lib(plt_ctx ctx, const char *name) -{ - if (!name) - return ctx->r_map; - - const char *val = NULL; - enum plt_selector sel = plt_get_selector(name, &val); - size_t val_len = strlen(val); - int libc = !strcmp(val, "c"); - - for (struct link_map *lm = ctx->r_map; lm != NULL; lm = lm->l_next) { - if (sel == PLT_SEL_LIB) { - const char *libname = get_lib_name(ctx, lm); - if (libc) { - if (strstr(libname, "/libc.so") - || strstr(libname, "/musl.so")) - return lm; - } else { - size_t len = val_len + 8; - char pattern[len]; - snprintf(pattern, len, "/lib%s.so", val); - if (strstr(libname, pattern)) - return lm; - } - } else if (sel == PLT_SEL_NONE || sel == PLT_SEL_FILE) { - const char *libname = get_lib_name(ctx, lm); - if (!strcmp(name, libname)) - return lm; - } else if (sel == PLT_SEL_SYM) { - if (get_offsets(ctx, lm, val, NULL) > 0) - return lm; - } +plt_lib plt_get_lib(plt_ctx ctx, const char *name) { + if (!name) + return ctx->r_map; + + const char *val = NULL; + enum plt_selector sel = plt_get_selector(name, &val); + size_t val_len = strlen(val); + int libc = !strcmp(val, "c"); + + for (struct link_map *lm = ctx->r_map; lm != NULL; lm = lm->l_next) { + if (sel == PLT_SEL_LIB) { + const char *libname = get_lib_name(ctx, lm); + if (libc) { + if (strstr(libname, "/libc.so") || strstr(libname, "/musl.so")) + return lm; + } else { + size_t len = val_len + 8; + char pattern[len]; + snprintf(pattern, len, "/lib%s.so", val); + if (strstr(libname, pattern)) + return lm; + } + } else if (sel == PLT_SEL_NONE || sel == PLT_SEL_FILE) { + const char *libname = get_lib_name(ctx, lm); + if (!strcmp(val, libname)) + return lm; + } else if (sel == PLT_SEL_SYM) { + if (get_offsets(ctx, lm, val, NULL) > 0) + return lm; } - return NULL; + } + return NULL; } struct rel_info { - ElfW(Rel) *tab; - ElfWord size; - ElfWord entry_sz; + ElfW(Rel) * tab; + ElfWord size; + ElfWord entry_sz; }; -static uintptr_t get_offset(struct rel_info *info, ElfW(Sym) *symtab, - const char *strtab, const char *name) -{ - ElfW(Rel) *rel = info->tab; - for (ElfWord i = 0; i < info->size / info->entry_sz; - ++i, rel = (void*)(((char *) rel) + info->entry_sz)) - { - ElfW(Sym) *sym = &symtab[ELF_R_SYM(rel->r_info)]; - - if (!strcmp(strtab + sym->st_name, name)) - return (uintptr_t) rel->r_offset; - } - return 0; +static uintptr_t get_offset(struct rel_info *info, ElfW(Sym) * symtab, + const char *strtab, const char *name) { + ElfW(Rel) *rel = info->tab; + for (ElfWord i = 0; i < info->size / info->entry_sz; + ++i, rel = (void *)(((char *)rel) + info->entry_sz)) { + ElfW(Sym) *sym = &symtab[ELF_R_SYM(rel->r_info)]; + + if (!strcmp(strtab + sym->st_name, name)) + return (uintptr_t)rel->r_offset; + } + return 0; } -static size_t get_offsets(plt_ctx ctx, plt_lib lib, const char *name, plt_offset ** offsets) -{ - ElfW(Sym) *symtab = (ElfW(Sym)*) lib_dt_lookup(lib, DT_SYMTAB); - const char *strtab = (const char*) lib_dt_lookup(lib, DT_STRTAB); - - ElfW(Rel) *jmprel = lib_dt_lookup(lib, DT_JMPREL); - ElfW(Rel) *rel = lib_dt_lookup(lib, DT_RELA); - ElfWord rel_sz = lib_dt_lookup_val(lib, DT_RELASZ); - ElfWord jmprel_sz = lib_dt_lookup_val(lib, DT_PLTRELSZ); - ElfWord relent_sz = lib_dt_lookup_val(lib, DT_RELAENT); - - if (!symtab || !strtab || !(rel || jmprel) || !(rel_sz || jmprel_sz) || !relent_sz) - return 0; +static size_t get_offsets(plt_ctx ctx, plt_lib lib, const char *name, + plt_offset **offsets) { + ElfW(Sym) *symtab = (ElfW(Sym) *)lib_dt_lookup(lib, DT_SYMTAB); + const char *strtab = (const char *)lib_dt_lookup(lib, DT_STRTAB); + + ElfW(Rel) *jmprel = lib_dt_lookup(lib, DT_JMPREL); + + ElfWord jmprel_sz = lib_dt_lookup_val(lib, DT_PLTRELSZ); + ElfWord relent_sz; + ElfWord rel_sz; + + // For relocation sections try DT_RELA first, then DT_REL. We don't deal with + // addends anyway. + ElfW(Rel) *rel = lib_dt_lookup(lib, DT_RELA); + if (!rel) { + rel = lib_dt_lookup(lib, DT_REL); + if (!rel) + return 0; + rel_sz = lib_dt_lookup_val(lib, DT_RELSZ); + relent_sz = lib_dt_lookup_val(lib, DT_RELENT); + } else { + rel_sz = lib_dt_lookup_val(lib, DT_RELASZ); + relent_sz = lib_dt_lookup_val(lib, DT_RELAENT); + } + + if (!symtab || !strtab || !(rel || jmprel) || !(rel_sz || jmprel_sz) || + !relent_sz) + return 0; - ElfW(Addr) base = (ElfW(Addr)) lib->l_addr; + ElfW(Addr) base = (ElfW(Addr))lib->l_addr; #ifdef __FreeBSD__ - if (lib == ctx->r_map) - base = 0; + if (lib == ctx->r_map) + base = 0; #endif - size_t n = 0; - if (offsets) { - *offsets = NULL; + size_t n = 0; + if (offsets) { + *offsets = NULL; + } + + struct rel_info info[] = {{ + .tab = rel, + .size = rel_sz, + .entry_sz = relent_sz, + }, + { + .tab = jmprel, + .size = jmprel_sz, + .entry_sz = relent_sz, + }}; + + for (size_t i = 0; i < sizeof(info) / sizeof(struct rel_info); ++i) { + uintptr_t off = get_offset(&info[i], symtab, strtab, name); + if (off) { + if (offsets) { + *offsets = mmk_realloc(*offsets, (n + 1) * sizeof(plt_offset)); + (*offsets)[n] = (plt_offset){.offset = (plt_fn **)(base + off)}; + } + ++n; } + } - struct rel_info info[] = { - { - .tab = rel, - .size = rel_sz, - .entry_sz = relent_sz, - }, - { - .tab = jmprel, - .size = jmprel_sz, - .entry_sz = relent_sz, - } - }; - - for (size_t i = 0; i < sizeof(info) / sizeof(struct rel_info); ++i) { - uintptr_t off = get_offset(&info[i], symtab, strtab, name); - if (off) { - if (offsets) { - *offsets = mmk_realloc(*offsets, (n + 1) * sizeof(plt_offset)); - (*offsets)[n] = (plt_offset) { .offset = (plt_fn **)(base + off) }; - } - ++n; - } - } + return n; +} - return n; +plt_offset *plt_get_offsets(plt_ctx ctx, plt_lib lib, const char *name, + size_t *n) { + plt_offset *ot = NULL; + *n = get_offsets(ctx, lib, name, &ot); + return ot; } -plt_offset *plt_get_offsets(plt_ctx ctx, plt_lib lib, const char *name, size_t *n) -{ - plt_offset *ot = NULL; - *n = get_offsets(ctx, lib, name, &ot); - return ot; +#define align2_down(v, d) ((v) & ~((d)-1)) + +void plt_set_offsets(plt_offset *offset, size_t nb_off, plt_fn *newval) { + for (size_t i = 0; i < nb_off; ++i) { + if (!offset[i].oldval) + offset[i].oldval = *offset[i].offset; + void *page_start = (void *)align2_down((uintptr_t)offset[i].offset, 4096); + + /* making a page in a rx segment rwx is usually very bad practice, + but this is a test context, we don't have to care as much. + Implementing this right assumes that we have a way to know + the protection of an existing page, which is not necessarily + available on all unices. */ + mmk_mprotect(page_start, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); +#if defined __clang__ // Check for Clang first, it may set __GNUC__ too. + __clear_cache(page_start, page_start + 4096); +#elif defined __GNUC__ + __builtin___clear_cache((char *)page_start, (char *)(page_start + 4096)); +#endif + *offset[i].offset = newval; + } } -#define align2_down(v, d) ((v) & ~((d) - 1)) - -void plt_set_offsets(plt_offset *offset, size_t nb_off, plt_fn *newval) -{ - for (size_t i = 0; i < nb_off; ++i) { - if (!offset[i].oldval) - offset[i].oldval = *offset[i].offset; - void *page_start = (void *)align2_down((uintptr_t)offset[i].offset, 4096); - - /* making a page in a rx segment rwx is usually very bad practice, - but this is a test context, we don't have to care as much. - Implementing this right assumes that we have a way to know - the protection of an existing page, which is not necessarily - available on all unices. */ - mmk_mprotect(page_start, 4096, PROT_READ|PROT_WRITE|PROT_EXEC); -# if defined __clang__ // Check for Clang first, it may set __GNUC__ too. - __clear_cache(page_start, page_start + 4096); -# elif defined __GNUC__ - __builtin___clear_cache((char *)page_start, (char *)(page_start + 4096)); -# endif - *offset[i].offset = newval; - } +void plt_reset_offsets(plt_offset *offset, size_t nb_off) { + for (size_t i = 0; i < nb_off; ++i) { + *offset[i].offset = offset[i].oldval; + } } -void plt_reset_offsets(plt_offset *offset, size_t nb_off) -{ - for (size_t i = 0; i < nb_off; ++i) { - *offset[i].offset = offset[i].oldval; - } +static unsigned long elf_hash(const char *s) { + unsigned long h = 0, high; + while (*s) { + h = (h << 4) + (unsigned char)*s++; + if ((high = h & 0xf0000000)) + h ^= high >> 24; + h &= ~high; + } + return h; } -static uint32_t elf_gnu_hash(const char * name) -{ - uint32_t h = 5381; +static ElfW(Sym) * elf_hash_find(ElfW(Word) * hash, ElfW(Sym) * symtab, + const char *strtab, const char *name) { + struct { + ElfW(Word) nb_buckets; + ElfW(Word) nb_chains; + } *h_info = (void *)hash; - for (unsigned char c = *name; c != '\0'; c = *++name) - h = (h << 5) + h + *name; + ElfW(Word) *buckets = (ElfW(Word) *)(h_info + 1); + ElfW(Word) *chains = (ElfW(Word) *)(h_info + 1) + h_info->nb_buckets; - return h; -} + unsigned long idx = elf_hash(name) % h_info->nb_buckets; -static unsigned long elf_hash (const char *s) -{ - unsigned long h = 0, high; - while (*s) { - h = (h << 4) + (unsigned char) *s++; - if ((high = h & 0xf0000000)) - h ^= high >> 24; - h &= ~high; - } - return h; + for (ElfW(Word) si = buckets[idx]; si != STN_UNDEF; si = chains[si]) { + if (mmk_streq(&strtab[symtab[si].st_name], name)) + return &symtab[si]; + } + return NULL; } -static ElfW(Sym) *elf_gnu_hash_find(const ElfW(Word) *gnu_hash, ElfW(Sym) *symtab, - const char *strtab, const char *name) -{ - const uint32_t namehash = elf_gnu_hash(name); - - const uint32_t nbuckets = gnu_hash[0]; - const uint32_t symoffset = gnu_hash[1]; - const uint32_t bloom_size = gnu_hash[2]; - const uint32_t bloom_shift = gnu_hash[3]; - const ElfWord *bloom = (void*)&gnu_hash[4]; - const uint32_t *buckets = (void*)&bloom[bloom_size]; - const uint32_t *chain = &buckets[nbuckets]; - - ElfWord word = bloom[(namehash / MMK_BITS) % bloom_size]; - ElfWord mask = 0 - | (ElfWord)1 << (namehash % MMK_BITS) - | (ElfWord)1 << ((namehash >> bloom_shift) % MMK_BITS); - - /* If at least one bit is not set, a symbol is surely missing. */ - if ((word & mask) != mask) - return NULL; - - uint32_t symix = buckets[namehash % nbuckets]; - if (symix < symoffset) - return NULL; - - size_t name_len = mmk_strlen(name); - - /* Loop through the chain. */ - while (1) { - const char *symname = strtab + symtab[symix].st_name; - const uint32_t hash = chain[symix - symoffset]; - size_t symname_len = strlen(symname); - size_t cmp_len = (name_len < symname_len) ? name_len : symname_len; - - if ((namehash|1) == (hash|1) && mmk_memcmp(name, symname, cmp_len) == 0) - return &symtab[symix]; - - /* Chain ends with an element with the lowest bit set to 1. */ - if (hash & 1) - break; - - symix++; - } +struct gnu_hash_header { + uint32_t nbuckets; + uint32_t symoffset; + uint32_t bloom_size; + uint32_t bloom_shift; +}; - return NULL; +static uint32_t gnu_hash(const char *s) { + const uint8_t *name = (const uint8_t *)s; + uint32_t h = 5381; + while (*name) + h = (h << 5) + h + *name++; + return h; } -static ElfW(Sym) *elf_hash_find(ElfW(Word) *hash, ElfW(Sym) *symtab, - const char *strtab, const char *name) -{ - struct { - ElfW(Word) nb_buckets; - ElfW(Word) nb_chains; - } *h_info = (void*) hash; +static ElfW(Sym) * gnu_hash_find(struct gnu_hash_header *gnuhash, + ElfW(Sym) * symtab, const char *strtab, + const char *name) { + ElfW(Off) *bloom = (ElfW(Off) *)(gnuhash + 1); + uint32_t *buckets = (uint32_t *)(bloom + gnuhash->bloom_size); + uint32_t *chains = buckets + gnuhash->nbuckets; - ElfW(Word) *buckets = (ElfW(Word)*) (h_info + 1); - ElfW(Word) *chains = (ElfW(Word)*) (h_info + 1) + h_info->nb_buckets; + uint32_t symhash = gnu_hash(name); - unsigned long idx = elf_hash(name) % h_info->nb_buckets; - - for (ElfW(Word) si = buckets[idx]; si != STN_UNDEF; si = chains[si]) { - if (mmk_streq(&strtab[symtab[si].st_name], name)) - return &symtab[si]; - } + // Grab the bloom filter entry, and test both h1 and h2 are present. + ElfW(Off) filter = bloom[(symhash / MMK_BITS) % gnuhash->bloom_size]; + if ((filter & (1UL << symhash % MMK_BITS)) == 0) + return NULL; + if ((filter & (1UL << (symhash >> gnuhash->bloom_shift) % MMK_BITS)) == 0) return NULL; + + for (uint32_t idx = buckets[symhash % gnuhash->nbuckets];; ++idx) { + uint32_t chainhash = chains[idx - gnuhash->symoffset]; + if ((chainhash | 1) == (symhash | 1) && + strcmp(strtab + symtab[idx].st_name, name) == 0) + return &symtab[idx]; + if ((chainhash & 1) != 0) + break; + } + return NULL; } -static ElfW(Sym) *sym_lookup_dyn(plt_lib lib, const char *name) -{ - ElfW(Sym) *symtab = (ElfW(Sym)*) lib_dt_lookup(lib, DT_SYMTAB); - if (!symtab) - return NULL; - - const char *strtab = (const char*) lib_dt_lookup(lib, DT_STRTAB); - if (!strtab) - return NULL; - - ElfW(Sym) *symbol = NULL; - - // DT_GNU_HASH is the "modern" way to lookup symbols. If we have that, - // use it. - ElfW(Word) *gnu_hash = (ElfW(Word)*) lib_dt_lookup(lib, DT_GNU_HASH); - if (gnu_hash) { - symbol = elf_gnu_hash_find(gnu_hash, symtab, strtab, name); - if (symbol) - return symbol; - } +static ElfW(Sym) * sym_lookup_dyn(plt_lib lib, const char *name) { + ElfW(Sym) *symtab = lib_dt_lookup(lib, DT_SYMTAB); + const char *strtab = lib_dt_lookup(lib, DT_STRTAB); - // DT_HASH is the older, deprecated way to find symbols. Only - // attempt to use it if we can't find the symbols via DT_GNU_HASH. - ElfW(Word) *hash = (ElfW(Word)*) lib_dt_lookup(lib, DT_HASH); - if (hash) - symbol = elf_hash_find (hash, symtab, strtab, name); + if (!symtab || !strtab) + return NULL; + +#ifdef DT_GNU_HASH + // trust GNU hash if we have it. + struct gnu_hash_header *gnu_hash = lib_dt_lookup(lib, DT_GNU_HASH); + if (gnu_hash) + return gnu_hash_find(gnu_hash, symtab, strtab, name); +#endif - return symbol; + // Look up symbol table using traditional ELF hash. + ElfW(Word) *hash = lib_dt_lookup(lib, DT_HASH); + if (hash) + return elf_hash_find(hash, symtab, strtab, name); + + // XXX: we could do a linear walk of the symbol table here... + return NULL; } -plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name) -{ - for (struct link_map *lm = ctx->r_map; lm != NULL; lm = lm->l_next) { - ElfW(Sym) *sym = sym_lookup_dyn(lm, name); - if (sym) { - /* Some compilers (e.g. ICC) put unresolved symbols into the - symbol table with a size of 0. We ignore them to avoid - getting the address of the PLT stub. */ - if (sym->st_size == 0) - continue; - - return (void *) (sym->st_value + lm->l_addr); - } +plt_fn *plt_get_real_fn(plt_ctx ctx, const char *name) { + for (struct link_map *lm = ctx->r_map; lm != NULL; lm = lm->l_next) { + ElfW(Sym) *sym = sym_lookup_dyn(lm, name); + if (sym) { + /* Some compilers (e.g. ICC) put unresolved symbols into the + symbol table with a size of 0. We ignore them to avoid + getting the address of the PLT stub. */ + if (sym->st_size == 0) + continue; + + return (void *)(sym->st_value + lm->l_addr); } - return NULL; + } + return NULL; } diff --git a/src/plt-mach-o.c b/src/plt-mach-o.c index 3db34ca..5603c3d 100644 --- a/src/plt-mach-o.c +++ b/src/plt-mach-o.c @@ -85,7 +85,7 @@ plt_lib plt_get_lib(plt_ctx ctx, const char *name) return i; } else if (sel == PLT_SEL_NONE || sel == PLT_SEL_FILE) { const char *img_name = _dyld_get_image_name(i); - if (img_name && !strcmp(img_name, name)) + if (img_name && !strcmp(img_name, val)) return i; } else if (sel == PLT_SEL_SYM) { plt_offset *off = plt_get_offsets(ctx, i, val, NULL); diff --git a/src/trampoline.c b/src/trampoline.c index ef3695b..22ece8a 100644 --- a/src/trampoline.c +++ b/src/trampoline.c @@ -34,93 +34,86 @@ extern void mmk_trampoline(); extern void mmk_trampoline_end(); #if defined HAVE_MMAP -# include -# include +#include +#include -# ifndef HAVE_MMAP_MAP_ANONYMOUS -# include -# endif +#ifndef HAVE_MMAP_MAP_ANONYMOUS +#include +#endif -# if defined __clang__ +#if defined __APPLE__ +#include // LLVM __clear_cache seems not working on Mac ARM64 +#elif defined __clang__ void __clear_cache(void *, void *); -# endif - -plt_fn *create_trampoline(void *ctx, plt_fn *routine) -{ - uintptr_t trampoline_sz = (uintptr_t) mmk_trampoline_end - - (uintptr_t) mmk_trampoline; - - mmk_assert(trampoline_sz < PAGE_SIZE); - -# if defined HAVE_MMAP_MAP_ANONYMOUS - void **map = mmap(NULL, PAGE_SIZE, -# if !defined __APPLE__ - PROT_READ | PROT_WRITE | PROT_EXEC, -# else - PROT_READ | PROT_WRITE, -# endif - MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); -# elif defined HAVE_MMAP_MAP_ANON - void **map = mmap(NULL, PAGE_SIZE, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, 0); -# else - int fd = open("/dev/zero", O_RDWR); - mmk_assert(fd != -1); - - void **map = mmap(NULL, PAGE_SIZE, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE, - fd, 0); - - mmk_assert(close(fd) != -1); -# endif - - mmk_assert(map != MAP_FAILED); - - *map = ctx; - *(map + 1) = (void *) routine; - memcpy(map + 2, mmk_trampoline, trampoline_sz); - mmk_assert(!mmk_mprotect(map, PAGE_SIZE, PROT_READ | PROT_EXEC)); -# if defined __clang__ // Check for Clang first, it may set __GNUC__ too. - __clear_cache(map, map + 2 + trampoline_sz); -# elif defined __GNUC__ - __builtin___clear_cache((char *)map, (char *)(map + 2 + trampoline_sz)); -# endif - return (plt_fn *) (map + 2); +#endif + +plt_fn *create_trampoline(void *ctx, plt_fn *routine) { + uintptr_t trampoline_sz = + (uintptr_t)mmk_trampoline_end - (uintptr_t)mmk_trampoline; + + mmk_assert(trampoline_sz < PAGE_SIZE); + +#if defined HAVE_MMAP_MAP_ANONYMOUS +#if !defined MAP_JIT +#define MAP_JIT 0 +#endif + void **map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT, -1, 0); +#elif defined HAVE_MMAP_MAP_ANON + void **map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); +#else + int fd = open("/dev/zero", O_RDWR); + mmk_assert(fd != -1); + + void **map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE, fd, 0); + + mmk_assert(close(fd) != -1); +#endif + + mmk_assert(map != MAP_FAILED); + + *map = ctx; + *(map + 1) = (void *)routine; + memcpy(map + 2, mmk_trampoline, trampoline_sz); + mmk_assert(!mmk_mprotect(map, PAGE_SIZE, PROT_READ | PROT_EXEC)); +#if defined __APPLE__ + sys_icache_invalidate(map, PAGE_SIZE); +#elif defined __clang__ // Check for Clang first, it may set __GNUC__ too. + __clear_cache(map, map + PAGE_SIZE); +#elif defined __GNUC__ + __builtin___clear_cache((char *)map, (char *)(map + 2 + trampoline_sz)); +#endif + return (plt_fn *)(map + 2); } -void destroy_trampoline(plt_fn *trampoline) -{ - munmap((void **) trampoline - 2, PAGE_SIZE); +void destroy_trampoline(plt_fn *trampoline) { + munmap((void **)trampoline - 2, PAGE_SIZE); } #elif defined _WIN32 -# include +#include -plt_fn *create_trampoline(void *ctx, plt_fn *routine) -{ - uintptr_t trampoline_sz = (uintptr_t) mmk_trampoline_end - - (uintptr_t) mmk_trampoline; +plt_fn *create_trampoline(void *ctx, plt_fn *routine) { + uintptr_t trampoline_sz = + (uintptr_t)mmk_trampoline_end - (uintptr_t)mmk_trampoline; - mmk_assert(trampoline_sz < PAGE_SIZE); - void **map = VirtualAlloc(NULL, PAGE_SIZE, - MEM_COMMIT, PAGE_EXECUTE_READWRITE); + mmk_assert(trampoline_sz < PAGE_SIZE); + void **map = + VirtualAlloc(NULL, PAGE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE); - *map = ctx; - *(map + 1) = (void *) routine; - memcpy(map + 2, mmk_trampoline, trampoline_sz); + *map = ctx; + *(map + 1) = (void *)routine; + memcpy(map + 2, mmk_trampoline, trampoline_sz); - DWORD old; - VirtualProtect(map, PAGE_SIZE, PAGE_EXECUTE_READ, &old); - return (plt_fn *) (map + 2); + DWORD old; + VirtualProtect(map, PAGE_SIZE, PAGE_EXECUTE_READ, &old); + return (plt_fn *)(map + 2); } -void destroy_trampoline(plt_fn *trampoline) -{ - VirtualFree((void **) trampoline - 2, 0, MEM_RELEASE); +void destroy_trampoline(plt_fn *trampoline) { + VirtualFree((void **)trampoline - 2, 0, MEM_RELEASE); } #else -# error Unsupported platform +#error Unsupported platform #endif diff --git a/src/vitals.c b/src/vitals.c index 0e11286..46bce7f 100644 --- a/src/vitals.c +++ b/src/vitals.c @@ -28,183 +28,159 @@ #include "vitals.h" -int mmk_memcmp(const void *s1, const void *s2, size_t n) -{ - for (const char *s1_ = s1, *s2_ = s2; n; ++s1_, ++s2_, --n) - if (*s1_ != *s2_) - return *s1_ < *s2_ ? -1 : 1; - return 0; +int mmk_memcmp(const void *s1, const void *s2, size_t n) { + for (const char *s1_ = s1, *s2_ = s2; n; ++s1_, ++s2_, --n) + if (*s1_ != *s2_) + return *s1_ < *s2_ ? -1 : 1; + return 0; } -void *mmk_memcpy(void *dst, const void *src, size_t n) -{ - const char *src_ = src; - for (char *dst_ = dst; n; ++dst_, ++src_, --n) - *dst_ = *src_; - return dst; +void *mmk_memcpy(void *dst, const void *src, size_t n) { + const char *src_ = src; + for (char *dst_ = dst; n; ++dst_, ++src_, --n) + *dst_ = *src_; + return dst; } -void *mmk_memset(void *dst, int value, size_t n) -{ - for (char *dst_ = dst; n > 0; --n, ++dst_) { - *dst_ = (char)value; - } - return dst; +void *mmk_memset(void *dst, int value, size_t n) { + for (char *dst_ = dst; n > 0; --n, ++dst_) { + *dst_ = (char)value; + } + return dst; } -int mmk_strneq(const char *src, const char *ref, size_t n) -{ - for (; *src && *ref && n; ++src, ++ref, --n) { - if (*src != *ref) - return 0; - } - return n == 0 || !*ref; +int mmk_strneq(const char *src, const char *ref, size_t n) { + for (; *src && *ref && n; ++src, ++ref, --n) { + if (*src != *ref) + return 0; + } + return n == 0 || !*ref; } -int mmk_streq(const char *src, const char *ref) -{ - for (; *src && *ref; ++src, ++ref) { - if (*src != *ref) - return 0; - } - return *ref == *src; +int mmk_streq(const char *src, const char *ref) { + for (; *src && *ref; ++src, ++ref) { + if (*src != *ref) + return 0; + } + return *ref == *src; } -char *mmk_strchr(const char *buf, int c) -{ - for (; *buf; ++buf) - if (*buf == c) - return (char *) buf; - return NULL; +char *mmk_strchr(const char *buf, int c) { + for (; *buf; ++buf) + if (*buf == c) + return (char *)buf; + return NULL; } -char *mmk_strcpy(char *dst, const char *src) -{ - for (; *src; ++dst, ++src) - *dst = *src; - *dst = '\0'; - return dst; +char *mmk_strcpy(char *dst, const char *src) { + for (; *src; ++dst, ++src) + *dst = *src; + *dst = '\0'; + return dst; } -char *mmk_strncpy(char *dst, const char *src, size_t n) -{ - for (; *src && n; ++dst, ++src, --n) - *dst = *src; - *dst = '\0'; - return dst; +char *mmk_strncpy(char *dst, const char *src, size_t n) { + for (; *src && n; ++dst, ++src, --n) + *dst = *src; + *dst = '\0'; + return dst; } -size_t mmk_strlen(const char *s) -{ - size_t len = 0; - for (; *s; ++len, ++s); - return len; +size_t mmk_strlen(const char *s) { + size_t len = 0; + for (; *s; ++len, ++s) + ; + return len; } -int mmk_isspace(int c) -{ - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +int mmk_isspace(int c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } int (*mmk_mprotect_)(void *, size_t, int); -int mmk_mprotect(void *addr, size_t len, int prot) -{ - return mmk_mprotect_(addr, len, prot); +int mmk_mprotect(void *addr, size_t len, int prot) { + return mmk_mprotect_(addr, len, prot); } void *(*mmk_malloc_)(size_t); -void *mmk_malloc(size_t size) -{ - return mmk_malloc_(size); -} +void *mmk_malloc(size_t size) { return mmk_malloc_(size); } void *(*mmk_realloc_)(void *, size_t); -void *mmk_realloc(void *ptr, size_t size) -{ - return mmk_realloc_(ptr, size); -} +void *mmk_realloc(void *ptr, size_t size) { return mmk_realloc_(ptr, size); } void (*mmk_free_)(void *); -void mmk_free(void *ptr) -{ - mmk_free_(ptr); -} +void mmk_free(void *ptr) { mmk_free_(ptr); } void (*mmk_abort_)(void); -void mmk_abort(void) -{ - mmk_abort_(); -} +void mmk_abort(void) { mmk_abort_(); } void (*mmk_vfprintf_)(FILE *, const char *, va_list); -void mmk_fprintf(FILE *f, const char *str, ...) -{ - va_list vl; - va_start(vl, str); - mmk_vfprintf_(f, str, vl); - va_end(vl); +void mmk_fprintf(FILE *f, const char *str, ...) { + va_list vl; + va_start(vl, str); + mmk_vfprintf_(f, str, vl); + va_end(vl); } -mmk_noreturn void mmk_panic(const char *str, ...) -{ - va_list vl; - va_start(vl, str); - mmk_vfprintf_(stderr, str, vl); - va_end(vl); - mmk_abort(); - mmk_unreachable(); +mmk_noreturn void mmk_panic(const char *str, ...) { + va_list vl; + va_start(vl, str); + mmk_vfprintf_(stderr, str, vl); + va_end(vl); + mmk_abort(); + mmk_unreachable(); } #ifdef HAVE___STDIO_COMMON_VFPRINTF -static int (__cdecl *mmk___stdio_common_vfprintf_)( - unsigned __int64, FILE *, char const *, _locale_t, va_list); +static int(__cdecl *mmk___stdio_common_vfprintf_)(unsigned __int64, FILE *, + char const *, _locale_t, + va_list); -static int win32_vfprintf_fallback(FILE *f, const char *fmt, va_list vl) -{ - return mmk___stdio_common_vfprintf_(_CRT_INTERNAL_LOCAL_PRINTF_OPTIONS, - f, fmt, NULL, vl); +static int win32_vfprintf_fallback(FILE *f, const char *fmt, va_list vl) { + return mmk___stdio_common_vfprintf_(_CRT_INTERNAL_LOCAL_PRINTF_OPTIONS, f, + fmt, NULL, vl); } #endif -# define INIT_VITAL_FUNC(Id) do { \ - mmk_ ## Id ## _ = (void *) plt_get_real_fn(ctx, #Id); \ - if (!mmk_ ## Id ## _) \ - mmk_panic("mimick: Initialization error: could not find " \ - "definition for vital function '" #Id "'.\n"); \ - } while (0) +#define INIT_VITAL_FUNC(Id) \ + do { \ + mmk_##Id##_ = (void *)plt_get_real_fn(ctx, #Id); \ + if (!mmk_##Id##_) \ + mmk_panic("mimick: Initialization error: could not find " \ + "definition for vital function '" #Id "'.\n"); \ + } while (0) -void mmk_init_vital_functions(plt_ctx ctx) -{ - mmk_vfprintf_ = (void *) plt_get_real_fn(ctx, "vfprintf"); - mmk_abort_ = (void *) plt_get_real_fn(ctx, "abort"); +void mmk_init_vital_functions(plt_ctx ctx) { + mmk_vfprintf_ = (void *)plt_get_real_fn(ctx, "vfprintf"); + mmk_abort_ = (void *)plt_get_real_fn(ctx, "abort"); #ifdef HAVE___STDIO_COMMON_VFPRINTF - /* Windows doesn't always dynlink to msvcstr.dll (when the universal CRT - is used), so we don't have the definition for vfprintf since ucrt - may define it as an inline function that calls - __stdio_common_vfprintf. */ - if (!mmk_vfprintf_) { - mmk___stdio_common_vfprintf_ = (void *) - plt_get_real_fn(ctx, "__stdio_common_vfprintf"); - if (mmk___stdio_common_vfprintf_) - mmk_vfprintf_ = (void *)win32_vfprintf_fallback; - } + /* Windows doesn't always dynlink to msvcstr.dll (when the universal CRT + is used), so we don't have the definition for vfprintf since ucrt + may define it as an inline function that calls + __stdio_common_vfprintf. */ + if (!mmk_vfprintf_) { + mmk___stdio_common_vfprintf_ = + (void *)plt_get_real_fn(ctx, "__stdio_common_vfprintf"); + if (mmk___stdio_common_vfprintf_) + mmk_vfprintf_ = (void *)win32_vfprintf_fallback; + } #endif - /* Don't use mmk_panic yet, since it depends on both mmk_abort and - mmk_vfprintf. */ - if (!mmk_abort_ || !mmk_vfprintf_) { - fprintf(stderr, "mimick: Initialization error: could not find " - "definitions for vital function(s): %s %s\n", - mmk_abort_ ? "" : "'abort'", - mmk_vfprintf_ ? "" : "'vfprintf'"); - abort(); - } - - INIT_VITAL_FUNC(malloc); - INIT_VITAL_FUNC(realloc); - INIT_VITAL_FUNC(free); -#ifdef HAVE_MMAP - INIT_VITAL_FUNC(mprotect); + /* Don't use mmk_panic yet, since it depends on both mmk_abort and + mmk_vfprintf. */ + if (!mmk_abort_ || !mmk_vfprintf_) { + fprintf(stderr, + "mimick: Initialization error: could not find " + "definitions for vital function(s): %s %s\n", + mmk_abort_ ? "" : "'abort'", mmk_vfprintf_ ? "" : "'vfprintf'"); + abort(); + } + + INIT_VITAL_FUNC(malloc); + INIT_VITAL_FUNC(realloc); + INIT_VITAL_FUNC(free); +#if defined(MMK_EXE_FMT_ELF) || defined(MMK_EXE_FMT_MACH_O) + INIT_VITAL_FUNC(mprotect); #endif } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e07b625..0a83c77 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ if (NOT MSVC) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Werror") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic") endif () add_library (foo SHARED libfoo.c)