Dwarf: cleanup and port to new std.io.BufferedReader API

This commit is contained in:
Jacob Young 2025-04-10 00:50:49 -04:00 committed by Andrew Kelley
parent 9dc0b4a98f
commit d76c8d0bd2
5 changed files with 253 additions and 360 deletions

View File

@ -51,17 +51,6 @@ const Opcode = enum(u8) {
pub const hi_user = 0x3f;
};
fn readBlock(stream: *std.io.FixedBufferStream) ![]const u8 {
const reader = stream.reader();
const block_len = try leb.readUleb128(usize, reader);
if (stream.pos + block_len > stream.buffer.len) return error.InvalidOperand;
const block = stream.buffer[stream.pos..][0..block_len];
reader.context.pos += block_len;
return block;
}
pub const Instruction = union(Opcode) {
advance_loc: struct {
delta: u8,
@ -147,12 +136,11 @@ pub const Instruction = union(Opcode) {
},
pub fn read(
stream: *std.io.FixedBufferStream,
reader: *std.io.BufferedReader,
addr_size_bytes: u8,
endian: std.builtin.Endian,
) !Instruction {
const reader = stream.reader();
switch (try reader.readByte()) {
switch (try reader.takeByte()) {
Opcode.lo_inline...Opcode.hi_inline => |opcode| {
const e: Opcode = @enumFromInt(opcode & 0b11000000);
const value: u6 = @intCast(opcode & 0b111111);
@ -163,7 +151,7 @@ pub const Instruction = union(Opcode) {
.offset => .{
.offset = .{
.register = value,
.offset = try leb.readUleb128(u64, reader),
.offset = try reader.takeLeb128(u64),
},
},
.restore => .{
@ -175,121 +163,84 @@ pub const Instruction = union(Opcode) {
Opcode.lo_reserved...Opcode.hi_reserved => |opcode| {
const e: Opcode = @enumFromInt(opcode);
return switch (e) {
.advance_loc,
.offset,
.restore,
=> unreachable,
.nop => .{ .nop = {} },
.set_loc => .{
.set_loc = .{
.address = switch (addr_size_bytes) {
2 => try reader.readInt(u16, endian),
4 => try reader.readInt(u32, endian),
8 => try reader.readInt(u64, endian),
else => return error.InvalidAddrSize,
},
.advance_loc, .offset, .restore => unreachable,
.nop => .nop,
.set_loc => .{ .set_loc = .{
.address = switch (addr_size_bytes) {
2 => try reader.takeInt(u16, endian),
4 => try reader.takeInt(u32, endian),
8 => try reader.takeInt(u64, endian),
else => return error.InvalidAddrSize,
},
},
.advance_loc1 => .{
.advance_loc1 = .{ .delta = try reader.readByte() },
},
.advance_loc2 => .{
.advance_loc2 = .{ .delta = try reader.readInt(u16, endian) },
},
.advance_loc4 => .{
.advance_loc4 = .{ .delta = try reader.readInt(u32, endian) },
},
.offset_extended => .{
.offset_extended = .{
.register = try leb.readUleb128(u8, reader),
.offset = try leb.readUleb128(u64, reader),
},
},
.restore_extended => .{
.restore_extended = .{
.register = try leb.readUleb128(u8, reader),
},
},
.undefined => .{
.undefined = .{
.register = try leb.readUleb128(u8, reader),
},
},
.same_value => .{
.same_value = .{
.register = try leb.readUleb128(u8, reader),
},
},
.register => .{
.register = .{
.register = try leb.readUleb128(u8, reader),
.target_register = try leb.readUleb128(u8, reader),
},
},
.remember_state => .{ .remember_state = {} },
.restore_state => .{ .restore_state = {} },
.def_cfa => .{
.def_cfa = .{
.register = try leb.readUleb128(u8, reader),
.offset = try leb.readUleb128(u64, reader),
},
},
.def_cfa_register => .{
.def_cfa_register = .{
.register = try leb.readUleb128(u8, reader),
},
},
.def_cfa_offset => .{
.def_cfa_offset = .{
.offset = try leb.readUleb128(u64, reader),
},
},
.def_cfa_expression => .{
.def_cfa_expression = .{
.block = try readBlock(stream),
},
},
.expression => .{
.expression = .{
.register = try leb.readUleb128(u8, reader),
.block = try readBlock(stream),
},
},
.offset_extended_sf => .{
.offset_extended_sf = .{
.register = try leb.readUleb128(u8, reader),
.offset = try leb.readIleb128(i64, reader),
},
},
.def_cfa_sf => .{
.def_cfa_sf = .{
.register = try leb.readUleb128(u8, reader),
.offset = try leb.readIleb128(i64, reader),
},
},
.def_cfa_offset_sf => .{
.def_cfa_offset_sf = .{
.offset = try leb.readIleb128(i64, reader),
},
},
.val_offset => .{
.val_offset = .{
.register = try leb.readUleb128(u8, reader),
.offset = try leb.readUleb128(u64, reader),
},
},
.val_offset_sf => .{
.val_offset_sf = .{
.register = try leb.readUleb128(u8, reader),
.offset = try leb.readIleb128(i64, reader),
},
},
.val_expression => .{
.val_expression = .{
.register = try leb.readUleb128(u8, reader),
.block = try readBlock(stream),
},
},
} },
.advance_loc1 => .{ .advance_loc1 = .{
.delta = try reader.takeByte(),
} },
.advance_loc2 => .{ .advance_loc2 = .{
.delta = try reader.takeInt(u16, endian),
} },
.advance_loc4 => .{ .advance_loc4 = .{
.delta = try reader.takeInt(u32, endian),
} },
.offset_extended => .{ .offset_extended = .{
.register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(u64),
} },
.restore_extended => .{ .restore_extended = .{
.register = try reader.takeLeb128(u8),
} },
.undefined => .{ .undefined = .{
.register = try reader.takeLeb128(u8),
} },
.same_value => .{ .same_value = .{
.register = try reader.takeLeb128(u8),
} },
.register => .{ .register = .{
.register = try reader.takeLeb128(u8),
.target_register = try reader.takeLeb128(u8),
} },
.remember_state => .remember_state,
.restore_state => .restore_state,
.def_cfa => .{ .def_cfa = .{
.register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(u64),
} },
.def_cfa_register => .{ .def_cfa_register = .{
.register = try reader.takeLeb128(u8),
} },
.def_cfa_offset => .{ .def_cfa_offset = .{
.offset = try reader.takeLeb128(u64),
} },
.def_cfa_expression => .{ .def_cfa_expression = .{
.block = try reader.take(try reader.takeLeb128(usize)),
} },
.expression => .{ .expression = .{
.register = try reader.takeLeb128(u8),
.block = try reader.take(try reader.takeLeb128(usize)),
} },
.offset_extended_sf => .{ .offset_extended_sf = .{
.register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(i64),
} },
.def_cfa_sf => .{ .def_cfa_sf = .{
.register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(i64),
} },
.def_cfa_offset_sf => .{ .def_cfa_offset_sf = .{
.offset = try reader.takeLeb128(i64),
} },
.val_offset => .{ .val_offset = .{
.register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(u64),
} },
.val_offset_sf => .{ .val_offset_sf = .{
.register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(i64),
} },
.val_expression => .{ .val_expression = .{
.register = try reader.takeLeb128(u8),
.block = try reader.take(try reader.takeLeb128(usize)),
} },
};
},
Opcode.lo_user...Opcode.hi_user => return error.UnimplementedUserOpcode,

View File

@ -68,14 +68,14 @@ pub const Error = error{
/// Expressions can be decoded for non-native address size and endianness,
/// but can only be executed if the current target matches the configuration.
pub fn StackMachine(comptime options: Options) type {
const addr_type = switch (options.addr_size) {
const Address = switch (options.addr_size) {
2 => u16,
4 => u32,
8 => u64,
else => @compileError("Unsupported address size of " ++ options.addr_size),
};
const addr_type_signed = switch (options.addr_size) {
const SignedAddress = switch (options.addr_size) {
2 => i16,
4 => i32,
8 => i64,
@ -86,7 +86,7 @@ pub fn StackMachine(comptime options: Options) type {
const Self = @This();
const Operand = union(enum) {
generic: addr_type,
generic: Address,
register: u8,
type_size: u8,
branch_offset: i16,
@ -101,38 +101,38 @@ pub fn StackMachine(comptime options: Options) type {
block: []const u8,
register_type: struct {
register: u8,
type_offset: addr_type,
type_offset: Address,
},
const_type: struct {
type_offset: addr_type,
type_offset: Address,
value_bytes: []const u8,
},
deref_type: struct {
size: u8,
type_offset: addr_type,
type_offset: Address,
},
};
const Value = union(enum) {
generic: addr_type,
generic: Address,
// Typed value with a maximum size of a register
regval_type: struct {
// Offset of DW_TAG_base_type DIE
type_offset: addr_type,
type_offset: Address,
type_size: u8,
value: addr_type,
value: Address,
},
// Typed value specified directly in the instruction stream
const_type: struct {
// Offset of DW_TAG_base_type DIE
type_offset: addr_type,
type_offset: Address,
// Backed by the instruction stream
value_bytes: []const u8,
},
pub fn asIntegral(self: Value) !addr_type {
pub fn asIntegral(self: Value) !Address {
return switch (self) {
.generic => |v| v,
@ -147,7 +147,7 @@ pub fn StackMachine(comptime options: Options) type {
else => return error.InvalidIntegralTypeSize,
};
return std.math.cast(addr_type, value) orelse error.TruncatedIntegralType;
return std.math.cast(Address, value) orelse error.TruncatedIntegralType;
},
};
}
@ -167,119 +167,87 @@ pub fn StackMachine(comptime options: Options) type {
const int_info = @typeInfo(@TypeOf(value)).int;
if (@sizeOf(@TypeOf(value)) > options.addr_size) {
return .{ .generic = switch (int_info.signedness) {
.signed => @bitCast(@as(addr_type_signed, @truncate(value))),
.signed => @bitCast(@as(SignedAddress, @truncate(value))),
.unsigned => @truncate(value),
} };
} else {
return .{ .generic = switch (int_info.signedness) {
.signed => @bitCast(@as(addr_type_signed, @intCast(value))),
.signed => @bitCast(@as(SignedAddress, @intCast(value))),
.unsigned => @intCast(value),
} };
}
}
pub fn readOperand(stream: *std.io.FixedBufferStream, opcode: u8, context: Context) !?Operand {
const reader = stream.reader();
pub fn readOperand(reader: *std.io.BufferedReader, opcode: u8, context: Context) !?Operand {
return switch (opcode) {
OP.addr => generic(try reader.readInt(addr_type, options.endian)),
OP.addr => generic(try reader.takeInt(Address, options.endian)),
OP.call_ref => switch (context.format) {
.@"32" => generic(try reader.readInt(u32, options.endian)),
.@"64" => generic(try reader.readInt(u64, options.endian)),
.@"32" => generic(try reader.takeInt(u32, options.endian)),
.@"64" => generic(try reader.takeInt(u64, options.endian)),
},
OP.const1u,
OP.pick,
=> generic(try reader.readByte()),
=> generic(try reader.takeByte()),
OP.deref_size,
OP.xderef_size,
=> .{ .type_size = try reader.readByte() },
OP.const1s => generic(try reader.readByteSigned()),
=> .{ .type_size = try reader.takeByte() },
OP.const1s => generic(try reader.takeByteSigned()),
OP.const2u,
OP.call2,
=> generic(try reader.readInt(u16, options.endian)),
OP.call4 => generic(try reader.readInt(u32, options.endian)),
OP.const2s => generic(try reader.readInt(i16, options.endian)),
=> generic(try reader.takeInt(u16, options.endian)),
OP.call4 => generic(try reader.takeInt(u32, options.endian)),
OP.const2s => generic(try reader.takeInt(i16, options.endian)),
OP.bra,
OP.skip,
=> .{ .branch_offset = try reader.readInt(i16, options.endian) },
OP.const4u => generic(try reader.readInt(u32, options.endian)),
OP.const4s => generic(try reader.readInt(i32, options.endian)),
OP.const8u => generic(try reader.readInt(u64, options.endian)),
OP.const8s => generic(try reader.readInt(i64, options.endian)),
=> .{ .branch_offset = try reader.takeInt(i16, options.endian) },
OP.const4u => generic(try reader.takeInt(u32, options.endian)),
OP.const4s => generic(try reader.takeInt(i32, options.endian)),
OP.const8u => generic(try reader.takeInt(u64, options.endian)),
OP.const8s => generic(try reader.takeInt(i64, options.endian)),
OP.constu,
OP.plus_uconst,
OP.addrx,
OP.constx,
OP.convert,
OP.reinterpret,
=> generic(try leb.readUleb128(u64, reader)),
=> generic(try reader.takeLeb128(u64)),
OP.consts,
OP.fbreg,
=> generic(try leb.readIleb128(i64, reader)),
=> generic(try reader.takeLeb128(i64)),
OP.lit0...OP.lit31 => |n| generic(n - OP.lit0),
OP.reg0...OP.reg31 => |n| .{ .register = n - OP.reg0 },
OP.breg0...OP.breg31 => |n| .{ .base_register = .{
.base_register = n - OP.breg0,
.offset = try leb.readIleb128(i64, reader),
.offset = try reader.takeLeb128(i64),
} },
OP.regx => .{ .register = try leb.readUleb128(u8, reader) },
OP.bregx => blk: {
const base_register = try leb.readUleb128(u8, reader);
const offset = try leb.readIleb128(i64, reader);
break :blk .{ .base_register = .{
.base_register = base_register,
.offset = offset,
} };
},
OP.regval_type => blk: {
const register = try leb.readUleb128(u8, reader);
const type_offset = try leb.readUleb128(addr_type, reader);
break :blk .{ .register_type = .{
.register = register,
.type_offset = type_offset,
} };
},
OP.piece => .{
.composite_location = .{
.size = try leb.readUleb128(u8, reader),
.offset = 0,
},
},
OP.bit_piece => blk: {
const size = try leb.readUleb128(u8, reader);
const offset = try leb.readIleb128(i64, reader);
break :blk .{ .composite_location = .{
.size = size,
.offset = offset,
} };
},
OP.implicit_value, OP.entry_value => blk: {
const size = try leb.readUleb128(u8, reader);
if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
const block = stream.buffer[stream.pos..][0..size];
stream.pos += size;
break :blk .{
.block = block,
};
},
OP.const_type => blk: {
const type_offset = try leb.readUleb128(addr_type, reader);
const size = try reader.readByte();
if (stream.pos + size > stream.buffer.len) return error.InvalidExpression;
const value_bytes = stream.buffer[stream.pos..][0..size];
stream.pos += size;
break :blk .{ .const_type = .{
.type_offset = type_offset,
.value_bytes = value_bytes,
} };
},
OP.deref_type,
OP.xderef_type,
=> .{
.deref_type = .{
.size = try reader.readByte(),
.type_offset = try leb.readUleb128(addr_type, reader),
},
OP.regx => .{ .register = try reader.takeLeb128(u8) },
OP.bregx => .{ .base_register = .{
.base_register = try reader.takeLeb128(u8),
.offset = try reader.takeLeb128(i64),
} },
OP.regval_type => .{ .register_type = .{
.register = try reader.takeLeb128(u8),
.type_offset = try reader.takeLeb128(Address),
} },
OP.piece => .{ .composite_location = .{
.size = try reader.takeLeb128(u64),
.offset = 0,
} },
OP.bit_piece => .{ .composite_location = .{
.size = try reader.takeLeb128(u64),
.offset = try reader.takeLeb128(i64),
} },
OP.implicit_value, OP.entry_value => .{
.block = try reader.take(try reader.takeLeb128(usize)),
},
OP.const_type => .{ .const_type = .{
.type_offset = try reader.takeLeb128(Address),
.value_bytes = try reader.take(try reader.takeByte()),
} },
OP.deref_type, OP.xderef_type => .{ .deref_type = .{
.size = try reader.takeByte(),
.type_offset = try reader.takeLeb128(Address),
} },
OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,
else => null,
};
@ -291,10 +259,11 @@ pub fn StackMachine(comptime options: Options) type {
allocator: std.mem.Allocator,
context: Context,
initial_value: ?usize,
) Error!?Value {
) anyerror!?Value {
if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
var stream: std.io.FixedBufferStream = .{ .buffer = expression };
while (try self.step(&stream, allocator, context)) {}
var reader: std.io.BufferedReader = undefined;
reader.initFixed(expression);
while (try self.step(&reader, allocator, context)) {}
if (self.stack.items.len == 0) return null;
return self.stack.items[self.stack.items.len - 1];
}
@ -302,16 +271,19 @@ pub fn StackMachine(comptime options: Options) type {
/// Reads an opcode and its operands from `stream`, then executes it
pub fn step(
self: *Self,
stream: *std.io.FixedBufferStream,
reader: *std.io.BufferedReader,
allocator: std.mem.Allocator,
context: Context,
) Error!bool {
if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != native_endian)
) anyerror!bool {
if (@sizeOf(usize) != @sizeOf(Address) or options.endian != native_endian)
@compileError("Execution of non-native address sizes / endianness is not supported");
const opcode = try stream.reader().readByte();
const opcode = reader.takeByte() catch |err| switch (err) {
error.EndOfStream => return false,
else => |e| return @errorCast(e),
};
if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
const operand = try readOperand(stream, opcode, context);
const operand = try readOperand(reader, opcode, context);
switch (opcode) {
// 2.5.1.1: Literal Encodings
@ -397,7 +369,7 @@ pub fn StackMachine(comptime options: Options) type {
try self.stack.append(allocator, .{
.regval_type = .{
.type_offset = register_type.type_offset,
.type_size = @sizeOf(addr_type),
.type_size = @sizeOf(Address),
.value = value,
},
});
@ -455,7 +427,7 @@ pub fn StackMachine(comptime options: Options) type {
const size = switch (opcode) {
OP.deref,
OP.xderef,
=> @sizeOf(addr_type),
=> @sizeOf(Address),
OP.deref_size,
OP.xderef_size,
=> operand.?.type_size,
@ -475,7 +447,7 @@ pub fn StackMachine(comptime options: Options) type {
}) return error.InvalidExpression;
}
const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) {
const value: Address = std.math.cast(Address, @as(u64, switch (size) {
1 => @as(*const u8, @ptrFromInt(addr)).*,
2 => @as(*const u16, @ptrFromInt(addr)).*,
4 => @as(*const u32, @ptrFromInt(addr)).*,
@ -544,7 +516,7 @@ pub fn StackMachine(comptime options: Options) type {
if (self.stack.items.len < 2) return error.InvalidExpression;
const b = try self.stack.pop().?.asIntegral();
self.stack.items[self.stack.items.len - 1] = .{
.generic = try std.math.sub(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
.generic = try std.math.sub(Address, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
};
},
OP.mod => {
@ -590,14 +562,14 @@ pub fn StackMachine(comptime options: Options) type {
if (self.stack.items.len < 2) return error.InvalidExpression;
const b = try self.stack.pop().?.asIntegral();
self.stack.items[self.stack.items.len - 1] = .{
.generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
.generic = try std.math.add(Address, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
};
},
OP.plus_uconst => {
if (self.stack.items.len == 0) return error.InvalidExpression;
const constant = operand.?.generic;
self.stack.items[self.stack.items.len - 1] = .{
.generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
.generic = try std.math.add(Address, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
};
},
OP.shl => {
@ -670,15 +642,7 @@ pub fn StackMachine(comptime options: Options) type {
break :blk try self.stack.pop().?.asIntegral() != 0;
} else true;
if (condition) {
const new_pos = std.math.cast(
usize,
try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
) orelse return error.InvalidExpression;
if (new_pos < 0 or new_pos > stream.buffer.len) return error.InvalidExpression;
stream.pos = new_pos;
}
if (condition) reader.seekBy(branch_offset) catch return error.InvalidExpression;
},
OP.call2,
OP.call4,
@ -722,7 +686,7 @@ pub fn StackMachine(comptime options: Options) type {
.generic => |v| .{
.regval_type = .{
.type_offset = type_offset,
.type_size = @sizeOf(addr_type),
.type_size = @sizeOf(Address),
.value = v,
},
},
@ -756,8 +720,9 @@ pub fn StackMachine(comptime options: Options) type {
if (isOpcodeRegisterLocation(block[0])) {
if (context.thread_context == null) return error.IncompleteExpressionContext;
var block_stream: std.io.FixedBufferStream = .{ .buffer = block };
const register = (try readOperand(&block_stream, block[0], context)).?.register;
var block_reader: std.io.BufferedReader = undefined;
block_reader.initFixed(block);
const register = (try readOperand(&block_reader, block[0], context)).?.register;
const value = mem.readInt(usize, (try abi.regBytes(context.thread_context.?, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
try self.stack.append(allocator, .{ .generic = value });
} else {
@ -778,14 +743,13 @@ pub fn StackMachine(comptime options: Options) type {
return error.UnknownExpressionOpcode;
},
}
return stream.pos < stream.buffer.len;
return true;
}
};
}
pub fn Builder(comptime options: Options) type {
const addr_type = switch (options.addr_size) {
const Address = switch (options.addr_size) {
2 => u16,
4 => u32,
8 => u64,
@ -888,9 +852,9 @@ pub fn Builder(comptime options: Options) type {
try writer.writeAll(value_bytes);
}
pub fn writeAddr(writer: anytype, value: addr_type) !void {
pub fn writeAddr(writer: anytype, value: Address) !void {
try writer.writeByte(OP.addr);
try writer.writeInt(addr_type, value, options.endian);
try writer.writeInt(Address, value, options.endian);
}
pub fn writeAddrx(writer: anytype, debug_addr_offset: anytype) !void {

View File

@ -51,22 +51,21 @@ pub fn readIntChecked(
return fbr.readInt(T);
}
pub fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
pub fn readLeb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
var br: std.io.BufferedReader = undefined;
br.initFixed(fbr.buf);
br.seek = fbr.pos;
const result = br.takeUleb128(T);
const result = br.takeLeb128(T);
fbr.pos = br.seek;
return @errorCast(result);
}
pub fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
return fbr.readLeb128(T);
}
pub fn readIleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
var br: std.io.BufferedReader = undefined;
br.initFixed(fbr.buf);
br.seek = fbr.pos;
const result = br.takeIleb128(T);
fbr.pos = br.seek;
return @errorCast(result);
return fbr.readLeb128(T);
}
pub fn readAddress(fbr: *FixedBufferReader, format: std.dwarf.Format) Error!u64 {

View File

@ -2025,18 +2025,15 @@ pub const VirtualMachine = struct {
assert(self.cie_row == null);
if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
var readers: [2]std.io.BufferedReader = undefined;
readers[0].initFixed(cie.initial_instructions);
readers[1].initFixed(fde.instructions);
var prev_row: Row = self.current_row;
var cie_stream: std.io.BufferedReader = undefined;
cie_stream.initFixed(cie.initial_instructions);
var fde_stream: std.io.BufferedReader = undefined;
fde_stream.initFixed(fde.instructions);
const streams: [2]*std.io.BufferedReader = .{ &cie_stream, &fde_stream };
for (&streams, 0..) |stream, i| {
while (stream.seek < stream.storageBuffer().len) {
const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
prev_row = try self.step(allocator, cie, i == 0, instruction);
for (&readers, [2]bool{ true, false }) |*reader, is_initial| {
while (reader.seek < reader.storageBuffer().len) {
const instruction = try std.debug.Dwarf.call_frame.Instruction.read(reader, addr_size_bytes, endian);
prev_row = try self.step(allocator, cie, is_initial, instruction);
if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
}
}

View File

@ -164,6 +164,21 @@ fn passthru_posReadVec(ctx: ?*anyopaque, data: []const []u8, off: u64) anyerror!
@panic("TODO");
}
pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void {
if (seek_by < 0) try br.seekBackwardBy(@abs(seek_by)) else try br.seekForwardBy(@abs(seek_by));
}
pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
if (seek_by > br.storage.buffer.items.len - br.seek) return error.Unseekable; // TODO
br.seek += @abs(seek_by);
}
pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
const seek, const need_unbuffered_seek = @subWithOverflow(br.seek, @abs(seek_by));
if (need_unbuffered_seek > 0) return error.Unseekable; // TODO
br.seek = seek;
}
/// Returns the next `n` bytes from `unbuffered_reader`, filling the buffer as
/// necessary.
///
@ -176,12 +191,31 @@ fn passthru_posReadVec(ctx: ?*anyopaque, data: []const []u8, off: u64) anyerror!
/// is returned instead.
///
/// See also:
/// * `peekAll`
/// * `toss`
pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {
return (try br.peekAll(n))[0..n];
}
/// Returns the next buffered bytes from `unbuffered_reader`, after filling the buffer
/// with at least `n` bytes.
///
/// Invalidates previously returned values from `peek`.
///
/// Asserts that the `BufferedReader` was initialized with a buffer capacity at
/// least as big as `n`.
///
/// If there are fewer than `n` bytes left in the stream, `error.EndOfStream`
/// is returned instead.
///
/// See also:
/// * `peek`
/// * `toss`
pub fn peekAll(br: *BufferedReader, n: usize) anyerror![]u8 {
const list = &br.storage.buffer;
assert(n <= list.capacity);
try fill(br, n);
return list.items[br.seek..][0..n];
try br.fill(n);
return list.items[br.seek..];
}
/// Skips the next `n` bytes from the stream, advancing the seek position. This
@ -563,93 +597,41 @@ pub fn takeEnum(br: *BufferedReader, comptime Enum: type, endian: std.builtin.En
return std.meta.intToEnum(Enum, int);
}
/// Read a single unsigned LEB128 value from the given reader as type T,
/// or error.Overflow if the value cannot fit.
pub fn takeUleb128(br: *std.io.BufferedReader, comptime T: type) anyerror!T {
const U = if (@typeInfo(T).int.bits < 8) u8 else T;
const ShiftT = std.math.Log2Int(U);
const max_group = (@typeInfo(U).int.bits + 6) / 7;
var value: U = 0;
var group: ShiftT = 0;
while (group < max_group) : (group += 1) {
const byte = try br.takeByte();
const ov = @shlWithOverflow(@as(U, byte & 0x7f), group * 7);
if (ov[1] != 0) return error.Overflow;
value |= ov[0];
if (byte & 0x80 == 0) break;
} else {
return error.Overflow;
}
// only applies in the case that we extended to u8
if (U != T) {
if (value > std.math.maxInt(T)) return error.Overflow;
}
return @truncate(value);
/// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit.
pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result {
const result_info = @typeInfo(Result).int;
return std.math.cast(Result, try br.takeMultipleOf7Leb128(@Type(.{ .int = .{
.signedness = result_info.signedness,
.bits = std.mem.alignForwardAnyAlign(u16, result_info.bits, 7),
} }))) orelse error.Overflow;
}
/// Read a single signed LEB128 value from the given reader as type T,
/// or `error.Overflow` if the value cannot fit.
pub fn takeIleb128(br: *std.io.BufferedReader, comptime T: type) anyerror!T {
const S = if (@typeInfo(T).int.bits < 8) i8 else T;
const U = std.meta.Int(.unsigned, @typeInfo(S).int.bits);
const ShiftU = std.math.Log2Int(U);
const max_group = (@typeInfo(U).int.bits + 6) / 7;
var value = @as(U, 0);
var group = @as(ShiftU, 0);
while (group < max_group) : (group += 1) {
const byte = try br.takeByte();
const shift = group * 7;
const ov = @shlWithOverflow(@as(U, byte & 0x7f), shift);
if (ov[1] != 0) {
// Overflow is ok so long as the sign bit is set and this is the last byte
if (byte & 0x80 != 0) return error.Overflow;
if (@as(S, @bitCast(ov[0])) >= 0) return error.Overflow;
// and all the overflowed bits are 1
const remaining_shift = @as(u3, @intCast(@typeInfo(U).int.bits - @as(u16, shift)));
const remaining_bits = @as(i8, @bitCast(byte | 0x80)) >> remaining_shift;
if (remaining_bits != -1) return error.Overflow;
} else {
// If we don't overflow and this is the last byte and the number being decoded
// is negative, check that the remaining bits are 1
if ((byte & 0x80 == 0) and (@as(S, @bitCast(ov[0])) < 0)) {
const remaining_shift = @as(u3, @intCast(@typeInfo(U).int.bits - @as(u16, shift)));
const remaining_bits = @as(i8, @bitCast(byte | 0x80)) >> remaining_shift;
if (remaining_bits != -1) return error.Overflow;
}
fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) anyerror!Result {
const result_info = @typeInfo(Result).int;
comptime assert(result_info.bits % 7 == 0);
var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits;
const UnsignedResult = @Type(.{ .int = .{
.signedness = .unsigned,
.bits = result_info.bits,
} });
var result: UnsignedResult = 0;
var fits = true;
while (true) {
const buffer: []const packed struct(u8) { bits: u7, more: bool } = @ptrCast(try br.peekAll(1));
for (buffer, 1..) |byte, len| {
if (remaining_bits > 0) {
result = @shlExact(@as(UnsignedResult, byte.bits), result_info.bits - 7) | @shrExact(result, 7);
remaining_bits -= 7;
} else if (fits) fits = switch (result_info.signedness) {
.signed => @as(i7, @bitCast(byte.bits)) == @as(i7, @truncate(@as(Result, @bitCast(result)) >> (result_info.bits - 1))),
.unsigned => byte.bits == 0,
};
if (byte.more) continue;
br.toss(len);
return if (fits) @as(Result, @bitCast(result)) >> remaining_bits else error.Overflow;
}
value |= ov[0];
if (byte & 0x80 == 0) {
const needs_sign_ext = group + 1 < max_group;
if (byte & 0x40 != 0 and needs_sign_ext) {
const ones = @as(S, -1);
value |= @as(U, @bitCast(ones)) << (shift + 7);
}
break;
}
} else {
return error.Overflow;
br.toss(buffer.len);
}
const result = @as(S, @bitCast(value));
// Only applies if we extended to i8
if (S != T) {
if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow;
}
return @truncate(result);
}
test initFixed {
@ -669,6 +651,10 @@ test peek {
return error.Unimplemented;
}
test peekAll {
return error.Unimplemented;
}
test toss {
return error.Unimplemented;
}
@ -766,10 +752,6 @@ test takeEnum {
return error.Unimplemented;
}
test takeUleb128 {
return error.Unimplemented;
}
test takeIleb128 {
test takeLeb128 {
return error.Unimplemented;
}