#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?? ");
}
Cela appelle-t-il indirectement main
? Comment?
c
c-preprocessor
obfuscation
Rajeev Singh
la source
la source
Réponses:
Le langage C définit l'environnement d'exécution en deux catégories: autonome et hébergé . Dans les deux environnements d'exécution, une fonction est appelée par l'environnement pour le démarrage du programme.
Dans un environnement autonome, la fonction de démarrage du programme peut être définie par l'implémentation alors que dans un environnement hébergé , elle devrait l'être
main
. Aucun programme en C ne peut s'exécuter sans la fonction de démarrage du programme sur les environnements définis.Dans votre cas,
main
est masqué par les définitions du préprocesseur.begin()
s'étendra àdecode(a,n,i,m,a,t,e)
laquelle sera étendu davantagemain
.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)
est une macro paramétrée avec 7 paramètres. La liste de remplacement pour cette macro estm##s##u##t
.m, s, u
ett
sont 4 e , 1 er , 3 e et 2 e paramètre utilisé dans la liste de remplacement.s, t, u, m, p, e, d 1 2 3 4 5 6 7
Le repos n'est d'aucune utilité ( juste pour obscurcir ). L'argument passé à
decode
est " a , n , i , m , a, t, e" donc, les identificateursm, s, u
ett
sont remplacés par des argumentsm, a, i
etn
, respectivement.la source
_start()
. Ou encore plus bas niveau, je peux essayer d'aligner simplement le début de mon programme avec l'adresse à laquelle l'adresse IP est définie après le démarrage.main()
est la bibliothèque C Standard . C lui-même n'impose aucune restriction à ce sujet.decode(a,n,i,m,a,t,e)
devenirm##a##i##n
? Remplace-t-il les caractères? Pouvez-vous fournir un lien vers la documentation de ladecode
fonction? Merci.begin
est défini pour être remplacé pardecode(a,n,i,m,a,t,e)
ce qui est défini avant. Cette fonction prend les argumentss,t,u,m,p,e,d
et les concatène sous cette formem##s##u##t
(##
signifie concaténer). C'est-à-dire qu'il ignore les valeurs de p, e et d. Lorsque vous «appelez»decode
avec s = a, t = n, u = i, m = m, il remplace effectivementbegin
parmain
.Essayez d'utiliser
gcc -E source.c
, la sortie se termine par:int main() { printf("Ha HA see how it is?? "); }
Ainsi, une
main()
fonction est en fait générée par le préprocesseur.la source
Le programme en question ne appel en
main()
raison de l' expansion macro, mais votre hypothèse est erronée - il n'a pas dû appelermain()
du tout!À proprement parler, vous pouvez avoir un programme C et être capable de le compiler sans avoir de
main
symbole.main
est quelque chose auquel lec library
s'attend à sauter, après avoir terminé sa propre initialisation. Habituellement, vous sautez àmain
partir du symbole libc connu sous le nom de_start
. Il est toujours possible d'avoir un programme très valide, qui exécute simplement l'assembly, sans avoir de main. Regarde ça:/* 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 */ }
Compilez ce qui précède avec
gcc -nostdlib without_main.c
et voyez-le imprimerHello World!
à l'écran simplement en émettant des appels système (interruptions) dans l'assemblage en ligne.Pour plus d'informations sur ce problème particulier, consultez le blog ksplice
Un autre problème intéressant est que vous pouvez également avoir un programme qui compile sans que le
main
symbole corresponde à une fonction C. Par exemple, vous pouvez avoir ce qui suit comme programme C très valide, qui ne fait gémir le compilateur que lorsque vous augmentez le niveau d'avertissement./* 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 };
Les valeurs du tableau sont des octets qui correspondent aux instructions nécessaires pour imprimer Hello World à l'écran. Pour un compte rendu plus détaillé du fonctionnement de ce programme spécifique, jetez un œil à ce billet de blog , où je l'ai également lu en premier.
Je veux faire un dernier avis sur ces programmes. Je ne sais pas s'ils s'enregistrent en tant que programmes C valides selon la spécification du langage C, mais les compiler et les exécuter est certainement très possible, même s'ils enfreignent la spécification elle-même.
la source
_start
partie d'une norme définie ou est-ce uniquement spécifique à la mise en œuvre? Votre "main en tant que tableau" est certainement spécifique à l'architecture. Également important, il ne serait pas déraisonnable que votre astuce "main en tant que tableau" échoue au moment de l'exécution en raison de restrictions de sécurité (bien que cela soit plus probable si vous n'utilisiez pas leconst
qualificatif, et que de nombreux systèmes le permettraient)._start
n'est pas dans le standard ELF, bien que le psABI AMD64 contienne une référence_start
à 3.4 Process Initialization . Officiellement, ELF ne connaît que l'adresse àe_entry
dans l'en-tête ELF,_start
c'est juste un nom que l'implémentation a choisi.const
peu importe un peu - le nom du symbole dans ce fichier exécutable binaire estmain
. Ni plus ni moins.const
est une construction C qui ne veut rien dire au moment de l'exécution.Quelqu'un essaie d'agir comme un magicien. Il pense qu'il peut nous tromper. Mais nous le savons tous, l'exécution du programme c commence par
main()
.Le
int begin()
sera remplacédecode(a,n,i,m,a,t,e)
par un passage d'étape de préprocesseur. Là encore,decode(a,n,i,m,a,t,e)
sera remplacé par m ## a ## i ## n. Comme par association positionnelle d'appel de macro, las
volonté a une valeur de caractèrea
. De même,u
sera remplacé par «i» ett
sera remplacé par «n». Et c'est ainsim##s##u##t
que deviendramain
En ce qui concerne le
##
symbole dans l'expansion de macro, c'est l'opérateur de prétraitement et il effectue le collage de jetons. Lorsqu'une macro est développée, les deux jetons de chaque côté de chaque opérateur '##' sont combinés en un seul jeton, qui remplace alors '##' et les deux jetons d'origine dans l'expansion de la macro.Si vous ne me croyez pas, vous pouvez compiler votre code avec
-E
flag. Il arrêtera le processus de compilation après le prétraitement et vous pourrez voir le résultat du collage de jetons.la source
decode(a,b,c,d,[...])
mélange les quatre premiers arguments et les joint pour obtenir un nouvel identifiant, dans l'ordredacb
. (Les trois arguments restants sont ignorés.) Par exemple,decode(a,n,i,m,[...])
donne l'identifiantmain
. Notez que c'est ce que lebegin
macro est définie.Par conséquent, la
begin
macro est simplement définie commemain
.la source
Dans votre exemple, la
main()
fonction est réellement présente, car ilbegin
s'agit d'une macro que le compilateur remplace par unedecode
macro qui à son tour est remplacée par l'expression m ## s ## u ## t. En utilisant l'expansion macro##
, vous atteindrez le mot àmain
partir dedecode
. Ceci est une trace:C'est juste une astuce à avoir
main()
, mais l'utilisation du nommain()
de la fonction d'entrée du programme n'est pas nécessaire en langage de programmation C. Cela dépend de vos systèmes d'exploitation et de l'éditeur de liens comme l'un de ses outils.Sous Windows, vous n'utilisez pas toujours
main()
, mais plutôtWinMain
ouwWinMain
, bien que vous puissiez utilisermain()
, même avec la chaîne d'outils de Microsoft . Sous Linux, on peut utiliser_start
.C'est à l'éditeur de liens en tant qu'outil du système d'exploitation de définir le point d'entrée, et non la langue elle-même. Vous pouvez même définir notre propre point d'entrée, et vous pouvez créer une bibliothèque qui est également exécutable !
la source
main()
fonction au langage de programmation C, ce qui n'est pas correct.