在 NASM 中链接 Linux 共享库时出错 -“将 R_X86_64_PC32 重新寻址为符号“foo””

问题描述 投票:0回答:1

我目前正在 NASM x64 中重新编码标准 C 库中的一些函数。目前我只有两个功能:

strlen

bits 64

section .text
    global  strlen
        strlen:
            xor rbx, rbx
            jmp strlen_loop

        strlen_loop:
            cmp byte [rdi + rbx], 0
            je strlen_ret
            inc rbx
            jmp strlen_loop

        strlen_ret:
            mov rax, rbx
            ret

putstr

bits 64

extern strlen

STDOUT equ 1

section .text
    global putstr
        putstr:
            call strlen
            mov rdx, rax
            xor rax, rax
            mov rdi, STDOUT
            syscall
            ret

这是 makefile:

CC      =   nasm
CFLAGS  =   -f elf64
SRC     =   $(wildcard src/*.asm)
OBJ     =   $(SRC:.asm=.o)
NAME    =   minilibc.so

all: $(NAME)

$(NAME): $(OBJ)
    ld -fPIC -shared -o $(NAME) $(OBJ) -nostdlib

%.o: %.asm
    $(CC) $(CFLAGS) $< -o $@

clean:
    rm -f $(OBJ)

fclean: clean
    rm -f $(NAME)

re: fclean all

test: re
    gcc -o test/test test/main.c
    LD_PRELOAD=./minilibc.so ./test/test

.PHONY: all clean fclean re

和测试代码:

#include <stdio.h>

extern size_t putstr(const char *str);

int main(void)
{
    const char *str = "Hello, World!\n";
    putstr(str);
    return (0);
}

这是我得到的错误:

rm -f src/putstr.o src/strlen.o
rm -f minilibc.so
nasm -f elf64 src/putstr.asm -o src/putstr.o
nasm -f elf64 src/strlen.asm -o src/strlen.o
ld -fPIC -shared -o minilibc.so src/putstr.o src/strlen.o -nostdlib
ld: src/putstr.o: attention: readdress on "strlen" in read-only ".text" section
ld: src/putstr.o: readdressing R_X86_64_PC32 to symbol "strlen" cannot be used when creating a shared object; recompile with -fPIC
ld: final link edit failed: wrong value
make: *** [Makefile:10: minilibc.so] Error 1

LD似乎告诉我要在PIC模式下重新编译,我已经这样做了,那么问题是我的函数与GLIBC函数同名吗?但是,我在没有标准库的情况下进行编译。

c assembly shared-libraries x86-64 nasm
1个回答
0
投票

使用

global strlen:function hidden
而不是
global strlen
使符号对链接器可见,以便其他目标文件可以引用它,但 not 从我们正在创建的
.so
共享对象导出,因此它不参与在符号插入中。

您的

global strlen
使符号完全全局化,参与符号插入。 链接器不会让您用
call rel32
引用该符号,因为在动态链接时使用的实际定义可能位于不同的共享中库(例如
libc.so
,因为您使用了标准 C 函数的名称),并且它可能会从该共享库加载超过 +- 2GiB。

在 32 位代码中,rel32 可以到达虚拟地址空间中的任何位置,因此“重新寻址”(如文本重定位)可以通过需要在只读页面中重写机器代码的警告来处理它(在

.text
)。


在这种情况下,如果动态链接器已经在不同的库中看到了

strlen
,您不希望让您的库调用更快的版本。您的
putstr
取决于 ABI 不需要的
strlen
的自定义行为(例如,不修改 RDI 指针 arg,以便在找到长度后可以使用它,而不需要保存/恢复堆栈上的任何内容)。

您的

strlen
也与编译器生成的代码不兼容 ABI;它修改 RBX,这是一个被调用破坏的寄存器。但即使从
putstr
调用它也是一个问题,因为它最终返回到 C 调用者。只需首先使用
rax
,而不是稍后复制到返回值的单独寄存器。或者计入 RDX,因为这个私有版本的
strlen
仅作为
putstr
的助手存在。 (也许也可以叫它不同的名字。)

顺便说一句,你的

putstr
也完全坏了;它不会将
void*
arg 的 RDI 复制到
write
,并且实际上进行
read
系统调用而不是
write
。 (系统调用号与标准文件描述符匹配,例如 x86-64 Linux 的
__NR_write = STDOUT_FD = 1
中的
unistd_64.h
,这是记住它们的有用选择。)想必您会注意到,如果您不在共享库中尝试此操作这样你就可以让它工作链接到可执行文件中。或者,如果您将
strlen
放在与
.asm
相同的
putstr
中,并且使其不是
global


相关:

© www.soinside.com 2019 - 2024. All rights reserved.