__attribute__((weakref)) not work for external function - c

Recently I 'm studying the linking process and when it comes to weak symbol, my textbook give a code below to demonstrate how to use __attribute__((weakref)) to declare a weak reference to external function:
//pthread.c
#include <stdio.h>
#include <pthread.h>
int pthread_create(
pthread_t*,
const pthread_attr_t*,
void* (*)(void*),
void*
)__attribute__((weak));
int main()
{
if(pthread_create){
printf("This is multi-thread version\n");
}else{
printf("This is single-thread version!\n");
}
}
Then the author use different ways of linking ,which gives the result:
$ gcc pthread.c -o pt
$ ./pt
This is single-thread version!
$ gcc pthread.c -lpthread -o pt
$ ./pt
This is multi-thread version!
I reproduced the same procedure on my machine, but both results give This is single-thread version!
I tried to find out what 's going on here, but I quickly stuck into two problem :
I think it might be that the author mistakenly write __attribute((weak))__instead of __attribute__((weakref))__ here. Because if one module declare a weak symbol, the linker would not find the definition of the symbol in the library during the static linking process. Considering that GCC use dynamic linking by default, I use static linking to verify that :
$ gcc -c pthread.c
$ nm pthread.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
w pthread_create
U puts
$ gcc -static pthread.c -lpthread -o pt
$ nm pt | grep 'pthread_create'
$
the symbol 'pthread_create' do not appear in the symbol table.
Now I use __attribute((weakref))__ to reproduce the procedures.
//pthread.c modified
#include <stdio.h>
#include <pthread.h>
static int pthread_create_dup(
pthread_t*,
const pthread_attr_t*,
void* (*)(void*),
void*
)__attribute__((weakref,alias("pthread_create")));
int main()
{
if(pthread_create_dup){
printf("This is multi-thread version!\n");
}else{
printf("This is single-thread version!\n");
}
}
still, after compiling and linking, the result is This is single-thread version!
$ gcc -static pthread.c -lpthread -o pt
$ nm pt | grep 'pthread_create'
$
I make another sample to simulate above:
test.c:
extern int foo(int a,int b);
static int foo_dup(int a,int b) __attribute__((weakref,alias("foo")));
int main(){
if(foo_dup){
printf("foo is linked\n");
}else{
printf("foo isn't linked\n");
}
}
ref_foo.c
int foo(int a ,int b){
return a+b;
}
Then compile those two:
$ gcc -c ref_foo.c test.c
$ ar rcs libfoo.a ref_foo.o
$ gcc -static test.o libfoo.a -o test_withlib
$ ./test_withlib
foo isn't linked
$ gcc -static test.o ref_foo.o -o test_withoutlib
$ ./test_withoutlib
foo is linked
So why would this happen? It's apparently I cannot extract pthread_create.o from libpthread.o and simply gcc pthread.c pthread_create.o -o pt.How to correctly implement pthread.c so it will print This is multi-thread version! when linking to the libpthread ?

Related

Linux, C - Is it possible to link against 2 dynamic librraries with equal functions names

I am forced to link two version of the same third party dynamic library (Linux .so, C language) into the same executable to support old and new functionality in the same process. Having two executables or remote services are undesirable.
I made the assumption that this must be a doable task. I tried to experiment with the naive approach of creating 2 proxy dynamic libraries each linked against one of the real libraries and have function renamed.
Unfortunately, this attempt failed – both new functions call the same target function.
I still want to believe that the problem is in the lack of my knowledge as there are plenty of compiler and linker ( gcc and ld) options.
I will appreciate any help. I also look forward to using dlopen/dlsym, but first want to check if the original approach can work.
Here is the sample code
/* ./old/b.c */
#include <stdio.h>
int b (int i)
{
printf("module OLD %d\n",i);
return 0;
}
/* ./old/Makefile */
libold.so: b.c
gcc -c -g b.c
gcc -shared b.o -o $#
/* ./new/b.c */
#include <stdio.h>
int b (int i)
{
printf("module new %d\n",i);
return 0;
}
/* ./new/Makefile */
libnew.so: b.c
gcc -c -g b.c
gcc -shared b.o -o $#
/* ./a1.c */
#include <stdio.h>
int b(int);
void call_new(void)
{
printf("will call new 1\n");
b(1);
printf("called new 1\n");
}
/* ./a2.c */
#include <stdio.h>
int b(int);
void call_old(void)
{
printf("will call old 2\n");
b(2);
printf("called old 2\n");
}
/* ./main.c */
#include <stdio.h>
int call_new(void);
int call_old(void);
int main()
{
call_new();
call_old();
return 0;
}
/* ./Makefile */
.PHONY: DEPSNEW DEPSOLD clean
main: liba1.so liba2.so main.c
gcc -c main.c
gcc -o main main.o -rdynamic -Wl,-rpath=new -Wl,-rpath=old -L . -la1 -la2
DEPSNEW:
make -C new
DEPSOLD:
make -C old
liba1.so: DEPSNEW a1.c
gcc -c -fpic a1.c
gcc -shared a1.o -L new -lnew -o liba1.so
liba2.so: DEPSOLD a2.c
gcc -c -fpic a2.c
gcc -shared a2.o -L old -lold -o liba2.so
clean:
find -name "*.so" -o -name "*.o" -o -name main | xargs -r rm
/* ./run.sh */
#/bin/sh
LD_LIBRARY_PATH=new:old:. main
The result is not that I want - function from "new" library is called twice
will call new 1
module new 1
called new 1
will call old 2
module new 2
called old 2
In this case, you can not control the automatic loading of the dynamic library in order to assure which library will be loaded for the depending libraries. What you can do, is to use one of the libraries (the new one) for the dynamic linker and to link the second library manually as follows:
Add function to dynamically load and link the function from the library.
a2.c
#include <stdio.h>
#include <dlfcn.h>
static int (*old_b)(int);
void init_old(void) {
void* lib=dlopen("./old/libold.so", RTLD_LOCAL | RTLD_LAZY);
old_b=dlsym(lib,"b");
}
void call_old(void)
{
printf("will call old 2\n");
old_b(2);
printf("called old 2\n");
}
call the initialization function
main.c
#include <stdio.h>
void init_old(void);
int call_new(void);
int call_old(void);
int main()
{
init_old();
call_new();
call_old();
return 0;
}
Modify the linker options to add the dynamic loading library -ldl
liba2.so: DEPSOLD a2.c
gcc -c -fpic a2.c
gcc -shared a2.o -L old -lold -ldl -o liba2.so
After this modification
~$ ./run.sh
will call new 1
module new 1
called new 1
will call old 2
module OLD 2
called old 2

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 shared library with libm

I have a program which is linked (dynamically) with libm.
There are also several plugins for this program.
Plugins are loaded explicitely with dlopen().
Some of these plugins use round() from libm.
On one system (Linux Mint 19.1 gcc 7.5.0) the program
does not work because of unresolved round.
Here is simple example:
Library (lib.c)
#include <stdio.h>
#include <math.h>
void func(double a, double b)
{
double c;
c = round(a + b);
printf("c = %lf\n", c);
}
Main program (main.c)
#include <stdio.h>
#include <dlfcn.h>
void *dll;
void (*f)(double, double);
double a = 1.234, b = 4.321;
int main(void)
{
dll = dlopen("./lib.so", RTLD_LAZY);
f = dlsym(dll, "func");
f(a,b);
return 0;
}
Building (Makefile)
all:
gcc -Wall -Os -shared -fPIC lib.c -o lib.so
gcc -Wall -Os -rdynamic -fPIC main.c -o main -ldl -lm
Run on Debian 8, gcc 4.9.2
./main
c = 6.000000
Run on Linux Mint 19.1, gcc 7.5.0
./main
./main: symbol lookup error: ./lib.so: undefined symbol: round
Now, add -lm for dll compilation
gcc -Wall -Os -shared -fPIC lib.c -o lib.so -lm
./main
c = 6.000000
So, the question is - why on this particular system one must use -lm not only for main program but for plugin also?
Just like an executable program, shared libraries are linked entities (unlike static libraries which are archives of object files).
Since shared libraries are linked like executables, you also need to link with the libraries that your library depends on:
gcc -Wall -Os -shared -fPIC lib.c -o lib.so -lm

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

Hierarchical Linking in 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.

Resources