在x86+Linux上寫的程式,在PC機上執行得很好。可是使用ARM的gcc進行交叉編譯,再送到DaVinci目標板上執行的時候,出現了Bus error。
出現的位置如下(其中Debug的內容是我在程式中新增的除錯資訊):
[email protected]:~# arm_v5t_le-gcc -g shit.c
[email protected]:~# ./a.out
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52
Bus error
開啟偵錯程式進行除錯:
[email protected]:~# gdb a.out
GNU gdb 6.3 (MontaVista 6.3-20.0.22.0501131 2005-07-22)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "armv5tl-montavista-linuxeabi"...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) run // 執行程式
Starting program: /home/zpf/a.out
Debug: in get_program_info()
Debug: in conn_server(char *err_buf_ptr)
Debug: gonna create a socket
Debug: gonna fill in the serv_addr structure
Debug: gonna connect to a server
Debug: gonna send LIN_RST
Debug: in my_sendn()
Debug: send 4 bytes to server:
Debug: gonna receive LIN_RSP
Debug: in my_recvn()
Debug: nleft = 3
Debug: received first 3 bytes from server: 7
Debug: gonna check if 3rd byte is the package type
Debug: received package length = 55
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52

Program received signal SIGBUS, Bus error. // 在這裡出現了錯誤
0x00009624 in alloc_prog_mem (detail_buf=0x13118 "/001/002",
err_buf_ptr=0xbefffc40 "") at shit.c:631
631 g_data_ptr->progtype_num = *(short *)ptr ;
(gdb) print ptr // 檢視一下ptr的值
$1 = 0x13119 "/002" // 地址起始是奇數!!!
(gdb) set ptr=0x1311a// 想改一下
(gdb) continue
Continuing.

Program terminated with signal SIGBUS, Bus error.
The program no longer exists. // 可惜程式已經退出
(gdb) quit

其中,g_data_ptr->progtype_num是一個short型別的值。
把強制型別轉換改為用memcpy()寫值之後,再除錯
[email protected]:~# gdb test
GNU gdb 6.3 (MontaVista 6.3-20.0.22.0501131 2005-07-22)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "armv5tl-montavista-linuxeabi"...Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) break 626 // 把剛剛的那句強制型別轉換變成記憶體拷貝
Breakpoint 1 at 0x9630: file test.c, line 626.
(gdb) run
Starting program: /home/zpf/test
Debug: in get_program_info()
Debug: in conn_server(char *err_buf_ptr)
Debug: gonna create a socket
Debug: gonna fill in the serv_addr structure
Debug: gonna connect to a server
Debug: gonna send LIN_RST
Debug: in my_sendn()
Debug: send 4 bytes to server:
Debug: gonna receive LIN_RSP
Debug: in my_recvn()
Debug: nleft = 3
Debug: received first 3 bytes from server: 7
Debug: gonna check if 3rd byte is the package type
Debug: received package length = 55
Debug: malloc space for the actual data: temp_buf = 0x13118
Debug: in my_recvn()
Debug: nleft = 52

Breakpoint 1, alloc_prog_mem (detail_buf=0x13118 "/001/002",
err_buf_ptr=0xbefffc40 "") at test.c:626
warning: Source file is more recent than executable.

626 memcpy(&(g_data_ptr->prog_num), ptr, 2) ; // 在這一句中斷
(gdb) print ptr // 再看看ptr
$1 = 0x1311b "/003" // 還是奇數地址
(gdb) continue // 繼續執行
Continuing.
Debug: sum_progtype = 2 , sum_prog = 3
Debug: gonna malloc space for progtype_ptr
Debug: gonna malloc space for prog_ptr
Debug: in mv_pkg2prog_list()
Debug: gonna set ProgramType program_type_name
Debug: ProgramType program_type_name set OK
Debug: in $ == *ptr, j = 0
Debug: g_data_ptr->progtype_ptr[j].prog_ptr = temp_prog_ptr
Debug: in @ == *ptr, ptr = 0x13126
Debug: temp_prog_ptr->format = *ptr
Debug: temp_prog_ptr->program_id = *(int *)ptr
Debug: gonna set Program program_name
Debug: Program program_name set OK
Debug: finished one loop of while
Debug: in @ == *ptr, ptr = 0x1312f
Debug: temp_prog_ptr->format = *ptr
Debug: temp_prog_ptr->program_id = *(int *)ptr
Debug: gonna set Program program_name
Debug: Program program_name set OK
Debug: finished one loop of while
Debug: gonna set ProgramType program_type_name
Debug: ProgramType program_type_name set OK
Debug: in $ == *ptr, j = 1
Debug: g_data_ptr->progtype_ptr[j].prog_ptr = temp_prog_ptr
Debug: in @ == *ptr, ptr = 0x13142
Debug: temp_prog_ptr->format = *ptr
Debug: temp_prog_ptr->program_id = *(int *)ptr
Debug: gonna set Program program_name
Debug: Program program_name set OK
Debug: finished one loop of while
program type[0]
program_type_id = 1
program_type_name = love
program_num = 2
prog_ptr = 0x131d8
program[0]
program_id = 1001
program_name = you
format = 1
program[1]
program_id = 1002
program_name = me
format = 2
program type[1]
program_type_id = 2
program_type_name = hatred
program_num = 1
prog_ptr = 0x13248
program[0]
program_id = 2005
program_name = kill
format = 5
Debug: gonna return an OK
Debug: Entering send_exit_requstion()
Debug: in conn_server(char *err_buf_ptr)
Debug: gonna create a socket
Debug: gonna fill in the serv_addr structure
Debug: gonna connect to a server
Debug: gonna send EXIT_RST
Debug: in my_sendn()
Debug: send 4 bytes to server:
Debug: in my_recvn()
Debug: nleft = 4
Debug: gonna return an OK

Program exited normally. // 執行通過了!!!!
(gdb)

總結:
問題總算找到了,就是我企圖在一個奇數地址起始的地方強制型別轉換得到一個short值。
在Intel系列處理器上,可以在任一奇數記憶體地址儲存任何變數或陣列,不會導致任何致命的錯誤影響,只是效率可能會降低。但在DaVinci上,這一點不行。所以必須對大於一個位元組的資料型別小心謹慎,比較安全的方法是使用記憶體拷貝函式memcpy(),或者使用下面的代替方法:
// 先定義一個聯合體
union {
short short_val ;
char short_byte[2] ;
} myshort ;
// 然後,把程式中本來應該是
// g_data_ptr->progtype_num = *(short *)ptr ;
// ptr += 2 ;
// 這兩句的地方換成下面五句:
myshort.short_byte[0] = *ptr ;
ptr++ ;
myshort.short_byte[1] = *ptr ;
ptr++ ;
g_data_ptr->progtype_num = myshort.short_val ;
// 當然,最簡單的方法是換成下面兩句:
// memcpy(&(g_data_ptr->progtype_num), ptr, 2) ;
// ptr += 2 ;

對於這個問題的進一步探討:

在DaVinci上應該注意記憶體編址模式的問題。
struct {
char struc_char ;
int struc_int ;
short struc_short ;
long struct_long ;
} struc_val ;
在寬鬆模式下,儘管struc_char只有1個位元組,struc_short佔2個位元組,但編譯器可能給這兩個變數分別分配了4個位元組,結果整個結構的大小變成了16個位元組,而在編譯器設為緊湊模式時,則正好是11個位元組。根據計算機資料匯流排的位數,不同的編址模式存取資料的速度不一樣。我認為在符合匯流排字長的情況下,效率是最高的,因為只需進行一次匯流排操作。
記憶體編址模式會影響位元組對齊方式,位元組對齊操作可以解決以下兩個主要的問題:
1.訪存效率問題;一般的編譯器要對記憶體進行對齊,在處理變數時,編譯器會根據一定的設定將長短不同的變數的資料長度進行對齊以加快記憶體處理速度。
2.強制型別轉換問題:在x86上,位元組不對齊的操作只會影響效率,但是在DaVinci上,可能就是一個Bus error, 因為它要求必須位元組對齊。
位元組對齊的準則
1.資料型別自身的對齊值:對於char型資料,其自身對齊值為1,對於short型為2,對於int,long,float,double型別,其自身對齊值為4位元組。
2.結構體的自身對齊值:其成員中自身對齊值最大的那個值。
3.指定對齊值:#pragma pack (value)時的指定對齊值value。
4.資料成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值。
對於平時定義變數,儘可能先定義長度為4的倍數的變數,然後是長度是2的變數,最後是長度為1的變數。

通過測試,GCC編譯器是按照4位元組對齊存放於記憶體的。而我還沒有發現更改編址模式的引數。程式如下:
#include
.