函数
这里在文件 power.s 中编写一个名为 power 的函数,该函数实现的功能是计算某个数字的幂,我们给这个函数 两个参数:某个参数 x 和指数 n。
以下是完整的程序
# 目的:展示函数如何工作的程序# 本程序将计算 2^3+5^2# 主程序中的所有内容都存储在寄存器中,因此数据段不含任何内容.section .data.section .text.global _start_start:pushl $3 # 压入第二个参数pushl $2 # 压入第一个参数call power # 调用 power 函数addl $8, %esp # 将指针向后移动 8 个字节:因为3和2为int类型4字节,所以一共8字节pushl %eax # 在调用下一个函数之前,保存第一个答案pushl $2 # 压入第二个参数pushl $5 # 压入第一个参数call power # 调用 power 函数add $8, %esp # 将栈指针向后移动popl %ebx # 第二个答案已经在 %eax 中。我们之前已经将第一个答案存储到栈中,# 所以现在可以将其弹出到 %ebxadd %eax, %ebx # 将两者相加结果在 %ebx 中movl $1, %eax # 退出(返回 %ebx)int $0x80# 目的:本函数用于计算一个函数的幂# 输入:第一个参数-底数# 第二个参数-底数的指数# 输出:以返回值的形式给出结果# 注意:指数必须大于等于1# 变量:%ebx-保存底数 %ecx-保存指数# -4(%ebp)-保存当前结果# %eax 用于暂时存储.type power, @functionpower:pushl %ebp # 保留旧基址指针movl %esp, %ebp # 将基址指针设为栈指针subl $4, %esp # 为本地存储保留空间movl 8(%ebp), %ebx # 将第一个参数放入 %eaxmovl 12(%ebp), %ecx # 将第二个参数放入 %ecxmovl %ebx, -4(%ebp) # 存储当前结果power_loop_start:cmpl $1, %ecx # 如果是 1 次方,那么我们已经获得结果je end_powermovl -4(%ebp), %eax # 将当前结果移入 %eaximull %ebx, %eax # 将当前结果与底数相乘movl %eax, -4(%ebp) # 保存当前结果decl %ecx # 指数递减jmp power_loop_start # 为递减后的指数进行幂计算end_power:movl -4(%ebp), %eax # 将返回值移入 %eaxmovl %ebp, %esp # 恢复栈指针popl %ebp # 恢复基址指针ret
将上述代码编译运行
as power.s -o power.o --32ld power.o -o power -m elf_i386
使用 echo $? 查看该程序最后的返回值,即 23 + 52 的大小,结果为 33 ,说明程序运行正确

注意:因为我们上面使用了 pushl 和 popl 所以需要的是 32 位的环境,汇编和链接程序的时候就需要为 32 位模式,故在命令后添加参数指明程序环境。
使用GDB调试代码
下面逐步剖一下,上面是怎么和寄存器配合实现相关的功能的,首先使用
as power.s -o power.o --gstabs --32ld power.o -o power -m elf_i386
在命令中添加 —gstabs 添加调试信息,随后开始调试。
输入
gdb power 进入调试界面
在命令的开始 _start 处打断点:
输入
run 命令开始执行程序
输入
step 命令单步调试程序
输入
info registers 查看寄存器状态

另外,使用命令 print 加上指定寄存器也可以显示寄存器的值:
查看内存地址值

gdb 查看内存数据,格式为
x/nfu Address
- n 表示要显示的内存单元的个数,比如:20
- f 表示显示方式, 可取如下值:
- x 按十六进制格式显示变量。
- d 按十进制格式显示变量。
- u 按十进制格式显示无符号整型。
- o 按八进制格式显示变量。
- t 按二进制格式显示变量。
- a 按十六进制格式显示变量。
- i 指令地址格式
- c 按字符格式显示变量。
- f 按浮点数格式显示变量。
- u 表示一个地址单元的长度:
- b表示单字节,
- h表示双字节,
- w表示四字节,
- g表示八字节

