#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
¿Esto llama indirectamente main
? ¿cómo?
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
¿Esto llama indirectamente main
? ¿cómo?
Respuestas:
El lenguaje C define el entorno de ejecución en dos categorías: independiente y alojado . En ambos entornos de ejecución, el entorno llama a una función para el inicio del programa.
En un entorno independiente , la función de inicio del programa se puede definir por implementación, mientras que en el entorno alojado debería serlo main
. Ningún programa en C puede ejecutarse sin la función de inicio del programa en los entornos definidos.
En su caso, main
está oculto por las definiciones del preprocesador. begin()
se expandirá a lo decode(a,n,i,m,a,t,e)
que más se expandirá main
.
int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main()
decode(s,t,u,m,p,e,d)
es una macro parametrizada con 7 parámetros. La lista de reemplazo para esta macro es m##s##u##t
. m, s, u
y t
son 4 º , 1 st , 3 rd y 2 nd parámetro utilizado en la lista de reemplazo.
s, t, u, m, p, e, d
1 2 3 4 5 6 7
El descanso no sirve de nada ( solo para ofuscar ). El argumento que se pasa a decode
es " a , n , i , m , a, t, e", por lo que los identificadores m, s, u
y t
se reemplazan con argumentos m, a, i
y n
, respectivamente.
m --> m
s --> a
u --> i
t --> n
_start()
. O incluso en un nivel más bajo, puedo intentar alinear el inicio de mi programa con la dirección a la que se establece la IP después del inicio. main()
es la biblioteca C estándar . C en sí mismo no impone restricciones sobre esto.
decode(a,n,i,m,a,t,e)
convierte m##a##i##n
? ¿Reemplaza a los personajes? ¿Puede proporcionar un enlace a la documentación de la decode
función? Gracias.
begin
se define para ser reemplazado por lo decode(a,n,i,m,a,t,e)
que se define antes. Esta función toma los argumentos s,t,u,m,p,e,d
y los concatena en esta forma m##s##u##t
( ##
significa concatenar). Es decir, ignora los valores de p, e y d. A medida que "llama" decode
con s = a, t = n, u = i, m = m, efectivamente reemplaza begin
con main
.
Intente usar gcc -E source.c
, la salida termina con:
int main()
{
printf("Ha HA see how it is?? ");
}
Entonces, una main()
función es realmente generada por el preprocesador.
El programa en cuestión hace la llamada main()
debido a la expansión de la macro, pero su suposición es errónea - que no tiene que llamar main()
a todos!
Estrictamente hablando, puede tener un programa en C y poder compilarlo sin tener un main
símbolo. main
es algo a lo que c library
espera saltar, después de que haya terminado su propia inicialización. Por lo general, salta main
desde el símbolo libc conocido como _start
. Siempre es posible tener un programa muy válido, que simplemente ejecute ensamblado, sin tener un main. Mira esto:
/* This must be compiled with the flag -nostdlib because otherwise the
* linker will complain about multiple definitions of the symbol _start
* (one here and one in glibc) and a missing reference to symbol main
* (that the libc expects to be linked against).
*/
void
_start ()
{
/* calling the write system call, with the arguments in this order:
* 1. the stdout file descriptor
* 2. the buffer we want to print (Here it's just a string literal).
* 3. the amount of bytes we want to write.
*/
asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13));
asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */
}
Compile lo anterior con gcc -nostdlib without_main.c
, y véalo imprimiendo Hello World!
en la pantalla simplemente emitiendo llamadas al sistema (interrupciones) en ensamblado en línea.
Para obtener más información sobre este problema en particular, consulte el blog de ksplice
Otro tema interesante, es que también puedes tener un programa que compile sin que el main
símbolo corresponda a una función C. Por ejemplo, puede tener lo siguiente como un programa en C muy válido, que solo hace que el compilador gime cuando sube el nivel de Advertencias.
/* These values are extracted from the decimal representation of the instructions
* of a hello world program written in asm, that gdb provides.
*/
const int main[] = {
-443987883, 440, 113408, -1922629632,
4149, 899584, 84869120, 15544,
266023168, 1818576901, 1461743468, 1684828783,
-1017312735
};
Los valores de la matriz son bytes que corresponden a las instrucciones necesarias para imprimir Hello World en la pantalla. Para obtener una descripción más detallada de cómo funciona este programa específico, eche un vistazo a esta publicación de blog , que es donde también la leí primero.
Quiero hacer un último aviso sobre estos programas. No sé si se registran como programas C válidos de acuerdo con la especificación del lenguaje C, pero compilarlos y ejecutarlos es ciertamente muy posible, incluso si violan la especificación en sí.
_start
parte de un estándar definido o es solo una implementación específica? Ciertamente, su "principal como matriz" es específico de la arquitectura. También es importante que no sea irrazonable que su truco "main as an array" falle en tiempo de ejecución debido a restricciones de seguridad (aunque eso sería más probable si no usara el const
calificador, y aún así muchos sistemas lo permitirían).
_start
no está en el estándar ELF, aunque el AMD64 psABI contiene una referencia a _start
al 3.4 proceso de inicialización . Oficialmente, ELF solo conoce la dirección e_entry
en el encabezado ELF, _start
es solo un nombre que eligió la implementación.
const
no importará ni un bit, el nombre del símbolo en ese archivo ejecutable binario es main
. Ni mas ni menos. const
es una construcción de C que no significa nada en el momento de la ejecución.
Alguien está tratando de actuar como un mago. Cree que puede engañarnos. Pero todos sabemos que la ejecución del programa c comienza con main()
.
El int begin()
será reemplazado decode(a,n,i,m,a,t,e)
por una pasada de la etapa de preprocesador. Luego, nuevamente, decode(a,n,i,m,a,t,e)
será reemplazado por m ## a ## i ## n. Como por asociación posicional de llamada macro, s
tendrá un valor de carácter a
. Asimismo, u
será reemplazado por 'i' y t
será reemplazado por 'n'. Y así es como m##s##u##t
se convertirámain
En cuanto al ##
símbolo en la expansión macro, es el operador de preprocesamiento y realiza el pegado de tokens. Cuando se expande una macro, los dos tokens a cada lado de cada operador '##' se combinan en un solo token, que luego reemplaza el '##' y los dos tokens originales en la expansión de macro.
Si no me cree, puede compilar su código con -E
flag. Detendrá el proceso de compilación después del preprocesamiento y podrá ver el resultado de pegar el token.
gcc -E FILENAME.c
decode(a,b,c,d,[...])
mezcla los primeros cuatro argumentos y los une para obtener un nuevo identificador, en el orden dacb
. (Los tres argumentos restantes se ignoran). Por ejemplo, decode(a,n,i,m,[...])
proporciona el identificador main
. Tenga en cuenta que así begin
se define la macro.
Por lo tanto, la begin
macro se define simplemente como main
.
En su ejemplo, la main()
función está realmente presente, porque begin
es una macro que el compilador reemplaza con una decode
macro que a su vez reemplaza por la expresión m ## s ## u ## t. Usando la expansión macro ##
, llegará a la palabra main
de decode
. Este es un rastro:
begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main
Es solo un truco main()
, pero usar el nombre main()
de la función de entrada del programa no es necesario en el lenguaje de programación C. Depende de sus sistemas operativos y del vinculador como una de sus herramientas.
En Windows, no siempre se utiliza main()
, pero en lugar WinMain
owWinMain
, aunque se puede utilizar main()
, incluso con cadena de herramientas de Microsoft . En Linux, se puede usar _start
.
Depende del vinculador como herramienta del sistema operativo establecer el punto de entrada, y no el idioma en sí. ¡Incluso puede establecer nuestro propio punto de entrada y puede crear una biblioteca que también sea ejecutable !
main()
función al lenguaje de programación C, lo cual no es correcto.