• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <fixdwarf.h>
2 #include <common.h>
3 #include <debug.h>
4 #include <hash.h>
5 
6 #include <libelf.h>
7 #include <libebl.h>
8 #include <libebl_arm.h>
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 
19 /* When this macro is set to a nonzero value, we maintain a BST where we store each address once
20    we update the value at that address, and check to make sure that the address has not been
21    visited before we udpate it.  This way we make sure that we do not do multiple updates at any
22    any given address.  The feature is disabled by default because it is very expensive.  It should
23    be enabled as a first step in debugging problems with the DWARF patches that this code makes.
24 */
25 
26 #define PARANOIA (0)
27 
28 #define _str(name) #name
29 #define _id(a,b) a ## b
30 
31 #if PARANOIA
32 #define COLLECT_BACKTRACES (0)
33 
34 #if COLLECT_BACKTRACES
35 #include <execinfo.h>
36 #endif
37 #endif/*PARANOIA*/
38 
39 #include <dwarf.h>
40 
41 int load_debug_section (enum dwarf_section_display_enum debug, void *file);
42 void free_debug_section (enum dwarf_section_display_enum debug);
43 
44 static shdr_info_t *s_shdr_info;
45 static int s_shdr_info_len;
46 static int dwarf_to_shdr[max];
47 static shdr_info_t *s_cached_find_section_result = NULL;
48 static int s_num_total_patches = 0;
49 static int s_num_failed_patches = 0;
50 
51 static void init_value_free_lists();
52 
53 #if PARANOIA
54 typedef struct value_struct {
55     unsigned long key;
56     struct value_struct *left;
57     struct value_struct *right;
58 #if COLLECT_BACKTRACES
59 #define BACKTRACE_DEPTH (10)
60     void *backtrace[BACKTRACE_DEPTH];
61     int backtrace_depth;
62 #endif/*COLLECT_BACKTRACES*/
63 } value_t;
64 
65 static value_t *s_visited_values; /* BST of visited values */
66 #endif/*PARANOIA*/
67 
68 static void dump_dwarf_section (enum dwarf_section_display_enum dwarf_idx);
69 static void byte_set_little_endian (
70     unsigned char *field, int size, dwarf_vma val);
71 static void byte_set_big_endian (
72     unsigned char *field, int size, dwarf_vma val);
73 static void (*byte_set) (unsigned char *, int, dwarf_vma);
74 
update_dwarf_if_necessary(Elf * elf,GElf_Ehdr * ehdr,Elf * newelf,shdr_info_t * shdr_info,int num_shdr_info,int * num_total_patches,int * num_failed_patches)75 void update_dwarf_if_necessary(Elf *elf __attribute__((unused)),
76                                GElf_Ehdr *ehdr,
77                                Elf *newelf __attribute__((unused)),
78                                shdr_info_t *shdr_info, int num_shdr_info,
79                                int *num_total_patches, int *num_failed_patches)
80 {
81     /* Find the debug sections */
82 
83     int cnt;
84 
85     /* Initialize the static variables, which might have been left in
86        nondefault states from a previous call to this function.
87     */
88     s_shdr_info = NULL;
89     s_cached_find_section_result = NULL;
90     s_shdr_info_len = 0;
91     s_num_total_patches = 0;
92     s_num_failed_patches = 0;
93     memset(dwarf_to_shdr, 0, sizeof(dwarf_to_shdr));
94     for(cnt = 0; cnt < max; cnt++)
95         free_debug_section(cnt);
96 #if PARANOIA
97     s_visited_values = NULL;
98     init_value_free_lists();
99 #endif/*PARANOIA*/
100     init_dwarf_variables();
101 
102     cnt = 0;
103 
104     /* Locate the .debug_<xxx> sections, and save
105        their indices (in shdr_info) in the respective
106        idx_debug_<xxx> variable. If a section is not
107        prwesent in the file, the variable will have
108        a negative value after this loop.
109     */
110 
111 #define CHECK_DEBUG_SECTION(sname)                                           \
112         ASSERT(shdr_info[cnt].name != NULL);                                 \
113         if (!strcmp(shdr_info[cnt].name,                                     \
114                     ".debug_" _str(sname))) {                                \
115             FAILIF(dwarf_to_shdr[sname] > 0,                                 \
116                    ".debug_" _str(sname) " is already found at index %d!\n", \
117                    dwarf_to_shdr[sname]);                                    \
118             INFO("Index of \".debug_" _str(name) " is %d", cnt);             \
119             if (shdr_info[cnt].idx > 0)                                      \
120                 dwarf_to_shdr[sname] = cnt;                                  \
121             else INFO(", but the section is being removed.");                \
122             INFO("\n");                                                      \
123         }
124 
125     for(cnt = 1; cnt < num_shdr_info; cnt++) {
126         CHECK_DEBUG_SECTION(aranges);
127         CHECK_DEBUG_SECTION(info);
128         CHECK_DEBUG_SECTION(abbrev);
129         CHECK_DEBUG_SECTION(line);
130         CHECK_DEBUG_SECTION(frame);
131         CHECK_DEBUG_SECTION(loc);
132         CHECK_DEBUG_SECTION(ranges);
133         CHECK_DEBUG_SECTION(pubnames);
134         CHECK_DEBUG_SECTION(str);
135     }
136 #undef CHECK_DEBUG_SECTION
137 
138     {
139         is_relocatable = (ehdr->e_type == ET_REL);
140         eh_addr_size = 4;
141 
142         if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) {
143             byte_get = byte_get_little_endian;
144             byte_set = byte_set_little_endian;
145         }
146         else {
147             ASSERT(ehdr->e_ident[EI_DATA] == ELFDATA2MSB);
148             byte_get = byte_get_big_endian;
149             byte_set = byte_set_big_endian;
150         }
151     }
152 
153 #define ADJUST_IF_NECESSARY(sname)                                                   \
154     do {                                                                             \
155         if (dwarf_to_shdr[sname] > 0) {                                              \
156             INFO("\nAdjusting for %s.\n", shdr_info[dwarf_to_shdr[sname]].name);     \
157             dump_dwarf_section(sname);                                               \
158         }                                                                            \
159         else {                                                                       \
160             INFO("\nNot adjusting for %s.\n", shdr_info[dwarf_to_shdr[sname]].name); \
161         }                                                                            \
162     } while(0)
163 
164     s_shdr_info = shdr_info;
165     s_shdr_info_len = num_shdr_info;
166 
167     ADJUST_IF_NECESSARY(info);
168     ADJUST_IF_NECESSARY(loc);
169     ADJUST_IF_NECESSARY(aranges);
170     ADJUST_IF_NECESSARY(frame);
171     ADJUST_IF_NECESSARY(ranges);
172     ADJUST_IF_NECESSARY(line);
173     ADJUST_IF_NECESSARY(str);
174     ADJUST_IF_NECESSARY(pubnames);
175     ADJUST_IF_NECESSARY(abbrev);
176 
177 #undef ADJUST_IF_NECESSRY
178 
179     *num_total_patches = s_num_total_patches;
180     *num_failed_patches = s_num_failed_patches;
181 }
182 
183 int
load_debug_section(enum dwarf_section_display_enum debug,void * file)184 load_debug_section (enum dwarf_section_display_enum debug,
185                     void *file __attribute__((unused)))
186 {
187     struct dwarf_section *section = &debug_displays [debug].section;
188     int shdr_idx = dwarf_to_shdr[debug];
189     if (!shdr_idx) {
190         INFO("Could not load section %s: it is not in the file.\n",
191              debug_displays[debug].section.name);
192         return 0;
193     }
194     ASSERT(s_shdr_info);
195 
196     INFO("Loading DWARF section type %s index %d (type %d)\n",
197          s_shdr_info[shdr_idx].name,
198          s_shdr_info[shdr_idx].idx,
199          debug);
200 
201     /* If it is already loaded, do nothing.  */
202     if (section->start != NULL) {
203         INFO("\tAlready loaded DWARF section type %s (type %d)\n", s_shdr_info[shdr_idx].name, debug);
204         return 1;
205     }
206 
207     ASSERT(s_shdr_info[shdr_idx].newdata);
208 
209     section->address = s_shdr_info[shdr_idx].shdr.sh_addr;
210     section->start = s_shdr_info[shdr_idx].newdata->d_buf;
211     section->size = s_shdr_info[shdr_idx].newdata->d_size;
212     ASSERT(s_shdr_info[shdr_idx].newdata->d_off == 0);
213 
214     ASSERT(section->size != 0);
215     ASSERT(s_shdr_info[shdr_idx].shdr.sh_size == s_shdr_info[shdr_idx].newdata->d_size);
216     ASSERT(section->start != NULL);
217 
218     return 1;
219 }
220 
221 void
free_debug_section(enum dwarf_section_display_enum debug)222 free_debug_section (enum dwarf_section_display_enum debug)
223 {
224     struct dwarf_section *section = &debug_displays [debug].section;
225 
226     INFO("Unloading DWARF section type %d\n", debug);
227 
228     if (section->start == NULL)
229         return;
230 
231     section->start = NULL;
232     section->address = 0;
233     section->size = 0;
234 }
235 
236 static void
dump_dwarf_section(enum dwarf_section_display_enum dwarf_idx)237 dump_dwarf_section (enum dwarf_section_display_enum dwarf_idx)
238 {
239     int shdr_idx = dwarf_to_shdr[dwarf_idx];
240     ASSERT(shdr_idx);
241     ASSERT(s_shdr_info);
242     ASSERT(s_shdr_info[shdr_idx].idx);
243     ASSERT(s_shdr_info[shdr_idx].name);
244 
245     ASSERT(!strcmp (debug_displays[dwarf_idx].section.name, s_shdr_info[shdr_idx].name));
246 
247     if (!debug_displays[dwarf_idx].eh_frame) {
248         struct dwarf_section *sec = &debug_displays [dwarf_idx].section;
249 
250         if (load_debug_section (dwarf_idx, NULL)) {
251             INFO("Dumping DWARF section [%s] (type %d).\n",
252                  s_shdr_info[shdr_idx].name,
253                  dwarf_idx);
254             debug_displays[dwarf_idx].display (sec, NULL);
255             if (dwarf_idx != info && dwarf_idx != abbrev)
256                 free_debug_section (dwarf_idx);
257         }
258     }
259 }
260 
find_section(int value)261 static shdr_info_t *find_section(int value)
262 {
263     ASSERT(s_shdr_info != NULL);
264     ASSERT(s_shdr_info_len > 0);
265 
266 #define IN_RANGE(v,s,l) ((s)<=(v) && (v)<((s)+(l)))
267     if (s_cached_find_section_result != NULL &&
268         IN_RANGE((unsigned)value,
269                  s_cached_find_section_result->old_shdr.sh_addr,
270                  s_cached_find_section_result->old_shdr.sh_size)) {
271         return s_cached_find_section_result;
272     }
273 
274     /* Find the section to which the address belongs. */
275     int cnt;
276     for (cnt = 0; cnt < s_shdr_info_len; cnt++) {
277         if (s_shdr_info[cnt].idx > 0 &&
278             (s_shdr_info[cnt].old_shdr.sh_flags & SHF_ALLOC) &&
279             IN_RANGE((unsigned) value,
280                      s_shdr_info[cnt].old_shdr.sh_addr,
281                      s_shdr_info[cnt].old_shdr.sh_size)) {
282 
283             s_cached_find_section_result = s_shdr_info + cnt;
284             return s_cached_find_section_result;
285         }
286     }
287 #undef IN_RANGE
288 
289     return NULL;
290 }
291 
292 #if PARANOIA
293 static value_t **s_value_free_lists;
294 static int s_num_free_lists;
295 static int s_cur_free_list;
296 static int s_alloc_values; /* number of allocated values in the list */
297 #define LISTS_INCREMENT (10)
298 #define NUM_VALUES_PER_LIST (10000)
299 
init_value_free_lists()300 static void init_value_free_lists()
301 {
302     if (s_value_free_lists) {
303         value_t **trav = s_value_free_lists;
304         while(s_cur_free_list) {
305             FREE(*trav++);
306             s_cur_free_list--;
307         }
308         FREE(s_value_free_lists);
309         s_value_free_lists = NULL;
310     }
311     s_num_free_lists = 0;
312     s_alloc_values = 0;
313 }
314 
alloc_value()315 static value_t *alloc_value()
316 {
317     if (s_alloc_values == NUM_VALUES_PER_LIST) {
318         s_cur_free_list++;
319         s_alloc_values = 0;
320     }
321 
322     if (s_cur_free_list == s_num_free_lists) {
323         s_num_free_lists += LISTS_INCREMENT;
324         s_value_free_lists = REALLOC(s_value_free_lists,
325                                      s_num_free_lists * sizeof(value_t *));
326         memset(s_value_free_lists + s_cur_free_list,
327                0,
328                (s_num_free_lists - s_cur_free_list) * sizeof(value_t *));
329     }
330 
331     if (s_value_free_lists[s_cur_free_list] == NULL) {
332         s_value_free_lists[s_cur_free_list] = MALLOC(NUM_VALUES_PER_LIST*sizeof(value_t));
333     }
334 
335     return s_value_free_lists[s_cur_free_list] + s_alloc_values++;
336 }
337 
338 static value_t *would_be_parent = NULL;
find_value(unsigned long val)339 static value_t *find_value(unsigned long val)
340 {
341     would_be_parent = NULL;
342     value_t *trav = s_visited_values;
343     while(trav) {
344         would_be_parent = trav;
345         if (val < trav->key)
346             trav = trav->left;
347         else if (val > trav->key)
348             trav = trav->right;
349         else if (val == trav->key) {
350             return trav;
351         }
352     }
353     return NULL;
354 }
355 
value_visited(unsigned long val)356 static int value_visited(unsigned long val)
357 {
358     value_t *found = find_value(val);
359     if (found != NULL) {
360 #if COLLECT_BACKTRACES
361         void *new_bt[BACKTRACE_DEPTH];
362         int new_bt_depth = backtrace(new_bt, BACKTRACE_DEPTH);
363         char **symbols = backtrace_symbols(new_bt, new_bt_depth);
364         PRINT("NEW VISIT AT %x\n", val);
365         if (symbols != NULL) {
366             int cnt = 0;
367             while(cnt < new_bt_depth) {
368                 PRINT("\t%s\n", symbols[cnt]);
369                 cnt++;
370             }
371         }
372         FREE(symbols);
373         PRINT("OLD VISIT AT %x\n", val);
374         symbols = backtrace_symbols(found->backtrace, found->backtrace_depth);
375         if (symbols != NULL) {
376             int cnt = 0;
377             while(cnt < new_bt_depth) {
378                 PRINT("\t%s\n", symbols[cnt]);
379                 cnt++;
380             }
381         }
382         FREE(symbols);
383 #else
384         ERROR("DWARF: Double update at address 0x%lx!\n", val);
385 #endif/*COLLECT_BACKTRACES*/
386         return 1;
387     }
388     found = alloc_value();
389     found->left = found->right = NULL;
390     found->key = val;
391 #if COLLECT_BACKTRACES
392     found->backtrace_depth = backtrace(found->backtrace, BACKTRACE_DEPTH);
393 #endif/*COLLECT_BACKTRACES*/
394     if (would_be_parent == NULL) {
395         s_visited_values = found;
396     } else {
397         if (val < would_be_parent->key)
398             would_be_parent->left = found;
399         else
400             would_be_parent->right = found;
401     }
402     return 0;
403 }
404 #else
value_visited(unsigned long val)405 static int value_visited(unsigned long val __attribute__((unused)))
406 {
407     return 0;
408 }
409 #endif /*PARANOIA*/
410 
value_hook(void * data,int size,int val)411 void value_hook(void *data, int size, int val)
412 {
413     shdr_info_t *shdr = find_section(val);
414     s_num_total_patches++;
415     if(shdr == NULL) {
416         PRINT("DWARF: cannot map address 0x%x to any section!\n", val);
417         s_num_failed_patches++;
418         return;
419     }
420     long delta = shdr->shdr.sh_addr - shdr->old_shdr.sh_addr;
421     if(delta) {
422         if (!value_visited((unsigned long)data)) {
423             INFO("DWARF: adjusting %d-byte value at %p: 0x%x -> 0x%x (delta %d per section %s)\n",
424                  size, data,
425                  val, (int)(val + delta), (int)delta,
426                  shdr->name);
427             byte_set(data, size, val + delta);
428         }
429     }
430 }
431 
base_value_pair_hook(void * data,int size,int base,int begin,int end)432 void base_value_pair_hook(void *data, int size,
433                           int base, int begin, int end)
434 {
435     shdr_info_t *shdr = find_section(base + begin);
436     s_num_total_patches++;
437 
438     if (begin > end) {
439         PRINT("DWARF: start > end in range 0x%x:[0x%x, 0x%x)!\n",
440               base,
441               begin,
442               end);
443         s_num_failed_patches++;
444         return;
445     }
446 
447     if(shdr == NULL) {
448         PRINT("DWARF: cannot map range 0x%x:[0x%x, 0x%x) to any section!\n",
449               base,
450               begin,
451               end);
452         s_num_failed_patches++;
453         return;
454     }
455 
456     if (unlikely(begin != end)) {
457         shdr_info_t *end_shdr = find_section(base + end - 1);
458         if (shdr != end_shdr) {
459             printf("DWARF: range 0x%x:[%x, %x) maps to different sections: %s and %s!\n",
460                    base,
461                    begin, end,
462                    shdr->name,
463                    (end_shdr ? end_shdr->name : "(none)"));
464             s_num_failed_patches++;
465             return;
466         }
467     }
468 
469     long delta = shdr->shdr.sh_addr - shdr->old_shdr.sh_addr;
470     if(delta) {
471         if (!value_visited((unsigned long)data)) {
472             INFO("DWARF: adjusting %d-byte value at %p: 0x%x -> 0x%x (delta %d per section %s)\n",
473                  size, data,
474                  begin, (int)(begin + delta), (int)delta,
475                  shdr->name);
476             byte_set(data, size, begin + delta);
477             byte_set(data + size, size, end + delta);
478         }
479     }
480 }
481 
signed_value_hook(void * data,int pointer_size,int is_signed,int value)482 void signed_value_hook(
483     void *data,
484     int pointer_size,
485     int is_signed,
486     int value)
487 {
488     INFO("DWARF frame info: initial PC value: %8x (width %d), %ssigned\n",
489          value, pointer_size,
490          (!is_signed ? "un" : ""));
491 
492     ASSERT(s_shdr_info != NULL);
493 
494     /* Find the section to which the address belongs. */
495     shdr_info_t *shdr = find_section(value);
496     s_num_total_patches++;
497     if(shdr == NULL) {
498         PRINT("DWARF: cannot map address 0x%x to any section!\n", value);
499         s_num_failed_patches++;
500         return;
501     }
502 
503     long delta = shdr->shdr.sh_addr - shdr->old_shdr.sh_addr;
504 
505     INFO("DWARF frame info: initial PC value: 0x%lx -> 0x%lx (delta %ld per section %s).\n",
506          (long)value,
507          (long)(value + delta),
508          delta,
509          shdr->name);
510 
511     if (delta) {
512         if (!value_visited((unsigned long)data)) {
513             value += delta;
514             if (is_signed) {
515                 switch (pointer_size) {
516                 case 1:
517                     value &= 0xFF;
518                     value = (value ^ 0x80) - 0x80;
519                     break;
520                 case 2:
521                     value &= 0xFFFF;
522                     value = (value ^ 0x8000) - 0x8000;
523                     break;
524                 case 4:
525                     value &= 0xFFFFFFFF;
526                     value = (value ^ 0x80000000) - 0x80000000;
527                     break;
528                 case 8:
529                     break;
530                 default:
531                     FAILIF(1, "Unsupported data size %d!\n", pointer_size);
532                 }
533             }
534             byte_set(data, pointer_size, value);
535         }
536     }
537 }
538 
byte_set_little_endian(unsigned char * field,int size,dwarf_vma val)539 static void byte_set_little_endian (unsigned char *field, int size, dwarf_vma val)
540 {
541     switch (size) {
542     case 1:
543         FAILIF(val > 0xFF,
544                "Attempting to set value 0x%lx to %d-bit integer!\n",
545                val, size*8);
546         *((uint8_t *)field) = (uint8_t)val;
547         break;
548     case 2:
549         FAILIF(val > 0xFFFF,
550                "Attempting to set value 0x%lx to %d-bit integer!\n",
551                val, size*8);
552         field[1] = (uint8_t)(val >> 8);
553         field[0] = (uint8_t)val;
554         break;
555     case 4:
556 #if 0
557 		// this will signal false negatives when running on a 64 bit system.
558         FAILIF(val > 0xFFFFFFFF,
559                "Attempting to set value 0x%lx to %d-bit integer!\n",
560                val, size*8);
561 #endif
562         field[3] = (uint8_t)(val >> 23);
563         field[2] = (uint8_t)(val >> 16);
564         field[1] = (uint8_t)(val >> 8);
565         field[0] = (uint8_t)val;
566         break;
567     default:
568         FAILIF(1, "Unhandled data length: %d\n", size);
569     }
570 }
571 
byte_set_big_endian(unsigned char * field,int size,dwarf_vma val)572 static void byte_set_big_endian (unsigned char *field __attribute__((unused)),
573                                  int size __attribute__((unused)),
574                                  dwarf_vma val __attribute__((unused)))
575 {
576     FAILIF(1, "Not implemented.\n");
577 }
578