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

Problème d'alignement mémoire

17 réponses
Avatar
JKB
Bonjour à tous,

Un petit problème d'alignement mémoire (dans un module du noyau
Linux que j'essaye de débugguer sur sun4v...). Ce qui m'intéresse
est là :

ptr est un tableau passé en argument à la fonction
static int insert_geo_m_pg(u8 *ptr, u64 sec), ptr étant
défini lors des appels par un appel à page_address(tio->pvec[0]),
donc contenant chez moi des adresses sur 64 bits.

u32 ncyl, *p;
p = (u32 *)(ptr + 1);

Ce truc fonctionne sur les architectures qui n'ont pas besoin
d'aligner les données. Sur Sparc64, ça coince. J'ai corrigé le
reste des alignements, mais celui-ci me pose problème (j'obtiens un
superbe Oops).

Arrêtez-moi si je me plante, mais p = (u32 *)(ptr + 1) signifie pour moi
mettre dans la variable p qui est un pointeur sur un entier non
signé de 32 bits (taille du pointeur 64 bits), le pointeur formé par
ptr[1]...ptr[8]. Après avoir fouillé dans le code (iscsi-target
disponible sur le svn de http://iscsitarget.sourceforge.net/),
j'écris :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));

J'aurais préféré un
for(i = 0; i < 8; i++) p |= (void *) ptr[i] << (8 * (7 - i));
mais visiblement, gcc n'aime pas...

La compilation se passe bien, mais je récolte invariablement un

Unable to handle kernel paging request at virtual address
0000000011580000
tsk->{mm,active_mm}->context = 00000000000009cf
tsk->{mm,active_mm}->pgd = fffff800ff506000
\|/ ____ \|/
"@'/ .. \`@"
/_| \__/ |_\
\__U_/
istiod1(4534): Oops [#2]
TSTATE: 0000004480001603 TPC: 000000001017417c TNPC: 0000000010174180 Y:
00000000 Tainted: G D
TPC: <insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]>

J'avoue ne pas trop comprendre... Merci de m'éclairer...

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.

10 réponses

1 2
Avatar
Eric Levenez
Le 17/10/07 12:36, dans ,
« JKB » a écrit :

ptr est un tableau passé en argument à la fonction
static int insert_geo_m_pg(u8 *ptr, u64 sec), ptr étant
défini lors des appels par un appel à page_address(tio->pvec[0]),
donc contenant chez moi des adresses sur 64 bits.

u32 ncyl, *p;
p = (u32 *)(ptr + 1);


ptr étant un pointeur sur u8 (supposé des octets), "ptr + 1" est le pointeur
suivant, c'est donc à l'adresse de ptr plus 1 octet (car sizeof u8). Ensuite
ce pointeur vers un u8 est converti vers un pointeur vers un u32 (aucune
modification de valeur).

Si ptr valait 1000, p vaudra 1001. C'est ce que tu veux faire ?

--
Éric Lévénez -- <http://www.levenez.com/>
Unix is not only an OS, it's a way of life.

Avatar
JKB
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
Eric Levenez écrivait dans fr.comp.lang.c :
Le 17/10/07 12:36, dans ,
« JKB » a écrit :

ptr est un tableau passé en argument à la fonction
static int insert_geo_m_pg(u8 *ptr, u64 sec), ptr étant
défini lors des appels par un appel à page_address(tio->pvec[0]),
donc contenant chez moi des adresses sur 64 bits.

u32 ncyl, *p;
p = (u32 *)(ptr + 1);


ptr étant un pointeur sur u8 (supposé des octets), "ptr + 1" est le pointeur
suivant, c'est donc à l'adresse de ptr plus 1 octet (car sizeof u8). Ensuite
ce pointeur vers un u8 est converti vers un pointeur vers un u32 (aucune
modification de valeur).

Si ptr valait 1000, p vaudra 1001. C'est ce que tu veux faire ?


Moi, je ne veux rien faire ;-) Enfin si, convertir cette foutue
ligne dans un truc qui ne perdra pas de données en raison d'un
problème d'alignement mémoire...

J'ai bien compris que si ptr[0] vaut 0x3D et ptr[1] 0x5A, p va
contenir après p = (u32 *)(ptr + 1) la valeur 0x5A. Le problème,
c'est que ce fichu truc est un pointeur sur un u32. Pour que ce
pointeur soit complet (arrêtez-moi sir je me plante), il faut que je
récupère d'une manière ou d'une autre les sept octets suivants
(parce que les pointeurs font chez moi 64 bits). Et
je pense que c'est là que le noyau m'insulte car il n'y a aucune
raison que le truc soit aligné sur 64 bits. J'essaye donc de
bricoler octet par octet pour réaligner tout cela par quelque chose
du genre de :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));

qui ne fonctionne pas et je ne vois toujours pas pourquoi :-(

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.


Avatar
Marc Boyer
On 2007-10-17, JKB wrote:
ptr est un tableau passé en argument à la fonction
static int insert_geo_m_pg(u8 *ptr, u64 sec), ptr étant
défini lors des appels par un appel à page_address(tio->pvec[0]),
donc contenant chez moi des adresses sur 64 bits.

u32 ncyl, *p;
p = (u32 *)(ptr + 1);

Ce truc fonctionne sur les architectures qui n'ont pas besoin
d'aligner les données. Sur Sparc64, ça coince. J'ai corrigé le
reste des alignements, mais celui-ci me pose problème (j'obtiens un
superbe Oops).


Ca coince même avant de faire un accès à *p ?

Arrêtez-moi si je me plante, mais p = (u32 *)(ptr + 1) signifie pour moi
mettre dans la variable p qui est un pointeur sur un entier non
signé de 32 bits (taille du pointeur 64 bits), le pointeur formé par
ptr[1]...ptr[8]. Après avoir fouillé dans le code (iscsi-target
disponible sur le svn de http://iscsitarget.sourceforge.net/),
j'écris :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));


Pourquoi pas un bête
memcpy(p,ptr+1,sizeof(p)) ?

J'aurais préféré un
for(i = 0; i < 8; i++) p |= (void *) ptr[i] << (8 * (7 - i));
mais visiblement, gcc n'aime pas...

La compilation se passe bien, mais je récolte invariablement un

Unable to handle kernel paging request at virtual address
0000000011580000
tsk->{mm,active_mm}->context = 00000000000009cf
tsk->{mm,active_mm}->pgd = fffff800ff506000
|/ ____ |/
"@'/ .. `@"
/_| __/ |_
__U_/
istiod1(4534): Oops [#2]
TSTATE: 0000004480001603 TPC: 000000001017417c TNPC: 0000000010174180 Y:
00000000 Tainted: G D
TPC: <insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]>

J'avoue ne pas trop comprendre... Merci de m'éclairer...


Ce qui m'étonne, c'est qu'il plante dans l'affectation de p est pas
dans l'accès à *p.
Le memcpy que je propose résoudra la ligne d'affectation de p,
mais surement pas les accès par la suite.
Moi, je proposerais de changer le code en
u32 ncyl, toto;
memcpy(&toto, ptr+1, sizeof(toto) );

puis de changer tous les accès en lecture sur *p par des
accès en lecture à toto, et les accès en écriture, au cas par
cas, soit par une écriture dans toto, soit dans (ptr+1) par
memcpy.


Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
Marc Boyer
On 2007-10-17, JKB wrote:
Le 17-10-2007, à propos de
J'essaye donc de
bricoler octet par octet pour réaligner tout cela par quelque chose
du genre de :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));

qui ne fonctionne pas et je ne vois toujours pas pourquoi :-(


Ben, ptr[i] est un pointeur, donc une valeur sur 64 bits chez toi,
alors que j'imagine que tu voudrais récuperer 8 bits par 8 bits.
Donc, plutôt faire:
unsigned char char_ptr= (unsigned char*) ptr;
p= 0x0;
for(i= 0; i < 8 ; ++i)
p+= char_ptr[i] << (8 * (7-i) );
si on veut reprendre ton code (qui reste douteux).

Mais bon, pourquoi pas un memcpy ?

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
Charlie Gordon
"Marc Boyer" a écrit dans le message
de news:
On 2007-10-17, JKB wrote:
Le 17-10-2007, à propos de
J'essaye donc de
bricoler octet par octet pour réaligner tout cela par quelque chose
du genre de :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));

qui ne fonctionne pas et je ne vois toujours pas pourquoi :-(


Ben, ptr[i] est un pointeur, donc une valeur sur 64 bits chez toi,
alors que j'imagine que tu voudrais récuperer 8 bits par 8 bits.
Donc, plutôt faire:
unsigned char char_ptr= (unsigned char*) ptr;
p= 0x0;
for(i= 0; i < 8 ; ++i)
p+= char_ptr[i] << (8 * (7-i) );
si on veut reprendre ton code (qui reste douteux).

Mais bon, pourquoi pas un memcpy ?


J'ai l'impression que vous etes sur une fausse piste: p = (u32*)(ptr + 1)
ne construit pas la valeur du pointeur p à partir des octets pointés par
ptr, mais à partir de la valeur de ptr. On ne sait pas quel est le type de
ptr: supposons que c'est un pointeur sur u8, p pointerait alors sur un
entier stocké en mémoire aux adresses ptr+1, ptr+2, ptr+3, ptr+4. On ne
peut pas déréférencer (ptr+1) pour lire un u32 sur un Sun, mais on peut lire
l'entier avec memcpy:

u32 n;
memcpy(&n, ptr + 1, sizeof(n));

reste à gérer les problèmes d'endianness.

--
Chqrlie.


Avatar
JKB
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
Marc Boyer écrivait dans fr.comp.lang.c :
On 2007-10-17, JKB wrote:
ptr est un tableau passé en argument à la fonction
static int insert_geo_m_pg(u8 *ptr, u64 sec), ptr étant
défini lors des appels par un appel à page_address(tio->pvec[0]),
donc contenant chez moi des adresses sur 64 bits.

u32 ncyl, *p;
p = (u32 *)(ptr + 1);

Ce truc fonctionne sur les architectures qui n'ont pas besoin
d'aligner les données. Sur Sparc64, ça coince. J'ai corrigé le
reste des alignements, mais celui-ci me pose problème (j'obtiens un
superbe Oops).


Ca coince même avant de faire un accès à *p ?

Arrêtez-moi si je me plante, mais p = (u32 *)(ptr + 1) signifie pour moi
mettre dans la variable p qui est un pointeur sur un entier non
signé de 32 bits (taille du pointeur 64 bits), le pointeur formé par
ptr[1]...ptr[8]. Après avoir fouillé dans le code (iscsi-target
disponible sur le svn de http://iscsitarget.sourceforge.net/),
j'écris :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));


Pourquoi pas un bête
memcpy(p,ptr+1,sizeof(p)) ?


Parce que je n'ai pas l'habitude d'utiliser cette fonction et que je
n'y ai pas pensé... J'ai essayé d'utiliser des unions, des tas de
trucs, mais pas memcpy()...

J'aurais préféré un
for(i = 0; i < 8; i++) p |= (void *) ptr[i] << (8 * (7 - i));
mais visiblement, gcc n'aime pas...

La compilation se passe bien, mais je récolte invariablement un

Unable to handle kernel paging request at virtual address
0000000011580000
tsk->{mm,active_mm}->context = 00000000000009cf
tsk->{mm,active_mm}->pgd = fffff800ff506000
|/ ____ |/
"@'/ .. `@"
/_| __/ |_
__U_/
istiod1(4534): Oops [#2]
TSTATE: 0000004480001603 TPC: 000000001017417c TNPC: 0000000010174180 Y:
00000000 Tainted: G D
TPC: <insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]>

J'avoue ne pas trop comprendre... Merci de m'éclairer...


Ce qui m'étonne, c'est qu'il plante dans l'affectation de p est pas
dans l'accès à *p.
Le memcpy que je propose résoudra la ligne d'affectation de p,
mais surement pas les accès par la suite.
Moi, je proposerais de changer le code en
u32 ncyl, toto;
memcpy(&toto, ptr+1, sizeof(toto) );


Je viens de mettre dans mon code :

q = (u32 *)(ptr + 1);
p = memcpy(p,ptr+1,sizeof(p));
printk("%p %pn", p, q);

simplement pour voir si les deux méthodes renvoient le même
résultat, et cela me donne :

Kernel unaligned access at TPC[1017417c] insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]
fffff800f7d3e034 fffff800f7d3e035
Kernel unaligned access at TPC[1017417c] insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]

Dans la suite, j'utilise q, parce que l'utilisation de p me renvoie
une double faute et je me retrouve avec le prompt de l'OBP...

Question certainement triviale. Pourquoi les deux valeurs sont-elles
différentes ?

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.


Avatar
Erwan David
JKB écrivait :

Bonjour à tous,

Un petit problème d'alignement mémoire (dans un module du noyau
Linux que j'essaye de débugguer sur sun4v...). Ce qui m'intéresse
est là :

ptr est un tableau passé en argument à la fonction
static int insert_geo_m_pg(u8 *ptr, u64 sec), ptr étant
défini lors des appels par un appel à page_address(tio->pvec[0]),
donc contenant chez moi des adresses sur 64 bits.

u32 ncyl, *p;
p = (u32 *)(ptr + 1);

Ce truc fonctionne sur les architectures qui n'ont pas besoin
d'aligner les données. Sur Sparc64, ça coince. J'ai corrigé le
reste des alignements, mais celui-ci me pose problème (j'obtiens un
superbe Oops).

Arrêtez-moi si je me plante, mais p = (u32 *)(ptr + 1) signifie pour moi
mettre dans la variable p qui est un pointeur sur un entier non
signé de 32 bits (taille du pointeur 64 bits), le pointeur formé par
ptr[1]...ptr[8].


Non.

Si ptr vaut 0x8.
ptr+1 est l'adresse du u8 suivant (vu tes notations je pense que u8 est
un octet).

DOnc ptr+1 vaut 9

Tu fais donc p=(u32 *)(9) -> violation d'alignement...



--
Erwan

Avatar
Marc Boyer
On 2007-10-17, Charlie Gordon wrote:
"Marc Boyer" a écrit dans le message
de news:
On 2007-10-17, JKB wrote:
Le 17-10-2007, à propos de
J'essaye donc de
bricoler octet par octet pour réaligner tout cela par quelque chose
du genre de :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));

qui ne fonctionne pas et je ne vois toujours pas pourquoi :-(


Ben, ptr[i] est un pointeur, donc une valeur sur 64 bits chez toi,
alors que j'imagine que tu voudrais récuperer 8 bits par 8 bits.
Donc, plutôt faire:
unsigned char char_ptr= (unsigned char*) ptr;
p= 0x0;
for(i= 0; i < 8 ; ++i)
p+= char_ptr[i] << (8 * (7-i) );
si on veut reprendre ton code (qui reste douteux).

Mais bon, pourquoi pas un memcpy ?


J'ai l'impression que vous etes sur une fausse piste: p = (u32*)(ptr + 1)
ne construit pas la valeur du pointeur p à partir des octets pointés par
ptr, mais à partir de la valeur de ptr. On ne sait pas quel est le type de
ptr: supposons que c'est un pointeur sur u8, p pointerait alors sur un
entier stocké en mémoire aux adresses ptr+1, ptr+2, ptr+3, ptr+4. On ne
peut pas déréférencer (ptr+1) pour lire un u32 sur un Sun, mais on peut lire
l'entier avec memcpy:

u32 n;
memcpy(&n, ptr + 1, sizeof(n));


Voui. Ce que je lui ai dit dans l'autre post.

reste à gérer les problèmes d'endianness.


Tu veux dire quoi là ?

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)



Avatar
JKB
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
Marc Boyer écrivait dans fr.comp.lang.c :
On 2007-10-17, Charlie Gordon wrote:
"Marc Boyer" a écrit dans le message
de news:
On 2007-10-17, JKB wrote:
Le 17-10-2007, à propos de
J'essaye donc de
bricoler octet par octet pour réaligner tout cela par quelque chose
du genre de :

p = 0x0;
for(i = 0; i < 8; i++) p += ptr[i] << (8 * (7 - i));

qui ne fonctionne pas et je ne vois toujours pas pourquoi :-(


Ben, ptr[i] est un pointeur, donc une valeur sur 64 bits chez toi,
alors que j'imagine que tu voudrais récuperer 8 bits par 8 bits.
Donc, plutôt faire:
unsigned char char_ptr= (unsigned char*) ptr;
p= 0x0;
for(i= 0; i < 8 ; ++i)
p+= char_ptr[i] << (8 * (7-i) );
si on veut reprendre ton code (qui reste douteux).

Mais bon, pourquoi pas un memcpy ?


J'ai l'impression que vous etes sur une fausse piste: p = (u32*)(ptr + 1)
ne construit pas la valeur du pointeur p à partir des octets pointés par
ptr, mais à partir de la valeur de ptr. On ne sait pas quel est le type de
ptr: supposons que c'est un pointeur sur u8, p pointerait alors sur un
entier stocké en mémoire aux adresses ptr+1, ptr+2, ptr+3, ptr+4. On ne
peut pas déréférencer (ptr+1) pour lire un u32 sur un Sun, mais on peut lire
l'entier avec memcpy:

u32 n;
memcpy(&n, ptr + 1, sizeof(n));


Voui. Ce que je lui ai dit dans l'autre post.


Ouaips, memset est une bonne idée, mais ça coince toujours (les
résultats de q = (u32 *)(ptr + 1); et de memcpy(p,ptr+1,sizeof(p));
diffèrent de 1).

reste à gérer les problèmes d'endianness.


Tu veux dire quoi là ?


Je ne vois pas pourquoi il y aurait ici un tel problème...

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.




Avatar
Marc Boyer
On 2007-10-17, JKB wrote:
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
Marc Boyer écrivait dans fr.comp.lang.c :
Ce qui m'étonne, c'est qu'il plante dans l'affectation de p est pas
dans l'accès à *p.
Le memcpy que je propose résoudra la ligne d'affectation de p,
mais surement pas les accès par la suite.
Moi, je proposerais de changer le code en
u32 ncyl, toto;
memcpy(&toto, ptr+1, sizeof(toto) );


Je viens de mettre dans mon code :

q = (u32 *)(ptr + 1);
p = memcpy(p,ptr+1,sizeof(p));
printk("%p %pn", p, q);

simplement pour voir si les deux méthodes renvoient le même
résultat, et cela me donne :

Kernel unaligned access at TPC[1017417c] insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]
fffff800f7d3e034 fffff800f7d3e035
Kernel unaligned access at TPC[1017417c] insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]

Dans la suite, j'utilise q, parce que l'utilisation de p me renvoie
une double faute et je me retrouve avec le prompt de l'OBP...

Question certainement triviale. Pourquoi les deux valeurs sont-elles
différentes ?


Ben.. Si mes souvenirs sont bon, ton cast de prt+1, de type u8* en
un u32* est dépendant du compilo.

Tu veux pas faire un
printk("ptr=%p ptr+1=%p p=%p q=%pn", ptr, ptr+1, p, q);
dans ton code plutôt. Le fait que q soit sur une adresse impaire
me chagrine un peu.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


1 2