Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined reference when linking with static library, but successful link when compiling with src

I'm trying to create a C library, and a C++ test program for the library using Boost::Test. The code that I posted in this question is a simplification of my code, and it displays the exact same problem. Please help!

Here's the directory structure. childlib is the library I'm trying to create, and test is the test program.

> tree
.
`-- src
    |-- main
    |   |-- c
    |   |   `-- childlib.c
    |   |-- childlib.h
    |   `-- makefile
    `-- test
        |-- cpp
        |   `-- test.cpp
        `-- makefile

5 directories, 5 files

I can make childlib into a static library successfully:

> cd src/main    
> make
gcc -c -fPIC -o c/childlib.o c/childlib.c
ar rcs libchildlib.a c/childlib.o
ranlib libchildlib.a

But I can't make my test program by linking with it:

> cd ../test/
> make
g++ -I. -I../main -Imy_boost_install_dir/include -c -std=c++11 -o cpp/test.o cpp/test.cpp
g++ -L../main -Lmy_boost_install_dir/lib -lchildlib -lboost_unit_test_framework -Wl,-rpath=my_boost_install_dir/lib -o test_childlib cpp/test.o 
cpp/test.o: In function `test_func1::test_method()':
test.cpp:(.text+0x15e7e): undefined reference to `childlib_func1()'
collect2: error: ld returned 1 exit status
make: *** [test_childlib] Error 1

On the other hand, if I run the compilation manually by removing the reference to the static lib and adding childlib.c as a source file, I can make the test program successfully:

> g++  -Lmy_boost_install_dir/lib -lboost_unit_test_framework -Wl,-rpath=my_boost_install_dir/lib -o test_childlib cpp/test.o ../main/c/childlib.c 
> ./test_childlib 
Running 1 test case...

*** No errors detected

Here's the various source files. test.cpp:

#include <cstdlib>
#include <stdint.h>

#define BOOST_TEST_MODULE childlib test
#include <boost/test/unit_test.hpp>
#include <boost/test/included/unit_test.hpp>

#include "childlib.h"

BOOST_AUTO_TEST_CASE( test_func1 ) {
  childlib_func1();

  BOOST_CHECK(true);
}

childlib.h:

#ifndef CHILDLIB_H_
#define CHILDLIB_H_

void childlib_func1();

#endif /* CHILDLIB_H_ */

and finally childlib.c:

#include <stdint.h>

void childlib_func1() {
  return;
}

The two makefiles are below.

childlib makefile: Eventually I want to also create a dynamic library but right now it's commented out of make all:

# childlib

CC = gcc

SRCS             = $(wildcard c/*.c)
OBJS             = $(SRCS:.c=.o)
HDRS             = $(wildcard *.h)
MODULE_BASE_NAME = childlib
MODULE_LIB_A     = lib$(MODULE_BASE_NAME).a
MODULE_LIB_SO    = lib$(MODULE_BASE_NAME).so
INCLUDE_DIRS     = .
INCLUDE          = $(addprefix -I,$(INCLUDE_DIRS))
CFLAGS           = -c -fPIC


all: $(MODULE_LIB_A) # $(MODULE_LIB_SO)

$(MODULE_LIB_A): $(OBJS)
    ar rcs $@ $^
    ranlib $@

$(MODULE_LIB_SO): $(OBJS)
    $(CC) -shared -o $@ $^  

.c.o:
    $(CC) $(CFLAGS) -o $@ $^

clean:
    -rm -f $(MODULE_LIB_A) $(MODULE_LIB_SO) $(OBJS)
    -find . -name '*~' -delete

Here's the test harness makefile:

# test

CC        = g++

SRCS             = $(wildcard cpp/*.cpp)
OBJS             = $(SRCS:.cpp=.o)
MODULE_EXE       = test_childlib

MAIN_DIR         = ../main

BOOST_DIR        = my_boost_install_dir
BOOST_INC_DIR    = $(BOOST_DIR)/include
BOOST_LIB_DIR    = $(BOOST_DIR)/lib
BOOST_LIBS       = boost_unit_test_framework
INCLUDE          = $(addprefix -I,. $(MAIN_DIR) $(BOOST_INC_DIR))
LIB_DIRS         = $(addprefix -L,$(MAIN_DIR) $(BOOST_LIB_DIR))
LIBS             = $(addprefix -l,childlib $(BOOST_LIBS))
LINKER_OPTS      = -Wl,-rpath=$(BOOST_LIB_DIR)

CFLAGS           = -c -std=c++11


all: test

test: $(MODULE_EXE)
    ./$(MODULE_EXE)


$(MODULE_EXE): $(OBJS)
    $(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o $@ $^ 

.cpp.o:
    $(CC) $(INCLUDE) $(CFLAGS) -o $@ $^

clean:
    -rm -f $(MODULE_EXE) $(OBJS)
    -find . -name '*~' -delete

I'm using gcc & g++ 4.8.1. I'm also using boost 1.54.0 compiled with gcc 4.7.2.

It seems like I need to just provide the right options to g++ in my test makefile, but I don't know what they are. Can someone please help me link the childlib library with my test program?

like image 361
Avi Tevet Avatar asked Dec 14 '22 22:12

Avi Tevet


2 Answers

I haven't seen all your code, but the problem is most likely caused by how static linking works (http://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking/).

Try changing the order of the libraries, and specifically, try changing this:

$(MODULE_EXE): $(OBJS)
  $(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o $@ $^ 

into this:

$(MODULE_EXE): $(OBJS)
  $(CC) $(LIB_DIRS) $(LINKER_OPTS) -o $@ $^ $(LIBS)
like image 154
Ivan Vergiliev Avatar answered Dec 17 '22 11:12

Ivan Vergiliev


Libraries and object are processed in the order encountered on the linking command line: object files (or source files after transformation into object files) are always included. Their undefined symbols are added to the list of symbols to be resolved. Libraries are inspected to look for any undefined symbols the point they are encountered. Any object file from a library being investigated is included if it defines, at least, one symbol which is undefined so far. Once processed, the libraries are otherwise forgotten.

tl;dr: put the object file first, the libraries last.

like image 41
Dietmar Kühl Avatar answered Dec 17 '22 12:12

Dietmar Kühl