2.7.3. Variable Attributes

The following variable attributes are supported by the c29clang 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 c29clang compiler interprets an alias declared in a local block as 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 generates assembly instructions for the above code that defines the global variable red_fish and creates a symbolic link from blue_fish to red_fish so that any references to blue_fish are 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 assumes 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 generates assembly instructions for the above code that do the following:

  • Align var1 to an 8-byte boundary.

  • Align var2 to a 16-byte boundary.

  • Align var3’s char type member, m3, by padding 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 assumes 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 emits the following diagnostic information:

%> c29clang -mcpu=c29.c0 -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. location

The location variable attribute can be used to specify a variable’s run-time address from within the C/C++ source. The c29clang compiler embeds linker instructions within a given compiler-generated object file that dictates where in target memory the variable definition are 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 defines xyz in a special .TI.bound:xyz section. It also emits 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:

%> c29clang -mcpu=c29.c0 location_var_attr.c -o a.out -Wl,-llnk.cmd,-ma.map
%> cat a.map
******************************************************************************
C29 Clang Linker Unix v1.2.0
******************************************************************************
>> Linked Tue Jan 19 18:49:47 2024

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.5. 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 generates assembly instructions for the above code that do the following:

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

  • Emits symbol metadata information along with the definition of noinit_global to indicate that the section where noinit_global is defined is not to be initialized.

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

  • Two pieces of symbol metadata information are 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.6. packed

If the program in question is being built for a C29x 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 is aligned to a 1 byte boundary. Support for unaligned memory accesses 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.7. 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 does 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++;
  }
}

generates a definition of x that is directly initialized 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.8. 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 generates assembly instructions that define bufferA in a common block, whereas bufferB is defined in the my_sect section.

2.7.3.9. unused

When the unused-variable category of warning diagnostics is enabled, the c29clang compiler generates 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), c29clang emits a warning about unused variable b_var, but not a_var.

%> c29clang -mcpu=c29.c0 -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.10. used/retain

The used or retain variable attribute, when applied, instructs the c29clang 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 c29clang compiler generates 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 does 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:

%> c29clang -mcpu=c29.c0 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.11. 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 overrides 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 ise 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 bind to the symbol and other dynamic modules cannot override the symbol.

Example

The following use of the visibility attribute sets 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 reflects that it has a protected visibility kind:

%> c29clang -mcpu=c29.c0 -c visibility_var_attr.c
%> c29ofd -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.12. weak

The weak variable attribute causes the c29clang 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 overrides 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 is chosen by the linker to resolve the reference to it in weak_var_attr.c:

%> c29clang -mcpu=c29.c0 weak_var_attr.c weak_def.c -o a.out -Wl,-llnk.cmd,-ma.map

The output when the program is loaded and run is as follows:

my_var is: 5

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

%> c29clang -mcpu=c29.c0 weak_var_attr.c weak_def.c strong_def.c -o a.out -Wl,-llnk.cmd,-ma.map

The output when the program is loaded and run is as follows:

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 does not use the strong definition of the symbol since the reference to the symbol has already been resolved.

2.7.3.13. 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 succeeds, but the run-time behavior will be unpredictable as there will be a reference to an undefined symbol:

%> c29clang -mcpu=c29.c0 weakref_var_attr.c -o a.out -Wl,-llnk.cmd,-ma.map

The output when the program is loaded and run is as follows:

Data fetch: 00000000 is outside configured memory

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

%> c29clang -mcpu=c29.c0 weakref_var_attr.c strong_def.c -o a.out  -Wl,-llnk.cmd,-ma.map

The output when the program is loaded and run is as follows:

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 c29clang reports an unresolved symbol reference:

%> c29clang -mcpu=c29.c0 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.