robomon
Posts: 9
Joined: Thu Aug 27, 2015 8:31 pm

Why doesn't arm-none-eabi-gcc use my custom _start symbol

Sat Oct 24, 2015 5:37 pm

I am compiling the below code with "-nostdlib". My understanding was that arm-none-eabi-gcc will not use the _start in "crt0.o" but it will use the user defined _start. For this I was expecting to create a start.S file and put the _start symbol.

But if I compile the below shown code without the _start symbol defined from my side, I am not getting any warning. I was expecting "warning: cannot find entry symbol _start;"

Questions:

1) Why am I not getting the warning ? From where did GCC get the _start symbol ?

2) If gcc got the _start symbol from a file from somewhere, could you let me know how to ask GCC to use the _start from my start.S file ?

$ cat test.c

Code: Select all

int main()
{
    volatile int i=0;
    i = i+1;

    return 0;
}
$ cat linker.ld

Code: Select all

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 20K
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}
$ arm-none-eabi-gcc -Wall -Werror -O2 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7 -nostdlib -T linker.ld test.c -o test.o

$ arm-none-eabi-gcc --version

arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.9.3 20150529 >(release) [ARM/embedded-4_9-branch revision 224288]

JacobL
Posts: 76
Joined: Sun Apr 15, 2012 2:23 pm

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sat Oct 24, 2015 7:14 pm

The start is not defined by any specific symbol name, it is just whatever happens to be at the starting address of the platform. For Raspberry Pis, this address is normally 0x8000, which means that you will need to make sure that your startup code is linked at this address. This is normally done by placing the code in its own section, often named .init and then placing this section at address 0x8000 in the linker script. You would want to do this with your code in start.S.

Your questions:

1) There is no concept of a start symbol when using -nostdlib. Therefore no warning.
2) This would automatically be handled by placing at address 0x8000. But you might want to tell the linker about what your start symbol is called, since it might otherwise think it is dead code and remove it. You do that by specifying --entry=<your start symbol name>

robomon
Posts: 9
Joined: Thu Aug 27, 2015 8:31 pm

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sun Oct 25, 2015 6:39 am

I tried adding ENTRY(symbol) to my linker script. But still no success. Please see the assembly listing created using objdump. You can see that the main is at 0x8000 and not the _start.

test.c

Code: Select all

int main()
{
    volatile int i=0;
    i = i+1;

    return 0;
} 
start.S

Code: Select all

.globl _start
_start:
    mov sp,#0x8000 /*stack grows down*/
    bl main
loop: b loop
linker.ld

Code: Select all

ENTRY(_start)
 
MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 20K
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}
Compilation command

Code: Select all

arm-none-eabi-gcc -Wall -Werror -O2 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7 -nostdlib -T linker.ld test.c start.S -o output.o
Assembly listing creation

Code: Select all

arm-none-eabi-objdump -D output.o > out.asm
Assembly listing from objdump

Code: Select all

output.o:     file format elf32-littlearm


Disassembly of section .text:

00008000 <main>:
    8000:	e24dd008 	sub	sp, sp, #8
    8004:	e3a00000 	mov	r0, #0
    8008:	e58d0004 	str	r0, [sp, #4]
    800c:	e59d3004 	ldr	r3, [sp, #4]
    8010:	e2833001 	add	r3, r3, #1
    8014:	e58d3004 	str	r3, [sp, #4]
    8018:	e28dd008 	add	sp, sp, #8
    801c:	e12fff1e 	bx	lr

00008020 <_start>:
    8020:	e3a0d902 	mov	sp, #32768	; 0x8000
    8024:	ebfffff5 	bl	8000 <main>

00008028 <loop>:
    8028:	eafffffe 	b	8028 <loop>

rst
Posts: 410
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sun Oct 25, 2015 11:04 am

The address of _start is written into a field of the ELF header in the .elf file. When you use objcopy to generate a flat binary image this info is lost. The entry must be at the beginning of the first file given to the linker (or to gcc if used for linking). There is another way using a special init segment for the start code and placing it first in the linker script.

dwelch67
Posts: 955
Joined: Sat May 26, 2012 5:32 pm

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sun Oct 25, 2015 12:59 pm

linker.ld:

Code: Select all

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}
start.s

Code: Select all

.globl _start
_start:
    ldr sp,=0x8000
    bl main
    b .
main.c

Code: Select all

int main ( void )
{
    return(7);
}
Makefile

Code: Select all

one:
	arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -c main.c -o main.o
	arm-none-eabi-as start.s -o start.o
	arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -T linker.ld start.o main.o -o main.elf
	arm-none-eabi-objdump -D main.elf > main.list

two:
	arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -c main.c -o main.o
	arm-none-eabi-as start.s -o start.o
	arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -T linker.ld main.o start.o -o main.elf
	arm-none-eabi-objdump -D main.elf > main.list

three:
	arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -c main.c -o main.o
	arm-none-eabi-as start.s -o start.o
	arm-none-eabi-ld -T linker.ld start.o main.o -o main.elf
	arm-none-eabi-objdump -D main.elf > main.list

four:
	arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -c main.c -o main.o
	arm-none-eabi-as start.s -o start.o
	arm-none-eabi-ld -T linker.ld main.o start.o -o main.elf
	arm-none-eabi-objdump -D main.elf > main.list
one output

Code: Select all

00008000 <_start>:
    8000:	e3a0d902 	mov	sp, #32768	; 0x8000
    8004:	eb000000 	bl	800c <main>
    8008:	eafffffe 	b	8008 <_start+0x8>

0000800c <main>:
    800c:	e3a00007 	mov	r0, #7
    8010:	e12fff1e 	bx	lr
two output that might be your problem right there

Code: Select all

00008000 <main>:
    8000:	e3a00007 	mov	r0, #7
    8004:	e12fff1e 	bx	lr

00008008 <_start>:
    8008:	e3a0d902 	mov	sp, #32768	; 0x8000
    800c:	ebfffffb 	bl	8000 <main>
    8010:	eafffffe 	b	8010 <_start+0x8>
emphasized by these two
three output

Code: Select all

00008000 <_start>:
    8000:	e3a0d902 	mov	sp, #32768	; 0x8000
    8004:	eb000000 	bl	800c <main>
    8008:	eafffffe 	b	8008 <_start+0x8>

0000800c <main>:
    800c:	e3a00007 	mov	r0, #7
    8010:	e12fff1e 	bx	lr
four output

Code: Select all

00008000 <main>:
    8000:	e3a00007 	mov	r0, #7
    8004:	e12fff1e 	bx	lr

00008008 <_start>:
    8008:	e3a0d902 	mov	sp, #32768	; 0x8000
    800c:	ebfffffb 	bl	8000 <main>
    8010:	eafffffe 	b	8010 <_start+0x8>
the no stdlib does/perhaps gets rid of crt0.o but also understand the linker places objects in the order they are found on the command line (unless overridden by the linker script itself)

Because you are ultimately making a binary image (objcopy -O binary) the entry point is that which is defined by whatever is using that binary image in this case the first word in the binary image. so you dont need to set an entry point in the elf file it does you no good. If you were to have start not be the first thing (as with having main before start on the linker command line) and the user of this binary can deal with elf or other formats that support an entry point, then you would care to set the entry point if it is not the first word in .text (or might have to set the entry point no matter what).

thanks for this opportunity for me to learn this but _start is not important as a name in this situation (as I think was already mentioned by the other answer)

Code: Select all

.globl notstart
notstart:
    ldr sp,=0x8000
    bl main
    b .
make one and nobody complains that there is a lack of a _start symbol and the code works out fine

Code: Select all

00008000 <notstart>:
    8000:	e3a0d902 	mov	sp, #32768	; 0x8000
    8004:	eb000000 	bl	800c <main>
    8008:	eafffffe 	b	8008 <notstart+0x8>

0000800c <main>:
    800c:	e3a00007 	mov	r0, #7
    8010:	e12fff1e 	bx	lr
David

JacobL
Posts: 76
Joined: Sun Apr 15, 2012 2:23 pm

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sun Oct 25, 2015 8:55 pm

I think David's example illustrates well why you really want to have a separate section for _start. Because the symbol order within a section depends on the order it is specified on the command line then it is really easy to get it wrong, especially when the project grows bigger.

It could be done like this:

linker.ld:

Code: Select all

    MEMORY
    {
        ram : ORIGIN = 0x8000, LENGTH = 0x1000
    }
    SECTIONS
    {
        .init : { *(.init*) } > ram
        .text : { *(.text*) } > ram
        .bss : { *(.bss*) } > ram
    }
start.s:

Code: Select all

    .section .init
    .globl _start
    _start:
        ldr sp,=0x8000
        bl main
        b .
A word about specifying the entry point to the linker: This by itself has no effect on which symbol is placed first in the final binary, however you may need to specify --entry=... on the command line anyway. The linker does dead code removal as an optimisation, and without this, the linker may think that the entry symbol is unreferenced and remove it. Using --entry=... (I believe it is equivalent to specifying ENTRY(...) in the linker map) tells the linker not to remove this. Note that you can also specify KEEP() around any linker section in the script in order to prevent unreferenced symbols in it from being removed. I think GCC uses "_start" as the default for --entry though, so it might be done implicitly for you.

dwelch67
Posts: 955
Joined: Sat May 26, 2012 5:32 pm

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sun Oct 25, 2015 10:13 pm

Good points by Jacob. I was actually a bit disturbed that a lack of _start was not an error. you really want the linker to have a beginning point for dependencies. in my example start.s defines main as an external dependency. how does the toolchain know what code is dead and it can remove?
doing something like this

Code: Select all

MEMORY
{
    ram : ORIGIN = 0xA000, LENGTH = 0x1000
}

SECTIONS
{
    START : { start.o } > ram
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}
overrides the command line order, but put some ascii garbage in the file

Code: Select all

0000a000 <notstart>:
    a000:	e3a0d902 	mov	sp, #32768	; 0x8000
    a004:	eb000005 	bl	a020 <main>
    a008:	eafffffe 	b	a008 <notstart+0x8>
    a00c:	00001341 	andeq	r1, r0, r1, asr #6
    a010:	61656100 	cmnvs	r5, r0, lsl #2
    a014:	01006962 	tsteq	r0, r2, ror #18
    a018:	00000009 	andeq	r0, r0, r9
    a01c:	01080106 	tsteq	r8, r6, lsl #2

Disassembly of section .text:

0000a020 <main>:
    a020:	e3a00007 	mov	r0, #7
    a024:	e12fff1e 	bx	lr

JacobL
Posts: 76
Joined: Sun Apr 15, 2012 2:23 pm

Re: Why doesn't arm-none-eabi-gcc use my custom _start symbo

Sun Oct 25, 2015 10:31 pm

David, it looks like there is some implicit alignment of sections taking place in your example. At least, your .text starts at an address that aligns with 0x20 (32) bytes. Why the padding is seemingly weird garbage data instead of being zeroes, I don't know. Note the 32 byte cache line size on ARM, so there could be some logic to this particular alignment.

Return to “Bare metal, Assembly language”