1 /*
2   Copyright (c) 2009-2015 David Anderson.
3   All rights reserved.
4 
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7   * Redistributions of source code must retain the above copyright
8     notice, this list of conditions and the following disclaimer.
9   * Redistributions in binary form must reproduce the above copyright
10     notice, this list of conditions and the following disclaimer in the
11     documentation and/or other materials provided with the distribution.
12   * Neither the name of the example nor the
13     names of its contributors may be used to endorse or promote products
14     derived from this software without specific prior written permission.
15 
16   THIS SOFTWARE IS PROVIDED BY David Anderson ''AS IS'' AND ANY
17   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   DISCLAIMED. IN NO EVENT SHALL David Anderson BE LIABLE FOR ANY
20   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 */
28 /*  simplereader.c
29     This is an example of code reading dwarf .debug_frame.
30     It is kept as simple as possible to expose essential features.
31     It does not do all possible error reporting or error handling.
32 
33     It specifically calls dwarf_expand_frame_instructions()
34     to verify that works without crashing!
35 
36     To use, try
37         make
38         ./frame1 frame1
39 
40     gcc/clang may produce .eh_frame without .debug_frame.
41     To read .eh_frame call dwarf_get_fde_list_eh()
42     below instead of dwarf_get_fde_list() .
43 */
44 #include <sys/types.h> /* For open() */
45 #include <sys/stat.h>  /* For open() */
46 #include <fcntl.h>     /* For open() */
47 #include <stdlib.h>     /* For exit() */
48 #include <unistd.h>     /* For close() */
49 #include <string.h>     /* For strcmp* */
50 #include <stdio.h>
51 #include <errno.h>
52 #include "dwarf.h"
53 #include "libdwarf.h"
54 
55 
56 static void read_frame_data(Dwarf_Debug dbg);
57 static void print_fde_instrs(Dwarf_Debug dbg, Dwarf_Fde fde,
58     Dwarf_Error *error);
59 static void print_regtable(Dwarf_Regtable3 *tab3);
60 static void
61 print_cie_instrs(Dwarf_Cie cie,Dwarf_Error *error);
62 
63 
64 #define UNDEF_VAL 2000
65 #define SAME_VAL 2001
66 #define CFA_VAL 2002
67 
68 
69 int
main(int argc,char ** argv)70 main(int argc, char **argv)
71 {
72 
73     Dwarf_Debug dbg = 0;
74     int fd = -1;
75     const char *filepath = "<stdin>";
76     int res = DW_DLV_ERROR;
77     Dwarf_Error error;
78     Dwarf_Handler errhand = 0;
79     Dwarf_Ptr errarg = 0;
80     int regtabrulecount = 0;
81 
82     if(argc < 2) {
83         fd = 0; /* stdin */
84     } else {
85         filepath = argv[1];
86         fd = open(filepath,O_RDONLY);
87     }
88     if(fd < 0) {
89         printf("Failure attempting to open %s\n",filepath);
90     }
91     res = dwarf_init(fd,DW_DLC_READ,errhand,errarg, &dbg,&error);
92     if(res != DW_DLV_OK) {
93         printf("Giving up, dwarf_init failed, cannot do DWARF processing\n");
94         exit(1);
95     }
96     /*  Do this setting after init before any real operations.
97         These return the old values, but here we do not
98         need to know the old values.  The sizes and
99         values here are higher than most ABIs and entirely
100         arbitrary.
101 
102         The setting of initial_value to
103         the same as undefined-value (the other possible choice being
104         same-value) is arbitrary, different ABIs do differ, and
105         you have to know which is right. */
106     regtabrulecount=1999;
107     dwarf_set_frame_undefined_value(dbg, UNDEF_VAL);
108     dwarf_set_frame_rule_initial_value(dbg, UNDEF_VAL);
109     dwarf_set_frame_same_value(dbg,SAME_VAL);
110     dwarf_set_frame_cfa_value(dbg,CFA_VAL);
111     dwarf_set_frame_rule_table_size(dbg,regtabrulecount);
112 
113     read_frame_data(dbg);
114     res = dwarf_finish(dbg,&error);
115     if(res != DW_DLV_OK) {
116         printf("dwarf_finish failed!\n");
117     }
118     close(fd);
119     return 0;
120 }
121 
122 static void
read_frame_data(Dwarf_Debug dbg)123 read_frame_data(Dwarf_Debug dbg)
124 {
125     Dwarf_Error error;
126     Dwarf_Signed cie_element_count = 0;
127     Dwarf_Signed fde_element_count = 0;
128     Dwarf_Cie *cie_data = 0;
129     Dwarf_Fde *fde_data = 0;
130     int res = DW_DLV_ERROR;
131     Dwarf_Signed fdenum = 0;
132 
133 
134     /*  If you wish to read .eh_frame data, use
135         dwarf_get_fde_list_eh() instead.  */
136     res = dwarf_get_fde_list(dbg,&cie_data,&cie_element_count,
137         &fde_data,&fde_element_count,&error);
138     if(res == DW_DLV_NO_ENTRY) {
139         printf("No .debug_frame data present\n");
140         printf("Try dwarf_get_fde_list_eh() to read .eh_frame data\n");
141         exit(0);
142     }
143     if( res == DW_DLV_ERROR) {
144         printf("Error reading frame data ");
145         exit(1);
146     }
147     printf( "%" DW_PR_DSd " cies present. "
148         "%" DW_PR_DSd " fdes present. \n",
149         cie_element_count,fde_element_count);
150     /*if(fdenum >= fde_element_count) {
151         printf("Want fde %d but only %" DW_PR_DSd " present\n",fdenum,
152             fde_element_count);
153         exit(1);
154     }*/
155 
156     for(fdenum = 0; fdenum < fde_element_count; ++fdenum) {
157         Dwarf_Cie cie = 0;
158         printf("Print cie of fde %" DW_PR_DSd  "\n",fdenum);
159         res = dwarf_get_cie_of_fde(fde_data[fdenum],&cie,&error);
160         if(res != DW_DLV_OK) {
161             printf("Error accessing fdenum %" DW_PR_DSd
162                 " to get its cie\n",fdenum);
163             exit(1);
164         }
165         print_cie_instrs(cie,&error);
166         printf("Print fde %" DW_PR_DSd  "\n",fdenum);
167         print_fde_instrs(dbg,fde_data[fdenum],&error);
168     }
169 
170     /* Done with the data. */
171     dwarf_fde_cie_list_dealloc(dbg,cie_data,cie_element_count,
172         fde_data, fde_element_count);
173     return;
174 }
175 static void
print_cie_instrs(Dwarf_Cie cie,Dwarf_Error * error)176 print_cie_instrs(Dwarf_Cie cie,Dwarf_Error *error)
177 {
178     int res = DW_DLV_ERROR;
179     Dwarf_Unsigned bytes_in_cie = 0;
180     Dwarf_Small version = 0;
181     char *augmentation = 0;
182     Dwarf_Unsigned code_alignment_factor = 0;
183     Dwarf_Signed data_alignment_factor = 0;
184     Dwarf_Half return_address_register_rule = 0;
185     Dwarf_Ptr instrp = 0;
186     Dwarf_Unsigned instr_len = 0;
187 
188     res = dwarf_get_cie_info(cie,&bytes_in_cie,
189         &version, &augmentation, &code_alignment_factor,
190         &data_alignment_factor, &return_address_register_rule,
191         &instrp,&instr_len,error);
192     if(res != DW_DLV_OK) {
193         printf("Unable to get cie info!\n");
194         exit(1);
195     }
196 }
197 
198 static void
print_frame_instrs(Dwarf_Frame_Op * frame_op_list,Dwarf_Signed frame_op_count)199 print_frame_instrs(Dwarf_Frame_Op *frame_op_list,
200   Dwarf_Signed frame_op_count)
201 {
202     Dwarf_Signed i = 0;
203     printf("Base op. Ext op. Reg. Offset. Instr-offset.\n");
204     for(i = 0; i < frame_op_count; ++i) {
205         printf("[%" DW_PR_DSd "]", i);
206         printf(" %d. ", frame_op_list[i].fp_base_op);
207         printf(" %d. ", frame_op_list[i].fp_extended_op);
208         printf(" %" DW_PR_DSd ". ", frame_op_list[i].fp_offset);
209         printf(" 0x%" DW_PR_DUx ". ", frame_op_list[i].fp_instr_offset);
210         printf("\n");
211     }
212 }
213 
214 static void
print_fde_instrs(Dwarf_Debug dbg,Dwarf_Fde fde,Dwarf_Error * error)215 print_fde_instrs(Dwarf_Debug dbg,
216     Dwarf_Fde fde, Dwarf_Error *error)
217 {
218     int res;
219     Dwarf_Addr lowpc = 0;
220     Dwarf_Unsigned func_length = 0;
221     Dwarf_Ptr fde_bytes;
222     Dwarf_Unsigned fde_byte_length = 0;
223     Dwarf_Off cie_offset = 0;
224     Dwarf_Signed cie_index = 0;
225     Dwarf_Off fde_offset = 0;
226     Dwarf_Addr arbitrary_addr = 0;
227     Dwarf_Addr actual_pc = 0;
228     Dwarf_Regtable3 tab3;
229     int oldrulecount = 0;
230     Dwarf_Ptr outinstrs = 0;
231     Dwarf_Unsigned instrslen = 0;
232     Dwarf_Frame_Op * frame_op_list = 0;
233     Dwarf_Signed frame_op_count = 0;
234     Dwarf_Cie cie = 0;
235 
236 
237     res = dwarf_get_fde_range(fde,&lowpc,&func_length,&fde_bytes,
238         &fde_byte_length,&cie_offset,&cie_index,&fde_offset,error);
239     if(res != DW_DLV_OK) {
240         printf("Problem getting fde range \n");
241         exit(1);
242     }
243 
244     arbitrary_addr = lowpc + (func_length/2);
245     printf("function low pc 0x%" DW_PR_DUx
246         "  and length 0x%" DW_PR_DUx
247         "  and addr we choose 0x%" DW_PR_DUx
248         "\n",
249         lowpc,func_length,arbitrary_addr);
250 
251     /*  1 is arbitrary. We are winding up getting the
252         rule count here while leaving things unchanged. */
253     oldrulecount = dwarf_set_frame_rule_table_size(dbg,1);
254     dwarf_set_frame_rule_table_size(dbg,oldrulecount);
255 
256     tab3.rt3_reg_table_size = oldrulecount;
257     tab3.rt3_rules = (struct Dwarf_Regtable_Entry3_s *) malloc(
258         sizeof(struct Dwarf_Regtable_Entry3_s)* oldrulecount);
259     if (!tab3.rt3_rules) {
260         printf("Unable to malloc for %d rules\n",oldrulecount);
261         exit(1);
262     }
263 
264     res = dwarf_get_fde_info_for_all_regs3(fde,arbitrary_addr ,
265         &tab3,&actual_pc,error);
266 
267     if(res != DW_DLV_OK) {
268         printf("dwarf_get_fde_info_for_all_regs3 failed!\n");
269         exit(1);
270     }
271     print_regtable(&tab3);
272 
273     res = dwarf_get_fde_instr_bytes(fde,&outinstrs,&instrslen,error);
274     if(res != DW_DLV_OK) {
275         printf("dwarf_get_fde_instr_bytes failed!\n");
276         exit(1);
277     }
278     res = dwarf_get_cie_of_fde(fde,&cie,error);
279     if(res != DW_DLV_OK) {
280         printf("Error getting cie from fde\n");
281         exit(1);
282     }
283 
284     res = dwarf_expand_frame_instructions(cie,
285         outinstrs,instrslen,&frame_op_list,
286         &frame_op_count,error);
287     if(res != DW_DLV_OK) {
288         printf("dwarf_expand_frame_instructions failed!\n");
289         exit(1);
290     }
291     printf("Frame op count: %" DW_PR_DUu "\n",frame_op_count);
292     print_frame_instrs(frame_op_list,frame_op_count);
293 
294     dwarf_dealloc(dbg,frame_op_list, DW_DLA_FRAME_BLOCK);
295     free(tab3.rt3_rules);
296 }
297 
298 static void
print_reg(int r)299 print_reg(int r)
300 {
301    switch(r) {
302    case SAME_VAL:
303         printf(" %d SAME_VAL ",r);
304         break;
305    case UNDEF_VAL:
306         printf(" %d UNDEF_VAL ",r);
307         break;
308    case CFA_VAL:
309         printf(" %d (CFA) ",r);
310         break;
311    default:
312         printf(" r%d ",r);
313         break;
314    }
315 }
316 
317 static void
print_one_regentry(const char * prefix,struct Dwarf_Regtable_Entry3_s * entry)318 print_one_regentry(const char *prefix,
319     struct Dwarf_Regtable_Entry3_s *entry)
320 {
321     int is_cfa = !strcmp("cfa",prefix);
322     printf("%s ",prefix);
323     printf("type: %d %s ",
324         entry->dw_value_type,
325         (entry->dw_value_type == DW_EXPR_OFFSET)? "DW_EXPR_OFFSET":
326         (entry->dw_value_type == DW_EXPR_VAL_OFFSET)? "DW_EXPR_VAL_OFFSET":
327         (entry->dw_value_type == DW_EXPR_EXPRESSION)? "DW_EXPR_EXPRESSION":
328         (entry->dw_value_type == DW_EXPR_VAL_EXPRESSION)?
329             "DW_EXPR_VAL_EXPRESSION":
330             "Unknown");
331     switch(entry->dw_value_type) {
332     case DW_EXPR_OFFSET:
333         print_reg(entry->dw_regnum);
334         printf(" offset_rel? %d ",entry->dw_offset_relevant);
335         if(entry->dw_offset_relevant) {
336             printf(" offset  %" DW_PR_DSd " " ,
337                 entry->dw_offset_or_block_len);
338             if(is_cfa) {
339                 printf("defines cfa value");
340             } else {
341                 printf("address of value is CFA plus signed offset");
342             }
343             if(!is_cfa  && entry->dw_regnum != CFA_VAL) {
344                 printf(" compiler botch, regnum != CFA_VAL");
345             }
346         } else {
347             printf("value in register");
348         }
349         break;
350     case DW_EXPR_VAL_OFFSET:
351         print_reg(entry->dw_regnum);
352         printf(" offset  %" DW_PR_DSd " " ,
353             entry->dw_offset_or_block_len);
354         if(is_cfa) {
355             printf("does this make sense? No?");
356         } else {
357             printf("value at CFA plus signed offset");
358         }
359         if(!is_cfa  && entry->dw_regnum != CFA_VAL) {
360             printf(" compiler botch, regnum != CFA_VAL");
361         }
362         break;
363     case DW_EXPR_EXPRESSION:
364         print_reg(entry->dw_regnum);
365         printf(" offset_rel? %d ",entry->dw_offset_relevant);
366         printf(" offset  %" DW_PR_DSd " " ,
367             entry->dw_offset_or_block_len);
368         printf("Block ptr set? %s ",entry->dw_block_ptr?"yes":"no");
369         printf(" Value is at address given by expr val ");
370         /* printf(" block-ptr  0x%" DW_PR_DUx " ",
371             (Dwarf_Unsigned)entry->dw_block_ptr); */
372         break;
373     case DW_EXPR_VAL_EXPRESSION:
374         printf(" expression byte len  %" DW_PR_DSd " " ,
375             entry->dw_offset_or_block_len);
376         printf("Block ptr set? %s ",entry->dw_block_ptr?"yes":"no");
377         printf(" Value is expr val ");
378         if(!entry->dw_block_ptr) {
379             printf("Compiler botch. ");
380         }
381         /* printf(" block-ptr  0x%" DW_PR_DUx " ",
382             (Dwarf_Unsigned)entry->dw_block_ptr); */
383         break;
384     }
385     printf("\n");
386 }
387 
388 static void
print_regtable(Dwarf_Regtable3 * tab3)389 print_regtable(Dwarf_Regtable3 *tab3)
390 {
391     int r;
392     /* We won't print too much. A bit arbitrary. */
393     int max = 10;
394     if(max > tab3->rt3_reg_table_size) {
395         max = tab3->rt3_reg_table_size;
396     }
397     print_one_regentry("cfa",&tab3->rt3_cfa_rule);
398 
399     for(r = 0; r < max; r++) {
400         char rn[30];
401         snprintf(rn,sizeof(rn),"reg %d",r);
402         print_one_regentry(rn,tab3->rt3_rules+r);
403     }
404 
405 
406 }
407 
408 
409 
410 
411