嵌入式Arm程序通过core文件找到dump位置

一 编译带调试信息的程序

二 配置core文件参数

1)查看当前系统限制情况

ulimit -a

2)设置crash dump文件大小

ulimit -c unlimited
unlimited表示无大小限制,也可以指定具体的大小
ulimit -c 1000
表示文件大小限制为1000Kb
若要全局生效需要在/etc/profile中加入上面的命令ulimit -c 1000,然后source /etc/profile使之生效

3) 配置dump文件生成路径

/proc/sys/kernel/core_uses_pid
文件内容为1表示dump文件以pid为扩展名
/proc/sys/kernel/core_pattern
core_pattern接受的是core文件名称的pattern,它包含任何字符串,并且用%作为转移符号生成一些标示符,为core文件名称加入特殊含义。已定义的标示符有如下这些:
%%:相当于%
%p:相当于pid
%u:相当于uid
%g:相当于gid
%s:相当于导致dump的信号的数字
%t:相当于dump的时间
%e:相当于执行文件的名称
%h:相当于hostname
除以上这些标志位外,还规定:
1、末尾的单个%可以直接去除;
2、%加上除上述以外的任何字符,%和该字符都会被去除;
3、所有其他字符都作为一般字符加入名称中;
4、core文件的名称最大值为64个字节(包括’\0’);
5、core_pattern中默认的pattern为core;
6、为了保持兼容性,通过设置core_uses_pid,可以在core文件的末尾加上%p;
7、pattern中可以包含路径信息。
echo “/corefile/core-%e-%p-%t” > /proc/sys/kernel/core_pattern
将会控制所产生的core文件会存放到/corefile目录下(/corefile目录需要提前创建),产生的文件名为core-执行文件名-pid-时间戳

在发生SIGSEGV时系统会自动在程序目录下生成core.xxxx文件,此即dump文件;
注意,此时程序不能再注册SIGSEGV的处理函数!

三 使用gdb分析dump文件

1)使用arm-linux-gdb 载入arm开发板生成的core

把生成的core文件拷贝到与可执行文件同一个目录下

执行 gdb a.out core.xxx

2)使用GDB命令调试

2.1 使用where 或bt命令,将显示#x这样的堆栈信息

2.2 使用frame x,能查看更详细的函数调用信息:frame x,x为#后的数字

四 使用maps查看crash的函数名

运行着个程序发生了crash,并且生成了core文件,

./sample_main

Segmentation fault (core dumped)

通过gdb+core看到了crash的地址

GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/nap/sample_main...(no debugging symbols found)...done.
[New LWP 4170]


warning: Can't read pathname for load map: Input/output error.
Core was generated by `./sample_main'.
Program terminated with signal 11, Segmentation fault.
#0  0xb773a478 in ???
(gdb) bt
#0  0xb773a478 in ???
#1  0x08048770 in ???
#2  0x08048763 in ???
(gdb)


通过查看进程的map可以看出crash的位置在libsample_fun.so的代码段,

08048000-08049000 r-xp 00000000 08:05 1059318    /home/nap/sample_main
08049000-0804a000 r--p 00000000 08:05 1059318    /home/nap/sample_main
0804a000-0804b000 rw-p 00001000 08:05 1059318    /home/nap/sample_main
b7449000-b744b000 rw-p 00000000 00:00 0
b744b000-b7467000 r-xp 00000000 08:05 1311741    /lib/i386-linux-gnu/libgcc_s.so.1
b7467000-b7468000 r--p 0001b000 08:05 1311741    /lib/i386-linux-gnu/libgcc_s.so.1
b7468000-b7469000 rw-p 0001c000 08:05 1311741    /lib/i386-linux-gnu/libgcc_s.so.1
b7469000-b7493000 r-xp 00000000 08:05 1315013    /lib/i386-linux-gnu/libm-2.15.so
b7493000-b7494000 r--p 00029000 08:05 1315013    /lib/i386-linux-gnu/libm-2.15.so
b7494000-b7495000 rw-p 0002a000 08:05 1315013    /lib/i386-linux-gnu/libm-2.15.so
b7495000-b7638000 r-xp 00000000 08:05 1310845    /lib/i386-linux-gnu/libc-2.15.so
b7638000-b763a000 r--p 001a3000 08:05 1310845    /lib/i386-linux-gnu/libc-2.15.so
b763a000-b763b000 rw-p 001a5000 08:05 1310845    /lib/i386-linux-gnu/libc-2.15.so
b763b000-b763f000 rw-p 00000000 00:00 0
b763f000-b7717000 r-xp 00000000 08:05 1575071    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7717000-b7718000 ---p 000d8000 08:05 1575071    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7718000-b771c000 r--p 000d8000 08:05 1575071    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b771c000-b771d000 rw-p 000dc000 08:05 1575071    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b771d000-b7724000 rw-p 00000000 00:00 0
b773a000-b773b000 r-xp 00000000 08:05 1059319    /home/nap/libsample_fun.so
b773b000-b773c000 r--p 00000000 08:05 1059319    /home/nap/libsample_fun.so
b773c000-b773d000 rw-p 00001000 08:05 1059319    /home/nap/libsample_fun.so
b773d000-b773f000 rw-p 00000000 00:00 0
b773f000-b7740000 r-xp 00000000 00:00 0          [vdso]
b7740000-b7760000 r-xp 00000000 08:05 1311879    /lib/i386-linux-gnu/ld-2.15.so
b7760000-b7761000 r--p 0001f000 08:05 1311879    /lib/i386-linux-gnu/ld-2.15.so
b7761000-b7762000 rw-p 00020000 08:05 1311879    /lib/i386-linux-gnu/ld-2.15.so
bf97d000-bf99e000 rw-p 00000000 00:00 0          [stack]


接下来我们要求出crash的函数在libsample_fun.so中的地址,计算方式如下

发生crash的地址 - 动态库的起始地址 + 偏移地址

在我们的例子中为0xb773a478 - 0xb773a000 + 0x00000000 = 0x478

通过 nm -n libsample_fun.so将这个动态库中的符号表按地址顺序输出

nap@nap-HP-431-Notebook-PC:~$ nm -n libsample_fun.so
         w _Jv_RegisterClasses
         w __cxa_finalize@@GLIBC_2.1.3
         w __gmon_start__
         U printf@@GLIBC_2.0
00000330 T _init
000003a0 t __do_global_dtors_aux
00000420 t frame_dummy
00000457 t __i686.get_pc_thunk.bx
0000045c T _Z8testFun4v
000004a0 t __do_global_ctors_aux
000004d8 T _fini
00000574 r __FRAME_END__
00001f14 d __CTOR_LIST__
00001f18 d __CTOR_END__
00001f1c d __DTOR_LIST__
00001f20 d __DTOR_END__
00001f24 d __JCR_END__
00001f24 d __JCR_LIST__
00001f28 a _DYNAMIC
00001ff4 a _GLOBAL_OFFSET_TABLE_
0000200c d __dso_handle
00002010 A __bss_start
00002010 A _edata
00002010 b completed.6159
00002014 b dtor_idx.6161
00002018 A _end

找到最接近0x478且比0x478下的地址,这个地址的函数就是发生crash的函数

使用c++filt将这个函数名打印出来

nap@nap-HP-431-Notebook-PC:~$ c++filt _Z8testFun4v
testFun4()

这样我们就找到了crash的函数。

五 set solib-search-path he set solib-absolute-prefix区别

solib-absolute-prefix 和solib-search-path是 gdb比较常用的选项,用来设置符号文件的位置。

那么,它们有什么区别呢?

solib-absolute-prefix设置的是被搜索文件路径的前缀,solib-search-path设置的是被搜索文件的路径。

solib-search-path可以有多个路径,中间按用:隔开, solib-absolute-prefix的值只能有一个。

下面以一个例子说明。

先载入一个 core dump文件:

    (gdb) core-file core
    [New LWP 489]
    warning: Could not load shared library symbols for 2 libraries, e.g. /mnt/libshared.so.
    Use the "info sharedlibrary" command to see the complete listing.
    Do you need "set solib-search-path" or "set sysroot"?
    Core was generated by `./main'.
    Program terminated with signal 11, Segmentation fault.
    #0  0x2ac02508 in ?? ()

先执行下 info sharedlibrary:

    (gdb) info sharedlibrary
    From        To          Syms Read   Shared Object Library
                            No          /mnt/libshared.so
                            No          /lib/libc.so.6
    0x2ab417e0  0x2ab52bf0  Yes (*)     /home/charles/code/gcc-arm-2012.04-linux/arm-linux-gnueabi/libc/lib/ld-linux.so.3
    (*): Shared library is missing debugging information.

有两个动态库还没有载入。

假设当前目录下有文件 libshared.so, 可以执行下面的命令:

    (gdb) set solib-search-path .
    Reading symbols from /home/charles/libshared.so...done.
    Loaded symbols for /home/charles/libshared.so

再看下动态库的加载情况:

    (gdb) info sharedlibrary
    From        To          Syms Read   Shared Object Library
    0x2ac0240c  0x2ac02510  Yes         /home/charles/libshared.so
                            No          /lib/libc.so.6
    0x2ab417e0  0x2ab52bf0  Yes (*)     /home/charles/code/gcc-arm-2012.04-linux/arm-linux-gnueabi/libc/lib/ld-linux.so.3
    (*): Shared library is missing debugging information.


还有一个没有载入。

假设文件 libc.so.6在 /media/DATA/lib/libc.so.6下:

    ~$ ls /media/DATA/lib/ -l
    total 860
    -rw------- 1 charles charles 876656 Nov 18 03:56 libc.so.6

这时,可以用 solib-absolute-path。

    (gdb) set solib-absolute-prefix /media/DATA
    Reading symbols from /media/DATA/lib/libc.so.6...(no debugging symbols found)...done.
    Loaded symbols for /media/DATA/lib/libc.so.6

这时,gdb用前缀 /media/DATA + /lib/libc.so.6,就找到了文件。

 /lib 是可执行文件在 目标机上  link libc.so.6的位置。

    (gdb) bt full
    #0  0x2ac02508 in fun () at testsharedlib.c:3
    No locals.
    #1  0x00008570 in main () at main.c:6
    No locals.
    (gdb) l *0x2ac02508
    0x2ac02508 is in fun (testsharedlib.c:3).
    1    void fun(void)
    2    {
    3        *(int *)0 = 0;
    4    }
 

 

<think>好的,我现在需要帮助用户了解如何在嵌入式系统中生成core文件。首先,我需要回忆之前的对话历史。用户之前询问了嵌入式C程序崩溃时如何查看core文件,我详细介绍了生成core文件的挑战、配置步骤、分析流程以及常见问题和替代方案。现在用户的问题更具体,直接询问如何在嵌入式系统中生成core文件。 我需要确定用户的需求。用户可能是一位嵌入式开发人员,遇到了程序崩溃的问题,但发现无法生成core文件,或者希望在开发过程中配置系统以方便后续调试。用户的真实需求可能是想了解具体的配置步骤和注意事项,特别是在资源受限的嵌入式环境中如何实现core文件的生成。 接下来,我需要考虑嵌入式环境的多样性。不同的嵌入式系统可能有不同的操作系统,比如Linux、RTOS(如FreeRTOS、Zephyr)或者裸机系统。生成core文件的方法在这些环境中会有所不同。比如,Linux嵌入式系统可能支持标准的core dump机制,而RTOS或裸机系统可能需要借助调试工具或自定义机制。 我需要分情况讨论。首先,针对Linux嵌入式系统,需要配置内核和系统参数,如ulimit、core_pattern等。然后,对于RTOS或无文件系统的情况,可能需要通过调试器或自定义内存转储函数来捕获内存快照。此外,还要考虑资源限制,比如存储空间不足的问题,可能需要将core文件保存到RAM或通过网络传输。 用户可能还关心实际操作的示例,比如具体的命令和代码片段。比如如何在Linux中设置ulimit,如何修改core_pattern,或者如何在RTOS中触发内存转储。同时,需要提醒用户注意符号文件的一致性、存储空间管理以及调试器配置等问题。 另外,用户可能遇到无法生成core文件的常见问题,比如权限不足、存储路径不可写、内核不支持等。需要给出相应的解决建议,比如检查内核配置、挂载存储设备、调整core文件大小限制等。 最后,需要总结关键点,帮助用户快速回顾,并强调根据具体环境选择合适的方法。可能还需要提到替代方案,比如实时调试或日志记录,以备无法生成core文件时使用。 总结来说,我需要结构清晰地将生成core文件的方法按不同嵌入式环境分类,给出具体步骤和示例,并涵盖常见问题及解决方案,确保用户能够根据自身情况实施配置。</think>在嵌入式系统中生成 `core` 文件(核心转储文件)是调试程序崩溃的重要手段,但由于嵌入式设备的资源限制(如存储空间小、无文件系统等),其配置方法与常规 Linux 环境有所不同。以下是针对不同嵌入式场景的 **`core` 文件生成方法**及注意事项: --- ### **1. 嵌入式 Linux 系统生成 `core` 文件** 若嵌入式设备运行 Linux 且支持核心转储功能,需按以下步骤配置: #### **步骤 1:配置内核支持 Core Dump** - 确保 Linux 内核编译时启用了 `CONFIG_COREDUMP` 选项(通常在 `Kernel hacking` 或 `Processor type and features` 菜单中)。 - 若设备使用精简内核(如 `Buildroot` 或 `Yocto` 定制),需手动添加此配置。 #### **步骤 2:设置系统参数** 在设备终端执行以下命令(或在启动脚本中添加): ```bash # 解除core文件大小限制 ulimit -c unlimited # 指定core文件保存路径(需确保路径可写) echo "/tmp/core.%e.%p" > /proc/sys/kernel/core_pattern # 可选:禁用core文件名追加PID echo "0" > /proc/sys/kernel/core_uses_pid ``` - **路径选择**: 若设备存储空间有限,可将 `core` 文件保存到临时目录(如 `/tmp`)或外接存储设备(如 SD 卡)。 #### **步骤 3:测试生成 Core 文件** - 触发一个崩溃(如空指针访问): ```c // test.c int main() { int *p = NULL; *p = 42; // 触发段错误 return 0; } ``` - 编译并运行: ```bash arm-linux-gnueabihf-gcc -g test.c -o test ./test ``` - 检查 `/tmp` 目录是否生成 `core` 文件(如 `core.test.1234`)。 --- ### **2. RTOS 或无文件系统设备生成 Core 文件** 对于运行 RTOS(如 FreeRTOS、Zephyr)或裸机系统的设备,通常无法直接生成标准 `core` 文件,但可通过以下替代方法捕获崩溃时的内存快照: #### **方法 1:通过调试器手动捕获内存** - **适用场景**:通过 JTAG/SWD 调试器(如 J-Link、ST-Link)连接的设备。 - **操作步骤**: 1. 使用 GDB 连接目标设备: ```bash arm-none-eabi-gdb -ex "target remote :3333" your_firmware.elf ``` 2. 当程序崩溃时,手动触发内存转储: ```bash (gdb) dump memory core.bin 0x20000000 0x2000C000 # 转储指定内存区域 ``` 3. 使用 `core.bin` 结合 ELF 文件离线分析崩溃现场。 #### **方法 2:自定义内存转储函数** 在代码中添加硬件异常处理函数,将关键内存区域保存到特定地址或通过串口发送: ```c // 示例:ARM Cortex-M 的 HardFault 处理函数 void HardFault_Handler(void) { uint32_t *sp = (uint32_t*)__get_MSP(); // 获取栈指针 uint32_t crash_pc = sp[6]; // PC 寄存器在栈中的位置 uint32_t crash_lr = sp[5]; // LR 寄存器 // 将关键数据保存到固定内存区域(如外部 Flash 或 RAM) memcpy((void*)0x2000F000, sp, 1024); // 保存栈内容 // 通过串口发送数据(需实现串口发送函数) uart_send_blocking("Crash PC: 0x%08x\n", crash_pc); while(1); // 挂起系统 } ``` --- ### **3. 资源受限设备的优化策略** 若设备存储空间不足,可采取以下优化: - **压缩 Core 文件**: 使用 `gzip` 压缩 `core` 文件(需设备支持): ```bash echo "| /bin/gzip > /tmp/core.gz" > /proc/sys/kernel/core_pattern ``` - **网络传输**: 将 `core` 文件直接发送到远程服务器(需网络支持): ```bash echo "| nc 192.168.1.100 1234" > /proc/sys/kernel/core_pattern ``` - **仅保存关键内存区域**: 通过脚本限制 `core` 文件大小(如只保存栈和堆): ```bash echo "/opt/save_core.sh %e %p" > /proc/sys/kernel/core_pattern ``` ```bash # save_core.sh 示例(需自定义) #!/bin/sh gdb -batch -ex "dump memory /tmp/core-$1-$2 0x20000000 0x2000C000" ./your_program.elf ``` --- ### **4. 常见问题与解决** #### **(1) 无法生成 Core 文件** - **可能原因**: - 内核未启用 `CONFIG_COREDUMP`。 - 存储路径不可写(如 `/tmp` 未挂载为可写文件系统)。 - 程序权限不足(需 `root` 或 `CAP_SYS_PTRACE` 权限)。 - **解决**: - 检查内核配置,挂载存储路径为可写: ```bash mount -t tmpfs tmpfs /tmp ``` - 以 `root` 权限运行程序。 #### **(2) Core 文件不完整** - **可能原因**:存储空间不足或程序崩溃时系统资源耗尽。 - **解决**:限制 `core` 文件大小(需权衡信息完整性): ```bash ulimit -c 1024 # 限制 core 文件为 1KB ``` --- ### **5. 关键注意事项** - **调试符号**:确保程序编译时添加 `-g` 选项以包含调试信息。 - **一致性**:分析 `core` 文件时,主机上的 ELF 文件必须与设备运行的二进制文件完全一致。 - **实时性**:某些嵌入式系统在崩溃后会重启,需在重启前保存 `core` 文件(如通过看门狗延迟)。 --- ### **总结** 在嵌入式系统中生成 `core` 文件的核心要点: 1. **Linux 设备**:配置内核支持,设置 `ulimit` 和 `core_pattern`。 2. **RTOS/裸机**:通过调试器捕获内存或自定义异常处理函数。 3. **资源优化**:压缩、网络传输或仅保存关键内存区域。 根据实际硬件环境和调试需求选择合适的方法,并结合交叉编译工具链(如 `arm-none-eabi-gdb`)分析崩溃原因。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值