今天我学习了汇编语言和许多指令,比如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语言
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创建的。无论如何传统上,无论出于什么原因,你都可以在谷歌上搜索。代码(基本上是指令)在一个名为.text的段中。预初始化的数据是.data,未初始化的数据为.bss。gnu在名称、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
...
这些都是对象转储,请注意,对于从偏移量零开始的输出,每个段都有自己的数据块。还要注意,这是反汇编程序,因此它试图将数据反汇编为指令,这很令人困惑。
这里的想法是隔离这些不同的数据/信息类型,以便控制它们的去向。例如,在微控制器中,您可能在一个地址空间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,这对我来说是一个新的名称,但无论哪种方式,我都只是查看对象并匹配链接器脚本中的内容,然后控制它们的去向。
最后一个主要内容:再一次是一个标签,你已经看到编译函数的函数名基本上是一个用标签表示的地址。该函数/子例程的入口点。您的C程序不进入main(),在二进制文件中有在main之前运行的引导程序代码,然后该代码调用main。当您使用gcc hello_world.c -o hello_world
时,需要进行预处理、编译到程序集、组装到对象并使用默认链接器和C库引导程序进行链接,以生成特定于目标(操作系统)的二进制文件,这样您就可以运行它./hello_world
。
你遇到的代码可能是以这种方式链接和运行的(即使你发布的内容不起作用)。或者作者只是习惯于使用main这个词,就像我们这些编写C程序的人一样。即使是编译成汇编语言的C代码,就工具而言,它也只是另一个标签。C的引导程序将专门对其进行外部调用,因此当所有内容都被链接时,其中一个对象中需要有一个main(),但您可以在不使用名为main()的函数的情况下构建二进制文件,如果您掌握了这些工具,它就可以工作。至少对于gnu。由于某些原因,其他工具可能需要该函数名称。