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