authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-05 09:55:54-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
logad5f74c0b1762e16d98115d7cc7c7f58c40aee7b
treeca020f6fca7c25d53c9dffcdcabda7e3cedc2af7
parent576ffaa3298b4e99b7e46d4eccad4763b18985f8

dwarf: introduce ExpressionContext, add more expression opcodes


4 files changed, 186 insertions(+), 46 deletions(-)

lib/std/debug.zig+1-1
...@@ -448,7 +448,7 @@ pub inline fn getContext(context: *StackTraceContext) bool {...@@ -448,7 +448,7 @@ pub inline fn getContext(context: *StackTraceContext) bool {
448 if (native_os == .macos) {448 if (native_os == .macos) {
449 // TODO: Temp, to discover this size via aarch64 CI449 // TODO: Temp, to discover this size via aarch64 CI
450 if (context.mcsize != @sizeOf(std.c.mcontext_t)) {450 if (context.mcsize != @sizeOf(std.c.mcontext_t)) {
451 print("context.mcsize does not match! {} vs {}\n", .{ context.mcsize, @sizeOf(std.c.mcontext_t)});451 print("context.mcsize does not match! {} vs {}\n", .{ context.mcsize, @sizeOf(std.c.mcontext_t) });
452 }452 }
453453
454 assert(context.mcsize == @sizeOf(std.c.mcontext_t));454 assert(context.mcsize == @sizeOf(std.c.mcontext_t));
lib/std/dwarf.zig+11-7
...@@ -1673,7 +1673,14 @@ pub const DwarfInfo = struct {...@@ -1673,7 +1673,14 @@ pub const DwarfInfo = struct {
1673 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;1673 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
1674 }1674 }
16751675
1676 const compile_unit: ?*const CompileUnit = di.findCompileUnit(fde.pc_begin) catch null;1676 var expression_context = .{
1677 .isValidMemory = context.isValidMemory,
1678 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1679 .ucontext = &context.ucontext,
1680 .reg_ctx = context.reg_ctx,
1681 .cfa = context.cfa,
1682 };
1683
1677 context.vm.reset();1684 context.vm.reset();
1678 context.reg_ctx.eh_frame = cie.version != 4;1685 context.reg_ctx.eh_frame = cie.version != 4;
16791686
...@@ -1691,9 +1698,7 @@ pub const DwarfInfo = struct {...@@ -1691,9 +1698,7 @@ pub const DwarfInfo = struct {
1691 const value = try context.stack_machine.run(1698 const value = try context.stack_machine.run(
1692 expression,1699 expression,
1693 context.allocator,1700 context.allocator,
1694 compile_unit,1701 expression_context,
1695 &context.ucontext,
1696 context.reg_ctx,
1697 context.cfa orelse 0,1702 context.cfa orelse 0,
1698 );1703 );
16991704
...@@ -1704,6 +1709,7 @@ pub const DwarfInfo = struct {...@@ -1704,6 +1709,7 @@ pub const DwarfInfo = struct {
1704 };1709 };
17051710
1706 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;1711 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
1712 expression_context.cfa = context.cfa;
17071713
1708 // Buffering the modifications is done because copying the ucontext is not portable,1714 // Buffering the modifications is done because copying the ucontext is not portable,
1709 // some implementations (ie. darwin) use internal pointers to the mcontext.1715 // some implementations (ie. darwin) use internal pointers to the mcontext.
...@@ -1740,9 +1746,7 @@ pub const DwarfInfo = struct {...@@ -1740,9 +1746,7 @@ pub const DwarfInfo = struct {
17401746
1741 try column.resolveValue(1747 try column.resolveValue(
1742 context,1748 context,
1743 compile_unit,1749 expression_context,
1744 &context.ucontext,
1745 context.reg_ctx,
1746 new_value,1750 new_value,
1747 );1751 );
1748 }1752 }
lib/std/dwarf/call_frame.zig+8-16
...@@ -218,13 +218,7 @@ pub const Instruction = union(Opcode) {...@@ -218,13 +218,7 @@ pub const Instruction = union(Opcode) {
218 break :blk result;218 break :blk result;
219 },219 },
220 Opcode.lo_user...Opcode.hi_user => error.UnimplementedUserOpcode,220 Opcode.lo_user...Opcode.hi_user => error.UnimplementedUserOpcode,
221 else => |opcode| blk: {221 else => error.InvalidOpcode,
222
223 // TODO: Remove this
224 std.debug.print("Opcode {x}\n", .{opcode});
225
226 break :blk error.InvalidOpcode;
227 },
228 };222 };
229 }223 }
230};224};
...@@ -295,9 +289,7 @@ pub const VirtualMachine = struct {...@@ -295,9 +289,7 @@ pub const VirtualMachine = struct {
295 pub fn resolveValue(289 pub fn resolveValue(
296 self: Column,290 self: Column,
297 context: *dwarf.UnwindContext,291 context: *dwarf.UnwindContext,
298 compile_unit: ?*const dwarf.CompileUnit,292 expression_context: dwarf.expressions.ExpressionContext,
299 ucontext: *const std.os.ucontext_t,
300 reg_ctx: abi.RegisterContext,
301 out: []u8,293 out: []u8,
302 ) !void {294 ) !void {
303 switch (self.rule) {295 switch (self.rule) {
...@@ -311,9 +303,9 @@ pub const VirtualMachine = struct {...@@ -311,9 +303,9 @@ pub const VirtualMachine = struct {
311 .same_value => {},303 .same_value => {},
312 .offset => |offset| {304 .offset => |offset| {
313 if (context.cfa) |cfa| {305 if (context.cfa) |cfa| {
314 const ptr: *const usize = @ptrFromInt(try applyOffset(cfa, offset));306 const addr = try applyOffset(cfa, offset);
315307 if (expression_context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidAddress;
316 // TODO: context.isValidMemory(ptr)308 const ptr: *const usize = @ptrFromInt(addr);
317 mem.writeIntSliceNative(usize, out, ptr.*);309 mem.writeIntSliceNative(usize, out, ptr.*);
318 } else return error.InvalidCFA;310 } else return error.InvalidCFA;
319 },311 },
...@@ -329,7 +321,7 @@ pub const VirtualMachine = struct {...@@ -329,7 +321,7 @@ pub const VirtualMachine = struct {
329 },321 },
330 .expression => |expression| {322 .expression => |expression| {
331 context.stack_machine.reset();323 context.stack_machine.reset();
332 const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?);324 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
333325
334 if (value != .generic) return error.InvalidExpressionValue;326 if (value != .generic) return error.InvalidExpressionValue;
335 if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;327 if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;
...@@ -339,12 +331,12 @@ pub const VirtualMachine = struct {...@@ -339,12 +331,12 @@ pub const VirtualMachine = struct {
339 },331 },
340 .val_expression => |expression| {332 .val_expression => |expression| {
341 context.stack_machine.reset();333 context.stack_machine.reset();
342 const value = try context.stack_machine.run(expression, context.allocator, compile_unit, ucontext, reg_ctx, context.cfa.?);334 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
343335
344 if (value != .generic) return error.InvalidExpressionValue;336 if (value != .generic) return error.InvalidExpressionValue;
345 mem.writeIntSliceNative(usize, out, value.generic);337 mem.writeIntSliceNative(usize, out, value.generic);
346 },338 },
347 .architectural => return error.UnimplementedRule,339 .architectural => return error.UnimplementedRegisterRule,
348 }340 }
349 }341 }
350 };342 };
lib/std/dwarf/expressions.zig+166-22
...@@ -17,6 +17,24 @@ pub const StackMachineOptions = struct {...@@ -17,6 +17,24 @@ pub const StackMachineOptions = struct {
17 call_frame_mode: bool = false,17 call_frame_mode: bool = false,
18};18};
1919
20/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
21/// Callers should specify all the fields relevant to their context. If a field is required
22/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
23pub const ExpressionContext = struct {
24 /// If specified, any addresses will pass through this function before being
25 isValidMemory: ?*const fn (address: usize) bool = null,
26
27 /// The compilation unit this expression relates to, if any
28 compile_unit: ?*const dwarf.CompileUnit = null,
29
30 /// Register context
31 ucontext: ?*std.os.ucontext_t,
32 reg_ctx: ?abi.RegisterContext,
33
34 /// Call frame address, if in a CFI context
35 cfa: ?usize,
36};
37
20/// A stack machine that can decode and run DWARF expressions.38/// A stack machine that can decode and run DWARF expressions.
21/// Expressions can be decoded for non-native address size and endianness,39/// Expressions can be decoded for non-native address size and endianness,
22/// but can only be executed if the current target matches the configuration.40/// but can only be executed if the current target matches the configuration.
...@@ -41,6 +59,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -41,6 +59,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
41 const Operand = union(enum) {59 const Operand = union(enum) {
42 generic: addr_type,60 generic: addr_type,
43 register: u8,61 register: u8,
62 type_size: u8,
44 base_register: struct {63 base_register: struct {
45 base_register: u8,64 base_register: u8,
46 offset: i64,65 offset: i64,
...@@ -60,22 +79,46 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -60,22 +79,46 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
60 },79 },
61 deref_type: struct {80 deref_type: struct {
62 size: u8,81 size: u8,
63 offset: u64,82 type_offset: u64,
64 },83 },
65 };84 };
6685
67 const Value = union(enum) {86 const Value = union(enum) {
68 generic: addr_type,87 generic: addr_type,
88
89 // Typed value with a maximum size of a register
69 regval_type: struct {90 regval_type: struct {
70 // Offset of DW_TAG_base_type DIE91 // Offset of DW_TAG_base_type DIE
71 type_offset: u64,92 type_offset: u64,
93 type_size: u8,
72 value: addr_type,94 value: addr_type,
73 },95 },
96
97 // Typed value specified directly in the instruction stream
74 const_type: struct {98 const_type: struct {
75 // Offset of DW_TAG_base_type DIE99 // Offset of DW_TAG_base_type DIE
76 type_offset: u64,100 type_offset: u64,
101 // Backed by the instruction stream
77 value_bytes: []const u8,102 value_bytes: []const u8,
78 },103 },
104
105 pub fn asIntegral(self: Value) !addr_type {
106 return switch (self) {
107 .generic => |v| v,
108
109 // TODO: For these two prongs, look up the type and assert it's integral?
110 .regval_type => |regval_type| regval_type.value,
111 .const_type => |const_type| {
112 return switch (const_type.value_bytes.len) {
113 1 => mem.readIntSliceNative(u8, const_type.value_bytes),
114 2 => mem.readIntSliceNative(u16, const_type.value_bytes),
115 4 => mem.readIntSliceNative(u32, const_type.value_bytes),
116 8 => mem.readIntSliceNative(u64, const_type.value_bytes),
117 else => return error.InvalidIntegralTypeLength,
118 };
119 },
120 };
121 }
79 };122 };
80123
81 stack: std.ArrayListUnmanaged(Value) = .{},124 stack: std.ArrayListUnmanaged(Value) = .{},
...@@ -111,9 +154,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -111,9 +154,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
111 => generic(try reader.readInt(addr_type, options.endian)),154 => generic(try reader.readInt(addr_type, options.endian)),
112 OP.const1u,155 OP.const1u,
113 OP.pick,156 OP.pick,
157 => generic(try reader.readByte()),
114 OP.deref_size,158 OP.deref_size,
115 OP.xderef_size,159 OP.xderef_size,
116 => generic(try reader.readByte()),160 => .{ .type_size = try reader.readByte() },
117 OP.const1s => generic(try reader.readByteSigned()),161 OP.const1s => generic(try reader.readByteSigned()),
118 OP.const2u,162 OP.const2u,
119 OP.call2,163 OP.call2,
...@@ -199,7 +243,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -199,7 +243,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
199 => .{243 => .{
200 .deref_type = .{244 .deref_type = .{
201 .size = try reader.readByte(),245 .size = try reader.readByte(),
202 .offset = try leb.readULEB128(u64, reader),246 .type_offset = try leb.readULEB128(u64, reader),
203 },247 },
204 },248 },
205 OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,249 OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,
...@@ -211,14 +255,12 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -211,14 +255,12 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
211 self: *Self,255 self: *Self,
212 expression: []const u8,256 expression: []const u8,
213 allocator: std.mem.Allocator,257 allocator: std.mem.Allocator,
214 compile_unit: ?*const dwarf.CompileUnit,258 context: ExpressionContext,
215 ucontext: *const std.os.ucontext_t,
216 reg_ctx: abi.RegisterContext,
217 initial_value: usize,259 initial_value: usize,
218 ) !Value {260 ) !Value {
219 try self.stack.append(allocator, .{ .generic = initial_value });261 try self.stack.append(allocator, .{ .generic = initial_value });
220 var stream = std.io.fixedBufferStream(expression);262 var stream = std.io.fixedBufferStream(expression);
221 while (try self.step(&stream, allocator, compile_unit, ucontext, reg_ctx)) {}263 while (try self.step(&stream, allocator, context)) {}
222 if (self.stack.items.len == 0) return error.InvalidExpression;264 if (self.stack.items.len == 0) return error.InvalidExpression;
223 return self.stack.items[self.stack.items.len - 1];265 return self.stack.items[self.stack.items.len - 1];
224 }266 }
...@@ -228,9 +270,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -228,9 +270,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
228 self: *Self,270 self: *Self,
229 stream: *std.io.FixedBufferStream([]const u8),271 stream: *std.io.FixedBufferStream([]const u8),
230 allocator: std.mem.Allocator,272 allocator: std.mem.Allocator,
231 compile_unit: ?*const dwarf.CompileUnit,273 context: ExpressionContext,
232 ucontext: *const std.os.ucontext_t,
233 reg_ctx: dwarf.abi.RegisterContext,
234 ) !bool {274 ) !bool {
235 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())275 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
236 @compileError("Execution of non-native address sizees / endianness is not supported");276 @compileError("Execution of non-native address sizees / endianness is not supported");
...@@ -281,7 +321,9 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -281,7 +321,9 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
281 } });321 } });
282 },322 },
283323
284 OP.addrx, OP.constx => {324 OP.addrx,
325 OP.constx,
326 => {
285 const debug_addr_index = (try readOperand(stream, opcode)).?.generic;327 const debug_addr_index = (try readOperand(stream, opcode)).?.generic;
286328
287 // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic329 // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic
...@@ -292,13 +334,13 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -292,13 +334,13 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
292334
293 // 2.5.1.2: Register Values335 // 2.5.1.2: Register Values
294 OP.fbreg => {336 OP.fbreg => {
295 if (compile_unit == null) return error.ExpressionRequiresCompileUnit;337 if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;
296 if (compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;338 if (context.compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
297339
298 const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);340 const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);
299 _ = offset;341 _ = offset;
300342
301 switch (compile_unit.?.frame_base.?.*) {343 switch (context.compile_unit.?.frame_base.?.*) {
302 .ExprLoc => {344 .ExprLoc => {
303 // TODO: Run this expression in a nested stack machine345 // TODO: Run this expression in a nested stack machine
304 return error.UnimplementedOpcode;346 return error.UnimplementedOpcode;
...@@ -314,34 +356,136 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {...@@ -314,34 +356,136 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
314 else => return error.InvalidFrameBase,356 else => return error.InvalidFrameBase,
315 }357 }
316 },358 },
317 OP.breg0...OP.breg31, OP.bregx => {359 OP.breg0...OP.breg31,
360 OP.bregx,
361 => {
362 if (context.ucontext == null) return error.IncompleteExpressionContext;
363
318 const base_register = (try readOperand(stream, opcode)).?.base_register;364 const base_register = (try readOperand(stream, opcode)).?.base_register;
319 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(ucontext, base_register.base_register, reg_ctx)));365 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(context.ucontext.?, base_register.base_register, context.reg_ctx)));
320 value += base_register.offset;366 value += base_register.offset;
321 try self.stack.append(allocator, .{ .generic = @intCast(value) });367 try self.stack.append(allocator, .{ .generic = @intCast(value) });
322 },368 },
323 OP.regval_type => {369 OP.regval_type => {
324 const register_type = (try readOperand(stream, opcode)).?.register_type;370 const register_type = (try readOperand(stream, opcode)).?.register_type;
325 const value = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, register_type.register, reg_ctx));371 const value = mem.readIntSliceNative(usize, try abi.regBytes(context.ucontext.?, register_type.register, context.reg_ctx));
326 try self.stack.append(allocator, .{372 try self.stack.append(allocator, .{
327 .regval_type = .{373 .regval_type = .{
328 .value = value,
329 .type_offset = register_type.type_offset,374 .type_offset = register_type.type_offset,
375 .type_size = @sizeOf(addr_type),
376 .value = value,
330 },377 },
331 });378 });
332 },379 },
333380
334 // 2.5.1.3: Stack Operations381 // 2.5.1.3: Stack Operations
382 OP.dup => {
383 if (self.stack.items.len == 0) return error.InvalidExpression;
384 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1]);
385 },
386 OP.drop => {
387 _ = self.stack.pop();
388 },
389 OP.pick, OP.over => {
390 const stack_index = if (opcode == OP.over) 1 else (try readOperand(stream, opcode)).?.generic;
391 if (stack_index >= self.stack.items.len) return error.InvalidExpression;
392 try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
393 },
394 OP.swap => {
395 if (self.stack.items.len < 2) return error.InvalidExpression;
396 mem.swap(Value, &self.stack.items[self.stack.items.len - 1], &self.stack.items[self.stack.items.len - 2]);
397 },
398 OP.rot => {
399 if (self.stack.items.len < 3) return error.InvalidExpression;
400 const first = self.stack.items[self.stack.items.len - 1];
401 self.stack.items[self.stack.items.len - 1] = self.stack.items[self.stack.items.len - 2];
402 self.stack.items[self.stack.items.len - 2] = self.stack.items[self.stack.items.len - 3];
403 self.stack.items[self.stack.items.len - 3] = first;
404 },
405 OP.deref,
406 OP.xderef,
407 OP.deref_size,
408 OP.xderef_size,
409 OP.deref_type,
410 OP.xderef_type,
411 => {
412 if (self.stack.items.len == 0) return error.InvalidExpression;
413 var addr = try self.stack.pop().asIntegral();
414 const addr_space_identifier: ?usize = switch (opcode) {
415 OP.xderef,
416 OP.xderef_size,
417 OP.xderef_type,
418 => try self.stack.pop().asIntegral(),
419 else => null,
420 };
335421
336 OP.dup => {},422 // Usage of addr_space_identifier in the address calculation is implementation defined.
423 // This code will need to be updated to handle any architectures that utilize this.
424 _ = addr_space_identifier;
337425
338 else => {426 if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression;
339 std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode});427
340 unreachable;428 const operand = try readOperand(stream, opcode);
429 const size = switch (opcode) {
430 OP.deref => @sizeOf(addr_type),
431 OP.deref_size,
432 OP.xderef_size,
433 => operand.?.type_size,
434 OP.deref_type,
435 OP.xderef_type,
436 => operand.?.deref_type.size,
437 else => unreachable,
438 };
439
440 const value: u64 = switch (size) {
441 1 => @as(*const u8, @ptrFromInt(addr)).*,
442 2 => @as(*const u16, @ptrFromInt(addr)).*,
443 4 => @as(*const u32, @ptrFromInt(addr)).*,
444 8 => @as(*const u64, @ptrFromInt(addr)).*,
445 else => return error.InvalidExpression,
446 };
447
448 if (opcode == OP.deref_type) {
449 try self.stack.append(allocator, .{
450 .regval_type = .{
451 .type_offset = operand.?.deref_type.type_offset,
452 .type_size = operand.?.deref_type.size,
453 .value = value,
454 },
455 });
456 } else {
457 try self.stack.append(allocator, .{ .generic = value });
458 }
459 },
460 OP.push_object_address,
461 OP.form_tls_address,
462 => {
463 return error.UnimplementedExpressionOpcode;
464 },
465 OP.call_frame_cfa => {
466 if (context.cfa) |cfa| {
467 try self.stack.append(allocator, .{ .generic = cfa });
468 } else return error.IncompleteExpressionContext;
469 },
470
471 // 2.5.1.4: Arithmetic and Logical Operations
472 OP.abs => {
473 if (self.stack.items.len == 0) return error.InvalidExpression;
474 const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
475 self.stack.items[self.stack.items.len - 1] = .{ .generic = std.math.absCast(value) };
476 },
477 OP.@"and" => {
478 if (self.stack.items.len < 2) return error.InvalidExpression;
479 const a = try self.stack.pop().asIntegral();
480 self.stack.items[self.stack.items.len - 1] = .{ .generic = a & try self.stack.items[self.stack.items.len - 1].asIntegral() };
341 },481 },
342482
343 // These have already been handled by readOperand483 // These have already been handled by readOperand
344 OP.lo_user...OP.hi_user => unreachable,484 OP.lo_user...OP.hi_user => unreachable,
485 else => {
486 //std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode});
487 return error.UnknownExpressionOpcode;
488 },
345 }489 }
346490
347 return stream.pos < stream.buffer.len;491 return stream.pos < stream.buffer.len;