2.7.3. Variable Attributes

The following variable attributes are supported by the tiarmclang compiler:

2.7.3.1. alias

The alias variable attribute allows you to create multiple symbol aliases that effectively refer to the same definition of a data object. However, an alias must be declared in the same compilation unit as the definition of the data object that it is an alias for. Furthermore, you cannot declare aliases to local variables. The tiarmclang compiler will interpret an alias declared in a local block to be a local variable, ignoring the alias attribute in such cases.

Syntax

<type> new symbol __attribute((alias(”old symbol”)));

  • new symbol - is the name of the alias.

  • old symbol - is the name of the variable to be aliased.

Example

Consider the following C code:

#include <stdio.h>

int red_fish = 10;
extern int blue_fish __attribute__((alias("red_fish")));

int main() {
  printf("blue_fish: %d\n", blue_fish);
  return 0;
}

The compiler generated code for the above code will contain the following assembly:

...
        .hidden red_fish                        @ @red_fish
        .type   red_fish,%object
        .section        .data.red_fish,"aw",%progbits
        .globl  red_fish
        .p2align        2
red_fish:
        .long   10                              @ 0xa
        .size   red_fish, 4
...
        .globl  blue_fish
        .hidden blue_fish
        .set    blue_fish, red_fish

In this case, there is a definition of the global variable red_fish and the subsequent .set directive creates a symbolic link from blue_fish to red_fish so that any references to blue_fish will be resolved by the definition of red_fish.

2.7.3.2. aligned

The aligned attribute can be used to specify a minimum alignment for a given data object, where the alignment boundary is specified in bytes.

Syntax

<type> symbol __attribute((aligned(alignment)));

  • symbol - is the variable/data object that is subject to the specified minimum alignment.

  • alignment - is the minimum alignment (in bytes) relative to the section that symbol is defined in. If an alignment value is not specified, then the compiler will assume default alignment based on the type of the data object.

Example

Consider the C source code below (align_var_attr.c):

int var1 __attribute__((aligned(8))) = 5;

unsigned char var2[10] __attribute__((aligned(16))) = { 15, 25, 35 };

struct {
  int m1;
  short m2;
  char  m3 __attribute__((aligned(4)));
  short m4;
} var3 = { 10, 20, 30, 40 };

short var4[3] __attribute__((aligned)) = { 100, 200, 300 };

The compiler generated code for the above code will contain the following assembly:

...
        .hidden var1                            @ @var1
        .type   var1,%object
        .section        .data.var1,"aw",%progbits
        .globl  var1
        .p2align        3
var1:
        .long   5                               @ 0x5
        .size   var1, 4

        .hidden var2                            @ @var2
        .type   var2,%object
        .section        .data.var2,"aw",%progbits
        .globl  var2
        .p2align        4
var2:
        .asciz  "\017\031#\000\000\000\000\000\000"
        .size   var2, 10

        .hidden var3                            @ @var3
        .type   var3,%object
        .section        .data.var3,"aw",%progbits
        .globl  var3
        .p2align        2
var3:
        .long   10                              @ 0xa
        .short  20                              @ 0x14
        .zero   2
        .byte   30                              @ 0x1e
        .zero   1
        .short  40                              @ 0x28
        .size   var3, 12

        .hidden var4                            @ @var4
        .type   var4,%object
        .section        .data.var4,"aw",%progbits
        .globl  var4
        .p2align        3
var4:
        .short  100                             @ 0x64
        .short  200                             @ 0xc8
        .short  300                             @ 0x12c
        .size   var4, 6

Some things to notice about the compiler-generated assembly:

  • var1 is aligned to an 8-byte boundary via the .p2align 3 directive (the .p2align operand is interpreted as an exponent in the expression “2 ^^ exp”).

  • var2 is aligned to a 16-byte boundary via the .p2align 4 directive.

  • The alignment of var3’s char type member, m3, is effected via the .zero 2 directive that pads the start of m3 to a 4-byte boundary relative to the start of the structure.

  • The aligned attribute associated with var4 does not take an alignment argument, so the compiler will assume default alignment for an array of short, 8-bytes.

2.7.3.3. deprecated

The deprecated variable attribute can be used to mark a symbol as deprecated to enable the compiler to detect and report warnings on uses of a symbol whose definition is known to be deprecated.

Syntax

<type> symbol __attribute__((deprecated));

  • symbol - identifies the name of the variable being marked as deprecated.

Example

Consider the following C code (deprecated_var_attr.c):

extern int dep_var __attribute__((deprecated));
void foo() {
  dep_var = 5;
}

When compiled, the compiler will emit the following diagnostic information:

%> tiarmclang -mcpu=cortex-m0 -c deprecated_var_attr.c
deprecated_var_attr.c:4:3: warning: 'dep_var' is deprecated [-Wdeprecated-declarations]
  dep_var = 5;
  ^
deprecated_var_attr.c:2:35: note: 'dep_var' has been explicitly marked deprecated here
extern int dep_var __attribute__((deprecated));
                                  ^
1 warning generated.

The deprecated attribute can be particularly useful in a large C/C++ source file when trying to find all the references to a deprecated symbol that need to be modified.

2.7.3.4. local

When using Smart Function and Data Placement, the local variable attribute can be used to more easily place data in memory local to the processor (for example, in Tightly Coupled Memory (TCM)).

Syntax

<type> symbol __attribute__((local(priority)));

  • priority - is an optional integer argument indicating the placement order of the object relative to other objects aggregated in the same output section. This influences how the placements are sorted at link-time. If omitted, the priority is assumed to be the highest priority, which is ‘1’.

Example

int abc __attribute__((local(3))) = 10;

2.7.3.5. location

The location variable attribute can be used to specify a variable’s run-time address from within the C/C++ source. The tiarmclang compiler will embed linker instructions within a given compiler-generated object file that will dictate where in target memory the variable definition will be placed at link-time.

Syntax

<type> symbol __attribute__((location(address)));

  • address - is the run-time target memory address where the definition of symbol is to be placed at link-time.

Example

Consider the following C source where a location attribute applied to a global variable (location_var_attr.c):

#include <stdio.h>

int xyz __attribute__((location(0x30000000))) = 10;

int main()
{
  printf("address of xyz is 0x%lx\n", (unsigned long)&xyz);
  return 0;
}

The compiler will define xyz in a special .TI.bound:xyz section and it will also emit symbol metadata information to instruct the linker to place xyz at target memory address 0x30000000 (805306368 in decimal) at link-time.

...
        .hidden xyz                             @ @xyz
        .type   xyz,%object
        .section        ".TI.bound:xyz","aw",%progbits
        .globl  xyz
        .p2align        2
xyz:
        .long   10                              @ 0xa
        .size   xyz, 4
        .sym_meta_info  xyz, "location", 805306368

If the above program is compiled and linked, the linker-generated map file, a.map, reveals that the run-time address of xyz is indeed 0x30000000:

%> tiarmclang -mcpu=cortex-m0 location_var_attr.c -o a.out -Wl,-llnk.cmd,-ma.map
%> cat a.map
******************************************************************************
TI ARM Clang Linker Unix v1.2.0
******************************************************************************
>> Linked Tue Jan 19 18:49:47 2021

OUTPUT FILE NAME:   <a.out>
ENTRY POINT SYMBOL: "_c_int00"  address: 0000187d

...
SECTION ALLOCATION MAP

 output                                  attributes/
section   page    origin      length       input sections
--------  ----  ----------  ----------   ----------------
...
.TI.bound:xyz
*          0    30000000    00000004     UNINITIALIZED
                  30000000    00000004     location_var_attr-baf510.o (.TI.bound:xyz)
...

2.7.3.6. noinit

The noinit variable attribute is especially useful in applications where non-volatile memory is in use. The noinit attribute identifies a global or static variable that should not be initialized at startup or reset (typically, global and static variables that aren’t explicitly initialized in the source code are zero-initialized at startup and reset).

The noinit attribute can be used in conjunction with the location attribute to specify the placement of variables at special target memory locations, like memory-mapped registers, without generating unwanted writes.

The noinit attribute may only be used with uninitialized variables.

Syntax

<type> symbol __attribute__((noinit));

Example

Consider the following C source (noinit_var_attr.c):

#include <stdio.h>

extern void usei(int *x);

__attribute__((noinit)) int noinit_global;
__attribute__((noinit,location(0x100))) int noinit_location_global;

int main() {
  usei(&noinit_global);
  usei(&noinit_location_global);
  return 0;
}

The compiler generated code for the above code will contain the following assembly:

...
        .hidden noinit_global                   @ @noinit_global
        .type   noinit_global,%object
        .section        .TI.noinit,"aw",%nobits
        .globl  noinit_global
        .p2align        2
noinit_global:
        .long   0                               @ 0x0
        .size   noinit_global, 4
        .sym_meta_info  noinit_global, "noinit", 1

        .hidden noinit_location_global          @ @noinit_location_global
        .type   noinit_location_global,%object
        .section        ".TI.bound:noinit_location_global","aw",%nobits
        .globl  noinit_location_global
        .p2align        2
noinit_location_global:
        .long   0                               @ 0x0
        .size   noinit_location_global, 4
        .sym_meta_info  noinit_location_global, "location", 256
        .sym_meta_info  noinit_location_global, "noinit", 1
...

Some things to notice about the above compiler-generated code for the definitions of noinit_global and noinit_location_global:

  • noinit_global is defined in a special section, .TI.noinit, to keep such data objects apart from other variable definitions that will be initialized

  • Symbol metadata information is emitted along with the definition of noinit_global to indicate that the section where noinit_global is defined is not to be initialized

  • noinit_location_global is also defined in a special section, .TI.bound:noinit_location_global, that the linker will consider for placement early in the link-step

  • There are two pieces of symbol metadata information emitted by the compiler with the definition of noinit_location_global. The first instructs the linker to place the section where noninit_location_global is defined at target memory address 0x100 (256 decimal), and the second indicates to the linker that the section where noinit_location_global is defined is not to be initialized

2.7.3.7. offchip

When using Smart Function and Data Placement, the offchip variable attribute can be used to more easily place data in offchip FLASH.

Syntax

<type> symbol __attribute__((offchip(priority)));

  • priority - is an optional integer argument indicating the placement order of the object relative to other objects aggregated in the same output section. This influences how the placements are sorted at link-time. If omitted, the priority is assumed to be the highest priority, which is ‘1’.

Example

int abc __attribute__((offchip(2))) = 10;

2.7.3.8. onchip

When using Smart Function and Data Placement, the onchip variable attribute can be used to more easily place data in onchip RAM.

Syntax

<type> symbol __attribute__((onchip(priority)));

  • priority - is an optional integer argument indicating the placement order of the object relative to other objects aggregated in the same output section. This influences how the placements are sorted at link-time. If omitted, the priority is assumed to be the highest priority, which is ‘1’.

Example

int abc __attribute__((onchip(5))) = 10;

2.7.3.9. packed

If the program in question is being built for an Arm processor variant that has support for unaligned memory accesses, then the packed variable attribute can be used to compress data layout.

The packed attribute specifies that a variable or structure field should have the smallest possible alignment - one byte for a variable, and one bit for a bit field - unless a larger alignment requirement is indicated with an aligned attribute.

Syntax

<type> symbol __attribute__((packed));

Example

Consider the following C code in which a packed attribute is applied to a struct member:

struct _stag {
  char m1;
  int  m2 __attribute__((packed));
} my_struct = { 10, 20 };

In this case, the m2 member of my_struct will be aligned to a 1 byte boundary. Support for unaligned memory accesses will need to be in effect for code to access the content of m2.

Note that when accessing a packed member of a struct, the member should be accessed via a reference through the base of the structure itself (e.g. “my_struct.m2”) or via an offset from a pointer that has been set to the base of the struct (e.g. “struct _stag *ps = &my_struct; ps->m2 = 30;”).

2.7.3.10. persistent

The persistent variable attribute is especially useful in applications where non-volatile memory is in use. The persistent attribute identifies a global or static variable that is to be initialized at load-time, but should not be re-initialized at reset.

The persistent attribute can be used in conjunction with the location attribute to specify the placement of variables at special target memory locations, like memory-mapped registers, without generating unwanted writes.

The persistent attribute may only be used with statically initialized variables.

Syntax

<type> symbol __attribute__((persistent);

Example

If you are using non-volatile RAM, you can define a persistent variable with an initial value of zero loaded into RAM. The program can increment that variable over time as a counter, and that count will not disappear if the device loses power and restarts, because the memory is non-volatile and the boot routines do not initialize it back to zero.

For example, compiling the following C code:

extern void run_init(void);
extern void run_actions(int n);
extern void delay(unsigned int cycles);

__attribute__((persistent, location(0xC200))) int x = 0;

void main() {
  run_init();
  while (1) {
    run_actions(x);
    delay(1000000);
    x++;
  }
}

will generate the following definition of x:

...
        .hidden x                               @ @x
        .type   x,%object
        .section        ".TI.bound:x","aw",%progbits
        .globl  x
        .p2align        2
x:
        .long   0                               @ 0x0
        .size   x, 4
        .sym_meta_info  x, "location", 49664
        .sym_meta_info  x, "noinit", 1
...

The variable x is directly initialized by the .long directive in the initialized section where x is defined. Symbol metadata for x is embedded in the compiler-generated code to instruct the linker to place the section where x is defined at address 0xC200 (49664 decimal), and to not initialize the definition of x on reset.

2.7.3.11. section

The section variable attribute can be used to instruct the compiler to place the definition of a data object in a specific section. This is useful if you’d like to place specific data objects separately from their default sections (e.g. .bss, .rodata, .data).

Syntax

<type> symbol __attribute__((section(”section_name”)));

  • section name - is the name of the section where symbol will be defined. It must be specified as a string argument in the section attribute specification. used to instruct the compiler to place the definition of a data object in a spec

Example

The following C code uses the section attribute to generate the definition of bufferB into a different section from bufferA:

char bufferA[512];
__attribute__((section("my_sect"))) char bufferB[512];

The compiler-generated assembly snippet for the above C code shows that bufferA gets defined in a common block by the .comm directive, whereas bufferB gets defined in the my_sect section.

...
        .hidden bufferA                         @ @bufferA
        .type   bufferA,%object
        .comm   bufferA,512,1
        .hidden bufferB                         @ @bufferB
        .type   bufferB,%object
        .section        my_sect,"aw",%nobits
        .globl  bufferB
bufferB:
        .zero   512
        .size   bufferB, 512

2.7.3.12. unused

When the unused-variable category of warning diagnostics is enabled, the tiarmclang compiler will generate a warning if a variable is declared in a compilation unit, but never referenced in the same compilation unit. The unused variable attribute can be applied to a variable declaration to disable the unused-variable warning with respect to that variable.

Syntax

<type> symbol __attribute__((unused));

Example

In the following C code, a_var is marked as unused:

void foo()
{
  static int my_stat = 0;
  int a_var __attribute__((unused));
  int b_var;
  my_stat;
}

When compiled with unused-variable warnings enabled (via -Wall option in this case), tiarmclang emits a warning about unused variable b_var, but not a_var.

%> tiarmclang -mcpu=cortex-m0 -Wall -c unused_var_attr.c
unused_var_attr.c:6:3: warning: expression result unused [-Wunused-value]
  my_stat;
  ^~~~~~~
unused_var_attr.c:5:7: warning: unused variable 'b_var' [-Wunused-variable]
  int b_var;
       ^
2 warnings generated

2.7.3.13. used/retain

The used or retain variable attribute, when applied, will instruct the tiarmclang compiler to embed information in the compiler-generated code to instruct the linker to include the definition of the variable in the link of a given application, even if it is not referenced elsewhere in the application.

Syntax

<type> symbol __attribute__((used));

<type> symbol __attribute__((retain));

Example

In the following C code example, the tiarmclang compiler will generate a definition of the file static data object keep_this even though it is not referenced elsewhere in the compilation unit:

static int lose_this = 1;
static int keep_this __attribute__((used)) = 2; // retained in object file

The compiler-generated code also includes a .no_dead_strip directive that instructs the linker to include the definition of the keep_this in a link that includes the compiler generated object file from the above code:

...
        .type   keep_this,%object               @ @keep_this
        .section        .data.keep_this,"aw",%progbits
        .p2align        2
keep_this:
        .long   2                               @ 0x2
        .size   keep_this, 4

        .no_dead_strip  keep_this
...

The compiler will not produce a definition of lose_this.

The example below shows a variable used_varX that is annotated with a retain attribute:

#include <stdio.h>

int used_varX __attribute__((retain)) = 10;

int main()
{
  printf("hello\n");
  return 0;
}

After compiling and linking with the following command:

%> tiarmclang -mcpu=cortex-m0 retain_init_global_var.c -o a.out -Wl,-llnk.cmd,-ma.map

The contents of a.map reveals that used_varX was retained in the linked program:

%> cat a.map
...
SECTION ALLOCATION MAP

 output                                  attributes/
section   page    origin      length       input sections
--------  ----  ----------  ----------   ----------------
.text      0    00000020    000012a0
...
.data      0    2000a020    000001d1     UNINITIALIZED
                  2000a020    000000f0     libc.a : defs.c.obj (.data._ftable)
                  ...
                  2000a1ec    00000004     retain_init_global_var-d1e7b9.o (.data.used_varX)
                  ...

2.7.3.14. visibility

The visibility variable attribute provides a way for you to dictate what visibility setting is to be associated with a variable in the compiler-generated ELF symbol table. Visibility is particularly applicable for applications that make use of dynamic linking.

Syntax

<type> symbol __attribute__((visibility(”visibility-kind)));

  • visibility-kind indicates the visibility setting to be written into the symbol table entry for symbol in the compiler-generated ELF object file. The specified visibility kind will override the visibility setting that the compiler would otherwise assign to the symbol. The specified visibility-kind must be one of the following:

    • default - external linkage; symbol will be included in the dynamic symbol table, if applicable, and can be accessed from other dynamic objects in the same application. This is the default visibility if no visibility-kind argument is specified with the visibility attribute.

    • hidden - not included in the dynamic symbol table; symbol cannot be directly accessed from outside the current object, but may be accessed via an indirect pointer.

    • protected - the symbol is included in the dynamic symbol table; references from within the same dynamic module will bind to the symbol and other dynamic modules cannot override the symbol.

Example

The following use of the visibility attribute will set the visibility of my_var to protected:

int my_var __attribute__((visibility("protected"))) = 1;

When compiled to an object file, the symbol table entry for my_var will reflect that it has a protected visibility kind:

%> tiarmclang -mcpu=cortex-m0 -c visibility_var_attr.c
%> tiarmofd -v visibility_var_attr.o
...
 Symbol Table ".symtab"

    <0> ""
       Value:    0x00000000  Kind:        undefined
       Binding:  local       Type:        none
       Size:     0x0         Visibility:  STV_DEFAULT

    <1> "visibility_var_attr.c"
       Value:    0x00000000  Kind:        absolute
       Binding:  local       Type:        file
       Size:     0x0         Visibility:  STV_DEFAULT

    <2> "my_var" (defined in section ".data.my_var" (3))
       Value:    0x00000000  Kind:        defined
       Binding:  global      Type:        object
       Size:     0x4         Visibility:  STV_PROTECTED
 ...

2.7.3.15. weak

The weak variable attribute causes the tiarmclang compiler to emit a weak symbol to the symbol table for the symbol’s declaration. At link-time, if a strong definition of a symbol with the same name is included in the link, then the strong definition of the symbol will override the weak definition.

Syntax

<type> symbol __attribute__((weak));

Example

Consider the following program with weak_var_attr.c:

#include <stdio.h>

extern int my_var;

int main() {
  printf("my_var is: %d\n", my_var);
  return 0;
}

weak_def.c:

int my_var __attribute__((weak)) = 5;

and strong_def.c:

int my_var = 10;

If the program is compiled without strong_def.c, then the weak definition of my_var will be chosen by the linker to resolve the reference to it in weak_var_attr.c:

%> tiarmclang -mcpu=cortex-m0 weak_var_attr.c weak_def.c -o a.out -Wl,-llnk.cmd,-ma.map
%> load470 -q a.out
my_var is: 5

If both weak_def.c and strong_def.c are included in the program build, then the linker will choose the strong definition of my_var to resolve the reference to it in weak_var_attr.c:

%> tiarmclang -mcpu=cortex-m0 weak_var_attr.c weak_def.c strong_def.c -o a.out -Wl,-llnk.cmd,-ma.map
%> load470 -q a.out
my_var is: 10

Note

Strong vs. Weak and Object Libraries

At link-time, if a weak definition of a symbol is available in the object files that are input to the linker and a strong definition of the symbol exists in an object library that is made available to the link, then the linker will not use the strong definition of the symbol since the reference to the symbol has already been resolved.

2.7.3.16. weakref

The weakref variable attribute can be used to mark a declaration of a static variable as a weak reference. The symbol that the attribute applies to is interpreted as an alias of a target symbol, and also indicates that a definition of the target symbol is not required.

Syntax

<type> symbol __attribute__((weakref(”target symbol)));

<type> symbol __attribute__((weakref, alias(”target symbol”)));

  • target symbol - identifies the name of a variable that the symbol being declared is to be treated as an alias for. If a target symbol argument is provided with the weakref attribute, then symbol is interpreted as an alias of target symbol. Otherwise, an alias attribute must be combined with the weakref attribute to identify the target symbol.

Example

Consider the following program with weakref_var_attr.c:

#include <stdio.h>

extern int my_var;
static int a_sym __attribute__((weakref("my_var")));

int main(void) {
  int my_loc = a_sym;

  printf("my_loc is %d\n", my_loc);
  return 0;
}

and strong_def.c:

int my_var = 10;

If the above program is compiled and linked without strong_def.c, the build will succeed, but the run-time behavior will be unpredictable as there will be a reference to an undefined symbol:

%> tiarmclang -mcpu=cortex-m0 weakref_var_attr.c -o a.out -Wl,-llnk.cmd,-ma.map
%> load470 -q a.out
Data fetch: 00000000 is outside configured memory

However, if we include strong_def.c in the link, then the reference to a_sym will resolve to the definition of my_var via the weakref attribute:

%> tiarmclang -mcpu=cortex-m0 weakref_var_attr.c strong_def.c -o a.out -Wl,-llnk.cmd,-ma.map
%> load470 -q a.out
my_loc is 10

If we were to replace the weakref attribute with an alias attribute in weakref_var_attr.c:

#include <stdio.h>

extern int my_var;
// static int a_sym __attribute__((weakref("my_var")));
static int a_sym __attribute__((alias("my_var")));

int main(void) {
  int my_loc = a_sym;

  printf("my_loc is %d\n", my_loc);
  return 0;
}

and strong_def.c was not included in the build, then tiarmclang will report an unresolved symbol reference:

%> tiarmclang -mcpu=cortex-m0 weakref_var_attr.c -o a.out -Wl,-llnk.cmd,-ma.map
weakref_var_attr.c:6:33: error: alias must point to a defined variable or function
static int a_sym __attribute__((alias("my_var")));
                                ^
1 error generated.