Linux下软件编译常见问题

Page content

编译过程说明

在Linux下使用诸如apt、pacman和yum这样的软件管理工具安装软件比较简单,往往一条命令就足够了。但是,如果想安装一个旧版本软件进行测试与研究,或者是一个比较冷门的软件,就需要用户获取源代码从头进行编译。前一段时间我积累了许多这种经验,因此本篇文章从实用的角度描述编译时可能出现的问题及解决办法。

在自己开始编译源码前,首先要阅读软件给出的相关说明。它们往往在README或者INSTALL文件中,对软件所依赖的包、编译过程进行一些描述。

绝大多数软件源码的编译方式不过如下几种。

  1. 如果看到源码目录里有CMakeLists.txt,你可能需要在当前目录下建立一个build文件夹,然后在build里面执行cmake ..(或者直接在CMakeLists.txt同级目录下执行cmake .);然后再执行make、sudo make install命令。

  2. 如果目录里面有configure,往往是执行configure,然后执行make和sudo make install。

  3. 如果有autogen.sh这样的文件,往往是执行autogen.sh,生成configure,然后再执行configure、make和sudo make install。

虽然总体上的编译方式不多,可能出现的问题却千奇百怪,下面为我结合实际经验总结出的问题及可能的解决方法。

问题1:在autogen.sh或者configure运行时提示缺少相关的库。

这些缺少的库提示往往以lib开头,比如libdbus。但是,直接使用apt安装libdbus是不行的,因为该库对应的软件包不一定就叫libdbus。因此,可以使用sudo apt-cache search libdbus进行搜索。所需安装的包往往是搜索结果中以dev结尾的包。其实,直接将相关提示复制到Google进行搜索也可以得知缺少的库。

有时,软件还对库的版本进行限制。比如我需要安装libdbus-1-dev的某个指定版本,可以使用sudo apt-cache policy libdbus-1-dev搜索备选版本。如果我想安装结果中的1.10.6-1ubuntu3.5版本,则可以使用sudo apt install libdbus-1-dev=1.10.6-1ubuntu3.5来安装。

如果备选版本中没有所需的版本,则可以去Google搜索、下载相应版本的库代码自行编译安装,或者是使用deb包进行安装。http://packages.debian.org 和 http://packages.ubuntu.com 往往会提供所需的deb包。

问题2:没有configure文件

如果源码目录中没有CMakeLists.txt,也没有autogen.sh、configure等常见的安装方式,不过存在configure.ac文件,可以在源码目录中执行以下指令,尝试生成configure。

  1. aclocal
  2. autoconf
  3. autoheader(出现什么AC_CONFIG_HEADERS not found in configure.ac 可以忽略)
  4. automake --add-missing(出现ltmain.sh not found,需要执行autoreconf -ivf)

问题3:undefined reference to ‘xxx’

这个问题会出现在make过程中,原因往往是默认的编译链接选项没有指定程序所需的库文件。将该错误提示进行搜索就可以得知缺少的是什么库、以及类似于export LDFLAGS="-lxx"的解决办法。注:export、unset、env是在shell中设置、取消、查看环境变量的常用命令。

问题4:提示文件内容错误

在编译过程中可能会提示Makefile某一行错误,或者源码某一行错误。这种问题比较棘手。出现的原因可能是源文件的确有错误,或者是代码太古老,一些写法现在的标准已经不支持。这种情况可以试一试通过注释掉一些代码来解决。

编译过程中出现的问题大部分还是因为编译环境引起的,尤其是缺少某些特定版本的库。实在解决不了,可以看看代码是哪一年写的,去 http://old-releases.ubuntu.com/releases/ 下载当年的系统镜像进行尝试。

为什么某个源代码文件没有生成目标文件?

进行安全研究时,可能发现代码中的vuln.c文件存在漏洞;可在对程序编译后,却没有发现它的目标文件(如 vuln.o),影响下一步探索,这是为什么呢?

在多数情况下,这是因为目标代码是程序中的插件,或者说一个模块。比如,一个程序的数据库存储可能支持sqlite、mysql,但是默认是使用mysql,那么和sqlite相关的代码可能就不被编译,这就造成了没有生成目标文件的问题。

该如何使得目标代码被编译呢?Linux常见的程序编译方式有两种,一是autogen.sh->configure->make;还有一种是cmake->make。

对于第一种,可以试一试./configure –help的输出。还是以数据库举例,输出中可能会告诉你–with-sqlite可以指定使用sqlite,那么执行./configure时加上该选项就可以在新生成的Makefile中添加对sqlite相关代码的编译。

对于第二种,往往在CMakeLists.txt中会有选择的地方。可以根据目标代码的关键字进行搜索,将OFF改为ON来启动相应模块。

CMakeLists.txt中设置示例

上面所述的方法是比较规范、易解决的,有的程序不提供相关选项,而是根据判断某个包存不存在来自动选择是否编译某模块。

某子文件夹中的CMakeLists.txt

在上图中,想要mp3tunes被编译,需要满足几个条件。这些条件检查某些包是不是存在、是不是有某些选项。简单粗暴的方法是将那些if直接注释掉,当然也可以研究研究怎么满足这些条件,而不是修改它。

如果上面的方法还是不能获得目标文件,可以去到目标代码所在的子文件夹,若有Makefile,尝试直接make编译该子模块。