如何在Linux上更改解释器路径并将命令行参数传递给“可执行”共享库?

混合使用:

这是“可执行”共享库(假定文件名:)的最小示例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将如下所示:

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

现在的问题是:
如何更改上面的程序以将命令行参数传递给.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!

如果目标是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

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

tux3:

如何更改上面的程序以将命令行参数传递给.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

该代码将参数从堆栈复制到适当的寄存器中,然后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);
}

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

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

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

如果目标是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);
}

并且有两个不同的开始文件。
一种用于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

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

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

对于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这些定义可自动选择正确的解释器,以及根据目标编译库的两种略有不同的方式。

因此,如果我们真的想一直走下去,剩下的就是创建一个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

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何将命令行参数传递给从可执行脚本启动的NodeJS

获取 bash 命令的输出并将其作为命令行参数传递给 ac 可执行文件(管道)

如何在命令行中将参数传递给SoapUI?

如何在命令行中将参数传递给testng?

将命令行参数传递给javaws(Java WebStart)可执行文件

是否可以在命令行中将输入参数传递给可执行文件

Windows上的Julia。如何将命令行选项传递给可执行文件

如何获取传递给UNIX / Linux系统上正在运行的进程的命令行参数?

与CMake交叉编译:如何排除默认qt库并将其不传递给链接器的命令行

如果将目录路径作为命令行参数传递给python,则执行哪个python脚本?

如何更改命令行可执行文件?

是否可以通过命令行参数传递给“执行启动器”操作?

如何在命令行中将参数传递给文件并使用python更改文件中的字符串?

将命令行参数传递给电子可执行文件(安装已打包的应用程序后)

是否可以确定将哪些命令行参数传递给Windows可执行文件?

将多个命令行参数传递给带有文本文件的可执行文件

如何将命令行参数传递给堆栈执行

在没有脚本文件的情况下将命令行参数传递给python解释器

如何将命令行参数传递给Deno?

如何访问传递给Go程序的命令行参数?

如何将命令行参数传递给GHCi

如何将命令行参数传递给gnuplot?

如何将命令行参数传递给sed?

如何从命令行将参数传递给主管

如何在gradle中将命令行参数传递给主类?

如何在Artifactory Maven插件中将-X和-B等命令行参数传递给rtMavenRun?

如何在Android / NDK上将命令行参数从gradlew.bat传递给Clang

如何在命令行中将参数数组传递给cURL?

如何在shell脚本中将变量作为参数传递给Bigquery命令行