汇编代码入门 ATT指令格式
计算机系统结构
博文末尾支持二维码赞赏哦 _
cpu内部:
1. PC Program Counter指令指针寄存器指向下一条指令的地址EIP(X86-32)或者RIP(X86-64)
2. 寄存器与寄存器堆Registers在处理器CPU内部以名字来访问的快速存储单元
3. 条件状态码Condition Codes用于存储最近执行指令的结果状态信息用于条件指令的判断执行
内存单元Memory:以字节编码的连续存储空间存储程序代码、数据、运行栈stack 以及操作系统数据
汇编语言数据格式
c 语言 数据类型 汇编代码后缀 大小(字节为单位)
char 字节 b byte 1
short 字 w word 2
int 双字 l 4
long int 双字 l 4
long long int 无 无 4
char* 双字 l 4
float 单精度 s 4
double 双精度 l 8
long double 扩展精度 t 10/12
第一条汇编指令实例
c 语言代码:int t = x+y // 两个整数(32位)
汇编代码:addl 8(%ebp) %eax//l表示双字 8是位偏移量操作数:x: 寄存器 Register eaxy: 内存 Memory M[ebp+8] ebp是函数栈基址寄存器t: 寄存器 Register eax结果t保存在寄存器eax中
类似于表达:x += y
或者:int eax;int* ebp;eax += ebp[2];//这里按字节
数据传送指令
movel 源地 目的地
将一个双字从源地移动到目的地
允许的操作数类型有:立即数Imm:常整数如: $0x400, $-533可以用1,2或4个字节来表示寄存器 Reg:8个通用寄存器之一%eax%ebx%ecx%edx%esi%edi%esp 栈顶%ebp 栈底存储器Mem:四个连续的字节汇编 类似C语言
立即数--->寄存器 movl $0x41, %eax temp = 0x41;
立即数--->内存 movl $-43, (%eax) *p = -43;
寄存器--->寄存器 movl %eax, %edx temp2 = temp;
寄存器--->内存 movl %eax, (%edx) *p = temp;
内存 --->寄存器 movl (%eax), %edx temp = *p;
不允许内存到内存
简单得寻址模式
1. 间接寻址 (R) Mem[Reg[R]]
寄存器R 指定得内存地址
movl (%ecx), %eax
2. 基址+偏移量 寻址 D(R) Mem[Reg[R] + D]
寄存器R 指定内存的起始地址
常数D 给出偏移地址
movl 8(%ebp), %ecx
寻址模式使用示例
交换两个数
C语言
void swap(int* xp, int* yp){
int t0 = *xp;
int t1 = *yp;
*xp = t1;
*yp = t0;
}汇编语言分析
寄存器 变量:
%ecx yp
%edx xp
%eax t1
%ebx t0
对应汇编:
movl 12(%ebp),%ecx # ecx = yp 是地址 放到ecx寄存器中
movl 8(%ebp), %edx # edx = xp 是地址 放到edx寄存器中
movl (%ecx), %eax # eax = *yp t1 值 取寄存器中地址指向的内存地址中的内容放入 寄存器eax中
movl (%edx), %ebx # ebx = *xp t0 值 取寄存器中地址指向的内存地址中的内容放入 寄存器ebx中
movl %eax, (%edx) # *xp = eax 交换内容放入原来内存指向得地址中
movl %ebx, (%ecx) # *yp = ebxebp 是函数栈 基地址
ebp+8 的位置 存储 指针xp 指向内存的一个地址
ebp+12 的位置 存储 指针yp 指向内存的一个地址
变址寻址
常见形式:D(Rb,Ri,S) Mem[Reg[Rb] + S*Reg[Ri] + D]D: 常量(地址偏移量)Rb: 基址寄存器:8个通用寄存器之一Ri: 索引寄存器: %esp不作为索引寄存器一般%ebp也不做这个用途S: 比例因子, 1,2,4,8其他变形:D(Rb,Ri) Mem[Reg[Rb] + Reg[Ri] + D](Rb,Ri) Mem[ Reg[Rb] + Reg[Ri] ](Rb,Ri) Mem[Reg[Rb] + S*Reg[Ri]]
地址计算指令 leal , lea +l
leal src, destsrc 是地址计算表达式子 D(Rb,Ri,S) ---> Reg[Rb] + S*Reg[Ri] + D计算出来得地址赋给 dest
使用实例:地址计算,无需访问内存 auto *p = &x[i];进行x+k*y这一类型得整数计算,k = 1,2,4,8
整数计算指令:
addl src,dest # dest = dest + src #加法
subl src,dest # dest = dest - src #减法
imull src,dest # dest = dest * src #乘法
sall src,dest # dest = dest << src #左移位 等价于shll
sarl src,dest # dest = dest >> src #算术右移位 补 被移动数的最高位
shrl src,dest # dest = dest >> src #逻辑右移位 左边单纯补 0
xorl src,dest # dest = dest ^ src #按位异或
andl src,dest # dest = dest & src #按位与
orl src,dest # dest = dest | src #按位或incl dest # dest = dest + 1 #++ 自增1
decl dest # dest = dest - 1 #-- 自减1
negl dest # dest = - dest # 取非
notl dest # dest = ~ dest # 取反
将leal指令用于计算
c语言:int temp(int x, int y){int t1 = x+y;int t2 = z+t1;int t3 = x+4;int t4 = y*48;int t5 = t3+t4;int ret = t2*t5return ret;}
汇编代码:movl 8(%ebp), %eax # eax = xmovl 12(%ebp), %edx # edx = yleal (%eax, %edx), %ecx # t1 = ecx = x+y addl 16(%ebp), %ecx # t2 = ecx = z+t1leal (%edx,%edx,2), %edx # edx = 3*ysall $4, %edx # t4 = edx = 2^4 * 3 * y =16*3*y = 48*yleal 4(%eax,%edx), %eax # t5 = eax =4+x+t4imul %ecx, %eax # ret = eax = t2*t5
逻辑运算示例
C语言:int logical_(int x, int y){int t1 = x^y;int t2 = t1>>17;int mask = (1<<13) - 7;int ret = t2 & mask;return ret;}
汇编语言:
movl 8(%ebp), %eax # eax = x
xorl 12(%ebp), %eax # t1 = eax = x^y
sarl $17, %eax # t2 = eax = t1 >> 12
andl $8185, %eax # ret = eax = t2 & 8185 2^13-7 = 8185 这里编译器会算出来
x86-32 与 x86-64主要类型数据宽度的区别
x86-32 x86-64long int 4 8char* 4 8 内存地址编号长度
使用 条件码(标志寄存器) 来进行控制
addl src,dest t =a+bCF Carry Flag 进位标志 可用于无符号整数运算的溢出 SF Sign Flag 符号位标志 结果<0 的话,被设置为1ZF Zero Flag 0结果标志 结果为0 的话,被设置为1OF Overflow Flag 溢出标志 if a>0 & b>0 & t<0 被设置为1
比较指令 compare 被比较数 比较数 compare b,a a-b(但是不会改变a的值)
compare b,a a-bif a == b , ZF =1if a < b , SF=1if (a>0 && b<0 && (a-b)<0)||(a<0 && b>0 && (a-b)>0) OF=1
测试指令 test 做与操作
testl s2,s1testq s2,s1计算 s1 & s2并设置相应的条件码,不改变目的操作数testl b,a 计算a&bif a&b ==0, ZF =1if a&b < 0, SF =1CF=0,OF=0
setX指令 获取条件码状态
sete 相等 结果为0 ZF =1时setne 不相等 ZF =0sets 结果<0 SF =1setns 结果>=0 SF=0
有符号数:setg 大于 setge 大于等于setl 小于setle 小于等于
无符号数:setb 小于 belowseta 大于 ablove
读取条件码实例
c语言:int gt(int x, int y){return x>y;}
汇编语言:
movl 12(%ebp), %eax # eax = y
cmpl %eax, 8(%ebp) # compare x:y x-y
setg %al # al寄存器是eax的低八位得名字 al = x>y
movzbl #al, %eax # 8位扩展到16位
跳转指令 Jx
jmp 无条件跳转je 相等jne 不相等js 结果<0jns 结果>=0有符号 jg 大于jge 大于等于jl 小于jle 小与等与无符号 ja 大于jb 小于
跳转指令 Jx 实例
c语言:int abs_(int x, int y){int rest;if(x>y){rest = x - y;}else{rest = y-x;}return rest;}
汇编语言:movl 8(%ebp), %edx # edx = xmovl 12(%ebp), %eax # eax = ycmpl %eax, %edx # x - yjle .L7 # x <=y 跳转到.L7subl %eax, %edx # x>y , 计算rest = x-ymovl %edx, %eax # 结果放在 寄存器eax指向的地址.L8:leaveret.L7:subl %edx, %eax # x <=y 跳转到.L7 计算 y-x jmp L8
循环的汇编语言表示
for循环:for(Init初始条件; 判断测试条件Test; 更新量Update)循环体Body;转换到 While-do结构:Init初始条件;while(判断测试条件Test){循环体Body;更新量Update;}再转换成go-to结构:Init初始条件;goto middle; # 无条件跳转 jmp
loop:循环体Body;更新量Update;
middle:if(判断测试条件Test)goto loop;
switch case的汇编语言格式
c语言版本:long switch_eg(long x, long y, long z){long w = 1;switch(x){case 1:w = y*z;break;case 2:w = y/z;break;case 3:w +=z;break;case 5: case 6:w -= z;break; defult: // 这里 case 0 和 case 4 和其他情况w = 2;}return w;
}汇编语言版本:pushl %ebp # 保存 寄存器老的 ebp的值
movl %esp, %ebp # 函数堆栈 栈顶指针 %esp 存放在%ebp
pushl %ebx # 保存 寄存器ebx的值movl 8(%ebp), %ebx # 函数堆栈距离栈顶 偏移8位处存放x的值
movl 12(%ebp), %eax # y
movl 16(%ebp), %ecx # z
cmpl $6, %ebx # x - 6
jbe .L11 # X <= 6 才进入正常的 case.L8: # default 部分movl $2, %eax # w = 2jmp .L12 # break.L11:jmp *.L7(,%ebx,4) %访问.L7段表 对应的 case block.section .rodata
.L7:.long .L8 # case 0 情况 进入 default.long .L3 # case 1 情况 .long .L4 # case 2 情况 .long .L9 # case 3 情况 .long .L8 # case 4 情况 进入 default .long .L6 # case 5 情况 case 6.long .L6 # case 6 情况 进入 default.L12: # breakpopl %ebx # 后进先出popl %ebp # 先进后出ret # 函数段返回.L3: # case 1imull %ecx, %eax # w = w*zjmp .L12 # break.L4: # case 2movl %eax, %edx sarl $31, %edxidivl %ecx # w =y/zaddl %ecx, %eax # case 3 w += z jmp .L12 # break.L9: # case 3movl %1, %eax # w = 1addl %ecx, %eax # w +=zjmp .L12 # break.L6: # case 6 case 5movl $1, %eax # w = 1subl %ecx, %eax # w -= Zjmp .L12 # break
x86-32的程序栈
栈---水桶结构的一块内存区域---->只有一个出口(栈底%ebp (桶口)高地址(计算机地址逆向生长))先进后出后进先出 FILO寄存器 %esp 存储栈顶地址(下部)寄存器 %ebp 存储栈底地址(上部)注意 %esp %ebp 始终指向当前正在运行的活动的函数过程栈
存储的内容
局部变量返回地址临时空间
栈帧的分配与释放
1. 进入过程 先分配栈帧空间set-up code2. 过程返回时 释放栈帧空间finish code
pushl 压栈 把大象装进水桶的操作
水面栈顶%esp会上什(步进单位为4个内存地址块)
pushl src # 从src 取得操作数# %esp = %esp-4 栈顶上升 水面上什
popl 出栈操作 把大象从水桶中取出来
水面栈顶%esp会下降
popl Dest # 读取栈顶数据(%esp) 放入 目标位置Dest# %esp = %esp+4 栈顶下降 水面下降
过程调用 call label 过程返回指令 ret
过程调用指令:
call label #将返回地址(call指令的下一条指令地址(%eip)压入栈),跳转至label继续执行
过程返回指令
ret #跳转至栈顶的返回地址
函数调用时 寄存器的使用惯例
8个寄存器:两个特殊寄存器:调用者和被调用者可能都需要保存始终指向当前活动的堆栈上两端%ebp 栈底指针%esp 栈顶指针三个由调用者保存:%eax 常用于保存过程的返回值的%ecx%edx三个由被调用者保存:%ebx %esi%edi
递归调用实例
c语言版本:
int rfact(int x){int rval;// 递归出口if(x<=1) return 1;// 递归调用rval = rfact(x-1);return rval * x;}
汇编语言版本:
调用者栈情况 前 %ebp <- %ebp 栈底前 %ebx变量x 父过程调用的参数返回地址 adr <- %esp 栈顶
# 进入过程 先分配栈帧空间
被调用者(函数rfact) 老的 %ebp 第一步需要保存%ebp pushl %ebp 指向 前%ebp <- %esp 栈顶 栈顶自动调整第二步 movl %esp, %ebp <- %ebp 栈第指针也指向了栈顶 (栈基址)老的 %ebx 第三步 pushl %ebx <- %esp 栈顶 栈顶自动调整 (用来存储函数内的变量值,之前的值需要先保存)主体代码:
movl 8(%ebp), %ebx # ebx = x
cmpl $1, %ebx # compare x : 1 , 计算 x - 1
jle .L78 # x <= 1 直接跳转到递归出口 .L78
leal -1(%ebx), %eax # eax = x-1 被调用者这里需要用到 %eax 作为过程的返回值的
pushl %eax # push x-1 入栈保存 这里用作为调用者需要保存 %eax
call rfact # rval = rfact(x-1), 函数返回值保存在 %eax中
imull %ebx, %eax # rval * x
jmp .L79 # 跳转到 done
.L 78:movl $1, %eax # return eax = 1# 返回代码 过程返回时 释放栈帧空间
.L79:movl -4(%ebp), %ebx # 复原老的 %ebx 的值movl %ebp, %esp # 栈顶指针 %esp 也指向%ebp指向的位置popl %ebp # 恢复老的 %ebp ret
x86-64的通用寄存器
x86-32 有 8个32位的寄存器8个寄存器:两个特殊寄存器:调用者和被调用者可能都需要保存始终指向当前活动的堆栈上两端%ebp 栈底指针%esp 栈顶指针三个由调用者保存:%eax 常用于保存过程的返回值的 累加器 计算操作数和存放结果数据%ecx 计数寄存器%edx 数据寄存器三个由被调用者保存:%ebx 基础寄存器 指向DS数据段的数据指针%esi 源索引%edi 目的索引
x86-64 有 16个64位的寄存器:与 x86-32兼容的8个64位的寄存器%rax 低16位也叫 %eax eax 低16位称为ax,ax的高8位称为ah,低8位称为al%rbx %ebx ebx 低16位称为bx,bx的高8位称为bh,低8位称为bl%rcx %ecx ecx 低16位称为cx,cx的高8位称为ch,低8位称为cl%rdx %edx edx 低16位称为dx,dx的高8位称为dh,低8位称为dl%rsi %esi esi 低16位称为si%rdi %edi edi 低16位称为di%rsp %esp esp 低16位称为sp%rbp %ebp ebp 低16位称为bp
新增加的8个64位的寄存器:%r8 低16位也叫 %r8d%r9 %r9d%r10 %r10d%r11 %r11d%r12 %r12d%r13 %r13d%r14 %r14d%r15 %r15d
x86-32 6个16位的段寄存器,定义内存中的段
CS 代码段 存储指令和执行的地方DS, ES, FS, GS 数据段SS 堆栈段 当前程序存储堆栈的地方
数组的表示 访问 操作
访问:
c语言:
typedef int array_i_5[5];// 定义一个长度为5的整数数组类型 array_i_5
array_i_5 cmu = {1, 2, 5, 3, 4};// 定义一个数组cmu 函数五个整形变量
cum[index];//可以获取相应索引位置处的 元素汇编语言:
# %edx = cmu 数组首地址
# %eax = index 索引
movl (%edx, %eax, 4), %eax # cmu[index] 4为一个整形类型所占据的直接数量
数组循环遍历修改
c语言:
void temp(array_i_5 z){
int i;
for (i=0; i<5; i++)z[i]++;// 数组各元素自增1
}
汇编语言:
movl $0, %eax # 循环变量 %eax = i
.L4:addl $1, (%edx, %eax, 4) # z[i]++addl $1, %eax # i++cmpl $5, %eax # compare i:5jne .L4 # i < 5 循环
多维数组的表示
int arr_i_i[row_index][col_index];
一个int类型占据4个字节地址
则 arr_i_i[y][x] 的实际地址为 arr_i_i + y*row_index*col_index + 4*x
例如一个 arrii[4][5]的二维数组,每一行占有 4*5=20个字节
那么 arrii[i][j] 的地址为 arrii + i*20 + 4*jarrii + 4*(i+4*i) + 4*j
相关汇编代码:
先计算列地址
leal 0(,%ecx,4), %edx # 4*j
leal (%eax, %eax, 4), %eax # 5 *i
movl arrii(%edx, %eax, 4), %eax # arrii + 4*j + 4*(i+4*i)
hello world程序
//helloworld.c#include <stdlib.h>#include <stdio.h>int main(){printf("Hello World\n");exit(0);return 0;//这里就不会编译了}// gcc 编译gcc -S -O2 helloworld.c-S 生成汇编代码(与系统类型相同) helloworld.s-m32 64位系统生成32位汇编-On -O2 2级编译优化// asm movl $LC0 (%esp) #设置过程调用参数 字符串首地址 放在栈顶对应的地址call _putsmovl $0, (%esp) #设置过程调用参数call _exit
标记符号
.text #代码段
.p2align 4,,15 #对齐方式 按16 = 2^4字节对齐,填充,, 0, 最大填充15个字节
.section .rdata "dr" #只读数据段
LC0:# 字符串的起始地址.ascii "Hello World\12\0"
linux 汇编命令
编译:
//gcc -S -O2 helloworld.cas -o my-object-file.o helloworld.s# -gstabs 产生带调试信息的object文件# 64位环境下添加命令行 --32 生成 32位目标文件链接成执行程序ld -o my-exe-file my-object-file.o# 64位环境下添加命令行 -m elf_i386 生成 32位目标文件
helloworld.s
.data #数据段
msg: # 字符 首地址.ascii "Hello World\n"len = .-msg # 字符长度 . 表示当前地址
.text # 代码段
.globl _start # 汇编程序入口地址,如同C语言的main函数
_start:movl $len, %edx # 字符串长度 字节数量movl $msg, %ecx # 字符串存储位置的起始地址movl $1, %ebx # 系统输出(write 系统调用1)movl $4, %eax #int $0x80 # 中断 来指向系统调用# eax存放的是系统调用的功能号# ebx 为参数 为1时 是 write 显示器输出# edx 字符串长度 字节数量# ecx 字符串存储位置的起始地址movl $0, %ebx # 系统调用(程序退出 0)movl $4, eaxint $0x80 # 中断 来指向系统调用
汇编代码入门 ATT指令格式相关推荐
- Java JVM 汇编代码入门 GitChat链接
为什么 new Integer(151)=151?我来带你们一起学习下 JVM 汇编代码吧,窥探下神奇的 Java 中间语言到底什么样子的,能帮你更深入的理解 Java. 本文包含以下内容 工具介绍 ...
- 分析一个简单的汇编代码
分析一个简单的汇编代码 部分常见的寄存器 寄存器 16位 32位 64位 累加寄存器 AX EAX RAX 基址寄存器 BX EBX RBX 计数寄存器 CX ECX RCX 数据寄存器 DX EDX ...
- 程序编码(机器级代码+汇编代码+C代码+反汇编)
[-1]相关声明 本文总结于csapp: 了解详情,或有兴趣,建议看原版书籍: [0]程序编码 GCC调用了一系列程序,将源代码转化成可执行代码的流程如下: (1)C预处理器扩展源代码,插入所有用#i ...
- 【单片机】汇编指令入门学习|单片机启动代码汇编
目录 汇编指令入门学习 [汇编指令]MSP430汇编指令 IAR下的汇编/单片机启动代码汇编 ARM 汇编&ARM 指令集 学习笔记 汇编指令入门学习 在进行汇编程序设计时,MSP430 ...
- Linux下的ATT语法(即GNU as 汇编语法)入门
学习这么长时间,一直在C语言这一层面上钻研和打拼,日积月累,很多关于C的疑惑在书本和资料中都难以找到答案.程序员是追求完美的一个种群,其头 脑中哪怕是存在一点点的思维黑洞都会让其坐卧不宁.不久前在it ...
- 寄存器(3)KDB入门+MIPS汇编及汇编代码详解
KDB入门和MIPS汇编 1. KDB 介绍及进入退出命令 1.1 KDB 介绍 1.2 进入KDB及退出KDB 2. KDB 调试 2.1 断点类 2.2 内存操作类 md 2.3 堆栈跟踪类 2. ...
- 51单片机开发系列一-51单片机开发环境搭建以及入门汇编代码
51单片机开发系列一 51单片机开发环境搭建以及入门汇编代码 象棋小子 1048272975 1. 51单片机概述 51单片机是对所有兼容Intel 8031指令系统的单片机的统称.目前教科书基 ...
- ATT与Intel汇编代码格式
ATT(根据"AT&T"命名的,AT&T是运营贝尔实验室多年的公司)格式汇编代码,是GCC.OBJDUMP和其他一些工具的默认格式.其他一些编程工具,包括Micro ...
- 郁金香汇编代码注入怎么写看雪_汇编语言入门五:流程控制(一)
回顾 前面说到过这样几个内容: 几条简单的汇编指令 寄存器 内存访问 对应到C语言的学习过程中,无非就是这样几个内容: 超级简单的运算 变量 好了,到这里,我们继续接下来的话题,程序中的流程控制. 文 ...
最新文章
- 树的高度从零还是一开始数_数据结构与算法之1——树与二叉树
- 网络最大流的三种基础算法
- 三个球数求最大值c语言,C语言中一个简单的球3个数最大数的程序中,最后一步:printf(apos;apos;max=%d\napos;apos;,max);怎么理解...
- Uva 11077 Find the Permutation
- Spanning-tree Potocol(整理)
- [Android] Implementation vs API dependency
- Linux基础:find命令总结
- iPhone第二季度在美销量降23%,但iPhone SE有亮点
- mysql hang分析_mysql hang
- CSAPP-C1-计算机系统漫游
- Pandas基础(一)——Pandas基础
- iOS 开发的9个超有用小技巧
- 可视化大屏设计尺寸_大屏数据可视化设计规律
- 聚类分析与判别分析十题_数学建模系列
- 2022年最新BIM计费标准,涉及14省市
- (个人笔记)EDEM耦合Recurdyn流程
- 系统测试总结报告模板
- 【noip模拟题】华尔街的秘密
- 网站底部添加公安备案HTML代码
- js获取浏览器默认语言设置并自动跳转