编译问题记录

1.关于在项目中使用gcc时遇到的一些问题:

注:星号表示cgo相关
1*.cgo使用的编译器是gcc/g++(需要另外自己单独安装)。
2.gcc头文件搜索的方式有三种:
a.参数 -I 指定搜索目录,或者在包含头文件的文件的同级目录。
b.设置环境变量C_INCLUDE_PATH 和CPLUS_INCLUDE_PATH。
c.内定的路径,查看方式,另写一个小测试案例,使用gcc test.c -v ,其中一行输出内容是:#include <…> search starts here:…可查看gcc的默认查找目录。
3*.cgo中参数的设置,CFLAGS处理.c文件头文件搜索,CXXFLAGS处理.cpp文件头文件搜索,LDFLAGS库路径搜索,一般使用绝对路径,也可以使用$(SRCDIR)–当前相对路径。
4.对于PATH环境变量的理解:exe(PATH)执行文件会在环境变量中搜寻
5*.go build会生成缓存文件,如果需要编译的文件不改变则不会重新编译,-a选项可以强制重新编译。
6.库的链接默认搜索路径查看:ld --verbose | grep SEARCH;
7.库链接时进行的链接操作过程查看:gcc test.c -Wl,–verbose;
8.动态库的搜索路径LD_LIBRARY_PATH;

2 c/c++条件编译

介绍c语言中条件编译相关的预编译指令,包括 #define、#undef、#ifdef、#ifndef、#if、#elif、#else、#endif、defined。

#define 定义一个预处理宏
#undef 取消宏的定义

#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义

#if 编译预处理中的条件命令,相当于C语法中的if语句
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else

#endif #if, #ifdef, #ifndef这些条件命令的结束标志.

defined  与#if, #elif配合使用,判断某个宏是否被定义

#ifdef 和 #if defined 两者的区别,前者的常用法是:
#ifdef XXX
#else
#endif
在两者中选择是否有定义(当然也可以如2.2中简化使用)。对于后者,常用法是:
#if defined xxx1
#elif defined xxx2
#elif defined xxx3
#endif
可以在多个中选择是否有定义.

2.2 不同平台编译宏

#ifdef _WIN32
//define something for Windows (32-bit and 64-bit, this part is common)
	#ifdef _WIN64
      //define something for Windows (64-bit only)
	#else
      //define something for Windows (32-bit only)
	#endif
#elif __APPLE__
    #include "TargetConditionals.h"
    #if TARGET_IPHONE_SIMULATOR
         // iOS Simulator
    #elif TARGET_OS_IPHONE
        // iOS device
    #elif TARGET_OS_MAC
        // Other kinds of Mac OS
    #else
    #   error "Unknown Apple platform"
    #endif
#elif __ANDROID__
    // android
#elif __linux__
    // linux
#elif __unix__ // all unices not caught above
    // Unix
#elif defined(_POSIX_VERSION)
    // POSIX
#else
#   error "Unknown compiler"
#endif

3.gcc编译参数fpic和fpie

Position-Independent-Executable是Binutils,glibc和gcc的一个功能, 能用来创建介于共享库和通常可执行代码之间的代码–能像共享库一样可重分配地址的程序, 这种程序必须连接到Scrt1.o。标准的可执行程序需要固定的地址,并且只有被装载到这个地址时, 程序才能正确执行。PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关, 并链接为ELF共享对象。

引入PIE的原因是让程序能装载在随机的地址,通常情况下,内核都在固定的地址运行,如果能改用位置无关, 那攻击者就很难借助系统中的可执行码实施攻击了。类似缓冲区溢出之类的攻击将无法实施。而且这种安全提升的代价很小

谈到PIE就不得不说说Pax和Grsec内核。这两个东西都是为了构建坚不可摧到安全系统而准备的。 PaX是Linux内核安全增强补丁,它能在两方面保证安全性,一是ASLR(Address Space Layout Randomization, 地址空间分布随机化),这是一种将所有数据装载到内存时都随机化地址的方式,当使用PIE选项编译应用时, PaX能将应用的地址做随机加法;二是能提供不可执行的内存空间,这样就能使得攻击者放入内存中的 恶意代码不可执行。不过PaX官网上能支持的最新内核是2.6.27,已经是一年前的更新了。Grsec也时类似的内核补丁, 更新较为频繁能支持最新的2.6.32内核。这两种方式都能将内核完全位置无关,除了Grub和Glibc中无法位置无关的汇编码。

PIE最早由RedHat的人实现,他在连接起上增加了-pie选项,这样使用-fPIE编译的对象就能通过连接器 得到位置无关可执行程序。fPIE和fPIC有些不同。可以参考Gcc和Open64中的-fPIC选项.

gcc中的-fpic选项,使用于在目标机支持时,编译共享库时使用。编译出的代码将通过全局偏移表 (Global Offset Table)中的常数地址访存,动态装载器将在程序开始执行时解析GOT表项(注意, 动态装载器操作系统的一部分,连接器是GCC的一部分).而gcc中的-fPIC选项则是针对某些特殊机型做了 特殊处理,比如适合动态链接并能避免超出GOT大小限制之类的错误。而Open64仅仅支持不会导致GOT表溢出的PIC编译。 gcc中的-fpie和-fPIE选项和fpic及fPIC很相似,但不同的是,除了生成为位置无关代码外, 还能假定代码是属于本程序。通常这些选项会和GCC链接时的-pie选项一起使用。fPIE选项仅能在编译可执行码时用, 不能用于编译库。所以,如果想要PIE的程序,需要你除了在gcc增加-fPIE选项外,还需要在ld时增加-pie选项 才能产生这种代码。即gcc -fpie -pie来编译程序。单独使用哪一个都无法达到效果。

3.1 GCC的W参数

-Wa,< option > 该选项传递参数给汇编器(option是汇编器使用的参数)

-Wp,< option > 该选项传递参数给预处理器

-Wl,< option > 该选项传递参数给链接器(更多关于option的值可以查阅ld的参数)

e.g :
gcc -Wl,-Bstatic -lxx -Wl,-Bdynamic -lxxx -Wl,-Map,xxx.map(GNU ld可以使用-Wl,-Map=xxx.map)
该编译选项告诉链接器xx库使用静态链接,xxx库使用动态链接,并生成map文件

使用GCC 的-l参数指定库时可以使用 -l :filename,即可以指定库的全名,而不是去除lib和.a的短名称

4.linux下动态库路径设置

第一种:将库拷贝到/usr/lib和/lib下。
sudo cp libmyhello.so /usr/lib
sudo cp libmyhello.so /lib
此时再执行./hello即可得到正确的显示结果。

第二种:修改LD_LIBRARY_PATH环境变量:
sudo vim /etc/bashrc 对所有用户生效 或者修改 ~/.bashrc 这样就只对当前用户生效,
在文件最后,添加:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/linux/file/dongtaiku
保存退出,重启终端,此时再执行./hello即可得到正确的显示结果。

第三种:添加/etc/ld.so.conf.d/XXX.conf文件,或者直接在/etc/ld.so.conf文件中添加动态库目录,
vim /etc/ld.so.conf.d/XXX.conf或vim /etc/ld.so.conf,
在文件内添加动态库的目录,
/home/linux/file/dongtaiku,
保存退出,执行ldconfig刷新缓存,生效,
sudo ldconfig
注:使用ldconfig -p可以打印当前缓存(缓存保存在/etc/ld.so.cache中)

第四种:编译时使用Wl,rpath,xxx指定运行时的库路径

5. 动态库的显示加载和隐式加载

动态库在链接到程序中时,可以有两种不同的方式(linux):

  1. 隐示加载,编译时需要链接具体的库名和头文件;使用过程中,直接调用动态库api即可。
  2. 显示加载,通过类dlopen函数来使用动态库,链接时无需具体的库名和头文件,但是需要链接相关库
    -ldl

6.gcc常用参数

-lrt 一些实时库,例如,shm_open
-dl 可以显式加载动态库的动态函数库
-rdynamic 指示连接器把所有符号都在加载到符号表中,以便那些通过 dlopen() 或 backtrace() (这一系列函数使用.dynsym表内符号)这样的函数使用。

注意:在使用gcc时,一般将被依赖的库放在依赖的后面,例如A依赖B,则gcc -A -B,参数的解析是从右往左

7.关于系统库

libc(ANSI C)和glibc(GNU C)都是属于c语言标准库,目前大多数使用glibc;libc++和libstdc++属于c++标准库

链接方式:最理想的标准库链接方式就是半静态链接,通常会选择将 libgcc 与 libstdc++ 这两个标准库静态链接(libc也会存在版本兼容问题,静态链接就是为了解决版本兼容问题,高版本的系统库编译的程序不能在低版本的系统库环境下运行)
-static-libgcc:静态链接 gcc 库
-static-libstdc++:静态链接 c++ 库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值