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

Error linking a Linux shared library in NASM - 'readdressing R_X86_64_PC32 to symbol "foo"'

提问人:Virgil G. 提问时间:9/21/2023 最后编辑:Peter CordesVirgil G. 更新时间:9/25/2023 访问量:63

问:

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

斯特伦

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

普斯特:

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 程序集 共享库 x86-64 NASM

评论

0赞 Some programmer dude 9/21/2023
请阅读有关为 64 位系统构建的 nasm 文档,阅读其中关于位置无关代码的内容。
1赞 Some programmer dude 9/21/2023
你在汇编程序中这样做的原因是什么?你的实际问题是什么?你的实际任务?你的实际目标是什么?
0赞 فِرجِيل 9/21/2023
@Someprogrammerdude我这样做是为了练习(但这不是重点),我的问题是我在共享库模式下出现编译错误。我会阅读这个文档,谢谢。

答:

2赞 Peter Cordes 9/25/2023 #1

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

或者,为了允许在库内进行有效调用,但仍然将符号导出到外部,请在同一位置定义第二个标签,例如并使内部标签成为标签,而另一个标签只是普通的非隐藏标签。我不知道有更好的方法可以做到这一点,要调用 visibility=default 符号,请仅转到此共享对象中的定义。strlen_internal: strlen:hiddenglobal strlen:functionld


全局 strlen 使符号完全全局,参与符号插入。链接器不允许您使用 a 引用该符号,因为在动态链接时使用的实际定义可能位于不同的共享库中(例如,因为您使用了标准 C 函数的名称),并且它可能加载到距离此共享库超过 +- 2GiB 的地方。call rel32libc.so

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


在这种情况下,如果动态链接器已在其他库中看到过一个版本,则不希望让库调用更快的版本。你的依赖于你的自定义行为,而 ABI 不需要(例如,不修改 RDI 指针参数,以便在找到长度后可用,而无需保存/恢复堆栈上的任何内容)。strlenputstrstrlen

Your 也与编译器生成的代码不兼容;它修改调用方的 RBX,它是标准 x86-64 调用约定中的调用保留寄存器。但是,即使从调用它,这也是一个问题,因为它最终会返回给 C 调用者。首先只需使用 AX,而不是稍后复制到返回值的单独寄存器。或者算作 RDX,因为这个私有版本的 仅作为 的助手存在。(也许也可以叫它另一个名字。strlenputstrstrlenputstr

顺便说一句,你的也只是普通的坏了;它不会将 RDI 复制到 RSI 以使 arg 到 ,并且实际上使系统调用不是。(系统调用号与标准文件描述符匹配,例如 对于 x86-64 Linux,这是记住它们的有用选择。想必你会注意到,如果你不是在共享库中尝试这个,这样你就可以让它链接到可执行文件中工作。或者,如果你把你的放在和它一样的地方,而不是.putstrvoid*writereadwrite__NR_write = STDOUT_FD = 1unistd_64.hstrlen.asmputstrglobal


相关:

评论

0赞 فِرجِيل 9/25/2023
感谢您的回答,您可能注意到我是 NASM x64 的初学者,所以我对您的回答有几个问题: 1. 由于我的项目是重新编码我的 GLIBC 版本(用于培训目的),我如何确保 strlen 可以在任何地方调用,而不仅仅是用于内部函数?2. 如何调用我自己创建的函数,这些函数也会在共享对象中被喜欢?(与 GLIBC 函数同名)。
1赞 Peter Cordes 9/25/2023
@VirgilG.:如在共享库中调用另一个对象文件中的函数而不使用 PLT 中所述?,您可以在同一位置创建两个标签:一个用于库内的调用,另一个用于导出。比如也许和.我不知道是否有更清洁的方法可以有效地做到这一点。低效的方法是,即使在库内也要通过PLT或GOT进行调用,就像从另一个共享库使用一样,如YASM /标准库Q&A所示。strlen_internal:strlen:call [rel strlen wrt ..got]
0赞 Sep Roland 9/25/2023
"...它修改了 RBX,这是一个调用中断的寄存器。这不应该更好地读作“call-preserved register(被叫方保存的寄存器)”我知道这很大程度上取决于整个句子的阅读和理解方式,但我一直觉得这很奇怪。
1赞 Peter Cordes 9/25/2023
@SepRoland:那只是我的脑子放屁;我已经在考虑我接下来要写什么了(选择一个电话咔嚓的寄存器)。感谢您在我的答案中发现这样的错误;我们知道我的意思,但初学者很容易混淆,因为他们还不了解这个主题,所以解决这样的事情很重要!