如何在 NASM x64 中测试自己的 GLIBC 实现?

How can I test my own implementation of GLIBC in NASM x64?

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

问:

我目前正在开发我的 NASM x64 版本的 GLIBC,但我在测试它时遇到了问题。ASM 代码首先以 .so 格式编译,然后通过 GCC 编译我的测试代码(C 语言),然后我通过加载我的共享库并执行我的二进制文件。LD_PRELOAD

这是我的 ASM 实现:putchar()

bits 64

STDOUT equ 1

section .text
    global putchar
    putchar:
        push rdi
        mov rax, 1
        mov rdi, STDOUT
        mov rsi, rsp
        mov rdx, 1
        syscall
        pop rdi
        ret

这是测试的主要内容:

#include <stddef.h>

extern size_t putchar(const char str);

int main(void)
{
    putchar('z');
    return (0);
}

这是 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 -g3
    LD_PRELOAD=./minilibc.so ./test/test

.PHONY: all clean fclean re

问题是当我尝试设置断点时,GDB 继续使用 glibc 函数:

(gdb) b putchar
Breakpoint 1 at 0x1030
...
Breakpoint 1, 0x00007ffff7c7bb20 in putchar () from /usr/lib/libc.so.6

我也尝试过编译(在make test中),但它不起作用:-nostlib

ld: warning: cannot find entry symbol _start; defaulting to 0000000000001020
...
/home/mimahk/Documents/Workspace C/minilibc/test/main.c:7:(.text+0xa): undefined reference to `putchar'

我知道实际上二进制文件需要一个标签才能运行,但尽管进行了多次搜索,我还是无法对其进行编码。_start

我测试了这个,但它不起作用:

asm(".global _start; _start: call main; movq %rax, %rdi; movq $60, %rax;");

我得到:main.c:9:(.text+0x19): undefined reference to putchar

你能告诉我如何测试我的共享库吗?

C 程序集 x86-64 nasm libc

评论

0赞 Severin Pappadeux 9/25/2023
stackoverflow.com/questions/53382589/ 怎么样......
0赞 Peter Cordes 9/25/2023
@SeverinPappadeux:我看不出这有什么关系,除非你的意思是 OP 应该在 asm 中手写自己的,然后从那里调用 C?在哪种情况下,如何在没有 Glibc 或 CRT 启动文件的情况下在 _start 中获取参数值并使用 C 中的内联汇编调用 main()? 是一个更好的链接。还是手动调用它们的 asm 函数?(这可能隐藏了这样一个事实,即 putchar 按值获取 char,而不是可以传递给 的指针。_startmainwrite
0赞 Peter Cordes 9/25/2023
当然,是的,他们可以通过将代码正常链接到可执行文件中来测试他们的代码,也许使用不同的函数名称,这样他们就可以在不编写完整 libc 的情况下测试一些函数。
1赞 Jester 9/25/2023
在这里工作正常:LD_PRELOADBreakpoint 1, 0x00007ffff7bd81f0 in putchar () from ./minilibc.so
1赞 Jester 9/25/2023
段错误可能是因为您没有调用 .但是 gdb 应该告诉你崩溃的位置。-nostdlibexit

答:

2赞 فِرجِيل 9/25/2023 #1

多亏了@PeterCordes和@Jester的评论,我才能够纠正这个问题。

首先,我必须修改代码,使其前面有一个名为 的 ASM 标签。main()_start

然后,我修改了 Makefile 中的几个选项,以在运行时直接链接 .so(并添加了基本的调试选项):

-g3:启用使用 GDB 进行深入调试。

-fno-builtin:删除 GCC 的自动代码替换(例如 由。printf("%s\n", str)puts(str)

-nostdlib:不包括 GLIBC。

-fno-plt:直接使用GOT而不使用PLT。

-L./ -lminilibc:指示链接器将我的可执行文件链接到库(因此应称为 libminilibc.so)。minilibc

-Wl,-rpath=./:告诉链接器我的库在运行时的位置(因为它不在 中),并省去了使用 ./usr/lib/LD_PRELOAD

完整代码如下:

main.c

extern int putchar(const char str);

asm(".global _start; _start: call main; movq %rax, %rdi; movq $60, %rax; syscall"); // Call the main() function and exit with the code corresponding to the return of main().

int main(void)
{
    putchar('H');
    return (0);
}

生成文件

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

all: $(NAME)

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

%.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 -g3 -fno-builtin -nostdlib -fno-plt -L./ -lminilibc -Wl,-rpath=./

.PHONY: all clean fclean re

评论

1赞 Peter Cordes 9/25/2023
-fnostdlib不是 GCC 选项。你是说。-nostdlib
1赞 Peter Cordes 9/25/2023
此外,在 Makefile 的命令中,不是一个选项。 是 GCC 代码生成选项; 不做代码,它只是链接。 确实存在,但表示不在标准路径中搜索您用 指定的内容。Bare不会链接任何你没有告诉它的东西,所以这并不是说它不链接东西,它只是设置搜索路径。(如果它根本不是一个选项,它将被解析为 、 设置和输出文件名。不要随意传递东西!例如 是ld-nostdlib-fPICld-fPICldld -nostdlib-lldld -n -o stdlib--nmagicld-fPIC--auxiliary=PIC)