From d76c8d0bd2305ff5c45653702dd92505e3fb8f6a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 10 Apr 2025 00:50:49 -0400 Subject: [PATCH] Dwarf: cleanup and port to new `std.io.BufferedReader` API --- lib/std/debug/Dwarf/call_frame.zig | 209 +++++++++++----------------- lib/std/debug/Dwarf/expression.zig | 208 ++++++++++++--------------- lib/std/debug/FixedBufferReader.zig | 15 +- lib/std/debug/SelfInfo.zig | 19 ++- lib/std/io/BufferedReader.zig | 162 ++++++++++----------- 5 files changed, 253 insertions(+), 360 deletions(-) diff --git a/lib/std/debug/Dwarf/call_frame.zig b/lib/std/debug/Dwarf/call_frame.zig index 704060a81d..73bf255dae 100644 --- a/lib/std/debug/Dwarf/call_frame.zig +++ b/lib/std/debug/Dwarf/call_frame.zig @@ -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, diff --git a/lib/std/debug/Dwarf/expression.zig b/lib/std/debug/Dwarf/expression.zig index b68eb05bcf..ddc69e2652 100644 --- a/lib/std/debug/Dwarf/expression.zig +++ b/lib/std/debug/Dwarf/expression.zig @@ -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 { diff --git a/lib/std/debug/FixedBufferReader.zig b/lib/std/debug/FixedBufferReader.zig index ff9c817bcb..c50b730053 100644 --- a/lib/std/debug/FixedBufferReader.zig +++ b/lib/std/debug/FixedBufferReader.zig @@ -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 { diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index 7482acadf0..3b44ffcd6e 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -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; } } diff --git a/lib/std/io/BufferedReader.zig b/lib/std/io/BufferedReader.zig index a158de7e27..3c9ddecf63 100644 --- a/lib/std/io/BufferedReader.zig +++ b/lib/std/io/BufferedReader.zig @@ -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; }