From 032100cc3bdb437562d84d1541678bcadeaea297 Mon Sep 17 00:00:00 2001 From: Nathan Hjelm Date: Wed, 30 Mar 2016 07:57:43 -0600 Subject: [PATCH] patcher: fix powerpc/power support in patcher/linux This commit does the following: - Move code necessary for powerpc/power support to the patcher base. The code is needed by both the overwrite and linux components. - Move patch structure down to base and move the patch list to mca_patcher_base_module_t. The structure has been modified to include a function pointer to the function that will unapply the patch. This allows the mixing of multiple different types of patches in the patch_list. - Update linux patching code to keep track of the matching between got entry and original (unpatched) address. This allows us to completely clean up the patch on finalize. Signed-off-by: Nathan Hjelm --- opal/mca/patcher/base/Makefile.am | 3 +- opal/mca/patcher/base/base.h | 43 ++++ opal/mca/patcher/base/patcher_base_frame.c | 11 + opal/mca/patcher/base/patcher_base_patch.c | 175 +++++++++++++++ opal/mca/patcher/linux/patcher_linux.h | 32 +-- .../patcher/linux/patcher_linux_component.c | 2 +- opal/mca/patcher/linux/patcher_linux_module.c | 171 ++++++++++----- .../mca/patcher/overwrite/patcher_overwrite.h | 49 +---- .../overwrite/patcher_overwrite_component.c | 4 +- .../overwrite/patcher_overwrite_module.c | 206 ++++-------------- opal/mca/patcher/patcher.h | 13 +- 11 files changed, 411 insertions(+), 298 deletions(-) create mode 100644 opal/mca/patcher/base/patcher_base_patch.c diff --git a/opal/mca/patcher/base/Makefile.am b/opal/mca/patcher/base/Makefile.am index 0a3d2c8224e..441e1c645d2 100644 --- a/opal/mca/patcher/base/Makefile.am +++ b/opal/mca/patcher/base/Makefile.am @@ -21,4 +21,5 @@ headers += base/base.h -libmca_patcher_la_SOURCES += base/patcher_base_frame.c +libmca_patcher_la_SOURCES += base/patcher_base_frame.c \ + base/patcher_base_patch.c diff --git a/opal/mca/patcher/base/base.h b/opal/mca/patcher/base/base.h index f52dce6104f..65b48fc00c0 100644 --- a/opal/mca/patcher/base/base.h +++ b/opal/mca/patcher/base/base.h @@ -30,11 +30,54 @@ BEGIN_C_DECLS +#define MCA_BASE_PATCHER_MAX_PATCH 32 + +struct mca_patcher_base_patch_t; + +typedef void (*mca_patcher_base_restore_fn_t) (struct mca_patcher_base_patch_t *); + +struct mca_patcher_base_patch_t { + /** patches are list items */ + opal_list_item_t super; + /** name symbol to patch */ + char *patch_symbol; + /** address of function to call instead */ + uintptr_t patch_value; + /** original address of function */ + uintptr_t patch_orig; + /** patch data */ + unsigned char patch_data[MCA_BASE_PATCHER_MAX_PATCH]; + /** original data */ + unsigned char patch_orig_data[MCA_BASE_PATCHER_MAX_PATCH]; + /** size of patch data */ + unsigned patch_data_size; + /** function to undo the patch */ + mca_patcher_base_restore_fn_t patch_restore; +}; + +typedef struct mca_patcher_base_patch_t mca_patcher_base_patch_t; + +OBJ_CLASS_DECLARATION(mca_patcher_base_patch_t); + /** * Framework struct declaration for this framework */ OPAL_DECLSPEC extern mca_base_framework_t opal_patcher_base_framework; OPAL_DECLSPEC int opal_patcher_base_select (void); +OPAL_DECLSPEC int mca_patcher_base_patch_hook (mca_patcher_base_module_t *module, uintptr_t hook); +OPAL_DECLSPEC void mca_base_patcher_patch_apply_binary (mca_patcher_base_patch_t *patch); + +static inline uintptr_t mca_patcher_base_addr_text (uintptr_t addr) { +#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && _CALL_ELF != 2 + struct odp_t { + uintptr_t text; + uintptr_t toc; + } *odp = (struct odp_t *) addr; + return (odp)?odp->text:0; +#else + return addr; +#endif +} END_C_DECLS #endif /* OPAL_BASE_PATCHER_H */ diff --git a/opal/mca/patcher/base/patcher_base_frame.c b/opal/mca/patcher/base/patcher_base_frame.c index b65371f1393..5091011e9ae 100644 --- a/opal/mca/patcher/base/patcher_base_frame.c +++ b/opal/mca/patcher/base/patcher_base_frame.c @@ -39,6 +39,9 @@ int opal_patcher_base_select (void) return rc; } + OBJ_CONSTRUCT(&best_module->patch_list, opal_list_t); + OBJ_CONSTRUCT(&best_module->patch_list_mutex, opal_mutex_t); + if (best_module->patch_init) { rc = best_module->patch_init (); if (OPAL_SUCCESS != rc) { @@ -53,6 +56,14 @@ int opal_patcher_base_select (void) static int opal_patcher_base_close (void) { + mca_patcher_base_patch_t *patch; + OPAL_LIST_FOREACH_REV(patch, &opal_patcher->patch_list, mca_patcher_base_patch_t) { + patch->patch_restore (patch); + } + + OPAL_LIST_DESTRUCT(&opal_patcher->patch_list); + OBJ_DESTRUCT(&opal_patcher->patch_list_mutex); + if (opal_patcher->patch_fini) { return opal_patcher->patch_fini (); } diff --git a/opal/mca/patcher/base/patcher_base_patch.c b/opal/mca/patcher/base/patcher_base_patch.c new file mode 100644 index 00000000000..94a0c12e387 --- /dev/null +++ b/opal/mca/patcher/base/patcher_base_patch.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2016 Los Alamos National Security, LLC. All rights + * reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" + +#include "opal/mca/patcher/patcher.h" +#include "opal/mca/patcher/base/base.h" +#include "opal/util/sys_limits.h" +#include "opal/prefetch.h" +#include + +static void mca_patcher_base_patch_construct (mca_patcher_base_patch_t *patch) +{ + patch->patch_symbol = NULL; + patch->patch_data_size = 0; +} + +static void mca_patcher_base_patch_destruct (mca_patcher_base_patch_t *patch) +{ + free (patch->patch_symbol); +} + +OBJ_CLASS_INSTANCE(mca_patcher_base_patch_t, opal_list_item_t, + mca_patcher_base_patch_construct, + mca_patcher_base_patch_destruct); + +#if defined(__PPC__) + +// PowerPC instructions used in patching +// Reference: "PowerPC User Instruction Set Architecture" +static unsigned int addis(unsigned int RT, unsigned int RS, unsigned int UI) { + return (15<<26) + (RT<<21) + (RS<<16) + (UI&0xffff); +} +static unsigned int ori(unsigned int RT, unsigned int RS, unsigned int UI) { + return (24<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); +} +static unsigned int oris(unsigned int RT, unsigned int RS, unsigned int UI) { + return (25<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); +} +static unsigned int mtspr(unsigned int SPR, unsigned int RS) { + return (31<<26) + (RS<<21) + ((SPR&0x1f)<<16) + ((SPR>>5)<<11) + (467<<1); +} +static unsigned int bcctr(unsigned int BO, unsigned int BI, unsigned int BH) { + return (19<<26) + (BO<<21) + (BI<<16) + (BH<<11) + (528<<1); +} +static unsigned int rldicr(unsigned int RT, unsigned int RS, unsigned int SH, unsigned int MB) +{ + return (30<<26) + (RS<<21) + (RT<<16) + ((SH&0x1f)<<11) + ((SH>>5)<<1) + + ((MB&0x1f)<<6) + ((MB>>5)<<5) + (1<<2); +} + +static int PatchLoadImm (uintptr_t addr, unsigned int reg, size_t value) +{ +#if defined(__PPC64__) + *(unsigned int *) (addr + 0) = addis ( reg, 0, (value >> 48)); + *(unsigned int *) (addr + 4) = ori ( reg, reg, (value >> 32)); + *(unsigned int *) (addr + 8) = rldicr( reg, reg, 32, 31); + *(unsigned int *) (addr +12) = oris ( reg, reg, (value >> 16)); + *(unsigned int *) (addr +16) = ori ( reg, reg, (value >> 0)); + return 20; +#else + *(unsigned int *) (addr + 0) = addis ( reg, 0, (value >> 16)); + *(unsigned int *) (addr + 4) = ori ( reg, reg, (value >> 0)); + return 8; +#endif +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) + +static void flush_and_invalidate_cache (unsigned long a) +{ +#if defined(__i386__) + /* does not work with AMD processors */ + __asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a)); +#elif defined(__x86_64__) + __asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a)); +#elif defined(__ia64__) + __asm__ volatile ("fc %0;; sync.i;; srlz.i;;" : : "r"(a) : "memory"); +#endif +} +#endif + +// modify protection of memory range +static void ModifyMemoryProtection (uintptr_t addr, size_t length, int prot) +{ + long page_size = opal_getpagesize (); + uintptr_t base = (addr & ~(page_size-1)); + uintptr_t bound = ((addr + length + page_size-1) & ~(page_size-1)); + + length = bound - base; + +#if defined(__PPC__) + /* NTH: is a loop necessary here? */ + do { + if (mprotect((void *)base, page_size, prot)) + perror("MemHook: mprotect failed"); + base += page_size; + } while (base < addr + length); +#else + if (mprotect((void *) base, length, prot)) { + perror("MemHook: mprotect failed"); + } +#endif +} + +static inline void apply_patch (unsigned char *patch_data, uintptr_t address, size_t data_size) +{ + ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ|PROT_WRITE); + memcpy ((void *) address, patch_data, data_size); +#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) + for (size_t i = 0 ; i < data_size ; i += 16) { + flush_and_invalidate_cache (address + i); + } +#endif + + ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ); +} + +static void mca_base_patcher_patch_unapply_binary (mca_patcher_base_patch_t *patch) +{ + apply_patch (patch->patch_orig_data, patch->patch_orig, patch->patch_data_size); +} + +void mca_base_patcher_patch_apply_binary (mca_patcher_base_patch_t *patch) +{ + memcpy (patch->patch_orig_data, (void *) patch->patch_orig, patch->patch_data_size); + apply_patch (patch->patch_data, patch->patch_orig, patch->patch_data_size); + patch->patch_restore = mca_base_patcher_patch_unapply_binary; +} + + +int mca_patcher_base_patch_hook (mca_patcher_base_module_t *module, uintptr_t hook_addr) +{ +#if defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__) + mca_patcher_base_patch_t *hook_patch; + const unsigned int nop = 0x60000000; + unsigned int *nop_addr; + + fprintf (stderr, "Patching hook @ 0x%lx\n", hook_addr); + + hook_patch = OBJ_NEW(mca_patcher_base_patch_t); + if (OPAL_UNLIKELY(NULL == hook_patch)) { + return OPAL_ERR_OUT_OF_RESOURCE; + } + + // locate reserved code space in hook function + for (nop_addr = (unsigned int *)hook_addr ; ; nop_addr++) { + if (nop_addr[0] == nop && nop_addr[1] == nop && nop_addr[2] == nop + && nop_addr[3] == nop && nop_addr[4] == nop) { + break; + } + } + // generate code to restore TOC + register unsigned long toc asm("r2"); + hook_patch->patch_orig = (uintptr_t) nop_addr; + hook_patch->patch_data_size = PatchLoadImm((uintptr_t)hook_patch->patch_data, 2, toc); + + /* put the hook patch on the patch list so it will be undone on finalize */ + opal_list_append (&module->patch_list, &hook_patch->super); + + mca_base_patcher_patch_apply_binary (hook_patch); +#endif + + return OPAL_SUCCESS; +} diff --git a/opal/mca/patcher/linux/patcher_linux.h b/opal/mca/patcher/linux/patcher_linux.h index 529380c9b7e..877902f812a 100644 --- a/opal/mca/patcher/linux/patcher_linux.h +++ b/opal/mca/patcher/linux/patcher_linux.h @@ -20,35 +20,25 @@ #include "opal/class/opal_list.h" #include "opal/threads/mutex.h" -struct mca_patcher_linux_patch_t { - /** patches are list items */ +struct mca_patcher_linux_patch_got_t { opal_list_item_t super; - /** name symbol to patch */ - char *symbol; - /** address of function to call instead */ - uintptr_t value; - /** original address of function */ - uintptr_t orig; + void **got_entry; + void *got_orig; }; -typedef struct mca_patcher_linux_patch_t mca_patcher_linux_patch_t; - -OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_t); +typedef struct mca_patcher_linux_patch_got_t mca_patcher_linux_patch_got_t; -struct mca_patcher_linux_module_t { - mca_patcher_base_module_t super; +OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_got_t); - opal_list_t patch_list; - opal_mutex_t patch_list_mutex; +struct mca_patcher_linux_patch_t { + mca_patcher_base_patch_t super; + opal_list_t patch_got_list; }; -typedef struct mca_patcher_linux_module_t mca_patcher_linux_module_t; +typedef struct mca_patcher_linux_patch_t mca_patcher_linux_patch_t; -extern mca_patcher_linux_module_t mca_patcher_linux_module; +OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_t); -int mca_patcher_linux_install_dlopen (void); -int mca_patcher_linux_remove_dlopen (void); -int mca_patcher_linux_patch_symbol (const char *symbol_name, uintptr_t replacement, uintptr_t *orig); -int mca_patcher_linux_remove_patch (mca_patcher_linux_patch_t *patch); +extern mca_patcher_base_module_t mca_patcher_linux_module; #endif /* !defined(OPAL_PATCHER_LINUX_H) */ diff --git a/opal/mca/patcher/linux/patcher_linux_component.c b/opal/mca/patcher/linux/patcher_linux_component.c index 4030888b439..dc71a0a7f9a 100644 --- a/opal/mca/patcher/linux/patcher_linux_component.c +++ b/opal/mca/patcher/linux/patcher_linux_component.c @@ -13,7 +13,7 @@ static int mca_patcher_linux_query (mca_base_module_t **module, int *priority) { - *module = &mca_patcher_linux_module.super.super; + *module = &mca_patcher_linux_module.super; *priority = 37; return OPAL_SUCCESS; } diff --git a/opal/mca/patcher/linux/patcher_linux_module.c b/opal/mca/patcher/linux/patcher_linux_module.c index f6ca2852076..cc8c4a3fb65 100644 --- a/opal/mca/patcher/linux/patcher_linux_module.c +++ b/opal/mca/patcher/linux/patcher_linux_module.c @@ -15,6 +15,8 @@ #include "patcher_linux.h" +#include "opal/mca/patcher/base/base.h" + #include "opal/constants.h" #include "opal/util/sys_limits.h" #include "opal/util/output.h" @@ -54,15 +56,29 @@ typedef struct mca_patcher_linux_elf_symtab { typedef struct mca_patcher_linux_dl_iter_context { mca_patcher_linux_patch_t *patch; + bool remove; int status; } mca_patcher_linux_dl_iter_context_t; -/* List of patches to be applied to additional libraries */ -static bool mca_patcher_linux_dlopen_installed; +OBJ_CLASS_INSTANCE(mca_patcher_linux_patch_got_t, opal_list_item_t, NULL, NULL); + +static void mca_patcher_linux_patch_construct (mca_patcher_linux_patch_t *patch) +{ + OBJ_CONSTRUCT(&patch->patch_got_list, opal_list_t); +} -OBJ_CLASS_INSTANCE(mca_patcher_linux_patch_t, opal_list_item_t, NULL, NULL); +static void mca_patcher_linux_patch_destruct (mca_patcher_linux_patch_t *patch) +{ + OPAL_LIST_DESTRUCT(&patch->patch_got_list); +} +OBJ_CLASS_INSTANCE(mca_patcher_linux_patch_t, mca_patcher_base_patch_t, mca_patcher_linux_patch_construct, + mca_patcher_linux_patch_destruct); + +/* List of patches to be applied to additional libraries */ +static bool mca_patcher_linux_dlopen_installed; static mca_patcher_linux_patch_t *mca_patcher_linux_dlopen_patch; +static void *(*orig_dlopen) (const char *, int); static const ElfW(Phdr) * mca_patcher_linux_get_phdr_dynamic(const ElfW(Phdr) *phdr, uint16_t phnum, int phent) @@ -218,7 +234,7 @@ mca_patcher_linux_modify_got (ElfW(Addr) base, const ElfW(Phdr) *phdr, const cha mca_patcher_linux_get_symtab (base, dphdr, &symtab); mca_patcher_linux_get_strtab (base, dphdr, &strtab); - entry = mca_patcher_linux_get_got_entry (base, ctx->patch->symbol, &jmprel, &symtab, &strtab); + entry = mca_patcher_linux_get_got_entry (base, ctx->patch->super.patch_symbol, &jmprel, &symtab, &strtab); if (entry == NULL) { return OPAL_SUCCESS; } @@ -231,7 +247,39 @@ mca_patcher_linux_modify_got (ElfW(Addr) base, const ElfW(Phdr) *phdr, const cha return OPAL_ERR_NOT_SUPPORTED; } - *entry = (void *) ctx->patch->value; + if (!ctx->remove) { + if (*entry != (void *) ctx->patch->super.patch_value) { + mca_patcher_linux_patch_got_t *patch_got = OBJ_NEW(mca_patcher_linux_patch_got_t); + if (NULL == patch_got) { + return OPAL_ERR_OUT_OF_RESOURCE; + } + + opal_output_verbose (MCA_BASE_VERBOSE_TRACE, opal_patcher_base_framework.framework_output, + "modifying got entry %p with original value %p\n", (void *) entry, *entry); + + patch_got->got_entry = entry; + patch_got->got_orig = *entry; + + opal_list_append (&ctx->patch->patch_got_list, &patch_got->super); + + *entry = (void *) ctx->patch->super.patch_value; + } + } else { + if (*entry == (void *) ctx->patch->super.patch_value) { + /* find the appropriate entry and restore the original value */ + mca_patcher_linux_patch_got_t *patch_got; + OPAL_LIST_FOREACH(patch_got, &ctx->patch->patch_got_list, mca_patcher_linux_patch_got_t) { + if (patch_got->got_entry == entry) { + opal_output_verbose (MCA_BASE_VERBOSE_TRACE, opal_patcher_base_framework.framework_output, + "restoring got entry %p with original value %p\n", (void *) entry, patch_got->got_orig); + *entry = patch_got->got_orig; + opal_list_remove_item (&ctx->patch->patch_got_list, &patch_got->super); + OBJ_RELEASE(patch_got); + break; + } + } + } + } return OPAL_SUCCESS; } @@ -264,6 +312,27 @@ static int mca_patcher_linux_apply_patch (mca_patcher_linux_patch_t *patch) { mca_patcher_linux_dl_iter_context_t ctx = { .patch = patch, + .remove = false, + .status = OPAL_SUCCESS, + }; + + /* Avoid locks here because we don't modify ELF data structures. + * Worst case the same symbol will be written more than once. + */ + (void)dl_iterate_phdr(mca_patcher_linux_phdr_iterator, &ctx); + if (ctx.status == OPAL_SUCCESS) { + opal_output_verbose (MCA_BASE_VERBOSE_INFO, opal_patcher_base_framework.framework_output, + "modified '%s' to 0x%lx", ctx.patch->super.patch_symbol, ctx.patch->super.patch_value); + } + + return ctx.status; +} + +static int mca_patcher_linux_remove_patch (mca_patcher_linux_patch_t *patch) +{ + mca_patcher_linux_dl_iter_context_t ctx = { + .patch = patch, + .remove = true, .status = OPAL_SUCCESS, }; @@ -273,7 +342,7 @@ static int mca_patcher_linux_apply_patch (mca_patcher_linux_patch_t *patch) (void)dl_iterate_phdr(mca_patcher_linux_phdr_iterator, &ctx); if (ctx.status == OPAL_SUCCESS) { opal_output_verbose (MCA_BASE_VERBOSE_INFO, opal_patcher_base_framework.framework_output, - "modified '%s' to 0x%lx", ctx.patch->symbol, ctx.patch->value); + "modified '%s' to 0x%lx", ctx.patch->super.patch_symbol, ctx.patch->super.patch_value); } return ctx.status; @@ -281,8 +350,7 @@ static int mca_patcher_linux_apply_patch (mca_patcher_linux_patch_t *patch) static void *mca_patcher_linux_dlopen(const char *filename, int flag) { - void *(*orig_dlopen) (const char *, int) = - (void *(*) (const char *, int)) mca_patcher_linux_dlopen_patch->orig; + OPAL_PATCHER_BEGIN; mca_patcher_linux_patch_t *patch; void *handle; @@ -298,16 +366,20 @@ static void *mca_patcher_linux_dlopen(const char *filename, int flag) opal_mutex_lock (&mca_patcher_linux_module.patch_list_mutex); OPAL_LIST_FOREACH(patch, &mca_patcher_linux_module.patch_list, mca_patcher_linux_patch_t) { opal_output_verbose (MCA_BASE_VERBOSE_INFO, opal_patcher_base_framework.framework_output, - "in dlopen(), re-applying '%s' to %p", patch->symbol, (void *) patch->value); - mca_patcher_linux_apply_patch (patch); + "in dlopen(), re-applying '%s' to %p", patch->super.patch_symbol, (void *) patch->super.patch_value); + /* ignore hook binary patches */ + if (!patch->super.patch_data_size) { + mca_patcher_linux_apply_patch (patch); + } } opal_mutex_unlock (&mca_patcher_linux_module.patch_list_mutex); } + OPAL_PATCHER_END; return handle; } -static void *mca_patcher_linux_get_orig (const char *symbol, void *replacement) +static intptr_t mca_patcher_linux_get_orig (const char *symbol, void *replacement) { const char *error; void *func_ptr; @@ -325,11 +397,12 @@ static void *mca_patcher_linux_get_orig (const char *symbol, void *replacement) opal_output_verbose (MCA_BASE_VERBOSE_INFO, opal_patcher_base_framework.framework_output, "original %s() is at %p", symbol, func_ptr); - return func_ptr; + + return (intptr_t) func_ptr; } /* called with lock held */ -int mca_patcher_linux_install_dlopen (void) +static int mca_patcher_linux_install_dlopen (void) { int rc; @@ -337,9 +410,13 @@ int mca_patcher_linux_install_dlopen (void) return OPAL_SUCCESS; } - mca_patcher_linux_dlopen_patch->orig = - (uintptr_t) mca_patcher_linux_get_orig (mca_patcher_linux_dlopen_patch->symbol, - (void *) mca_patcher_linux_dlopen_patch->value); + orig_dlopen = (void *(*) (const char *, int)) mca_patcher_linux_get_orig (mca_patcher_linux_dlopen_patch->super.patch_symbol, + (void *) mca_patcher_linux_dlopen_patch->super.patch_value); + + rc = mca_patcher_base_patch_hook (&mca_patcher_linux_module, mca_patcher_linux_dlopen_patch->super.patch_value); + if (OPAL_SUCCESS != rc) { + return rc; + } rc = mca_patcher_linux_apply_patch (mca_patcher_linux_dlopen_patch); if (OPAL_SUCCESS != rc) { @@ -351,13 +428,13 @@ int mca_patcher_linux_install_dlopen (void) return OPAL_SUCCESS; } -int mca_patcher_linux_remove_dlopen (void) +static int mca_patcher_linux_remove_dlopen (void) { mca_patcher_linux_dlopen_installed = false; return mca_patcher_linux_remove_patch (mca_patcher_linux_dlopen_patch); } -int mca_patcher_linux_patch_symbol (const char *symbol_name, uintptr_t replacement, uintptr_t *orig) +static int mca_patcher_linux_patch_symbol (const char *symbol_name, uintptr_t replacement, uintptr_t *orig) { mca_patcher_linux_patch_t *patch = OBJ_NEW(mca_patcher_linux_patch_t); int rc; @@ -366,63 +443,58 @@ int mca_patcher_linux_patch_symbol (const char *symbol_name, uintptr_t replaceme return OPAL_ERR_OUT_OF_RESOURCE; } - patch->symbol = strdup (symbol_name); - if (NULL == patch->symbol) { + patch->super.patch_symbol = strdup (symbol_name); + if (NULL == patch->super.patch_symbol) { OBJ_RELEASE(patch); return OPAL_ERR_OUT_OF_RESOURCE; } - patch->value = replacement; + patch->super.patch_value = mca_patcher_base_addr_text (replacement); + patch->super.patch_restore = (mca_patcher_base_restore_fn_t) mca_patcher_linux_remove_patch; /* Take lock first to handle a possible race where dlopen() is called * from another thread and we may end up not patching it. */ opal_mutex_lock (&mca_patcher_linux_module.patch_list_mutex); do { - rc = mca_patcher_linux_install_dlopen (); + rc = mca_patcher_base_patch_hook (&mca_patcher_linux_module, patch->super.patch_value); if (OPAL_SUCCESS != rc) { + OBJ_RELEASE(patch); break; } - *orig = patch->orig = (uintptr_t) mca_patcher_linux_get_orig (symbol_name, (void *) replacement); - rc = mca_patcher_linux_apply_patch (patch); if (OPAL_SUCCESS != rc) { + OBJ_RELEASE(patch); break; } - opal_list_append (&mca_patcher_linux_module.patch_list, &patch->super); + *orig = mca_patcher_linux_get_orig (patch->super.patch_symbol, (void *) replacement); + + opal_list_append (&mca_patcher_linux_module.patch_list, &patch->super.super); } while (0); opal_mutex_unlock (&mca_patcher_linux_module.patch_list_mutex); return rc; } -int mca_patcher_linux_remove_patch (mca_patcher_linux_patch_t *patch) -{ - patch->value = patch->orig; - return mca_patcher_linux_apply_patch (patch); -} - static int mca_patcher_linux_init (void) { int rc; - OBJ_CONSTRUCT(&mca_patcher_linux_module.patch_list, opal_list_t); - OBJ_CONSTRUCT(&mca_patcher_linux_module.patch_list_mutex, opal_mutex_t); - mca_patcher_linux_dlopen_patch = OBJ_NEW(mca_patcher_linux_patch_t); if (NULL == mca_patcher_linux_dlopen_patch) { return OPAL_ERR_OUT_OF_RESOURCE; } - mca_patcher_linux_dlopen_patch->symbol = strdup ("dlopen"); - if (NULL == mca_patcher_linux_dlopen_patch->symbol) { + mca_patcher_linux_dlopen_patch->super.patch_symbol = strdup ("dlopen"); + if (NULL == mca_patcher_linux_dlopen_patch->super.patch_symbol) { OBJ_RELEASE(mca_patcher_linux_dlopen_patch); return OPAL_ERR_OUT_OF_RESOURCE; } - mca_patcher_linux_dlopen_patch->value = (intptr_t) mca_patcher_linux_dlopen; + mca_patcher_linux_dlopen_patch->super.patch_value = mca_patcher_base_addr_text ((intptr_t) mca_patcher_linux_dlopen); + mca_patcher_linux_dlopen_patch->super.patch_restore = (mca_patcher_base_restore_fn_t) mca_patcher_linux_remove_patch; rc = mca_patcher_linux_install_dlopen (); if (OPAL_SUCCESS != rc) { @@ -434,31 +506,18 @@ static int mca_patcher_linux_init (void) static int mca_patcher_linux_fini (void) { - opal_list_t *patch_list = &mca_patcher_linux_module.patch_list; - mca_patcher_linux_patch_t *patch; - int rc; - - /* restore the originals in reverse order */ - OPAL_LIST_FOREACH_REV(patch, patch_list, mca_patcher_linux_patch_t) { - (void) mca_patcher_linux_remove_patch (patch); - } - - rc = mca_patcher_linux_remove_dlopen (); + int rc = OPAL_SUCCESS; if (mca_patcher_linux_dlopen_patch) { + rc = mca_patcher_linux_remove_dlopen (); OBJ_RELEASE(mca_patcher_linux_dlopen_patch); } - OPAL_LIST_DESTRUCT(&mca_patcher_linux_module.patch_list); - OBJ_DESTRUCT(&mca_patcher_linux_module.patch_list_mutex); - return rc; } -mca_patcher_linux_module_t mca_patcher_linux_module = { - .super = { - .patch_init = mca_patcher_linux_init, - .patch_fini = mca_patcher_linux_fini, - .patch_symbol = mca_patcher_linux_patch_symbol, - }, +mca_patcher_base_module_t mca_patcher_linux_module = { + .patch_init = mca_patcher_linux_init, + .patch_fini = mca_patcher_linux_fini, + .patch_symbol = mca_patcher_linux_patch_symbol, }; diff --git a/opal/mca/patcher/overwrite/patcher_overwrite.h b/opal/mca/patcher/overwrite/patcher_overwrite.h index 1af29c21bc1..5c112d9198f 100644 --- a/opal/mca/patcher/overwrite/patcher_overwrite.h +++ b/opal/mca/patcher/overwrite/patcher_overwrite.h @@ -26,53 +26,6 @@ #include "opal/mca/patcher/patcher.h" #include "opal/class/opal_list.h" -/** - * maximum size of a binary patch - */ -#if defined(__i386__) -#define MCA_PATCHER_OVERWRITE_MAX_PATCH 5 -#elif defined(__x86_64__) -#define MCA_PATCHER_OVERWRITE_MAX_PATCH 13 -#elif defined(__ia64__) -#define MCA_PATCHER_OVERWRITE_MAX_PATCH 32 -#elif defined(__PPC__) -#define MCA_PATCHER_OVERWRITE_MAX_PATCH 32 -#endif - -/** - * overwrite patcher module - */ -struct mca_patcher_overwrite_module_t { - mca_patcher_base_module_t super; - /** list of active patches */ - opal_list_t patch_list; - /** mutex protecting patch_list */ - opal_mutex_t patch_list_mutex; -}; - -typedef struct mca_patcher_overwrite_module_t mca_patcher_overwrite_module_t; - -/** - * structure describing a binary patch - */ -struct mca_patcher_overwrite_patch_t { - opal_list_item_t super; - /** address of patched function */ - intptr_t orig_addr; - /** address of hook */ - intptr_t patch_addr; - /** unpatched instructions from orig_addr */ - unsigned char orig_data[MCA_PATCHER_OVERWRITE_MAX_PATCH]; - /** patched instructions */ - unsigned char patched_data[MCA_PATCHER_OVERWRITE_MAX_PATCH]; - /** size of orig_data and patched_data */ - unsigned data_size; -}; - -OBJ_CLASS_DECLARATION(mca_patcher_overwrite_patch_t); - -typedef struct mca_patcher_overwrite_patch_t mca_patcher_overwrite_patch_t; - -extern mca_patcher_overwrite_module_t mca_patcher_overwrite_module; +extern mca_patcher_base_module_t mca_patcher_overwrite_module; #endif /* !defined(OPAL_PATCHER_OVERWRITE_H) */ diff --git a/opal/mca/patcher/overwrite/patcher_overwrite_component.c b/opal/mca/patcher/overwrite/patcher_overwrite_component.c index 08cd024f91c..e20bd130c70 100644 --- a/opal/mca/patcher/overwrite/patcher_overwrite_component.c +++ b/opal/mca/patcher/overwrite/patcher_overwrite_component.c @@ -13,11 +13,9 @@ #include "opal/mca/mca.h" #include "opal/mca/base/base.h" -OBJ_CLASS_INSTANCE(mca_patcher_overwrite_patch_t, opal_list_item_t, NULL, NULL); - static int mca_patcher_overwrite_query (mca_base_module_t **module, int *priority) { - *module = &mca_patcher_overwrite_module.super.super; + *module = &mca_patcher_overwrite_module.super; *priority = 10; return OPAL_SUCCESS; } diff --git a/opal/mca/patcher/overwrite/patcher_overwrite_module.c b/opal/mca/patcher/overwrite/patcher_overwrite_module.c index 8b7b8db029d..79cb6ce5c72 100644 --- a/opal/mca/patcher/overwrite/patcher_overwrite_module.c +++ b/opal/mca/patcher/overwrite/patcher_overwrite_module.c @@ -12,6 +12,8 @@ #include "patcher_overwrite.h" +#include "opal/mca/patcher/base/base.h" + #include "opal/constants.h" #include "opal/util/sys_limits.h" #include "opal/util/output.h" @@ -26,60 +28,8 @@ #include #include -// modify protection of memory range -static void ModifyMemoryProtection (uintptr_t addr, size_t length, int prot) -{ - long page_size = opal_getpagesize (); - uintptr_t base = (addr & ~(page_size-1)); - uintptr_t bound = ((addr + length + page_size-1) & ~(page_size-1)); - - length = bound - base; - -#if defined(__PPC__) - /* NTH: is a loop necessary here? */ - do { - if (mprotect((void *)base, page_size, prot)) - perror("MemHook: mprotect failed"); - base += page_size; - } while (base < addr + length); -#else - if (mprotect((void *) base, length, prot)) { - perror("MemHook: mprotect failed"); - } -#endif -} -#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) - -static void flush_and_invalidate_cache (unsigned long a); -#endif - -static void apply_patch (unsigned char *patch_data, uintptr_t address, size_t data_size) -{ - ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ|PROT_WRITE); - memcpy ((void *) address, patch_data, data_size); -#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) - for (size_t i = 0 ; i < data_size ; i += 16) { - flush_and_invalidate_cache (address + i); - } -#endif - - ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ); -} - #if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) -static void flush_and_invalidate_cache (unsigned long a) -{ -#if defined(__i386__) - /* does not work with AMD processors */ - __asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a)); -#elif defined(__x86_64__) - __asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a)); -#elif defined(__ia64__) - __asm__ volatile ("fc %0;; sync.i;; srlz.i;;" : : "r"(a) : "memory"); -#endif -} - #if defined(__ia64__) #define INSERT_BIT(d,p,v) do { \ @@ -132,25 +82,22 @@ static void make_ia64_bundle (unsigned char *dst, } #endif /* defined(__ia64__) */ -static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *patch) +static int mca_patcher_overwrite_apply_patch (mca_patcher_base_patch_t *patch) { - uintptr_t func_old_addr = patch->orig_addr; - uintptr_t func_new_addr = patch->patch_addr; + uintptr_t func_new_addr = patch->patch_value; { #if defined(__i386__) - memcpy (patch->orig_data, func_old_addr, 5); - patch->data_size = 5; - *(unsigned char *)(patch->patched_data+0) = 0xe9; - *(unsigned int *) (patch->patched_data+1) = (unsigned int)(func_new_addr - func_old_addr - 5); + patch->patch_data_size = 5; + *(unsigned char *)(patch->patch_data+0) = 0xe9; + *(unsigned int *) (patch->patch_data+1) = (unsigned int)(func_new_addr - func_old_addr - 5); #elif defined(__x86_64__) - memcpy (patch->orig_data, (void *) func_old_addr, 13); - patch->data_size = 13; - *(unsigned short*)(patch->patched_data + 0) = 0xbb49; - *(unsigned long* )(patch->patched_data + 2) = (unsigned long) func_new_addr; - *(unsigned char*) (patch->patched_data +10) = 0x41; - *(unsigned char*) (patch->patched_data +11) = 0xff; - *(unsigned char*) (patch->patched_data +12) = 0xe3; + patch->patch_data_size = 13; + *(unsigned short*)(patch->patch_data + 0) = 0xbb49; + *(unsigned long* )(patch->patch_data + 2) = (unsigned long) func_new_addr; + *(unsigned char*) (patch->patch_data +10) = 0x41; + *(unsigned char*) (patch->patch_data +11) = 0xff; + *(unsigned char*) (patch->patch_data +12) = 0xe3; #elif defined(__ia64__) { /* @@ -186,23 +133,22 @@ static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *pat (1ULL << 6) | (0x0ULL << 0); - memcpy (patch->orig_data, func_old_addr, 32); patch->data_size = 32; make_ia64_bundle(buf, movl, (glb_ptr>>22)&0x1FFFFFFFFFFULL, nop, 5); for (int i = 0 ; i < 16 ; ++i) { - patch->patched_data[16-i-1] = buf[i]; + patch->patch_data[16-i-1] = buf[i]; } make_ia64_bundle(buf, brl, ((imm64>>24)&0x7FFFFFFFFFULL)<<2, nop, 5); for (int i = 0 ; i < 16 ; ++i) { - patch->patched_data[32-i-1] = buf[i]; + patch->patch_data[32-i-1] = buf[i]; } } #endif } - apply_patch (patch->patched_data, patch->orig_addr, patch->data_size); + mca_base_patcher_patch_apply_binary (patch); return OPAL_SUCCESS; } @@ -211,36 +157,24 @@ static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *pat // ------------------------------------------------- PPC equivalent: #elif defined(__PPC__) -static inline uintptr_t addr_text (uintptr_t addr) { -#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && _CALL_ELF != 2 - struct odp_t { - uintptr_t text; - uintptr_t toc; - } *odp = (struct odp_t *) addr; - return (odp)?odp->text:0; -#else - return addr; -#endif -} - // PowerPC instructions used in patching // Reference: "PowerPC User Instruction Set Architecture" -unsigned int addis(unsigned int RT, unsigned int RS, unsigned int UI) { +static unsigned int addis(unsigned int RT, unsigned int RS, unsigned int UI) { return (15<<26) + (RT<<21) + (RS<<16) + (UI&0xffff); } -unsigned int ori(unsigned int RT, unsigned int RS, unsigned int UI) { +static unsigned int ori(unsigned int RT, unsigned int RS, unsigned int UI) { return (24<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); } -unsigned int oris(unsigned int RT, unsigned int RS, unsigned int UI) { +static unsigned int oris(unsigned int RT, unsigned int RS, unsigned int UI) { return (25<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); } -unsigned int mtspr(unsigned int SPR, unsigned int RS) { +static unsigned int mtspr(unsigned int SPR, unsigned int RS) { return (31<<26) + (RS<<21) + ((SPR&0x1f)<<16) + ((SPR>>5)<<11) + (467<<1); } -unsigned int bcctr(unsigned int BO, unsigned int BI, unsigned int BH) { +static unsigned int bcctr(unsigned int BO, unsigned int BI, unsigned int BH) { return (19<<26) + (BO<<21) + (BI<<16) + (BH<<11) + (528<<1); } -unsigned int rldicr(unsigned int RT, unsigned int RS, unsigned int SH, unsigned int MB) +static unsigned int rldicr(unsigned int RT, unsigned int RS, unsigned int SH, unsigned int MB) { return (30<<26) + (RS<<21) + (RT<<16) + ((SH&0x1f)<<11) + ((SH>>5)<<1) + ((MB&0x1f)<<6) + ((MB>>5)<<5) + (1<<2); @@ -264,48 +198,22 @@ PatchLoadImm(uintptr_t addr, unsigned int reg, size_t value) } -static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *patch) +static int mca_patcher_overwrite_apply_patch (mca_patcher_base_patch_t *patch) { uintptr_t sys_addr, hook_addr; - - int offset; -#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) - unsigned int *nop_addr; - const unsigned int nop = 0x60000000; - mca_patcher_overwrite_patch_t *hook_patch; -#endif + int offset, rc; // get system function address - sys_addr = addr_text(patch->orig_addr); - hook_addr = addr_text(patch->patch_addr); + sys_addr = mca_patcher_base_addr_text(patch->patch_orig); + hook_addr = mca_patcher_base_addr_text(patch->patch_value); // Patch for hook function: #if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) - hook_patch = OBJ_NEW(mca_patcher_overwrite_patch_t); - if (OPAL_UNLIKELY(NULL == hook_patch)) { - return OPAL_ERR_OUT_OF_RESOURCE; + rc = mca_patcher_base_patch_hook (&mca_patcher_overwrite_module, hook_addr); + if (OPAL_SUCCESS != rc) { + return rc; } - // locate reserved code space in hook function - for (nop_addr = (unsigned int *)hook_addr ; ; nop_addr++) { - if (nop_addr[0] == nop && nop_addr[1] == nop && nop_addr[2] == nop - && nop_addr[3] == nop && nop_addr[4] == nop) { - break; - } - } - // generate code to restore TOC - register unsigned long toc asm("r2"); - hook_patch->orig_addr = (uintptr_t) nop_addr; - hook_patch->data_size = PatchLoadImm((uintptr_t)hook_patch->patched_data, 2, toc); - - // save the original code - memcpy (hook_patch->orig_data, nop_addr, hook_patch->data_size); - - /* put the hook patch on the patch list so it will be undone on finalize */ - opal_list_append (&mca_patcher_overwrite_module.patch_list, &hook_patch->super); - - apply_patch(hook_patch->patched_data, hook_patch->orig_addr, hook_patch->data_size); - #if _CALL_ELF == 2 sys_addr += 8; hook_addr += 8; @@ -316,16 +224,13 @@ static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *pat // generate patch code // r11 is a volatile register according to PowerPC EABI const unsigned int gr = 11; - offset = PatchLoadImm ((uintptr_t) patch->patched_data, gr, hook_addr); - *(unsigned int *) (patch->patched_data + offset + 0) = mtspr (9, gr); // 9 = CTR - *(unsigned int *) (patch->patched_data + offset + 4) = bcctr (20, 0, 0);// 20 = always - patch->data_size = offset + 8; - patch->orig_addr = sys_addr; + offset = PatchLoadImm ((uintptr_t) patch->patch_data, gr, hook_addr); + *(unsigned int *) (patch->patch_data + offset + 0) = mtspr (9, gr); // 9 = CTR + *(unsigned int *) (patch->patch_data + offset + 4) = bcctr (20, 0, 0);// 20 = always + patch->patch_data_size = offset + 8; + patch->patch_orig = sys_addr; - /* save the original code so it can be restored at finalize */ - memcpy (patch->orig_data, (void *) patch->orig_addr, patch->data_size); - - apply_patch(patch->patched_data, patch->orig_addr, patch->data_size); + mca_base_patcher_patch_apply_binary (patch); return OPAL_SUCCESS; } @@ -334,16 +239,16 @@ static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *pat static int mca_patcher_overwrite_patch_address (uintptr_t sys_addr, unsigned long hook_addr) { - mca_patcher_overwrite_patch_t *patch; + mca_patcher_base_patch_t *patch; int rc; - patch = OBJ_NEW(mca_patcher_overwrite_patch_t); + patch = OBJ_NEW(mca_patcher_base_patch_t); if (OPAL_UNLIKELY(NULL == patch)) { return OPAL_ERR_OUT_OF_RESOURCE; } - patch->orig_addr = sys_addr; - patch->patch_addr = hook_addr; + patch->patch_orig = sys_addr; + patch->patch_value = hook_addr; opal_mutex_lock (&mca_patcher_overwrite_module.patch_list_mutex); do { @@ -396,34 +301,7 @@ static int mca_patcher_overwrite_patch_symbol (const char *func_symbol_name, uin return mca_patcher_overwrite_patch_address (old_addr, func_new_addr); } -static int mca_patcher_overwrite_patch_init (void) -{ - OBJ_CONSTRUCT(&mca_patcher_overwrite_module.patch_list, opal_list_t); - OBJ_CONSTRUCT(&mca_patcher_overwrite_module.patch_list_mutex, opal_mutex_t); - - return OPAL_SUCCESS; -} - -static int mca_patcher_overwrite_patch_fini (void) -{ - opal_list_t *patch_list = &mca_patcher_overwrite_module.patch_list; - mca_patcher_overwrite_patch_t *patch; - - OPAL_LIST_FOREACH(patch, patch_list, mca_patcher_overwrite_patch_t) { - apply_patch (patch->orig_data, patch->orig_addr, patch->data_size); - } - - OPAL_LIST_DESTRUCT(patch_list); - OBJ_DESTRUCT(&mca_patcher_overwrite_module.patch_list_mutex); - - return OPAL_SUCCESS; -} - -mca_patcher_overwrite_module_t mca_patcher_overwrite_module = { - .super = { - .patch_symbol = mca_patcher_overwrite_patch_symbol, - .patch_address = mca_patcher_overwrite_patch_address, - .patch_init = mca_patcher_overwrite_patch_init, - .patch_fini = mca_patcher_overwrite_patch_fini, - }, +mca_patcher_base_module_t mca_patcher_overwrite_module = { + .patch_symbol = mca_patcher_overwrite_patch_symbol, + .patch_address = mca_patcher_overwrite_patch_address, }; diff --git a/opal/mca/patcher/patcher.h b/opal/mca/patcher/patcher.h index 145b1103805..25af9b1376a 100644 --- a/opal/mca/patcher/patcher.h +++ b/opal/mca/patcher/patcher.h @@ -16,8 +16,9 @@ #include "opal/mca/mca.h" #include "opal/mca/base/base.h" +#include "opal/class/opal_list.h" -/* Any function being patched in must use SYMBOLPATCH_BEGIN at the top, +/* Any function being patched in as a hook must use SYMBOLPATCH_BEGIN at the top, * and SYMBOLPATCH_END before it returns (this is just for PPC). */ #if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && defined(OPAL_GCC_INLINE_ASSEMBLY) @@ -80,14 +81,18 @@ typedef int (*mca_patcher_base_fini_fn_t) (void); */ typedef struct mca_patcher_base_module_t { mca_base_module_t super; + /** list of patches */ + opal_list_t patch_list; + /** lock for patch list */ + opal_mutex_t patch_list_mutex; /** function to call if the patcher module is used. can * be NULL. */ - mca_patcher_base_init_fn_t patch_init; + mca_patcher_base_init_fn_t patch_init; /** function to call when patcher is unloaded. this function * MUST clean up all active patches. can be NULL. */ - mca_patcher_base_fini_fn_t patch_fini; + mca_patcher_base_fini_fn_t patch_fini; /** hook a symbol. may be NULL */ - mca_patcher_base_patch_symbol_fn_t patch_symbol; + mca_patcher_base_patch_symbol_fn_t patch_symbol; /** hook a function pointer. may be NULL */ mca_patcher_base_patch_address_fn_t patch_address; } mca_patcher_base_module_t;