Gtest-dyn1.c revision 39b83981594d2e49a83e4aaedcab8395c01ddd03
1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2002-2003 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH 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 <libunwind.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <signal.h>
34#include <sys/mman.h>
35
36#if UNW_TARGET_ARM
37#define MAX_FUNC_SIZE   96 	/* FIXME: arch/compiler dependent */
38#else
39#define MAX_FUNC_SIZE	2048	/* max. size of cloned function */
40#endif
41
42#define panic(args...)				\
43	{ fprintf (stderr, args); exit (-1); }
44
45typedef void (*template_t) (int, void (*)(),
46			    int (*)(const char *, ...), const char *,
47			    const char **);
48
49int verbose;
50
51static const char *strarr[] =
52  {
53    "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", NULL
54  };
55
56#ifdef __ia64__
57struct fdesc
58  {
59    long code;
60    long gp;
61  };
62# define get_fdesc(fdesc,func)	(fdesc = *(struct fdesc *) &(func))
63# define get_funcp(fdesc)	((template_t) &(fdesc))
64# define get_gp(fdesc)		((fdesc).gp)
65#elif __arm__
66struct fdesc
67  {
68    long code;
69    long is_thumb;
70  };
71/* Workaround GCC bug: https://bugs.launchpad.net/gcc-linaro/+bug/721531 */
72# define get_fdesc(fdesc,func)  ({long tmp = (long) &(func); \
73                                  (fdesc).code = (long) &(func) & ~0x1; \
74                                  (fdesc).is_thumb = tmp & 0x1;})
75/*# define get_fdesc(fdesc,func)  ({(fdesc).code = (long) &(func) & ~0x1; \
76                                  (fdesc).is_thumb = (long) &(func) & 0x1;})*/
77# define get_funcp(fdesc)       ((template_t) ((fdesc).code | (fdesc).is_thumb))
78# define get_gp(fdesc)          (0)
79#else
80struct fdesc
81  {
82    long code;
83  };
84# define get_fdesc(fdesc,func)	(fdesc.code = (long) &(func))
85# define get_funcp(fdesc)	((template_t) (fdesc).code)
86# define get_gp(fdesc)		(0)
87#endif
88
89#ifndef __GNUC__
90extern void flush_cache (void *addr, size_t len);
91#endif
92
93void
94template (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
102static void
103sighandler (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
161int
162dev_null (const char *format __attribute__((unused)), ...)
163{
164  return 0;
165}
166
167int
168main (int argc, char *argv[] __attribute__((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#ifdef __GNUC__
191  __builtin___clear_cache(mem, mem + MAX_FUNC_SIZE);
192#else
193  flush_cache (mem, MAX_FUNC_SIZE);
194#endif
195
196  signal (SIGSEGV, sighandler);
197
198  /* register the new function: */
199  region = alloca (_U_dyn_region_info_size (2));
200  region->next = NULL;
201  region->insn_count = 3 * (MAX_FUNC_SIZE / 16);
202  region->op_count = 2;
203  _U_dyn_op_alias (&region->op[0], 0, -1, fdesc.code);
204  _U_dyn_op_stop (&region->op[1]);
205
206  memset (&di, 0, sizeof (di));
207  di.start_ip = (long) mem;
208  di.end_ip = (long) mem + 16*region->insn_count/3;
209  di.gp = get_gp (fdesc);
210  di.format = UNW_INFO_FORMAT_DYNAMIC;
211  di.u.pi.name_ptr = (unw_word_t) "copy_of_template";
212  di.u.pi.regions = region;
213
214  _U_dyn_register (&di);
215
216  /* call new function: */
217  fdesc.code = (long) mem;
218  funcp = get_funcp (fdesc);
219
220  if (verbose)
221    (*funcp) (10, funcp, printf, "iteration %c%s\n", strarr);
222  else
223    (*funcp) (10, funcp, dev_null, "iteration %c%s\n", strarr);
224
225  _U_dyn_cancel (&di);
226  return -1;
227}
228