AV при генерации своего исключения

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

AV при генерации своего исключения

Сообщение Иван Шихалев »

Ситуация такая — при некотором условии в методе генерируется (raise) исключение. Этот метод вызывается из другого, где он завернут в try ... finally, который в свою очередь вызывается из третьего, где завернут в try ... except. Но до блока finally управление не доходит — где-то возникает Access Violation. До raise доходит точно — проверено отладочным выводом.

Что интересно, если raise закомментировать и просто возвращать некое специфическое значение, всё проходит на ура, т.е. повреждений стека перед ним нет.

Локализовать в минимальном примере не получается. Закомментированная проблемная строка на гитхабе — вот.

Может быть, кто-то сталкивался уже с подобным — куда копать-то вообще?
NTFS
постоялец
Сообщения: 388
Зарегистрирован: 05.11.2007 13:57:50
Откуда: Краснодар
Контактная информация:

Сообщение NTFS »

При неожиданном поведении своего кода иногда срабатывает ПОЛНОЕ стирание всего, включая ОС, компилятор, библиотеки - и установка на чистую машину заново.

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

Добавлено спустя 1 минуту 29 секунд:
Val2Str и Inspect - эти две функции смущают. Если их заменить на строковую константу, поведение изменится?
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

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

Берешь отладчик, определяешь, из-за чего возникло AV (скорее всего, на стеке окажется не то значение, которое там должно быть), после чего ставишь на этот адрес hardware breakpoint или watch, и оказываешься в нужном месте кода (перед этим побывав во всех местах, которые изменяют проблемный адрес легитимно).
Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

Сообщение Иван Шихалев »

NTFS писал(а):Еще, когда у меня начинали сыпаться в неизвестном месте AV или SIGSEGV, я выкидывал весь модуль и делал его заново - как правило, срабатывает.


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

NTFS писал(а):Val2Str и Inspect - эти две функции смущают. Если их заменить на строковую константу, поведение изменится?


Пробовал заменять на строковый литерал — не изменилось. Еще пробовал их результат помещать в переменную и выводить сначала WriteLn'ом — выводится, а на raise все тоже самое.
Я тоже на них думал, особенно на Val2Str, поскольку там преобразование внешнего PChar в строку происходит. Но без исключений оно работает.

Добавлено спустя 26 секунд:
Sergei I. Gorelkin писал(а):Берешь отладчик, определяешь, из-за чего возникло AV


А здесь можно поподробнее?

Добавлено спустя 7 минут 24 секунды:
Пока что отладчиком удалось установить только то, что AV происходит на выходе конструктора исключения, все, что до отрабатывается правильно (с виду).
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

Иван Шихалев писал(а):А здесь можно поподробнее?

Нужно определить инструкцию, на уровне ассемблера, на которой происходит AV, и посмотреть, к какому адресу памяти она обращается. Иногда приходится помучаться, особенно если имеет место, например, вызов виртуального метода уничтоженного объекта - тогда управление передается по левому адресу и стек вызовов не несет полезной информации.

Если AV происходит на выходе из конструктора исключения, при том что аргументом является строковый литерал, то тут подозрение падает больше не на стек, а на кучу.
Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

Сообщение Иван Шихалев »

В общем, с raise подозрения снимаются — и без него повторная ошибка в ruby дает AV. Что забавно — между первой, которая не дает, и второй может быть сколько угодно успешных вызовов... При этом rb_eval_string_protect() работает в точности как полагается, но от нее пришлось отказаться, поскольку она свято уверена, что строка в US-ASCII, а параллельных вариантов, как для просто преобразования строк, с кодировками для нее не предусмотрено...

Добавлено спустя 39 минут 32 секунды:
Впрочем, с rb_eval_string_protect() тоже странно получается — вроде бы и все ок, никаких AV, вылетает только мое сгенерированное исключение... Но почему-то оно вылетает сразу на дефолтный обработчик с MessageBox'ом, а не перехватывается в try ... except...

Добавлено спустя 50 минут 8 секунд:
А не, сорри — там ловилось не то (иерархия исключений крива оказалась). rb_eval_string_protect() таки полностью правильно работает.

Добавлено спустя 1 час 34 минуты 15 секунд:
Вот еще что странно: попробовал везде заменить cdecl на stdcall (т.е. в объявлениях внешних фунций и своих, передаваемых во внешние). Не изменилось ничего, хотя, казалось бы должно посыпаться... Что-то я не понимаю, похоже.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

Иван Шихалев писал(а):Вот еще что странно: попробовал везде заменить cdecl на stdcall (т.е. в объявлениях внешних фунций и своих, передаваемых во внешние). Не изменилось ничего, хотя, казалось бы должно посыпаться... Что-то я не понимаю, похоже.


Разница между stdcall и cdecl реально есть только на i386, на остальных платформах, действительно, может работать... пока речь не идет о переменном числе параметров.
Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

Сообщение Иван Шихалев »

Sergei I. Gorelkin писал(а):Разница между stdcall и cdecl реально есть только на i386

Т.е. на других платформах стек очищает таки вызываемая подпрограмма? Но при переменном числе — вызывающая? Как-то все это сложно... Да, у меня x64.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

Там нет очистки стека как таковой, используются т.н. фиксированные кадры стека, память для всех аргументов (по максимуму) выделяется на входе в вызывающую процедуру. С переменным числом параметров связаны всякие нюансы типа "число задействованных xmm регистров передается в регистре al" (это для x86_64-linux).
Кроме того, на уровне языка параметры типа "array of const" для cdecl-функций считаются "переменным числом параметров", а для остальных - как array of TVarRec известного размера.
Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

Сообщение Иван Шихалев »

Мда, подотстал я от жизни... А где можно про все это поподробней почитать? Можно на английском...
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

Для Linux/BSD - спросить у гугля про "System V ABI", из результатов выбрать интересующий процессор :)
Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

Сообщение Иван Шихалев »

Проблема локализована. Правда, на гитхабе пока не пофиксил. При работе с чужеродными исключениями требуется:

Код: Выделить всё

{$IMPLICITEXCEPTIONS OFF}
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

Это маскирует проблему, но не устраняет ее. Свалится где-то в другом месте.
Аватара пользователя
Иван Шихалев
энтузиаст
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург
Контактная информация:

Сообщение Иван Шихалев »

Нет, это не маскирует. Суть проблемы в том, что ruby-исключения не должны возникать в try-контексте. А паскалевские, в свою очередь — в rb_protect.
Так вот, чтобы гарантировать отсутствие try, в некоторых местах нужно запретить его неявное создание. Да, само собой, в этих местах нельзя использовать динамические типы даже неявно (собственно, во всяких неявностях и была проблема).
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
Сообщения: 1409
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение Sergei I. Gorelkin »

Теперь до меня дошло, что речь о двух разных видах исключений: FPC vs. Ruby, причем последние еще и бросаются из другого исполняемого модуля (dll/so). Тогда да, неиспользование исключений является как бы решением...
Ответить