From 5783f52d96a0acec03b194edc9e6abc75d34a669 Mon Sep 17 00:00:00 2001 From: Nathan Hjelm Date: Mon, 28 Mar 2016 22:35:18 -0600 Subject: [PATCH] opal: add code patcher framework This commit adds a framework to abstract runtime code patching. Components in the new framework can provide functions for either patching a named function or a function pointer. The later functionality is not being used but may provide a way to allow memory hooks when dlopen functionality is disabled. This commit adds two different flavors of code patching. The first is provided by the overwrite component. This component overwrites the first several instructions of the target function with code to jump to the provided hook function. The hook is expected to provide the full functionality of the hooked function. The linux patcher component is based on the memory hooks in ucx. It only works on linux and operates by overwriting function pointers in the symbol table. In this case the hook is free to call the original function using the function pointer returned by dlsym. Both components restore the original functions when the patcher framework closes. Signed-off-by: Nathan Hjelm --- contrib/platform/lanl/cray_xe6/debug-common | 3 - .../platform/lanl/cray_xe6/optimized-common | 3 - opal/common_sym_whitelist.txt | 1 + opal/mca/memory/patcher/configure.m4 | 2 +- opal/mca/memory/patcher/memory_patcher.h | 2 +- .../memory/patcher/memory_patcher_component.c | 105 ++-- opal/mca/patcher/Makefile.am | 39 ++ opal/mca/patcher/base/Makefile.am | 24 + opal/mca/patcher/base/base.h | 40 ++ opal/mca/patcher/base/patcher_base_frame.c | 66 +++ opal/mca/patcher/linux/Makefile.am | 47 ++ opal/mca/patcher/linux/configure.m4 | 43 ++ opal/mca/patcher/linux/patcher_linux.h | 54 ++ .../patcher/linux/patcher_linux_component.c | 29 ++ opal/mca/patcher/linux/patcher_linux_module.c | 464 ++++++++++++++++++ opal/mca/patcher/overwrite/Makefile.am | 47 ++ opal/mca/patcher/overwrite/configure.m4 | 41 ++ .../mca/patcher/overwrite/patcher_overwrite.h | 78 +++ .../overwrite/patcher_overwrite_component.c | 33 ++ .../overwrite/patcher_overwrite_module.c} | 305 +++++++----- opal/mca/patcher/patcher.h | 116 +++++ opal/runtime/opal_finalize.c | 2 + opal/runtime/opal_init.c | 9 + opal/util/Makefile.am | 2 - opal/util/opal_patcher.h | 61 --- 25 files changed, 1390 insertions(+), 226 deletions(-) create mode 100644 opal/mca/patcher/Makefile.am create mode 100644 opal/mca/patcher/base/Makefile.am create mode 100644 opal/mca/patcher/base/base.h create mode 100644 opal/mca/patcher/base/patcher_base_frame.c create mode 100644 opal/mca/patcher/linux/Makefile.am create mode 100644 opal/mca/patcher/linux/configure.m4 create mode 100644 opal/mca/patcher/linux/patcher_linux.h create mode 100644 opal/mca/patcher/linux/patcher_linux_component.c create mode 100644 opal/mca/patcher/linux/patcher_linux_module.c create mode 100644 opal/mca/patcher/overwrite/Makefile.am create mode 100644 opal/mca/patcher/overwrite/configure.m4 create mode 100644 opal/mca/patcher/overwrite/patcher_overwrite.h create mode 100644 opal/mca/patcher/overwrite/patcher_overwrite_component.c rename opal/{util/opal_patcher.c => mca/patcher/overwrite/patcher_overwrite_module.c} (53%) create mode 100644 opal/mca/patcher/patcher.h delete mode 100644 opal/util/opal_patcher.h diff --git a/contrib/platform/lanl/cray_xe6/debug-common b/contrib/platform/lanl/cray_xe6/debug-common index 98f6a5f84e6..6bb806362d5 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 a6169ae173b..937bf6b5982 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 d3c525b54d9..4f2ffe1042e 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 bb19dd9985c..4e2949fd636 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 0d51a34922e..0650f30c159 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 1baf85a6de0..cacefc3232f 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 00000000000..664683bad89 --- /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 00000000000..0a3d2c8224e --- /dev/null +++ b/opal/mca/patcher/base/Makefile.am @@ -0,0 +1,24 @@ +# +# 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 diff --git a/opal/mca/patcher/base/base.h b/opal/mca/patcher/base/base.h new file mode 100644 index 00000000000..f52dce6104f --- /dev/null +++ b/opal/mca/patcher/base/base.h @@ -0,0 +1,40 @@ +/* -*- 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 + +/** + * 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); + +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 00000000000..b65371f1393 --- /dev/null +++ b/opal/mca/patcher/base/patcher_base_frame.c @@ -0,0 +1,66 @@ +/* -*- 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; + } + + 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->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/linux/Makefile.am b/opal/mca/patcher/linux/Makefile.am new file mode 100644 index 00000000000..a0facb5ce76 --- /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 00000000000..cd3c3d69f0f --- /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 00000000000..529380c9b7e --- /dev/null +++ b/opal/mca/patcher/linux/patcher_linux.h @@ -0,0 +1,54 @@ +/* -*- 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_t { + /** patches are list items */ + 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; +}; + +typedef struct mca_patcher_linux_patch_t mca_patcher_linux_patch_t; + +OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_t); + +struct mca_patcher_linux_module_t { + mca_patcher_base_module_t super; + + opal_list_t patch_list; + opal_mutex_t patch_list_mutex; +}; + +typedef struct mca_patcher_linux_module_t mca_patcher_linux_module_t; + +extern mca_patcher_linux_module_t mca_patcher_linux_module; + +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); + +#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 00000000000..4030888b439 --- /dev/null +++ b/opal/mca/patcher/linux/patcher_linux_component.c @@ -0,0 +1,29 @@ +/* -*- 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_query (mca_base_module_t **module, int *priority) +{ + *module = &mca_patcher_linux_module.super.super; + *priority = 37; + 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, + }, +}; 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 00000000000..f6ca2852076 --- /dev/null +++ b/opal/mca/patcher/linux/patcher_linux_module.c @@ -0,0 +1,464 @@ +/* -*- 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/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; + 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_t, opal_list_item_t, NULL, NULL); + +static mca_patcher_linux_patch_t *mca_patcher_linux_dlopen_patch; + +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 char *symbol, + const mca_patcher_linux_elf_jmprel_t *jmprel, + const mca_patcher_linux_elf_symtab_t *symtab, + const mca_patcher_linux_elf_strtab_t *strtab) +{ + ElfW(Rela) *rela, *relaend; + const char *relsymname; + uint32_t relsymidx; + + 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) +{ + const ElfW(Phdr) *dphdr; + mca_patcher_linux_elf_jmprel_t jmprel; + mca_patcher_linux_elf_symtab_t symtab; + mca_patcher_linux_elf_strtab_t strtab; + long page_size = opal_getpagesize (); + void **entry; + void *page; + int ret; + + 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); + + entry = mca_patcher_linux_get_got_entry (base, ctx->patch->symbol, &jmprel, &symtab, &strtab); + 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; + } + + *entry = (void *) ctx->patch->value; + + 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, + .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->symbol, ctx.patch->value); + } + + return ctx.status; +} + +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; + 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->symbol, (void *) patch->value); + mca_patcher_linux_apply_patch (patch); + } + opal_mutex_unlock (&mca_patcher_linux_module.patch_list_mutex); + } + + return handle; +} + +static void *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 func_ptr; +} + +/* called with lock held */ +int mca_patcher_linux_install_dlopen (void) +{ + int rc; + + if (mca_patcher_linux_dlopen_installed) { + 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); + + rc = mca_patcher_linux_apply_patch (mca_patcher_linux_dlopen_patch); + if (OPAL_SUCCESS != rc) { + return rc; + } + + mca_patcher_linux_dlopen_installed = true; + + return OPAL_SUCCESS; +} + +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) +{ + 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->symbol = strdup (symbol_name); + if (NULL == patch->symbol) { + OBJ_RELEASE(patch); + return OPAL_ERR_OUT_OF_RESOURCE; + } + + patch->value = replacement; + + /* 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 (); + if (OPAL_SUCCESS != rc) { + 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) { + break; + } + + opal_list_append (&mca_patcher_linux_module.patch_list, &patch->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) { + 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; + + rc = mca_patcher_linux_install_dlopen (); + if (OPAL_SUCCESS != rc) { + OBJ_RELEASE(mca_patcher_linux_dlopen_patch); + } + + return rc; +} + +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 (); + + if (mca_patcher_linux_dlopen_patch) { + 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, + }, +}; diff --git a/opal/mca/patcher/overwrite/Makefile.am b/opal/mca/patcher/overwrite/Makefile.am new file mode 100644 index 00000000000..e9e4a317181 --- /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 00000000000..02394cc9586 --- /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 00000000000..1af29c21bc1 --- /dev/null +++ b/opal/mca/patcher/overwrite/patcher_overwrite.h @@ -0,0 +1,78 @@ +/* -*- 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" + +/** + * 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; + +#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 00000000000..08cd024f91c --- /dev/null +++ b/opal/mca/patcher/overwrite/patcher_overwrite_component.c @@ -0,0 +1,33 @@ +/* -*- 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" + +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; + *priority = 10; + 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, + }, +}; diff --git a/opal/util/opal_patcher.c b/opal/mca/patcher/overwrite/patcher_overwrite_module.c similarity index 53% rename from opal/util/opal_patcher.c rename to opal/mca/patcher/overwrite/patcher_overwrite_module.c index 0463decbf52..8b7b8db029d 100644 --- a/opal/util/opal_patcher.c +++ b/opal/mca/patcher/overwrite/patcher_overwrite_module.c @@ -10,11 +10,12 @@ * $HEADER$ */ -#include "opal_patcher.h" +#include "patcher_overwrite.h" #include "opal/constants.h" #include "opal/util/sys_limits.h" #include "opal/util/output.h" +#include "opal/prefetch.h" #include #include @@ -25,7 +26,47 @@ #include #include -#if OPAL_ENABLE_DLOPEN_SUPPORT && (defined(__i386__) || defined(__x86_64__) || defined(__ia64__)) +// 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) { @@ -91,24 +132,25 @@ 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_overwrite_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_old_addr = patch->orig_addr; + uintptr_t func_new_addr = patch->patch_addr; { #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); + 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); #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; + 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; #elif defined(__ia64__) { /* @@ -144,65 +186,30 @@ static int patch_code (uintptr_t func_old_addr, unsigned long func_new_addr) (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) { - ((unsigned char *)func_old_addr)[16-i-1] = buf[i]; + patch->patched_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->patched_data[32-i-1] = buf[i]; } } #endif } - flush_and_invalidate_cache(func_old_addr+ 0); - flush_and_invalidate_cache(func_old_addr+16); - -#if 1 - if (mprotect((void*)(func_old_addr&~(pg_sz-1)), pg_sz, PROT_EXEC)) { - perror("mprotect failed\n"); - } -#endif + apply_patch (patch->patched_data, patch->orig_addr, patch->data_size); - 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__) +#elif defined(__PPC__) static inline uintptr_t addr_text (uintptr_t addr) { #if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && _CALL_ELF != 2 @@ -216,19 +223,6 @@ static inline uintptr_t addr_text (uintptr_t 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); -} - // PowerPC instructions used in patching // Reference: "PowerPC User Instruction Set Architecture" unsigned int addis(unsigned int RT, unsigned int RS, unsigned int UI) { @@ -270,93 +264,166 @@ PatchLoadImm(uintptr_t addr, unsigned int reg, size_t value) } -static void -apply_patch(char *patch, void *addr, int length) -{ - 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) +static int mca_patcher_overwrite_apply_patch (mca_patcher_overwrite_patch_t *patch) { - 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]; + 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 // 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 = addr_text(patch->orig_addr); + hook_addr = addr_text(patch->patch_addr); // 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; + } + // locate reserved code space in hook function - nop_addr = (unsigned int *)hook_addr; - for (; ; nop_addr++) + 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) + && 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); + 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 - assert(len_to_patch_hook <= MAX_PATCH_SIZE); - //memcpy(save, (void *)addr_to_patch_hook, len_to_patch_hook); // meh -#endif + 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); -// Patch for system function: -#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && _CALL_ELF == 2 + apply_patch(hook_patch->patched_data, hook_patch->orig_addr, hook_patch->data_size); + +#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->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; - assert(len_to_patch_sys <= MAX_PATCH_SIZE); - //memcpy(save, (void *)addr_to_patch_sys, len_to_patch_sys); + /* 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); - 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) +#endif + +static int mca_patcher_overwrite_patch_address (uintptr_t sys_addr, unsigned long hook_addr) { - return true; + mca_patcher_overwrite_patch_t *patch; + int rc; + + patch = OBJ_NEW(mca_patcher_overwrite_patch_t); + if (OPAL_UNLIKELY(NULL == patch)) { + return OPAL_ERR_OUT_OF_RESOURCE; + } + + patch->orig_addr = sys_addr; + patch->patch_addr = 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; } -#else -int opal_patch_symbol (const char *sys_func, uintptr_t hook_addr) +static int mca_patcher_overwrite_patch_symbol (const char *func_symbol_name, uintptr_t func_new_addr, + uintptr_t *func_old_addr) { - return OPAL_ERR_NOT_SUPPORTED; + 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); } -bool opal_patch_supported (void) +static int mca_patcher_overwrite_patch_init (void) { - return false; + 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; } -#endif +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, + }, +}; diff --git a/opal/mca/patcher/patcher.h b/opal/mca/patcher/patcher.h new file mode 100644 index 00000000000..145b1103805 --- /dev/null +++ b/opal/mca/patcher/patcher.h @@ -0,0 +1,116 @@ +/* -*- 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" + +/* 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 + +/** + * 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; + /** 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 382deb3c324..7138d1680d7 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 2eade6204b6..0a19a70a8e1 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 10c91ea5844..5c4cb2945ed 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 571cbead7e5..00000000000 --- 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) */