星期五, 十一月 6

nasm 初学

本来想委屈点用 wine + masm32, 费了不少劲算是配出来了,正常的 win32 和 console32 的程序能正常编译了,但试验例子时发现一个麻烦事,例子要用 Irvine32.lib 里的函数,把头文件、库文件拷过去后,能编译过去了,连接时却发现报错,说找不到符号。想了半天也没明白,那些头文件看得晕晕的,上网也什么也没找着。我 是不想安装恐怖的VS来找编译命令了,以后有时间再说吧。

真是郁闷,学的什么课都要用微软的东西,离开微软就没法活了吗?放着开源好用小巧的软件不用,非要安装动辄几G的破解的缓慢的“易用”的大家伙。vc6.0 + mfc 、 vs + masm vb.net ……,估计以后还会有不少。

我想学汇编用 nasm 就足够了,masm中那么多宏对理解汇编语言本身并没多大用,而且有些细节masm处理的并不好,masm32过于依赖 w32api,调用C函数则要考虑更多细节,而且用它时不易接触到底层的东西,它并不是学汇编语言的首选。

在我看来,gas汇编有许多好用的辅助工具,对初学者是非常不错的,但权衡一下,还是用 nasm吧。

用 nasm可以考虑两种函数调用方式(linux下),用 int 80 软中断和用 libc 库,如果是比较简单的函数,我感觉用系统中断更好,因为那样可以用 ld 直接连接,用成的代码很小,调试时反汇编一目了然,但如果频繁用C函数,还是省点事连到 libc 库吧,那样连接时可以直接用 gcc 处理,生成的代码大了许多倍,直接反汇编已经看不出头绪。二者还是一些细节上的差距,用入口函数名。

举个例子:

//file n.c

#include

int main()
{
int num = 50247;
printf("%d ly%d\n", 54, num);
return 0;
}

nasm 中断调用:(由于sys_write无法直接格式化输出,这里简化一下)

; n1.asm

section .data
mstr db "54 ly50247", 0Ah, 0
len equ $ - mstr

section .text
global _start ;入口函数,nasm中默认是 _start

_start:
mov edx, len ;参数三:字符串长度
mov ecx, mstr ;参数二:字符串地址
mov ebx, 1 ;参数一:stdout(文件描述符)
mov eax, 4 ;sys_write(系统调用号)
int 80h ;调用中断

mov ebx, 0 ;参数:退出状态
mov eax, 1 ;sys_exit
int 80h

编译:nasm -f elf n1.asm

连接:ld -s -o n1 n1.o

用 中断前要先查用法,eax是调用号,ebx,ecx,edx,esi,edi 是参数,如果超过5个参数, 全部参数要依次放在一块连续的内存区域里,在寄存器 ebx 中保存指向该内存区域的指针。系统调用完成之后,返回值保存在eax里,这种调用方式和Windows下的各种调用协议都不一样。入口函数最好用 _start,因为这是nasm默认的,如果不一样编译有警告,但一般还会正确,如果代码复杂则可能会出错。

nasm 调用 libc库:

;file n2.asm

section .data
mstr db "%d ly%d", 0Ah, 0

section .text
extern printf
extern exit
global main

main:
add esp, 4d ; int num;
mov [esp], dword 50247d ; num = 50247;
push dword [esp] ; 3rd
push 54 ; 2ed
push mstr ; 1st
call printf ; printf(mstr, 54, num);
sub esp, 0Ch ; 恢复栈

push 0d
call exit
sub esp, 04h

编译:nasm -f elf n2.asm

连接:gcc -o n2 n2.o

栈好像让我弄不平衡了,不管怎样,能正常运行。入口函数要用main。

n2 和 n 的大小差不多(9k),都是连接时加上去了,n1则很小(440b)。

能看出来,真接调用C函数真是方便,比有限的n个系统调用好用多了。

没有评论:

发表评论