OVH Cloud OVH Cloud

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.

7 réponses

1 2
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:
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.


Moi aussi, comme le warning à la compilation :

/usr/src/iscsi/trunk/kernel/target_disk.c:75: warning: 'p' is used
uninitialized in this function

sur la ligne du memcpy().

Résultat :

ptrÿfff800f7d46034 ptr+1ÿfff800f7d46035 pÿfff800f7d46034 qÿfff800f7d46035
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[1017418c] insert_geo_m_pg+0x94/0xa0 [iscsi_trgt]
ptrÿfff800f7d08034 ptr+1ÿfff800f7d08035 pÿfff800f7d08034 qÿfff800f7d08035
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[1017418c] insert_geo_m_pg+0x94/0xa0 [iscsi_trgt]

Je sèche de plus en plus...

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


Moi aussi, comme le warning à la compilation :

/usr/src/iscsi/trunk/kernel/target_disk.c:75: warning: 'p' is used
uninitialized in this function

sur la ligne du memcpy().

Résultat :

ptrÿfff800f7d46034 ptr+1ÿfff800f7d46035 pÿfff800f7d46034 qÿfff800f7d46035
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[1017418c] insert_geo_m_pg+0x94/0xa0 [iscsi_trgt]
ptrÿfff800f7d08034 ptr+1ÿfff800f7d08035 pÿfff800f7d08034 qÿfff800f7d08035
Kernel unaligned access at TPC[10174184] insert_geo_m_pg+0x8c/0xa0 [iscsi_trgt]
Kernel unaligned access at TPC[1017418c] insert_geo_m_pg+0x94/0xa0 [iscsi_trgt]

Je sèche de plus en plus...


Oui, d'autant que je t'ai dis une énormité. Alors qu'on ait même pas
vendredi. Le
memcpy(p,ptr+1,sizeof(p));
ne correspond pas du tout à ce que tu veux faire. C'est un équivalent
(modulo les pb d'alignement) à
*p= *( (u32*) ptr+1 );
Et le compilo rale tout à fait a bon escient.

On reprend à 0. Tu as un code du genre
void foo(u8* ptr){
// Code actuel
u32* p;
p= (u32*) ptr+1;
// usage de *p
}
qui plante, ce qui me semble tout à fait normal.
La question à laquelle tu n'a pas répondu est: il plante dès
l'affectation, ou dès qu'on déréférence p (qu'on utilise *p) ?

Même s'il rale dès l'affectation, et qu'on arrive à le faire taire
à ce moment là, de toute façon, on ne pourra pas accéder à
(ptr+1) pour y stocker un u32 de manière transparente, à cause
des pb d'alignement.
La seule solution que je vois, c'est de déclarer une variable
u32, et de l'utiliser (voir mon autre post avec toto, ou celui
de Charlie avec n).

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 :
Oui, d'autant que je t'ai dis une énormité. Alors qu'on ait même pas
vendredi. Le
memcpy(p,ptr+1,sizeof(p));
ne correspond pas du tout à ce que tu veux faire. C'est un équivalent
(modulo les pb d'alignement) à
*p= *( (u32*) ptr+1 );
Et le compilo rale tout à fait a bon escient.

On reprend à 0. Tu as un code du genre
void foo(u8* ptr){
// Code actuel
u32* p;
p= (u32*) ptr+1;
// usage de *p
}
qui plante, ce qui me semble tout à fait normal.
La question à laquelle tu n'a pas répondu est: il plante dès
l'affectation, ou dès qu'on déréférence p (qu'on utilise *p) ?


En fait, l'erreur est récupérée par le noyau, et je ne suis pas
capable de dire où celui-ci râle.

Même s'il rale dès l'affectation, et qu'on arrive à le faire taire
à ce moment là, de toute façon, on ne pourra pas accéder à
(ptr+1) pour y stocker un u32 de manière transparente, à cause
des pb d'alignement.
La seule solution que je vois, c'est de déclarer une variable
u32, et de l'utiliser (voir mon autre post avec toto, ou celui
de Charlie avec n).


Bon, je vais essayer la solution avec le n...

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
JKB
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
JKB écrivait dans fr.comp.lang.c :
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
Marc Boyer écrivait dans fr.comp.lang.c :
Oui, d'autant que je t'ai dis une énormité. Alors qu'on ait même pas
vendredi. Le
memcpy(p,ptr+1,sizeof(p));
ne correspond pas du tout à ce que tu veux faire. C'est un équivalent
(modulo les pb d'alignement) à
*p= *( (u32*) ptr+1 );
Et le compilo rale tout à fait a bon escient.

On reprend à 0. Tu as un code du genre
void foo(u8* ptr){
// Code actuel
u32* p;
p= (u32*) ptr+1;
// usage de *p
}
qui plante, ce qui me semble tout à fait normal.
La question à laquelle tu n'a pas répondu est: il plante dès
l'affectation, ou dès qu'on déréférence p (qu'on utilise *p) ?


En fait, l'erreur est récupérée par le noyau, et je ne suis pas
capable de dire où celui-ci râle.

Même s'il rale dès l'affectation, et qu'on arrive à le faire taire
à ce moment là, de toute façon, on ne pourra pas accéder à
(ptr+1) pour y stocker un u32 de manière transparente, à cause
des pb d'alignement.
La seule solution que je vois, c'est de déclarer une variable
u32, et de l'utiliser (voir mon autre post avec toto, ou celui
de Charlie avec n).


Bon, je vais essayer la solution avec le n...


J'arrive à ceci qui fonctionne :

u32 n;
memcpy(&n,ptr+1,sizeof(u32));
n = n | cpu_to_be32(ncyl);
memcpy(ptr+1, &n, sizeof(u32));

Merci à tous,

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
Charlie Gordon
"JKB" a écrit dans le message de news:

Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
JKB écrivait dans fr.comp.lang.c :
Le 17-10-2007, à propos de
Re: Problème d'alignement mémoire,
Marc Boyer écrivait dans fr.comp.lang.c :
Oui, d'autant que je t'ai dis une énormité. Alors qu'on ait même pas
vendredi. Le
memcpy(p,ptr+1,sizeof(p));
ne correspond pas du tout à ce que tu veux faire. C'est un équivalent
(modulo les pb d'alignement) à
*p= *( (u32*) ptr+1 );
Et le compilo rale tout à fait a bon escient.

On reprend à 0. Tu as un code du genre
void foo(u8* ptr){
// Code actuel
u32* p;
p= (u32*) ptr+1;
// usage de *p
}
qui plante, ce qui me semble tout à fait normal.
La question à laquelle tu n'a pas répondu est: il plante dès
l'affectation, ou dès qu'on déréférence p (qu'on utilise *p) ?


En fait, l'erreur est récupérée par le noyau, et je ne suis pas
capable de dire où celui-ci râle.

Même s'il rale dès l'affectation, et qu'on arrive à le faire taire
à ce moment là, de toute façon, on ne pourra pas accéder à
(ptr+1) pour y stocker un u32 de manière transparente, à cause
des pb d'alignement.
La seule solution que je vois, c'est de déclarer une variable
u32, et de l'utiliser (voir mon autre post avec toto, ou celui
de Charlie avec n).


Bon, je vais essayer la solution avec le n...


J'arrive à ceci qui fonctionne :

u32 n;
memcpy(&n,ptr+1,sizeof(u32));
n = n | cpu_to_be32(ncyl);
memcpy(ptr+1, &n, sizeof(u32));


Comme quoi il fallait bien faire gaffe à l'endianness ;-)

--
Chqrlie.



Avatar
Charlie Gordon
"JKB" a écrit dans le message de news:

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


Parce que si tu lis des entiers mal alignés, c'est sans doute que lis une
structure d'origine extérieure, genre header d'un fichier, block d'un
device, partie de paquet TCP... et donc l'endianness n'est pas
nécessairement celle du host.

--
Chqrlie.





Avatar
Antoine Leca
En news:, JKB va escriure:
[bla bla bla]
Sparc64,
[Moi pas connaître, donc pas taper si c'est une ânerie]

[bla bla bla]
pointeur 64 bits),
[bla bla bla]

istiod1(4534): Oops [#2]
TSTATE: 0000004480001603 TPC: 000000001017417c TNPC: 0000000010174180
^

Y: 00000000 Tainted: G D
TPC: <insert_geo_m_pg+0x84/0xa0 [iscsi_trgt]>


TPC est probablement un pointeur vers une structure. Ici, ton pointeur n'est
pas aligné sur 64 bits (le bit 2 est positionné à 1).

Solution : probablement rectifier la construction de l'objet pointé pour
s'assurer qu'il est bien effectivement aligné sur 64 bits (et pas seulement
sur 32 bits).


Antoine

1 2