Seuls les membres ayant 30 points peuvent parler sur le chat.

Forum Casio - Vos tutoriels et astuces


Index du Forum » Vos tutoriels et astuces » Quelques mythes brumeux du développement C
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Quelques mythes brumeux du développement C

Posté le 23/04/2015 10:57

Vous les avez sans doute déjà croisés si vous programmez vos chères Casio en C, sans toutefois être en mesure de comprendre leur rôle. Voici quelques explications.



Les #pragma du code d'exemple

Le code d'exemple se termine par ceci :

//****************************************************************************
//**************                                              ****************
//**************                 Notice!                      ****************
//**************                                              ****************
//**************  Please do not change the following source.  ****************
//**************                                              ****************
//****************************************************************************


#pragma section _BR_Size
unsigned long BR_Size;
#pragma section


#pragma section _TOP

//****************************************************************************
//  InitializeSystem
//
//  param   :   isAppli   : 1 = Application / 0 = eActivity
//              OptionNum : Option Number (only eActivity)
//
//  retval  :   1 = No error / 0 = Error
//
//****************************************************************************
int InitializeSystem(int isAppli, unsigned short OptionNum)
{
    return INIT_ADDIN_APPLICATION(isAppli, OptionNum);
}

#pragma section


On va débroussailler un peu et voir ce que ça donne.

#pragma section _BR_Size
unsigned long BR_Size;
#pragma section

#pragma section _TOP
int InitializeSystem(int isAppli, unsigned short OptionNum)
{
    return INIT_ADDIN_APPLICATION(isAppli, OptionNum);
}
#pragma section


Bon, c'est déjà plus clair. Pas pour vous ? Ça va venir.

Vous savez déjà que tout ce qui commence par un '#' est une instruction à destination du préprocesseur. Outre les #include, #define, #if et autres #warning, il y a les pragma (#pragma, vous l'aurez compris) qui permettent de donner des instructions au compilateur.

Dans notre cas, ces instructions sont liées aux sections. Dans le code objet, une section est un segment du fichier dans lequel on regroupe du binaire « de même nature ». Par exemple, il y a classiquement les sections .text pour le code, .data pour les données et la section .bss qui elle, est initialisée à zéro au début de l'exécution (on y fout des variables initialisées à zéro et plein d'autres trucs).

Néanmoins, on appelle les sections comme on veut, on les met où on veut, et on en fait ce qu'on veut, au risque toutefois de faire n'importe quoi. Là, le code crée une section appelée _BR_size qui contient juste une variable (l'instruction #pragma section ferme la section ouverte au-dessus). L'intérêt, c'est que quand l'éditeur de liens va assembler les sections pour faire le fichier g1a, on pourra connaître l'adresse de cette variable, et du coup on pourra la modifier (sinon on ne peut pas avoir son adresse).

La deuxième section s'apelle _TOP. Son rôle est clair : en fait, c'est le morceau de code qui est dans cette section qui sera appelé au lancement de l'add-in.

Quand j'ai dit qu'on pouvait arranger les sections comme on voulait, c'est vrai, à quelques exceptions près. Et en l'occurrence, il faut que le point d'entrée soit au bon endroit. Le point d'entrée, c'est là où commence l'exécution du programme, et ce n'est pas toujours main(). Il y a souvent des trucs à initialiser avant et là, c'est INIT_ADDIN_APPLICATION() qui s'en charge.

Vous savez que lorsqu'on lance un add-in, le fichier est chargé à l'adresse 0x300000, et, une fois les 0x200 octets de header passés, on se retrouve à 0x300200 et le code commence à s'exécuter. Oui mais voilà : encore faut-il être sûr que le code d'initialisation a bien été mis à cet endroit. Dans notre cas, la section _TOP est unique (c'est le but) donc il suffit de mettre les sections _TOP en premières et on est sûrs que le point d'entrée est bien positionné.

Bon, il se sont sentis obligés de créer une fonction qui appelle INIT_ADDIN_APPLICATION(), cela dit si la fonction INIT_ADDIN_APPLICATION() s'était tout de suite trouvée dans la section _TOP, ça aurait fonctionné aussi (enfin, ils devaient avoir une bonne raison de ne pas faire ainsi, je suppose).

Du coup, j'ai pas donné le rôle de la variable _BR_Size : en fait, je pense qu'elle sert à stocker, après calcul, la taille des sections .data et .bss réunies. Je ne sais pas à quoi ça sert, ni pourquoi c'est fait, mais je pense que c'est ça. À mon avis, ce n'est pas utilisé à l'exécution, mais à l'édition des liens.

Enfin, voyez plus bas pour ces explications.



The size of B and R section should be 0x2000 bytes or less

Une erreur assez classique :

The size of B and R section should be 0x2000 bytes or less.


Je n'ai pas de certitudes sur cette erreur mais des éléments déjà assez solides pour en parler.

À mon avis, la variable _BR_Size plus haut permet de calculer la taille des sections .data et .bss, et un morceau de code s'assure que cette taille ne dépasse pas 0x2000 (8192) octets.

Pourquoi ? Tout simplement, je pense, parce que les sections .bss et .data sont chargées en RAM et que la zone de RAM prévue pour ça ne fait que 8192 octets. Donc si vous voulez en mettre plus, il renvoie une erreur pour vous empêcher de faire des conneries.

À noter que sous Linux, la taille est calculée mais aucun test n'est fait dessus, donc on peut faire des conneries. Remerciez donc plutôt cette erreur qui vous empêche de massacrer votre machine.



Nonexisting memory by data read/write access at ...

Ça, c'est parce que le programme fait des conneries, encore une fois.

Vous savez qu'avec les pointeurs on peut représenter des adresses mémoire, mais combien de pointeurs différents y a-t-il ? Ouch. 4 milliards, soit 4 Gio de données.

C'est clair que la calculatrice n'a pas 4 Gio de mémoire. En fait, elle a 4 Mio. Mais alors, où sont passées les autres adresses ?

Il faut savoir que lorsque vous demandez les deux premiers octets à l'adresse 0x300200 (c'est pour l'exemple, c'est la première instruction exécutée au lancement du programme), un module appelé MMU, pour Memory Management Unit (Unité de Gestion de la Mémoire pour les anglophobes prononcés), va transformer votre adresse (0x300200) en une autre adresse qui elle pointera dans la mémoire physique.

En fait, ce système existe pour permettre d'avoir des espaces d'adressage différents selon les processus.

Pour faire simple, le MMU va diviser la mémoire logique (entre 0x00000000 et 0xffffffff, les 4 Gio) entre les processus et à chaque accès, va récupérer l'adresse physique correspondante. Ça permet d'éviter de la contention entre les processus et d'autres problèmes assez chiants. Je vais pas détailler ici (c'est un peu compliqué), si vous voulez plus d'infos voyez la section 3, « Memory Management Unit (MMU) » de la documentation hardware du groupe sh7705.

Et donc, si vous demandez au MMU d'accéder à une adresse logique aléatoire, il y a une très forte probabilité pour qu'il n'y ait pas de mémoire physique en face ! D'où la fameuse erreur « mémoire inexistante pour l'accès en lecture/écriture à l'adresse ... ».

Surveillez donc vos pointeurs si vous avez cette erreur ! Ah oui, et si elle se produit à la fin de l'exécution de l'add-in, quand vous revenez au menu, voyez le paragraphe suivant.



Nonexisting memory... à la fin de l'add-in

Si vous avez cette erreur à la fin de l'exécution, et que le programme n'utilise pas de GetKey() mais que des IsKeyDown() et affiliés, alors... c'est normal.

J'ai fait pas mal de tests sur ma propre calculatrice et j'ai pu constater le fait suivant :

Quelle que soit l'application, compilée avec gcc ou le SDK, un add-in lancé juste après un démarrage de la calculatrice qui ne réponde pas à certaines conditions provoquera un reset une fois l'exécution terminée, lorsqu'une autre application sera lancée depuis le menu.

Parmi les conditions qui permettent d'éviter ça, il y a « utiliser GetKey() », c'est pour ça que les applications du système ne plantent pas. Je pense que c'est lié au gestionnaire d'interruptions.

D'un autre côté, je vous avais prévenus qu'il fallait utiliser GetKey() (xD). Comme l'émulateur redémarre à chaque fois, le bug se produit tout le temps et produit une erreur d'accès mémoire. Sur la calculatrice, c'est sans doute la même, mais ça cause un reset processeur et la machine redémarre.



Cannot delete internal file

Cette erreur-là est purement liée au SDK : vous l'avez peut-être déjà rencontrée en tentant d'y compiler un programme.

En fait, le compilateur utilise tout un tas de fichiers avant de parvenir à votre cher exécutable g1a : citons, entre autres intéressants, FXADDINror.bin, qui doit, sous réserve que je ne dis pas trop de bêtises, contenir le code binaire de l'application, avant le passage au wrapper qui génère 0x200 octets d'en-tête. Au passage, FXADDINror.map décrit l'arrangement des sections dans le code.

Lorsqu'on recompile, le SDK supprime certains de ces fichiers pour en ré-assembler les composants et/ou les recréer. Le message d'erreur « Cannot delete internal file » signifie simplement qu'il n'a, pour une raison quelconque, pas pu le supprimer, et que par conséquent il ne peut pas terminer la compilation.



Warning : the following dependant file does not exist

Celle-là aussi est classique et uniquement liée au SDK. C'est une bizarrerie du logiciel, mais le meilleur moyen de gérer les fichiers d'en-tête est simplement de ne pas les ajouter dans la liste des fichiers du projet.

On ne sait pas trop comment le SDK les gère, mais si on les ajoute on obtient ce message d'erreur à ce sujet. Pour s'en débarrasser, il suffit de retirer le fichier concerné de la liste des fichiers du projet.



System ERROR !!

Celle-là est classique, mais on ne sait pas toujours d'où elle vient. En fait, c'est un cas de gestion des exceptions.

Lorsque vous faites un truc interdit par le kernel, celui-ci n'apprécie pas et il vous le fait savoir. Le cas classique c'est déréférencer le pointeur nul, ce qui est par convention (eh oui) illégal. Il y a plein d'autres cas comme la division par zéro mais ils ne sont pas spécifiquement intéressants ici.

Dans tous les cas, c'est votre programme qui fait n'importe quoi. Les infos ne sont pas anodines : l'adresse est celle indiquée dans le registre tea (voyez la doc pour plus d'infos) et la valeur PC est le contenu du registre pc (program counter) au moment où l'exception s'est produite, c'est-à-dire l'adresse de l'instruction qui a causé l'exception plus 4.



Exception Blocked Reset

Sans doute la meilleure. Tous ceux qui ont travaillé avec le moteur de gris en ont au moins déjà rencontré une. En gros, c'est une erreur qui apparaît aléatoirement quand on utilise le moteur de gris et qui provoque le reset de la machine. Pas mal hein ?

En fait, l'EBR n'est pas une exception hyper grave pire que 100 System ERRORS cumulées, en fait non, c'est juste une exception. Le message parle de lui-même : « Exception Blocked Reset ». En gros, elle signale que l'exception n'a pas pu être traitée parce que le processeur était déjà en train de traiter une exception.

Ce que Kucalc a fait c'est qu'il a remplacé le gestionnaire d'interruptions par le moteur de gris. Oh, wait... je dois parler chinois là. Quelques explications :

Lorsque vous faites un truc illégal avec un programme (genre diviser par 0), ça génère une exception. L'exception est traitée dans un mode d'exécution spécial et parfois le système affiche une popup avec marqué « System ERROR !! » et quelques infos dedans. Les exceptions sont gérées par une routine qu'on appelle exception handler. Dans notre cas, il y en a trois, selon le type d'exception :

→ exception générique : division par zéro, déréférencement d'un pointeur nul, etc.
→ exception tlb : en gros, quand vous demander n'importe quelle adresse au MMU.
→ interruption : pression de touches, connexion du port usb, etc.

Les trois gestionnaires sont liés parce qu'ils sont dans un espace de mémoire logique appelé espace vectoriel. L'adresse de base de cet espace est contenue dans un registre appelé vbr, qui signifie « vector base register ». Le calcul est simple :

→ l'exception handler est à l'adresse vbr + 0x100.
→ le tlb exception handler est à vbr + 0x400.
→ l'interrupt handler est à vbr + 0x600.

Ce que Kucalc a fait c'est qu'il a bougé vbr pour que vbr + 0x600 tombe sur sa fonction pour switcher les écrans. Il a aussi fait un autre truc horrible, c'est qu'il a forcé le processeur à ignorer toutes les interruptions, sauf celles de son timer. Donc quand vous appuyiez sur une touche, çe ne faisait strictement rien. Même le système ne le savait pas.

Le problème, c'est que à vbr + 0x600, on avait sa fonction, mais à vbr + 0x100 et vbr + 0x400 ? Eh bien, rien. Du code aléatoire. Des données. On ne sait pas. Donc, vous imaginez, si votre code produit une exception quelconque, on va aller à vbr + 0x100 pour demander à l'exception handler de la gérer, et on va trouver n'importe quoi. Du genre, des opcodes illégaux qui produisent eux-mêmes des exceptions.

Oui mais voilà, on est en mode d'exception donc on pas lancer la procédure de gestion. Donc, ben on arrête tout et ça fait un reset. Voilà, en gros, l'EBR, c'est ça. Une exception stupide dégénérée par un moteur stupide.

Personnellement, je n'en ai rencontré qu'une et ça m'a bien suffi. En général, il n'y a pas besoin de regarder votre code si vous en avez une... à moins que vous ne programmiez des applications ésotériques instables.



Voilà, c'est à peu près tout. Maintenant, vous n'avez plus de droit de dire que vous ne comprenez pas les erreurs qui vous tombent dessus !


Mgl64200Hors ligneMembrePoints: 649 Défis: 0 Message

Citer : Posté le 23/04/2015 12:02 | #


Moi j'y comprends rien
Toi t'es en train de lire ma signature là...
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Citer : Posté le 23/04/2015 12:04 | #


Mgl64200 a écrit :
Moi j'y comprends rien

Si tu es débutant en C (je cite), tu vas avoir du mal oui, c'est vrai

Au fait, si j'ai laissé passé des erreurs ou des trucs, dites-le, je les ajouterai dans la limite de mes humbles connaissances
DarkysunHors ligneMembrePoints: 1747 Défis: 52 Message

Citer : Posté le 23/04/2015 12:05 | #


Je pense qu un renommage aiderais mieux genre : "D ou viennent les TLB(...) errors ?
Sinon bravo cest tres recherche peut etre a passer en topic de qualite ?
Si je ne réponds pas à un post depuis trop longtemps : envoyez-moi un message pour me le rappeler !




Mgl64200Hors ligneMembrePoints: 649 Défis: 0 Message

Citer : Posté le 23/04/2015 12:06 | #


Merci d'avoir lu
, moi je réponds, merci d'être sur le site, et de montrer que tu es plus fort que les autres.d'écrire des articles
Toi t'es en train de lire ma signature là...
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Citer : Posté le 23/04/2015 12:07 | #


Darkysun a écrit :
Je pense qu un renommage aiderais mieux genre : "D ou viennent les TLB(...) errors ?

Ben, ça concerne bien plus que les tlb errors (émulateur, code, ebr, etc )

Darkysun a écrit :
Sinon bravo cest tres recherche peut etre a passer en topic de qualite ?

C'est pas à moi d'en décider ça

Mgl64200 a écrit :
Moi je réponds, merci d'être sur le site, et de montrer que tu es plus fort que les autres.d'écrire des articles

Partager ses connaissances est de loin le meilleur moyen de les rendre utiles
Drac0300Hors ligneMembrePoints: 839 Défis: 39 Message

Citer : Posté le 23/04/2015 12:12 | #


Lephenixnoir a écrit :
Personnellement, je n'en rencontrée qu'une

Moi je n'en rencontrée pas !
Dans Z/1Z, 42==666
Coïncidence ? Je ne pense pas.
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Citer : Posté le 23/04/2015 12:14 | #


Drac0300 a écrit :
Lephenixnoir a écrit :
Personnellement, je n'en rencontrée qu'une

Moi je n'en rencontrée pas !

Oh ça va hein !
IntelligideHors ligneMembre de CreativeCalcPoints: 46 Défis: 5 Message

Citer : Posté le 23/04/2015 12:16 | #


j'ai pas trop le temps de lire, je lirais ça pendant les vacances; ça promet d'être très intéressant
PositonHors ligneRédacteurPoints: 2396 Défis: 57 Message

Citer : Posté le 23/04/2015 16:03 | #


Dans le code objet, une section et un segment du fichier dans lequel on regroupe du binaire « de même nature ».

Premièrement, il y a une faute. Soit il y a un problème orthographique, soit ta phrase ne contient pas de verbe conjugué. Au choix.

Deuxièmement, ce n'est pas très clair.
Qu'entends-tu par "code objet" ?
Peux-tu définir ce terme ?
Le considères-tu comme un sous-ensemble de l'ensemble "code" ou de l'ensemble "objet" ? Exclusif ou non ?
En résumé, ne crois-tu pas qu'il serait temps de faire preuve d'un peu de rigueur au moins une fois dans ta vie ?

Bon d'accord j'arrête de faire mon chiant.
Bref, quand tu dis "fichier objet" tu parles du .o, c'est ça ?
<<< Si jusque là vous aviez cru que mon pseudo est "Position", il est encore temps de regarder à gauche

Ça m'énerve les gens qui ne finissent pas leurs
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Citer : Posté le 23/04/2015 17:04 | #


Alors, Positon, il faut savoir qu'en programmation les termes de « code objet » et « code source » relèvent du même ordre d'évidence que les termes « passé », « présent » et « futur » dans le cadre d'une situation temporelle. Donc je ne pense pas avoir spécialement besoin de les définir plus

Le fichier .o est un exemple de fichier censé contenir du code objet, ceci dit il y a de nombreux autres types tels que des fichiers .obj ou .robj.

J'ai corrigé la faute, merci.
Dark stormHors ligneMembre d'honneurPoints: 10936 Défis: 176 Message

Citer : Posté le 25/04/2015 09:50 | #


Très bon boulot ! Même si je savais une partie des ces infos, ça fait toujours du bien d'avoir un rappel approfondi
Je pense que ça sera utile pour les futurs afficionados du C !
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
NinestarsHors ligneMembrePoints: 2254 Défis: 22 Message

Citer : Posté le 27/04/2015 20:10 | #


Très instructif, merci
Elle est définie où cette fonction : INIT_ADDIN_APPLICATION()
AzHors ligneMembre de CreativeCalcPoints: 536 Défis: 10 Message

Citer : Posté le 28/04/2015 02:02 | #


Vraiment intéressant! Je me posais toujours des questions sur ce bout de code a la fin...
Y a-t il une explication pour "Can not delete internal file" , quand on doit faire "Rebuild all"?

"An Ye Harm None, Do What Ye Will"

Xavier59Hors ligneMembre de CreativeCalcPoints: 1337 Défis: 12 Message

Citer : Posté le 28/04/2015 10:39 | #


Az a écrit :
Vraiment intéressant! Je me posais toujours des questions sur ce bout de code a la fin...
Y a-t il une explication pour "Can not delete internal file" , quand on doit faire "Rebuild all"?


Ça m'est arrive mais jamais avec le SDK casio.
Souvent c'est parce que tu as un des fichiers ouvert ou qui bloque quand le compilo veux le supprimer
1337
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Citer : Posté le 15/05/2015 21:25 | #


Az a écrit :
Y a-t il une explication pour "Can not delete internal file" , quand on doit faire "Rebuild all"?

J'ai ajouté quelques informations sur ce message, purement lié au SDK lui-même

J'ai aussi ajouté quelques infos sur « Warning : the following dependant file does not exist ».
Breizh_craftHors ligneModérateurPoints: 1020 Défis: 7 Message

Citer : Posté le 15/05/2015 23:27 | #


Moi j'ai juste compris que Kucalc a fait n'importe quoi, et que le SDK fonctionne d'une façon étrange...

C'est exactement le genre d'article à montrer en disant "Tu vois, chez Planète Casio, on a des experts en la matière."


Un adminsys qui aime les galettes.
Ny
Statut : Invité

Citer : Posté le 10/04/2016 05:43 | #


Lephenixnoir a écrit :
si votre code produit une exception quelconque, on va allezr à vbr + 0x100


Mais à par ça, merci pour cet article
LephenixnoirHors ligneAdministrateurPoints: 17156 Défis: 142 Message

Citer : Posté le 10/04/2016 07:20 | #


Bien joué pour l'avoir trouvée ! C'est corrigé.

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2020 | Il y a 29 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd