链接静态库时未定义引用,但使用src编译时链接成功
Undefined reference when linking with static library, but successful link when compiling with src
我正在尝试创建一个C库,并使用Boost:: test库的c++测试程序。我在这个问题中发布的代码是对我的代码的简化,它显示了完全相同的问题。请帮助!
下面是目录结构。childlib
是我要创建的库,test
是测试程序。
> tree
.
`-- src
|-- main
| |-- c
| | `-- childlib.c
| |-- childlib.h
| `-- makefile
`-- test
|-- cpp
| `-- test.cpp
`-- makefile
5 directories, 5 files
我可以使childlib
成功地变成一个静态库:
> cd src/main
> make
gcc -c -fPIC -o c/childlib.o c/childlib.c
ar rcs libchildlib.a c/childlib.o
ranlib libchildlib.a
但是我不能通过链接来创建我的测试程序:
> 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
另一方面,如果我通过删除对静态库的引用并添加childlib.c作为源文件来手动运行编译,我可以使测试程序成功:
> 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
这里是各种源文件。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_ */
最后是childlib.c:
#include <stdint.h>
void childlib_func1() {
return;
}
下面是两个make文件。
childlib
makefile:最终我想创建一个动态库,但现在它被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
下面是测试工具的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
我正在使用gcc &g++ 4.8.1。我也使用boost 1.54.0与gcc 4.7.2编译。
似乎我只需要在我的测试makefile中为g++提供正确的选项,但我不知道它们是什么。谁能帮我把childlib
库和我的测试程序链接起来?
我还没有看到你所有的代码,但问题很可能是由静态链接的工作方式引起的(http://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking/)。
试着改变库的顺序,特别是,试着改变这个:
$(MODULE_EXE): $(OBJS)
$(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o $@ $^
这:
$(MODULE_EXE): $(OBJS)
$(CC) $(LIB_DIRS) $(LINKER_OPTS) -o $@ $^ $(LIBS)
库和对象按照链接命令行中遇到的顺序进行处理:始终包括对象文件(或转换为对象文件后的源文件)。它们的未定义符号被添加到待解析的符号列表中。检查库以查找遇到的任何未定义符号。如果正在研究的库中的任何对象文件定义了至少一个到目前为止未定义的符号,则包含该文件。否则,这些库将被遗忘。
在"on other hand"的情况下,用g++
编译childlib
。
在原始情况下,您使用gcc
编译它。
C和c++是不同的语言。您应该决定要为childlib
使用哪种语言并坚持使用它,而不是尝试编写适合两种语言的公共子集的代码。
链接C生成的对象文件和c++生成的对象文件是没有问题的,只要你指定c++对象文件应该使用C兼容的格式,或者它们需要链接的函数名在C生成的对象文件中。
假设您希望将childlib.c
保留为C代码。后者更常见,这样做的方法是确保childlib.h
中的所有声明和定义都包装在extern "C" {
中…你的代码……}
,当它们从c++源文件中包含时。实现这一点的一种方法是让头文件包含:
#ifdef __cplusplus
extern "C" {
#endif
// the code
#ifdef __cplusplus
}
#endif
那么头文件总是在两种语言中工作,并且您不必依赖于记住在包含它的所有文件中执行extern "C" {
#include...
。它也允许你有extern
块之外的东西,例如包括系统头。
我知道您没有使用自动生成,但在我的情况下,我需要将我的*_LIBRARIES
目标重命名为*_LTLIBRARIES
,以便Makefile将使用libtool构建*.la
文件而不是*.a
文件。
由于某些原因,当我使用另一种语法时,嵌套的静态库将不能正确地添加到顶级静态库中(对顶级库的nm
转储导致"文件格式不可识别"错误;当阅读它内部的嵌套库时)导致您的问题中的链接器错误,或者我只是在调用ar
并打印其帮助消息时得到错误。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- CMake-按正确顺序将项目与C运行时对象文件链接
- 从链接列表c++中删除一个项目
- 有根的二进制搜索树.保留与其父级的链接
- 读取文件的最后一行并输入到链接列表时出错
- 静态数据成员的问题-修复链接错误会导致编译器错误
- node-gyp 在 macOS 上未正确链接库
- 未知的 GCC 链接器错误,但已成功构建
- 将cxxtest链接在cmake中,它说它成功了,但无法构建
- 在静态库中链接失败,但链接共享库成功
- 在C 中,我如何在下面的示例中成功将类别链接在一起
- 不满意的链接错误在iOS但不是Android上,loadLibrary总是成功的
- CGAL:如何成功编译和链接CGAL示例(在Mac OS X 10.9 Mavericks上)
- 链接使用 arm-none-eabi-g++ 成功,但不能使用 arm-none-eabi-gcc
- 编译伪C++程序时链接不成功
- 使用CUDA nvcc编译时,与提升库的链接失败,使用gcc编译成功
- 为什么在c++中调用库(构建库是成功的)时会出现链接错误?
- 链接静态库时未定义引用,但使用src编译时链接成功
- 当我们把类的定义放在头文件中时,为什么链接成功了?
- 如何在原生 Node 插件中成功链接 Flex、Bison 和 Node.js