Programowanie w asemblerze ARM Practicum

Transkrypt

Programowanie w asemblerze ARM Practicum
Programowanie w asemblerze
ARM Practicum
Zbigniew Jurkiewicz, Instytut Informatyki UW
16 grudnia 2014
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Wprowadzenie
ARM przez przykłady
Testowane pod QEMU
GNU Assembler
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Pierwszy program (jeden.s)
/* Komentarz */
.global main
.func main
@ ’main’ jest funkcja˛
main:
mov r0, #2
bx lr
@ return z main (też komentarz)
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Testujemy
as -o first.o first.s
gcc -o first first.o
./first
./first ; echo $?
2
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Dodawanie
.global main
.func main
main:
mov r1, #3
mov r2, #4
add r0, r1, r2
bx lr
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Dane w pamieci
˛ (cz.1)
.data
/* Dane 4-bajtowe, wyrównane do 4 bajtów */
.balign 4
var1:
.word 3
.balign 4
var2:
.word 4
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Dane w pamieci
˛ (cz.2)
.text
.global main
.balign 4
main:
ldr r1,
ldr r1,
ldr r2,
ldr r2,
add r0,
bx lr
addr_of_var1
[r1]
addr_of_var2
[r2]
r1, r2
/* Dost˛
ep do danych przez mini-GOT */
addr_of_var1: .word var1
addr_of_var2: .word var2
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Dane w pamieci
˛ — komentarze
.word to rezerwacja czterech bajtów! Podstawowa
jednostka adresacji procesorów ARM.
Dla dwóch bajtów używa sie˛ .hword.
Adresy danych w kodzie jako offset do mini-GOT (Global
Object Table), bo brak miejsca w instrukcji na pełny adres.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Zapisywanie do pamieci:
˛ zmienne (cz.1)
.data
/* Zmienne wyrównane do 4 bajtów */
.balign 4
var1:
.word 0
.balign 4
var2:
.word 0
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Zapisywanie do pamieci:
˛ zmienne (cz.2)
.text
.global main
/* Kod też */
.balign 4
main:
/* Inicjowanie zmiennych */
ldr r1, addr_of_var1
mov r3, #3
str r3, [r1]
ldr r2, addr_of_var2
mov r3, #4
str r3, [r2]
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Zapisywanie do pamieci:
˛ zmienne (cz.3)
/* Dalej jak poprzednio */
ldr r1, addr_of_var1
ldr r1, [r1]
ldr r2, addr_of_var2
ldr r2, [r2]
add r0, r1, r2
bx lr
/* Adresy zmiennych */
addr_of_var1: .word var1
addr_of_var2: .word var2
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Debugger
„Business as usual” czyli gdb:
$ gdb ./store1
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redist
There is NO WARRANTY, to the extent permitted by law. T
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
...
Reading symbols from /home/roger/asm/chapter03/store01..
(gdb) start
Temporary breakpoint 1 at 0x8390
Starting program: /home/roger/asm/chapter03/store01
Temporary breakpoint 1, 0x00008390 in main ()
(gdb) _
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Debugger mówi prawde˛
(gdb) disassemble
Dump of assembler code for function
=> 0x00008390 : ldr r1, [pc, #40] ;
0x00008394 : mov r3, #3
0x00008398 : str r3, [r1]
0x0000839c : ldr r2, [pc, #32] ;
0x000083a0 : mov r3, #4
0x000083a4 : str r3, [r2]
0x000083a8 : ldr r1, [pc, #16] ;
0x000083ac : ldr r1, [r1]
0x000083b0 : ldr r2, [pc, #12] ;
0x000083b4 : ldr r2, [r2]
0x000083b8 : add r0, r1, r2
0x000083bc : bx lr
End of assembler dump.
Zbigniew Jurkiewicz, Instytut Informatyki UW
main:
0x83c0
0x83c4
0x83c0
0x83c4
Programowanie w asemblerze ARM Practicum
Debugger mówi prawde˛
Adresy zostały zastapione
˛
przesunieciami,
˛
dlatego dwie
instrukcje ldr odwołujace
˛ sie˛ do tego miejsca
addr_of_myvarX maja˛ inna˛ wartość argumentu.
Strzałka arrow => wskazuje na nastepn
˛ a˛ instrukcje˛ do
wykonania. Zanim jednak ruszymy dalej, warto obejrzeć
rejestry.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Rejestry
(gdb) info registers r0 r1 r2 r3
r0
0x1 1
r1
0xbefff744 3204446020
r2
0xbefff74c 3204446028
r3
0x8390 33680
(gdb) p $r0 = 2
$1 = 2
(gdb) info registers r0 r1 r2 r3
r0
0x2 2
r1
0xbefff744 3204446020
r2
0xbefff74c 3204446028
r3
0x8390 33680
(gdb) p $1
$2 = 2
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Ruszamy
(gdb) stepi
0x00008394 in main ()
(gdb) info register r1
r1
0x10564 66916
(gdb) p &var1
$3 = ( *) 0x10564
Czyli w r1 jest adres naszej zmiennej var1.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Sterowanie
Rejestr r15 ma też druga˛ nazwe:
˛ pc.
Zwykle używa sie˛ tej drugiej nazwy, napotkanie r15 jest
mało prawdopodobne.
Jest to bowiem licznik instrukcji (znany w gwarze x86 jako
ip — instruction pointer).
Normalnie (gdy nie ma skoku ani wywołania funkcji) rejestr
ten zwieszany
˛
jest o 4 po wykonaniu każdej instrukcji.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Skok bezwarunkowy (i bezsensowny)
.text
.global main
main:
mov r0, #2
b end
mov r0, #3
end:
bx lr
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Skoki warunkowe
Rejestr stanu cpsr (Current Program Status Register)
zawiera cztery podstawowe flagi dla warunków: N
(Negative), Z (Zero), C (carry) and V (oVerflow).
Skoki warunkowe maja˛ mnemoniki b<warunek>, przy
czym warunek to jeden z ciagów
˛
liter: eq (equal), neq (not
equal), ge (greater or equal, dla liczb ze znakiem), lt (lower
than), gt (greather than), le (lower or equal), mi
(minus/negative), pl (plus/positive), os (overflow set), oc
(overflow clear), hi (higher, bez znaku), ls (lower or same),
cs/hs (carry set/higher or same), cc/lo (carry clear/lower).
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Porównywanie
.text
.global main
main:
mov r1, #2
mov r2, #2
cmp r1, r2
beq gdy_rowne
gdy_inne:
mov r0, #2
b end
gdy_rowne:
mov r0, #1
end:
bx lr
@ zmienia flagi warunków w cpsr
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Iteracja
Suma liczb od 1 do 22:
.text
.global main
main:
mov r1, #0
mov r2, #1
loop:
cmp r2, #22
bgt end
add r1, r1, r2
add r2, r2, #1
b loop
end:
mov r0, r1
bx lr
@ suma
@ licznik
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Tryby adresowe
Dotad
˛ używaliśmy jako argumentów instrukcji (z wyjatkiem
˛
ldr i str) tylko rejestrów lub stałych.
Pierwszy argument (wynikowy) powinien być rejestrem,
wyjatek
˛
to np. instrukcja str, ale tam wynik idzie do
pamieci).
˛
Ostatni argument może być dodatkowo obrócony lub
przesuniety
˛
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Tablice
.data
.balign 4
a: .skip 400
@ tablica 100 liczb
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Tablice
/* Ustawiamy jej elementy na 100 kolejnych liczb */
.text
.global main
main:
ldr r1, addr_of_a
mov r2, #0
loop:
cmp r2, #100
beq end
add r3, r1, r2, LSL #2
str r2, [r3]
add r2, r2, #1
b loop
end:
bx lr
addr_of_a: .word a
Zbigniew Jurkiewicz, Instytut Informatyki UW
@ bazowy adres tablicy a
@ poczatkowy
˛
indeks
@ koniec?
@ adres kolejnego elementu
Programowanie w asemblerze ARM Practicum
Funkcje
Gdy używamy funkcji dwa kolejne rejestry staja˛ sie˛
specjalne: r13 i r14.
Druga nazwa dla r14 to lr (link register) czyli rejestr
łacz
˛ acy:
˛
w nim zapisuje sie˛ adres powrotny dla wywołania
funkcji.
Rejestr r13 ma też nazwe˛ sp (stack pointer) i jest to
wskaźnik wierzchołka stosu.
Parametry przkazywane konwencjonalnie w rejestrach r0,
r1, r2 i r3. Jeśli wiecej,
˛
to na stosie.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Konwencje
Nie należy nic zakładać o zawartości cspr (czyli o flagach
warunków).
Wolno zmieniać bezkarnie rejestry r0, r1, r2 i r3. Inne
należy zachowywać (np. na stosie) i odtwarzać.
Wartość funkcji zwracana w rejestrze r0 jeśli mieści sie˛ w
32 bitach. Dla C long i float sa˛ 4-bajtowe.
Wartości 64-bitowe (czyli double) zwracane w parze
rejestrów r0,r1.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Wywołanie funkcji
Dwa sposoby wywołania:
Jeśli nazwa funkcji (a właściwie jej adres) znana w czasie
kompilacji to instrukcja˛
bl hnazwai
Drugi sposób to wywołanie pośrednie przez rejestr, adres
funkcji w rejestrze podanym jako argument
blx hrejestri
Powrót z wywołania funkcji przez
bx lr
Oczywiście można użyć innego rejestru.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Witaj świecie
.data
greeting:
.asciz "Witaj świecie"
.balign 4
return: .word 0
@ magazyn na adres powrotny dla main
.text
.global main
.global puts
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Witaj świecie
main:
ldr r1, address_of_return
str lr, [r1]
ldr r0, address_of_greeting
bl puts
@ adres powrotny dla m
@ parametr dla puts
ldr r1, address_of_return
ldr lr, [r1]
bx lr
@ return z main
address_of_greeting: .word greeting
address_of_return: .word return
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Witaj lepszy świecie
A w każdym razie bardziej leniwy: budowe˛ GOT można
zepchnać
˛ na asembler:
main:
ldr r1, =return
str lr, [r1]
ldr r0, =greeting
bl puts
@ adres powrotny dla main
@ parametr dla puts
ldr r1, =return
ldr lr, [r1]
bx lr
Zbigniew Jurkiewicz, Instytut Informatyki UW
@ return z main
Programowanie w asemblerze ARM Practicum
Pełna interakcja: printf i scanf
.data
.balign 4
message1: .asciz "Podaj liczb˛
e: "
.balign 4
message2: .asciz "Wczytałem liczb˛
e %d\n"
.balign 4
scanf_pattern : .asciz "%d"
.balign 4
number_read: .word 0
.balign 4
return: .word 0
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Pełna interakcja: printf i scanf
.text
.global main
.global printf
.global scanf
main:
ldr r1, =return
str lr, [r1]
ldr r0, =message1
bl printf
@ call to printf
ldr r0, =scanf_pattern
ldr r1, =number_read
bl scanf
@ call to scanf
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Pełna interakcja: printf i scanf
ldr r0, =message2
ldr r1, =number_read
ldr r1, [r1]
bl printf
@ call to printf
ldr r0, =number_read
ldr r0, [r0]
ldr lr, =return
ldr lr, [lr]
bx lr
Zbigniew Jurkiewicz, Instytut Informatyki UW
@ return from main
Programowanie w asemblerze ARM Practicum
Własna funkcja (mnożenia przez 5 ;-)
.data
.balign 4
message1: .asciz "Podaj liczb˛
e: "
.balign 4
message2: .asciz "%d razy 5 = %d\n"
/* Format pattern for scanf */
.balign 4
scanf_pattern : .asciz "%d"
.balign 4
number_read: .word 0
.balign 4
return: .word 0
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Własna funkcja (mnożenia przez 5 ;-)
.text
.global printf
.global scanf
.global main
/* mult_by_5 local function */
mult_by_5:
add r0, r0, r0, LSL #2
bx lr
@ r0 * 5
@ return using lr
main:
ldr r1, =return
str lr, [r1]
ldr r0, =message1
bl printf
Zbigniew Jurkiewicz, Instytut Informatyki UW
@ call to printf
Programowanie w asemblerze ARM Practicum
Własna funkcja (mnożenia przez 5 ;-)
ldr r0, =scanf_pattern
ldr r1, =number_read
bl scanf
@ call to scanf
ldr r0, =number_read
ldr r0, [r0]
bl mult_by_5
@ call to mult_by_5
mov r2, r0
ldr r1, =number_read
ldr r1, [r1]
ldr r0, =message2
bl printf
@ call to printf
ldr lr, =return
ldr lr, [lr]
bx lr
Zbigniew Jurkiewicz, Instytut Informatyki UW
@ return from main
Programowanie w asemblerze ARM Practicum
Stos
Wskażnik stosu (sp) musi być zawsze wyrównany do 4
bajtów.
Procedure Call Standard for the ARM architecture
(AAPCS) wymaga żeby wskaźnik stosu był wyrównany do
8 bajtów, bo niektóre biblioteki tego potrzebuja.
˛
Stos może rozrastać sie˛ w góre˛ lub w dół. Dla Linuxa stos
rośnie w dół, tzn. przy dokładaniu na stos wartość sp
zmniejsza sie.
˛
Oznacza to , że adresy zmiennych lokalnych w C maja˛
duże wartości.
Wskaźnik stosu może wskazywać na najwyższy element
lub na pierwsze wolne miejsce. W Linuxie wskazuje na
najwyższy element.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Silnia rekurencyjnie
.data
message1: .asciz "Type a number: "
format:
.asciz "%d"
message2: .asciz "The factorial of %d is %d\n"
.text
.globl main
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Silnia rekurencyjnie
factorial:
str lr, [sp,#-4]!
str r0, [sp,#-4]!
cmp r0, #0
bne is_nonzero
mov r0, #1
b end
is_nonzero:
@ base case
@ call factorial(n-1)
sub r0, r0, #1
bl factorial
ldr r1, [sp]
mul r0, r0, r1
end:
add sp, sp, #+4
ldr lr, [sp], #+4
bx lr
@ clean the stack
@ return address to lr
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Silnia rekurencyjnie
main:
str lr, [sp,#-4]!
sub sp, sp, #4
@ room for the number entered by the u
ldr r0, =message1
bl printf
@ call printf
ldr r0, =format
mov r1, sp
bl scanf
@ Parameters for scanf
@ (top of stack is the second paramete
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Silnia rekurencyjnie
ldr r0, [sp]
bl factorial
@ Call factorial on integer read by sc
mov r2, r0
ldr r1, [sp]
ldr r0, =message2
bl printf
@ Parameters for printf
add sp, sp, #+4
ldr lr, [sp], #+4
bx lr
@ Clean the stack and return
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum
Inspiracja czyli komu to ukradłem
Poczatek
˛
na „blogu”
http://thinkingeek.com/2013/01/09/arm-assembler-raspberry-pi-chapter-1/
Autor: rferrer ?
Oryginalnie dla Raspberry Pi Model B z Raspbianem (2
porty USB, 512 MB RAM, 32 rejestry zmiennopozycyjne).
ARM website: http://infocenter.arm.com/
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze ARM Practicum