3.1. Memory Model

The compiler treats memory as a single linear block that is partitioned into subblocks of code and data. Each subblock of code or data generated by a C program is placed in its own continuous memory space. The compiler assumes that a full 32-bit address space is available in target memory.

Note

The Linker Defines the Memory Map

The linker, not the compiler, defines the memory map and allocates code and data into target memory. The compiler assumes nothing about the types of memory available, about any locations not available for code or data (holes), or about any locations reserved for I/O or control purposes. The compiler produces relocatable code that allows the linker to allocate code and data into the appropriate memory spaces. For example, you can use the linker to allocate global variables into on-chip RAM or to allocate executable code into external ROM. You can allocate each block of code or data individually into memory, but this is not a general practice (an exception to this is memory-mapped I/O, although you can access physical memory locations with C/C++ pointer types).

3.1.1. Sections

The compiler produces relocatable blocks of code and data called sections. The sections are allocated into memory in a variety of ways to conform to a variety of system configurations. For more information about sections and allocating them, see Introduction to Object Modules.

There are two basic types of sections:

  • Initialized sections contain data or executable code. Initialized sections are usually, but not always, read-only. The C/C++ compiler creates the following initialized sections:

    • The .binit section contains linker-generated boot time copy tables. This is a read-only section. For details on BINIT, see Boot-Time Copy Tables.

    • The .cinit section contains auto-initialization records for global variables. See Automatic Initialization of Variables for more information. The .cinit section is read-only.

    • The .pinit section contains a table of pointers to global constructor functions to be run at system boot time. See Automatic Initialization of Variables for more information. The .pinit section is read-only.

    • The .init_array section contains a table of pointers to global constructor functions for a dynamic shared object. This section is a read-only section.

    • The .fini_array section contains a table of pointers to global destructor functions for a dynamic shared object. This section is read-only.

    • The .ovly section contains linker-generated copy tables for unions in which different sections have the same run address. See Linker-Generated Copy Table Sections and Symbols for an example of copy tables used in conjunction with a UNION in a linker command file. The .ovly section is read-only.

    • The .data section contains initialized non-const global and static variables. This .data section is read-write.

    • The .rodata section contains read-only data, typically string constants and static-scoped objects defined with the C/C++ qualifier const. Note that not all static-scoped objects marked with const are placed in the .rodata section (see const Keyword).

    • The .text section contains all the executable code. It also contains string literals, switch tables, and compiler-generated constants. This section is usually read-only. Note that some string literals may instead be placed in .rodata.str sections.

    • The .TI.crctab section contains CRC checking tables. This is a read-only section.

  • Uninitialized sections reserve space in memory (usually RAM). A program can use this space at run time to create and store variables. The compiler creates the following uninitialized sections:

    • The .bss section reserves space for uninitialized global and static variables. Uninitialized variables that are also unused are usually created as common symbols (unless you specify --common=off) instead of being placed in .bss so that they can be excluded from the resulting application.

    • The .stack section reserves memory for the C/C++ software stack.

    • The .sysmem section reserves space for dynamic memory allocation. This space is used by dynamic memory allocation routines, such as malloc(), calloc(), realloc(), or new().

The .ARM.exidx and .ARM.extab sections are generated if C++ exceptions are enabled. These are initialized sections.

The assembler creates the default .text section, even if it is empty. You can instruct the compiler to create additional sections by using the section function and variable attributes.

The linker takes the individual sections from different object files and combines sections that have the same name. The resulting output sections and the appropriate placement in memory for each section are listed in the following table. You can place these output sections anywhere in the address space as needed to meet system requirements.

Summary of Sections and Memory Placement

Section

Type of Memory

Section

Type of Memory

.bss

RAM

.fini_array

ROM or RAM

.cinit

ROM or RAM

.pinit

ROM or RAM

.rodata

ROM or RAM

.stack

RAM

.data

RAM

.sysmem

RAM

.init_array

ROM or RAM

.text

ROM or RAM

You can use the SECTIONS directive in the linker command file to customize the section-allocation process. For more information about allocating sections into memory, see Linker Description.

3.1.2. C/C++ System Stack

The C/C++ compiler uses a stack to:

  • Allocate local variables

  • Pass arguments to functions

  • Save register contents

The run-time stack grows from the high addresses to the low addresses.

The compiler uses the R13 register to manage this stack. R13 is the stack pointer (SP), which points to the next unused location on the stack.

The linker sets the stack size, creates a global symbol, __TI_STACK_SIZE, and assigns it a value equal to the stack size in bytes. The default stack size is 2048 bytes. You can change the stack size at link time by using the --stack_size option with the tiarmlnk command (use -Wl, or -Xlinker prefix for the linker option if invoking the linker from tiarmclang, e.g. -Wl,--stack_size=256). For more information on the --stack_size option, see Linker Description.

At system initialization, SP is set to a designated address for the top of the stack. This address is the first location past the end of the .stack section. Since the position of the stack depends on where the .stack section is allocated, the actual address of the stack is determined at link time.

The C/C++ environment automatically decrements SP at the entry to a function to reserve all the space necessary for the execution of that function. The stack pointer is incremented at the exit of the function to restore the stack to the state before the function was entered. If you interface assembly language routines to C/C++ programs, be sure to restore the stack pointer to the same state it was in before the function was entered.

For more information about using the stack pointer, see Register Conventions; for more information about the stack, see Function Structure and Calling Conventions.

To debug issues related to the stack size, we recommend using the CCS Stack Usage view to see the static stack usage of each function in the application. See Stack Usage View in CCS for more information. Using the Stack Usage View requires that source code be built with debug enabled. This feature relies on the –call_graph capability provided by the tiarmofd - Object File Display Utility.

Note

Stack Overflow and Stack Smashing Detection

A stack overflow disrupts the run-time environment, causing your program to fail. Be sure to allow enough space for the stack to grow. You can use the -finstrument-functions option to add code to the beginning of each function to check for stack overflow. See Function Entry/Exit Hook Options for more information.

Stack smashing occurs when a given function writes past the stack space that has been allocated for it. You can use the fstack-protector option to enable stack smashing detection for your application. See Stack Smashing Detection Options for more information.

3.1.3. Dynamic Memory Allocation

The run-time-support library supplied with the compiler contains several functions (such as malloc, calloc, and realloc) that allow you to allocate memory dynamically for variables at run time.

Memory is allocated from a global pool, or heap, that is defined in the .sysmem section. You can set the size of the .sysmem section by using the --heap_size=<n> option with the tiarmlnk command ((use -Wl, or -Xlinker prefix for the linker option if invoking the linker from tiarmclang, e.g. -Wl,--heap_size=1024). The linker also creates a global symbol, __TI_SYSMEM_SIZE, and assigns it a value equal to the size of the heap in bytes. The default size is 2048 bytes. For more information on the --heap_size option, see Linker Description.

If you use any C I/O function (e.g. printf), the RTS library allocates an I/O buffer for each file you access. This buffer will be a bit larger than BUFSIZ, which is defined in stdio.h and defaults to 256. Make sure you allocate a heap large enough for these buffers or use setvbuf() to change the buffer to a statically-allocated buffer.

Dynamically allocated objects are not addressed directly (they are always accessed with pointers) and the memory pool is in a separate section (.sysmem); therefore, the dynamic memory pool can have a size limited only by the amount of available memory in your system. To conserve space in the .bss section, you can allocate large arrays from the heap instead of defining them as global or static. For example, instead of a definition such as:

struct big table[100];

Use a pointer and call the malloc function:

struct big *table;
table = (struct big *)malloc(100*sizeof(struct big));

Warning

When allocating from a heap, make sure the size of the heap is large enough for the allocation. This is particularly important when allocating variable-length arrays.