gcc編譯出來的庫出現buserror
1. gcc編譯出現錯誤怎麼辦
有時候我們編譯一個大的項目的時候,會出現很多錯誤使得屏幕堆滿了很多無用的信息。一般情況下我們需要找到首次出現錯誤的地方,在gcc中添加編譯選項可以使編譯停止在第一次出現錯誤的地方:
$ gcc -Wfatal-errors foo.c // GCC 4.0 and later$ g++ -Wfatal-errors foo.cpp
$ g++ -fmax-errors=N foo.cpp // 在出現第 N 此錯誤的時候停止編譯,GCC 4.6 and later
2. 為什麼程序會出現Bus Error
最近在論壇上看到一個程序員在詢問Bus Error的問題。他有一個非常簡單的測試程序,將一個short數組中第二個成員變數的地址附給一個int型的指針,接著給該指針賦值,大致代碼如下:
short array[10];
int * p = (int *) &array[1];*p = 1;
運行的平台是Solaris for SPARC,使用的編譯器是GCC,程序執行到指針賦值的時候的出現Bus Error出錯信息,程序coremp。他覺得非常奇怪,代碼在X86平台運行的很好。其實這個問題完全是由於CPU硬體不同所造成的。
Bus Error通常都是因為非對齊訪問造成的。CPU在設計上為了性能上的考慮,要求待訪問,操作的數據地址都要對齊。如果發現沒有對齊的訪問,就會向當前進程發出SIGBUS信號,使程序崩潰。RISC包括SPARC都是這種類型的晶元。而X86架構就沒有這種對齊要求。所以這段代碼在Solaris 10 for X86下面沒有問題,當然這是有性能的代價。Bus Error也有可能是因為機器物理問題或者訪問無效物理地址,但這種情況非常少見。
上面的代碼正是這種非對齊訪問造成的Bus Error。short是兩個位元組,其地址偏移量必是2的倍數。而對於int指針來說,需要偏移量地址是4的倍數的數據,所以直接用int指針來操作地址,就很有可能導致系統發出SIGBUS信號,程序崩潰。
這段程序如果用Sun Studio編譯器的話,運行就沒有問題。這是因為Sun Studio默認對32位編譯使用的參數是-xmemalign=8i,其中i選項設置明確不會產生這種SIGBUS信號。不過如果編譯成64位程序,Sun Studio使用的-xmemalign=8s,其中s選項設置意味對這種非對齊訪問產生SIGBUS信號,仍舊會遇到這個錯誤。
當然你也可以堅持在SPARC上使用GCC去編譯這種代碼。GCC有一個Type Attributes特性,在需人工對齊的變數後加上:__attribute__ ((aligned (4))); 4就是指定偏移量。比如:
short array[10] __attribute__ ((aligned (4)));
不過這個屬性只對Linker連接器可見的變數有效,也就是說對local variable無效。而且這種特性只能照顧到該數組,即第一個元素,並不為數組的每個成員設置偏移量。
如果一定要針對local variable或者數組的每個成員進行偏移量設置,可以考慮定義一個union的類型:union {short s;int i;}
3. 如何解決bus error
在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 ing" 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".
4. 為什麼程序會出現Bus Error
最近在論壇上看到一個程序員在詢問Bus Error的問題。他有一個非常簡單的測試程序,將一個short數組中第二個成員變數的地址附給一個int型的指針,接著給該指針賦值,大致代碼如下:
short array[10];
int * p = (int *) &array[1];
*p = 1;
運行的平台是Solaris for SPARC,使用的編譯器是GCC,程序執行到指針賦值的時候的出現Bus Error出錯信息,程序coremp。他覺得非常奇怪,代碼在X86平台運行的很好。其實這個問題完全是由於CPU硬體不同所造成的。
Bus Error通常都是因為非對齊訪問造成的。CPU在設計上為了性能上的考慮,要求待訪問,操作的數據地址都要對齊。如果發現沒有對齊的訪問,就會向當前進程發出SIGBUS信號,使程序崩潰。RISC包括SPARC都是這種類型的晶元。而X86架構就沒有這種對齊要求。所以這段代碼在Solaris 10 for X86下面沒有問題,當然這是有性能的代價。Bus Error也有可能是因為機器物理問題或者訪問無效物理地址,但這種情況非常少見。
上面的代碼正是這種非對齊訪問造成的Bus Error。short是兩個位元組,其地址偏移量必是2的倍數。而對於int指針來說,需要偏移量地址是4的倍數的數據,所以直接用int指針來操作地址,就很有可能導致系統發出SIGBUS信號,程序崩潰。
這段程序如果用Sun Studio編譯器的話,運行就沒有問題。這是因為Sun Studio默認對32位編譯使用的參數是-xmemalign=8i,其中i選項設置明確不會產生這種SIGBUS信號。不過如果編譯成64位程序,Sun Studio使用的-xmemalign=8s,其中s選項設置意味對這種非對齊訪問產生SIGBUS信號,仍舊會遇到這個錯誤。
當然你也可以堅持在SPARC上使用GCC去編譯這種代碼。GCC有一個Type Attributes特性,在需人工對齊的變數後加上:__attribute__ ((aligned (4))); 4就是指定偏移量。比如:
short array[10] __attribute__ ((aligned (4)));
不過這個屬性只對Linker連接器可見的變數有效,也就是說對local variable無效。而且這種特性只能照顧到該數組,即第一個元素,並不為數組的每個成員設置偏移量。
如果一定要針對local variable或者數組的每個成員進行偏移量設置,可以考慮定義一個union的類型:
union {
short s;
int i;
}
5. 為什麼程序會出現Bus Error
最近在論壇上看到一個程序員在詢問Bus Error的問題。他有一個非常簡單的測試程序,將一個short數組中第二個成員變數的地址附給一個int型的指針,接著給該指針賦值,大致代碼如下:
short array[10];
int * p = (int *) &array[1];*p = 1;
運行的平台是Solaris for SPARC,使用的編譯器是GCC,程序執行到指針賦值的時候的出現Bus Error出錯信息,程序coremp。他覺得非常奇怪,代碼在X86平台運行的很好。其實這個問題完全是由於CPU硬體不同所造成的。
Bus Error通常都是因為非對齊訪問造成的。CPU在設計上為了性能上的考慮,要求待訪問,操作的數據地址都要對齊。如果發現沒有對齊的訪問,就會向當前進程發出SIGBUS信號,使程序崩潰。RISC包括SPARC都是這種類型的晶元。而X86架構就沒有這種對齊要求。所以這段代碼在Solaris 10 for X86下面沒有問題,當然這是有性能的代價。Bus Error也有可能是因為機器物理問題或者訪問無效物理地址,但這種情況非常少見。
6. 程序報出bus err 錯誤後什麼不能用backtrace沒有輸出內容
×
loading..
資訊
安全
論壇
下載
讀書
程序開發
資料庫
系統
網路
電子書
微信學院
站長學院
QQ
手機軟體
考試
軟體開發|
web前端|
Web開發|
移動開發|
綜合編程|
首頁 > 程序開發 > 軟體開發 > C語言 > 正文
善用backtrace解決大問題
2011-07-22
0 個評論
收藏
我要投稿
一.用途:
主要用於程序異常退出時尋找錯誤原因
二.功能:
回溯堆棧,簡單的說就是可以列出當前函數調用關系
三.原理:
1. 通過對當前堆棧的分析,找到其上層函數在棧中的幀地址,再分析上層函數的堆棧,再找再上層的幀地址……一直找到最頂層為止,幀地址指的是一塊:在棧上存放局部變數,上層返回地址,及寄存器值的空間。
2.
由於不同處理器堆棧方式不同,此功能的具體實現是編譯器的內建函數__buildin_frame_address及
__buildin_return_address中,它涉及工具glibc和gcc,
如果編譯器不支持此函數,也可自己實現此函數,舉例中有arm上的實現
四.方法:
在程序中加入backtrace及相關函數調用
五.舉例:
1. 一般backtrace的實現
i. 程序
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define PRINT_DEBUG
static void print_reason(int sig, siginfo_t * info, void *secret)
{
void *array[10];
size_t size;
#ifdef PRINT_DEBUG
char **strings;
size_t i;
size = backtrace(array, 10);
strings = backtrace_symbols(array, size);
printf("Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++)
printf("%s\n", strings[i]);
free(strings);
#else
int fd = open("err.log", O_CREAT | O_WRONLY);
size = backtrace(array, 10);
backtrace_symbols_fd(array, size, fd);
close(fd);
#endif
exit(0);
}
void die()
{
char *test1;
char *test2;
char *test3;
char *test4 = NULL;
strcpy(test4, "ab");
}
void test1()
{
die();
}
int main(int argc, char **argv)
{
struct sigaction myAction;
myAction.sa_sigaction = print_reason;
sigemptyset(&myAction.sa_mask);
myAction.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &myAction, NULL);
sigaction(SIGUSR1, &myAction, NULL);
sigaction(SIGFPE, &myAction, NULL);
sigaction(SIGILL, &myAction, NULL);
sigaction(SIGBUS, &myAction, NULL);
sigaction(SIGABRT, &myAction, NULL);
sigaction(SIGSYS, &myAction, NULL);
test1();
}
ii. 編譯參數
gcc main.c -o test -g -rdynamic
2. 根據不同的處理器自已實現backtrace
i. arm的backtrace函數實現
static int backtrace_xy(void **BUFFER, int SIZE)
{
volatile int n = 0;
volatile int *p;
volatile int *q;
volatile int ebp1;
volatile int eip1;
volatile int i = 0;
p = &n;
ebp1 = p[4];
eip1 = p[6];
fprintf(stderr, "======================= backtrace_xy addr: 0x%0x, param1: 0x%0x, param2: 0x%0x\n",
backtrace_xy, &BUFFER, &SIZE);
fprintf(stderr, "n addr is 0x%0x\n", &n);
fprintf(stderr, "p addr is 0x%0x\n", &p);
for (i = 0; i < SIZE; i++)
{
fprintf(stderr, "ebp1 is 0x%0x, eip1 is 0x%0x\n", ebp1, eip1);
BUFFER[i] = (void *)eip1;
p = (int*)ebp1;
q = p - 5;
eip1 = q[5];
ebp1 = q[2];
if (ebp1 == 0 || eip1 == 0)
break;
}
fprintf(stderr, "total level: %d\n", i);
return i;
}
六.舉例2:
/*main.c*/
#include "sigsegv.h"
#include <string.h>
int die() {
char *err = NULL;
strcpy(err, "gonner");
return 0;
}
int main() {
return die();
}
/*sigsegv.c*/
#define _GNU_SOURCE
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include <dlfcn.h>
#include <execinfo.h>
#define NO_CPP_DEMANGLE
#ifndef NO_CPP_DEMANGLE
#include <cxxabi.h>
#endif
#if defined(REG_RIP)
# define SIGSEGV_STACK_IA64
# define REGFORMAT "%016lx"
#elif defined(REG_EIP)
# define SIGSEGV_STACK_X86
# define REGFORMAT "%08x"
#else
# define SIGSEGV_STACK_GENERIC
# define REGFORMAT "%x"
#endif
static void signal_segv(int signum, siginfo_t* info, void*ptr) {
static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};
size_t i;
ucontext_t *ucontext = (ucontext_t*)ptr;
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
int f = 0;
Dl_info dlinfo;
void **bp = 0;
void *ip = 0;
#else
void *bt[20];
char **strings;
size_t sz;
#endif
fprintf(stderr, "Segmentation Fault!\n");
fprintf(stderr, "info->si_signo = %d\n", signum);
fprintf(stderr, "info->si_errno = %d\n", info->si_errno);
// fprintf(stderr, "info->si_code = %d (%s)\n", info->si_code, info->si_codes[si_code]);
fprintf(stderr, "info->si_addr = %p\n", info->si_addr);
for(i = 0; i < NGREG; i++)
fprintf(stderr, "reg[%02d] = 0x" REGFORMAT "\n", i, ucontext->uc_mcontext.gregs[i]);
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
# if defined(SIGSEGV_STACK_IA64)
ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
# elif defined(SIGSEGV_STACK_X86)
ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
# endif
fprintf(stderr, "Stack trace:\n");
while(bp != & ip) {
if(!dladdr(ip, &dlinfo))
break;
const char *symname = dlinfo.dli_sname;
#ifndef NO_CPP_DEMANGLE
int status;
char *tmp = __cxa_demangle(symname, NULL, 0, &status);
if(status == 0 !=& tmp)
symname = tmp;
#endif
fprintf(stderr, "% 2d: %p < %s+%u> (%s)\n",
++f,
ip,
symname,
(unsigned)(ip - dlinfo.dli_saddr),
dlinfo.dli_fname);
#ifndef NO_CPP_DEMANGLE
if(tmp)
free(tmp);
#endif
if(dlinfo.dli_sname != !strcmp(dlinfo.dli_sname, "main"))
break;
ip = bp[1];
bp = (void**)bp[0];
}
#else
fprintf(stderr, "Stack trace (non-dedicated):\n");
sz = backtrace(bt, 20);
strings = backtrace_symbols(bt, sz);
for(i = 0; i < sz; ++i)
fprintf(stderr, "%s\n", strings[i]);
#endif
fprintf(stderr, "End of stack trace\n");
exit (-1);
}
int setup_sigsegv() {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_sigaction = signal_segv;
action.sa_flags = SA_SIGINFO;
if(sigaction(SIGSEGV, &action, NULL) < 0) {
perror("sigaction");
return 0;
}
return 1;
}
#ifndef SIGSEGV_NO_AUTO_INIT
static void __attribute((constructor)) init(void)
{
setup_sigsegv();
}
#endif
/*sigsegv.h*/
#ifndef __sigsegv_h__
#define __sigsegv_h__
#ifdef __cplusplus
extern "C" {
#endif
int setup_sigsegv();
#ifdef __cplusplus
}
#endif
#endif /* __sigsegv_h__ */
編譯時需要加入-rdynamic -ldl –ggdb
void
handle_signal_error(int rec_signal,siginfo_t* signal_info,void* context)
{
NE_Info* __attribute__ ((unused)) ne_info = NULL;
struct sigaction action;
FILE* file;
void* backtr[NUMBER_OF_BACKTRACE];
cpal_uns32 __attribute__ ((unused)) i = 0;
cpal_uns32 backtr_size = 0;
ucontext_t *u_context;
time_t seconds_time;
struct tm* time_struct;
cpal_si32 ret_t;
char filename[SIZE_OF_FILENAME];
if(g_handler_running)
return;
g_handler_running = CPAL_TRUE;
ret_t = time(&seconds_time);
if(ret_t != - 1)
{
time_struct = gmtime(&seconds_time);
snprintf(filename,SIZE_OF_FILENAME,"%s%d%d%d-%d%d%d-%s",BACKTRACE_FILE_PATH,time_struct->tm_mon,time_struct->tm_mday,
(time_struct->tm_year-100)+2000,time_struct->tm_hour,time_struct->tm_min,time_struct->tm_sec,BACKTRACE_FILE);
}
else
{
snprintf(filename,SIZE_OF_FILENAME,"%s",BACKTRACE_FILE);
}
file = fopen(filename,"w");
if(file == NULL)
{
return;
}
if(signal_info == NULL)
{
return;
}
if(context == NULL)
{
return;
}
u_context = (ucontext_t*)context;
/*Restore the default action for this signal and re-raise it, so that the default action occurs. */
action.sa_sigaction = SIG_DFL;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART;
sigaction(rec_signal,&action,NULL);
/* Print out the backtrace. */
backtr_size = backtrace(backtr,20);
/* The backtrace points to sigaction in libc, not to where the signal was actually raised.
This overwrites the sigaction with where the signal was sent, so we can resolve the sender. */
#if __WORDSIZE == 64
backtr[1] = (void*)u_context->uc_mcontext.gregs[REG_RIP];
#else
backtr[1] = (void*)u_context->uc_mcontext.gregs[REG_EIP];
#endif //__WORDSIZE
backtrace_symbols_fd(backtr,backtr_size,fileno(file));
fprintf(file,"Backtrace is above.\nFatal signal %d received.\n",rec_signal);
#if __WORDSIZE == 64
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info->si_addr,
u_context->uc_mcontext.gregs[REG_RIP]);
#else
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info->si_addr,
u_context->uc_mcontext.gregs[REG_EIP]);
#endif //__WORDSIZE
#if CPAL_LM_DEBUG
/* Print all NE_Infos */
for(; i < MAX_NO_OF_CONNS; i++)
{
ne_info = g_ne_hash_tab[i];
while(ne_info != NULL)
{
ne_info = ne_info->next_ne;
}
}
#endif
fflush(file);
fclose(file);
sleep (50); /* Sleep for 50 seconds */
g_handler_running = *_FALSE;
raise(rec_signal);
}
7. 如何解決bus error
在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 ing" 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 ing" 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
int main()
{
struct {
char struc_char ;
int struc_int ;
short struc_short ;
long struct_long ;
} struc_val ;
char c_char ;
int i_int ;
short s_short ;
long l_long ;
printf("sizeof(struc_val) = %d\n", sizeof(struc_val));
printf("sizeof(c_char) = %d\n", sizeof(c_char));
printf("sizeof(i_int) = %d\n", sizeof(i_int));
printf("sizeof(s_short) = %d\n", sizeof(s_short));
printf("sizeof(l_long) = %d\n", sizeof(l_long));
printf("address of struc_val = %p\n", &struc_val);
printf("address of struc_char = %p\n", &(struc_val.struc_char));
printf("address of struc_int = %p\n", &(struc_val.struc_int));
printf("address of struc_short = %p\n", &(struc_val.struc_short));
printf("address of struct_long = %p\n", &(struc_val.struct_long));
printf("address of c_char = %p\n", &c_char);
printf("address of i_int = %p\n", &i_int);
printf("address of s_short = %p\n", &s_short);
printf("address of l_long = %p\n", &l_long);
return 0 ;
}
測試結果:
sizeof(struc_val) = 16
sizeof(c_char) = 1
sizeof(i_int) = 4
sizeof(s_short) = 2
sizeof(l_long) = 4
address of struc_val = 0xbf885278
address of struc_char = 0xbf885278
address of struc_int = 0xbf88527c
address of struc_short = 0xbf885280
address of struct_long = 0xbf885284
address of c_char = 0xbf885277
address of i_int = 0xbf885270
address of s_short = 0xbf88526e
address of l_long = 0xbf885268
所以對於一個32位的數據來講,如果其沒有在4位元組整除的內存地址處存放,那麼處理器就需要2個匯流排周期對其進行訪問。
0x08 | byte8 | byte9 | byteA | byteB |
0x04 | byte4 | byte5 | byte6 | byte7 |
0x00 | byte0 | byte1 | byte2 | byte3 |
對於我剛剛的那個出現Bus error的程序,假設指針ptr剛好是指向了byte3(地址是0x0),然後想進行short強制類型轉換,使用byte3,byte4來構成一個short類型的值,由於第一次匯流排的數據只有byte0,byte1,byte2,byte3,取不到byte4,這在DaVinci板子上,就是一個Bus error了,因為沒有達到邊界對齊。如果ptr指的是byte2(地址0x02),就沒有問題了。因為0x02地址值是sizeof(short)的整數倍。
8. arm-linux-gcc 編譯簡單的helloworld 程序出現bus error,這有什麼辦法解決嗎
是gcc的問題。
要不你下載2009q3試試.
http://crztech.iptime.org:8080/Release/Toolchain/arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
我測試了,沒問題,編譯出來的程序在目標板(arm11,cortex-a8)上運行也正常。
9. 為什麼程序會出現Bus Error
最近在論壇上看到一個程序員在詢問Bus Error的問題。他有一個非常簡單的測試程序,將一個short數組中第二個成員變數的地址附給一個int型的指針,接著給該指針賦值,大致代碼如下:
short array[10];
int * p = (int *) &array[1];*p = 1;
運行的平台是Solaris for SPARC,使用的編譯器是GCC,程序執行到指針賦值的時候的出現Bus Error出錯信息,程序coremp。他覺得非常奇怪,代碼在X86平台運行的很好。其實這個問題完全是由於CPU硬體不同所造成的。
Bus Error通常都是因為非對齊訪問造成的。CPU在設計上為了性能上的考慮,要求待訪問,操作的數據地址都要對齊。如果發現沒有對齊的訪問,就會向當前進程發出SIGBUS信號,使程序崩潰。RISC包括SPARC都是這種類型的晶元。而X86架構就沒有這種對齊要求。所以這段代碼在Solaris 10 for X86下面沒有問題,當然這是有性能的代價。Bus Error也有可能是因為機器物理問題或者訪問無效物理地址,但這種情況非常少見。
上面的代碼正是這種非對齊訪問造成的Bus Error。short是兩個位元組,其地址偏移量必是2的倍數。而對於int指針來說,需要偏移量地址是4的倍數的數據,所以直接用int指針來操作地址,就很有可能導致系統發出SIGBUS信號,程序崩潰。
這段程序如果用Sun Studio編譯器的話,運行就沒有問題。這是因為Sun Studio默認對32位編譯使用的參數是-xmemalign=8i,其中i選項設置明確不會產生這種SIGBUS信號。不過如果編譯成64位程序,Sun Studio使用的-xmemalign=8s,其中s選項設置意味對這種非對齊訪問產生SIGBUS信號,仍舊會遇到這個錯誤。
當然你也可以堅持在SPARC上使用GCC去編譯這種代碼。GCC有一個Type Attributes特性,在需人工對齊的變數後加上:__attribute__ ((aligned (4))); 4就是指定偏移量。比如:
short array[10] __attribute__ ((aligned (4)));
不過這個屬性只對Linker連接器可見的變數有效,也就是說對local variable無效。而且這種特性只能照顧到該數組,即第一個元素,並不為數組的每個成員設置偏移量。
如果一定要針對local variable或者數組的每個成員進行偏移量設置,可以考慮定義一個union的類型:union {short s;int i;}
10. 為什麼程序會出現Bus Error
最近在論壇上看到一個程序員在詢問Bus Error的問題。他有一個非常簡單的測試程序,將一個short數組中第二個成員變數的地址附給一個int型的指針,接著給該指針賦值,大致代碼如下: short array[10]; int * p = (int *) &array[1];*p = 1; 運行的平台是Solaris for SPARC,使用的編譯器是GCC,程序執行到指針賦值的時候的出現Bus Error出錯信息,程序coremp。他覺得非常奇怪,代碼在X86平台運行的很好。其實這個問題完全是由於CPU硬體不同所造成的。 Bus Error通常都是因為非對齊訪問造成的。CPU在設計上為了性能上的考慮,要求待訪問,操作的數據地址都要對齊。如果發現沒有對齊的訪問,就會向當前進程發出SIGBUS信號,使程序崩潰。RISC包括SPARC都是這種類型的晶元。而X86架構就沒有這種對齊要求。所以這段代碼在Solaris 10 for X86下面沒有問題,當然這是有性能的代價。Bus Error也有可能是因為機器物理問題或者訪問無效物理地址,但這種情況非常少見。 上面的代碼正是這種非對齊訪問造成的Bus Error。short是兩個位元組,其地址偏移量必是2的倍數。而對於int指針來說,需要偏移量地址是4的倍數的數據,所以直接用int指針來操作地址,就很有可能導致系統發出SIGBUS信號,程序崩潰。 這段程序如果用Sun Studio編譯器的話,運行就沒有問題。這是因為Sun Studio默認對32位編譯使用的參數是-xmemalign=8i,其中i選項設置明確不會產生這種SIGBUS信號。不過如果編譯成64位程序,Sun Studio使用的-xmemalign=8s,其中s選項設置意味對這種非對齊訪問產生SIGBUS信號,仍舊會遇到這個錯誤。 當然你也可以堅持在SPARC上使用GCC去編譯這種代碼。GCC有一個Type Attributes特性,在需人工對齊的變數後加上:__attribute__ ((aligned (4))); 4就是指定偏移量。比如: short array[10] __attribute__ ((aligned (4))); 不過這個屬性只對Linker連接器可見的變數有效,也就是說對local variable無效。而且這種特性只能照顧到該數組,即第一個元素,並不為數組的每個成員設置偏移量。 如果一定要針對local variable或者數組的每個成員進行偏移量設置,可以考慮定義一個union的類型:union {short s;int i;}