"Argv [0] = name-of-executable" è uno standard accettato o solo una convenzione comune?


102

Quando si passa un argomento a main() in un'applicazione C o C ++, sarà argv[0]sempre il nome dell'eseguibile? O è solo una convenzione comune e non è garantito che sia vero il 100% delle volte?


19
Su Unix, prendere in considerazione: execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. Il nome dell'eseguibile non ha alcuna relazione con il valore in argv[0].
Jonathan Leffler

Risposte:


118

Le supposizioni (anche le congetture istruite) sono divertenti, ma per essere sicuri devi davvero consultare i documenti degli standard. Ad esempio, ISO C11 afferma (la mia enfasi):

Se il valore di argcè maggiore di zero, la stringa puntata da argv[0] rappresenta il nome del programma; argv[0][0]deve essere il carattere null se il nome del programma non è disponibile dall'ambiente host.

Quindi no, è solo il nome del programma se quel nome è disponibile. E "rappresenta" il nome del programma, non necessariamente è il nome del programma. La sezione prima di quella afferma:

Se il valore di argcè maggiore di zero, i membri dell'array argv[0]tramite argv[argc-1]inclusivo conterranno puntatori a stringhe, a cui vengono forniti valori definiti dall'implementazione dall'ambiente host prima dell'avvio del programma.

Questo è invariato rispetto a C99, lo standard precedente, e significa che anche i valori non sono dettati dallo standard: dipende interamente dall'implementazione.

Ciò significa che il nome del programma può essere vuoto se l'ambiente host non lo fornisce e qualsiasi altra cosa se l'ambiente host lo fa fornisce, a condizione che "qualsiasi altra cosa" rappresenti in qualche modo il nome del programma. Nei miei momenti più sadici, prenderei in considerazione la possibilità di tradurlo in swahili, eseguirlo attraverso un cifrario di sostituzione e quindi memorizzarlo in ordine inverso dei byte :-).

Tuttavia, l'attuazione definiti ha un significato specifico nelle norme ISO - il documento di attuazione must come funziona. Quindi anche UNIX, che può mettere tutto ciò che vuole argv[0]con la execfamiglia di chiamate, deve (e lo fa) documentarlo.


3
Potrebbe essere lo standard, ma unix semplicemente non lo impone e non puoi contare su di esso.
dmckee --- gattino ex moderatore

4
La domanda non ha menzionato UNIX a tutti . Era una domanda in C chiara e semplice, quindi ISO C è il documento di riferimento. Il nome del programma è l'implementazione definita nello standard, quindi un'implementazione è libera di fare ciò che vuole, incluso consentire qualcosa che non è il nome effettivo - pensavo di averlo chiarito nella penultima frase.
paxdiablo

2
Pax, non ti ho votato, e non approvo quelli che l'hanno fatto perché questa risposta è il più autorevole possibile . Ma penso che l'inaffidabilità del valore di argv[0]sia relativa alla programmazione nel mondo reale.
dmckee --- gattino ex moderatore

4
@caf, è corretto. L'ho visto contenere cose così diverse come il percorso completo del programma ('/ progpath / prog'), solo il nome del file ('prog'), un nome leggermente modificato ('-prog'), un nome descrittivo (' prog - un programma per progging ') e niente (' '). L'implementazione deve definire ciò che contiene, ma questo è tutto ciò che lo standard richiede.
paxdiablo

3
Grazie a tutti! Grande discussione da una domanda (apparentemente) semplice. Sebbene la risposta di Richard sia valida per i sistemi operativi * nix, ho scelto la risposta di paxdiablo perché sono meno interessato al comportamento di uno specifico sistema operativo e principalmente all'esistenza (o all'assenza di) uno standard accettato. (Se sei curioso: nel contesto della domanda originale, non ho un sistema operativo. Sto scrivendo codice per creare il buffer argc / argv grezzo per un eseguibile caricato su un dispositivo incorporato e avevo bisogno di sapere cosa avrei dovuto fare con argv [0]). +1 a StackOverflow per essere fantastico!
Mike Willekes

48

In *nixsistemi di tipo con exec*()chiamate, argv[0]sarà tutto ciò che il chiamante mette nel argv0posto nella exec*()chiamata.

La shell utilizza la convenzione secondo cui questo è il nome del programma e la maggior parte degli altri programmi segue la stessa convenzione, quindi di argv[0]solito il nome del programma.

Ma un programma Unix canaglia può chiamare exec()e fare argv[0]tutto ciò che vuole, quindi non importa cosa dice lo standard C, non puoi contare su questo il 100% delle volte.


4
Questa è una risposta migliore di quella di paxdiablo sopra. Lo standard lo chiama semplicemente "nome del programma", ma questo non è applicato da nessuna parte per quanto ne so. I kernel Unix passano in modo uniforme la stringa passata a execve () invariata al processo figlio.
Andy Ross

4
Lo standard C è limitato in ciò che può dire perché non sa di 'execve ()' ecc. Lo standard POSIX ( opengroup.org/onlinepubs/9699919799/functions/execve.html ) ha più da dire - chiarendo che ciò che è in argv [0] è al capriccio del processo che esegue la chiamata di sistema 'execve ()' (o correlata).
Jonathan Leffler

1
@ Anddy, sei libero di avere le tue opinioni :-) Ma ti sbagli sull'applicazione. Se un'implementazione non segue lo standard, allora non è conforme. E infatti, poiché è definito dall'implementazione in base a cosa sia il "nome del programma", un sistema operativo come UNIX è conforme fintanto che specifica quale sia il nome. Ciò include la possibilità di falsificare apertamente un nome di programma caricando argv [0] con tutto ciò che si desidera nella famiglia di chiamate exec.
paxdiablo

Questa è la bellezza della parola "rappresenta" nello standard quando si riferisce ad argv [0] ("rappresenta il nome del programma") e argv [1..N] ("rappresentano gli argomenti del programma"). "unladen swallow" è un nome di programma valido.
Richard Pennington

8

Secondo lo standard C ++, sezione 3.6.1:

argv [0] deve essere il puntatore al carattere iniziale di un NTMBS che rappresenta il nome utilizzato per invocare il programma o ""

Quindi no, non è garantito, almeno dallo Standard.


5
Presumo che sia una stringa multibyte terminata da null?
paxdiablo

5

ISO-IEC 9899 afferma:

5.1.2.2.1 Avvio del programma

Se il valore di argcè maggiore di zero, la stringa puntata da argv[0]rappresenta il nome del programma; argv[0][0]deve essere il carattere null se il nome del programma non è disponibile dall'ambiente host. Se il valore di argcè maggiore di uno, le stringhe puntate da argv[1]tramite argv[argc-1]rappresentano i parametri del programma .

Ho anche usato:

#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) */

E poi devi solo analizzare la stringa per estrarre il nome dell'eseguibile dal percorso.


2
Il /proc/self/path/a.outcollegamento simbolico può essere utilizzabile su Solaris 10 e versioni successive.
effimero

Upvoted per il codice (non dicendo che sia ideale o corretto, ad esempio su Windows GetModuleFileNameWdovrebbe essere usato per poter recuperare qualsiasi percorso, ma solo la presenza del codice costituisce una buona guida).
Saluti e salute. - Alf

4

Questa pagina afferma:

L'elemento argv [0] normalmente contiene il nome del programma, ma non ci si dovrebbe fare affidamento su questo - comunque è insolito che un programma non conosca il proprio nome!

Tuttavia, altre pagine sembrano confermare il fatto che è sempre il nome dell'eseguibile. Questo afferma:

Noterai che argv [0] è il percorso e il nome del programma stesso. Ciò consente al programma di scoprire informazioni su se stesso. Ne aggiunge anche uno in più all'array di argomenti del programma, quindi un errore comune quando si recuperano gli argomenti della riga di comando è quello di prendere argv [0] quando si vuole argv [1].


11
Alcuni programmi sfruttano il fatto di non conoscere il nome utilizzato per invocarli. Credo che BusyBox ( busybox.net/about.html ) funzioni in questo modo. C'è un solo eseguibile che implementa molte diverse utilità della riga di comando. Utilizza una serie di collegamenti simbolici e argv [0] per determinare quale strumento da riga di comando deve essere eseguito
Trent

Sì, ricordo di aver notato che "gunzip" era un collegamento simbolico a "gzip" e di essermi chiesto per un momento come funzionasse.
David Thornley,

2
Molti programmi guardano argv [0] per informazioni; per esempio, se l'ultimo componente del nome inizia con un trattino ('/ bin / -sh', per esempio), la shell eseguirà il profilo e altre cose come per una shell di login.
Jonathan Leffler

2
@ Jon: pensavo che le shell di login fossero state avviate con argv[0]="-/bin/sh"? Questo è il caso di tutte le macchine che ho usato, comunque.
effimero

3

Applicazioni di avere un argv[0] !=nome eseguibile

  • molte shell determinano se sono una shell di login controllando argv[0][0] == '-'. Le shell di accesso hanno proprietà diverse, in particolare che forniscono alcuni file predefiniti come/etc/profile .

    In genere è l'init stesso o gettyche aggiunge l'interlinea -, vedere anche: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152

  • binari multi-chiamata, forse in particolare Busybox . Questi link simbolici più nomi, ad esempio /bin/shea /bin/lsun singolo eseguibile /bin/busybox, che riconosce da quale strumento utilizzareargv[0] .

    Ciò rende possibile avere un unico piccolo eseguibile collegato staticamente che rappresenta più strumenti e funzionerà praticamente su qualsiasi ambiente Linux.

Vedi anche: /unix/315812/why-does-argv-include-the-program-name/315817

execveEsempio POSIX argv[0] !=eseguibile in cui nome eseguibile

Altri menzionati exec , ma ecco un esempio eseguibile.

AC

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

avanti Cristo

#include <stdio.h>

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

Poi:

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

Dà:

yada yada

Sì, argv[0]potrebbe anche essere:

Testato su Ubuntu 16.10.


2

Non sono sicuro che sia una convenzione o uno standard quasi universale, ma in entrambi i casi dovresti rispettarlo. Tuttavia, non l'ho mai visto sfruttato al di fuori di Unix e sistemi simili a Unix. Negli ambienti Unix - e forse in particolare ai vecchi tempi - i programmi potevano avere comportamenti significativamente diversi a seconda del nome con cui vengono invocati.

MODIFICATO: Vedo da altri post contemporaneamente al mio che qualcuno l'ha identificato come proveniente da uno standard particolare, ma sono sicuro che la convenzione è antecedente allo standard.


1
Spero proprio che se le persone vogliono "segnare" la mia risposta, diano qualche indicazione su ciò che non gli piace.
Joe Mabel

0

Se avvii un programma Amiga da Workbench argv [0] non sarà impostato, solo dalla CLI.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.