C程序因printf触发段错误且无编译警告,aarch64 Android环境复现
问题描述
以下是一段运行时触发段错误的简短C程序,即便在clang的pedantic严格编译级别下也无任何警告:
#include <stdio.h> #include <stdlib.h> typedef struct object { int type; } object; void write(object *obj) { switch (obj->type) { case 1: break; } } int main(void) { printf("hi"); return 0; }
该段错误仅在aarch64 Android环境中触发,x86 Linux环境下无此问题,相关环境信息如下:
$ gcc --version clang version 17.0.6 Target: aarch64-unknown-linux-android24 Thread model: posix InstalledDir: /data/data/com.termux/files/usr/bin $ uname -a Linux localhost 5.10.177-android13-4-00003-ga7208022a7ea-ab10815828 #1 SMP PREEMPT Fri Sep 15 16:40:54 UTC 2023 aarch64 Android
通过gdb调试发现,printf最终会调用自定义的write函数,移除main中的printf或自定义write函数后,程序可正常运行。
原因分析
核心问题是全局符号冲突:
- 标准C库(libc)中存在系统调用
write,用于向文件描述符写入数据,printf最终会调用这个系统级write输出内容。 - 自定义的
write函数未加static修饰,属于全局符号。在aarch64 Android的链接器规则下,该全局符号会覆盖libc中的同名符号,导致printf调用时实际执行的是自定义write函数。 - 系统级
write的参数为(int fd, const void *buf, size_t count),而自定义write的参数是(object *obj),参数类型完全不匹配。当printf传递系统级参数给自定义函数时,函数会错误地将这些参数解析为object*类型,访问obj->type时触发非法内存访问,进而导致段错误。
x86 Linux环境未出现问题,大概率是因为该平台下libc的write符号被标记为强符号,或者链接器的符号解析优先级不同,自定义全局符号无法覆盖系统库中的同名符号,因此printf仍调用正确的系统write。
解决方案
有两种简单的修复方式:
- 给自定义
write函数添加static修饰,将其限制为文件内局部符号,避免与全局系统write冲突:
static void write(object *obj) { switch (obj->type) { case 1: break; } }
- 直接给自定义函数改名(比如
object_write),从根源上避免同名冲突。
内容的提问来源于stack exchange,提问作者Tumok A. Byrd
相关产品推荐
相关产品推荐

