| ... | @@ -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 | }; |
| 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 | /// 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 | }; |
| 66 | | 85 | |
| 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 DIE | 91 | // 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 DIE | 99 | // 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 | }; |
| 80 | | 123 | |
| 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 | }, |
| 283 | | 323 | |
| 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; |
| 286 | | 328 | |
| 287 | // TODO: Read item from .debug_addr, this requires need DW_AT_addr_base of the compile unit, push onto stack as generic | 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,13 +334,13 @@ pub fn StackMachine(comptime options: StackMachineOptions) type { |
| 292 | | 334 | |
| 293 | // 2.5.1.2: Register Values | 335 | // 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; |
| 297 | | 339 | |
| 298 | const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic); | 340 | const offset: i64 = @intCast((try readOperand(stream, opcode)).?.generic); |
| 299 | _ = offset; | 341 | _ = offset; |
| 300 | | 342 | |
| 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 machine | 345 | // 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 | }, |
| 333 | | 380 | |
| 334 | // 2.5.1.3: Stack Operations | 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 => { | 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 | }, |
| 342 | | 482 | |
| 343 | // These have already been handled by readOperand | 483 | // 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 | } |
| 346 | | 490 | |
| 347 | return stream.pos < stream.buffer.len; | 491 | return stream.pos < stream.buffer.len; |