diff options
Diffstat (limited to 'tools/perf/util/genelf_debug.c')
-rw-r--r-- | tools/perf/util/genelf_debug.c | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/tools/perf/util/genelf_debug.c b/tools/perf/util/genelf_debug.c new file mode 100644 index 000000000000..5980f7d256b1 --- /dev/null +++ b/tools/perf/util/genelf_debug.c | |||
@@ -0,0 +1,610 @@ | |||
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 | } | ||