加载警告:找不到入口符号_start

load warning: cannot find entry symbol _start

我正在学习汇编编程。下面是打印 'Hello, World!' 的简单程序。虽然程序运行完美,但我在 loading

时收到警告消息

ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080

这是代码:

section .data
    msg db 'Hello, world!', 0xa
    len equ $ - msg

section .text
    global main

main:

    mov ebx, 1
    mov ecx, msg
    mov edx, len
    mov eax, 4
    int 0x80

    mov eax, 1
    int 0x80

谁能解释一下这个警告的意思。我正在使用 nasmubuntu 14

使用标签 _start 而不是 main 作为 ELF 入口点。 main 暗示它就像 C main 函数,但这甚至不是函数(例如 you can't ret)。


你没有说,但从错误消息和代码我假设你正在使用 nasm -felf32 hello32.asm && ld -melf_i386 -o hello32 hello32.o

构建你的 32 位代码

(如果您实际上正在构建 64 位代码,那么您很幸运它恰好可以工作,但是一旦您使用 esp 而不是 rsp 进行任何操作,它就会崩溃。 )

错误消息来自 ld,而不是来自 nasm。它在消息中说的很对。 Tim 的评论是正确的:ld 在它 link 的文件中查找 _start 符号,但如果找不到,则将入口点设置为文本段的开头。

您定义的其他 global/external 符号并不重要。 main 在这里完全没有关系,可以指向任何你想要的地方。它只对反汇编输出和类似的东西有用。如果您删除 global main / main: 行,或将它们更改为任何其他名称,您的代码将完全相同。


将其标记为 main 是不明智的,因为 ELF 入口点不是函数。它是 not main(),并且不接收 argcargv 参数,并且不能 ret 因为 ESP 指向argc 而不是 return 地址。


如果您 link 使用 gcc / glibc 的 CRT 启动代码查找 main 符号并在初始化 libc 后调用它,则仅使用 main。 (所以像 printf 这样的函数可以工作。技术上动态的 linker 钩子让 libc 在你的 _start 之前初始化自己,如果你 linked 它,但通常不要这样做,除非你确切地理解你的正在做)。相关:Assembling 32-bit binaries on a 64-bit system (GNU toolchain)

例如gcc -m32 -no-pie -o hello main.o 如果你定义一个 main:
而不是 gcc -m32 -static -nostdlib -o hello start.o
(相当于你的 ld)。

(在过去的几年里,Linux 发行版有 configured GCC with -pie as the default,它需要 position-independent 代码。但是在没有 RIP-relative 寻址的 32 位模式下,这真的很不方便(例如,查看 GCC asm 输出),这意味着 ld 不会为您将 call printf 转换为 call printf@plt。因此对于遵循大多数教程的大多数 hand-written asm,您需要传统的 non-PIE 可执行文件,因此不需要文本重定位。)

我建议您 link 使用 gcc 而不是 ld.

您的目标文件(无论它们是如何生成的)

gcc 将使用适当的选项调用 ld,因为它对源代码了解更多,并且将为 ld 所做的假设创建任何必要的东西。

你可以尝试用nasm[=15=编译汇编源文件,生成*.o文件,然后使用ld link带参数的*.o文件-e main.表示指定main为程序入口

您应该使用 _start 来指示 nasm 汇编程序应该从哪里开始执行,而不是 main。 敌人例如:

section .text
global _start
_start:
mov ebx, 1
mov ecx, msg
mov edx, len
mov eax, 4
int 0x80
mov eax, 1
int 0x80

你的程序有问题,比如 一些语法错误,例如您不能将寄存器值分配给常量,因为常量不能保存任何值,为了存储常量值,我们使用变量

在组装你的程序时我遇到下面提到的组装时间错误

没有这样的指令:msg db 72ello,world!440xa' assign.S:3: Error: no such instruction:len equ $ - msg' assign.S:4: 错误: 没有这样的指令: section .text' assign.S:5: Error: no such instruction:global main' assign.S:7: 错误:mov' assign.S:8: Error: too many memory references formov' 的内存引用过多 assign.S:9: 错误:mov' assign.S:10: Error: too many memory references formov 的内存引用过多' assign.S:11: 错误:int' assign.S:12: Error: too many memory references formov' 的操作数大小不匹配 assign.S:13: 错误:'int' 的操作数大小不匹配

这是在使用 32 位英特尔处理器的 gnu 编译器上为您提供相同输出的代码

.section .rodata 消息: .string "Hello World"

    .section .text
    .globl  main
    .type   main,@function

main:
    pushl   $msgp
    call    printf
    addl    ,%esp

    pushl   [=10=]
    call    exit

用最近取的名字保存此代码Hello.S 与 $ as -o Hello.o Hello.S link 与 $ ld -o Hello.o -lc -dynamic-linker /lib.ld.linux.so.2 -e main -Hello.o 至 运行 $ ./你好

希望对您有所帮助

要编译和执行您的程序,您可以创建 bash 脚本如下:

compile64.sh

!/bin//bash
echo "Assembling with Nasm"
nasm -f elf64 -o .o .asm
echo "Linking ... "
gcc -o  .o
echo "Done !"

$ ./compile64 nameOftheFile  (without extension)

我不知道这是否是一个有效的修复,但似乎对我有用:

尝试使用选项

--entry main

链接您的内核 C 代码时。

ld -o kernel.bin -Ttext 0x1000 kernel_entry.o kernel.o --oformat binary --entry main