Является ли «argv [0] = имя исполняемого файла» принятым стандартом или просто общепринятым соглашением?


102

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


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

Ответы:


119

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

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

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

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

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

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

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


3
Это может быть стандартом, но unix просто не обеспечивает его соблюдения, и вы не можете рассчитывать на это.
dmckee --- котенок экс-модератора

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

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

4
@caf, это правильно. Я видел, что он содержит такие разные вещи, как полный путь к программе ('/ progpath / prog'), только имя файла ('prog'), слегка измененное имя ('-prog'), описательное имя (' прога - программа для прогонки ') и ничего (' '). Реализация должна определять, что в ней содержится, но это все, что требует стандарт.
paxdiablo

3
Спасибо всем! Отличное обсуждение из (казалось бы) простого вопроса. Хотя ответ Ричарда действителен для операционных систем * nix, я выбрал ответ paxdiablo, потому что меня меньше интересует поведение конкретной ОС и в первую очередь интересует наличие (или отсутствие) принятого стандарта. (Если вам интересно: в контексте исходного вопроса - у меня нет операционной системы. Я пишу код для создания необработанного буфера argc / argv для исполняемого файла, загруженного на встроенное устройство, и мне нужно знать, что мне делать с argv [0]). +1 к StackOverflow за то, что он классный!
Майк Виллекес,

49

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

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

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


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

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

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

В этом прелесть слова «представляет» в стандарте, когда оно относится к argv [0] («представляет имя программы») и argv [1..N] («они представляют аргументы программы»). "незагруженная ласточка" - допустимое название программы.
Ричард Пеннингтон,

9

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

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

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


5
Я предполагаю, что многобайтовая строка с завершающим нулем?
paxdiablo

6

ISO-IEC 9899 утверждает:

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

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

Проголосовали за код (не говоря, что он идеальный или правильный, например, в Windows GetModuleFileNameWследует использовать для получения любого пути, но только наличие кода является хорошим руководством).
Приветствия и hth. - Alf

4

Приложения с argv[0] !=исполняемым именем

  • многие оболочки определяют, являются ли они оболочкой входа, путем проверки argv[0][0] == '-'. Оболочки входа в систему имеют разные свойства, в частности то, что они являются источником некоторых файлов по умолчанию, таких как /etc/profile.

    Обычно это сам init или gettyдобавляющий ведущий -, см. Также: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152

  • бинарные файлы с несколькими вызовами, возможно, в первую очередь Busybox . Эти символические ссылки на несколько имен, например, /bin/shи /bin/lsна один исполняемый файл /bin/busybox, который распознает, какой инструмент использовать argv[0].

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

См. Также: /unix/315812/why-does-argv-include-the-program-name/315817

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

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

ac

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

До нашей эры

#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.


3

На этой странице говорится:

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

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

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


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

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

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

2
@Jon: Я думал, что оболочки входа в систему были запущены argv[0]="-/bin/sh"? Во всяком случае, так обстоит дело на всех машинах, которые я использовал.
ephemient

3

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

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


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

0

Если вы запустите программу Amiga с помощью Workbench, argv [0] не будет установлен, только с помощью интерфейса командной строки.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.