GNU Make隐式编译未使用自定义LIBCFLAGS的原因探究
环境信息
Linux watvde0453 3.10.0-1062.1.1.el7.x86_64 #1 SMP Fri Sep 13 22:55:44 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux GNU Make 4.2.1 Built for x86_64-pc-linux-gnu
问题场景
用户编写Makefile构建共享库、静态库和可执行文件,尝试分离库与可执行文件的编译标志:
- 将
-fPIC存入自定义变量LIBCFLAGS - 通用编译标志
-O3 -g -Wall -Werror存入CFLAGS
执行make libso时,隐式编译生成.o文件的步骤仅调用了CFLAGS,未加载LIBCFLAGS,导致链接共享库时出现重定位错误:
$ make libso gcc -O3 -g -Wall -Werror -c -o hashfunction.o hashfunction.c gcc -O3 -g -Wall -Werror -c -o hashtable.o hashtable.c gcc -O3 -g -Wall -Werror -c -o hashtablelinkedlist.o hashtablelinkedlist.c gcc -fPIC -O3 -g -Wall -Werror -shared -o lib.so hashfunction.o hashtable.o hashtablelinkedlist.o /tools/oss/packages/x86_64-centos7/binutils/default/bin/ld: hashtable.o: relocation R_X86_64_32 against `.rodata.str1.8' can not be used when making a shared object; recompile with -fPIC hashtable.o: error adding symbols: Bad value collect2: error: ld returned 1 exit status make: *** [Makefile:12: libso] Error 1
用户将所有标志合并到CFLAGS后可正常构建,但想明确两个核心问题:
- 为什么自定义的
LIBCFLAGS未被隐式编译步骤调用? CFLAGS是否为GNU Make隐式编译的默认变量?
解答
1. 自定义LIBCFLAGS不被隐式规则使用的原因
GNU Make的隐式规则是内置的固定逻辑,编译.c文件生成.o文件的默认命令为:
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
该命令仅引用了几个预定义变量:CC(默认值为gcc)、CFLAGS(C语言编译标志)、CPPFLAGS(预处理器标志)。自定义的LIBCFLAGS不在内置命令的变量列表中,因此隐式编译时不会自动加载其内容。
你遇到的错误本质是:编译.o文件时未加入-fPIC,尽管链接共享库时补加了该标志,但.o文件已基于非位置无关代码生成,链接器无法将其转换为共享库所需的格式,因此报错。
2. CFLAGS确实是隐式编译默认使用的变量
是的,CFLAGS是GNU Make为C语言编译预定义的变量,专门用于传递编译阶段的标志,会自动被.c→.o的隐式规则调用。类似的预定义变量还有:
CXXFLAGS:C++编译标志LDFLAGS:链接阶段的标志CPPFLAGS:预处理器(如宏定义、头文件路径)标志
分离编译标志的解决思路
如果想继续区分库与可执行文件的编译标志,有两种常用方案:
- 方案一:自定义模式规则
针对生成库用的.o文件编写单独的模式规则,加入LIBCFLAGS:
后续构建共享库时,使用带%.lib.o: %.c $(CC) $(CFLAGS) $(LIBCFLAGS) $(CPPFLAGS) -c -o $@ $<.lib.o后缀的目标文件即可。 - 方案二:显式编写编译规则
不依赖隐式规则,手动写出每个.o文件的编译命令,在编译库对应的.o时加入LIBCFLAGS:hashfunction.o: hashfunction.c $(CC) $(CFLAGS) $(LIBCFLAGS) -c -o $@ $< hashtable.o: hashtable.c $(CC) $(CFLAGS) $(LIBCFLAGS) -c -o $@ $< # 其他.o文件同理
内容的提问来源于stack exchange,提问作者Shaggy1
相关产品推荐
相关产品推荐

