C语言中如何获取动态库的调用方信息

2022-09-07  本文已影响0人  onmeiei

在Java中,很方便可以通过Thread的StackTrackElement来获取到方法的调用方信息。那么在C中如何获取类似的信息呢?
答案是使用backtrace系列函数。

#include <execinfo.h>

//backtrace() 返回调用程序的回溯(跟踪)信息,存储在由
//buffer指向的数组中。
int backtrace(void **buffer, int size);

//backtrace() 返回一组地址,backtrace_symbols()象征性地
//翻译这些地址为一个描述地址的字符串数组。
char **backtrace_symbols(void *const *buffer, int size);

//backtrace_symbols_fd()的参数buffer、size同backtrace_symbos(),
//不同之处在于,backtrace_symbols_fd()并不会返回一个
//字符串数组给调用者,而是将字符串写入fd对应文件。
void backtrace_symbols_fd(void *const *buffer, int size, int fd);

示例:
准备三个源文件:libB.cpp,libA.cpp,main.cpp
关系如下:

调用关系展示
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>

int funB(){
    printf("====>>>> in funB(), printing backtrace ...\n");
    void *buffer[100];
    char **strings;
    int nptrs = backtrace(buffer, 100);
    printf("\nbacktrace() returned %d addresses\n", nptrs);
    strings = backtrace_symbols(buffer, nptrs);
    int j;
    for (j = 0; j < nptrs; j++) {
      printf("%s\n", strings[j]);
    }
    free(strings);
    return 0;
}
include <stdio.h>

int funB();

int funA(){
    printf("====>>>> in funA(), calling funB\n");
    funB();
    return 0;
}
int funA();
int funB();

#include<stdio.h>

int main(){

    printf("====>>>> in main(), calling funA()\n");
    funA();

    printf("\n\n---------------------------------------------------------\n\n\n");

    printf("====>>>> in main(), calling funB()\n");
    funB();

    return 0;
}

编译运行

  1. 简单编译并运行
#!/bin/bash
# 清理
rm lib/*.so -f
rm main_* -f

# 编译
gcc libB.cpp -fPIC -shared -o lib/libB.so
gcc libA.cpp -fPIC -shared -o lib/libA.so -Wl,-rpath=./lib -L./lib -lB
gcc main.cpp -o main_plain -L./lib -Wl,--copy-dt-needed-entries -lA

# 运行
export LD_LIBRARY_PATH=./lib
./main_plain

输出为:


调用输出
  1. 使用-O3优化编译并运行
#!/bin/bash
# 清理
rm lib/*.so -f
rm main_* -f

# 编译
gcc libB.cpp -fPIC -shared -o lib/libB.so -O3
gcc libA.cpp -fPIC -shared -o lib/libA.so -Wl,-rpath=./lib -L./lib -lB -O3
gcc main.cpp -o main_o3 -L./lib -Wl,--copy-dt-needed-entries -lA -O3

# 运行
export LD_LIBRARY_PATH=./lib
./main_o3

输出为:


调用输出
  1. 使用-O3以及-fomit-frame-pointer进行编译

-fomit-frame-pointer

Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register
available in many functions. It also makes debugging impossible on some machines.

On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automatically handles the frame pointer and nothing is saved by pretending it doesn't exist.
The machine-description macro "FRAME_POINTER_REQUIRED" controls whether a target machine supports this flag.

The default setting (when not optimizing for size) for 32-bit GNU/Linux x86 and 32-bit Darwin x86 targets is -fomit-frame-pointer. You can configure GCC with the --enable-frame-pointer
configure option to change the default.

Enabled at levels -O, -O2, -O3, -Os.

#!/bin/bash
# 清理
rm lib/*.so -f
rm main_* -f

# 编译
gcc -fomit-frame-pointer libB.cpp -fPIC -shared -o lib/libB.so -O3
gcc -fomit-frame-pointer libA.cpp -fPIC -shared -o lib/libA.so -Wl,-rpath=./lib -L./lib -lB -O3
gcc -fomit-frame-pointer main.cpp -o main_o3_fomit -L./lib -Wl,--copy-dt-needed-entries -lA -O3

# 运行
export LD_LIBRARY_PATH=./lib
./main_o3_fomit

输出为:


调用输出

结论:获取的信息还是挺清晰的

如果用来保障libB.so只能被认证过的程序调用,那么可以在libB中对上一层的程序进行校验。

上一篇下一篇

猜你喜欢

热点阅读