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.

                               * * *