1

I witnessed this error which is reproducible in AppleClang 12.0.0 but not with gcc 7.5.0. The issue is that I have an extern variable defined in a static library which a different static library wants to use. The linker states that the symbol for the variable is undefined. I tried to come up with an as minimal as possible repro:

externs.h
extern int A;
a.c
int A;
b.h
void bar();
b.c
#include "externs.h"

#include <stdio.h>

void bar()
{
    A = 100;
    printf("%d\n", A);
}
main.c
#include "b.h"

int main()
{
    bar();
}
makefile
liba.a: a.c
    gcc -c a.c -o a.o
    ar crs liba.a a.o

libb.a: b.c
    gcc -c b.c -o b.o
    ar crs libb.a b.o

program: main.c liba.a libb.a
    gcc main.c -L. -lb -la -o program

On Linux, this program compiles, links and runs without issue. On macOS (where gcc is AppleClang) I get the following output :

gcc -c a.c -o a.o
ar crs liba.a a.o
warning: /Library/Developer/CommandLineTools/usr/bin/ranlib: archive library: liba.a the table of contents is empty (no object file members in the library define global symbols)
gcc -c b.c -o b.o
ar crs libb.a b.o
gcc main.c -L. -lb -la -o program
Undefined symbols for architecture x86_64:
  "_A", referenced from:
      _bar in libb.a(b.o)
ld: symbol(s) not found for architecture x86_64

AFAICT, the warning is a red herring. If I add a dummy function to a.c, the warning doesn't show. Further, if I inspect liba.a with nm I get the following output:

liba.a(a.o):
0000000000000004 C _A

If I do add a dummy function foo (which doesn't even refer to A) to liba.a and invoke it from main.c, the linking issue gets magically resolved. It's as if if and only if main.o is dependent on a symbol in liba.a, then all the symbols of liba.a become available to anything that may be dependent on them.

screwnut
  • 1,367
  • 7
  • 26
  • Not of use to you, right now, but for what it's worth, I tried to reproduce this with clang on Ubuntu and did not replicate the error. I clang --version reports that I used `clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)`, compiled for `Target: x86_64-pc-linux-gnu` and with `Thread model: posix` – sinback Apr 11 '21 at 12:53
  • Look at the warning on `a.o`: it seems the compiler did not create an `int A`. Seems it created an empty object. – Paul Ogilvie Apr 11 '21 at 13:03
  • 3
    https://stackoverflow.com/questions/25050575/basic-use-of-c-archives-libraries-with-clang-linker – Hans Passant Apr 11 '21 at 13:10
  • @pmg That wouldn't explain why it works with `gcc` nor why it works if an additional function is added to `liba.a`. – screwnut Apr 11 '21 at 13:17
  • @PaulOgilvie Then why would that symbol show up with `nm`? – screwnut Apr 11 '21 at 13:17
  • @HansPassant That is it. If `A` is initialized with `= 0;`, the linking issue goes away. In my actual code, `int A` was actually a `struct`. Adding a `= {};` resolved it. Thanks. – screwnut Apr 11 '21 at 13:22
  • Adding `a = {};` means you're not using a Standard C compiler for your code ... maybe C++, maybe an extension?? – pmg Apr 11 '21 at 13:44
  • @pmg Interestingly, `-Wall -Wextra -Wpedantic` produces no warning about it (by AppleClang). Would `a = {0};` be the standard way to initialize a `struct` to zero? Mind you, I don't mind it to be initialized with trash since it gets overwritten at runtime given user input. – screwnut Apr 11 '21 at 15:05
  • 1
    Yes, the C Standard does not allow empty initializer list, so use `= {0}` to zero-initialize. You may want to add `-std=c11 -pedantic` to be *more* standard – pmg Apr 11 '21 at 15:17
  • https://clang.llvm.org/docs/UsersManual.html#differences-between-various-standard-modes ... If no `-std=xxx` is given clang defaults to `-std=gnu17` – pmg Apr 11 '21 at 15:39

0 Answers0