Perso je suis extêmement intéressé par cette comparaison mais je suis un gigantesque débutant si bien que je suis bien incapable d'intervenir pertinemment dans une discussion à ce propos...
PS: En revanche si ça intéresse personne d'autre et si tu as un peu de temps à perdre je veux bien que tu explicites un peu avec des exemples pas trop compliqués le genre de code que génère les Compilateurs, comparé à ce que ça donnerait codé directement en bas niveau. Enfin rapidement si ça te saoul pas trop de jouer les profs particuliers.... Ca serait super-méga-cool!
Tous les langages quel qu'ils soient finissent tous par générer des instructions machines qui agissent sur les circuits électroniques de l'UC pour produire les résultats cherchés.
Un langage de haut niveau génère pour chaque instruction du langage plusieurs instructions machines. Il peut même faire appel sans que tu le sache à des procédures ( sous programmes) de plusieurs milliers d'instructions.
L'assembleur, permet de générer une ou plusieurs instructions, mais c'est le programmeur qui décide.
Il peut ainsi choisir de programmer instruction par instruction, ou appeler des macro-instructions qui sont des paquets pré-préparés paramétrables, ils peut appeler des procédures ou utiliser des includes.
Par exemple, il permet d'effectuer des traitements qui sont sont soit impossibles, soit difficiles à faire avec des langages de haut niveau tel que agir sur une mémoire qui contient une adresse vers une autre mémoire qui contient une adresse,..etc.
Autre exemple, les nombres réels sont sur 4 ou 8 octets pour les langages de haut niveau. En assembleur on dispose de nombres réels sur 10 octets.
Par ailleurs les compilateurs de langages de haut niveau fabriquent des programmes exécutables qui n'utilisent qu'une petite partie des possibilités qu'offre le jeu d'instructions du micro-processeur.
J'ai fait souvent l'experience de demander au FORTRAN de me produire le code assembleur qu'il génére. Un programmeur en assembleur qui programmerait comme cela serait au minimum traité de maladroit.
Le desassembleur part du code binaire et reconstruit les instructions machines.
Les dé-assembleurs n'ont pas tous le même niveau de qualité.
Pour les meilleurs on obtient quelque chose de proche de ce qui aurait pu être programmé.
Pour d'autres, il faut ensuite faire un réel effort pour retrouver le programme tel qu'il aurait pu être programmé à l'origine.
__________________________
Si tu n'a pas trouvé la réponse sur MoteurProg...
mais que tu l'as trouvé ailleurs...
Merci de la donner sur MoteurProg.
Moby
Pas de problème, je vais prendre le temps (si tu le prends).
Je te propose des posts relativement "élémentaires" CAD : Un principe mécanique / son revers.
Ensuite tu testes ça sous RosAsm (puisque nous sommes dans la section idoine et que tu es un utilisateur de cet outil) pour vérifier si ce que j'affirme ici est fondé (c'est le seul moyen de renverser l'intox de manière individuelle et point par point).
Ainsi j'espère que nous ne grillerons pas les étapes et que nous éviterons les banalités habituelles.
Il serait bien que tu disposes d'un outil HLL (C++/C# ou ce que tu veux, de manière à avoir la main sur l'intention initiale et valider précisément le résultat). Tu t’apercevras vite que pour faire du code vaguement optimisé il faut coder en HLL d’une manière tellement proche de l’ASM que cela en devient ridicule. Les includes interminables sont responsables de la taille et de l’opacité du code généré (et dire que certains ASM y ont recours… on croit rêver !!!). Enfin, un compilateur ne peut comprendre que des blocs relativement petits de code (je parle au niveau de l’intention) Il est donc impossible de faire une optimisation stratégique variable et efficace (même si différentes tentatives existent, la seule solution véritablement efficace, mais nous le verrons limitée, c’est une dll bas niveau qui fait tout le boulot… donc de l’ASM).
Le principe des bibliothèques est lui aussi très limité. Il répond à une stratégie et une seule et dans le cas de traitements à haut niveau de répétitions s’écroule littéralement. Le dernier recours des HLL sont des enveloppes au-dessus des couches ASM du noyau (Direct blablabla). En fait, il est question là d’un déplacement du travail : C’est l’ASM qui bosse pour le HLL. Le noyau étant de plus en plus programmé comme un ensemble d’objets inter-reliés (GUID COM etc…) il devient intéressant d’utiliser un HLL pour piloter le noyau (plus opaque tu meurs et c’est le fabricant de l’OS qui rigole… en encaissant les dividendes.). Reste l’interface : Tu l’as remarqué, programmer une interface en ASM ou en C++ ne présente aucune différence de vitesse ou de complexité de mise en œuvre. Le but étant toujours de t’éloigner de l’OS. Je te rassure il existe des parades (et pas qu’avec Donald et Mickey).
La méthode (champs de control) est-elle conforme à tes attentes où désires-tu quelque chose de différent / amélioré / ciblé ...?
Par exemple comparer de soit disant ASM qui génèrent du code dans ton dos... :oops: histoire de clouer le bec à certains baratineurs...
@+rémi
PS : Moby, c'est bien le copier/coller mais je te rappelle que la prog objet n'est pas l'apanage de l'ASM et certaines impossibilités HLL que tu relèves n'existent que dans tes rêves... vérifies s'il te plaît ce que tu affirmes...
Apparemment, apparemment... tu connais bien ton sujet. Aurais-tu quelque(s) chose(s) à proposer dans la méthode, l'approche ?
As-tu des documents réalisés par des professionnels à ta disposition ?
[quote]
Les dé-assembleurs n'ont pas tous le même niveau de qualité. Pour les meilleurs on obtient quelque chose de proche de ce qui aurait pu être programmé. Pour d'autres, il faut ensuite faire un réel effort pour retrouver le programme tel qu'il aurait pu être programmé à l'origine.
[/quote]
Dis-moi... tu ne mélanges pas un peu tout... ?
Le dé-assembleur ne sert pas vraiment à retrouver le code initial (HLL ? ;))) ou proposer des solutions : Il montre juste ce que le compilateur a généré. Si ce que tu appelles qualité, c’est une transformation de la vérité… Je ne peux que te ré-exprimer mon profond et total désaccord sur ta notion de qualité.
Effectivement, les compilateurs génèrent un code d’une ‘t’elle qualité’ justement et il est souvent navrant de relire ces mégas de mélasse. Donc dire que la qualité du code lu provient du dé-assembleur serait (si c'est ton propos) une ânerie…
L’auteur de HLA la ramène bien… mais il est seulement esbroufé par le travail de Betov et ne comprend pas comment il à réussi le tour de force Dé-assembleur/Ré-assembleur.
C’est quelque chose d’unique en son genre tu sais…et je pense que tu t’engages sur un terrain que tu ne maîtrise pas vraiment. Tu te rappelles la question de l’ours ?
La baleine blanche (des assemblées)
__________________________
[url=http://]www.rosasm.org/[/url]
[url=http://]www.quanta-it.com/easbell/RosAsmForum[/url]
ERREUR BBCODE SUR CE MESSAGE : - La valeur de la balise "quote" n'est pas correcte !
Ok, moi je suis partant, maintenant j'avoue ne pas tout saisir de ce que tu veux dire par méthode par 'champs de control', enfin moi ce que je verrais bien mais c'est peut-être ce dont tu parles, ce serait par exemple de considérer une fonction élémentaire, genre les if/else bien que ce soit probablement peu adapté vue que pour ce niveau j'imagine que le C++ ne peut pas faire grand chose d'autre qu'un truc du genre 'cmp a,b | jnx else | ... | jmp endif | else: | ... | endif:' ce qui reste convenable pour une utilisation générale (même si en pratique ASM on peut souvent ruser genre virer le cmp par ex., ce qui au final n'apportera d'ailleurs qu'un gain sans doute bien peu perceptible! mais c'est tellement plus drôle...), et de comparer la façon de coder cette fonction en ASM, en HLL, et par le compilateur HLL en désassemblant (et pourquoi pas oui comparer au passage ce que tu considères comme 'pseudo assembleurs' !!!).
Enfin je dis ça, comme j'y connais pas grand chose, c'est peut-être pas pertinent pour un sou, je te laisse seul maître pour décider de ce qu'il te semble d'intérêt à étudier!
Autrement, et ce serait peut-être mieux de commencer par là, je ne saisi pas non plus parfaitement quand tu dis que les bibliothèques 'répond[ent] à une stratégie et une seule'. Laquelle?
Pour moi une bibliothèque regroupe un esnsemble de routines prés-faites que l'on inclu à son programme par une simple instruction. Il me parrait relativement naturel que les bibliothèques couramment utilisées par ex. en C++ ne soient pas du tout optimisées au sein d'un programme donné, leur but étant justement de pouvoir être utilisées en toutes situations. Je n'aime de toute façon pas trop ce type d'approche, c'est si agréable en ASM de pouvoir programmer à partir d'une page vierge, sans include et compagnie! (sauf qu'en Win32 c'est plus aussi chouette avec les API et tout mais bon l'adressage 32bit c'est quand même le confort absolu!)
Je me rappel que sous NASM j'avait codé une bibliothèque perso de quelques routines (16bits) pour 'débugger' mes programmes à la main, genre interruption du prog jusqu'à appui sur une touche, affichage d'une valeur en base n, affichage de texte ASCII, etc. c'était bien trippant!
Sinon pour ce qui est de l'interface des progs,j'ai récupérer DevC++ pour les "travaux pratiques" et c'est clair que l'architecture par défaut des exe Win32 est exactement pareil que ce qu'on peut voir sous RosAsm, c'est marrant! (normal en fait puisque ce sont des convention windows basées sur des API et donc au final des bibliothèques communes à tous les langages...). Mais c'est sûr que je me demande à quel point on peut, sous Win32, faire 'ce qu'on veut' comme par exemple écrire directement dans la mémoire vidéo sans passer par des API (un peu comme sous DOS quoi...), j'espère que c'est possible!!!! (même si c'est indépendant de l'ASM vue que j'imagine que si c'est possible en assembleur, ça l'est tout autant en C!)
Pour finir, deux pettes questions d'ordre plus théorique:
- les HLL sont conventionnels au niveau des instructions mais le sont-ils aussi au niveau de leurs traduction en code machine ou existe-t-il plusieurs façon de compiler un code C++ par exemple? Si oui n'y existe-t-il pas des méthodes plus 'optimisées' qui évitent les écueil observés lors des désassemblages?
- par rapport à ce que dit Moby ('Les dé-assembleurs n'ont pas tous le même niveau de qualité.') même si c'est sûr que désassembler un programme donné ne peut donner qu'un seul code au niveau instructions, il doit sans doute exister plusieurs façon de les présenter? (genre remplacer les adresses directs par des noms genre 'Var_xxx' ou 'Label_xxx' pour les sauts, voire tenter de différencier les adreesses de routines pour les identifier encore différemment, tenter des indentations et des sauts de lignes, etc.) Ce qui permettrait effectivement d'avoir au final 'quelque chose de proche de ce qui aurait pu être programmé', alors que 'pour d'autres [désassembleurs], il faut ensuite faire un réel effort pour retrouver le programme tel qu'il aurait pu être programmé à l'origine'. Non? (à condition évidemment que le remplacement des adresses par des chaînes de caractères soit transparent et que cela n'empèche pas de pouvoir accéder au code 'brut' si on le désire, sinon c'est plus du VRAI désassembleur me semble-t-il).
Oulà je m'enflamme et pose pleins de questions qui ne sont peut-être pas toutes en rapport direct avec le sujet de départ, mais je n'en attend que des réponses succintes, et je ne t'en voudrait pas de recentrer le discours sur l'essentiel: comparaison ASM / HLL désassemblé!
Tu peux commencer par faire une base semblable à la base3 de RosAsm, mais, en C++ et la dé-assembler avec RosAsm et la recompiler (Clic et clic quoi (arf)).
Première constatation : Y a du monde que tu n’as pas invité… vérification de version et ses potes… et toute une série de choses que tu n’as pas écrites. N’essaie pas de nettoyer dans un premier temps, c’est pas le plus grave... (bientôt MyCroque$oft pourra y mettre ce qu’il veut sans que personne ne puisse rien y faire, faudra payer un peu plus...).
Sur les API, les premières erreurs du compilateur que tu vas rencontrer ne sont pas dramatiques au niveau de l’optimisation en vitesse (c’est évident !) mais montre déjà certaines incohérences qui vont aller en dégénérant au fur et à mesure que tu t’éloignes de la gestion via API (ce qui représente une bonne partie du code voyez… à moins que vous ne codiez que des traitements de texte ou que la MFC soit pour vous la panacée de la souplesse et de la puissance.).
Les appels d’API peuvent se retrouver placés dans un registre, puis Call Registre (ce qui serait intéressant dans le cas d’appels en boucles (pour gagner rien sur rien !)) mais il commence aussi à y avoir des :
mov eax D$Handle
Call ‘Module.xxxxx’ eax
Au lieu de Call ‘Module.xxxxx’ D$Handle
Rien de bien dramatique à ce niveau… mais cela montre que même pour gérer une interface bateau via API, les compilateurs se perdent très vite… ça va pas aller en s’arrangeant :
Le plus amusant c’est de faire une base avec des imbrications (typiques des HLL).
La ça dégénère franchement et le compilateur commence à paniquer dans l’utilisation des registres. Au lieu d’utiliser eax de manière traditionnelle, il est incapable de ‘visualiser’ la gestion globale. De plus, regarde la gestion de pile (bien alambiquée): A vouloir absolument l’utiliser (pour gagner je ne sais pas quoi, vu que ce qui est appelé ensuite (API) représente un bon paquet d’instructions) il s’emmêle les pinceaux en imbriquant les différentes instructions jusqu'à obtenir un imbroglio incroyable, je charge je décharge, je push je pop, je décale les offsets (ça devient inintelligible : Au début je croyais qu’il y avait des trucs de furieux, genre optimisations que seuls trois générations de gurus peuvent t’expliquer… ben… non… c’est de la bouillie, là où une ou deux secondes de réflexion humaine traiteraient le problème de manière globale…
Utilises les points d’arrêt (même à la volée, F4 ou souris) F8 te permet de suivre ton code uniquement (saut des appels API) et F7 te permet de suivre ce que fait en interne l’APÏ en question. Tu verras que les tentatives d’optimisations à deux francs six sous ne représentent rien par rapport à la masse de travail fait par la dll appelée. Quand tu sais que tout ça est écrit en C++ tu imagines la mise en abîme ! (surtout qu’à l’intérieur plusieurs tests sont souvent refaits.. cas de la GDI32 par exemple.)
Donc, pour ‘optimiser’ ton C++, tu vas être obligé de dérouler le code, CAD mâcher le boulot du compilateur pour lui dire comment tu souhaites qu’il comprenne. Le source ainsi obtenu commence à devenir une décomposition qui va finalement te rappeler quelque chose (arf) et faire perdre tout l’intérêt d’utiliser un HLL.
Ces constations ne sont cependant pas dramatiques au niveau des performances (évidemment) à moins de faire un concours de clic sur 50 heures…
(Là où tu peux jeter un coup d’œil, c’est à la FPU par exemple… si tu maîtrises bien la gestion LIFO, c’est nettement plus comique et pas sur 50 heures !) Ne cherches pas à faire des benchmark, pour le moment, le mode User n’est absolument pas prioritaire sur le système et il est sans cesse interrompu par les divers services de l’OS.
Pour les optimisations sur les sauts conditionnels, tant que nous restons dans le domaine interface traditionnelle, tu constateras les mêmes optimisations scabreuses (D’ailleurs les Macros qui sont proposées dans Base3 ne sont données que pour une optimisation de LISIBILITE -> faciliter la maintenance de ton code dans les parties non critiques (c’est une autre forme d’optimisation importante face aux détracteurs de l’ASM, mais tu peux tout à fait t’amuser à perdre ton temps pour gagner une seconde année sur l’interface ;)) .
Une chose, cependant, relativement puissante en C++ (et dans certains cas) ce sont les Select (Switch) Case. Ils utilisent des tables de saut avec un ID message qui sert d’offset :
Une table de pointeurs vers les procédures de traitement appelées en fonction de l’Id MSG retourné par l’interface (la boucle du message pump de M$ utilise cette méthode, tout cela apparaît clairement dans la Base3 de RosAsm : Boucle Main pour la réception des MSG et MainWindowProc pour le traitement)
Les utiliser sous des formes un peu sioux permet d’optimiser d’une manière plus compréhensible pour le compilateur bon nombre d’opérations coûteuses. (mais là encore, il faut transformer ta manière d‘écrire et renoncer à une syntaxe compréhensible pour un codeur qui ne connaît pas tes méthodes…).
En ASM, tu l’as déjà remarqué, c’est toi qui décide des méthodes et qui contrôle bloc par bloc le choix de la meilleure stratégie.
Tiens (tenez) nous au courant de vos essais de bases. Nous poursuivrons avec des choses que j’espère plus comiques (enfin… comiques… ?)
Ayé, j'ai enfin trouvé le temps de faire quelques comparaisons entre la base de RosAsm et celle de Dev-C++, relativement équivalantes dans la forme: création d'une fenètre standard gérant le message WD_DESTROY (renvoyant un POSTQUIT entrainant la fermeture du prog en sortant de la boucle principal quand GetMessage renvoi 0, dans les deux cas).
On constate déjà que le code compilé est de 3584o pour RosAsm (PE sans les sources) et de 5632o pour celui de Dev-C++ (mais c'est vrai que ça ne veut pas dire grand chose).
Surtout on remarque c'est sûr après déssassemblage du code compilé par le C++ quelques façons de procéder étranges:
- à chaque appel d'API, le code est du type:
[quote]Label_APIxxx:
jmp 'User32.APIxxx'
Main:
call Label_APIxxx[/quote]
on a aussi un
[quote] mov eax 'msvcrt.__set_app_type'
call eax[/quote]
- la gestion de la pile pour le passage des paramètres est quant à elle plutôt obscure (même si ça ne remet pas en cause son efficacité, vu que j'ai du mal à cerner ce qui est fait):
[quote] mov eax D$ebp+014
push eax
mov eax D$ebp+10
push eax
mov eax D$ebp+12
push eax
mov eax D$ebp+8
push eax
call Label_APIxxx[/quote]
- autrement on a parfois des enchainements d'instructions dont le but m'échappe:
[quote] mov edx eax
mov eax edx[/quote]
[quote] sub esp 08
add esp 0-0C[/quote]
ou encore
[quote] mov eax eax
test eax eax[/quote] (de toute façon il me semble que le [-i]mov[/i] ne modifie pas les flag sur les x86 donc quel but de ce [-i]mov eax eax[/i]?)
- enfin on se rend compte que le code compilé renvoi à de nombreux API non invoqués dans le code originel en C++, notamment un bon nombre de 'msvcrt', dont je ne connais pas la signification.
Voilà mes premières observations, assez étranges il est vrai, même si j'ai dû mal à en comprendre le but et les implications (optimisation???).
ERREUR BBCODE SUR CE MESSAGE : - Une balise -i n'est pas fermée !