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