hare

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

info.ha (5100B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use errors;
      5 use debug::image;
      6 use format::elf;
      7 use io;
      8 use memio;
      9 
     10 def INFO_VERSION: u16 = 4;
     11 
     12 export type debug_info_reader = struct {
     13 	image: *image::image,
     14 	abbrev: abbrev_table,
     15 	strings: (string_table | void),
     16 	mem: *memio::stream,
     17 	rd: *table_reader,
     18 };
     19 
     20 // Reads the debug info from a DWARF image. Returns a [[debug_info_reader]],
     21 // call [[debug_info_next]] to retrieve the next DIE.
     22 //
     23 // Pass the return value to [[debug_info_finish]] after you're done with it.
     24 export fn read_debug_info(
     25 	image: *image::image,
     26 	offs: u64,
     27 ) (debug_info_reader | void | io::error) = {
     28 	const sec = match (image::section_byname(image, ".debug_info")) {
     29 	case let sec: *elf::section64 =>
     30 		yield sec;
     31 	case null =>
     32 		return;
     33 	};
     34 
     35 	const memrd = alloc(image::section_reader(image, sec));
     36 	io::seek(memrd, offs: io::off, io::whence::SET)?;
     37 
     38 	const rd = match (new_table_reader(memrd, true)?) {
     39 	case let rd: table_reader =>
     40 		yield alloc(rd);
     41 	case io::EOF =>
     42 		return;
     43 	};
     44 
     45 	const ver = read_uhalf(rd)!;
     46 	const abbrev_offs = read_secword(rd)!;
     47 	const asize = read_ubyte(rd)!;
     48 	assert(ver <= INFO_VERSION, "debug::dwarf: unsupported .debug_info version");
     49 	assert(asize == 8, "debug::dwarf: unsupported address size in .debug_info");
     50 
     51 	const abbrevs = match (load_abbrevs(image, abbrev_offs)?) {
     52 	case void => return;
     53 	case let tab: abbrev_table =>
     54 		yield tab;
     55 	};
     56 
     57 	return debug_info_reader {
     58 		image = image,
     59 		abbrev = abbrevs,
     60 		strings = load_strings(image)?,
     61 		mem = memrd,
     62 		rd = rd,
     63 	};
     64 };
     65 
     66 // Returns the next debug info [[entry]] (DIE) from a [[debug_info_reader]].
     67 // Pass the return value to [[entry_finish]] when done.
     68 export fn debug_info_next(di: *debug_info_reader) (entry | io::EOF) = {
     69 	if (read_iseof(di.rd)) {
     70 		return io::EOF;
     71 	};
     72 
     73 	let code = read_uleb128(di.rd)!;
     74 	for (code == 0) {
     75 		if (read_iseof(di.rd)) {
     76 			return io::EOF;
     77 		};
     78 		code = read_uleb128(di.rd)!;
     79 	};
     80 
     81 	const ref = get_abbrev(&di.abbrev, code);
     82 	assert(ref != null, "debug::dwarf: unknown abbreviated tag");
     83 	return read_die(di, di.rd, ref as *abbrev)!;
     84 };
     85 
     86 // Frees resources associated with a [[debug_info_reader]].
     87 export fn debug_info_finish(di: *debug_info_reader) void = {
     88 	free(di.mem);
     89 	free(di.rd);
     90 };
     91 
     92 // A debug entry.
     93 export type entry = struct {
     94 	tag: u32,
     95 	children: bool,
     96 	fields: []field,
     97 };
     98 
     99 // Frees resources associated with an [[entry]].
    100 export fn entry_finish(ent: *entry) void = {
    101 	free(ent.fields);
    102 };
    103 
    104 // A debug [[entry]] field.
    105 export type field = struct {
    106 	attr: u32,
    107 	form: u32,
    108 	union {
    109 		address: uintptr,
    110 		block: []u8,
    111 		constant: u64,
    112 		string: const str,
    113 		flag: bool,
    114 		reference: u64,
    115 		exprloc: []u8,
    116 		ptr: u64,
    117 	},
    118 };
    119 
    120 fn read_die(
    121 	ir: *debug_info_reader,
    122 	rd: *table_reader,
    123 	abbrev: *abbrev,
    124 ) (entry | io::error) = {
    125 	let fields: []field = [];
    126 	for (const abf &.. abbrev.fields) {
    127 		let field = field {
    128 			attr = abf.attr,
    129 			form = abf.form,
    130 			...
    131 		};
    132 		let form = abf.form;
    133 		for (form == DW_FORM_indirect) {
    134 			form = read_uleb128(rd)?: u32;
    135 		};
    136 
    137 		// NOTE: Only supports up to DWARF 4 forms for now
    138 		switch (form) {
    139 		case DW_FORM_addr =>
    140 			field.address = read_ulong(rd)?: uintptr;
    141 		case DW_FORM_block =>
    142 			field.block = read_slice(rd, read_uleb128(rd)?)?;
    143 		case DW_FORM_block1 =>
    144 			field.block = read_slice(rd, read_ubyte(rd)?)?;
    145 		case DW_FORM_block2 =>
    146 			field.block = read_slice(rd, read_uhalf(rd)?)?;
    147 		case DW_FORM_block4 =>
    148 			field.block = read_slice(rd, read_uword(rd)?)?;
    149 		case DW_FORM_data1 =>
    150 			field.constant = read_ubyte(rd)?;
    151 		case DW_FORM_data2 =>
    152 			field.constant = read_uhalf(rd)?;
    153 		case DW_FORM_data4 =>
    154 			field.constant = read_uword(rd)?;
    155 		case DW_FORM_data8 =>
    156 			field.constant = read_ulong(rd)?;
    157 		case DW_FORM_udata =>
    158 			field.constant = read_uleb128(rd)?;
    159 		case DW_FORM_sdata =>
    160 			field.constant = read_sleb128(rd)?: u64;
    161 		case DW_FORM_string =>
    162 			field.string = read_string(rd)?;
    163 		case DW_FORM_strp =>
    164 			// TODO: Look up in .debug_strings
    165 			const offs = read_secword(rd)?;
    166 			match (ir.strings) {
    167 			case let tab: string_table =>
    168 				field.string = get_strp(&tab, offs);
    169 			case void =>
    170 				field.string = "(unknown)";
    171 			};
    172 		case DW_FORM_flag =>
    173 			field.flag = read_ubyte(rd)? != 0;
    174 		case DW_FORM_flag_present =>
    175 			field.flag = true;
    176 		case DW_FORM_ref_addr =>
    177 			field.reference = read_secword(rd)?;
    178 		case DW_FORM_ref1 =>
    179 			field.reference = read_ubyte(rd)?;
    180 		case DW_FORM_ref2 =>
    181 			field.reference = read_uhalf(rd)?;
    182 		case DW_FORM_ref4 =>
    183 			field.reference = read_uword(rd)?;
    184 		case DW_FORM_ref8 =>
    185 			field.reference = read_ulong(rd)?;
    186 		case DW_FORM_ref_udata =>
    187 			field.reference = read_uleb128(rd)?;
    188 		case DW_FORM_ref_sig8 =>
    189 			field.reference = read_ulong(rd)?;
    190 		case DW_FORM_sec_offset =>
    191 			field.reference = read_secword(rd)?;
    192 		case DW_FORM_exprloc =>
    193 			field.exprloc = read_slice(rd, read_uleb128(rd)?)?;
    194 		case DW_FORM_indirect => abort();
    195 		case => return errors::unsupported;
    196 		};
    197 
    198 		append(fields, field);
    199 	};
    200 
    201 	return entry {
    202 		tag = abbrev.tag,
    203 		children = abbrev.has_children,
    204 		fields = fields,
    205 	};
    206 };