hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };