2006年7月9日星期日

C 与汇编代码结合

花了近一个星期,研究了一个看起来初级得不能再初级的问题,刚才终于成功了。就是用汇编写一个函数,导出到一个 C 程序里面调用它。想得很简单,不外乎就是 .asm 和 .c 分别编译成 .obj,然后链接。谁知问题多多。

汇编我用的是 NASM (http://sourceforge.net/projects/nasm),代码如下,实现一个类似 memcpy 的函数(myMemcpy.asm):

global _myMemcpy

;segment myMemcpy class=code

_myMemcpy:
mov eax, esp
push cx
push ds
push es

mov cx, [ss:eax + 12] ; count
mov ds, [ss:eax + 10] ; src segment
mov si, [ss:eax + 8] ; src offset
mov es, [ss:eax + 6] ; dest segment
mov di, [ss:eax + 4] ; dest offset

xor eax, eax
mov dx, es
mov ax, di ; return value is in dx:ax

rep movsb

pop es
pop ds
pop cx
retf

返回值存于 dx:ax。

C 代码如下,用 Turbo C 2.01 编译(test.c):

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

extern void* myMemcpy(void* dest, void* src, int count);

main()
{
const int c = 5;
char* a = (char*) malloc(c);
char* b = (char*) malloc(c);

sprintf(a, "Dest");
sprintf(b, "Src");

printf(myMemcpy(a, b, c));

return 0;
}

如果运行正确,则会输出 Src。

NASM 汇编语句为 nasmw myMemcpy.asm -f obj,没什么好说的。

C 编译语句为 tcc -mh -c test.c,此处的 -mh 表示选择 Huge 内存模型,而其他的内存模型都不可行。如果用 Small 或者 Tiny 模型会产生错误:

Fixup overflow in module TEST.C at _TEXT:0036, target = _MYMEMCPY

原因可参见 Coping with 'Fixup Overflow' messages.,而 Large 模型(-ml)会在程序结束前的一个 call 产生错误,错误代码 36。

链接语句为 tlink /x test.obj myMemcpy.obj lib\c0h.obj, , , lib\ch.lib ,c0h.obj 和 ch.lib 对应 Huge 模型的库文件。

如果在 myMemcpy.asm 没写 segment 语句,NASM 会自动把 _myMemcpy 分配到 __NASMDEFSEG 段里去。segment 语句后面的 class=code 会告诉链接器,这个段一个代码段。可以在链接时把 /x 改成 /s,生成详细的 map 文件,里面记载了每个段的类型。当然,这个 segment 语句不是必须的。

关于外部函数的声明,Coping with 'Fixup Overflow' messages. 里面提到可以写成

extern void (far * far myMemcpy)(void* dest, void* src, int count);

这样,产生的代码(可由 tcc -S test.c 产生)为

mov ax,seg _myMemcpy
mov es,ax
call dword ptr es:_myMemcpy

而一般的声明方式产生的代码是

call far ptr _myMemcpy

两种方法我都试过,不过似乎只有一般的 call far ptr _myMemcpy 可以正常运行,也不会产生任何链接错误或者警告。简单才是美嘛。

我这里生成的 exe 文件,程序的实际入口是在相对入口地址偏移 F8 的一个 call,用 W32Dasm 打开可以发现那是 call 0000:0000,也就是在 call 前的代码运行时修改了这个 call 的实际目标地址。而 call 里面的代码就和 tcc -S test.c 生成的 test.asm 类似了。

虽然简单的一个小程序,但遇到问题还是折磨死人。不过也好,幸亏有这些问题,我也学到很多关于系统底层、可执行文件和调试方面的知识。

没有评论: