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

问题描述:

我有一个hello world cpp文件。如果我编译c++ test.cpp -o test,我得到可执行的“test”文件(-rwxr-xr-x),如果我执行它,它会被执行并产生预期的结果。为什么可执行文件不可执行?

但是,如果我使用${CXX} -std=c++0x -I${INCLUDE_DIR1} -c test.cpp -o test -L{LIB_DIR1} -llib_name我也会得到“测试”文件,但在这种情况下它不可执行。所以,我无法执行它。我试图chmod +x,它获取权限被执行,但如果我尝试执行它会得到一个错误消息(无法执行)。

我在做什么错误以及如何纠正?

+0

'文件测试',看看它是什么类型 - 可能只是一个错误的.o文件。 – 2013-03-18 14:48:03

+0

如果我输入'file test',我会得到'test:ELF 64位LSB可重定位,x86-64,版本1(GNU/Linux),不会被剥离“,但我不知道它是什么意思。这个输出是否意味着我有一个“错误的.o文件”? – Roman 2013-03-18 15:02:06

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

如果您需要可执行文件,请删除-c开关。

有关完整的编译过程的详细信息,请参阅:How does the compilation/linking process work?

+0

如果我删除-c,我会得到:'/ usr/bin/ld:找不到-llib_name collect2:error:ld返回1退出状态。 – Roman 2013-03-18 15:03:29

+0

这只是意味着编译器无法找到该库。如果它不在默认搜索路径中,则需要使用'-L/path/to/lib'指定它所在的目录。 – Mat 2013-03-18 15:05:59

好像你在这个整体规划疯狂的开始。所以,如果你很难理解什么是错误命名的对象(在本例中称为.o)文件,编译器参数以及它们实际做了什么,甚至是库名称,我都明白。

我的假设是,从它的外观来看,你正在复制你正在执行的shell脚本行,以便从一个未知的例子中产生你的可执行文件(至少使其感觉如此)。所以,我会尽量简单,清楚,粗略地解释这里发生的一切。

首先,C++编译器将给定的源文件编译为可重定位的机器码。如果将编译器(和编译器)的排放存储在文件中,则该文件将成为前述的目标文件(请记住.o文件)。如果你注意了,这需要一个目标文件包含可重新定位的机器代码

现在,您已经编译了您的源代码,并且您的机器代码等同于存储在目标文件中。但是,这个目标文件不是直接可执行的;即使它确实包含CPU应该没有执行问题的机器代码。问题是,这个机器码没有链接。因此,第二个要点是:目标文件中的可重定位机器代码未链接,因此不能执行。除此之外,目标文件还可能包含其他元数据以帮助链接程序处理链接过程,从而生成实际的可执行文件。我们将调用整个这个中间表示的对象代码。因此,现在我们编译了我们的源代码并发送了目标文件,下一个显而易见的逻辑步骤就是将目标文件与任何想要生成可执行文件的库和/或其他目标文件链接起来我们可以像您的第一个./test二进制一样执行。这是链接器起作用的地方,链接器接受目标文件和库(注意:粗略地说,库是目标文件的集合),并执行一些链接魔术,例如解析跨模块的对象文件中的未定义引用,以及排列可执行文件等然后,链接器发出符合链接器被调用的目标平台的可执行文件格式的最终​​可执行文件。这是发射的这个文件,你可以像./test那样执行。

所以,现在你已经知道了基础知识,让我们看看你的shell调用有什么问题。首先,你应该知道通过调用c++你可以触发C++编译器(我认为它是g++clang++在这种情况下)链接器。其次,您应该知道-c标志告诉编译器编译给定的源文件,并单独发出其等效的目标代码和机器码。因此,调用C++编译器而不使用-c标志不仅会导致它编译给定的源代码,而且还会链接生成的目标代码并生成最终的可执行文件。这是你的第一个命令行中发生的事情。发出的文件名为test是链接的可执行文件。但是,在您的第二个命令行中存在-c标志。在这种情况下,编译器将只发布编译为test.cpp的目标代码。您需要链接此生成的目标文件才能生成可执行文件。看起来你告诉编译器将它生成的目标代码存储在一个名为test的文件中,这是Marc在提到错误行为(目标文件通常具有.o.obj后缀)时讲述的内容。

从此时起,您有两种选择,可以删除-c标志或链接目标文件。编译和链接(单独)的目标文件(包括可能-IT-shouldn't待有™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参数,你的编译器应该停止对它的大喊大叫。

对不起,很长,很长,很长的解释,但我希望你在这里学会了一个小点,并在那里轻咬,所有这些对你有用。