1、创建文件
连接主机...
连接主机成功
Last failed login: Sun Sep 24 11:07:55 CST 2023 from 218.92.0.93 on ssh:notty
There were 52583 failed login attempts since the last successful login.
Last login: Thu Sep 14 20:21:45 2023 from 123.127.3.78
[root@VM-8-6-centos ~]# cd /weilin/linux
-bash: cd: /weilin/linux: 没有那个文件或目录
[root@VM-8-6-centos ~]# cd /weilin/Linux
[root@VM-8-6-centos Linux]# vim main.c
检查main.c
文件
2、汇编
使用 gcc -S
将.c
文件编译成汇编文件.s
[root@VM-8-6-centos Linux]# gcc -S main.c -o main.s
[root@VM-8-6-centos Linux]# cat main.s
.file "main.c"
.text
.globl g
.type g, @function
g:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $3, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size g, .-g
.globl f
.type f, @function
f:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $8, %rsp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %edi
call g
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size f, .-f
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $8, %edi
call f
addl $1, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
.section .note.GNU-stack,"",@progbits
[root@VM-8-6-centos Linux]#
可以看到main.s
中有多行以.
开头的代码
这里使用sed
命令删掉以.
开头的行
sed -i '/[.]/d' main.s
3、删除多余代码
可以看到删除成功
4、分析
通过对比不难发现,汇编和C是一一对应的:g、f、main这三个函数一一对应。
g:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $3, %eax
popq %rbp
ret
f:
pushq %rbp
movq %rsp, %rbp
subq $8, %rsp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %edi
call g
leave
ret
main:
pushq %rbp
movq %rsp, %rbp
movl $8, %edi
call f
addl $1, %eax
popq %rbp
ret
下面是具体分析:
- 函数g:
在栈上保存旧的基址(pushq %rbp)和设置新的基址(movq %rsp, %rbp),以便在函数结束时恢复栈的状态。
将传入的参数edi存储到位于rbp-4的局部变量中(movl %edi, -4(%rbp))。
从rbp-4处加载值到寄存器eax(movl -4(%rbp), %eax)。
将寄存器eax中的值增加3(addl $3, %eax)。
恢复栈的状态并返回(popq %rbp和ret)。 - 函数f:
在栈上保存旧的基址(pushq %rbp)和设置新的基址(movq %rsp, %rbp)。
在栈上为局部变量分配空间(subq $8, %rsp)。
将传入的参数edi存储到位于rbp-4的局部变量中(movl %edi, -4(%rbp))。
从rbp-4处加载值到寄存器eax(movl -4(%rbp), %eax)。
将寄存器eax中的值移动到寄存器edi中(movl %eax, %edi)。
调用函数g(call g)。
清理栈的空间(leave)并返回(ret)。 - 函数main:
在栈上保存旧的基址(pushq %rbp)和设置新的基址(movq %rsp, %rbp)。
将值8存储到寄存器edi中,作为参数传递给函数f(movl $8, %edi)。
调用函数f(call f)。
将寄存器eax中的值增加1(addl $1, %eax)。
恢复栈的状态并返回(popq %rbp和ret)。
5、Arm架构下的汇编结果
.arch armv7-a
.syntax unified
.text
.global g
g:
push {lr}
add r3, r0, #3
mov r0, r3
pop {pc}
.global f
f:
push {lr}
sub sp, sp, #4
str r0, [sp]
ldr r0, [sp]
bl g
add sp, sp, #4
pop {pc}
.global main
main:
push {lr}
mov r0, #8
bl f
add r0, r0, #1
mov r7, #1
mov r2, r0
mov r1, r7
mov r0, #4
svc 0x00000000
pop {pc}
逐行分析代码
.arch armv7-a
.syntax unified
.text
.global g
g:
push {lr} ; 将链接寄存器lr的值保存在栈上
add r3, r0, #3 ; 将r0寄存器的值加上3,结果存储在r3寄存器中
mov r0, r3 ; 将r3寄存器的值复制到r0寄存器中
pop {pc} ; 从栈中恢复链接寄存器lr的值,然后跳转到lr寄存器所指示的地址
.global f
f:
push {lr} ; 将链接寄存器lr的值保存在栈上
sub sp, sp, #4 ; 将栈指针sp减去4个字节,为局部变量分配空间
str r0, [sp] ; 将r0寄存器的值存储到栈内存中
ldr r0, [sp] ; 从栈内存中加载值到r0寄存器中
bl g ; 调用函数g(通过跳转并将返回地址保存在链接寄存器中)
add sp, sp, #4 ; 恢复栈指针sp的值,释放局部变量所占空间
pop {pc} ; 从栈中恢复链接寄存器lr的值,然后跳转到lr寄存器所指示的地址
.global main
main:
push {lr} ; 将链接寄存器lr的值保存在栈上
mov r0, #8 ; 将8存储到r0寄存器中,作为参数传递给函数f
bl f ; 调用函数f(通过跳转并将返回地址保存在链接寄存器中)
add r0, r0, #1 ; 将r0寄存器的值加上1
mov r7, #1 ; 将1存储到系统调用号寄存器r7中(表示退出程序)
mov r2, r0 ; 将r0寄存器的值复制到r2寄存器中
mov r1, r7 ; 将r7寄存器的值复制到r1寄存器中
mov r0, #4 ; 将4存储到r0寄存器中,表示系统调用号为4(write)
svc 0x00000000 ; 触发系统调用,退出程序
pop {pc} ; 从栈中恢复链接寄存器lr的值,然后跳转到lr寄存器所指示的地址