加载警告:找不到条目符号 _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
谁能解释这个警告的含义。我正在使用nasm
和ubuntu 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()
,不接收argc
和argv
参数,也不能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
只会让人感到困惑。只有当它被传递argv
和argc
的东西调用时才调用你的函数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 for
mov' 的内存引用过多
assign.S:9:错误:mov'
assign.S:10: Error: too many memory references for
mov' 的内存引用过多
assign.S:11:错误:int'
assign.S:12: Error: too many memory references for
mov' 的操作数大小不匹配
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的主要内容,如果未能解决你的问题,请参考以下文章