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

时间:2013-03-18 14:46:10

标签: c++ compilation executable

我有一个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,它获得了执行权限但是如果我尝试执行它会得到一条错误消息(无法执行)。

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

2 个答案:

答案 0 :(得分:19)

-c告诉编译器生成可执行文件(这意味着“仅编译”)。它只创建一个目标文件,适合链接到可执行文件(可能与其他目标文件和库)。

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

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

答案 1 :(得分:2)

看起来你正处于整个编程疯狂的最开始。所以,我确实理解你是否很难掌握什么是错误命名的对象(在这种情况下称为.o)文件,编译器参数以及它们真正做了什么,甚至是库名。

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

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

现在您已经编译了源代码,并且您的机器代码等效存储在目标文件中。但是,此目标文件不能直接执行;即使确实包含CPU应该没有问题执行的机器代码。问题是,这个机器代码没有链接。所以,第二个要点是:目标文件中的可重定位机器代码没有链接,因此不能(好吧,更不应该)执行。除此之外,目标文件还可以包含其他元数据,以帮助链接器进行链接过程,从而生成实际的可执行文件。我们将调用整个中间表示目标代码

所以,既然我们编译了源代码并发出了目标文件,那么下一个显而易见的逻辑步骤就是将目标文件链接到你想要的任何库和/或其他目标文件,以便生成我们的可执行文件可以像你的第一个./test二进制文件一样执行。这是链接器发挥作用的地方,链接器接受目标文件和库(旁注:粗略地说,库是目标文件的集合),并做一些链接魔术,如跨模块解析目标文件中的未定义引用并安排地址空间可执行文件等然后,链接器将发出符合调用链接器的目标平台的可执行文件格式的最终​​可执行文件。发出此文件,您可以像./test一样执行。

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

从这一点开始,您有两个选项,要么删除.obj标志,要么链接目标文件。编译和链接(单独)目标文件(包括可能它应该' t-be-there™ -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 。如果您愿意使用C ++ 11(您将其称为C ++ 0x),那么如果它更好的话,它会更好。但是,如果你想在一行中编译和链接,使用它可以实现同样的目的:

libc++

这两个完全相同。但请注意,单行版本缺少${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参数,编译器就应该停止大喊大叫。

很抱歉,长期,长期的解释,但我希望你在这里学到一个蚕食,并在那里啃食,所有这一切都对你有用。