我目前正在重新编写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

以下是生成文件:

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函数同名是不是有问题?但是,我在没有标准库的情况下进行编译.

推荐答案

Use 100 instead of 101使符号对链接器可见,以便其他对象文件可以引用它,但not是从我们正在创建的.so共享对象导出的,因此它不参与符号插入.

或者,为了允许在库内高效调用,但仍然将符号导出到外部,请在同一位置定义第二个标签,如strlen_internal: strlen:,并使内部标签hidden但另一个标签只是普通的global strlen:function不隐藏.我不知道有什么更好的方法可以做到这一点,要让ld调用Visibility=Default符号,只需转到这个共享对象中的定义.


Your 100 makes the symbol fully global, participating in symbol interposition.链接器不允许您使用call rel32引用该符号,因为在动态链接时使用的实际定义可能在不同的共享库中(例如libc.so,因为您使用的是标准C函数的名称),并且它可能从该共享库加载超过+-2GiB.

在32位代码中,rel32可以到达虚拟地址空间中的任何地方,因此"重新寻址"(类似于文本重定位)可以处理它,只需要警告需要在只读页中重写机器代码(以.text为单位).


在这种情况下,如果动态链接器已经在不同的库中看到了更快的strlen版本,那么您不希望让库调用更快的版本.putstr依赖于ABI不需要的strlen的自定义行为(例如,不修改RDI指针参数,以便在找到长度后可以使用它,而不保存/恢复堆栈上的任何内容).

您的strlen也与编译器生成的代码不兼容;它修改了调用方的RBX,这是标准x86-64调用约定中的调用保留寄存器.但这是一个问题,即使它是从putstr调用的,因为它最终会返回给C调用者.首先只需使用Rax,而不是稍后复制到返回值中的单独寄存器.或者计入RDX,因为这个私有版本的strlen只作为putstr的帮助器存在.(或许也可以换个不同的名字.)

你的putstr也是完全坏掉的,顺便说一句;它不会从void*arg到write复制RDI到RSI,而实际上是进行read而不是write的系统调用.(系统调用号与标准文件描述符匹配,例如,对于x86-64 Linux,这是一个在unistd_64.h中的__NR_write = STDOUT_FD = 1,对于记住它们是一个很有帮助的 Select .)想必您会注意到,如果您try 不在共享库中这样做,那么您可以将其链接到可执行文件中.或者如果你把你的strlenputstr放在相同的.asm里,而不是global.


相关:

C++相关问答推荐

是否有任何情况(特定类型/值),类型双关在所有符合标准的C实现中产生相同的行为?

字符数组,字符指针,在一种情况下工作,但在另一种情况下不工作?

如何在IF语句中正确使用0.0

如何使fputs功能提示错误输入并要求用户重新输入.程序停止而不是请求新的输入

如何在C中通过套接字自定义数据类型读取原始变量?

如何知道我是否从非阻塞套接字读取所有内容

CSAPP微型shell 实验室:卡在sigprocmask

为什么函数是按照定义的顺序执行的,而不是按照从avr-c中的int main()调用的顺序执行的?

如何使用FSeek和文件流指针在C中查找文件的前一个元素和前一个减go 一个元素

是否定义了此函数的行为?

使用ld将目标文件链接到C标准库

用C++构建和使用DLL的困惑

C中的空指针是什么(_N)?

当读取可能会阻塞管道中的父进程时,为什么要等待子进程?

C: NULL>;NULL总是false?

C 程序不显示任何输出,但它接受 CS50 Lab1 的输入问题

无法将字符串文字分配给 C 中的字符数组

使用 c 中的 write() 函数将非 ASCII 字符写入标准输出

窗口消息处理函数以某种方式更改了应保持不变的 int 变量的值

设置具有非零终止字符串的大整数