Отладка приложений

       

Поиск функции, исходного файла и номера строки


Алгоритм для извлечения функции, исходного файла и номера строки из МАР-файла прост, но выполняется с применением шестнадцатеричной арифметики. Пусть, например, аварийный останов произошел по адресу 0x03901099 в модуле MAPDLL.DLL, показанном в листинге 8-1.

Прежде всего, нужно заглянуть в МАР-файлы своего проекта и найти файл, который содержит аварийный адрес. Сначала посмотрите на предпочтительный адрес загрузки и последний адрес в секции общих функций. Адрес ошибки должен находиться между этими значениями, если это не так, значит перед нами "неправильный" МАР-файл.

Чтобы отыскать функцию (или ближайшую общую функцию, если ошибка произошла в статической С-функции), найдите в колонке Rva+Base первый адрес, который превышает аварийный. Тогда предыдущий вход в МАР-файле и есть та функция, которая вызвала аварийный останов. Например, в листинге 8-1 первым адресом функции, превосходящим аварийный (0x3901099), является адрес Ox39010F6, следовательно, ошибку вызвала функция ?MapDLLHappyFunc@@YAPADPAD@z. Имя функции, которое начинается со знака вопроса, является декорированным именем (decorated name) языка C++. Чтобы транслировать это имя, передайте его как параметр команд- | ной строке программы UNDNAME.EXE из Platform SDK. Например, ?MapDLLHappyFunc@@YApADPAD@z переводится этой программой в обычное имя MapDLLHappyFunc, о чем, вероятно, можно было догадаться, просто взглянув на декорированное имя. Другие декорированные имена C++ расшифровы вать труднее, особенно когда используются перегруженные функции. Номер строки вычисляется по следующей формуле:

(crash address) — (preferred load address) — 0x1000

Напомним, что адреса указываются как смещение от начала первой секции кода, что и учитывает эта формула. Вероятно, нетрудно догадаться, почему вычитается предположительный адрес загрузки, но зачем вычитать еще и 0x1000? Адрес ошибки представляет собой смещение от начала секции кода, но эта секция не является первой частью двоичного файла. Его первой частью является РЕ заголовок, длина которого равна 0x1000 байт.


РЕ-файл — от англ. Portable Executable, переносимый (на другую платформу) исполняемый файл. — Пер.

Не знаю точно, почему компоновщик генерирует МАР-файлы, которые требуют этого добавочного вычисления. Разработчики утилиты LINK.EXE ввели колонку Rva+Base недавно, и не вполне понятно, почему они просто не установили в этой колонке и номер строки тоже.

Вычислив смещение, просматривайте МАР-файл, пока не найдете ближайший номер, который не превосходит расчетное значение. Имейте в виду, что на фазе генерации компилятор может перемещать коды, так что исходные строки не располагаются в порядке возрастания. В рассматриваемом примере использована следующая формула:

0x03901099 - 0x03900000 - 0x1000 = 0x99

В листинге 8-1 самый близкий, но не превышающий расчетное значение адреса 0001:00000099 номер строки равен 38 (с адресом 0001:00000096), т. е. речь идет о строке 38 в файле MAPDLL.CPP.

Рассматривая МАР-файл для модуля, написанного на языке Visual Basic, следует знать, что номера строк, о которых сообщает МАР-файл (и утилита CrashFinder), не соответствуют номерам строк, которые отображает редактор Visual Basic. Компилируемые двоичные файлы принимают во внимание полный заголовок в верхней части исходного файла, который Visual Basic скрывает от просмотра. Чтобы найти строку, о которой сообщает компилятор, нужно открыть VB-файл в текстовом редакторе, таком как редактор Visual C++, и перейти к строкам, перечисленным в соответствующем списке МАР-файла.



Содержание раздела