Al pasar un argumento a main()
en una aplicación C o C ++, ¿ argv[0]
siempre será el nombre del ejecutable? ¿O es solo una convención común y no se garantiza que sea cierta el 100% del tiempo?
Al pasar un argumento a main()
en una aplicación C o C ++, ¿ argv[0]
siempre será el nombre del ejecutable? ¿O es solo una convención común y no se garantiza que sea cierta el 100% del tiempo?
Respuestas:
Las conjeturas (incluso las conjeturas) son divertidas, pero para estar seguro es necesario consultar los documentos de estándares. Por ejemplo, ISO C11 establece (mi énfasis):
Si el valor de
argc
es mayor que cero, la cadena apuntada porargv[0]
representa el nombre del programa;argv[0][0]
será el carácter nulo si el nombre del programa no está disponible en el entorno del host.
Entonces no, es solo el nombre del programa si ese nombre está disponible. Y "representa" el nombre del programa, no necesariamente es el nombre del programa. La sección anterior dice:
Si el valor de
argc
es mayor que cero, los miembros de la matrizargv[0]
hastaargv[argc-1]
inclusive contendrán punteros a cadenas, a las que el entorno host da valores definidos por la implementación antes del inicio del programa.
Esto no ha cambiado con respecto a C99, el estándar anterior, y significa que incluso los valores no están dictados por el estándar, depende completamente de la implementación.
Esto significa que el nombre del programa puede estar vacío si el entorno del host no lo proporciona, y cualquier otra cosa si el entorno del host lo proporciona, siempre que "cualquier otra cosa" represente de alguna manera el nombre del programa. En mis momentos más sádicos, consideraría traducirlo al swahili, ejecutarlo a través de un cifrado de sustitución y luego almacenarlo en orden inverso de bytes :-).
Sin embargo, definido por la implementación hace tener un significado específico en las normas ISO - el documento de ejecución obligada cómo funciona. Así que incluso UNIX, que puede poner todo lo que quiera argv[0]
con la exec
familia de llamadas, tiene que documentarlo (y lo hace).
argv[0]
es pertinente a la programación en el mundo real.
En *nix
sistemas de tipo con exec*()
llamadas, argv[0]
será lo que la persona que llama ponga en el argv0
lugar de la exec*()
llamada.
El shell usa la convención de que este es el nombre del programa, y la mayoría de los otros programas siguen la misma convención, por lo que argv[0]
normalmente el nombre del programa.
Pero un programa Unix deshonesto puede llamar exec()
y hacer argv[0]
lo que quiera, así que no importa lo que diga el estándar C, no puede contar con esto el 100% del tiempo.
Según el estándar C ++, sección 3.6.1:
argv [0] será el puntero al carácter inicial de una NTMBS que representa el nombre utilizado para invocar el programa o ""
Entonces no, no está garantizado, al menos por el Estándar.
ISO-IEC 9899 establece:
5.1.2.2.1 Inicio del programa
Si el valor de
argc
es mayor que cero, la cadena apuntada porargv[0]
representa el nombre del programa;argv[0][0]
será el carácter nulo si el nombre del programa no está disponible en el entorno del host. Si el valor deargc
es mayor que uno, las cadenas apuntado porargv[1]
medioargv[argc-1]
representar los parámetros del programa .
También he usado:
#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) */
Y luego solo tiene que analizar la cadena para extraer el nombre del ejecutable de la ruta.
/proc/self/path/a.out
enlace simbólico se puede utilizar en Solaris 10 y posteriores.
GetModuleFileNameW
debería usarse para poder recuperar cualquier ruta, pero solo la presencia del código constituye una buena guía).
Esta página dice:
El elemento argv [0] normalmente contiene el nombre del programa, pero no se debe confiar en él; de todos modos, ¡es inusual que un programa no sepa su propio nombre!
Sin embargo, otras páginas parecen respaldar el hecho de que siempre es el nombre del ejecutable. Este dice:
Notará que argv [0] es la ruta y el nombre del programa en sí. Esto permite que el programa descubra información sobre sí mismo. También agrega uno más a la matriz de argumentos del programa, por lo que un error común al obtener argumentos de la línea de comandos es tomar argv [0] cuando desee argv [1].
argv[0]="-/bin/sh"
? Ese es el caso de todas las máquinas que he usado, de todos modos.
Aplicaciones de tener argv[0] !=
nombre ejecutable
muchos shells determinan si son un shell de inicio de sesión comprobando argv[0][0] == '-'
. Los shells de inicio de sesión tienen propiedades diferentes, en particular, que obtienen algunos archivos predeterminados como /etc/profile
.
Por lo general, es el propio init o el getty
que agrega el encabezado -
, consulte también: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152
binarios de múltiples llamadas, quizás más notablemente Busybox . Estos enlaces simbólicos de varios nombres, por ejemplo, /bin/sh
ya /bin/ls
un único ejecutable /bin/busybox
, que reconoce desde qué herramienta utilizar argv[0]
.
Esto hace posible tener un solo ejecutable pequeño vinculado estáticamente que representa múltiples herramientas y funcionará básicamente en cualquier entorno Linux.
Consulte también: /unix/315812/why-does-argv-include-the-program-name/315817
Ejemplo de POSIX ejecutable execve
donde argv[0] !=
el nombre del ejecutable
Otros mencionaron exec
, pero aquí hay un ejemplo ejecutable.
C.A
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
antes de Cristo
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
Luego:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
Da:
yada yada
Sí, argv[0]
también podría ser:
Probado en Ubuntu 16.10.
No estoy seguro de si se trata de una convención casi universal o de un estándar, pero de cualquier forma debería respetarlo. Sin embargo, nunca lo he visto explotado fuera de Unix y sistemas similares a Unix. En entornos Unix, y tal vez particularmente en los viejos tiempos, los programas pueden tener comportamientos significativamente diferentes según el nombre con el que se invocan.
EDITADO: Veo en otras publicaciones al mismo tiempo que la mía que alguien lo ha identificado como proveniente de un estándar en particular, pero estoy seguro de que la convención es anterior al estándar.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. El nombre del ejecutable no guarda relación con el valor enargv[0]
.