LINE: 在windows上運行原生linux程式 (3) : bash和gcc可用,源碼放出

來源:互聯網
上載者:User

bash和gcc都能運行了,離“可用的系統”又進了一步。今天整理了下代碼,放到了google code 上,有興趣的都可以下載下來看。要是有誰對這也感興趣,可以在下面留言,一起來玩。

如果把討論範圍縮小到x86平台,那麼linux和windows的區別,至少在使用者態層面的區別,比我們想象的要小很多,所以事實上如果你真想乾的話,在windows上實現各類*nix特性並沒有想象中那麼困難。反過來說,在*nix上實現windows特性也完全能做到。這不是隨口一說,前者有cygwin, coLinux,後者有wine,都是成熟的項目。我這個東西跟cygwin的區別前面已經說過很多次了,跟coLinux的區別在於,coLinux事實上還是有一層虛擬層的,它會在windows核心裡載入一個硬體虛擬層,然後在這虛擬層上跑一個*真正的*linux核心,大致的架構可以參考這篇文章。而LINE不需要任何虛擬層,它是在windows核心裡*直接*實現linux的系統調用,(當然,這是我的最終目標,目前還是要過cygwin這一類比層的)。考慮到linux的程式基本是在glibc之上啟動並執行,而glibc是純使用者態的東西,與核心的交流全部通過系統調用,如果我們真的能在windows核心裡把linux系統調用全部實現,那麼glibc壓根就不知道自己是跑在windows上還是linux上。

LINE的核心組件有兩個:核心態的int 80響應函數,以及使用者態的elf loader。目前int 80響應函數的實現非常簡單,20來行彙編就搞定了,因為是第一階段嘛,所有的東西全在使用者態實現,把邏輯搬到核心的工作是下一階段的事情。大致的代碼如下:

 1 _InterruptHandler proc
2
3 ; Check for SYSCALL_LINEXEC_HANDLER
4 CMP EAX, 0DEADBEEFh ;LINE.exe啟動並執行第一件事情就是設定使用者態響應函數的地址,以便從系統調用返回的時候可以轉到這裡。此一次int 80的eax設定成DEADBEEF,這個值不是任何系統調用號
5 JNE reflect_syscall
6
7 MOV DS:_syscallHandlerPtr, EBX ;儲存使用者態響應函數的地址
8
9 IRETD
10
11
12 reflect_syscall:
13 PUSH EAX
14
15 ; simple sanity check
16 MOV EAX, DS:_syscallHandlerPtr
17 CMP EAX, 0
18 JE no_handler
19
20 PUSH EBX
21
22 MOV EBX, DWORD PTR [ESP+8] ; 使用者態程式調用int 80時,該指令自動幫你儲存的返回地址
23 MOV DWORD PTR [ESP+8], EAX ; 把返回地址替換成我們在使用者態的響應函數
24
25 MOV EAX, DWORD PTR [ESP+8+12] ;
26 SUB EAX, 4
27 MOV DWORD PTR [ESP+8+12], EAX ;
28 MOV DWORD PTR [EAX], EBX ; 以上幾步把舊的返回地址儲存起來,使用者態響應函數做完事情後,就跳回這個地址
29
30 POP EBX
31
32 JMP exit_handler
33
34 no_handler:
35 POP EAX
36 PUSH -38 ; -38 == ENOSYS
37
38 exit_handler:
39 POP EAX
40 IRETD
41
42 _InterruptHandler endp
43
44 End

如上所示,這段彙編把原有的調用流程全改了。原有的中斷流程大概是這樣的:程式調用int 80中斷—>cpu自動將EIP壓棧—>(1)進入核心做事—>做完後核心調用iretd—>返回使用者態—>自動把EIP恢複。改完後的中斷流程大概是這樣的:程式調用int 80中斷—>cpu自動將EIP壓棧(我們假設為地址1)—>進入核心—>(2)更改棧上保留的EIP,改成使用者態的響應函數(我們假設為地址2)—>再儲存地址1—>iretd返回使用者態—>恢複EIP,此時恢複的是地址2—>轉到地址2做事情—>(3)做完後返回地址1。對於(2)和(3)之間的事情,int 80啟動器完全不知道,它只知道中斷前儲存的是哪個指令,最後回來執行的還是那條指令。如果第二階段完成後,(2)和(3)之間的東西也完全沒必要存在了,它們會移到(1)這個地方。

接下來我們說elf loader。我們知道一個進程的建立過程基本是這樣的:fork建立新進程—>子進程裡調用exec*函數,用目標程式替換現有程式。而替換過程基本是這樣的:load elf—>解析import table—>載入所有依賴的庫—>調整各類庫的匯出函數地址—>找到elf的入口函數地址—>跳轉。這一整套步驟在LINE裡都重新實現了一遍,因為windows上只有PE loader,根本認不出elf格式的東西,所以載入elf檔案的工作就得我們自己來。假設我們目前啟動並執行是bash程式,然後敲了一個命令ls –al,這個過程在linux上大概是這樣的:fork—>父進程等待,子進程exec*( “ls”, argv, env) //argv[0] = ls, argv[1] = –al。在LINE上則變成了這樣:fork—>父進程等待,子進程exec*( “Line.exe”, argv, env  ) // argv[0] = line, argv[1] = ls, argv[2] = –al。也就是說,原本是新開“ls –al”這樣一個進程,現在變成新開”line ls –al”這個進程,這樣所有的進程都變成由line來載入了。至於elf載入的具體細節,不管你是看linux核心源碼,還是看ld-linux這個庫的源碼,都能有超詳細的解釋,這裡就不多說了。

最後還有兩個比較棘手的問題:記憶體布局和路徑。不管我們怎麼弄,line.exe還是一個PE檔案,由windows負責載入,載入完後有一些記憶體塊就已經被佔用了(一般來說是0x40000000及以上的地址)。要是我們的elf檔案需要這些地址,那事情就麻煩了。幸運的是elf的首地址一般是0x08040000,離0x40000000還遠著呢,足夠我們用了。但事實上即使是0x40000000以下的地址,有一些也是windows標記為不可執行檔,我們必須在load elf檔案之前將需要的記憶體塊重新標記可執行。

而路徑絕對是windows上最噁心人的地方(之一)。記得在之前一篇部落格裡我曾說過可以用windows中的namespace類比單根的檔案系統,這在第二階段絕對是可行的,不過第一階段因為全是在使用者態實現所以會比較困難。目前我的解決方案是把所有的絕對路徑都改成相對路徑。比如我們的line.exe程式放在c:/line目錄裡,那麼當linux程式訪問/bin目錄時,我會把目前的目錄附加在該目錄前面,變成c:/line/bin目錄。同樣的/lib目錄則成了c:/line/lib目錄,以此類推。目前為止這套機制啟動並執行還算不錯。至於/proc,/dev這些虛擬路徑,目前還沒有實現,不過藉助cygwin應該也還是能做。轉換路徑的函數如下:

 1 void change_path_to_relative(char* des, char* src)
2 {
3 char root_path[MAX_PATH] = {0};
4 char* slash = NULL;
5 if( !src || !*src || !des){
6 return;
7 }
8 if( src[0] != '/' ){
9 strcpy(des, src);
10 return;
11 }
12 strcpy(root_path, linexec_exe); //我們假設line所在的目錄在啟動的時候就已經保留
13 slash = strrchr(root_path, '/');
14 if( !slash ){
15 strcpy(des, src);
16 return;
17 }
18 *slash = '\0';
19 strcpy(des, root_path);
20 strcat(des, src);
21 return;
22
23 }

最後附上編譯步驟以及運行若干,一個編譯好的包連上我從ReadHat 6.0上拷出來的庫壓縮後有200M+,等我找個免費的網路空間再上傳放出。

編譯驅動:

1. 下載安裝WDK

2. 運行WDK編譯環境,進入原始碼的src目錄

3. 進入int80目錄

4. 運行build –g –c

5. 將產生的int80.sys考入i386目錄

6. 用管理員權限運行install.bat

編譯器:

1. 下載安裝cygwin

2. 運行cygwin,進入原始碼src目錄

3. 運行make

4. 不出意外的話,會在src目錄產生幾個dll和exe,全拷貝到其他目錄裡,比如c:/line

5. 從cygwin的安裝目錄裡找到cygwin1.dll, cyggcc_s-1.dll這兩個檔案,拷貝到你的目錄裡

6. 找舊一點的linux程式和庫(確保沒有NPTL,這個我還沒想到辦法實現。。。),考到你的目錄裡,保留目錄結構,確保根目錄就是你的目錄

7. 運行cmd,進入你的目錄,執行”line.exe bash “命令

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.