Czy „argv [0] = nazwa-pliku wykonywalnego” jest akceptowanym standardem, czy tylko powszechną konwencją?
102
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?
Na Unix, należy rozważyć: execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. Nazwa pliku wykonywalnego nie ma związku z wartością w argv[0].
Jonathan Leffler
Odpowiedzi:
119
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ść argcjest większa od zera, ciąg wskazywany przez argv[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ść argcjest większa od zera, elementy składowe tablicy argv[0]za pośrednictwem argv[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 execrodziną połączeń, musi (i robi) dokument niego.
To może być standard, ale unix po prostu go nie wymusza i nie można na to liczyć.
dmckee --- kociak ex-moderator
4
Pytanie nie wspominając UNIX w ogóle . Było to proste i proste pytanie w C, stąd ISO C jest dokumentem odniesienia. Nazwa programu to implementacja zdefiniowana w standardzie, więc implementacja może robić, co chce, w tym dopuszczać coś, co nie jest rzeczywistą nazwą - pomyślałem, że wyjaśniłem to w przedostatnim zdaniu.
paxdiablo
2
Pax, nie głosowałem na ciebie i nie pochwalam tych, którzy to zrobili, ponieważ ta odpowiedź jest tak autorytatywna, jak to tylko możliwe . Ale myślę, że zawodność wartości argv[0]jest odpowiednia dla programowania w prawdziwym świecie.
dmckee --- kociak byłego moderatora
4
@caf, zgadza się. Widziałem, że zawiera tak różnorodne rzeczy, jak pełna ścieżka programu („/ progpath / prog”), tylko nazwa pliku („prog”), nieco zmodyfikowana nazwa („-prog”), nazwa opisowa („ prog - program do proggowania ') i nic (' '). Implementacja musi definiować, co zawiera, ale to wszystko, czego wymaga standard.
paxdiablo
3
Dziękuję wszystkim! Świetna dyskusja z (pozornie) prostego pytania. Chociaż odpowiedź Richarda jest ważna dla systemów operacyjnych * nix, wybrałem odpowiedź paxdiablo, ponieważ jestem mniej zainteresowany zachowaniem określonego systemu operacyjnego, a przede wszystkim interesuje mnie istnienie (lub brak) akceptowanego standardu. (Jeśli jesteś ciekawy: w kontekście pierwotnego pytania - nie mam systemu operacyjnego. Piszę kod, aby zbudować surowy bufor argc / argv dla pliku wykonywalnego załadowanego na urządzenie osadzone i chciałem wiedzieć, co powinienem zrobić z argv [0]). +1 do StackOverflow za bycie niesamowitym!
Mike Willekes
49
W *nixsystemach typu z exec*()połączeniami argv[0]będzie to, co dzwoniący umieści w argv0miejscu 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.
To lepsza odpowiedź niż powyższa odpowiedź paxdiablo. Standard nazywa to po prostu „nazwą programu”, ale według mojej wiedzy nie jest to nigdzie narzucane. Jądra systemu Unix jednolicie przekazują ciąg przekazany do execve () niezmieniony do procesu potomnego.
Andy Ross
4
Standard C jest ograniczony w tym, co może powiedzieć, ponieważ nie wie o 'execve ()' itp. Standard POSIX ( opengroup.org/onlinepubs/9699919799/functions/execve.html ) ma więcej do powiedzenia - wyjaśniając to że to, co znajduje się w argv [0], jest kaprysem procesu, wykonuje wywołanie systemowe „execve ()” (lub powiązane).
Jonathan Leffler
1
@Andy, możesz mieć swoje opinie :-) Ale mylisz się co do egzekwowania. Jeśli implementacja nie jest zgodna ze standardem, oznacza to, że jest niezgodna. I faktycznie, ponieważ jest zdefiniowana w ramach implementacji jako „nazwa programu”, system operacyjny taki jak UNIX jest zgodny, o ile określa, jaka jest nazwa. Obejmuje to możliwość rażącego fałszowania nazwy programu przez załadowanie do argv [0] wszystkiego, co chcesz z rodziny wywołań exec.
paxdiablo
Na tym polega piękno słowa „reprezentuje” w standardzie, kiedy odnosi się do argv [0] („reprezentuje nazwę programu”) i argv [1..N] („reprezentują argumenty programu”). „jaskółka nieobciążona” to poprawna nazwa programu.
Richard Pennington,
9
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.
Zakładam, że to ciąg wielobajtowy zakończony wartością zerową?
paxdiablo
6
ISO-IEC 9899 stwierdza:
5.1.2.2.1 Uruchomienie programu
Jeśli wartość argcjest większa od zera, łańcuch wskazywany przez argv[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ść argcjest większa niż jeden, ciągi wskazywane przez argv[1]przez argv[argc-1]reprezentują parametry programu .
/proc/self/path/a.outDowiązanie może być użyteczny w systemie Solaris 10 i wyżej.
ephemient
Głosowano za kodem (nie mówiąc, że jest idealny lub poprawny, np. W systemie Windows GetModuleFileNameWnależy użyć, aby móc pobrać dowolną ścieżkę, ale sama obecność kodu stanowi dobrą wskazówkę).
Pozdrawiam i hth. - Alf
4
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.
pliki binarne wielu wywołań, być może przede wszystkim Busybox . Te dowiązania symboliczne obejmują wiele nazw, np. /bin/shI /bin/lsdo 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.
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].
Niektóre programy wykorzystują fakt, że nie znają nazwy użytej do ich wywołania. Uważam, że BusyBox ( busybox.net/about.html ) działa w ten sposób. Jest tylko jeden plik wykonywalny, który implementuje wiele różnych narzędzi wiersza poleceń. Używa zestawu dowiązań symbolicznych i argumentu argv [0], aby określić, jakie narzędzie wiersza poleceń powinno zostać uruchomione
Trent
Tak, pamiętam, że zauważyłem, że „gunzip” był symbolicznym dowiązaniem do „gzip” i przez chwilę zastanawiałem się, jak to działa.
David Thornley,
2
Wiele programów szuka informacji w argv [0]; na przykład, jeśli ostatni składnik nazwy zaczyna się od myślnika (na przykład „/ bin / -sh”), to powłoka uruchomi profil i inne rzeczy, jak dla powłoki logowania.
Jonathan Leffler
2
@Jon: Myślałem, że powłoki logowania zostały uruchomione argv[0]="-/bin/sh"? Tak jest w każdym razie na wszystkich maszynach, których używałem.
ephemient
3
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]
.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):
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:
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]
zexec
rodziną połączeń, musi (i robi) dokument niego.źródło
argv[0]
jest odpowiednia dla programowania w prawdziwym świecie.W
*nix
systemach typu zexec*()
połączeniamiargv[0]
będzie to, co dzwoniący umieści wargv0
miejscu wexec*()
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.źródło
Zgodnie ze standardem C ++, sekcja 3.6.1:
Więc nie, nie jest to gwarantowane, przynajmniej przez Standard.
źródło
ISO-IEC 9899 stwierdza:
Użyłem też:
Następnie wystarczy przeanalizować ciąg, aby wyodrębnić nazwę pliku wykonywalnego ze ścieżki.
źródło
/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 wykonywalnejwiele 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 # 300152pliki 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, gdzieargv[0] !=
nazwa pliku wykonywalnegoWspomnieli o innych
exec
, ale oto przykład do uruchomienia.ac
pne
Następnie:
Daje:
Tak,
argv[0]
może być również:Testowane na Ubuntu 16.10.
źródło
Ta strona zawiera :
Jednak inne strony wydają się potwierdzać fakt, że zawsze jest to nazwa pliku wykonywalnego. Ten stwierdza:
źródło
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.
źródło
Jeśli uruchomisz program Amigi przez Workbench, argv [0] nie zostanie ustawiony, tylko przez CLI.
źródło