Using Linker Symbols in C/C++ Applications

Linker symbols have a name and a value. The value is a 32-bit unsigned integer, even if it represents a pointer value on a target that has pointers smaller than 32 bits.

The most common kind of symbol is generated by the compiler for each function and variable. The value represents the target address where that function or variable is located. When you refer to the symbol by name in the linker command file or in an assembly file, you get that 32-bit integer value.

However, in C and C++ names mean something different. If you have a variable named x that contains the value Y, and you use the name "x" in your C program, you are actually referring to the contents of variable x. If "x" is used on the right-hand side of an expression, the compiler fetches the value Y. To realize this variable, the compiler generates a linker symbol named x with the value &x. Even though the C/C++ variable and the linker symbol have the same name, they don't represent the same thing. In C, x is a variable name with the address &x and content Y. For linker symbols, x is an address, and that address contains the value Y.

Because of this difference, there are some tricks to referring to linker symbols in C code. The basic technique is to cause the compiler to creating a "fake" C variable or function and take its address. The details differ depending on the type of linker symbol.

Linker symbols that represent a function address: In C code, declare the function as an extern function. Then, refer to the value of the linker symbol using the same name. This works because function pointers "decay" to their address value when used without adornment. For example:

extern void _c_int00(void); printf("_c_int00 %lx\n", (unsigned long)&_c_int00);

Suppose your linker command file defines the following linker symbol:

func_sym=printf+100;

Your C application can refer to this symbol as follows:

extern void func_sym(void); printf("func_sym %lx\n", _symval(&func_sym)); /* these two are equivalent */ printf("func_sym %lx\n", (unsigned long)&func_sym);

Linker symbols that represent a data address: In C code, declare the variable as an extern variable. Then, refer to the value of the linker symbol using the & operator. Because the variable is at a valid data address, we know that a data pointer can represent the value.

Suppose your linker command file defines the following linker symbols:

data_sym=.data+100; xyz=12345

Your C application can refer to these symbols as follows:

extern far char data_sym; extern far int xyz; printf("data_sym %lx\n", _symval(&data_sym)); /* these two are equivalent */ printf("data_sym %p\n", &data_sym); myvar = &xyz;

Notice that linker symbols for data must be declared in C using the far keyword. This is because the compiler assumes that symbols declared without "far" have addresses relative to the data page register (DP). Using the "far" keyword causes the compiler to treat these as absolute symbols.

Linker symbols for an arbitrary address: In C code, declare this as an extern symbol. The type does not matter. If you are using GCC extensions, declare it as "extern void". If you are not using GCC extensions, declare it as "extern char". Then, refer to the value of the linker symbol mySymbol as _symval(&mySymbol). You must use the _symval operator, which is equivalent to a cast, because the 32-bit value of the linker symbol could be wider than a data pointer. The compiler treats _symval(&mySymbol) in a special way that can represent all 32 bits, even when pointers are 16 bits. Targets that have 32-bit pointers can usually use &mySymbol instead of the _symval operator. However, the portable way to access such linker symbols across TI targets is to use _symval(&mySymbol).

Suppose your linker command file defines the following linker symbol:

abs_sym=0x12345678;

Your C application can refer to this symbol as follows:

extern char abs_sym; printf("abs_sym %lx\n", _symval(&abs_sym));