lalahop
Categorie: Développement

Les expressions régulieres

Ceux qui me connaissent savent que je suis assez fan des expressions régulières depuis que je les ai connues.

Je me souviens encore de quelques unes des regex que j'ai écrit, il y a entre 3 et 5 ans et pour cause : elles trainent toujours dans un coin de mon disque dur. Toutes n'étaient pas optimisées, loin s'en faut. Mais, comme on l'entend fréquemment, l'expérience est le nom que nous donnons à nos erreurs 🙂 .

J'ai découverts les regex en PHP et à l'époque ça donnait ça :

*Retour dans le passé*

Pour récupérer la version d'Apache dans un phpinfo() :
#Apache/(.+) \(Fedora\)#

Pour récupérer la version de PHP dans un phpinfo() :
#PHP Version (.+)</h1>#

Pour contrôler un nom de fichier passé en GET afin d'éviter la remontée de répertoires :
'`\.`'

Pour récupérer plusieurs liens sur un site donné (j'ai oublié lequel :/) :
#<p><font face=\"Arial\"><a href="(.+)">(.+)</a>(.+) (.+)</font></p>#isU

La même mais pour des adresses email :
#<A HREF="mailto:(.+)">.+</A>#isU

Et on parle même pas de celles liées au BBCode :
#\[b:[a-z0-9]{8}\](.+)\[/b:[a-z0-9]{8}\]#isU
#\[i:[a-z0-9]{8}\](.+)\[/i:[a-z0-9]{8}\]#isU
#\[u:[a-z0-9]{8}\](.+)\[/u:[a-z0-9]{8}\]#
#\[color=(.+):[a-z0-9]{8}\](.+)\[/color:[a-z0-9]{8}\]#isU

#\[size=(.+):[a-z0-9]{8}\](.+)\[/size:[a-z0-9]{8}\]#isU
#\[quote:[a-z0-9]{8}\](.+)\[/quote:[a-z0-9]{8}\]#isU
#\[quote="(.+)":[a-z0-9]{8}\](.+)\[/quote:[a-z0-9]{8}\]#isU
#\[code:[a-z0-9]{8}\](.+)\[/code:[a-z0-9]{8}\]#isU
#\[list:[a-z0-9]{8}\]#isU
#\[list=[0-9]:[a-z0-9]{8}\]#isU
#\[/list:u:[a-z0-9]{8}\]#isU
#\[/list:o:[a-z0-9]{8}\]#isU

#\[\*:[a-z0-9]{8}\](.+)\[/\*:[a-z0-9]{1}:[a-z0-9]{8}\]#
#(<ul>|<ol>)<br />#
#</li><br />#
#\[img:[a-z0-9]{8}\](.+)\[/img:[a-z0-9]{8}\]#isU

#\[url:[a-z0-9]{8}\](.+)//(.+)\[/url:[a-z0-9]{8}\]#isU
#\[url=(.+)//(.+):[a-z0-9]{8}\](.+)\[/url:[a-z0-9]{8}\]#isU

*Retour dans le présent*

Récemment, j'ai eu à vérifier la validité d'un NIR pour un cas d'école et j'ai de suite pensé aux regex.

Voila les regex que j'ai écrit. Comme pour les autres regex présentées dans cet article, je n'en garantit pas la validité. J'ai testé tous les cas possibles mais une erreur peut toujours arriver (un code sans bugs est un code insuffisamment testé). On peut également les optimiser, ne serai-ce qu'en utilisant les classes de caractères. Ce coup-là, c'est Java qui sera utilisé :

Pour vérifier la validité de la clé :

^[0][1-9]|[1-8][0-9]|9[0-7]$

Pour le reste du NIR :

^[12][0-9][0-9](?:0[1-9]|1[012]|20)(?:(?:2[aA]|2[bB]|[0-8][1-9]|9[0-5])(?:[0-9][0-8][1-9]|[1-9][1-8][0-9]|990)|(?:(?:9[78][0-9](?:0[1-9]|[1-8][0-9]|90)))|99(?:[0-9][0-8][1-9]|[1-9][1-8][0-9]|990))(?:[0-9][0-9][1-9]|[1-9][1-9][0-9])$

L'avantage des regex, c'est qu'on trouve toujours quelqu'un qui en a un jour écrit une plus longue que la nôtre. Mais je pense que la reine des regex, c'est celle qui contrôle la validité d'une adresse email, et pour cause : la RFC 5322 (qui remplace la 2822 qui elle-même remplace la 822) qui définit le format des adresses email est à la fois diabolique et sadique. Quelques liens pour se faire une idée : Mail::RFC822::Address: regexp-based address validation et How to Find or Validate an Email Address.

Mise au point à propos des destructeurs

La programmation orientée objet introduit le concept de destructeur. Pour résumer, il s'agit d'une unique méthode de classe (dans le sens où une classe peut implémenter plusieurs constructeurs mais un seul destructeur) qui est exécutée lors de la destruction d'un objet de la classe afin de récupérer les ressources, mémoire notamment, empruntées lors de la création de l'objet.

Sur le net, on peut parfois lire qu'il ne sert à rien d'écrire soit-même le code du destructeur ou qu'il faut écrire le destructeur que lorsque l'on utilise des pointeurs. Parfois, c'est encore pire : on peut lire qu'un destructeur ne sert à rien. Faisons une petite mise au point.

La première chose à savoir est que l'on n'a pas besoin de destructeur dans les langages de programmation (Java, PHP, etc. ) qui implémentent un système de ramasse-miettes (en anglais, juste pour épater les filles : garbage collector) car la libération de la mémoire est alors automatique. Certains vont dire : dans ce cas, à quoi sert la méthode java.lang.object.finalize() en Java ?

La réponse est simple : d'une part, la méthode finalize() ne détruit pas l'objet : elle permet au programmeur de réaliser une action avant la destruction à proprement parlé de l'objet par le ramasse-miettes. D'autre part, elle n'est pas à utiliser, même pour fermer un descripteur de fichier car Java ne peut ni garantir que cette méthode sera bien exécutée ni quand elle le sera. De plus cela ruine la portabilité de l'application, ce qui est un peu dommage vu que c'est un des principaux objectifs de Java. Ce n'est pas moi qui le dit mais Joshua Bloch dans son livre "Effective Java: Programming Language Guide". Vu les antécédents de cette personne dans le domaine Java, on peut, peut-être, lui faire confiance.

Prenons un exemple rapide : réaliser un close() sur un objet de la classe FileOuputStream à l'intérieur d'une méthode finalize() redéfinie est une mauvaise méthode en plus d'être inutile. Le close() doit être fait dans le bloc finally (vous savez : try ... catch ... finally) qui suit le try dans lequel vous avez ouvert le fichier. Plus précisément, elle doit être faite dans un bloc try ... catch à l'intérieur d'un bloc finally car cette méthode peut soulever une exception de type IOException.

Pour lever tout ambiguïté concernant mes propos :
En PHP, la méthode magique __destruct permet de personnaliser le processus de destruction d'un objet (exemple : supprimer une ligne dans une table de la base de donnée lors de la destruction d'une instance d'une classe donnée). Néanmoins, PHP implémente un mécanisme de libération automatique de la mémoire. Avant PHP 5.3, il était basé sur un compteur de référence : dès qu'une variable n'est plus référencée, elle est supprimée. Depuis PHP 5.3, le mécanisme a été amélioré et on obtient un garbage collector qui, en plus de faire la même chose que le mécanisme du compteur de référence, permet d'éviter les problèmes liés aux références circulaires. C'est pour cela que je pense que, comme en Java, il n'est pas nécessaire de redéfinir un destructeur sauf à vouloir personnaliser la destruction d'un objet comme évoqué précédemment.

Ensuite, pour les langages orientés objet sans ramasse-miettes (C++ par exemple) :

- Pour tout ce qui est allocation statique (int, double, array, objet) à l'intérieur d'une classe et qui sera donc stocké sur la pile, vous n'avez pas besoin d'un destructeur. Même avec un pointeur (un tableau est un pointeur), tout sera supprimé automatiquement par le destructeur par défaut. C'est pour ça qu'il est faux de dire qu'il faut écrire soit-même le code du destructeur dès que l'on utilise un pointeur. Le must c'est que même les appels récursifs sont pris en compte. Par exemple : Soit une classe A qui créer un objet de classe B qui elle même créer un tableau. Le tableau sera bel et bien supprimé.

- Pour tout ce qui est allocation dynamique (utilisation de l'opérateur new ou malloc) à l'intérieur d'une classe et qui sera donc stocké sur le tas, il est impératif d'écrire soit-même le code du destructeur. C'est pour ça qu'il est faux de dire qu'un destructeur ne sert à rien ou qu'il ne faut jamais le re-implémenter.

Pour résumer :

  • Langage objet avec ramasse-miettes : pas de destructeur à implémenter
  • Langage objet sans ramasse-miettes :
    • utilisation de l'allocation statique à l'intérieur d'une classe : pas besoin de redéfinir le destructeur par défaut.
    • utilisation de l'allocation dynamique à l'intérieur d'une classe : il faut redéfinir le destructeur par défaut (mais aussi le constructeur de recopie et l'opérateur d'affectation pour bien faire ;)).

ÉDIT du 28/10/2011 à 14h : Et si vous avez un doute sur la libération de la mémoire, vous pouvez utiliser Valgrind. Je l'ai utilisé uniquement sur du C et du C++ mais, d’après le grand Web, il fonctionne aussi avec du Python. Fin de l'édit.

Une application miroir en C

Table des matières

Un miroir ? Euh on parle de quoi là, d'un dépôt de fichiers ? Non, pas du tout ! Je vais vous présenter un programme dont la source est identique à la sortie console. Uh ?

Le problème

Si nous avons une source C comme suit :

#include <stdio.h>
#include <stdlib.h>
int main()
{
	[ .... ] /* Tout plein de code ici */

	return 0;
}

On veut que la console affiche exactement la même chose, quelque soit le code entre [ .... ] :

#include <stdio.h>
#include <stdlib.h>

int main()
{
	[ .... ] /* Tout plein de code ici */
	return 0;

}

Cela paraît chose aisée, mais ça demande pas mal de réflexion (j'ai mis beaucoup de temps à trouver la solution !). Si vous voulez chercher par vous même, ne lisez pas la suite.

La réflexion

La base de la solution : la fonction printf(). Ca, tout le monde s'en serait douté. Mais la solution aurait été beaucoup plus compliqué avec un std::cout de c++ (voire impossible, je n'y ai pas vraiment réfléchi). Il va falloir tirer parti du couple de paramètres format / chaine de paramètres variables (les ... quand on regarde la syntaxe d'appel de la fonction !). Si vous voulez en savoir plus à ce sujet, je vous conseille de regarder ceci : http://msdn.microsoft.com/en-us/library/kb57fad8(VS.71).aspx. Ce n'est PAS une librairie exclusive MS ou quelque chose du genre, comme vous le verrez dans l'exemple, MS donne également un exemple sous UNIX.

Voici donc l'idée de départ : utiliser une variable à la fois comme format et comme paramètre de printf :

#include <stdio.h>
#include <stdlib.h>

int main()
{
	const char* var = "#include <stdio.h>

			#include <stdlib.h>
			int main()
			{
				const char* var = "%s";
				printf(var, var);
				return 0;
			}";
	printf(var, var);

	return 0;
}

Bien sûr ca ne fonctionne pas, on ne peut pas définir une chaîne sur plusieurs lignes 😉 Et puis les tabulations / nouvelles lignes ne seraient pas interprétées. Deuxième étape donc : mettre la variable sur une ligne

#include <stdio.h>
#include <stdlib.h>

int main()
{
	const char* var = "#include <stdio.h>\n#include <stdlib.h>\nint main()\n{\n\tconst char* var = \"%s\";\n\tprintf(var, var);\n\tsystem(\"pause\");\n\treturn 0;\n}";

	printf(var, var);
	system("pause");
	return 0;

}

Ce code fonctionne. Voici la sortie que vous devriez obtenir :

#include <stdio.h>
#include <stdlib.h>
int main()
{
	const char* var = "#include <stdio.h>

	#include <stdlib.h>
	int main()
	{
		const char* var = "%s";
		printf(var, var);
		system("pause");
		return 0;
	}";
	printf(var, var);
	system("pause");
	return 0;
}Appuyez sur une touche pour continuer...

Le system("pause"); c'est pas nécessaire, un fail de ma part.
Sur le principe, "l'énigme" est résolue, cependant la mise en forme n'est pas la même que dans notre code ! Il se pose donc une autre problématique. Comment faire en sorte que le const char* var affiché en console montre les \n les \t etc ?

La solution

Il va nous falloir créer une sorte de addslashes en c (pour les amateurs de php x) )
Voici donc le code final. Je vous passe les étapes pour en arriver là. Si vous ne comprenez pas tout demandez. Le code est un peu "condensé"/"compacté" pour la mise en ligne dans la variable

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
char* strrep(const char *str) {
	char ret[4096]; int oi=0, ri=0;

	for (;;)
	{
		if(str[oi] == 10) { ret[ri]=92; ret[++ri]='n'; }

		else if (str[oi] == 9) { ret[ri]=92; ret[++ri]='t'; }

			ri++; oi++;
	else if (str[oi] == 34) { ret[ri]=92; ret[++ri]=34; }

		else ret[ri] = str[oi];
		if(oi>=strlen(str)) break;

	}
	ret[ri]=0; return ret;
}
int main()

{
	const char* var = "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\nchar* strrep(const char *str) {\n\tchar ret[4096]; int oi=0, ri=0;\n\tfor (;;)\n\t{\n\t\tif(str[oi] == 10) { ret[ri]=92; ret[++ri]='n'; }\n\t\telse if (str[oi] == 9) { ret[ri]=92; ret[++ri]='t'; }\n\t\telse if (str[oi] == 34) { ret[ri]=92; ret[++ri]=34; }\n\t\telse ret[ri] = str[oi];\n\t\tri++; oi++;\n\t\tif(oi>=strlen(str)) break;\n\t}\n\tret[ri]=0; return ret;\n}\nint main()\n{\n\tconst char* var = \"%s\";\n\tprintf(var, strrep(var));\n\treturn 0;\n}";

	printf(var, strrep(var));
	return 0;
}

et voici la sortie console :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* strrep(const char *str) {
	char ret[4096]; int oi=0, ri=0;
	for (;;)

	{
		if(str[oi] == 10) { ret[ri]=92; ret[++ri]='n'; }
		else if (str[oi] == 9) { ret[ri]=92; ret[++ri]='t'; }
			ri++; oi++;
	else if (str[oi] == 34) { ret[ri]=92; ret[++ri]=34; }
		else ret[ri] = str[oi];
		if(oi>=strlen(str)) break;
	}
	ret[ri]=0; return ret;
}
int main()

{
	const char* var = "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\nchar* strrep(const char *str) {\n\tchar ret[4096]; int oi=0, ri=0;\n\tfor (;;)\n\t{\n\t\tif(str[oi] == 10) { ret[ri]=92; ret[++ri]='n'; }\n\t\telse if (str[oi] == 9) { ret[ri]=92; ret[++ri]='t'; }\n\t\telse if (str[oi] == 34) { ret[ri]=92; ret[++ri]=34; }\n\t\telse ret[ri] = str[oi];\n\t\tri++; oi++;\n\t\tif(oi>=strlen(str)) break;\n\t}\n\tret[ri]=0; return ret;\n}\nint main()\n{\n\tconst char* var = \"%s\";\n\tprintf(var, strrep(var));\n\treturn 0;\n}";
	printf(var, strrep(var));
	return 0;
}

Parfaitement identiques ! Désormais vous pouvez ajouter autant de code que vous le désirez, tout ce qu'il reste à faire est de l'ajouter également dans la variable var (et de le formatter bien entendu).

Rapide explication du nom des variables et de ce que fait le code ajouté : la fonction strrep (quand même ^^) :
oi = original index (l'index de la lettre courante dans la variable str passée en paramètre)
ri = return index (l'index de la lettre courante dans la variable retournée)
code ascii 92 = l'antislash
code ascii 10 = retour à la ligne (sous windows ! il se peut qu'il s'agisse de 13 sous linux et des deux sous mac, si mes souvenirs sont bons)
code ascii 9 = antislash t = tabulation
code ascii 34 = guillemets doubles

La boucle passe chacun des caractères du buffer d'entrée. S'il s'agit d'un caractère spécial (que nous avons écrit \t, \n ou \"), il est décomposé en 2 caractères ascii pour être affiché et non interprété.
Le ret[ri]=0 de la fin ajoute le caractère de fin de chaine pour le printf

Voilà, faites passer l'énigme, ne divulguez pas (directement) la solution, et réfléchissez un peu quand même =)

ÉDIT du 15/07/2012 à 21h30 par GuiGui : Si vous voulez en savoir plus et/ou voir d'autres exemples, je vous conseille ce billet : Programmes auto-reproducteurs (quines) chez Rom1v

True Warrior Tools

Rah, mon premier article. Il fallait un titre frappant 🙂

Que sont donc les True Warrior Tools ? En français, les outils du véritable guerrier. Pour être plus clair, je vais détailler ici les outils nécessaires à tout "véritable" développeur. Par véritable, je sous entends programmation (très) bas niveau (c natif, asm natif), et par "natif", j'entends : sans utiliser de librairies existantes, et sans utiliser d'API système, même s'il se peut que l'on ait recours aux fonctionnalités offertes par le bios

Vous l'aurez compris, nous allons faire un tour des outils existants pour parler directement à votre processeur sans passer par un OS. Plus précisément, les outils disponibles sous Windows. Je ne parlerai pas (ou peu) des outils utilisés sous Linux, puisqu'ils sont sûrement déjà installés et prêt à fonctionner.

Tout d'abord, il vous faudra un bon éditeur de texte (ça revient à chaque fois sur le tapis, mais on a toujours pas trouvé mieux). Personnellement, j'aime beaucoup la coloration syntaxique de Notepad++ (Npp) pour l'asm et le batch (oui oui nous allons faire du batch, aussi étrange que cela puisse paraître), mais pas pour le C. Si vous souhaitez le télécharger, http://sourceforge.net/projects/notepad-plus/files/

Ensuite, je vous conseille d'avoir une invite de commande intégrée dans votre explorateur, de sorte qu'un simple clic droit dans votre arborescence des dossiers ouvre une invite de commande déjà positionnée sur le dossier (ca vous évitera des cd dans tous les sens etc). Il va falloir tremper les mains dans le cambouis. Tout d'abord, Win+R / regedit. Rendez vous dans HKEY_CLASSES_ROOT (vache la liste !). Amateurs de sensations fortes, jetez un oeil au passage à la clef CLSID. Rendez vous dans la clef "Folder/Shell". Créez une clef nommée comme vous voulez (le nom ne sert pas). Puis modifiez la valeur de la REG_SZ par défaut. J'ai simplement mis "Invite de commande", mais mettez ce que vous voulez, c'est ce qui apparaîtra dans le menu contextuel. Puis créez une nouvelle clef "command" (pas d'erreur de frappe ici, sinon c'est le fail !) . Modifiez la REG_SZ par défaut de la nouvelle clef et donnez lui cette valeur :

C:\Windows\System32\cmd.exe /k cd "%1%"

. Voilà, pas besoin de reboot ni rien, c'est appliqué.

On est loin d'avoir fini la liste 😉 Alors on continue avec une machine virtuelle / un émulateur. J'utilise Vmware Workstation 7 personnellement. Mais quitte à se lancer, autant partir sur du gratuit avec Virtual Box disponible ici : http://www.virtualbox.org/wiki/Downloads . On entend beaucoup parler de Bochs. Un peu moche à mon gout, mais ca a l'air efficace, et surtout léger à côté de Vmware =p Disponible ici : http://sourceforge.net/projects/bochs/files/. Si vous recompilez Bochs sous linux vous pouvez spécifier d'ajouter l'interface graphique du debugger (assez poussée). Il y a aussi QEMU bien sûr, téléchargeable pour Windows ici : http://lassauge.free.fr/qemu/

Attention, si vous avez choisi Vmware, je vous déconseille d'activer le full debugger. A la première interruption non gérée, j'ai eu le droit à un joli freeze de ma machine (réelle, pas virtuelle !), avec le debugger qui tournait en tâche de fond jusqu'à kill du process.

Outil suivant : le compilateur asm. Je ne vais pas vous laisser le choix pour celui-ci, nasm à tout prix ! masm et tasm vous généreront des syntaxes un peu particulières, et le gas utilise la syntaxe AT&T (moins vous la fréquenterez, mieux vous vous porterez =) ) . nasm est disponible ici : http://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D . Je vous conseille la dernière version non RC (attention elles ne sont pas toujours triées par numéro de version). Puis dans le sous répertoire win32 vous avez un joli installeur ou un joli zip. Une fois installé / décompressé, il va vous falloir ajouter le dossier bin de nasm au PATH. Pour cela allez dans les propriétés systèmes / paramètres avancés / variables d'environnement / système : PATH / Modifier. Ajoutez un ; et collez le chemin du dossier bin (inclus !)

Héhé, je gardais le meilleur pour la fin : le compilateur C. Je vous conseille le portage de GCC sous win32, MinGW. Disponible ici : http://sourceforge.net/projects/mingw/files/ . Personnellement, j'ai pris l'Automated MinGW Installer / MinGW x.xx / MinGWxxx.exe . Qui marche très bien. Une fois installé (ou autre), ajoutez le dossier bin de MinGW au PATH.

C'est bien joli tout ça, vous pouvez désormais compiler n'importe quel .c en ouvrant une cmd dans le dossier du .c en tapant "gcc fichier.c". Aussi simple que ça. Ah non j'oubliais, MinGW étant le portage Windows de GCC, vous aurez un joli .exe bourré d'entêtes PE spécifiques à Windows : autrement dit vous ne pourrez rien en faire en dehors de Windows. Pour remédier à cela, nous allons devoir nous détacher de l'OS en laissant tomber ses entêtes et ses librairies (nous allons compiler un fichier binaire plat, ou "flat binary executable" =o).

Pour cela, 3 étapes sont nécessaires (sous Windows du moins !) :

- Changer ses habitudes

- Demander à gcc de compiler et d'assembler, mais de ne PAS linker

- Demander à ld de linker proprement

- Demander à objcopy de ne récupérer que l'essentiel

Tout d'abord, changer ses habitudes, en quoi cela consiste-t-il ? Désormais le point d'entrée de vos applications NE s'appellera PAS main. Pourquoi ? Parce que c'est un cas particulier, et lors du linkage il est détecté et traité spécialement. C'est agaçant :p Tout ce qu'il vous faudra faire c'est choisir un autre nom, du style "monMain" (vache c'est moche !) ou "entry_point" (ca pète déjà un peu plus 🙂 )

Deuxièmement, demander à gcc de faire du bon boulot :

gcc -c -ffreestanding -nostdinc -nostdlib -mno-stack-arg-probe -o #OUPUT#.o #INPUT#.c

#INPUT# et #OUTPUT# étant les noms de vos fichiers d'entrée et de sortie. Les options : -c demande de s'arrêter après l'assemblage, -ffreestanding demande de se passer du main et des librairies standards du c, -nostdinc -nostdlib demandent de ne pas inclure automatiquement les librairies standards, -mno-stack-arg-probe demande de ne pas utiliser l'instruction _alloca pour allouer la stack, et de simplement décrémenter le pointeur de pile à la place, et -o c'est juste pour choisir le nom =) GCC va vous générer un .o si vous compilez quelque chose avec cette commande

Troisième étape, le linker :

ld -i -e _#ENTRY POINT# -Ttext 0x0 -o #OUTPUT#.o #INPUT#.o

#INPUT# de ld = #OUTPUT# de gcc, attention #ENTRY POINT# correspond au nom de la fonction qui se substitue à l'habituel main (surtout ne pas oublier l'underscore devant le nom, sinon vous aurez une jolie erreur !). Les options : -o pour la sortie, comme avec gcc, -i pour pouvoir utiliser objcopy par la suite, -e pour spécifier le point d'entrée, -Ttext pour choisir l'adresse à laquelle doit être chargé le segement de code (0x0 pour être chargé au tout début =) ).

Enfin :

objcopy -R .note -R .comment -S -O binary #INPUT#.o #OUTPUT#

#INPUT# de objcopy = #OUTPUT# de ld. Les options : -R = enlève le segment spécifié, -O binary formatte la sortie en flat binary executable, et -S enlève les symboles et les informations écrites par -i de ld

Au final, vous avez un joli fichier sans extension. Pour le disséquer, rien de tel qu'un désassembleur ! J'imagine déjà "ah mince encore un soft à télécharger..." =) Point du tout, nasm apporte son propre désassembleur, qui se nomme ndisasm. Il suffit donc de faire "ndisasm #INPUT#" ou #INPUT# de ndisasm = #OUTPUT# de objcopy.

C'est bien joli tout ca mais je vais pas me retaper tout ça à chaque fois que je recompile ! C'est à ça que sert le batch. Créez un fichier .bat nommé comme vous le souhaitez, soit dans le dossier bin de nasm, soit dans le dossier bin de mingw (il faut qu'il soit facilement référencable par le PATH). Le contenu du fichier :

@echo off
gcc -c -ffreestanding -nostdinc -nostdlib -mno-stack-arg-probe -o %1.o %1.c
ld -i -e _%2 -Ttext 0x0 -o tmp.o %1.o
objcopy -R .note -R .comment -S -O binary tmp.o %1

ndisasm %1
del tmp.o
del %1.o

Voilà. Les del servent simplement à nettoyer un peu le dossier. La syntaxe d'appel de votre batch : "#BATCH# #FICHIER# #ENTRY POINT#", #BATCH# = nom du batch, #ENTRY POINT# = nom du main SANS l'underscore devant (ajoutée automatiquement dans le script), #FICHIER# = le nom du fichier à compiler SANS l'extension .c

Voilà, ce premier article est fini =p Si vous avez des outils à me suggérer, des critiques, des remarques, n'hésitez pas =)

Il y aura sûrement (beaucoup) d'autres articles dans cette catégorie =p