TODO
[platform/upstream/sysprof.git] / unwind.c
1 #include "elfparser.h"
2 #include "binparser.h"
3 #include <string.h>
4
5 /* Pointer encodings, from dwarf2.h.  */
6 typedef enum
7 {
8     DW_EH_PE_absptr     = 0x00, /* */
9     DW_EH_PE_omit       = 0xff, /* Value is not there */
10
11     DW_EH_PE_uleb128    = 0x01,
12     DW_EH_PE_udata2     = 0x02,
13     DW_EH_PE_udata4     = 0x03,
14     DW_EH_PE_udata8     = 0x04,
15     DW_EH_PE_sleb128    = 0x09,
16     DW_EH_PE_sdata2     = 0x0A,
17     DW_EH_PE_sdata4     = 0x0B,
18     DW_EH_PE_sdata8     = 0x0C,
19     DW_EH_PE_signed     = 0x08,
20
21     DW_EH_PE_pcrel      = 0x10, /* Value is *(cur + val) */
22     DW_EH_PE_textrel    = 0x20, /* Value is *(&text + val) */
23     DW_EH_PE_datarel    = 0x30, /* Value is *(&data + val) */
24     DW_EH_PE_funcrel    = 0x40, /* Value is *(fde.pc_begin + val) */
25     DW_EH_PE_aligned    = 0x50, /* Value is absolute, and stored
26                                  * at next align */
27
28     DW_EH_PE_indirect   = 0x80
29 } PointerEncoding;
30
31 typedef struct EncodedPointer EncodedPointer;
32 struct EncodedPointer
33 {
34     PointerEncoding     encoding;
35     guint64             value;
36 };
37
38 static guint64
39 get_length (const guchar **data)
40 {
41     guint64 len;
42
43     len = *(guint32 *)*data;
44
45     *data += 4;
46     
47     if (len == 0xffffffff)
48     {
49         len = *(guint64 *)data;
50         *data += 8;
51     }
52
53     return len;
54 }
55
56 static guint64
57 decode_uleb128 (const guchar **data)
58 {
59     guint64 result;
60     int shift;
61     guchar b;
62
63     result = 0;
64     shift = 0;
65     do
66     {
67         b = *(*data)++;
68         result |= (b & 0x7f) << shift;
69         shift += 7;
70
71     } while (b & 0x80);
72
73     return result;
74 }
75
76 static gint64
77 decode_sleb128 (const guchar **data)
78 {
79     gint64 result;
80     int shift;
81     guchar b;
82
83     result = 0;
84     shift = 0;
85     do
86     {
87         b = *(*data)++;
88         result |= (b & 0x7f) << shift;
89         shift += 7;
90     } while (b & 0x80);
91
92     if (b & 0x40 && shift < 64)
93         result |= - (1 << shift);
94
95     return result;
96 }
97
98 static void
99 decode_block (const guchar **data)
100 {
101     int len;
102     
103     /* FIXME */
104
105     len = decode_uleb128 (data);
106
107     (*data) += len;
108 }
109
110 static gulong
111 decode_address (const guchar **data)
112 {
113     /* FIXME */
114     gulong r;
115
116     r = *(guint32 *)*data;
117     (*data) += 4;
118     return r;
119 }
120
121 static const char *
122 decode_instruction (const guchar **data)
123 {
124     int opcode = *(*data)++;
125     int high2 = (opcode & 0xc0) >> 6;
126     int low6 = (opcode & 0x3f);
127
128     if (high2 == 0x01)
129     {
130         return "DW_CFA_advance_loc";
131     }
132     else if (high2 == 0x02)
133     {
134         g_print ("register: %d\n", low6);
135         g_print ("offset: %llu\n", decode_uleb128 (data));
136         
137         return "DW_CFA_offset";
138     }
139     else if (high2 == 0x03)
140     {
141         return "DW_CFA_restore";
142     }
143     else
144     {
145         g_assert ((opcode & 0xc0) == 0);
146         
147         switch (opcode)
148         {
149         case 0x0:
150             return "DW_CFA_nop";
151             
152         case 0x01:
153             g_print ("addr: %p\n", (void *)decode_address (data));
154             return "DW_CFA_set_loc";
155
156         case 0x02:
157             (*data)++;
158             return "DW_CFA_advance_loc1";
159
160         case 0x03:
161             (*data) += 2;
162             return "DW_CFA_advance_loc2";
163
164         case 0x04:
165             (*data) += 4;
166             return "DW_CFA_advance_loc4";
167
168         case 0x05:
169             decode_uleb128 (data);
170             decode_uleb128 (data);
171             return "DW_CFA_offset_extended";
172
173         case 0x06:
174             decode_uleb128 (data);
175             return "DW_CFA_restore_extended";
176
177         case 0x07:
178             decode_uleb128 (data);
179             return "DW_CFA_undefined";
180
181         case 0x08:
182             decode_uleb128 (data);
183             return "DW_CFA_same_value";
184
185         case 0x09:
186             decode_uleb128 (data);
187             decode_uleb128 (data);
188             return "DW_CFA_register";
189
190         case 0x0a:
191             return "DW_CFA_remember_state";
192
193         case 0x0b:
194             return "DW_CFA_restore_state";
195
196         case 0x0c:
197             g_print ("reg: %llu\n", decode_uleb128 (data));
198             g_print ("off: %llu\n", decode_uleb128 (data));
199             return "DW_CFA_def_cfa";
200
201         case 0x0d:
202             decode_uleb128 (data);
203             return "DW_CFA_def_cfa_register";
204
205         case 0x0e:
206             decode_uleb128 (data);
207             return "DW_CFA_def_cfa_offset";
208
209         case 0x0f:
210             decode_block (data);
211             return "DW_CFA_def_cfa_expression";
212
213         case 0x10:
214             decode_uleb128 (data);
215             decode_block (data);
216             return "DW_CFA_expression";
217
218         case 0x11:
219             decode_uleb128 (data);
220             decode_sleb128 (data);
221             return "DW_CFA_offset_extended_sf";
222
223         case 0x12:
224             decode_uleb128 (data);
225             decode_sleb128 (data);
226             return "DW_CFA_def_cfa_sf";
227
228         case 0x13:
229             decode_sleb128 (data);
230             return "DW_CFA_def_cfa_offset_sf";
231
232         case 0x14:
233             decode_uleb128 (data);
234             decode_uleb128 (data);
235             return "DW_CFA_val_offset";
236
237         case 0x15:
238             decode_uleb128 (data);
239             decode_sleb128 (data);
240             return "DW_CFA_val_offset_sf";
241
242         case 0x16:
243             decode_uleb128 (data);
244             decode_block (data);
245             return "DW_CFA_val_expression";
246
247         case 0x1c:
248             return "DW_CFA_lo_user";
249
250         case 0x3f:
251             return "DW_CFA_hi_user";
252
253         default:
254             return "UNKNOWN INSTRUCTION";
255         }
256     }
257 }
258
259 typedef struct CIE CIE;
260 struct CIE
261 {
262     PointerEncoding     encoding;
263 };
264
265 static void
266 decode_cie (const guchar **data, const guchar *end)
267 {
268     gboolean has_augmentation;
269     guint64 aug_len;
270     char *augmentation;
271     CIE *cie;
272     int i, field;
273     
274     g_print ("version: %d\n", *(*data)++);
275
276     augmentation = (*data);
277     
278     *data += strlen (*data) + 1;
279
280     g_print ("code alignment: %llu\n", decode_uleb128 (data));
281
282     g_print ("data alignment: %lld\n", decode_sleb128 (data));
283
284     g_print ("return register: %llu\n", decode_uleb128 (data));
285
286     g_print ("augmentation: %s\n", augmentation);
287     
288     if (augmentation[0] == 'z')
289     {
290         aug_len = decode_uleb128 (data);
291
292         g_print ("len: %llu\n", aug_len);
293         
294         for (i = 1; augmentation[i] != 0; ++i)
295         {
296             if (augmentation[i] == 'L')
297             {
298                 
299             }
300         }
301     }
302     
303     
304     if (has_augmentation)
305     {
306         g_print ("%x\n", **data);
307         
308         *data += aug_len;
309     }
310     
311     while (*data < end)
312         g_print ("  %s\n", decode_instruction (data));
313 }
314
315 static gboolean
316 decode_fde (const guchar **data, const guchar *end)
317 {
318     
319     
320     return FALSE;
321 }
322
323 static gboolean
324 decode_entry (const guchar **data, gboolean eh_frame)
325 {
326     guint64 len;
327     const guchar *end;
328     gboolean is_cie;
329     guint64 id;
330
331     len = get_length (data);
332
333     if (len == 0)
334         return FALSE;
335     
336     end = *data + len;
337     
338     g_print ("length: %llu\n", len);
339
340     /* CIE_id is 0 for eh frames, and 0xffffffff/0xffffffffffffffff for .debug_frame */
341     
342     id = *(guint32 *)*data;
343
344     g_print ("id: %lld\n", id);
345
346     is_cie = (eh_frame && id == 0) || (!eh_frame && id == 0xffffffff);
347
348     if (is_cie)
349         g_print ("is cie\n");
350     else
351         g_print ("is not cie\n");
352     
353     *data += 4;
354
355     if (is_cie)
356         decode_cie (data, end);
357     else
358         decode_fde (data, end);
359     
360     return TRUE;
361 }
362
363 /* The correct API is probably something like
364  *
365  *   gboolean
366  *   unwind (ElfParser   *parser,
367  *           gulong      *regs
368  *           int          n_regs,
369  *           MemoryReader reader,
370  *           gpointer     data);
371  *
372  */
373 void
374 unwind (ElfParser *elf)
375 {
376     const guchar *data;
377     gboolean eh_f;
378     
379     if ((data = elf_parser_get_debug_frame (elf)))
380     {
381         g_print ("Using .debug_frame\n");
382         eh_f = FALSE;
383     }
384     else if ((data = elf_parser_get_eh_frame (elf)))
385     {
386         g_print ("Using .eh_frame\n");
387         eh_f = TRUE;
388     }
389     else
390     {
391         g_print ("no debug info found\n");
392         return;
393     }
394
395     while (decode_entry (&data, eh_f))
396         return ;
397         ;
398 }
399