source: mainline/kernel/generic/src/debug/line.c

Last change on this file was 40eab9f, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 20 months ago

Print symbol names and line numbers in stacktraces for init tasks

Only useful in select few situations, but useful nonetheless.

  • Property mode set to 100644
File size: 20.0 KB
Line 
1/*
2 * Copyright (c) 2023 Jiří Zárevúcky
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
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <stdatomic.h>
30
31#include <stdlib.h>
32
33#include <debug/constants.h>
34#include <debug/line.h>
35#include <debug/names.h>
36#include <debug/sections.h>
37
38#include "util.h"
39
40static inline void line_program_reset(struct debug_line_program *lp)
41{
42 lp->address = 0;
43 lp->op_advance = 0;
44 lp->file = 1;
45 lp->line = 1;
46 lp->column = 0;
47 lp->end_sequence = false;
48}
49
50/**
51 * Quickly advance program just past the next sequence end, without processing
52 * anything on the way. This is useful for when we just want to get start
53 * addresses of every range to build a table.
54 *
55 * @param lp
56 */
57static void debug_line_program_skip_to_sequence_end(struct debug_line_program *lp)
58{
59 const uint8_t opcode_base = lp->hdr->opcode_base;
60
61 const uint8_t *program = lp->program;
62 const uint8_t *const program_end = lp->program_end;
63
64 if (lp->end_sequence)
65 line_program_reset(lp);
66
67 while (program < program_end) {
68 uint8_t opcode = read_byte(&program, program_end);
69
70 if (opcode >= opcode_base) {
71 // Special opcode.
72 continue;
73 }
74
75 switch (opcode) {
76 case DW_LNS_fixed_advance_pc:
77 safe_increment(&program, program_end, 2);
78 break;
79
80 case DW_LNS_copy:
81 case DW_LNS_negate_stmt:
82 case DW_LNS_set_basic_block:
83 case DW_LNS_set_prologue_end:
84 case DW_LNS_set_epilogue_begin:
85 case DW_LNS_const_add_pc:
86 break;
87
88 case DW_LNS_advance_pc:
89 case DW_LNS_advance_line:
90 case DW_LNS_set_file:
91 case DW_LNS_set_column:
92 case DW_LNS_set_isa:
93 skip_leb128(&program, program_end);
94 break;
95
96 case 0:
97 // Extended opcodes.
98 size_t length = read_uleb128(&program, program_end);
99
100 if (read_byte(&program, program_end) == DW_LNE_end_sequence) {
101 lp->program = program;
102 lp->end_sequence = true;
103 return;
104 }
105
106 safe_increment(&program, program_end, length - 1);
107 break;
108
109 default:
110 // Unknown standard opcode.
111
112 // If the opcode length array is truncated, there is already
113 // something wrong with the parse, so we don't care if we misparse
114 // the remainder.
115 if (opcode < lp->hdr->standard_opcode_lengths_size) {
116 int len = lp->hdr->standard_opcode_lengths[opcode];
117 for (int i = 0; i < len; i++) {
118 // Drop arguments.
119 skip_leb128(&program, program_end);
120 }
121 }
122 }
123 }
124
125 lp->program = program;
126 lp->truncated = true;
127}
128
129static void transfer_op_advance(struct debug_line_program *lp)
130{
131 if (lp->hdr->version < 5) {
132 lp->address += lp->op_advance * lp->hdr->minimum_instruction_length;
133 lp->op_advance = 0;
134 } else {
135 int maxops = lp->hdr->v5.maximum_operations_per_instruction;
136
137 // For branch prediction, tell GCC maxops is likely to be 1, because
138 // I don't want to punish sensible architectures for VLIW madness.
139 if (__builtin_expect(maxops == 1, 1)) {
140 lp->address += lp->op_advance * lp->hdr->minimum_instruction_length;
141 lp->op_advance = 0;
142 } else {
143 int adv = lp->op_advance;
144 int d = adv / maxops;
145 int r = adv % maxops;
146
147 lp->address += d * lp->hdr->minimum_instruction_length;
148 lp->op_advance = r;
149 }
150 }
151}
152
153static void debug_line_program_advance(struct debug_line_program *lp)
154{
155 const uint8_t opcode_base = lp->hdr->opcode_base;
156 const int8_t line_base = lp->hdr->line_base;
157 const uint8_t line_range = lp->hdr->line_range;
158 const int const_advance = (255 - opcode_base) / lp->hdr->line_range;
159
160 const uint8_t *program = lp->program;
161 const uint8_t *const program_end = lp->program_end;
162
163 if (lp->end_sequence)
164 line_program_reset(lp);
165
166 while (program < program_end) {
167 uint8_t opcode = read_byte(&program, program_end);
168
169 if (opcode >= opcode_base) {
170 // Special opcode.
171 int adjusted = opcode - opcode_base;
172 DEBUGF("DW_LNS_special(%d)\n", adjusted);
173
174 lp->op_advance += adjusted / line_range;
175 lp->line += line_base + (adjusted % line_range);
176
177 transfer_op_advance(lp);
178 lp->program = program;
179 return;
180 }
181
182 const char *opname = dw_lns_name(opcode);
183
184 uint64_t arg;
185 uint16_t arg16;
186
187 switch (opcode) {
188 case DW_LNS_copy:
189 DEBUGF("%s()\n", opname);
190
191 transfer_op_advance(lp);
192 lp->program = program;
193 return;
194
195 case DW_LNS_advance_pc:
196 arg = read_uleb128(&program, program_end);
197 DEBUGF("%s(%" PRIu64 ")\n", opname, arg);
198 lp->op_advance += arg;
199 break;
200
201 case DW_LNS_advance_line:
202 lp->line += read_sleb128(&program, program_end);
203 DEBUGF("%s(line = %d)\n", opname, lp->line);
204 break;
205
206 case DW_LNS_set_file:
207 lp->file = read_uleb128(&program, program_end);
208 DEBUGF("%s(%d)\n", opname, lp->file);
209 break;
210
211 case DW_LNS_set_column:
212 lp->column = read_uleb128(&program, program_end);
213 DEBUGF("%s(%d)\n", opname, lp->column);
214 break;
215
216 case DW_LNS_negate_stmt:
217 case DW_LNS_set_basic_block:
218 case DW_LNS_set_prologue_end:
219 case DW_LNS_set_epilogue_begin:
220 DEBUGF("%s()\n", opname);
221 // ignore
222 break;
223
224 case DW_LNS_set_isa:
225 // discard argument and ignore
226 arg = read_uleb128(&program, program_end);
227 DEBUGF("%s(%" PRIu64 ")\n", opname, arg);
228 break;
229
230 case DW_LNS_const_add_pc:
231 DEBUGF("%s(%d)\n", opname, const_advance);
232 lp->op_advance += const_advance;
233 break;
234
235 case DW_LNS_fixed_advance_pc:
236 arg16 = read_uint16(&program, program_end);
237 DEBUGF("%s(%u)\n", opname, arg16);
238
239 transfer_op_advance(lp);
240 lp->address += arg16;
241 lp->op_advance = 0;
242 break;
243
244 case 0:
245 /* Extended opcodes. */
246 size_t length = read_uleb128(&program, program_end);
247
248 opcode = read_byte(&program, program_end);
249 const char *opname = dw_lne_name(opcode);
250
251 switch (opcode) {
252 case DW_LNE_end_sequence:
253 DEBUGF("%s:%zu()\n", opname, length);
254
255 transfer_op_advance(lp);
256 lp->program = program;
257 lp->end_sequence = true;
258 return;
259
260 case DW_LNE_set_address:
261 lp->address = read_uint(&program, program_end, sizeof(uintptr_t));
262 lp->op_advance = 0;
263
264 DEBUGF("%s:%zu(0x%zx)\n", opname, length, lp->address);
265 break;
266
267 case DW_LNE_set_discriminator:
268 uint64_t arg = read_uleb128(&program, program_end);
269 DEBUGF("%s:%zu(%" PRIu64 ")\n", opname, length, arg);
270 break;
271
272 default:
273 DEBUGF("unknown extended opcode %d:%zu\n", opcode, length);
274 safe_increment(&program, program_end, length - 1);
275 }
276 break;
277
278 default:
279 DEBUGF("unknown standard opcode %d\n", opcode);
280
281 // If the opcode length array is truncated, there is already
282 // something wrong with the parse, so we don't care if we misparse
283 // the remainder.
284 if (opcode < lp->hdr->standard_opcode_lengths_size) {
285 int len = lp->hdr->standard_opcode_lengths[opcode];
286 for (int i = 0; i < len; i++) {
287 // Drop arguments.
288 skip_leb128(&program, program_end);
289 }
290 }
291 }
292 }
293
294 transfer_op_advance(lp);
295 lp->program = program;
296 lp->truncated = true;
297}
298
299static void debug_line_program_header_parse(debug_sections_t *scs, const uint8_t *data, const uint8_t *data_end, struct debug_line_program_header *hdr)
300{
301 const uint8_t *unit_start = data;
302
303#define FIELD(name, spec, expr) (hdr->name = expr, DEBUGF(#name ": %" spec "\n", hdr->name))
304
305 unsigned width;
306
307 FIELD(unit_length, PRId64, read_initial_length(&data, data_end, &width));
308 FIELD(width, "d", width);
309
310 hdr->unit_end = data;
311 safe_increment(&hdr->unit_end, data_end, hdr->unit_length);
312 DEBUGF("unit size: %zu\n", hdr->unit_end - unit_start);
313
314 data_end = hdr->unit_end;
315
316 FIELD(version, "u", read_uint16(&data, data_end));
317
318 if (hdr->version < 3 || hdr->version > 5) {
319 // Not supported.
320 hdr->header_end = hdr->unit_end;
321 return;
322 }
323
324 if (hdr->version >= 5) {
325 FIELD(v5.address_size, "u", read_byte(&data, data_end));
326 FIELD(v5.segment_selector_size, "u", read_byte(&data, data_end));
327 }
328
329 FIELD(header_length, PRIu64, read_uint(&data, data_end, width));
330
331 hdr->header_end = data;
332 safe_increment(&hdr->header_end, data_end, hdr->header_length);
333
334 data_end = hdr->header_end;
335
336 FIELD(minimum_instruction_length, "u", read_byte(&data, data_end));
337 if (hdr->version >= 5)
338 FIELD(v5.maximum_operations_per_instruction, "u", read_byte(&data, data_end));
339 FIELD(default_is_stmt, "u", read_byte(&data, data_end));
340 FIELD(line_base, "d", (int8_t) read_byte(&data, data_end));
341 FIELD(line_range, "u", read_byte(&data, data_end));
342 FIELD(opcode_base, "u", read_byte(&data, data_end));
343
344 hdr->standard_opcode_lengths = data;
345 safe_increment(&data, data_end, hdr->opcode_base - 1);
346 hdr->standard_opcode_lengths_size = data - hdr->standard_opcode_lengths;
347
348 if (hdr->version < 5) {
349 hdr->v3.include_directories = data;
350 while (data < data_end && *data != 0)
351 skip_string(&data, data_end);
352 hdr->v3.include_directories_end = data;
353
354 // Sanitize the list a little.
355 while (hdr->v3.include_directories < hdr->v3.include_directories_end && hdr->v3.include_directories_end[-1] != 0)
356 hdr->v3.include_directories_end--;
357
358 safe_increment(&data, data_end, 1);
359
360 hdr->v3.file_names = data;
361 }
362
363 if (hdr->version >= 5) {
364 FIELD(v5.directory_entry_format_count, "u", read_byte(&data, data_end));
365
366 hdr->v5.directory_entry_format = data;
367 skip_format(&data, data_end, hdr->v5.directory_entry_format_count);
368 hdr->v5.directory_entry_format_end = data;
369
370 print_format("directory_entry_format",
371 hdr->v5.directory_entry_format, hdr->v5.directory_entry_format_end);
372
373 FIELD(v5.directories_count, PRIu64, read_uleb128(&data, data_end));
374
375 hdr->v5.directories = data;
376 skip_formatted_list(&data, data_end, hdr->v5.directories_count,
377 hdr->v5.directory_entry_format, hdr->v5.directory_entry_format_end, width);
378 hdr->v5.directories_end = data;
379
380 print_formatted_list(scs, "directories", hdr->v5.directories, hdr->v5.directories_end,
381 hdr->v5.directory_entry_format, hdr->v5.directory_entry_format_end, width);
382
383 FIELD(v5.file_name_entry_format_count, "u", read_byte(&data, data_end));
384
385 hdr->v5.file_name_entry_format = data;
386 skip_format(&data, data_end, hdr->v5.file_name_entry_format_count);
387 hdr->v5.file_name_entry_format_end = data;
388
389 print_format("file_name_entry_format",
390 hdr->v5.file_name_entry_format, hdr->v5.file_name_entry_format_end);
391
392 FIELD(v5.file_names_count, PRIu64, read_uleb128(&data, data_end));
393
394 hdr->v5.file_names = data;
395 skip_formatted_list(&data, data_end, hdr->v5.file_names_count,
396 hdr->v5.file_name_entry_format, hdr->v5.file_name_entry_format_end, width);
397 hdr->v5.file_names_end = data;
398
399 print_formatted_list(scs, "file_names", hdr->v5.file_names, hdr->v5.file_names_end,
400 hdr->v5.file_name_entry_format, hdr->v5.file_name_entry_format_end, width);
401 }
402}
403
404static bool has_usable_name(const uint8_t *format, const uint8_t *format_end, unsigned width)
405{
406 // Check if there is an appropriate entry we can use.
407 bool has_usable_name = false;
408 const uint8_t *dummy = NULL;
409
410 while (format < format_end) {
411 uint64_t type = read_uleb128(&format, format_end);
412 uint64_t form = read_uleb128(&format, format_end);
413
414 if (type == DW_LNCT_path) {
415 if (form == DW_FORM_string || form == DW_FORM_line_strp) {
416 has_usable_name = true;
417 }
418 }
419
420 if (!skip_data(form, &dummy, NULL, width)) {
421 // Encountered DW_FORM that we don't understand,
422 // which means we can't skip it.
423 return false;
424 }
425 }
426
427 return has_usable_name;
428}
429
430static const char *get_file_name_v3(struct debug_line_program_header *hdr, int file, int *dir)
431{
432 if (file == 0) {
433 // We'd have to find and read the compilation unit header for this one,
434 // and we don't wanna.
435 return NULL;
436 }
437
438 file--;
439
440 const uint8_t *files = hdr->v3.file_names;
441 const uint8_t *files_end = hdr->header_end;
442
443 for (int i = 0; i < file; i++) {
444 if (files >= files_end || *files == 0) {
445 // End of list.
446 return NULL;
447 }
448
449 // Skip an entry.
450 skip_string(&files, files_end);
451 skip_leb128(&files, files_end);
452 skip_leb128(&files, files_end);
453 skip_leb128(&files, files_end);
454 }
455
456 if (files >= files_end || *files == 0)
457 return NULL;
458
459 const char *fn = (const char *) files;
460 skip_string(&files, files_end);
461 *dir = read_uleb128(&files, files_end);
462 return fn;
463}
464
465static const char *get_file_name_v5(debug_sections_t *scs, struct debug_line_program_header *hdr, int file, int *dir)
466{
467 // DWARF5 has dynamic layout for file information, which is why
468 // this is so horrible to decode. Enjoy.
469
470 if (!has_usable_name(hdr->v5.file_name_entry_format, hdr->v5.file_name_entry_format_end, hdr->width))
471 return NULL;
472
473 const uint8_t *fns = hdr->v5.file_names;
474 const uint8_t *fns_end = hdr->v5.file_names_end;
475
476 const char *filename = NULL;
477
478 for (uint8_t i = 0; i < hdr->v5.file_names_count; i++) {
479
480 const uint8_t *fnef = hdr->v5.file_name_entry_format;
481 const uint8_t *fnef_end = hdr->v5.file_name_entry_format_end;
482
483 for (uint8_t j = 0; j < hdr->v5.file_name_entry_format_count; j++) {
484 uint64_t type = read_uleb128(&fnef, fnef_end);
485 uint64_t form = read_uleb128(&fnef, fnef_end);
486
487 if (i == file && type == DW_LNCT_path) {
488 if (form == DW_FORM_string) {
489 filename = read_string(&fns, fns_end);
490 continue;
491 }
492
493 if (form == DW_FORM_line_strp) {
494 uint64_t offset = read_uint(&fns, fns_end, hdr->width);
495 if (offset < scs->debug_line_str_size) {
496 filename = scs->debug_line_str + offset;
497 }
498 continue;
499 }
500 }
501
502 if (i == file && type == DW_LNCT_directory_index) {
503 if (form == DW_FORM_data1) {
504 *dir = read_byte(&fns, fns_end);
505 continue;
506 }
507
508 if (form == DW_FORM_data2) {
509 *dir = read_uint16(&fns, fns_end);
510 continue;
511 }
512
513 if (form == DW_FORM_udata) {
514 *dir = read_uleb128(&fns, fns_end);
515 continue;
516 }
517 }
518
519 skip_data(form, &fns, fns_end, hdr->width);
520 }
521
522 if (i == file)
523 break;
524 }
525
526 return filename;
527}
528
529static const char *get_file_name(debug_sections_t *scs, struct debug_line_program_header *hdr, int file, int *dir)
530{
531 switch (hdr->version) {
532 case 3:
533 case 4:
534 return get_file_name_v3(hdr, file, dir);
535 case 5:
536 return get_file_name_v5(scs, hdr, file, dir);
537 default:
538 return NULL;
539 }
540}
541
542static const char *get_dir_name_v3(struct debug_line_program_header *hdr, int dir)
543{
544 if (dir == 0)
545 return ".";
546
547 const uint8_t *dirs = hdr->v3.include_directories;
548 const uint8_t *dirs_end = hdr->v3.include_directories_end;
549
550 for (int i = 1; i < dir; i++) {
551 skip_string(&dirs, dirs_end);
552 }
553
554 if (dirs < dirs_end)
555 return (const char *) dirs;
556 else
557 return NULL;
558}
559
560static const char *get_dir_name_v5(debug_sections_t *scs, struct debug_line_program_header *hdr, int dir)
561{
562 // TODO: basically a copypaste of get_file_name(). Try to deduplicate it.
563
564 if (!has_usable_name(hdr->v5.directory_entry_format, hdr->v5.directory_entry_format_end, hdr->width))
565 return NULL;
566
567 const uint8_t *fns = hdr->v5.directories;
568 const uint8_t *fns_end = hdr->v5.directories_end;
569
570 const char *filename = NULL;
571
572 for (uint8_t i = 0; i < hdr->v5.directories_count; i++) {
573
574 const uint8_t *fnef = hdr->v5.directory_entry_format;
575 const uint8_t *fnef_end = hdr->v5.directory_entry_format_end;
576
577 for (uint8_t j = 0; j < hdr->v5.directory_entry_format_count; j++) {
578 uint64_t type = read_uleb128(&fnef, fnef_end);
579 uint64_t form = read_uleb128(&fnef, fnef_end);
580
581 if (i == dir && type == DW_LNCT_path) {
582 if (form == DW_FORM_string) {
583 filename = read_string(&fns, fns_end);
584 continue;
585 }
586
587 if (form == DW_FORM_line_strp) {
588 uint64_t offset = read_uint(&fns, fns_end, hdr->width);
589 if (offset < scs->debug_line_str_size) {
590 filename = scs->debug_line_str + offset;
591 }
592 continue;
593 }
594 }
595
596 skip_data(form, &fns, fns_end, hdr->width);
597 }
598
599 if (i == dir)
600 break;
601 }
602
603 return filename;
604}
605
606static const char *get_dir_name(debug_sections_t *scs, struct debug_line_program_header *hdr, int dir)
607{
608 switch (hdr->version) {
609 case 3:
610 case 4:
611 return get_dir_name_v3(hdr, dir);
612 case 5:
613 return get_dir_name_v5(scs, hdr, dir);
614 default:
615 return NULL;
616 }
617}
618
619static const uint8_t *find_line_program(debug_sections_t *scs, uintptr_t addr)
620{
621 // TODO: use .debug_aranges to find the data quickly
622 // This implementation just iterates through the entire .debug_line
623
624 uintptr_t closest_addr = 0;
625 const uint8_t *closest_prog = NULL;
626
627 const uint8_t *debug_line_ptr = scs->debug_line;
628 const uint8_t *const debug_line_end = scs->debug_line + scs->debug_line_size;
629
630 while (debug_line_ptr < debug_line_end) {
631 const uint8_t *prog = debug_line_ptr;
632
633 // Parse header
634 struct debug_line_program_header hdr = { };
635 debug_line_program_header_parse(scs, prog, debug_line_end, &hdr);
636 assert(hdr.unit_end > debug_line_ptr);
637 assert(hdr.unit_end <= debug_line_end);
638 debug_line_ptr = hdr.unit_end;
639
640 struct debug_line_program lp = debug_line_program_create(
641 hdr.header_end, hdr.unit_end, &hdr);
642
643 while (lp.program < lp.program_end) {
644 // Find the start address of every sequence
645
646 debug_line_program_advance(&lp);
647 DEBUGF("<< address: %zx, line: %d, column: %d >>\n", lp.address, lp.line, lp.column);
648
649 if (!lp.truncated) {
650 if (lp.address <= addr && lp.address > closest_addr) {
651 closest_addr = lp.address;
652 closest_prog = prog;
653 }
654 }
655
656 if (!lp.end_sequence) {
657 debug_line_program_skip_to_sequence_end(&lp);
658 assert(lp.truncated || lp.end_sequence);
659 }
660 }
661 }
662
663 return closest_prog;
664}
665
666static bool get_info(const struct debug_line_program_header *hdr, uintptr_t addr, int op_index, int *file, int *line, int *column)
667{
668 struct debug_line_program lp = debug_line_program_create(
669 hdr->header_end, hdr->unit_end, hdr);
670
671 int last_file = 0;
672 int last_line = 0;
673 int last_column = 0;
674
675 while (lp.program < lp.program_end) {
676 bool first = lp.end_sequence;
677
678 debug_line_program_advance(&lp);
679
680 if (lp.truncated)
681 continue;
682
683 /*
684 * Return previous entry when we pass the target address, because
685 * the address may not be aligned perfectly on instruction boundary.
686 */
687 if (lp.address > addr || (lp.address == addr && lp.op_advance > op_index)) {
688 if (first) {
689 // First address is already too large, skip to the next sequence.
690 debug_line_program_skip_to_sequence_end(&lp);
691 continue;
692 }
693
694 *file = last_file;
695 *line = last_line;
696 *column = last_column;
697 return true;
698 }
699
700 last_file = lp.file;
701 last_line = lp.line;
702 last_column = lp.column;
703 }
704
705 return false;
706}
707
708bool debug_line_get_address_info(debug_sections_t *scs, uintptr_t addr, int op_index, const char **file_name, const char **dir_name, int *line, int *column)
709{
710 const uint8_t *data = find_line_program(scs, addr);
711 if (data == NULL) {
712 return false;
713 }
714
715 const uint8_t *const debug_line_end = scs->debug_line + scs->debug_line_size;
716
717 struct debug_line_program_header hdr = { };
718 debug_line_program_header_parse(scs, data, debug_line_end, &hdr);
719 assert(hdr.unit_end > data);
720 assert(hdr.unit_end <= debug_line_end);
721
722 int dir = -1;
723 int file = -1;
724
725 if (!get_info(&hdr, addr, op_index, &file, line, column)) {
726 // printf("no info for 0x%zx: prog offset 0x%zx\n", addr, (void *) data - debug_line);
727 return false;
728 }
729
730 // printf("got info for 0x%zx: file %d, line %d, column %d\n", addr, file, *line, *column);
731
732 if (file >= 0)
733 *file_name = get_file_name(scs, &hdr, file, &dir);
734
735 if (dir >= 0)
736 *dir_name = get_dir_name(scs, &hdr, dir);
737
738 return true;
739}
Note: See TracBrowser for help on using the repository browser.