2010-01-12 1 views
73

При передаче аргумента main() в приложении C или C++ будет argv[0] всегда быть именем исполняемого файла? Или это просто обычное соглашение и не гарантируется в 100% случаев?Является ли «argv [0] = имя исполняемого» принятым стандартом или просто общим соглашением?

+16

В Unix рассмотрим: 'execl ("/home/hacker/.hidden/malicious ","/bin/ls "," -s ", (char *) 0);'. Имя исполняемого файла не имеет отношения к значению в 'argv [0]'. –

ответ

89

Удовлетворение (даже образованные догадки) - это весело, но вам действительно нужно пойти в документы стандартов, чтобы быть уверенным. Например, ISO C11 состояния (курсив мой):

Если значение argc больше нуля, то строка, на которую указывает argv[0]представляет название программы; argv[0][0] должен быть нулевым символом, если имя программы недоступно в среде хоста.

Нет, это только имя программы, если это имя . И это «представляет» название программы, не обязательно является название программы. Раздел до того, что сказано:

Если значение argc больше нуля, члены массива argv[0] через argv[argc-1] включительно должны содержать указатели на строки, которые приведены реализации определенных значений принимающей окружающей среды до запуска программы ,

Это не отличается от C99, предыдущего стандарта, и означает, что даже значения не диктуются стандартом - это до реализации полностью.

Это означает, что имя программы может быть пустым, если принимающая среда не обеспечить его, и все остальное, если принимающая среда делает обеспечить его, при условии, что «все остальное» каким-то образом представляет собой название программы. В мои более садистские моменты я бы подумал о переводе его на суахили, запустив его через шифр замещения, а затем сохранил его в обратном порядке байта :-).

Однако в определении реализации есть определенное значение в стандартах ИСО - реализация должна документировать, как это работает. Таким образом, даже UNIX, который может поместить все, что ему нравится, в argv[0] с семейством вызовов exec, должен (и делает) документировать его.

+0

Можете ли вы добавить ссылку на этот документ стандартов, пожалуйста, чтобы мы все могли прочитать? Благодарю. – liwp

+3

Это может быть стандарт, но unix просто не применяет его, и вы не можете рассчитывать на него. – dmckee

+4

Вопрос не упоминал UNIX * вообще *. Вопрос был простым и простым, поэтому ISO C является справочным документом. Название программы - это реализация, определенная в стандарте, поэтому реализация может свободно делать то, что она хочет, в том числе разрешать что-то там, где это не настоящее имя, - я думал, что сделал это ясно в предпоследнем предложении. – paxdiablo

4

This page состояния:

элемент ARGV [0] обычно содержит название программы, но это не следует полагаться - в любом случае это необычно для программы не знать свое собственное имя!

Однако, другие страницы, похоже, поддерживают тот факт, что это всегда имя исполняемого файла. This one указывается:

Вы заметите, что argv [0] - это путь и имя самой программы. Это позволяет программе обнаруживать информацию о себе. Он также добавляет еще один массив аргументов программы, поэтому общая ошибка при извлечении аргументов из командной строки заключается в том, чтобы захватить argv [0], когда вы хотите argv [1].

+10

Некоторые программы используют тот факт, что они не знают имя, которое использовалось для их вызова. Я считаю, что BusyBox (http://www.busybox.net/about.html) работает таким образом. Существует только один исполняемый файл, реализующий множество различных утилит командной строки. Он использует кучу символических ссылок и argv [0], чтобы определить, какой инструмент командной строки должен быть запущен. – Trent

+0

Да, я помню, заметив, что «gunzip» был символической ссылкой на «gzip» и некоторое время размышлял над тем, как это работает. –

+2

Многие программы смотрят на argv [0] для информации; например, если последний компонент имени начинается с тире (например,/bin/-sh), то оболочка будет запускать профиль и другие материалы, как для оболочки входа. –

8

Согласно стандарту C++, раздел 3.6.1:

ARGV [0] должен быть указатель на исходного характера NTMBS, что представляет собой имя, используемое для вызова программы или ""

Нет, это не гарантируется, по крайней мере, стандартом.

+4

Я предполагаю, что это пустая строка с длинными байтами? – paxdiablo

+1

Да, действительно. – 2010-01-12 17:47:31

40

Под *nix систем типа с exec*() вызовов, argv[0] будет то, что абонент помещает в argv0 место в exec*() вызова.

В оболочке используется соглашение о том, что это имя программы, и большинство других программ соответствуют одному и тому же соглашению, поэтому argv[0] обычно является именем программы.

Но программа-мошенник Unix может позвонить exec() и сделать argv[0] всем, что ему нравится, поэтому независимо от того, что говорит стандарт C, вы не можете рассчитывать на это 100% времени.

+4

Это лучший ответ, чем выше paxdiablo. Стандарт просто называет это «имя программы», но это не применяется нигде, насколько мне известно. Ядра Unix равномерно передают строку, переданную execve(), без изменений в дочерний процесс. –

+4

Стандарт C ограничен в том, что он может сказать, потому что он не знает о «execve()» и т. Д. Стандарт POSIX (http://www.opengroup.org/onlinepubs/9699919799/functions/execve.html) имеет более того - ясно, что то, что находится в argv [0], по прихоти процесса выполняет «execve()» (или связанный) системный вызов. –

+1

@ Энди, ты можешь свободно высказывать свое мнение :-) Но ты не прав насчет принудительного исполнения. Если реализация не соответствует стандарту, то она не соответствует требованиям. И на самом деле, поскольку это реализация, определяющая, что такое «имя программы», такая ОС, как UNIX *, соответствует *, если она задает имя. Это включает в себя возможность явно подделывать имя программы, загружая argv [0] с помощью чего угодно в семействе вызовов exec. – paxdiablo

4

ISO-IEC 9899 гласит:

5.1.2.2.1 запуска программы

Если значение argc больше нуля, то строка, на которую указывает argv[0] представляет имя программы; argv[0][0] должен быть нулевым символом, если имя программы недоступно в среде хоста. Если значение argc больше единицы, строки, на которую указывает argv[1] через argv[argc-1] представляют программу параметры.

Я также использовал:

#if defined(_WIN32) 
    static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity) 
    { 
    return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity); 
    } 
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */ 
    #include <unistd.h> 
    static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity) 
    { 
    size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1); 
    pathName[pathNameSize] = '\0'; 
    return pathNameSize; 
    } 
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */ 
    #include <mach-o/dyld.h> 
    static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity) 
    { 
    uint32_t pathNameSize = 0; 

    _NSGetExecutablePath(NULL, &pathNameSize); 

    if (pathNameSize > pathNameCapacity) 
     pathNameSize = pathNameCapacity; 

    if (!_NSGetExecutablePath(pathName, &pathNameSize)) 
    { 
     char real[PATH_MAX]; 

     if (realpath(pathName, real) != NULL) 
     { 
     pathNameSize = strlen(real); 
     strncpy(pathName, real, pathNameSize); 
     } 

     return pathNameSize; 
    } 

    return 0; 
    } 
#else /* else of: #elif defined(__APPLE__) */ 
    #error provide your own implementation 
#endif /* end of: #if defined(_WIN32) */ 

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

+2

Символьная ссылка '/ proc/self/path/a.out' может использоваться на Solaris 10 и выше. – ephemient

+0

Упрощенный для кода (не сказать, что это идеальный или правильный, например, в Windows 'GetModuleFileNameW' должен использоваться, чтобы иметь возможность получить любой путь, но наличие кода является хорошим руководством). –

+0

BTW, в последнее время я выпустил https://github.com/gpakosz/whereami –

2

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

EDITED: Я вижу из других сообщений одновременно с моим, что кто-то определил его как исходящий из определенного стандарта, но я уверен, что соглашение давно предшествует стандарту.

+1

Я уверен, что если люди собираются «пометить» мой ответ, они дадут некоторое представление о том, что им не нравится в этом. –

2

Runnable POSIX execve пример, где argv[0] != имя исполняемого

Другие упоминали exec, но здесь является работоспособным пример.

a.c

#define _XOPEN_SOURCE 700 
#include <unistd.h> 

int main(void) { 
    char *argv[] = {"yada yada", NULL}; 
    char *envp[] = {NULL}; 
    execve("b.out", argv, envp); 
} 

b.c

#include <stdio.h> 

int main(int argc, char **argv) { 
    puts(argv[0]); 
} 

Тогда:

gcc a.c -o a.out 
gcc b.c -o b.out 
./a.out 

Дает:

yada yada 

Да, argv[0] также может быть:

Проверено на Ubuntu 16.10.