Skip to main content
Associate III
July 1, 2024
Solved

'used' attribute Appears Ineffective

  • July 1, 2024
  • 23 replies
  • 8163 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?

 

This topic has been closed for replies.
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.

23 replies

BarryWhit
Lead
July 2, 2024

I've manually verified my analysis from a prior comment, when using x86 GCC 14.1.1 on linux.

The only caveat is that the `used` and `retain` attributes appear to be complementary - i.e. you have to specify both.

Summary:

- if you attach the `used` AND `retain` attributes to a static global variable then

- ... even if you compile with -O2

- ... and even if you compile with -fdata-sections (AND)

- ... you link with -Wl,--gc-section

The variable will survive the process, and make it to the final ELF file. No modification to the linker script are required.

 

.PHONY: 1
1: 1.c
	gcc -O2 -fdata-sections -c 1.c -o 1.o
	gcc -Wl,--gc-section 1.o -o 1 
	-readelf -s 1.o | grep -i foo
	-readelf -s 1 | grep -i foo
#include <stdio.h>
#include <stddef.h>

#define KEEPER __attribute__((used)) __attribute__((retain))
static int foo KEEPER = 0;
int main(int argc, char const *argv[]) {
 printf("hello\n");
 return 0;
}

 

Output:

 

gcc -O2 -fdata-sections -c 1.c -o 1.o
gcc -Wl,--gc-section 1.o -o 1
readelf -s 1.o | grep -i foo
 4: 0000000000000000 4 OBJECT LOCAL DEFAULT 7 foo
readelf -s 1 | grep -i foo
 4: 000000000040400c 4 OBJECT LOCAL DEFAULT 24 foo

 

@Kier , is this a solution or not?

 

"- 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 III
July 3, 2024

Thanks again Barry for all the effort you have expended, it's appreciated.

Unfortunately it's a non starter because 'retain' is not supported in the compiler that comes with STM32CubeIDE.

Kier_0-1719992277543.png

Presumably this is because the conditions mentioned here are not met.

Kier_1-1719992563026.png

I've raised a ticket with ST. I'm sure they will come back to explain why it doesn't work.

BarryWhit
Lead
July 3, 2024

The binutils version for my old(er) CubeIDE is 2.38.20220708, so that's not the issue.

It must be the ABI requirement that's not met.

 

I'm sure they will come back to explain why it doesn't work.

 

The 'used` attribute is working exactly as intended. If you remove the  "--gc-sections" linker options, the variable will appear in the output.

The default linker options for Cube projects (helpfully) include "--gc-sections". Which means you've asked (*) the linker to prune the variable (to the linker, this is just an unused section). This also, is exactly as intended.

I suggested `retain` as a workaround since you could have used to achieve your goal. Unfortunate that it isn't supported as you rightfully pointed out.

 

You could certainly ask ST OLS for advice on how to achieve your goal. I can suggest one as well:

- remove "-fdata-sections" from the compilation flags. Be aware that this may result in a larger final binary (**).

 

Overall, we've definitely answered your original question (as stated).

 

(*)

GC2.jpg

(**)

GC3.jpg

"- 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."
Visitor II
July 2, 2024

Thanks buddy!!!

KierAuthor
Associate III
July 9, 2024

We'll have to agree to disagree.

I'm asking the toolchain to keep the variable irrespective of optimization by marking it in source code for special handling. Other compilers have set a precedent for being able to do just that but this particular toolchain does not do what I'm asking.

The same 'used' attribute does exactly what I'm asking in at at least one other compiler (TI arm clang) without jumping through further hoops.

2.7.3. Variable Attributes — TI Arm Clang Compiler Tools User's Guide

This is confirmed by my extensive use of this toolchain in other projects. It's not just a "claim".

It's possible that the problem here (with GCC) is that 'retain' is not supported. If 'retain' is required to stop the linker pruning then 'used' may as well not be supported either. I think that without 'retain', 'used' is like a bike with one wheel. The single wheel works perfectly well but the bike is useless as a bike.

In other words, 'used' is ineffective.

BarryWhit
Lead
July 9, 2024

This is confirmed by my extensive use of this toolchain in other projects. It's not just a "claim".

I believe you, and looked at the reference you provided. It does seem like the semantics of `used` is simply different for the ST vs. TI toolchains. (pavel did warn us about vendor-specific extensions early on...)

 

Here's another possible solution for GCC:

--gc-keep-exported
When --gc-sections is enabled, this option prevents garbage
collection of unused input sections that contain global
symbols having default or protected visibility. This option
is intended to be used for executables where unreferenced
sections would otherwise be garbage collected regardless of
the external visibility of contained symbols. Note that this
option has no effect when linking shared objects since it is
already the default behaviour. This option is only supported
for ELF format targets.

 

So, you still haven't explained why a variable that you expect someone/something to access someday needs to be static. What would the disadvantage be in exporting the symbol? in a bootloader it's meaningless, in an shlib it makes sense, and if it's an easter-egg for some venutian invader reverse-engineering the firmware for your gadget in the year 2099, it's harmless. 

 

as for whether "ineffective" is the proper term, we do disagree (but I, also, agree to).

 

"- 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."
Pavel A.
July 9, 2024

Anything that you write in the C source is seen by the compiler. So you want the compiler to leave a message to the linker (in the object file) not to discard this data item. The linker obviously should understand this message. New clang based toolchains have many cool features, they may do this one too.  Same about proprietary toolchains (IAR, Keil...) - they can do any proprietary tricks to help their sales. Now imagine what the GCC (opensource) maintainers would do? They would say - the ld already has KEEP exactly for this purpose; just add KEEP for your custom section name in your link script and call this a bike ))   Adding a special attribute "retain" (backporting from clang?)  to produce these special section names is just a syntactic sugar.