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





Учитывая, что fs.sf является структурой STACKFRAME64, вам необходимо инициализировать ее следующим образом перед передачей в StackWalk64: (c - структура CONTEXT)
DWORD machine = IMAGE_FILE_MACHINE_AMD64;
RtlCaptureContext (&c);
fs.sf.AddrPC.Offset = c.Rip;
fs.sf.AddrFrame.Offset = c.Rsp;
fs.sf.AddrStack.Offset = c.Rsp;
fs.sf.AddrPC.Mode = AddrModeFlat;
fs.sf.AddrFrame.Mode = AddrModeFlat;
fs.sf.AddrStack.Mode = AddrModeFlat;
Этот код взят из ACE (Adaptive Communications Environment), адаптированной из проекта StackWalker на CodeProject.
Ага, я пробовал stackframe.AddrFrame.Offset = context.Rsp, но это не помогло. (См. 2-й пункт выше.) Что является немного странно, поскольку сам код ASM использует rsp как базовый указатель на w.r.t. локальные переменные функции - например, "mov dword ptr [rsp + 54h], eax" при сохранении возвращаемого значения StackWalk64 ().
Я видел, как этот код работает, поэтому знаю, что это хорошо. Можете ли вы опубликовать больше своего кода - например, ваш вызов StackWalk64 () или полный пример?
Код по сути такой же, как и другой код, который действительно работает. На данный момент я думаю, что это произошло из-за параметров компилятора (флаги cl.exe) - я видел две среды сборки, обе с одной и той же версией компилятора (но с разными флагами), производили двоичные файлы, чьи буферы стека времени выполнения [вызова] выглядели совершенно по-разному. .
Стеки вызовов, создаваемые StackWalk, часто будут выглядеть по-разному в зависимости от многих внешних факторов: какой dbghelp доступен, типа символов / отладочной информации и т. д.
У меня тоже не вышло. Неверная схема адресации? (Vs2010, Windows 8)
FWIW, я перешел на использование CaptureStackBackTrace(), и теперь он работает нормально.
SymInitialize(process, nullptr, TRUE) должен быть вызван (один раз) перед StackWalk64().
FWIW, x64 ABI по умолчанию не использует регистр указателя кадра, как это делает x86, поэтому вы не можете развернуть стек, основываясь исключительно на состоянии регистра + содержимом стека. Тем не менее, ABI требует использования таблиц очистки, которые хранятся как часть исполняемого файла. Вероятно, отладчик загружает и использует их.