ATLAS Offline Software
UnwindBacktrace.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration.
3  */
13 
14 
15 #ifdef HAVE_LINUX_UNWIND_BACKTRACE
16 
17 
19 #include <atomic>
20 #include <unwind.h>
21 #include <stdio.h>
22 #include <malloc.h>
23 
24 
25 // A couple declarations from libgcc that are not in unwind.h.
26 extern "C" {
27  const void* _Unwind_Find_FDE (void*, void*);
28  void __register_frame_info (const void*, void*);
29  void __deregister_frame_info (const void*);
30 }
31 
32 
33 namespace CxxUtils {
34 
35 
36 typedef int sword __attribute__ ((mode (SI)));
37 typedef unsigned int uword __attribute__ ((mode (SI)));
38 using ubyte = unsigned char;
39 
40 
41 // Some DWARF definitions that we need.
42 enum
43  {
44  DW_EH_PE_absptr = 0x00
45  };
46 
47 // Stack unwinder instruction opcodes (see DWARF docs).
48 enum
49  {
50  DW_CFA_advance_loc = 0x40,
51  DW_CFA_offset = 0x80,
52  DW_CFA_restore = 0xc0,
53  DW_CFA_extended = 0,
54 
55  DW_CFA_nop = 0x00,
56  DW_CFA_set_loc = 0x01,
57  DW_CFA_advance_loc1 = 0x02,
58  DW_CFA_advance_loc2 = 0x03,
59  DW_CFA_advance_loc4 = 0x04,
60  DW_CFA_offset_extended = 0x05,
61  DW_CFA_restore_extended = 0x06,
62  DW_CFA_undefined = 0x07,
63  DW_CFA_same_value = 0x08,
64  DW_CFA_register = 0x09,
65  DW_CFA_remember_state = 0x0a,
66  DW_CFA_restore_state = 0x0b,
67  DW_CFA_def_cfa = 0x0c,
68  DW_CFA_def_cfa_register = 0x0d,
69 
70  DW_CFA_def_cfa_offset = 0x0e,
71  DW_CFA_def_cfa_expression = 0x0f,
72  DW_CFA_expression = 0x10,
73  DW_CFA_offset_extended_sf = 0x11,
74  DW_CFA_def_cfa_sf = 0x12,
75  DW_CFA_def_cfa_offset_sf = 0x13,
76  DW_CFA_val_offset = 0x14,
77  DW_CFA_val_offset_sf = 0x15,
78  DW_CFA_val_expression = 0x16,
79 
80  DW_CFA_low_user = 0x1c,
81  DW_CFA_MIPS_advance_loc8 = 0x1d,
82  DW_CFA_GNU_window_save = 0x2d,
83  DW_CFA_GNU_args_size = 0x2e,
84  DW_CFA_GNU_negative_offset_extended = 0x2f,
85  DW_CFA_high_user = 0x3f
86  };
87 
88 
89 // DWARF codes for x86_64 registers (see the ABI doc).
90 enum Registers
91 {
92  REG_RAX = 0,
93  REG_RDX = 1,
94  REG_RCX = 2,
95  REG_RBX = 3,
96  REG_RSI = 4,
97  REG_RDI = 5,
98  REG_RBP = 6,
99  REG_RSP = 7,
100  REG_R8 = 8,
101  REG_R9 = 9,
102  REG_R10 = 10,
103  REG_R11 = 11,
104  REG_R12 = 12,
105  REG_R13 = 13,
106  REG_R14 = 14,
107  REG_R15 = 15,
108  REG_RA = 16 // return address pseudo-register
109 };
110 
111 
114 struct SynthCIE
115 {
116  uword length;
117  sword CIE_id = 0;
118  ubyte version = 4;
119  unsigned char augmentation[1] = {0};
120  ubyte address_size = sizeof(void*);
121  ubyte segment_size = 0;
122  ubyte code_alignment_factor = 1;
123  ubyte data_alignment_factor = 0x78; // -8 in LEB128 encoding
124  ubyte return_address_register = REG_RA;
125  // Stack unwinding instructions.
126  // This is the default unwinding prolog generated by gas on x86_64,
127  // describing the state of things at the start of a function.
128  // We use this since we're assuming that we've called to bad code
129  // and crashed promptly.
130  // The CFA is RSP+8, and the RA is then -8(CFA) (eg, (SP)).
131  // We get -8 because the 1 below is multiplied by data_alignment_factor.
132  ubyte insns[9] = {DW_CFA_def_cfa, REG_RSP, 8,
133  DW_CFA_offset + REG_RA, 1,
134  DW_CFA_nop,DW_CFA_nop,DW_CFA_nop,DW_CFA_nop};
135 } __attribute__ ((packed, aligned (__alignof__ (void *))));
136 
138 struct SynthFDE
139 {
140  uword length;
141  sword CIE_delta;
142  void* pc_begin;
143  uintptr_t pc_range;
144  //ubyte insns[8] = {DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop};
145 } __attribute__ ((packed, aligned (__alignof__ (void *))));
146 
148 struct SynthFDEVector
149 {
150  const void* orig_data = nullptr;
151  size_t count = 0;
152  const struct SynthFDE* array[1] = {nullptr};
153 };
154 
156 struct SynthObject
157 {
158  void* pc_begin;
159  void* tbase;
160  void* dbase;
161  union {
162  const SynthFDE* single;
163  SynthFDEVector* sort;
164  } u;
165  struct {
166  unsigned long sorted : 1;
167  unsigned long from_aray : 1;
168  unsigned long mixed_encoding : 1;
169  unsigned long encoding : 8;
170  unsigned long count : 21;
171  } b;
172  struct object* next;
173  void* pad;
174 } __attribute__ ((packed, aligned (__alignof__ (void *))));
175 
180 struct SynthData
181 {
182  SynthCIE cie;
183  SynthFDE fde;
184  uword sentinel;
185  uword padding;
186  SynthObject ob;
187  SynthFDEVector* vec;
188  SynthData() : vec (reinterpret_cast<SynthFDEVector*>(malloc (sizeof (SynthFDEVector)))) {}
189  ~SynthData() { if (vec) free (vec); }
190  SynthData (const SynthData&) = delete;
191  SynthData& operator= (const SynthData&) = delete;
192 } __attribute__ ((packed, aligned (__alignof__ (void *))));
193 
194 
195 // Statically-allocated FDE data to inject.
196 static SynthData synthData ATLAS_THREAD_SAFE;
197 
198 
209 bool tryUnwindFixup (_Unwind_Ptr addr)
210 {
211  // Only allow going past this point once.
212  static std::atomic<int> fixed = 0;
213  int exp = 0;
214  if (!fixed.compare_exchange_strong (exp, 1)) {
215  return false;
216  }
217 
218  // Initialize synthetic CIE/FDE data.
219  SynthData& data = synthData;
220  data.cie.length = sizeof (data.cie) - sizeof (data.cie.length);
221  data.fde.length = sizeof (data.fde) - sizeof (data.fde.length);
222  // cppcheck-suppress comparePointers
223  data.fde.CIE_delta = reinterpret_cast<char*>(&data.fde.CIE_delta) - reinterpret_cast<char*>(&data.cie.length);
224  data.fde.pc_begin = (void*)addr;
225  data.fde.pc_range = 8;
226  data.sentinel = 0;
227 
228  // Register it with libgcc.
229  __register_frame_info (&data.fde, &data.ob);
230 
231  // The first time we try to use the new FDE, libgcc will try to allocate
232  // memory for the sorted FDE vector. But we want to avoid allocating memory
233  // here. We can do this by diddling the internal libgcc structure to point
234  // to a pre-allocated vector object. But this is inherently racy: we
235  // should be holding the libgcc lock when doing this, but that's not exported
236  // from the library. We make do by initializing the fields in an order
237  // that should be safe (enforced by memory fences) and hope for the best.
238  std::atomic_thread_fence (std::memory_order_seq_cst);
239  data.vec->orig_data = &data.fde;
240  data.vec->count = 1;
241  data.vec->array[0] = &data.fde;
242  std::atomic_thread_fence (std::memory_order_seq_cst);
243  data.ob.u.sort = data.vec;
244  data.vec = nullptr;
245  data.ob.b.encoding = DW_EH_PE_absptr;
246  data.ob.pc_begin = data.fde.pc_begin;
247  data.ob.b.count = 1;
248  std::atomic_thread_fence (std::memory_order_seq_cst);
249  data.ob.b.sorted = 1;
250 
251  return true;
252 }
253 
254 
255 //************************************************************************
256 
257 
259 struct dwarf_eh_bases
260 {
261  void *tbase;
262  void *dbase;
263  void *func;
264 };
265 
266 
268 struct UnwindBacktraceData
269 {
270  static constexpr unsigned MAX_DEPTH = 128;
271  unsigned naddr = 0;
272  _Unwind_Ptr addrs[MAX_DEPTH] = {0};
273  bool sawBad = false;
274 };
275 
276 
283 _Unwind_Reason_Code
284 unwindBacktraceCallback (_Unwind_Context *ctx, void *data_in)
285 {
286  // Record this IP.
287  auto& data = *reinterpret_cast<UnwindBacktraceData*> (data_in);
288  _Unwind_Ptr ip = _Unwind_GetIP (ctx);
289  data.addrs[data.naddr++] = ip;
290 
291  if (ip == 0 && !data.sawBad) {
292  // Is this the end of the stack, or a bad frame? Try to guess.
293  _Unwind_Word cfa = _Unwind_GetCFA (ctx);
294  if (cfa) {
295  void* x = *reinterpret_cast<void**>(cfa);
296  if (x) {
297  dwarf_eh_bases bases;
298  if (_Unwind_Find_FDE (x, &bases) != nullptr) {
299  data.sawBad = false;
300  return _URC_END_OF_STACK;
301  }
302  }
303  }
304  }
305  else {
306  // If we can't find the FDE for this address, set a flag and abort.
307  dwarf_eh_bases bases;
308  if (!data.sawBad && _Unwind_Find_FDE (reinterpret_cast<void*>(ip), &bases) == nullptr)
309  {
310  data.sawBad = true;
311  return _URC_END_OF_STACK;
312  }
313  }
314 
315  // Stop if we've hit the end of our buffer.
316  if (data.naddr == UnwindBacktraceData::MAX_DEPTH)
317  return _URC_END_OF_STACK;
318 
319  return _URC_NO_REASON;
320 }
321 
322 
329 void backtraceByUnwind (backtraceLineFn* lineFn, IOFD fd)
330 {
331  // Do the backtrace once.
332  UnwindBacktraceData data;
333  _Unwind_Backtrace (unwindBacktraceCallback, &data);
334 
335  // If we saw a bad entry, then try to fix it up.
336  // If that worked, retry the backtrace.
337  if (data.sawBad && tryUnwindFixup (data.addrs[data.naddr-1])) {
338  data.naddr = 0;
339  _Unwind_Backtrace (unwindBacktraceCallback, &data);
340  }
341 
342  // Clean a null entry from the end of the list.
343  if (data.naddr > 0 && data.addrs[data.naddr-1] == 0) {
344  --data.naddr;
345  }
346 
347  for (unsigned i = 0; i < data.naddr; i++) {
348  lineFn (fd, data.addrs[i]);
349  }
350 }
351 
352 
353 } // namespace CxxUtils
354 
355 
356 namespace {
357 
358 
359 // Make sure there's a block on the free list of the internal btree
360 // libgcc uses to keep track of registrations, so we won't have
361 // to call malloc later.
362 bool initUnwind()
363 {
364  CxxUtils::SynthData data;
365  data.cie.length = sizeof (data.cie) - sizeof (data.cie.length);
366  data.fde.length = sizeof (data.fde) - sizeof (data.fde.length);
367  // cppcheck-suppress comparePointers
368  data.fde.CIE_delta = reinterpret_cast<char*>(&data.fde.CIE_delta) - reinterpret_cast<char*>(&data.cie.length);
369  data.fde.pc_begin = (void*)&data;
370  data.fde.pc_range = 8;
371  data.sentinel = 0;
372 
373  __register_frame_info (&data.fde, &data.ob);
374  __deregister_frame_info (&data.fde);
375  return true;
376 }
377 const static bool initUnwindFlag = initUnwind();
378 
379 
380 } // anonymous namespace
381 
382 
383 #endif
data
char data[hepevt_bytes_allocation_ATLAS]
Definition: HepEvt.cxx:11
xAOD::char
char
Definition: TrigDecision_v1.cxx:38
vec
std::vector< size_t > vec
Definition: CombinationsGeneratorTest.cxx:12
UnwindBacktrace.h
Hacked backtrace that can go past a bad stack frame.
drawFromPickle.exp
exp
Definition: drawFromPickle.py:36
x
#define x
Trk::u
@ u
Enums for curvilinear frames.
Definition: ParamDefs.h:77
std::sort
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
Definition: DVL_algorithms.h:554
dqutils::padding
std::atomic< int > padding
Definition: MonitoringFile_MoveVertexMonitoring.cxx:20
XMLtoHeader.count
count
Definition: XMLtoHeader.py:85
CxxUtils::vec
typename vecDetail::vec_typedef< T, N >::type vec
Define a nice alias for the vectorized type.
Definition: vec.h:207
H5Utils::internal::packed
H5::CompType packed(H5::CompType in)
Definition: common.cxx:16
fillPileUpNoiseLumi.next
next
Definition: fillPileUpNoiseLumi.py:52
lumiFormat.i
int i
Definition: lumiFormat.py:85
generateReferenceFile.encoding
encoding
Definition: generateReferenceFile.py:15
IOFD
int IOFD
Type the system uses for channel descriptors.
Definition: SealCommon.h:27
CxxUtils
Definition: aligned_vector.h:29
find_tgc_unfilled_channelids.ip
ip
Definition: find_tgc_unfilled_channelids.py:3
Preparation.mode
mode
Definition: Preparation.py:94
TrigInDetValidation_Base.malloc
malloc
Definition: TrigInDetValidation_Base.py:132
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
lumiFormat.array
array
Definition: lumiFormat.py:91
ReadFromCoolCompare.fd
fd
Definition: ReadFromCoolCompare.py:196
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
SI
#define SI
Definition: EMECAccordionConstruction.cxx:45
get_generator_info.version
version
Definition: get_generator_info.py:33
__attribute__
__attribute__((always_inline)) inline uint16_t TileCalibDrawerBase
Definition: TileCalibDrawerBase.h:190
pickleTool.object
object
Definition: pickleTool.py:30
ATLAS_THREAD_SAFE
#define ATLAS_THREAD_SAFE
Definition: checker_macros.h:211
checker_macros.h
Define macros for attributes used to control the static checker.
length
double length(const pvec &v)
Definition: FPGATrackSimLLPDoubletHoughTransformTool.cxx:26