HPCToolkit
eh-frames.cpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*-
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 // This file uses libdwarf directly to compute the exception handler
48 // (eh) frame addresses and adds them to the list of function addrs.
49 // Dyninst and libdw don't support this, so we use libdwarf directly.
50 //
51 // Note: new dyninst uses libdw, and libdwarf and libdw don't play
52 // together well (overlap of function names, header files). So, we
53 // move this to its own file, compile it separately and access
54 // libdwarf via dlopen() and dlsym().
55 //
56 // Note: in order to isolate libdwarf as much as possible, for each
57 // file, we re-dlopen libdwarf.so, save the addrs in a set, dlclose
58 // libdwarf, and only then return the set of addrs (via a callback
59 // function). This way, nothing in the rest of fnbounds runs while
60 // libdwarf is open. We probably can relax this to open libdwarf.so
61 // just once.
62 
63 //***************************************************************************
64 
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <dlfcn.h>
68 #include <err.h>
69 #include <errno.h>
70 #include <fcntl.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <unistd.h>
74 
75 #include <boost/tokenizer.hpp>
76 #include <libdwarf.h>
77 
78 #include <include/hpctoolkit-config.h>
79 #include "eh-frames.h"
80 
81 #include <set>
82 #include <string>
83 
84 using namespace std;
85 
86 typedef set <void *> AddrSet;
87 
88 static void dwarf_frame_info_help(int, AddrSet &);
89 
90 #define EXT_LIBS_DIR_STR "HPCTOOLKIT_EXT_LIBS_DIR"
91 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
92 
93 #define LIBDWARF_NAME "libdwarf.so"
94 #define DLOPEN_OPTS (RTLD_NOW | RTLD_LOCAL)
95 
96 #define KEEP_LIBDWARF_OPEN 0
97 
98 //----------------------------------------------------------------------
99 
100 #ifdef DYNINST_USE_LIBDW
101 //
102 // get libdwarf.so functions via dlopen() and dlsym()
103 //
104 
105 typedef int dwarf_init_fcn_t
106  (int, Dwarf_Unsigned, Dwarf_Handler, Dwarf_Ptr, Dwarf_Debug *, Dwarf_Error *);
107 
108 typedef int dwarf_finish_fcn_t (Dwarf_Debug, Dwarf_Error *);
109 
110 typedef void dwarf_dealloc_fcn_t (Dwarf_Debug, void *, Dwarf_Unsigned);
111 
112 typedef int dwarf_get_fde_list_eh_fcn_t
113  (Dwarf_Debug, Dwarf_Cie **, Dwarf_Signed *, Dwarf_Fde **, Dwarf_Signed *, Dwarf_Error *);
114 
115 typedef int dwarf_get_fde_range_fcn_t
116  (Dwarf_Fde, Dwarf_Addr *, Dwarf_Unsigned *, Dwarf_Ptr *, Dwarf_Unsigned *,
117  Dwarf_Off *, Dwarf_Signed *, Dwarf_Off *, Dwarf_Error *);
118 
119 typedef void dwarf_fde_cie_list_dealloc_fcn_t
120  (Dwarf_Debug, Dwarf_Cie *, Dwarf_Signed, Dwarf_Fde *, Dwarf_Signed);
121 
122 static dwarf_init_fcn_t * real_dwarf_init = NULL;
123 static dwarf_finish_fcn_t * real_dwarf_finish = NULL;
124 static dwarf_dealloc_fcn_t * real_dwarf_dealloc = NULL;
125 static dwarf_get_fde_list_eh_fcn_t * real_dwarf_get_fde_list_eh = NULL;
126 static dwarf_get_fde_range_fcn_t * real_dwarf_get_fde_range = NULL;
127 static dwarf_fde_cie_list_dealloc_fcn_t * real_dwarf_fde_cie_list_dealloc = NULL;
128 
129 #define DWARF_INIT (* real_dwarf_init)
130 #define DWARF_FINISH (* real_dwarf_finish)
131 #define DWARF_DEALLOC (* real_dwarf_dealloc)
132 #define DWARF_GET_FDE_LIST_EH (* real_dwarf_get_fde_list_eh)
133 #define DWARF_GET_FDE_RANGE (* real_dwarf_get_fde_range)
134 #define DWARF_FDE_CIE_LIST_DEALLOC (* real_dwarf_fde_cie_list_dealloc)
135 
136 static string library_file;
137 static void * libdwarf_handle = NULL;
138 
139 static int found_library = 0;
140 static int dlopen_done = 0;
141 static int dlsym_done = 0;
142 
143 #else
144 //
145 // direct function calls, no dlopen
146 //
147 
148 #define DWARF_INIT dwarf_init
149 #define DWARF_FINISH dwarf_finish
150 #define DWARF_DEALLOC dwarf_dealloc
151 #define DWARF_GET_FDE_LIST_EH dwarf_get_fde_list_eh
152 #define DWARF_GET_FDE_RANGE dwarf_get_fde_range
153 #define DWARF_FDE_CIE_LIST_DEALLOC dwarf_fde_cie_list_dealloc
154 
155 #endif
156 
157 //----------------------------------------------------------------------
158 
159 // In the libdw case, we use dlopen() and dlsym() to access the
160 // libdwarf functions.
161 //
162 // Returns: 0 on success, 1 on failure.
163 //
164 static int
166 {
167 #ifdef DYNINST_USE_LIBDW
168 
169  // step 1 -- find libdwarf.so file, this only happens once.
170  if (! found_library) {
171  //
172  // try HPCTOOLKIT_EXT_LIBS_DIR first, this is set in the launch
173  // script.
174  //
175  char *str = getenv(EXT_LIBS_DIR_STR);
176 
177  if (str != NULL) {
178  library_file = string(str) + "/" + LIBDWARF_NAME;
179  libdwarf_handle = dlopen(library_file.c_str(), DLOPEN_OPTS);
180 
181  if (libdwarf_handle != NULL) {
182  found_library = 1;
183  dlopen_done = 1;
184  }
185  else {
186  warnx("unable to open %s in %s = %s", LIBDWARF_NAME, EXT_LIBS_DIR_STR, str);
187  }
188  }
189  }
190 
191  if (! found_library) {
192  //
193  // try LD_LIBRARY_PATH, in case running the binary directly. as
194  // long as make install copied libdwarf.so to ext-libs, this
195  // method pretty much has to work.
196  //
197  // boost::tokenizer and algorithm::split generate a lot of code
198  // bloat. we could rewrite this with string::find_first_of().
199  //
200  char *str = getenv(LD_LIBRARY_PATH);
201 
202  if (str != NULL) {
203  string path = str;
204  boost::char_separator <char> sep (":;", "", boost::drop_empty_tokens);
205  boost::tokenizer <boost::char_separator <char>> token (path, sep);
206 
207  for (auto it = token.begin(); it != token.end(); ++it) {
208  library_file = string(*it) + "/" + LIBDWARF_NAME;
209  libdwarf_handle = dlopen(library_file.c_str(), DLOPEN_OPTS);
210 
211  if (libdwarf_handle != NULL) {
212  found_library = 1;
213  dlopen_done = 1;
214  break;
215  }
216  }
217  if (! found_library) {
218  warnx("unable to find %s in %s = %s", LIBDWARF_NAME, LD_LIBRARY_PATH, str);
219  }
220  }
221  }
222 
223  if (! found_library) {
224  return 1;
225  }
226 
227  // step 2 -- dlopen()
228  if (! dlopen_done) {
229  libdwarf_handle = dlopen(library_file.c_str(), DLOPEN_OPTS);
230 
231  if (libdwarf_handle == NULL) {
232  warnx("unable to open: %s", library_file.c_str());
233  return 1;
234  }
235 
236  dlopen_done = 1;
237  }
238 
239  // step 3 -- dlsym() and check that the functions exist
240  if (! dlsym_done) {
241  real_dwarf_init = (dwarf_init_fcn_t *) dlsym(libdwarf_handle, "dwarf_init");
242  real_dwarf_finish = (dwarf_finish_fcn_t *) dlsym(libdwarf_handle, "dwarf_finish");
243  real_dwarf_dealloc = (dwarf_dealloc_fcn_t *) dlsym(libdwarf_handle, "dwarf_dealloc");
244  real_dwarf_get_fde_list_eh = (dwarf_get_fde_list_eh_fcn_t *)
245  dlsym(libdwarf_handle, "dwarf_get_fde_list_eh");
246  real_dwarf_get_fde_range = (dwarf_get_fde_range_fcn_t *)
247  dlsym(libdwarf_handle, "dwarf_get_fde_range");
248  real_dwarf_fde_cie_list_dealloc = (dwarf_fde_cie_list_dealloc_fcn_t *)
249  dlsym(libdwarf_handle, "dwarf_fde_cie_list_dealloc");
250 
251  if (real_dwarf_init == NULL
252  || real_dwarf_finish == NULL
253  || real_dwarf_dealloc == NULL
254  || real_dwarf_get_fde_list_eh == NULL
255  || real_dwarf_get_fde_range == NULL
256  || real_dwarf_fde_cie_list_dealloc == NULL)
257  {
258  warnx("dlsym(%s) failed", LIBDWARF_NAME);
259  return 1;
260  }
261 
262  dlsym_done = 1;
263  }
264 #endif
265 
266  return 0;
267 }
268 
269 // In the libdw case, dlclose() libdwarf after each file (for now).
270 //
271 static void
273 {
274 #if defined(DYNINST_USE_LIBDW) && ! KEEP_LIBDWARF_OPEN
275 
276  dlclose(libdwarf_handle);
277  real_dwarf_init = NULL;
278  real_dwarf_finish = NULL;
279 
280  dlopen_done = 0;
281  dlsym_done = 0;
282 
283 #endif
284 }
285 
286 //----------------------------------------------------------------------
287 
288 // Entry point from main.cpp. We return the list of addresses via the
289 // add_frame_addr() callback function.
290 //
291 void
293 {
294  static int open_failed = 0;
295 
296  if (fd < 0 || open_failed) {
297  return;
298  }
299 
300  if (get_libdwarf_functions() != 0) {
301  warnx("unable to open %s", LIBDWARF_NAME);
302  open_failed = 1;
303  return;
304  }
305 
306  AddrSet addrSet;
307  dwarf_frame_info_help(fd, addrSet);
308 
310 
311  for (auto it = addrSet.begin(); it != addrSet.end(); ++it) {
312  add_frame_addr(*it);
313  }
314 }
315 
316 //----------------------------------------------------------------------
317 
318 // Read the eh frame info (exception handler) from the FDE records
319 // (frame description entry) and put the list of addresses (low_pc)
320 // into addrSet.
321 //
322 static void
323 dwarf_frame_info_help(int fd, AddrSet & addrSet)
324 {
325  Dwarf_Debug dbg;
326  Dwarf_Error err;
327  int ret;
328 
329  ret = DWARF_INIT (fd, DW_DLC_READ, NULL, NULL, &dbg, &err);
330  if (ret == DW_DLV_ERROR) { DWARF_DEALLOC (dbg, err, DW_DLA_ERROR); }
331 
332  if (ret != DW_DLV_OK) {
333  return;
334  }
335 
336  Dwarf_Cie * cie_data = NULL;
337  Dwarf_Fde * fde_data = NULL;
338  Dwarf_Signed cie_count = 0;
339  Dwarf_Signed fde_count = 0;
340 
341  ret = DWARF_GET_FDE_LIST_EH (dbg, &cie_data, &cie_count, &fde_data, &fde_count, &err);
342  if (ret == DW_DLV_ERROR) { DWARF_DEALLOC (dbg, err, DW_DLA_ERROR); }
343 
344  if (ret == DW_DLV_OK ) {
345  for (long i = 0; i < fde_count; i++) {
346  Dwarf_Addr low_pc = 0;
347 
348  ret = DWARF_GET_FDE_RANGE (fde_data[i], &low_pc, NULL, NULL, NULL,
349  NULL, NULL, NULL, &err);
350  if (ret == DW_DLV_ERROR) { DWARF_DEALLOC (dbg, err, DW_DLA_ERROR); }
351 
352  if (ret == DW_DLV_OK && low_pc != 0) {
353  addrSet.insert((void *) low_pc);
354  }
355  }
356 
357  DWARF_FDE_CIE_LIST_DEALLOC (dbg, cie_data, cie_count, fde_data, fde_count);
358  }
359 
360  ret = DWARF_FINISH (dbg, &err);
361  if (ret == DW_DLV_ERROR) { DWARF_DEALLOC (dbg, err, DW_DLA_ERROR); }
362 }
#define LD_LIBRARY_PATH
Definition: eh-frames.cpp:91
err
Definition: names.cpp:1
static void release_libdwarf_functions(void)
Definition: eh-frames.cpp:272
#define DWARF_GET_FDE_RANGE
Definition: eh-frames.cpp:152
#define DWARF_INIT
Definition: eh-frames.cpp:148
#define EXT_LIBS_DIR_STR
Definition: eh-frames.cpp:90
set< void * > AddrSet
Definition: eh-frames.cpp:86
static void dwarf_frame_info_help(int, AddrSet &)
Definition: eh-frames.cpp:323
#define DWARF_GET_FDE_LIST_EH
Definition: eh-frames.cpp:151
#define DLOPEN_OPTS
Definition: eh-frames.cpp:94
#define DWARF_FDE_CIE_LIST_DEALLOC
Definition: eh-frames.cpp:153
static int get_libdwarf_functions(void)
Definition: eh-frames.cpp:165
void dwarf_eh_frame_info(int fd)
Definition: eh-frames.cpp:292
#define NULL
Definition: ElfHelper.cpp:85
void add_frame_addr(void *)
Definition: main.cpp:228
#define LIBDWARF_NAME
Definition: eh-frames.cpp:93
#define DWARF_FINISH
Definition: eh-frames.cpp:149
#define DWARF_DEALLOC
Definition: eh-frames.cpp:150