OS section organization

gregory.menke at gsfc.nasa.gov gregory.menke at gsfc.nasa.gov
Thu May 8 22:07:51 UTC 2003


Brett Swimley writes:
 > Thanks Greg, 
 > 
 > This is exactly what I want to do, but I'm not sure about how to identify the
 > desired code within my linker script.

The following is a quick brain dump from what I learned when doing
this kind of thing.  I'm sorry about the length, but its a tricky
issue.


Some of our flight code is organized into subsystems, one per
directory.  The linker script then assigns each subsystem its own
chunk of ram for text, data and bss.  By making the chunk of ram
bigger than the subsystem needs, it allows the operators to patch the
in-memory running image on a subsystem by subsystem basis.  OTOH it
makes for a truly byzantine script.  The upshot is if you can get all
your app's .o files into a subdirectory beneath the directory with the
makefile, then you can grab your .o's with a single wildcard.
Otherwise, you may end up having to enumerate all your files in the
script, which is a drag.  Be sure to follow your specific .o sections
with the general wildcard sections, which will pick up all of the
RTEMS kernel and library code, as well as anything else you've not
allocated already.  Its possible to use regexps to control membership
in wildcard results, but I've not pursued doing so.  There may also be
bsp_spec issues with your bsp, but that issue, if it exists, is
trivial to solve, so lets leave that aside.

We tend to divide text, data & bss into "userspace", then OS and then
C library, and putting them where we choose.  In your case, I imagine
you want to put frequently used stuff in your fast ram, which might
conceivably end up being a combination of userspace, OS and library.

Below is an extract from a linker script I cooked up for a proof of
concept some time ago.  Its difficult to follow, so don't read it in
detail just yet.  There are a couple things to observe; First, in the
MEMORY block a number of sections are defined along with their
lengths.  I imagine you would define one section for your fast ram and
another for regular dram.  Please note, this is only for C, you'll
need to fancy it up a bit for C++, but that only means you'll need a
few more magically named sections, some of which must be in particular
sequences.  Use the RTEMS link script for your bsp as a reference.

As you can see further on in the example, several subsystems put their
.text, .data and .bss into the section I chose for them.  Of
particular interest is the .bootstrap section, this is where the
bootloader is located & it must be smart enough to copy sections from
the rom image into the right ram area.  This means symbols
representing the eeprom and ram addresses must be defined for
everything thats going to be copied.  The _*_rom_start and _*_rom_end
entries correspond to the eeprom addresses, the _*_module_start and
_*_module_end entries within the individual sections are the
destination RAM addresses.  All of them are addressed as pointers to
32 bit unsigned integers;

extern unsigned32 _bootstrap_rom_start;

for example.  If you wanted to start copying the .bootstrap section
from eprom into ram, you'd use &_bootstrap_rom_start and
&_bootstrap_start as source and destination addresses.  They don't
exist as symbols, as I understand it, they are immediate values that
the linker inserts wherever referenced.  You'll also observe that only
.text and .data sections are rigged to be copied from eeprom.  The
other sections exist only as ranges of memory that the application is
linked to use- though the bootloader should make sure they're cleared.
The various _* symbols identify the addresses of those sections,
you'll see references to them in various places within RTEMS.

The standard RTEMS bootloaders are smart enough to copy the whole
image in one contiguous chunk of something rom-like to another
contiguous chunk of ram, so they won't be suitable in a situation like
this.  Therefore, you'll have to supply your own link script and a
replacement start.S.  I found it was easiest to modify the bsp's
start.S so it copies only the .bootstrap section into target ram, then
jump to it in ram and it handles copying in the rest of the
application using C, then return back into start.S and continue the
RTEMS startup.  It is possible to use C in a very restricted manner at
this stage of bootup, you can't call any library functions, only
fiddle around with pointers and loops- but it sure beats writing the
copy loops in assembly.

You don't have to modify the bsp link script and start.S, there are
linker flags that will let you supercede the bsp versions with yours,
that way you can cm the script and start code with the rest of your
source- and you won't get plastered when you upgrade RTEMS later on.

There is one big proviso for this kind of thing, your app will be very
sensitive to bugs in gcc, binutils or linker.  I've seen a number of
occasions where something in the compiler toolchain has introduced an
offset in the linker symbol values or emitted a few bytes of extra
data into the image, throwing everything off.  This is extremely hard
to to find and fix, so be sure you can easily relink with the standard
link script.  You may also end up running to problems with link
scripts that generate executables that look just fine (even when
examined by objdump), but won't run.  This is sometimes due to hidden
issues related to the order of sections, or maybe you have something
extra or something missing.  The example below is deceptively simple.
You shouldn't have much trouble with re-ordering .text .data or .bss
internally, but when you start mixing them, things can get difficult.



======================================================================

/* set these to .osdram so all workspace stuff will end up there */

_RamBase = DEFINED(_RamBase) ? _RamBase : 0x81500000;
_RamSize = DEFINED(_RamSize) ? _RamSize : 1M;

HeapSize = DEFINED(HeapSize) ? HeapSize : 0x20000;
_StackSize = DEFINED(_StackSize) ? _StackSize : 0x1000;
ClockRate = DEFINED(ClockRate) ? ClockRate : 12000000;




MEMORY
{
  /***********************************************************************/
  /*  DRAM Memory Sections                                               */
  /***********************************************************************/ 
   .appdram           : ORIGIN = 0x80B00000, LENGTH = 0x600000  /*  6MB  */
   .osdram            : ORIGIN = 0x81100000, LENGTH = 0x400000  /*  4MB  */
}


/* eof */



SECTIONS
{ 
  /* 
  ** this is kind of ugly, but it makes sure we get the the startup code
  ** layout table and task list first in the LMA region.
  */
  .bootstrap : AT(_lma_target_address)
  { 
    _bootstrap_start = .;
    _ftext = .;
    linkcmds/start.o(.text)
    linkcmds/tasklib.o
    os/taskList.o
    os/start/rtems.o
    . = . + 0x100;
    _bootstrap_end = .;
  } > .appdram 
  _bootstrap_rom_start = LOADADDR(.bootstrap);
  _bootstrap_rom_end  = _bootstrap_rom_start + SIZEOF(.bootstrap);



/*
 * If any of the task sections need to be updated, modify 'rc.template'
 * accordingly, then run it & paste the output into this link script,
 * replacing the contents below.  This will ensure all the tasks have a
 * consistent layout.
 */




/****** BEGIN PROGRAM GENERATED LINKSCRIPT, DO NOT EDIT BELOW ******/


  /***************  ci TASK  ***************/
  
  .text_ci ALIGN(0x8000) : AT( _bootstrap_rom_end )
  { 
    _ci_module_start = .;
    _ci_text_start = .;
        _app_ram_start = .;
    ci/*.o(.text) 
    ci/*.o(.rodata)
    ci/*.o(.rodata1)
    ci/*.o(.reginfo)
    . = . + 0x100;
    _ci_text_end = .;
  } > .appdram 
      _app_rom_start = _bootstrap_rom_end;
  _ci_rom_block_start = _bootstrap_rom_end;
  _ci_rom_text_start = _ci_rom_block_start;
  _ci_rom_text_end = _ci_rom_text_start + SIZEOF(.text_ci);
  
  .data_ci : AT( _ci_rom_text_end )
  { 
    . = ALIGN(4);
    _ci_data_start = .;
    ci/*.o(.data) 
    . = . + 0x100;
    _ci_data_end = .;
  } > .appdram 
  _ci_rom_data_start = _ci_rom_text_end;
  _ci_rom_data_end   = _ci_rom_data_start + SIZEOF(.data_ci);
  
  .bss_ci : 
  {
    . = ALIGN(4);
    _ci_bss_start = .;
    ci/*.o(.dynbss)
    ci/*.o(.bss)
    ci/*.o(COMMON)
    ci/*.o(.sbss)
    ci/*.o(.scommon)
    . = . + 0x400;
    _ci_bss_end = .;
    _ci_module_end = .;
  } > .appdram
  _ci_rom_bss_start = _ci_rom_data_end;
  _ci_rom_bss_end   = _ci_rom_bss_start;
  _ci_rom_block_end = _ci_rom_bss_end;




  /*************************** Kernel & C library **************************/


  .text : AT( _os_rom_end )
  { 
   . = ALIGN(8);
    _kernel_module_start = .;
    _kernel_text_start = .;
    *(.text)

    PROVIDE (__runtime_reloc_start = .);
    *(.rel.sdata)
    *(.rel.dyn)
    PROVIDE (__runtime_reloc_stop = .);

    _kernel_text_end = .;
  } > .osdram
  _kernel_rom_start = _os_rom_end;
  _kernel_rom_text_start = _kernel_rom_start;
  _kernel_rom_text_end = _kernel_rom_text_start + SIZEOF(.text);


  .ctors : AT( _kernel_rom_text_end )
  {
   . = ALIGN(8);

    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */

    KEEP (*crtbegin.o(.ctors))

    /* We don't want to include the .ctor section from
       from the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */

    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  } >.osdram
  _kernel_rom_ctors_start = _kernel_rom_text_end;
  _kernel_rom_ctors_end = _kernel_rom_ctors_start + SIZEOF(.ctors);


  .dtors : AT( _kernel_rom_ctors_end )
  {
   . = ALIGN(8);
    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
    etext  =  .;
    _etext  =  .;
  } >.osdram
  _kernel_rom_dtors_start = _kernel_rom_ctors_end;
  _kernel_rom_dtors_end = _kernel_rom_dtors_start + SIZEOF(.dtors);





  .rdata : AT( _kernel_rom_dtors_end )
  {
    _rdata = ALIGN(16);
    *(.rdata)
    *(.rodata)
    *(.rodata1)
    *(.rodata.*)
    *(.gnu.linkonce.r*)
  } >.osdram
  _kernel_rom_rdata_start = _kernel_rom_dtors_end;
  _kernel_rom_rdata_end = _kernel_rom_rdata_start + SIZEOF(.rdata);


  .data : AT(_kernel_rom_rdata_end)
  {
    . = ALIGN(16);
    _fdata = .;
    *(.data)
    *(.data.*)
    *(.gnu.linkonce.d*)
  } >.osdram
  _kernel_rom_data_start = _kernel_rom_rdata_end;
  _kernel_rom_data_end = _kernel_rom_data_start + SIZEOF(.data);




/* 

gp relative addresses are always via negative offset from the top of a
0x7ff0 byte long range.  There is presumably a good reason for this.
The upshot is the start of the gp-relative section must be 16 byte
aligned, with _gp set to a location 0x7ff0 higher in memory.  This rule
was derived from looking at the canonical GNU MIPS linkscripts &
objdumps of compiled code.  It might change with some upgrade to gcc.

This means _gp itself will be pointing into the middle of something
presumably .bss, because the gp section wouldn't ordinarily be big
enough for _gp to actually point to its top.  BUT nothing is stomped on
because _gp offsets are always negative relative to gp's 0x7ff0 logical
extent and gp is never used to address data which was not allocated to
the gp relative sections.  Sheesh.

*/

  . = ALIGN(16);
  _gp = . + 0x7ff0;

  .sdata : AT(_kernel_rom_data_end)
  {
    *(.sdata)
    *(.sdata)
    *(.gnu.linkonce.s*)
  } >.osdram 
  _kernel_rom_sdata_start = _kernel_rom_data_end;
  _kernel_rom_sdata_end = _kernel_rom_sdata_start + SIZEOF(.sdata);


  .lit8 : AT(_kernel_rom_sdata_end)
  {
    *(.lit8)
  } >.osdram
  _kernel_rom_lit8_start = _kernel_rom_sdata_end;
  _kernel_rom_lit8_end = _kernel_rom_lit8_start + SIZEOF(.lit8);


  .lit4 : AT(_kernel_rom_lit8_end)
  {
    *(.lit4)
  } >.osdram 
  _kernel_rom_lit4_start = _kernel_rom_lit8_end;
  _kernel_rom_lit4_end = _kernel_rom_lit4_start + SIZEOF(.lit4);
  _kernel_rom_end = _kernel_rom_lit4_end;



  .sbss :
  {
   _clear_start = .;
   edata  = .;
   _edata  = .;
   _fbss = .;
   *(.sbss)
   *(.scommon)
  } >.osdram 


  .bss :
  {
    _bss_start = .;
    *(.bss)
    *(.reginfo)
    *(COMMON)
    _kernel_module_end = .;
    end = .;
    _end = .;
  } >.osdram


  .stack_heap : 
  {
    . = ALIGN(64);

    _stack_limit = .;
    . += _StackSize;
    __stack = .;
    _stack_init = .;

    HeapBase = .;
    . += HeapSize;

    WorkspaceBase = .;
    . += 0x100000;
    _clear_end = .;
  } >.osdram

===================================================================


/*

Task copy routines


Note we don't include any c library calls here because the C lib isn't
initialized yet; we're using C only as a fancy assembler at this stage.

*/


/*
 * Cheat a bit & grab the declaration of the layout table...
 */



extern struct romramtable taskMemLayout[];



/*
 * Copy rom image to ram for the given entry of the taskMemLayout table.
 *
 */

int _copytask(int i, int textonly)
{
   unsigned int *src, *dst, *lim;

   int j = 0;

   //extern int putchar(int c);
   //for(k=0;k<10; k++) putchar('0');
   //putchar('\n');

   // run up thru the entry we want, make sure we don't pass the end of
   // the table...
   while( taskMemLayout[j].ram_module_start && j<i ) j++;

   if( taskMemLayout[j].ram_module_start )
   {
      // copy text
      src = (unsigned int *)taskMemLayout[j].rom_text_start;
      dst = (unsigned int *)taskMemLayout[j].ram_text_start;
      lim = (unsigned int *)taskMemLayout[j].ram_text_end;
      while( dst <= lim ){ *dst++ = *src++; }


      if( !textonly )
      {
         // zero the bss & data
         dst = (unsigned int *)taskMemLayout[j].ram_data_start;
         lim = (unsigned int *)taskMemLayout[j].ram_bss_end;
         while( dst <= lim ) { *dst++ = 0; }

         // copy data
         src = (unsigned int *)taskMemLayout[j].rom_data_start;
         dst = (unsigned int *)taskMemLayout[j].ram_data_start;
         lim = (unsigned int *)taskMemLayout[j].ram_data_end;
         while( dst <= lim ){ *dst++ = *src++; }
      }
   }
   else 
      return 0;


   return 1;    // success
}






/*
 * Copy OS rom image to ram
 *
 */

void _copyos(void)
{
   extern unsigned int _os_module_start, _os_module_end;
   extern unsigned int _os_text_start, _os_text_end, _os_data_start, _os_data_end;
   extern unsigned int _os_rom_text_start, _os_rom_data_start; 

   unsigned int *src, *dst, *lim;

   // zero the destination space (text, data, bss)

   dst = (unsigned int *)&_os_module_start;
   lim = (unsigned int *)&_os_module_end;
   while( dst <= lim ) { *dst++ = 0; }

   // copy text
   src = (unsigned int *)&_os_rom_text_start;
   dst = (unsigned int *)&_os_text_start;
   lim = (unsigned int *)&_os_text_end;
   while( dst <= lim ) { *dst++ = *src++; };

   // copy data
   src = (unsigned int *)&_os_rom_data_start;
   dst = (unsigned int *)&_os_data_start;
   lim = (unsigned int *)&_os_data_end;
   while( dst <= lim ) { *dst++ = *src++; }

   return;
}






/*
 * Copy kernel ROM image to ram
 *
 */

void _copykernel(void)
{
   extern unsigned int _kernel_module_start, _kernel_module_end;
   extern unsigned int _clear_start, _clear_end;
   extern unsigned int _kernel_rom_start,_kernel_rom_end;

   unsigned int *src, *dst, *lim;

   // zero the destination space
   dst = (unsigned int *)&_kernel_module_start;
   lim = (unsigned int *)&_kernel_module_end;
   while( dst <= lim ){ *dst++ = 0; }

   // zero the bss, heap & workspace
   dst = (unsigned int *)&_clear_start;
   lim = (unsigned int *)&_clear_end;
   while( dst <= lim ){ *dst++ = 0; }
   
   // copy kernel image (note we don't use the ram address to terminate
   // because the ram space is bloated w/ bss- ROM is only text & data
   src = (unsigned int *)&_kernel_rom_start;
   lim = (unsigned int *)&_kernel_rom_end;
   dst = (unsigned int *)&_kernel_module_start;
   while( src <= lim ){ *dst++ = *src++; }

   return;
}


/* eof */

============================================================




More information about the users mailing list