Today I met a really interesting problem with my NixOS distro. I just wanted to create a statically compiled OCaml progam and couldn`t do that. Then I tried to do that with an ANSI C canonical toy "hello world!" application:
$> cat mytest.c
#include <stdio.h>
int
main ()
{
puts ("hello world!") ;
}
My distro:
$> uname -a
Linux cat 4.19.36 #1-NixOS SMP Sat Apr 20 07:16:05 UTC 2019 x86_64 GNU/Linux
Compiler:
$> gcc -v
Using built-in specs.
COLLECT_GCC=/nix/store/myq0x6mjbdzxr9fckqn6kgy89kz19nkp-gfortran-7.4.0/bin/gcc
COLLECT_LTO_WRAPPER=/nix/store/myq0x6mjbdzxr9fckqn6kgy89kz19nkp-gfortran-7.4.0/libexec/gcc/x86_64-unknown-linux-gnu/7.4.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with:
Thread model: posix
gcc version 7.4.0 (GCC)
Cannot produce static exec :
$> gcc -static mytest.c -o hello
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
Any ideas?
Usually a "dynamically linked" program hello is generated by gccwithout a problem.
No such issues on Lubuntu. I was advised to try a different distro and test that exec is running on NixOS. I did it - produced an exec with gcc on Lubuntu and started it on NixOS. Therefore I think that the problem is not with gcc but with NixOS.
How does NixOS treat this problem (i.e., generation of statically compiled exec files)?
And, of course, I'm also interested in the results concerning ocamlopt compiler rather then gcc but I think that the problem is common for all compilers (I tried Haskell ghc too by the way with the same results).
brgs
UPDATE: from a discussion on another thread:
1 #Ston17 You may have the .so but not the .a – norok2
2
3 yes - i have .so what the difference? can the presence of .a improve the situation? – Ston17
4
5 Yes. You typically need .a library to have the static linking work correctly – norok2
$> find /nix/store/ -name *libc.a.*
$>
can this be the reason?
UPDATE2: as concerning ocamlopt:
source file
$> cat mytest.ml
print_string "hello world!" ;;
print_newline () ;;
as you can see no special calls to anything. let try to make static exec:
$> ocamlopt -ccopt -static mytest.ml -o ocaml_test
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lm
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -ldl
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
so ld just cannot link to static version of libc. and i cannot find libc.a in the hole system
any suggetions?
here https://vaibhavsagar.com/blog/2018/01/03/static-haskell-nix/ is explained how you could obtain in NixOS statical versions of sys libraries
nix-shell config file:
let
pkgs = import <nixpkgs> {} ;
in pkgs.buildFHSUserEnv {
name = "fhs" ;
targetPkgs = pkgs: with pkgs; [
pkgs.glibc.static
pkgs.zlib.static
pkgs.libffi
pkgs.libtool
pkgs.musl
pkgs.ghc
pkgs.gcc
pkgs.ocaml
] ;
}
after that in nix-shell you start chroot FHS and copy needed sys libs into your folder and close chroot FHS
and after that compile your file staticaly
good with gcc:
$> gcc -static -L/home/.local/lib/ mytest.c -o ansiC_test
$> ldd ansiC_test
not a dynamic executable
not so good but maybe working with ocaml:
ocamlopt -ccopt -static -cclib -L/home/.local/lib mytest.ml -o ocaml_test
ocamlopt -ccopt -static -cclib -L/home/nomad/.local/lib mytest.ml -o ocaml_test
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: /nix/store/j1v6kkxq081q4m4fw7gazaf6rb3vy87p-ocaml-4.06.1/lib/ocaml/libasmrun.a(unix.o): in function `caml_dlopen':
/build/ocaml-4.06.1/asmrun/unix.c:273: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
$> ldd ocaml_test
not a dynamic executable
dont work with ghc though :
ghc -static -optl-static -L/home/.local/lib/ mytest.hs -o haskell_test
[1 of 1] Compiling Main ( mytest.hs, mytest.o )
Linking haskell_test ...
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: /nix/store/wfgrz42bpcl1r635dasfk7r236hm83az-ghc-8.6.4/lib/ghc-8.6.4/rts/libHSrts.a(Linker.o): in function `internal_dlopen':
Linker.c:(.text.internal_dlopen+0x7): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/nix/store/0y7jmqnj48ikjh37n3dl9kqw9hnn68nq-binutils-2.31.1/bin/ld: cannot find -lffi
collect2: error: ld returned 1 exit status
`cc' failed in phase `Linker'. (Exit code: 1)
make: *** [makefile:5: haskell] Error 1
ok. ocaml works:
lubuntu#lubuntu:~/Documents$ ./ocaml_hello_nix
hello world!
lubuntu#lubuntu:~/Documents$ readelf -l ocaml_hello_nix
Elf file type is EXEC (Executable file)
Entry point 0x4017a0
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000005d8 0x00000000000005d8 R 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x0000000000107275 0x0000000000107275 R E 0x1000
LOAD 0x0000000000109000 0x0000000000509000 0x0000000000509000
0x00000000000db191 0x00000000000db191 R 0x1000
LOAD 0x00000000001e5140 0x00000000005e6140 0x00000000005e6140
0x0000000000008a18 0x000000000001dfe0 RW 0x1000
NOTE 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x0000000000000020 0x0000000000000020 R 0x4
NOTE 0x0000000000000258 0x0000000000400258 0x0000000000400258
0x0000000000000020 0x0000000000000020 R 0x8
TLS 0x00000000001e5140 0x00000000005e6140 0x00000000005e6140
0x0000000000000020 0x0000000000000060 R 0x8
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x00000000001e5140 0x00000000005e6140 0x00000000005e6140
0x0000000000002ec0 0x0000000000002ec0 R 0x1
Section to Segment mapping:
Segment Sections...
00 .note.ABI-tag .note.gnu.property .rela.plt
01 .init .plt .text __libc_freeres_fn __libc_thread_freeres_fn .fini
02 .rodata .eh_frame .gcc_except_table
03 .tdata .init_array .fini_array .data.rel.ro .got .got.plt .data __libc_subfreeres __libc_IO_vtables __libc_atexit __libc_thread_subfreeres .bss __libc_freeres_ptrs
04 .note.ABI-tag
05 .note.gnu.property
06 .tdata .tbss
07
08 .tdata .init_array .fini_array .data.rel.ro .got
haskell works. the reason was that binary NixOS packet was built without FFI support. i installed ghc from sources and all become fine:
lubuntu#lubuntu:~/Documents$ ./haskell_hello_nix
hello world!
lubuntu#lubuntu:~/Documents$ readelf -l haskell_hello_nix
Elf file type is EXEC (Executable file)
Entry point 0x405d40
There are 6 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000188ba4 0x0000000000188ba4 R E 0x1000
LOAD 0x0000000000188ec0 0x0000000000589ec0 0x0000000000589ec0
0x00000000000104c0 0x000000000001ac98 RW 0x1000
NOTE 0x0000000000000190 0x0000000000400190 0x0000000000400190
0x0000000000000020 0x0000000000000020 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
TLS 0x0000000000188ec0 0x0000000000589ec0 0x0000000000589ec0
0x0000000000000070 0x00000000000000b8 R 0x8
GNU_RELRO 0x0000000000188ec0 0x0000000000589ec0 0x0000000000589ec0
0x0000000000003140 0x0000000000003140 RW 0x20
Section to Segment mapping:
Segment Sections...
00 .note.ABI-tag .rela.plt .init .plt .text __libc_thread_freeres_fn .fini .rodata .gcc_except_table .eh_frame
01 .tdata .data.rel.ro.local .fini_array .init_array .data.rel.ro .preinit_array .got .got.plt .data .tm_clone_table __libc_IO_vtables __libc_atexit __libc_thread_subfreeres .bss __libc_freeres_ptrs
02 .note.ABI-tag
03
04 .tdata .tbss
05 .tdata .data.rel.ro.local .fini_array .init_array .data.rel.ro .preinit_array .got
good. tnx to all who was concerned. you save my a$$
Related
I'm compiling a c file in xv6 and I used their Makefile to compile. I used readelf and found out the ELF it outputs has just one program header.
ryan#ubuntu:~/Documents/xv6-OS-for-arm-v8-fixed/xv6-armv8/usr$ readelf -l _init --wide
Elf file type is EXEC (Executable file)
Entry point 0x0
There is 1 program header, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000078 0x0000000000000000 0x0000000000000000 0x0011a8 0x0011c0 RWE 0x8
Section to Segment mapping:
Segment Sections...
00 .text .rodata .data .bss
This program header has just 4 sections: .text .rodata .data .bss
I checked its CFLAGS and it's showed below:
CROSSCOMPILE := aarch64-none-elf-
# try linux eabi
# CROSSCOMPILE := arm-linux-gnueabi-
CC = $(CROSSCOMPILE)gcc
AS = $(CROSSCOMPILE)as
LD = $(CROSSCOMPILE)ld
OBJCOPY = $(CROSSCOMPILE)objcopy
OBJDUMP = $(CROSSCOMPILE)objdump
CFLAGS = -march=armv8-a -mtune=cortex-a57 -fno-pic -static -fno-builtin -fno-strict-aliasing -Wall -I. -g
LDFLAGS = -L.
ASFLAGS = -march=armv8-a
But I don't know which of these flags make it this way.
I'm build a ARM-based OS. How can I compile one of this ELF in ARM by myself?
Having the following assembly source:
# hello_asm.s
# as hello_asm.s -o hello_asm.o
# ld hello_asm.o -e _main -o hello_asm
.section __DATA,__data
str:
.asciz "Hello world!\n"
.section __TEXT,__text
.globl _main
_main:
movl $0x2000004, %eax # preparing system call 4
movl $1, %edi # STDOUT file descriptor is 1
movq str#GOTPCREL(%rip), %rsi # The value to print
movq $100, %rdx # the size of the value to print
syscall
#
# EXITING
#
movl $0, %ebx
movl $0x2000001, %eax # exit 0
syscall
by compiling and linking with the following instructions:
as sum.s -g -o sum.o
ld -arch x86_64 -e main -L /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/lib -lSystem sum.o -o sum
and by trying to debug it on LLDB, I get the following result:
❯❯❯❯ lldb sum.o ~/D/test
(lldb) target create "sum.o"
Current executable set to '/Users/mbertamini/Downloads/test/sum.o' (x86_64).
(lldb) list
(lldb) b 16
error: No selected frame to use to find the default file.
error: No file supplied and no default file available.
(lldb)
This is the dwarf:
❯❯❯❯ dwarfdump sum.o ~/D/t/summ
sum.o: file format Mach-O 64-bit x86-64
.debug_info contents:
0x00000000: Compile Unit: length = 0x00000094 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000098)
0x0000000b: DW_TAG_compile_unit
DW_AT_stmt_list (0x00000000)
DW_AT_low_pc (0x0000000000000000)
DW_AT_high_pc (0x0000000000000026)
DW_AT_name ("sum.s")
DW_AT_comp_dir ("<filepath>")
DW_AT_producer ("Apple clang version 12.0.0 (clang-1200.0.32.27)")
DW_AT_language (DW_LANG_Mips_Assembler)
0x0000007e: DW_TAG_label
DW_AT_name ("main")
DW_AT_decl_file ("<filepath-file>")
DW_AT_decl_line (10)
DW_AT_low_pc (0x0000000000000000)
DW_AT_prototyped (0x00)
0x00000095: DW_TAG_unspecified_parameters
0x00000096: NULL
0x00000097: NULL
❯❯❯❯ as -v ~/D/t/summ
Apple clang version 12.0.0 (clang-1200.0.32.27)
Target: x86_64-apple-darwin20.2.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/clang" -cc1as -triple x86_64-apple-macosx11.0.0 -filetype obj -main-file-name - -target-cpu penryn -fdebug-compilation-dir /Users/mbertamini/Downloads/test/summ -dwarf-debug-producer "Apple clang version 12.0.0 (clang-1200.0.32.27)" -dwarf-version=4 -mrelocation-model pic -o a.out -
what's the problem? How am I supposed to do?
The issue is that the source file for which the debugging info is mapped should be used (sum.s):
$ as sum.s -g -o sum.o
$ ld -arch x86_64 -e _main -macosx_version_min 10.13 -lSystem sum.o -o sum
$ lldb sum
(lldb) target create "sum"
Current executable set to 'sum' (x86_64).
(lldb) b sum.s:16
Breakpoint 1: where = sum`main + 26, address = 0x0000000100000fac
(lldb)
When assembling use the -O0 optimization along the -g Code Generation Option.(This is important only when compiling with clang; this doesn't apply with as)
↳ lldb: resolving breakpoints to locations
I have to compile c++ code with g++ 6.4.0 (Homebrew g++-6) to a static lib, which is then wrapped into a C static lib (Homebrew gcc-6) and linked to a clang++ (clang 8.1.0) app on macos sierra. So the picture is:
c++ (gcc) wrapped in c (gcc) linked to clang app.
As a testcase I use shared-lib.cpp:
#include <iostream>
using namespace std;
void foo()
{
cerr << "Hi from the shared lib" << endl;
}
together with shared-lib.h
extern void foo();
and wrapper-lib.c
#include "shared-lib.h"
int wrapper()
{
foo();
return 123;
}
along with wrapper-lib.h
#ifdef __cplusplus
extern "C"
{
#endif
extern int wrapper();
#ifdef __cplusplus
}
#endif
The main.cpp that uses all the libs looks like
#include <iostream>
#include <string>
#include "shared-lib.h"
#include "wrapper-lib.h"
using namespace std;
int main()
{
auto s = "Hello world from main";
cout << s << endl;
foo(); // from c++ lib
int result = wrapper(); // from c wrapper lib
cout << "wrapper returned " << result << endl;
return 0;
}
My test built script is
g++-6 --version
echo -----------------------
echo build shared-lib .o with g++
g++-6 -c -Wall -fpic -std=c++11 shared-lib.cpp
echo build a wrapper library in C with gcc
gcc-6 -c -Wall -fpic wrapper-lib.c
echo build static libshared-lib.a
ar rcs libshared-lib.a shared-lib.o
echo build static libwrapper-lib.a
ar rcs libwrapper-lib.a wrapper-lib.o
echo build main with clang
clang++ --version
echo ----------------------
clang++ -v -L/Users/worker -Wall -std=c++11 -stdlib=libstdc++ -lwrapper-lib -lshared-lib main.cpp -o main
echo start the app
./main
If I only call the gcc c++ function foo() then everything works fine.
If I call the C wrapper function wrapper(), then clang comes up with:
Undefined symbols for architecture x86_64:
"_foo", referenced from:
_wrapper in libwrapper-lib.a(wrapper-lib.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Maybe someone can simply spot, what's wrong with my workflow?
Note, for completeness the whole build script output
Note2, since ar in the gcc#6 toolchain does not work (liblto_plugin.so missing) I use clang's ar tool...
mac-mini:~ worker$ ./build-test.sh
g++-6 (Homebrew GCC 6.4.0) 6.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----------------------
build shared-lib .o with g++
build a wrapper library in C with gcc
build static libshared-lib.a
build static libwrapper-lib.a
build main with clang
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.1.0 (clang-802.0.41)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
----------------------
Apple LLVM version 8.1.0 (clang-802.0.41)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
clang: warning: libstdc++ is deprecated; move to libc++ [-Wdeprecated]
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.12.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-cpu penryn -target-linker-version 278.4 -v -dwarf-column-info -debugger-tuning=lldb -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0 -stdlib=libstdc++ -Wall -std=c++11 -fdeprecated-macro -fdebug-compilation-dir /Users/worker -ferror-limit 19 -fmessage-length 166 -stack-protector 1 -fblocks -fobjc-runtime=macosx-10.12.0 -fencode-extended-block-signature -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o /var/folders/18/m18t0kxx03d7__31kg3wrsr40000gq/T/main-337db7.o -x c++ main.cpp
clang -cc1 version 8.1.0 (clang-802.0.41) default target x86_64-apple-darwin16.7.0
ignoring nonexistent directory "/usr/include/c++/4.2.1/i686-apple-darwin10/x86_64"
ignoring nonexistent directory "/usr/include/c++/4.0.0"
ignoring nonexistent directory "/usr/include/c++/4.0.0/i686-apple-darwin8/"
ignoring nonexistent directory "/usr/include/c++/4.0.0/backward"
#include "..." search starts here:
#include <...> search starts here:
/usr/include/c++/4.2.1
/usr/include/c++/4.2.1/backward
/usr/local/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0/include
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
/usr/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
End of search list.
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch x86_64 -macosx_version_min 10.12.0 -o main -L/Users/worker -lwrapper-lib -lshared-lib /var/folders/18/m18t0kxx03d7__31kg3wrsr40000gq/T/main-337db7.o -lstdc++ -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0/lib/darwin/libclang_rt.osx.a
Undefined symbols for architecture x86_64:
"_foo", referenced from:
_wrapper in libwrapper-lib.a(wrapper-lib.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
You compile shared-lib.cpp with:
g++-6 -c -Wall -fpic -std=c++11 shared-lib.cpp
And you compile wrapper-lib.c with:
gcc-6 -c -Wall -fpic wrapper-lib.c
Have a look at the symbol table of shared-lib.o. It's something like:
$ readelf -s shared-lib.o
Symbol table '.symtab' contains 24 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS shared-lib.cpp
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 1 OBJECT LOCAL DEFAULT 5 _ZStL19piecewise_construc
7: 0000000000000000 1 OBJECT LOCAL DEFAULT 4 _ZStL8__ioinit
8: 0000000000000032 73 FUNC LOCAL DEFAULT 1 _Z41__static_initializati
9: 000000000000007b 21 FUNC LOCAL DEFAULT 1 _GLOBAL__sub_I_shared_lib
10: 0000000000000000 0 SECTION LOCAL DEFAULT 6
11: 0000000000000000 0 SECTION LOCAL DEFAULT 9
12: 0000000000000000 0 SECTION LOCAL DEFAULT 10
13: 0000000000000000 0 SECTION LOCAL DEFAULT 8
14: 0000000000000000 50 FUNC GLOBAL DEFAULT 1 _Z3foov
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZSt4cerr
17: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZStlsISt11char_traitsIcE
18: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZSt4endlIcSt11char_trait
19: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNSolsEPFRSoS_E
20: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNSt8ios_base4InitC1Ev
21: 0000000000000000 0 NOTYPE GLOBAL HIDDEN UND __dso_handle
22: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNSt8ios_base4InitD1Ev
23: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND __cxa_atexit
(I'm working on Ubuntu, not OS X.)
Note that there is only one global function defined in this object file and
its name is _Z3foov.
That's the mangled name of the C++ function called foo in shared-lib.cpp. That's
the name the linker sees.
Now the symbol table of wrapper-lib.o:
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS wrapper-lib.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 wrapper
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
This object file makes an undefined reference to foo, because wrapper-lib.c
is a C source file and you compiled it as such. C does not mangle names. No definition
of foo is provided by any object file in your linkage, so it fails with that
symbol unresolved.
To avoid this and accomplish your linkage, you can direct the C++ compiler
not to mangle the name foo, when compiling shared-lib.cpp. You do so like:
shared-lib.cpp
#include <iostream>
using namespace std;
extern "C" {
void foo()
{
cerr << "Hi from the shared lib" << endl;
}
} //extern "C"
Enclosing the definition of foo in extern "C" {...} has no effect on
C++ compilation except the one you want: the symbol foo will be emitted
as a C symbol; not mangled.
Having done that, you must of course follow suit in shared-lib.h:
shared-lib.h
#ifndef SHARED_LIB_H
#define SHARED_LIB_H
#ifdef __cplusplus
extern "C" {
#endif
void foo();
#ifdef __cplusplus
}
#endif
#endif
With those corrections, let's try again:
$ g++-6 -c -Wall -fpic -std=c++11 shared-lib.cpp
and check the symbol table:
$ readelf -s shared-lib.o | grep foo
14: 0000000000000000 50 FUNC GLOBAL DEFAULT 1 foo
Now the one global function defined is foo, not _Z3foov, and your
linkage will succeed.
If you want to write a C++ library that exports a C++ API and not a C API to
the linker, then you cannot call its API from C except by discovering the
mangled names of the API (with readelf, nm, objdump) and explicitly
calling those mangled names from C. Thus without those extern "C" fixes,
your linkage would also succeed with:
wrapper-lib.c
extern void _Z3foov(void);
int wrapper()
{
_Z3foov();
return 123;
}
g++ (GCC) 5.2.0
clang version 3.7.1 (tags/RELEASE_371/final)
GNU gdb (GDB) 7.12
Gdb is unable to locate the definition of std::string when compiled with clang for some reason. I have custom compiled and build gcc and clang as Centos 6.5 comes with older version of gcc.
Example code
#include <string>
int main()
{
std::string s("This is a string");
return 0;
}
Compile with g++ and debug - works just fine
[~]$ g++ -ggdb3 -std=c++14 stl.cpp
[~]$ gdb a.out
GNU gdb (GDB) 7.12
Reading symbols from a.out...done.
(gdb) break main
Breakpoint 1 at 0x400841: file stl.cpp, line 5.
(gdb) r
Starting program: /home/vagrant/a.out
Breakpoint 1, main () at stl.cpp:5
5 std::string s("This is a string");
(gdb) n
7 return 0;
(gdb) p s
$1 = {static npos = <optimized out>,
_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x612c20 "This is a string"}, _M_string_length = 16, {
_M_local_buf = "\020\000\000\000\000\000\000\000\300\b#\000\000\000\000", _M_allocated_capacity = 16}}
(gdb)
Check that it is linking with my rpm build version of libstdc++ and not system
[~]$ ldd a.out
linux-vdso.so.1 => (0x00007ffd709e0000)
libstdc++.so.6 => /opt/spotx-gcc/lib64/libstdc++.so.6 (0x00007f29318fa000)
libm.so.6 => /lib64/libm.so.6 (0x00007f2931676000)
libgcc_s.so.1 => /opt/spotx-gcc/lib64/libgcc_s.so.1 (0x00007f293145f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f29310cb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2931c93000)
[~]$ objdump -T -C a.out
a.out: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4.21 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4 std::allocator<char>::~allocator()
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4.21 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4 std::allocator<char>::allocator()
0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_Resume
0000000000400700 DF *UND* 0000000000000000 CXXABI_1.3 __gxx_personality_v0
All looks good now if I try the same with clang
[~]$ clang++ -std=c++14 -g stl.cpp
[~]$ gdb a.out
GNU gdb (GDB) 7.12
Reading symbols from a.out...done.
(gdb) break main
Breakpoint 1 at 0x400853: file stl.cpp, line 5.
(gdb) r
Starting program: /home/vagrant/a.out
Breakpoint 1, main () at stl.cpp:5
5 std::string s("This is a string");
(gdb) n
7 return 0;
(gdb) p s
$1 = <incomplete type>
(gdb)
Now I get an incomplete type - but the same libraries are being used
[~]$ ldd a.out
linux-vdso.so.1 => (0x00007fff5352d000)
libstdc++.so.6 => /opt/spotx-gcc/lib64/libstdc++.so.6 (0x00007f76b4023000)
libm.so.6 => /lib64/libm.so.6 (0x00007f76b3d9f000)
libgcc_s.so.1 => /opt/spotx-gcc/lib64/libgcc_s.so.1 (0x00007f76b3b88000)
libc.so.6 => /lib64/libc.so.6 (0x00007f76b37f4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f76b43bc000)
[~]$ objdump -T -C a.out
a.out: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4.21 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4 std::allocator<char>::~allocator()
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4.21 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
0000000000000000 DF *UND* 0000000000000000 GLIBCXX_3.4 std::allocator<char>::allocator()
0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_Resume
0000000000400700 DF *UND* 0000000000000000 CXXABI_1.3 __gxx_personality_v0
Does anyone have any advice on where to look or something that I've missed. Both compilers are bootstrapped when building them - everything seems fine - it just appears to be std::string is not defined when using clang.
As ks1322 mentioned, this is because clang has decided not to emit debug information for libstc++.
You can force clang to do so by providing the following flag:
-D_GLIBCXX_DEBUG
I would only provide the flag for debug builds, but if debug is the default and release builds are a special target you should remove it:
release: CXXFLAGS := $(filter-out -D_GLIBCXX_DEBUG,$(CXXFLAGS)) -O2
This has fixed the same problem for me.
The last workaround mentioned in bug 24202 as linked by ks1322 is worth having a look at:
-fno-limit-debug-info will make your debug info larger, slow link (if you're not using -gsplit-dwarf) and debugger performance. But, yes, will address this.
Using -fno-limit-debug-info forces Clang to emit debug information for e.g. std::string at the cost of a larger binary while preserving compatibility with other libraries and the rest of the system/SDK.
As ks1322 and Kevin mentioned, one can instead use -D_GLIBCXX_DEBUG to switch libstdc++ into debug mode but this comes at a heavy price: any library you link against and with which you exchange STL containers (string, vector, etc.) must also be built with -D_GLIBCXX_DEBUG. Meaning: your system/SDK must either support this with a separate set of libraries or you will have to rebuild them yourself.
I've reproduced this issue on Fedora with system clang.
It appears that clang is not emitting debug information for std::string because it was told that libstdc++ provides it. See this comment from bug 24202:
Looks like you don't have debug information for libstdc++ installed:
Missing separate debuginfos, use: dnf debuginfo-install libgcc-5.1.1-4.fc22.x86_64 libstdc++-5.1.1-4.fc22.x86_64
Clang is not emitting debug information for std::string because it was
told that libstdc++ provides it (but in your case, it's not
installed); this is a debug size optimization that GCC apparently
doesn't perform.
Does this work if you install the debug information for libstdc++?
I've installed debug info for libstdc++ with command dnf debuginfo-install libstdc++-6.2.1-2.fc25.x86_64 and that resolved the issue.
clang trusts that debugging symbols for libstd++ are available, so you have to install them. See ks1322's answer for how to do that on Fedora. On Ubuntu, run:
sudo apt-get install libstdc++6-dbgsym
After that, things will just work.
Do not define _GLIBCXX_DEBUG since that'll break libstdc++'s abi.
-fno-limit-debug-info will make clang emit debug info that's larger than necessary, so I'd advise against that too. Just install the debug info package for libstdc++.
for me:
-fno-limit-debug-info is the real solution for clang / clion.
_GLIBCXX_DEBUG cause link error with some library
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
CLion 2022.1.3
Build #CL-221.5921.27, built on June 21, 2022
In my setup using gcc-arm-none-eabi 4.8 and binutils 2.26 I get pretty undefined behavior when compiling the object files separately with -mthumb but leaving that flag out in the final linking step using ld without getting any warning from the linker. Why is that the case?
The undefined behavior is probably (according to the very helpful FOSS developers I have asked first) due to the default multilib architecture chosen by the linker due to the lack of the flag. However, why doesn't the linker warn about this issue? Can't it easily detect the ISA of the linked functions from the object and library files to determine that something fishy is going on?
Ideally yes it should know from the objects being linked. Disassemble and examine the interaction between functions of different objects.
I was recently trying to demonstrate how good the gnu tools/linker was at adding trampolines. When it consistently failed miserably. It would put the trampoline in for you for one direction but not the other (thumb to/from arm). I think this was an assembly to/from C issue and I didnt use a plethera of directives like the compiler uses, so that was probably it.
Fairly simple to setup some test cases, make an object out of functions built for arm, make another out of functions built for thumb, have them call each other. Put enough structure around it to get the tools to link without error and then disassemble and examine.
armstart.s
.global _start
_start:
bl notmain
b hang
hang: b .
notmain.c
extern unsigned int one ( unsigned int );
int notmain ( void )
{
one(1);
return(0);
}
one.c
extern unsigned int two ( unsigned int x );
unsigned int one ( unsigned int x )
{
return(two(x+5));
}
two.c
extern unsigned int one ( unsigned int x );
unsigned int two ( unsigned int x )
{
return(one(x+7));
}
Makefile
ARMGNU = arm-none-eabi
#ARMGNU = arm-linux-gnueabi
AOPS = --warn --fatal-warnings
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
all : notmain.bin
clean:
rm -f *.bin
rm -f *.o
rm -f *.elf
rm -f *.list
rm -f *.bc
rm -f *.opt.s
rm -f *.norm.s
rm -f *.hex
armstart.o : armstart.s
$(ARMGNU)-as $(AOPS) armstart.s -o armstart.o
notmain.o : notmain.c
$(ARMGNU)-gcc $(COPS) -c notmain.c -o notmain.o
two.o : two.c
$(ARMGNU)-gcc $(COPS) -mthumb -c two.c -o two.o
one.o : one.c
$(ARMGNU)-gcc $(COPS) -c one.c -o one.o
notmain.bin : memmap armstart.o notmain.o one.o two.o
$(ARMGNU)-ld -o notmain.elf -T memmap armstart.o notmain.o one.o two.o
$(ARMGNU)-objdump -D notmain.elf > notmain.list
$(ARMGNU)-objcopy notmain.elf notmain.hex -O ihex
$(ARMGNU)-objcopy notmain.elf notmain.bin -O binary
some of the disassembly:
20000024 <one>:
20000024: e92d4010 push {r4, lr}
20000028: e2800005 add r0, r0, #5
2000002c: eb000007 bl 20000050 <__two_from_arm>
20000030: e8bd4010 pop {r4, lr}
20000034: e12fff1e bx lr
20000038 <two>:
20000038: b510 push {r4, lr}
2000003a: 3007 adds r0, #7
2000003c: f000 f804 bl 20000048 <__one_from_thumb>
20000040: bc10 pop {r4}
20000042: bc02 pop {r1}
20000044: 4708 bx r1
20000046: 46c0 nop ; (mov r8, r8)
20000048 <__one_from_thumb>:
20000048: 4778 bx pc
2000004a: 46c0 nop ; (mov r8, r8)
2000004c: eafffff4 b 20000024 <one>
20000050 <__two_from_arm>:
20000050: e59fc000 ldr ip, [pc] ; 20000058 <__two_from_arm+0x8>
20000054: e12fff1c bx ip
20000058: 20000039 andcs r0, r0, r9, lsr r0
2000005c: 00000000 andeq r0, r0, r0
And the toolchain in this case
arm-none-eabi-gcc (GCC) 6.1.0
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
(binutils 2.26.20160125)
Worked quite nicely.
Likewise with gcc 5.4.0 and binutils 2.26.1.
Looking at the differences between readelf for the two objects we see for example:
< 00000004 00000a1d R_ARM_JUMP24 00000000 two
---
> 00000004 00000a0a R_ARM_THM_CALL 00000000 one
compiling the object files separately with -mthumb but leaving that flag out in the final linking step using ld without getting any warning from the linker. Why is that the case?
The resulting executable file should work fine on most ARM platforms except for the microcontroller profiles (Cortex-M). There is even a good reason to mix thumb code with ARM libraries: Code size.