Back to ARM page
How to use:
Let's say you want to compile a simple C program like: example.c
void main(void)
{
int a=2;
int b=2*a;
if (b==4)
{
a=0;
}
else
{
a=1;
}
}
To compile this, follow the next steps:
- first go to the gcc-arm-elf/bin dir. Compile your source in ARM-elf format and
output them as GnuAs ARM assembler:
./arm-elf-gcc example.c -S -mcpu=arm920 -fomit-frame-pointer -fpic -ffixed-r0 -ffixed-r1 -o a.s
Commentary:
This should produce an a.s ARM source file that save the required registers (PC,LR)
and that doesn't use the R0 and R1 because of the Saturn emulator (-ffixed flag).
The -fpic option means the code has to be 'position independant'. That's the way to tell
the compiler not to produce absolute adressing, but rather PC offset variables accessing or
heap stack.
- use the assembler for arm920 target, with 32bits PC,no fpu and little endian:
./arm-elf-as -EL -k -mcpu=arm920 -mno-fpu -mapcs-32 a.s -o a.o
- link the object(s) in elf format (with others if needed):
./arm-elf-ld a.o -o a.exe --entry=main
Commentary:
Here we asked the linker to use 'main' as the entry point of our program,
we can change this to point to another function that can do a first init
of the HP. I.e.: let's say entry is a complex asm 'startup()' function that
does some initialisations common to a lot of programs (such as save registers,
init screen grayscale, clock speed, ...) before calling 'main'.
Then just link with the correct elf-object file to make it run.
At this stage, this entry point is useless (read below), since the code extractor
will not use this entry point as reference.
- now that you have a static binary object file, get the binary code:
./arm-elf-objcopy -O binary a.exe a.bin
- finally convert it to a HP49 string to be able to lunch it with the
ARM launcher ("A>CP" version, see Al's page):
./convert a.bin a.hp
Commentary:
The convert program is done in C and can be
compiled using gcc for cygwin (gcc should
be installed if selected during Cygwin setup).
Warnings:
This way, you only get the code section of the elf format.
This will strictly work for C programs without globals&arrays&linking.
Some issues, hints& tricks
These ideas were get through discussions with people who wrote to me.
Registers Save&Load :
Al said that R4-R12 should be saved as well. For that, put asm code (see asm volatile ("") directive
from gcc) at the begining of your main function and restore registers at the end.
Others caveats about gcc for arm:
Gcc and ld produce elf compatible objects.
In elf format, the entry point is stored in a '.text' section
that doesn't always start by the main() function.
For example, try running 'arm-elf-readelf -a yourprog.exe' where main is not written at the begining,
you'll get something like this:
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: 0x80
Start of program headers: 52 (bytes into file)
Start of section headers: 33348 (bytes into file)
Flags: 0x222, has entry point, GNU EABI, position independent, software FP
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 6
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 008000 0000d4 00 AX 0 0 4
[ 2] .data PROGBITS 000001d4 0081d4 000000 00 WA 0 0 1
[ 3] .bss NOBITS 000001d4 0081d4 000000 00 WA 0 0 1
[ 4] .comment PROGBITS 00000000 0081d4 000034 00 0 0 1
[ 5] .stack PROGBITS 00080000 008208 000000 00 W 0 0 1
[ 6] .shstrtab STRTAB 00000000 008208 00003c 00 0 0 1
[ 7] .symtab SYMTAB 00000000 0083ac 000190 10 8 c 4
[ 8] .strtab STRTAB 00000000 00853c 000081 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x008000 0x00000000 0x00000000 0x001d4 0x001d4 RWE 0x8000
Section to Segment mapping:
Segment Sections...
00 .text
More explanations: if you write a C program like:
int myfunc(int a)
{
...
}
void main(void)
{
int a=myfunc(1);
}
Then the final linked elf object will have myfunc() placed first in the binary then main() second
(all coded in arm asm) and the elf object will have information about where the entry point is
(let's say: "Entry point adresse: 0x80").
After using objcopy -O binary, the obtained binary doesn't start by main() but by myfunc()...
When using ld with 2 or more objects file, we have the same problem and the entry-point is still
given in the elf header.
My conclusions:
- 1. It is not possible to use ld to directly obtain a final binary using objcopy.
- 2. A quick solution is to put the functions inside main(), or start writing the source by main()
function and link with this file first on the ld arguments. That's ugly, but it works.
- 3. A better solution to bypass this issue is to insert at the begining of the main asm
file obtained with gcc a B(ranch) arm instruction to jump to the main() fonction.
But linking with numerous objects file is still impossible, except if you put your main.o
file at the begining of the ld command line... Very unconvenient.
- 4. Maybe we need to make our own small ld tool(or objcopy),
using a classical executable file format like elf (which is portable)
and that does the relocation and strips unneeded sections of elf file format.
Need at lot of time.
- 5. I looked the TIGCC way of resolving that: they build their
own ld tool (since linking can be easy when only code and data sections are used).