diff --git a/contrib/platform/lanl/cray_xe6/debug-common b/contrib/platform/lanl/cray_xe6/debug-common index 98f6a5f84e..6bb806362d 100644 --- a/contrib/platform/lanl/cray_xe6/debug-common +++ b/contrib/platform/lanl/cray_xe6/debug-common @@ -32,8 +32,5 @@ enable_mca_direct=pml-ob1 # enable development headers with_devel_headers=yes -# enable ptmalloc (enables lazy deregistration) -with_memory_manager=linux - # disable valgrind with_valgrind=no diff --git a/contrib/platform/lanl/cray_xe6/optimized-common b/contrib/platform/lanl/cray_xe6/optimized-common index a6169ae173..937bf6b598 100644 --- a/contrib/platform/lanl/cray_xe6/optimized-common +++ b/contrib/platform/lanl/cray_xe6/optimized-common @@ -38,8 +38,5 @@ enable_mca_direct=pml-ob1 # enable development headers with_devel_headers=yes -# enable ptmalloc (enables lazy deregistration) -with_memory_manager=linux - # disable valgrind with_valgrind=no diff --git a/opal/common_sym_whitelist.txt b/opal/common_sym_whitelist.txt index d3c525b54d..4f2ffe1042 100644 --- a/opal/common_sym_whitelist.txt +++ b/opal/common_sym_whitelist.txt @@ -4,3 +4,4 @@ opal_show_help_yyleng opal_show_help_yytext opal_util_keyval_yyleng opal_util_keyval_yytext +__curbrk diff --git a/opal/mca/memory/patcher/configure.m4 b/opal/mca/memory/patcher/configure.m4 index bb19dd9985..4e2949fd63 100644 --- a/opal/mca/memory/patcher/configure.m4 +++ b/opal/mca/memory/patcher/configure.m4 @@ -21,7 +21,7 @@ # # $HEADER$ # -AC_DEFUN([MCA_opal_memory_patcher_PRIORITY], [40]) +AC_DEFUN([MCA_opal_memory_patcher_PRIORITY], [41]) AC_DEFUN([MCA_opal_memory_patcher_COMPILE_MODE], [ AC_MSG_CHECKING([for MCA component $2:$3 compile mode]) diff --git a/opal/mca/memory/patcher/memory_patcher.h b/opal/mca/memory/patcher/memory_patcher.h index 0d51a34922..0650f30c15 100644 --- a/opal/mca/memory/patcher/memory_patcher.h +++ b/opal/mca/memory/patcher/memory_patcher.h @@ -16,7 +16,7 @@ #include "opal_config.h" #include "opal/mca/memory/memory.h" -#include "opal/util/opal_patcher.h" +#include "opal/mca/patcher/patcher.h" typedef struct opal_memory_patcher_component_t { opal_memory_base_component_2_0_0_t super; diff --git a/opal/mca/memory/patcher/memory_patcher_component.c b/opal/mca/memory/patcher/memory_patcher_component.c index 1baf85a6de..cacefc3232 100644 --- a/opal/mca/memory/patcher/memory_patcher_component.c +++ b/opal/mca/memory/patcher/memory_patcher_component.c @@ -28,6 +28,7 @@ #include "opal/util/output.h" #include "opal/util/show_help.h" #include "opal/mca/memory/base/empty.h" +#include "opal/mca/memory/base/base.h" #include "opal/memoryhooks/memory.h" #include @@ -95,39 +96,51 @@ void *__mmap (void *start, size_t length, int prot, int flags, int fd, off_t off #define memory_patcher_syscall syscall #endif +static void *(*original_mmap)(void *, size_t, int, int, int, off_t); + static void *intercept_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) { OPAL_PATCHER_BEGIN; void *result = 0; if (prot == PROT_NONE) { - opal_mem_hooks_release_hook (start, length, 0); + opal_mem_hooks_release_hook (start, length, true); } + if (!original_mmap) { #if OPAL_MEMORY_PATCHER_HAVE___MMAP - /* the darwin syscall returns an int not a long so call the underlying __mmap function */ - result = __mmap (start, length, prot, flags, fd, offset); + /* the darwin syscall returns an int not a long so call the underlying __mmap function */ + result = __mmap (start, length, prot, flags, fd, offset); #else - result = (void*)(intptr_t) memory_patcher_syscall(SYS_mmap, start, length, prot, flags, fd, offset); + result = (void*)(intptr_t) memory_patcher_syscall(SYS_mmap, start, length, prot, flags, fd, offset); #endif -// I thought we had some issue in the past with the above line for IA32, -// like maybe syscall() wouldn't handle that many arguments. But just now -// I used gcc -m32 and it worked on a recent system. But there's a possibility -// that older ia32 systems may need some other code to make the above syscall. + // I thought we had some issue in the past with the above line for IA32, + // like maybe syscall() wouldn't handle that many arguments. But just now + // I used gcc -m32 and it worked on a recent system. But there's a possibility + // that older ia32 systems may need some other code to make the above syscall. + } else { + result = original_mmap (start, length, prot, flags, fd, offset); + } OPAL_PATCHER_END; return result; } +static int (*original_munmap) (void *, size_t); + static int intercept_munmap(void *start, size_t length) { OPAL_PATCHER_BEGIN; int result = 0; - opal_mem_hooks_release_hook (start, length, 0); + opal_mem_hooks_release_hook (start, length, false); - result=memory_patcher_syscall(SYS_munmap, start, length); + if (!original_munmap) { + result = memory_patcher_syscall(SYS_munmap, start, length); + } else { + result = original_munmap (start, length); + } OPAL_PATCHER_END; return result; @@ -135,38 +148,42 @@ static int intercept_munmap(void *start, size_t length) #if defined (SYS_mremap) +static void *(*original_mremap) (void *, size_t, size_t, int, ...); + static void *intercept_mremap (void *start, size_t oldlen, size_t newlen, int flags, ...) { OPAL_PATCHER_BEGIN; void *result = 0; #ifdef MREMAP_FIXED va_list ap; - void *new_address; #endif + void *new_address = NULL; - opal_mem_hooks_release_hook (start, oldlen, 0); + opal_mem_hooks_release_hook (start, oldlen, false); #ifdef MREMAP_FIXED if (flags & MREMAP_FIXED) { va_start(ap, flags); new_address = va_arg(ap, void*); - result=(void *)(intptr_t) memory_patcher_syscall( - SYS_mremap, start, oldlen, newlen, flags, new_address); + va_end(ap); - } else { - result=(void*)memory_patcher_syscall( - SYS_mremap, start, oldlen, newlen, flags); } -#else - result=(void*)(intptr_t) memory_patcher_syscall(SYS_mremap, start, oldlen, newlen, flags); #endif + if (!original_mremap) { + result = (void *)(intptr_t) memory_patcher_syscall (SYS_mremap, start, oldlen, newlen, flags, new_address); + } else { + result = original_mremap (start, oldlen, newlen, flags, new_address); + } + OPAL_PATCHER_END; return result; } #endif +static int (*original_madvise) (void *, size_t, int); + static int intercept_madvise (void *start, size_t length, int advice) { OPAL_PATCHER_BEGIN; @@ -178,9 +195,14 @@ static int intercept_madvise (void *start, size_t length, int advice) #endif advice == POSIX_MADV_DONTNEED) { - opal_mem_hooks_release_hook (start, length, 0); + opal_mem_hooks_release_hook (start, length, false); + } + + if (!original_madvise) { + result = memory_patcher_syscall(SYS_madvise, start, length, advice); + } else { + result = original_madvise (start, length, advice); } - result = memory_patcher_syscall(SYS_madvise, start, length, advice); OPAL_PATCHER_END; return result; @@ -192,6 +214,8 @@ static int intercept_madvise (void *start, size_t length, int advice) void *__curbrk; /* in libc */ #endif +static int (*original_brk) (void *); + static int intercept_brk (void *addr) { OPAL_PATCHER_BEGIN; @@ -204,23 +228,32 @@ static int intercept_brk (void *addr) old_addr = sbrk (0); #endif - /* get the current_addr */ - new_addr = (void *) (intptr_t) memory_patcher_syscall(SYS_brk, addr); + if (!original_brk) { + /* get the current_addr */ + new_addr = (void *) (intptr_t) memory_patcher_syscall(SYS_brk, addr); #if OPAL_MEMORY_PATCHER_HAVE___CURBRK - /* - * Note: if we were using glibc brk/sbrk, their __curbrk would get - * updated, but since we're going straight to the syscall, we have - * to update __curbrk or else glibc won't see it. - */ - __curbrk = new_addr; + /* + * Note: if we were using glibc brk/sbrk, their __curbrk would get + * updated, but since we're going straight to the syscall, we have + * to update __curbrk or else glibc won't see it. + */ + __curbrk = new_addr; #endif + } else { + result = original_brk (addr); +#if OPAL_MEMORY_PATCHER_HAVE___CURBRK + new_addr = __curbrk; +#else + new_addr = sbrk (0); +#endif + } if (new_addr < addr) { errno = ENOMEM; result = -1; } else if (new_addr < old_addr) { - opal_mem_hooks_release_hook (new_addr, (intptr_t) old_addr - (intptr_t) new_addr, 0); + opal_mem_hooks_release_hook (new_addr, (intptr_t) old_addr - (intptr_t) new_addr, true); } OPAL_PATCHER_END; return result; @@ -241,7 +274,7 @@ static int patcher_register (void) static int patcher_query (int *priority) { - if (opal_patch_supported ()) { + if (opal_patcher->patch_symbol) { *priority = mca_memory_patcher_priority; } else { *priority = -1; @@ -263,30 +296,30 @@ static int patcher_open (void) /* set memory hooks support level */ opal_mem_hooks_set_support (OPAL_MEMORY_FREE_SUPPORT | OPAL_MEMORY_MUNMAP_SUPPORT); - rc = opal_patch_symbol ("mmap", (uintptr_t) intercept_mmap); + rc = opal_patcher->patch_symbol ("mmap", (uintptr_t) intercept_mmap, (uintptr_t *) &original_mmap); if (OPAL_SUCCESS != rc) { return rc; } - rc = opal_patch_symbol ("munmap", (uintptr_t)intercept_munmap); + rc = opal_patcher->patch_symbol ("munmap", (uintptr_t)intercept_munmap, (uintptr_t *) &original_munmap); if (OPAL_SUCCESS != rc) { return rc; } #if defined (SYS_mremap) - rc = opal_patch_symbol ("mremap",(uintptr_t)intercept_mremap); + rc = opal_patcher->patch_symbol ("mremap",(uintptr_t)intercept_mremap, (uintptr_t *) &original_mremap); if (OPAL_SUCCESS != rc) { return rc; } #endif - rc = opal_patch_symbol ("madvise", (uintptr_t)intercept_madvise); + rc = opal_patcher->patch_symbol ("madvise", (uintptr_t)intercept_madvise, (uintptr_t *) &original_madvise); if (OPAL_SUCCESS != rc) { return rc; } #if defined (SYS_brk) - rc = opal_patch_symbol ("brk", (uintptr_t)intercept_brk); + rc = opal_patcher->patch_symbol ("brk", (uintptr_t)intercept_brk, (uintptr_t *) &original_brk); #endif return rc; diff --git a/opal/mca/patcher/Makefile.am b/opal/mca/patcher/Makefile.am new file mode 100644 index 0000000000..664683bad8 --- /dev/null +++ b/opal/mca/patcher/Makefile.am @@ -0,0 +1,39 @@ +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2010 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +# main library setup +noinst_LTLIBRARIES = libmca_patcher.la +libmca_patcher_la_SOURCES = + +# local files +headers = patcher.h +libmca_patcher_la_SOURCES += $(headers) + +# Conditionally install the header files +if WANT_INSTALL_HEADERS +opaldir = $(opalincludedir)/$(subdir) +nobase_opal_HEADERS = $(headers) +endif + +include base/Makefile.am + +distclean-local: + rm -f base/static-components.h diff --git a/opal/mca/patcher/base/Makefile.am b/opal/mca/patcher/base/Makefile.am new file mode 100644 index 0000000000..441e1c645d --- /dev/null +++ b/opal/mca/patcher/base/Makefile.am @@ -0,0 +1,25 @@ +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2009 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +headers += base/base.h + +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 new file mode 100644 index 0000000000..65b48fc00c --- /dev/null +++ b/opal/mca/patcher/base/base.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana + * University Research and Technology + * Corporation. All rights reserved. + * Copyright (c) 2004-2006 The University of Tennessee and The University + * of Tennessee Research Foundation. All rights + * reserved. + * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, + * University of Stuttgart. All rights reserved. + * Copyright (c) 2004-2005 The Regents of the University of California. + * All rights reserved. + * Copyright (c) 2016 Los Alamos National Security, LLC. All rights + * reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + * + */ + +#ifndef OPAL_PATCHER_BASE_H +#define OPAL_PATCHER_BASE_H + +#include "opal_config.h" +#include "opal/mca/base/mca_base_framework.h" +#include "opal/mca/patcher/patcher.h" + + +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 new file mode 100644 index 0000000000..8d685d3fa4 --- /dev/null +++ b/opal/mca/patcher/base/patcher_base_frame.c @@ -0,0 +1,81 @@ +/* -*- 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/mca/patcher/base/static-components.h" + +/* + * Local variables + */ +static mca_patcher_base_module_t empty_module; + +/* + * Globals + */ +mca_patcher_base_module_t *opal_patcher = &empty_module; + +int opal_patcher_base_select (void) +{ + mca_patcher_base_module_t *best_module; + mca_patcher_base_component_t *best_component; + int rc, priority; + + rc = mca_base_select ("patcher", opal_patcher_base_framework.framework_output, + &opal_patcher_base_framework.framework_components, + (mca_base_module_t **) &best_module, (mca_base_component_t **) &best_component, + &priority); + if (OPAL_SUCCESS != rc) { + 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) { + return rc; + } + } + + opal_patcher = best_module; + + return OPAL_SUCCESS; +} + +static int opal_patcher_base_close (void) +{ + if (opal_patcher == &empty_module) { + return OPAL_SUCCESS; + } + + 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 (); + } + + return OPAL_SUCCESS; +} + +/* Use default register/open functions */ +MCA_BASE_FRAMEWORK_DECLARE(opal, patcher, "runtime code patching", NULL, NULL, + opal_patcher_base_close, mca_patcher_base_static_components, + 0); 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 0000000000..94a0c12e38 --- /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/Makefile.am b/opal/mca/patcher/linux/Makefile.am new file mode 100644 index 0000000000..a0facb5ce7 --- /dev/null +++ b/opal/mca/patcher/linux/Makefile.am @@ -0,0 +1,47 @@ +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2009-2010 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2015 Research Organization for Information Science +# and Technology (RIST). All rights reserved. +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +if MCA_BUILD_opal_patcher_linux_DSO +component_noinst = +component_install = mca_patcher_linux.la +else +component_noinst = libmca_patcher_linux.la +component_install = +endif + +linux_SOURCES = \ + patcher_linux.h \ + patcher_linux_module.c \ + patcher_linux_component.c + +mcacomponentdir = $(opallibdir) +mcacomponent_LTLIBRARIES = $(component_install) +mca_patcher_linux_la_SOURCES = $(linux_SOURCES) +nodist_mca_patcher_linux_la_SOURCES = $(linux_nodist_SOURCES) +mca_patcher_linux_la_LDFLAGS = -module -avoid-version + +noinst_LTLIBRARIES = $(component_noinst) +libmca_patcher_linux_la_SOURCES = $(linux_SOURCES) +nodist_libmca_patcher_linux_la_SOURCES = $(linux_nodist_SOURCES) +libmca_patcher_linux_la_LIBADD = $(patcher_linux_LIBS) +libmca_patcher_linux_la_LDFLAGS = -module -avoid-version diff --git a/opal/mca/patcher/linux/configure.m4 b/opal/mca/patcher/linux/configure.m4 new file mode 100644 index 0000000000..cd3c3d69f0 --- /dev/null +++ b/opal/mca/patcher/linux/configure.m4 @@ -0,0 +1,43 @@ +# -*- shell-script -*- +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2015 Research Organization for Information Science +# and Technology (RIST). All rights reserved. +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +# MCA_patcher_linux_CONFIG(action-if-can-compile, +# [action-if-cant-compile]) +# ------------------------------------------------ +AC_DEFUN([MCA_opal_patcher_linux_CONFIG],[ + AC_CONFIG_FILES([opal/mca/patcher/linux/Makefile]) + + OPAL_VAR_SCOPE_PUSH([opal_patcher_linux_CPPFLAGS_save]) + + opal_patcher_linux_happy=no + if test $OPAL_ENABLE_DLOPEN_SUPPORT = 1; then + OPAL_CHECK_PACKAGE([patcher_linux], [dlfcn.h], [dl], [dl_iterate_phdr], [], [], [], + [opal_patcher_linux_happy=yes],[]) + AC_CHECK_HEADERS([elf.h],[],[opal_patcher_linux_happy=no]) + AC_CHECK_HEADERS([sys/auxv.h]) + fi + + AS_IF([test $opal_patcher_linux_happy = yes], [$1], [$2]) + OPAL_VAR_SCOPE_POP +]) diff --git a/opal/mca/patcher/linux/patcher_linux.h b/opal/mca/patcher/linux/patcher_linux.h new file mode 100644 index 0000000000..de4e85b35d --- /dev/null +++ b/opal/mca/patcher/linux/patcher_linux.h @@ -0,0 +1,45 @@ +/* -*- 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$ + */ + +#if !defined(OPAL_PATCHER_LINUX_H) +#define OPAL_PATCHER_LINUX_H + +#include "opal_config.h" + +#include "opal/mca/patcher/base/base.h" +#include "opal/mca/patcher/patcher.h" + +#include "opal/class/opal_list.h" +#include "opal/threads/mutex.h" + +struct mca_patcher_linux_patch_got_t { + opal_list_item_t super; + void **got_entry; + void *got_orig; +}; + +typedef struct mca_patcher_linux_patch_got_t mca_patcher_linux_patch_got_t; + +OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_got_t); + +struct mca_patcher_linux_patch_t { + mca_patcher_base_patch_t super; + opal_list_t patch_got_list; +}; + +typedef struct mca_patcher_linux_patch_t mca_patcher_linux_patch_t; + +OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_t); + +extern mca_patcher_base_module_t mca_patcher_linux_module; +extern mca_patcher_base_component_t mca_patcher_linux_component; + +#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 new file mode 100644 index 0000000000..445ee23d6a --- /dev/null +++ b/opal/mca/patcher/linux/patcher_linux_component.c @@ -0,0 +1,43 @@ +/* -*- 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 "patcher_linux.h" + +static int mca_patcher_linux_priority; + +static int mca_patcher_linux_register (void) +{ + mca_patcher_linux_priority = 13; + mca_base_component_var_register (&mca_patcher_linux_component.patcherc_version, + "priority", "Priority of the linux binary patcher component", + MCA_BASE_VAR_TYPE_INT, NULL, 0, 0, OPAL_INFO_LVL_5, + MCA_BASE_VAR_SCOPE_CONSTANT, &mca_patcher_linux_priority); + + return OPAL_SUCCESS; +} + +static int mca_patcher_linux_query (mca_base_module_t **module, int *priority) +{ + *module = &mca_patcher_linux_module.super; + *priority = mca_patcher_linux_priority; + return OPAL_SUCCESS; +} + +mca_patcher_base_component_t mca_patcher_linux_component = { + .patcherc_version = { + OPAL_PATCHER_BASE_VERSION_1_0_0, + .mca_component_name = "linux", + MCA_BASE_MAKE_VERSION(component, OPAL_MAJOR_VERSION, OPAL_MINOR_VERSION, + OPAL_RELEASE_VERSION), + .mca_query_component = mca_patcher_linux_query, + .mca_register_component_params = mca_patcher_linux_register, + }, +}; diff --git a/opal/mca/patcher/linux/patcher_linux_module.c b/opal/mca/patcher/linux/patcher_linux_module.c new file mode 100644 index 0000000000..2d74c4c9f0 --- /dev/null +++ b/opal/mca/patcher/linux/patcher_linux_module.c @@ -0,0 +1,460 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (C) Mellanox Technologies Ltd. 2001-2015. ALL RIGHTS RESERVED. + * Copyright (c) 2016 Los Alamos National Security, LLC. All rights + * reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ +/* + * Copied from OpenUCX + */ + +#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" +#include "opal/prefetch.h" + +#if defined(HAVE_SYS_AUXV_H) +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static void *mca_patcher_linux_dlopen(const char *filename, int flag); + +typedef struct mca_patcher_linux_elf_strtab { + char *tab; + ElfW(Xword) size; +} mca_patcher_linux_elf_strtab_t; + +typedef struct mca_patcher_linux_elf_jmpreltab { + ElfW(Rela) *tab; + ElfW(Xword) size; +} mca_patcher_linux_elf_jmprel_t; + +typedef struct mca_patcher_linux_elf_symtab { + ElfW(Sym) *tab; + ElfW(Xword) entsz; +} mca_patcher_linux_elf_symtab_t; + +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; + +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); +} + +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 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) +{ + for (uint16_t i = 0; i < phnum; ++i) { + if (phdr->p_type == PT_DYNAMIC) { + return phdr; + } + phdr = (ElfW(Phdr)*)((char*)phdr + phent); + } + return NULL; +} + +static const ElfW(Dyn)* +mca_patcher_linux_get_dynentry(ElfW(Addr) base, const ElfW(Phdr) *pdyn, uint32_t type) +{ + for (ElfW(Dyn) *dyn = (ElfW(Dyn)*)(base + pdyn->p_vaddr); dyn->d_tag; ++dyn) { + if (dyn->d_tag == type) { + return dyn; + } + } + return NULL; +} + +static void mca_patcher_linux_get_jmprel(ElfW(Addr) base, const ElfW(Phdr) *pdyn, + mca_patcher_linux_elf_jmprel_t *table) +{ + const ElfW(Dyn) *dyn; + + dyn = mca_patcher_linux_get_dynentry(base, pdyn, DT_JMPREL); + table->tab = (dyn == NULL) ? NULL : (ElfW(Rela)*)dyn->d_un.d_ptr; + dyn = mca_patcher_linux_get_dynentry(base, pdyn, DT_PLTRELSZ); + table->size = (dyn == NULL) ? 0 : dyn->d_un.d_val; +} + +static void mca_patcher_linux_get_symtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn, + mca_patcher_linux_elf_symtab_t *table) +{ + const ElfW(Dyn) *dyn; + + dyn = mca_patcher_linux_get_dynentry(base, pdyn, DT_SYMTAB); + table->tab = (dyn == NULL) ? NULL : (ElfW(Sym)*)dyn->d_un.d_ptr; + dyn = mca_patcher_linux_get_dynentry(base, pdyn, DT_SYMENT); + table->entsz = (dyn == NULL) ? 0 : dyn->d_un.d_val; +} + +static void mca_patcher_linux_get_strtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn, + mca_patcher_linux_elf_strtab_t *table) +{ + const ElfW(Dyn) *dyn; + + dyn = mca_patcher_linux_get_dynentry(base, pdyn, DT_STRTAB); + table->tab = (dyn == NULL) ? NULL : (char *)dyn->d_un.d_ptr; + dyn = mca_patcher_linux_get_dynentry(base, pdyn, DT_STRSZ); + table->size = (dyn == NULL) ? 0 : dyn->d_un.d_val; +} + +static void * mca_patcher_linux_get_got_entry (ElfW(Addr) base, const ElfW(Phdr) *phdr, int16_t phnum, + int phent, const char *symbol) +{ + mca_patcher_linux_elf_jmprel_t jmprel; + mca_patcher_linux_elf_symtab_t symtab; + mca_patcher_linux_elf_strtab_t strtab; + ElfW(Rela) *rela, *relaend; + const ElfW(Phdr) *dphdr; + const char *relsymname; + uint32_t relsymidx; + + dphdr = mca_patcher_linux_get_phdr_dynamic (phdr, phnum, phent); + + mca_patcher_linux_get_jmprel (base, dphdr, &jmprel); + mca_patcher_linux_get_symtab (base, dphdr, &symtab); + mca_patcher_linux_get_strtab (base, dphdr, &strtab); + + relaend = (ElfW(Rela) *)((char *)jmprel.tab + jmprel.size); + for (rela = jmprel.tab; rela < relaend; ++rela) { +#if SIZEOF_VOID_P == 8 + relsymidx = ELF64_R_SYM(rela->r_info); +#else + relsymidx = ELF32_R_SYM(rela->r_info); +#endif + relsymname = strtab.tab + symtab.tab[relsymidx].st_name; + if (!strcmp(symbol, relsymname)) { + return (void *)(base + rela->r_offset); + } + } + return NULL; +} + +static int mca_patcher_linux_get_aux_phent (void) +{ +#if !defined(HAVE_SYS_AUXV_H) +#define MCA_PATCHER_LINUX_AUXV_BUF_LEN 16 + static const char *proc_auxv_filename = "/proc/self/auxv"; + static int phent = 0; +#if SIZEOF_VOID_P == 8 + Elf64_auxv_t buffer[MCA_PATCHER_LINUX_AUXV_BUF_LEN]; +#else + Elf32_auxv_t buffer[MCA_PATCHER_LINUX_AUXV_BUF_LEN]; +#endif + unsigned count; + ssize_t nread; + int fd; + + /* Can avoid lock here - worst case we'll read the file more than once */ + if (phent == 0) { + fd = open(proc_auxv_filename, O_RDONLY); + if (fd < 0) { + opal_output_verbose (MCA_BASE_VERBOSE_ERROR, opal_patcher_base_framework.framework_output, + "failed to open '%s' for reading: %s", proc_auxv_filename, + strerror (errno)); + return OPAL_ERROR; + } + + /* Use small buffer on the stack, avoid using malloc() */ + do { + nread = read(fd, buffer, sizeof(buffer)); + if (nread < 0) { + opal_output_verbose (MCA_BASE_VERBOSE_ERROR, opal_patcher_base_framework.framework_output, + "failed to read %lu bytes from %s (ret=%ld): %s", sizeof (buffer), + proc_auxv_filename, nread, strerror (errno)); + break; + } + + count = nread / sizeof(buffer[0]); + for (unsigned i = 0 ; i < count && AT_NULL != buffer[i].a_type ; ++i) { + if (AT_PHENT == buffer[i].a_type) { + phent = buffer[i].a_un.a_val; + opal_output_verbose (MCA_BASE_VERBOSE_ERROR, opal_patcher_base_framework.framework_output, + "read phent from %s: %d", proc_auxv_filename, phent); + break; + } + } + } while ((count > 0) && (phent == 0)); + + close(fd); + } + + return phent; +#else + return getauxval (AT_PHENT); +#endif +} + +static int +mca_patcher_linux_modify_got (ElfW(Addr) base, const ElfW(Phdr) *phdr, const char *phname, + int16_t phnum, int phent, mca_patcher_linux_dl_iter_context_t *ctx) +{ + long page_size = opal_getpagesize (); + void **entry; + void *page; + int ret; + + entry = mca_patcher_linux_get_got_entry (base, phdr, phnum, phent, ctx->patch->super.patch_symbol); + if (entry == NULL) { + return OPAL_SUCCESS; + } + + page = (void *)((intptr_t)entry & ~(page_size - 1)); + ret = mprotect(page, page_size, PROT_READ|PROT_WRITE); + if (ret < 0) { + opal_output_verbose (MCA_BASE_VERBOSE_ERROR, opal_patcher_base_framework.framework_output, + "failed to modify GOT page %p to rw: %s", page, strerror (errno)); + return OPAL_ERR_NOT_SUPPORTED; + } + + 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, + "patch %p (%s): modifying got entry %p. original value %p. new value %p\n", ctx->patch, + ctx->patch->super.patch_symbol, (void *) entry, *entry, (void *) ctx->patch->super.patch_value); + + 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_REV(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; +} + +static int mca_patcher_linux_phdr_iterator(struct dl_phdr_info *info, size_t size, void *data) +{ + mca_patcher_linux_dl_iter_context_t *ctx = data; + int phent; + + phent = mca_patcher_linux_get_aux_phent(); + if (phent <= 0) { + opal_output_verbose (MCA_BASE_VERBOSE_ERROR, opal_patcher_base_framework.framework_output, + "failed to read phent size"); + ctx->status = OPAL_ERR_NOT_SUPPORTED; + return -1; + } + + ctx->status = mca_patcher_linux_modify_got (info->dlpi_addr, info->dlpi_phdr, + info->dlpi_name, info->dlpi_phnum, + phent, ctx); + if (ctx->status == OPAL_SUCCESS) { + return 0; /* continue iteration and patch all objects */ + } else { + return -1; /* stop iteration if got a real error */ + } +} + +/* called with lock held */ +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, + }; + + /* 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 void *mca_patcher_linux_dlopen(const char *filename, int flag) +{ + OPAL_PATCHER_BEGIN; + mca_patcher_linux_patch_t *patch; + void *handle; + + assert (orig_dlopen); + handle = orig_dlopen (filename, flag); + if (handle != NULL) { + /* + * Every time a new object is loaded, we must update its relocations + * with our list of patches (including dlopen itself). This code is less + * efficient and will modify all existing objects every time, but good + * enough. + */ + 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->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 intptr_t mca_patcher_linux_get_orig (const char *symbol, void *replacement) +{ + const char *error; + void *func_ptr; + + func_ptr = dlsym(RTLD_DEFAULT, symbol); + if (func_ptr == replacement) { + (void)dlerror(); + func_ptr = dlsym(RTLD_NEXT, symbol); + if (func_ptr == NULL) { + error = dlerror(); + opal_output_verbose (MCA_BASE_VERBOSE_ERROR, opal_patcher_base_framework.framework_output, + "could not find address of original %s(): %s", symbol, error ? error : "Unknown error"); + } + } + + opal_output_verbose (MCA_BASE_VERBOSE_INFO, opal_patcher_base_framework.framework_output, + "original %s() is at %p", symbol, func_ptr); + + return (intptr_t) func_ptr; +} + +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; + + if (OPAL_UNLIKELY(NULL == patch)) { + return OPAL_ERR_OUT_OF_RESOURCE; + } + + patch->super.patch_symbol = strdup (symbol_name); + if (NULL == patch->super.patch_symbol) { + OBJ_RELEASE(patch); + return OPAL_ERR_OUT_OF_RESOURCE; + } + + 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_base_patch_hook (&mca_patcher_linux_module, patch->super.patch_value); + if (OPAL_SUCCESS != rc) { + OBJ_RELEASE(patch); + break; + } + + rc = mca_patcher_linux_apply_patch (patch); + if (OPAL_SUCCESS != rc) { + OBJ_RELEASE(patch); + break; + } + + *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; +} + +/* called with lock held */ +static int mca_patcher_linux_install_dlopen (void) +{ + return mca_patcher_linux_patch_symbol ("dlopen", (uintptr_t) mca_patcher_linux_dlopen, + (uintptr_t *) &orig_dlopen); +} + +static int mca_patcher_linux_init (void) +{ + return mca_patcher_linux_install_dlopen (); +} + +mca_patcher_base_module_t mca_patcher_linux_module = { + .patch_init = mca_patcher_linux_init, + .patch_symbol = mca_patcher_linux_patch_symbol, +}; diff --git a/opal/mca/patcher/overwrite/Makefile.am b/opal/mca/patcher/overwrite/Makefile.am new file mode 100644 index 0000000000..e9e4a31718 --- /dev/null +++ b/opal/mca/patcher/overwrite/Makefile.am @@ -0,0 +1,47 @@ +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2009-2010 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2015 Research Organization for Information Science +# and Technology (RIST). All rights reserved. +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +if MCA_BUILD_opal_patcher_overwrite_DSO +component_noinst = +component_install = mca_patcher_overwrite.la +else +component_noinst = libmca_patcher_overwrite.la +component_install = +endif + +overwrite_SOURCES = \ + patcher_overwrite.h \ + patcher_overwrite_module.c \ + patcher_overwrite_component.c + +mcacomponentdir = $(opallibdir) +mcacomponent_LTLIBRARIES = $(component_install) +mca_patcher_overwrite_la_SOURCES = $(overwrite_SOURCES) +nodist_mca_patcher_overwrite_la_SOURCES = $(overwrite_nodist_SOURCES) +mca_patcher_overwrite_la_LDFLAGS = -module -avoid-version + +noinst_LTLIBRARIES = $(component_noinst) +libmca_patcher_overwrite_la_SOURCES = $(overwrite_SOURCES) +nodist_libmca_patcher_overwrite_la_SOURCES = $(overwrite_nodist_SOURCES) +libmca_patcher_overwrite_la_LIBADD = $(patcher_overwrite_LIBS) +libmca_patcher_overwrite_la_LDFLAGS = -module -avoid-version diff --git a/opal/mca/patcher/overwrite/configure.m4 b/opal/mca/patcher/overwrite/configure.m4 new file mode 100644 index 0000000000..02394cc958 --- /dev/null +++ b/opal/mca/patcher/overwrite/configure.m4 @@ -0,0 +1,41 @@ +# -*- shell-script -*- +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved. +# Copyright (c) 2015 Research Organization for Information Science +# and Technology (RIST). All rights reserved. +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +# MCA_patcher_overwrite_CONFIG(action-if-can-compile, +# [action-if-cant-compile]) +# ------------------------------------------------ +AC_DEFUN([MCA_opal_patcher_overwrite_CONFIG],[ + AC_CONFIG_FILES([opal/mca/patcher/overwrite/Makefile]) + + opal_patcher_overwrite_happy=no + if test $OPAL_ENABLE_DLOPEN_SUPPORT = 1; then + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if !defined(__i386__) && !defined(__x86_64__) && !defined(__ia64__) && !defined(__PPC__) +#error "platform not supported" +#endif +]],[])],[opal_patcher_overwrite_happy=yes],[]) + fi + + AS_IF([test $opal_patcher_overwrite_happy = yes], [$1], [$2]) +]) diff --git a/opal/mca/patcher/overwrite/patcher_overwrite.h b/opal/mca/patcher/overwrite/patcher_overwrite.h new file mode 100644 index 0000000000..9c2ad58dfd --- /dev/null +++ b/opal/mca/patcher/overwrite/patcher_overwrite.h @@ -0,0 +1,32 @@ +/* -*- 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$ + */ +/** + * @file pather_overwrite.h + * + * This component works by overwritting the first couple instructions in + * the target function with a jump instruction to the hook function. The + * hook function will be expected to implement the functionality of the + * hooked function when using this module. + * + * Note: This component only supports x86, x86_64, ia64, and powerpc/power. + */ + +#if !defined(OPAL_PATCHER_OVERWRITE_H) +#define OPAL_PATCHER_OVERWRITE_H + +#include "opal_config.h" +#include "opal/mca/patcher/patcher.h" +#include "opal/class/opal_list.h" + +extern mca_patcher_base_module_t mca_patcher_overwrite_module; +extern mca_patcher_base_component_t mca_patcher_overwrite_component; + +#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 new file mode 100644 index 0000000000..5211d4deae --- /dev/null +++ b/opal/mca/patcher/overwrite/patcher_overwrite_component.c @@ -0,0 +1,45 @@ +/* -*- 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 "patcher_overwrite.h" +#include "opal/mca/mca.h" +#include "opal/mca/base/base.h" + +static int mca_patcher_overwrite_priority; + +static int mca_patcher_overwrite_register (void) +{ + mca_patcher_overwrite_priority = 37; + mca_base_component_var_register (&mca_patcher_overwrite_component.patcherc_version, + "priority", "Priority of the overwrite binary patcher component", + MCA_BASE_VAR_TYPE_INT, NULL, 0, 0, OPAL_INFO_LVL_5, + MCA_BASE_VAR_SCOPE_CONSTANT, &mca_patcher_overwrite_priority); + + return OPAL_SUCCESS; +} + +static int mca_patcher_overwrite_query (mca_base_module_t **module, int *priority) +{ + *module = &mca_patcher_overwrite_module.super; + *priority = mca_patcher_overwrite_priority; + return OPAL_SUCCESS; +} + +mca_patcher_base_component_t mca_patcher_overwrite_component = { + .patcherc_version = { + OPAL_PATCHER_BASE_VERSION_1_0_0, + .mca_component_name = "overwrite", + MCA_BASE_MAKE_VERSION(component, OPAL_MAJOR_VERSION, OPAL_MINOR_VERSION, + OPAL_RELEASE_VERSION), + .mca_query_component = mca_patcher_overwrite_query, + .mca_register_component_params = mca_patcher_overwrite_register, + }, +}; diff --git a/opal/util/opal_patcher.c b/opal/mca/patcher/overwrite/patcher_overwrite_module.c similarity index 50% rename from opal/util/opal_patcher.c rename to opal/mca/patcher/overwrite/patcher_overwrite_module.c index 0463decbf5..79cb6ce5c7 100644 --- a/opal/util/opal_patcher.c +++ b/opal/mca/patcher/overwrite/patcher_overwrite_module.c @@ -10,11 +10,14 @@ * $HEADER$ */ -#include "opal_patcher.h" +#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" +#include "opal/prefetch.h" #include #include @@ -25,19 +28,7 @@ #include #include -#if OPAL_ENABLE_DLOPEN_SUPPORT && (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(__i386__) || defined(__x86_64__) || defined(__ia64__) #if defined(__ia64__) @@ -91,24 +82,22 @@ static void make_ia64_bundle (unsigned char *dst, } #endif /* defined(__ia64__) */ -static int patch_code (uintptr_t func_old_addr, unsigned long func_new_addr) +static int mca_patcher_overwrite_apply_patch (mca_patcher_base_patch_t *patch) { - long pg_sz = opal_getpagesize (); - - if (mprotect((void*)(func_old_addr&~(pg_sz-1)), pg_sz, PROT_EXEC|PROT_READ|PROT_WRITE)) { - perror("mprotect failed\n"); - } + uintptr_t func_new_addr = patch->patch_value; { #if defined(__i386__) - *(unsigned char*)(func_old_addr+0) = 0xe9; - *(unsigned int *)(func_old_addr+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__) - *(unsigned short*)(func_old_addr+ 0) = 0xbb49; - *(unsigned long* )(func_old_addr+ 2) = (unsigned long) func_new_addr; - *(unsigned char*) (func_old_addr+10) = 0x41; - *(unsigned char*) (func_old_addr+11) = 0xff; - *(unsigned char*) (func_old_addr+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__) { /* @@ -144,109 +133,48 @@ static int patch_code (uintptr_t func_old_addr, unsigned long func_new_addr) (1ULL << 6) | (0x0ULL << 0); + patch->data_size = 32; + make_ia64_bundle(buf, movl, (glb_ptr>>22)&0x1FFFFFFFFFFULL, nop, 5); for (int i = 0 ; i < 16 ; ++i) { - ((unsigned char *)func_old_addr)[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) { - ((unsigned char *)func_old_addr+16)[16-i-1] = buf[i]; + patch->patch_data[32-i-1] = buf[i]; } } #endif } - flush_and_invalidate_cache(func_old_addr+ 0); - flush_and_invalidate_cache(func_old_addr+16); + mca_base_patcher_patch_apply_binary (patch); -#if 1 - if (mprotect((void*)(func_old_addr&~(pg_sz-1)), pg_sz, PROT_EXEC)) { - perror("mprotect failed\n"); - } -#endif - - return 0; -} - -int opal_patch_symbol (const char *func_symbol_name, uintptr_t func_new_addr) -{ - void *sym_addr; - char *error; - uintptr_t func_old_addr; - - /* NTH: might want to update opal/mca/dl to handle lookups in the default - * handle. */ - sym_addr = dlsym(RTLD_DEFAULT, func_symbol_name); - if ( (sym_addr == NULL) && ((error = dlerror()) != NULL) ) { - opal_output(0, "error locating symbol %s to patch. %s", func_symbol_name, - error); - return -1; - } - func_old_addr = (unsigned long)sym_addr; - -#if defined(__ia64__) - /* On IA64 addresses are all indirect */ - func_new_addr = *(unsigned long *)func_new_addr; - func_old_addr = *(unsigned long *)func_old_addr; -#endif - - patch_code(func_old_addr, func_new_addr); return OPAL_SUCCESS; } -bool opal_patch_supported (void) -{ - return true; -} - /* end of #if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) */ // ------------------------------------------------- PPC equivalent: -#elif OPAL_ENABLE_DLOPEN_SUPPORT && 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 -} - -// modify protection of memory range -static void -ModifyMemoryProtection(uintptr_t addr, size_t length, int prot) -{ - long page_size = opal_getpagesize (); - uintptr_t page_addr = (addr & ~(page_size-1)); - do { - if (mprotect((void *)page_addr, page_size, prot)) - perror("MemHook: mprotect failed"); - page_addr += page_size; - } while (page_addr < addr + length); -} +#elif defined(__PPC__) // 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); @@ -270,93 +198,110 @@ PatchLoadImm(uintptr_t addr, unsigned int reg, size_t value) } -static void -apply_patch(char *patch, void *addr, int length) +static int mca_patcher_overwrite_apply_patch (mca_patcher_base_patch_t *patch) { - if (length == 0) { return; } - ModifyMemoryProtection((uintptr_t)addr, length, - PROT_EXEC|PROT_READ|PROT_WRITE); - memcpy((void *)addr, patch, length); - ModifyMemoryProtection((uintptr_t)addr, length, PROT_EXEC|PROT_READ); -} - -#define MAX_PATCH_SIZE 1024 -int opal_patch_symbol (const char *sys_func, uintptr_t hook_addr) -{ - void *addr_to_patch_hook; - int len_to_patch_hook = 0; - char patch_hook[MAX_PATCH_SIZE]; - - void *addr_to_patch_sys; - int len_to_patch_sys = 0; - char patch_sys[MAX_PATCH_SIZE]; - - int offset; -#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) - unsigned int *nop_addr; - const unsigned int nop = 0x60000000; -#endif + uintptr_t sys_addr, hook_addr; + int offset, rc; // get system function address - uintptr_t sys_addr = add_text(dlsym(RTLD_NEXT, sys_func)); - if(sys_addr == 0) sys_addr = add_text(dlsym(RTLD_DEFAULT, sys_func)); - hook_addr = add_text(hook_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__)) - // locate reserved code space in hook function - nop_addr = (unsigned int *)hook_addr; - for (; ; 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"); - addr_to_patch_hook = nop_addr; - len_to_patch_hook = PatchLoadImm((uintptr_t)patch_hook, 2, toc); + rc = mca_patcher_base_patch_hook (&mca_patcher_overwrite_module, hook_addr); + if (OPAL_SUCCESS != rc) { + return rc; + } - // save the original code - assert(len_to_patch_hook <= MAX_PATCH_SIZE); - //memcpy(save, (void *)addr_to_patch_hook, len_to_patch_hook); // meh -#endif - -// Patch for system function: -#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && _CALL_ELF == 2 +#if _CALL_ELF == 2 sys_addr += 8; hook_addr += 8; #endif /* _CALL_ELF == 2*/ +#endif - addr_to_patch_sys = (void*) sys_addr; + // Patch for system function: // generate patch code // r11 is a volatile register according to PowerPC EABI const unsigned int gr = 11; - offset = PatchLoadImm((uintptr_t)patch_sys, gr, hook_addr); - *(unsigned int *) (patch_sys + offset + 0) = mtspr (9, gr); // 9 = CTR - *(unsigned int *) (patch_sys + offset + 4) = bcctr (20, 0, 0);// 20 = always - len_to_patch_sys = offset + 8; + 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; - assert(len_to_patch_sys <= MAX_PATCH_SIZE); - //memcpy(save, (void *)addr_to_patch_sys, len_to_patch_sys); + mca_base_patcher_patch_apply_binary (patch); - apply_patch(patch_hook, addr_to_patch_hook, len_to_patch_hook); - apply_patch(patch_sys, addr_to_patch_sys, len_to_patch_sys); return OPAL_SUCCESS; } -bool opal_patch_supported (void) -{ - return true; -} -#else - -int opal_patch_symbol (const char *sys_func, uintptr_t hook_addr) -{ - return OPAL_ERR_NOT_SUPPORTED; -} - -bool opal_patch_supported (void) -{ - return false; -} - #endif + +static int mca_patcher_overwrite_patch_address (uintptr_t sys_addr, unsigned long hook_addr) +{ + mca_patcher_base_patch_t *patch; + int rc; + + patch = OBJ_NEW(mca_patcher_base_patch_t); + if (OPAL_UNLIKELY(NULL == patch)) { + return OPAL_ERR_OUT_OF_RESOURCE; + } + + patch->patch_orig = sys_addr; + patch->patch_value = hook_addr; + + opal_mutex_lock (&mca_patcher_overwrite_module.patch_list_mutex); + do { + rc = mca_patcher_overwrite_apply_patch (patch); + if (OPAL_SUCCESS != rc) { + break; + } + + opal_list_append (&mca_patcher_overwrite_module.patch_list, &patch->super); + } while (0); + + opal_mutex_unlock (&mca_patcher_overwrite_module.patch_list_mutex); + + return OPAL_SUCCESS; +} + +static int mca_patcher_overwrite_patch_symbol (const char *func_symbol_name, uintptr_t func_new_addr, + uintptr_t *func_old_addr) +{ + void *sym_addr; + char *error; + uintptr_t old_addr; + + /* NTH: might want to update opal/mca/dl to handle lookups in the default + * handle. */ + sym_addr = dlsym (RTLD_NEXT, func_symbol_name); + if (NULL == sym_addr) { + sym_addr = dlsym(RTLD_DEFAULT, func_symbol_name); + if ( (sym_addr == NULL) && ((error = dlerror()) != NULL) ) { + opal_output(0, "error locating symbol %s to patch. %s", func_symbol_name, + error); + return OPAL_ERR_NOT_FOUND; + } + } + + old_addr = (unsigned long)sym_addr; + +#if defined(__ia64__) + /* On IA64 addresses are all indirect */ + func_new_addr = *(unsigned long *)func_new_addr; + old_addr = *(unsigned long *) old_addr; +#endif + + if (func_old_addr) { + /* we will be overwritting part of the original function. do not return + * its address */ + *func_old_addr = 0; + } + + return mca_patcher_overwrite_patch_address (old_addr, func_new_addr); +} + +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 new file mode 100644 index 0000000000..25af9b1376 --- /dev/null +++ b/opal/mca/patcher/patcher.h @@ -0,0 +1,121 @@ +/* -*- 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$ + */ + +#ifndef OPAL_MCA_PATCHER_PATCHER_H +#define OPAL_MCA_PATCHER_PATCHER_H + +#include "opal_config.h" + +#include "opal/mca/mca.h" +#include "opal/mca/base/base.h" +#include "opal/class/opal_list.h" + +/* 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) + +/* special processing for ppc64 to save and restore TOC (r2) + * Reference: "64-bit PowerPC ELF Application Binary Interface Supplement 1.9" */ +#define OPAL_PATCHER_BEGIN \ + unsigned long toc_save; \ + asm volatile ("std 2, %0" : "=m" (toc_save)); \ + asm volatile ("nop; nop; nop; nop; nop"); +#define OPAL_PATCHER_END \ + asm volatile ("ld 2, %0" : : "m" (toc_save)); + +#else /* !__PPC64__ */ + +#define OPAL_PATCHER_BEGIN +#define OPAL_PATCHER_END + +#endif + +/** + * Make any calls to the named function redirect to a new function + * + * @param[in] func_symbol_name function to hook + * @param[in] func_new_addr function pointer of hook + * @param[out] func_old_addr address of func_symbol_name + * + * This function redirects all calls to the function func_symbol_name to + * the function pointer func_new_addr. If it is possible for the hook + * function to call the original function the patcher module will return + * the old function's address in func_old_addr. + */ +typedef int (*mca_patcher_base_patch_symbol_fn_t)(const char *func_symbol_name, uintptr_t func_new_addr, + uintptr_t *func_old_addr); + +/** + * Make any calls to a function redirect to a new function + * + * @param[in] func_symbol_name function to hook + * @param[in] func_new_addr function pointer of hook + * @param[out] func_old_addr address of func_symbol_name + * + * This function redirects all calls to the function at func_addr to + * the function pointer func_new_addr. + */ +typedef int (*mca_patcher_base_patch_address_fn_t)(uintptr_t func_addr, uintptr_t func_new_addr); + +/** + * Set up the patcher module + */ +typedef int (*mca_patcher_base_init_fn_t) (void); + +/** + * Finalize the patcher module + */ +typedef int (*mca_patcher_base_fini_fn_t) (void); + +/** + * Structure for patcher modules. + */ +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; + /** 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; + /** hook a symbol. may be NULL */ + 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; + + +OPAL_DECLSPEC extern mca_patcher_base_module_t *opal_patcher; + +/** + * Structure for patcher components. + */ +typedef struct mca_patcher_base_component_1_0_0_t { + /** MCA base component */ + mca_base_component_t patcherc_version; + /** MCA base data */ + mca_base_component_data_t patcherc_data; +} mca_patcher_base_component_1_0_0_t; + +typedef mca_patcher_base_component_1_0_0_t mca_patcher_base_component_t; + +/* + * Macro for use in components that are of type patcher + */ +#define OPAL_PATCHER_BASE_VERSION_1_0_0 \ + OPAL_MCA_BASE_VERSION_2_1_0("patcher", 1, 0, 0) + +#endif /* OPAL_MCA_PATCHER_PATCHER_H */ diff --git a/opal/runtime/opal_finalize.c b/opal/runtime/opal_finalize.c index 382deb3c32..7138d1680d 100644 --- a/opal/runtime/opal_finalize.c +++ b/opal/runtime/opal_finalize.c @@ -41,6 +41,7 @@ #include "opal/mca/memchecker/base/base.h" #include "opal/mca/memcpy/base/base.h" #include "opal/mca/memory/base/base.h" +#include "opal/mca/patcher/base/base.h" #include "opal/mca/backtrace/base/base.h" #include "opal/mca/sec/base/base.h" #include "opal/mca/timer/base/base.h" @@ -160,6 +161,7 @@ opal_finalize(void) hooks to the bowels of the mem_free code can still occur any time between now and end of application (even post main()!) */ (void) mca_base_framework_close(&opal_memory_base_framework); + (void) mca_base_framework_close(&opal_patcher_base_framework); /* close the memcpy framework */ (void) mca_base_framework_close(&opal_memcpy_base_framework); diff --git a/opal/runtime/opal_init.c b/opal/runtime/opal_init.c index 2eade6204b..0a19a70a8e 100644 --- a/opal/runtime/opal_init.c +++ b/opal/runtime/opal_init.c @@ -45,6 +45,7 @@ #include "opal/datatype/opal_datatype.h" #include "opal/mca/installdirs/base/base.h" #include "opal/mca/memory/base/base.h" +#include "opal/mca/patcher/base/base.h" #include "opal/mca/memcpy/base/base.h" #include "opal/mca/hwloc/base/base.h" #include "opal/mca/sec/base/base.h" @@ -430,6 +431,14 @@ opal_init(int* pargc, char*** pargv) goto return_error; } + if (OPAL_SUCCESS != (ret = mca_base_framework_open(&opal_patcher_base_framework, 0))) { + error = "opal_patcher_base_open"; + goto return_error; + } + + /* select a patcher module. if a patcher module can not be found it is not an error. */ + (void) opal_patcher_base_select (); + /* open the memory manager components. Memory hooks may be triggered before this (any time after mem_free_init(), actually). This is a hook available for memory manager hooks diff --git a/opal/util/Makefile.am b/opal/util/Makefile.am index 10c91ea584..5c4cb2945e 100644 --- a/opal/util/Makefile.am +++ b/opal/util/Makefile.am @@ -53,7 +53,6 @@ headers = \ numtostr.h \ opal_environ.h \ opal_getcwd.h \ - opal_patcher.h \ opal_pty.h \ os_dirpath.h \ os_path.h \ @@ -89,7 +88,6 @@ libopalutil_la_SOURCES = \ numtostr.c \ opal_environ.c \ opal_getcwd.c \ - opal_patcher.c \ opal_pty.c \ os_dirpath.c \ os_path.c \ diff --git a/opal/util/opal_patcher.h b/opal/util/opal_patcher.h deleted file mode 100644 index 571cbead7e..0000000000 --- a/opal/util/opal_patcher.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ -/* - * Copyright (c) 2016 Los Alamos National Security, LLC. All rights - * reserved. - * ******** ADD IBM COPYRIGHT HERE ****** - * $COPYRIGHT$ - * - * Additional copyrights may follow - * - * $HEADER$ - */ - -#if !defined(OPAL_PATCHER_H) -#define OPAL_PATCHER_H - -#include "opal_config.h" - -// Any function being patched in 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) - -// special processing for ppc64 to save and restore TOC (r2) -// Reference: "64-bit PowerPC ELF Application Binary Interface Supplement 1.9" -#define OPAL_PATCHER_BEGIN \ - unsigned long toc_save; \ - asm volatile ("std 2, %0" : "=m" (toc_save)); \ - asm volatile ("nop; nop; nop; nop; nop"); -#define OPAL_PATCHER_END \ - asm volatile ("ld 2, %0" : : "m" (toc_save)); - -#else // !__PPC64__ - -#define OPAL_PATCHER_BEGIN -#define OPAL_PATCHER_END - -#endif - - -/** - * Patch all instances of calls to a function - * - * @param[in] func_symbol_name Name of symbol to patch - * @param[in] func_new_addr Pointer to new function to call - * - * @returns OPAL_SUCCESS on success - * @returns OPAL_ERR_NOT_AVAILABLE if symbol patching is not supported on this - * platform. - * - * This function patches any calls to the named symbol with a new function - * address. Any function that is passed into func_new_addr MUST begin with - * OPAL_PATCHER_BEGIN and end with OPAL_PATCHER_END. - */ -int opal_patch_symbol (const char *func_symbol_name, uintptr_t func_new_addr); - -/** - * Check if symbol patching is available - */ -bool opal_patch_supported (void); - -#endif /* !defined(OPAL_PATCHER_H) */