Makefile中LDFLAGS的一个问题的查证记录


昨天在刷Learn C The Hard Way的ex26时遇到了一个Makefile中有关LDFLAGS的问题,将整个查证解决的过程记录在这里。

首先,在上面的文章中,作者给出了这样的一个Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PREFIX?=/usr/local
CFLAGS=-g -Wall -I${PREFIX}/apr/include/apr-1 -I${PREFIX}/apr/include/apr-util-1
LDFLAGS=-L${PREFIX}/apr/lib -lapr-1 -pthread -laprutil-1

all: devpkg

devpkg: bstrlib.o db.o shell.o commands.o

install: all
install -d $(DESTDIR)/$(PREFIX)/bin/
install devpkg $(DESTDIR)/$(PREFIX)/bin/

clean:
rm -f *.o
rm -f devpkg
rm -rf *.dSYM

一开始,我直接照抄了这个Makefile的内容,以及上述文章中的其他程序源文件,并且安装了apr和apr-util两个库,lib文件都存放在/user/local/apr/lib文件夹下面

1
2
3
yubo@ubuntu:~$ ls /usr/local/apr/lib
apr.exp libapr-1.a libapr-1.so libapr-1.so.0.4.6 libaprutil-1.la libaprutil-1.so.0 pkgconfig
aprutil.exp libapr-1.la libapr-1.so.0 libaprutil-1.a libaprutil-1.so libaprutil-1.so.0.4.1

但是当我运行make时,却报这样的连接错误:
1
2
3
4
5
6
7
8
9
yubo@ubuntu:~/path_to_devpkg$ make
cc -g -Wall -I/usr/local/apr/include/apr-1 -I/usr/local/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1 -c -o bstrlib.o bstrlib.c
cc -g -Wall -I/usr/local/apr/include/apr-1 -I/usr/local/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1 -c -o db.o db.c
cc -g -Wall -I/usr/local/apr/include/apr-1 -I/usr/local/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1 -c -o shell.o shell.c
cc -g -Wall -I/usr/local/apr/include/apr-1 -I/usr/local/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1 -c -o commands.o commands.c
cc -g -Wall -I/usr/local/apr/include/apr-1 -I/usr/local/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1 -L/usr/local/apr/lib -lapr-1 -pthread -laprutil-1 devpkg.c bstrlib.o db.o shell.o commands.o -o devpkg
/tmp/ccyibR3g.o: In function `main':
/home/yubo/Code/GitCode/PersonalGitCode/saber/learncthehardway/devpkg/devpkg.c:14: undefined reference to `apr_pool_initialize'
...

明显的,这是在链接时没有找到apr相关的库。可是我已经安装了apr和apr-util,并且也通过-L指定了链接时搜索的目录为/usr/local/apr/lib了。
首先,我检查了make生成的cc语句:
1
cc -g -Wall -I/usr/local/apr/include/apr-1 -I/usr/local/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1  -L/usr/local/apr/lib -lapr-1 -pthread -laprutil-1  devpkg.c bstrlib.o db.o shell.o commands.o   -o devpkg

看上去没什么问题,-L参数正确,apr和apr-util都指定好了。
接下来,我想到去求助万能的google,找到了这么一篇帖子,按照其中所说的,把-l参数调整到cc语句的最后,果然可以编译通过了。
但是还没结束,怎么修改Makefile可以使得-l语句默认放到所生成的cc语句的最后呢?一番搜索,在Stackoverflow上找到了这篇文章,按照其中所说,把LDFLAGS中的-l部分拆分到LDLIBS中,得到如下Makefile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PREFIX?=/usr/local
CFLAGS=-g -Wall -I${PREFIX}/apr/include/apr-1 -I${PREFIX}/apr/include/apr-util-1 -D_LARGEFILE64_SOURCE=1

all: devpkg

devpkg: bstrlib.o db.o shell.o commands.o

LDFLAGS=-L${PREFIX}/apr/lib
LDLIBS=-lapr-1 -pthread -laprutil-1

install: all
install -d ${DESTDIR}/${PREFIX}/bin/
install devpkg ${DESTDIR}/${PREFIX}/bin/

clean:
rm -f *.o
rm -f devpkg
rm -rf *.dSYM

此时再调用make,就可以自动的把-l参数放到最后了。


知道了怎么做,还应该知道为什么。
其实man cc看一下文档就会明白了。在 -llibrary节有这么一段话:

It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not be loaded.

也就是说,当linker遇到.o文件中一个无法识别的名字时,就会去按照cc语句中的顺序去搜索库。由于处理到bar.o的时候,-lz在它的左边,所以将不会被搜索到。因此如果在bar.o中使用了z库中的变量或函数,就会无法解析。因此,一般写cc语句的时候,会把-l选项放在所有.o文件之后。
事实上,-l选项一般也的确是放在LDLIBS中的。不知为何,Learn C The Hard Way的作者将其放在了LDFLAGS里。
那么对于object files中的变量,最开始也是类似库文件的顺序查找,但是现代编译器一般都做了优化,会在所有指定的.o文件中进行变量名的查找,所以.o文件的次序变得不那么重要。(参见这篇文章)。


编译成功后,当我第一次调用生成的可执行文件时,有这样的错误信息输出:

./devpkg: error while loading shared libraries: libapr-1.so.0: cannot open shared object file: No such file or directory

这个问题只需要将/usr/local/apr/lib加到/etc/ld.so.conf.d/下的任意文件中(一般会自己新建一个conf文件),然后调用ldconfig即可解决。


整个程序的文件在我的GitHub

转载请注明出处: http://blog.guoyb.com/2016/05/25/makefile-ld/

欢迎关注我的微信公众号TechTalking,技术·生活·思考:
后端技术小黑屋

Comments