как exec может изменить поведение исполняемой программы

Я пытаюсь отследить очень странную аварию. Что в этом такого странного, так это обходной путь, который кто-то обнаружил и который я не могу объяснить.

Обходной путь - это небольшая программа, которую я буду называть «бегуном»:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    if (argc == 1)
    {
        fprintf(stderr, "Usage: %s prog [args ...]\n", argv[0]);
        return 1;
    }

    execvp(argv[1], argv + 1);

    fprintf(stderr, "execv failed: %s\n", strerror(errno));

    // If exec returns because the program is not found or we
    // don't have the appropriate permission
    return 255;
}

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

Программа аварийно завершает работу при прямом вызове из командной строки:

/path/to/prog args  # this crashes

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

/path/to/runner /path/to/prog args   # works successfully

На всю жизнь я могу понять, как дополнительный exec может изменить поведение запущенной программы (как вы можете видеть, программа не меняет среду).

Немного предыстории аварии. Сам сбой происходит во время выполнения C++. В частности, когда программа выполняет throw, сбойная версия ошибочно считает, что нет подходящего улова (хотя он есть), и вызывает terminate. Когда я вызываю программу через бегун, исключение правильно перехватывается.

Мой вопрос: есть идеи, почему дополнительный exec меняет поведение исполняемой программы?


person R Samuel Klatchko    schedule 22.03.2010    source источник
comment
Я не знаю: но меняет ли execvp рабочий каталог? Какие аргументы вы передаете?   -  person Martin York    schedule 22.03.2010
comment
@MartinYork - AFAIK, execvp никогда не меняет рабочий каталог (для этого требуется вызов chdir, а бегун этого не делает). Конкретные аргументы не имеют значения; описанное поведение не зависит от конкретных аргументов, переданных программе.   -  person R Samuel Klatchko    schedule 22.03.2010
comment
Будет ли то же самое, если вы используете execv() вместо execvp()?   -  person caf    schedule 23.03.2010
comment
Пробовали ли вы использовать strace в программе, вызывающей сбой, чтобы попытаться определить причину сбоя?   -  person clstrfsck    schedule 23.03.2010
comment
@caf - такое же поведение с execv   -  person R Samuel Klatchko    schedule 23.03.2010
comment
@spong - да, я пытался использовать как strace, так и ltrace и не вижу ничего, что могло бы объяснить разницу в поведении.   -  person R Samuel Klatchko    schedule 23.03.2010


Ответы (5)


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

person Mark B    schedule 22.03.2010
comment
Проблема заключается в том, отображает ли ld-linux.so.2 определенный общий объект в адресное пространство перед основным двоичным файлом или после (фактическая ошибка находится в другом месте, но из-за обстоятельств ошибка проявляется только тогда, когда SO отображается с более низким адресом, чем основной двоичный файл). - person R Samuel Klatchko; 27.03.2010

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

person baol    schedule 23.03.2010
comment
В отличие от нормального, все еще доступного в выходных блоках, Valgrind не обнаруживает никаких ошибок в версии, которая завершается (или версии, которая не завершается, если на то пошло). - person R Samuel Klatchko; 23.03.2010

Как выстрел в темноте: двойное выполнение может изменить порядок переменных окружения в ОЗУ.

Окружение — это структура памяти с указателями; ядро копирует эту структуру в адресное пространство нового процесса. Фактический порядок элементов в ОЗУ может измениться во время этого копирования (переменные среды не упорядочены семантически, но адреса в ОЗУ имеют порядок). С двумя exec() порядок может быть изменен дважды.

То, что изменение порядка строк в ОЗУ приводит к ошибке, несколько странно, но случались и более странные вещи.

person Thomas Pornin    schedule 22.03.2010
comment
Спасибо за предложение, но, похоже, это не так. Я сбросил необработанный блок среды, и в обоих они имеют одинаковый порядок. - person R Samuel Klatchko; 23.03.2010

Интересно, передаете ли вы что-то другое в argv[0] тому, что представляет собой оболочка. Я не вижу очевидного из того, что вы пишете выше, но возможно, что вы устанавливаете argv[0] на фактический первый аргумент программы, тогда как оболочка устанавливает его на вызываемое имя (например, полный или короткий путь )

person MarkR    schedule 22.03.2010
comment
@MarkR - спасибо за ваше предложение. Я модифицировал runner, чтобы argv[0] не включал путь. К сожалению, я все еще вижу то же самое поведение. - person R Samuel Klatchko; 23.03.2010

Я думаю, две вещи, которые вы могли бы сравнить между «рабочей» и «сбойной» версиями — дескрипторы открытых файлов и обработчики сигналов — поскольку они передаются exec.

Я не понимаю, чем они являются проблемой/отличаются, но, возможно, стоит их устранить.

person Douglas Leeder    schedule 23.03.2010