File: addr_to_line.ha

package info (click to toggle)
hare 0.26.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,352 kB
  • sloc: asm: 1,374; makefile: 123; sh: 117; lisp: 101
file content (108 lines) | stat: -rw-r--r-- 2,316 bytes parent folder | download
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);
	};
};