Skip to content

Commit

Permalink
patcher: fix powerpc/power support in patcher/linux
Browse files Browse the repository at this point in the history
This commit does the following:

 - Move code necessary for powerpc/power support to the patcher
   base. The code is needed by both the overwrite and linux
   components.

 - Move patch structure down to base and move the patch list to
   mca_patcher_base_module_t. The structure has been modified to
   include a function pointer to the function that will unapply the
   patch. This allows the mixing of multiple different types of
   patches in the patch_list.

 - Update linux patching code to keep track of the matching between
   got entry and original (unpatched) address. This allows us to
   completely clean up the patch on finalize.

Signed-off-by: Nathan Hjelm <[email protected]>
  • Loading branch information
hjelmn committed Mar 30, 2016
1 parent 5783f52 commit e401a03
Show file tree
Hide file tree
Showing 11 changed files with 415 additions and 298 deletions.
3 changes: 2 additions & 1 deletion opal/mca/patcher/base/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@

headers += base/base.h

libmca_patcher_la_SOURCES += base/patcher_base_frame.c
libmca_patcher_la_SOURCES += base/patcher_base_frame.c \
base/patcher_base_patch.c
43 changes: 43 additions & 0 deletions opal/mca/patcher/base/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,54 @@

BEGIN_C_DECLS

#define MCA_BASE_PATCHER_MAX_PATCH 32

struct mca_patcher_base_patch_t;

typedef void (*mca_patcher_base_restore_fn_t) (struct mca_patcher_base_patch_t *);

struct mca_patcher_base_patch_t {
/** patches are list items */
opal_list_item_t super;
/** name symbol to patch */
char *patch_symbol;
/** address of function to call instead */
uintptr_t patch_value;
/** original address of function */
uintptr_t patch_orig;
/** patch data */
unsigned char patch_data[MCA_BASE_PATCHER_MAX_PATCH];
/** original data */
unsigned char patch_orig_data[MCA_BASE_PATCHER_MAX_PATCH];
/** size of patch data */
unsigned patch_data_size;
/** function to undo the patch */
mca_patcher_base_restore_fn_t patch_restore;
};

typedef struct mca_patcher_base_patch_t mca_patcher_base_patch_t;

OBJ_CLASS_DECLARATION(mca_patcher_base_patch_t);

/**
* Framework struct declaration for this framework
*/
OPAL_DECLSPEC extern mca_base_framework_t opal_patcher_base_framework;
OPAL_DECLSPEC int opal_patcher_base_select (void);
OPAL_DECLSPEC int mca_patcher_base_patch_hook (mca_patcher_base_module_t *module, uintptr_t hook);
OPAL_DECLSPEC void mca_base_patcher_patch_apply_binary (mca_patcher_base_patch_t *patch);

static inline uintptr_t mca_patcher_base_addr_text (uintptr_t addr) {
#if (defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)) && _CALL_ELF != 2
struct odp_t {
uintptr_t text;
uintptr_t toc;
} *odp = (struct odp_t *) addr;
return (odp)?odp->text:0;
#else
return addr;
#endif
}

END_C_DECLS
#endif /* OPAL_BASE_PATCHER_H */
15 changes: 15 additions & 0 deletions opal/mca/patcher/base/patcher_base_frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ int opal_patcher_base_select (void)
return rc;
}

OBJ_CONSTRUCT(&best_module->patch_list, opal_list_t);
OBJ_CONSTRUCT(&best_module->patch_list_mutex, opal_mutex_t);

if (best_module->patch_init) {
rc = best_module->patch_init ();
if (OPAL_SUCCESS != rc) {
Expand All @@ -53,6 +56,18 @@ int opal_patcher_base_select (void)

static int opal_patcher_base_close (void)
{
if (opal_patcher == &empty_module) {
return OPAL_SUCCESS;
}

mca_patcher_base_patch_t *patch;
OPAL_LIST_FOREACH_REV(patch, &opal_patcher->patch_list, mca_patcher_base_patch_t) {
patch->patch_restore (patch);
}

OPAL_LIST_DESTRUCT(&opal_patcher->patch_list);
OBJ_DESTRUCT(&opal_patcher->patch_list_mutex);

if (opal_patcher->patch_fini) {
return opal_patcher->patch_fini ();
}
Expand Down
175 changes: 175 additions & 0 deletions opal/mca/patcher/base/patcher_base_patch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
* Copyright (c) 2016 Los Alamos National Security, LLC. All rights
* reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/

#include "opal_config.h"

#include "opal/mca/patcher/patcher.h"
#include "opal/mca/patcher/base/base.h"
#include "opal/util/sys_limits.h"
#include "opal/prefetch.h"
#include <sys/mman.h>

static void mca_patcher_base_patch_construct (mca_patcher_base_patch_t *patch)
{
patch->patch_symbol = NULL;
patch->patch_data_size = 0;
}

static void mca_patcher_base_patch_destruct (mca_patcher_base_patch_t *patch)
{
free (patch->patch_symbol);
}

OBJ_CLASS_INSTANCE(mca_patcher_base_patch_t, opal_list_item_t,
mca_patcher_base_patch_construct,
mca_patcher_base_patch_destruct);

#if defined(__PPC__)

// PowerPC instructions used in patching
// Reference: "PowerPC User Instruction Set Architecture"
static unsigned int addis(unsigned int RT, unsigned int RS, unsigned int UI) {
return (15<<26) + (RT<<21) + (RS<<16) + (UI&0xffff);
}
static unsigned int ori(unsigned int RT, unsigned int RS, unsigned int UI) {
return (24<<26) + (RS<<21) + (RT<<16) + (UI&0xffff);
}
static unsigned int oris(unsigned int RT, unsigned int RS, unsigned int UI) {
return (25<<26) + (RS<<21) + (RT<<16) + (UI&0xffff);
}
static unsigned int mtspr(unsigned int SPR, unsigned int RS) {
return (31<<26) + (RS<<21) + ((SPR&0x1f)<<16) + ((SPR>>5)<<11) + (467<<1);
}
static unsigned int bcctr(unsigned int BO, unsigned int BI, unsigned int BH) {
return (19<<26) + (BO<<21) + (BI<<16) + (BH<<11) + (528<<1);
}
static unsigned int rldicr(unsigned int RT, unsigned int RS, unsigned int SH, unsigned int MB)
{
return (30<<26) + (RS<<21) + (RT<<16) + ((SH&0x1f)<<11) + ((SH>>5)<<1)
+ ((MB&0x1f)<<6) + ((MB>>5)<<5) + (1<<2);
}

static int PatchLoadImm (uintptr_t addr, unsigned int reg, size_t value)
{
#if defined(__PPC64__)
*(unsigned int *) (addr + 0) = addis ( reg, 0, (value >> 48));
*(unsigned int *) (addr + 4) = ori ( reg, reg, (value >> 32));
*(unsigned int *) (addr + 8) = rldicr( reg, reg, 32, 31);
*(unsigned int *) (addr +12) = oris ( reg, reg, (value >> 16));
*(unsigned int *) (addr +16) = ori ( reg, reg, (value >> 0));
return 20;
#else
*(unsigned int *) (addr + 0) = addis ( reg, 0, (value >> 16));
*(unsigned int *) (addr + 4) = ori ( reg, reg, (value >> 0));
return 8;
#endif
}

#endif

#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__)

static void flush_and_invalidate_cache (unsigned long a)
{
#if defined(__i386__)
/* does not work with AMD processors */
__asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a));
#elif defined(__x86_64__)
__asm__ volatile("mfence;clflush %0;mfence" : :"m" (*(char*)a));
#elif defined(__ia64__)
__asm__ volatile ("fc %0;; sync.i;; srlz.i;;" : : "r"(a) : "memory");
#endif
}
#endif

// modify protection of memory range
static void ModifyMemoryProtection (uintptr_t addr, size_t length, int prot)
{
long page_size = opal_getpagesize ();
uintptr_t base = (addr & ~(page_size-1));
uintptr_t bound = ((addr + length + page_size-1) & ~(page_size-1));

length = bound - base;

#if defined(__PPC__)
/* NTH: is a loop necessary here? */
do {
if (mprotect((void *)base, page_size, prot))
perror("MemHook: mprotect failed");
base += page_size;
} while (base < addr + length);
#else
if (mprotect((void *) base, length, prot)) {
perror("MemHook: mprotect failed");
}
#endif
}

static inline void apply_patch (unsigned char *patch_data, uintptr_t address, size_t data_size)
{
ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ|PROT_WRITE);
memcpy ((void *) address, patch_data, data_size);
#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__)
for (size_t i = 0 ; i < data_size ; i += 16) {
flush_and_invalidate_cache (address + i);
}
#endif

ModifyMemoryProtection (address, data_size, PROT_EXEC|PROT_READ);
}

static void mca_base_patcher_patch_unapply_binary (mca_patcher_base_patch_t *patch)
{
apply_patch (patch->patch_orig_data, patch->patch_orig, patch->patch_data_size);
}

void mca_base_patcher_patch_apply_binary (mca_patcher_base_patch_t *patch)
{
memcpy (patch->patch_orig_data, (void *) patch->patch_orig, patch->patch_data_size);
apply_patch (patch->patch_data, patch->patch_orig, patch->patch_data_size);
patch->patch_restore = mca_base_patcher_patch_unapply_binary;
}


int mca_patcher_base_patch_hook (mca_patcher_base_module_t *module, uintptr_t hook_addr)
{
#if defined(__PPC64__) || defined(__powerpc64__) || defined(__PPC__)
mca_patcher_base_patch_t *hook_patch;
const unsigned int nop = 0x60000000;
unsigned int *nop_addr;

fprintf (stderr, "Patching hook @ 0x%lx\n", hook_addr);

hook_patch = OBJ_NEW(mca_patcher_base_patch_t);
if (OPAL_UNLIKELY(NULL == hook_patch)) {
return OPAL_ERR_OUT_OF_RESOURCE;
}

// locate reserved code space in hook function
for (nop_addr = (unsigned int *)hook_addr ; ; nop_addr++) {
if (nop_addr[0] == nop && nop_addr[1] == nop && nop_addr[2] == nop
&& nop_addr[3] == nop && nop_addr[4] == nop) {
break;
}
}
// generate code to restore TOC
register unsigned long toc asm("r2");
hook_patch->patch_orig = (uintptr_t) nop_addr;
hook_patch->patch_data_size = PatchLoadImm((uintptr_t)hook_patch->patch_data, 2, toc);

/* put the hook patch on the patch list so it will be undone on finalize */
opal_list_append (&module->patch_list, &hook_patch->super);

mca_base_patcher_patch_apply_binary (hook_patch);
#endif

return OPAL_SUCCESS;
}
32 changes: 11 additions & 21 deletions opal/mca/patcher/linux/patcher_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,25 @@
#include "opal/class/opal_list.h"
#include "opal/threads/mutex.h"

struct mca_patcher_linux_patch_t {
/** patches are list items */
struct mca_patcher_linux_patch_got_t {
opal_list_item_t super;
/** name symbol to patch */
char *symbol;
/** address of function to call instead */
uintptr_t value;
/** original address of function */
uintptr_t orig;
void **got_entry;
void *got_orig;
};

typedef struct mca_patcher_linux_patch_t mca_patcher_linux_patch_t;

OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_t);
typedef struct mca_patcher_linux_patch_got_t mca_patcher_linux_patch_got_t;

struct mca_patcher_linux_module_t {
mca_patcher_base_module_t super;
OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_got_t);

opal_list_t patch_list;
opal_mutex_t patch_list_mutex;
struct mca_patcher_linux_patch_t {
mca_patcher_base_patch_t super;
opal_list_t patch_got_list;
};

typedef struct mca_patcher_linux_module_t mca_patcher_linux_module_t;
typedef struct mca_patcher_linux_patch_t mca_patcher_linux_patch_t;

extern mca_patcher_linux_module_t mca_patcher_linux_module;
OBJ_CLASS_DECLARATION(mca_patcher_linux_patch_t);

int mca_patcher_linux_install_dlopen (void);
int mca_patcher_linux_remove_dlopen (void);
int mca_patcher_linux_patch_symbol (const char *symbol_name, uintptr_t replacement, uintptr_t *orig);
int mca_patcher_linux_remove_patch (mca_patcher_linux_patch_t *patch);
extern mca_patcher_base_module_t mca_patcher_linux_module;

#endif /* !defined(OPAL_PATCHER_LINUX_H) */
2 changes: 1 addition & 1 deletion opal/mca/patcher/linux/patcher_linux_component.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

static int mca_patcher_linux_query (mca_base_module_t **module, int *priority)
{
*module = &mca_patcher_linux_module.super.super;
*module = &mca_patcher_linux_module.super;
*priority = 37;
return OPAL_SUCCESS;
}
Expand Down
Loading

0 comments on commit e401a03

Please sign in to comment.