程序的机器级表示
2014-08-18 16:05
351 查看
1, 操作数提示符:
三种:立即数:常数值, 在ATT格式的汇编代码中, 书写格式是 $ + 整数, 如:$-123 $0X12
寄存器:如:32位的%eax 16位的%ax 8位的%al
存储器引用:
Imm(Eb) 表示地址为:Eb+Imm
Imm(Eb, Ei) 地址为:Eb(基址)+ Ei(变址) + Imm(立即数偏移),
Imm(Eb, Ei, s) 地址为: Eb + Ei * s + Imm
2, 字节传送指令:
mov 同等传送, 即俩者的大小一致 (如:movb byte, movew word, movel longword(DW))movs/ movz 不同等传送, 即俩者的大小可以不一致, 如:movsbl 表示将源byte传送到目的longword, 用1 或0补充剩余位(取决于源byte的最高位是否为1), movzbl 与movsbl相似, 用0补充剩余位
push & pop
3, 特殊指令:
lea: 直接上例:lea 4(%eax) %ebx //表示 %ebx 里面的值为%eax + 4 。 而 mov 4(%eax) %ebx //表示 %ebx里面的值为【eax+4】这个地址的值
其实, 复杂的来说, lea 表示获取源地址的偏移地址, 对于上例来说, 偏移地址当然是%eax + 4 咯。
int fun (unsigned x) { int val = 0; while (x) { val ^= x; x >>= 1; } return val & 0X1; }这个函数是测试x二进制里面1的个数的奇偶, 奇返回1, 偶返回0;
理解, 第一着重于移位操作, 每次移位 都使前一位移动至后一位(即使得n位与n-1位之间的运算成为可能), 第二 着重于异或操作,对于1bit ^ 1bit来说, 恰好是查看这俩者的1的总个数是否为偶, 第三着重于第一位, 每次移位操作 都使得前一位都能跟当前第一位进行异或操作, 而这一位的结果是这一位上的1的个数是否为偶。loop, 测试前前一位。。。。。 again and again until it ends.
int fun (unsigned x) { int val = 0; int i; for (i = 0; i< 32; i++) { val = (val <<1) | (x & 0x1); x >>= 1; } return val; }这是获取x的位反转制造出来的镜像
4, switch指令较条件跳转的优越性
条件跳转, 在asm中, 是以类似cmp + jne 等指令实现的。 对于N种情况来说, 就意味着要N对cmp-jne这些指令。switch对于条件转换来说, 是利用了跳转表来进行的(其实就是地址偏移),例如情况为100-106, 即只需要计算当前值-100 的大小(k), 跳转到jt【k】所指的位置即可。
其实这个也说明了某种问题, 也就是对于疏散的情况集合, 对于跳转表的使用会不会添加空白无用的区域浪费呢?
to be continued.......
5, Union的作用:
类似struct可以存放数据, 不同在于, Union所有数据共用内存, 即Union的大小为数据中大小最大的, 而不是大小之和。用处: 类型转换。。。注意小端法读取数据
6, 为什么要数据对齐:
条件:1,平台获取数据都是以固定字节数获取的。 2,有些数据不规则,如:struct{int a, char c;}A; 其标准大小是5Bytes导致的情况: 比如固定字节数为8, 不对齐数据就意味着第一次读取, 获得 A1 以及A2的部分数据, 为了读取A2的值, 必须读取两次,以及需要进行高低位的修补。。。。
所以数据对齐, 是一种以空间换时间的策略。
这也是为什么 sizeof(A)!= 5 而是8(test in VS2013)。
测试:
struct A{ int a; char c; int b; }; void testSizeOf() { printf("size of A is %d\n", sizeof(A)); A k = { 1, 'c', 2 }; printf("A:\na: %d\nc: %c\nb: %d\n\n", k.a, k.c, k.b); printf("A:\na: %x\nc: %x\nb: %x\n\n", k, k, k); //其中%x表示十六进制整数。 科普:\x 表示其后面两位为十六进制数,常用于printf cout等。 } 结果为: size of A is 12 A: a: 1 c: c b: 2 A: a: 1 c: cccccc63 // 63是 'c'的十六进制ASCII字符表示形式 b: 2
表明:
1, 对于结构体k来说, 执行printf多次输出,其过程类似于文件流读取(读即前进)。
2, 结构体A中, c占用的字节为4, 其中多余3字节作为补充。
注:char *c 对象大小为4, 因为里面装的是地址。。。。。 void *v 大小亦是4,同理。
这也表明了数据位置排列的重要性。一般来说 应按照从大到小排列。 因为这样可以缩小对齐要求的字节数,如4->>2。(???若是从小到大, 会怎样? 会呵呵。。。)
7, 理解指针:
1) 指针指向函数:
int func(int a, int *b){...} typedef int (*fp) (int a, int *b); fp = func; int result = fp(a, &b);其实函数调用call也是移动到某一指令, 跟指针有差别吗?
2)指针跟数组无异:
int a, *(a+1) = a [1];
3) 改变类型后 寻址的区别:
char *cp; (int*)cp + 7 => (cp, 4, 7)的位置 (int*)(cp+7) => (cp, 1, 7)的位置
8, 存储器的越界引用以及缓冲区溢出:
常见的做法是 类似于char chs[8], strcpy(chs, "1234...........");导致缓冲区溢出, 覆盖其他地址的值(被保存的寄存器的值, 以及返回地址)防范:
1, 栈随机化: 即同一个程序, 运行时的栈的位置不固定。2, 栈破坏检测: 即在保存寄存器附近放置哨兵值, 程序运行后检查哨兵值是否发生改变, 若有, 立即终止程序。当然 这意味着哨兵值必须保存俩处, 而另一处唯一的保障是,只读,不能被修改, 似乎有点薄弱。。。。
3, 设置不能执行区域机制, 限制可执行区域。
总结(自我感觉):
1, 每次调用其他函数是, 会将参数列表反序压栈 + 返回地址。 接着是%ebp %ebx(或许这个跟参数有关, 因为每次push ebx之后在函数结尾都需要pop ebx 意味着ebx是一个需要保存的值, 不能改变, 这个岂不是传值参数传递不影响参数的值的依据?) 如果此时有申请缓冲区(char buffer【8】)则在此后面开辟一个*8(8byte)的空间,(其序号排列顺序是从自栈顶到栈底升序) 如果对这个缓冲区进行strcpy等操作, 就有可能修改到ebp ebx等 甚至是返回地址。
2, 在有保护的代码中, 常常是局部变量比buffer更靠近栈顶, 这样buffer溢出,不会破坏局部变量的值。
注: sizeof(“1234”)is 5; because of ‘\0’(0x00); 但是strlen返回的是4, 故在malloc的调用时, 应该malloc ( strlen(buf)+1 ). 记得要对返回值进行NULL判断。
9. AGE OF 64:
特殊数字: 0x101010101010101 72340172838076673相关文章推荐
- 读书笔记——《深入理解计算机系统》第三章_程序的机器级表示(二)
- 3、深入理解计算机系统笔记:程序的机器级表示
- 程序机器级表示
- csapp读书笔记 chapter 3 程序的机器级表示
- 六星经典CSAPP-笔记(3)程序的机器级表示
- 《深入理解计算机系统》[第3章 程序的机器级表示]
- 程序的机器级表示
- CSI-IV:程序的机器级表示-反汇编基础
- 程序的机器级表示——《深入理解计算机系统》
- 深入理解计算机系统(笔记):程序的机器级表示
- chap3: 程序的机器级表示
- 深入理解计算机体系结构-----之 第三章 程序的机器级表示
- 《深入理解计算机系统》第3章 程序的机器级表示
- 深入理解计算机系统之旅(三)程序的机器级表示
- 3 Machine-Level Representation 程序的机器级表示
- 计算机基础一 -- 程序的机器表示
- CSAPP第三章:程序的机器级表示 小结
- 程序的机器级表示(二)
- 程序的机器级表示
- 程序的机器级表示