27

In our source files we usually have a version string like that:

static const char srcvers[] = "VERSION/foo.c/1.01/09.04.15";

When that string isn't optimized away, it's quite useful in certain cases, as one can determine the version of each source file linked to an executable by simply calling strings a.out | grep VERSION.

Unfortunately it is optimized away by gcc (using '-O'). So my question is, is there a simple way (a compiler switch would be great) to make gcc keep that variable (its name is always the same) without switching off any other optimizations.

Edit

What, in my opinion, makes the question different from that one, is that I'm was hoping to find a solution for which I wouldn't have to touch thousands of source files.

Community
  • 1
  • 1
Ingo Leonhardt
  • 9,435
  • 2
  • 24
  • 33
  • What about add `-v` option on all your binary displaying this var ? – Ôrel Apr 09 '15 at 17:34
  • Maybe try tricking `gcc` into thinking the variable is used (something like `strlen(srcvers);`)? – Drew McGowen Apr 09 '15 at 17:36
  • 3
    Tried to make it `volatile`? It should work: `volatile static const char srcvers[] = "VERSION/foo.c/1.01/09.04.15";` – Sam Protsenko Apr 09 '15 at 17:36
  • @Ôrel do you mean `a.out -v`? that would work for one source file only, and we do that already. I would like to get information about all files linked together – Ingo Leonhardt Apr 09 '15 at 17:39
  • possible duplicate of [How to prevent compiler optimization on a small piece of code?](http://stackoverflow.com/questions/7083482/how-to-prevent-compiler-optimization-on-a-small-piece-of-code) – bentank Apr 09 '15 at 17:39
  • @Drew it's thousands of source files (I'm serious, really), so that was to much effort vs. profit – Ingo Leonhardt Apr 09 '15 at 17:40
  • @Sam right, that works. Although it doesn't help for all the files unchanged, it's at least a good idea for the future. Thanks – Ingo Leonhardt Apr 09 '15 at 17:44
  • in your build system, generate a file with all you version and display it with -v – Ôrel Apr 09 '15 at 17:45
  • You could just remove the word static. Since it is not static, it must be public so the compiler will not remove it. May be thousands of files but you could use sed to do them. – cup Apr 09 '15 at 18:09
  • @IngoLeonhardt I added the answer which takes into account your desire **not** to modify source files. – Sam Protsenko Apr 09 '15 at 18:13
  • Try `-Dsrcvers='volatile __attribute__((used)) srcvers`. – n. m. could be an AI Nov 09 '17 at 15:08

5 Answers5

39

You can use __attribute__((used)) gcc (also works in clang) specific (I see that the question is tagged gcc) attributes for this:

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.

From https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Demo:

$ cat a.c
static const char srcvers[] __attribute__((used)) = "VERSION/foo.c/1.01/09.04.15";
$ gcc -O3 -c a.c
$ strings a.o
VERSION/foo.c/1.01/09.04.15

You can use some #ifs and #defines to make this terser and also compile on compilers which don't support this extension.

Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • 2
    Although the documentation excerpt describes usage of this attribute with functions, the same attribute can also be used with variables, provided they have static storage (as in this case). – John Bollinger Apr 09 '15 at 17:46
  • great idea, thank you. But the ideal solution for was one where I wouldn't need to modify every source file. – Ingo Leonhardt Apr 09 '15 at 17:49
  • @IngoLeonhardt - you can do something similar with a linker script. – Carl Norum Apr 09 '15 at 18:00
  • http://stackoverflow.com/questions/9827157/what-does-keep-mean-in-a-linker-script – Carl Norum Apr 09 '15 at 18:12
  • I finally decided to accept your answer although it's not quite what I hoped for but it seems that what I hoped for doesn't exist and this is the second best thing to do. Thanks to all – Ingo Leonhardt Apr 13 '15 at 10:48
  • Note that attribute((used)) cannot be used on locals, and gcc 6 has gotten smarter about optimizing away locals that you don't appear to be using. – Daniel Santos Apr 15 '17 at 21:43
  • Is there a similar mechanism for MSVC? Or does that compiler never remove unused static variables in a static library? – void.pointer Jun 05 '18 at 18:38
  • 7
    ```__attribute__((used))``` did not work for me with a global variable (the documentation does imply it only works on functions) (arm-none-eabi gcc 7), but putting the symbol in a different section via ```__attribute__((section(".data")))``` did work. This is presumably because the linker's is only able to strip symbols when they are given their own section via `-fdata-sections`. I do not like it, but it worked. – szmoore Jan 25 '19 at 10:04
  • @szmoore Unfortunately specifying section doesn't work as you describe for AVR (GCC 5.4.0). Object will not be placed in `.data` (why though?) and linker flushes the unused section out. After reading all I can find online, the only way to keep an object is to somehow actually use it... I feel like this is much too hard, and that it shouldn't be. – Tammi Apr 09 '23 at 14:34
  • @Tammi Have a look here: https://stackoverflow.com/questions/9827157/what-does-keep-mean-in-a-linker-script – nielsen Jun 29 '23 at 07:39
5

As I understand your question, you need to add version string to every object file without touching sources. It can be done using next way.

Create header file, for example include/version.h:

#ifndef VERSION_H
#define VERSION_H

static const char _ver[] __attribute__((used)) = "VERSION/foo.c/1.01/09.04.15";

#endif /* VERSION_H */

Then in your Makefile (or whatever your build system is) add next gcc flag:

CPPFLAGS += -include include/version.h

Of course it should be passed to gcc, e.g. like this:

%.o: %.c
    $(CC) $(CFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).c

Now you can observe your _ver string compiled to every object file:

$ objdump -DS src/main.o | grep _ver

Which will show you something like that:

Disassembly of section .rodata._ver:
00000000 <_ver>:
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
  • the version string is already there but are optimized away (and of course I need that one with dates like '03.01.01', some files are really old). But the idea wih `-include` smells a little bit like I could use it to solve the problem anyway. Thank you again – Ingo Leonhardt Apr 09 '15 at 18:13
  • Ah, your source files have different versions, I see. Thought that version stands for project itself. – Sam Protsenko Apr 09 '15 at 18:15
  • nope that would be too easy (in fact that's an additional information we already have, but somtimes you just need to be sure if a certain change in a file included by a library .. is linked or not) – Ingo Leonhardt Apr 09 '15 at 18:15
  • Well, the best way to fix it then is to modify code in each source file (adding `__attribute__((used))`). It can be easily done with bash oneliner, using `find` and `sed` commands. Do you have something preventing you to do that? – Sam Protsenko Apr 09 '15 at 18:19
  • yes of course, but I really want old files to remain literally old. Besides the files are checked in in an sccs based release control system (have I already mentioned 'old'?) but that would be solvable, of course. – Ingo Leonhardt Apr 09 '15 at 18:22
  • I figured some way to do what you want, but it's kinda ugly and you are probably not gonna like it :) You can generate **new** source files before running compilation, adding `__attribute__((used))` to your version string, and then run build process using generated files. Right after build you can remove those generated files. – Sam Protsenko Apr 09 '15 at 18:27
1

Declaring the variable as volatile can also help. That's why it is used in the first place, preventing any optimizations by the compiler regarding that variable.

codingEnthusiast
  • 3,800
  • 2
  • 25
  • 37
  • It definitely helps; I have already checked. But again, this would mean touching each and every file :-(. Thanks anyway – Ingo Leonhardt Apr 09 '15 at 17:57
  • 4
    Note that this is *implementation defined*. There is no guarantee that this will work, as standard only requires that `volatile` affects accesses to the variable. If variable is never used, compiler can remove it freely. – user694733 Nov 09 '17 at 10:09
  • Yup, just to confirm @user694733 - just tried `volatile` in hopes to prevent removal/optimization of unused variable in a GCC C project, but it got optimized away anyways. – sdbbs Sep 28 '22 at 18:29
1

As it seems that all the solutions require some kind of decoration of the version string in the source, it may help to define a macro containing all the necessary syntax and then use this macro in the source or header files whenever need:

#define SRCVERSION(file, version, data) static const char _ver[] __attribute__((used)) = "VERSION/" file "/" version "/" date;

Then in your source just put

SRCVERSION("foo.c", "1.01", "09.04.15")

The macro may be in a central project header file or on the compiler's command line.

That way, at least you do not have to touch all the source files again if you want to change something about the definition.

Note how the macro definition uses string concatenation to build the final version string. Also it contains the final semicolon so you can remove everything by defining an empty macro if needed.

Ber
  • 40,356
  • 16
  • 72
  • 88
0

You are concerned about gcc removing an unused static char[] variable. AFAIK, the compiler is right to do so.

Other answers provided suggestion to improve that. But you don't want to change the source code of thousands of files.

Then, you might perhaps change your build (e.g. some Makefile) so that every such source file using your trick (which is slightly wrong, as discussed here...) would not need to be changed. So you might invoke GCC specifically. You want

 static const char _ver[] __attribute__((used));

(this is a declaration, not a definition) to be compiled before anything else. Put the line above in some _declare_ver.h file, and compile with gcc -include _declare_ver.h command (instead of gcc). If using make add

 CFLAGS += -include _declare_ver.h

in your Makefile.

BTW, that is a dirty trick. You should consider doing something better (following other answers).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547