Search code examples
gccbare-metal

How to explicitly assign a section in C code (like .text, .init, .fini) (mainly for arm)?


I am trying to make an embedded system. I have some C code, however, before the main function runs, some pre-initialization is needed. Is there a way to tell the gcc compiler, that a certain function is to be put in the .init section rather than the .text section?

this is the code:

#include <stdint.h>

#define REGISTERS_BASE 0x3F000000
#define MAIL_BASE 0xB880  // Base address for the mailbox registers
// This bit is set in the status register if there is no space to write into the mailbox
#define MAIL_FULL 0x80000000
// This bit is set in the status register if there is nothing to read from the mailbox
#define MAIL_EMPTY 0x40000000

struct Message
{
  uint32_t messageSize;
  uint32_t requestCode;
  uint32_t tagID;
  uint32_t bufferSize;
  uint32_t requestSize;
  uint32_t pinNum;
  uint32_t on_off_switch;
  uint32_t end;
};

struct Message m =
{
  .messageSize = sizeof(struct Message),
  .requestCode =0,
  .tagID = 0x00038041,
  .bufferSize = 8,
  .requestSize =0,
  .pinNum = 130,
  .on_off_switch = 1,
  .end = 0,
};

void _start()
{
  __asm__
  (
    "mov sp, #0x8000 \n"
    "b main"
  );
}

/** Main function - we'll never return from here */
int main(void)
{
  uint32_t mailbox = MAIL_BASE + REGISTERS_BASE + 0x18;
  volatile uint32_t status;

  do
  {
    status = *(volatile uint32_t *)(mailbox);
  }
  while((status & 0x80000000));

  *(volatile uint32_t *)(MAIL_BASE + REGISTERS_BASE + 0x20) = ((uint32_t)(&m) & 0xfffffff0) | (uint32_t)(8);

  while(1);
}

EDIT: using __attribute__(section("init")) doesn't seem to be working


Solution

  • Dont understand why you think you need a .init section for baremetal. A complete working example for a pi zero (using .init)

    start.s

    .section .init
    
    .globl _start
    _start:
        mov sp,#0x8000
        bl centry
        b .
    

    so.c

    unsigned int data=5;
    unsigned int bss;
    
    unsigned int centry ( void )
    {
        return(0);
    }
    

    so.ld

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

    build

    arm-none-eabi-as start.s -o start.o
    arm-none-eabi-gcc -O2 -c so.c -o so.o
    arm-none-eabi-ld -T so.ld start.o so.o -o so.elf
    arm-none-eabi-objdump -D so.elf
    
    Disassembly of section .init:
    
    00008000 <_start>:
        8000:   e3a0d902    mov sp, #32768  ; 0x8000
        8004:   eb000000    bl  800c <centry>
        8008:   eafffffe    b   8008 <_start+0x8>
    
    Disassembly of section .text:
    
    0000800c <centry>:
        800c:   e3a00000    mov r0, #0
        8010:   e12fff1e    bx  lr
    
    Disassembly of section .bss:
    
    00008014 <bss>:
        8014:   00000000    andeq   r0, r0, r0
    
    Disassembly of section .data:
    
    00008018 <data>:
        8018:   00000005    andeq   r0, r0, r5
    

    Note if you do it right you dont need to init .bss in the bootstrap (put .data after .bss and make sure there is at least one item in .data)

    hexdump -C so.bin
    00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 00 00 a0 e3  |................|
    00000010  1e ff 2f e1 00 00 00 00  05 00 00 00              |../.........|
    0000001c
    

    if you want them in separate places then yes your linker script gets instantly more complicated as well as your bootstrap (with lots of room for error).

    The only thing the extra work that .init buys you here IMO is that you can re-arrange the linker command line

    arm-none-eabi-ld -T so.ld so.o start.o -o so.elf
    

    get rid of .init all together

    Disassembly of section .text:
    
    00008000 <_start>:
        8000:   e3a0d902    mov sp, #32768  ; 0x8000
        8004:   eb000000    bl  800c <centry>
        8008:   eafffffe    b   8008 <_start+0x8>
    
    0000800c <centry>:
        800c:   e3a00000    mov r0, #0
        8010:   e12fff1e    bx  lr
    
    Disassembly of section .bss:
    
    00008014 <bss>:
        8014:   00000000    andeq   r0, r0, r0
    
    Disassembly of section .data:
    
    00008018 <data>:
        8018:   00000005    andeq   r0, r0, r5
    

    No issues, works fine. Just have to know that with gnu ld (and probably others) if you dont call out something in the linker script then it fills things in in the order presented (on the command line).

    Whether or not a compiler uses sections or what they are named is compiler specific, so you have to dig into the compiler specific options to see if there are any to change the defaults. Using C to bootstrap C is more work than it is worth, gcc will accept assembly files if it is a Makefile issue you are having problems with, very rarely is there a reason to use inline assembly when you can use real assembly and have something more reliable and maintainable. In real assembly then these things are trivial.

    .section .helloworld
    
    .globl _start
    _start:
        mov sp,#0x8000
        bl centry
        b .
    
    
    Disassembly of section .helloworld:
    
    00008000 <_start>:
        8000:   e3a0d902    mov sp, #32768  ; 0x8000
        8004:   ebfffffd    bl  8000 <_start>
        8008:   eafffffe    b   8008 <bss>
    
    Disassembly of section .text:
    
    00008000 <centry>:
        8000:   e3a00000    mov r0, #0
        8004:   e12fff1e    bx  lr
    
    Disassembly of section .bss:
    
    00008008 <bss>:
        8008:   00000000    andeq   r0, r0, r0
    
    Disassembly of section .data:
    
    0000800c <data>:
        800c:   00000005    andeq   r0, r0, r5
    

    real assembly is generally used for bootstrapping, no compiler games required, no needing to go back and maintain the code every so often because of compiler games, porting is easier, etc.