Can't link libFuzzer.a using clang with libc++

1.8k views Asked by At

I'm trying to link together:

  • libFuzzer.a, compiled with clang++-5.0 and -std=c++11
  • my fuzz driver, compiled with clang++-5.0 and -std=c++11 -stdlib=libc++
  • libcurl, compiled with clang-5.0

Specifically, this linker command is being executed:

/bin/bash ../../libtool --silent --tag=CXX --mode=link clang++-5.0 -I../../include -I../../lib -I../../lib -I../../tests/fuzz -fsanitize=address -fsanitize-address-use-after-scope -fsanitize-coverage=trace-pc-guard,trace-cmp -std=c++11 -stdlib=libc++ -o curl_fuzzer curl_fuzzer-curl_fuzzer.o ../../lib/libcurl.la /root/checkouts/Fuzzer/libFuzzer.a -lssh2 -lssl -lcrypto -lssl -lcrypto -lz -lpthread -lm

Executing this command outputs the following:

/bin/bash ../../libtool --silent --tag=CXX   --mode=link clang++-5.0 -I../../include -I../../lib -I../../lib -I../../tests/fuzz -fsanitize=address -fsanitize-address-use-after-scope -fsanitize-coverage=trace-pc-guard,trace-cmp -std=c++11 -stdlib=libc++   -o curl_fuzzer curl_fuzzer-curl_fuzzer.o ../../lib/libcurl.la /root/checkouts/Fuzzer/libFuzzer.a  -lssh2 -lssl -lcrypto -lssl -lcrypto -lz -lpthread -lm > /tmp/cat.txt 2>&1; head -20 /tmp/cat.txt
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `fuzzer::FileToVector(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long, bool)':
/root/checkouts/Fuzzer/./FuzzerIO.cpp:34: undefined reference to `std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::_Ios_Openmode)'
/root/checkouts/Fuzzer/./FuzzerIO.cpp:40: undefined reference to `std::istream::seekg(long, std::_Ios_Seekdir)'
/root/checkouts/Fuzzer/./FuzzerIO.cpp:41: undefined reference to `std::istream::tellg()'
/root/checkouts/Fuzzer/./FuzzerIO.cpp:47: undefined reference to `std::istream::seekg(long, std::_Ios_Seekdir)'
/root/checkouts/Fuzzer/./FuzzerIO.cpp:49: undefined reference to `std::istream::read(char*, long)'
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `~basic_ifstream':
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/fstream:534: undefined reference to `VTT for std::basic_ifstream<char, std::char_traits<char> >'
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/fstream:534: undefined reference to `std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()'
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `~basic_ios':
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/basic_ios.h:282: undefined reference to `std::ios_base::~ios_base()'
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `~basic_ifstream':
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/fstream:534: undefined reference to `VTT for std::basic_ifstream<char, std::char_traits<char> >'
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/fstream:534: undefined reference to `std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()'
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `~basic_ios':
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/basic_ios.h:282: undefined reference to `std::ios_base::~ios_base()'
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `fuzzer::FileToString(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
/root/checkouts/Fuzzer/./FuzzerIO.cpp:54: undefined reference to `std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::_Ios_Openmode)'
/root/checkouts/Fuzzer/libFuzzer.a(FuzzerIO.o): In function `~basic_ifstream':
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/fstream:534: undefined reference to `VTT for std::basic_ifstream<char, std::char_traits<char> >'
<snip>

I've tried doing the following to fix this:

  • Adding -lc++ - doesn't do anything
  • Adding -lc++abi - doesn't do anything

It feels like I'm missing something obvious but I'm not sure what...

My installation of clang-5.0 comes from clang-5.0/kali-rolling.

3

There are 3 answers

0
Max Dymond On

Looks like for whatever reason kali's version of clang-5.0 doesn't link binaries with libc++ properly. I tested the same code with google's ossfuzz version of clang (clang-6.0) and everything linked together successfully.

0
Marshall Clow On

You have compiled part of your code with libstdc++ and part with libc++. When you link them together (using libc++), the parts that have references to libstdc++ don't get resolved.

For example, std::__cxx11::basic_string is definitely a libstdc++ symbol.

0
VonC On

The OP mentioned (2017):

Looks like for whatever reason kali's version of clang-5.0 doesn't link binaries with libc++ properly.
I tested the same code with google's ossfuzz version of clang (clang-6.0) and everything linked together successfully.

At the time, libstdc++ was needed.
But now, from the current LibFuzzer documentation (March 2021)

Recent versions of Clang (starting from 6.0) include libFuzzer, and no extra installation is necessary.

As an illustration of that new ("new" meaning "post CLang 5") usage, Git 2.31.1 (Q1 2021), references the same LibFuzzer 11.0 documentation section:

See commit 68b5c3a (08 Mar 2021) by Andrzej Hunt (ahunt).
(Merged by Junio C Hamano -- gitster -- in commit af10702, 19 Mar 2021)

Makefile: update 'make fuzz-all' docs to reflect modern clang

Signed-off-by: Andrzej Hunt

Clang no longer produces a libFuzzer.a.
Instead, you can include libFuzzer by using -fsanitize=fuzzer.
Therefore we should use that in the example command for building fuzzers.

We also add -fsanitize=fuzzer-no-link to the CFLAGS to ensure that all the required instrumentation is added when compiling git 1, and remove -fsanitize-coverage=trace-pc-guard as it is deprecated.

I happen to have tested with LLVM 11 - however -fsanitize=fuzzer appears to work in a wide range of reasonably modern clangs.

(On my system: what used to be libFuzzer.a now lives under the following path, which is tricky albeit not impossible for a novice such as myself to find:
/usr/lib64/clang/11.0.0/lib/linux/libclang_rt.fuzzer-x86_64.a )