Why clear SoftStep during exception taken from EL0 to EL1? (arm64, linux) - debugging

This is a code from kernel_entry macro (in liniux-5.10.0 arch/arm64/entry.S ),
.macro kernel_entry, el, regsize = 64
...(reduced)
.if \el == 0
clear_gp_regs
mrs x21, sp_el0
ldr_this_cpu tsk, __entry_task, x20
msr sp_el0, tsk
/*
* Ensure MDSCR_EL1.SS is clear, since we can unmask debug exceptions
* when scheduling.
*/
ldr x19, [tsk, #TSK_TI_FLAGS]
disable_step_tsk x19, x20 <== Question
When the exception occured in EL0, the EL0's stack pointer is stored in x21 and the original kernel stack pointer is loaded from memory (per-cpu) to tsk(this is defined to be x28). In the line marked Q1, tsk contains the flag value which is the first element of the thread_info which is also the first element of task_struct. (task_struct sits at the bottom of the process stack area).
My question is : why does it clear the softstep(MDSCR_EL1.SS) for the process? I can't understand the comment in the code below.
/* Ensure MDSCR_EL1.SS is clear, since we can unmask debug exceptions
when scheduling. */
Is it to prevent debug exception during scheduling, so that the debugger works only for the user process? If someone could cleary explain it to me, I would be very thankful.

Related

Rust debugging doesn't stop at the breakpoints when debugging stm32f407 via openocd and gdb

I have a problem debugging an stm32f407vet6 board and rust code.
The point of the problem is that GDB ignores breakpoints.
After setting breakpoints and executing the "continue" command in gdb, the program continues to ignore all breakpoints.
The only way to stop the program running is to cause an interrupt using the "ctrl + c" command.
After this command, the board stops its execution on the line currently being executed.
I have tried to set breakpoints on all lines where I can set them, but all the attempts are unsuccessful.
$ openocd
Open On-Chip Debugger 0.10.0 (2020-07-01) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 2000 kHz
Error: libusb_open() failed with LIBUSB_ERROR_NOT_SUPPORTED
Info : STLINK V2J35S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 6.436364
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
$ arm-none-eabi-gdb -q target\thumbv7em-none-eabihf\debug\test_blink
Reading symbols from target\thumbv7em-none-eabihf\debug\test_blink...
(gdb) target remote :3333
Remote debugging using :3333
0x00004070 in core::ptr::read_volatile (src=0xe000e010) at C:\Users\User\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src/libcore/ptr/mod.rs:1005
1005 pub unsafe fn read_volatile<T>(src: *const T) -> T {
(gdb) load
Loading section .vector_table, size 0x1a8 lma 0x0
Loading section .text, size 0x47bc lma 0x1a8
Loading section .rodata, size 0xbf0 lma 0x4970
Start address 0x47a2, load size 21844
Transfer rate: 100 KB/sec, 5461 bytes/write.
(gdb) b main
Breakpoint 1 at 0x1f2: file src\main.rs, line 15.
(gdb) continue
Continuing.
Program received signal SIGINT, Interrupt.
0x00001530 in cortex_m::peripheral::syst::<impl cortex_m::peripheral::SYST>::has_wrapped (self=0x1000fc6c)
at C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-0.6.3\src\peripheral/syst.rs:135
135 pub fn has_wrapped(&mut self) -> bool {
(gdb) bt
#0 0x00001530 in cortex_m::peripheral::syst::<impl cortex_m::peripheral::SYST>::has_wrapped (self=0x1000fc6c)
at C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-0.6.3\src\peripheral/syst.rs:135
#1 0x00003450 in <stm32f4xx_hal::delay::Delay as embedded_hal::blocking::delay::DelayUs<u32>>::delay_us (self=0x1000fc6c, us=500000)
at C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\stm32f4xx-hal-0.8.3\src/delay.rs:69
#2 0x0000339e in <stm32f4xx_hal::delay::Delay as embedded_hal::blocking::delay::DelayMs<u32>>::delay_ms (self=0x1000fc6c, ms=500)
at C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\stm32f4xx-hal-0.8.3\src/delay.rs:32
#3 0x00000318 in test_blink::__cortex_m_rt_main () at src\main.rs:40
#4 0x000001f6 in main () at src\main.rs:15
memory.x file:
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* TODO Adjust these memory regions to match your device memory layout */
/* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */
CCMRAM : ORIGIN = 0x10000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 128K
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
}
/* This is where the call stack will be allocated. */
/* The stack is of the full descending type. */
/* You may want to use this variable to locate the call stack and static
variables in different memory regions. Below is shown the default value */
_stack_start = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
/* You can use this symbol to customize the location of the .text section */
/* If omitted the .text section will be placed right after the .vector_table
section */
/* This is required only on microcontrollers that store some configuration right
after the vector table */
/* _stext = ORIGIN(FLASH) + 0x400; */
/* Example of putting non-initialized variables into custom RAM locations. */
/* This assumes you have defined a region RAM2 above, and in the Rust
sources added the attribute `#[link_section = ".ram2bss"]` to the data
you want to place there. */
/* Note that the section will not be zero-initialized by the runtime! */
/* SECTIONS {
.ram2bss (NOLOAD) : ALIGN(4) {
*(.ram2bss);
. = ALIGN(4);
} > RAM2
} INSERT AFTER .bss;
*/
openocd.cfg file:
# Sample OpenOCD configuration for the STM32F3DISCOVERY development board
# Depending on the hardware revision you got you'll have to pick ONE of these
# interfaces. At any time only one interface should be commented out.
# Revision C (newer revision)
source [find interface/stlink.cfg]
# Revision A and B (older revisions)
# source [find interface/stlink-v2.cfg]
source [find target/stm32f4x.cfg]
# use hardware reset, connect under reset
# reset_config none separate
main.rs file:
#![no_main]
#![no_std]
#![allow(unsafe_code)]
// Halt on panic
#[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964
extern crate panic_halt; // panic handler
use cortex_m;
use cortex_m_rt::entry;
use stm32f4xx_hal as hal;
use crate::hal::{prelude::*, stm32};
#[entry]
fn main() -> ! {
if let (Some(dp), Some(cp)) = (
stm32::Peripherals::take(),
cortex_m::peripheral::Peripherals::take(),
) {
let rcc = dp.RCC.constrain();
let clocks = rcc
.cfgr
.sysclk(168.mhz())
.freeze();
let mut delay = hal::delay::Delay::new(cp.SYST, clocks);
let gpioa = dp.GPIOA.split();
let mut l1 = gpioa.pa6.into_push_pull_output();
let mut l2 = gpioa.pa7.into_push_pull_output();
loop {
l1.set_low().unwrap();
l2.set_high().unwrap();
delay.delay_ms(500u32);
l1.set_high().unwrap();
l2.set_low().unwrap();
delay.delay_ms(500u32);
}
}
loop {}
}
Cargo.toml file:
[package]
name = "test_blink"
version = "0.1.0"
authors = ["Alex"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
embedded-hal = "0.2"
nb = "0.1.2"
cortex-m = "0.6"
cortex-m-rt = "0.6"
# Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives
panic-halt = "0.2"
cortex-m-log="0.6.2"
[dependencies.stm32f4xx-hal]
version = "0.8.3"
features = ["rt", "stm32f407"]
I am new to rust embedded and maybe I have done something wrong, but I have already tried all the options I can find on the Internet.
At first I thought it was a problem with the cortex-debug plugin for vscode and even created the issue, but the guys couldn't help me because the problem is obviously not on their side.
Debugging "C" code in cubeIDE works, so I dare to assume that the problem is somewhere in rust--gdb--openocd. Perhaps I am missing something, but unfortunately I cannot find it myself yet.
I would appreciate any resources or ideas to solve this problem.
I'm hoping you checked out this resources:
Discovery - debug
From your screen-grab of arm-none-eabi-gdb it does indeed look it it did not hit the break point.
you should have seen this message afterwards:
Note: automatically using hardware breakpoints for read-only addresses.
Breakpoint 1, main () at ...
Did you compile your source with symbols, and unoptimised?
Your config all looks right to me otherwise.

Loading variable addresses into registers PowerPC inline Assembly

I am trying to put together and example of coding inline assembly code in a 'C' program. I have not had any success. I am using GCC 4.9.0. As near as I can tell my syntax is correct. However the build blows up with the following errors:
/tmp/ccqC2wtq.s:48: Error: syntax error; found `(', expected `,'
Makefile:51: recipe for target 'all' failed
/tmp/ccqC2wtq.s:48: Error: junk at end of line: `(31)'
/tmp/ccqC2wtq.s:49: Error: syntax error; found `(', expected `,'
/tmp/ccqC2wtq.s:49: Error: junk at end of line: `(9)'
These are related to the input/output/clobbers lines in the code. Anyone have an idea where I went wrong?
asm volatile("li 7, %0\n" // load destination address
"li 8, %1\n" // load source address
"li 9, 100\n" // load count
// save source address for return
"mr 3,7\n"
// prepare for loop
"subi 8,8,1\n"
"subi 9,9,1\n"
// perform copy
"1:\n"
"lbzu 10,2(8)\n"
"stbu 10,2(7)\n"
"subi 9,9,1\n" // Decrement the count
"bne 1b\n" // If zero, we've finished
"blr\n"
: // no outputs
: "m" (array), "m" (stringArray)
: "r7", "r8"
);
It's not clear what you are trying to do with the initial instructions:
li 7, %0
li 8, %1
Do you want to load the address of the variables into those registers? In general, this is not possible because the address is not representable in an immediate operand. The easiest way out is to avoid using r7 and r8, and instead use %0 and %1 directly as the register names. It seems that you want to use these registers as base addresses, so you should use the b constraint:
: "b" (array), "b" (stringArray)
Then GCC will take care of the details of materializing the address in a suitable fashion.
You cannot return using blr from inline assembler because it's not possible to tear down the stack frame GCC created. You also need to double-check the clobbers and make sure that you list all the things you clobber (including condition codes, memory, and overwritten input operands).
I was able to get this working by declaring a pair of pointers and initializing them with the addresses of the arrays. I didn't realize that the addresses wouldn't be available directly. I've used inline assembly very sparsely in the past, usually just
to raise or lower interrupt masks, not to do anything that references variables. I just
had some folks who wanted an example. And the "blr" was leftover when I copied a
snipet of a pure assembly routine to use as a starting point. Thanks for the responses.
The final code piece looks like this:
int main()
{
char * stringArrayPtr;
unsigned char * myArrayPtr;
unsigned char myArray[100];
stringArrayPtr = (char *)&stringArray;
myArrayPtr = myArray;
asm volatile(
"lwz 7,%[myArrayPtr]\n" // load destination address
"lwz 8, %[stringArrayPtr]\n" // load source address
"li 9, 100\n" // load count
"mr 3,7\n" // save source address for return
"subi 8,8,1\n" // prepare for loop
"subi 9,9,1\n"
"1:\n" // perform copy
"lbzu 10,1(8)\n"
"stbu 10,1(7)\n"
"subic. 9,9,1\n" // Decrement the count
"bne 1b\n" // If zero, we've finished
// The outputs / inputs / clobbered list
// No output variables are specified here
// See GCC documentation on extended assembly for details.
:
: [stringArrayPtr] "m" (stringArrayPtr), [myArrayPtr]"m"(myArrayPtr)
: "7","8","9","10"
);
}

bpf_asm returns single ' on compile

I am new to Berkeley Packet Filter . I am trying to learn how to hand roll my own bpf code and then compile it using the bpf_asm. I am working on ubuntu 16.04 with kernel 4.4.0-137. I have downloaded the source code and I am working my way through the recommended reading, in Documentation/networking, specifically [filter.txt][1]. I have install binutils, built and installed bpf_asm located in tools/net using the provided makefile. Everything to this point appears to have gone okay. When I run bpf_asm -c bpf_example the program produces a single ' mark to standard out. The code I am trying to compile is the sample code provided in Documentation/networking/filter.txt I am including it here for completeness.
ld [4] /* offsetof(struct seccomp_data, arch) */
jne #0xc000003e, bad /* AUDIT_ARCH_X86_64 */
ld [0] /* offsetof(struct seccomp_data, nr) */
jeq #15, good /* __NR_rt_sigreturn */
jeq #231, good /* __NR_exit_group */
jeq #60, good /* __NR_exit */
jeq #0, good /* __NR_read */
jeq #1, good /* __NR_write */
jeq #5, good /* __NR_fstat */
jeq #9, good /* __NR_mmap */
jeq #14, good /* __NR_rt_sigprocmask */
jeq #13, good /* __NR_rt_sigaction */
jeq #35, good /* __NR_nanosleep */
bad: ret #0 /* SECCOMP_RET_KILL_THREAD */
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
The output that is mentioned in filter.txt is
$ ./bpf_asm -c foo
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 1, 0x00000806 },
{ 0x06, 0, 0, 0xffffffff },
{ 0x06, 0, 0, 0000000000 },
However, my output is
./bpf_asm -c bpf_example
'
I am clearly messing something up. Can someone point out what I have overlooked, provide suggestions for things to try, or provide additional literature supplementary to filter.txt?
Thank you.
Edit
After reading the code more carefully I found that bpf_exp.y was invoking yyerror. With a message "lex unknown character", which I find somewhat strange since I yanked the text from filter.txt directly into a new file. Playing with bpf_exp.l I found some strange behavior in the production for ., which is used to catch any output that is not caught by the rest of the lexer and output an error. Commenting those out... which is probably a terrible idea, I was able to produce bpf output. However, it isn't equivalent to what filter.txt suggests as the output. It does however contain the same number of lines as the bpf that was input and after running it through the bpf_dbg program I recovered the same output that I input. Is this program no longer maintained? Or I am still not using it correctly? Furthermore, it would seem very difficult for the input bpf program in filter.txt to be output as the suggested program since I don't believe the parser has any sort of optimization for the output code. Hence it would seem like it needs to have the same number of line. Is that a correct assumption?

Tracing VB6 runtime error by debugging it

I started using WinDbg to debug VB6 runtime errors but can't really get it to work, all I need is to know the source of the error (the procedure where the error comes from).
I created a small app to test and made it to throw an overflow runtime error like this:
Private Sub Command1_Click()
Dim a As Byte
a = 1000
End Sub
I compiled it with "Create Symbolic debug Info" option checked to create the PDB file.
Then I attached the application to WinDbg and clicked the button to throw the error, but when I check the call stack I don't find any trace of the Command1_Click procedure. All I get is the following :
0:001> ~* k
0 Id: 56c.173c Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr
0012ea08 7e419418 ntdll!KiFastSystemCallRet
0012ea40 7e4249c4 USER32!NtUserWaitMessage+0xc
0012ea68 7e43a956 USER32!InternalDialogBox+0xd0
0012ed28 7e43a2bc USER32!SoftModalMessageBox+0x938
0012ee78 7e43a10b USER32!MessageBoxWorker+0x2ba
0012eee4 729af829 USER32!MessageBoxIndirectA+0xb8
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ef24 729af6a5 MSVBVM60!IID_IVbaHost+0x411e9
0012ef4c 729af9a0 MSVBVM60!IID_IVbaHost+0x41065
0012ef7c 729a3d68 MSVBVM60!IID_IVbaHost+0x41360
0012efe0 729a3db6 MSVBVM60!IID_IVbaHost+0x35728
0012f000 72a0c411 MSVBVM60!IID_IVbaHost+0x35776
0012f01c 72a0c6f3 MSVBVM60!_vbaOnGoCheck+0xba
0012f05c 7c9032a8 MSVBVM60!EbGetErrorInfo+0x115
0012f080 7c90327a ntdll!ExecuteHandler2+0x26
0012f130 7c90e46a ntdll!ExecuteHandler+0x24
0012f130 7c812aeb ntdll!KiUserExceptionDispatcher+0xe
0012f484 72a10dcf kernel32!RaiseException+0x53
0012f4a4 72a0e228 MSVBVM60!EbGetHandleOfExecutingProject+0x22b3
0012f4b8 72a0e28c MSVBVM60!rtcDoEvents+0x131
0012f4c8 72a219ee MSVBVM60!rtcDoEvents+0x195
0012f644 72992667 MSVBVM60!_vbaUI1I2+0x12
0012f668 729f4657 MSVBVM60!IID_IVbaHost+0x24027
0012f698 7299ce49 MSVBVM60!DllCanUnloadNow+0x149a5
0012f6c0 7299f97d MSVBVM60!IID_IVbaHost+0x2e809
0012f71c 7299e22c MSVBVM60!IID_IVbaHost+0x3133d
0012f740 7299dc6d MSVBVM60!IID_IVbaHost+0x2fbec
0012f7ac 729c223a MSVBVM60!IID_IVbaHost+0x2f62d
0012f92c 7299ce49 MSVBVM60!IID_IVbaHost+0x53bfa
0012f954 7299f97d MSVBVM60!IID_IVbaHost+0x2e809
0012f9b0 7e418734 MSVBVM60!IID_IVbaHost+0x3133d
0012f9dc 7e418816 USER32!InternalCallWinProc+0x28
0012fa44 7e42927b USER32!UserCallWinProcCheckWow+0x150
0012fa80 7e4292e3 USER32!SendMessageWorker+0x4a5
0012faa0 7e44ff7d USER32!SendMessageW+0x7f
0012fab8 7e4465d2 USER32!xxxButtonNotifyParent+0x41
0012fad4 7e425e94 USER32!xxxBNReleaseCapture+0xf8
0012fb58 7e43b082 USER32!ButtonWndProcWorker+0x6df
0012fb78 7e418734 USER32!ButtonWndProcA+0x5d
0012fba4 7e418816 USER32!InternalCallWinProc+0x28
0012fc0c 7e42a013 USER32!UserCallWinProcCheckWow+0x150
0012fc3c 7e42a998 USER32!CallWindowProcAorW+0x98
0012fc5c 7299d082 USER32!CallWindowProcA+0x1b
0012fcc8 729f492d MSVBVM60!IID_IVbaHost+0x2ea42
0012fcf0 7299ce49 MSVBVM60!DllCanUnloadNow+0x14c7b
0012fd18 7299f97d MSVBVM60!IID_IVbaHost+0x2e809
0012fd74 7e418734 MSVBVM60!IID_IVbaHost+0x3133d
0012fda0 7e418816 USER32!InternalCallWinProc+0x28
0012fe08 7e4189cd USER32!UserCallWinProcCheckWow+0x150
0012fe68 7e4196c7 USER32!DispatchMessageWorker+0x306
0012fe78 7294a6c8 USER32!DispatchMessageA+0xf
0012feb8 7294a63f MSVBVM60!_vbaStrToAnsi+0x2f1
0012fefc 7294a51d MSVBVM60!_vbaStrToAnsi+0x268
0012ff18 7294a4e8 MSVBVM60!_vbaStrToAnsi+0x146
0012ff3c 72943644 MSVBVM60!_vbaStrToAnsi+0x111
0012ffb8 00401246 MSVBVM60!ThunRTMain+0xa0
0012fff0 00000000 Project1!__vbaS+0xa
# 1 Id: 56c.10a8 Suspend: 1 Teb: 7ffdd000 Unfrozen
ChildEBP RetAddr
00f0ffc8 7c950010 ntdll!DbgBreakPoint
00f0fff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d
Note that WinDbg doesn't break on the error, so I manually break into it after I get the error message and before clicking OK, because if I clicked OK, the application closes.
I'm new to WinDbg but I read that it should break on error but it doesn't, all I get is "First chance Exception" and I don't get a "Second chance Exception" when I click OK and let it crash.
I think that somehow WinDbg considers that VB6 handled the error even though it didn't handle it. Is there something I'm missing here? How can I get the trace of the error to the calling procedure?
Running the application in VB6 itself results in a VB6 runtime error 6, which stands for "overflow".
First chance exception
Running the application under WinDbg (6.2.9200), I first see a
(6cc.6d4): Unknown exception - code c000008f (first chance)
which is probably the most common VB6 exception. By default, WinDbg does not break on first chance exceptions of this type. If you want it to do so, you need to enable that explicitly by
sxe c000008f
Second chance exception
The first chance exception is followed by a message box
This message box is the first indicator that the exception must have been caught, otherwise there would have been a second chance exception immediately afterwards.
And in fact, VB6 creates an exception handler:
0:000> u
Project1!Form1::Command_Click [Form1 # 5]:
004019a0 55 push ebp
004019a1 8bec mov ebp,esp
004019a3 83ec0c sub esp,0Ch
004019a6 68b6104000 push offset Project1!__vbaExceptHandler (004010b6)
004019ab 64a100000000 mov eax,dword ptr fs:[00000000h]
...
004019df b9e8030000 mov ecx,3E8h
004019e4 ff1530104000 call dword ptr [Project1!_imp___vbaUI1I2 (00401030)]
...
At 004019a6 you see that VB6 is working with __vbaExceptHandler. mov dword ptr fs:[0],esp is the beginning of a try/catch block. At 004019df it stores 0x3E8 (hex) or 1000 (dec) to ECX. That's where your code is.
After confirming the message, the process terminates, but not due to a second chance exception but because of an ExitProcess() call. That's the way VB6 "handles" exceptions.
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ff28 7c81bfb6 ntdll!KiFastSystemCallRet
0012ff3c 73393657 kernel32!ExitProcess+0x14
0012ffb8 0040113a MSVBVM60!ThunRTMain+0xb3
0012fff0 00000000 image00400000+0x113a
Symbols
To see if the symbols have been loaded correctly, type lm (list modules). It should look like this:
0:000> lm
start end module name
00400000 00404000 Project1 (private pdb symbols) C:\Programme\Microsoft Visual Studio\VB98\Project1.pdb
If you see (deferred), the symbols have not been loaded yet. Type ld Project1 to load them.
With symbols, you can set breakpoints by first finding the method with x and then setting a breakpoint bp:
0:000> x Project1!Form1*
004019a0 Project1!Form1::Command_Click (void)
0:000> bp Project1!Form1::Command_Click
0:000> bl
0 e 004019a0 0001 (0001) 0:**** Project1!Form1::Command_Click
[...]
Breakpoint 0 hit
eax=004016b4 ebx=00000001 ecx=00000000 edx=733a3dd8 esi=0012f5e4 edi=0012f514
eip=004019a0 esp=0012f50c ebp=0012f514 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
Project1!Form1::Command_Click:
004019a0 55 push ebp
0:000> k
ChildEBP RetAddr
0012f508 733e1ce3 Project1!Form1::Command_Click [Form1 # 5]
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f524 733e1fe4 MSVBVM60!IID_IVbaHost+0x23703
...
But a breakpoint is not what you wanted...
Why the method is not on the call stack
Unfortunately enabling first chance exceptions and loading symbols does still not help seeing the method on the call stack at the time the exception is thrown. It's somewhere in DoEvents():
0:000> .exr -1
ExceptionAddress: 7c812fd3 (kernel32!RaiseException+0x00000052)
ExceptionCode: c000008f
ExceptionFlags: 00000001
NumberParameters: 2
Parameter[0]: deadcafe
Parameter[1]: deadcafe
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f484 73460c29 kernel32!RaiseException+0x52
0012f4a4 7345e082 MSVBVM60!EbGetHandleOfExecutingProject+0x22b3
0012f4b8 7345e0e6 MSVBVM60!rtcDoEvents+0x131
...
Have you noticed the warning about the stack unwind information? Let's look at the stack manually:
0:000> dps #ebp
0012f484 0012f4a4
0012f488 73460c29 MSVBVM60!EbGetHandleOfExecutingProject+0x22b3
0012f48c c000008f
0012f490 00000001
0012f494 00000002
0012f498 0012f49c
0012f49c deadcafe
0012f4a0 deadcafe
...
0012f4d8 004019e7 Project1!Form1::Command_Click+0x53 [Form1 # 7]
0012f4dc 0012f514
0012f4e0 0012f5e4
0012f4e4 00000001
0012f4e8 00000000
0012f4ec 00000000
0012f4f0 00000000
0012f4f4 0012fa34
0012f4f8 004010b6 Project1!__vbaExceptHandler
I guess you can't do anything about it. VB6 is not very good in creating symbols and also not in maintaining a nice call stack.

nt!KeWaitForSingleObject without Args

I'm currently trying to debug a system deadlock and I'm having a hard time understanding this.
Child-SP RetAddr : Args to Child : Call Site
fffff880`035cb760 fffff800`02ecef72 : 00000000`00000002 fffffa80`066e8b50 00000000`00000000 fffffa80`066a16e0 : nt!KiSwapContext+0x7a
fffff880`035cb8a0 fffff800`02ee039f : fffffa80`0b9256b0 00000000`000007ff 00000000`00000000 00000000`00000000 : nt!KiCommitThreadWait+0x1d2
fffff880`035cb930 fffff880`0312a5e4 : 00000000`00000000 fffff800`00000000 fffffa80`079a3c00 00000000`00000000 : nt!KeWaitForSingleObject+0x19
Why would the first argument for KeWaitForSingleObject be null?
Unless I'm misunderstanding isn't the first argument the object being waited on?
Is the deadlock simply that this thread is waiting on nothing or is this ordinary behavior?
Additionally I see another process (services.exe) showing a similar stack trace:
1: kd> .thread fffffa800d406b50
Implicit thread is now fffffa80`0d406b50
1: kd> kv
*** Stack trace for last set context - .thread/.cxr resets it
Child-SP RetAddr : Args to Child : Call Site
fffff880`09ed4800 fffff800`02ecef72 : fffffa80`0d406b50 fffffa80`0d406b50 00000000`00000000 fffff8a0`00000000 : nt!KiSwapContext+0x7a
fffff880`09ed4940 fffff800`02ee039f : 00000000`000000b4 fffffa80`0b1df7f0 00000000`0000005e fffff800`031ae5e7 : nt!KiCommitThreadWait+0x1d2
fffff880`09ed49d0 fffff800`031d1e3e : fffffa80`0d406b00 00000000`00000006 00000000`00000001 00000000`093bf000 : nt!KeWaitForSingleObject+0x19f
fffff880`09ed4a70 fffff800`02ed87d3 : fffffa80`0d406b50 00000000`77502410 fffff880`09ed4ab8 fffffa80`0b171a50 : nt!NtWaitForSingleObject+0xde
Is this thread waiting on itself essentially?
You're debugging a 64-bit process.
Remember the x64 calling convention, which is explained here. The first 4 arguments are passed in registers. After that, arguments are pushed onto the stack.
Unfortunately, kv blindly displays the stack arguments. In fact, it would be quite difficult (and sometimes impossible) for it to determine what the first 4 arguments actually were at the time of the call since they may not have been stored anywhere that can ever be recovered.
So, you are looking at the 5th argument to nt!NtWaitForSingleObject, where a nullptr is a pretty typical argument for a Timeout.
Luckily for us debugging types, all is not lost! There is a windbg extension which does its best to reconstruct the arguments when the function was called. The extension is called CMKD. You can place the extension DLL in your winext folder and call it like so:
0:000> !cmkd.stack -p
Call Stack : 7 frames
## Stack-Pointer Return-Address Call-Site
00 000000a408c7fb28 00007ffda95b1148 ntdll!NtWaitForSingleObject+a
Parameter[0] = 0000000000000034
Parameter[1] = 0000000000000000
Parameter[2] = 0000000000000000
Parameter[3] = (unknown)
01 000000a408c7fb30 00007ff7e44c13f1 KERNELBASE!WaitForSingleObjectEx+98
Parameter[0] = 0000000000000034
Parameter[1] = 00000000ffffffff
Parameter[2] = 0000000000000000
Parameter[3] = 00007ff7e44cba28
02 000000a408c7fbd0 00007ff7e44c3fed ConsoleApplication2!main+41
Parameter[0] = (unknown)
Parameter[1] = (unknown)
Parameter[2] = (unknown)
Parameter[3] = (unknown)
Notice that it does not always succeed at finding the argument, as some of them are (unknown). But, it does a pretty good job and can be an invaluable tool when debugging 64-bit code.
This looks like a 64-bit OS, and therefore the calling convention is not to pass all the parameters on the stack. Rather, the first four parameters get passed in RCX, RDX, R8, and R9, with the remaining parameters on the stack. So if you catch the call to KeWaitForSingleObject it's easy to see what's in RCX and go from there. Once you are a few stack frames beyond that, it's much hard to tell since something will have been loaded into that register. The original value is probably stored somewhere, but it will be difficult to find.

Resources