Witam,
Chciałbym napisać bibliotekę przydatnych funkcji wejścia / wyjścia, dzięki której zaoszczędzę czas w przyszłości. Przykładowo arcyprosta procedura wyświetlania nowej linii korzystająca tylko z funkcji systemowych zdaje się działać bez zarzutu, testowałem w pętli, aby sprawdzić czy nie zmienia wartości rejestru ECX.
Kod programu:
section .text ; begin of the code section
global main ; ld needs that label
; constants for Linux@x86
%define sys_exit 1
%define sys_read 3
%define sys_write 4
%define stdin 0
%define stdout 1
; external libc functions
extern printf
extern scanf
;
; main program
;
main:
mov ecx, 10 ; do 10 times
test_loop:
call print_nl ; prints new line
loop test_loop ; jump to petla if ECX != 0
call print_nl
; mov eax, test_string
; call print_string
mov esi, test_string
mov edi, test_string_len
call print_string_2
mov esi, test_string ; string to print (by print_string_2)
mov edi, test_string_len ; length of string to print (by print_string_2)
mov ecx, 10 ; do 10 times
test_loop1:
call print_nl
call print_string_2
loop test_loop1 ; jump to petla if ECX != 0
mov eax, 0
ret ; main returns to OS
;
; end main program
;
;
; library procedures
;
print_nl: ; seems to be OK
; put registers on stack
push edx
push ecx
push ebx
push eax
push ebp ; set up stack frame
mov ebp, esp
mov eax, sys_write
mov ebx, stdout
mov ecx, char_nl
mov edx, 1 ; length of 0ah
int 80h ; syscall
mov esp, ebp ; takedown stack frame
pop ebp
pop eax
pop ebx
pop ecx
pop edx
ret ; return to previous block
;
; print_string: prints string which pointer is stored in EAX
;
print_string: ; printf("%s", EAX)
push ebp ; set up stack frame
mov ebp, esp
;
; do: printf("%s", EAX)
;
push eax ; address of string to printf
push dword str_string ; address of: "%s", 0
call printf ; call C function
add esp, 8 ; pop stack 2 times 4 bytes
mov esp, ebp ; takedown stack frame
pop ebp
pop eax
ret
;
; print_string_2: prints string, which pointer is stored in ESI, length in EDI
;
print_string_2:
; put registers on stack
push edx
push ecx
push ebx
push eax
push ebp ; set up stack frame
mov ebp, esp
mov eax, sys_write
mov ebx, stdout
mov ecx, esi
mov edx, edi
int 80h ; call kernel
mov esp, ebp ; takedown stack frame
pop ebp
pop eax
pop ebx
pop ecx
pop edx
ret
section .bss ; uninitialized data section
a resw 2 ; long a - reserve 2 words (4 bytes)
; b resw 2 ; long b
; c resw 2 ; long c
section .data ; initialized data section
str_integer db "%d", 0
str_char db "%c", 0
str_string db "%s", 0
test_string db "Hello World!", 0ah, 0
test_string_len equ $-test_string
char_nl db 0ah
Problemy:
- Pierwszy pojawia się, gdy próbuję napisać procedurę print_string, która będzie wyświetlała char*, którego adres jest w rejestrze EAX korzystając z printf. Brakuje mi rozsądnego pomysłu na rozwiązanie tego problemu.
Na początek wykombinowałem sobie taką procedurę:
;
; print_string: prints string which pointer is stored in EAX
;
print_string: ; printf("%s", EAX)
push ebp ; set up stack frame
mov ebp, esp
;
; do: printf("%s", EAX)
;
push eax ; address of string to printf
push dword str_string ; address of: "%s", 0
call printf ; call C function
add esp, 8 ; pop stack 2 times 4 bytes
mov esp, ebp ; takedown stack frame
pop ebp
pop eax
ret
Jednak nie działa tzn. po pierwszym wywołaniu inne instrukcje przestają działać zgodnie z oczekiwaniem. Podejrzewam, że chodzi o modyfikowanie wartości rejestrów i nie przywracanie ich wartości.
Następnie próbowałem obejść problem:
; put registers on stack
push edx
push ecx
push ebx
push eax
push ebp ; set up stack frame
mov ebp, esp
mov eax, sys_write
mov ebx, stdout
mov ecx, esi
mov edx, edi
int 80h ; call kernel
mov esp, ebp ; takedown stack frame
pop ebp
pop eax
pop ebx
pop ecx
pop edx
Wywołuje się ją następująco, jest dłuższa w wywoływaniu, ale w przeciwieństwie do print_string nie "psuje" rejestrów.
mov esi, test_string
mov edi, test_string_len
call print_string_2
Jednak chciałbym wywoływać procedury C podobnie jak funkcje systemowe w celu szybkiego odczytywania / wczytywania danych konkretnego typu. Idealnie byłoby gdzieś skopiować zawartość rejestrów, ale stos się do tego nie nadaje. Mogę utworzyć kilka zmiennych dword (32 bit) w pamięci i tam zrobić kopie, ale jeśli nie trzeba to nie powinno się korzystać z pamięci (bo jest wolna w stosunku do rejestrów). Będę wdzięczny za wskazanie właściwej drogi.
- Zgodnie z tabelą: http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html funkcja sys_exit ma nr 1. Dlaczego wywołanie jej, w przypadku, gdy program korzysta wcześniej z bibliotek C wywołuje problemy? Jeżeli się jej pozbędę z końca programu i wstawię:
mov eax, 0
ret ; main returns to OS
to wszystko zaczyna działać jak należy, natomiast:
mov eax, sys_exit
mov ebx, 0
int 80h
powoduje na przykład nieprawidłowe wywoływanie funkcji z libc. Nie rozumiem dlaczego.
Pozdrawiam,