FreeRDP
unwind/debug.c
1 
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
23 #endif
24 
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <unwind.h>
28 
29 #include <winpr/string.h>
30 #include "debug.h"
31 
32 #include <winpr/wlog.h>
33 #include "../log.h"
34 
35 #include <dlfcn.h>
36 
37 #define TAG WINPR_TAG("utils.unwind")
38 
39 #define UNWIND_MAX_LINE_SIZE 1024ULL
40 
41 typedef struct
42 {
43  union
44  {
45  uintptr_t uw;
46  void* pv;
47  } pc;
48  union
49  {
50  uintptr_t uptr;
51  void* pv;
52  } langSpecificData;
53 } unwind_info_t;
54 
55 typedef struct
56 {
57  size_t pos;
58  size_t size;
59  unwind_info_t* info;
60 } unwind_context_t;
61 
62 static const char* unwind_reason_str(_Unwind_Reason_Code code)
63 {
64  switch (code)
65  {
66 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__) && \
67  !defined(__SEH__)
68  case _URC_OK:
69  return "_URC_OK";
70 #else
71  case _URC_NO_REASON:
72  return "_URC_NO_REASON";
73  case _URC_FATAL_PHASE2_ERROR:
74  return "_URC_FATAL_PHASE2_ERROR";
75  case _URC_FATAL_PHASE1_ERROR:
76  return "_URC_FATAL_PHASE1_ERROR";
77  case _URC_NORMAL_STOP:
78  return "_URC_NORMAL_STOP";
79 #endif
80  case _URC_FOREIGN_EXCEPTION_CAUGHT:
81  return "_URC_FOREIGN_EXCEPTION_CAUGHT";
82  case _URC_END_OF_STACK:
83  return "_URC_END_OF_STACK";
84  case _URC_HANDLER_FOUND:
85  return "_URC_HANDLER_FOUND";
86  case _URC_INSTALL_CONTEXT:
87  return "_URC_INSTALL_CONTEXT";
88  case _URC_CONTINUE_UNWIND:
89  return "_URC_CONTINUE_UNWIND";
90 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__) && \
91  !defined(__SEH__)
92  case _URC_FAILURE:
93  return "_URC_FAILURE";
94 #endif
95  default:
96  return "_URC_UNKNOWN";
97  }
98 }
99 
100 static const char* unwind_reason_str_buffer(_Unwind_Reason_Code code, char* buffer, size_t size)
101 {
102  const char* str = unwind_reason_str(code);
103  (void)_snprintf(buffer, size, "%s [0x%02x]", str, code);
104  return buffer;
105 }
106 
107 static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg)
108 {
109  unwind_context_t* ctx = arg;
110 
111  assert(ctx);
112 
113  if (ctx->pos < ctx->size)
114  {
115  unwind_info_t* info = &ctx->info[ctx->pos++];
116  info->pc.uw = _Unwind_GetIP(context);
117 
118  /* _Unwind_GetLanguageSpecificData has various return value definitions,
119  * cast to the type we expect and disable linter warnings
120  */
121  // NOLINTNEXTLINE(google-readability-casting,readability-redundant-casting)
122  info->langSpecificData.pv = (void*)_Unwind_GetLanguageSpecificData(context);
123  }
124 
125  return _URC_NO_REASON;
126 }
127 
128 void* winpr_unwind_backtrace(DWORD size)
129 {
130  _Unwind_Reason_Code rc = _URC_FOREIGN_EXCEPTION_CAUGHT;
131  unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t));
132  if (!ctx)
133  goto fail;
134  ctx->size = size;
135  ctx->info = calloc(size, sizeof(unwind_info_t));
136  if (!ctx->info)
137  goto fail;
138 
139  rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx);
140  if (rc != _URC_END_OF_STACK)
141  {
142  char buffer[64] = { 0 };
143  WLog_ERR(TAG, "_Unwind_Backtrace failed with %s",
144  unwind_reason_str_buffer(rc, buffer, sizeof(buffer)));
145  goto fail;
146  }
147 
148  return ctx;
149 fail:
150  winpr_unwind_backtrace_free(ctx);
151  return NULL;
152 }
153 
154 void winpr_unwind_backtrace_free(void* buffer)
155 {
156  unwind_context_t* ctx = buffer;
157  if (!ctx)
158  return;
159  free(ctx->info);
160  free(ctx);
161 }
162 
163 char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used)
164 {
165  union
166  {
167  char* cp;
168  char** cpp;
169  } cnv;
170  unwind_context_t* ctx = buffer;
171  cnv.cpp = NULL;
172 
173  if (!ctx)
174  return NULL;
175 
176  cnv.cpp = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*));
177  if (!cnv.cpp)
178  return NULL;
179 
180  if (used)
181  *used = ctx->pos;
182 
183  for (size_t x = 0; x < ctx->pos; x++)
184  {
185  char* msg = cnv.cp + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
186  const unwind_info_t* info = &ctx->info[x];
187  Dl_info dlinfo = { 0 };
188  int rc = dladdr(info->pc.pv, &dlinfo);
189 
190  cnv.cpp[x] = msg;
191 
192  if (rc == 0)
193  (void)_snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", info->pc.pv);
194  else
195  (void)_snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]",
196  dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr);
197  }
198 
199  return cnv.cpp;
200 }