Solaris SPARCメモリアクセスアドレスが整列していないことによるクラッシュ分析
3446 ワード
プログラムcoredumpから
この例の目的は、bufferがまず整数len 1を格納し、6文字msgを格納し、次に整数len 2を格納する誤ったシーンを作成することである.後でbufferから格納された2つの整数を読み出します.Solaris SPARCでコンパイル実行:
GDBでエラーの場所を表示するには:
明らかに25行目にBus Error問題が発生した.このプログラムの終了コードは138であり、最大127に換算した戻り値は10であり、対応するsignalは10、すなわちSIGBUSである
同じプログラムはLinux 86プラットフォームでコンパイルして実行することができます
通常out 1とout 2が印刷されます.
なぜなら、SPARCはRISCタイプのプロセッサであり、メモリアドレスにアクセスする必要がある場合には位置合わせ条件を満たす必要があり、具体的には、メモリアドレスから2バイト、例えばshortを読み書きする場合、このアドレスは2バイトで整列されなければならない.すなわち、アドレスは2で整列させることができる. メモリアドレスから4バイト、例えばintを読み書きする場合、このアドレスは4バイト整列でなければならない.すなわち、アドレスは4で割り切れる.一般的には、アドレスの末尾は0 x 0、0 x 4、0 x 8、0 xCでなければならない. メモリアドレスから8バイト、例えばlong longを読み書きする場合、このアドレスは8バイトで整列しなければならない.すなわち、アドレスは8で整列することができ、一般的にはアドレスの末尾は0 x 0、0 x 8でなければならない.
例では、p 1の値の末尾が0 x 0であることを示します.これは4バイト整列のアドレスであるため、intタイプout 1に割り当てられ、p 2の値の末尾が0 xaであり、4バイト整列のアドレスではないので、intタイプに割り当てることはできませんが、実はこれは2バイト整列のアドレスであり、shortに値を割り当てることができます.
Solaris SPARC環境では、プログラムコンパイル全体が正常に動作していることがわかります.
それはなぜLinux 86環境の下で問題がないのか、これは主にCISCプロセッサの特徴に依存している.CISCとRISCプロセッサの特徴の違いは、具体的に彼らのドキュメントを見なければならない.私も説明できない.RISCはなぜこのように制限されているのか.
リファレンスドキュメント
Runtime Checking Errors: https://docs.oracle.com/cd/E18659_01/html/821-1380/blaiu.html
#include
#include
#include
int main(int argc, char * argv[]) {
int len1 = 1;
int len2 = 2;
const char * msg = "AAAAAA";
unsigned char * buffer = malloc(20);
int out1;
int out2;
int i = 0;
memset(buffer, 0, 20);
for (i = 0; i < 20; i++) { printf("%02X ", buffer[i]); } printf("
");
memcpy(buffer, &len1, sizeof(int));
memcpy(buffer+sizeof(int), msg, strlen(msg));
memcpy(buffer+sizeof(int)+strlen(msg), &len2, sizeof(int));
for (i = 0; i < 20; i++) { printf("%02X ", buffer[i]); } printf("
");
printf("p1=%p,p2=%p
", buffer, buffer+sizeof(int)+strlen(msg));
out1 = *(int *)buffer;
printf("out1=%d
", out1);
out2 = *(int *)(buffer+sizeof(int)+strlen(msg));
printf("out1=%d
", out2);
return 0;
}
この例の目的は、bufferがまず整数len 1を格納し、6文字msgを格納し、次に整数len 2を格納する誤ったシーンを作成することである.後でbufferから格納された2つの整数を読み出します.Solaris SPARCでコンパイル実行:
bash-3.2$ uname -a
SunOS 5.10 Generic_148888-02 sun4v sparc sun4v
bash-3.2$ gcc -g t.c
bash-3.2$ ./a.out
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 01 41 41 41 41 41 41 00 00 00 02 00 00 00 00 00 00
p1=20dc8,p2=20dd2
out1=1
Bus Error (core dumped)
bash-3.2$ echo $?
138
bash-3.2$
GDBでエラーの場所を表示するには:
bash-3.2$ gdb a.out core
...
#0 0x10aa4 in main (argc=1, argv=0xffbffaac) at t.c:25
25 out2 = *(int *)(buffer+sizeof(int)+strlen(msg));
(gdb)
明らかに25行目にBus Error問題が発生した.このプログラムの終了コードは138であり、最大127に換算した戻り値は10であり、対応するsignalは10、すなわちSIGBUSである
同じプログラムはLinux 86プラットフォームでコンパイルして実行することができます
$ uname -a
Linux 4.1.12-94.3.8.el7uek.x86_64 #2 SMP Fri Jun 30 10:40:13 PDT 2017 x86_64 x86_64 x86_64 GNU/Linux
$ gcc -g t.c
$ ./a.out
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00 41 41 41 41 41 41 02 00 00 00 00 00 00 00 00 00
p1=0xcb6010,p2=0xcb601a
out1=1
out2=2
通常out 1とout 2が印刷されます.
なぜなら、SPARCはRISCタイプのプロセッサであり、メモリアドレスにアクセスする必要がある場合には位置合わせ条件を満たす必要があり、具体的には、
例では、p 1の値の末尾が0 x 0であることを示します.これは4バイト整列のアドレスであるため、intタイプout 1に割り当てられ、p 2の値の末尾が0 xaであり、4バイト整列のアドレスではないので、intタイプに割り当てることはできませんが、実はこれは2バイト整列のアドレスであり、shortに値を割り当てることができます.
$ vim t.c
//int out2;
short out2;
...
out2 = *(int *)(buffer+sizeof(int)+strlen(msg));
printf("out=%d
", out2);
$ gcc t.c && ./a.out
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00 41 41 41 41 41 41 02 00 00 00 00 00 00 00 00 00
p1=0x15431010,p2=0x1543101a
out=1
out=2
Solaris SPARC環境では、プログラムコンパイル全体が正常に動作していることがわかります.
それはなぜLinux 86環境の下で問題がないのか、これは主にCISCプロセッサの特徴に依存している.CISCとRISCプロセッサの特徴の違いは、具体的に彼らのドキュメントを見なければならない.私も説明できない.RISCはなぜこのように制限されているのか.
リファレンスドキュメント
Runtime Checking Errors: https://docs.oracle.com/cd/E18659_01/html/821-1380/blaiu.html