加载警告:找不到条目符号 _start

Posted

技术标签:

【中文标题】加载警告:找不到条目符号 _start【英文标题】:load warning: cannot find entry symbol _start 【发布时间】:2016-04-17 23:02:10 【问题描述】:

我正在学习汇编编程。下面是打印“Hello, World!”的简单程序。当程序完美运行时,我收到警告消息,而loading

ld:警告:找不到入口符号_start;默认为 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 的入口点(它找不到,因为它不存在)。 Stack Overflow 似乎很成熟,有类似的问题,所以请四处搜索。 @TimBiegeleisen 其实我也这么认为。但我没有得到的是为什么它与main 一起工作。为什么nasm 通过警告而不是错误,如果它想要明确地start_。我用谷歌搜索了它,发现每个人都在他们的汇编代码中使用 start_ 关键字,但是为什么它使用 main 关键字。 “为什么 nasm 通过警告而不是错误”。据我所知,警告来自链接器,而不是来自 nasm。链接器找不到入口点,因此它可能默认为 .text 部分的开头。 相关:***.com/questions/10995118/… 【参考方案1】:

使用标签 _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 在它链接的文件中查找 _start 符号,但如果找不到,则将入口点设置为文本段的开头。

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


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


仅当您链接 gcc / glibc 的 CRT 启动代码时才使用 main,该代码查找 main 符号并在初始化 libc 后调用它。 (所以像 printf 这样的函数工作。技术上动态链接器钩子让 libc 在你的 _start 之前初始化自己,如果你链接它,但通常不要这样做,除非你完全理解你在做什么)。相关: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,它需要与位置无关的代码。但是在没有 RIP 相对寻址的 32 位模式下,这真的很不方便(例如,查看 GCC asm 输出),并且意味着ld 不会为您将 call printf 转换为 call printf@plt。因此,对于大多数遵循大多数教程的手写 asm,您需要传统的非 PIE 可执行文件,因此不需要文本重定位。)

【讨论】:

实际上在我使用的教程中,他们使用了main而不是start_ @Atinesh:那你应该用gcc -m32 -o hello32 hello32.o构建,而不是直接用ld,所以标准的libc启动代码首先运行,并调用你的main函数。我假设本教程的下一步将使用一些 libc 函数或其他东西(尽管您仍然可以通过与 libc 链接从 _start 执行此操作)。或者教程可能不是很好。没有充分的理由标记您的入口点main。或者如果你这样做了,你应该使用-e main 告诉链接器它是入口点,而不是在你的教程中留下一个令人困惑的警告。 32 位 elf 加载器在堆栈上传递参数和环境变量,但不是以 C main 函数所期望的方式。 0(esp) 是 argc,4(esp) 是 argv[0](程序名),然后堆栈上的每个后续值都是指向每个剩余参数的指针,后跟一个 NULL 指针,然后每个堆栈值是指向每个环境变量的指针,后跟一个 NULL 指针。之后还有其他指针和值,但超出了大多数汇编程序通常使用的范围。 您使用gcc 与crt 链接的解决方案会产生一个我不明白的警告:warning: relocation in read-only section .txt。也许我做错了什么? @mathway:你是否省略了-no-pie? (编辑:我忘了这个答案不包括-no-pie;我在想另一个)在非PIE可执行文件中,最终加载地址是链接时间常量,因此不需要运行时文本重定位。 (除非您使用仅在共享库中定义的全局变量的地址。像call printf 这样的函数调用将由链接器转换为call printf@plt,以避免文本重定位以跳转/调用共享库代码,但是访问数据需要额外的指令来从 GOT 加载一个指针,所以链接器不能这样做。)【参考方案2】:

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

尝试使用选项

--entry main

在链接您的内核 C 代码时。

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

【讨论】:

这是对不同问题的正确答案。这个问题是关于创建一个在 Linux 下运行的 Linux ELF 可执行文件,而不是一个独立的内核。正如我在回答中解释的那样,使用 --entry main 只会让人感到困惑。只有当它被传递argvargc的东西调用时才调用你的函数main,并且当它完成时它可以ret【参考方案3】:

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

compile64.sh

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

$ ./compile64 nameOftheFile  (without extension)

【讨论】:

【参考方案4】:

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

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

没有这样的指令: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    $4,%esp

    pushl   $0
    call    exit

用一些名字保存这个代码最新的Hello.S 组装 $ as -o Hello.o Hello.S 链接到 $ ld -o Hello.o -lc -dynamic-linker /lib.ld.linux.so.2 -e main -Hello.o 跑步 $ ./你好

希望对你有帮助

【讨论】:

您遇到了错误,因为提问者说他​​们使用的是nasm。您正在使用使用不同语法的 GNU 汇编器 (as)。【参考方案5】:

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

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

【讨论】:

【参考方案6】:

可以尝试用nasm编译汇编源文件,生成*.o文件,然后用ld链接带参数-e main。这意味着main被指定为程序入口。

【讨论】:

这只是令人困惑,因为main 通常用作在 C 库初始化函数完成后运行的函数的名称,因此您可以使用 printf 等等。要从 ELF 入口点执行此操作,您需要自己调用 glibc init 函数(或使用动态链接)。所以最好继续调用你的ELF入口点_start,并且只使用main作为从CRT启动代码调用的函数。【参考方案7】:

我建议您使用 gcc 而不是 ld 链接您的目标文件(无论它们是如何生成的)。

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

【讨论】:

您愿意详细说明一下吗? :)

以上是关于加载警告:找不到条目符号 _start的主要内容,如果未能解决你的问题,请参考以下文章

未知的警告来源:“找不到虚拟表的链接器符号...”

警告:找不到入口符号 Reset_Handler

Java 错误:找不到或无法加载主类

java中提示找不到符号

java里显示找不到符号?

maven 找不到或无法加载主类,找不到符号,类