64位机器一次变长参数打印内存访问错误的定位

64位机器一次变长参数打印内存访问错误的定位

在维护路由代码时,修改了一份调试函数在shell中打印,发现打印后引发进程访问错误的内存。所有宏用数字代替,为错误的代码如下所示:
 snprintf(buf, 512, "[%#x]%c%c%s [%d/%lu] ", rib->flags,
        CHECK_FLAG (rib->flags, FLAG_SELECTED)
        ? '>' : ' ',
        CHECK_FLAG (rib->flags, FLAG_STALE)
        ? 'p' : ' ',
        route_type_str (rib->type), rib->distance, rib->metric)

其中route_type_str在另一个文件中定义:

char *route_type_str (u_char type)
{
  switch (type)
    {
    case 1:
      return "kernel";
    case 2:
      return "connected";
    case 3:
      return "static";
    case 4:
    case 5:
      return "rip";
    case 6:
    case 7:
      return "ospf";
    case 8:
      return "bgp";
    case 9:
      return "isis";
    default:
      return "unknown";
    }
}

当打印到route_type_str指向的字符串后,发现内存访问错误。当定义一个局部变量string先获取route_type_str后,再打印就不会有错误。
在有错误的情况下,使用GDB跟踪代码,发现在打印是获取到的字符串指针是错误的。
比如访问的是”static”,其指针为 0x00599177,但是在打印时,通过va_list获取的地址为:0x00007fef00599177。
一开始怀疑va_list设置时错了,在网上搜索64位的va_list处理,获取到如下信息:

GCC的调用约定跟VC不同。前6个整数参数会依次放到rdi, rsi, rdx, rcx, r8, r9中,前8个浮点参数放到xmm0到xmm7中。除了使用了更多的寄存器,与vc不同的是,整数和浮点数寄存器是混合使用的不用为没用的参数预留。还是刚才的例子,第一个参数是int,第二个是double,第三个char*,第四个double,参数数会依次放到 rdi,xmm0,rsi,xmm1. 另外,没有在栈上预留寄存器区。<

使用GDB info register获取到寄存器信息,入参是正确的。在通过disassemble看到该打印函数调用处的上下文,也没有问题。再次跟踪到上次调用函数,反汇编看到如下代码:

   0x00000000004690e6 <+71>:    callq  0x460a98 <route_type_str>
   0x00000000004690eb <+76>:    mov    %eax,%ecx

由于是64位机器,应该使用rxx的寄存器才对,这里使用了exx寄存器,导致后续在mov返回值的时候,只存了低4字节的指针。这样在后续处理时,导致指针地址获取错误。
但是为什么会出现这个问题呢?objdump整个进程,发现所有的这个函数的反汇编调用处都是exx寄存器。后来发现该函数没有声明,所有的调用处都是在其他文件中默认链接的。这些地方都默认返回int,在64位机器上,当使用这个返回指针时就出错了。

为了验证这个,写了2个小文件测试了下:
1.c:

char *rib_str(char type)
{
    switch (type)
    {
        case 1:
            return "kernel";
        case 2:
            return "static";
        default:
            return "unkown";
    }
    return "Null";
}

2.c

#include <stdio.h>
#include <string.h>

int main()
{
    printf("%s\n",rib_str(1));
    return 0;
}

gcc -o rib 1.c 2.c
objdump -d rib > 1.txt

143 0000000000400560 <main>:
144   400560:   55                      push   %rbp
145   400561:   48 89 e5                mov    %rsp,%rbp
146   400564:   bf 01 00 00 00          mov    $0x1,%edi
147   400569:   b8 00 00 00 00          mov    $0x0,%eax
148   40056e:   e8 bd ff ff ff          callq  400530 <rib_str>
149   400573:   89 c6                   mov    %eax,%esi
150   400575:   bf 35 06 40 00          mov    $0x400635,%edi
151   40057a:   b8 00 00 00 00          mov    $0x0,%eax
152   40057f:   e8 8c fe ff ff          callq  400410 <printf@plt>
153   400584:   b8 00 00 00 00          mov    $0x0,%eax

因此一定要注意编译告警,注意函数声明。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值