Skip to main content
Associate II
July 1, 2024
Solved

'used' attribute Appears Ineffective

  • July 1, 2024
  • 14 replies
  • 7954 views

Hello,

According to the manual , "This attribute, attached to a variable with static storage, means that the variable must be emitted even if it appears that the variable is not referenced."

In the following code, unreferenced_variable is always discarded:

static uint8_t unreferenced_variable __attribute__((used));

There's no warning from the compiler.

  1. Is 'used' supported in principle please?
  2. Is there a problem with the syntax?

 

Best answer by Kier

OK, I stumbled on a workaround while browsing the linker script. I added line 9:

 

 .bss :
 {
 /* This is used by the startup in order to initialize the .bss section */
 _sbss = .; /* define a global symbol at bss start */
 __bss_start__ = _sbss;
 *(.bss)
 *(.bss*)
 *(COMMON)
 KEEP (*(.bss.unreferenced_variable*))
 . = ALIGN(4);
 _ebss = .; /* define a global symbol at bss end */
 __bss_end__ = _ebss;
 } >RAM_D1

 

And there it is:

Kier_2-1719993243147.png

Bafflingly, this works even if 'used' attribute is not applied. I would have thought it would be the minimum necessary to emit the variable in the .o file and that the KEEP line would be the last piece of the puzzle, but no.

So 'used' is still, frankly, pointless.

14 replies

AScha.3
Super User
July 1, 2024

Hi,

see here:

https://stackoverflow.com/questions/31637626/whats-the-usecase-of-gccs-used-attribute

>

This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function is referenced only in inline assembly.

<

+

If you declare a global variable or function that is unused, gcc will optimized it out (with warning), but if you declared the global variable or the function with '__attribute__((used))', gcc will include it in object file (and linked executable).

or from ARM , for GCC :

https://developer.arm.com/documentation/dui0472/m/Compiler-specific-Features/--attribute----used---variable-attribute

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

 

"If you feel a post has answered your question, please click ""Accept as Solution""."
BarryWhit
Lead
July 1, 2024

By default optimized builds are compiled with `-ffunction-sections -fdata-sections` options and linked with the `-gc-sections` option. So, in all likelihood, the unused variable is preserved by the compiler (not optimized away) as AScha.3 explained (i.e. it's present in the .o file), but is then pruned away at link time (i.e. it's eliminated from the final .elf file).

GC.jpg

 

This is a very good thing to have on by default for optimized builds since it significantly reduces binary size, and space is usually a scarce resource in embedded systems.

"- If someone's post helped resolve your issue, please thank them by clicking ""Accept as Solution"".- Please post an update with details once you've solved your issue. Your experience may help others."
KierAuthor
Associate II
July 2, 2024

Many thanks for your replies.

It's true that my variable is being pruned away by the linker but why? That's my question. All the sources cited suggest this should not happen.

In @AScha.3's reply, it says: "will include it in object file (and linked executable)"

Also the ARM doc URL cited says: 

"Data marked with __attribute__((used)) is tagged in the object file to avoid removal by linker unused section removal."

In both cases the effect in bold above is not being realised. The point of this attribute is to ultimately override unused section removal in the linker. If it's only partially effective in the toolchain it might as well not be supported at all.

Note that exactly the same source code in a different compiler toolchain (TI Arm Clang) has the desired effect even when unused section elimination is switched on.

I tried to use 'retain' but this is actively ignored (not supported?) by the compiler.

I conclude so far that I'm using it correctly and the expectation that it shall survive linker pruning is legitimate. The problem remains, however, why it's not happening in this case.

BarryWhit
Lead
July 2, 2024

"Data marked with __attribute__((used)) is tagged in the object file to avoid removal by linker unused section removal."

I was not aware of this claim. Find more information about this "tag", and you should able to make progress figuring out why the linker is pruning the variable. You could probably use `objdump` or `readelf` to look at the object file and identify the nature of this "mark", if there is no documentation. Than we need to find out why ld isn't considering it. Perhaps the default linker flags used by ST neglect to add a special exclusion pattern based on this tag. I'm not familiar with that functionality in `ld`, but apparently some kind of linker option for doing this does exist. Find it, report it to ST, wait 6-18 months, and presto you may have solved this problem for every ST customer that walks in your footsteps. Well done!

"- If someone's post helped resolve your issue, please thank them by clicking ""Accept as Solution"".- Please post an update with details once you've solved your issue. Your experience may help others."
KierAuthor
Associate II
July 2, 2024

Thanks for the encouragement Barry.

I'll do a bit more digging but I'll also raise a support ticket now that I know the topic has been sanity checked.

BarryWhit
Lead
July 2, 2024

Sigh. So there seems to be some disagreement about the semantics of used. Some say it means it should be emitted to the object file (but may be prune by linker gc options), and some some say it should remain the the final elf.

Please see this SO answer, which suggest what you want is actually the "retain" attribute.

The gcc documentation suggest this is correct (at least when the attribute is applied to functions):

 

retain

For ELF targets that support the GNU or FreeBSD OSABIs, this attribute will save the
 function from linker garbage collection. To support this behavior, functions that have
 not been placed in specific sections (e.g. by the section attribute, 
or the -ffunction-sections option), will be placed in new, unique sections.

This additional functionality requires Binutils version 2.36 or later.

 

The way I explain my way out of this bowl of spaghetti is:

1. An unreferenced static (i.e. file-scoped) global variable will be eliminated by the compiler if (Certain) optimizations are turned on.

2. ...Unless the the variable is annotated with the "used" attribute, In which case it will be included in both the object and the final elf file.

3. ...Unless you specifically ask the linker to gc unused sections  (and used -fdata-section during compilation),

 in which case the variable will not be included by the linker in the final output elf file.

4. ...Unless you annotate the variable with the "retain" attribute, in which case the linker GC will skip over the variable, and it will be emitted in the final elf file.

"- If someone's post helped resolve your issue, please thank them by clicking ""Accept as Solution"".- Please post an update with details once you've solved your issue. Your experience may help others."
KierAuthor
Associate II
July 2, 2024

Thanks Barry.

I'm still struggling to understand why emitting the symbol in the object file but then discarding in the linker has any use.

It's true that 'retain' may be the answer but it's not supported.

I'm happy to use any attribute, keyword or pragma that succinctly achieves the desired effect, that of making an exception for 'discard unused sections' link option.

A typical use case could be that a part number string is compiled in a boot loader and the string is located at a fixed address. There are no references to that part number in the BL program, only the application program is required to read it by pointer. How can I prevent the part number from being discarded when I compile the BL? It seems the only way is to write some dummy code in the BL to to reference the BL part number.

Pavel A.
Super User
July 2, 2024

Per the gcc documentation attribute retain applies to functions, not to data.

CubeIDE uses GCC compilers and linkers. Other compilers (TI, Keil v.5 and so on) may have the __attribute__ keyword like GCC and clang, but the attributes and their effect can differ. Avoid non-portable compiler extensions, be safe.

 

KierAuthor
Associate II
July 2, 2024

It depends which page you're reading. 'retain' is described for variables too.

Common Variable Attributes (Using the GNU Compiler Collection (GCC))

Pavel A.
Super User
July 2, 2024

Oops I stand corrected. 

But note that "To support this behavior, variables that have not been placed in specific sections (e.g. by the section attribute, or the -fdata-sections option), will be placed in new, unique sections." - so the linker script should be compatible with these new sections, or they may end in a wrong place.

BarryWhit
Lead
July 2, 2024

Sure, but the default compilation flags under Cube already specify -ffunction-sections and -fdata-sections, I don't think anything manual needs to be done. I've worked with and modified the linker scripts in the past (to reserve a section of flash for NV storage for example), but I don't remember seeing anything there that explicitly handles these sections. I wonder how it works on the nitty-gritty level.

 

Just FYI, these options are very common optimizations used in embedded context. I've also seen them elsewhere outside the STM32 ecosystem.

"- If someone's post helped resolve your issue, please thank them by clicking ""Accept as Solution"".- Please post an update with details once you've solved your issue. Your experience may help others."
KierAuthorBest answer
Associate II
July 2, 2024

OK, I stumbled on a workaround while browsing the linker script. I added line 9:

 

 .bss :
 {
 /* This is used by the startup in order to initialize the .bss section */
 _sbss = .; /* define a global symbol at bss start */
 __bss_start__ = _sbss;
 *(.bss)
 *(.bss*)
 *(COMMON)
 KEEP (*(.bss.unreferenced_variable*))
 . = ALIGN(4);
 _ebss = .; /* define a global symbol at bss end */
 __bss_end__ = _ebss;
 } >RAM_D1

 

And there it is:

Kier_2-1719993243147.png

Bafflingly, this works even if 'used' attribute is not applied. I would have thought it would be the minimum necessary to emit the variable in the .o file and that the KEEP line would be the last piece of the puzzle, but no.

So 'used' is still, frankly, pointless.

KierAuthor
Associate II
July 9, 2024

ST support mentioned that KEEP is needed in the linker script but have not acknowledged that this works without the used attribute. On the one hand there is a solution but on the other, the assertion in the title still holds true. 'used' attribute Appears Ineffective.

It looks like its only benefit is to document variables in the source that one intends to bypass unused section removal. In addition, if the same source (with 'used' attributes) was ported to a toolchain that did support 'used' (e.g. TI arm clang) it would not need further linker instructions. So for these reasons it is probably better to keep the 'used' attribute even though it doesn't do anything useful in the STM scenario.

BarryWhit
Lead
July 9, 2024

> 'used' attribute Appears Ineffective.

 

But it's not ineffective. It works as intended. You're compiling with -fdata-sections and --gc-sections, so you're explicitly asking for the variable to be pruned. You can't blame the toolchain for doing exactly what you're asking it to do, and you can't call the attribute ineffective because of it.

 

 >  In addition, if the same source (with 'used' attributes) was ported to a toolchain that did support 'used'

> (e.g. TI arm clang) it would not need further linker instructions.

 

This would surprise me. Have you really tested this claim against TI's compiler (with the same options compiler/linker)?

 

Because from what I understand, other compilers would behave in the same way. i.e. specifying --gc-sections explicitly asks the linker to ignore the used attribute. If the `retain` attribute is not available, manually adding a carve-out in the linker script is probably the best you'll get.

It's also possible (untested) that you could get what you want by using the `section` attribute instead.

If you manually place the variable in a section that contains any other used variables, it should not get pruned.

 

Also, the use case you gave earlier was that of a version string kept a fixed memory location in a bootloader. But there would be no need to make the variable static in that case (in fact, exporting it as a symbol would be good documentation), and you could not get it to be at a known fixed location with manually manipulating the linker script, anyway.

 

 

"- If someone's post helped resolve your issue, please thank them by clicking ""Accept as Solution"".- Please post an update with details once you've solved your issue. Your experience may help others."
BarryWhit
Lead
July 2, 2024

Manually editing the linker script is "cheating".

 

Also, the gcc compiler has no idea what's in the linker script. So if it really is an attribute-less, static, global, unreferenced variable, and you're compiling with -O2 (or whatever), and the object file contains this variable, it really shouldn't be there. That would really be a bug.

"- If someone's post helped resolve your issue, please thank them by clicking ""Accept as Solution"".- Please post an update with details once you've solved your issue. Your experience may help others."