[NASM] Tworzenie procedur wywołujących procedury libc

0

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:

  1. 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.

  1. 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,

0

Nie mam duzo czasu, wiec tylko pierwszy kod:

;
; print_string: prints string which pointer is stored in EAX
;
print_string: ; printf("%s", EAX)
  push ebp ; set up stack frame
  mov ebp, esp

Po tym esp wskazuje na miejsce na stosie na ktorym lezy ebp.

  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

Teraz niby wrzucasz 2 wartosci, ale potem wyrownujesz stos, wiec esp dalej wskazuje na ebp.

  mov esp, ebp ; takedown stack frame
  pop ebp

Zdjales ebp, zmiennych lokalnych nie bylo, wszystko ok. Esp wskazuje na adres powrotny.

  pop eax
ret
</asm>
No wlasnie... Adres powrotny zostaje zdjety do eax i nie wiadomo gdzie wracasz ;)
0

Dzięki. Dodatkowo aby procedurka print_string działała w pętli wartość rejestru ecx jest wkładana i zabierana ze stosu.

Pozdrawiam,

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