reader.ha (5135B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bufio; 5 use endian; 6 use errors; 7 use io; 8 use memio; 9 use strings; 10 use types; 11 12 export type table_reader = struct { 13 src: *memio::stream, 14 orig_length: size, 15 length: size, 16 is64: bool, 17 }; 18 19 // Creates a new DWARF table reader. 20 // 21 // If "read_length" is true, this function will read the length from the start 22 // of the table. Returns [[io::EOF]] immediately if there is insufficient data 23 // available in the provided I/O handle. 24 // 25 // The reader will return [[io::underread]] if the DWARF table is truncated. 26 fn new_table_reader( 27 in: *memio::stream, 28 read_length: bool, 29 ) (table_reader | io::EOF | io::error) = { 30 let rd = table_reader { 31 src = in, 32 orig_length = types::SIZE_MAX, 33 length = types::SIZE_MAX, 34 is64 = false, 35 }; 36 37 if (read_length) { 38 const word = match (read_uword(&rd)) { 39 case let uw: u32 => 40 yield uw; 41 case io::underread => 42 return io::EOF; 43 case let err: io::error => 44 return err; 45 }; 46 47 if (word == 0xffffffff) { 48 rd.is64 = true; 49 const long = match (read_ulong(&rd)) { 50 case let ul: u64 => 51 yield ul; 52 case let err: io::error => 53 if (err is io::underread) { 54 return io::EOF; 55 }; 56 return err; 57 }; 58 rd.length = long: size; 59 } else if (word >= 0xfffffff0) { 60 // Reserved value 61 return errors::invalid; 62 } else { 63 rd.length = word: size; 64 }; 65 }; 66 67 rd.orig_length = rd.length; 68 return rd; 69 }; 70 71 fn read_iseof(rd: *table_reader) bool = rd.length == 0; 72 73 fn read_advance(rd: *table_reader, nbyte: size) (void | io::error) = { 74 if (rd.length < nbyte) { 75 return 0: io::underread; 76 }; 77 rd.length -= nbyte; 78 }; 79 80 // Aligns the reader on a given alignment. This function is needed because both 81 // binutils and LLVM inexplicably add padding to .debug_aranges to align the 82 // first tuple on the address size * 2, despite the fact that this is mentioned 83 // nowhere in the DWARF specification and in fact section 7.25 specifically 84 // states that DWARF data is not aligned. It took me 6 hours to figure this out. 85 fn read_align(rd: *table_reader, alignment: size) (void | io::error) = { 86 let cur = rd.orig_length - rd.length + size(u32); 87 if (rd.is64) { 88 cur += size(u64); 89 }; 90 91 const offs = alignment - (cur % alignment); 92 if (offs == 0) { 93 return; 94 }; 95 let buf: [128]u8 = [0...]; 96 io::readall(rd.src, buf[..offs])?; 97 rd.length -= offs; 98 }; 99 100 // Returns the current location of the reader from the start of the section. 101 fn read_tell(rd: *table_reader) size = { 102 const offs = rd.orig_length - rd.length; 103 if (rd.is64) { 104 return offs + size(u32) + size(u64); 105 } else { 106 return offs + size(u32); 107 }; 108 }; 109 110 fn read_sbyte(rd: *table_reader) (i8 | io::error) = { 111 read_advance(rd, size(i8))?; 112 113 match (bufio::read_byte(rd.src)?) { 114 case let byte: u8 => 115 return byte: i8; 116 case io::EOF => 117 return 0: io::underread; 118 }; 119 }; 120 121 fn read_ubyte(rd: *table_reader) (u8 | io::error) = { 122 read_advance(rd, size(u8))?; 123 124 match (bufio::read_byte(rd.src)?) { 125 case let byte: u8 => 126 return byte; 127 case io::EOF => 128 return 0: io::underread; 129 }; 130 }; 131 132 fn read_uhalf(rd: *table_reader) (u16 | io::error) = { 133 read_advance(rd, size(u16))?; 134 135 let buf: [size(u16)]u8 = [0...]; 136 match (io::readall(rd.src, buf)?) { 137 case io::EOF => 138 return 0: io::underread; 139 case size => 140 return endian::host.getu16(buf); 141 }; 142 }; 143 144 fn read_uword(rd: *table_reader) (u32 | io::error) = { 145 read_advance(rd, size(u32))?; 146 147 let buf: [size(u32)]u8 = [0...]; 148 match (io::readall(rd.src, buf)?) { 149 case io::EOF => 150 return 0: io::underread; 151 case size => 152 return endian::host.getu32(buf); 153 }; 154 }; 155 156 fn read_ulong(rd: *table_reader) (u64 | io::error) = { 157 read_advance(rd, size(u64))?; 158 159 let buf: [size(u64)]u8 = [0...]; 160 match (io::readall(rd.src, buf)?) { 161 case io::EOF => 162 return 0u64: io::underread: io::error; 163 case size => 164 return endian::host.getu64(buf); 165 }; 166 }; 167 168 fn read_secword(rd: *table_reader) (u64 | io::error) = { 169 if (rd.is64) { 170 return read_ulong(rd)?; 171 } else { 172 return read_uword(rd)?: u64; 173 }; 174 }; 175 176 fn read_uleb128(rd: *table_reader) (u64 | io::error) = { 177 let bits = 0u64, val = 0u64; 178 for (true) { 179 const x = read_ubyte(rd)?; 180 val |= (x & ~0x80) << bits; 181 if (x & 0x80 == 0) break; 182 bits += 7; 183 }; 184 return val; 185 }; 186 187 fn read_sleb128(rd: *table_reader) (i64 | io::error) = { 188 let bits = 0u64, uval = 0u64; 189 for (true) { 190 const x = read_ubyte(rd)?; 191 uval |= (x & ~0x80) << bits; 192 bits += 7; 193 if (x & 0x80 == 0) break; 194 }; 195 let val = uval: i64; 196 let bits = bits: i64; 197 if (val & (1 << (bits-1)) != 0) { 198 val |= -1 << bits; 199 }; 200 return val; 201 }; 202 203 // Borrowed from underlying source 204 fn read_slice(rd: *table_reader, amt: size) ([]u8 | io::error) = { 205 match (memio::borrowedread(rd.src, amt)) { 206 case let sl: []u8 => 207 rd.length -= len(sl); 208 return sl; 209 case io::EOF => 210 return 0: io::underread; 211 }; 212 }; 213 214 // Borrowed from underlying source 215 fn read_string(rd: *table_reader) (const str | io::error) = { 216 // XXX: Leaks, should probably borrow from memio 217 match (bufio::read_tok(rd.src, 0)?) { 218 case let data: []u8 => 219 rd.length -= len(data) + 1; 220 return strings::fromutf8(data)!; 221 case io::EOF => 222 return 0: io::underread; 223 }; 224 };