Commit | Line | Data |
---|---|---|
598b7c69 SE |
1 | /* |
2 | * genelf_debug.c | |
3 | * Copyright (C) 2015, Google, Inc | |
4 | * | |
5 | * Contributed by: | |
6 | * Stephane Eranian <eranian@google.com> | |
7 | * | |
8 | * Released under the GPL v2. | |
9 | * | |
10 | * based on GPLv2 source code from Oprofile | |
11 | * @remark Copyright 2007 OProfile authors | |
12 | * @author Philippe Elie | |
13 | */ | |
14 | #include <sys/types.h> | |
15 | #include <stdio.h> | |
16 | #include <getopt.h> | |
17 | #include <stddef.h> | |
18 | #include <libelf.h> | |
19 | #include <string.h> | |
20 | #include <stdlib.h> | |
21 | #include <inttypes.h> | |
22 | #include <limits.h> | |
23 | #include <fcntl.h> | |
24 | #include <err.h> | |
25 | #include <dwarf.h> | |
26 | ||
27 | #include "perf.h" | |
28 | #include "genelf.h" | |
29 | #include "../util/jitdump.h" | |
30 | ||
31 | #define BUFFER_EXT_DFL_SIZE (4 * 1024) | |
32 | ||
33 | typedef uint32_t uword; | |
34 | typedef uint16_t uhalf; | |
35 | typedef int32_t sword; | |
36 | typedef int16_t shalf; | |
37 | typedef uint8_t ubyte; | |
38 | typedef int8_t sbyte; | |
39 | ||
40 | struct buffer_ext { | |
41 | size_t cur_pos; | |
42 | size_t max_sz; | |
43 | void *data; | |
44 | }; | |
45 | ||
46 | static void | |
47 | buffer_ext_dump(struct buffer_ext *be, const char *msg) | |
48 | { | |
49 | size_t i; | |
50 | warnx("DUMP for %s", msg); | |
51 | for (i = 0 ; i < be->cur_pos; i++) | |
52 | warnx("%4zu 0x%02x", i, (((char *)be->data)[i]) & 0xff); | |
53 | } | |
54 | ||
55 | static inline int | |
56 | buffer_ext_add(struct buffer_ext *be, void *addr, size_t sz) | |
57 | { | |
58 | void *tmp; | |
59 | size_t be_sz = be->max_sz; | |
60 | ||
61 | retry: | |
62 | if ((be->cur_pos + sz) < be_sz) { | |
63 | memcpy(be->data + be->cur_pos, addr, sz); | |
64 | be->cur_pos += sz; | |
65 | return 0; | |
66 | } | |
67 | ||
68 | if (!be_sz) | |
69 | be_sz = BUFFER_EXT_DFL_SIZE; | |
70 | else | |
71 | be_sz <<= 1; | |
72 | ||
73 | tmp = realloc(be->data, be_sz); | |
74 | if (!tmp) | |
75 | return -1; | |
76 | ||
77 | be->data = tmp; | |
78 | be->max_sz = be_sz; | |
79 | ||
80 | goto retry; | |
81 | } | |
82 | ||
83 | static void | |
84 | buffer_ext_init(struct buffer_ext *be) | |
85 | { | |
86 | be->data = NULL; | |
87 | be->cur_pos = 0; | |
88 | be->max_sz = 0; | |
89 | } | |
90 | ||
91 | static inline size_t | |
92 | buffer_ext_size(struct buffer_ext *be) | |
93 | { | |
94 | return be->cur_pos; | |
95 | } | |
96 | ||
97 | static inline void * | |
98 | buffer_ext_addr(struct buffer_ext *be) | |
99 | { | |
100 | return be->data; | |
101 | } | |
102 | ||
103 | struct debug_line_header { | |
104 | // Not counting this field | |
105 | uword total_length; | |
106 | // version number (2 currently) | |
107 | uhalf version; | |
108 | // relative offset from next field to | |
109 | // program statement | |
110 | uword prolog_length; | |
111 | ubyte minimum_instruction_length; | |
112 | ubyte default_is_stmt; | |
113 | // line_base - see DWARF 2 specs | |
114 | sbyte line_base; | |
115 | // line_range - see DWARF 2 specs | |
116 | ubyte line_range; | |
117 | // number of opcode + 1 | |
118 | ubyte opcode_base; | |
119 | /* follow the array of opcode args nr: ubytes [nr_opcode_base] */ | |
120 | /* follow the search directories index, zero terminated string | |
121 | * terminated by an empty string. | |
122 | */ | |
123 | /* follow an array of { filename, LEB128, LEB128, LEB128 }, first is | |
124 | * the directory index entry, 0 means current directory, then mtime | |
125 | * and filesize, last entry is followed by en empty string. | |
126 | */ | |
127 | /* follow the first program statement */ | |
128 | } __attribute__((packed)); | |
129 | ||
130 | /* DWARF 2 spec talk only about one possible compilation unit header while | |
131 | * binutils can handle two flavours of dwarf 2, 32 and 64 bits, this is not | |
132 | * related to the used arch, an ELF 32 can hold more than 4 Go of debug | |
133 | * information. For now we handle only DWARF 2 32 bits comp unit. It'll only | |
134 | * become a problem if we generate more than 4GB of debug information. | |
135 | */ | |
136 | struct compilation_unit_header { | |
137 | uword total_length; | |
138 | uhalf version; | |
139 | uword debug_abbrev_offset; | |
140 | ubyte pointer_size; | |
141 | } __attribute__((packed)); | |
142 | ||
143 | #define DW_LNS_num_opcode (DW_LNS_set_isa + 1) | |
144 | ||
145 | /* field filled at run time are marked with -1 */ | |
146 | static struct debug_line_header const default_debug_line_header = { | |
147 | .total_length = -1, | |
148 | .version = 2, | |
149 | .prolog_length = -1, | |
150 | .minimum_instruction_length = 1, /* could be better when min instruction size != 1 */ | |
151 | .default_is_stmt = 1, /* we don't take care about basic block */ | |
152 | .line_base = -5, /* sensible value for line base ... */ | |
153 | .line_range = -14, /* ... and line range are guessed statically */ | |
154 | .opcode_base = DW_LNS_num_opcode | |
155 | }; | |
156 | ||
157 | static ubyte standard_opcode_length[] = | |
158 | { | |
159 | 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 | |
160 | }; | |
161 | #if 0 | |
162 | { | |
163 | [DW_LNS_advance_pc] = 1, | |
164 | [DW_LNS_advance_line] = 1, | |
165 | [DW_LNS_set_file] = 1, | |
166 | [DW_LNS_set_column] = 1, | |
167 | [DW_LNS_fixed_advance_pc] = 1, | |
168 | [DW_LNS_set_isa] = 1, | |
169 | }; | |
170 | #endif | |
171 | ||
172 | /* field filled at run time are marked with -1 */ | |
173 | static struct compilation_unit_header default_comp_unit_header = { | |
174 | .total_length = -1, | |
175 | .version = 2, | |
176 | .debug_abbrev_offset = 0, /* we reuse the same abbrev entries for all comp unit */ | |
177 | .pointer_size = sizeof(void *) | |
178 | }; | |
179 | ||
180 | static void emit_uword(struct buffer_ext *be, uword data) | |
181 | { | |
182 | buffer_ext_add(be, &data, sizeof(uword)); | |
183 | } | |
184 | ||
185 | static void emit_string(struct buffer_ext *be, const char *s) | |
186 | { | |
187 | buffer_ext_add(be, (void *)s, strlen(s) + 1); | |
188 | } | |
189 | ||
190 | static void emit_unsigned_LEB128(struct buffer_ext *be, | |
191 | unsigned long data) | |
192 | { | |
193 | do { | |
194 | ubyte cur = data & 0x7F; | |
195 | data >>= 7; | |
196 | if (data) | |
197 | cur |= 0x80; | |
198 | buffer_ext_add(be, &cur, 1); | |
199 | } while (data); | |
200 | } | |
201 | ||
202 | static void emit_signed_LEB128(struct buffer_ext *be, long data) | |
203 | { | |
204 | int more = 1; | |
205 | int negative = data < 0; | |
206 | int size = sizeof(long) * CHAR_BIT; | |
207 | while (more) { | |
208 | ubyte cur = data & 0x7F; | |
209 | data >>= 7; | |
210 | if (negative) | |
211 | data |= - (1 << (size - 7)); | |
212 | if ((data == 0 && !(cur & 0x40)) || | |
213 | (data == -1l && (cur & 0x40))) | |
214 | more = 0; | |
215 | else | |
216 | cur |= 0x80; | |
217 | buffer_ext_add(be, &cur, 1); | |
218 | } | |
219 | } | |
220 | ||
221 | static void emit_extended_opcode(struct buffer_ext *be, ubyte opcode, | |
222 | void *data, size_t data_len) | |
223 | { | |
224 | buffer_ext_add(be, (char *)"", 1); | |
225 | ||
226 | emit_unsigned_LEB128(be, data_len + 1); | |
227 | ||
228 | buffer_ext_add(be, &opcode, 1); | |
229 | buffer_ext_add(be, data, data_len); | |
230 | } | |
231 | ||
232 | static void emit_opcode(struct buffer_ext *be, ubyte opcode) | |
233 | { | |
234 | buffer_ext_add(be, &opcode, 1); | |
235 | } | |
236 | ||
237 | static void emit_opcode_signed(struct buffer_ext *be, | |
238 | ubyte opcode, long data) | |
239 | { | |
240 | buffer_ext_add(be, &opcode, 1); | |
241 | emit_signed_LEB128(be, data); | |
242 | } | |
243 | ||
244 | static void emit_opcode_unsigned(struct buffer_ext *be, ubyte opcode, | |
245 | unsigned long data) | |
246 | { | |
247 | buffer_ext_add(be, &opcode, 1); | |
248 | emit_unsigned_LEB128(be, data); | |
249 | } | |
250 | ||
251 | static void emit_advance_pc(struct buffer_ext *be, unsigned long delta_pc) | |
252 | { | |
253 | emit_opcode_unsigned(be, DW_LNS_advance_pc, delta_pc); | |
254 | } | |
255 | ||
256 | static void emit_advance_lineno(struct buffer_ext *be, long delta_lineno) | |
257 | { | |
258 | emit_opcode_signed(be, DW_LNS_advance_line, delta_lineno); | |
259 | } | |
260 | ||
261 | static void emit_lne_end_of_sequence(struct buffer_ext *be) | |
262 | { | |
263 | emit_extended_opcode(be, DW_LNE_end_sequence, NULL, 0); | |
264 | } | |
265 | ||
266 | static void emit_set_file(struct buffer_ext *be, unsigned long idx) | |
267 | { | |
268 | emit_opcode_unsigned(be, DW_LNS_set_file, idx); | |
269 | } | |
270 | ||
271 | static void emit_lne_define_filename(struct buffer_ext *be, | |
272 | const char *filename) | |
273 | { | |
274 | buffer_ext_add(be, (void *)"", 1); | |
275 | ||
276 | /* LNE field, strlen(filename) + zero termination, 3 bytes for: the dir entry, timestamp, filesize */ | |
277 | emit_unsigned_LEB128(be, strlen(filename) + 5); | |
278 | emit_opcode(be, DW_LNE_define_file); | |
279 | emit_string(be, filename); | |
280 | /* directory index 0=do not know */ | |
281 | emit_unsigned_LEB128(be, 0); | |
282 | /* last modification date on file 0=do not know */ | |
283 | emit_unsigned_LEB128(be, 0); | |
284 | /* filesize 0=do not know */ | |
285 | emit_unsigned_LEB128(be, 0); | |
286 | } | |
287 | ||
288 | static void emit_lne_set_address(struct buffer_ext *be, | |
289 | void *address) | |
290 | { | |
291 | emit_extended_opcode(be, DW_LNE_set_address, &address, sizeof(unsigned long)); | |
292 | } | |
293 | ||
294 | static ubyte get_special_opcode(struct debug_entry *ent, | |
295 | unsigned int last_line, | |
296 | unsigned long last_vma) | |
297 | { | |
298 | unsigned int temp; | |
299 | unsigned long delta_addr; | |
300 | ||
301 | /* | |
302 | * delta from line_base | |
303 | */ | |
304 | temp = (ent->lineno - last_line) - default_debug_line_header.line_base; | |
305 | ||
306 | if (temp >= default_debug_line_header.line_range) | |
307 | return 0; | |
308 | ||
309 | /* | |
310 | * delta of addresses | |
311 | */ | |
312 | delta_addr = (ent->addr - last_vma) / default_debug_line_header.minimum_instruction_length; | |
313 | ||
314 | /* This is not sufficient to ensure opcode will be in [0-256] but | |
315 | * sufficient to ensure when summing with the delta lineno we will | |
316 | * not overflow the unsigned long opcode */ | |
317 | ||
318 | if (delta_addr <= 256 / default_debug_line_header.line_range) { | |
319 | unsigned long opcode = temp + | |
320 | (delta_addr * default_debug_line_header.line_range) + | |
321 | default_debug_line_header.opcode_base; | |
322 | ||
323 | return opcode <= 255 ? opcode : 0; | |
324 | } | |
325 | return 0; | |
326 | } | |
327 | ||
328 | static void emit_lineno_info(struct buffer_ext *be, | |
329 | struct debug_entry *ent, size_t nr_entry, | |
330 | unsigned long code_addr) | |
331 | { | |
332 | size_t i; | |
333 | ||
334 | /* | |
335 | * Machine state at start of a statement program | |
336 | * address = 0 | |
337 | * file = 1 | |
338 | * line = 1 | |
339 | * column = 0 | |
340 | * is_stmt = default_is_stmt as given in the debug_line_header | |
341 | * basic block = 0 | |
342 | * end sequence = 0 | |
343 | */ | |
344 | ||
345 | /* start state of the state machine we take care of */ | |
346 | unsigned long last_vma = code_addr; | |
347 | char const *cur_filename = NULL; | |
348 | unsigned long cur_file_idx = 0; | |
349 | int last_line = 1; | |
350 | ||
351 | emit_lne_set_address(be, (void *)code_addr); | |
352 | ||
353 | for (i = 0; i < nr_entry; i++, ent = debug_entry_next(ent)) { | |
354 | int need_copy = 0; | |
355 | ubyte special_opcode; | |
356 | ||
357 | /* | |
358 | * check if filename changed, if so add it | |
359 | */ | |
360 | if (!cur_filename || strcmp(cur_filename, ent->name)) { | |
361 | emit_lne_define_filename(be, ent->name); | |
362 | cur_filename = ent->name; | |
363 | emit_set_file(be, ++cur_file_idx); | |
364 | need_copy = 1; | |
365 | } | |
366 | ||
367 | special_opcode = get_special_opcode(ent, last_line, last_vma); | |
368 | if (special_opcode != 0) { | |
369 | last_line = ent->lineno; | |
370 | last_vma = ent->addr; | |
371 | emit_opcode(be, special_opcode); | |
372 | } else { | |
373 | /* | |
374 | * lines differ, emit line delta | |
375 | */ | |
376 | if (last_line != ent->lineno) { | |
377 | emit_advance_lineno(be, ent->lineno - last_line); | |
378 | last_line = ent->lineno; | |
379 | need_copy = 1; | |
380 | } | |
381 | /* | |
382 | * addresses differ, emit address delta | |
383 | */ | |
384 | if (last_vma != ent->addr) { | |
385 | emit_advance_pc(be, ent->addr - last_vma); | |
386 | last_vma = ent->addr; | |
387 | need_copy = 1; | |
388 | } | |
389 | /* | |
390 | * add new row to matrix | |
391 | */ | |
392 | if (need_copy) | |
393 | emit_opcode(be, DW_LNS_copy); | |
394 | } | |
395 | } | |
396 | } | |
397 | ||
398 | static void add_debug_line(struct buffer_ext *be, | |
399 | struct debug_entry *ent, size_t nr_entry, | |
400 | unsigned long code_addr) | |
401 | { | |
402 | struct debug_line_header * dbg_header; | |
403 | size_t old_size; | |
404 | ||
405 | old_size = buffer_ext_size(be); | |
406 | ||
407 | buffer_ext_add(be, (void *)&default_debug_line_header, | |
408 | sizeof(default_debug_line_header)); | |
409 | ||
410 | buffer_ext_add(be, &standard_opcode_length, sizeof(standard_opcode_length)); | |
411 | ||
412 | // empty directory entry | |
413 | buffer_ext_add(be, (void *)"", 1); | |
414 | ||
415 | // empty filename directory | |
416 | buffer_ext_add(be, (void *)"", 1); | |
417 | ||
418 | dbg_header = buffer_ext_addr(be) + old_size; | |
419 | dbg_header->prolog_length = (buffer_ext_size(be) - old_size) - | |
420 | offsetof(struct debug_line_header, minimum_instruction_length); | |
421 | ||
422 | emit_lineno_info(be, ent, nr_entry, code_addr); | |
423 | ||
424 | emit_lne_end_of_sequence(be); | |
425 | ||
426 | dbg_header = buffer_ext_addr(be) + old_size; | |
427 | dbg_header->total_length = (buffer_ext_size(be) - old_size) - | |
428 | offsetof(struct debug_line_header, version); | |
429 | } | |
430 | ||
431 | static void | |
432 | add_debug_abbrev(struct buffer_ext *be) | |
433 | { | |
434 | emit_unsigned_LEB128(be, 1); | |
435 | emit_unsigned_LEB128(be, DW_TAG_compile_unit); | |
436 | emit_unsigned_LEB128(be, DW_CHILDREN_yes); | |
437 | emit_unsigned_LEB128(be, DW_AT_stmt_list); | |
438 | emit_unsigned_LEB128(be, DW_FORM_data4); | |
439 | emit_unsigned_LEB128(be, 0); | |
440 | emit_unsigned_LEB128(be, 0); | |
441 | emit_unsigned_LEB128(be, 0); | |
442 | } | |
443 | ||
444 | static void | |
445 | add_compilation_unit(struct buffer_ext *be, | |
446 | size_t offset_debug_line) | |
447 | { | |
448 | struct compilation_unit_header *comp_unit_header; | |
449 | size_t old_size = buffer_ext_size(be); | |
450 | ||
451 | buffer_ext_add(be, &default_comp_unit_header, | |
452 | sizeof(default_comp_unit_header)); | |
453 | ||
454 | emit_unsigned_LEB128(be, 1); | |
455 | emit_uword(be, offset_debug_line); | |
456 | ||
457 | comp_unit_header = buffer_ext_addr(be) + old_size; | |
458 | comp_unit_header->total_length = (buffer_ext_size(be) - old_size) - | |
459 | offsetof(struct compilation_unit_header, version); | |
460 | } | |
461 | ||
462 | static int | |
463 | jit_process_debug_info(uint64_t code_addr, | |
464 | void *debug, int nr_debug_entries, | |
465 | struct buffer_ext *dl, | |
466 | struct buffer_ext *da, | |
467 | struct buffer_ext *di) | |
468 | { | |
469 | struct debug_entry *ent = debug; | |
470 | int i; | |
471 | ||
472 | for (i = 0; i < nr_debug_entries; i++) { | |
473 | ent->addr = ent->addr - code_addr; | |
474 | ent = debug_entry_next(ent); | |
475 | } | |
476 | add_compilation_unit(di, buffer_ext_size(dl)); | |
477 | add_debug_line(dl, debug, nr_debug_entries, 0); | |
478 | add_debug_abbrev(da); | |
479 | if (0) buffer_ext_dump(da, "abbrev"); | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
484 | int | |
485 | jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries) | |
486 | { | |
487 | Elf_Data *d; | |
488 | Elf_Scn *scn; | |
489 | Elf_Shdr *shdr; | |
490 | struct buffer_ext dl, di, da; | |
491 | int ret; | |
492 | ||
493 | buffer_ext_init(&dl); | |
494 | buffer_ext_init(&di); | |
495 | buffer_ext_init(&da); | |
496 | ||
497 | ret = jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di); | |
498 | if (ret) | |
499 | return -1; | |
500 | /* | |
501 | * setup .debug_line section | |
502 | */ | |
503 | scn = elf_newscn(e); | |
504 | if (!scn) { | |
505 | warnx("cannot create section"); | |
506 | return -1; | |
507 | } | |
508 | ||
509 | d = elf_newdata(scn); | |
510 | if (!d) { | |
511 | warnx("cannot get new data"); | |
512 | return -1; | |
513 | } | |
514 | ||
515 | d->d_align = 1; | |
516 | d->d_off = 0LL; | |
517 | d->d_buf = buffer_ext_addr(&dl); | |
518 | d->d_type = ELF_T_BYTE; | |
519 | d->d_size = buffer_ext_size(&dl); | |
520 | d->d_version = EV_CURRENT; | |
521 | ||
522 | shdr = elf_getshdr(scn); | |
523 | if (!shdr) { | |
524 | warnx("cannot get section header"); | |
525 | return -1; | |
526 | } | |
527 | ||
528 | shdr->sh_name = 52; /* .debug_line */ | |
529 | shdr->sh_type = SHT_PROGBITS; | |
530 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | |
531 | shdr->sh_flags = 0; | |
532 | shdr->sh_entsize = 0; | |
533 | ||
534 | /* | |
535 | * setup .debug_info section | |
536 | */ | |
537 | scn = elf_newscn(e); | |
538 | if (!scn) { | |
539 | warnx("cannot create section"); | |
540 | return -1; | |
541 | } | |
542 | ||
543 | d = elf_newdata(scn); | |
544 | if (!d) { | |
545 | warnx("cannot get new data"); | |
546 | return -1; | |
547 | } | |
548 | ||
549 | d->d_align = 1; | |
550 | d->d_off = 0LL; | |
551 | d->d_buf = buffer_ext_addr(&di); | |
552 | d->d_type = ELF_T_BYTE; | |
553 | d->d_size = buffer_ext_size(&di); | |
554 | d->d_version = EV_CURRENT; | |
555 | ||
556 | shdr = elf_getshdr(scn); | |
557 | if (!shdr) { | |
558 | warnx("cannot get section header"); | |
559 | return -1; | |
560 | } | |
561 | ||
562 | shdr->sh_name = 64; /* .debug_info */ | |
563 | shdr->sh_type = SHT_PROGBITS; | |
564 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | |
565 | shdr->sh_flags = 0; | |
566 | shdr->sh_entsize = 0; | |
567 | ||
568 | /* | |
569 | * setup .debug_abbrev section | |
570 | */ | |
571 | scn = elf_newscn(e); | |
572 | if (!scn) { | |
573 | warnx("cannot create section"); | |
574 | return -1; | |
575 | } | |
576 | ||
577 | d = elf_newdata(scn); | |
578 | if (!d) { | |
579 | warnx("cannot get new data"); | |
580 | return -1; | |
581 | } | |
582 | ||
583 | d->d_align = 1; | |
584 | d->d_off = 0LL; | |
585 | d->d_buf = buffer_ext_addr(&da); | |
586 | d->d_type = ELF_T_BYTE; | |
587 | d->d_size = buffer_ext_size(&da); | |
588 | d->d_version = EV_CURRENT; | |
589 | ||
590 | shdr = elf_getshdr(scn); | |
591 | if (!shdr) { | |
592 | warnx("cannot get section header"); | |
593 | return -1; | |
594 | } | |
595 | ||
596 | shdr->sh_name = 76; /* .debug_info */ | |
597 | shdr->sh_type = SHT_PROGBITS; | |
598 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | |
599 | shdr->sh_flags = 0; | |
600 | shdr->sh_entsize = 0; | |
601 | ||
602 | /* | |
603 | * now we update the ELF image with all the sections | |
604 | */ | |
605 | if (elf_update(e, ELF_C_WRITE) < 0) { | |
606 | warnx("elf_update debug failed"); | |
607 | return -1; | |
608 | } | |
609 | return 0; | |
610 | } |