rustc如何从bash进程替换编译源代码但是gcc不能?

时间:2018-06-11 13:29:04

标签: c linux bash unix rust

$ rustc <(echo 'fn main(){ print!("Hello world!");}')
$ ls
63
$ gcc <(echo '#include<stdio.h> int main(){ printf("Hello world!\n"); return 0;}')
/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status

为什么ld无法链接该计划?

2 个答案:

答案 0 :(得分:5)

gcc命令主要是一个调度引擎。对于每个输入文件,它从文件名的扩展名确定它是的文件类型,然后将文件传递到适当的处理器。所以.c文件由C编译器编译,.h文件被组装成预编译头,.go文件被发送到cgo编译器,依此类推。

如果文件名没有扩展名或者扩展名无法识别,gcc会假定它是某种应该参与最终链接步骤的目标文件。这些文件传递给collect2实用程序,然后调用ld,可能两次。这将是流程替换的情况,它会生成/dev/fd/63等文件名,但不包含扩展名。

ld不依赖于文件名来标识目标文件格式。它通常由几个不同的目标文件识别器构建,每个目标文件识别器都依赖于某种类型的魔术数字&#34; (即文件开头或附近的特殊模式)。它一次调用这些识别器,直到找到一个乐于解释文件的识别器。如果文件未被识别为二​​进制格式,ld假定它是链接描述文件(纯文本文件)并尝试解析它。

当然,在尝试ld之间需要倒回文件,并且由于进程替换安排传递管道而不是文件,因此搜索将失败。 (如果您尝试通过将stdin重定向到管道来传递文件,也会发生同样的情况,您可以这样做:如果您将stdin指定为文件名,gcc会将-作为文件处理。但是它坚持要告诉它它是什么类型的文件。见下文。)

由于ld无法倒回文件,因此在文件与其第一次猜测不匹配后,它将失败。因此来自ld的错误消息,这有点误导,因为您可能认为该文件已经被编译并且后续失败在链接步骤中。事实并非如此;因为文件名没有扩展名,gcc直接跳到链接阶段,几乎立即失败。

在进程替换,管道,标准输入和命名错误的文件的情况下,您仍然可以手动告诉gcc文件是什么。您可以使用-x中的ld选项执行此操作(尽管在这种情况下,该选项实际上控制了输入的类型)。

在互联网上有很多类似问题的答案,包括StackOverflow上的各种答案,声称GCC试图检测输入文件的语言。它没有这样做,它从来没有。 (我怀疑它会永远存在,因为它编译的某些语言彼此足够相似,因此不可能进行准确的检测。)唯一进行自动检测的组件是[1,2,3,4,5,6,7],它只做一次GCC已不可撤销地决定将输入文件视为目标文件或链接描述文件。

答案 1 :(得分:2)

至少在您的情况下,您可以使用-xc手动指定输入语言时使用进程替换。但是,您应该在include语句之后添加换行符。

$ gcc -xc <(echo '#include<stdio.h>
int main(){ printf("Hello world!\n"); return 0;}')
$ ls
a.out
$ ./a.out 
Hello world!

有可能出现这种情况的原因,请参阅Charles&#39;回答和对这个答案的评论。