AV при генерации своего исключения
Модератор: Модераторы
- Иван Шихалев
- энтузиаст
- Сообщения: 1138
- Зарегистрирован: 15.05.2006 11:26:13
- Откуда: Екатеринбург
- Контактная информация:
AV при генерации своего исключения
Ситуация такая — при некотором условии в методе генерируется (raise) исключение. Этот метод вызывается из другого, где он завернут в try ... finally, который в свою очередь вызывается из третьего, где завернут в try ... except. Но до блока finally управление не доходит — где-то возникает Access Violation. До raise доходит точно — проверено отладочным выводом.
Что интересно, если raise закомментировать и просто возвращать некое специфическое значение, всё проходит на ура, т.е. повреждений стека перед ним нет.
Локализовать в минимальном примере не получается. Закомментированная проблемная строка на гитхабе — вот.
Может быть, кто-то сталкивался уже с подобным — куда копать-то вообще?
Что интересно, если raise закомментировать и просто возвращать некое специфическое значение, всё проходит на ура, т.е. повреждений стека перед ним нет.
Локализовать в минимальном примере не получается. Закомментированная проблемная строка на гитхабе — вот.
Может быть, кто-то сталкивался уже с подобным — куда копать-то вообще?
-
NTFS
- постоялец
- Сообщения: 388
- Зарегистрирован: 05.11.2007 13:57:50
- Откуда: Краснодар
- Контактная информация:
При неожиданном поведении своего кода иногда срабатывает ПОЛНОЕ стирание всего, включая ОС, компилятор, библиотеки - и установка на чистую машину заново.
Еще, когда у меня начинали сыпаться в неизвестном месте AV или SIGSEGV, я выкидывал весь модуль и делал его заново - как правило, срабатывает.
Добавлено спустя 1 минуту 29 секунд:
Val2Str и Inspect - эти две функции смущают. Если их заменить на строковую константу, поведение изменится?
Еще, когда у меня начинали сыпаться в неизвестном месте AV или SIGSEGV, я выкидывал весь модуль и делал его заново - как правило, срабатывает.
Добавлено спустя 1 минуту 29 секунд:
Val2Str и Inspect - эти две функции смущают. Если их заменить на строковую константу, поведение изменится?
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Внешняя работоспособность программы не означает, что стек не поврежден. Например, после того, как функция прочитала свои аргументы, область стека, где находились эти аргументы, можно перезаписать без последствий.
Берешь отладчик, определяешь, из-за чего возникло AV (скорее всего, на стеке окажется не то значение, которое там должно быть), после чего ставишь на этот адрес hardware breakpoint или watch, и оказываешься в нужном месте кода (перед этим побывав во всех местах, которые изменяют проблемный адрес легитимно).
Берешь отладчик, определяешь, из-за чего возникло 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
- Откуда: Зеленоград
Иван Шихалев писал(а):А здесь можно поподробнее?
Нужно определить инструкцию, на уровне ассемблера, на которой происходит 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 (т.е. в объявлениях внешних фунций и своих, передаваемых во внешние). Не изменилось ничего, хотя, казалось бы должно посыпаться... Что-то я не понимаю, похоже.
Добавлено спустя 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
- Откуда: Зеленоград
Иван Шихалев писал(а):Вот еще что странно: попробовал везде заменить 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
- Откуда: Зеленоград
Там нет очистки стека как таковой, используются т.н. фиксированные кадры стека, память для всех аргументов (по максимуму) выделяется на входе в вызывающую процедуру. С переменным числом параметров связаны всякие нюансы типа "число задействованных xmm регистров передается в регистре al" (это для x86_64-linux).
Кроме того, на уровне языка параметры типа "array of const" для cdecl-функций считаются "переменным числом параметров", а для остальных - как array of TVarRec известного размера.
Кроме того, на уровне языка параметры типа "array of const" для cdecl-функций считаются "переменным числом параметров", а для остальных - как array of TVarRec известного размера.
- Иван Шихалев
- энтузиаст
- Сообщения: 1138
- Зарегистрирован: 15.05.2006 11:26:13
- Откуда: Екатеринбург
- Контактная информация:
Мда, подотстал я от жизни... А где можно про все это поподробней почитать? Можно на английском...
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Для Linux/BSD - спросить у гугля про "System V ABI", из результатов выбрать интересующий процессор 
- Иван Шихалев
- энтузиаст
- Сообщения: 1138
- Зарегистрирован: 15.05.2006 11:26:13
- Откуда: Екатеринбург
- Контактная информация:
Проблема локализована. Правда, на гитхабе пока не пофиксил. При работе с чужеродными исключениями требуется:
Код: Выделить всё
{$IMPLICITEXCEPTIONS OFF}- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Это маскирует проблему, но не устраняет ее. Свалится где-то в другом месте.
- Иван Шихалев
- энтузиаст
- Сообщения: 1138
- Зарегистрирован: 15.05.2006 11:26:13
- Откуда: Екатеринбург
- Контактная информация:
Нет, это не маскирует. Суть проблемы в том, что ruby-исключения не должны возникать в try-контексте. А паскалевские, в свою очередь — в rb_protect.
Так вот, чтобы гарантировать отсутствие try, в некоторых местах нужно запретить его неявное создание. Да, само собой, в этих местах нельзя использовать динамические типы даже неявно (собственно, во всяких неявностях и была проблема).
Так вот, чтобы гарантировать отсутствие try, в некоторых местах нужно запретить его неявное создание. Да, само собой, в этих местах нельзя использовать динамические типы даже неявно (собственно, во всяких неявностях и была проблема).
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Теперь до меня дошло, что речь о двух разных видах исключений: FPC vs. Ruby, причем последние еще и бросаются из другого исполняемого модуля (dll/so). Тогда да, неиспользование исключений является как бы решением...
