0

今天我学习了汇编语言和许多指令,如 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 语言

4

1 回答 1

1

是的,所有这些都是汇编语言。请注意,汇编语言是由工具而不是目标定义的。因此 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

但是您通常不希望这样做,就好像您更改分支指令和目标之间的指令/字节数一样,您必须不断调整一些/许多偏移量。

所以标签是地址,在 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用了名字、文本、数据、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
    ...

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

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

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

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

于 2020-11-16T16:55:47.390 回答