table of contents
EXECVE(2) | Руководство программиста Linux | EXECVE(2) |
ИМЯ¶
execve - выполнить программу
ОБЗОР¶
#include <unistd.h>
int execve(const char *filename, char *const
argv[],
char *const envp[]);
ОПИСАНИЕ¶
execve() выполняет программу, задаваемую аргументом filename. В filename должно быть указано имя двоичного исполняемого файла или сценарий, начинающийся со строки вида:
#! интерпретатор [необязательные параметры]
Подробней о сценариях написано далее в "Интерпретируемые сценарии".
argv — это массив строковых параметров, передаваемых новой программе. По соглашению, в первой строке должно содержаться имя файла, относящееся к запускаемой программе. envp — это массив строк в формате ключ=значение, которые передаются новой программе в качестве окружения (environment). Оба массива argv и envp завершаются указателем NULL. К массиву параметров и окружению можно обратиться из вызываемой программой функции main, если она определена как:
int main(int argc, char *argv[], char *envp[])
При успешном выполнении execve() управление не возвращается, а код, данные, bss и стек вызвавшего процесса перезаписываются загруженной программой.
Если текущая программа выполнялась под управлением ptrace, то после успешного вызова execve() ей посылается сигнал SIGTRAP.
Если у файла программы, указанного в filename, установлен бит set-user-ID и файловая система, в которой он хранится, не смонтирована с параметром nosuid (флаг MS_NOSUID у mount(2)), и вызывающий процесс не выполняется под управлением ptrace, то фактический идентификатор пользователя вызывающего процесса меняется на идентификатор владельца файла программы. Точно также, если на файле программы установлен бит set-group-ID, то фактический идентификатор группы вызывающего процесса становится равным группе, которой принадлежит файл программы.
Фактический идентификатор пользователя процесса копируется в сохранённый идентификатор пользователя (set-user-ID), также фактический идентификатор группы копируется в сохранённый идентификатор группы (set-group-ID). Это копирование выполняется после изменения любого фактического идентификатора, которое происходит из-за выставленных бит прав set-user-ID и set-group-ID.
Если исполняемый файл является динамически-скомпонованным файлом в формате a.out, содержащим заглушки для динамических библиотек, то в начале выполнения этого файла вызывается динамический компоновщик Linux — ld.so(8), который загружает библиотеки в память и компонует их с исполняемым файлом.
Если исполняемый файл является динамически-скомпонованным файлом в формате ELF, то для загрузки необходимых динамических библиотек используется интерпретатор, указанный в сегменте PT_INTERP. Обычно, это /lib/ld-linux.so.2 для программ, скомпонованных с glibc 2. (для программ, скомпонованных со старой Linux libc5, обычно это /lib/ld-linux.so.1).
При вызове execve() сохраняются все свойства процесса, за исключением:
- Значения обработчиков всех захватываемых сигналов сбрасываются в значения по умолчанию (signal(7)).
- Любой альтернативный стек сигнала не сохраняется (sigaltstack(2)).
- Проецирование памяти не сохраняется (mmap(2)).
- Подключённые общие сегменты памяти System V отключаются (shmat(2)).
- Области общей памяти POSIX становятся неспроецированными (shm_open(3)).
- Открытые дескрипторы в очереди сообщений POSIX закрываются (mq_overview(7)).
- Все открытые именные семафоры POSIX закрываются (sem_overview(7)).
- Таймеры POSIX не сохраняются (timer_create(2)).
- Все открытые потоки каталогов (directory streams) закрываются (opendir(3)).
- Блокировки памяти не сохраняются (mlock(2), mlockall(2)).
- Обработчики завершения работы (exit handlers) не сохраняются (atexit(3), on_exit(3)).
- Окружения плавающей точки сбрасываются в настройки по умолчанию (fenv(3)).
В POSIX.1-2001 определён список сохраняемых свойств процесса. Следующие свойства процесса, имеющиеся только в Linux, также не сохраняются при execve():
- Устанавливается флаг PR_SET_DUMPABLE (prctl(2)), если выполняемая программа не имеет установленных бит set-user-ID или set-group-ID; в противном случае он очищается.
- Флаг PR_SET_KEEPCAPS (prctl(2)) очищается.
- Имя процесса, установленное через prctl(2) PR_SET_NAME (и отображаемое ps -o comm), изменяется на имя нового исполняемого файла.
- Сигнал завершения (termination signal) устанавливается в SIGCHLD (clone(2)).
Также стоит учитывать следующее:
- Все нити (threads), отличные от вызывающей, уничтожаются execve(). Мьютексы, условные переменные и другие объекты pthreads не сохраняются.
- При запуске программы выполняется эквивалент setlocale(LC_ALL, "C").
- В POSIX.1-2001 указано, что действия по отношению к любым игнорируемым или имеющим настройку по умолчанию сигналам, остаются неизменными. В POSIX.1-2001 есть одно исключение: если SIGCHLD игнорируется, то реализация может оставить обработку сигнала (disposition) неизменной или вернуть настройку по умолчанию; в Linux используется первое.
- Все ожидающие выполнения асинхронные операции ввода-вывод отменяются (aio_read(3), aio_write(3)).
- Как происходит обработка мандатов (capabilities) при вызове execve(), см. capabilities(7).
- По умолчанию, файловые дескрипторы остаются открытыми после execve(). Файловые дескрипторы, помеченные как close-on-exec (закрывать при запуске), закрываются; см. описание FD_CLOEXEC в fcntl(2). (Если файловый дескриптор закрыт, это приводит к освобождению всех имеющихся блокировок, полученных на соответствующий файл данным процессом. Подробней см. fcntl(2).) В POSIX.1-2001 сказано, что если бы файловые дескрипторы 0, 1 и 2 были закрыты после успешного вызова execve(), и процесс получил бы привилегии из-за установленных битов set-user_ID или set-group_ID на исполняемом файле, то система смогла бы открыть произвольный файл для каждого из этих дескрипторов. Считается, что переносимая программа, с привилегиями или без, не может рассчитывать, что эти три файловых дескриптора будут оставаться закрытыми после execve().
Интерпретируемые сценарии¶
Интерпретируемый сценарий — это текстовый файл, у которого установлен бит выполнения и первая строка имеет вид:
#! интерпретатор [необязательные параметры]
В поле интерпретатор должно быть указано имя файла запуска, это не имя самого файла сценария. Если в аргументе filename для execve() указан интерпретируемый сценарий, то интерпретатор будет вызван со следующими параметрами:
интерпретатор [необязательный параметр] имя файла параметр...
где параметр... — последовательность слов, указываемых аргументом argv в execve().
В целях переносимости, необязательный параметр должен быть или пустым, или задаваться одним словом (т.е., не должен содержать пробельных символов); см. ЗАМЕЧАНИЯ далее.
Ограничения на размер параметров и окружения¶
Большинство реализаций UNIX накладывает некоторые ограничения на полный размер параметра командной строки (argv) и окружения (envp), которые можно передать новой программе. POSIX.1 позволяет реализации объявить это ограничение через константу ARG_MAX (определённую в <limits.h> или сделать её доступной во время выполнения через вызов sysconf(_SC_ARG_MAX)).
В ядре Linux до версии 2.6.23 размер памяти, используемый для хранения окружения и строк параметров, был ограничен 32 страницами (определялся ядерной константой MAX_ARG_PAGES). На архитектурах с 4-КиБ размером страницы это давало максимальный размер в 128 КиБ.
Начиная с ядра версии 2.6.23, большинство архитектур поддерживают предельный размер, высчитываемый от мягкого ограничения ресурса RLIMIT_STACK (см. getrlimit(2)), который действует во время вызова execve(). (Исключение составляют архитектуры без механизма управления памятью: в них ограничение рассчитывается как и до версии 2.6.23.) Это изменение позволяет программам иметь больший список параметров и/или окружения. Для этих архитектур полный размер ограничен до 1/4 разрешённого размера стека. (Накладываемое ограничение в 1/4 позволяет новой программе всегда иметь некоторое пространство под стек.) Начиная с Linux версии 2.6.25, ядро отводит нижние 32 страницы для этого предельного размера, поэтому, даже когда RLIMIT_STACK задан слишком низко, приложения гарантированно получат, по крайней мере, столько же пространства под параметры и окружение, сколько бы они получили при работе с Linux 2.6.23 и ранее. (Это гарантия не обеспечивалась в Linux 2.6.23 и 2.6.24.) Также, размер строки ограничен 32 страницами (ядерная константа MAX_ARG_STRLEN), а максимальное число строк может быть 0x7FFFFFFF.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ¶
При успешном выполнении execve() не возвращает управление. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.
ОШИБКИ¶
- E2BIG
- Слишком большое общее количество байт для окружения (envp) и списка параметров (argv).
- EACCES
- В одном из каталогов префикса filename или интерпретатора не разрешён поиск. (см. также path_resolution(7))
- EACCES
- Файл или интерпретатор не являются обычным файлом.
- EACCES
- Не установлен бит выполнения на файле или сценарии или интерпретаторе ELF.
- EACCES
- Файловая система смонтирована с noexec.
- EFAULT
- Аргумент filename указывает за пределы доступного адресного пространства.
- EINVAL
- Исполняемый ELF-файл содержит более одного сегмента PT_INTERP (т.е., в нём указано более одного интерпретатора).
- EIO
- Произошла ошибка ввода-вывода.
- EISDIR
- Интерпретатор ELF является каталогом.
- ELIBBAD
- Не распознан формат интерпретатора ELF.
- ELOOP
- Во время определения filename, имени сценария или интерпретатора ELF встретилось слишком много символьных ссылок.
- EMFILE
- Было достигнуто ограничение по открытым файловым дескрипторам на процесс.
- ENAMETOOLONG
- Слишком длинное значение аргумента filename.
- ENFILE
- Достигнуто максимальное количество открытых файлов в системе.
- ENOENT
- Файл filename, сценарий или интерпретатор ELF не существует, или не найдена динамическая библиотека, необходимая для файлового интерпретатора.
- ENOEXEC
- Не распознан формат исполняемого файла, он не подходит для архитектуры, или имеет ошибки в формате, из-за чего не может быть выполнен.
- ENOMEM
- Недостаточное количество памяти ядра.
- ENOTDIR
- Компонент пути в filename, сценарии или интерпретаторе ELF в действительности не является каталогом.
- EPERM
- Файловая система смонтирована с nosuid, пользователь не имеет прав суперпользователя, а у файла установлен бит set-user-ID или set-group-ID.
- EPERM
- Над процессом выполняется трассировка, пользователь не имеет прав суперпользователя, а у файла установлен бит set-user-ID или set-group-ID.
- ETXTBSY
- Исполняемый файл был открыт на запись одним или более процессов.
СООТВЕТСТВИЕ СТАНДАРТАМ¶
SVr4, 4.3BSD, POSIX.1-2001. В POSIX.1-2001 не описано поведение #!, но в остальном совместимость есть.
ЗАМЕЧАНИЯ¶
Над процессами с установленными set-user-ID и set-group-ID не может выполняться ptrace(2).
В Linux игнорируются биты set-user-ID и set-group-ID на файлах со сценариями.
Результат работы при монтировании файловой системы с параметром nosuid различается в разных версиях ядра Linux: некоторые будут отказывать в запуске исполняемых файлов с установленными битами set-user-ID и set-group-ID, если это дало бы пользователю больше прав чем уже есть (и возвращать EPERM), другие просто проигнорируют биты set-user-ID и set-group-ID и успешно выполнят exec().
При указании интерпретатора сценариев в #! максимальная длина строки равна 127 символов.
Семантика необязательного параметра интерпретатора сценариев различна в разных реализациях. В Linux, вся строка после имени интерпретатора передаётся интерпретатору как единый параметр, и эта строка может содержать пробельные символы. Однако, такое поведение отличается от других систем. Некоторые системы используют первый пробел в качестве признака окончания необязательного параметра. В других системах, интерпретатор сценариев может иметь несколько параметров, и пробелы в необязательном параметре используются для их разграничения.
В Linux аргумент argv может быть задан значением NULL, что приводит к тому же результату, как если бы этот аргумент указывал бы на список, содержащий единственный указатель на NULL. Не используйте мнимые преимущества данного свойства! Это нестандартное поведение и не переносимо: в большинстве других систем UNIX это приводит к ошибке (EFAULT).
В POSIX.1-2001 указано, что значения, возвращаемые sysconf(3), должны быть неизменны в течении существования процесса. Однако, начиная с версии Linux 2.6.23, если изменяется ограничение ресурса RLIMIT_STACK, то значение, возвращаемое для _SC_ARG_MAX, также будет изменено, чтобы отразить, что ограничение на пространство для хранения параметров командной строки и окружения было изменено.
Историческая справка¶
В UNIX V6 список аргументов вызова exec() заканчивался 0, а список аргументов main заканчивался -1. Поэтому, этот список аргументов не мог быть использован напрямую в последующем вызове exec(). Начиная с UNIX V7 оба списка стали оканчиваться NULL.
ПРИМЕР¶
Данная программа запускается второй программой, представленной ниже. Она просто выводит свои параметры командной строки по одному на строку.
/* myecho.c */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) {
int j;
for (j = 0; j < argc; j++)
printf("argv[%d]: %s\n", j, argv[j]);
exit(EXIT_SUCCESS); }
Эта
программа
может
использоваться
для
запуска
программы,
чьё имя
указано в
параметре
командной
строки.
/* execve.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) {
char *newargv[] = { NULL, "hello", "world", NULL };
char *newenviron[] = { NULL };
if (argc != 2) { fprintf(stderr, "Использование: %s <запускаемый_файл>\n", argv[0]); exit(EXIT_FAILURE);
}
newargv[0] = argv[1];
execve(argv[1], newargv, newenviron);
perror("execve"); /* execve() возвращается только при ошибке */
exit(EXIT_FAILURE); }
Мы можем использовать вторую программу для запуска первой:
$ cc myecho.c -o myecho $ cc execve.c -o execve $ ./execve ./myecho argv[0]: ./myecho argv[1]: hello argv[2]: world
Также мы можем использовать эти программы для демонстрации использования интерпретатора сценариев. Для этого создадим сценарий, чей "интерпретатор" указывает на нашу программу myecho:
$ cat > script.sh #! ./myecho script-arg ^D $ chmod +x script.sh
Теперь мы можем использовать нашу программу для запуска сценария:
$ ./execve ./script.sh argv[0]: ./myecho argv[1]: script-arg argv[2]: ./script.sh argv[3]: hello argv[4]: world
СМОТРИТЕ ТАКЖЕ¶
chmod(2), fork(2), ptrace(2), execl(3), fexecve(3), getopt(3), credentials(7), environ(7), path_resolution(7), ld.so(8)
2012-05-04 | Linux |