У меня есть инструмент C ++, который обходит стек вызовов в одной точке. В коде он сначала получает копию действующих регистров процессора (через RtlCaptureContext ()), затем использует несколько блоков "#ifdef ...
" для сохранения имен регистров, специфичных для процессора, в stackframe.AddrPC.Offset
, ... AddrStack
... и ... AddrFrame
...; также для каждого из 3 Addr
... членов, указанных выше, устанавливается stackframe.Addr
... .Mode = AddrModeFlat
. (Это было заимствовано из примера кода, с которым я столкнулся некоторое время назад.)
С бинарным файлом x86 это прекрасно работает. Однако в двоичном формате x64 StackWalk64 () передает поддельные адреса. (При первом вызове API единственное явно фиктивное значение адреса появляется в AddrReturn
(== 0xFFFFFFFF'FFFFFFFE
- 3-й аргумент StackWalk64 (), псевдо-дескриптор, возвращаемый GetCurrentThread ()) Однако если API вызывается второй раз, все Addr
... переменные получают поддельные адреса.) Это происходит независимо от того, как установлено AddrFrame
:
- с использованием одного из рекомендованных регистров процессора x64 «указатель базы / кадра»:
rbp
(= 0xf
) или rdi
(= 0x0
)
- с использованием
rsp
(не ожидал, что это сработает, но все равно попробовал)
- устанавливает
AddrPC
и AddrStack
нормально, но оставляя AddrFrame
обнуленным (видно в другом примере кода)
- обнуление всех
Addr
... значений, чтобы StackWalk64 () заполнял их из переданного контекста регистра ЦП (видно в другом примере кода)
FWIW, содержимое буфера физического стека также отличается на x64 и x86 (конечно, после учета разной ширины указателя и расположения стекового буфера). Независимо от причины, StackWalk64 () все равно должен правильно обходить стек вызовов - черт, отладчик все еще способен обходить стек вызовов, и он, похоже, сам использует StackWalk64 () за кулисами. Странность заключается в том, что (правильный) стек вызовов, сообщаемый отладчиком, содержит значения указателя базового адреса и адреса возврата, составляющие байты которого фактически не существуют в буфере стека (ниже или выше текущего указателя стека).
(FWIW # 2: Учитывая странность буфера стека, описанную выше, я попытался отключить ASLR (/dynamicbase:no
), чтобы увидеть, имеет ли он значение, но двоичный файл все еще демонстрирует то же поведение.)
Итак. Любые идеи, почему это будет работать на x86, но есть проблемы на x64? Любые предложения о том, как это исправить?