[C/ASM] Wstawka w Asm - implementacja printf

0

Witam,
postanowiłem sobie, że zrobię taką prymitywną implementację funkcji printf w Asemblerze. Napisałem program główny w C a własną implementację printf() w wstawce w NASMie. Oczywiście nic nie działa (no jakże by inaczej). Program jest kompilowany GCC pod Linuksem. Oto i kod źródłowy:
Plik testuj.c

#include <stdio.h>

extern  void myPrintF(char * string, unsigned int n);

int main()
{
	char msg[20] = {0};
	unsigned int dlugosc = 20;
	printf("Przykladowe dzialanie wstawki asemblera.\nPodaj napis do wyswietlenia:");
	scanf("%19s", msg);
	myPrintF(msg, dlugosc);
	printf("Po pracy wstawki koncze dzialanie\n");

	return 0;
}

a teraz plik Test.asm (ten z funkcją):

;UŻYCIE:
;[EBP + 4]  = adres powrotu z funcji (tego nie ruszać)
;[EBP + 8]  = wskaźnik na tablicę znaków
;[EBP + 12] = ilość znaków do wypisania 
global myPrintF
section .text
myPrintF:
	mov  ebp,esp
	push ebp

	mov ecx, [ebp + 12]
	mov dword [msgOff], ecx
	xor edi, edi
	mov esi, 8
	
kopiowanie:
	mov bl, byte [ebp + esi]	
	mov byte [msg + edi], bl	
	inc esi	
	inc edi
	loop kopiowanie

wyswietl:
	mov eax, 4
	mov ebx, 1
	mov ecx, msg
	mov edx, [msgOff]
	int 80h
	
koniec:
	pop ebp
	mov esp, ebp
	ret
section .data
msg: times 40	db	0
msgOff		equ	$ - msg

Chciałem zrobić tak, że funkcja tworzy wewnętrzny bufor na 40 bajtów, i kopiuje z podanego na stosie adresu cały ciąg do wewnętrznego bufora, jednak gdy to wszystko skompiluję i zlinkuje przy pomocy takie pliku Makefile

testuj: test.o testuj.o
	gcc test.o testuj.o -o testuj
testuj.o: testuj.c
	gcc -c testuj.c -o testuj.o
test.o: test.asm
	nasm -f elf test.asm -o test.o

To mam wybitnie nieciekawy wynik w konsoli:

bartek@bartek-laptop:~$ ./testuj 
Przykladowe dzialanie wstawki asemblera.
Podaj napis do wyswietlenia:blablabla
Segmentation fault (core dumped)

Kompletnie nie mam zielonego pojęcia co jest błędne, bo jak na moje oko samo kopiowanie danych i ich wyświetlenie w test.asm jest bardzo dobrze napisane i nie widzę żadnych błędów.

0

0.Jestem cienki z asma ;-)
1.Po wyjściu z myPrintF w ebp i esp będzie taka sama wartość - to chyba nie było zamierzone...
2.Z pętli kopiującej program nigdy nie wyjdzie, bo nie modyfikujesz CX. Daj jakieś dec cx po inc edi i powinno być dobrze.

0

A mnie zastanawia czemu nie stosuje gotowych rozwiązań. Z tego co pamiętam to można włączyć automatyczne obudowywanie funkcji tak by sama trzymała format funkcji C lub Pascala. Już z 10 lat nic nie robiłem w asemblerze więc dokładnie nie pamiętam jak to się robiło.
Co do pętli to jest dobrze, bo loop poza skokiem warunkowym zmniejsza ECX (CX) o 1.

0
cyriel napisał(a)

2.Z pętli kopiującej program nigdy nie wyjdzie, bo nie modyfikujesz CX. Daj jakieś dec cx po inc edi i powinno być dobrze.

Instrukcja loop dekrementuje wartość ecx.

mov  ebp,esp
        push ebp

Powinno być: odłóż stare ebp, a potem ustaw ebp równe esp. Czyli odwrotnie. Może coś jeszcze, ale dalej się nie wczytywałem.

0

Z tego co pamiętam są specjalne rozkazy (począwszy od 80286), które to robią hurtem: na wejście enter i na wyjście leave.

0

A mama zawsze mówiła "Używaj ENTER i LEAVE, bo Ci się pomyli z tymi ramkami stosu". No trzeba było posłuchać. Jak w ogóle nie robię kopiowania, czyli to wszystko wywalam a daję od razu do wywołania sys_write tak:

mov eax, 4
mov ebx, 1
mov ecx, [ebp +  8 ]
mov edx, [ebp + 12]
int 80h

I nie działa. Wywala mi tutaj naruszenie stosu i komunikaty debugujące (smashing stack detected bodajże). I nie wiem co jest źle. Przecież wywołanie systemowe sys_write chce w ECX adres ciągu, no to dostaje go w [EBP + 8] (sprawdzałem char * zajmuje 4 bajty) a potem ma ilość znaków w [EBP + 12] i nic. Program wykrzacza się. Bo wcale nie chce mi się robić kopiowania ciągu znaków do wewnętrznego bufora.

0

Oj ludzie ludzie... dlaczego ludzie bez pojęcia o assemblerze tak usilnie starają się nim zajmować?

cyriel napisał(a)

1.Po wyjściu z myPrintF w ebp i esp będzie taka sama wartość - to chyba nie było zamierzone...
2.Z pętli kopiującej program nigdy nie wyjdzie, bo nie modyfikujesz CX. Daj jakieś dec cx po inc edi i powinno być dobrze.

Ad 1. to się nazywa ramka stosu, o ile esp z każdym wrzuceniem na stos się zmienia o tyle podstawa ramki stosu w ebp jest stała, używa się tego m.in. ze wzdlędu na swobodne adresowanie zmiennych lokalnych i argumentów oraz ew. zwijanie stosu po wywołaniach w cdecl.
Ad 2. instrukcja loop dekrementuje ecx i wykonuje skok póki nie będzie == 0.

MarekR22 napisał(a)

Z tego co pamiętam to można włączyć automatyczne obudowywanie funkcji tak by sama trzymała format funkcji C lub Pascala. Już z 10 lat nic nie robiłem w asemblerze więc dokładnie nie pamiętam jak to się robiło
.
Masz na myśli makro proc itd.

Burżuj napisał(a)

Powinno być: odłóż stare ebp, a potem ustaw ebp równe esp. Czyli odwrotnie. Może coś jeszcze, ale dalej się nie wczytywałem.

Usuwanie ramki stosu i przywracania starego kontekstu też jest tak zwalone...

doles2 napisał(a)

A mama zawsze mówiła "Używaj ENTER i LEAVE, bo Ci się pomyli z tymi ramkami stosu". No trzeba było posłuchać.
To Ci mama źle mówiła, o ile leave się jeszcze faktycznie często stosuje to używać enter się nie używa od wieków, Intel nie zaleca stosowania tego starocia.
Dobra, bierzemy się za problemy z pierwszego postu:

postanowiłem sobie, że zrobię taką prymitywną implementację funkcji printf w Asemblerze.
Najważniejszą cechą printf jest to, że formatuje tekst, samo wypisywanie to skopiowanego bufora to raczej nie printf.

doles2 napisał(a)
        mov  ebp,esp
        push ebp

Powinno być dokładnie odwrotnie - zachowujesz oryginalną wartość ebp i potem umieszczasz w ebp podstawę własnej ramki stosu; swoją drogą nie wiem po kiego grzyba tutaj tworzysz tamkę stosu?

doles2 napisał(a)
mov dword [msgOff], ecx
;...
msgOff                equ        $ - msg

msgOff równy jest rozmiarowi bufora - ma wartość 20 a Ty używasz tego jako adresu...

(int*)20 = coś;

Sensowne?

doles2 napisał(a)
kopiowanie:
        mov bl, byte [ebp + esi]        
        mov byte [msg + edi], bl        
        inc esi        
        inc edi
        loop kopiowanie

Możesz mi powiedzieć co tutaj kopiujesz? Kopiujesz do bufora argumenty a nie otrzymany string. Kolejna sprawa - to już nie 286 żeby być zmuszonym do inteksowania za pomocą esi i edi... a właśnie, gdzie zachowujesz ich oryginalne wartości? Funkja ma z definicji nie modyfikować zawnętrznie ramki stosu oraz rejestrów ebx, esi i edi - samo to spowoduje wysypanie się programu, która to już rzecz, trzecia?

doles2 napisał(a)
        mov edx, [msgOff]

I znowu, zapisujesz pod adres będący dla Ciebie nie tyle adresem co stałym rozmiarem bufora..

doles2 napisał(a)
section .data
msg: times 40        db        0

Wszystko pięknie tylko dlaczego jest to bufor statyczny? Dlaczego rozmiaru nie sprawdzasz otrzymanego stringa nie sprawdzasz?

Bo wcale nie chce mi się robić kopiowania ciągu znaków do wewnętrznego bufora.

Żartujesz?

zuo:
        mov     edx, [esp+4]
        mov     ecx, [esp+8]
        mov     ebx, 1
        mov     eax, 4
        int     80h
        retn

albo ze zbędną w tym wypadku ramką stosu /żebyś wiedział jak to ma wyglądać w ogóle.../:

zuo:
        push    ebp
        mov     ebp, esp
        mov     edx, [ebp+8]
        mov     ecx, [ebp+12]
        mov     ebx, 1
        mov     eax, 4
        int     80h
        leave
        retn

Weź się zastanów czy potrzebujesz używać assemblera - assembler to ostateczność, sięga się po niego gdy nie da się rozwiązać problemu bez jego użycia.

p.s. /niemal/ wszystkie implementacje printf były pisane w C, nie w asm...

0

Nie wiem jak Ty ale ja nie urodziłem się od razu boskim programistą. Śmiej się i krytuj, ale uczę się Asemblera i mam prawo popełniać błędy. Jedyne czego się mogę powstydzić to ramka stosu bo nie raz jej używałem a tutaj taki dziecinny błąd. Może kiedyś dorobię formatowanie tekstu, na razie chciałem samo kopiowanie. Niemniej uwagi się przydały, program poprawiłem i nawet działa :)
PS: A Asemblera mogę sobie używać do czego mi się tylko spodoba, mam do tego święte prawo wolnego wyboru. To już nawet nie mogę się go uczyć ?

0

Nie krytykowałem tylko wymieniłem wasze błędy; o użyciu assemblera mowiłem w kategoriach programowania, nie zabawy - nie odbieraj tego jako bezpodstawnej krytyki. Gdzie się naśmiewam, hm? Assemblera w prawadziwym oprogramowaniu nie używa się wcale chyba, że jest bezwzglęnie konieczny - koszty rozwoju i konserwacji projektu /o jego złożoności nie wspominająć/ rosną błyskawicznie. Aktualnie trzeba mieć niezłą wiedzę i ogromne doświadczenie aby z palca napisać lepszy kod niż zrobiłby to kompilator /a często jest tak, że nie ma w czym kompilatora przebić po prostu/. Znajomość assemblera jest cenna owszem, ale należy pamiętać o granicach jego użycia. Zresztą, za kilka lat po przejsciu na nowe technologie assembler stanie się jeszcze bardziej niszowym rozwiązaniem, zbędnym dla każdego normalnego programisty.

0

... a na wielu uczelniach ciagle jeszcze ucza programowania mikroprocesorow tylko i wylacznie w asm...

0

A to deus przepraszam bo Twoja wypowiedź brzmiała nieco agresywnie, zrozumiałem ją jako rodzaj jakiegoś osobistego ataku. Mniejsza z tym. Jak bardzo niszowy by ten język nie był to on na prawdę mi się podoba, nie podam przyczyny dlaczego bowiem sam jej nie znam. Nie zrozum mnie źle, śniadania sobie nie robię w Asemblerze, nie wszystko zaraz muszę w nim próbować kodować. Ale zobaczysz kiedyś jeszcze będę dobry w tym języku, potrzeba mi tylko dużo czasu :)...

//q: w takim razie zycze Ci dorownania deusowi :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1