diff --git a/config/opal_configure_options.m4 b/config/opal_configure_options.m4 index ce06b9edd2..06619dcee6 100644 --- a/config/opal_configure_options.m4 +++ b/config/opal_configure_options.m4 @@ -283,7 +283,8 @@ else OPAL_ENABLE_DLOPEN_SUPPORT=1 AC_MSG_RESULT([yes]) fi - +AC_DEFINE_UNQUOTED(OPAL_ENABLE_DLOPEN_SUPPORT, $OPAL_ENABLE_DLOPEN_SUPPORT, + [Whether we want to enable dlopen support]) # # Heterogeneous support diff --git a/opal/mca/memory/patcher/Makefile.am b/opal/mca/memory/patcher/Makefile.am new file mode 100644 index 0000000000..ce4172617f --- /dev/null +++ b/opal/mca/memory/patcher/Makefile.am @@ -0,0 +1,32 @@ +# +# 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$ +# + +# This component is only ever built statically (i.e., slurped into +# libopen-pal) -- it is never built as a DSO. +noinst_LTLIBRARIES = libmca_memory_patcher.la +libmca_memory_patcher_la_SOURCES = \ + memory_patcher.h \ + memory_patcher_component.c +libmca_memory_patcher_la_LDFLAGS = \ + -module -avoid-version $(memory_patcher_LDFLAGS) +libmca_memory_patcher_la_LIBADD = $(memory_patcher_LIBS) diff --git a/opal/mca/memory/patcher/configure.m4 b/opal/mca/memory/patcher/configure.m4 new file mode 100644 index 0000000000..bb19dd9985 --- /dev/null +++ b/opal/mca/memory/patcher/configure.m4 @@ -0,0 +1,90 @@ +# -*- 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$ +# +AC_DEFUN([MCA_opal_memory_patcher_PRIORITY], [40]) + +AC_DEFUN([MCA_opal_memory_patcher_COMPILE_MODE], [ + AC_MSG_CHECKING([for MCA component $2:$3 compile mode]) + $4="static" + AC_MSG_RESULT([$$4]) +]) + + +# MCA_memory_patcher_CONFIG(action-if-can-compile, +# [action-if-cant-compile]) +# ------------------------------------------------ +AC_DEFUN([MCA_opal_memory_patcher_CONFIG],[ + AC_CONFIG_FILES([opal/mca/memory/patcher/Makefile]) + + OPAL_VAR_SCOPE_PUSH([memory_patcher_have___curbrk memory_patcher_have___mmap memory_patcher_have___syscall memory_patcher_have___mmap_prototype memory_patcher_have___syscall_prototype]) + + memory_patcher_have___curbrk=0 + memory_patcher_have___mmap=0 + memory_patcher_have___mmap_prototype=0 + memory_patcher_have___syscall=0 + memory_patcher_have___syscall_prototype=0 + + AC_MSG_CHECKING([for __curbrk symbol]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([extern char *__curbrk;],[char *tmp = __curbrk;])], + [AC_MSG_RESULT([yes]) + memory_patcher_have___curbrk=1], + [AC_MSG_RESULT([no])]) + AC_DEFINE_UNQUOTED([OPAL_MEMORY_PATCHER_HAVE___CURBRK], [$memory_patcher_have___curbrk], + [Whether the glibc __curbrk exists]) + + AC_MSG_CHECKING([whether __mmap prototype exists]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ],[char *tmp = __mmap (NULL, 0, 0, 0, 0, 0);])], + [AC_MSG_RESULT([yes]) + memory_patcher_have___mmap_prototype=1], + [AC_MSG_RESULT([no])]) + AC_DEFINE_UNQUOTED([OPAL_MEMORY_PATCHER_HAVE___MMAP_PROTO], [$memory_patcher_have___mmap_prototype], + [Whether the internal __mmap call has a prototype]) + + AC_MSG_CHECKING([whether __mmap symbol exists]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([void *__mmap ();],[char *tmp = __mmap ();])], + [AC_MSG_RESULT([yes]) + memory_patcher_have___mmap=1], + [AC_MSG_RESULT([no])]) + AC_DEFINE_UNQUOTED([OPAL_MEMORY_PATCHER_HAVE___MMAP], [$memory_patcher_have___mmap], + [Whether the internal __mmap call exists]) + + AC_MSG_CHECKING([whether __syscall prototype exists]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ],[char *tmp = __syscall (SYS_mmap, NULL);])], + [AC_MSG_RESULT([yes]) + memory_patcher_have___syscall_prototype=1], + [AC_MSG_RESULT([no])]) + AC_DEFINE_UNQUOTED([OPAL_MEMORY_PATCHER_HAVE___SYSCALL_PROTO], [$memory_patcher_have___syscall_prototype], + [Whether the internal __syscall call has a prototype]) + + AC_MSG_CHECKING([whether __syscall symbol exists]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([void *__syscall ();],[char *tmp = __syscall ();])], + [AC_MSG_RESULT([yes]) + memory_patcher_have___syscall=1], + [AC_MSG_RESULT([no])]) + AC_DEFINE_UNQUOTED([OPAL_MEMORY_PATCHER_HAVE___SYSCALL], [$memory_patcher_have___syscall], + [Whether the internal __syscall call exists]) + + [$1] + + OPAL_VAR_SCOPE_POP +]) diff --git a/opal/mca/memory/patcher/memory_patcher.h b/opal/mca/memory/patcher/memory_patcher.h new file mode 100644 index 0000000000..0d51a34922 --- /dev/null +++ b/opal/mca/memory/patcher/memory_patcher.h @@ -0,0 +1,27 @@ +/* -*- 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_MEMORY_PATCHER_H) +#define OPAL_MEMORY_PATCHER_H + +#include "opal_config.h" + +#include "opal/mca/memory/memory.h" +#include "opal/util/opal_patcher.h" + +typedef struct opal_memory_patcher_component_t { + opal_memory_base_component_2_0_0_t super; +} opal_memory_patcher_component_t; + +extern opal_memory_patcher_component_t mca_memory_patcher_component; + +#endif /* !defined(OPAL_MEMORY_PATCHER_H) */ diff --git a/opal/mca/memory/patcher/memory_patcher_component.c b/opal/mca/memory/patcher/memory_patcher_component.c new file mode 100644 index 0000000000..f8de71d60c --- /dev/null +++ b/opal/mca/memory/patcher/memory_patcher_component.c @@ -0,0 +1,276 @@ +/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ +/* + * Copyright (c) 2004-2007 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-2014 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2013-2016 Los Alamos National Security, LLC. All rights + * reserved. + * Copyright (c) 2016 Research Organization for Information Science + * and Technology (RIST). All rights reserved. + * ******** Add IBM COPYRIGHT HERE *********** + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "memory_patcher.h" + +#include "opal/util/output.h" +#include "opal/util/show_help.h" +#include "opal/mca/memory/base/empty.h" +#include "opal/memoryhooks/memory.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "memory_patcher.h" +#undef opal_memory_changed + +static int patcher_open(void); +static int patcher_close(void); +static int patcher_register(void); + +opal_memory_patcher_component_t mca_memory_patcher_component = { + .super = { + .memoryc_version = { + OPAL_MEMORY_BASE_VERSION_2_0_0, + + /* Component name and version */ + .mca_component_name = "patcher", + MCA_BASE_MAKE_VERSION(component, OPAL_MAJOR_VERSION, OPAL_MINOR_VERSION, + OPAL_RELEASE_VERSION), + + /* Component open and close functions */ + .mca_open_component = patcher_open, + .mca_close_component = patcher_close, + .mca_register_component_params = patcher_register, + }, + .memoryc_data = { + /* The component is checkpoint ready */ + MCA_BASE_METADATA_PARAM_CHECKPOINT + }, + + /* Memory framework functions. */ + .memoryc_register = opal_memory_base_component_register_empty, + .memoryc_deregister = opal_memory_base_component_deregister_empty, + .memoryc_set_alignment = opal_memory_base_component_set_alignment_empty, + }, + + /* Component-specific data, filled in later (compiler will 0/NULL + it out) */ +}; + +#if OPAL_MEMORY_PATCHER_HAVE___MMAP && !OPAL_MEMORY_PATCHER_HAVE___MMAP_PROTO +/* prototype for Apple's internal mmap function */ +void *__mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset); +#endif + +#if OPAL_MEMORY_PATCHER_HAVE___SYSCALL_PROTO && OPAL_MEMORY_PATCHER_HAVE___SYSCALL +/* calling __syscall is preferred on some systems when some arguments may be 64-bit. it also + * has the benefit of having an off_t return type */ +#define memory_patcher_syscall __syscall +#else +#define memory_patcher_syscall syscall +#endif + +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); + } + +#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); +#else + 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. + + OPAL_PATCHER_END; + return result; +} + +static int intercept_munmap(void *start, size_t length) +{ + OPAL_PATCHER_BEGIN; + int result = 0; + + opal_mem_hooks_release_hook (start, length, 0); + + result=memory_patcher_syscall(SYS_munmap, start, length); + + OPAL_PATCHER_END; + return result; +} + +#if defined (SYS_mremap) + +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 + + opal_mem_hooks_release_hook (start, oldlen, 0); + +#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 + + OPAL_PATCHER_END; + return result; +} + +#endif + +static int intercept_madvise (void *start, size_t length, int advice) +{ + OPAL_PATCHER_BEGIN; + int result = 0; + + if (advice == MADV_DONTNEED || +#ifdef MADV_REMOVE + advice == MADV_REMOVE || +#endif + advice == POSIX_MADV_DONTNEED) + { + opal_mem_hooks_release_hook (start, length, 0); + } + result = memory_patcher_syscall(SYS_madvise, start, length, advice); + + OPAL_PATCHER_END; + return result; +} + +#if defined SYS_brk + +#if OPAL_MEMORY_PATCHER_HAVE___CURBRK +void *__curbrk; /* in libc */ +#endif + +static int intercept_brk (void *addr) +{ + OPAL_PATCHER_BEGIN; + int result = 0; + void *old_addr, *new_addr; + +#if OPAL_MEMORY_PATCHER_HAVE___CURBRK + old_addr = __curbrk; +#else + old_addr = sbrk (0); +#endif + + /* 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; +#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_PATCHER_END; + return result; +} + +#endif + +static int patcher_register (void) +{ + return OPAL_SUCCESS; +} + +static int patcher_open (void) +{ + static int was_executed_already = 0; + int rc; + + if (was_executed_already) { + return OPAL_SUCCESS; + } + + was_executed_already = 1; + + rc = opal_patch_symbol ("mmap", (uintptr_t) intercept_mmap); + if (OPAL_SUCCESS != rc) { + return rc; + } + + rc = opal_patch_symbol ("munmap", (uintptr_t)intercept_munmap); + if (OPAL_SUCCESS != rc) { + return rc; + } + +#if defined (SYS_mremap) + rc = opal_patch_symbol ("mremap",(uintptr_t)intercept_mremap); + if (OPAL_SUCCESS != rc) { + return rc; + } +#endif + + rc = opal_patch_symbol ("madvise", (uintptr_t)intercept_madvise); + if (OPAL_SUCCESS != rc) { + return rc; + } + +#if defined (SYS_brk) + rc = opal_patch_symbol ("brk", (uintptr_t)intercept_brk); +#endif + + return rc; +} + +static int patcher_close(void) +{ + /* NTH: is it possible to unpatch the symbols? */ + return OPAL_SUCCESS; +} diff --git a/opal/util/Makefile.am b/opal/util/Makefile.am index f80cbd50ec..10c91ea584 100644 --- a/opal/util/Makefile.am +++ b/opal/util/Makefile.am @@ -12,6 +12,8 @@ # Copyright (c) 2007-2015 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2013 NVIDIA Corporation. All rights reserved. # Copyright (c) 2013 Intel, Inc. All rights reserved +# Copyright (c) 2016 Los Alamos National Security, LLC. All rights +# reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -51,6 +53,7 @@ headers = \ numtostr.h \ opal_environ.h \ opal_getcwd.h \ + opal_patcher.h \ opal_pty.h \ os_dirpath.h \ os_path.h \ @@ -86,6 +89,7 @@ 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.c b/opal/util/opal_patcher.c new file mode 100644 index 0000000000..74e88c3a87 --- /dev/null +++ b/opal/util/opal_patcher.c @@ -0,0 +1,347 @@ +/* -*- 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$ + */ + +#include "opal_patcher.h" + +#include "opal/constants.h" +#include "opal/util/sys_limits.h" +#include "opal/util/output.h" + +#include +#include +#include +#include +#include +#include +#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(__ia64__) + +#define INSERT_BIT(d,p,v) do { \ + unsigned char c=*(d); \ + assert(((p) < 8) && ((p) >= 0)); \ + c&= ~(1<<(p)); \ + c|= ((v)<<(p)); \ + *(d) = c; \ + } while (0) + +static inline void +copy_instr_slot(unsigned char **dst, int *dst_bitpos, unsigned long instr_slot) +{ + for (int i = 40 ; i >= 0 ; --i) { + INSERT_BIT(*dst, *dst_bitpos, (instr_slot>>i)&1); + if (*dst_bitpos == 0) { + ++*dst; + *dst_bitpos = 7; + } else { + --*dst_bitpos; + } + } +} + +static void make_ia64_bundle (unsigned char *dst, + unsigned long i2, + unsigned long i1, + unsigned long i0, + unsigned template) +{ +/* + * each instr is 41 bits, template is 5 bits + * + * generate the bit concatenation of i2:i1:i0:t, all in all 128 bits + * + */ + + int dst_bitpos = 7; + + copy_instr_slot(&dst, &dst_bitpos, i2); + copy_instr_slot(&dst, &dst_bitpos, i1); + copy_instr_slot(&dst, &dst_bitpos, i0); + + assert(dst_bitpos == 4); + + for (int i = 4 ; i >= 0 ; --i) { + INSERT_BIT(dst, dst_bitpos, (template>>i)&1); + --dst_bitpos; + } +} +#endif /* defined(__ia64__) */ + +static int patch_code (uintptr_t func_old_addr, unsigned long func_new_addr) +{ + 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"); + } + + { +#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); +#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; +#elif defined(__ia64__) + { +/* + * target64 = IP + ((i << 59 | imm39 << 20 | imm20) << 4) + * imm64 = i << 63 | imm41 << 22 | ic << 21 | imm5c << 16 | imm9d << 7 | imm7b + */ + unsigned char buf[16]; + unsigned long long imm64 = func_new_addr - func_old_addr - 16; + register unsigned long long glb_ptr __asm__("r1"); + unsigned long long nop = + (0x0ULL<<37) | /* O */ + (0x0ULL<<36) | /* i */ + (0x0ULL<<33) | /* x3 */ + (0x1ULL<<27) | /* x6 */ + (0x0ULL<< 6) | /* imm20 */ + (0x0ULL<< 0); /* qp */ + unsigned long long brl = + (0xcULL << 37) | + (((imm64>>63)&0x1ULL) << 36) | + (0x0ULL << 35) | + (0x0ULL << 33) | + (((imm64>>4)&0xFFFFFULL) << 13) | + (0x0ULL << 6) | + (0x0ULL << 0); + unsigned long long movl = + (0x6ULL << 37) | + (((glb_ptr>>63)&0x1ULL) << 36) | + (((glb_ptr>> 7)&0x1FFULL) << 27) | + (((glb_ptr>>16)&0x1FULL) << 22) | + (((glb_ptr>>21)&0x1ULL) << 21) | + (0ULL << 20) | + (((glb_ptr>> 0)&0x7FULL) << 13) | + (1ULL << 6) | + (0x0ULL << 0); + + 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]; + } + + 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]; + } + } +#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 + + 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; +} + +/* 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); +} + +// PowerPC instructions used in patching +// Reference: "PowerPC User Instruction Set Architecture" +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) { + return (24<<26) + (RS<<21) + (RT<<16) + (UI&0xffff); +} +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) { + 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) { + 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) +{ + 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 +} + + +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) +{ + 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 + + // 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); + +// 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); + + // 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 + sys_addr += 8; + hook_addr += 8; +#endif /* _CALL_ELF == 2*/ + + addr_to_patch_sys = (void*) sys_addr; + // 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; + + assert(len_to_patch_sys <= MAX_PATCH_SIZE); + //memcpy(save, (void *)addr_to_patch_sys, len_to_patch_sys); + + 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; +} + +#else + +int opal_patch_symbol (const char *sys_func, uintptr_t hook_addr) { + return OPAL_ERR_NOT_SUPPORTED; +} + +#endif diff --git a/opal/util/opal_patcher.h b/opal/util/opal_patcher.h new file mode 100644 index 0000000000..19c04c800f --- /dev/null +++ b/opal/util/opal_patcher.h @@ -0,0 +1,57 @@ +/* -*- 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); + + +#endif /* !defined(OPAL_PATCHER_H) */