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