| ... | ... | @@ -17,6 +17,24 @@ pub const StackMachineOptions = struct { |
| 17 | 17 | call_frame_mode: bool = false, |
| 18 | 18 | }; |
| 19 | 19 | |
| 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. |
| 23 | pub 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 | 38 | /// A stack machine that can decode and run DWARF expressions. |
| 21 | 39 | /// Expressions can be decoded for non-native address size and endianness, |
| 22 | 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 | 59 | const Operand = union(enum) { |
| 42 | 60 | generic: addr_type, |
| 43 | 61 | register: u8, |
| 62 | type_size: u8, |
| 44 | 63 | base_register: struct { |
| 45 | 64 | base_register: u8, |
| 46 | 65 | offset: i64, |
| ... | ... | @@ -60,22 +79,46 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 60 | 79 | }, |
| 61 | 80 | deref_type: struct { |
| 62 | 81 | size: u8, |
| 63 | | offset: u64, |
| 82 | type_offset: u64, |
| 64 | 83 | }, |
| 65 | 84 | }; |
| 66 | 85 | |
| 67 | 86 | const Value = union(enum) { |
| 68 | 87 | generic: addr_type, |
| 88 | |
| 89 | // Typed value with a maximum size of a register |
| 69 | 90 | regval_type: struct { |
| 70 | 91 | // Offset of DW_TAG_base_type DIE |
| 71 | 92 | type_offset: u64, |
| 93 | type_size: u8, |
| 72 | 94 | value: addr_type, |
| 73 | 95 | }, |
| 96 | |
| 97 | // Typed value specified directly in the instruction stream |
| 74 | 98 | const_type: struct { |
| 75 | 99 | // Offset of DW_TAG_base_type DIE |
| 76 | 100 | type_offset: u64, |
| 101 | // Backed by the instruction stream |
| 77 | 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 | }; |
| 80 | 123 | |
| 81 | 124 | stack: std.ArrayListUnmanaged(Value) = .{}, |
| ... | ... | @@ -111,9 +154,10 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 111 | 154 | => generic(try reader.readInt(addr_type, options.endian)), |
| 112 | 155 | OP.const1u, |
| 113 | 156 | OP.pick, |
| 157 | => generic(try reader.readByte()), |
| 114 | 158 | OP.deref_size, |
| 115 | 159 | OP.xderef_size, |
| 116 | | => generic(try reader.readByte()), |
| 160 | => .{ .type_size = try reader.readByte() }, |
| 117 | 161 | OP.const1s => generic(try reader.readByteSigned()), |
| 118 | 162 | OP.const2u, |
| 119 | 163 | OP.call2, |
| ... | ... | @@ -199,7 +243,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 199 | 243 | => .{ |
| 200 | 244 | .deref_type = .{ |
| 201 | 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 | 249 | OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode, |
| ... | ... | @@ -211,14 +255,12 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 211 | 255 | self: *Self, |
| 212 | 256 | expression: []const u8, |
| 213 | 257 | 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, |
| 217 | 259 | initial_value: usize, |
| 218 | 260 | ) !Value { |
| 219 | 261 | try self.stack.append(allocator, .{ .generic = initial_value }); |
| 220 | 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 | 264 | if (self.stack.items.len == 0) return error.InvalidExpression; |
| 223 | 265 | return self.stack.items[self.stack.items.len - 1]; |
| 224 | 266 | } |
| ... | ... | @@ -228,9 +270,7 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 228 | 270 | self: *Self, |
| 229 | 271 | stream: *std.io.FixedBufferStream([]const u8), |
| 230 | 272 | 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, |
| 234 | 274 | ) !bool { |
| 235 | 275 | if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian()) |
| 236 | 276 | @compileError("Execution of non-native address sizees / endianness is not supported"); |
| ... | ... | @@ -281,7 +321,9 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 281 | 321 | } }); |
| 282 | 322 | }, |
| 283 | 323 | |
| 284 | | OP.addrx, OP.constx => { |
| 324 | OP.addrx, |
| 325 | OP.constx, |
| 326 | => { |
| 285 | 327 | const debug_addr_index = (try readOperand(stream, opcode)).?.generic; |
| 286 | 328 | |
| 287 | 329 | // 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 | 334 | |
| 293 | 335 | // 2.5.1.2: Register Values |
| 294 | 336 | 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; |
| 297 | 339 | |
| 298 | 340 | const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic); |
| 299 | 341 | _ = offset; |
| 300 | 342 | |
| 301 | | switch (compile_unit.?.frame_base.?.*) { |
| 343 | switch (context.compile_unit.?.frame_base.?.*) { |
| 302 | 344 | .ExprLoc => { |
| 303 | 345 | // TODO: Run this expression in a nested stack machine |
| 304 | 346 | return error.UnimplementedOpcode; |
| ... | ... | @@ -314,34 +356,136 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 314 | 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 | 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 | 366 | value += base_register.offset; |
| 321 | 367 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); |
| 322 | 368 | }, |
| 323 | 369 | OP.regval_type => { |
| 324 | 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 | 372 | try self.stack.append(allocator, .{ |
| 327 | 373 | .regval_type = .{ |
| 328 | | .value = value, |
| 329 | 374 | .type_offset = register_type.type_offset, |
| 375 | .type_size = @sizeOf(addr_type), |
| 376 | .value = value, |
| 330 | 377 | }, |
| 331 | 378 | }); |
| 332 | 379 | }, |
| 333 | 380 | |
| 334 | 381 | // 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 | }; |
| 335 | 421 | |
| 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; |
| 337 | 425 | |
| 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() }; |
| 341 | 481 | }, |
| 342 | 482 | |
| 343 | 483 | // These have already been handled by readOperand |
| 344 | 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 | } |
| 346 | 490 | |
| 347 | 491 | return stream.pos < stream.buffer.len; |