assembly 程序集中的_start符号是否可以替换为其他单词?

eyh26e7m  于 2023-05-23  发布在  其他
关注(0)|答案(2)|浏览(132)

两天前我开始学习组装,我在网上找不到这些问题,如果你能帮助我,我会很高兴。我了解到程序的起始点必须指定为global _start。我有两个问题。首先,在我见过的所有代码中,global _start部分都写在***文本部分***部分内。是否可以将global _start部分写在 * 文本部分 * 之外?我的第二个问题是,global _start中的_start部分可以更改吗?因此,如果我键入global _asdglobal qwe来定义程序的起始点,我会得到语法错误吗?
注意:我目前使用的是Linux Ubuntu。我使用***nasm***tool作为汇编器,***ld***作为链接器。

ubof19bj

ubof19bj1#

这是一个gnul ld问题,不是nasm。当ld链接时,它正在寻找要标记为入口点的符号。你的问题是模糊的目标,但说明nasm表示x86,当然Linux并不模糊。
因此,由于您正在加载从Linux等操作系统构建的程序,因此入口点是至关重要的,除非您以某种方式操作二进制文件或以某种方式向链接器指示您的入口点。你的程序将无法正常运行,很可能只是崩溃,如果程序没有按照正确的顺序执行,你不能只是跳到程序的中间并希望成功,更不用说尝试以.data或其他非代码开头执行。
现在,正如在评论中提到的(请对评论投赞成票),如果你不想使用_start标签,你可以改变入口点标签。如果你没有指定_start,ld会给予一个警告并继续,但是如果你没有给它另一个标签,那么你就有进入错误位置的风险。
例如,如果这是微控制器的裸机,那么你没有操作系统将程序加载到存储器中并输入你指定的二进制文件中的任何地方,而是由硬件/逻辑管理,并且必须符合其规则并制作代码、链接器脚本、命令行等以生成二进制文件以匹配逻辑指定的入口点。在这种情况下,您可以不使用_start,而是采用默认的LD在其输出二进制文件中放置的任何内容,然后在某个时候用于对MCU中的闪存/ROM进行编程(从二进制文件中剥离所有这些知识,包括入口点)。
我不是很确定nasm,但假设你总是在某个部分,所以标签会降落在某个地方。如果它不在.text节中,并且您正在使用它作为入口点(默认情况下,不指定其他内容)。即使它是.text节声明之前的最后一行,链接器也会将该标签与其他标签一起放置在它所到达的节中,因此因为它在文件中正好在.text声明之前而不是之后,它可能会到达一个与源文件中的代码完全不同的地址。
在一些例子中,使用gnu工具,问题是特定于ld的,所以目标和汇编器在这里不一定重要。

MEMORY
{
    one   : ORIGIN = 0x1000, LENGTH = 0x1000
    two   : ORIGIN = 0x2000, LENGTH = 0x1000
    three : ORIGIN = 0x3000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > one
    .data   : { *(.data*)   } > two
    .bss    : { *(.bss*)    } > three
}

.globl _start
_start:
    nop

构建和使用readelf

Entry point address:               0x1000

现在如果我

.globl here
here:
    nop

.globl _start
_start:
    nop

.globl there
there:
    nop

00001000 <here>:
    1000:   e1a00000    nop         ; (mov r0, r0)

00001004 <_start>:
    1004:   e1a00000    nop         ; (mov r0, r0)

00001008 <there>:
    1008:   e1a00000    nop         ; (mov r0, r0)

  Entry point address:               0x1000

这可能会让人困惑。。但我们继续吧

arm-linux-gnueabi-ld -nostdlib -nostartfiles -e _start -T so.ld so.o -o so.elf

  Entry point address:               0x1004

或者相反

ENTRY(_start)
MEMORY
{
    one   : ORIGIN = 0x1000, LENGTH = 0x1000
...

  Entry point address:               0x1004

但我也可以这样做:

.globl here
    here:
        nop
    
        nop
    
    .globl there
    there:
        nop

ENTRY(there)
MEMORY
{
    one   : ORIGIN = 0x1000, LENGTH = 0x1000

  Entry point address:               0x1008

注意链接器没有警告_start
如果我现在从链接器脚本中删除ENTRY()。

Entry point address:               0x1000

但如果我这样做:

arm-none-eabi-ld so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000008000

这意味着没有链接器脚本,因此它将使用默认值,然后它将查找它。我们可以自己做

ENTRY(_start)
MEMORY
{

但没有defined _start全局标签

arm-linux-gnueabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000

所以如果你只是

nasm stuff myprog.asm stuff myprog.o
ld myprog.o -o myprog

您正在使用工具/环境的任何默认链接器设置/脚本,并且它可能具有ENTRY(_start)或等效值作为默认值。如果你完全控制了链接器,并且你想将一个程序加载到Linux中,那么你需要一个安全/合理的入口点来让程序工作,否则ld默认为二进制文件的开头或.text的开头,我们可以测试:

SECTIONS
{
    .text   : { *(.text*)   } > two
    .data   : { *(.data*)   } > one
    .bss    : { *(.bss*)    } > three
}

.globl here
here:
    nop

.data
.word 0x12345678

arm-linux-gnueabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000002000

Disassembly of section .text:

00002000 <here>:
    2000:   e1a00000    nop         ; (mov r0, r0)

Disassembly of section .data:

00001000 <.data>:
    1000:   12345678

所以.text的开头不是二进制文件中的开头或第一个地址空间

ENTRY(somedata)
MEMORY
{
    one   : ORIGIN = 0x1000, LENGTH = 0x1000
    two   : ORIGIN = 0x2000, LENGTH = 0x1000
    three : ORIGIN = 0x3000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > two
    .data   : { *(.data*)   } > one
    .bss    : { *(.bss*)    } > three
}

.globl here
here:
    nop

.data
.globl somedata
somedata: .word 0x12345678

  Entry point address:               0x1000

这对于nasm和ld来说是微不足道的,正如上面用gas和ld所证明的那样。这表明_start实际上并不比main()对ld(甚至gcc)更有魔力。_start看起来/感觉很神奇,因为默认的链接器脚本会调用它,所以人们认为它很神奇。main()是有魔力的,因为语言是这样定义它的,但实际上是 Bootstrap 使它如此,如果你只是简单地

gcc helloworld.c -o helloworld

您将获得默认的 Bootstrap 和链接器脚本。但是你可以创建自己的 Bootstrap 或者修改你的C库中的 Bootstrap 并使用它,而不是在你的程序中使用main(),工具不会在意它会正常工作。(当然不是所有的工具,因为有些工具确实会检测main()并添加通常不会添加的关键内容,特别是对于C++)。但是,gnu工具特别灵活和通用,这使得它们可以用于许多目标,从裸机到内核驱动程序到操作系统应用程序。
使用你拥有的工具,它们非常强大,先做上面这样的实验。

qvtsj1bj

qvtsj1bj2#

我了解到程序的起始点必须指定为global _start
不,那是错的!我们可以为起始点设置任何名称,而不是**_start**
是否可以将global _start部分写在文本部分之外?
好耶!
global _start中的_start部分可以更改吗?因此,如果我输入global _asd或global qwe来定义程序的起始点,我会得到一个语法错误吗?
是的,它可以改变,你不会得到任何错误,但需要指定的名称从CLI的起点,而链接。
ld -e starting_point_name app.o -o app

相关问题