0

I want to define a external global symbol (const char*) used by the program. Add the symbol at linking time with a given value. This is useful for example commit hash or build time.

I found --defsym which does something else. The Go linker supports this functionality via the -X option. (Yeah I know, Go Strings are managed, but I am talking about plain old zero terminated c strings)

For example:

extern const char *git_commit;

int main(int argc, char *argv[]) {
    puts(git_commit);
    return 0;
}
gcc main.o -Wl<something here that adds git_commit and set it to '84e5...'>

I am aware of config.h approach and building object files containing those string. But its 2019 by know. Such a simple task should be easy.

Edit more precise question Is there a equivalent option in gcc/binutils for Go Linker's -X option.

Lars M.
  • 179
  • 10
  • 1
    How is putting the string in a `config.h` not easy? Finding linker parameters that do what you want (including getting the encoding right) seems nightmarish in comparison. – nwp May 10 '19 at 10:23
  • 1
    No it is stupid. You have to rebuild every file that uses this `config.h` file. Which can be a lot due to include hell. Go Linker supports it for a good reason. – Lars M. May 10 '19 at 10:30
  • If you don't want to invoke a compiler or anything, you can always just write a program that directly writes an object file with a fitting symbol definition… – Michael Kenzel May 10 '19 at 10:31
  • My question was if it is possible. Everyone here is solving a different problem. I used the git_commit as an example to simple make clear what the outcome should be. – Lars M. May 10 '19 at 10:35
  • All that being said, artifacts from the VCS (SVN revisions, GIT hashes etc.) are a poor way of software version control. Your software should have its own versioning, which is independent of the version control of its sources. – DevSolar May 10 '19 at 10:35
  • All that being said, given a binary, knowing which commit it origins from is helpful. – Lars M. May 10 '19 at 10:37
  • You should tag the commit accordingly, not use artifacts of the VCS. (Consider, for example, what happens when you migrate to a different VCS software. Tags, branches etc. usually survive migration. Git's hash values don't, necessarily.) – DevSolar May 10 '19 at 10:41

4 Answers4

5

There is a change to do it in compile/preprocessing time, consider this:

#include <stdio.h>

const char * git_commit = GIT_COMMIT;

int main(int argc, char ** argv) {
        puts(git_commit);
        return 0;
}

and in command line:

gcc -o test test.c -DGIT_COMMIT="\"This is a test\""

assuming you are using GCC compiler.

Diodacus
  • 663
  • 3
  • 8
  • 1
    Ninja'd. +1, this is the most "natural" (i.e., least "tricky") approach. Portable, too. – DevSolar May 10 '19 at 10:32
  • @nwp: Which recompile problem? – DevSolar May 10 '19 at 10:36
  • 1
    The problem of wanting to change the string without having to recompile everything. – nwp May 10 '19 at 10:37
  • @nwp: This is not a header file. This is a source file. Declare `extern const char * git commit;` in some header, and you can reference the definition in this source file from anywhere in your source. _There is no recompile problem._ You have to re-*link*, but you would have to do that with a linking-time solution just as well. – DevSolar May 10 '19 at 10:38
  • Ah, I see. You just build an object file that gets linked, same as the other answer. – nwp May 10 '19 at 10:40
  • @nwp: Exactly. Because that's "natural" to C/C++, while putting your arm up to the elbow into the linker process and fiddling with the squishy bits isn't. ;-) – DevSolar May 10 '19 at 10:43
2

One way:

echo "char const* git_commit = \"$(git rev-parse HEAD)\";" > git_commit.c
gcc -c -o git_commit.o git_commit.c
gcc -o main main.o git_commit.o

When you implement this in a makefile you may like to only recreate git_commit.c when the revision changes, so that it doesn't relink it on each make invokation.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Does not provide an answer to the questions, but instead just solves the problem used as example. – Lars M. May 10 '19 at 10:36
  • 2
    @LarsM. To be fair, you only have statements in your post, not questions. – Maxim Egorushkin May 10 '19 at 10:38
  • 4
    @LarsM. Seems like you need a better example. One where solving the example also solves the problem. – nwp May 10 '19 at 10:38
  • 1
    @LarsM.: There is often a situation where people have a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) -- they have a problem X, which they tried to solve in the way Y, and then post a question about Y. Many experienced SO users / developers recognize this, and actually address the X problem instead of the Y problem. Consider if this might actually solve your X problem... – DevSolar May 10 '19 at 10:47
1

I'm not aware of any way to do what you're seeking to do directly via a flag using one of the commonly used linkers. In general, if you want to link the definition of such an object into your program, you'll have to provide an object file containing a suitable symbol definition.

The probably simplest way to get there would be to just invoke the compiler to compile the definition of your variable with the content being fed from a macro defined via the command line like already suggested in the other answers. If you want to avoid creating temporary source files, gcc can also receive input straight from stdin:

echo "const char git_commit[] = GIT_COMMIT;" | gcc -DGIT_COMMIT=\"asdf\" -c -o git_commit.obj -xc++ -

And in your code your just declare it as

extern const char git_commit[];

Note: I'm using const char git_commit[] rather than const char* git_commit. That way, git_commit will directly be an array of suitable size initialized to hold the contents of the commit hash. const char* git_commit, on the other hand, will create a global pointer object initialized to hold the address of a separate string literal object, which means you introduce an unnecessary indirection. Not that it will really matter here, but it also doesn't really cost you anything to skip the inefficiency, however tiny it might be…

There would also be the objcopy utility which can be used to wrap arbitrary binary content in an object file, see, e.g., here How do I embed the contents of a binary file in an executable on Mac OS X? It may even be possible to pass input to objcopy straight via stdin as well. Finally, you could also just write your own tool that directly writes an object file containing a suitable symbol definition. Consider, however, that, at the end of the day, you're seeking to generate an object file that can be linked with the other object files making up your program. Simply using the same compiler you use to compile the rest of the code is probably the most robust way of going about doing that. With other solutions, you'll always have to manually make sure that the object files are compatible in terms of target architecture, memory layout, …

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
0

You could embed it as a ressource file, for example:

GitInfoHTML               HTML                    "GitInfo.html"

This will placed into the executable by the linker (at link time) and can then be loaded.

For more info, see: https://learn.microsoft.com/en-us/cpp/windows/resource-files-visual-studio?view=vs-2019

darune
  • 10,480
  • 2
  • 24
  • 62