gcc weak symbol with __attribute((weak)) handling with shared library - c

write file, one with the weak file and another with the normal symbol. Below are the contents of the files: where weak_ex() is the definition declared with weak symbol.
bgl-ads-2997:~/weak_symbol > cat m.c
#include <stdio.h>
#include "weak.h"
void main (){
printf ("weak = %d\n", weak_ex());
}
bgl-ads-2997:~/weak_symbol > cat weak.c
int __attribute__((weak)) weak_ex()
{
return 1;
}
bgl-ads-2997:~/weak_symbol > cat strong.c
int weak_ex()
{
return 10;
}
bgl-ads-2997:~/weak_symbol > cat weak.h
int weak_ex();
So with the above definitions weak symbol will return 1, and strong will return 10.
Then I compiled them as below:
gcc -g -c -o m.o m.c
gcc -g -c -o weak.o weak.c
gcc -g -c -o strong.o strong.c
gcc -shared -fpic -o libstrong.so strong.o
gcc -shared -fpic -o libweak.so weak.o
then linked them and executed as below:
using only the object file:
bgl-ads-2997:~/weak_symbol > gcc m.o weak.o strong.o -L`pwd` -Wl,-R`pwd` -o shared
bgl-ads-2997:~/weak_symbol > ./shared
weak = 10
In this case weak symbol is masked, and strong signal is taken as expected.
link with weak symbol in object file and strong symbol in shared object.
bgl-ads-2997:~/weak_symbol > gcc -lstrong m.o weak.o -L`pwd` -Wl,-R`pwd` -o shared
bgl-ads-2997:~/weak_symbol > ./shared
weak = 1
In this case weak symbol is not getting replaced.
Seems symbol are resolved in compilation time and not as part of execution, not not considering the shared Expecting this also to print 10 but not so, So is this the expected behaviour? is there a way to get the weak symbol replaced from the shared library?

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.

Symbol not found when static linking on MacOSX

I am trying to create a static library and link it on MacOS X (several versions):
File foo.c:
char foo[111];
File bar.c:
#include <string.h>
extern char foo[];
int bar(char *src) {
strcpy(foo, src);
return strlen(foo);
}
Create a library:
$ cc -c foo.c bar.c
$ ar r libfoobar.a foo.o bar.o
ar: creating archive libfoobar.a
$ ranlib libfoobar.a
$ nm libfoobar.a
libfoobar.a(foo.o):
000000000000006f C _foo
libfoobar.a(bar.o):
U ___strcpy_chk
0000000000000000 T _bar
U _foo
U _strlen
Create a small test program:
File main.c:
#include <stdio.h>
int bar(char *);
int main(void) {
printf("foobarbar = %i\n", bar("123"));
return 0;
}
Compile and link:
$ cc -c main.c
$ cc -o m main.o -L. -lfoobar
Undefined symbols for architecture x86_64:
"_foo", referenced from:
_bar in libfoobar.a(bar.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Why is the symbol not found? It is defined in foo.c? Shouldn't at least ranlib create an index in the library that allows a random order of the files there?
The same code works well under Linux (gcc), and also when the symbol in foo.c is not a char array, but an int.
There is a similar question: Object files not properly added to archive on mac which has this answer:
Option 1:
ar -rs my_archive.a foo.o bar.o other_object_files.o
ranlib -c my_archive.a
Option 2:
libtool -c -static -o my_archive.a foo.o bar.o other_object_files.o
It is -c flag that makes a difference for both options on ranlib and libtool respectively:
-c
Include common symbols as definitions with respect to the table
of contents. This is seldom the intended behavior for linking
from a library, as it forces the linking of a library member
just because it uses an uninitialized global that is undefined
at that point in the linking. This option is included only
because this was the original behavior of ranlib. This option
is not the default.

compiling a shared object written in haskell and template haskell and linking it with main in c

i am trying to compile several literate haskell (.lhs) files to a shared object (.so), and then to link it with a main written in c. The issue here, though, is that 2 of the files used to create the .so are template haskell. i followed the rules for compiling a .so with template haskell, which means that i did the following steps:
1. I compiled each .lhs files ,statically
2. Then i compiled them all second time dynamically.
3. I created the shared object from the object files i got from steps 1 & 2.
4. I compiled the main.c into main.o
5. I created an executable from steps 3 & 4.
there are 3 files from which the .so is created. Dep1.lhs, Dep2.lhs & Dep3.lhs, and a main written in c
when i compile makefile i get this message:
my_directory >> make all
rm -f *.o *.hi *.so *.dyn_hi *.dyn_o main
ghc -c Dep3.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep3.o
ghc -c Dep3.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep3.dyn_o -osuf dyn_o -hisuf dyn_hi
ghc -c Dep2.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep2.o
ghc -c Dep2.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep2.dyn_o -osuf dyn_o -hisuf dyn_hi
ghc -c Dep1.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep1.o
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
ghc -c Dep1.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep1.dyn_o -osuf dyn_o -hisuf dyn_hi
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
ghc -O2 -dynamic -shared -fPIC Dep1.dyn_o Dep2.dyn_o Dep3.dyn_o -o libShared.so -lHSrts-ghc7.6.3
gcc -O2 -I/usr/local/lib/ghc-7.6.3/include -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -c Main.c -o main.o
gcc -o main main.o -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -L. -lShared -Wl,-rpath,/usr/local/lib/ghc-7.6.3 -L/home/tal/a_prerequisites/new_haskell/ghc-7.6.3/libraries/base/dist-install/build/libHSbase-4.6.0.1-ghc7.6.3. -lHStemplate-haskell-2.8.0.0
/usr/bin/ld: dynamic variable `ghczmprim_GHCziTypes_True_closure' is zero size
/usr/bin/ld: dynamic variable `ghczmprim_GHCziTypes_ZMZN_closure' is zero size
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Syntax__1744.o)(.text+0x77): unresolvable R_X86_64_32 relocation against symbol `ghczmprim_GHCziTypes_True_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__228.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__137.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__227.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__124.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure
and an executable 'main' is created, but when i try to run it, the following occurs:
host113#/home/tal/Documents/mfbus >> main
main: error while loading shared libraries: libHSbase-4.6.0.1-ghc7.6.3.so: cannot open shared object file: No such file or directory
i tried to include in the last rule (main) the directory of ' libHSbase-4.6.0.1-ghc7.6.3.so' in the '-l' option so that it will load it.but it doesnt seem to work. May someone have an insight to the error?
the code for Dep1.lhs:
> {-# LANGUAGE TemplateHaskell #-} <br/>
> {-# LANGUAGE ForeignFunctionInterface #-} <br/>
> module Dep1 where
> import Foreign <br/>
> import Foreign.C.Types <br/>
> import Dep3 <br/>
> data MyData = MyData
> { foo :: String
> , bar :: Int
> }
> emptyShow ''MyData
> foreign export ccall some_func :: IO () <br/>
> foreign export ccall factorial :: Int -> Int
> some_func :: IO () <br/>
> some_func = print $ MyData { foo = "bar", bar = 5 }
> factorial :: Int -> Int <br/>
> factorial 0 = 1 <br/>
> factorial n = n *(factorial $ n - 1)
the code for Dep3.lhs (comes here because Dep1.lhs imports it):
> {-# LANGUAGE TemplateHaskell, FlexibleInstances #-}
> module Dep3 where
> import Language.Haskell.TH
> emptyShow :: Name -> Q [Dec] <br/>
> emptyShow name = [d|instance Show $(conT name) where show _ = "some meaningful sentence"|]
the code for Dep2.lhs:
> {-# LANGUAGE ForeignFunctionInterface #-}
> module Dep2 where <br/>
> import Foreign <br/>
> import Foreign.C.Types
> foreign export ccall power :: CInt -> CInt
> power :: CInt -> CInt
> power n = n*n
the code for Main.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "Tal2_stub.h"
#endif
#ifdef __GLASGOW_HASKELL__
extern void __stginit_power ( void );
#endif
// int power(int i){ return i*i; }
int fact(int i){
if (i == 0) return 1;
else return i * fact(i-1);
}
nt main(int argc, char *argv[]){
hs_init(&argc, &argv);
#ifdef __GLASGOW_HASKELL__
hs_add_root(__stginit_power);
#endif
printf("what is 5!?\n");
char buf[2048];
scanf("%s",buf);
int x = atoi(buf);
if(x == fact(5)){
printf("You're right!\n");
} else {
printf("You're wrong!\n");
}
printf("what is the power of 2?\n");
scanf("%s",buf);
x = atoi(buf);
if(x == power(2)){
printf("You're right!\n");
} else {
printf("You're wrong!\n");
}
hs_exit();
return 0;
}
my makefile code:
all : clean main
main : shared main.o
gcc -o main main.o -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -L. -lShared -Wl,-rpath,/usr/local/lib/ghc-7.6.3 -L/home/tal/a_prerequisites/new_haskell/ghc-7.6.3/libraries/base/dist-install/build/libHSbase-4.6.0.1-ghc7.6.3. -lHStemplate-haskell-2.8.0.0
main.o :
gcc -O2 -I/usr/local/lib/ghc-7.6.3/include -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -c Main.c -o main.o
shared : dep3second dep2second dep1second
ghc -O2 -dynamic -shared -fPIC Dep1.dyn_o Dep2.dyn_o Dep3.dyn_o -o libShared.so -lHSrts-ghc7.6.3
dep1second : dep1first
ghc -c Dep1.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep1.dyn_o -osuf dyn_o -hisuf dyn_hi
dep2second : dep2first
ghc -c Dep2.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep2.dyn_o -osuf dyn_o -hisuf dyn_hi
dep3second: dep3first
ghc -c Dep3.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep3.dyn_o -osuf dyn_o -hisuf dyn_hi
dep1first :
ghc -c Dep1.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep1.o
dep2first :
ghc -c Dep2.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep2.o
dep3first :
ghc -c Dep3.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep3.o
.PHONY : clean
clean :
-rm -f *.o *.hi *.so *.dyn_hi *.dyn_o main
Note: my knowledge of Haskell is limited and this answer doesn't assume anything about Haskell at all.
The error says:
<executable>: error while loading shared libraries: <shared lib>.so: cannot open shared object file: No such file or directory
which means that when trying to execute your program, the shared object wasn't found. Note the difference between finding the shared object during link and during execution.
Roughly speaking, when you use -L and -l to link to a shared object, the link is not completely done, but remembered to be done at runtime. When you execute the executable that depends on that shared object, somebody else (not the compiler) must be able to find the shared object and do the linking: again roughly speaking, Linux.
So you need to have Linux find your shared object too. There are several ways to do so, some of which are deprecated. The way I would recommend is to write a file with the path of the shared object and put it under /etc/ld.so.conf.d/. Then, execute ldconfig as root (e.g. with sudo) and Linux should be able to find the shared object.
I haven't tested this, but if you just edit /etc/ld.so.conf and add a line containing ., the Linux should be able to find the shared object if it's located in the same directory the executable is being executed from. This could be ok for development, but I would not recommend it if the shared object is to be installed.

How to make gcc link strong symbol in static library to overwrite weak symbol?

My problem can be summarised in the following:
bar.c:
#include <stdio.h>
void bar() {
printf("bar\n");
}
main.c:
#include <stdio.h>
void __attribute__((weak)) bar() {
printf("foo\n");
}
int main() {
bar();
return 0;
}
Makefile:
all:
gcc -c bar.c
ar -rc libbar.a bar.o
gcc main.c -L. -lbar
Output:
$ ./a.out
foo
So the weak symbol bar in main.c is not overwritten by the strong symbol in bar.c due to bar.c being linked to main.c in a static library libbar.a.
How can I tell gcc to make the strong symbol in libbar.a to overwritten the weak symbol in main.c?
I am puzzled by the answer given by max.haredoom (and that it was accepted). The answer deals with shared libraries and dynamic linking, whereas the question was clearly about the behavior of static linking using static libraries. I believe this is misleading.
When linking static libraries, ld does not care about weak/strong symbols by default: it simply resolves an undefined symbol to a first-encountered symbol (so the order of static libraries in the command line is important).
However, this default behavior can be changed using the --whole-archive option. If you rewrite your last step in Makefile as follows:
gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive
Then you will see:
$ ./a.out
bar
In a nutshell, --whole-archive forces the linker to scan through all its symbols (including those already resolved). If there is a strong symbol that was already resolved by a weak symbol (as in our case), the strong symbol will overrule the weak one.
Also see a great post on static libraries and their linking process "Library order in static linking" by Eli Bendersky and this SO question.
Generally speaking: if you don't put a weak implementation into your main, the linker will resolve it at last at runtime. But if you implement it in main.c, you will only be able to override it with a strong bound (bar.c) when linking this static.
Please read https://bottomupcs.com/ch09s05.html - it contains a lot of interesting stuff on this topic.
I've made a test myself:
bar.c
#include <stdio.h>
void bar()
{
puts("bar.c: i'm the strong bar()");
}
baz.c
#include <stdio.h>
void __attribute__((weak)) bar()
{
puts("baz.c: i'm the weak bar()");
}
main.c
#include <stdio.h>
#ifdef V2
void __attribute__((weak)) bar()
{
puts("main: i'm the build in weak bar()");
}
#else
void __attribute__((weak)) bar();
#endif
int main()
{
bar();
return 0;
}
My Makefile:
all:
gcc -c -o bar.o bar.c
gcc -shared -fPIC -o libbar.so bar.o
gcc -c -o baz.o baz.c
gcc -shared -fPIC -o libbaz.so baz.o
gcc -o main1 main.c -L. -lbar -lbaz
gcc -o main2 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. ./main1 # => bar.c
LD_LIBRARY_PATH=. ./main2 # => baz.c
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main1 # => baz.c (!!)
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main2 # => baz.c
gcc -o main3 main.c bar.o baz.o
gcc -o main4 main.c baz.o bar.o
./main3 # => bar.c
./main4 # => bar.c
gcc -DV2 -o main5 main.c -L. -lbar -lbaz
gcc -DV2 -o main6 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. ./main5 # => main's implementation
LD_LIBRARY_PATH=. ./main6 # => main's implementation
gcc -DV2 -o main7 main.c -L. -lbar -lbaz
gcc -DV2 -o main8 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main7 # => main's implementation
LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main8 # => main's implementation
gcc -DV2 -o main9 main.c -L. -lbar -lbaz
gcc -DV2 -o main10 main.c -L. -lbaz -lbar
LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main9 # => main's implementation
LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main10 # => main's implementation
gcc -c bar.c
gcc -c baz.c
gcc -o main11 main.c bar.o baz.o
gcc -o main12 main.c baz.o bar.o
./main11 # => bar.c
./main12 # => bar.c
gcc -o main13 -DV2 main.c bar.o baz.o
gcc -o main14 -DV2 main.c baz.o bar.o
./main13 # => bar.c
./main14 # => bar.c
Take a look at main1 && main2... if you don't put any weak implementation into main.c but keep the weak one in a library and the strong one in another lib., you'll be able to override the weak one if the strong lib defines a strong implementation of bar().
You should separate the weak implementation into another library.
Only declare it in main.

Resources