1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use debug::image;
use io;
use path;
// Determines the file path, line number, and column number of a given address
// in the program image. Returns void if unknown. The return value is statically
// allocated.
export fn addr_to_line(
image: *image::image,
addr: uintptr,
) ((const str, uint, uint) | void | io::error | nomem) = {
const dinfo_offs = match (arange_lookup(image, addr)) {
case let offs: u64 =>
yield offs;
case =>
return; // XXX: We could walk .debug_info I guess
};
const dinfo = match (read_debug_info(image, dinfo_offs)?) {
case let rd: debug_info_reader =>
yield rd;
case =>
return;
};
defer debug_info_finish(&dinfo);
let comp_dir = "";
let stmt_list = 0u64, found = false;
for (!found) {
const entry = match (debug_info_next(&dinfo)) {
case io::EOF =>
return;
case nomem =>
return nomem;
case let ent: entry =>
yield ent;
};
defer entry_finish(&entry);
if (entry.tag != DW_TAG_compile_unit) {
continue;
};
for (const field &.. entry.fields) {
switch (field.attr) {
case DW_AT_stmt_list =>
stmt_list = field.constant;
found = true;
case DW_AT_comp_dir =>
comp_dir = field.string;
case => void;
};
};
};
const prog = match (exec_line_program(image, stmt_list)) {
case let prog: line_program =>
yield prog;
case =>
return;
};
defer line_program_finish(&prog);
let last = line_state { ... };
for (const state => line_next(&prog)?) {
defer last = state;
if (state.file == 1) {
continue;
};
if (state.addr < addr) {
continue;
};
// If this is the first state we've seen, use it
if (last.vm_loc != 0) {
state = last;
};
if (state.file == 0) {
return;
};
const file = &prog.head.files[state.file - 1];
static let path = path::buffer { ... };
path::set(&path)!;
if (!path::abs(file.name)) {
let dir = "";
if (file.dir != 0) {
dir = prog.head.dirs[file.dir - 1];
if (!path::abs(dir) && comp_dir != "") {
path::set(&path, comp_dir, dir)!;
} else {
path::set(&path, dir)!;
};
} else if (comp_dir != "") {
path::set(&path, comp_dir)!;
};
};
path::push(&path, file.name)!;
return (path::string(&path), state.line, state.column);
};
};
|