assembly 如何从MASM汇编器中调用的C函数中调用printf?

uhry853o  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(192)

我有一个名为C. c的C文件,现在在这个文件中,我有一个函数,它比较两个数字,并将较大的数字保存到寄存器eax中。
我需要帮助创建第二个函数,该函数将在内部调用printf并打印一些文本。如何在C中添加对printf的支持,使我可以从MASM汇编程序中调用?
我的masm文件:

TITLE MASM Template (main.asm)
.386
.model FLAT
.stack 4096

WriteString PROTO stdcall
ReadChar PROTO stdcall
Crlf PROTO stdcall
ExitProcess PROTO stdcall:DWORD
greater PROTO C :DWORD, :DWORD

.data
PorukaZaIspis db "Poruka za ispis", 0

.code
_main PROC

push 8
push 3
call greater

call Crlf
mov edx, OFFSET PorukaZaIspis
call WriteString
call ReadChar
Invoke ExitProcess, 0
_main ENDP
END _main

END

我的抄送文件:

int greater(int a, int b) {
if ( a > b)
return a;
else 
return b;
}
bq8i3lrv

bq8i3lrv1#

你需要链接到一个合适的库(例如msvcrt.lib),并且你需要知道函数的导出名称。
C函数的调用约定称为“cdecl”。参数被压入堆栈,并且在调用后必须调整堆栈。如果你将函数声明为PROTO C,你可以让MASM使用INVOKE来完成这项工作。
示例:

测试.asm:

.686
.model FLAT

INCLUDELIB msvcrt.lib
printf PROTO C, :VARARG
exit PROTO C, :DWORD

; Functions in C.c:
greater PROTO C :DWORD, :DWORD          ; Declaration of greater (int,int)
hello PROTO C                           ; Declaration of hello (void)

.data
fmt db "%s %u", 10, 0                   ; '10': printf of msvcrt.dll doesn't accept "\n"
PorukaZaIspis db "Message from ASM: ", 0

.code
_main PROC
    invoke greater, 8, 3
    call output                         ; "Message from ASM ..."
    invoke hello                        ; "Message from C ..."
    invoke exit, 0
_main ENDP

output PROC                             ; ARG: EAX
    invoke printf, OFFSET fmt, OFFSET PorukaZaIspis, eax
    ret
output ENDP

END _main

让我们在C文件中添加一个输出函数:

抄送:

#include <stdio.h>

void hello ()
{
    puts ("Message from C: hello");
}

int greater(int a, int b)
{
    if ( a > b)
        return a;
    else
        return b;
}

使用以下批处理文件播放:

@ECHO OFF
SET VS_PATH=<Full\Path\to\Visual Studio\e.g.\C:\Program Files\Microsoft Visual Studio 10.0>

SET PATH=%VS_PATH%\VC\bin
SET LIB=%VS_PATH%\VC\lib
SET INCLUDE=%VS_PATH%\VC\include

SET CALLER=test.asm
SET CALLEE=C.c
SET TARGET=test.exe

echo cl_first
del %TARGET%
call :cl_first %CALLER% %CALLEE% %TARGET%
if exist %TARGET% %TARGET%

echo.
echo ml_first
del %TARGET%
call :ml_first %CALLER% %CALLEE% %TARGET%
if exist %TARGET% %TARGET%

goto :eof

:cl_first
cl /nologo /c %2
ml /nologo /Fe%3 %1 %~n2.obj /link /nologo
goto :eof

:ml_first
ml /nologo /c %1
cl /nologo /Fe%3 %~n1.obj %~n2.c /link /nologo
goto :eof
ny6fqffe

ny6fqffe2#

我刚从MASM for x86附带的tutorial/console/demo 1开始做了这个。它说使用下拉菜单“Project”中的console选项进行编译。它运行得很好,我认为它接近入门的最低要求:

.486                                    ; create 32 bit code
.model flat, stdcall                    ; 32 bit memory model
option casemap :none                    ; case sensitive

include \masm32\include\windows.inc     ; always first
include \masm32\macros\macros.asm       ; MASM support macros

include \masm32\include\masm32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\msvcrt.inc

includelib \masm32\lib\masm32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib

.code 

start:  

print chr$("Hey, this actually works.",13,10)

mov         eax, 0
mov         ebx, 0
printf("%d\t %d\t\n", eax, ebx);

mov         eax, 100
mov         ebx, 100
printf("%d\t %d\t\n", eax, ebx);

mov         eax, 100
mov         ebx, 100
add eax, ebx
printf("%d\t %d\t\n", eax, ebx)

exit
end start

我在这里使用printf从寄存器中打印。我发现printf本身会影响寄存器EAX、ECX和EDX,因为如果在printf语句传递add之前没有重新加载eax,add将不会给予正确的结果。(在调试器中单步调试并观察寄存器值的变化时,也会看到这种情况)。
这将从eax和ebx寄存器打印,但是你可以按照C语法打印任何你想要的东西。重要的是包括这两行:

include \masm32\include\msvcrt.inc
includelib \masm32\lib\msvcrt.lib

您需要同时包含这两个属性; .inc定义了printf() C样式宏,因此没有它语法将无法工作。
rkhb对这个问题的回答说明了如何手动声明printf,然后像调用其他函数一样用标准的MASM INVOKE调用它,或者甚至自己推参数,在没有MASM的情况下执行一个call printf

相关问题