Bezpośrednie zarządzanie stosem

Odpowiedz Nowy wątek
2018-12-13 22:16
1

Cześć Wam

Chciałem z ciekawości zaimplementować sobie wydajnego quick sorta w asmie. Stwierdziłem, że będę zarządzał stosem bezpośrednio podczas przekazywania argumentów do funkcji zamiast używać instrukcji push i rejestrów służących do zarządzania argumentami właśnie typu rdi, rsi, rdx, rcx. No i pierwsze co to trzeba wydrukować jakiś tekst na standardowe wyjście. Okazało się, że bezpośrednie zarządzanie stosem przy architekturze x86-64 jest dość specyficzne być może i nie działa poprawnie.

Zobaczcie:

global main
extern printf

section .text
    main:
        push rbp                                        
        mov rbp, rsp                                    
        sub rsp, 16
        mov dword [rbp-16], type_size                       
        call printf
        leave
        ret

section .rodata
type_size db "Type the size of an array: ", 0

Coś takiego generuje segmentation fault. Test z kodem na 32 bitową architekturę:

global main
extern printf

section .text
    main:
        push ebp                                    
        mov ebp, esp                                    
        sub esp, 16
        mov dword [ebp-16], type_size                       
        call printf
        leave
        ret

section .rodata
type_size db "Type the size of an array: ", 0

Generuje poprawny wynik. Oto kompilacja i linkowanie, które przeprowadziłem dla architektury 64 bitowej:

nasm -f elf64 q_sort.asm
gcc q_sort.o -Wall -Wextra -o q_sort

A to dla architektury 32 bitowej:

nasm -f elf32 q_sort.asm
gcc q_sort.o -Wall -Wextra -m32 -o q_sort

Assembler to NASM. Czy ktoś ma pomysł skąd takie wyniki i dlaczego? O_o


Pozostało 580 znaków

2018-12-14 15:51
1

Poszperałem trochę głębiej i okazało się, że chodzi tutaj o tzw. rejestry wektorowe. Rejestry te są obecne właśnie w architekturze 64 bitowej np. podczas wywoływań funkcji printf ze standardowej biblioteki C. W architekturze 32 bitowej kompilatory nie zwracają uwagi uwagi na te rejestry dlatego wypisywanie na ekran działa bez zarzutów. Informacja o tym czy dana funkcja ma zwrócić uwagę na rejestry wektorowe podczas jej wywołania jest przechowywana w rejestrze rax/eax. Zatem taki kod:

global main
extern printf

section .text
    main:
        push rbp                                        
        mov rbp, rsp                                    
        sub rsp, 16
        mov dword [rbp-16], type_size
        mov eax, 0 ; funkcja ma nie korzystac z rejestrow wektorowych                       
        call printf
        leave
        ret

section .rodata
type_size db "Type the size of an array: ", 0

... już działa poprawnie. Oczywiście w rejestrze eax przechowywana jest informacja nie tyle o "obecności" rejestrów wektorowych podczas wywołania, ale raczej chodzi tutaj o ilość tych rejestrów.


Pozostało 580 znaków

2018-12-15 01:03
0

@Shizzer, jak to skompilowałeś, ponieważ u mnie ten kod nie zadziałał (naruszenie ochrony pamięci (zrzut pamięci), czy coś takiego pisało)? Natomiast taki kod już tak:

global main
extern printf

section .text
main:
push rbp
mov rbp, rsp
mov dword [rbp - 4], edi

mov esi, [rbp - 4]
mov rdi, type_size
xor eax, eax
call printf

leave
ret

section .rodata
type_size: db "Type the size of an array: ", 0

Kompilacja: nasm -f elf64 push_push.asm && gcc push_push.o -Wall -Wextra -o push_push && ./push_push

Powyższy kod skopiowałem i zmodyfikowałem stąd: https://www.linuxquestions.or[...]/x86_64-assembly-help-524748/

Inne link'i:

http://wazniak.mimuw.edu.pl/i[...]:_Synteza_modelu_programowego
http://cs.brown.edu/courses/c[...]ocs/guides/x64_cheatsheet.pdf
https://bart.disi.unige.it/zx[...]urse-2017/x86intro_slides.pdf
https://chyla.org/blog/assemb[...]cz2_Stos_i_wywolanie_funkcji/

edytowany 2x, ostatnio: Neutral, 2018-12-15 03:00

Pozostało 580 znaków

2018-12-15 13:41
0

Wydaje mi się, że musiałem coś źle wkleić w każdym razie już piszę naprostowanie. Na architekturze x86 są inne konwencje wywołania funkcji niż na x86-64. Mianowicie odkryto, że bezpośrednie działanie na stosie jest rzeczą, która powoduje na przykład opóźnienia na procesorach 64 bitowych. Jeśli kogoś by to interesowało to tutaj jest bardzo ciekawy artykuł na ten temat -> https://stackoverflow.com/que[...]386-and-x86-6/2538212#2538212.

Z tego wynika, że można używać na przykład instrukcji push do przekazywania argumentów do funkcji lub właśnie stosu bezpośrednio, ale wykorzystywanie stosu bezpośrednio w architekturze 64 bitowej generuje takie właśnie dziwne błędy. Zgodnie z konwencją wywołań metod na x86-64 lepiej używać do tego rejestrów rdi, rsi, rdx itd. Także po prostu najlepiej napisać taki kod:

global main
extern puts

section .text
    main:
        push rbp
        mov rbp, rsp
        mov rdi, type_size
        xor eax, eax
        call puts
        pop rbp
        ret

section .rodata
type_size db "Type size: ", 0

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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