Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[Amd64/C] Récupération de données sur la pile

11 réponses
Avatar
JKB
Bonjour à tous,

J'écris actuellement un serveur pour L4 et je dois donc récupérer
des données dans la pile (argc et argv) et j'avoue ne pas comprendre
ce qui se passe. Le système utilise les frame pointers et le
processeur est un core2duo.

Du côté du programme lançant la tâche, j'ai alloué une pile et tout
ce qu'il faut pour que la tâche puisse être lancée.

user_stack est un pointeur sur une pile valide. L'allocateur de la
roottask me renvoie l'adresse 6000. Le tableau init_vars contient
les paramètres à transmettre. vms$pointer est un entier non signé
à 64 bits.

*(--user_stack) = 1; // argc
*(--user_stack) = (vms$pointer) init_vars; // arguments

Pour lancer une tâche, je dois appeler un appel système donnant
le pointeur programme et le pointeur de pile initial de la tâche. Je
sais faire et il n'y a pas de problème.

Sur la console série, je peux lire :

%SYS-F-PAGEFLT, pagefault request from $3C at $0000000000800110
%SYS-F-PAGEFLT, pagefault request from $3C at $0000000000005FE8
%SYS-F-PAGEFLT, pagefault request from $3C at $00000000008017E0
%SYS-F-PAGEFLT, pagefault request from $3C at $0000000000801E50
%RUN-S-STARTED, PAGER.SYS process started

Le premier défaut de page correspond au point d'entrée (main()) du
serveur, ce qui est normal. Le deuxième défaut à la première adresse
de la pile dans laquelle écrit ce serveur, ce qui est tout aussi
normal.

Les deux autres défauts correspondent à un appel système permettant
d'écrire le message "%RUN-S-STARTED, PAGER.SYS process started". La
tâche est donc lancée correctement puisqu'elle arrive à envoyer un
IPC à la roottask.

La tâche minimale, à titre d'information, est :

int
main(int argc, char **argv)
{
void *arg[16];

L4_InitIpc(); // Initialisation du segment bss pour les appels système

// Écriture d'un message envoyé à la roottask par un stringitem
vms$string_initializer(message, 80);
rtl$strcpy(&message, RUN_S_STARTED "PAGER.SYS process started");
rtl$print(&message, NULL);
// Fonctionne parfaitement.

// rtl$strcpy se comporte comme strcpy() mais avec un descripteur de
// chaîne contenant un tableau statique, une longueur maximale et
// une longueur initialisée.
// rtl$print utilise le format passé au travers d'un descripteur de
// chaîne puis les données à afficher dans un tableau de pointeurs.
// rtl$print envoie un StringItem à la roottask pour affichage.
int i = 1;
arg[0] = &i;
rtl$strcpy(&message, "%d");
rtl$print(&message, arg);
// Affiche 1, donc l'appel système fonctionne.

// On essaye d'afficher argc
arg[0] = &argc;
rtl$strcpy(&message, "%d");
rtl$print(&message, arg);
// Affiche -2147237888 (?)

for(;;);
}

Pourtant, un dump de la mémoire me donne :

> memdump
Dump address [0x0]: 5F00
0000000000005f00 0000000000000000 0000000000000000 ........ ........
0000000000005f10 0000000000000000 0000000000000000 ........ ........
0000000000005f20 0000000000000000 0000000000000000 ........ ........
0000000000005f30 0000000000000000 0000000000000000 ........ ........
0000000000005f40 0000000000000000 0000000000000000 ........ ........
0000000000005f50 0000000000000000 2d532d4e55526425 ........ %dRUN-S-
0000000000005f60 2c44455452415453 532e524547415020 STARTED, PAGER.S
0000000000005f70 65636f7270205359 7472617473207373 YS proce ss start
0000000000005f80 0000000000006465 0000000000000000 ed...... ........
0000000000005f90 0000000000000000 0000000000000000 ........ ........
0000000000005fa0 0000000000000000 0000000000000050 ........ P.......
0000000000005fb0 0000000000000002 0000000000005f58 ........ X_......
0000000000005fc0 0000000000000000 0000000000000000 ........ ........
0000000000005fd0 0000000001012200 ffffffffffffffff ."...... ��������
0000000000005fe0 0000000000000000 ffffffffc0833000 ........ .0..����
0000000000005ff0 0000000000300000 0000000000000001 ..0..... .#######

Mes données sont bien écrites dans 5ff0 et 5ff8 (adresse initiale
de la pile 6000 et adresse du tableau d'arguments 300000). La
première adresse utilisée dans la pile est 5fe8, ce qui est encore
normal.

1/ Je ne vois pas d'où provient la valeur '-2147237888'.
2/ Plus généralement, je ne vois pas d'où vient mon erreur d'autant
que mon exemple minimal semble être conforme à ce que je peux voir
dans le code d'iguana. Je pensais tout d'abord à un problème
d'alignement de la pile, mais ce n'est pas le souci.

Toute idée est la bienvenue...

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr

1 réponse

1 2
Avatar
Antoine Leca
Marc écrivit :
Marc Espie wrote:
je sais plus comment ca s'appelle sur amd64, mais toutes les instructions
vectorielles successeur de mmx et consorts ont tres fortement tendance
a vouloir des adresses alignes sur 128 bits, et a ne pas marcher du tout



SSE ? (voire AVX maintenant)



Rigolo que tu mentionnes AVX dans ce contexte.
Les instructions AVX introduisent deux nouveautés dans le contexte que
mentionne Marc E (qui a par ailleurs parfaitement raison sur le fond de
la raison pour laquelle on aligne aujourd'hui sur 128 bits) :
- la première, c'est qu'il y a maintenant des registres 256 bits (YMMx)
et des instructions pour les manipuler, et bien sûr ces instructions
fonctionnent « mieux » si les opérandes sont alignées... sur 256 bits ;
- la seconde, qui ÀMHA est la directe conséquence du sujet qui nous
occupe (/l'ABI amd64 aligne sur 128 bits/), c'est que le nouveau jeu
d'instructions N'est PAS aussi strict que SSE, et acceptent que
certaines opérandes ne sont pas complètement alignées, et permet même
aux instructions de fonctionner avec des opérandes alignés sur 32 voire
16 bits, ce qui est donc compatible /de facto/ avec l'ABI i386.

sinon (comme c'est des trucs recents, pour une fois intel ne s'est pas
emmerde a faire de la compatibilite avec les vieux trucs, et donc quand
c'est pas aligne, c'est pas juste moins performant, ca ne marche pas du
tout).





Ce n'est pas une question de s'em****er ou pas, c'est une question de
compatibilité ascendante avec l'existant. Pour les instructions de base
x86, Intel a voulu être compatible avec le code existant pour le 8080,
et comme ce code ne faisait pas d'hypothèse d'alignement sur 16 bits,
les ingénieurs ont été obligé d'accepter les opérandes non alignés ;
pour le 80386, on recommence, là il fallait être compatible avec le code
DOS. Et ces compatibilités sont aussi ce qui a permis à Intel (puis plus
tard à AMD avec le x86-64) de gagner des parts de marché face à ses
concurrents.
Avec SSE, et après avoir tirer les leçons de l'expérience MMX, Intel
avait les mains libres au niveau des opérandes, donc ils ont pu forcer
un alignement « maximaliste », ce qui évidement est très bénéfique pour
les performances ; avec AVX, on voit que pour imposer cette nouvelle
extension du jeu d'instructions la marge de man½uvre n'est plus aussi
grande, il faut être compatible avec le SSE existant... et donc se
contenter d'alignement 128 bits, même avec des opérandes 256 ou 512 bits
(à venir).

Toute cette dissertation peut s'appliquer au langage C, voire à
n'importe quel projet continu : il faut toujours trouver un équilibre
entre la compatibilité avec le passé qui permet de capitaliser sur les «
parts de marché » existantes, et les opportunités d'amélioration ; et
les « bonnes » améliorations sont ÀMHA celles qui anticipent bien les
besoins futurs (pour éviter de se cogner quelques années plus tard à
nouveau contre la même barrière de performances) tout en évitant de
gâcher inutilement du temps processeur ou plus généralement des
ressources : un exemple de ce qu'il ne faut pas faire, a été la gestion
dans les années 1992-97 de l'adressage des disques durs au niveau du
BIOS PC, avec les « translations de géométrie » et autres mauvaises
solutions pour un problème initial pourtant clair : l'interface initiale
était limitée à 24 bits, et il était clair que l'on toucherait la limite
au plus tard à cinq ans...


Antoine
1 2