2013-04-22 205 views
-1

我试图在运行时打印一个函数的操作码。为此我写了一个C程序,该程序应该在该地址打印地址和十六进制数据。 这里试图打印多功能的内容。在运行时检查机器指令

#include <stdio.h> 

int add(int a, int b) 
{ 
    printf("Adding..\n"); 
    return a+b; 
} 

int sub(int a, int b) 
{ 
    printf("Subtracting...\n"); 
    return a-b; 
} 

int mul(int a, int b) 
{ 
    printf("Multiplying...\n"); 
    return add(a,b) * sub(a,b); 
} 

int main() 
{ 
    char *ptr; 
    int i; 
    char a; 

    int (*func)(int,int); 

    mul(4,3); 
    func = &mul; 
    ptr = (char *)func; 

    do 
    { 
     a = *ptr; 
     printf("%p %x\n",ptr,a); 
     ptr++; 
    }while (a != 0xffffffc3); 
    //op code for ret is c3, which specifies end of function 
    //however, i am not certain why it opcode is being padded by 0xffffff 
} 

它输出它给是

Multiplying... 
Adding.. 
Subtracting... 
0x4005a4 55 
0x4005a5 48 
0x4005a6 ffffff89 
0x4005a7 ffffffe5 
0x4005a8 53 
0x4005a9 48 
0x4005aa ffffff83 
0x4005ab ffffffec 
0x4005ac 18 
0x4005ad ffffff89 
0x4005ae 7d 
0x4005af ffffffec 
0x4005b0 ffffff89 
0x4005b1 75 
0x4005b2 ffffffe8 
0x4005b3 ffffffbf 
0x4005b4 c 
0x4005b5 7 
0x4005b6 40 
0x4005b7 0 
0x4005b8 ffffffe8 
0x4005b9 63 
0x4005ba fffffffe 
0x4005bb ffffffff 
0x4005bc ffffffff 
0x4005bd ffffff8b 
0x4005be 55 
0x4005bf ffffffe8 
0x4005c0 ffffff8b 
0x4005c1 45 
0x4005c2 ffffffec 
0x4005c3 ffffff89 
0x4005c4 ffffffd6 
0x4005c5 ffffff89 
0x4005c6 ffffffc7 
0x4005c7 ffffffe8 
0x4005c8 ffffff90 
0x4005c9 ffffffff 
0x4005ca ffffffff 
0x4005cb ffffffff 
0x4005cc ffffff89 
0x4005cd ffffffc3 

输出几乎是我期望但一些操作码正在被在左侧0XFFFFFF填充和被读为负值。为什么这样?

ELF文件的objdump的下面

00000000004005a4 <mul>: 
    4005a4: 55      push %rbp 
    4005a5: 48 89 e5    mov %rsp,%rbp 
    4005a8: 53      push %rbx 
    4005a9: 48 83 ec 18    sub $0x18,%rsp 
    4005ad: 89 7d ec    mov %edi,-0x14(%rbp) 
    4005b0: 89 75 e8    mov %esi,-0x18(%rbp) 
    4005b3: bf 0c 07 40 00   mov $0x40070c,%edi 
    4005b8: e8 63 fe ff ff   callq 400420 <[email protected]> 
    4005bd: 8b 55 e8    mov -0x18(%rbp),%edx 
    4005c0: 8b 45 ec    mov -0x14(%rbp),%eax 
    4005c3: 89 d6     mov %edx,%esi 
    4005c5: 89 c7     mov %eax,%edi 
    4005c7: e8 90 ff ff ff   callq 40055c <add> 
    4005cc: 89 c3     mov %eax,%ebx 
    4005ce: 8b 55 e8    mov -0x18(%rbp),%edx 
    4005d1: 8b 45 ec    mov -0x14(%rbp),%eax 
    4005d4: 89 d6     mov %edx,%esi 
    4005d6: 89 c7     mov %eax,%edi 
    4005d8: e8 a1 ff ff ff   callq 40057e <sub> 
    4005dd: 0f af c3    imul %ebx,%eax 
    4005e0: 48 83 c4 18    add $0x18,%rsp 
    4005e4: 5b      pop %rbx 
    4005e5: 5d      pop %rbp 
    4005e6: c3      retq 

给出的十六进制代码是几乎相同的,除了0XFFFFFF的填充。我无法弄清楚为什么?

回答

4

这是因为在您的系统上,char已签名。如果您使用的是C实现,请使用unsigned char或(如评论中所建议的)uint8_t。另外,由于你不能将函数指针移植到void *,所以我不认为你可以轻易地认为函数指针指向可读的内存,该内存在机器代码中保存函数的表示。

我意识到这是典型和有点合乎逻辑,但我不认为C保证它。在这种情况下,该程序将触发未定义的行为。希望它不会做任何有害的事情,并且仍然具有启发性(双关语意)。

+1

甚至更​​好,标准化的8位无符号整数类型“uint8_t”。 – 2013-04-22 09:24:48

+0

@JoachimPileborg:'uint8_t'完全没用 - 它存在的任何地方,'unsigned char'必须*也是一个8位无符号整数类型。 – caf 2013-04-22 09:27:23

+0

@caf uint8_t - 如果存在 - 保证是无符号的8位数量。如果只是为了更好地编写代码,最好使用它,而不是使用隐式宽度的类型之一。 – JeremyP 2013-04-22 09:40:07