The C28x Code Generation Tools now support an ELF-based ABI, which allows support for new features such as shared object files. This new ELF ABI is referred to as "EABI".
This document describes changes that should be made when migrating existing C28x COFF ABI libraries and applications to use the C28x EABI. This document is not an overview of EABI or new features available only in EABI. For information about C28x EABI, see the following documents:
- C28x Embedded Application Binary Interface Application Report (SPRAC71)
- TMS320C28x Optimizing C/C++ Compiler User's Guide (SPRU514 version R or later)
- TMS320C28x x Assembly Language Tools User's Guide (SPRU513 version R or later)
This document is intended for object library vendors and developers who have been supporting COFF and wish to migrate their code base. The focus of this document is on migrating COFF ABI applications to EABI or producing code that works equally well with both COFF ABI and EABI.
Most Common EABI Migration Issues
While this document details changes between COFF ABI and EABI and the changes needed to support both, most users only need to perform a few changes to their code to move from COFF to ELF. The most common issues you are likely to encounter are:
- The
double
type is 64 bits. The size of double changes from 32 bits to 64 bits when you migrate from COFF to EABI. - There is no leading underscore on symbols. COFF adds a leading underscore to symbol names, but EABI does not. Assembly file references to symbols need special handling.
Migration Strategies
There are various migration strategies to choose from.
Do You Need to Migrate?
Before converting a COFF project to ELF, consider whether you need any EABI features. A working COFF program need not be converted to ELF immediately unless ELF-only features are needed. COFF will continue to be supported for some time. We encourage our customers to migrate applications to EABI for systems that are actively being developed.
Will COFF Support be Eliminated?
ELF and EABI will eventually completely displace COFF and the COFF ABI; however, COFF will continue to be supported for some time. COFF ABI support will be phased out slowly.
- Most new compiler features will only be supported in EABI mode going forward. Some features like dynamic linking cannot be added to COFF due to limitations of the COFF format.
- TI continuously improves the C2000 device family by introducing
advanced C2000 ISAs. For new ISAs, the compiler defaults to EABI mode. You must
use the
--abi=coffabi
option to build COFF ABI objects for new ISAs. - At some point in the future, new C2000 family ISAs will only be supported in EABI mode.
- Later, at a time chosen based on feedback from our customers, we will completely stop supporting COFF ABI in new compiler releases. At this point users will be required to use older versions of the compiler to compile for COFF ABI.
Supporting Both COFF and ELF
By using conditional compilation judiciously, it is easy to make code work for both COFF and ELF. However, you will need to compile two sets of object files, because it is not possible to link COFF and ELF object files together.
Conditional Compilation Using a Predefined Symbol
Both the compiler and assembler pre-define the symbol __TI_EABI__
to indicate that source is being compiled under EABI. This option is
defined when the --abi=eabi
option is specified. If C code or
assembly code cannot be written in a way that works for both COFF ABI
and EABI, use this symbol to conditionally compile the appropriate
version of the code.
#if defined(__TI_EABI__)
static char abi[] = "EABI";
#else
static char abi[] = "COFF ABI";
#endif
printf("ABI used: %s\n", abi);
Recommendations for Library Vendors
Library vendors are encouraged to distribute both COFF and ELF versions of each library. For portably written C code, the effort to support both COFF and ELF is minor. For assembly code, the effort is typically a matter of renaming global symbols using conditional compilation.
Creating an Index Library that Includes both COFF and EABI Libraries
Use the libinfo2000 tool to create an index library (like the RTS index
libc.a
) that can be linked against instead of directly linking to a COFF
or EABI-specific library. The linker then uses the index library to
choose the appropriate version of the library to use.
Dealing With COFF-Only Object Libraries
To convert an object file from COFF ABI to EABI, it is strongly recommended that you have access to at least the assembly code so that it can be appropriately modified and reassembled. If you do not have source code, for example because you only have an object library from a vendor, the best choices are to either leave the application as a COFF ABI application or to request that the vendor release an EABI version.
There is no tool for converting a COFF object file to an ELF object file; reverse-engineering the assembly code by using a disassembler is error-prone and could violate licensing agreements for some packages.
C and C++ Code Changes
Programs written entirely in C or C++ have the easiest migration path. Portably written C and C++ code may not need any changes at all, so such code can be shared unmodified between COFF and ELF projects.
Maximally portable C and C++:
- Does not rely on exact sizes of types beyond what the C standard guarantees
- Does not assume a particular bit-field layout
- Does not assume a particular enum type size
- Does not use intrinsics
- Does not use asm() statements
If your code avoids non-portable assumptions, the code may be reused without modification or inspection. Code that does make any of these assumptions needs to be examined to determine if the code will behave differently for EABI and COFF ABI. This section describes areas where EABI and COFF ABI differ with regard to C and C++ language features.
The double
Type is 64 Bits in EABI
Floating-point types have the following widths in COFF and EABI.
Type | COFF | EABI |
---|---|---|
float | 32 bits | 32 bits |
double | 32 bits | 64 bits |
long double | 64 bits | 64 bits |
Notice that the double
floating-point type is 64
bits wide in the EABI model, but 32 bits wide in the COFF ABI
model. To make your code portable between COFF ABI and EABI, you should declare
32-bit floating-point variables as float
, not as double
.
This change to type sizes when migrating is true for both CLA and C28x. For CLA we provide basic arithmetic helper functions such as add, multiply, etc., but do not provide functions defined in math.h, since these do not exist for 32-bit floating types.
Another approach to using floating-point types portably is to use the float32_t
and float64_t
types. These are conditionally defined in hw_types.h
based on whether __TI_EABI__
is defined.
Use of 64-bit Floating Point Operations
If you want all floating-point operations to be performed using 32-bit operations, set the following compiler option to 32 to emit errors if any 64-bit floating point operations are used:
--float_operations_allowed=32
This option defines the floating-point precision accepted by the compiler. If you do not specify this option, the compiler defaults to --float_operations_allowed=all, which allows both 32-bit and 64-bit operations.
Use of Floating Point Unit (FPU) Intrinsics
If you want to use 64-bit hardware floating point support (FPU64), you must use EABI mode.
The 32-bit hardware floating point support (FPU32) is portable between COFF and EABI modes.
Floating Point Constants
With EABI, floating point constants without any suffix are interpreted as doubles. You can use the "f" suffix to declare 32-bit floating point constants.
The following example first converts the constants to 64-bit doubles, then adds the constants, and finally converts back to 32-bit float.
float flt = 1.0 + 2.0;`
The following example does not use any 64-bit operations:
float flt = 1.0f + 2.0f;`
Type Change for Floating-Point Intrinsics
The definitions of the following intrinsics have been changed to
use float
instead of double
so that the types will always be 32 bits wide.
This change affects both COFF ABI and EABI. Though the change affects
declarations, it should have no effect on the behavior of these intrinsics.
int __f32toi16r(float);
uint __f32toui16r(float);
float __fracf32(float);
float __eisqrtf32(float);
float __einvf32(float);
float __fmin(float, float);
float __fmax(float, float);
float __fsat(float, float, float);
void __f32_min_idx(float &, float, float &, float);
void __f32_max_idx(float &, float, float &, float);
void __swapff(float &, float &);
void __swapf(float &, float &);
float __divf32(float, float);
float __mpy2pif32(float);
float __div2pif32(float);
float __sinpuf32(float);
float __cospuf32(float);
float __atanpuf32(float);
float __atan2puf32(float);
float __quadf32(float &, float, float);
C Declarations of Assembly Functions Involving double
C-callable assembly functions written for the COFF ABI that accept or
return a 32-bit double value will have been declared in the C code with
a prototype that uses the double
or float
type. Change the prototypes for such
functions to use only the float
type.
Bit-Field Layout
The declared type of a bit-field is the type that appears in the source code. The container type of a bit-field is the type of addressable storage unit used to hold the field. The container type determines how bit-fields are packed and aligned.
The size and layout of the container of a bit-field may differ in COFF ABI and EABI. In summary:
- For EABI, the container type is the same as the declared type.
- For COFF ABI, the smallest possible container is used.
For code that must be portable between COFF ABI and EABI, bit-fields should not be used. If they must be used, the bit-field may need to be declared with conditionally compiled code.
C and C++ Standard Requirements for Bit-Fields
To hold the value of a bit-field, the C and C++ standards allow an implementation to allocate any addressable storage unit large enough to hold the specified number of bits; the type need not be the same as the declared type. (This strategy is used for COFF, but not for EABI.)
C89, C99, and C++ have different options for possible declared types:
- C89:
int
,unsigned int
,signed int
- C99:
int
,unsigned int
,signed int
,_Bool
, or "some other implementation-defined type" - C++: any integral or enumeration type, including
bool
There is no long long
type in strict C++, but because C99 has it, C++
compilers commonly support it as an extension. The C99 standard does not
require an implementation to support long
or long long
declared types
for bit-fields, but because C++ allows it, it is not uncommon for C
compilers to support them as well.
The TI compiler supports using any integral type as the declared type of a bit field in
both C and C++, but only in EABI. For COFF ABI, bit-fields must have
a declared type of int
, unsigned int
, or signed int
.
EABI Layout Scheme
For EABI, the declared type is also used as the container type. This has two major consequences:
- The containing structure will be at least as large as the declared type.
- If there is not enough unused space in the current container, the bit-field will be aligned to the next container.
If a 1-bit field has a declared type of int
, the EABI layout allocates an
entire int
container for the bit-field. Other fields can share the
container, but each field is guaranteed to be stored in a container
exactly the size of the bit-field.
Further reading on the bit-field layout can be found in the Itanium C++ ABI specification (https://itanium-cxx-abi.github.io/cxx-abi/abi.html).
COFF ABI Layout Scheme
The COFF ABI scheme uses a different strategy. It starts by using the smallest possible container. It grows the current container if growing it allows the bit-field to be allocated at the current position.
Examples Comparing Layout with COFF and EABI
P
stands for padding
Example 1:
struct S { int a:1; };
For both COFF and EABI, the container is one 16-bit int
with the following contents:
aPPPPPPPPPPPPPPP
Example 2:
struct S { long a:1; };
The container size and layout differ for COFF and EABI.
COFF: aPPPPPPPPPPPPPPP
(one 16-bit container. which is the smallest available container size)
EABI: aPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
(one 32-bit container, as specified in the declared type)
Example 3:
struct S { int a:1; int b:1};
For both COFF and EABI, the container is one 16-bit int
with the following contents:
abPPPPPPPPPPPPPP
Example 4:
struct S { int a:15; int b:2; };
For both COFF and EABI, the container is two 16-bit ints with the following contents:
aaaaaaaaaaaaaaaPbbPPPPPPPPPPPPPP
Although the emphasis for bit fields is on packing efficiency, if multiple fields do not fit in the current field, a new container is created and packing begins at the start of that container.
Example 5:
struct S { int a:15; long b:2 };
For both COFF and EABI, the container is one 32-bit container and one 16-bit container inside the 32-bit container.
aaaaaaaaaaaaaaabbPPPPPPPPPPPPPPP
Compatibility Impact of EABI
As shown above, EABI can produce a bit-field layout that is not quite the same as it would be with COFF ABI. Programs that rely on bit-fields for precise data layout--such as for reading a binary file or setting bits in a status register--should be examined for compatibility. Such test cases may need to use conditional compilation to change the declared types of bit-field definitions. However, many existing test cases will be unchanged.
Incompatibilities fall into two categories:
- Structure size: Structures can be larger with EABI if they contain bit-fields with mostly unused bits. If a structure needs to use the smaller size that would have been used with COFF ABI, the declared type needs to be changed to a type of the desired size, such as char.
- Bit-field position: Bit-fields can usually be stored at a different position only if there is enough space left in the current container to fit the next bit-field, but not a properly aligned object of the declared type. The narrower the declared type of a bit-field, the more likely that there will be an incompatibility. Declaring all bit-fields with an int-sized type (as is typical of code written for C89), minimizes the incompatibility of bit-field positions.
Access Type
For efficiency, the compiler may access bit-fields using a type that does not match either the declared type or the container type. The declared type and container type are used only to determine bit field packing and alignment.
The type used by the compiler to actually load the bit-field is the access type, which can be a narrower type computed from the size and offset of the bit-field. In the following EABI example, the container type is 32 bits, but the bit-field is loaded using a 16-bit access:
struct S { long af:16; long bf:16; };
For EABI, the compiler does not use a narrower type to access volatile
bit-fields (bit fields declared with a volatile-qualified type). EABI
instead uses exactly the declared type. (COFF does use a smaller, more
efficient size for volatile` bit-fields if possible.)
Enumerated Type Sizes
Many enumerated types have members with values that are small enough to
fit into integer types smaller than int
. Both COFF ABI and EABI use
int-sized containers to store variables of such enumerated types.
For C++ code, both COFF ABI and EABI use integer types wider than int
for enumerated types with values too large to fit in an int
.
In short, there are no COFF ABI to EABI migration issues related to enumerated types.
Data Page (DP) Pointer Load Optimization
The C28x supports direct addressing through the data page pointer register, DP. The DP register points to the beginning of a page, which is 64 words long. To avoid loading the DP for every direct access, the compiler "blocks" some data and sections. Blocking ensures that an object is either entirely contained within a single page or is aligned to a 64-word page boundary.
Differences between the blocking rules for COFF and EABI are described in Section 3.11 of the TMS320C28x Optimizing C/C++ Compiler User's Guide.
STABS Debugging Not Supported
The --symdebug:coff
and --symdebug:profile_coff
compiler options request
the use of STABS debugging information, which is
available only for COFF files. These compiler options are not supported in
EABI mode. ELF files must use DWARF as required by
the ELF specification.
Run-Time-Support Library
The compiler automatically links with an EABI version of the Run-Time Support Library
when you compile with the --abi=eabi
option.
If libc.a is explicitly used, the appropriate run-time-support library is included in the search order where libc.a is specified.
Statements in asm()
The contents of asm()
statements are assembly code rather than C/C++ code, and need to
be changed as shown below.
Conflicts Between Variable and Register Names
In the following code, the variables acc
and p
are
named the same as the machine registers ACC and P:
extern unsigned long acc;
unsigned long p;
void foo(void)
{
acc = p;
}
When compiled under COFF ABI, the acc
and p
symbols get an
underscore prefix in the assembly code that is generated by the
compiler.
Under the EABI model, however, the compiler does not add an
underscore prefix to symbol names. The compiler avoids
conflicts between variable names in C/C++ and register names by
using an escape character sequence around C/C++ variables. In the above
example, the compiler refers to the acc
and p
variables
using ||acc||
and ||p||
, respectively.
For example, compiling the above for EABI yields assembly like the following:
.global ||foo||
.global ||acc||
.global ||p||
.bss ||p||,4,4
.sect ".text"
||foo||:
MOVW DP, #||p||
MOVW ACC, #||p||
MOVW DP, #||acc||
MOVW @||acc||, ACC
LRETR
Assembly Code Changes
The C ABI is how the compiler expresses C code programs in assembly language. Assembly code that defines a C-callable function or calls a C function must conform to the ABI. This section describes changes that must be made to assembly code due to the differences between COFF and EABI in the way C and C++ features are implemented in assembly code.
The changes that need to be made to existing assembly code are primarily limited to places where assembly code interfaces with C or C++ code. Assembly functions that do not interface with C or C++ code directly do not need to be changed.
COFF Underscore Name Mangling
The COFF ABI uses underscores to keep the assembly code namespace and the C code namespace separate. The C compiler prepends an underscore to every externally visible identifier so that it will not collide with an assembly object with the same name. We call this the COFF underscore.
For example, this source code:
int x;
int func(int y) { }
Becomes the following in assembly compiled for the COFF ABI:
.bss _x, 4, 4
_func:
Note how the C/C++ symbol x
becomes _x
in the assembly code.
Assembly functions that attempt to use x
need to use the name _x
.
EABI does not add the COFF underscore. This is a generic ELF requirement. The code must ensure that user-defined names don't collide. Assembly code that is intended to work for both COFF ABI and EABI must handle the difference in mangling (see Conditional Redefinition Method or Double Label Method).
The C source code above becomes the following in EABI:
.bss x, 4, 4
func:
Removing the COFF Underscore
The COFF ABI adds a leading underscore to C and C++ symbols to prevent name
collisions with symbols defined in hand-coded assembly, but EABI does
not add this underscore. When using COFF ABI, a function named red_fish
written in C produces a function entry label with the name
_red_fish
in the assembly source. Under the EABI, the name of the
function as it appears in the assembly source will be exactly as it
appears in the C code, so the function entry label for red_fish
will be
red_fish
.
Functions and variables may be defined in assembly code and used in C code. To use functions and variables in a hand-coded assembly file from a COFF ABI program in EABI, the symbol label needs to be changed, or augmented with a second label. There are approaches to managing this issue.
Conditional Redefinition Method
The preferred solution for code that will be used with both COFF and
EABI is to replace the COFF ABI mangled name with an EABI C name using
an .asg
assembler directive.
For example, a function red_fish
called
from C will have a definition in the COFF ABI assembly code with a
function entry label named _red_fish
. Insert a conditional .asg
directive in front of the definition as follows:
.if __TI_EABI__
.asg red_fish, _red_fish
.endif
.global _red_fish
_red_fish:
<start of function>
In the above example, all instances of _red_fish
are replaced with
red_fish
due to substitution symbol expansion. The assembler
defines the label, red_fish
and makes it visible externally via the
.global
directive.
Double Label Method
Another easy solution is to provide two labels, one providing the COFF ABI mangled name, and the other providing the EABI name. For example:
.global _red_fish, red_fish
_red_fish:
red_fish:
<start of function>
A drawback to this solution is that there remains an extra symbol name that might collide with a user-defined name.
Preprocessor Redefinition Method
For projects in which the assembly code cannot be readily modified, the
assembler's substitution symbol mechanism can be used to redefine
individual symbols. The technique is to create either a C source header
file or an assembly include file that redefines each symbol. This
include file can then be implicitly included in an assembly file
using the --include_file
assembler option.
C++ Name Mangling and Overloaded Functions
The compiler uses name mangling to encode into the name of C++ functions the types of its parameters so that the linker can distinguish overloaded functions.
COFF ABI and EABI use different name mangling schemes for C++ functions, so assembly code that refers to the mangled names directly must be changed to use the EABI mangling.
This is an example of the difference in name mangling:
int func(int); |
int func(float); |
|
---|---|---|
COFF ABI | _func__Fi |
_func__Ff |
EABI | _Z4funci |
_Z4funcf |
Mangled function names become more complex for functions with multiple arguments.
Direct references to mangled C++ names are unlikely unless the output assembly file from compiling a C++ file was captured and hand modified. The best migration path is to just re-compile the original C++ file. If the hand-modifications are too extensive to do this, the fastest way to find the EABI mangled names is to re-compile the original C++ file and examine the generated assembly code to see the EABI mangled names.
Pass the --abi=eabi
option to dem2000
to demangle EABI C++ names.
Structures Passed or Returned by Value
With COFF ABI, all structs that are passed or returned by value in C code are transformed by the compiler so that in the generated assembly code they are passed by reference. The compiler puts the struct in a temporary location and passes a pointer to this temporary location in place of the struct.
With EABI, small, homogeneous float structs passed or returned by value in C code are passed or returned by value in the generated assembly code, either in a register or on the stack as appropriate. Larger structures are passed by reference as with the COFF ABI. A small, homogeneous float struct is defined as a struct of a size less 128 bits that contains only float typed data or consists of nested structs/arrays that themselves only contain float-typed structs. For example, these are all small, homogeneous float structs:
struct S1 { float f1; float f2 }
struct S2 { float f1; float array[2]; }
struct S3 { struct S1 array[2]; }
Structs passed in registers use the same registers as scalars, R0H-R3H. C-callable assembly functions that accept, return, or pass small structures by value need to be re-written to follow this convention.
Structure Passing with the CLA Compiler
For CLA functions compiled in COFF ABI mode, all structs that are passed or returned by value in C code are transformed by the compiler so that in the generated assembly code they are passed by reference. As in C28x generated code, the compiler puts the struct in a temporary location and passes a pointer to this temporary location in place of the struct.
For CLA functions compiled in EABI mode, small structs containing only scalar type members up to 96-bits in size that are passed or returned by value in C code are passed or returned by value in the generated assembly code, either in registers or on the stack as appropriate. Similarly, small structs containing only pointer type members up to 64-bits in size that are passed or returned by value in C code are passed or returned by value in generated assembly code, whether in registers or on the stack as appropriate.
Larger structures are passed by reference with EABI (as in COFF ABI mode). Scalar type members of a struct, up to 32-bits in size will be passed in MR0, MR1, or MR2, and returned in MR0, MR1, or MR2. Any 32-bit pointer type members of a struct are passed and returned in MAR0 or MAR1.
The exact registers used in a call to a given function depend on the type and order of the arguments. Likewise, the exact registers containing the return struct type value depend on the details of the return struct type's members.
Relocation Expressions Are Not Supported by EABI
Assembly expressions involving two or more relocatable symbols cannot be represented in C2000 ELF object files. Any such expression will need to be rewritten into two or more instructions.
For example, the following will not work with EABI if both symbols are resolved at link time:
thing_size: .word (thing_end - thing_begin)
Legacy .cinit Use in Assembly Source
The COFF ABI uses the .cinit mechanism to initialize global variables. This mechanism is intended to be used only by the compiler, but some hand-coded assembly source encodes variable initialization with hand-encoded .cinit tables. This will work under COFF ABI so long as the encoding is correct. However, this method will not work in EABI, because EABI uses direct initialization instead, which means the linker creates all .cinit records.
The recommended migration path is to rewrite the .cinit initialization as direct initialization and let the linker handle creating the initialization record. For example, the following .cinit record can be rewritten as shown:
glob: .usect ".far", 8, 4 ; 8-byte object aligned to 4 bytes in uninitialized section ".far"
.sect ".cinit"
.align 8
.field 8, 32 ; length in bytes
.field glob, 32 ; address of memory to initialize
.field 2, 32 ; initialize first word to 2
.field 3, 32 ; initialize second word to 3
.sect ".fardata", RW ; 8-byte object in initialized section ".fardata"
.align 4
glob: .field 2, 32 ; directly initialize first word to 2
.field 3, 32 ; directly initialize first word to 3
For more information about using direct initialization, see the TMS320C28x Optimizing C/C++ Compiler User's Guide (SPRU514).
Legacy STABS Directives in Assembly Source
If there are any STABS (COFF debug) directives in an assembly file (this typically only happens for assembly code generated by the compiler), these directives must be deleted or conditionally compiled out when using EABI mode.
ELF does not support STABS, and the assembler emits an error message if the input file contains STABS directives. To reuse the file for EABI, strip out all the STABS directives.
Example STABS directives are: .file
, .func
, .block
, .sym
Run-Time-Support Library Helper Functions
The RTS library contains some helper functions to perform complicated
operations for certain high-level language features. It is not expected
that hand-coded assembly code would call these functions, but it is
possible, particularly if the output of the compiler is tweaked by hand
and transformed into an assembly input file. These helper functions have
different names in EABI. If the assembly code directly calls a library
helper function, the code needs to use the new name for the
function. The easiest way to deal with these functions is to use the
assembler --include_file
option to include a list of assembler defines
to change the names of the old functions.
For example, create a C header file coff_to_elf_helpers.h
:
#define __divi __c28xabi_divi
#define __divu __c28xabi_divu
...
Include this file in another header file coff_to_elf_helpers.i
:
.cdecls C, LIST, "coff_to_elf_helpers.h"
Then include this file at the beginning of every assembly file:
cl2000 --include_file=coff_to_elf_helpers.i
Linker Command File Changes
When porting a COFF ABI application to EABI, the most likely place you will need to make changes is the linker command file. The linker supports linker command file preprocessing. See the TMS320C28x x Assembly Language Tools User's Guide (SPRU513).
EABI Sections
The names of some sections have changed for EABI. EABI re-uses most compiler-generated section names used by COFF ABI. It also introduces some new section names. Each section needs to be allocated to appropriate memory.
Your linker command file should have an explicit placement for each section. If you do not do this, the linker will try to use the placement for the old section name if it is specified in the linker command file.
Make the following changes when migrating from COFF to EABI:
.ebss
->.bss
.econst
->.const
.esysmem
->.sysmem
.pinit
->.init_array
.cio
->.bss:.cio
The following sections have the same names in both COFF and EABI:
.text
.data
.cinit
.args
.stack
The following sections are new for EABI:
.c28xabi.exidx
(like.const
).c28xabi.extab
(like.const
)

Conditional Control of Sections
You can use the __TI_EABI__
predefined symbol to conditionally control section placement.
#if defined(__TI_EABI__)
.init_array : > FLASHC, PAGE = 0
.const : > FLASHB, PAGE = 1
#else
.pinit : > FLASHC, PAGE = 0
.econst : > FLASHB, PAGE = 1
#endif //__TI_EABI__
Read-Only Sections
EABI introduces the following read-only sections
.init_array
data, used to register C++ global variable constructors.c28xabi.exidx
data, index table for C++ exception handling.c28xabi.extab
data, unwinding instructions for C++ exception hand
The data section .init_array
serves the same purpose that .pinit
does for
COFF ABI. EABI does not use the name .pinit
.
The .init_array
Section
For EABI, if .init_array
is not allocated in the linker command file and the .pinit
section is allocated, the linker automatically allocates the
.init_array
section to the same memory area as the .pinit
section.
If the -w
option is used, the linker also generates a warning.
Either explicitly allocate the .init_array
section in the linker
command file or if there are no .pinit
sections, remove the .pinit
allocation from the linker command file.
No Leading Underscores
The symbol names used in linker command files are the names that appear in object files. For COFF, this means the mangled names. For EABI, the object file names are the same as the high-level language names, so any reference or definition of a symbol in a linker command file will need to be changed.
For example, to set a symbol to the value of the main
function.
COFF ABI:
mainaddr = _main;
_symbol = 0x1234;
EABI:
mainaddr = main;
symbol = 0x1234;
Section and Struct Initialization Issues
In EABI mode, the compiler initializes any uninitialized sections. This supports the C language, which states that uninitialized global variables are initialized to zero.
Such zero-initialization can cause some issues:
Header file structures: One issue occurs when using peripheral or register structs in header files and in the linker command files associated with those header files. All of these structs are uninitialized, so when compiling for EABI, the registers are zero-initialized during the
__c_int00
code. This causes the CPU to write to those registers. However, writing to DCSM key registers may cause a device reset, which would make__c_int00
unable to complete and get tomain()
.Therefore, it is recommended that such sections (including all register sections from the header file structures) should be set to NOINIT in the linker command file. For example:
DcsmZ1RegsFile : > DCSM_Z1, PAGE = 1, type=NOINIT
- Message RAMs: Another issue concerns message RAMs. The CPU is not able to write to some message RAMs that are meant to be read-only by that CPU. In this case, you should also add NOINIT to the message RAMs. Additionally, some RAM init bits are available for the application to use to initialize data in the message RAMs.
Another way to prevent structs from being zero initialized,
is to use the noinit
attribute when declaring them in C code.
To disable all zero-initialization in EABI mode, you may use the --zero_init=off
linker option.
Partial Linking
Relocation entries are not processed during a partial link under the
EABI. Relocation entries that involve a static base reference will simply
be carried forward until you are ready to create an executable
output file with the linker. At that point, the linker defines a
value for the __TI_STATIC_BASE
symbol used in the resolution
of any static-base relative relocation that is encountered.
Conditional Linking: Section Removal or Retention
In COFF ABI mode, if an object file is explicitly included in the link step or is pulled in from an object library to resolve a symbol definition, then, by default, the linker includes all sections in such an object file into the linked output file. This can be inefficient when an object file contains many sections that are not needed in the link but are included solely because one section in the file resolves a symbol.
To alleviate this inefficiency in COFF, the Code Generation Tools have
for a long time provided a .clink
assembler directive, which allows you
to indicate that a section is eligible for removal by
the linker if it is not referenced. This technique is referred to
as conditional linking. In COFF ABI the compiler generates a .clink
directive for code sections automatically. That is, for COFF, compiler-generated
code sections are eligible for removal via conditional
linking. (Compiler-generated data sections are always linked by the
linker.)
In EABI mode, by contrast, all sections are eligible for removal via conditional linking by default. This means that when migrating a COFF ABI application to EABI, one must make sure that needed but unreferenced sections (such as overlays or debug functionality) are explicitly retained.
To help developers migrate COFF applications to EABI and create new EABI applications, the Code Generation Tools now support the following mechanisms:
The following pragma can be used in C/C++ source files
#pragma RETAIN(`<section_name>`)
When this pragma is applied to a function or data object symbol, it instructs the compiler to generate a
.retain
assembler directive into the section that contains the definition of the symbol. This provides a mechanism for the developer to indicate in their C/C++ source that a section containing the specified symbol is to be considered ineligible for removal during conditional linking.NOTE: When compiling code for an interrupt function that is written in C/C++, the compiler generates a
.retain
directive into the section that contains the definition of the interrupt function. This can be overridden by applying
#pragma CLINK()
to the interrupt function symbol.The following directive can be used in assembly source files:
.retain ["<section name>"]
If a "section name" argument is provided to the
.retain
directive, the assembler marks the specified section as ineligible for removal by conditional linking. If no "section name" argument is specified, then the currently active initialized section is marked as ineligible for removal via conditional linking.The linker option
--retain
can be used to specify a symbol or section to be retained by the linker.
Note also that the compiler now automatically detects interrupt vectors. It marks them ineligible for conditional linking to help users easily migrate from COFF ABI to EABI.
If you want to retain all sections, you can use the --unused_section_elimination=off
linker option. However, be aware that code size may be significantly larger with this setting.
Special Symbol Names
Both COFF ABI and EABI define special symbols to manage ABI
functionality and memory usage. Some variables were renamed for EABI to
bring them in line with the EABI standard. Note that a __TI
prefix is
now a standard part of many symbol names.
Symbol Name Changes
In the table below, the COFF ABI name is the name as it would appear in assembly code or the linker command file. The EABI name is the new name for the symbol in EABI. Uses of the old name in linker command files or assembler files should be updated or conditionalized to refer to the new name, as appropriate.
COFF ABI Name | EABI Name | Purpose | COFF names in EABI? | Notes |
---|---|---|---|---|
___binit__ or binit |
__binit__ or binit |
Boot-time initialization | Changes from 3 leading underscores to 2 | |
___c_args__ |
__c_args__ |
Command-line arguments | Yes | Changes from 3 leading underscores to 2 |
___cinit__ or cinit |
__TI_CINIT_Base |
Start of C global variable initializers table | No | New compressed table format in EABI. Not NULL terminated in EABI; see __TI_CINIT_Limit |
N/A | __TI_CINIT_Limit |
End of C global variable initializer data | ||
___data__ |
N/A | Beginning of the .data section | ||
___edata__ |
N/A | End of the .data section | ||
`_end | N/A | End of the .bss section | ||
___etext__ |
N/A | End of the .text section | ||
___pinit__ or pinit |
__TI_INITARRAY_BASE |
Start of C++ global object initializers table | Yes | Not NULL terminated in EABI; see __TI_INITARRAY_Limit |
N/A | __TI_INITARRAY_Limit |
End of C++ global object initializer data | ||
___text__ or .text |
N/A | Beginning of the .text section | ||
__bss__ .bss or $bss |
__TI_STATIC_BASE |
Start of DP-relative data | Yes | |
__STACK_SIZE |
__TI_STACK_SIZE |
Size available for function frame stack | Yes | |
__SYSMEM_SIZE |
__TI_SYSMEM_SIZE |
Size available for heap allocation | Yes | |
__STACK_END |
__TI_STACK_END |
End of the .stack section | Yes | |
C$$EXIT |
C$$EXIT |
Special host I/O trap | ||
C$$IO$$ |
C$$IO$$ |
Special host I/O trap |
Symbol Termination and Format
In COFF ABI .pinit
and .cinit
are terminated with NULL records.
In EABI the ending addresses of .init_array
and .cinit
are indicated by
the corresponding LIMIT symbol. For more information about the format of
.init_array
and .cinit
, see the
TMS320C28x Optimizing C/C++ Compiler User's Guide (SPRU514
and the C28x Embedded Application Binary Interface Application Report (SPRAC71).
Also note that with EABI the CINIT table has a new compressed format. For details, see the TMS320C28x Optimizing C/C++ Compiler User's Guide.
Backward Compatibility
To provide backward compatibility under EABI, the linker will continue to recognize several of the old symbols names and will define them at link time if they are used, while also generating a warning referring you to their new name. Refer to the "COFF names in EABI?" column in the table above to identify symbols that are supported in this manner.
Resources
TI Code Composer Studio Product PageRelated Technical Documents
TI E2E Technical Forums

This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.