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