hare

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

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