二进制或汇编中如何判断数据的类型(比如是有符号类型还是无符号类型)
众所周知,在二进制代码中的数据是不存在类型这个概念的,我们只能知道某个寄存器或者内存上有一个多少位的数据。那么,计算机是如何来判断某个数据能进行哪些操作的呢?这就是在编译和汇编时需要完成的工作了。通过分析指令或者已知函数操作内存地址的方式,我们可以获得一些信息。
下面看几个例子。
例1
int main(){int count = 1000;unsigned char loop = 0;for (; loop < count; loop++) ;return 0;
}
其中涉及到 int 类型和 unsigned char 类型的比较。上面这段代码翻译成汇编语言是:
00000000000005fa :5fa: 55 push %rbp5fb: 48 89 e5 mov %rsp,%rbp5fe: c7 45 fc e8 03 00 00 movl $0x3e8,-0x4(%rbp)605: c6 45 fb 00 movb $0x0,-0x5(%rbp)609: 0f b6 45 fb movzbl -0x5(%rbp),%eax60d: 39 45 fc cmp %eax,-0x4(%rbp)610: 7e 0c jle 61e 612: 0f b6 45 fb movzbl -0x5(%rbp),%eax616: 83 c0 01 add $0x1,%eax619: 88 45 fb mov %al,-0x5(%rbp)61c: eb eb jmp 609 61e: b8 00 00 00 00 mov $0x0,%eax623: 5d pop %rbp624: c3 retq625: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)62c: 00 00 0062f: 90 nop
可以看出使用了 movzbl 指令先从 -0x5(%rbp) 拷贝到 %eax ,zbl 后缀表示零扩展从 byte 类型到 long 类型,所以只拷贝了低 8位。对 %eax 加1后再用 mov 把 %al 拷贝回 -0x5(%rbp) ,因为是 %al 所以只有低8位。因此,通过指令把数据限制在了低8位。
例2
我们用下面两段代码的汇编代码来看下,对于同样是32位的有符号整数和无符号整数,计算机是如何区别的。
- 代码1
#include
int main(){int32_t count = INT32_MAX;int32_t loop = 0;for (; loop < count; loop++) ;return 0;
}
对应
00000000000005fa :5fa: 55 push %rbp5fb: 48 89 e5 mov %rsp,%rbp5fe: c7 45 fc ff ff ff 7f movl $0x7fffffff,-0x4(%rbp)605: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)60c: 8b 45 f8 mov -0x8(%rbp),%eax60f: 3b 45 fc cmp -0x4(%rbp),%eax612: 7d 06 jge 61a 614: 83 45 f8 01 addl $0x1,-0x8(%rbp)618: eb f2 jmp 60c 61a: b8 00 00 00 00 mov $0x0,%eax61f: 5d pop %rbp620: c3 retq621: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)628: 00 00 0062b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
- 代码2
#include
int main(){uint32_t count = UINT32_MAX;uint32_t loop = 0;for (; loop < count; loop++) ;return 0;
}
对应
00000000000005fa :5fa: 55 push %rbp5fb: 48 89 e5 mov %rsp,%rbp5fe: c7 45 fc ff ff ff ff movl $0xffffffff,-0x4(%rbp)605: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)60c: 8b 45 f8 mov -0x8(%rbp),%eax60f: 3b 45 fc cmp -0x4(%rbp),%eax612: 73 06 jae 61a 614: 83 45 f8 01 addl $0x1,-0x8(%rbp)618: eb f2 jmp 60c 61a: b8 00 00 00 00 mov $0x0,%eax61f: 5d pop %rbp620: c3 retq621: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)628: 00 00 0062b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
从两段代码我们可以看出主要的区别就只有 612 这个地址上的内容。对于有符号数,比较时用的是 jge ,而无符号数使用 jae 。这样的原因是计算机无需数据类型,只要指令使用得正确即可执行。因此可以知道,单独从数据来看,是无法得到其类型的,我们只有通过指令操作来推断出类型。
例3
补充一个例子,和前面例2的代码2可以一起看。
#include
int main(){uint32_t count = UINT32_MAX;int32_t loop = 0;for (; loop < count; loop++) ;return 0;
}
对应
00000000000005fa :5fa: 55 push %rbp5fb: 48 89 e5 mov %rsp,%rbp5fe: c7 45 fc ff ff ff ff movl $0xffffffff,-0x4(%rbp)605: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)60c: 8b 45 f8 mov -0x8(%rbp),%eax60f: 39 45 fc cmp %eax,-0x4(%rbp)612: 76 06 jbe 61a 614: 83 45 f8 01 addl $0x1,-0x8(%rbp)618: eb f2 jmp 60c 61a: b8 00 00 00 00 mov $0x0,%eax61f: 5d pop %rbp620: c3 retq621: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)628: 00 00 0062b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
分析两段汇编代码,其实含义上没有任何区别。对于例2中的代码2, cmp count, loop 时,用的是 jae 指令, loop 无符号大于等于 count 则跳转退出;对于后面这个代码, cmp loop, count 时,用的是 jbe 指令, count 无符号小于等于 loop 则跳转退出。两个意思是一样的。这说明在后面的这个例子中,执行到 loop < count 时,有符号类型变量 int32_t loop 会被强转成无符号类型。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
