Scott Craig and Justin Tanner

The C Runtime

(We provide two custom C runtime files: crt0.S written in assembler; and crt0.c written in C. A description of crt0.S follows.)

crt stands for C runtime. crt0.o is an object file with code that is prepended to object code supplied by the user to make an executable. It initializes variables and the stack, and starts the user's program, among other things.

Modification

We need to modify the runtime to behave differently than the usual way. We want to start executing our code at the function OS_Init() rather than main().

(Note: prior to gcc 4.x, main() was treated differently from other functions. If you want to also include a function called main(), then use a WinAVR release at least 20070525.)

An overall skeleton of our crt0.S file is shown below.

#include <avr/io.h>
#include <avr/sfr_defs.h>

#define __zero_reg__ r1

   .macro       vector name
   .weak        \name
   .set         \name, __vector_not_set
   jmp          \name
   .endm

   /* initialize the interrupt vector table */
   /* vector 0 is the "reset" vector */
   .section .vectors,"ax",@progbits
   .global      __vectors
   .func        __vectors
__vectors:
   jmp          __init
   vector       __vector_1
   vector       __vector_2
   vector       __vector_3
        ...
   vector       __vector_37
   .endfunc

   .text
   .global      __vector_not_set
   .func        __vector_not_set
__vector_not_set:
   jmp          vectors
   .endfunc

   /* beginning of our executable code */
   .section .init0,"ax",@progbits
   .weak        __init
__init:
   .weak        __stack
   .weak        __heap_end
   .set         __heap_end, 0
   .set         __stack, RAMEND

   /* initialize a default stack at the end of RAM */
   .section .init2,"ax",@progbits
   clr          __zero_reg__
   out          _SFR_IO_ADDR(SREG), __zero_reg__
   ldi          r28,lo8(__stack)
   ldi          r29,hi8(__stack)
   out         _SFR_IO_ADDR(SPH), r29
   out         _SFR_IO_ADDR(SPL), r28

   /* copy initial global data values from FLASH into RAM */
   .section .init4,"ax",@progbits
   .global __do_copy_data
__do_copy_data:
   ldi         r17, hi8(__data_end)
   ldi         r26, lo8(__data_start)
   ldi         r27, hi8(__data_start)
   ldi         r30, lo8(__data_load_start)
   ldi         r31, hi8(__data_load_start)
   ldi         r16, hh8(__data_load_start)
   out         _SFR_IO_ADDR(RAMPZ), r16
   rjmp       .L__do_copy_data_start
.L__do_copy_data_loop:
   elpm       r0, Z+
   st         X+, r0
.L__do_copy_data_start:
   cpi        r26, lo8(__data_end)
   cpc        r27, r17
   brne       .L__do_copy_data_loop

   /* now, we are ready to call our application's main program */
   .section .init9,"ax",@progbits
   call       OS_Init       /* changed "main" to "OS_Init" */
   jmp        exit          /* if returns, loop forever */

To use this modified crt0.S file instead of the default file in AVR Studio, add a linker option in Project>>Configuration Options>>Custom Options. Highlight "[Linker Options]" and enter "-nostartfiles" in the box beside the Add button. Press Add.

Default Linker Script

An explanation of how this crt0.S file is linked to our application's executable follows.

The gnu linker, ld, is invoked directly, or by executing gcc on object files. AVR Studio arranges for ld to read an appropriate linker script for the device, for example, WinAVR/avr/lib/ldscripts/avr5.x. This file describes the memory of the device, and gives the layout of the order in which the linker should place object code. It contains a list of "sections", some of which are allowed to be empty. The important sections for us are:

The .text section contains labels to where code thus labeled will go. An abridged version of the .text section of avr5.x is:

    *(.vectors)
    *(.init0)  /* Start here after reset.  */
    *(.init1)
    *(.init2)  /* Clear __zero_reg__, set up stack pointer.  */
    *(.init3)
    *(.init4)  /* Initialize data and BSS.  */
    ...
    *(.init9)  /* Call main().  */
    *(.text)
    *(.fini9)  /* _exit() starts here.  */
    ...
    *(.fini0)  /* Infinite loop after program termination.  */

The asterisks indicate the sections may be empty. We need a .vectors section to indicate where the interrupt service routines reside; some initialization sections, .init0 to .init9 ; a section for most code, called .text; and some finalization sections, .fini9 down to .fini0 .

The .data and .bss sections come next. The .data section contains the initialization values for variables declared with initializers in the C code. The .bss section is for uninitialized variables.

  .data     : AT (ADDR (.text) + SIZEOF (.text))
  {
     PROVIDE (__data_start = .) ;
    *(.data)
    *(.gnu.linkonce.d*)
    . = ALIGN(2);
     _edata = . ;
     PROVIDE (__data_end = .) ;
  }  > data
  .bss  SIZEOF(.data) + ADDR(.data) :
  {
     PROVIDE (__bss_start = .) ;
    *(.bss)
    *(COMMON)
     PROVIDE (__bss_end = .) ;
  }  > data
   __data_load_start = LOADADDR(.data);
   __data_load_end = __data_load_start + SIZEOF(.data);

After the data from the various object files are arranged in these sections, the linker creates the symbols __data_start, etc., by setting them equal to the address at which the symbol appears in the layout via a statement such as __data_start = ., or with an arithmetic assignment. These six symbols are available for the object files to use. (We use them in our crt0.S.)

Normally, the linker looks to find the necessary initialization functions in an object file relevant to the hardware, such as in WinAVR/avr/lib/avr5/crtusb1287.o. This file is compiled from avr-libc/crt1/gcrt1.S with the appropriate #defines set by the linker. This file needs #defines included in avr-libc/common/macros.inc and avr-libc/include/avr/common.h. Our custom crt0.S was created by modifying these files.

The linker also looks in libgcc.a for some initialization code in the gcc compiler's libgcc.a. Here reside default implementations of the routines:

The version of __do_copy_data given in libgcc.S is the one for the 64 K Flash versions of avr chips. This routine needs to be overridden for chips with 128 K of flash, such as the AT90USB1287. There is another version of this routine in gcrt1.S, which needs to be duplicated in crt0.S. The routine makes use of the locations in memory of the data provided by the linker from the load script.

crt0.S also needs a routine to initialize the stack pointer, and to initialize the "zero register." The gcc compiler requires the second register, r1, to be 0, in order to speed up some machine instructions. These are provided in gcrt1.S, and so in the modified crt0.S.

__do_clear_bss and _exit are fine as they are in libgcc.o.

All these routines are placed in the correct sections by virtue of a section declaration such as .section .init4,"ax",@progbits above the routine in the assembler file. It is also possible to place compiled C code in particular sections other than .text. See http://www.nongnu.org/avr-libc/user-manual/mem_sections.html for a discussion. (This is what we do in crt0.c.)