[assembler] Problem z warunkiem

0

Witam,
Panowie, mam następujący problem. Piszę program do zamiany liczb z jednego systemu na drugi, który zamierzam wywoływać w ten sposób, np. zamiana -d -b 123, co oznaczałoby : zamień liczbę 123 z dziesiętnego na binarny. Problem pojawia się już przy sprawdzeniu opcji, a konkretnie : czy argument programu zaczyna się od '-'. Jeśli nie, to chcę wyświetlić użytkownikowi poprawny sposób użycia (tak samo później z następnym argumentem, ale problem jest już przy pierwszym). Oto kod :

## definicje stalych
READ = 3
WRITE = 4
OPEN = 5
CLOSE = 6

STDIN = 0
STDOUT = 1

EXIT = 1
SYSCALL = 0x80 
EXIT_SUCCES = 0
## koniec definicji stalych

.data
  argc: .byte 0
  argcDl = . - argc

  arg1: .skip 3
  arg1Dl = . - arg1

  nap1: .asciz "----------------- Program konwertujacy z/na popularne systemy liczbowe -----------------\n"
  nap1Dl = . - nap1

  nap2: .asciz "\tPrzyklad uzycia :\tconv <system wejsciowy> <system wyjsciowy> <liczba> \n"
  nap2Dl = . - nap2

  nap3: .asciz "\tObslugiwane systemy:
  			\t-b binarny
  			\t-o osemkowy
  			\t-d dziesietny
  			\t-h szesnastkowy\n\n"
  nap3Dl = . - nap3

  nap4: .asciz "binarnie: "
  nap4Dl = . - nap4

  nap5: .asciz "osemkowo: "
  nap5Dl = . - nap5

  nap6: .asciz "dziesietnie: "
  nap6Dl = . - nap6

  nap7: .asciz "szesnastkowo: "
  nap7Dl = . - nap7

  mysln: .asciz "Myslnik.\n"
  myslnDl = . - mysln
  pom: .skip 1
  // makro do wypisywania tekstu
  .macro wypisz tekst, dlugosc
    mov $WRITE, %eax
    mov $STDOUT, %ebx
    mov \tekst, %ecx
    mov \dlugosc, %edx
    int $SYSCALL
  .endm
  // koniec makra

  znak: .byte 0

.text

.global _start
_start:
  push %ebp
  mov %esp, %ebp
  
  // jesli liczba argumentow jest != 4 to pokaz uzycie i wyjdz
  mov 4(%ebp), %eax
  //mov %eax, argc
  cmp $4, %eax
  jne uzycie

  mov 12(%ebp), %eax
  mov %eax, arg1
  xor %edi, %edi
  // sprawdzamy, czy pierwszy znak opcji to myslnik, a jesli nie, to wyswietlamy
  // prawidlowy sposob uzycia
  mov arg1(, %edi, 1), %eax
  mov %eax, znak
  cmpb $'-', znak  // <-- tu pojawia sie problem
  jne uzycie
  // tu jakies dalsze dzialanie, np. sprawdzenie nastepnego argumentu, itp.
  //wyjscie
  jmp koniec

// pokazuje prawidlowe uzycie programu
uzycie:
  wypisz $nap1, $nap1Dl
  wypisz $nap2, $nap2Dl
  wypisz $nap3, $nap3Dl
  
// procedura konczaca
koniec:
  mov %ebp, %esp
  pop %ebp
  mov $EXIT, %eax
  mov $EXIT_SUCCES, %ebx
  int $SYSCALL

Jak widać, piszę w składni AT&T (z przymusu). W zaznaczonej linii, program testuje warunek, jednak nie ustawia flagi ZF - krótko mówiąc, nie działa tak, jak powinien (sprawdzalem gdb). Ma ktoś pomysł, dlaczego tak się dzieje ?
Byłbym wdzięczny za pomoc, bo męczę się z tym już którąś godzinę i nie mogę dojśc, dlaczego.

Pozdrawiam,
Zbychu

0
  1. Dziwna składnia...

1.5. Głupie pytanie ale skąd wziąłeś ten znak? Nic do niego nie przesyłasz, a odczytujesz. W tej składni (wtf?) parametry MOV są odwrócone (bo na to wygląda...)?

  1. co oznacza to '$' przed '-'? Może na wszelki wypadek użyj odpowiednika '-' czyli 2D (na 99%)
0

Witam,
dzięki za szybką odpowiedź. Jest to składnia assemblera GNU Assemblera (piszę pod Linuxem). Jest trochę dziwna, ale jak mówiłem, to wymóg, którego nie mogę zmienić.
Znak biorę stąd :

xor %edi, %edi
mov arg1(, %edi, 1), %eax
mov %eax, znak

czyli : kopiuję 1 bajt, licząc od 0, ze zmiennej arg1 (konkretniej : z adresu, na jaki ta zmienna wskazuje).
Sprawdzalem debuggerem, że "znak" jest równy '-' (przy takim wywołaniu, jakie podałem za przykładowe), więc teoretycznie wszystko gra.
Co do znaku '$', to GAS wymaga go przy operowaniu na stałych (w tym przypadku jakby stałej znakowej), inaczej się nie kompiluje.

0

Co do znaku chodziło mi o to że "normalne" asemblery interpretują mov jako MOV gdzie, skąd; a twój najwyraźniej jako MOV skąd, gdzie;. Mniejsza z tym.

Zresztą skoro sprawdzasz pod debugerem to spekulacje nt. ustawienia znaku nie mają sensu.

To kolejna propozycja (a kto wie, może i dobra): porównujesz eax, czyli używasz aż 4 bajtów! Znak zajmuje 1 bajt. Czyli może być tak:

Eax: 01010010 00110011 00100100 (bity znaku)
Znak: 00100010 00100100 01010010 (bity znaku)

i nie są równe. Sprawdź.

0

Składnia jest odwrócona, to prawda, czyli kolejność operandów jest zamieniona, to jedna z tych głównych różnic GAS vs. wszystkie-inne-assemblery :)

Co do sprawdzania, to wiem, co masz na myśli i też tak sądziłem, jednak spójrz :

cmpb $'-', znak

Chodzi o to porównanie. cmpb -> to 'b' oznacza, że porównuję tylko bajt, a nie 4 bajty. W dodatku, porównuję ten bajt (tu konkretnie '-', czyli jakby 45 -> kod ASCII) ze zmienną "znak", w której również znajduje się liczba 45 (bo wywołaliśmy program przez : program -d -b 123). Sprawdzałem gdb i powinno śmigać, jednak tak nie jest. Po prostu, nie ustawia się flaga ZF i kompletnie nie mam pojęcia dlaczego.
Najgorsze, że męczę się z tym już naprawdę od wielu godzin i dalej nie widzę rozwiązania, a musi być ono tuż obok, przynajmniej tak sądzę.

0

Heh, wygląda na to że będzie musiał przyjść ktoś bardziej zaawansowany w asemblerze (pełno tu takich) niż ja żeby ci pomóc :/
Jeśli chodzi o to cmpb to wybacz, po prostu nie zauważyłem.

Jeszcze 2 idiotyczne pytania (raczej nie pomogę, ale może chociaż naprowadzę)

  • cmp nie zmienia flagi czy zmienia, ale 'odwrotnie'?
  • (ok, jedno, to pytanie było głupie)
0

Witam,
spoko, i tak cieszę się, że ktoś w ogóle stara mi się pomóc, bo jak mówiłem, jestem już lekko zdesperowany.
Co zaś się tyczy Twojego pytania :
przed wykonaniem linijki

cmpb $'-', znak

stan moich rejestrów wygląda tak :

eax            0xbffff65b	-1073744293
ecx            0x0	0
edx            0x0	0
ebx            0x0	0
esp            0xbffff4bc	0xbffff4bc
ebp            0xbffff4bc	0xbffff4bc
esi            0x0	0
edi            0x0	0
eip            0x8048095	0x8048095 <_start+33>
eflags         0x200246	[ PF ZF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x0	0

zaś, po jej wykonaniu, wygląda tak :

eax            0xbffff65b	-1073744293
ecx            0x0	0
edx            0x0	0
ebx            0x0	0
esp            0xbffff4bc	0xbffff4bc
ebp            0xbffff4bc	0xbffff4bc
esi            0x0	0
edi            0x0	0
eip            0x804809c	0x804809c <_start+40>
eflags         0x200296	[ PF AF SF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x0	0

czyli, mówiąc krótko, cmpb nie ustawia mi flagi ZF. Dodam, że w pierwszym "zrzucie" flaga jest ustawiona - bo parę linijek wcześniej dokonuję porównania (liczby 4 z liczbą argumentów "argc") i tam wszystko śmiga, tutaj nie.

Proszę o pomoc, bo sam, niestety, nie mam pojęcia.

Pozdrawiam,
Zbychu

0

A spójrz co a AL siedzi? Bo nijak mi wartości '-' nie przypomina, masz tam jakieś '5b', nie 0x2d...

0

Witam,
nie do końca wiem, co masz na myśli. Chodzi Ci o część EAX ? Pamiętaj, że porównanie wygląda tak

cmpb $'-', znak

więc moim zdaniem wartość AL nie ma tu nic do rzeczy, no chyba, że się mylę, bo w assemblerze stawiam pierwsze kroki.
Jednak, jak pisałem, chodzi o to, że do momentu wykonania tej linijki wszystko jest OK, tj. w zmiennej "znak" znajduje się wartość '-' -> 45 (można zresztą sprawdzić, że w EAX znajduje się w tym momencie adres zmiennej "znak", co akurat nie ma raczej znaczenia, ale to à propos Twojego pytania), więc jak napisałem powyżej, powinno działać.
Powinno, ale jednak coś nie trybi, w dodatku, wyświetla jakiś dziwny "hasz" w pierwsze linijce programu (tej z wieloma znakami ---).
Kompletnie tego nie rozumiem, proszę jednak o dalsze sugestie, bo zależy mi na napisaniu tego programu.

Zbychu

0
Zbigniew napisał(a)

nie do końca wiem, co masz na myśli. Chodzi Ci o część EAX ? Pamiętaj, że porównanie wygląda tak

cmpb $'-', znak

więc moim zdaniem wartość AL nie ma tu nic do rzeczy, no chyba, że się mylę, bo w assemblerze stawiam pierwsze kroki.
Jednak, jak pisałem, chodzi o to, że do momentu wykonania tej linijki wszystko jest OK, tj. w zmiennej "znak" znajduje się wartość '-' -> 45 (można zresztą sprawdzić, że w EAX znajduje się w tym momencie adres zmiennej "znak", co akurat nie ma raczej znaczenia, ale to à propos Twojego pytania), więc jak napisałem powyżej, powinno działać.

  mov %eax, znak
  cmpb $'-', znak  // <;-- tu pojawia sie problem

To w końcu adres czy wartość, hm? W takim układzie to porównujesz '-' z częścią adresu znaku, nie z wartością... Szczerze mówiąc nie chcę sobie przypominać AT&T, zaraz to skompiluję i pociągnę disasmem dla pewności...

0

Próba kompilacji as daje (na tej linii z feralnym porównaniem)

zuozuo.s:82: Error: bad expression
0

Witam,
prawdę mówiąc, to już sam nie wiem.
Z tego co rozumiem, "znak" oznacza po prostu zmienną o rozmiarze jednego bajta, zainicjalizowaną wartością 0, zgodnie z deklaracją

znak: .byte 0

Jak mówiłem, gdb przy poleceniu
x/1bc znak
zwraca wartość

(gdb) x /1bc znak
0xbffff65b:	45 '-'

więc niby "znak" rzeczywiście przechowuje po prostu kod ASCII znaku '-'.

Sam już nie wiem, o co w tym wszystkim chodzi. Byłbym Ci bardzo wdzięczny za pomoc, zresztą, komukolwiek :)

Zbychu

0

Hmm, no to dziwne, bo u mnie cały kod, który w pierwszy poście zacytowałem, się kompiluje, dostaje tylko warningi, że przełamałem linię (w sekcji .data) i tyle.
Na pewno dobrze sprawdzałeś ? Podeślę może jeszcze raz kod, który na pewno się u mnie kompiluje (Ubuntu 10.4, AS 2.20.1)
http://wklej.org/id/328749/

Skopiuj może ten, i sprawdź jeszcze raz jeśli mógłbyś.

Zbychu

0

Z tego co ja pamietam o AT&T to ten dolar sluzy to wyciagania wartosci spod etykiet zmiennych i stalych, poniewaz poslugujac sie sama etykieta w przypadku oznaczenia jakiegos zarezerwowanego obszaru pamieci, sama etykieta jest adresem, a nie wartoscia.
Moge sie mylic, ale wydaje mi sie, ze powinnien byc tez dolar przed nazwa zmiennej.

0

Tak, sorry, wywaliłem ten lipny komentarz (//...) i się skompilowało... AT&T nie używałem od paru lat, pamiętam, że wspiera kilka form komentarzy...

Wynik disasma czymś sensownym:

.text:00000000 proc            _start near
.text:00000000
.text:00000000 arg_4           = dword ptr  0Ch
.text:00000000
.text:00000000                 push    ebp
.text:00000001                 mov     ebp, esp
.text:00000003                 mov     eax, [ebp+4]
.text:00000006                 cmp     eax, 4
.text:00000009                 jnz     short uzycie
.text:0000000B                 mov     eax, [ebp+arg_4]
.text:0000000E                 mov     [dword ptr arg1], eax
.text:00000013                 xor     edi, edi
.text:00000015                 mov     eax, [dword ptr arg1+edi]
.text:0000001C                 mov     [dword ptr znak], eax
.text:00000021                 cmp     [byte ptr ds:$znak], 2Dh ; '-'
.text:00000028                 jnz     short uzycie
.text:0000002A                 jmp     short koniec
.text:0000002C ; ---------------------------------------------------------------------------
.text:0000002C
.text:0000002C uzycie:                                 ; CODE XREF: _start+9j
.text:0000002C                                         ; _start+28j
.text:0000002C                 mov     eax, 4
.text:00000031                 mov     ebx, 1
.text:00000036                 mov     ecx, offset nap1 ; "----------------- Program konwertujacy "...
.text:0000003B                 mov     edx, 5Ah ; 'Z'
.text:00000040                 int     80h
.text:00000042                 mov     eax, 4
.text:00000047                 mov     ebx, 1
.text:0000004C                 mov     ecx, offset nap2 ; "\tPrzyklad uzycia :\tconv <system wejscio"...
.text:00000051                 mov     edx, 49h ; 'I'
.text:00000056                 int     80h
.text:00000058                 mov     eax, 4
.text:0000005D                 mov     ebx, 1
.text:00000062                 mov     ecx, offset nap3 ; "\tObslugiwane systemy:\n                 "...
.text:00000067                 mov     edx, 0B9h ; '¦'
.text:0000006C                 int     80h
.text:0000006E
.text:0000006E koniec:                                 ; CODE XREF: _start+2Aj
.text:0000006E                 mov     esp, ebp
.text:00000070                 pop     ebp
.text:00000071                 mov     eax, 1
.text:00000076                 mov     ebx, 0
.text:0000007B                 int     80h 

Po pierwsze to zapisujesz byte jako dword, nie byte (chociaż to wielkiego wpływu nie ma). Po drugie co to za ebp+4 na początku? Tam znajduje się adres powrotu, pierwszy argument to ebp+8, drugi+12... Potem zaś odczytujesz adres argumentu i zapisujesz do 'znak' zamiast jego wartości, stąd dziwne wartości.

0

Witam,
dzięki Ci za zaangażowanie, jednak mam dwie wątpliwości :

  1. Piszesz, że zapisuję byte jako dword (czyli long, przekładając na dialekt GASa), tylko problem w tym, że nie wiem, w który miejscu się to dzieje, bo przecież zmienna "znak" jest u mnie typu byte.

  2. To ebp+4 wzięło się stąd :

Po wykonaniu typowego prologu do funkcji (czyli push ebp / mov ebp, esp), zmienna argc znajduje się w [ebp+4], wskaźniki do parametrów linii poleceń zaczynają się od [ebp+8] i idą w górę stosu, po nich jest wskaźnik zerowy i dalej w górę są wskaźniki do zmiennych środowiska, też zakończone wskaźnikiem zerowym.

vide : http://rudy.mif.pg.gda.pl/~bogdro/linux/linux12.html
Konkretnie : ebp+4 oznaczało liczbę argumentów i było mi potrzebne w pierwszym sprawdzeniu czy program został wywołany poprawnie. Następnie, ebp+8 to byłby pierwszy wskaźnik do stringów z linii poleceń — konkretnie, byłaby to nazwa programu, a chciałem sprawdzić realny pierwszy parametr, i dlatego dałem ebp+12.

Wybacz, że nie odpowiedziałem od razu — po prostu nie było mnie w domu.

Zbychu

0
Zbigniew napisał(a)
  1. Piszesz, że zapisuję byte jako dword (czyli long, przekładając na dialekt GASa), tylko problem w tym, że nie wiem, w który miejscu się to dzieje, bo przecież zmienna "znak" jest u mnie typu byte.

Widać w deadlistingu, który dałem:

.text:0000000E                 mov     [dword ptr arg1], eax
.text:00000013                 xor     edi, edi
.text:00000015                 mov     eax, [dword ptr arg1+edi]
.text:0000001C                 mov     [dword ptr znak], eax

u Ciebie w kodzie:

  mov %eax, arg1
  xor %edi, %edi
  // sprawdzamy, czy pierwszy znak opcji to myslnik, a jesli nie, to wyswietlamy
  // prawidlowy sposob uzycia
  mov arg1(, %edi, 1), %eax
  mov %eax, znak

Widzisz, Ty operujesz na eax, to jest dword, niższa połowa eax to ax (word), niższa połowa ax to al(bajt).

To w kursie jest błąd:

  • najpierw w [esp] jest adres powrotu, potem pierwszy argument w [esp+4];
  • po push ebp w [esp] jest oryginalna wartość ebp na potrzeby ramki stosu, potem w [esp+4] adres powrotu, następnie w [esp+8] pierwszy argument;
  • zrównujesz ebp z esp, wtedy pierwszy argument to [ebp+8], więc drugi to [ebp+12] itd.
0

Witam,
dzięki za info, choć sugerowałem się również tym
http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html#stack
i z tego, co tu widać, to wygląda, że informacje z wspomnianego kursu i to z linku się pokrywają, tj. w ebp+4 jest ilość parametrów, w ebp+8 nazwa programu, i ebp+12 pierwszy argument, itp.
Zresztą, pokrywa się to również z wynikiem debuggera, bo po skopiowaniu z ebp+12 pierwszego bajtu do "znak" zawsze znajduje się tam pierwszy znak pierwszego argumentu.
Nie mam oczywiście na myśli, że się mylisz, tylko być może to zależy po prostu skąd liczymy, jednak dalej nie rozwiązuje to problemu (albo ja przynajmniej nadal nie widzę rozwiązania).

Czy mógłbyś wobec tego zaproponować, jak sprawić, żeby ten kod działał ? Byłbym wdzięczny, bo z assemblerem to dopiero od niedawna walczę :)

Zbychu

0

Dodam jeszcze tylko, bo nie mogę edytować, że co do zawartości kursu i wyniku debuggera, to jeszcze pokrywały by się informacje stąd
http://download.savannah.gnu.org/releases-noredirect/pgubook/ProgrammingGroundUp-1-0-booksize.pdf
z rozdziału "All about functions".

Mam totalny mętlik od tego. :)

Zbychu

0

Hm, dobra, sorry, nie doczytałem, że pod tym śmiesznym systemem operacyjnym (Linux) entry point programu nie jest funkcją, nie ma na stosie adresu powrotu... Jak zwykle linuksiarze odstawiają cholera wie co, całkowicie o tym absurdzie zapomniałem.

0

Nie gniewam się :)

Masz jednak jakiś pomysł, jak to poprawić ? Bo najgorsze jest to, że wszystko powinno działać, te kilka śmiesznych linijek blokuje mi dalsze kodowanie, a zupełnie już nie wiem dlaczego.

Zbychu

0

Ekhm, trochę się machnąłem, tak:

_start:
        pushl   %ebp            # jak tak bardzo chcesz miec ramke stosu
        movl    %ebp, %ebp      # tutaj jest zbedna

        cmpl    $4, 4(%ebp)     # sprawdzamy pierwszy argument
        jne     uzycie

        movl    12(%ebp), %eax   # pobieramy argv[1], durne linukssące ułożenie argumentów, do dupy nie podobne
        cmpb    $'-', (%eax)
        jne     koniec
0

Witam,
dzięki, niestety jednak, otrzymuję "Segmentation fault"

Nie sądziłem, że tak, zdawało by się, banalna rzecz mnie na tak długi czas zatrzyma. Nie mam już chyba pomysłów.

Zbychu

0

Wygląda na to, że działa :)

Dzięki Ci wielkie, obawiałem się, że już nic się z tym nie da zrobić :)
Będę jednak pewnie potrzebował jeszcze pomocy przy tym, jednak dziękuję za pomoc dotychczasową :)

Pozdrawiam,
Zbychu

PS. Pozwolicie, mam nadzieję, że resztę pytań również zapiszę w tym wątku, żeby już niepotrzebnie zaczynać drugiego.

0

Mówiłem że przyjdzie deus i coś wymyśli ;)

PS. Pozwolicie, mam nadzieję, że resztę pytań również zapiszę w tym wątku, żeby już niepotrzebnie zaczynać drugiego.

Nie radzę... Stwórz lepiej nowy wątek - jeśli ktoś będzie chciał odpowiedzieć to nie będzie musiał czytać 3-stronicowego, przeterminowanego kontekstu

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.