При передаче аргумента main()
в приложение C или C ++ argv[0]
всегда будет имя исполняемого файла? Или это просто обычное соглашение и не гарантируется, что оно будет верным в 100% случаев?
При передаче аргумента main()
в приложение C или C ++ argv[0]
всегда будет имя исполняемого файла? Или это просто обычное соглашение и не гарантируется, что оно будет верным в 100% случаев?
Ответы:
Предположения (даже обоснованные предположения) - это весело, но вам действительно нужно обратиться к документам стандартов, чтобы быть уверенным. Например, ISO C11 гласит (выделено мной):
Если значение
argc
больше нуля, строка, на которую указывает,argv[0]
представляет имя программы;argv[0][0]
должен быть нулевым символом, если имя программы недоступно в среде хоста.
Так что нет, это только имя программы, если оно доступно. И это «представляет» название программы, не обязательно является названием программы. В разделе перед этим говорится:
Если значение
argc
больше нуля, элементы массиваargv[0]
доargv[argc-1]
включительно должны содержать указатели на строки, которым среда хоста присваивает значения, определяемые реализацией, до запуска программы.
Это не отличается от C99, предыдущего стандарта, и означает, что даже значения не продиктованы стандартом - это полностью зависит от реализации.
Это означает, что имя программы может быть пустым, если среда хоста его не предоставляет, и что-либо еще, если среда хоста его предоставляет, при условии, что «что-нибудь еще» каким-то образом представляет имя программы. В мои более садистские моменты я бы подумал о том, чтобы перевести его на суахили, пропустить через шифр подстановки, а затем сохранить в обратном порядке байтов :-).
Однако реализация определенная действительно имеет определенное значение в стандартах ISO - реализация должна документировать , как это работает. Так что даже UNIX, который может помещать все, что угодно, argv[0]
с exec
семейством вызовов, должен (и делает) это документально.
argv[0]
имеет отношение к программированию в реальном мире.
В *nix
системах типов с exec*()
вызовами argv[0]
это будет то, что вызывающий абонент ставит на argv0
место в exec*()
вызове.
Оболочка использует соглашение, что это имя программы, и большинство других программ следуют тому же соглашению, поэтому argv[0]
обычно это имя программы.
Но мошенническая программа Unix может вызывать exec()
и создавать argv[0]
все, что ей заблагорассудится, поэтому независимо от того, что говорит стандарт C, вы не можете рассчитывать на это в 100% случаев.
Согласно стандарту C ++, раздел 3.6.1:
argv [0] должен быть указателем на начальный символ NTMBS, который представляет имя, используемое для вызова программы, или ""
Так что нет, это не гарантируется, по крайней мере, стандартом.
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) */
А затем вам просто нужно проанализировать строку, чтобы извлечь имя исполняемого файла из пути.
/proc/self/path/a.out
Символическая может использоваться на Solaris 10 и выше.
GetModuleFileNameW
следует использовать для получения любого пути, но только наличие кода является хорошим руководством).
Приложения с 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.
На этой странице говорится:
Элемент argv [0] обычно содержит имя программы, но на него не следует полагаться - в любом случае программа не знает своего имени!
Однако другие страницы, похоже, подтверждают тот факт, что это всегда имя исполняемого файла. В нем говорится:
Вы заметите, что argv [0] - это путь и имя самой программы. Это позволяет программе обнаруживать информацию о себе. Он также добавляет еще один к массиву аргументов программы, поэтому распространенной ошибкой при получении аргументов командной строки является захват argv [0], когда вы хотите argv [1].
argv[0]="-/bin/sh"
? Во всяком случае, так обстоит дело на всех машинах, которые я использовал.
Я не уверен, является ли это почти универсальным соглашением или стандартом, но в любом случае вы должны его соблюдать. Однако я никогда не видел, чтобы он использовался за пределами Unix и Unix-подобных систем. В средах Unix - и, возможно, особенно в былые времена - программы могли иметь существенно разное поведение в зависимости от имени, под которым они вызывались.
EDITED: из других сообщений одновременно с моими я вижу, что кто-то определил, что это происходит из определенного стандарта, но я уверен, что соглашение задолго до этого стандарта.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Имя исполняемого файла не имеет отношения к значению вargv[0]
.