«Argv [0] = nom-de-l'exécutable» est-il une norme acceptée ou simplement une convention courante?

102

Lorsque vous passez l'argument à main()dans une application C ou C ++, sera argv[0]toujours le nom de l'exécutable? Ou est-ce juste une convention commune et n'est pas garantie d'être vraie à 100%?

Mike Willekes
la source
20
Sur Unix, pensez à : execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. Le nom de l'exécutable n'a aucun rapport avec la valeur dans argv[0].
Jonathan Leffler

Réponses:

119

Les devinettes (même les devinettes éclairées) sont amusantes, mais vous devez vraiment consulter les documents de normes pour en être sûr. Par exemple, l'ISO C11 déclare (je souligne):

Si la valeur de argcest supérieure à zéro, la chaîne pointée par argv[0] représente le nom du programme; argv[0][0]doit être le caractère nul si le nom du programme n'est pas disponible à partir de l'environnement hôte.

Donc non, ce n'est que le nom du programme si ce nom est disponible. Et ce « représente » le nom du programme, pas nécessairement est le nom du programme. La section précédente indique:

Si la valeur de argcest supérieure à zéro, les membres du tableau argv[0]à argv[argc-1]inclusif doivent contenir des pointeurs vers des chaînes, qui reçoivent des valeurs définies par l'implémentation par l'environnement hôte avant le démarrage du programme.

Ceci est inchangé par rapport à C99, la norme précédente, et signifie que même les valeurs ne sont pas dictées par la norme - cela dépend entièrement de la mise en œuvre.

Cela signifie que le nom du programme peut être vide si l'environnement hôte ne fournit, et toute autre chose si l'environnement d'accueil ne lui fournir, à condition que « tout autre » représente en quelque sorte le nom du programme. Dans mes moments les plus sadiques, j'envisagerais de le traduire en swahili, de le faire passer par un chiffrement de substitution puis de le stocker dans l'ordre inverse des octets :-).

, La mise en œuvre défini mais n'avoir un sens spécifique dans les normes ISO - le document de mise en œuvre doit comment cela fonctionne. Ainsi, même UNIX, qui peut mettre tout ce qu'il veut avec la famille d'appels, doit (et le fait) le documenter.argv[0]exec

paxdiablo
la source
3
C'est peut-être la norme, mais Unix ne l'applique tout simplement pas, et vous ne pouvez pas compter dessus.
dmckee --- ex-moderator chaton
4
La question ne mentionne pas UNIX du tout . C'était une question C claire et simple, donc ISO C est le document de référence. Le nom du programme est une implémentation définie dans la norme, donc une implémentation est libre de faire ce qu'elle veut, y compris d'autoriser quelque chose qui n'est pas le nom réel - je pensais l'avoir clairement indiqué dans l'avant-dernière phrase.
paxdiablo du
2
Pax, je ne vous ai pas voté contre, et je n'approuve pas ceux qui l'ont fait parce que cette réponse fait aussi autorité qu'elle peut l' être. Mais je pense que le manque de fiabilité de la valeur de argv[0]est à propos de la programmation dans le monde réel.
dmckee --- ex-moderator chaton
4
@caf, c'est correct. Je l'ai vu contenir des choses aussi diverses que le chemin complet du programme ('/ progpath / prog'), juste le nom de fichier ('prog'), un nom légèrement modifié ('-prog'), un nom descriptif (' prog - un programme pour progging ') et rien (' '). L'implémentation doit définir ce qu'elle contient, mais c'est tout ce que la norme exige.
paxdiablo
3
Merci tout le monde! Grande discussion à partir d'une question (apparemment) simple. Bien que la réponse de Richard soit valable pour les systèmes d'exploitation * nix, j'ai choisi la réponse de paxdiablo parce que je suis moins intéressé par le comportement d'un système d'exploitation spécifique, et principalement par l'existence (ou l'absence) d'un standard accepté. (Si vous êtes curieux: dans le contexte de la question initiale - je n'ai pas de système d'exploitation. J'écris du code pour créer le tampon argc / argv brut pour un exécutable chargé sur un périphérique embarqué et dont je devais savoir ce que je devais faire avec argv [0]). +1 à StackOverflow pour être génial!
Mike Willekes
49

Sous *nixles systèmes de type avec exec*()appels, argv[0]sera ce que l'appelant met dans la argv0tache dans l' exec*()appel.

Le shell utilise la convention selon laquelle c'est le nom du programme, et la plupart des autres programmes suivent la même convention, donc argv[0]généralement le nom du programme.

Mais un programme Unix non autorisé peut appeler exec()et faire argv[0]tout ce qu'il veut, donc peu importe ce que dit le standard C, vous ne pouvez pas compter sur cela à 100%.

Richard Pennington
la source
4
C'est une meilleure réponse que celle de paxdiablo ci-dessus. La norme l'appelle simplement le "nom du programme", mais cela n'est appliqué nulle part à ma connaissance. Les noyaux Unix transmettent uniformément la chaîne passée à execve () inchangée au processus enfant.
Andy Ross
4
Le standard C est limité dans ce qu'il peut dire car il ne connaît pas 'execve ()' etc. Le standard POSIX ( opengroup.org/onlinepubs/9699919799/functions/execve.html ) a plus à dire - ce qui est clair que ce qui est dans argv [0] est au gré du processus, il exécute l'appel système 'execve ()' (ou associé).
Jonathan Leffler
1
@Andy, vous êtes libre d'avoir votre avis :-) Mais vous vous trompez sur l'application. Si une implémentation ne suit pas la norme, c'est qu'elle n'est pas conforme. Et en fait, étant donné qu'il est défini par l'implémentation quant à ce qu'est le "nom du programme", un système d'exploitation comme UNIX est conforme tant qu'il spécifie le nom. Cela inclut la possibilité de falsifier de manière flagrante un nom de programme en chargeant argv [0] avec tout ce que vous voulez dans la famille d'appels exec.
paxdiablo
C'est la beauté du mot "représente" dans la norme quand il fait référence à argv [0] ("il représente le nom du programme") et argv [1..N] ("ils représentent les arguments du programme"). "hirondelle à vide" est un nom de programme valide.
Richard Pennington
9

Selon la norme C ++, section 3.6.1:

argv [0] doit être le pointeur vers le caractère initial d'un NTMBS qui représente le nom utilisé pour appeler le programme ou ""

Donc non, ce n'est pas garanti, du moins par la norme.


la source
5
Je suppose que c'est une chaîne multi-octets terminée par null?
paxdiablo
6

L'ISO-CEI 9899 déclare:

5.1.2.2.1 Démarrage du programme

Si la valeur de argcest supérieure à zéro, la chaîne pointée par argv[0]représente le nom du programme; argv[0][0]doit être le caractère nul si le nom du programme n'est pas disponible à partir de l'environnement hôte. Si la valeur de argcest supérieure à un, les chaînes pointées par argv[1]through argv[argc-1]représentent les paramètres du programme .

J'ai également utilisé:

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

Et puis il vous suffit d'analyser la chaîne pour extraire le nom de l'exécutable du chemin.

Gregory Pakosz
la source
2
Le /proc/self/path/a.outlien symbolique peut être utilisable sur Solaris 10 et plus.
éphémère
Voté pour le code (ne pas dire qu'il est idéal ou correct, par exemple sous Windows GetModuleFileNameWdevrait être utilisé pour pouvoir récupérer n'importe quel chemin, mais juste la présence du code constitue un bon guide).
Acclamations et hth. - Alf du
4

Applications d'avoir argv[0] !=un nom d'exécutable

  • de nombreux shells déterminent s'il s'agit d'un shell de connexion en vérifiant argv[0][0] == '-'. Les shells de connexion ont des propriétés différentes, notamment le fait qu'ils fournissent certains fichiers par défaut tels que /etc/profile.

    C'est typiquement l'init lui-même ou gettyqui ajoute le premier -, voir aussi: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152

  • binaires multi-appels, peut-être plus particulièrement Busybox . Ces liens symbolisent plusieurs noms, par exemple /bin/shet /bin/lsvers un seul exécutable /bin/busybox, qui reconnaît l'outil à utiliser argv[0].

    Cela permet d'avoir un seul petit exécutable lié statiquement qui représente plusieurs outils, et fonctionnera essentiellement sur n'importe quel environnement Linux.

Voir aussi: /unix/315812/why-does-argv-include-the-program-name/315817

Runnable Posix execveexemple où le argv[0] !=nom exécutable

D'autres ont mentionné exec, mais voici un exemple exécutable.

ac

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

avant JC

#include <stdio.h>

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

Ensuite:

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

Donne:

yada yada

Oui, argv[0]pourrait aussi être:

Testé sur Ubuntu 16.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
3

Cette page indique:

L'élément argv [0] contient normalement le nom du programme, mais il ne faut pas s'y fier - de toute façon, il est inhabituel pour un programme de ne pas connaître son propre nom!

Cependant, d'autres pages semblent confirmer le fait qu'il s'agit toujours du nom de l'exécutable. Celui-ci déclare:

Vous remarquerez que argv [0] est le chemin et le nom du programme lui-même. Cela permet au programme de découvrir des informations sur lui-même. Il en ajoute également un de plus au tableau des arguments du programme, donc une erreur courante lors de la récupération des arguments de ligne de commande est de saisir argv [0] lorsque vous voulez argv [1].

ChrisF
la source
11
Certains programmes profitent du fait qu'ils ne connaissent pas le nom qui a été utilisé pour les invoquer. Je crois que BusyBox ( busybox.net/about.html ) fonctionne de cette façon. Il n'y a qu'un seul exécutable qui implémente de nombreux utilitaires de ligne de commande différents. Il utilise un tas de liens symboliques et argv [0] pour déterminer quel outil de ligne de commande doit être exécuté
Trent
Ouais, je me souviens avoir remarqué que "gunzip" était un lien symbolique vers "gzip", et je me suis demandé un instant comment cela fonctionnait.
David Thornley
2
De nombreux programmes consultent argv [0] pour obtenir des informations; par exemple, si le dernier composant du nom commence par un tiret ('/ bin / -sh', par exemple), alors le shell exécutera le profil et d'autres éléments comme pour un shell de connexion.
Jonathan Leffler
2
@Jon: Je pensais que les shells de connexion avaient été lancés argv[0]="-/bin/sh"? C'est le cas sur toutes les machines que j'ai utilisées, de toute façon.
éphémère le
3

Je ne sais pas s'il s'agit d'une convention ou d'une norme presque universelle, mais dans tous les cas, vous devriez la respecter. Cependant, je ne l'ai jamais vu exploité en dehors des systèmes Unix et de type Unix. Dans les environnements Unix - et peut-être en particulier dans l'ancien temps - les programmes peuvent avoir des comportements très différents selon le nom sous lequel ils sont appelés.

EDITED: Je vois dans d'autres articles en même temps que le mien que quelqu'un l'a identifié comme venant d'une norme particulière, mais je suis sûr que la convention est antérieure à la norme.

Joe Mabel
la source
1
Je souhaite vraiment que si les gens «notent» ma réponse, ils donnent une indication de ce qu'ils n'aiment pas à ce sujet.
Joe Mabel
0

Si vous démarrez un programme Amiga par Workbench, argv [0] ne sera pas défini, uniquement par CLI.

Polluks
la source