32

这是我的汇编级代码...

section .text
global _start
_start: mov eax, 4
        mov ebx, 1
        mov ecx, mesg
        mov edx, size
        int 0x80
exit:   mov eax, 1
        int 0x80
section .data
mesg    db      'KingKong',0xa
size    equ     $-mesg

输出:

root@bt:~/Arena# nasm -f elf a.asm -o a.o
root@bt:~/Arena# ld -o out a.o
root@bt:~/Arena# ./out 
KingKong

我的问题是全局 _start 用于什么?我用Mr.Google试了试运气,发现它是用来告诉我程序的起点的。为什么我们不能_start像下面给出的那样告诉程序从哪里开始,它会在屏幕上产生一种警告

section .text
_start: mov eax, 4
        mov ebx, 1
        mov ecx, mesg
        mov edx, size
        int 0x80
exit:   mov eax, 1
        int 0x80
section .data
mesg    db      'KingKong',0xa
size    equ     $-mesg

root@bt:~/Arena# nasm -f elf a.asm
root@bt:~/Arena# ld -e _start -o out a.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
root@bt:~/Arena# ld -o out a.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
4

4 回答 4

47

global指令是 NASM 特定的。它用于将代码中的符号导出到它在生成的目标代码中指向的位置。在这里,您将_start符号标记为全局,以便将其名称添加到目标代码 ( a.o) 中。链接器 ( ld) 可以读取目标代码中的符号及其值,因此它知道在输出可执行文件中标记为入口点的位置。当您运行可执行文件时,它从_start代码中标记的位置开始。

如果某个global符号缺少指令,则该符号将不会放在目标代码的导出表中,因此链接器无法知道该符号。

如果你想使用不同的入口点名称_start(默认),你可以-e为 ld 指定参数,如:

ld -e my_entry_point -o out a.o
于 2013-07-27T14:47:24.567 回答
6

_start默认 Binutils 的ld链接器脚本使用它作为入口点

我们可以看到该链接描述文件的相关部分:

 ld -verbose a.o | grep ENTRY

输出:

ENTRY(_start)

ELF 文件格式(以及我想的其他对象格式)明确说明程序将通过e_entry标头字段从哪个地址开始运行。

ENTRY(_start)告诉链接器在_start从目标文件生成 ELF 文件时将该条目设置为符号的地址。

然后当操作系统开始运行程序(Linux 上的exec系统调用)时,它会解析 ELF 文件,将可执行代码加载到内存中,并将指令指针设置为指定地址。

Sedat-e提到的标志会覆盖默认符号。_start

-T <script>您还可以使用该选项替换整个默认链接器脚本,这是一个设置一些裸机组件的具体示例

.global是一个汇编指令,在 ELF 文件中将符号标记为全局

ELF 文件包含每个符号的一些元数据,指示其可见性。

观察这一点的最简单方法是使用该nm工具。

例如在 Linux x86_64 GAS 独立的 hello world 中:

电源

.text
.global _start
_start:
asm_main_after_prologue:
    /* write */
    mov $1, %rax   /* syscall number */
    mov $1, %rdi   /* stdout */
    lea msg(%rip), %rsi  /* buffer */
    mov $len, %rdx /* len */
    syscall

    /* exit */
    mov $60, %rax   /* syscall number */
    mov $0, %rdi    /* exit status */
    syscall
msg:
    .ascii "hello\n"
    len = . - msg

GitHub 上游

编译并运行:

gcc -ffreestanding -static -nostdlib -o main.out main.S
./main.out

nm给出:

00000000006000ac T __bss_start
00000000006000ac T _edata
00000000006000b0 T _end
0000000000400078 T _start
0000000000400078 t asm_main_after_prologue
0000000000000006 a len
00000000004000a6 t msg

man nm告诉我们:

如果是小写,符号通常是本地的;如果是大写,则符号是全局的(外部的)。

所以我们看到它_global在外部是可见的(大写T),但msg我们没有标记为.global的不是(小写t)。

如果看到多个具有相同名称的全局符号,链接器就会知道如何崩溃,或者更聪明的做法是看到更多奇异的符号类型

如果我们不标记_start为全球性的,ld就会变得难过并说:

找不到入口符号 _start

于 2015-07-13T09:26:57.747 回答
5

在您将标签声明为全局之前,标签不是显式全局的,因此您必须使用 global 指令。

链接器需要全局标签“_start”,如果没有全局 _start 地址,则链接器会抱怨,因为它找不到。您没有将 _start 声明为全局,因此它在该代码模块/对象之外不可见,因此对链接器不可见。

这与 C 中的事物相反,除非您声明它们是本地的,否则事物被暗示为全局的

unsigned int hello;
int fun ( int a )
{
  return(a+1);
}

hello 和 fun 是全局的,在对象外部可见,但是这个

static unsigned int hello;
static int fun ( int a )
{
  return(a+1);
}

使它们在本地不可见。

所有本地:

_start:
hello:
fun:
more_fun:

这些现在对链接器和其他对象是全局可用的

global _start
_start:
global hello
hello:
...
于 2013-07-27T15:23:29.970 回答
1

global _start只是一个指向内存地址的标签。在 _start 的情况下,当涉及到 ELF 二进制文件时,它是用作程序启动地址的默认标签。

C语言也有main_mainmain_已知,并且由“通常”链接到的“启动代码”调用 - 如果您使用的是C语言。

希望这可以帮助。

于 2016-07-30T15:31:02.540 回答