页面加载中,请稍候
来源:电子工程专辑发布时间:2023-06-231693浏览
询问 AIuint16_t checksum(uint8_t *data, uint8_t len){ uint8_t i; uint16_t sum = 0, res; for (i = 0; i < len; i++) { sum += data[i]; } res = sum ; return res;}

[12-21 19:45:38]checksum 128 len: 4294967295[12-21 19:45:38]0 4294967295[12-21 19:45:38]1 4294967295[12-21 19:45:38]2 4294967295[12-21 19:45:38]3 4294967295[12-21 19:45:38]4 4294967295[12-21 19:45:38]5 4294967295[12-21 19:45:38]6 4294967295[12-21 19:45:38]7 4294967295[12-21 19:45:38]8 4294967295[12-21 19:45:38]9 4294967295[12-21 19:45:38]10 4294967295 。。。省略很多[12-21 19:45:38]250 4294967295[12-21 19:45:38]251 4294967295[12-21 19:45:38]252 4294967295[12-21 19:45:38]253 4294967295[12-21 19:45:38]254 4294967295[12-21 19:45:38]255 4294967295[12-21 19:45:38]256 4294967295[12-21 19:45:38]257 4294967295[12-21 19:45:38]258 4294967295[12-21 19:45:38]259 4294967295[12-21 19:45:38]260 4294967295
uint16_t res = checksum(data[0], len - 1);/*一个结构体定义数据不要急于吐槽它的定义,这代码是开源的,冤有头。。。还有不要怀疑是字节对齐不对齐的问题,曾经我也怀疑过,最后知道真相的时候,我被打脸了!*/typedef struct _data_t { /* result, final result */ uint8_t len; uint8_t flag; uint8_t passwd_len; uint8_t *passwd; uint8_t ssid_len; uint8_t *ssid; uint8_t token_len; uint8_t *token; uint8_t bssid_type_len; uint8_t *bssid; uint8_t ssid_is_gbk; uint8_t ssid_auto_complete_disable; uint8_t data[127]; uint8_t checksum;} data_t;
/* 1.c 调用checksum的C文件 *//* 定义全局的数据 */static data_t g_data;/* 设置全局的数据 */void set_global_data(void){ g_data.len = 0;}void handle_global_data(void){ uint16_t res = checksum(g_data.data[0], g_data.len - 0); //sometimes no return form checksum}void test_func_entry(void){ set_global_data(); handle_global_data();}
/* 2.c 定义checksum函数的工具类 */uint16_t checksum(uint8_t *data, uint8_t len){ uint8_t i; uint16_t sum = 0, res; for (i = 0; i < len; i++) { sum += data[i]; } res = sum ; return res;}
突然我脑子里,闪过一个疑问:“会不会那段for循环的checksum函数,正是因为调用方没有申明checksum函数,也就是说没有include对应的头文件导致编译器做了默认处理呢?”?uint16_t res = checksum(g_data.data[0], g_data.len - 0);
/* checksum 函数的汇编代码 */ .section .text.checksum,"ax",%progbits .align 1 .global checksum .code 16 .thumb_func .type checksum, %functionchecksum:.LFB4: .loc 1 125 0 .cfi_startproc @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0.LVL27: push {r4, r5, r6, lr} .cfi_def_cfa_offset 16 .cfi_offset 4, -16 .cfi_offset 5, -12 .cfi_offset 6, -8 .cfi_offset 14, -4 .loc 1 125 0 movs r4, r0 movs r5, r1 // r1 -> r5 ,即 len的值存在r5中 .loc 1 129 0 movs r2, r1 ldr r0, .L29.LVL28: bl printf //打印len的值.LVL29: movs r3, r4 .loc 1 127 0 movs r0, #0 adds r5, r4, r5.LVL30:.L26: .loc 1 130 0 discriminator 1 cmp r3, r5 //for循环里面的关键判断,即 i < len beq .L28 // 退出for循环 .loc 1 131 0 discriminator 3 //下面就是for循环的循环执行体 ldrb r2, [r3] adds r3, r3, #1.LVL31: adds r0, r0, r2.LVL32: lsls r0, r0, #16 lsrs r0, r0, #16.LVL33: b .L26.LVL34:.L28: .loc 1 136 0 @ sp needed.LVL35: pop {r4, r5, r6, pc}.L30: .align 2.L29: .word .LC12 .cfi_endproc.LFE4: .size checksum, .-checksum
.section .text.verify_checksum,"ax",%progbits .align 1 .global verify_checksum .code 16 .thumb_func .type verify_checksum, %functionverify_checksum:.LFB5: .loc 1 81 0 .cfi_startproc @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0.LVL17: push {r4, lr} .cfi_def_cfa_offset 8 .cfi_offset 4, -8 .cfi_offset 14, -4 .loc 1 83 0 ldr r4, .L20 .loc 1 91 0 @ sp needed .loc 1 83 0 movs r0, r4 //r0存储结构体g_data的地址 ldrb r1, [r4] //将g_data的第一个字节,即g_data.len赋值为r1 adds r0, r0, #34 //r0的地址偏移34个字节,即偏移到g_data.data的位置; subs r1, r1, #1 //关键的一步:r1 = r1 - 1 由于我们复现问题的时候,g_data.len是为0的,所以此时r1的值就是0xFFFFFFFF bl checksum //调用checksum函数,第1-2个入参,分别是r0和r1.LVL18: .loc 1 84 0 adds r4, r4, #160 .loc 1 89 0 ldrb r3, [r4] lsls r0, r0, #24.LVL19: lsrs r0, r0, #24 subs r0, r0, r3 .loc 1 91 0 pop {r4, pc}.L21: .align 2.L20: .word .LANCHOR4 .cfi_endproc.LFE5: .size verify_checksum, .-verify_checksum
* 添加头文件之后的汇编代码 */ .section .text.verify_checksum,"ax",%progbits .align 1 .global verify_checksum .code 16 .thumb_func .type verify_checksum, %functionverify_checksum:.LFB5: .loc 1 81 0 .cfi_startproc @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0.LVL17: push {r4, lr} .cfi_def_cfa_offset 8 .cfi_offset 4, -8 .cfi_offset 14, -4 .loc 1 83 0 ldr r4, .L20 .loc 1 91 0 @ sp needed .loc 1 83 0 movs r0, r4 ldrb r1, [r4] adds r0, r0, #34 subs r1, r1, #1 //r1寄存器的一样的操作 r1 = r1 - 1 lsls r1, r1, #24 //关键改变!!!r1 = r1 * (2的24次幂),也就是算术左移24位 lsrs r1, r1, #24 //关键改变!!!r1 = r1 / (2的24次幂),也就是算术右移24位 bl checksum.LVL18: .loc 1 84 0 adds r4, r4, #160 .loc 1 89 0 ldrb r3, [r4] lsls r0, r0, #24.LVL19: lsrs r0, r0, #24 subs r0, r0, r3 .loc 1 91 0 pop {r4, pc}.L21: .align 2.L20: .word .LANCHOR4 .cfi_endproc.LFE5: .size verify_checksum, .-verify_checksum




作为本文的作者,稍微跟评论席的朋友聊一聊“评论席的几个观点”。
1、关于0warning0error的推荐,我是强烈支持,但是考虑到团队整体的接受程度,以及开发调试效率的取舍,我们放弃了部分编译警告,这是我们的失误。另外,在遇到这个问题之前,直到解决了这个问题,才发现原来不声明函数的情况下还可以导致这种致命错误。
2、不能一下子就说gcc很low,正如第1点所讲,这种不声明函数的代码,我当然可以在gcc上配置编译参数让你产生error,中断你的编译流程,这个并不是iar的独特权利。相反的一方面,我倒是觉得gcc作为一个开源的非商业编译器,它保留了足够的开放型和灵活度,在你深度了解gcc后,你可以深度定制它,这一点我觉得是非常优秀的。另外,gcc在跨芯片平台也有比较好的支持,以至于我们切入新的芯片平台,优先考虑这个芯片是否支持gcc编译器。
以上观点,仅代表个人逻辑,欢迎大家各抒己见。感谢关注这个课题。谢谢。
新闻来源:电子工程专辑,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!
2026-06-16

2026-07-03