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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use errors;
use debug::image;
use format::elf;
use io;
use memio;
def INFO_VERSION: u16 = 4;
export type debug_info_reader = struct {
image: *image::image,
abbrev: abbrev_table,
strings: (string_table | void),
mem: *memio::stream,
rd: *table_reader,
};
// Reads the debug info from a DWARF image. Returns a [[debug_info_reader]],
// call [[debug_info_next]] to retrieve the next DIE.
//
// Pass the return value to [[debug_info_finish]] after you're done with it.
export fn read_debug_info(
image: *image::image,
offs: u64,
) (debug_info_reader | void | io::error | nomem) = {
const sec = match (image::section_byname(image, ".debug_info")) {
case let sec: *elf::section64 =>
yield sec;
case null =>
return;
};
let ok = false;
const memrd = alloc(image::section_reader(image, sec))?;
defer if (!ok) free(memrd);
io::seek(memrd, offs: io::off, io::whence::SET)?;
const rd = match (new_table_reader(memrd, true)) {
case let rd: table_reader =>
yield alloc(rd)?;
case io::EOF =>
return;
};
defer if (!ok) free(rd);
const ver = read_uhalf(rd)!;
const abbrev_offs = read_secword(rd)!;
const asize = read_ubyte(rd)!;
assert(ver <= INFO_VERSION, "debug::dwarf: unsupported .debug_info version");
assert(asize == 8, "debug::dwarf: unsupported address size in .debug_info");
const abbrevs = match (load_abbrevs(image, abbrev_offs)?) {
case let tab: abbrev_table =>
yield tab;
case void =>
return;
};
ok = true;
return debug_info_reader {
image = image,
abbrev = abbrevs,
strings = load_strings(image)?,
mem = memrd,
rd = rd,
};
};
// Returns the next debug info [[entry]] (DIE) from a [[debug_info_reader]].
// Pass the return value to [[entry_finish]] when done.
export fn debug_info_next(di: *debug_info_reader) (entry | io::EOF | nomem) = {
if (read_iseof(di.rd)) {
return io::EOF;
};
let code = read_uleb128(di.rd)!;
for (code == 0) {
if (read_iseof(di.rd)) {
return io::EOF;
};
code = read_uleb128(di.rd)!;
};
const ref = get_abbrev(&di.abbrev, code);
assert(ref != null, "debug::dwarf: unknown abbreviated tag");
match (read_die(di, di.rd, ref as *abbrev)) {
case io::error => abort();
case nomem => return nomem;
case let ent: entry =>
return ent;
};
};
// Frees resources associated with a [[debug_info_reader]].
export fn debug_info_finish(di: *debug_info_reader) void = {
free(di.mem);
free(di.rd);
};
// A debug entry.
export type entry = struct {
tag: u32,
children: bool,
fields: []field,
};
// Frees resources associated with an [[entry]].
export fn entry_finish(ent: *entry) void = {
free(ent.fields);
};
// A debug [[entry]] field.
export type field = struct {
attr: u32,
form: u32,
union {
address: uintptr,
block: []u8,
constant: u64,
string: const str,
flag: bool,
reference: u64,
exprloc: []u8,
ptr: u64,
},
};
fn read_die(
ir: *debug_info_reader,
rd: *table_reader,
abbrev: *abbrev,
) (entry | io::error) = {
let fields: []field = [];
let ok = false;
defer if (!ok) {
fields = [];
free(fields);
};
for (const abf &.. abbrev.fields) {
let field = field {
attr = abf.attr,
form = abf.form,
...
};
let form = abf.form;
for (form == DW_FORM_indirect) {
form = read_uleb128(rd)?: u32;
};
// NOTE: Only supports up to DWARF 4 forms for now
switch (form) {
case DW_FORM_addr =>
field.address = read_ulong(rd)?: uintptr;
case DW_FORM_block =>
field.block = read_slice(rd, read_uleb128(rd)?)?;
case DW_FORM_block1 =>
field.block = read_slice(rd, read_ubyte(rd)?)?;
case DW_FORM_block2 =>
field.block = read_slice(rd, read_uhalf(rd)?)?;
case DW_FORM_block4 =>
field.block = read_slice(rd, read_uword(rd)?)?;
case DW_FORM_data1 =>
field.constant = read_ubyte(rd)?;
case DW_FORM_data2 =>
field.constant = read_uhalf(rd)?;
case DW_FORM_data4 =>
field.constant = read_uword(rd)?;
case DW_FORM_data8 =>
field.constant = read_ulong(rd)?;
case DW_FORM_udata =>
field.constant = read_uleb128(rd)?;
case DW_FORM_sdata =>
field.constant = read_sleb128(rd)?: u64;
case DW_FORM_string =>
field.string = read_string(rd)?;
case DW_FORM_strp =>
// TODO: Look up in .debug_strings
const offs = read_secword(rd)?;
match (ir.strings) {
case let tab: string_table =>
field.string = get_strp(&tab, offs);
case void =>
field.string = "(unknown)";
};
case DW_FORM_flag =>
field.flag = read_ubyte(rd)? != 0;
case DW_FORM_flag_present =>
field.flag = true;
case DW_FORM_ref_addr =>
field.reference = read_secword(rd)?;
case DW_FORM_ref1 =>
field.reference = read_ubyte(rd)?;
case DW_FORM_ref2 =>
field.reference = read_uhalf(rd)?;
case DW_FORM_ref4 =>
field.reference = read_uword(rd)?;
case DW_FORM_ref8 =>
field.reference = read_ulong(rd)?;
case DW_FORM_ref_udata =>
field.reference = read_uleb128(rd)?;
case DW_FORM_ref_sig8 =>
field.reference = read_ulong(rd)?;
case DW_FORM_sec_offset =>
field.reference = read_secword(rd)?;
case DW_FORM_exprloc =>
field.exprloc = read_slice(rd, read_uleb128(rd)?)?;
case DW_FORM_indirect => abort();
case => return errors::unsupported;
};
append(fields, field)?;
};
ok = true;
return entry {
tag = abbrev.tag,
children = abbrev.has_children,
fields = fields,
};
};
|