2

In an LJ post, the --defsym flag was used for passing the build date into the source code:

#include <stdio.h>

extern char __BUILD_DATE;

void main(void) {
    printf("Build date: %u\n", (unsigned long) &__BUILD_DATE);
}

by linking with the following flags:

gcc example.c -Xlinker --defsym -Xlinker __BUILD_DATE=$(date +%Y%m%d)

According to the ld manual,

--defsym symbol=expression Create a global symbol in the output file, containing the absolute address given by expression.

I am trying to understand the following:

  1. How is the 9-character string for the build date (YYYYmmdd+\0) stored in memory?
  2. If --defsym creates a symbol containing an address, why __BUILD_DATE is defined as a char and not as a pointer or as an integral type?
  3. Why __BUILD_DATE is defined as char and not unsigned long if it is eventually casted to unsigned long?
Sparkler
  • 2,581
  • 1
  • 22
  • 41
  • That looks like 3 questions to me. Not one. Maybe you want to post 3 individual questions..? – Jesper Juhl Nov 12 '18 at 22:10
  • @JesperJuhl I think it's 3 small parts of one questions about the usage of `--defsym`. It makes no sense to me to split the question. – Sparkler Nov 12 '18 at 22:14
  • "Why __BUILD_DATE is defined as char and not unsigned long if it is eventually casted to unsigned long?" Is definitely a standalone question IMHO. As is "If --defsym creates a symbol containing an address, why __BUILD_DATE is defined as a char and not as a pointer or as an integral type?". But, that may just be me. – Jesper Juhl Nov 12 '18 at 22:17

1 Answers1

2

Linkers see globals as addresses (pointers to the "actual" global rather than the actual global -- even if the actual global doesn't exist at that address). -Xlinker --defsym -Xlinker __BUILD_DATE=$(date +%Y%m%d) sets the address of __BUILD_DATE not the value. When the __BUILD_DATE linker entity has an address but not a value, you can get the address by declaring the entity as anything and then taking the address of that.

In:

#include <stdio.h>

//extern long __BUILD_DATE[128];
//extern int __BUILD_DATE;
extern char __BUILD_DATE;

int main(void) 
{
    printf("Build date: %lu\n", (unsigned long)&__BUILD_DATE);
}

Any of the three declarations should work. Just don't try to use the value of that (pseudo) global. That would be like dereferencing an invalid pointer.

That should answer 2 and 3. To answer 1, -Xlinker --defsym -Xlinker __BUILD_DATE=$(date +%Y%m%d) stores the number returned (stdout) by $(date %Y%m%d) as the address of __BUILD_DATE. It doesn't store a string.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • so anything passed using `--defsym` can only be as large as `size_t`/`ptrdiff_t`? – Sparkler Nov 12 '18 at 22:59
  • @Sparkler There doesn't need to be much of a correlation between the linker's limits and the limits of C types, but practically you very likely won't be able to store addresses larger than your SIZE_MAX. – Petr Skocik Nov 12 '18 at 23:02
  • 3
    Interestingly, the above's only working for me with clang. I can't quite get it to work if I use gcc for linking (with whatever `-fuse-ld` option or without it). The debugger always shows the stored timestamp if I do `p (unsigned long)&__BUILD_DATE` but the program's printing the stored value offset by `0xffffaaaaaaaac000` but only if my system's address layout randomization's off. (Otherwise it's random). I don't have an explanation for this. – Petr Skocik Nov 12 '18 at 23:07