如何改变解释器路径并将命令行参数传递给“可执行文件". Linux上的共享库? [英] How to change interpreter path and pass command line arguments to an "executable" shared library on Linux?

查看:72
本文介绍了如何改变解释器路径并将命令行参数传递给“可执行文件". Linux上的共享库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是可执行"共享库的一个最小示例(假定文件名:mini.c):

Here is a minimal example for an "executable" shared library (assumed file name: 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);
}

如果使用例如gcc -fPIC -o mini.so -shared -Wl,-e,entry mini.c进行编译. 运行"结果.so将如下所示:

If one compiles it with e.g.: gcc -fPIC -o mini.so -shared -Wl,-e,entry mini.c. "Running" the resulting .so will look like this:

confus@confusion:~$ ./mini.so
WooFoo!

我的问题现在是:
如何更改上面的程序以将命令行参数传递给.so文件的调用?更改后的示例shell会话可能例如看起来像这样:

My question is now:
How do I have to change the above program to pass command line arguments to a call of the .so-file? An example shell session after the change might e.g. look like this:

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!

在编译时进行检测也很不错,只要目标是32位或64位二进制文​​件即可相应地更改解释器字符串.否则,将收到正在访问损坏的共享库" 警告.像这样:

It would also be nice to detect on compile time, wheter the target is a 32-Bit or 64-Bit binary to change the interpreter string accordingly. Otherwise one gets a "Accessing a corrupted shared library" warning. Something like:

#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

甚至更好的是,完全自动检测适当的路径,以确保该路径适用于在其上编译库的系统.

Or even better, to detect the appropriate path fully automatically to ensure it is right for the system the library is compiled on.

推荐答案

如何更改上面的程序以将命令行参数传递给.so文件的调用?

运行共享库时,argcargv将被传递到堆栈上的输入函数.

When you run your shared library, argc and argv will be passed to your entry function on the stack.

问题在于,在x86_64 linux上编译共享库时使用的调用约定将是

The problem is that the calling convention used when you compile your shared library on x86_64 linux is going to be that of the System V AMD64 ABI, which doesn't take arguments on the stack but in registers.

您将需要一些ASM粘合代码,该代码从堆栈中获取参数并将其放入正确的寄存器中.

You'll need some ASM glue code that fetches argument from the stack and puts them into the right registers.

这是一个简单的.asm文件,您可以将其另存为entry.asm,并只需链接以下内容即可:

Here's a simple .asm file you can save as entry.asm and just link with:

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

该代码将参数从堆栈复制到适当的寄存器中,然后以与位置无关的方式调用entry函数.

That code copies the arguments from the stack into the appropriate registers, and then calls your entry function in a position-independent way.

然后您就可以像编写main常规函数一样编写entry:

You can then just write your entry as if it was a regular main function:

// 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);
}

这就是您随后编译库的方式:

And this is how you would then compile your library:

nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o

优点是您不会在C代码中混入内联asm语句,而是将真正的入口点清晰地抽象到了起始文件中.

The advantage is that you won't have inline asm statements mixed with your C code, instead your real entry point is cleanly abstracted away in a start file.

在目标是32位或64位二进制代码以相应地更改解释器字符串时,也可以在编译时进行检测.

不幸的是,没有没有完全干净,可靠的方法.您能做的最好的事情就是依赖具有正确定义的首选编译器.

Unfortunately, there's no completely clean, reliable way to do that. The best you can do is rely on your preferred compiler having the right defines.

由于使用了GCC,因此您可以这样编写C代码:

Since you use GCC you can write your C code like this:

#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);
}

并有两个不同的开始文件.
一种用于64位:

And have two different start files.
One for 64bit:

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

一个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

这意味着您现在有两种稍微不同的方式来为每个目标编译库.

Which means you now have two slightly different ways to compile your library for each target.

对于64位:

nasm entry.asm -f elf64
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry.o -m64

对于32位:

nasm entry32.asm -f elf32
gcc -fPIC -o mini.so -shared -Wl,-e,_entry mini.c entry32.o -m32

总而言之,您现在有两个开始文件entry.asmentry32.asm,在mini.c中有一组定义,它们自动选择正确的解释器,以及两种不同的编译库的方法,具体取决于目标.

So to sum it up you now have two start files entry.asm and entry32.asm, a set of defines in your mini.c that picks the right interpreter automatically, and two slightly different ways of compiling your library depending on the target.

因此,如果我们真的想一路走下去,剩下的就是创建一个Makefile来检测正确的目标并相应地构建您的库.
让我们做到这一点:

So if we really want to go all the way, all that's left is to create a Makefile that detects the right target and builds your library accordingly.
Let's do just that:

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

我们在这里完成了.只需运行make来构建您的库并让魔术发生.

And we're done here. Just run make to build your library and let the magic happen.

这篇关于如何改变解释器路径并将命令行参数传递给“可执行文件". Linux上的共享库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆