Linux跨平台编译问题记录

1. qt安装后,demo执行时必然会出现找不到gl库或者gl头文件的问题,问题解决可参考博客:

【Qt】错误GL/gl.h: No such file or directory的解决方法(以及cannot find -lGL解决方法)_郭老二-优快云博客

如果是虚拟机安装的Linux,在安装mesa包的时候会报错。尝试重装系统之后,在安装vmware-tools之前 执行上面的安装命令是能够安装成功的,所以原因大致是下方博客中所述的问题:

Linux下Qt编译出现"cannot find -lGL"以及无法安装libgl1-mesa-dev_qq_17835733的博客-优快云博客

2. 依赖库配置查找的命令

3. 某些只可读文件在终端修改的常用操作步骤

1) vi /usr/xxx.conf 

2) 输入I,进入插入模式,输入要追加或修改的内容

3) ESC退出追加模式

4) SHIFT+:切换到命令输入模式,输入命令!wq强制保存并退出

4. ldd命令可查看某个库的所有依赖库信息

3. 好用的locate命令,默认不支持,需要apt安装:

Linux 中的 locate 命令详解_Linux教程_Linux公社-Linux系统门户网站

4. apt update只能获取检查当前是否有可更新包,并提示获取可更新列表。可通过upgrade命令选择性更新部分软件。

5. windows共享目录的软连接问题

由于Windows的文件系统不支持软链接,所以在解压包含软链接的压缩包、拷贝含软链接的文件或尝试共享目录下建立软链接都是会报错的。实际开发中采取了折中的方案,共享目录下的头文件等保持不动,拷贝必要的动态库到Linux系统文件目录下,并在拷贝后的目录中建立软链接。同时用sed修改pri等文件中的库引用路径,关键代码如下:

#!/bin/bash
echo === 1. find all .so files
#linux目录下放置拷贝的库文件
dtarget=/home/$USER/project/depends/centos64
#windows存放库的目录
dsource=$(pwd)/centos64
dlen=${#dsource}
mkdir -p $dtarget
#查找所有so结尾的动态库文件,为了提升检索效率,通过-prune参数跳过某些文件较多的不必要目录
dylibs=`find $dsource \( -path '*/.svn/*' -o -path '*/include/*' \) -prune -o -name *.so.*` -print

echo === 2. copy files
cd $dsource
for f in $dylibs 
do
    #截取父级目录部分,因为库目录组织方式是libs/boost/boost_xx.so.2.7.0,
    #拷贝时希望该部分路径不变,所以做截取处理
	lib=${f: ($dlen+1)}
    #这里要做模式匹配是因为find的结果中包含prune掉的路径,要过滤掉
	if [[ $lib == *\.so\.* ]]
	then
        #拷贝并创建所有的父级目录
		cp --parents $lib $dtarget
	fi
done

echo === 3. modify .pri
#替换pri中的将所有的-L"xxx/a.so"换为dtarget的目录
esctarget=`echo $dtarget | sed 's|/|\\\\/|g'`
pattern=s/L\"[^\"]*centos64/L\"${esctarget}/
#sed命令很强大,拼这个pattern比较麻烦,替换并保存要用-i参数
sed -i $pattern ./../../code/project/qtcreator/common_depends.pri 
	
echo === 4. rebuild symbol link
ln -s a.so a.so.2.0.0

find的检索非常耗时,通过-path参数尝试跳过某些路径检索并不生效,只有配合-prune参数指定路径才可以,stackoverflow上有篇相关的帖子很好,学习下可以大大提升find的实用性:

regex - How to use '-prune' option of 'find' in sh? - Stack Overflow

sed可以做文件或字符串中的文本匹配替换,但是正则表达式的语法与常规的正则语法不太一样,尤其是在通配符的处理上,可参考帖子:

sed中的正则匹配 - 简书

某些情况下命令中的模式字符串会引用某个变量,甚至引用的变量中包含字符。可按如下方式处理:

pattern="s|^LIBS\s*+=\s*-L[^\]*$|"$esctarget"|g"
#通过引号包裹变量引用
sed -i "$pattern" ./../../../project/qtcreator/common_depends.pri 

6. make通过-j参数可以进行多核编译,但是qmake中如果配了前置命令会导致前置命令和编译一起并发执行,如果前置是删除文件处理,会出现删除失败的情况,因为是编译器已经在写文件了,肯定是删不掉的。

7. 头文件包含路径大小写、不规范问题检测工具,解决大小写导致的编译错误。

Linux编译中的头文件路径大小写校验脚本-优快云博客

8. 编译后的成果物链接或启动加载时报错,可用如下工具校验:

a. ldd或libtree工具查看所有动态库或可执行文件依赖的所有库文件的版本及加载路径,发现其中版本不匹配或库缺失的问题。libtree是github上的开源工具,能够以树形结构展示所有依赖库,类似windows下的depends;需要下载后,拷贝到/usr/bin目录下才能启动。

b. objdump、readelf、nm查看库文件中的导出接口、文件头等的字段的工具。

9. 依赖库的rpath问题

Linux下的依赖库跟window下的库加载逻辑不一样,windows下进程的所有动态库必须放在exe的同级目录下才能加载;Linux的库加载则没有这样的约束,ld会按照一定的检索逻辑在很多位置检索依赖库。关于动态库加载,参考博文:

Shared Libraries: Understanding Dynamic Loading

其中优先级最高是可执行文件中的rpath,这个rpath可通过readelf或chrpath查看,如

qt的插件库:

RUNPATH的意思是libqxcb.so的依赖库会在该目录位置查找,若当前路径不存在则按其他规则继续查找。libqxcb.so会依赖Qt的其他库,假如你的成果物组织方式中没有lib,可能会导致加载的是其他位置的Qt库。比如,我这里是直接放在platforms的上级目录中,为此要修libqxcb.so的RUNPATH,如下命令:

这样就能达到从父级目录加载Qt库的目的。特别说明下,$ORIGIN指的是进程的运行目录。当然,能修改的前提是库中已经有RUNPATH,如果没有这个字段,是不支持修改的。那么,如何在编译时指定rpath?pro文件中配置如下:

QMAKE_LFLAGS += -Wl,-rpath,"'\$$ORIGIN'" -Wl,-rpath-link,"$(ORIGIN)" -Wl,--exclude-libs,ALL -Wl,-z,defs

10. QtCreator下GDB调试

常用的堆栈、断点和单步调试等,界面操作是可以的。如果要产看更多的调试信息,如库加载、指定内存位置中的数据查看等还是要用到GDB的命令查看则要用到GDB命令。调试状态下,可调出地命令窗口,如下图所示:

 GDB的命令手册,如下链接:

Top (Debugging with GDB)

11. 将库文件中的调试信息剥离到独立的符号文件

gdb使用symbol文件调试程序 - 简书

12. QtCreator下的源码调试

程序可调试的前提是可执行文件中带有调试信息(debug info),不是符号信息,或者可执行文件匹配的符号文件已正确加载。调试信息记录了符号对应的行号、源代码路径等信息,通常是绝对路径。调试时将按照该绝对路径尝试查找源文件,如果没找到,调试中就无法加载并定位源文件,非本机编译的文件一般是找不到的。调试器支持设置源码路径映射(或替换),gdb有如下命令

For example, if the file /foo/bar/baz.c was moved to /mnt/cross/baz.c, then the command

          (gdb) set substitute-path /usr/src /mnt/cross

配置成功后,可通过list查看当前堆栈对应的源码。也可以通过directory指定源码的检索路径。那么,如何知道调试信息对应的源码路径:

readelf -wi查看库中的调试信息,获取源码路径:

对于比较大的库文件,可采用如下脚本:

readelf -wi $1 | grep -B1 DW_AT_comp_dir | awk '/DW_AT_name/{name = $NF; getline; print $NF"/"name}'

针对QtCreator,可以在堆栈区单击右键调出菜单,找打调试器配置 

 

 这里添加源文件路径映射,再次启动调试就能够做源码调试了。

13. 附加调试问题

GDB附加到进程调试时,通过info share查看,有些库虽然有调试信息,但并没有加载。这是因为动态库的符号文件的读取有默认路径,可以通过show solib-search-path来查看,如下:

(gdb) show solib-search
The search path for loading non-absolute shared library symbol files is .

也就是说默认是当前路径,也就是pwd。所以要让符号读取能到可执行文件所属目录,三种方式:

1) 启动gdb时,先切换当前路径;

2) 通过gdb的启动参数-cd来指派;

3) 在启动gdb之前或之后,通过set soli-search-path修改检索路径;

附:GDB常用命令链接如下:

https://www.cs.princeton.edu/courses/archive/fall16/cos432/hw2/gdb-refcard.pdf

获取gdb工具的help信息:gdb --help

14. 手动抓core文件

程序执行中出现卡死等问题,需要在测试环境上抓core文件,操作如下:

1) gdb -p $pid附加到进程,或者gdb attach $pid。貌似attach命令挂载速度更快

2) gcore $core_path生成core文件

3) detach

15. 动态库的符号链接问题

编译可执行文件的时候,遇到其间接依赖库的链接问题,报错"无法解析的符号"。比如,run(可执行文件)->b.so->a.so,run链接的时候会报错找不到a.so中的符号。按正常理解,run链接只需定位其直接依赖库的符号解析就可以了,关"b对a的依赖”什么事。可linux的下链接器真就这么干了,在可执行文件链接的时候不仅要解析其直接依赖的库,还有解析间接依赖的库的存在性。所以,在编译可执行文件时不仅要配置直接依赖库,还要指定间接依赖库的位置,这就是-rpath-link的作用。github有篇文章解释了这个问题:

c - Why does ld need -rpath-link when linking an executable against a so that needs another so? - Stack Overflow

简单理解来说,二次检测的目的是因为依赖库a有可能发生变化,比如接口增删或版本更替,在可执行文件的链接器报错,可保证启动时可正常加载。而且这配置的值不会打在ELF头中,纯粹是链接时用到的参数,只对可执行文件生成有效,动态库和静态库的链接是用不到这个配置的。

16. 修改core文件的生成目录,解决echo输出到core_pattern报错无权限问题:

linux - Why is editing core_pattern restricted? - Unix & Linux Stack Exchange

17. Linux系统配置生成core文件

1) 输入ulimit -a命令我们可以看到第一行core文件大小为0,即我们关闭了core文件产生,若不为0,就打开了呗。或ulimit -c只显示core文件大小。
        使用ulimit -c filesize命令,可以限制core文件的大小(filesize的单位为kbyte)。如果生成的信息超过此大小,将会被裁剪,最终生成一个不完整的core文件。在调试此core文件的时候,gdb会提示错误。若ulimit -c unlimited,则表示core文件的大小不受限制。
如果想让修改永久生效,则需要修改配置文件,如 .bash_profile、/etc/profile或/etc/security/limits.conf。将上述配置命令追加到配置文件末尾;

2)设置core文件的生成路径及文件名称:

sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t

18. 在磁盘中按关键字查找文件,命令:find / -name hello*

19. gdb的附加到进程的操作:1)gdb打开调试工具,attach $pid附加到指定进程;

2) bt查看当前堆栈; f 3切换堆栈到3号栈帧; p当前展位置所有的局部变量; p variable打印变量的当前值。x 按指定字节数查看内存数据。

20. 创建windows共享路径映射的命令: 

sudo mount -t  cifs -o username=admin,password=Abc12345,vers=2.0,dir_mode=0777,file_mode=0777,mfsymlinks //127.0.0.1/share ./

最后两个路径分别为Windows下共享目录的路径,Linux下的对应映射路径

21. GCC的编译器版本不一致可能出现链接失败的问题,尤其新版本的编译器,代码中用了老版本的库,会出现未定义符号的报错。通度nm命令可以查看库中所有对外导出符号,.o文件反汇编能够看到其连接的符号名称,二者是有差异的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值