链接器脚本问题(未定义对“puts”的引用)

Problem with linker script (undefined reference to `puts')

提问人:GeoMldr 提问时间:2/18/2021 更新时间:2/18/2021 访问量:766

问:

所以,这是我的问题。

我一直在尝试使用 ld 脚本将 .asm 与 .c 文件链接,但我得到的只是 hello.c:(.text+0xa):对“puts”的未定义引用

我的文件如下:

bootinit.asm:


    global main
    extern zain

    KERNEL_STACK_SIZE equ 4096                  ; size of stack in bytes

        section .bss
        align 4                                     ; align at 4 bytes
        kernel_stack:                               ; label points to beginning of memory
            resb KERNEL_STACK_SIZE                  ; reserve stack for the kernel
            mov esp, kernel_stack + KERNEL_STACK_SIZE   ; point esp to the start of the
                                                    ; stack (end of memory area)

            
    section .text
    main:
        mov ecx,'A'
        mov edx, 1
        int 0x80
        call zain 
        ret 

hello.c:


    #include <stdio.h>

    void zain(){
            printf("Hello! \n\n");

            
    }

链接器.ld:


    ENTRY(main)

    MEMORY {
        bootsector(rwx) : ORIGIN = 0x70000, LENGTH = 50k 
    }

makefile:

    ARCH_PREFIX:=i386
    LINKER=linker.ld

    LDFLAGS = \
        -T$(LINKER)

    C_FILES := $(shell find ./ -type f -name '*.c' | sort)
    ASM_FILES := $(shell find ./ -type f -name '*.asm' | sort)
    OBJ := $(ASM_FILES:.asm=.o) $(C_FILES:.c=.o)


    all:  bootFinal.bin 

    bootFinal.bin: bootinit.elf
        objcopy --binary-architecture=i386:x86-64  --remove-section=.comment $< $@

    bootinit.elf: $(OBJ)
        ld  -melf_x86_64 -T$(LINKER) $(OBJ)  -o $@ 

    %.o: %.asm
        nasm $< -f elf64  -o $@

    %.o: %.c
        gcc -I/usr/include  -m64 -libc  -Wall -Wextra -Werror  -c -o $@ $< 
        

.c 和 .asm 可以毫无问题地链接,如果我运行

gcc -m64 $(OBJ) -o $@

而不是相应的 ld 命令,但这没有考虑 linker.ld 。

我尝试了 -lc 选项,但它也不起作用。

我使用 Fedora 33 如果它起到任何作用。

有什么建议吗?

程序集 gcc 链接 链接器错误 ld

评论

0赞 fuz 2/18/2021
-libc是错误的(正确的应该在命令的末尾,但编译器无论如何都会隐式附加它),并且在与 一起使用时无效。您需要在链接期间添加所有相关库,而不是在编译期间添加。-lc-c
0赞 fuz 2/18/2021
此外,整个事情没有任何意义。操作系统为您提供了一个堆栈,无需自己制作堆栈。你尝试用于此目的的代码将不起作用(你不能把代码放进去,即使你可以,你也必须跳到那里去执行它,而你没有)。kernel_stack.bss
0赞 fuz 2/18/2021
真的,我不明白所有的努力是为了什么。不需要链接器脚本。您无需分配堆栈。无需手动调用链接器。让 C 编译器处理所有这些。将每个文件组装成一个对象,然后使用 C 编译器将它们全部链接到二进制文件中。
0赞 GeoMldr 2/18/2021
谢谢你的建议。同时使用 -libc 和 -c 是我最终根据我到目前为止在这里读到的内容尝试几乎随机解决方案的结果
1赞 Peter Cordes 2/18/2021
mov esp, kernel_stack + KERNEL_STACK_SIZE在 BSS ,没有任何东西会运行它。(无论如何,BSS 中都不能有非零字节;NASM 会警告您这一点)。为什么你称它为KERNEL堆栈?你把它链接到一个用户空间程序中,以便在 Linux 下运行,对吧?这就是您使用 printf / put 等 C 库函数的原因。

答:

1赞 fuz 2/18/2021 #1

要将汇编文件集成到普通的 C 程序中,只需编写汇编代码并将其正常链接到程序中即可。例如,您可以简单地执行以下操作

main.asm

        global  main:function
        extern  zain
        section .text
main:   push    rbp
        mov     rbp, rsp
        call    zain
        pop     rbp
        ret

你好.c

#include <stdio.h>

void zain(){
        printf("Hello! \n\n"); 
}

然后像这样组装、编译和链接:

nasm -felf64 main.asm
cc -o hello main.o hello.c

这将做正确的事情,你最终会得到一个有效的二进制文件。无需编写链接器脚本、初始化代码、堆栈设置代码,甚至无需自己调用链接器。让 C 编译器处理所有这些。

需要注意的是,每当调用 C 函数时,都需要满足 AMD64 SysV ABI。这不仅规定了哪些参数进入哪些寄存器,而且还告诉您在函数调用时堆栈指针必须与 16 的倍数对齐。出于这个原因,使用了序言。它在堆栈上推送 8 个字节,以恢复在启动代码推送返回地址 时丢失的对齐方式。push rbp; mov rbp, rspmain