GNU Linker no longer overrides Weak symbols? [duplicate] - c

This question already has answers here:
Getting a "multiple definition of 'X' first defined here" error in C, although I only defined X once
(1 answer)
Multiple definitions errors showed up in project history in 2020. Now no commit before that point can be built without adding "extern"
(1 answer)
Closed last month.
When trying to compile and link two c modules with GCC, both containing a global variable with the same name, I get an error of multiple definitions.
Despite the fact that both are so-called weak symbols.
A code example:
I've tried to compile and link the 2 following c modules:
// b1.c
int x;
int main() {}
// b2.c
int x;
Now, supposedly, the var x in both files, should be a weak symbol.
So when linking, both should reference the same value.
But, for some reason, I get the following error:
$ gcc b1.c b2.c
/usr/bin/ld: /tmp/ccCP4DM3.o:(.bss+0x0): multiple definition of `x`; /tmp/ccN4Bd4O.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
$ gcc --version
gcc 11.3.0
$ ld --version
GNU ld 2.38
So, did something change? Because according to the "Computer Systems
A Programmer’s Perspective" section 7.6, it should compile and link just fine using gcc without any special flags.

Related

clang not recognizing unitialized pointer found in static library

I've found a curiosity when compiling with clang (on a MacBook, if it helps). Suppose I have two files:
blah.c
int *p;
main.c
#include <stdio.h>
extern int *p;
int main() {
printf("%p\n", p);
return 0;
}
If I compile with
clang blah.c main.c
everything works out fine. However, if I do
clang -c blah.c
ar rcs libblah.a blah.o
clang main.c libblah.a
I get a linker error:
Undefined symbols for architecture x86_64:
"_p", referenced from:
_main in test-4bf0d6.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Interestingly, if I initialize the variable in blah.c,
#include <stddef.h>
int *p = NULL;
the error goes away.
Also, compiling with gcc doesn't produce this behavior. What exactly is going on with clang here?
Here's the output from clang --version:
Apple clang version 13.0.0 (clang-1300.0.29.30)
Target: x86_64-apple-darwin21.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
What exactly is going on with clang here?
TL;DR: Your Clang has a bug. You can probably work around it without modifying your code by adding -fno-common to your compile options.
More detail
Both variations of your code are correct, and as far as the C language specification is concerned, they have the same meaning. On my Linux machine, GCC 8.5 and Clang 12 both accept both variations and successfully build working executables, whether blah.o is linked directly or from a library.
But if you use nm to examine the library built with and without the initializer for p, you will likely get a hint about what is happening. Without an initializer, I see (with either compiler) that p has type 'C' (common). With an initializer (to null), I see that it has type 'B' (BSS).
That is reflective of a traditional behavior of Unix C implementations: to merge multiple definitions of the same symbol as long as no more than one is defined with an explicit initializer. That is an extension to standard C, for the language requires that there be exactly one definition of each external symbol that a program references. Among other things, that extension covers the common error of omitting extern from variable declarations in headers, provided that the header does not specify an initializer.
To implement that, the toolchain needs to distinguish between symbols defined with an explicit initializer and those defined without, and that's where (for C) symbol type "common" comes in -- it is used to convey a symbol that is defined, but without an explicit initializer. Typical linker behavior would be to treat all such symbols as undefined ones if one of the objects being linked has a definition for that symbol with a different type, or else to treat all but one of them as undefined, and the other as having type B (implying default initializtion).
But the MacOS development toolchain seems to have hatched a bug. In your example, it is erroneously failing to recognize the type C symbol as a viable definition when that appears in a library. The issue might be either in the Clang front end or in the system linker, or in a combination of both. Perhaps this arrived together with Apple's recent tightening (and subsequent re-loosening) of the compiler's default conformance settings.
You can probably work around this issue by adding --fno-common to your C compiler flags. GCC and Clang both accept that for disabling the symbol merging described above, and, at least on my machine, they both implement that by emitting the symbol as type B when it is defined without an explicit initializer, just as if it had been explicitly initialized to a null pointer. Note well, however, that this will break any code that is presently relying on that merging behavior.

ceil() only works for rvalues [duplicate]

This question already has answers here:
Why does the order of '-l' option in gcc matter? [duplicate]
(3 answers)
Closed 3 years ago.
I'm trying to compile this program in ubuntu 18.04, 64 bits:
#include <math.h>
#include <stdio.h>
int main() {
double x = 1.9;
float y = 1.8;
int x2 = ceil(x);
int y2 = ceil(y);
printf("%d, %d\n", x2, y2);
return 0;
}
The gcc command I'm using is:
gcc -std=c99 -lm main.c -o main
And the error I'm obtaining is:
/tmp/ccWL94J9.o: In function `main':
main.c:(.text+0x30): undefined reference to `ceil'
main.c:(.text+0x41): undefined reference to `ceil'
collect2: error: ld returned 1 exit status
Although, if I replace ceil(x) by ceil(1.2) for example, and something similar for ceil(y), I can build and execute the program.
In addition, I have checked that I do have libm.so installed:
bash> find /usr/lib -name "*libm.so*"
/usr/lib/x86_64-linux-gnu/libm.so
What I'm missing?
With the following line, it compiles:
gcc -std=c99 main.c -o main -lm
(putting -lm after -o main)
See https://stackoverflow.com/a/11894098/4030665
With some expressions E, particularly constants, the compiler can evaluate ceil(E) while it is compiling, and it does so if this optimization is not disabled. Then the compiler generates code that uses the result and does not call ceil. When the compiler fails to evaluate ceil(E) during compilation, it generates code that calls ceil.
The switch -lm is an abbreviation for the standard math library. The linker processes input files in the order they appear on the command line. When the linker processes a library, it extracts from the library all object modules that contain a definition for a symbol that is currently needed (referenced but not defined) in the executable file (or other output) it is building.
GCC maintains the order of the various units on its command line. Given -lm main.c, it compiles main.c to product an object module, and then it passes -lm and the object module to the linker in that order. Since the math library is first, when the linker processes it, it has not yet seen any symbols that reference it, and therefore it does not take any modules from the library.
If GCC is instead given main.c -lm, the linker processes the math library after the object module for main. Then, when the linker is processing the math library, it will know that main references ceil, so it will extract from the math library the module that defines ceil.
Thus gcc -std=c99 -lm main.c -o main will work with source code that uses only constants with ceil but will not work with the source code in the example, but gcc -std=c99 main.c -lm -o main will work.

Why is clang removing an underscore from a function declared as 'extern "C"'?

I'm watching a video in an attempt to better understand object files. The presenter uses the following as an example of a program that produces a very simple object file:
extern "C" void _start() {
asm("mov $60, %eax\n"
"mov $24567837, %edi\n"
"syscall\n");
}
The program is compiled via
clang++ -c step0.cpp -O1 -o step0.o
and linked via
ld -static step0.o -o step0
I get this error message when trying to link:
Undefined symbols for architecture x86_64:
"start", referenced from:
-u command line option
(maybe you meant: __start)
ld: symbol(s) not found for inferred architecture x86_64
I don't pass the -u command line option, so I'm not sure why I'm getting that error message.
clang isn't removing an underscore, it's adding an underscore. Your program is actually exporting a __start symbol, but ld expects you to have a start symbol for your entry point, i.e. ld runs with -u start by default for your architecture.
You could disable this check in ld with -U start (which suppresses the error from the start symbol being undefined) or via -undefined suppress (which suppresses all undefined symbol errors). However, you will end up with an executable that does not have an entry point for your architecture, so the program won't actually work.
Instead of suppressing the error, I suggest controlling the symbol that clang chooses directly. You can tell clang what symbol to generate by using a standalone asm declaration:
void _start() asm ("start");
Make sure this standalone declaration is separate from the function definition.
You can read more about controlling the symbols generated by gcc here: https://stackoverflow.com/a/1035937/12928775
Also, as was pointed out in a comment to a similar answer, you will most likely want to use __attribute__((naked)) on the function definition to prevent clang from generating a stack frame on entry. See: https://stackoverflow.com/a/60311490/12928775

Unable to link to libgfortran.a [duplicate]

This question already has answers here:
Why does the order in which libraries are linked sometimes cause errors in GCC?
(9 answers)
Closed 8 years ago.
I have gfortran installed on my system and the file libgfortran.a can be found at /usr/lib/gcc/x86_64-linux-gnu/4.6/. Using nm I made sure that the function _gfortran_compare_string is defined in there:
$ nm /usr/lib/gcc/x86_64-linux-gnu/4.6/libgfortran.a | grep _gfortran_compare_string
Returns
0000000000000000 T _gfortran_compare_string
0000000000000000 T _gfortran_compare_string_char4
But, the linker of my CUDA-C program throws errors:
/usr/local/cuda-6.0/bin/nvcc --cudart static -L/usr/lib/gcc/x86_64-linux-gnu/4.6 -L/home/chung/lapack-3.5.0 -link -o "pQP" ./src/pQP.o -lgfortran -llapacke -llapack -lcublas -lblas -lcurand
nvcc warning : The 'compute_10' and 'sm_10' architectures are deprecated, and may be removed in a future release.
/home/chung/lapack-3.5.0/liblapack.a(ilaenv.o): In function `ilaenv_':
ilaenv.f:(.text+0x81): undefined reference to `_gfortran_compare_string'
and later on another error, again related to libgfortran:
/home/chung/lapack-3.5.0/liblapack.a(xerbla.o): In function `xerbla_':
xerbla.f:(.text+0x49): undefined reference to `_gfortran_st_write'
xerbla.f:(.text+0x54): undefined reference to `_gfortran_string_len_trim'
xerbla.f:(.text+0x66): undefined reference to `_gfortran_transfer_character_write'
xerbla.f:(.text+0x76): undefined reference to `_gfortran_transfer_integer_write'
xerbla.f:(.text+0x7e): undefined reference to `_gfortran_st_write_done'
xerbla.f:(.text+0x87): undefined reference to `_gfortran_stop_string'
But, again using nm, I found that _gfortran_st_write, etc are defined in libgfortran.a.
Links: Complete log and source code.
Note: Lapack makes use of libgfortran. I recently installed lapack and ran all the tests and they all passed.
You need to change the order in which you specify static libraries to the linker. If you do something like this:
nvcc --cudart static -L/usr/lib/gcc/x86_64-linux-gnu/4.6 \
-L/home/chung/lapack-3.5.0 -link -o "pQP" ./src/pQP.o \
-llapacke -llapack -lcublas -lblas -lcurand -lgfortran
You should find it will work.
The underlying reason (and this is a trait of the gcc/gnu toolchain and not anything to do with nvcc) is that linking dependency lists for static libraries are parsed from left to right by the gnu linker. If you specify a static library before any library which depends on it, it will be skipped because it has no dependencies in the link list at the point in processing when it is first encountered.

Linker command failed with exit code 1 In Xcode

I'm writing C program in Xcode, and I got this problem:
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Any idea to solve it?
Note: I've just update my OS to Mountain Lion yesterday.
A common error is to define a symbol in a header when you wanted to declare it:
When you for instance declare a global variable and forget the extern or you define an inline function and forget the inline. In these cases the compiler emits the symbol in each compile unit that includes this header and you end up with multiple definitions of a symbol.
Anyway you should just look for the symbol in question.

Resources