|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | ||
|
Membre habitué
![]() amateur Inscription : avril 2012 Messages : 145 ![]() |
Bonjour,
Sur la voie d'apprendre l'assembleur (moderne)... J'ai réalisé une petite routine juste pour afficher un chiffre. J'aimerais bien une critique (constructive et sympathique, mais critique) ; voir code ci-dessous. Sinon, cela m'a amené à quelques questions : 1. Paramètres octets: Comment passer un paramètre de taille inférieure au mot (en particulier un byte) sur la pile, et non dans un registre, et le retrouver facilement ? (problèmes d'endianness et de pile la tête en bas) Je n'arrive pas à penser ça. Quand j'essaie de le faire, ma routine affiche toujours 'n' pour une raison que je ne comprends pas. J'ai essayé de lire [esp], [esp+3], [esp-3] (logiquement mon byte devrait être à l'une de ces 3 adresses) et ça donne toujours 'n'. (Pour l'instant j'utilise un registre.) 2. Méta-infos: Y a-t-il une façon standard de transmettre en retour une méta-info, comme un signal d'erreur, par opposition à une véritable valeur de retour ? Pour ces dernières, on utilise généralement eax; y a-t-il un registre couramment dédié aux erreurs ou autres méta-infos ? (Pour l'instant je décide d'utiliser ebx, qui est souvent libre.) 3. Labels locaux: Y a-t-il une façon ordinaire de définir des labels locaux (aussi bien pour les données que pour les cibles de sauts) ? Je sais que certains assembleurs permettent de faire cela, mais y a-t-il une méthode simple et courante indépendamment de l'assembleur utilisé? Aussi, comment ces assembleurs réalisent-ils cela ? (Pour l'intant, je préfixe mes labels locaux avec le nom de la routine.) Merci, Denis PS : Désolé de vous transmettre ça en anglais, j'ai l'habitude d'échanger du code avec des gens de toute origine, et souvent anglophones. Code :
|
||
|
|
00
|
|
|
#2 |
|
Membre habitué
![]() Technicien maintenance Inscription : mars 2008 Messages : 111 ![]() |
je voie que tu uttilise l'intrruption 80h pour appeler tes fonctions système je suppose donc que tu uttilise linux si oui est ce que tu aurais une bonne doc sur les fonction disponible via l'int80h? ça m'aiderait a comprendre mieux (et de me mettre enfin a l'assembleur pour inux)
dans les anciens système ont uttilisait les registres du processeur pour les arguments des sous fonctions au lieux de la pile ce qui permettait de passer des argment de la taille d'un octet |
|
00
|
|
|
#3 | ||||
|
Membre habitué
![]() amateur Inscription : avril 2012 Messages : 145 ![]() |
Citation:
PS: Je l'ai retrouvée sur internet, ça vient de là: http://docs.cs.up.ac.za/programming/.../syscalls.html (comme ça j'ai pas à te la passer en privé) Une liste explicative des syscalls Linux, je sais même pas si ça existe! Encore moins une classée par fonctionnalité (I/O, gestion des processus, ...). Je suis tombé sur 2-3 listes vaguement "pédagogiques" dans des tutos, mais avec juste une douzaine d'appels chacune. Si tu trouves mieux, je suis preneur. Après, pour les détails, tu as toujours les man pages, section 2 pour les syscalls. Exemple pour sys_write, tape dans un terminal: Mais il faut déjà savoir au sujet de quoi chercher la doc ! Citation:
A plus long terme, j'aurais envie en fait d'écrire un petit préprocesseur qui traduit les appels et retours de fonctions en suivant une logique (calling convention) bien définie. Je lance mon préprocesseur et en sortie j'ai le code assembleur (Nasm) bien écrit, simple et sûr. Genre: Code :
Denis |
||||
|
|
00
|
|
|
#4 | |
|
Membre habitué
![]() Technicien maintenance Inscription : mars 2008 Messages : 111 ![]() |
Citation:
c'est curieux que ta fonction n'affiche que des "n" la seule erreur qui pourrait explique cela c'est que ecx ne pointe pas a l'endroit voulu. est ce que la valeur "digits" contient bien l'adresse mémoire de la chaine et qu'il n'y aurait pas un offset en plus lors de la compilation? |
|
|
00
|
|
|
#5 | |
|
Membre habitué
![]() amateur Inscription : avril 2012 Messages : 145 ![]() |
Citation:
Denis |
|
|
|
00
|
|
|
#6 | ||
|
Membre habitué
![]() Technicien maintenance Inscription : mars 2008 Messages : 111 ![]() |
j'ai réussi a modifier ton ptit programme pour passer les argument par la pile
Code :
) donc le décalage dans la pile varie en fonction de la façon dont on appelle la sous fonctionet pour pouvoir empiler seulement un octet je fait (pour empiler ah) |
||
|
00
|
|
|
#7 | ||
|
Membre habitué
![]() amateur Inscription : avril 2012 Messages : 145 ![]() |
Merci, Bifur !
Tu m'as permis de trouver le moyen de faire comme je le souhaitais, bien que ce soit pas exactement comme tu le fais toi, là. Mon problème était je crois l'histoire de l'adresse de retour inscrite à esp par call, comme tu le dis. [Comment on fait pour insérer du code inline avec BBCode ?] Bien, la valeur numérique du chiffre est maintenant passée sur la pile, dans l'octet de poids faible d'un mot. Je le lis là, à esp+4 et non esp, donc, en remplissant le reste du mot de zéros grâce à l'instruction movzx. Et voilà ! J'ai modifié 2, 3 autres petits trucs ici et là, ci-dessous la nouvelle version au cas où ça intéresse quiconque. Entretemps j'ai écrit la routine qui utilise celle-ci : écriture d'un nombre naturel en base quelconque de 2 à 36 ; mais c'est pas prêt, elle bugge: Code :
Exception en point flottant (core dumped) Merci encore, Denis PS: Je cherche maintenant une liste des instructions x86 (32/64), avec spécification exacte et explications claires, si possible. Je viens de tomber sur enter/leave que je connaissais pas. Code :
|
||
|
|
00
|
|
|
#8 |
|
Membre habitué
![]() Technicien maintenance Inscription : mars 2008 Messages : 111 ![]() |
pour une description complète des instruction assembleur je conseille le volume 2 du document "Intel Architecture Software Developer’s Manual" c'est en british mais je ne pense pas que ça t'effrais
pour ton probleme d'exception bizzard je flaire aussi un problème de traduction et si tu uttilise l'instruction div en 32 bits, est ce que tu fait bien gaffe a initialiser edx par zéro pour éviter l'exception division par zéro (déclenché en fait à chaque fois que les registres de sortie sont trop petit pour recevoir les résultats) ça pourrait être la source de ton exception bizzaroïde edit: après un test vite fait sur mon linux en provoquant volontairement une division par zéro j'ai le même message d'errreur donc ça signifife bien qu'il y ai une couille au niveau d'une division |
|
00
|
|
|
#9 | ||||
|
Membre confirmé
![]() Inscription : juin 2002 Messages : 113 ![]() |
Bonjour.
Je ne suis pas sûr que stocker un octet seul sur la pile soit une bonne chose lorsqu'on programme en 32 bits. En effet, dans un programme en 32 bits, il peut s'avérer nécessaire qu'une variable soit alignée sur une frontière de DWORD. Et comme les variables locales sont stockées sur la pile, il est alors nécessaire que le registre ESP soit lui-même aligné sur une frontière de DWORD, ce qui ne sera pas le cas si un octet a été empilé. Ce que je viens d'expliquer s'applique à la programmation sous Windows. A titre d'exemple, les chaines unicode comptées doivent êtres alignées sur une frontière de DWORD, de même que la structure TOKEN_PRIVILEGES. Je ne connais pas la programmation sous Linux, mais il se pourrait qu'une contrainte similaire existe pour l'alignement de certaines variables. Prudence donc ... La plupart des compilateurs Assembleurs prennent en charge le passage des paramètres aux procédures ainsi que les variables locales, sans que le programmeur n'ait à s'occuper de choses du style [EBP +12] ou [EBP -8]. Il suffit de déclarer la procédure et ses arguments et/ou ses variables locales. Exemple avec TASM : Code :
Le mot réservé ARG ( pour ARGument ) signifie que indice_1 et indice_2 sont deux paramètres passés à la procédure par l'intermédiaire de la pile. Attention à l'ordre de l'empilement ! Ici, il s'agit d'un programme tournant sous Windows, c'est donc l'ordre d'appel standard qui est utilisé, ce qui est précisé au début du programme par la directive STDCALL. Dans la pratique, le dernier paramètre placé sur la pile est le premier figurant dans la liste après ARG. Le mot réservé LOCAL signifie que compteur_1, compteur_2, indice_3 sont trois variables locales à la procédure. Si on ne précise pas la taille des variables locales, comme c'est la cas ici, ce sont des DWORD par défaut en programmation 32 bits. ( ce qui est signalé au début du programme par la directive .386 ) Le compilateur TASM se charge de toute la mécanique à mettre en oeuvre pour accéder par leur nom aux arguments et aux variables locales. Voici une partie du listing généré par TASM : Code :
Elle équivaut ici à : L'instruction LEAVED supprime les 3 variables locales. Elle équivaut ici à : Enfin l'instruction RET 08h dépile le registre EIP et ajoute 8 à ESP pour supprimer les deux arguments placés sur la pile. De plus, le compilateur se charge de convertir indice_1 en [EBP +12] ( ligne 9020 ) ou indice_3 en [EBP -4] ( ligne 9026 ). Ainsi le programmeur n'a pas besoin de s'occuper de ces détails, ce qui lui facilite le travail. Pour finir les explications, je signale que j'ai utilisé dans la procédure des labels locaux : @@1 et @@9 ( et d'autres que je n'ai pas recopiés ). Ils ne sont visibles qu'à l'intérieur de la procédure, ce qui permet de les utiliser ailleurs sans qu'il y ait confusion. Cela facilite grandement le nommage des labels, car dans un programme important il y en a des centaines ... Seule contrainte : leur noms doivent commencer par @@. L'utilisation des labels locaux est précisé au début du programme par la directive locals. Personnellement, j'utilise TASM ( et quelquefois MASM32 ) mais la plupart des compilateurs offrent les possibilités exposées plus haut. Néanmoins, la syntaxe peut être différente d'un compilateur à l'autre. Dans ce cas, voir dans leur documentation les directives à utiliser pour chacun d'eux. |
||||
|
|
10
|
|
|
#10 | |
|
Membre habitué
![]() amateur Inscription : avril 2012 Messages : 145 ![]() |
Citation:
Denis |
|
|
|
00
|
|
|
#11 | ||
|
Membre habitué
![]() amateur Inscription : avril 2012 Messages : 145 ![]() |
Citation:
En ce qui concerne le passage d'un octet, je voulais dire simplement le poser sur la pile et le retrouver là depuis la routine appelée. Mais il est passé à l'intérieur d'un mot, le registre eax en fait. Ensuite pour le retrouver, je fais "movzx eax, [ebp+8]" (ou une autre adresse). Citation:
Merci, prof ! Tu portes bien ton pseudo, c'est très clair. J'utilise Nasm, dont la syntaxe de base est très chouette (à part le fait qu'on doit répéter des tailles d'operation ou opérande) et d'après divers commentaires proche de la syntaxe Intel. (La syntaxe de Gas est insupportable, mais c'est une cible de compilo, plutôt qu'un langage de prog.) En Nasm, les labels locaux sont juste précédés de '.', genre ".loop_start". Bien, je n'ai encore exploré qu'une toute petite partie des "features" de Nasm en dehors de l'assebleur de base. Il y a très certainement l'équivalent de ce que tu montres là, mais pour l'instant je réapprends l'assembleur (après une très longue pause, et mon expérience est limitée aux processeurs 8 bits genre 6502 et Z80!). Donc, je m'en tiens au codage manuel pour quelque temps, et ça fait déjà pas mal à intégrer. C'est aussi pourquoi je ne fais pas appel aux routines C. Je me fais une petite boite à outils de débuggage comme première série de mini-projets, pour me motiver. Merci encore, Denis |
||
|
|
00
|
Copyright © 2000-2013 - www.developpez.com