Home | History | Annotate | Download | only in tests
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2002-2003 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) hpl.hp.com>
      4 
      5 This file is part of libunwind.
      6 
      7 Permission is hereby granted, free of charge, to any person obtaining
      8 a copy of this software and associated documentation files (the
      9 "Software"), to deal in the Software without restriction, including
     10 without limitation the rights to use, copy, modify, merge, publish,
     11 distribute, sublicense, and/or sell copies of the Software, and to
     12 permit persons to whom the Software is furnished to do so, subject to
     13 the following conditions:
     14 
     15 The above copyright notice and this permission notice shall be
     16 included in all copies or substantial portions of the Software.
     17 
     18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     25 
     26 /* This file tests dynamic code-generation via function-cloning.  */
     27 
     28 #include "flush-cache.h"
     29 
     30 #include "compiler.h"
     31 
     32 #include <libunwind.h>
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <unistd.h>
     37 #include <signal.h>
     38 #include <sys/mman.h>
     39 
     40 #if UNW_TARGET_ARM
     41 #define MAX_FUNC_SIZE   96 	/* FIXME: arch/compiler dependent */
     42 #else
     43 #define MAX_FUNC_SIZE	2048	/* max. size of cloned function */
     44 #endif
     45 
     46 #define panic(args...)				\
     47 	{ fprintf (stderr, args); exit (-1); }
     48 
     49 typedef void (*template_t) (int, void (*)(),
     50 			    int (*)(const char *, ...), const char *,
     51 			    const char **);
     52 
     53 int verbose;
     54 
     55 static const char *strarr[] =
     56   {
     57     "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", NULL
     58   };
     59 
     60 #ifdef __ia64__
     61 struct fdesc
     62   {
     63     long code;
     64     long gp;
     65   };
     66 # define get_fdesc(fdesc,func)	(fdesc = *(struct fdesc *) &(func))
     67 # define get_funcp(fdesc)	((template_t) &(fdesc))
     68 # define get_gp(fdesc)		((fdesc).gp)
     69 #elif __arm__
     70 struct fdesc
     71   {
     72     long code;
     73     long is_thumb;
     74   };
     75 /* Workaround GCC bug: https://bugs.launchpad.net/gcc-linaro/+bug/721531 */
     76 # define get_fdesc(fdesc,func)  ({long tmp = (long) &(func); \
     77                                   (fdesc).code = (long) &(func) & ~0x1; \
     78                                   (fdesc).is_thumb = tmp & 0x1;})
     79 /*# define get_fdesc(fdesc,func)  ({(fdesc).code = (long) &(func) & ~0x1; \
     80                                   (fdesc).is_thumb = (long) &(func) & 0x1;})*/
     81 # define get_funcp(fdesc)       ((template_t) ((fdesc).code | (fdesc).is_thumb))
     82 # define get_gp(fdesc)          (0)
     83 #else
     84 struct fdesc
     85   {
     86     long code;
     87   };
     88 # define get_fdesc(fdesc,func)	(fdesc.code = (long) &(func))
     89 # define get_funcp(fdesc)	((template_t) (fdesc).code)
     90 # define get_gp(fdesc)		(0)
     91 #endif
     92 
     93 void
     94 template (int i, template_t self,
     95 	  int (*printer)(const char *, ...), const char *fmt, const char **arr)
     96 {
     97   (*printer) (fmt, arr[11 - i][0], arr[11 - i] + 1);
     98   if (i > 0)
     99     (*self) (i - 1, self, printer, fmt, arr);
    100 }
    101 
    102 static void
    103 sighandler (int signal)
    104 {
    105   unw_cursor_t cursor;
    106   char name[128], off[32];
    107   unw_word_t ip, offset;
    108   unw_context_t uc;
    109   int count;
    110 
    111   if (verbose)
    112     printf ("caught signal %d\n", signal);
    113 
    114   unw_getcontext (&uc);
    115   unw_init_local (&cursor, &uc);
    116 
    117   count = 0;
    118   while (!unw_is_signal_frame (&cursor))
    119     {
    120       if (unw_step (&cursor) < 0)
    121 	panic ("failed to find signal frame!\n");
    122 
    123       if (count++ > 20)
    124 	{
    125 	  panic ("Too many steps to the signal frame (%d)\n", count);
    126 	  break;
    127 	}
    128     }
    129   unw_step (&cursor);
    130 
    131   count = 0;
    132   do
    133     {
    134       unw_get_reg (&cursor, UNW_REG_IP, &ip);
    135       name[0] = '\0';
    136       off[0] = '\0';
    137       if (unw_get_proc_name (&cursor, name, sizeof (name), &offset) == 0
    138 	  && offset > 0)
    139 	snprintf (off, sizeof (off), "+0x%lx", (long) offset);
    140       if (verbose)
    141 	printf ("ip = %lx <%s%s>\n", (long) ip, name, off);
    142       ++count;
    143 
    144       if (count > 20)
    145 	{
    146 	  panic ("Too many steps (%d)\n", count);
    147 	  break;
    148 	}
    149 
    150     }
    151   while (unw_step (&cursor) > 0);
    152 
    153   if (count != 13)
    154     panic ("FAILURE: expected 13, not %d frames below signal frame\n", count);
    155 
    156   if (verbose)
    157     printf ("SUCCESS\n");
    158   exit (0);
    159 }
    160 
    161 int
    162 dev_null (const char *format UNUSED, ...)
    163 {
    164   return 0;
    165 }
    166 
    167 int
    168 main (int argc, char *argv[] UNUSED)
    169 {
    170   unw_dyn_region_info_t *region;
    171   unw_dyn_info_t di;
    172   struct fdesc fdesc;
    173   template_t funcp;
    174   void *mem;
    175 
    176   if (argc > 1)
    177     ++verbose;
    178 
    179   mem = malloc (getpagesize ());
    180 
    181   get_fdesc (fdesc, template);
    182 
    183   if (verbose)
    184     printf ("old code @ %p, new code @ %p\n", (void *) fdesc.code, mem);
    185 
    186   memcpy (mem, (void *) fdesc.code, MAX_FUNC_SIZE);
    187   mprotect ((void *) ((long) mem & ~(getpagesize () - 1)),
    188 	    2*getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC);
    189 
    190   flush_cache (mem, MAX_FUNC_SIZE);
    191 
    192   signal (SIGSEGV, sighandler);
    193 
    194   /* register the new function: */
    195   region = alloca (_U_dyn_region_info_size (2));
    196   region->next = NULL;
    197   region->insn_count = 3 * (MAX_FUNC_SIZE / 16);
    198   region->op_count = 2;
    199   _U_dyn_op_alias (&region->op[0], 0, -1, fdesc.code);
    200   _U_dyn_op_stop (&region->op[1]);
    201 
    202   memset (&di, 0, sizeof (di));
    203   di.start_ip = (long) mem;
    204   di.end_ip = (long) mem + 16*region->insn_count/3;
    205   di.gp = get_gp (fdesc);
    206   di.format = UNW_INFO_FORMAT_DYNAMIC;
    207   di.u.pi.name_ptr = (unw_word_t) "copy_of_template";
    208   di.u.pi.regions = region;
    209 
    210   _U_dyn_register (&di);
    211 
    212   /* call new function: */
    213   fdesc.code = (long) mem;
    214   funcp = get_funcp (fdesc);
    215 
    216   if (verbose)
    217     (*funcp) (10, funcp, printf, "iteration %c%s\n", strarr);
    218   else
    219     (*funcp) (10, funcp, dev_null, "iteration %c%s\n", strarr);
    220 
    221   _U_dyn_cancel (&di);
    222   return -1;
    223 }
    224