| ... | ... | @@ -171,7 +171,7 @@ pub const Instruction = union(Opcode) { |
| 171 | 171 | advance_loc4: InstructionType(.{ .delta = .u32_delta }), |
| 172 | 172 | undefined: InstructionType(.{ .register = .uleb128_register }), |
| 173 | 173 | same_value: InstructionType(.{ .register = .uleb128_register }), |
| 174 | | register: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), |
| 174 | register: InstructionType(.{ .register = .uleb128_register, .target_register = .uleb128_register }), |
| 175 | 175 | remember_state: InstructionType(.{}), |
| 176 | 176 | restore_state: InstructionType(.{}), |
| 177 | 177 | def_cfa: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }), |
| ... | ... | @@ -229,15 +229,20 @@ pub const VirtualMachine = struct { |
| 229 | 229 | architectural: void, |
| 230 | 230 | }; |
| 231 | 231 | |
| 232 | | /// Each row contains unwinding rules for a set of registers at a specific location in the |
| 232 | /// Each row contains unwinding rules for a set of registers. |
| 233 | 233 | pub const Row = struct { |
| 234 | | /// Offset from pc_begin |
| 234 | /// Offset from `FrameDescriptionEntry.pc_begin` |
| 235 | 235 | offset: u64 = 0, |
| 236 | |
| 236 | 237 | /// Special-case column that defines the CFA (Canonical Frame Address) rule. |
| 237 | 238 | /// The register field of this column defines the register that CFA is derived |
| 238 | | /// from, while other columns define registers in terms of the CFA. |
| 239 | /// from, while other columns define register rules in terms of the CFA. |
| 239 | 240 | cfa: Column = .{}, |
| 240 | 241 | columns: ColumnRange = .{}, |
| 242 | |
| 243 | /// Indicates that the next write to any column in this row needs to copy |
| 244 | /// the backing column storage first. |
| 245 | copy_on_write: bool = false, |
| 241 | 246 | }; |
| 242 | 247 | |
| 243 | 248 | pub const Column = struct { |
| ... | ... | @@ -339,6 +344,17 @@ pub const VirtualMachine = struct { |
| 339 | 344 | self.stepTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian()); |
| 340 | 345 | } |
| 341 | 346 | |
| 347 | fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void { |
| 348 | if (!self.current_row.copy_on_write) return; |
| 349 | |
| 350 | const new_start = self.columns.items.len; |
| 351 | if (self.current_row.columns.len > 0) { |
| 352 | try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len); |
| 353 | self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row)); |
| 354 | self.current_row.columns.start = new_start; |
| 355 | } |
| 356 | } |
| 357 | |
| 342 | 358 | /// Executes a single instruction. |
| 343 | 359 | /// If this instruction is from the CIE, `is_initial` should be set. |
| 344 | 360 | /// Returns the value of `current_row` before executing this instruction |
| ... | ... | @@ -355,14 +371,36 @@ pub const VirtualMachine = struct { |
| 355 | 371 | |
| 356 | 372 | const prev_row = self.current_row; |
| 357 | 373 | switch (instruction) { |
| 358 | | inline .advance_loc, .advance_loc1, .advance_loc2, .advance_loc4 => |i| { |
| 374 | .set_loc => |i| { |
| 375 | if (i.operands.address <= self.current_row.offset) return error.InvalidOperation; |
| 376 | // TODO: Check cie.segment_selector_size != for DWARFV4 |
| 377 | self.current_row.offset = i.operands.address; |
| 378 | }, |
| 379 | inline .advance_loc, |
| 380 | .advance_loc1, |
| 381 | .advance_loc2, |
| 382 | .advance_loc4, |
| 383 | => |i| { |
| 359 | 384 | self.current_row.offset += i.operands.delta * cie.code_alignment_factor; |
| 385 | self.current_row.copy_on_write = true; |
| 360 | 386 | }, |
| 361 | | inline .offset, .offset_extended => |i| { |
| 387 | inline .offset, |
| 388 | .offset_extended, |
| 389 | .offset_extended_sf, |
| 390 | => |i| { |
| 391 | try self.resolveCopyOnWrite(allocator); |
| 362 | 392 | const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 363 | 393 | column.rule = .{ .offset = @intCast(i64, i.operands.offset) * cie.data_alignment_factor }; |
| 364 | 394 | }, |
| 365 | | inline .restore, .restore_extended => |i| { |
| 395 | // .offset_extended_sf => |i| { |
| 396 | // try self.resolveCopyOnWrite(allocator); |
| 397 | // const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 398 | // column.rule = .{ .offset = i.operands.offset * cie.data_alignment_factor }; |
| 399 | // }, |
| 400 | inline .restore, |
| 401 | .restore_extended, |
| 402 | => |i| { |
| 403 | try self.resolveCopyOnWrite(allocator); |
| 366 | 404 | if (self.cie_row) |cie_row| { |
| 367 | 405 | const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 368 | 406 | column.rule = for (self.rowColumns(cie_row)) |cie_column| { |
| ... | ... | @@ -371,20 +409,31 @@ pub const VirtualMachine = struct { |
| 371 | 409 | } else return error.InvalidOperation; |
| 372 | 410 | }, |
| 373 | 411 | .nop => {}, |
| 374 | | .set_loc => {}, |
| 375 | | .undefined => {}, |
| 376 | | .same_value => {}, |
| 377 | | .register => {}, |
| 412 | .undefined => |i| { |
| 413 | try self.resolveCopyOnWrite(allocator); |
| 414 | const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 415 | column.rule = .{ .undefined = {} }; |
| 416 | }, |
| 417 | .same_value => |i| { |
| 418 | try self.resolveCopyOnWrite(allocator); |
| 419 | const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 420 | column.rule = .{ .same_value = {} }; |
| 421 | }, |
| 422 | .register => |i| { |
| 423 | try self.resolveCopyOnWrite(allocator); |
| 424 | const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 425 | column.rule = .{ .register = i.operands.target_register }; |
| 426 | }, |
| 378 | 427 | .remember_state => { |
| 379 | 428 | try self.stack.append(allocator, self.current_row.columns); |
| 380 | | errdefer _ = self.stack.pop(); |
| 381 | | |
| 382 | | const new_start = self.columns.items.len; |
| 383 | | if (self.current_row.columns.len > 0) { |
| 384 | | try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len); |
| 385 | | self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row)); |
| 386 | | self.current_row.columns.start = new_start; |
| 387 | | } |
| 429 | self.current_row.copy_on_write = true; |
| 430 | |
| 431 | // const new_start = self.columns.items.len; |
| 432 | // if (self.current_row.columns.len > 0) { |
| 433 | // try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len); |
| 434 | // self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row)); |
| 435 | // self.current_row.columns.start = new_start; |
| 436 | // } |
| 388 | 437 | }, |
| 389 | 438 | .restore_state => { |
| 390 | 439 | const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation; |
| ... | ... | @@ -396,29 +445,48 @@ pub const VirtualMachine = struct { |
| 396 | 445 | self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]); |
| 397 | 446 | }, |
| 398 | 447 | .def_cfa => |i| { |
| 448 | try self.resolveCopyOnWrite(allocator); |
| 399 | 449 | self.current_row.cfa = .{ |
| 400 | 450 | .register = i.operands.register, |
| 401 | 451 | .rule = .{ .offset = @intCast(i64, i.operands.offset) }, |
| 402 | 452 | }; |
| 403 | 453 | }, |
| 454 | .def_cfa_sf => |i| { |
| 455 | try self.resolveCopyOnWrite(allocator); |
| 456 | self.current_row.cfa = .{ |
| 457 | .register = i.operands.register, |
| 458 | .rule = .{ .offset = i.operands.offset * cie.data_alignment_factor }, |
| 459 | }; |
| 460 | }, |
| 404 | 461 | .def_cfa_register => |i| { |
| 405 | | // TODO: Verify the the current row is using a register and offset (validation) |
| 462 | try self.resolveCopyOnWrite(allocator); |
| 463 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .offset) return error.InvalidOperation; |
| 406 | 464 | self.current_row.cfa.register = i.operands.register; |
| 407 | 465 | }, |
| 408 | 466 | .def_cfa_offset => |i| { |
| 409 | | // TODO: Verify the the current row is using a register and offset (validation) |
| 467 | try self.resolveCopyOnWrite(allocator); |
| 468 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .offset) return error.InvalidOperation; |
| 410 | 469 | self.current_row.cfa.rule = .{ .offset = @intCast(i64, i.operands.offset) }; |
| 411 | 470 | }, |
| 471 | .def_cfa_offset_sf => |i| { |
| 472 | try self.resolveCopyOnWrite(allocator); |
| 473 | if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .offset) return error.InvalidOperation; |
| 474 | self.current_row.cfa.rule = .{ .offset = i.operands.offset * cie.data_alignment_factor }; |
| 475 | }, |
| 412 | 476 | .def_cfa_expression => |i| { |
| 477 | try self.resolveCopyOnWrite(allocator); |
| 413 | 478 | self.current_row.cfa.register = undefined; |
| 414 | 479 | self.current_row.cfa.rule = .{ |
| 415 | 480 | .expression = i.operands.block, |
| 416 | 481 | }; |
| 417 | 482 | }, |
| 418 | | .expression => {}, |
| 419 | | .offset_extended_sf => {}, |
| 420 | | .def_cfa_sf => {}, |
| 421 | | .def_cfa_offset_sf => {}, |
| 483 | .expression => |i| { |
| 484 | try self.resolveCopyOnWrite(allocator); |
| 485 | const column = try self.getOrAddColumn(allocator, i.operands.register); |
| 486 | column.rule = .{ |
| 487 | .expression = i.operands.block, |
| 488 | }; |
| 489 | }, |
| 422 | 490 | .val_offset => {}, |
| 423 | 491 | .val_offset_sf => {}, |
| 424 | 492 | .val_expression => {}, |