提问人:Virgil G. 提问时间:9/21/2023 最后编辑:Peter CordesVirgil G. 更新时间:9/25/2023 访问量:63
在 NASM 中链接 Linux 共享库时出错 - “将R_X86_64_PC32重新寻址为符号”foo“”
Error linking a Linux shared library in NASM - 'readdressing R_X86_64_PC32 to symbol "foo"'
问:
我目前正在重新编码 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 函数同名的问题是什么?但是,我在没有标准库的情况下进行编译。
答:
使用 global strlen:function hidden
而不是全局 strlen
使符号对链接器可见,以便其他对象文件可以引用它,但不会从我们正在创建的共享对象中导出,因此它不会参与符号插入。.so
或者,为了允许在库内进行有效调用,但仍然将符号导出到外部,请在同一位置定义第二个标签,例如并使内部标签成为标签,而另一个标签只是普通的非隐藏标签。我不知道有更好的方法可以做到这一点,要调用 visibility=default 符号,请仅转到此共享对象中的定义。strlen_internal: strlen:
hidden
global strlen:function
ld
全局 strlen
使符号完全全局,参与符号插入。链接器不允许您使用 a 引用该符号,因为在动态链接时使用的实际定义可能位于不同的共享库中(例如,因为您使用了标准 C 函数的名称),并且它可能加载到距离此共享库超过 +- 2GiB 的地方。call rel32
libc.so
在 32 位代码中,rel32 可以到达虚拟地址空间中的任何位置,因此“重新寻址”(如文本重定位)可以处理它,只需警告需要在只读页面 (in ) 中重写机器代码。.text
在这种情况下,如果动态链接器已在其他库中看到过一个版本,则不希望让库调用更快的版本。你的依赖于你的自定义行为,而 ABI 不需要(例如,不修改 RDI 指针参数,以便在找到长度后可用,而无需保存/恢复堆栈上的任何内容)。strlen
putstr
strlen
Your 也与编译器生成的代码不兼容;它修改调用方的 RBX,它是标准 x86-64 调用约定中的调用保留寄存器。但是,即使从调用它,这也是一个问题,因为它最终会返回给 C 调用者。首先只需使用 AX,而不是稍后复制到返回值的单独寄存器。或者算作 RDX,因为这个私有版本的 仅作为 的助手存在。(也许也可以叫它另一个名字。strlen
putstr
strlen
putstr
顺便说一句,你的也只是普通的坏了;它不会将 RDI 复制到 RSI 以使 arg 到 ,并且实际上使系统调用不是。(系统调用号与标准文件描述符匹配,例如 对于 x86-64 Linux,这是记住它们的有用选择。想必你会注意到,如果你不是在共享库中尝试这个,这样你就可以让它链接到可执行文件中工作。或者,如果你把你的放在和它一样的地方,而不是.putstr
void*
write
read
write
__NR_write = STDOUT_FD = 1
unistd_64.h
strlen
.asm
putstr
global
相关:
https://nasm.us/doc/nasmdoc8.html#section-8.9.5 - 指令的扩展记录了这一点。
elf
GLOBAL
在另一个对象文件中调用函数而不在共享库中使用 PLT?GAS 版本的相同问题。在 GAS 中,你会做 和 .而IIRC,
.globl strlen
.hidden strlen
.type strlen, @function
无法从汇编 (yasm) 代码调用 64 位 Linux 上的 C 标准库函数 - 调用其他共享库中的函数的三种方法:PLT、样式或使非 PIE 可执行文件,以便链接器通过 PLT 为您间接发明。链接器底部的示例“放松”了从间接/PLT 到直接的调用,提到了 NASM 的标志。
gcc -fno-plt
hidden
NASM Linux 共享对象错误:针对“.data”的重定位R_X86_64_32S还提到了该指令的 NASM 标志。
hidden
global
评论
strlen_internal:
strlen:
call [rel strlen wrt ..got]
评论