32ビットプログラムの64ビット化に関する問題点を拾ってみる

|

64ビット化擬きの真似事の続きの続きです。

64ビット化に関して色々と検索していると、「移植に関する問題のチェックリスト」なるマイクロソフトドキュメントを見つけ、簡単そうな問題点を拾ってコンパイルするだけのサンプルを作ってみました。

ただ最近までは、配列の添え字の確からしさを含め処理する事が、プログラマの努めだった筈なのに? 添え字の"-1"って、普通のソフトウエアでは有り得ない?


<ポインタ算術演算の注意点>

①符号無しと符号付きの演算には注意

 この問題は x が符号無しのために発生します(式全体が符号無しになる)が、y が負の値でない限り成功します。
 64ビットの Windows では、この32ビットの符号無し負の値は、大きな64ビットの正の数値になり、間違った結果が得られます。
 (この問題を解決するには、x を符号付きの値として宣言するか、式を明示的に LONG 型にキャストします。と在りますが・・・)

②16進定数と符号無しの値を使用する場合には注意

 64ビットシステムでは、条件式は真に成りません。
 (UINT64)(TEST_SIZE - 1) = 0x0000000000000fff ⇒ ~((UINT_PTR)(TEST_SIZE - 1)) = 0xfffffffffffff000
 ~(TEST_SIZE - 1) = 0xfffff000        ⇒ (UINT_PTR)(~(TEST_SIZE - 1)) = 0x00000000fffff000

③NOT演算には注意

 この問題は、~(b - 1) が 0x0000 0000 xxxx xxxx を生成し、0xFFFF FFFF xxxx xxxx とは成らないことです。
 コンパイラはこれを検出しません。

④バッファサイズを計算する場合には注意

 ポインタの演算ではポインタをPCHARにキャストする。とありますが、

 何故これが移植に関する問題点に挙がっているのか、良く分かりません。8バイトどうしの減算結果が4バイトに収まる保証は無い、のは確かです。

 だとしても、32ビットでも当該事項が指摘されていないのが不可解です。バッファサイズと言うなら、"1"ではなく"4"ですよね?
 これも、プログラマとして想定内だと思うのですが・・・。

⑤64ビットの Windows では、0xFFFFFFFF は -1 と同じでは無いことに注意

 32ビット機の場合:p[index - 1] == p[0xffffffff] == p[-1]
 64ビット機の場合:p[index - 1] == p[0x00000000ffffffff] != p[-1]、(index が4バイトの時)

以上の様な説明書きが、ドキュメントに在りました。


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual C++ 2008 cross x64、native x64


/*---- 32ビット ------------------- コマンドライン -----------------------*/

D:\vc2008\x86x64>cl test32.c

/*----------------------------------------------------------------------------*/

/*---- 64ビット ------------------- コマンドライン -----------------------*/

D:\vc2008\x86x64>cl test64.c

/*----------------------------------------------------------------------------*/


/*---- 32ビットデータ -------------------- お試し結果 --------------------*/

D:\vc2008\x86x64>test32
①-1:pVar2 = 0012FF7C, pVar1 = 0012FF6C, y = 2, x = 3
①-2:pVar2 = 0012FF5C, pVar1 = 0012FF6C, y = -2, x = 3
②:Equal, ~((UINT_PTR)(TEST_SIZE - 1)) = 0xfffff000, (UINT_PTR)(~(TEST_SIZE - 1)) = 0xfffff000
③:a = 0x12141210
④:len = 0x1
⑤:Equal, p[index - 1] = 0, p[-1] = 0

/*----------------------------------------------------------------------------*/

/*---- そのまま64ビット化 --------------- お試し結果 ---------------------*/

D:\vc2008\x86x64>test64
①-1:pVar2 = 000000000012FEE0, pVar1 = 000000000012FED0, y = 2, x = 3
①-2:pVar2 = 000000040012FEC0, pVar1 = 000000000012FED0, y = -2, x = 3
②:NOT equal, ~((UINT_PTR)(TEST_SIZE - 1)) = 0xfffffffffffff000, (UINT_PTR)(~(TEST_SIZE - 1)) = 0xfffff000
③:a = 0x12141210
④:len = 0xffffffff
⑤:例外発生 ← コメント化
⑥:index - 1 = 0xffffffff, -1 = 0xffffffff

/*----------------------------------------------------------------------------*/

/*---- 手直し64ビット化 ----------------- お試し結果 ---------------------*/

D:\vc2008\x86x64>test64
①-1:pVar2 = 000000000012FED0, pVar1 = 000000000012FEC0, y = 2, x = 3
①-2:pVar2 = 000000040012FEB0, pVar1 = 000000000012FEC0, y = -2, x = 3
①-3:pVar2 = 000000000012FEB0, pVar1 = 000000000012FEC0, y = -2, x = 3
②:NOT equal, ~((UINT_PTR)(TEST_SIZE - 1)) = 0xfffffffffffff000, (UINT_PTR)(~(TEST_SIZE - 1)) = 0xfffff000
③:a = 0x9012141210
④:len = 0xfffffffc
⑤:Equal, p[index - 1] = 0, p[-1] = 0
⑥:index - 1 = 0xffffffffffffffff, -1 = 0xffffffff
⑦:ptr2 = 000000000012FEF8, ptr1 = 000000000012FEFC, ptr2 - ptr1 = FFFFFFFFFFFFFFFF

/*----------------------------------------------------------------------------*/


/*---- test32.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <windows.h>

#define TEST_SIZE 0x1000UL

int main(char argc, char *argv[])
{
    ULONG x = 3;
    LONG y = 2;
    LONG z;
    LONG *pVar1 = &y;
    LONG *pVar2 = &z;

    UINT_PTR a = 0x12345678;
    ULONG b = 0xabcdef;

    LONG c;
    LONG d;
    ULONG len;
    LONG *ptr1 = &c;
    LONG *ptr2 = &d;

    DWORD index = 0;
    CHAR *p = "ABC";

    pVar2 = pVar1 + y * (x - 1);
    printf("\n①-1:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);
    y = -2;
    pVar2 = pVar1 + y * (x - 1);
    printf("①-2:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);

    if(~((UINT_PTR)(TEST_SIZE - 1)) == (UINT_PTR)(~(TEST_SIZE - 1))) { // warning C4127: 条件式が定数です。
        printf("②:Equal, ~((UINT_PTR)(TEST_SIZE - 1)) = %#x, (UINT_PTR)(~(TEST_SIZE - 1)) = %#x\n", ~((UINT_PTR)(TEST_SIZE - 1)), (UINT_PTR)(~(TEST_SIZE - 1)));
    } else {
        printf("②:NOT equal, ~((UINT_PTR)(TEST_SIZE - 1)) = %#llx, (UINT_PTR)(~(TEST_SIZE - 1)) = %#llx\n", ~((UINT_PTR)(TEST_SIZE - 1)), (UINT_PTR)(~(TEST_SIZE - 1)));
    }

    a = a & ~(b - 1);
    printf("③:a = %#x\n", a);

    len = ptr2 - ptr1;
    printf("④:len = %#x\n", len);

    if(p[index - 1] == p[-1]) {
        printf("⑤:Equal, p[index - 1] = %#x, p[-1] = %#x\n", p[index - 1], p[-1]); // On 32-bit machines : p[index-1] == p[0xffffffff] == p[-1]
    } else {
        printf("⑤:NOT equal, p[index - 1] = %#llx, p[-1] = %#llx\n", p[index - 1], p[-1]);
    }
}

/*----------------------------------------------------------------------------*/

/*---- test64.c --------------- そのまま64ビット化ソース -----------------*/

#include <stdio.h>
#include <windows.h>

#define TEST_SIZE 0x1000UL

int main(char argc, char *argv[])
{
    ULONG x = 3;
    LONG y = 2;
    LONG z;
    LONG *pVar1 = &y;
    LONG *pVar2 = &z;

    UINT_PTR a = 0x9012345678;
    ULONG b = 0xabcdef;

    LONG c;
    LONG d;
    ULONG len;
    LONG *ptr1 = &c;
    LONG *ptr2 = &d;

    DWORD index = 0;
    CHAR *p = "ABC";

    pVar2 = pVar1 + y * (x - 1);
    printf("\n①-1:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);
    y = -2;
    pVar2 = pVar1 + y * (x - 1);
    printf("①-2:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);

    if(~((UINT_PTR)(TEST_SIZE - 1)) == (UINT_PTR)(~(TEST_SIZE - 1))) { // warning C4127: 条件式が定数です。
        printf("②:Equal, ~((UINT_PTR)(TEST_SIZE - 1)) = %#x, (UINT_PTR)(~(TEST_SIZE - 1)) = %#x\n", ~((UINT_PTR)(TEST_SIZE - 1)), (UINT_PTR)(~(TEST_SIZE - 1)));
    } else {
        printf("②:NOT equal, ~((UINT_PTR)(TEST_SIZE - 1)) = %#llx, (UINT_PTR)(~(TEST_SIZE - 1)) = %#llx\n", ~((UINT_PTR)(TEST_SIZE - 1)), (UINT_PTR)(~(TEST_SIZE - 1)));
    }

    a = a & ~(b - 1);
    printf("③:a = %#llx\n", a);

    len = ptr2 - ptr1; // warning C4242: '=' : '__int64' から 'ULONG' への変換です。データが失われる可能性があります。
    printf("④:len = %#x\n", len);
/*
    if(p[index - 1] == p[-1]) { // // causes access violation on 64-bit Windows!
        printf("⑤:Equal, p[index - 1] = %#x, p[-1] = %#x\n", p[index - 1], p[-1]);
    } else {
        printf("⑤:NOT equal, p[index - 1] = %#llx, p[-1] = %#llx\n", p[index - 1], p[-1]); // // On 64-bit machines : p[index-1] == p[0x00000000ffffffff] != p[-1]
    }
*/
    printf("⑥:index - 1 = %#llx, -1 = %#llx\n", index - 1, -1);
}

/*----------------------------------------------------------------------------*/

/*---- test64.c ---------------- 手直し64ビット化ソース ------------------*/

#include <stdio.h>
#include <windows.h>

#define TEST_SIZE 0x1000UL

int main(char argc, char *argv[])
{
    ULONG x = 3;
    LONG y = 2;
    LONG z;
    LONG *pVar1 = &y;
    LONG *pVar2 = &z;

    UINT_PTR a = 0x9012345678;
    ULONG b = 0xabcdef;

    LONG c;
    LONG d;
    ULONG len;
    LONG *ptr1 = &c;
    LONG *ptr2 = &d;

    DWORD_PTR index = 0;
    CHAR *p = "ABC";

    pVar2 = pVar1 + y * (x - 1);
    printf("\n①-1:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);
    y = -2;
    pVar2 = pVar1 + y * (x - 1);
    printf("①-2:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);
    pVar2 = (LONG)(pVar1 + y * (x - 1)); // warning C4305: '型キャスト' : 'LONG *' から 'LONG' へ切り詰めます。warning C4047: '=' : 間接参照のレベルが 'LONG *' と 'LONG' で異なっています。
    printf("①-3:pVar2 = %p, pVar1 = %p, y = %d, x = %d\n", pVar2, pVar1, y, x);

    if(~((UINT_PTR)(TEST_SIZE - 1)) == (UINT_PTR)(~(TEST_SIZE - 1))) { // warning C4127: 条件式が定数です。
        printf("②:Equal, ~((UINT_PTR)(TEST_SIZE - 1)) = %#x, (UINT_PTR)(~(TEST_SIZE - 1)) = %#x\n", ~((UINT_PTR)(TEST_SIZE - 1)), (UINT_PTR)(~(TEST_SIZE - 1)));
    } else {
        printf("②:NOT equal, ~((UINT_PTR)(TEST_SIZE - 1)) = %#llx, (UINT_PTR)(~(TEST_SIZE - 1)) = %#llx\n", ~((UINT_PTR)(TEST_SIZE - 1)), (UINT_PTR)(~(TEST_SIZE - 1)));
    }

    a = a & ~((UINT_PTR)b - 1);
    printf("③:a = %#llx\n", a);

    len = (PCHAR)ptr2 - (PCHAR)ptr1; // warning C4242: '=' : '__int64' から 'ULONG' への変換です。データが失われる可能性があります。
    printf("④:len = %#x\n", len);

    if(p[index - 1] == p[-1]) {
        printf("⑤:Equal, p[index - 1] = %#x, p[-1] = %#x\n", p[index - 1], p[-1]);
    } else {
        printf("⑤:NOT equal, p[index - 1] = %#llx, p[-1] = %#llx\n", p[index - 1], p[-1]);
    }
    printf("⑥:index - 1 = %#llx, -1 = %#llx\n", index - 1, -1);

    printf("⑦:ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %p\n", ptr2, ptr1, ptr2 - ptr1);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/