洞經常是越鑽越大。本來是要嘗試如何能使 S60 程式擺脫預設的啟動代碼,而是用自己的啟動代碼的。
追尋這個目標的第一步是查看
Symbian 可執行程式的真正進入點,這個很快就找到了,是一個名叫 _E32Startup 的函數。立刻照貓畫虎仿效當前自己在 Windows
下寫 WinMainCRTStartup 的做法,寫了一個同名函數,把實現留空,試圖替換掉編譯器預設提供的入口。
這個小工作的進
展就不那麼順利了,最開頭寫的是 void _E32Startup(),結果編譯和連結很順利,但查看同時產生的 map 檔案發現進入點
_E32Startup 仍然是位於 uc_exe_.o
中的代碼。我意識到可能是函數原型或者是函數修飾上出了問題(其實是都出了問題),於是最後改成了正確的 extern "C" TInt
_E32Startup(),不過連結仍然通不過,報告“有多個 _E32Startup 存在”。
無奈之下我只好去檢查連結器的命令
行。Carbide.c++ 並不提供任何可以修改工程的編譯選項或者連結選項的介面,事實上,這些東西也是它自動產生的。最終我在 SDK 的
epoc32/BUILD 下中找到了 Carbide.c++ 為我的測試工程產生的 make 檔案(測試工程名為 tiny,產生的 make
檔案被自動命名為 tiny.armv5 了),開啟該檔案,在 tiny.exe
的建置規則中,找到了強行連結預設啟動代碼的證據,即在命令列裡引用了
$(EPOCSTATLINKUREL)/EEXE.LIB(UC_EXE_.o)。我把該引用刪除,回到 Carbide.c++ Integration Environment裡做了
Clean 並進行了 Build 操作(謝天謝地,我修改後的 make 沒有被自動再次產生而覆蓋),目標檔案 tiny.exe
順利誕生,從對應的 map 檔案裡看,非常乾淨,不再存在其他的符號了。
從道理上講,事情本來應該就此了結。但我不巧前幾天剛剛翻騰過
E32 Loader 的相關資料,想起來載入器會檢查 EXE 的進入點第一條指令是不是 TST PC, 0。很顯然我用 C++ 寫的
_E32Startup
函數不可能符合這個要求,於是想到了用內嵌彙編的方式來搞。誰知道這時才真正踏入泥潭。首先有個問題需要解決:那條指令必須是第一條,所以不能讓編譯器自
動建立函數的 prolog 和 epilog。這就要求該函數是 naked 屬性。幸好,RVCT 支援此類函數,在函數前加上 __asm
修飾即可。這時,函數體仍然是空的,我編譯後得到的錯誤資訊是:進入點沒有指向任何指令(L6204E: Entry point
(0x00008000) does not point to an
instruction)。然後我信心滿滿地寫下了那條該死的指令,再次編譯,結果還是編譯不過去,錯誤資訊發生了變化:指令、位移、立即數以及寄存器的
組合在當前指令集下不被支援(A1616E: Instruction, offset, immediate or register
combination is not supported by the current instruction
set)。然後我又改變了一次策略,把那條指令對應的作業碼用 DCD 指令就地寫入程式碼,這下錯誤又回到了開始(即進入點非指令)。
由於之前和其他在程式中嘗試過使用內嵌彙編的同事有交流,我決定不再和內嵌彙編較勁,而傾向於使用獨立的組合語言模組來解決問題。用了大概一天的時間,才摸清楚了一點 armasm 所接受的組譯工具的格式,能夠把一個 .s 的來源程式編譯出對應的 .o 來。
上
面的敘述略去一個糾結的中間過程,即 GCCE 的內嵌彙編與 RVCT 的內嵌彙編的格式不同問題。我所看到的 Symbian 裡的彙編代碼都寫在
.cia 檔案中,這是一種 C++ 與彙編混合寫就的檔案,但是彙編指令的格式採用了 GCCE 的規範。現在要用 RVCT
中帶的彙編器編譯,涉及到一個翻譯過程。在網上搜資料時偶然發現有人說,.cia 檔案如果是在 RVCT 環境下編譯,會被編譯系統預先處理,以產生
RVCT 的彙編器可以接受的合法輸入。我費了很大的勁才在 Symbian 的第二代編譯系統 Raptor 中找到翻譯程式的代碼,是用 perl
實現的,名字叫 tranasm.pl。使用它把一個 .cia 檔案翻譯之後進行參照對比,讓我收穫很多,解釋清了不少我直接看 .cia
中的彙編指令時存在的不解之謎。在此提到這個工具,希望能對後來的同好者有所協助。
遺留的疑惑。我的同事在我的研究過程中給予了協助,有
一個奇怪的事情至今沒有答案:上文中提到的,RVCT
內嵌彙編指令有的編譯不過去,但是在他的工程裡時可以的,區別在於,他的工程是一個驅動程式。這個差異帶來這樣不同的結果似乎可以說得通,但是我還沒有找
到編譯器竟然會根據目標檔案的類型和作用不同而採用不同的內嵌彙編指令審核規則的證據,需要有時間進一步瞭解一下。