[NASM] dziwne zachowanie podczas wywołania printf

0

Witam,
Kod programu jest następujący:

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

; ************** ;
;  main program  ;
; ************** ;

main:
  mov eax, 5 ; EAX = 5
  call print_int ; print integer in EAX
  call print_nl ; newline

  mov eax, 0
  ret ; main returns to OS

;
; end main program
;


; ******************** ;
;  library procedures  ;
; ******************** ;
print_nl: ; printf("\n")
  ; 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_int: printf("%d", EAX)
;
print_int:
  push ebp ; set up stack frame
  mov ebp, esp

  ;
  ; do: printf("%d", EAX)
  ;
  push eax ; value of int to printf 
  push dword str_int ; address of: "%d", 0
  call printf ; call C function
  add esp, 8 ; pop stack 2 times 4 bytes

  mov esp, ebp ; takedown stack frame
  pop ebp

ret ; return to previous block

section .bss ; uninitialized data section
section .data ; initialized data section
  str_int db "%d", 0
  char_nl db 0ah

Problem: Dane wyjściowe nie są prezentowane we właściwy sposób.
5margor@xeon:~/programowanie/projekty/5nasmlib>

Spodziewany efekt:
5
margor@xeon:~/programowanie/projekty/5nasmlib>

Czytam mój kod i nie widzę błędu. Wychodzi na to, że ret nie wraca tam, gdzie powinno. Będę wdzięczny za podpowiedź. Jak najlepiej debugować problemy tego typu?

Pozdrawiam,

0

Mniej wiecej jest ok, choc nie wiem po co Ci te ramki, przeciez nie maja te funkcje argumentow, ani zmiennych lokalnych ?

mov eax, sys_write
mov ebx, stdout
mov ecx, char_nl
mov edx, 1 ; length of 0ah
int 80h ; syscall

Nad tym bym sie zastanowil, czy przypadkiem ecx nie potrzebuje adresu stringu zakonczonego 0 i czy przypadkiem jesli potrzebuje nie trzeba wtedy edx dac 2 ?

0

Rzeczywiście, dzięki za zwrócenie uwagi z tą zbędną ramką stosu, za dużo się naczytałem o procedurach C.
Zmodyfikowałem program do następującej postaci (dodanie znaku 0 na konieć 0ah oraz długość 2 w edx):

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

; ************** ;
;  main program  ;
; ************** ;

main:
  mov eax, 5 ; EAX = 5
  call print_int ; print integer in EAX
  call print_nl ; newline

  mov eax, 0
  ret ; main returns to OS

;
; end main program
;


; ******************** ;
;  library procedures  ;
; ******************** ;
print_nl: ; printf("\n")
  ; put registers on stack
  push edx
  push ecx
  push ebx
  push eax

  mov eax, sys_write
  mov ebx, stdout
  mov ecx, char_nl
  mov edx, 2 ; length of "\n\0"
  int 80h ; syscall

  pop eax
  pop ebx
  pop ecx
  pop edx
ret ; return to previous block

;
; print_int: printf("%d", EAX)
;
print_int:
  push ebp ; set up stack frame
  mov ebp, esp

  ;
  ; do: printf("%d", EAX)
  ;
  push eax ; value of int to printf
  push dword str_int ; address of: "%d", 0
  call printf ; call C function
  add esp, 8 ; pop stack 2 times 4 bytes

  mov esp, ebp ; takedown stack frame
  pop ebp

ret ; return to previous block

section .bss ; uninitialized data section
section .data ; initialized data section
  str_int db "%d", 0
  char_nl db 0ah, 0

Na wyjściu dalej otrzymuje:
margor@xeon:~/programowanie/projekty/5nasmlib/1> ./main

5margor@xeon:~/programowanie/projekty/5nasmlib/1>

Zrobiłem następujące obejście, ale fajnie byłoby rozwiązać w czystym asemblerze:

print_newline:
  push ebp ; set up stack frame
  mov ebp, esp

  ;
  ; do: puts("")
  ;
  push dword empty ; address of: ""
  call puts ; call C function
  add esp, 4 ; pop stack 1 times 4 bytes

  mov esp, ebp ; takedown stack frame
  pop ebp
ret ; return to previous block

gdzie empty to po prostu: empty db 0

Ciekawe co jest nie tak z tą oryginalną procedurą.

0

Moim zdaniem problem lezy po stronie systemu, zobacz:

[section .data]
	fnl	db	0x0A
	fnb	db	"%d",0x00

[section .text]
	extern	printf

global main
main:
	xor	eax, eax
   petla:
	call	print_nb
	inc	eax
	cmp	eax, 10
	jne	petla

	call	print_nl
	call	print_nl
	call	print_nl	

	xor	eax, eax
	ret

print_nb:
	pushad
	push	eax
	push	dword fnb	
	call	printf
	add	esp, 0x08
	popad
	ret

print_nl:
	pushad
	mov	eax, 4
	xor	ebx, ebx
	inc	ebx
	mov	ecx, fnl
	xor	edx, edx
	inc	edx
	int	0x80
	popad
	ret

Tak czy inaczej najpierw wypisuje to co chcielismy za pomoca przerwania int 0x80, a dopiero pozniej wypisuje to co z printf ;>

0

Otrzymuje nieprawidłowe dane wyjściowe:
margor@xeon:~/programowanie/projekty/5nasmlib/2> ./main

0123456789margor@xeon:~/programowanie/projekty/5nasmlib/2>

0

Nom, bo tylko przyklad dalem, ale funkcje print_nb, print_nl sa poprawne, wiec mozesz sobie ich uzyc jak chcesz. Tylko tak jak mowie pierwszenstwo ma int 0x80, a dopiero potem printf, chociaz przy wiekszej ilosci danych wypisuje w roznej kolejnosci. Wylaczenie buforowania funkcja setbuf nie pomaga przywrocic normalnej kolejnosci ;/

0

OK, dzięki. W takim razie dla większej ilości danych będę próbował stosować obejście z puts, teraz niestety mam coś innego do zrobienia i nie mogę przetestować.

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