Czy podczas przekazywania argumentu main()
w aplikacji C lub C ++ argv[0]
zawsze będzie nazwa pliku wykonywalnego? A może to tylko powszechna konwencja i nie gwarantuje się, że będzie prawdziwa w 100% przypadków?
Czy podczas przekazywania argumentu main()
w aplikacji C lub C ++ argv[0]
zawsze będzie nazwa pliku wykonywalnego? A może to tylko powszechna konwencja i nie gwarantuje się, że będzie prawdziwa w 100% przypadków?
Odpowiedzi:
Zgadywanie (nawet zgadywanie oparte na wiedzy) jest fajne, ale naprawdę musisz przejść do dokumentów norm, aby mieć pewność. Na przykład ISO C11 stwierdza (moje wyróżnienie):
Jeśli wartość
argc
jest większa od zera, ciąg wskazywany przezargv[0]
reprezentuje nazwę programu;argv[0][0]
będzie znakiem pustym, jeśli nazwa programu nie jest dostępna w środowisku hosta.
Więc nie, to tylko nazwa programu, jeśli ta nazwa jest dostępna. I „reprezentuje” nazwę programu, niekoniecznie jest to nazwa programu. W poprzedniej sekcji podano:
Jeśli wartość
argc
jest większa od zera, elementy składowe tablicyargv[0]
za pośrednictwemargv[argc-1]
włącznie powinny zawierać wskaźniki do łańcuchów, którym środowisko hosta nadaje wartości zdefiniowane w ramach implementacji przed uruchomieniem programu.
Jest to niezmienione od C99, poprzedniego standardu, i oznacza, że nawet wartości nie są podyktowane przez standard - to całkowicie zależy od implementacji.
Oznacza to, że nazwa programu może być pusta, jeśli środowisko gospodarz nie zapewniają go, i cokolwiek innego, jeśli środowisko gospodarz ma dostarczyć go, pod warunkiem, że „coś jeszcze” jakoś reprezentuje nazwę programu. W moich bardziej sadystycznych momentach rozważałbym przetłumaczenie go na suahili, przepuszczenie przez szyfr podstawieniowy, a następnie zapisanie w odwrotnej kolejności bajtów :-).
Jednak realizacja zdefiniowanej ma mieć znaczenie szczególne w normach ISO - dokument realizacja musi, jak to działa. Więc nawet UNIX, który można umieścić cokolwiek ona lubi się argv[0]
z exec
rodziną połączeń, musi (i robi) dokument niego.
argv[0]
jest odpowiednia dla programowania w prawdziwym świecie.
W *nix
systemach typu z exec*()
połączeniami argv[0]
będzie to, co dzwoniący umieści w argv0
miejscu w exec*()
wywołaniu.
Powłoka używa konwencji, że jest to nazwa programu, a większość innych programów stosuje tę samą konwencję, więc argv[0]
zwykle jest to nazwa programu.
Ale fałszywy program Unix może wywołać exec()
i zrobić argv[0]
wszystko, co mu się podoba, więc bez względu na to, co mówi standard C, nie możesz liczyć na 100% czasu.
Zgodnie ze standardem C ++, sekcja 3.6.1:
argv [0] będzie wskaźnikiem do początkowego znaku NTMBS, który reprezentuje nazwę używaną do wywołania programu lub ""
Więc nie, nie jest to gwarantowane, przynajmniej przez Standard.
ISO-IEC 9899 stwierdza:
5.1.2.2.1 Uruchomienie programu
Jeśli wartość
argc
jest większa od zera, łańcuch wskazywany przezargv[0]
reprezentuje nazwę programu;argv[0][0]
będzie znakiem pustym, jeśli nazwa programu nie jest dostępna w środowisku hosta. Jeśli wartośćargc
jest większa niż jeden, ciągi wskazywane przezargv[1]
przezargv[argc-1]
reprezentują parametry programu .
Użyłem też:
#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) */
Następnie wystarczy przeanalizować ciąg, aby wyodrębnić nazwę pliku wykonywalnego ze ścieżki.
/proc/self/path/a.out
Dowiązanie może być użyteczny w systemie Solaris 10 i wyżej.
GetModuleFileNameW
należy użyć, aby móc pobrać dowolną ścieżkę, ale sama obecność kodu stanowi dobrą wskazówkę).
Aplikacje o argv[0] !=
nazwie wykonywalnej
wiele powłok określa, czy są one powłoką logowania, sprawdzając argv[0][0] == '-'
. Powłoki logowania mają różne właściwości, w szczególności to, że pobierają niektóre domyślne pliki, takie jak /etc/profile
.
Zwykle jest to sam init lub getty
dodaje wiodący -
, zobacz też: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152
pliki binarne wielu wywołań, być może przede wszystkim Busybox . Te dowiązania symboliczne obejmują wiele nazw, np. /bin/sh
I /bin/ls
do jednego pliku wykonywalnego /bin/busybox
, który rozpoznaje narzędzie, z którego należy korzystać argv[0]
.
Dzięki temu możliwe jest posiadanie jednego małego, statycznie połączonego pliku wykonywalnego, który reprezentuje wiele narzędzi i będzie działał w zasadzie w każdym środowisku Linux.
Zobacz też: /unix/315812/why-does-argv-include-the-program-name/315817
execve
Przykład Runnable POSIX, gdzie argv[0] !=
nazwa pliku wykonywalnego
Wspomnieli o innych exec
, ale oto przykład do uruchomienia.
ac
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
pne
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
Następnie:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
Daje:
yada yada
Tak, argv[0]
może być również:
Testowane na Ubuntu 16.10.
Element argv [0] zwykle zawiera nazwę programu, ale nie należy na tym polegać - w każdym razie jest to niezwykłe, że program nie zna swojej nazwy!
Jednak inne strony wydają się potwierdzać fakt, że zawsze jest to nazwa pliku wykonywalnego. Ten stwierdza:
Zauważysz, że argv [0] to ścieżka i nazwa samego programu. Pozwala to programowi na znalezienie informacji o sobie. Dodaje również jeden więcej do tablicy argumentów programu, więc częstym błędem podczas pobierania argumentów wiersza poleceń jest pobranie argv [0], gdy chcesz argv [1].
argv[0]="-/bin/sh"
? Tak jest w każdym razie na wszystkich maszynach, których używałem.
Nie jestem pewien, czy jest to prawie uniwersalna konwencja, czy norma, ale tak czy inaczej należy jej przestrzegać. Jednak nigdy nie widziałem jego wykorzystania poza systemami uniksowymi i uniksopodobnymi. W środowiskach uniksowych - a może szczególnie w dawnych czasach - programy mogą zachowywać się znacznie inaczej w zależności od nazwy, pod jaką są wywoływane.
EDYTOWANO: Z innych postów w tym samym czasie co mój widzę, że ktoś zidentyfikował to jako pochodzące z określonego standardu, ale jestem pewien, że konwencja ta jest na długo przed standardem.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. Nazwa pliku wykonywalnego nie ma związku z wartością wargv[0]
.