The Matt
The Matt

Reputation: 1734

MacOS Clang Extern Tentative Definition is not Respected

Running into a compilation issue on MacOS 12. Seemingly valid C code is failing to compile into a static library. Compiling this code normally works, and this compiles just fine using the GCC compiler.

MacOS 12 x86-64 MBA.

Compiler version:

$ clang --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

When I compile the code as a static library, I get the follow error when I try to create an executable with the static library.

$ clang -c -Wall -std=c11 ./extern_test.c
$ ar rcs libextern.a extern_test.o
warning: /Library/Developer/CommandLineTools/usr/bin/ranlib: archive library: libextern.a the table of contents is empty (no object file members in the library define global symbols)
$ clang -Wall -std=c11 extern_main.c libextern.a
Undefined symbols for architecture x86_64:
  "_external_number", referenced from:
      _main in extern_main-61037c.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Trying to static link a custom library. The following code example fails if the variable external_number is defined with the following tentative definition:

// File: extern_test.h
extern int external_number;

// File: extern_test.c
int external_number; // The tentative definition.

However, if this variable is defined with an explicit definition expression, this code works as expected.

// File: extern_test.c
int external_number = 0; // Explicit definition.

Full code example:

// File: extern_test.h
#pragma once

extern int external_number;
// File: extern_test.c
#include "extern_test.h"

int external_number /* = 0 */;
// File: extern_main.c
#include <stdio.h>

#include "extern_test.h"

int main() {
    printf("external_number = %d\n", external_number);
    return 0;
}

Linkage

Using the nm command to look at the linkage of variables given by the compiler.

After compiler the initial .o file, I see the following results for the linkage of the extern_test.c file.

$ clang -c -Wall -std=c11 ./extern_test.c
$ nm -m extern_test.o 
0000000000000004 (common) (alignment 2^2) external _external_number 

It's my understanding that "common" linkage is incorrect. If I use the version that sets external_number = 0;, I get the following results from nm:

% gcc -c -Wall -std=c11 ./extern_test.c
% nm -m ./extern_test.o                
0000000000000000 (__DATA,__common) external _external_number

The immediate solution to the problem is to explicitly set the variable to 0 with external_number = 0, but C allows for global variables to be zero initialized by default, so this should not be required.

Question

Is this a clang compiler bug, or is there something else going on here?

See Also

Mac specific documentation on checking external linkage attributes using the nm command: https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/ReducingExports.html#//apple_ref/doc/uid/20001864-CJBJFIDD

C example of using an extern tentative declaration to define a global variable:

https://en.cppreference.com/w/c/language/storage_duration

https://en.cppreference.com/w/c/language/extern

Upvotes: 2

Views: 35

Answers (0)

Related Questions