免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2040 | 回复: 2
打印 上一主题 下一主题

[其他] 慎用c语言的#pragma pack [复制链接]

论坛徽章:
13
程序设计版块每日发帖之星
日期:2016-06-29 06:20:00每日论坛发贴之星
日期:2016-08-14 06:20:00操作系统版块每日发帖之星
日期:2016-08-14 06:20:00每日论坛发贴之星
日期:2016-08-13 06:20:00数据库技术版块每日发帖之星
日期:2016-08-13 06:20:00程序设计版块每日发帖之星
日期:2016-08-13 06:20:00IT运维版块每日发帖之星
日期:2016-08-13 06:20:00每日论坛发贴之星
日期:2016-08-12 06:20:00数据库技术版块每日发帖之星
日期:2016-08-12 06:20:00程序设计版块每日发帖之星
日期:2016-08-12 06:20:00操作系统版块每日发帖之星
日期:2016-08-12 06:20:00综合交流区版块每日发帖之星
日期:2016-08-09 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-08-06 17:37 |只看该作者 |倒序浏览
本帖最后由 karma303 于 2016-08-06 18:19 编辑

  下午调整了一下ARP模块的代码,因为要支持多网卡。
  修改完之后,发现出去的ARP包的source ip字段总是255,0.0.0 。

  我开始没当回事,猜肯定是哪行代码手误了。
  找到source ip字段的赋值语句:
  arphdr->myip = htonl(netdev->ip);
  这句的含义是,arg报文从哪个网卡出去,就携带哪个网卡的IP。
  我在它前面加了一句:
  info_nic(netdev);
  想看看这个netdevice的基本信息。 打印输出的信息里,ip是正常的:192.168.0.9。

  于是再在前面插一句:
  oprintf("< %x >", netdev->ip);
  输出的却是<0xff000000>。

  这时候觉得不对劲儿了,因为info_nic()里访问ip字段,用的几乎就是这句代码了。 而且这两句代码连在一起:
  oprintf("< %x >", netdev->ip);
  info_nic(netdev);
  看的真真切切,访问的也是同一个netdev呀。

  这时候有些慌。。还是缺少裸机调试的经验,离开了gdb,正常的思维都没有了。

  这时候,正确的思维,是再插上一句
  oprintf("%x", &netdev->ip)。                                          //打印ip字段的地址,而非ip字段
  同时在info_nic()里也插上这么一句。看看,两处访问的ip字段怎么就不一样了!

  我当时打印的是&netdev。发现输出是相同的,就放弃这个方向了。 转而去观察这个异常的ip字段附近的内存面貌:
----------------为了便于阅读,代码有细微调整------------
    struct eax { u8 al; u8 ah; u8 AL; u8 AH;};
    struct eax *ip = (void *)&netdev->ip;
    struct eax *_eax = ip - 1;
    struct eax *eax_ = ip + 1;
    oprintf("  +eax:%u.%u.%u.%u    direct ip: %u.%u.%u.%u     -eax:%u.%u.%u.%u",
                   eax_->AH, eax_->AL, eax_->ah, eax_->al,
                    ip->AH, ip->AL, ip->ah, ip->al,
                    _eax->AH, _eax->AL, _eax->ah, _eax->al);
  上面的代码看不懂也没关系,反正就是打印ip字段两侧的内存。eax没什么特殊含义,顺手取的名字。
  输出 +eax: 0.9.255.255  direct ip: 255.0.0.0  -eax 2.1.0.0 。
  最前面那个"0.9" 不就是ip字段"192.168.0.9"的尾巴么。 "255.255.255.0"不就是它旁边的子网掩码么。
  struct net_device{
    ...
    u32 ipmask;
    u32 ip;
    u32 gateway_ip;
};
    赶紧扩大范围看一看:
  (省略测试代码)
   ++eax 0.1.192.168 +eax: 0.9.255.255  direct ip: 255.0.0.0  -eax 2.1.0.0  --eax 0.0.0.0

  这就有线索了,程序代码本来是访问ip字段的,但是偏了6个字节。

  这时立刻想到是 #pragma pack()的问题,只有它会造成同一个结构体在两个c文件里编译后,成员的布局有微小的差异。

  为了确定,分别在info_nic()(在dev.c里)和arp.c里 oprintf("%x", sizeof(struct net_device));
  分别输出96和90 。
  果然,而且整好相差的就是6个字节。

  接下来就没什么难度了,很快找到是arp.h里的:
#pragma pack(push)
#pragma pack(1)
忘了写对应的#pragma pack(pop) 。

  总结一下:感觉调试的时候,最忌讳的就是先怀疑basic facility出了问题(例如怀疑自己实现的printf()有bug), 怀疑内存越界。首先一定要用最常规的思路分析,直到山穷水尽,再怀疑到内存越界或者basic facility头上,那这个时间的耽误也是值得的。不然调试会很痛苦。

  最后,补上一点关于pack的话:
  感觉gcc对#pragma pack(push) 与 #pragma pack(pop)的配对检查挺弱的,有时间还是自己写个语法检查的小程序为好,就让它扫描项目里的所有文件,发现push没有匹配的pop就报错,或者push和pop之间include了其它头文件,也要报错。
  另外,gcc是有个__attribute__ ((packed))的扩展的,但感觉不实用,因为结构体里常常嵌套结构体,它对后者就不起作用。像:
struct abc{
        struct{
            char a;
            short b;
            char c;
        } inner;
        int c;
    }__attribute__((packed));
打印sizeof,输出是10。
其实我们想要的是8. 只有inner也加上packed修饰,输出才对。


==========================================
  最后,请教管理员,论坛上的积分能用钱买吗? 我想把自己的github链接放到个人签名里,但是照我的发帖速度,我估计我猴年马月也攒不够分。。。

评分

参与人数 1可用积分 +6 收起 理由
Godbach + 6 感谢分享

查看全部评分

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
2 [报告]
发表于 2016-08-06 21:21 |只看该作者
回复 1# karma303

多多发帖就可以增加积分。

比如本月发帖版块排行第一,还有额外的上千积分的奖励。


   

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
3 [报告]
发表于 2016-08-16 15:20 |只看该作者
有时间还是自己写个语法检查的小程序为好,就让它扫描项目里的所有文件,发现push没有匹配的pop就报错,或者push和pop之间include了其它头文件,也要报错。

这个思路不错,也挺实用的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP