2008-12-07:

LOOP vs. domyślny assembler na Mac OS X

assembler:macosx:easy
Panowie z Apple chyba lubią stare toolsy. Ostatnio grzebiąc z Unavowed'em przy pewnym projekcie (o którym napisze przy innej okazji), a konkretniej, próbując ów projekt przeportować na Mac OS X'a, natrafiliśmy na pewną przeszkodę która przedstawiła się nam jako Apple Inc version cctools-698.1~1, GNU assembler version 1.38. Jest to defaultowy assembler (as) używany na (aktualnym) Mac OS X, i najwyraźniej pamięta on jeszcze czasy gdy na chleb mówiłem 'bep' a na muchy 'tapty' (oby to 1.38 oznaczało tylko inną numeracje, ponieważ - dla porównania, obecna wersja wg. wiki to 2.19, mój MinGW twierdzi że ma wersje 2.18.50, w roku 2000 była wydana wersja 2.11, a w obecnym changelogu as najstarszy wpis dotyczy wersji 1.93.01).

Oczywiście w starym sofcie nie ma totalnie nic złego. Oprócz niepoprawionych błędów, i brakującej funkcjonalności.

Pomijając już problemy z .fill, .string czy .ascii, czy też aliasami pewnych instrukcji, to najbardziej zaskoczył nas LOOP oraz LOOPNE.

Otóż okazało się że owszem, ten mnemonik jest obsługiwany. I w małych programikach działa nawet poprawnie. Niestety, przy większym assemblerowym projekcie okazało się że argument loop jest źle tłumaczony. Rzućmy okiem na przykład.

Źródło (składnia AT&T):
jump_f09d4:
 mov    (%edi),%al
 mov    (%eax,%esi,1),%bl
 mov    %bl,(%edi)
 add    $0x1,%edi
 loop   jump_f09d4
 jmp    jump_f0a0b


Plik wynikowy:
(gdb) x/10i $eip
0xe318a <jump_f09d4>:    mov    (%edi),%al
0xe318c <jump_f09d4+2>:  mov    (%eax,%esi,1),%bl
0xe318f <jump_f09d4+5>:  mov    %bl,(%edi)
0xe3191 <jump_f09d4+7>:  add    $0x1,%edi
0xe3194 <jump_f09d4+10>: loop   0xe3187 <jump_f09bb+26>
0xe3196 <jump_f09d4+12>: jmp    0xe31cb <jump_f0a0b>


Jak widać, loop w pliku wynikowym powinien skakać do 0xe318a, a skacze do 0xe3187, czyli 3 bajty za wcześnie. I co ciekawe, zawsze były to właśnie 3 bajty. Wygląda na to, że assembler stwierdził że instrukcja loop nagle zamiast opcode + 1 bajt argumentu (2 bajty), ma opcode + 4 bajty argumentu (5 bajtów), i traktował pozycje wyjściową skoku jako eip+5 zamiast prawidłowo eip+2. No i się zrobiły 3 bajty różnicy. LOOPNE zachowywało się tak samo. Natomiast LOOPE nie mieliśmy nigdzie w kodzie ;>

Problem został rozwiązany poprzez zastąpienie LOOP kodem równoważnym:
 sub $0x1,%ecx
 jnz ETYKIETA

Natomiast LOOPNE zostało zastąpione przez:
 jz 0f
 sub $0x1,%ecx
 jnz ETYKIETA
 jmp 1f
0:sub $0x1,%ecx
1:


No i wszystko nagle zaczęło działać.

Oby Apple jednak kiedyś zdecydowało się na upgrade assemblera ;D

Comments:

2008-12-11 16:03:15 = arvind
{
I've like no idea coś Ty tu napisał :)
ale postanowiłem się przywitać, pamiętasz mnie jeszcze? :)
}
2008-12-12 01:51:17 = Gynvael Coldwind
{
Hii arvind ;>
Sure że Cię pamiętam ;>>>
Kurcze, nawet nie wiedziałem że masz nowego bloga, i to widzę że bardzo ciekawego, super ;> RSS++
Thx za odwiedziny ;>
}

Add a comment:

Nick:
URL (optional):
Math captcha: 8 ∗ 9 + 8 =