diff --git a/CMakeLists.txt b/CMakeLists.txt index f6c542e..808281e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ option(GOTCHA_ENABLE_TESTS "Enable internal tests" Off) option(GOTCHA_ENABLE_COVERAGE_TESTS "Enable coverage tests" Off) -project(gotcha) +project(gotcha C CXX ASM) include(CheckCXXCompilerFlag) include(cmake/gotcha.cmake) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) diff --git a/docs/index.rst b/docs/index.rst index 8e69bea..c050b71 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -101,8 +101,8 @@ general concepts that are worth understanding: application’s start-up, perhaps at the top of main or in a library constructor. -Example -------- +Wrapping Example +---------------- This example shows how to use gotcha to wrap the open and fopen libc calls. This example is self-contained, though in typical gotcha @@ -188,6 +188,91 @@ the process, which would have led to an error return. The calls to fprintf on lines 17 and 25 are stomping on the value of errno, which could be set in the open and fopen calls on lines 16 and 24. +Signature-Free Wrapping Example +------------------------------- +This small example shows how to make signature-free wrappers with Gotcha. +Unlike regular wrappers, signature-free wrappers to not need to match the +signature of the function they are wrapping. This means they cannot +inspect or modify arguments or return values, but they can be placed around +arbitrary functions. + +.. code-block:: c + :linenos: + + #include + #include + #include + #include + #include + #include + #include + #include "gotcha/gotcha.h" + + static gotcha_wrappee_handle_t open_handle; + static gotcha_wrappee_handle_t read_handle; + static gotcha_wrappee_handle_t close_handle; + + static void start_stopwatch(gotcha_wrappee_handle_t handle, void **opaque_value) + { + struct timeval *start = (struct timeval *) malloc(sizeof(struct timeval)); + gettimeofday(start, NULL); + *opaque_value = (void *) start; + } + + static void end_stopwatch(gotcha_wrappee_handle_t handle, void *opaque_value) + { + struct timeval *start, end, diff; + start = (struct timeval *) opaque_value; + gettimeofday(&end, NULL); + timersub(&end, start, &diff); + + fprintf(stderr, "Function %s took %lu microseconds\n", gotcha_get_wrappee_name(handle), + diff.tv_sec * 1000000 + diff.tv_usec); + free(start); + } + + static gotcha_sigfree_binding_t bindings[] = { + { "open", start_stopwatch, end_stopwatch, &open_handle }, + { "read", start_stopwatch, end_stopwatch, &read_handle }, + { "close", start_stopwatch, end_stopwatch, &close_handle } + }; + + int main(int argc, char *argv[]) + { + int fd; + unsigned char buffer[4096]; + + gotcha_sigfree_wrap(bindings, 3, "demotool"); + + fd = open("/dev/random", O_RDONLY); + read(fd, buffer, sizeof(buffer)); + close(fd); + + return 0; + } + +Running this program will produce output similar to: + +.. code-block:: none + + Function open took 9 microseconds + Function read took 32 microseconds + Function close took 1 microseconds + +This example inserts timing calls around the open, read, and close functions. +The start_stopwatch routine starts a timer, and passes it to the end_stopwatch +routine via the opaque_value parameter. + +Note that, unlike the first example, the same wrapper can be used on multiple +functions. The wrapper does not receive (or need to match) the arguments and +return value of the wrappee. The start_stopwatch wrapper is not responsible +for calling the wrappee, and instead simply returns. + +This example ignores error handling for brevity. In practice, a user should +check whether wrappees were found, and it should preserve the errno value +across wrappers. In addition, calling malloc/free from every wrapper +invocation would likely lead to performance issues. + API Reference ------------- @@ -228,12 +313,59 @@ to describe all the wrapping actions that a tool would like gotcha to perform. The name field is the name of a function to wrap. The wrapper_pointer is -a function pointer (but cast to a void*) to the wrapper function. The +a function pointer (but cast to a void\*) to the wrapper function. The function_handle is a handle that can be used to get a function pointer to the wrappee. This type is accessible by including gotcha.h. +sigfree_pre_wrapper_t +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + typedef void (*sigfree_pre_wrapper_t)(gotcha_wrappee_handle_t handle, void **opaque_val); + +This type describes the function signature that can be used to create the +pre-wrapper in singnature-free wrappers. See the gotcha_sigfree_wrap +function for more information. + +sigfree_post_wrapper_t +^^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: c + + typedef void (*sigfree_post_wrapper_t)(gotcha_wrappee_handle_t handle, void *opaque_val); + +This type describes the function signature that can be used to create the +post-wrapper in signature-free wrappers. See the gotcha_sigfree_wrap +function for more information. + +gotcha_sigfree_binding_t +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + typedef struct gotcha_sigfree_binding_t { + const char *name; + sigfree_pre_wrapper_t pre_wrapper; + sigfree_post_wrapper_t post_wrapper; + gotcha_wrappee_handle_t *function_handle; + } gotcha_sigfree_binding_t; + +This type describes a signature-free function wrapping action that +gotcha should perform. A table of multiple gotcha_binding_t objects +is typically used to describe all the wrapping actions that a tool +would like gotcha to perform. + +The name field is the name of a function to wrap. The pre_wrapper +and post_wrapper arguments are function pointers to the wrappers +that should be called around the wrappee (see gotcha_sigfree_wrap). +Either pre_wrapper or post_wrapper can have a NULL value if the +wrapper should not be called. The function_handle is a handle that +can be used to get a function pointer to the wrappee. + +This type is accessible by including gotcha.h. + gotcha_error_t ^^^^^^^^^^^^^^ @@ -293,6 +425,44 @@ gotcha_wrap will return one of the following values: This function is accessible by including gotcha.h. +gotcha_sigfree_wrap +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + enum gotcha_error_t gotcha_sigfree_wrap( + gotcha_sigfree_binding_t *bindings, + int num_actions, + const char *tool_name); + +The gotcha_sigfree_wrap function enables a set of signature-free wrappings +(also known as interface-independent wrappings). It behaves like +gotcha_wrap, except there are two wrapper functions for each wrappee, a +pre-wrapper and post-wrapper function. The pre-wrapper and post-wrapper are +respectively called before and after the wrappee executes. The pre-wrapper +executes and must return before the wrappee starts executing. The post-wrapper +executes immediately after the wrappee returns. + +Signature-free wrappers are less powerful than the wrappers installed by +gotcha_wrap. They do not have access to the wrappees function arguments or +return value. However, signature-free wrappers are more versatile as they +can be installed around arbitrary functions. + +The pre-wrapper and post-wrapper both take an opaque_val parameter. For the +pre_wrapper this is a void\* output parameter that can be set to an arbitrary +value. The input parameter will receive the same value as an input void\*. +This can be used to communicate data through paired pre and post wrappers. + +The pre-wrapper and post-wrapper also take a gotcha_wrappee_handle_t. This can +be used to look up the name of the next wrappee, which is useful when the same +signature-free wrapper is placed around multiple wrappees. + +The gotcha_sigfree_wrap bindings, num_actions, and tool_name arguments behave +like the similarly named arguments to the gotcha_wrap call. gotcha_sigfree_wrap +has the same error returns and error handling semantics as gotcha_wrap. + +This function is accessible by including gotcha.h. + gotcha_get_wrappee ^^^^^^^^^^^^^^^^^^ @@ -311,6 +481,19 @@ to eventually call the actual wrappee. This function is accessible by including gotcha.h. +gotcha_get_wrappee_name +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + const char *gotcha_get_wrappee(gotcha_wrappee_handle_t handle); + +The function returns the name of the wrappee function associated with +the given handle. The memory behind the returned string should be +considered owned by gotcha and not be freed by an application. + +This function is accessible by including gotcha.h. + gotcha_set_priority ^^^^^^^^^^^^^^^^^^^ diff --git a/include/gotcha/gotcha.h b/include/gotcha/gotcha.h index 30018a2..e7644ef 100644 --- a/include/gotcha/gotcha.h +++ b/include/gotcha/gotcha.h @@ -72,6 +72,8 @@ extern "C" { GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* bindings, int num_actions, const char* tool_name); +GOTCHA_EXPORT enum gotcha_error_t gotcha_sigfree_wrap(struct gotcha_sigfree_binding_t *bindings, int num_actions, const char *tool_name); + /*! ****************************************************************************** * @@ -118,6 +120,19 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_get_priority(const char* tool_name, int */ GOTCHA_EXPORT void* gotcha_get_wrappee(gotcha_wrappee_handle_t handle); +/*! + ****************************************************************************** + * + * \fn enum void* gotcha_get_wrappee_name(gotcha_wrappee_handle_t) + * + * \brief Given a GOTCHA wrapper's handle, returns the name of the wrappee function + * + * \param handle The wrappee handle to return the name for + * + ****************************************************************************** + */ +GOTCHA_EXPORT const char* gotcha_get_wrappee_name(gotcha_wrappee_handle_t handle); + #if defined(__cplusplus) } #endif diff --git a/include/gotcha/gotcha_types.h b/include/gotcha/gotcha_types.h index 5e247f7..173ca54 100644 --- a/include/gotcha/gotcha_types.h +++ b/include/gotcha/gotcha_types.h @@ -30,6 +30,9 @@ extern "C" { typedef void* gotcha_wrappee_handle_t; +typedef void (*sigfree_pre_wrapper_t)(gotcha_wrappee_handle_t handle, void **opaque_val); +typedef void (*sigfree_post_wrapper_t)(gotcha_wrappee_handle_t handle, void *opaque_val); + /*! * The representation of a Gotcha action * as it passes through the pipeline @@ -37,9 +40,16 @@ typedef void* gotcha_wrappee_handle_t; typedef struct gotcha_binding_t { const char* name; //!< The name of the function being wrapped void* wrapper_pointer; //!< A pointer to the wrapper function - gotcha_wrappee_handle_t* function_handle; //!< A pointer to the function being wrapped + gotcha_wrappee_handle_t* function_handle; //!< A handle to the function being wrapped }gotcha_binding_t; +typedef struct gotcha_sigfree_binding_t { + const char *name; //!< The name of the function being wrapped + sigfree_pre_wrapper_t pre_wrapper; //!< A pointer to the pre-wrapper function + sigfree_post_wrapper_t post_wrapper; //!< A pointer to the post-wrapper function + gotcha_wrappee_handle_t *function_handle; //!< A handle for referring to this wrapping +} gotcha_sigfree_binding_t; + /*! * The representation of an error (or success) of a Gotcha action */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1132a4c..430c3cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,10 +10,20 @@ set(GOTCHA_SOURCES library_filters.c gotcha_dl.c translations.c + thin.c ) -add_library(gotcha SHARED ${GOTCHA_SOURCES}) +if ( ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" ) + list(APPEND GOTCHA_SOURCES thin_x86.s) +elseif ( ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le" ) + list(APPEND GOTCHA_SOURCES thin_ppc.s) +elseif ( ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64" ) + list(APPEND GOTCHA_SOURCES thin_ppc.s) +else() + message( FATAL_ERROR "Unknown architecture ${CMAKE_SYSTEM_PROCESSOR}" ) +endif() +add_library(gotcha SHARED ${GOTCHA_SOURCES}) set_target_properties(gotcha PROPERTIES SOVERSION ${LIBTOOL_INTERFACE}) set_target_properties(gotcha PROPERTIES VERSION "${LIBTOOL_INTERFACE}.${LIBTOOL_REVISION}.${LIBTOOL_AGE}") diff --git a/src/gotcha.c b/src/gotcha.c index 8e9524d..3e676d3 100644 --- a/src/gotcha.c +++ b/src/gotcha.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "gotcha_dl.h" #include "elf_ops.h" #include "tool.h" +#include "thin.h" static void writeAddress(void* write, void* value){ *(void**)write = value; @@ -265,7 +266,7 @@ void update_all_library_gots(hash_table_t *bindings) } } -GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bindings, int num_actions, const char* tool_name) +static enum gotcha_error_t internal_gotcha_wrap(struct gotcha_binding_t* user_bindings, int num_actions, const char* tool_name, gotcha_sigfree_binding_t *sigfree) { int i, not_found = 0, new_bindings_count = 0; tool_t *tool; @@ -300,7 +301,7 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bind debug_printf(2, "Moved current_generation to %u in gotcha_wrap\n", current_generation); debug_printf(2, "Creating internal binding data structures and adding binding to tool\n"); - binding_t *bindings = add_binding_to_tool(tool, user_bindings, num_actions); + binding_t *bindings = add_binding_to_tool(tool, user_bindings, sigfree, num_actions); if (!bindings) { error_printf("Failed to create bindings for tool %s\n", tool_name); return GOTCHA_INTERNAL; @@ -344,6 +345,26 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bind return GOTCHA_SUCCESS; } +GOTCHA_EXPORT enum gotcha_error_t gotcha_wrap(struct gotcha_binding_t* user_bindings, int num_actions, const char* tool_name) +{ + return internal_gotcha_wrap(user_bindings, num_actions, tool_name, NULL); +} + +enum gotcha_error_t gotcha_sigfree_wrap(struct gotcha_sigfree_binding_t *bindings, int num_actions, const char *tool_name) +{ + gotcha_binding_t *newbindings; + int i; + + void *mem = allocate_trampoline_memory(num_actions); + newbindings = (gotcha_binding_t *) gotcha_malloc(sizeof(gotcha_binding_t) * num_actions); + for (i = 0; i < num_actions; i++) { + newbindings[i].name = bindings[i].name; + newbindings[i].wrapper_pointer = create_thin_wrapper(newbindings + i, mem, i); + newbindings[i].function_handle = bindings[i].function_handle; + } + return internal_gotcha_wrap(newbindings, num_actions, tool_name, bindings); +} + static enum gotcha_error_t gotcha_configure_int(const char* tool_name, enum gotcha_config_key_t configuration_key , int value){ tool_t * tool = get_tool(tool_name); if(tool==NULL){ @@ -383,3 +404,7 @@ GOTCHA_EXPORT enum gotcha_error_t gotcha_get_priority(const char* tool_name, int GOTCHA_EXPORT void* gotcha_get_wrappee(gotcha_wrappee_handle_t handle){ return ((struct internal_binding_t*)handle)->wrappee_pointer; } + +GOTCHA_EXPORT const char *gotcha_get_wrappee_name(gotcha_wrappee_handle_t handle) { + return ((struct internal_binding_t*)handle)->user_binding->name; +} diff --git a/src/thin.c b/src/thin.c new file mode 100644 index 0000000..1a570b7 --- /dev/null +++ b/src/thin.c @@ -0,0 +1,223 @@ +/* +This file is part of GOTCHA. For copyright information see the COPYRIGHT +file in the top level directory, or at +https://github.com/LLNL/gotcha/blob/master/COPYRIGHT +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License (as published by the Free +Software Foundation) version 2.1 dated February 1999. This program is +distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the terms and conditions of the GNU Lesser General Public License +for more details. You should have received a copy of the GNU Lesser General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include "thin.h" +#include "libc_wrappers.h" +#include "tool.h" +#include "gotcha_utils.h" + +#define INITIAL_SAVESTATE_SIZE 128 +static __thread void *savestate = NULL; + +typedef struct { + void *stack_location; + void *retaddr; + void *opaque_handle; + void *unused; +} stacksave_entry_t; + +typedef struct { + unsigned long savesize; + unsigned long cursize; + void *unused; + void *unused2; +} stacksave_info_t; + + +/** + * grow_stackstate, push_stack_addr, and pop_stack_state manage a + * per-thread stack that tracks return addresses. When we enter a + * thin trampoline we override the original return address to point + * into the trampoline. After the trampoline finishes, we need to get + * the original return back into place. + * These stack routines manage the location where we save the return + * addresses. + **/ +static void grow_stackstate(unsigned long newsize) +{ + stacksave_info_t *oldstack = (stacksave_info_t *) savestate; + unsigned long oldsize; + stacksave_info_t *newstack; + + newstack = (stacksave_info_t *) gotcha_malloc(newsize * sizeof(stacksave_info_t)); + newstack->savesize = newsize; + savestate = (void *) newstack; + if (!oldstack) { + newstack->cursize = 1; + return; + } + + oldsize = oldstack->cursize; + memcpy(newstack, oldstack, oldsize * sizeof(stacksave_entry_t)); + gotcha_free(oldstack); + savestate = (void *) newstack; +} + +static void push_stack_addr(void **addr, void *opaque_handle) +{ + unsigned long cur; + stacksave_info_t *info = NULL; + stacksave_entry_t *entries; + if (!savestate) { + grow_stackstate(INITIAL_SAVESTATE_SIZE); + } + info = (stacksave_info_t *) savestate; + if (info->savesize == info->cursize) { + grow_stackstate(info->savesize * 2); + info = (stacksave_info_t *) savestate; + } + entries = (stacksave_entry_t *) savestate; + assert(info->cursize >= 1); + assert(info->cursize < info->savesize); + cur = info->cursize; + while ((cur != 1) && (entries[cur-1].stack_location <= (void *) addr)) + cur--; + entries[cur].stack_location = (void *) addr; + entries[cur].retaddr = *addr; + entries[cur].opaque_handle = opaque_handle; + entries[cur].unused = NULL; + info->cursize = cur+1; +} + +static void* pop_stack_state(void **addr, void **opaque_handle) +{ + stacksave_info_t *info = (stacksave_info_t *) savestate; + stacksave_entry_t *entries = (stacksave_entry_t *) savestate; + int cur = info->cursize-1; + + while ((cur != 0) && (entries[cur].stack_location != (void *) addr)) { + cur--; + } + assert(cur); + info->cursize = cur; + *opaque_handle = entries[cur].opaque_handle; + return entries[cur].retaddr; +} + +/** + * pre and post are infrastructure for the thin wrappers. Rather than + * have the assembly snippet call the user code directly it calls these + * routines. They setup datastructures and call onwards to the user wrappers. + **/ +static void* pre(gotcha_binding_t *binding, void **retaddr) +{ + internal_binding_t *int_binding = *((internal_binding_t **) (binding->function_handle)); + void *opaque_handle = NULL, *wrappee; + sigfree_pre_wrapper_t wrapper = int_binding->pre_wrapper; + if (wrapper) { + wrapper((gotcha_wrappee_handle_t *) int_binding, &opaque_handle); + } + push_stack_addr(retaddr, opaque_handle); + wrappee = gotcha_get_wrappee(*binding->function_handle); + debug_printf(3, "In pre thin wrapper for %s. Next wrappee at %p, " + "retaddr at %p\n", binding->name, wrappee, retaddr); + return wrappee; +} + +static void post(gotcha_binding_t *binding, void **retaddr) +{ + void *orig_retaddr; + void *opaque_handle; + internal_binding_t *int_binding = *((internal_binding_t **) (binding->function_handle)); + + orig_retaddr = pop_stack_state(retaddr, &opaque_handle); + sigfree_post_wrapper_t wrapper = int_binding->post_wrapper; + if (wrapper) { + wrapper((gotcha_wrappee_handle_t *) int_binding, opaque_handle); + } + *retaddr = orig_retaddr; + debug_printf(3, "In post thin wrapper for %s. Set retaddr back to %p\n", + binding->name, orig_retaddr); +} + +void *create_thin_wrapper(gotcha_binding_t *binding, void *tramp_memory, int binding_num) +{ + debug_printf(2, "Creating thin wrapper around %s\n", binding->name); + return create_trampoline(pre, post, binding, tramp_memory, binding_num); +} + +extern unsigned char snippet_start, snippet_end; + +void *allocate_trampoline_memory(int entries) +{ + void *mem; + size_t size; + size = (size_t) (&snippet_end - &snippet_start); + size *= entries; + mem = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (mem == MAP_FAILED) + return NULL; + return mem; +} + +void *create_trampoline(void *prewrapper, void *postwrapper, void *param, void *trampmem, int num) +{ + unsigned char *mem; + size_t size, i; + + size = (size_t) (&snippet_end - &snippet_start); + + mem = ((unsigned char *) trampmem) + (num * size); + memcpy(mem, &snippet_start, size); + + +#if defined(__x86_64__) + for (i = 0; i < size-8; i++) { + if (*((unsigned long *) (mem + i)) == 0x1111111111111111) + memcpy(mem + i, &prewrapper, 8); + if (*((unsigned long *) (mem + i)) == 0x2222222222222222) + memcpy(mem + i, ¶m, 8); + if (*((unsigned long *) (mem + i)) == 0x3333333333333333) + memcpy(mem + i, &postwrapper, 8); + } +#elif defined(__PPC64__) + unsigned char *paramc = (unsigned char *) ¶m; + unsigned char *prec = (unsigned char *) &prewrapper; + unsigned char *postc = (unsigned char *) &postwrapper; + for (i = 0; i < size-2; i++) { + if (*((unsigned short *) (mem + i)) == 0x1111) + memcpy(mem + i, paramc + 6, 2); + if (*((unsigned short *) (mem + i)) == 0x2222) + memcpy(mem + i, paramc + 4, 2); + if (*((unsigned short *) (mem + i)) == 0x3333) + memcpy(mem + i, paramc + 2, 2); + if (*((unsigned short *) (mem + i)) == 0x4444) + memcpy(mem + i, paramc, 2); + + if (*((unsigned short *) (mem + i)) == 0x5555) + memcpy(mem + i, prec + 6, 2); + if (*((unsigned short *) (mem + i)) == 0x6666) + memcpy(mem + i, prec + 4, 2); + if (*((unsigned short *) (mem + i)) == 0x7777) + memcpy(mem + i, prec + 2, 2); + if (*((unsigned short *) (mem + i)) == 0x8888) + memcpy(mem + i, prec, 2); + + if (*((unsigned short *) (mem + i)) == 0x9999) + memcpy(mem + i, postc + 6, 2); + if (*((unsigned short *) (mem + i)) == 0xaaaa) + memcpy(mem + i, postc + 4, 2); + if (*((unsigned short *) (mem + i)) == 0xbbbb) + memcpy(mem + i, postc + 2, 2); + if (*((unsigned short *) (mem + i)) == 0xcccc) + memcpy(mem + i, postc, 2); + } +#endif + + debug_printf(2, "Created thin trampoline at %p to +%lu with parameter at %p\n", + mem, (unsigned long) size, param); + return mem; +} diff --git a/src/thin.h b/src/thin.h new file mode 100644 index 0000000..1d3d0f4 --- /dev/null +++ b/src/thin.h @@ -0,0 +1,26 @@ +/* +This file is part of GOTCHA. For copyright information see the COPYRIGHT +file in the top level directory, or at +https://github.com/LLNL/gotcha/blob/master/COPYRIGHT +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License (as published by the Free +Software Foundation) version 2.1 dated February 1999. This program is +distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the terms and conditions of the GNU Lesser General Public License +for more details. You should have received a copy of the GNU Lesser General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#if !defined(THIN_H_) +#define THIN_H_ + +#include "gotcha/gotcha.h" +#include "tool.h" + +void *create_trampoline(void *prewrapper, void *postwrapper, void *param, void *tramp_memory, int num); +void *create_thin_wrapper(gotcha_binding_t *binding, void *tramp_memory, int binding_num); +void *allocate_trampoline_memory(int num_actions); + +#endif diff --git a/src/thin_ppc.s b/src/thin_ppc.s new file mode 100644 index 0000000..1f0d24f --- /dev/null +++ b/src/thin_ppc.s @@ -0,0 +1,85 @@ + .text + .globl snippet_start + .globl snippet_end + +snippet_start: + mflr %r0 +# std %r0,16(%r1) +# std %r31,-8(%r1) + stdu %r1,-120(%r1) + std %r12,96(%r1) + std %r0,88(%r1) + std %r3,80(%r1) + std %r4,72(%r1) + std %r5,64(%r1) + std %r6,56(%r1) + std %r7,48(%r1) + std %r8,40(%r1) + std %r9,32(%r1) + std %r10,24(%r1) + lis %r3,0x1111 + ori %r3,%r3,0x2222 + sldi %r3,%r3,32 + oris %r3,%r3,0x3333 + ori %r3,%r3,0x4444 + addi %r4,%r1,88 + lis %r12,0x5555 + ori %r12,%r12,0x6666 + sldi %r12,%r12,32 + oris %r12,%r12,0x7777 + ori %r12,%r12,0x8888 + mtctr %r12 + bctrl + mtctr %r3 + mr %r12,%r3 + ld %r0,88(%r1) + ld %r3,80(%r1) + ld %r4,72(%r1) + ld %r5,64(%r1) + ld %r6,56(%r1) + ld %r7,48(%r1) + ld %r8,40(%r1) + ld %r9,32(%r1) + ld %r10,24(%r1) + addi %r1,%r1,120 + bctrl + stdu %r1,-120(%r1) + std %r12,96(%r1) + std %r0,88(%r1) + std %r3,80(%r1) + std %r4,72(%r1) + std %r5,64(%r1) + std %r6,56(%r1) + std %r7,48(%r1) + std %r8,40(%r1) + std %r9,32(%r1) + std %r10,24(%r1) + lis %r3,0x1111 + ori %r3,%r3,0x2222 + sldi %r3,%r3,32 + oris %r3,%r3,0x3333 + ori %r3,%r3,0x4444 + addi %r4,%r1,88 + lis %r12,0x9999 + ori %r12,%r12,0xaaaa + sldi %r12,%r12,32 + oris %r12,%r12,0xbbbb + ori %r12,%r12,0xcccc + mtctr %r12 + bctrl + ld %r12,96(%r1) + ld %r0,88(%r1) + ld %r3,80(%r1) + ld %r4,72(%r1) + ld %r5,64(%r1) + ld %r6,56(%r1) + ld %r7,48(%r1) + ld %r8,40(%r1) + ld %r9,32(%r1) + ld %r10,24(%r1) + addi %r1,%r1,120 + mtlr %r0 + blr +snippet_end: + nop + diff --git a/src/thin_x86.s b/src/thin_x86.s new file mode 100644 index 0000000..e013a6d --- /dev/null +++ b/src/thin_x86.s @@ -0,0 +1,63 @@ + .text + .globl snippet_start + .globl snippet_end + +snippet_start: + pushq %rsi + lea 8(%rsp), %rsi #Save the location of the retaddr into the a parameter + pushq %rax #Save all parameter registers, which are live across a call + pushq %rdi + pushq %rcx + pushq %rdx + pushq %r8 + pushq %r9 + lea -64(%rsp), %rsp + vmovq %xmm0, 56(%rsp) + vmovq %xmm1, 48(%rsp) + vmovq %xmm2, 40(%rsp) + vmovq %xmm3, 32(%rsp) + vmovq %xmm4, 24(%rsp) + vmovq %xmm5, 16(%rsp) + vmovq %xmm6, 8(%rsp) + vmovq %xmm7, (%rsp) + movq $0x2222222222222222,%rdi #Load the binding val into a parameter + movq $0x1111111111111111,%rax + call *%rax #Call 'pre()' with the binding and location of original ret + movq %rax,%r11 #'pre' returns the wrappee in rax, save it + vmovq 56(%rsp), %xmm0 + vmovq 48(%rsp), %xmm1 + vmovq 40(%rsp), %xmm2 + vmovq 32(%rsp), %xmm3 + vmovq 24(%rsp), %xmm4 + vmovq 16(%rsp), %xmm5 + vmovq 8(%rsp), %xmm6 + vmovq 0(%rsp), %xmm7 + lea 64(%rsp), %rsp + popq %r9 #Restore the parameter registers + popq %r8 + popq %rdx + popq %rcx + popq %rdi + popq %rax + popq %rsi + addq $8,%rsp #Overwrite original retaddr with one that comes back to this tramp + callq *%r11 #Call the wrappee (returned from pre) + subq $8,%rsp + movq %rsp,%rsi #Save the location of the retaddr into a parameter + movq $0x2222222222222222,%rdi #Save the binding val into a parameter + pushq %rax #Save the registers used for return values + pushq %rdx + lea -16(%rsp), %rsp + vmovq %xmm0, 8(%rsp) + vmovq %xmm1, 0(%rsp) + movq $0x3333333333333333,%rax + callq *%rax #Call post, which will put the original return address back + vmovq 8(%rsp), %xmm0 + vmovq 0(%rsp), %xmm1 + lea 16(%rsp), %rsp + popq %rdx #Restore the regsiters used for return values + popq %rax + ret +snippet_end: + nop + diff --git a/src/tool.c b/src/tool.c index 1f43b5e..a04728f 100644 --- a/src/tool.c +++ b/src/tool.c @@ -94,17 +94,19 @@ tool_t *get_tool(const char *tool_name) return NULL; } -binding_t *add_binding_to_tool(tool_t *tool, struct gotcha_binding_t *user_binding, int user_binding_size) +binding_t *add_binding_to_tool(tool_t *tool, struct gotcha_binding_t *user_binding, gotcha_sigfree_binding_t *sigfree, int user_binding_size) { binding_t *newbinding; int result, i; newbinding = (binding_t *) gotcha_malloc(sizeof(binding_t)); newbinding->tool = tool; - struct internal_binding_t* internal_bindings = (struct internal_binding_t*)gotcha_malloc(sizeof(struct internal_binding_t)*user_binding_size); + struct internal_binding_t* internal_bindings = (struct internal_binding_t*)gotcha_malloc(sizeof(struct internal_binding_t) * user_binding_size); for(i=0;iinternal_bindings = internal_bindings; newbinding->internal_bindings_size = user_binding_size; diff --git a/src/tool.h b/src/tool.h index 3fc1dae..e1db192 100644 --- a/src/tool.h +++ b/src/tool.h @@ -80,12 +80,14 @@ typedef struct tool_t { struct tool_t * parent_tool; } tool_t; -struct internal_binding_t { +typedef struct internal_binding_t { struct binding_t* associated_binding_table; struct gotcha_binding_t* user_binding; struct internal_binding_t* next_binding; + sigfree_pre_wrapper_t pre_wrapper; + sigfree_post_wrapper_t post_wrapper; void* wrappee_pointer; -}; +} internal_binding_t; tool_t *create_tool(const char *tool_name); tool_t *get_tool(const char *tool_name); @@ -94,7 +96,7 @@ void reorder_tool(tool_t* new_tool); void remove_tool_from_list(struct tool_t* target); void print_tools(); -binding_t *add_binding_to_tool(tool_t *tool, struct gotcha_binding_t *user_binding, int user_binding_size); +binding_t *add_binding_to_tool(tool_t *tool, struct gotcha_binding_t *user_binding, gotcha_sigfree_binding_t *sigfree_bindings, int user_binding_size); binding_t *get_bindings(); binding_t *get_tool_bindings(tool_t *tool); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 32a524c..01963bc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(priority) add_subdirectory(multi_agent_dlopen) add_subdirectory(ppc_stress_multi_module) add_subdirectory(wrap_main) +add_subdirectory(thin) if(COMPILER_SUPPORTS_CXX11) add_subdirectory(hammer) endif() diff --git a/test/thin/CMakeLists.txt b/test/thin/CMakeLists.txt new file mode 100644 index 0000000..18154b1 --- /dev/null +++ b/test/thin/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(thinwrappee SHARED wrappees.c) +add_executable(thinwrappers wrappers.c) +target_link_libraries(thinwrappers thinwrappee gotcha) +gotcha_add_test(thin thinwrappers) diff --git a/test/thin/wrappees.c b/test/thin/wrappees.c new file mode 100644 index 0000000..ca17fa8 --- /dev/null +++ b/test/thin/wrappees.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include "wrappees.h" + +#define STR2(X) #X +#define STR(X) STR2(X) + +int many_params(int one, double two, float three, char four, char *five, long six, void *seven, short eight, struct nine_t nine, int *ten, long eleven_twelve[2]) +{ +#define CHECK(comparison, val, print_str) do { \ + if (comparison) { \ + fprintf(stderr, "many_params error: " STR(val) \ + " has unexpected value " print_str "\n", val); \ + return -1; \ + } \ + } while (0) + + CHECK(one != 1, one, "%d"); + CHECK(two <= 1.9l || two >= 2.1l, two, "%lf"); + CHECK(three <= 2.9 || three >= 3.1, three, "%f"); + CHECK(four != 4, four, "%uc"); + CHECK(strcmp(five, "five") != 0, five, "%s"); + CHECK(six != 6, six, "%ld"); + CHECK(seven != (void*) 0x7, seven, "%p"); + CHECK(eight != 8, eight, "%hd"); + CHECK(nine.nine != 9, nine.nine, "%lu"); + CHECK(*ten != 10, *ten, "%d"); + CHECK(eleven_twelve[0] != 11, eleven_twelve[0], "%ld"); + CHECK(eleven_twelve[1] != 12, eleven_twelve[1], "%ld"); + return 0; +} + +int bigparam(struct bigparam_t bp) +{ + int i; + for (i = 0 ; i < 1024; i++) { + if (bp.buffer[i] != i) { + fprintf(stderr, "Error - bigparam buffer[%d] was %d, expected %d\n", + i, (int) bp.buffer[i], i); + return -1; + } + } + return 0; +} + +volatile int zero = 0; + +int recurse(int num, call_recurse_t cr) +{ + if (num == 0) + return 0; + int ret = cr(num - 1); + if (zero != 0) { + fprintf(stderr, "Error, zero was not zero. What?"); + exit(-1); + } + return ret; +} + +double fp_func(double one, double two, double three, double four, + double five, double six, double seven, double eight, + double nine, double ten) +{ + return one + two + three + four + five + six + seven + eight + nine + ten; +} diff --git a/test/thin/wrappees.h b/test/thin/wrappees.h new file mode 100644 index 0000000..154b79c --- /dev/null +++ b/test/thin/wrappees.h @@ -0,0 +1,24 @@ +#if !defined(WRAPPEES_H_) +#define WRAPPEES_H_ + +struct nine_t { + long nine; + char unused; +}; + +struct bigparam_t { + int buffer[1024]; +}; + +int many_params(int one, double two, float three, char four, char *five, long six, void *seven, short eight, struct nine_t nine, int *ten, long eleven_twelve[2]); + +int bigparam(struct bigparam_t bp); + +double fp_func(double one, double two, double three, double four, + double five, double six, double seven, double eight, + double nine, double ten); + +typedef int (*call_recurse_t)(int); +int recurse(int num, call_recurse_t cr); + +#endif diff --git a/test/thin/wrappers.c b/test/thin/wrappers.c new file mode 100644 index 0000000..5ae3c81 --- /dev/null +++ b/test/thin/wrappers.c @@ -0,0 +1,127 @@ +#include +#include +#include "wrappees.h" +#include "gotcha/gotcha.h" + +int call_recurse(int num) +{ + int ret = recurse(num, call_recurse); + if (ret != 0) { + fprintf(stderr, "Error, return value of recurse was %d, not zero", ret); + exit(-1); + } + return 0; +} + +void call_big_param() +{ + struct bigparam_t bp; + int i, ret; + for (i = 0; i < 1024; i++) { + bp.buffer[i] = i; + } + ret = bigparam(bp); + if (ret != 0) { + fprintf(stderr, "Error - bigparam returned %d, expected 0\n", ret); + exit(-1); + } +} + +void call_many_params() +{ + long eleven_twelve[2]; + struct nine_t nine; + int ret; + int ten; + + nine.nine = 9; + ten = 10; + eleven_twelve[0] = 11; + eleven_twelve[1] = 12; + + ret = many_params(1, 2.0f, 3.0, 4, "five", 6, (void*) 7, 8, + nine, &ten, eleven_twelve); + if (ret != 0) { + fprintf(stderr, "error - many_params test returned %d\n", ret); + exit(-1); + } +} + +void call_fp_func() +{ + double val = fp_func(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); + if (val <= 44.9 || val >= 55.1) { + fprintf(stderr, "Incorrect return value %lf of fp_func, expected 55.0\n", val); + exit(-1); + } +} + +static int pre_called = 0; +static int post_called = 0; + +void pretramp(gotcha_wrappee_handle_t handle, void **opaque) +{ + printf("pre-call for function %s\n", gotcha_get_wrappee_name(handle)); + pre_called++; + *opaque = (void *) 0x4; +} + +void posttramp(gotcha_wrappee_handle_t handle, void *opaque) +{ + printf("post-call for function %s\n", gotcha_get_wrappee_name(handle)); + post_called++; + if (opaque != (void *) 0x4) { + fprintf(stderr, "Error, did not properly pass-through opaque value\n"); + exit(-1); + } +} + +static gotcha_wrappee_handle_t many_params_handle; +static gotcha_wrappee_handle_t bigparam_handle; +static gotcha_wrappee_handle_t recurse_handle; +static gotcha_wrappee_handle_t fp_handle; + +struct gotcha_sigfree_binding_t bindings[] = { + { "many_params", pretramp, posttramp, &many_params_handle }, + { "bigparam", pretramp, posttramp, &bigparam_handle }, + { "recurse", pretramp, posttramp, &recurse_handle }, + { "fp_func", pretramp, posttramp, &fp_handle }, + { NULL, NULL, NULL, NULL } +}; + + +int main() +{ + call_many_params(); + call_big_param(); + call_recurse(7); + call_fp_func(); + + if (pre_called != 0) { + fprintf(stderr, "Expected pre_called (%d) to equal 0\n", pre_called); + return -1; + } + if (post_called != 0) { + fprintf(stderr, "Expected post_called (%d) to equal 0\n", post_called); + return -1; + } + + gotcha_sigfree_wrap(bindings, 4, "sigfree_test"); + + call_many_params(); + call_big_param(); + call_recurse(7); + call_fp_func(); + + int correct_val = 11; + if (pre_called != correct_val) { + fprintf(stderr, "Expected pre_called (%d) to equal %d\n", pre_called, correct_val); + return -1; + } + if (post_called != correct_val) { + fprintf(stderr, "Expected post_called (%d) to equal %d\n", post_called, correct_val); + return -1; + } + + return 0; +} diff --git a/test/unit/gotcha_unit_tests.c b/test/unit/gotcha_unit_tests.c index d8ac2d3..44029a3 100644 --- a/test/unit/gotcha_unit_tests.c +++ b/test/unit/gotcha_unit_tests.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include #include "gotcha/gotcha.h" #include "testing_lib.h"