汇编什么是数据、.main 和 arr?

Assembly what are data, .main and arr?

今天学习了汇编语言和很多指令,比如J JAL BNE等等... 但是突然间我看到了以下内容:

data

arr: .word 2, 4, 6, 8

n: .word 9

.text

main:   add     t0, x0, x0

             addi    t1, x0, 1

             la      t3, n

             lw      t3, 0(t3)

fib:       beq     t3, x0, finish

             add     t2, t1, t0

             mv      t0, t1

             mv      t1, t2

             addi    t3, t3, -1

             j       fib

有人可以举例说明这些数据、arr、n 和.text 是什么吗?这些是汇编语言的一部分吗?为什么我们需要 main?这不是 C 语言

是的,都是汇编语言。请注意,汇编语言是由工具而不是目标定义的。因此,用于 risc-v 的 gnu 汇编器(gas)可能与其他用于 risc-v 的汇编器不同。大多数差异将出现在其他内容中,即说明以外的内容,但有时说明也会从一种工具更改为另一种工具。唯一不变的是机器代码,如果汇编程序可以生成正确的指令,汇编语言很容易看起来像这样

add banana, orange

但无论如何。

unsigned int fun ( unsigned int a, unsigned int b )
{
    return(a+b+7);
}

我目前为该目标使用的 gcc 编译器是从该代码生成的。

    .file   "so.c"
    .option nopic
    .attribute arch, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
    .attribute unaligned_access, 0
    .attribute stack_align, 16
    .text
    .align  1
    .globl  fun
    .type   fun, @function
fun:
    addi    a1,a1,7
    add a0,a1,a0
    ret
    .size   fun, .-fun
    .ident  "GCC: (GNU) 10.2.0"

其中一些事情有些明显,有些则不那么明显,实际上需要很少的东西才能为这个目标和工具制作一个有用的对象

    .globl  fun
fun:
    addi    a1,a1,7
    add a0,a1,a0
    ret

fun: 简单来说就是一个标签,也就是一个地址。链接器将对象放在一起以制作可用程序时,对标签进行分类,并根据需要将它们转换为固定地址或相对地址。这是一种省力的设备。否则

00000000 <skip-0x8>:
   0:   c501                    beqz    x10,8 <skip>
   2:   0001                    nop
   4:   0001                    nop
   6:   0001                    nop

00000008 <skip>:
   8:   00c58533            add x10,x11,x12


00000000 <skip-0xc>:
   0:   c511                    beqz    x10,c <skip>
   2:   0001                    nop
   4:   0001                    nop
   6:   0001                    nop
   8:   0001                    nop
   a:   0001                    nop

0000000c <skip>:
   c:   00c58533            add x10,x11,x12

你可以自己查的指令的编码,基本上包括到目的地的距离,如果没有标签,我们将不得不自己计算指令的数量,并且在汇编语言中以某种方式指示向前跳转3个半字, 向后跳转10个半字。

Gnu 汇编程序对此有语法。

nop
nop
nop
beqz x10,.+4
nop
nop
nop
nop
nop
nop


00000000 <.text>:
   0:   0001                    nop
   2:   0001                    nop
   4:   0001                    nop
   6:   c111                    beqz    x10,a <.text+0xa>
   8:   0001                    nop
   a:   0001                    nop
   c:   0001                    nop
   e:   0001                    nop
  10:   0001                    nop
  12:   0001                    nop

但是您通常真的不想这样做,就好像您在分支指令和目标之间更改 instructions/bytes 的数量一样,您必须不断调整偏移量的 some/many。

所以标签是地址,在 gnu 汇编器中它们以冒号结尾并且不使用保留字。一些汇编语言不使用冒号。

细分:

int mybss;
int mydata=5;
int text ( void )
{
    mybss=3;
    return(++mydata);
}


Disassembly of section .text:

00000000 <text>:
   0:   000007b7            lui x15,0x0
   4:   0007a503            lw  x10,0(x15) # 0 <text>
   8:   00000737            lui x14,0x0
   c:   468d                    li  x13,3
   e:   0505                    addi    x10,x10,1
  10:   00d72023            sw  x13,0(x14) # 0 <text>
  14:   00a7a023            sw  x10,0(x15)
  18:   8082                    ret

Disassembly of section .sbss:

00000000 <mybss>:
   0:   0000                    unimp
    ...

Disassembly of section .sdata:

00000000 <mydata>:
   0:   0005                    c.nop   1
    ...

嗯,这很有趣,那是 gnu gcc 创建的。反正。传统上无论出于何种原因,您都可以 google。代码(基本上是说明)位于名为 .text 的段中。预初始化数据为.data,未初始化数据为.bss。好吧gnu使用name,text,data,bss前面的点。

Gnu 汇编器有一些快捷方式

.text
nop
.data
.word 1,2,3

但是完整的语法是

.section .text

.section .data

你可以随意编造任何东西,我假设它没有以某种方式保留:

.section .hello
    nop
    add x11,x12,x13
    j .
.word 0xA,0xBBBB

.section .world

mystuff: .word 1,2,3,4

Disassembly of section .hello:

00000000 <.hello>:
   0:   0001                    nop
   2:   00d605b3            add x11,x12,x13
   6:   a001                    j   6 <.hello+0x6>
   8:   000a                    c.slli  x0,0x2
   a:   0000                    unimp
   c:   0000bbbb            0xbbbb

Disassembly of section .world:

00000000 <mystuff>:
   0:   0001                    nop
   2:   0000                    unimp
   4:   0002                    c.slli64    x0
   6:   0000                    unimp
   8:   00000003            lb  x0,0(x0) # 0 <mystuff>
   c:   0004                    0x4
    ...

这些是对象转储,注意每个段如何有自己的数据块,因为此输出从偏移量零开始。另请注意,这是反汇编程序,因此它试图将数据反汇编为令人困惑的指令。

这里的想法是隔离这些不同的 data/information 类型,以便您可以控制它们的去向。例如,在微控制器中,您可能在一个地址 space 0x00000000 处有闪存,而在另一个地址 0x20000000 处可能有 sram,因此您希望从 read/write 数据中隔离只读代码和只读数据,以便您可以告诉链接器把东西放在哪里。

int mybss;
int mydata=5;
const int myrodata = 25;
int text ( void )
{
    mybss=3;
    return(++mydata);
}

您可以在链接器命令行上或使用特定于工具、链接器而不是目标通用的链接器脚本将点连接到链接器。与汇编语言一样,不期望代码将逐字移植到其他工具链。

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)       } > bob
    .rodata : { *(.srodata*)    } > bob
    .data   : { *(.sdata*)      } > ted
    .bss    : { *(.sbss*)       } > ted
}

显然不是实际可用的二进制文件,但工具不知道它们只是按照我的要求做了

Disassembly of section .text:

00000000 <text>:
   0:   200007b7            lui x15,0x20000
   4:   0007a503            lw  x10,0(x15) # 20000000 <mydata>
   8:   20000737            lui x14,0x20000
   c:   468d                    li  x13,3
   e:   0505                    addi    x10,x10,1
  10:   00d72223            sw  x13,4(x14) # 20000004 <mybss>
  14:   00a7a023            sw  x10,0(x15)
  18:   8082                    ret

Disassembly of section .rodata:

0000001c <myrodata>:
  1c:   0019                    c.nop   6
    ...

Disassembly of section .data:

20000000 <mydata>:
20000000:   0005
    ...

Disassembly of section .bss:

20000004 <mybss>:
20000004:   0000
    ...

在我提到 bob 和 ted 的地方,大多数人会使用 rom 和 ram 或其他更有用的名称。在左边我有 .text 和 .data 的地方你也可以在那里做东西,需要与将读取这个二进制文件的工具匹配并寻找外部工具想要看到的某些关键字。但是使用 gnu 链接器你可以弥补这些。中间的名称虽然需要匹配对象中的名称,所以我不知道为什么它是 .sdata 而不是 .data,这对我来说是今天的新名称,但无论哪种方式,我只是简单地查看对象并匹配链接描述文件中的内容,然后控制它们的去向。

最后是main:又是一个标签,你已经看到编译后的函数,函数名基本上是一个地址,用标签表示。 function/subroutine 的入口点。您的 C 程序不会在 main() 处输入,在 运行s 的二进制文件中有 运行s 在 main 之前的 bootstrap 代码,然后该代码调用 main。当您使用 gcc hello_world.c -o hello_world 时,会进行预处理、编译为汇编、汇编为对象并与默认链接器和 C 库链接 bootstrap 以生成特定于目标(操作系统)的二进制文件,以便您可以然后 运行 它 ./hello_world.

您遇到的代码可能有意(即使您发布的内容不起作用)以这种方式进行链接和 运行。或者作者只是习惯于使用 main 这个词,因为我们必须编写 C 程序。即使编译成汇编语言的 C 代码,就工具而言,它也只是另一个标签。 C 的 bootstrap 将专门对其进行外部调用,因此当所有内容都链接时,其中一个对象中需要有一个 main() ,但是您可以构建二进制文件而不使用名为 main() 的函数并拥有它如果您掌握了工具,就可以工作。至少在 gnu 中。出于某种原因,其他工具可能需要该函数名称。