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 withconst
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().
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 A15 register to manage this stack. A15 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 c29lnk command (use -Wl, or -Xlinker prefix for the linker option if invoking the linker from c29clang, 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.
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 c29ofd - 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 c29lnk command ((use -Wl, or -Xlinker prefix for the linker option if invoking the linker from c29clang, 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.