最近更新: 2007-06-13

無聊之下寫的程式,把程式碼當資料...

前些日子閱讀《沒有時間的世界》,書中說,圖靈 的圖靈機概念,最偉大的貢獻在於「把程式當資料儲存」的想法。我一時無聊,又想起了以前在 DOS 時代的記憶,就寫了一段 C 語言程式,把程式碼當資料複製,然後再去執行那段被複製的程式碼。

#include <stdio.h>
#include <stdlib.h>
#include <mem.h>

void funcA() {
    puts("hello\n");
}

void funcB() {}

int main() {
    void (*pfV)();

    size_t contextLength = (size_t)funcB - (size_t)funcA;
    printf("context length: %x\n", contextLength);
    pfV = (void(*)()) malloc(contextLength);
    memcpy((void*)pfV, (void*)funcA, contextLength);
    
    printf("%x\n", funcA);
    for (int i = 0; i < 16; ++i) {
        printf("%x,", ((char*)funcA)[i]);
    }
    puts("\n");

    printf("%x\n", pfV);
    for (int i = 0; i < 16; ++i) {
        printf("%x,", ((char*)pfV)[i]);
    }
    puts("\n");

    funcA();
    (*pfV)(); //segment exception
    return 0;
}

這個程式在現代的作業系統中應該是無法執行到結束的... 因為現代的作業系統都實踐了記憶體節區保護的概念,當程式試圖將 ip (指令指標) 指向一段標記為資料區的記憶體位址時,就會擲出系統性錯誤而中止程式執行。現在一般要改用系統呼叫才能做這件事;用 C 標準庫不允許這麼做。

不過在 DOS 時代,這段程式技巧卻相當普遍。在 640K + EMS/XMS 的限制下,幾乎有點資歷的程序員都會用這一招實現動態載入程式碼的功能。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/3461127.html