Obsuga FPU - pobranie wartości ze stosu

0

Mam problem z pobraniem wartości ze stosu FPU. Program działa jako wywołanie funkcji "dodawanie" w pliku .c
Potrzebuję wynik operacji pobrany ze stosu zapisać do eax, ale mam problem z naruszeniem ochrony pamięci.
Składnia AT&T, Linux x86

.section .data
liczba1: .int 1

liczba2: .int 1

liczba3: .double 1

.section .text

.global dodawanie

dodawanie:

push %rbp
mov %rsp, %rbp

mov %rdi, (liczba1)
mov %rsi, (liczba2)

finit
fild (liczba1)
fild (liczba2)

faddp %st, %st(1)

fstp (%eax)

leaveq
retq

Próbowałam zapisać do liczba3 i potem do eax, ale wtedy z kolei wyrzuca losową wartość.

fstp (liczba3)
mov $liczba3, %eax

Wynik dodawania na stosie jest poprawny, problem jest z pobraniem wartości.
Będę bardzo wdzięczna za pomoc.

0

liczba typu double jest 64-bitowa i nie zmieści się w 32-bitowym rejestrze eax.
Po co w ogóle chcesz to robić?

0

Przecież nie można napisać fstp (zwykły rejestr), bo nawet nie ma takiej instrukcji. Możesz spokojnie zwrócić wartość przez st0.

0

Jestem bardzo początkująca i nie wiedziałam, że można przekazywać wartość przez inny rejestr.
Musze poszukać jak to zrobić, dziękuje za odpowiedzi, może już sobie poradzę:)

1

Zobacz sobie tutorial na http://www.website.masmforum.com/tutorials/fptute/index.html sam z niego często korzystałem, fajnie i prosto wyjaśnione korzystanie z FPU.

0

Przykładowy test pod win32:

/* main.c */

#include <stdio.h>

extern double fpu_test();

int main(void) {
    printf("wynik = %lf\n", fpu_test());
    return 0;
}
; fpu_test.asm

global _fpu_test

section .text

_fpu_test:
    fld1
    ret
> nasm -f win32 fpu_test.asm
> gcc main.c fpu_test.obj
> a.exe
wynik = 1.000000
0

To ja już nie rozumiem... Przerobiłam pliki tylko żeby przetestować:

 #include <stdio.h>
extern double dodawanie();
int main()
{
	printf("Wynik to %lf\n",dodawanie());
	return 0;
}
.global dodawanie
.section .text
dodawanie:
finit
fld1
ret

I wyrzuca mi wynik 0. A gdy jeszcze miałam wczytywanie liczb (bez przekazywania ich do "dodawanie") to wyrzucał drugą wczytaną liczbę.

	.file	"zad2.c"
	.section	.rodata
.LC0:
	.string	"Wynik to %lf\n"
	.text
.globl main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	movq	%rsp, %rbp
	.cfi_offset 6, -16
	.cfi_def_cfa_register 6
	movl	$0, %eax
	call	dodawanie
	movl	$.LC0, %eax
	movq	%rax, %rdi
	movl	$1, %eax
	call	printf
	movl	$0, %eax
	leave
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
	.section	.note.GNU-stack,"",@progbits
1

Pamiętaj o tym, że argumenty przekazywane są przez stos. Dodawanie można zaimplementować tak:

; double dodawanie(double x, double y);
; [RET]  [x]  [y]
;        ^4   ^12
_dodawanie:
    fld  qword [esp+4]
    fadd qword [esp+12]
    ret
0

Działa :) Bardzo, bardzo dziękuję:)
(jedyna różnica to adres esp+8, zamiast esp+12 działa w moim przypadku)

.section .text

.global dodawanie

dodawanie:

finit
fild 8(%esp)
fild 4(%esp)
faddp %st, %st(1)

ret

Po zamianie na:

finit
fldl 12(%esp)
fldl 4(%esp)
halo:

Break przy halo
st0 6.3659873738839482370166397329427641e-314 (raw 0x3beec000000080000000)
st1 4.8535791833262006676910555829069706e-270 (raw 0x3c80a41a87fffede4000)

Przed finit
st0 0 (raw 0x00000000000000000000)
st1 0 (raw 0x00000000000000000000)

 #include <stdio.h>
extern double dodawanie(int liczba1, int liczba2);
extern double odejmowanie(int liczba1, int liczba2);
extern double mnozenie(int liczba1, int liczba2);
extern double dzielenie(int liczba1, int liczba2);

int main()
{
	double a,b,c;
	char znak;
	printf ("Podaj znak operacji\n");
	scanf("%c",&znak);
	printf ("Podaj liczbe\n");
	scanf("%lf",&a);
	printf ("Podaj liczbe\n");
	scanf("%lf",&b);
	switch(znak)
	{
		case '+':c=dodawanie(a,b);break;
	}
	printf("Wynik to %lf\n",c);
	return 0;

}
0

Mam jeszcze problem z inną wersją tego programu, użyciem scanf w pliku .s
O ile printf działa dobrze, to nie potrafię pobrać scanf wartości wpisywanej liczby. Docelowo próbowałam zapisać ją do liczba1 i liczba2, do rejestrów, ale coś chyba robię źle. We wrzuconej wersji wyrzuca wyniki liczbowe, ale zupełnie bez sensu.

SYSREAD=3
SYSWRITE=4
STDOUT=1
STDIN=0

.section .rodata

.liczba:
.string "Wynik to %f\n"

.pytanie1:
.string "Podaj liczbe\n"

.odczyt:
.string "%f"

.section .data

msg_pytanie3: .ascii "Podaj znak\n "
msg_pytanie3_len= . - msg_pytanie3

msg_znak: .ascii "+"
msg_znak_len= . - msg_znak

liczba1: .int 25

liczba2: .int 8

.text
.global main
main:

mov $SYSWRITE, %eax
mov $STDOUT, %ebx
mov $msg_pytanie3, %ecx
mov $msg_pytanie3_len, %edx

int $0x80

mov $SYSREAD, %eax
mov $STDIN, %ebx
mov $msg_znak, %ecx
mov $msg_znak_len, %edx

int $0x80

finit

subl $0x24, %esp

movl $.pytanie1, (%esp)

call printf 

pushl $liczba1         ;chodzi o ten moment
pushl $.odczyt

call scanf

popl %edx
popl (liczba1)

movl $.pytanie1, (%esp)

call printf 

pushl $liczba2
pushl $.odczyt

call scanf

popl %edx
popl (liczba2)

fild (liczba2)
fild (liczba1)

addl $0x24, %esp

mov $1, %ecx

mov $msg_znak, %ebx
movb -1(%ebx,%ecx, 1), %al

cmp $0x2B, %al
je dodawanie

dodawanie:
	faddp %st, %st(1)
	jmp koniec

koniec:
	subl $0x12, %esp
	fstpl 0x04(%esp)

movl $.liczba, (%esp)

call  printf

addl $0x12, %esp

xorl  %eax,%eax
ret

Make file jak coś (ale nie sądzę, żeby to był problem):

 zad1: zad1.o
	gcc -m32 -o zad1 zad1.o
zad1.o:
	as -32 -o zad1.o zad1.s
1

Nie trawię składni AT&T, ale scanf ma taki prototyp http://www.cplusplus.com/reference/cstdio/scanf/, więc wywołując scanf powinnaś zapisać na stosie offsety do zmiennych gdzie znajdą się wartości ze zdefiniowanego string-a formatującego, więc samo "pop liczba1" nie zwróci Ci tam żadnej wartości, bo ona już powinna tam być pod tą zmienną, te pop tylko zniszczy tą wartość, bo scanf to funkcja w konwencji cdecl i te 2x pop jedynie skorygują stos i sprawią, że nadpiszesz wartość pod zmienną liczba1

Reasumując - parametry dla scanf powinny zawierać offset do ciągu formatującego i offset do zmiennej liczba1, a po wywołaniu scanf daj tylko add esp,4*2 (rozmiar parametrów na stosie) i powinno być ok.

PS. Ciąg formatujący "%f" odpowiada za typ float, a ty pod liczba1 masz zdefiniowaną wartość typu int. Wprawdzie oba mają rozmiar 32 bitów, jednak to inny typ danych.

0

Mam nadzieję, że to już ostatnie moje pytanie w tym temacie.
Chcę ustawić bity RC w słowie kontrolnym (11 i 10) dla wyboru rodzaju zaokrąglania. I po dodaniu danego fragmentu kodu pojawia mi się problem z naruszeniem ochrony pamięci.

finit

subl $0x12, %esp

fnstcw (%esp)

movl %esp, %eax      

or $3072, %eax

movl %eax, %esp      
          
fldcw (%esp) 

addl $0x12, %esp 

Dokładnie rzecz biorąc problematyczna jest środkowa linijka:

movl %eax, %esp  

bez niej wszystko jest w porządku (jeśli chodzi o naruszenie ochrony pamięci).
Próbowałam wersji z:

pushl %eax
addl $0x04, (%esp)

ale tu z kolei po pushl i sprawdzeniu rejestru esp, nie ma tam przepisanej wartości z eax.
Będę wdzięczna za pomoc.

0

cofasz stos o 18 bajtów, być może chodzi o ochronę pamięci i pisanie po adresie niewyrównanym do 4-ki, spróbuj cofnąć o wartość, która jest wielokrotnością 4-ki, czyli w tym wypadku 16, w przeciwnym wypadku zostaje debugger, z którym trzeba się zaprzyjaźnić pisząc w assemblerze

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