Hierarchical Linking in C - c

I want to link three files but in hierarchical way.
// a.c
int fun1(){...}
int fun2(){...}
// b.c
extern int parameter;
int fun3(){...//using parameter here}
// main.c
int parameter = 1;
int main(){...// use fun1 fun2 fun3}
So, I first compile three files separately into object file a.o, b.o and main.o. And then I want to combine a.o and b.o into another object file tools.o. And eventually use tools.o and main.o to generate executable file.
But, when I try to combine a.o and b.o like ld -o tools.o a.o b.o, the linker says undefined reference to 'parameter'. How could I link those object files into an intermediate object file?

You want the -r option to produce a relocatable object file (think 'reusable'):
ld -o tools.o -r a.o b.o
Working code
abmain.h
extern void fun1(void);
extern void fun2(void);
extern void fun3(void);
extern int parameter;
a.c
#include <stdio.h>
#include "abmain.h"
void fun1(void){printf("%s\n", __func__);}
void fun2(void){printf("%s\n", __func__);}
b.c
#include <stdio.h>
#include "abmain.h"
void fun3(void){printf("%s (%d)\n", __func__, ++parameter);}
main.c
#include <stdio.h>
#include "abmain.h"
int parameter = 1;
int main(void){fun1();fun3();fun2();fun3();return 0;}
Compilation and execution
$ gcc -Wall -Wextra -c a.c
$ gcc -Wall -Wextra -c b.c
$ gcc -Wall -Wextra -c main.c
$ ld -r -o tools.o a.o b.o
$ gcc -o abmain main.o tools.o
$ ./abmain
fun1
fun3 (2)
fun2
fun3 (3)
$
Proved on Mac OS X 10.11.6 with GCC 6.1.0 (and the XCode 7.3.0 loader, etc). However, the -r option has been in the ld command on mainstream Unix since at least the 7th Edition Unix (circa 1978), so it is likely to be available with most Unix-based compilation systems, even if it is one of the more widely unused options.

Related

Why is clang reporting an undefined symbol for an extern variable referenced in a static library

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.

Different behavior between clang and gcc-10 when linking to static library containing global variables

I have a statically linked library, containing a global variable barvar. I can compile the library with no problems with either gcc-10 or clang (this is on macOS Catalina). Interestingly, the behavior differs between the two when I try to link it into a program that uses the library. Here's the code:
In globvars.h, int barvar is declared:
#ifndef H_GLOBVARS_H
#define H_GLOBVARS_H
extern int barvar;
#endif
In globvars.c, int barvar is defined:
#include "globvars.h"
int barvar;
In foo.c, the function foo sets and prints barvar:
#include <stdio.h>
#include "globvars.h"
void foo()
{
barvar = 10;
printf("barvar is: %d\n", barvar);
return;
}
Here's test.c, the program that uses the library:
void foo();
int main(int argc, char **argv)
{
foo();
return 0;
}
When I compile and link with gcc-10, no problems:
gcc-10 -c foo.c -o foo.o
gcc-10 -c globvars.c -o globvars.o
gcc-10 -c test.c -o test.o
gcc-ar-10 rcs liblinktest.a foo.o globvars.o
gcc -o testlinkrun test2.o -L. -llinktest
When I compile and link with clang, I get an undefined symbol error at the last step:
cc -c foo.c -o foo.o
cc -c globvars.c -o globvars.o
cc -c test.c -o test.o
ar rcs liblinktest.a foo.o globvars.o
cc -o testlinkrun test2.o -L. -llinktest
with error:
Undefined symbols for architecture x86_64:
"_barvar", referenced from:
_foo in liblinktest.a(foo.o)
Any ideas? Interestingly, it appears the only step that has to be done with gcc-10 is compiling globvars.c. I can use clang and the clang linker for all other steps, and everything is fine. Is it possible that clang is optimizing away all the variables in globvars.c? How can I prevent this?
As #EricPostpischil observed in this comment, the issue is that clang defaults to treating barvar as a common symbol. Either changing int barvar; to int barvar = 0;, or compiling with -fno-common, fix the issue.
Beginning with gcc-10, gcc's default behavior is -fno-common instead of -fcommon.

Linking two object files together causes segmentation fault 11

I am experimenting with externs and various methods of linking to better understand the linking process.
I have three files:
foo.c:
#include "foo.h"
int a = 4;
test.c:
#include <stdio.h>
#include "foo.h"
int main(int, char**);
int mymain();
int mymain() {
main(0, 0);
printf("test\r\n");
return 0;
}
int main(int argc, char** argv) {
printf("extern a has %d\r\n", a);
return 0;
}
foo.h:
extern int a; // defined in foo.c
If I build each file together and link at compile time using gcc like this:
gcc *.c -o final.bin
I can execute final.bin as:
./final.bin
and get expected output
extern a has 4
However, if I compile (but don't link) test.c and foo.c separately, then try and link the object files together at runtime to produce a binary, I get a segmentation fault 11 (which from what I can gather is some generic memory corruption bug like a normal segfault(?)
Here is my makefile I'm using to compile and link separately. Note I am specifying my own entry point and linking against libc to get printf()...
all: test.o foo.o
#echo "Making all..."
ld test.o foo.o -o together.bin -lc -e _mymain
test.o: test.c
#echo "Making test..."
gcc -c test.c -o test.o
foo.o: foo.c
#echo "Making foo..."
gcc -c foo.c -o foo.o
Output when running 'together.bin':
./together.bin
extern a has 4
test
Segmentation fault: 11
Perhaps my function signature for 'mymain' is wrong? My guess is that something is wrong with my 'myentry' usage.
Also, if anyone has any recommendations on good books for how linkers and loaders work, I am certainly in the market for one. I've heard mixed things about 'Linkers and Loaders', so I'm waiting on more opinions before I invest the time in that book in particular.
Thanks for any help on this... My understanding of linkers is sub-par to say the least.
Unless if you have a good reason to do so, just use gcc to link:
$ gcc test.o foo.o "-Wl,-e,_mymain" -o ./final.bin; ./final.bin
extern a has 4
test
gcc calls ld---though, with a few more arguments than you are providing in your example. If you want to know exactly how gcc invokes ld, use the -v option. Example:
$ gcc -v test.o foo.o "-Wl,-e,_mymain" -o ./final.bin
Apple LLVM version 8.0.0 (clang-800.0.38)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -dynamic -arch x86_64 -macosx_version_min 10.12.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -o ./final.bin test.o foo.o -e _mymain -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0/lib/darwin/libclang_rt.osx.a

Using extern in C doesn't work as expected

I have created two files:
tunables.h
#ifndef TUNABLES_H
#define TUNABLES_H
void tunables_load_conservative();
void tunables_load_aggressive();
extern int timer_x;
#endif /*TUNABLES_H */
and tunables.c
#include "tunables.h"
int timer_x;
void tunables_load_conservative(){
timer_x = 3;
}
void tunables_load_aggressive(){
timer_x = 1;
}
All the other files of my project includes "tunables.h". When I load the project both A.c and B.c calls tunables_load_conservative but if, after a while, I call in file A.c tunables_load_aggressive() in file B.c the timer_x remains 3. Why?
This is my Makefile:
INCLUDE=`pwd`/include
GCCFLAGS= -ansi -O3 -pedantic -Wall -Wunused -I${INCLUDE} -DDEBUG
GCCOTHERFLAGS= -ggdb -pg
all: A B
A: A.o tunables.o
gcc -o A ${GCCFLAGS} ${GCCOTHERFLAGS} tunables.o
B: B.o tunables.o
gcc -o LBfixed ${GCCFLAGS} ${GCCOTHERFLAGS} tunables.o
A.o: A.c
gcc -c ${GCCFLAGS} ${GCCOTHERFLAGS} A.c
B.o: B.c
gcc -c ${GCCFLAGS} ${GCCOTHERFLAGS} B.c
tunables.o: tunables.c
gcc -c ${GCCFLAGS} ${GCCOTHERFLAGS} tunables.c
clean:
rm -rf *.o A B
It looks like you've got two separate processes, A and B. The extern declaration does not set up shared memory across processes, but instead allows different compilation units within the same process to access the same variable.
To share a variable across processes, you will need to use system-dependent IPC methods.

Why doesn't __attribute__((constructor)) work in a static library?

In the following example, the program should print "foo called\n":
// foo.c
#include <stdio.h>
__attribute__((constructor)) void foo()
{
printf("foo called\n");
}
// main.c
int main()
{
return 0;
}
If the program is compiled like this, it works:
gcc -o test main.c foo.c
However, if foo.c is compiled into a static library, the program prints nothing.
gcc -c main.c
gcc -c foo.c
as rcs foo.a foo.o
gcc -o test foo.a main.o
Why does this happen?
The linker does not include the code in foo.a in the final program because nothing in main.o references it. If main.c is rewritten as follows, the program will work:
//main.c
void foo();
int main()
{
void (*f)() = foo;
return 0;
}
Also, when compiling with a static library, the order of the arguments to gcc (or the linker) is significant: the library must come after the objects that reference it.
gcc -o test main.o foo.a
As it was stated, unreferenced symbols from archive does not make it to the output binary, because linker discards them by default.
To override this behaviour when linking with static library, --whole-archive/--no-whole-archive options for the linker may be used, like this:
gcc -c main.c
gcc -c foo.c
ar rcs foo.a foo.o
gcc -o test -Wl,--whole-archive foo.a -Wl,--no-whole-archive main.o
This may lead to bloated binary, because all symbols from foo.a will be included by the linker to the output, but sometimes it is justified.

Resources