@zoand
2017-04-12T15:55:05.000000Z
字数 2814
阅读 1364
asm
在 windows 上使用汇编, 不外乎两种方式:
在 windows 平台下, 没必要整个软件都使用汇编来编写。将某个部分 c/c++无法实现的代码或者需要更高效的代码用汇编来编写作为库函数, 链接到 c/c++ 代码里就足够了! 有一种所谓的 win32 masm 汇编形式, 只不过利用一些宏将 c 语言伪装成汇编的语法而已, 既然这样为何不用 c, 而用不伦不类的伪汇编!
你可以使用微软的 masm (宏汇编) 语言, 也可以使用开源免费的 nasm语言。在这里我将它们定性为语言,虽然它们都是 x86/x64平台上的汇编语言编译器,但语法上有些出入,因此区分为不同的语言更为合适。
若使用 masm 语法, 需要使用 ml.exe (32位) 或者 ml64.exe (64位) 进行编译, 它们都是集成在 visual studio 软件里, 在 vc 的 bin 及 bin\amd64 目录里, 例如: C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin 以及 C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64。
下面是一个用于 masm 的简单示例模版:
;;
;; 这是一个 masm 示例代码 libm.asm
;; (1) x86 下编译: ml /c libm.asm
;; (2) x64 下编译: ml64 /c /D_M_AMD64 libm.asm
;;
IFDEF _M_AMD64 ; 假如定义了 _M_AMD64(x64)
;; empty for x64 ML ; x64 下不需要
ELSE
.586p ; x86 下需要定义 target 体系
ENDIF
; ###### 下面导出函数 #######
IFDEF _M_AMD64
;; 在 x64 下导出的函数
PUBLIC ReadTsc ; 在 x64 下无须定义参数
ELSE
;; 在 x86 下导出的函数
PUBLIC _ReadTsc@0 ; 在 x86 下符号前加 "_" 前缀,后缀:"@+参数字节"
ENDIF
;; ###### 定义代码段 #######
_TEXT SEGMENT
;---------------------------------------------
; ULONG64 ReadTsc();
; 参数:
; 无
; 返回:
; 64 位的 TSC 值
;---------------------------------------------
ReadTsc:
_ReadTsc@0:
rdtsc
IFDEF _M_AMD64
;;
;; x64 版本
;;
shl rdx, 32
or rax, rdx
ENDIF
ret
假设源文件名为 libm.asm
在 x64 下使用 ml64.exe /c /D_M_AMD64 libm.asm 进行编译
/c 命令选项指示只编译并不链接,
/D 命令选项是定义一个宏。编译后生成 libm.obj 文件,
然后在 vc 里设置 project 属性。
如果 c/c++ 项目, 同时支持 win32 与 x64 平台, 那么每个平台都需要设置, 这样才能被链接到 c/c++ 代码里 (这里的汇编代码将被静态链接到 c/c++ 里)。
在 c/c++ 代码引用 ReadTsc() 这个函数前, 需要作出声明:
#ifdef __cplusplus
extern "C" {
#endif
extern ULONG64 __stdcall ReadTsc(); // 声明引用 libm 库的 ReadTsc 函数
#ifdef __cplusplus
}
#endif
最好是将 libm 库所有导出的函数声明,单独放在一个头文件里。在 c/c++ 代码放 include 这个头文件即可。
使用 nasm 更简些, 下面以 QueryCpuidInfo 函数实现为例:
;;
;; 这是一个 nasm 示例代码 libs.asm
;; (1) x86 下编译: nasm -fwin32 libs.asm
;; (2) x64 下编译: nasm -fwin64 libs.asm
;;
%if __BITS__ == 64
;; #### 导出 x64 函数
global QueryCpuidInfo
%else
;; #### 导出 x86 函数
global _QueryCpuidInfo@12 ; x86 下符号前加 "_" 前缀, 后缀: "@ + 参数字节数"
%endif
SECTION .text align=8 execute
;--------------------------------------------------------------------------
; VOID QueryCpuidInfo(ULONG Leaf, ULONG SubLeaf, PCPUID_BLOCK CpuidBlock)
;
; 参数:
; Leaf - CPUID 编号
; SubLeaf - CPUID 辅助编号
; CpuidBlock - 接收 cpuid info
;--------------------------------------------------------------------------
QueryCpuidInfo:
_QueryCpuidInfo@12:
%if __BITS__ == 64
;;
;; x64 版本
;;
mov eax, ecx ; leaf
mov ecx, edx ; sub-leaf
cpuid
mov [r8], eax
mov [r8+4], ecx
mov [r8+8], edx
mov [r8+12], ebx
ret
%else
;;
;; win32 版本
;;
push esi
mov eax, [esp+8] ; leaf
mov ecx, [esp+12] ; sub-leaf
mov esi, [esp+16] ; CpuidBlock
cpuid
mov [esi], eax
mov [esi+4], ecx
mov [esi+8], edx
mov [esi+12], ebx
pop esi
ret 12
%endif
假设源文件名为 libs.asm, 使用下面的命令进行编译:
win32:
nasm -fwin32 libs.asm
amd64:nasm -fwin64 libs.asm
同样需要配置 c/c++ 项目设置, 才能链接到汇编代码,方法和 masm 是一样的。
@邓志:windows 上使用汇编