为什么可执行文件不能执行?

3
我有一个hello world的cpp文件。如果我使用编译它,就会得到一个可执行的"test"文件(<-rwxr-xr-x>),如果我执行它,它会执行并生成一个期望的结果。
然而,如果我使用<${CXX} -std=c++0x -I${INCLUDE_DIR1} -c test.cpp -o test -L{LIB_DIR1} -llib_name>,我也会得到一个"test"文件,但是在这种情况下它不能被执行。所以,我无法执行它。我试过,它获得了执行权限,但如果我尝试执行它,会出现一个错误消息(无法执行)。
我做错了什么,如何纠正呢?

检查file test文件的类型 - 很可能只是一个命名错误的.o文件。 - undefined
如果我输入file test,我得到的结果是test: ELF 64-bit LSB relocatable, x86-64, version 1 (GNU/Linux), not stripped,但我不知道它的意思。这个输出是否意味着我有一个"命名错误的.o文件"? - undefined
2个回答

22

-c选项告诉编译器不要生成可执行文件(它的意思是“仅编译”)。 它只创建一个目标文件,适合与其他目标文件和库链接成可执行文件。

如果要生成可执行文件,请删除-c选项。

有关完整编译过程的更多详细信息,请参见:编译/链接过程如何工作?


如果我移除-c,则会得到:/usr/bin/ld: 无法找到-llib_name collect2: 错误:ld 返回 1 退出状态 - undefined
这只是意味着编译器找不到那个库。如果它不在默认的搜索路径中,你需要使用-L/path/to/lib来指定它所在的目录。 - undefined

2
看起来你刚入门编程,对于被错误命名的对象(在这里称为 .o 文件)、编译器参数及其实际作用或者库名称感到难以理解,我理解你的困惑。从你的情况来看,你正在复制一行shell脚本,以便根据未知示例生成可执行文件(至少让-llib_name这个指令产生了这种感觉)。所以,我会尽可能地简单、清晰和粗略地解释这里发生了什么。
首先,C++编译器将给定的源文件编译成可重定位机器代码。如果你将编译器(只是编译器)的输出存储到一个文件中,这个文件就会成为前面提到的对象文件(记住.o文件)。如果你注意了的话,这意味着对象文件包含可重定位机器代码
现在,你已经编译了源代码,并且把它的机器码等效形式存储在了一个对象文件中。然而,这个对象文件并不能直接执行;即使它< strong>确实包含了CPU可以执行的机器代码。问题在于,这些机器代码没有被链接。因此,第二个要点就来了:对象文件中的可重定位机器代码未经过链接,因此不能(实际上应该不能)被执行。除此之外,对象文件还可能包含一些元数据,来帮助链接器进行链接处理,从而产生实际的可执行文件。我们将这个中间表示称为目标代码
所以,现在我们已经编译了源代码并发出了对象文件,下一个显而易见、逻辑上合理的步骤是将对象文件与任何你想要的库和/或其他对象文件链接起来,以生成我们可以像第一个./test二进制文件一样执行的可执行文件。这就是链接器的作用,链接器接受对象文件和库(附注:大致上,库是对象文件的集合),并进行一些链接魔法,比如解决跨模块的对象文件中未定义的引用,并安排可执行文件的地址空间,等等。然后,链接器会发出符合调用链接器的目标平台的可执行文件格式的最终可执行文件。正是这个被发出的文件,你可以像 ./test 一样执行。
所以,现在您已经了解了基础知识,让我们看看您的shell调用存在什么问题。首先,您应该知道通过调用,您既可以触发C++编译器(我认为在这种情况下是g++clang++),可以触发链接器。其次,您应该知道-c标志告诉编译器编译给定的源文件并仅发出其等效的目标代码和机器代码。因此,调用C++编译器没有-c标志不仅会导致它编译给定的源代码,还会链接生成的目标代码并生成最终的可执行文件。这就是在您的第一个命令行中发生的事情。被发出的名为test的文件是链接的可执行文件。然而,在您的第二个命令行中,存在-c标志。在这种情况下,编译器将仅发出由编译test.cpp产生的目标代码。您需要链接此结果对象文件才能生成可执行文件。看起来您要求编译器将其生成的对象代码存储在名为test的文件中,这就是Marc提到的错误命名的原因(对象文件通常具有.o.obj后缀)。
从这一点开始,您有两个选择,要么删除-c标志,要么链接对象文件。为了(分别)编译和链接对象文件(包括可能不应该存在的™liblib_name):
${CXX} -std=c++0x -stdlib=libc++ -I${INCLUDE_DIR1} -c test.cpp -o test.o
${CXX} -std=c++0x -stdlib=libc++ -L${LIB_DIR1} -llib_name test.o -o test

我已经自作主张介绍了libc++。如果您愿意使用C++11(您称之为C++0x),最好使用它。但是,如果您想在一行中编译和链接,可以使用以下内容来实现相同的效果:

${CXX} -std=c++0x -stdlib=libc++ -I${INCLUDE_DIR1} -L${LIB_DIR1} -llib_name test.cpp -o test

这两种方法的作用完全相同。但请注意,单行版本缺少-c标志。
现在我们几乎讲解完所有内容,最后一个问题是-I-L-l是什么意思。 -I指定编译器将搜索源代码中包含的头文件的路径。这个路径不会覆盖编译器的默认头文件搜索路径,只是作为补充。 -L也是同样的道理,但是用于库。你的编译器将在这个路径中查找你明确链接的库(例如在这种情况下的liblib_name)。同样地,这不会覆盖默认库搜索路径。最后,-l标志用于指定要链接到可执行文件的链接器库。在你的情况下,你正在链接一个名为liblib_name-l标志指定的名称省略了库文件名中的第一个lib)的库,而你说你的编译器抱怨该库不存在。你确定你真的想将liblib_name链接到你的可执行文件中吗?这是一个真正的库吗?如果不是,请删除整个-llib_name参数,你的编译器就不会再抱怨了。
抱歉,解释有点长,但我希望你能在这里学到一些有用的东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接