从汇编代码中调用fgets函数

3
我想知道如何从汇编代码中调用fgets函数。我读了这些问题,它们与此问题完全相同:如何在x86汇编中调用fgets函数?以及如何在汇编语言中使用C库函数fgets?但两者都不令人满意,因为: 1. 我真的想使用fgets函数,因为我想让我的代码在Windows和Linux上运行(我正在使用NASM) 2. 我查看了fgets函数的反汇编版本,但不幸的是,它没有提供必要的细节来重现表示stdin的方式。以下是我使用gcc -S fgets.c获得的C和汇编代码。

fgets.c

#include <stdio.h>
int main()
{
    char name[15];
    fgets(name, 16, stdin);
    return 0;
}

fgets.s

    .file   "fgets.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    call    ___main
    movl    __imp___iob, %eax
    movl    %eax, 8(%esp)
    movl    $16, 4(%esp)
    leal    17(%esp), %eax
    movl    %eax, (%esp)
    call    _fgets
    movl    $0, %eax
    leave
    ret
    .def    _fgets; .scl    2;  .type   32; .endef

首先,我不擅长阅读AT&T语法,因此很难理解上面的汇编源代码。

所以,有人能帮我弄清楚吗?(i)我的本地变量name位于哪里?在ESP+17吗?(ii)如果__imp___iob表示stdin,那么它是从哪里来的,这样我就可以使用它了吗?

谢谢


1
我也不喜欢AT&T语法。 使用gcc -S -masm = intel fgets.c获取更好的反汇编。 - Michael Burr
@Burr:哈哈哈,有点让人不舒服。谢谢你的提示!!!救了我一命。 - Khan2011
2个回答

4

是的,名称在esp+17处。最好初始化它。按照cdecl调用约定,参数从右到左传递,最深的在堆栈底部。 __imp___iob 是从CRT导出的,在stdio.h头文件中可以找到。搜索stdin。__imp前缀是Microsoft约定,使来自DLL的导出更快。正确地处理这个问题显然是编译器的工作。


@Passant:我追踪了__imp_iob,发现它是在某个DLL(ntdll.dll?)中定义的数据。正如你所指出的,编译器在这里做得很好,但我该如何访问并使用该值呢?我可以在.text部分中执行一些extern __imp_iob,但这样做是否正确呢?欢迎进一步建议! - Khan2011
如果编译器生成了代码,那么它很可能是正确的。你需要链接与编译器使用相同的CRT库。你似乎在混合使用工具,所以很难做出判断。这样做的目的是什么? - Hans Passant
@Khan2011:我猜你正在使用MinGW。__imp_iob/mingww/include/stdio.h 中声明,并在 libmsvcrt.a 中解析,它是msvcrt.dll的导入库。你应该能够使用相同的库来链接你的汇编程序。我不确定C运行时可能需要什么特殊的初始化(如果有)。通过查看MinGW的内部工作过程可以回答这个问题。 - Michael Burr
@Passant:经过你的解释,很清楚我不能依赖于fgets。更好的方法是让C在汇编中完成“打印”和“扫描”的部分以实现可移植性,但调用fgets会自动丢弃可移植性部分。因此,进一步追求这个问题没有意义。 @Burr:感谢您提供的详细信息。研究CRT初始化部分不再必要[不可移植]。 - Khan2011

1
(i)我的本地变量name在哪里?在ESP+17吗?
是的 - 实际上,当您进行调用时,“name”会衰减为指针 - 整个数组都在堆栈上。第一个字节位于ESP+17处,其余字节随后。请注意,代码使用leal来获取该数组的第一个字节的地址以传递给fgets()
(ii)如果__imp___iob表示stdin,则我可以从哪里获取它以便使用它?
您应该能够直接使用为您的机器定义的任何stdin。例如,在我的Mac上,它最终成为___stdinp。您可以在系统头文件中追踪定义(从stdio.h开始)。您可能需要一个.globl指令来使汇编器不抱怨,但它应该正确链接。__imp___iob看起来是您的正确选择。
以下是我在stdio.h中找到的内容:
#if __DARWIN_UNIX03
#define stdin   __stdinp
#define stdout  __stdoutp
#define stderr  __stderrp
#else /* !__DARWIN_UNIX03 */
#define stdin   (&__sF[0])
#define stdout  (&__sF[1])
#define stderr  (&__sF[2])
#endif /* __DARWIN_UNIX03 */

我的汇编代码需要额外的_,因为编译器无法自动添加它。


@Norum:感谢您清晰的解释!我在stdio.h中找到了我的stdin,但我该如何在汇编代码中使用它?我有一些编译的代码,但它不能完成工作!我相对于stackoverflow上比较新,所以我不确定在哪里发布我的代码。感谢您提供任何新的见解! - Khan2011

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接