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 to main().

    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 Page
Related Technical Documents
TI E2E Technical Forums

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