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

10 réponses

1 2
Avatar
JKB
Le Tue, 7 Dec 2010 09:10:21 +0000 (UTC),
JKB écrivait :
Bonjour à tous,



<snip>

Bon, le problème semble devir de gcc qui balance les arguments dans
dans des registres. Le but du jeu serait donc de forcer la fonction
main() à relire ces arguments dans pile. J'ai l'impression que rien
n'est prévu dans gcc pour cela (au moins avec l'ABI amd64).

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
Avatar
Jean-Marc Bourguet
JKB writes:

Le Tue, 7 Dec 2010 09:10:21 +0000 (UTC),
JKB écrivait :
Bonjour à tous,



<snip>

Bon, le problème semble devir de gcc qui balance les arguments dans
dans des registres. Le but du jeu serait donc de forcer la fonction
main() à relire ces arguments dans pile. J'ai l'impression que rien
n'est prévu dans gcc pour cela (au moins avec l'ABI amd64).



Regarde ce qui est fait avec les fonctions variadiques -- il est possible
que pour ces fonctions gcc n'utilise pas les registres, au moins pour les
arguments passés pour ... et le précédent, et si ce n'est pas le cas,
va_start doit gaire des choses intéressantes. Le contenu de la lib ffi
pourrait également t'intéresser.

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
Mickaël Wolff
On 07/12/10 11:41, JKB wrote:
Bon, le problème semble devir de gcc qui balance les arguments dans
dans des registres. Le but du jeu serait donc de forcer la fonction
main() à relire ces arguments dans pile. J'ai l'impression que rien
n'est prévu dans gcc pour cela (au moins avec l'ABI amd64).



volatile est un modificateur qui indique au compilateur qu'il doit
synchroniser les registres avec les variables en mémoire quand il est
susceptible d'optimiser.

Ça devrait te sauver.
Avatar
JKB
Le Tue, 07 Dec 2010 20:39:59 +0000,
Mickaël Wolff écrivait :
On 07/12/10 11:41, JKB wrote:
Bon, le problème semble devir de gcc qui balance les arguments dans
dans des registres. Le but du jeu serait donc de forcer la fonction
main() à relire ces arguments dans pile. J'ai l'impression que rien
n'est prévu dans gcc pour cela (au moins avec l'ABI amd64).



volatile est un modificateur qui indique au compilateur qu'il doit
synchroniser les registres avec les variables en mémoire quand il est
susceptible d'optimiser.

Ça devrait te sauver.



Merci à tous. Pour l'instant, j'ai sauvé le coup avec un bout
d'assembleur. Je vais creuser les deux pistes évoquées dans le fil.

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
Avatar
JKB
Le Thu, 09 Dec 2010 09:32:55 +0100,
Antoine Leca écrivait :
Jean-Marc Bourguet écrivit :
JKB writes:

Bon, le problème semble devir de gcc qui balance les arguments dans
dans des registres. Le but du jeu serait donc de forcer la fonction
main() à relire ces arguments dans pile.





Ouf... bin t'es pas rendu...

Un autre truc qui va t'apporter bien du plaisir, c'est que le
compilateur à l'entrée de la fonction « aligne » le pointeur de pile sur
une frontière de 128 bits (si je me souviens bien ; ou est-ce à l'appel)...



Non, ça, je sais faire. D'ailleurs si quelqu'un pouvait m'expliquer
pourquoi sur un processeur 64bits les concepteurs ont décidé
d'aligner la pile (enfin, une partie d'icelle) sur 128 bits, ce
serait sympa de m'éclairer...

J'ai l'impression que rien
n'est prévu dans gcc pour cela (au moins avec l'ABI amd64).





Plus précisement l'ABI ELF http://www.x86-64.org/documentation/abi.pdf
L'ABI Windows (pour le même processeur) est différente, par exemple en
nombre et utilisation des registres, ce qui garantit l'impossibilité
d'écrire des bibliothèques compatibles entre les deux systèmes,
contrairement à ce qui se passe pour i386 mais un peu semblable à la
situation du nommage C++. Joie, plaisir.


Regarde ce qui est fait avec les fonctions variadiques



Je ne crois pas que cela va le faire : pour amd64 (ELF), pour les
fonctions variadiques les premiers paramètres sont passés dans les
registres exactement comme une fonction normale, ce qui fait d'ailleurs
que l'utilisation de <stdarg.h> au grand complet est absolument
indispensable, y compris va_end et sans utiliser de court-circuit (genre
déréférencer un va_list).



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
Avatar
Antoine Leca
JKB écrivit :
Non, ça, je sais faire. D'ailleurs si quelqu'un pouvait m'expliquer
pourquoi sur un processeur 64bits les concepteurs ont décidé
d'aligner la pile (enfin, une partie d'icelle) sur 128 bits, ce
serait sympa de m'éclairer...



La TLB (sais pas traduire en français) du i386 et au-dessus n'agit pas
sur les 4 lignes d'adresses de poids faible, ce qui fait 16 octets soit
128 bits. Donc pour maximiser l'effet de cache (ou minimiser les
nécessités d'éliminer du cache certaines lignes), il est bon d'aligner
les structures (dont procédure et cadre de pile) sur des frontières de
128 bits.


Antoine
Avatar
JKB
Le Thu, 09 Dec 2010 09:58:33 +0100,
Antoine Leca écrivait :
JKB écrivit :
Non, ça, je sais faire. D'ailleurs si quelqu'un pouvait m'expliquer
pourquoi sur un processeur 64bits les concepteurs ont décidé
d'aligner la pile (enfin, une partie d'icelle) sur 128 bits, ce
serait sympa de m'éclairer...



La TLB (sais pas traduire en français) du i386 et au-dessus n'agit pas
sur les 4 lignes d'adresses de poids faible, ce qui fait 16 octets soit
128 bits. Donc pour maximiser l'effet de cache (ou minimiser les
nécessités d'éliminer du cache certaines lignes), il est bon d'aligner
les structures (dont procédure et cadre de pile) sur des frontières de
128 bits.



Ça, je veux bien, mais tout n'est pas aligné dans cette fichue
pile... Enfin, bon, ce n'est pas important.

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
Avatar
Lucas Levrel
Le 9 décembre 2010, JKB a écrit :
Non, ça, je sais faire. D'ailleurs si quelqu'un pouvait m'expliquer
pourquoi sur un processeur 64bits les concepteurs ont décidé
d'aligner la pile (enfin, une partie d'icelle) sur 128 bits, ce
serait sympa de m'éclairer...



Juste après avoir lu ton message, j'ouvre la doc du compilateur Intel, et
en vagabondant je tombe par hasrad sur des classes C++ qui « provide an
interface to SIMD operations » et stockent les données par paquets de 128
bits (4 float ou 2 double). Ça a peut-être un rapport ?

Page en question :
http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/cref_cls/common/cppref_class_fp_ovrvw.htm
Racine de la doc :
http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/index.htm

--
LL
Avatar
espie
In article <idq5np$mm6$,
Antoine Leca wrote:
JKB écrivit :
Non, ça, je sais faire. D'ailleurs si quelqu'un pouvait m'expliquer
pourquoi sur un processeur 64bits les concepteurs ont décidé
d'aligner la pile (enfin, une partie d'icelle) sur 128 bits, ce
serait sympa de m'éclairer...



La TLB (sais pas traduire en français) du i386 et au-dessus n'agit pas
sur les 4 lignes d'adresses de poids faible, ce qui fait 16 octets soit
128 bits. Donc pour maximiser l'effet de cache (ou minimiser les
nécessités d'éliminer du cache certaines lignes), il est bon d'aligner
les structures (dont procédure et cadre de pile) sur des frontières de
128 bits.



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
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).
Avatar
Marc
Marc Espie wrote:

JKB écrivit :
Non, ça, je sais faire. D'ailleurs si quelqu'un pouvait m'expliquer
pourquoi sur un processeur 64bits les concepteurs ont décidé
d'aligner la pile (enfin, une partie d'icelle) sur 128 bits, ce
serait sympa de m'éclairer...






[...]
je sais plus comment ca s'appelle sur amd64,



SSE ? (voire AVX maintenant)

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
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).



En fait pour plusieurs choses il y a une instruction qui suppose le bon
alignement et une (plus lente évidemment) qui ne le suppose pas.
1 2