先看一下例子
#include
int main()
{
int i;
unsigned char *p;
char *p1;
int a[] = {0xffffffff, 0xffffffff, 0xffffffff};
p = a;
p1 = a;
for(i = 0 ; i < 8 ; i++) {
printf(" 0x%02x 0x%02x \n", p[i], p1[i]);
}
}
$ gcc main.c
main.c: In function ‘main’:
main.c:10: warning: assignment from incompatible pointer type
main.c:11: warning: assignment from incompatible pointer type
$ ./a.out
0xff 0xffffffff
0xff 0xffffffff
0xff 0xffffffff
0xff 0xffffffff
0xff 0xffffffff
0xff 0xffffffff
0xff 0xffffffff
0xff 0xffffffff
看来是在printf的参数传递过程中出现了问题,%x传递的参数的大小是四个字节,反汇编看一下
¥gcc -S main.c -o main.s
$ vi main.s
.file "main.c"
.section .rodata
.LC0:
.string " 0x%02x 0x%02x \n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $48, %esp
movl $-1, 24(%esp)
movl $-1, 28(%esp)
movl $-1, 32(%esp)
leal 24(%esp), %eax
movl %eax, 40(%esp)
leal 24(%esp), %eax
movl %eax, 44(%esp)
movl $0, 36(%esp)
jmp .L2
.L3:
movl 36(%esp), %eax
addl 44(%esp), %eax
movzbl (%eax), %eax
movsbl %al, %ecx
movl 36(%esp), %eax
addl 40(%esp), %eax
movzbl (%eax), %eax
movzbl %al, %edx
movl $.LC0, %eax
movl %ecx, 8(%esp)
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
addl $1, 36(%esp)
.L2:
cmpl $7, 36(%esp)
jle .L3
leave
ret
.size main, .-main
.ident "GCC: (GNU) 4.4.5 20101112 (Red Hat 4.4.5-2)"
.section .note.GNU-stack,"",@progbits
可见,在printf执行的时候传递的两个参数分别使用了 movsbl 和movzbl,这两个指令有什么差别呢
movsbl、movzbl的区别:
%dh = 8D
%eax = 98765432
movb %dh,%al //%eax = 9876548D
movsbl %dh,%eax //%eax = FFFFFF8D
movzbl %dh,%eax //%eax = 0000008D
如果 我们要一个字节一个字节的打印内存的信息的时候,还是需要使用unsigned char 作为指针进行打印比较好。
看一下mips 反汇编的结果
lw $2,24($fp)
slt $2,$2,8
beq $2,$0,$L3
lw $3,28($fp)
lw $2,24($fp)
addu $2,$3,$2
lbu $5,0($2)
lw $3,32($fp)
lw $2,24($fp)
addu $2,$3,$2
lb $2,0($2)
la $4,$LC1
move $6,$2
jal printf
lw $2,24($fp)
addiu $2,$2,1
sw $2,24($fp)
b $L2
看一下arm 平台下运行的结果
# ./a.out
0xff 0xff
0xff 0xff
0xff 0xff
0xff 0xff
0xff 0xff
0xff 0xff
0xff 0xff
0xff 0xff
# cat /proc/cpuinfo
Processor : ARM926EJ-S rev 5 (v5l)
BogoMIPS : 99.12
Features : swp half thumb fastmult edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant : 0x0
CPU part : 0x926
CPU revision : 5
Hardware : Atmel AT91SAM9260-EK
Revision : 0000
Serial : 0000000000000000
#
再看一下arm 的反汇编
.L3:
ldr r3, [fp, #-16]
mov r2, r3
ldr r3, [fp, #-12]
add r3, r2, r3
ldrb r3, [r3, #0] @ zero_extendqisi2
mov r1, r3
ldr r3, [fp, #-16]
mov r2, r3
ldr r3, [fp, #-8]
add r3, r2, r3
ldrb r3, [r3, #0] @ zero_extendqisi2
ldr r0, .L6+4
mov r2, r3
bl printf
ldr r3, [fp, #-16]
add r3, r3, #1
str r3, [fp, #-16]
arm 平台跟 mips 和 x86 有比较大的差别
LDRB 指令从value 与basereg 将一个字节加载到dest
当然,正如网友说的,
根本原因如下:
%x是无符号整数的16进制,是无符号!而你的printf传递的实际是字符型,于是有一个类型提升的问题。p是无符号数,那么0xff提升为无符号整数仍然是0xff,而p1是有符号的,0xff实际的值是-1.那么对应的无符号整数仍然应该为-1的对应值,也就是0xffffffff。这才是根本原因。
其实对于这个问题根本不需要使用反汇编。C语言基础就解决了!
printf 要打印一个%x的话,要传递一个4字节的数据,而对于singed 和unsigned char 的处理来说将一个char 转化成一个4字节的数据 在x86 平台上有movsbl、movzbl 区别的对待有符号和无符号的数据,但是对于arm 来说,编译器通过Ldr只把一个字节的数据传递到参数里面就行,这个是由于编译器的不同而不同的。我只是想看一下,对于有符号和无符号的数据处理,编译器到底是怎么处理的?为什么要照搬教条性的东西呢?如果按照教条性的东西,为什么有的平台打印会不同呢?
阅读(22944) | 评论(8) | 转发(1) |