当前位置: 首页 > 工具软件 > GCC for SPARC > 使用案例 >

gcc 编译参数 so查找路径

柳威
2023-12-01

基本的命令

把一个源文件,编译成可执行文件,经过了预处理、汇编、编译与链接。

-E: 对目标文件只进行预处理,生成的还是源码。

例如:g++ -E test.cpp -o test.i

-S: 对目标文件进行预处理、编译操作,生成汇编代码。

例如:g++ -S test.cpp -o test.s

-o: 对目标文件进行预处理、编译和汇编操作,生效obj文件。

例如:g++ -c test.cpp -o test.o

-fsyntax-only : 对代码只进行语法检查,不做其它任何动作。

常用安全编译选项

ALSR 地址随机化处理

系统级的配置,不属于gcc编译器的范畴。

栈保护

栈保护在编译阶段进行,所以下面的参数是传给编译器的。

直接上英文, 更容易明白。

  • -fstack-protector:

    Emit extra code to check for buffer overflows, such as stack smashing attacks.
    This is done by adding a guard variable to functions with vulnerable objects.
    This includes functions that call alloca, and functions with buffers larger than
    or equal to 8 bytes. The guards are initialized when a function is entered and
    then checked when the function exits. If a guard check fails, an error message
    is printed and the program exits. Only variables that are actually allocated
    on the stack are considered, optimized away variables or variables allocated in
    registers don’t count.

  • -fstack-protector-all:

    Like ‘-fstack-protector’ except that all functions are protected.

  • -fstack-protector-strong:

    Like ‘-fstack-protector’ but includes additional functions to be protected —
    those that have local array definitions, or have references to local frame addresses. Only variables that are actually allocated on the stack are considered,
    optimized away variables or variables allocated in registers don’t count.

  • -fstack-protector-explicit:

    Like ‘-fstack-protector’ but only protects those functions which have the
    stack_protect attribute.

说明:

  1. 原理就是在缓冲区与函数返回控制信息之间插入一个哨兵变量, 当缓冲区溢出时, 哨兵变量的值会首先被覆盖掉, 这样就能判断发生了溢出攻击。
  2. 使用堆栈保护选项在对字符数组进行溢出防护的同时,还有意将局部变量中的数组放到函数栈的高地址,而将其它变量放在低地址。
  3. 性能损失度:fstack-protector-all > fstack-protector-strong > fstack-protector.

堆栈不可执行保护

该保护作用于链接阶段,所以需要把参数传递给链接器。 使用-Wl,<optional>的格式传递给链接器。

使用格式为:-Wl, -z noexecstack 选项。可用于动态库、ELF格式的可执行文件。

作用原理:首先,缓冲区溢出成功后都是通过执行shellcode来达到攻击目的的, 而shellcode基本都是放到缓冲区中,只要操作系统限制了堆栈内存区域不可执行状态就可以,一旦被攻击就报错并返回。

GOT表的保护

该保护作用于链接阶段, 同样把参数传递给链接器。

使用格式:

  • -Wl, -z relro: Create RELRO program header, RELRO表示 relocation read only, 重定位段为只读。 GOT表就是用于重定位的。
  • -Wl, -z now: Mark object non-lazy runtime binding

作用原理:动态链的ELF二进制程序使用称为全局偏移表(GOT)的的查找表去动态解析位于共享库的函数。攻击者通过缓冲区溢出修改GOT表项的函数地址来达到攻击目的。通过增加RELRO选项可以防止GOT表被恶意重写。 另外,GOT表的保护会影响到程序的加载速度。

地址无关的代码 -fPIC

作用阶段为编译器,参数格式为:-fPIC 或者 -fpic.

  • -fpic: Generate position-independent code (PIC) suitable for use in a shared library,
    if supported for the target machine. Such code accesses all constant addresses
    through a global offset table (GOT). The dynamic loader resolves the GOT
    entries when the program starts (the dynamic loader is not part of GCC; it
    is part of the operating system). If the GOT size for the linked executable
    exceeds a machine-specific maximum size, you get an error message from the
    linker indicating that ‘-fpic’ does not work; in that case, recompile with ‘-fPIC’
    instead. (These maximums are 8k on the SPARC, 28k on AArch64 and 32k on
    the m68k and RS/6000. The x86 has no such limit.)
    Position-independent code requires special support, and therefore works only on
    certain machines. For the x86, GCC supports PIC for System V but not for the
    Sun 386i. Code generated for the IBM RS/6000 is always position-independent.
    When this flag is set, the macros pic and PIC are defined to 1.

  • -fPIC: If supported for the target machine, emit position-independent code, suitable
    for dynamic linking and avoiding any limit on the size of the global offset table.
    This option makes a difference on AArch64, m68k, PowerPC and SPARC.
    Position-independent code requires special support, and therefore works only
    on certain machines

地址无关可执行 -fPIE

作用于编译器, 参数格式为:-fPIE 或者 -fPIC, 与fPIC非常类似。

These options are similar to ‘-fpic’ and ‘-fPIC’, but the generated positionindependent code can be
only linked into executables. Usually these options are used to compile code that will be linked using
the ‘-pie’ GCC option.

说明: PIE是GCC与操作系统的产物,提供了地址无关的功能。 ASLR是基础,只有操作系统开启了ASLR功能时,-fpie选项添加的随机化特征才会在程序加载和运行时展现。

栈检查 -fstack-check

stack-check选项作用于编译器。它会在每个栈空间最低层部分设置一个安全缓冲区,如果函数中申请的栈空间进入了该区域,就会触发异常。对应的英文资料如下:

Generate code to verify that you do not go beyond the boundary of the stack. You should specify this flag if you are running in an environment with multiple threads, but you only rarely need to specify it in a single-threaded environment since stack overflow is automatically detected on nearly all systems if there is only one stack.
Note that this switch does not actually cause checking to be done; the operating system or the language runtime must do that. The switch causes generation of code to ensure that they see the stack being extended.

整数溢出检查 -ftrapv

编译选项,使用了它之后,在执行有符号整数间的加减乘运算时,不是通过CPU的指令,而是用包含了GCC附属库的libgcc.c里面的函数来实现。对性能影响比较大。

忽略栈指针-fomit-frame-pointer和-fno-omit-frame-pointer

如果在编译时指定了-fno-omit-frame-pointer,那么就没有帧指针了,所以也就无法进行栈回溯了,默认有帧指针。backtrace

常用告警选项

-w 选项:

禁止了所有的告警(完全不推荐使用)

-Werror选项:

把所有的警告标识为错误。

-Werror= ** 选项:

把指定的警告标识为错误。

- Wno-error= **选项:

取消指定的警告为错误。

- Wchar-subscripts 选项:

对数组下标为char类型给出警告。这是一个常见的错误。因为大多数char类型为有符号数。

- Wfatal-errors 选项:

编译器遇到第一个错误时,就停止下来,不继续检查更多错误。
我不建议使用, 每次编译就出一个错误,多浪费时间。

-Wreturn-type 选项:

Warn whenever a function is defined with a return type that defaults to int.
当函数定义了非void类型的返回值类型但是没有return时,给出警告。
当函数定义中,没有控制所有的返回值路径时,也会给出警告。

-Wunused选项:

定义了变量不使用,一个函数声明为静态但不定义,函数有返回值,调用了函数不使用(想要避免,加void强转一下), 等等等等, 都会给警告。具体包含了以下这些:

-Wunused-but-set-parameter
-Wunused-but-set-variable
-Wunused-const-variable
-Wunused-const-variable=
-Wunused-dummy-argument
-Wunused-function
-Wunused-label
-Wunused-local-typedefs
-Wunused-macros
-Wunused-parameter
-Wunused-result
-Wunused-value
-Wunused-variable

-Wuninitialized 选项:

变量不初始化就使用, 给警告。
c++类的能成员变量为非静态的引用或者const变量, 如果不使用构造函数进行初始化, 给警告。

Warn if an automatic variable is used without first being initialized. In C++,
warn if a non-static reference or non-static const member appears in a class
without constructors.

Wsign-compare 选项:

对有符号数与无符号数的比较,给出警告。

-Wall 选项:

它打开了一大堆常用的编译告警选项,能覆盖大部分的需求了。

-Waddress
-Warray-bounds=1 (only with ‘-O2’)
-Wbool-compare
-Wbool-operation
-Wc++11-compat -Wc++14-compat
-Wcatch-value (C++ and Objective-C++ only)
-Wchar-subscripts
-Wcomment
-Wduplicate-decl-specifier (C and Objective-C only)
-Wenum-compare (in C/ObjC; this is on by default in C++)
-Wenum-conversion in C/ObjC;
-Wformat
-Wformat-overflow
-Wformat-truncation
-Wint-in-bool-context
-Wimplicit (C and Objective-C only)
-Wimplicit-int (C and Objective-C only)
-Wimplicit-function-declaration (C and Objective-C only)
-Winit-self (only for C++)
-Wlogical-not-parentheses
-Wmain (only for C/ObjC and unless ‘-ffreestanding’)
-Wmaybe-uninitialized
-Wmemset-elt-size
-Wmemset-transposed-args
-Wmisleading-indentation (only for C/C++)
-Wmissing-attributes
-Wmissing-braces (only for C/ObjC)
-Wmultistatement-macros
-Wnarrowing (only for C++)
-Wnonnull
-Wnonnull-compare
-Wopenmp-simd
-Wparentheses
-Wpessimizing-move (only for C++)
-Wpointer-sign
-Wreorder
-Wrestrict
-Wreturn-type
-Wsequence-point
-Wsign-compare (only in C++)
-Wsizeof-pointer-div
-Wsizeof-pointer-memaccess
-Wstrict-aliasing
-Wstrict-overflow=1
-Wswitch
-Wtautological-compare
-Wtrigraphs
-Wuninitialized
-Wunknown-pragmas
-Wunused-function
-Wunused-label
-Wunused-value
-Wunused-variable
-Wvolatile-register-var
-Wzero-length-bounds

-Wextra选项(原为-W, 已经弃用了):

它会打开-Wall选项没有打开的一些编译警告,包括:

-Wclobbered
-Wcast-function-type
-Wdeprecated-copy (C++ only)
-Wempty-body
-Wignored-qualifiers
-Wimplicit-fallthrough=3
-Wmissing-field-initializers
-Wmissing-parameter-type (C only)
-Wold-style-declaration (C only)
-Woverride-init
-Wsign-compare (C only)
-Wstring-compare
-Wredundant-move (only for C++)
-Wtype-limits
-Wuninitialized
-Wshift-negative-value (in C++03 and in C99 and newer)
-Wunused-parameter (only with ‘-Wunused’ or ‘-Wall’)
-Wunused-but-set-parameter (only with ‘-Wunused’ or ‘-Wall’)

 -Wl

表示编译器将后面的参数传递给链接器ld。

-Wl,-rpath-link=./lib

-Wl,-rapth='${ORIGIN}'/lib//ORIGIN elf文件位置,so就是so的位置所在目录

 --enable-new-dtags //-rpath 产生DL_RUNPATH 建议用这个,可以被LD_LIBRARY_PATH覆盖
 --disable-new-dtags
 //-rpath 产生DL_RPATH
 

编译时链接库需要分为两类: 直接引用 间接引用

  • 直接引用 被源码中直接调用的库
  • 间接引用 被调用库的依赖库
  • -lxxx 指定具体的库名称,编译时需要显式指定直接引用的库名称
  • -L 指定链接库的位置,编译时需要显式指定直接引用的库位置
  • -Wl,-rpath-link ,用于编译时指定间接引用的库位置
    如果知道所有间接引用的库文件名称,并且不嫌麻烦,也可以用-lxxx显式指定每一个库(不推荐-lxxx)
  • -Wl,-rpath ,有两个作用:
    1. 用于编译时指定间接引用的库位置,作用同-Wl,-rpath-link
    2. 用于运行时指定所有引用库的位置,作用同修改环境变量(LD_LIBRARY_PATH),并且库路径引用优先级高于LD_LIBRARY_PATH
  • 使用建议
    1. 编译命令中使用-Wl,-rpath-link 指定间接引用库位置(编译时),使用-Wl,-rpath 指定引用库位置(运行时)
    2. -Wl,-rpath-link 在 -Wl,-rpath 前

指定搜索路径的其他方法:

1 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:my_lib_dir


/etc下三个ld.so的条目

$ls /etc/ | grep ld.so
ld.so.cache
ld.so.conf
ld.so.conf.d

ld.so.conf.d是在ld.so.conf中include的

$cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf

/usr/local/lib

2 /etc/ld.so.cache中缓存了动态库路径,可以通过修改配置文件/etc/ld.so.conf中指定的动态库搜索路径,然后执行ldconfig命令来改变。

3 放到系统默认搜索路径下,找到缺少的动态库,将其加到/lib,/usr/lib中的一个文件夹下, 系统默认的搜索路径。将库文件放置在其中,运行时就可以搜索到了。

顺序
Unless loading object has RUNPATH:
    RPATH of the loading object,
        then the RPATH of its loader (unless it has a RUNPATH), ...,
        until the end of the chain, which is either the executable
        or an object loaded by dlopen
    Unless executable has RUNPATH:
        RPATH of the executable
LD_LIBRARY_PATH
RUNPATH of the loading object
ld.so.cache
default dirs
 类似资料: