con*_*use 8 c linux linker compilation shared-libraries
以下是"可执行"共享库的最小示例(假定文件名:)mini.c
:
// Interpreter path is different on some systems
//+definitely different for 32-Bit machines
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#include <stdio.h>
#include <stdlib.h>
int entry() {
printf("WooFoo!\n");
exit (0);
}
Run Code Online (Sandbox Code Playgroud)
如果用例如:编译它gcc -fPIC -o mini.so -shared -Wl,-e,entry mini.c
."运行"结果.so
将如下所示:
confus@confusion:~$ ./mini.so
WooFoo!
Run Code Online (Sandbox Code Playgroud)
我现在的问题是:
如何更改上述程序以将命令行参数传递给.so
-file 的调用?更改后的示例shell会话可能如下所示:
confus@confusion:~$ ./mini.so 2 bar
1: WooFoo! bar!
2: WooFoo! bar!
confus@confusion:~$ ./mini.so 3 bla
1: WooFoo! bla!
2: WooFoo! bla!
3: WooFoo! bla!
5: WooFoo! Bar!
Run Code Online (Sandbox Code Playgroud)
在编译时检测也是很好的,目标是32位或64位二进制文件,以相应地更改解释器字符串.否则,会收到"访问已损坏的共享库"警告.就像是:
#ifdef SIXTY_FOUR_BIT
const char my_interp[] __attribute__((section(".interp"))) = "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#else
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
#endif
Run Code Online (Sandbox Code Playgroud)
或者甚至更好,完全自动检测适当的路径,以确保编译库的系统是正确的.
我如何更改上述程序以将命令行参数传递给 .so 文件的调用?
当你运行你的共享库,argc
并且argv
将被传递到堆栈上的输入功能。
问题是在 x86_64 linux 上编译共享库时使用的调用约定将是System V AMD64 ABI的调用约定,它不接受堆栈上的参数,而是寄存器中的参数。
您将需要一些 ASM 粘合代码,从堆栈中获取参数并将它们放入正确的寄存器中。
这是一个简单的 .asm 文件,您可以将其另存为 entry.asm 并链接到:
global _entry
extern entry, _GLOBAL_OFFSET_TABLE_
section .text
BITS 64
_entry:
mov rdi, [rsp]
mov rsi, rsp
add rsi, 8
call .getGOT
.getGOT:
pop rbx
add rbx,_GLOBAL_OFFSET_TABLE_+$$-.getGOT wrt ..gotpc
jmp entry wrt ..plt
Run Code Online (Sandbox Code Playgroud)
该代码将参数从堆栈复制到适当的寄存器中,然后entry
以与位置无关的方式调用您的函数。
然后,您可以entry
像编写常规main
函数一样编写您的代码:
// Interpreter path is different on some systems
//+definitely different for 32-Bit machines
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#include <stdio.h>
#include <stdlib.h>
int entry(int argc, char* argv[]) {
printf("WooFoo! Got %d args!\n", argc);
exit (0);
}
Run Code Online (Sandbox Code Playgroud)
这就是您编译库的方式:
nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o
Run Code Online (Sandbox Code Playgroud)
优点是您不会将内联 asm 语句与您的 C 代码混合在一起,而是您的真正入口点在起始文件中被清晰地抽象出来。
在编译时检测也是很好的,目标是 32 位还是 64 位二进制文件以相应地更改解释器字符串。
不幸的是,没有完全干净、可靠的方法来做到这一点。您能做的最好的事情就是依靠您的首选编译器具有正确的定义。
由于您使用 GCC,您可以像这样编写 C 代码:
#if defined(__x86_64__)
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#elif defined(__i386__)
const char my_interp[] __attribute__((section(".interp")))
= "/lib/ld-linux.so.2";
#else
#error Architecture or compiler not supported
#endif
#include <stdio.h>
#include <stdlib.h>
int entry(int argc, char* argv[]) {
printf("%d: WooFoo!\n", argc);
exit (0);
}
Run Code Online (Sandbox Code Playgroud)
并且有两个不同的启动文件。
一个 64 位:
global _entry
extern entry, _GLOBAL_OFFSET_TABLE_
section .text
BITS 64
_entry:
mov rdi, [rsp]
mov rsi, rsp
add rsi, 8
call .getGOT
.getGOT:
pop rbx
add rbx,_GLOBAL_OFFSET_TABLE_+$$-.getGOT wrt ..gotpc
jmp entry wrt ..plt
Run Code Online (Sandbox Code Playgroud)
一个是 32 位的:
global _entry
extern entry, _GLOBAL_OFFSET_TABLE_
section .text
BITS 32
_entry:
mov edi, [esp]
mov esi, esp
add esi, 4
call .getGOT
.getGOT:
pop ebx
add ebx,_GLOBAL_OFFSET_TABLE_+$$-.getGOT wrt ..gotpc
push edi
push esi
jmp entry wrt ..plt
Run Code Online (Sandbox Code Playgroud)
这意味着您现在有两种稍微不同的方法来为每个目标编译您的库。
对于 64 位:
nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o -m64
Run Code Online (Sandbox Code Playgroud)
对于 32 位:
nasm entry32.asm -f elf32
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry32.o -m32
Run Code Online (Sandbox Code Playgroud)
总而言之,您现在有两个启动文件entry.asm
和entry32.asm
一组定义,可以mini.c
自动选择正确的解释器,以及根据目标编译库的两种略有不同的方法。
因此,如果我们真的想一路走下去,剩下的就是创建一个 Makefile 来检测正确的目标并相应地构建您的库。
让我们这样做:
ARCH := $(shell getconf LONG_BIT)
all: build_$(ARCH)
build_32:
nasm entry32.asm -f elf32
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry32.o -m32
build_64:
nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o -m64
Run Code Online (Sandbox Code Playgroud)
我们已经完成了。只需运行make
即可构建您的库,让奇迹发生。