[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