Arm Cross-Tools unter Linux
---------------------------
H. Hoegl, 2003-04-28
URL: http://www.fh-augsburg.de/~hhoegl/doc/doc.html#howto
Neulich habe ich einen gcc Cross-Compiler und einen dazu passenden gdb
Debugger auf meinem Debian Linux (Woody) installiert. Dazu bin ich
wie folgt vorgegangen:
Philip Blundell hat unter http://handhelds.org/~pb/deb-toolchain.html
bereits eine Anleitung geschrieben, von der man die wesentlichen
Punkte zur Installation eines Cross-Compilers entnehmen kann. Ich habe
die einzelnen Schritte nachvollzogen und diese nochmal aufgeschrieben:
1. Das "dpkg-cross" Paket installieren (falls dies noch nicht
gemacht wurde)
apt-get install dpkg-cross
2. Nachsehen, ob /etc/dpkg/cross-compile die Zeile
crossbase = /usr
enthaelt. Falls ja, dann ist alles in Ordnung. Falls nein, dann
aendern.
3. Die Bibliotheken fuer Arm holen von
http://http.us.debian.org/debian/pool/main/g/glibc und
http://http.us.debian.org/debian/pool/main/d/db1-compat
Installation der Bibliotheken mit
dpkg-cross --arch arm --install libc6_2.3.1-17_arm.deb \
libdb1-compat_2.1.3-7_arm.deb
dpkg-cross --arch arm --install libc6-dev_2.3.1-17_arm.deb
4. Die Pakete gcc-arm-linux (Version 3.2) and binutils-arm-linux
von http://handhelds.org/~pb/cross/ holen und wie gewohnt mit
dpkg installieren.
Die installierten Tools heissen dann:
arm-linux-addr2line arm-linux-gcov arm-linux-size
arm-linux-ar arm-linux-ld arm-linux-strings
arm-linux-as arm-linux-nm arm-linux-strip
arm-linux-c++filt arm-linux-objcopy
arm-linux-cpp arm-linux-objdump
arm-linux-gcc arm-linux-ranlib
arm-linux-gccbug arm-linux-readelf
Damit ist die Installation des gcc Cross-Compilers fuer Arm abgeschlossen.
Nun zum GDB. Nach einer ausgiebigen Suche nach einem passenden
cross-gdb fuer Arm bin ich auf das GBA Projekt von Jean-Fracois
Deverge gestossen. GB steht fuer "Game Boy" (A ist fuer Advanced) -
diese Dinger scheinen nun auch schon mit dem Arm Prozessor aufgebaut
zu sein. Das Projekt ist sehr umfangreich und bietet auch den gcc, g++
und spezielle Bibliotheken fuer den Game Boy. Der Cross-gcc heisst
z.B. gcc-arm-thumb-elf-gp32_3.2.1-1_i386.deb. Ich habe allerdings nur
den gdb verwendet. Um diesen gdb zu installieren, erweitert man
/etc/apt/sources.list um folgende Zeile:
deb http://didaho1.free.fr/cross unstable devel
Danach kann man den gdb installieren mit
apt-get install gdb-arm-thumb-elf
Nach der Installation hat man die neuen Programme arm-thumb-elf-gdb
(Version 5.2.1) und arm-thumb-elf-run. Letzteres Programm scheint ein
weiterer Simulator fuer Arm Prozessoren zu sein, zumindest deutet
der Hilfetext darauf hin:
Usage: arm-thumb-elf-run [options] program [program args]
Options:
-a args Pass `args' to simulator.
-m size Set memory size of simulator, in bytes.
-t Perform instruction tracing.
Note: Very few simulators support tracing.
-v Verbose output.
program args Arguments to pass to simulated program.
Note: Very few simulators support this.
Studieren der Texte mit "strings /usr/bin/arm-thumb-elf-run" ergab,
dass es sich um den "ARMulator" handelt. XXX To Do: Gibt es weiter
Informationen ueber diesen Simulator?
Der arm-gdb scheint nicht den ARMulator zu verwenden, zumindest
funktioniert arm-gdb auch dann noch, wenn man arm-thumb-elf-run
voruebergehend entfernt.
Test des GDB
------------
Mein hauptsaechliches Motiv zur Installation der Arm Tools ist die
Simulation von C- und Assembler Programmen. Vor allem moechte ich den
Arm Befehlssatz genauer kennenlernen und deshalb Programme in
Arm-Assembler schreiben.
Ich gehe von folgendem simplen C Programm aus (der Inhalt ist ohne
grosse Bedeutung):
int foo(void);
int
main()
{
int i;
foo();
for (i = 0; i< 2; i++) {
}
return 0;
}
int foo()
{
return 5;
}
Mit dem Kommando
arm-linux-gcc -nostartfiles -nostdlib -g -o main main.c
habe ich die ausfuehrbare Datei "main" im ELF Format erzeugt. Da ich
das Programm nicht direkt auf meinem x86 Rechner ausfuehren kann, habe
ich weder den Startup-Code noch die C Standardbibliothek dazugefuegt.
Auf einem x86 PC hat man jedoch die Moeglichkeit, die Datei in den
Arm-Simulator zu laden, der in den gdb fuer Arm eingebaut ist (siehe
weiter unten).
Mit folgender Kommandozeile kann man den gcc anweisen, Assembler-Code
auszugeben.
arm-linux-gcc -nostartfiles -nostdlib -S main.c
Hinweis: Wenn man die -g Option hinzufuegt, dann aendert sich der
Assembler-Output.
Das Ergebnis der Assemblierung heisst main.s (in diesem Fall ohne -g
Option) und sieht so aus:
.file "main.c"
.text
.align 2
.global main
.type main,function
main:
@ args = 0, pretend = 0, frame = 4
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
sub sp, sp, #4
bl foo
mov r3, #0
str r3, [fp, #-16]
.L2:
ldr r3, [fp, #-16]
cmp r3, #1
ble .L4
b .L3
.L4:
ldr r3, [fp, #-16]
add r3, r3, #1
str r3, [fp, #-16]
b .L2
.L3:
mov r3, #0
mov r0, r3
ldmea fp, {fp, sp, pc}
.Lfe1:
.size main,.Lfe1-main
.align 2
.global foo
.type foo,function
foo:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
mov r3, #5
mov r0, r3
ldmea fp, {fp, sp, pc}
.Lfe2:
.size foo,.Lfe2-foo
.ident "GCC: (GNU) 3.2 (Debian)"
Fuer die folgenden Versuche mit gdb sollte man das C Programm
mit folgender Kommandozeile uebersetzen:
arm-linux-gcc -nostartfiles -nostdlib -g -o main main.c
Den gdb fuer Arm kann man in einer grafischen Umgebung danach wie folgt
starten:
ddd --debugger arm-thumb-elf-gdb
Das GUI des ddd ist dreigeteilt (siehe [1]). Im Fenster ganz unten
kann man Kommandos an den GDB eingeben, genauso wie man das von der
Kommandozeilenversion des GDB kennt. In unserem Beispiel muessen die
folgenden Kommandos eingegeben werden:
file main
target sim
load
Das Kommando "load" zeigt nach dem Laden die Startadresse des
geladenen Programmes an. In diesem Beispiel ist diese Adresse 0x8074.
Danach kann man das Programm im gdb disassemblieren:
disas 0x8074
disas foo
Einen Breakpoint erzeugt man mit dem "break" Kommando. Beispiele sind:
break *0x8074
br main
br foo
Wenn man Adressen direkt als Zahlen eingibt, dann nicht den Stern "*"
vergessen! Welche Breakpoints gerade definiert sind findet man heraus mit:
info breakpoints
oder abgekuerzt mit "info br".
Breakpoints loescht man mit dem "clear" Kommando. Beispiele sind:
clear *0x8074
clear foo
Bevor man das Programm laufen laesst, sollte man genau einen
Breakpoint auf den Start des Hauptprogrammes setzen ("break *0x8074").
Dann kann man im Kommandofenster "run" eintippen. Das Programm stoppt
sofort an der Adresse 0x8074 (rotes "Stop-Schild" im DDD). Nun kann
man das Program im Einzelschrittbetrieb mit stepi und nexti
ausfuehren. Mit nexti werden Prozeduraufrufe bis zum naechsten Return
in Echtzeit ausgefuehrt.
Beim schrittweisen Ausfuehren des Programmes moechte man auch die
Register betrachten. Diese bekommt man angezeigt ueber den Menuepunkt
Status/Registers. Auf der Kommandozeile des gdb kann alternativ dazu
auch "info reg" tippen.
Man versetzt das Programm wieder in den Anfangszustand durch eintippen
des "load" Kommandos.
Man kann sich auch eigene gdb Kommandos schreiben und diese in ein
File ~/.gdbinit im Home-Verzeichnis unterbringen. Zum Beispiel koennte
man das Laden des Arm Binaerfiles automatisieren:
define armload
file $arg0
target sim
load
end
Programmieren in Assembler
--------------------------
Den Assembler kann man direkt mit folgender Kommandozeile aufrufen:
arm-linux-as --gstabs main.s
Das Ergebnis der Kompilierung ist ein a.out Binaerfile im ELF Format.
ARM Binutils
------------
Mit objdump kann man sich z.B. den Inhalt einer Binaerdatei
disassemblieren lassen:
arm-linux-objdump --disassemble a.out
Das readelf Programm liefert ausfuehrliche Informationen ueber den
Inhalt eines ELF Binaries:
arm-linux-readelf -a main
Bei meinem main Demo-Programm beginnt der Output von readelf mit dem
ELF Header:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 61 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: ARM
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x8074
Start of program headers: 52 (bytes into file)
Start of section headers: 936 (bytes into file)
Flags: 0x2, has entry point, GNU EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 16
Section header string table index: 13
[hier stehen weitere Informationen des readelf Outputs...]
Hinweise
--------
[1] Screenshot des DDD Debuggers mit angekoppelten arm-gdb:
[2] Es gibt viele andere Arm Cross-Pakete im Internet. Zum Beispiel gibt
es RPM Pakete fuer Arm (arm-elf-binutils, arm-elf-gcc, arm-elf-gdb)
unter http://rpm.pbone.net/index.php3/stat/21/year/2001/month/07/day/18.
* * *