[OSy] zahada: Volani funkce promenneho poctu parametru pred navratem z jine funkci
David Matousek
david at matousec.com
Mon Oct 29 10:23:36 CET 2007
Dobry den,
moc rad bych pozadal nekoho, kdo rozumi architekture MIPS a assembleru,
aby se mi kouknul na nasledujici zahadu, nemam proto zadne vysvetleni,
budu vdecny za vysvetleni - co je spatne, jak se tomu SPRAVNE vyvarovat:
muj kod vypada zhruba takto - jedna se o ceckovy kod, ktery je volan z assembleru,
konkretne nize uvedena funkceX je ekvivalentem "exception" z kalista,
ktera se vola z "handle_general_exception", ale to zrejme neni podstatne:
kod1:
funkceX(argument)
{
... kod ...
debugovaci_vypis(argument,format);
return;
}
vlozil jsem do nej stopku pro simulator takto:
kod2:
funkceX(argument)
{
... kod ...
STOPKA
debugovaci_vypis(arg1,format);
return;
}
zde bych upozornil, ze za formatem mohou byt argumenty, ale v tomto pripade
zadne nejsou, jen pro predstavu, ze funkce se vola obecne takto
debugovaci_vypis(arg1,format,argA,argB,argC,...);
v instrukcnim vypisu pak celou situaci kod2 vidime takto:
A) STOPKA:
800019C4 ---
B) volani debugovaciho vypisu:
800019C8 lui a1, 0x8000 # 0x8000=32768, a1: 0x80005f18->0x80000000
800019CC lw ra, 0x18(sp) # 0x18=24, ra: 0x80001a1c->0x8000040c
800019D0 lw s1, 0x14(sp) # 0x14=20, s1: 0x80f98f28->0x12345678
800019D4 lw s0, 0x10(sp) # 0x10=16, s0: 0x8000->0x80000000
800019D8 addiu a1, a1, 0x5bf8 # 0x5bf8=23544, a1: 0x80000000->0x80005bf8
800019DC addiu a0, 0, 0x40 # 0x40=64, a0: 0x0->0x40
C) skok na funkci debugovaciho vypisu
800019E0 j +0x1b80 # 0x1b80=7040
Q)
800019E4 addiu sp, sp, 0x20 # 0x20=32, sp: 0x80f98f08->0x80f98f28
80001B80 lui v0, 0xff11 # 0xff11=65297, v0: 0xff11f33f->0xff110000
80001B84 ori v0, v0, 0xf33f # 0xfffff33fh=62271, v0: 0xff110000->0xff11f33f
80001B88 addiu sp, sp, 0xffe0 # 0xffffffe0=65504, sp: 0x80f98f28->0x80f98f08
80001B8C and a0, a0, v0 # a0: 0x40->0x0
80001B90 sw ra, 0x18(sp) # 0x18=24
R)
80001B94 sw a2, 0x28(sp) # 0x28=40
80001B98 beq a0, 0, 0x6 # 0x6=6
80001B9C sw a3, 0x2c(sp) # 0x2c=44
80001BB4 lw ra, 0x18(sp) # 0x18=24
D) konec funkce debugovaciho vypisu
80001BB8 jr ra
80001BBC addiu sp, sp, 0x20 # 0x20=32, sp: 0x80f98f08->0x80f98f28
E) navrat do kodu, ktery volal funkce(argument)
8000040C lui v0, 0x8000 # 0x8000=32768, v0: 0xff11f33f->0x80000000
v bode A) je registr sp hodnote 0x80f98f08 a vse je v tomto okamziku v poradku
na adrese 0x80f98f30 je magic hodnota 0x12345678 a tu nam pujde (ve skutecnosti je to obsah
registru (cast kontextu) uschovany pomoci
addiu $sp, -EX_STACK_FRAME
STORE_GENERAL_REGS $sp
)
v B) se pripravuje volani debugovaciho vypisu
0x80005bf8 ktery putuje do a1 je ukazatel na formatovaci string string
0x40 ktery putujde do a0 je arg1
vsimete si, ze do registru ra se uklada adresa 0x8000040c,
je to adresa do funkce, ktera volala funkceX
kompilator je nastaven na -O2, takze dela optimalizaci, ze volani funkce pred returnem
se jiz nevraci do volajici funkceX, ale rovnou do funkce jeste o jednu drive - ekvivalent
handle_general_exception
v C) se pak skoci na debugovaci_vypis, ale jeste se provede Q)
Q) je dealokace lokalnich promennych "funkceX"
stack tak vyleze az na 0x80f98f28
a debugovaci_vypis tedy zacina s sp=0x80f98f28
tam se provede nejake srovnani arg1 s debugovaci konstantou 0xff11f33f
u nas srovnani (bitovy and) vyjde nula, takze se dalsi telo deugovaciho vypisu neprovede (nic se nevypise)
je tam neco jako
if ((arg1 & KONSTANTA) == 0) return;
takze jak je videt, tak beq instrukce na 0x80001B98 se nevykonala
a jde na return
ale jeste predtim se provede
80001B88 addiu sp, sp, 0xffe0
tedy sp se pohne sp: 0x80f98f28->0x80f98f08
to je zrejme alokace lokalnich promennych na stacku
to by nebylo nic mimoradneho kdyby pod R)
se nedelo nejake [sp+28]:=a2
jenze sp je ted 80f98f08 a sp+28 je 80f98f28+28=80F98F30
to je ale magic hodnota - obsah v1 registru z kontextu, ktery byl ulozen
je tedy takto brutalne prepsan v R)
v D) se pak skoci na funkci, ktera volala "funkceX" a jede se dal,
kde se restorne kontext a pokracuje se
v dusledku pak tento kod ve vlakne
int volatile i=0x12345678;
while (i==0x12345678)
{
if (i!=0x12345678) panic("spatne i=0x%x",i);
}
panic("cyklus skoncil i=0x%x",i);
hodi jeden nebo druhy panic podle toho, kdy se zavola preruseni
cely cyklus je par instrukci, kde v0 drzi konstantu 0x12345678 a v1 se furt nacita z &i
a tyto se porovnavaji furt dokola
pak nastane preruseni, nastane vyse uvedena situace, prepise se kontext, z v1 se stane 0x1
a porovnani v0=0x12345678 s v1=0x1 vede do panicu
postrehy:
1) kompilace na -O0 chybu nedela -> kod vypada uplne jinak, k chybe nedochazi
2) pokud kod1 prepisu na
kod3:
funkceX(argument)
{
... kod ...
debugovaci_vypis(arg1,format);
if (nesmyslna_podminka) STOPKA;
return;
}
tak k chybe taky nedojde (protoze z debugovaciho vypisu se skoci jeste zpet do funkceX)
pro uplnost dodam, ze kod debugovaciho vypisu vypada nejak takto:
void debugovaci_vypis(unsigned int arg1,char *format,...)
{
if ((arg1 & KONSTANTA) == 0) return;
va_list args;
va_start(args,format);
printk_internal(format,args);
va_end(args);
return;
}
Snazne prosim kohokoliv, kdo mi umi rict, co jsem udelal spatne, co s tim?
Pokud chyba neni na moji strane, je spravne reseni dat kompilatoru -O0 ?
Dekuji
DM
More information about the NSWI004
mailing list