ARM Linux崩潰分析(一) - 應用程序Segmentation fault的分析

一、測試代碼

測試代碼如下所示:

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


void getmemory(char *p)
{
	p=(char *)malloc(100);
	strcpy(p,"helloworld");
	return;
}
void test1_printf()
{
	printf("aefawfaeeaa  /n");
	return;
}


void test1_printf2()
{
	int bbb  = 0x5555;
	
	printf("test = %d  /n",bbb);
	return;
}


void test1_printf3()
{
	printf("aefawfaeeaa  /n");
	return;
}


void test1_printf4()
{
	int bbb  = 0x5555;
	
	printf("test = %d  /n",bbb);
	return;
}



void teswtaaaaa(void )
{
		char*str=NULL;
	getmemory(str);
	*str = 12;
	printf("%s /n",str);
	return;
}




int main(int argc,char *argv[])
{
	test1_printf();

	test1_printf2();
	

	teswtaaaaa();


	test1_printf3();


	test1_printf4();

	
	
#if 0
	char strss[10] = {0};
	strss[11] = 6;
	
	printf("%s /n",strss);
	
#endif


	return 0;
}

編譯以Hisi3559A爲例
生成.o :aarch64-himix100-linux-gcc -c test.c -Wall -Werror -g
生成可執行文件:aarch64-himix100-linux-gcc -o test test.o -Wall -Werror -g

說明:
-Wall :選項打開所有最常用到的編譯警告,強烈建議打開,可以捕捉到許多在C編程中最常發生的錯誤。
-Werror:將警告當做錯誤
-g:. 創建符號表,符號表包含了程序中使用的變量名稱的列表。
. 關閉所有的優化機制,以便程序執行過程中嚴格按照原來的C代碼進行

二、測試運行

放到板上執行:
出錯了。
錯誤信息如下:

test[1775]: unhandled level 2 translation fault (11) at 0x00000000, esr 0x92000046
pgd = ffffffc020807000
[00000000] *pgd=00000000617ac003, *pud=00000000617ac003
, *pmd=0000000000000000

CPU: 1 PID: 1775 Comm: test Tainted: P           O    4.9.37 #1
Hardware name: Hisilicon HI3559AV100 DEMO Board (DT)
task: ffffffc021f53000 task.stack: ffffffc02100c000
PC is at 0x4006d8
LR is at 0x4006d0
pc : [<00000000004006d8>] lr : [<00000000004006d0>] pstate: 60000000
sp : 0000007ffbbcae20
x29: 0000007ffbbcae20 x28: 0000000000000000 
x27: 0000000000000000 x26: 0000000000000000 
x25: 0000000000000000 x24: 0000000000000000 
x23: 0000000000000000 x22: 0000000000000000 
x21: 0000000000400490 x20: 0000000000000000 
x19: 0000000000400728 x18: 0000000000000000 
x17: 0000000000411000 x16: 0000007fabc52780 
x15: 0000000000000482 x14: 0000000000000005 
x13: 0000000000000000 x12: 0000000000000000 
x11: 0000000000000018 x10: 0101010101010101 
x9 : ffffff80ffffffc8 x8 : 0000000000000003 
x7 : 000000000000007b x6 : 000000006e2f2020 
x5 : 0000000000000071 x4 : 0000000000413020 
x3 : 0000000000000071 x2 : 726f776f6c6c6568 
x1 : 000000000000000c x0 : 0000000000000000 

三 、錯誤分析

3.1 ARM PC寄存器

PC 代表程序計數器,流水線使用三個階段,因此指令分爲三個階段執行:1.取指(從存儲器裝載一條指令);2.譯碼(識別將要被執行的指令);3.執行(處理 指令並將結果寫回寄存器)。而R15(PC)總是指向“正在取指”的指令,而不是指向“正在執行”的指令或正在“譯碼”的指令

3.2 ARM LR寄存器:

異常的發生會導致程序正常運行的被打斷, 並將控制流轉移到相應的異常處理(異常響應),有些異常(fiq、irq)事件處理後,系統還希望能回 到當初異常發生時被打斷的源程序斷點處繼續完成源程序的執行(異常返回),這就需要一種解決方案, 用於記錄源程序的斷點位置,以便正確的異常返回。
類似的還有子程序的調用和 返回。在主程序中(通過子程序調用指令)調用子程序時,也需要記錄下主程序中的調用點位置,以便將來的子程序的返回。
在ARM處理器中使用 R14實現對斷點和調用點的記錄,即使用R14用作返 回連接寄存器(LR)。在硬件上和指令執行上,CPU 自動完成相應返回點的記錄。在ARM 彙編語言程序設計時,R14和LR通用。
ARM處理器相應異常時,會自動完成將當前的PC保存到LR寄存器。

出錯時 這兩個寄存器值如下:

PC is at 0x4006d8
LR is at 0x4006d0

3.4 nm命令

3.4 nm命令被用於顯示二進制目標文件的符號表

aarch64-himix100-linux-nm test

顯示如下:

         U abort@@GLIBC_2.17
0000000000411040 B __bss_end__
0000000000411040 B _bss_end__
0000000000411038 B __bss_start
0000000000411038 B __bss_start__
00000000004004d8 t call_weak_fn
0000000000411038 b completed.7391
0000000000411028 D __data_start
0000000000411028 W data_start
00000000004004f0 t deregister_tm_clones
0000000000400570 t __do_global_dtors_aux
0000000000410df8 t __do_global_dtors_aux_fini_array_entry
0000000000411030 D __dso_handle
0000000000410e08 d _DYNAMIC
0000000000411038 D _edata
0000000000411040 B _end
0000000000411040 B __end__
00000000004007a4 T _fini
00000000004005a0 t frame_dummy
0000000000410df0 t __frame_dummy_init_array_entry
00000000004007f8 r __FRAME_END__
00000000004005e0 T getmemory
0000000000410fd8 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000400408 T _init
0000000000410df8 t __init_array_end
0000000000410df0 t __init_array_start
00000000004007b8 R _IO_stdin_used
0000000000410e00 d __JCR_END__
0000000000410e00 d __JCR_LIST__
00000000004007a0 T __libc_csu_fini
0000000000400728 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.17
00000000004006f8 T main
                 U malloc@@GLIBC_2.17
                 U printf@@GLIBC_2.17
0000000000400530 t register_tm_clones
0000000000400490 T _start
0000000000400624 T test1_printf
0000000000400644 T test1_printf2
0000000000400670 T test1_printf3
0000000000400690 T test1_printf4
00000000004006bc T teswtaaaaa
0000000000411038 D __TMC_END__

3.5 objdump命令

objdump命令是Linux下的反彙編目標文件或者可執行文件的命令,它以一種可閱讀的格式讓你更多地瞭解二進制文件可能帶有的附加信息

 aarch64-himix100-linux-objdump -x -s -d test

截取部分內容:

00000000004006bc <teswtaaaaa>:
  4006bc:	a9be7bfd 	stp	x29, x30, [sp, #-32]!
  4006c0:	910003fd 	mov	x29, sp
  4006c4:	f9000fbf 	str	xzr, [x29, #24]
  4006c8:	f9400fa0 	ldr	x0, [x29, #24]
  4006cc:	97ffffc5 	bl	4005e0 <getmemory>
  4006d0:	f9400fa0 	ldr	x0, [x29, #24]
  4006d4:	52800181 	mov	w1, #0xc                   	// #12
  4006d8:	39000001 	strb	w1, [x0]
  4006dc:	90000000 	adrp	x0, 400000 <_init-0x408>
  4006e0:	911fc000 	add	x0, x0, #0x7f0
  4006e4:	f9400fa1 	ldr	x1, [x29, #24]
  4006e8:	97ffff66 	bl	400480 <printf@plt>
  4006ec:	d503201f 	nop
  4006f0:	a8c27bfd 	ldp	x29, x30, [sp], #32
  4006f4:	d65f03c0 	ret

可以看到是函數teswtaaaaa中的某條指定錯誤

3.6 addr2line 工具

使用addr2line工具,有的時候可以直接使用這個工具定位到應用程序的行數:

 addr2line  0x4006d8   -e test -f

打印如下:

$ addr2line  0x4006d8   -e test -f
teswtaaaaa
/home/lie/code_test/test.c:49

結束!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章