Symbian 程序自定义入口点以及使用 ARM 汇编的一点记录

洞经常是越钻越大。本来是要尝试如何能使 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++ 集成环境里做了 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 内嵌汇编指令有的编译不过去,但是在他的工程里时可以的,区别在于,他的工程是一个驱动程序。这个差异带来这样不同的结果似乎可以说得通,但是我还没有找到编译器竟然会根据目标文件的类型和作用不同而采用不同的内嵌汇编指令审核规则的证据,需要有时间进一步了解一下。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注