1
1

opal/memory: add support for patch based memory hooks

This commit adds support for runtime binary patching. The support is
broken down into two parts: util/opal_patcher.[ch] which contains the
functionality for runtime patching of symbols, and mca/memory/patcher
which patches the various symbols needed to provide support for memory
hooks. This work is preliminary and is based off work donated by IBM.

The patcher code is disabled if dlopen is disabled.

Signed-off-by: Nathan Hjelm <hjelmn@lanl.gov>
Этот коммит содержится в:
Nathan Hjelm 2016-03-22 15:27:06 -06:00 коммит произвёл Nathan Hjelm
родитель 4135cdf2c5
Коммит 7aa03d66b3
8 изменённых файлов: 835 добавлений и 1 удалений

Просмотреть файл

@ -283,7 +283,8 @@ else
OPAL_ENABLE_DLOPEN_SUPPORT=1 OPAL_ENABLE_DLOPEN_SUPPORT=1
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
fi fi
AC_DEFINE_UNQUOTED(OPAL_ENABLE_DLOPEN_SUPPORT, $OPAL_ENABLE_DLOPEN_SUPPORT,
[Whether we want to enable dlopen support])
# #
# Heterogeneous support # Heterogeneous support

32
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)

90
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 <sys/mman.h>],[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 <sys/syscall.h>],[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
])

27
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) */

Просмотреть файл

@ -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 <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/syscall.h>
#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;
}

Просмотреть файл

@ -12,6 +12,8 @@
# Copyright (c) 2007-2015 Cisco Systems, Inc. All rights reserved. # Copyright (c) 2007-2015 Cisco Systems, Inc. All rights reserved.
# Copyright (c) 2013 NVIDIA Corporation. All rights reserved. # Copyright (c) 2013 NVIDIA Corporation. All rights reserved.
# Copyright (c) 2013 Intel, Inc. All rights reserved # Copyright (c) 2013 Intel, Inc. All rights reserved
# Copyright (c) 2016 Los Alamos National Security, LLC. All rights
# reserved.
# $COPYRIGHT$ # $COPYRIGHT$
# #
# Additional copyrights may follow # Additional copyrights may follow
@ -51,6 +53,7 @@ headers = \
numtostr.h \ numtostr.h \
opal_environ.h \ opal_environ.h \
opal_getcwd.h \ opal_getcwd.h \
opal_patcher.h \
opal_pty.h \ opal_pty.h \
os_dirpath.h \ os_dirpath.h \
os_path.h \ os_path.h \
@ -86,6 +89,7 @@ libopalutil_la_SOURCES = \
numtostr.c \ numtostr.c \
opal_environ.c \ opal_environ.c \
opal_getcwd.c \ opal_getcwd.c \
opal_patcher.c \
opal_pty.c \ opal_pty.c \
os_dirpath.c \ os_dirpath.c \
os_path.c \ os_path.c \

347
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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <assert.h>
#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

57
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) */