Ao passar o argumento para main()
em um aplicativo C ou C ++, argv[0]
sempre será o nome do executável? Ou isso é apenas uma convenção comum e não é garantido que seja verdade 100% do tempo?
Ao passar o argumento para main()
em um aplicativo C ou C ++, argv[0]
sempre será o nome do executável? Ou isso é apenas uma convenção comum e não é garantido que seja verdade 100% do tempo?
Respostas:
O trabalho de adivinhação (até mesmo adivinhação educada) é divertido, mas você realmente precisa consultar os documentos de padrões para ter certeza. Por exemplo, ISO C11 afirma (grifo meu):
Se o valor de
argc
for maior que zero, a string apontada porargv[0]
representa o nome do programa;argv[0][0]
deve ser o caractere nulo se o nome do programa não estiver disponível no ambiente do host.
Portanto, não, é apenas o nome do programa, se esse nome estiver disponível. E "representa" o nome do programa, não necessariamente é o nome do programa. A seção anterior afirma:
Se o valor de
argc
for maior que zero, os membros da matrizargv[0]
atéargv[argc-1]
inclusive devem conter ponteiros para strings, que recebem valores definidos pela implementação pelo ambiente host antes da inicialização do programa.
Isso não foi alterado em relação ao C99, o padrão anterior, e significa que mesmo os valores não são ditados pelo padrão - depende inteiramente da implementação.
Isto significa que o nome do programa pode estar vazio se o ambiente de host não fornecê-la, e qualquer outra coisa, se o ambiente de acolhimento faz fornecer, desde que "qualquer outra coisa" represente de alguma forma o nome do programa. Em meus momentos mais sádicos, consideraria traduzi-lo para o suaíli, executá-lo por meio de uma cifra de substituição e armazená-lo na ordem reversa dos bytes :-).
No entanto, a implementação definida não tem um significado específico nas normas ISO - o documento de implementação deve como ele funciona. Assim, mesmo UNIX, o que pode colocar qualquer coisa que ele gosta em argv[0]
com a exec
família de chamadas, tem de (e faz) documento lo.
argv[0]
é pertinente à programação no mundo real.
Em *nix
sistemas de tipo com exec*()
chamadas, argv[0]
será o que o chamador colocar no argv0
lugar da exec*()
chamada.
O shell usa a convenção de que este é o nome do programa, e a maioria dos outros programas segue a mesma convenção, então argv[0]
geralmente o nome do programa.
Mas um programa Unix desonesto pode chamar exec()
e fazer argv[0]
qualquer coisa que quiser, então não importa o que o padrão C diga, você não pode contar com isso 100% do tempo.
De acordo com o padrão C ++, seção 3.6.1:
argv [0] deve ser o ponteiro para o caractere inicial de um NTMBS que representa o nome usado para invocar o programa ou ""
Então não, não é garantido, pelo menos pela Norma.
ISO-IEC 9899 afirma:
5.1.2.2.1 Inicialização do programa
Se o valor de
argc
for maior que zero, a string apontada porargv[0]
representa o nome do programa;argv[0][0]
deve ser o caractere nulo se o nome do programa não estiver disponível no ambiente do host. Se o valor deargc
for maior que um, as strings apontadas porargv[1]
atravésargv[argc-1]
representam os parâmetros do programa .
Eu também usei:
#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 então você só precisa analisar a string para extrair o nome do executável do caminho.
/proc/self/path/a.out
link simbólico pode ser usado no Solaris 10 e superior.
GetModuleFileNameW
deve ser usado para ser capaz de recuperar qualquer caminho, mas apenas a presença do código constitui uma boa orientação).
Esta página afirma:
O elemento argv [0] normalmente contém o nome do programa, mas não se deve confiar nele - de qualquer forma, é incomum um programa não saber seu próprio nome!
No entanto, outras páginas parecem apoiar o fato de que é sempre o nome do executável. Este afirma:
Você notará que argv [0] é o caminho e o nome do próprio programa. Isso permite que o programa descubra informações sobre si mesmo. Ele também adiciona mais um ao array de argumentos do programa, portanto, um erro comum ao buscar argumentos de linha de comando é pegar argv [0] quando quiser argv [1].
argv[0]="-/bin/sh"
? De qualquer forma, esse é o caso de todas as máquinas que usei.
Aplicativos com argv[0] !=
nome executável
muitos shells determinam se são um shell de login verificando argv[0][0] == '-'
. Os shells de login têm propriedades diferentes, principalmente por fornecerem alguns arquivos padrão, como /etc/profile
.
Normalmente é o próprio init ou getty
que adiciona o líder -
, consulte também: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152
binários de multi-chamada, talvez mais notavelmente Busybox . Esses símbolos vinculam vários nomes, por exemplo, /bin/sh
e /bin/ls
a um único executável /bin/busybox
, que reconhece de qual ferramenta usar argv[0]
.
Isso torna possível ter um único pequeno executável estaticamente vinculado que representa várias ferramentas e funcionará basicamente em qualquer ambiente Linux.
Veja também: /unix/315812/why-does-argv-include-the-program-name/315817
execve
Exemplo de POSIX argv[0] !=
executável em que o nome do executável
Outros mencionados exec
, mas aqui está um exemplo executável.
ac
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
aC
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
Então:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
Dá:
yada yada
Sim, argv[0]
também pode ser:
Testado em Ubuntu 16.10.
Não tenho certeza se é uma convenção quase universal ou um padrão, mas de qualquer forma você deve obedecê-la. Eu nunca vi isso explorado fora do Unix e sistemas semelhantes ao Unix, no entanto. Em ambientes Unix - e talvez particularmente nos velhos tempos - os programas podem ter comportamentos significativamente diferentes, dependendo do nome sob o qual são chamados.
EDITADO: Vejo em outros posts ao mesmo tempo que o meu que alguém o identificou como vindo de um padrão específico, mas tenho certeza de que a convenção é muito anterior ao padrão.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. O nome do executável não tem relação com o valor emargv[0]
.