Подтвердить что ты не робот

Как отслеживать нарушение доступа "по адресу 00000000"

Я знаю, как создать файл .map для отслеживания ошибок нарушения прав доступа, когда сообщение об ошибке содержит фактический адрес.

Но что, если в сообщении об ошибке говорится

Access violation at address 00000000. Read of address 00000000.

Где я начинаю искать причину этой проблемы...?

4b9b3361

Ответ 1

Нарушение доступа в любом месте рядом с адресом "00000000" указывает на доступ нулевого указателя. Вы используете что-то прежде, чем оно когда-либо было создано, скорее всего, или после того, как оно было FreeAndNil() 'd.

Много раз это вызвано доступом к компоненту в неправильном месте во время создания формы или путем использования вашей основной формы и доступа к чему-то в еще не созданном датамодуле.

MadExcept позволяет легко отслеживать эти вещи и бесплатно для некоммерческого использования. (На самом деле, лицензия на коммерческое использование довольно недорогая, а также стоит денег.)

Ответ 2

Принятый ответ не рассказывает всю историю.

Да, всякий раз, когда вы видите нули, используется указатель NULL. Это потому, что NULL по определению равен нулю. Поэтому вызов 0 NULL может не так много говорить.

Интересно, что - сообщение о том, что NULL упоминается дважды. Фактически, сообщение, которое вы сообщаете, выглядит немного похоже на сообщения, в которых операционные системы Windows-бренда показывают пользователя.

В сообщении говорится, что адрес NULL попытался прочитать NULL. Так что это значит? В частности, как сам адрес читается?

Мы обычно думаем о инструкциях по чтению и записи адреса из памяти по определенным адресам. Знание этого позволяет нам анализировать сообщение об ошибке. Сообщение пытается сформулировать, что инструкция по адресу NULL пыталась прочитать NULL.

Конечно, нет инструкции по адресу NULL, поэтому мы думаем о NULL как о специальном в нашем коде. Но каждая инструкция может рассматриваться как начинающаяся с попытки прочитать себя. Если регистр ЦП EIP находится по адресу NULL, тогда ЦП попытается прочитать код операции для инструкции с адреса 0x00000000 (NULL). Эта попытка чтения NULL завершится с ошибкой и сгенерирует полученное сообщение.

В отладчике обратите внимание, что EIP равно 0x00000000 при получении этого сообщения. Это подтверждает описание, которое я вам дал.

Затем возникает вопрос: "Почему моя программа пытается выполнить адрес NULL". Существует три возможности, которые spring:

  • Вы пытаетесь выполнить вызов функции с помощью указателя функции, который вы объявили, присвоенного NULL, никогда не инициализировали в противном случае и не выполняете разыменование.
  • Аналогично, вы можете вызвать "абстрактный" С++-метод, который имеет запись NULL в объекте vtable. Они создаются в вашем коде с синтаксисом virtual function_name()=0.
  • В вашем коде буфер буфера был переполнен при написании нулей. Нули были записаны за конец буфера стека по сохраненному адресу возврата. Когда функция позже выполняет свою инструкцию ret, из перезаписанного места памяти загружается значение 0x00000000 (NULL). Этот тип ошибки, переполнение стека, является eponym нашего форума.

Поскольку вы упоминаете, что вы вызываете стороннюю библиотеку, я укажу, что это может быть ситуация в библиотеке, которая ожидает, что вы укажете указатель функции не NULL в качестве ввода некоторого API. Они иногда называются функциями "обратного вызова".

Вам придется использовать отладчик, чтобы сузить причину вашей проблемы дальше, но указанные выше возможности помогут вам решить загадку.

Ответ 3

Вы начинаете смотреть рядом с тем кодом, который, как вы знаете, побежали, и вы перестаете смотреть, когда достигнете кода, который, как вы знаете, не запускался.

То, что вы ищете, вероятно, в каком-то месте, где ваша программа вызывает функцию через указатель функции, но этот указатель имеет значение null.

Возможно также, что у вас есть повреждение стека. Возможно, вы перезаписали адрес возврата функции с нулем, и исключение происходит в конце функции. Проверьте наличие возможных переполнений буфера, и если вы вызываете какие-либо функции DLL, убедитесь, что вы использовали правильное соглашение о вызове и количество параметров.

Это не обычный случай использования нулевого указателя, как непризнанная ссылка на объект или PChar. В этих случаях вы получите ненулевое значение "по адресу x". Поскольку инструкция возникла при нулевом адресе, вы знаете, что указатель инструкции CPU не указывал на какую-либо действительную инструкцию. Поэтому отладчик не может показать вам, какая строка кода вызвала проблему - нет строки кода. Вам нужно найти его, найдя код, который ведет к тому месту, где процессор перешел на неверный адрес.

Стол вызовов может оставаться неповрежденным, что должно по крайней мере приблизиться к вашей цели. Если у вас есть повреждение стека, вы, возможно, не сможете доверять стеку вызовов.

Ответ 4

Если вы получаете "Нарушение доступа по адресу 00000000.", вы вызываете указатель функции, который не был назначен - возможно, обработчик события или функция обратного вызова.

например

type
TTest = class(TForm);
protected
  procedure DoCustomEvent;
public
  property OnCustomEvent : TNotifyEvent read FOnCustomEvent  write FOnCustomEvent;
end;

procedure TTest.DoCustomEvent;
begin
  FOnCustomEvent(Self);  
end;

Вместо

procedure TTest.DoCustomEvent;
begin
  if Assigned(FOnCustomEvent) then // need to check event handler is assigned!
    FOnCustomEvent(Self);  
end;

Если ошибка присутствует в стороннем компоненте и вы можете отследить код нарушения, используйте пустой обработчик событий для предотвращения AV.

Ответ 5

Когда я наткнулся на эту проблему, я обычно начинаю смотреть на места, где я FreeAndNil() или просто xxx: = NIL; переменные и код после этого.

Когда ничего другого не помогло, я добавил функцию Log() для вывода сообщений из разных подозрительных мест во время выполнения, а затем позже просмотрел этот журнал, чтобы отслеживать, где в коде появляется нарушение доступа.

Есть много более элегантных решений, доступных для отслеживания этих нарушений, но если у вас их нет, старомодный метод проб и ошибок работает нормально.

Ответ 6

Вероятно, потому, что вы прямо или косвенно через вызов библиотеки обращаетесь к указателю NULL. В этом конкретном случае похоже, что вы перешли на NULL-адрес, который является b бит волосатым.

По моему опыту, самый простой способ отслеживать это - запустить его с помощью отладчика и выгрузить трассировку стека.

В качестве альтернативы вы можете сделать это "вручную" и добавить много записей в журнал, пока вы не сможете точно определить, какая функция (и, возможно, LOC) произошла.

Посмотрите Stack Tracer, что может помочь вам улучшить отладку.

Ответ 7

Используйте MadExcept. Или JclDebug.

Ответ 8

Я буду второй madExcept и аналогичные инструменты, такие как Eurekalog, но я думаю, что вы также можете пойти с FastMM. При включенном режиме debugmode он должен дать вам некоторые подсказки о том, что не так.

В любом случае, несмотря на то, что Delphi использует FastMM по умолчанию, стоит получить полный FastMM для дополнительного контроля за протоколированием.

Ответ 9

Вот реальное быстрое временное исправление, по крайней мере, до перезагрузки, но оно избавится от постоянного доступа. Я установил программу, которая работает нормально, но по какой-то причине есть точка, которая не была правильно установлена ​​в правильном файле. Поэтому, когда он не может получить доступ к файлу, он открывает доступ, но вместо него он пытается запустить его, поэтому даже поиск местоположения, чтобы остановить его навсегда, будет продолжать появляться все больше и больше каждый 3 секунды. Чтобы остановить это, по крайней мере, временно, сделайте следующее...

  • Ctl + Alt + Del
  • Откройте диспетчер задач
  • Запишите имя программы, запрашивающей доступ (вы можете увидеть ее на вкладке своего приложения)
  • Перейдите на вкладку "Процессы".
  • Прокрутите страницу до тех пор, пока не найдете, что процесс соответствует имени программы и щелкните по ней.
  • Нажмите "Завершить процесс"

Это предотвратит постоянное появление окна, по крайней мере, до перезагрузки. Я знаю, что это не решает проблему, но, как и все, существует процесс устранения, и этот шаг здесь, по крайней мере, сделает его немного менее раздражающим.