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 {
448448 if (native_os == .macos) {
449449 // TODO: Temp, to discover this size via aarch64 CI
450450 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) });
452452 }
453453
454454 assert(context.mcsize == @sizeOf(std.c.mcontext_t));
lib/std/dwarf.zig+11-7
......@@ -1673,7 +1673,14 @@ pub const DwarfInfo = struct {
16731673 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
16741674 }
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
16771684 context.vm.reset();
16781685 context.reg_ctx.eh_frame = cie.version != 4;
16791686
......@@ -1691,9 +1698,7 @@ pub const DwarfInfo = struct {
16911698 const value = try context.stack_machine.run(
16921699 expression,
16931700 context.allocator,
1694 compile_unit,
1695 &context.ucontext,
1696 context.reg_ctx,
1701 expression_context,
16971702 context.cfa orelse 0,
16981703 );
16991704
......@@ -1704,6 +1709,7 @@ pub const DwarfInfo = struct {
17041709 };
17051710
17061711 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
1712 expression_context.cfa = context.cfa;
17071713
17081714 // Buffering the modifications is done because copying the ucontext is not portable,
17091715 // some implementations (ie. darwin) use internal pointers to the mcontext.
......@@ -1740,9 +1746,7 @@ pub const DwarfInfo = struct {
17401746
17411747 try column.resolveValue(
17421748 context,
1743 compile_unit,
1744 &context.ucontext,
1745 context.reg_ctx,
1749 expression_context,
17461750 new_value,
17471751 );
17481752 }
lib/std/dwarf/call_frame.zig+8-16
......@@ -218,13 +218,7 @@ pub const Instruction = union(Opcode) {
218218 break :blk result;
219219 },
220220 Opcode.lo_user...Opcode.hi_user => error.UnimplementedUserOpcode,
221 else => |opcode| blk: {
222
223 // TODO: Remove this
224 std.debug.print("Opcode {x}\n", .{opcode});
225
226 break :blk error.InvalidOpcode;
227 },
221 else => error.InvalidOpcode,
228222 };
229223 }
230224};
......@@ -295,9 +289,7 @@ pub const VirtualMachine = struct {
295289 pub fn resolveValue(
296290 self: Column,
297291 context: *dwarf.UnwindContext,
298 compile_unit: ?*const dwarf.CompileUnit,
299 ucontext: *const std.os.ucontext_t,
300 reg_ctx: abi.RegisterContext,
292 expression_context: dwarf.expressions.ExpressionContext,
301293 out: []u8,
302294 ) !void {
303295 switch (self.rule) {
......@@ -311,9 +303,9 @@ pub const VirtualMachine = struct {
311303 .same_value => {},
312304 .offset => |offset| {
313305 if (context.cfa) |cfa| {
314 const ptr: *const usize = @ptrFromInt(try applyOffset(cfa, offset));
315
316 // TODO: context.isValidMemory(ptr)
306 const addr = try applyOffset(cfa, offset);
307 if (expression_context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidAddress;
308 const ptr: *const usize = @ptrFromInt(addr);
317309 mem.writeIntSliceNative(usize, out, ptr.*);
318310 } else return error.InvalidCFA;
319311 },
......@@ -329,7 +321,7 @@ pub const VirtualMachine = struct {
329321 },
330322 .expression => |expression| {
331323 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
334326 if (value != .generic) return error.InvalidExpressionValue;
335327 if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;
......@@ -339,12 +331,12 @@ pub const VirtualMachine = struct {
339331 },
340332 .val_expression => |expression| {
341333 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
344336 if (value != .generic) return error.InvalidExpressionValue;
345337 mem.writeIntSliceNative(usize, out, value.generic);
346338 },
347 .architectural => return error.UnimplementedRule,
339 .architectural => return error.UnimplementedRegisterRule,
348340 }
349341 }
350342 };
lib/std/dwarf/expressions.zig+166-22
......@@ -17,6 +17,24 @@ pub const StackMachineOptions = struct {
1717 call_frame_mode: bool = false,
1818};
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
2038/// A stack machine that can decode and run DWARF expressions.
2139/// Expressions can be decoded for non-native address size and endianness,
2240/// but can only be executed if the current target matches the configuration.
......@@ -41,6 +59,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
4159 const Operand = union(enum) {
4260 generic: addr_type,
4361 register: u8,
62 type_size: u8,
4463 base_register: struct {
4564 base_register: u8,
4665 offset: i64,
......@@ -60,22 +79,46 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
6079 },
6180 deref_type: struct {
6281 size: u8,
63 offset: u64,
82 type_offset: u64,
6483 },
6584 };
6685
6786 const Value = union(enum) {
6887 generic: addr_type,
88
89 // Typed value with a maximum size of a register
6990 regval_type: struct {
7091 // Offset of DW_TAG_base_type DIE
7192 type_offset: u64,
93 type_size: u8,
7294 value: addr_type,
7395 },
96
97 // Typed value specified directly in the instruction stream
7498 const_type: struct {
7599 // Offset of DW_TAG_base_type DIE
76100 type_offset: u64,
101 // Backed by the instruction stream
77102 value_bytes: []const u8,
78103 },
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 }
79122 };
80123
81124 stack: std.ArrayListUnmanaged(Value) = .{},
......@@ -111,9 +154,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
111154 => generic(try reader.readInt(addr_type, options.endian)),
112155 OP.const1u,
113156 OP.pick,
157 => generic(try reader.readByte()),
114158 OP.deref_size,
115159 OP.xderef_size,
116 => generic(try reader.readByte()),
160 => .{ .type_size = try reader.readByte() },
117161 OP.const1s => generic(try reader.readByteSigned()),
118162 OP.const2u,
119163 OP.call2,
......@@ -199,7 +243,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
199243 => .{
200244 .deref_type = .{
201245 .size = try reader.readByte(),
202 .offset = try leb.readULEB128(u64, reader),
246 .type_offset = try leb.readULEB128(u64, reader),
203247 },
204248 },
205249 OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,
......@@ -211,14 +255,12 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
211255 self: *Self,
212256 expression: []const u8,
213257 allocator: std.mem.Allocator,
214 compile_unit: ?*const dwarf.CompileUnit,
215 ucontext: *const std.os.ucontext_t,
216 reg_ctx: abi.RegisterContext,
258 context: ExpressionContext,
217259 initial_value: usize,
218260 ) !Value {
219261 try self.stack.append(allocator, .{ .generic = initial_value });
220262 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)) {}
222264 if (self.stack.items.len == 0) return error.InvalidExpression;
223265 return self.stack.items[self.stack.items.len - 1];
224266 }
......@@ -228,9 +270,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
228270 self: *Self,
229271 stream: *std.io.FixedBufferStream([]const u8),
230272 allocator: std.mem.Allocator,
231 compile_unit: ?*const dwarf.CompileUnit,
232 ucontext: *const std.os.ucontext_t,
233 reg_ctx: dwarf.abi.RegisterContext,
273 context: ExpressionContext,
234274 ) !bool {
235275 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
236276 @compileError("Execution of non-native address sizees / endianness is not supported");
......@@ -281,7 +321,9 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
281321 } });
282322 },
283323
284 OP.addrx, OP.constx => {
324 OP.addrx,
325 OP.constx,
326 => {
285327 const debug_addr_index = (try readOperand(stream, opcode)).?.generic;
286328
287329 // 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 {
292334
293335 // 2.5.1.2: Register Values
294336 OP.fbreg => {
295 if (compile_unit == null) return error.ExpressionRequiresCompileUnit;
296 if (compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
337 if (context.compile_unit == null) return error.ExpressionRequiresCompileUnit;
338 if (context.compile_unit.?.frame_base == null) return error.ExpressionRequiresFrameBase;
297339
298340 const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic);
299341 _ = offset;
300342
301 switch (compile_unit.?.frame_base.?.*) {
343 switch (context.compile_unit.?.frame_base.?.*) {
302344 .ExprLoc => {
303345 // TODO: Run this expression in a nested stack machine
304346 return error.UnimplementedOpcode;
......@@ -314,34 +356,136 @@ pub fn StackMachine(comptime options: StackMachineOptions) type {
314356 else => return error.InvalidFrameBase,
315357 }
316358 },
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
318364 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)));
320366 value += base_register.offset;
321367 try self.stack.append(allocator, .{ .generic = @intCast(value) });
322368 },
323369 OP.regval_type => {
324370 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));
326372 try self.stack.append(allocator, .{
327373 .regval_type = .{
328 .value = value,
329374 .type_offset = register_type.type_offset,
375 .type_size = @sizeOf(addr_type),
376 .value = value,
330377 },
331378 });
332379 },
333380
334381 // 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 => {
339 std.debug.print("Unimplemented DWARF expression opcode: {x}\n", .{opcode});
340 unreachable;
426 if (context.isValidMemory) |isValidMemory| if (!isValidMemory(addr)) return error.InvalidExpression;
427
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() };
341481 },
342482
343483 // These have already been handled by readOperand
344484 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 },
345489 }
346490
347491 return stream.pos < stream.buffer.len;