为什么 Make 不为隐式规则定义 LD 变量?



我目前正在学习汇编。我写简单的测试程序,所以我把一个简单的makefile放在一起:

AS = nasm                                                                       
CC = ld                                                                         
          
ASFLAGS = -f elf64                                                              
          
all:    hello_name put_digit                                                    
          
hello_name: hello_name.o                                                        
          
put_digit:  put_digit.o                                                         
          
clean:                                                                          
${RM} *.o                                                           
          
fclean:     clean                                                               
${RM} hello_name put_digit                                          
          
.PHONY: all clean fclean

我一直认为隐式规则并没有真正的用处,因为你最终会在任何实质性的项目中重写规则。但是,尝试在试验时快速建立makefile对他们来说似乎是一个很好的用例,而不是编写构建脚本。所以我在这里使用它们。

发现没有LD变量是多么令人沮丧,所以我不得不破解CC来获得ld。隐式规则也不考虑.asm文件,所以我不得不将它们伪装成.s文件,尽管我使用的是intel/nasm语法。

这有什么好的/历史的原因吗?或者隐含的规则是半生不熟的吗?

有一个内置和定义的LD变量,只是它没有在这个隐式规则中使用。规则本身就是:

%: %.o
#  recipe to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

LINK.o被定义为

# default
LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH)

我相信这是一条正确的道路。通常情况下,应该调用编译器驱动程序来运行链接器(除非您使用自定义链接脚本(,它还包括一些编译器提供的启动文件,否则您需要自己指定这些文件,这是特定于编译器的。

考虑这个简单的例子:

$ cat hello.c
int main(int argc, char *argv[]) {
return 0;
}

有人可能认为这是一个完整的程序,但事实并非如此。它所编译的平台需要一些额外的准备。事实上,链接器本身会发出警告:

$ gcc -c hello.c
$ ld -o hello hello.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
$ ./hello
Segmentation fault

当使用编译器驱动程序时,它会向链接器提供一组选项和启动文件:

$ gcc -Wl,-v -o hello hello.o
collect2 version 9.3.0
/usr/bin/ld -plugin /usr/lib/gcc/x86_64-linux-gnu/9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccRdkBP1.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. -v hello.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o
GNU ld (GNU Binutils for Ubuntu) 2.34
$ ./hello
$ echo $?
0

因此,最终的hello二进制文件是从Scrt1.ocrti.ocrtbeginS.ohello.ocrtendS.ocrtn.o链接而来的,其中大部分由编译器提供。这些是二进制文件工作所必需的。

您的具体示例使用了不同的工具,这些工具可能与这种方法不兼容。这并不意味着隐含的规则";半生不熟的";欢迎您自行定义链接规则以匹配所使用的工具集。

最新更新