3.6. Interfacing C and C++ With Assembly Language — TI Arm Clang Compiler Tools User's Guide (original) (raw)

The following are ways to use assembly language with C/C++ code:

3.6.1. Using Assembly Language Modules With C/C++ Code

Interfacing C/C++ with assembly language functions is straightforward if you follow the calling conventions defined in Function Structure and Calling Conventions, and the register conventions defined in Register Conventions.

C/C++ code can access variables and call functions defined in assembly language, and assembly code can access C/C++ variables and call C/C++ functions.

Follow these guidelines to interface assembly language and C:

3.6.2. Accessing Assembly Language Functions From C/C++

Functions defined in C++ that will be called from assembly should be defined as extern “C” in the C++ file. Functions defined in assembly that will be called from C++ must be prototyped as extern “C” in C++.

Example 1 below illustrates a C++ function called main(), which calls an assembly language function called asmfunc, which is shown in Example 2. The asmfunc function takes its single argument, adds it to the C++ global variable called gvar, and returns the result.

Example 1: Calling an Assembly Language Function From a C/C++ Program

extern "C" { extern int asmfunc(int a); /* declare external asm function / int gvar = 0; / define global variable */ }

void main() { int I = 5;

I = asmfunc(I); /* call function normally */

}

Example 2: Assembly Language Program Called by Example 1

     .global asmfunc
     .global gvar

asmfunc: LDR r1, gvar_a LDR r2, [r1, #0] ADD r0, r0, r2 STR r0, [r1, #0] MOV pc, lr gvar_a .field gvar, 32

In the C++ program in Example 1, the extern “C” declaration tells the compiler to use C naming conventions (that is, no name mangling). When the linker resolves the .global _asmfunc reference, the corresponding definition in the assembly file will match.

The parameter i is passed in R0, and the result is returned in R0. R1 holds the address of the global gvar. R2 holds the value of gvar before adding the value i to it.

3.6.3. Accessing Assembly Language Variables From C/C++

It is sometimes useful for a C/C++ program to access variables or constants defined in assembly language. There are several methods that you can use to accomplish this, depending on where and how the item is defined: a variable defined in the .bss section, a variable not defined in the .bss section, or a linker symbol.

3.6.3.1. Accessing Assembly Language Global Variables

Accessing variables from the .bss section or a section named with .usect is straightforward:

  1. Use the .bss or .usect directive to define the variable.
  2. Use the .def or .global directive to make the definition external.
  3. Use the appropriate linkname in assembly language.
  4. In C/C++, declare the variable as extern and access it normally.

Example 3 and Example 4 show how you can access a variable defined in .bss.

Example 3: Assembly Language Variable Program

.bss var,4,4 ; Define the variable .global var ; Declare the variable as external

Example 4: C Program to Access Assembly Language From Example 3

extern int var; /* External variable / var = 1; / Use the variable */

3.6.3.2. Accessing Assembly Language Constants

You can define global constants in assembly language by using the .set directive in combination with either the .def or .global directive, or you can define them in a linker command file using a linker assignment statement. These constants are accessible from C/C++ only with the use of special operators.

For variables defined in C/C++ or assembly language, the symbol table contains the address of the value contained by the variable. When you access an assembly variable by name from C/C++, the compiler gets the value using the address in the symbol table.

For assembly constants, however, the symbol table contains the actual value of the constant. The compiler cannot tell which items in the symbol table are addresses and which are values. If you access an assembly (or linker) constant by name, the compiler tries to use the value in the symbol table as an address to fetch a value. To prevent this behavior, you must use the & (address of) operator to get the value. In other words, if x is an assembly language constant, its value in C/C++ is &x. See Using Linker Symbols in C/C++ Applications for more examples.

For more about symbols and the symbol table, refer to Symbols.

You can use casts and #defines to ease the use of these symbols in your program, as in Example 5 and Example 6.

Example 5: Accessing an Assembly Language Constant From C

extern int table_size; /*external ref / #define TABLE_SIZE ((int) (&table_size)) . / use cast to hide address-of / . . for (I=0; i<TABLE_SIZE; ++I) / use like normal symbol */

Example 6: Assembly Language Program for Example 5

_table_size .set10000 ; define the constant .global _table_size ; make it global

Because you are referencing only the symbol’s value as stored in the symbol table, the symbol’s declared type is unimportant. In Example 5, int is used. You can reference linker-defined symbols in a similar manner.

3.6.5. Using Inline Assembly Language

Within a C/C++ program, you can use the asm statement to insert a single line of assembly language into the assembly language file created by the compiler. A series of asm statements places sequential lines of assembly language into the compiler output with no intervening code. For more information, see naked.

The asm statement is useful for inserting comments in the compiler output. Simply start the assembly code string with a semicolon (;) as shown below:

asm(";*** this is an assembly language comment");

Note

Using the asm Statement Keep the following in mind when using the asm statement:

3.6.6. Modifying Compiler Output

You can inspect and change the compiler’s assembly language output by compiling the source and then editing the assembly output file before assembling it. Specify the -S option on the compiler command line to capture the compiler generated assembly.