ぐだぐだ低レベルプログラミング(246)x86(16/32bit)、GDTのデスクリプタ列挙

Joseph Halfmoon

前回で、古き「よき」時代のDOSエクステンダ「DOS4GW」が各セグメントレジスタにセットしてくれているデフォルトセグメントの属性観察はできたようです。ここまでくればGDT(グローバル・ディスクリプタ・テーブル)のダンプなど指呼の間であります。どんなディククリプタがGDTに詰まっているのか興味シンシンなんであります。。

※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら

プログラミング

※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3
Global Descriptor Table(GDT)

GDTこそは、プロテクトモードの元凶?ともいえる偉大にして怪奇なテーブルであります。だいたい全てのものはここから始まるもんです。通常「レベルの低い」下々のものは、そこに登録されているセグメントの極一部を呼び出して使うことがせいぜい、GDTを眺めてやろう、などとは思わないもんであります。しかし、前回LAR命令とLSL命令で学んだ通り、アクセス許されない「高いレベルの」デスクリプタに関してLARやLSLの適用を試みても「不成功」と言われるだけで制御までは取り上げられません。それどころか今回のDOS4GWのユーザー環境は、まったく「プロテクト」など意図してないレベル0っす。やり放題だあ。メモリセグメントデスクリプタに限らずシステムセグメントデスクリプタでも「だいたい」はのぞき見できる筈。一部、IDT(インタラプト・デスクリプタ・テーブル)関係と未定義なところのみは除外ですけど。

そこで前回の実験コードをチョイ直しして作成した今回の実験コードが以下に。デフォルトセグメントののぞき見にて既にその存在を確かめてある 0x2D 番目までのデスクリプタを総当たりでのぞき見してみるもの。

#include <stdio.h>

void printSelector(const char* nam, short sel)
{
    printf("Sel: %s 0x%04X ", nam, (sel & 0xFFF)>>3);
    if ((sel & 0x4) == 0) {
        printf("GDT");
    } else {
        printf("LDT");
    }
    printf(" RPL=%d",(sel & 0x3));
}

void printAR(long ar)
{
    printf("MEM ");
    if ((ar & 0x800000)!=0) {
        printf("4Kpg");
    } else {
        printf("Byte");
    }
    printf(" DPL=%d ",(ar & 0x6000)>>13);
    switch ((ar & 0xe00)>>9) {
    case 0:
        printf("R/O(U)");
        break;
    case 1:
        printf("R/W(U)");
        break;
    case 2:
        printf("R/O(D)");
        break;
    case 3:
        printf("R/W(D)");
        break;
    case 4:
        printf("E/O   ");
        break;
    case 5:
        printf("E/R   ");
        break;
    case 6:
        printf("E/O(C)");
        break;
    default:
        printf("E/R(C)");
        break;
    }
    if ((ar & 0x100)!=0) {
        printf(" ACC");
    }
}

void printSYS(long ar)
{
    printf("SYS AR ");
    printf("DPL=%d ",(ar & 0x6000)>>13);
    if ((ar & 0x800)!=0) {
        printf("386 ");
        switch ((ar & 0x700)>>8) {
        case 1:
            printf("TSS(A)");
            break;
        case 3:
            printf("TSS(B)");
            break;
        case 4:
            printf("CallG ");
            break;
        default:
            printf("NOT ALLOWED");
            break;
        }
    } else {
        printf("286 ");
        switch ((ar & 0x700)>>8) {
        case 1:
            printf("TSS(A)");
            break;
        case 2:
            printf("LDT   ");
            break;
        case 3:
            printf("TSS(B)");
            break;
        case 4:
            printf("CallG ");
            break;
        case 5:
            printf("TaskG ");
            break;
        default:
            printf("NOT ALLOWED");
            break;
        }
    }
}

int isMemDescriptor(long ar) {
    if ((ar & 0x1000) != 0) {
        return 1;
    }
    return 0;
}

int dumpSelector(const char* nam, short sel) {
    static long _ar;
    static long _limit;
    static int _ok;

    _asm {
        .386p
        xor eax, eax
        mov ax, sel
        lar eax, eax
        jnz  lbl_ng
        mov _ok, 1
        mov _ar, eax
        mov ax, sel
        lsl eax, eax

        mov _limit, eax
        jmp lbl_nxt
lbl_ng: mov _ok, 0
lbl_nxt:
    }

    printSelector(nam, sel);
    if (_ok != 1) {
        printf(", NG\n");
    } else {
        printf(", OK ");
        if (isMemDescriptor(_ar)) {
            printAR(_ar);
            printf(" Lim 0x%08X\n", _limit);
        } else {
            printSYS(_ar);
            printf("\n");
        }
    }
}

void main()
{
    short work_selector, i;
    char selnum[8];
    int sel;

    for (i=1; i<0x2E; i++) {
        work_selector = i<<3;
        sprintf(selnum, "%d", i);
        dumpSelector(selnum, work_selector);
    }
}
実行結果

上記 dumpgdt.c ソースについて、以下のコマンドラインを適用すると、WatcomCコンパイラが灰の中から蘇り、DOS/4GW上で実行されるオブジェクト dumpgdt.exe を生成してくれます。

wcl386 /l=dos4g /d2 dumpgdt.c

実行は普通にFreeDOSのDOSプロンプトからでOKデス。

実行結果の冒頭部分が以下に(色枠はお惚け老人の書き加え)dumpgdt_results

0番目はnullなので、GDTの最初の1番目から17番目が上記です。その中で11番目には

386 TSS(B)

とな。386形式のTSS(タスク・ステート・セグメント)であります。(B)はBUSY(現在実行中)の意味です。これこそが、現在実行中の自分自身のTSSだあ。

また13番目をみやると、LDTデスクリプタも存在。DOS4GWのデフォルト・セグメントは皆GDT登録でしたが、一応LDTも存在しているみたい。

多数のセグメントの中には、いろいろお役目があると思われるので解読必至(多分次回以降。)

なお、44番目(0x2C)、45番目(0x2D)には前回までのぞき見していたカレントのコード・セグメント(黄枠)とデータ/スタック共用のセグメント(緑枠)が鎮座してます。dumpgdt_results2

さらにお惚け老人の追及は続く、ホントか?

ぐだぐだ低レベルプログラミング(245)x86(16/32bit)、DOS4GWセグメント続 へ戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です