在 NASM x64 中编译共享库时出错

问题描述 投票: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 makefile 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
。 (系统调用号与标准文件描述符匹配,例如
__NR_write = STDOUT_FD = 1
。)您可能会注意到,如果您不在共享库中尝试此操作,则可以将其链接到可执行文件中。或者,如果您将
strlen
放在与
.asm
相同的
putstr
中,并且使其不是
global


相关:

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