【问题标题】:Assembly code of a hello world program in CC语言中的hello world程序的汇编代码
【发布时间】:2017-12-22 22:53:55
【问题描述】:

这是一个hello world程序的示例代码:

#include <stdio.h>


int main(void)
{
    printf("Hello, world!\n");
    return 0;
}

这是我得到的目标文件的反汇编:

File Type: COFF OBJECT

main:
  0000000000000000: 48 83 EC 28        sub         rsp,28h
  0000000000000016: C3                 ret

__local_stdio_printf_options:
  0000000000000000: 48 8D 05 00 00 00  lea         rax,[?_OptionsStorage@?1??__local_stdio_printf_options@@9@9]
                    00
  0000000000000007: C3                 ret

_vfprintf_l:
  0000000000000000: 4C 89 4C 24 20     mov         qword ptr [rsp+20h],r9
  0000000000000014: 48 83 EC 38        sub         rsp,38h
  0000000000000018: E8 00 00 00 00     call        __local_stdio_printf_options
  0000000000000039: E8 00 00 00 00     call        __stdio_common_vfprintf
  000000000000003E: 48 83 C4 38        add         rsp,38h
  0000000000000042: C3                 ret

printf:
  0000000000000000: 48 89 4C 24 08     mov         qword ptr [rsp+8],rcx
  0000000000000027: E8 00 00 00 00     call        __acrt_iob_func
  000000000000002C: 4C 8B 4C 24 28     mov         r9,qword ptr [rsp+28h]
  000000000000003C: E8 00 00 00 00     call        _vfprintf_l
  0000000000000041: 89 44 24 20        mov         dword ptr [rsp+20h],eax
  000000000000004E: 8B 44 24 20        mov         eax,dword ptr [rsp+20h]
  0000000000000056: C3                 ret

printf 函数中,我们还有两个函数调用:__acrt_iob_func_vfprintf_l

  1. 第一个函数具体是做什么的?

  2. 我们如何在目标文件中拥有_vprintf_l 函数?

请提供详细解释。

编译: cl /TC main.cpp

反汇编为: dumpbin /disasm main.obj

PS:我删除了部分汇编代码,以便我可以发布问题。

【问题讨论】:

  • sub rsp,28h / ret 因为main 的整个主体是零意义。如果您将此目标文件链接到可执行文件并运行它,它就不会是实际运行的内容,因为它只会崩溃(除非某些东西使用jmp 以低于当前rsp 值的返回地址输入main,但是Windows ABI 没有红色区域...)
  • @PeterCordes 你仔细阅读了这个问题吗?
  • 哦,您是否手动编辑它而不留下...; comment?你说的“PS”就是这个意思吗?哦,我猜是这样,这可以解释main 中这些指令地址的不连续性。我建议留在main 中的call 指令中,这样就不会分散注意力,并且对于那些好奇实际上以什么顺序调用哪个辅助函数的人来说。
  • 您删除的汇编代码使您发布的内容变得毫无意义
  • 这真的不是一个很好的第一个程序开始:我想看看程序的汇编。尝试类似 int fun ( int x, int y ) { return (x+y+4); } assemble 反对反汇编。然后变得更复杂,但一开始调用库函数并不是那么有趣。了解调用和返回的基础知识以及简单的操作。

标签: c assembly visual-c++ x86 msvcrt


【解决方案1】:

VS2015 及更高版本现在在头文件中包含 printf 的部分代码(以前它只是一个基于库的函数)。对于 C / C++,这不是问题,但在我的情况下,否则只能是汇编项目,我现在在项目中包含一个小的 C 源文件,该文件调用 printf,它生成所需的代码,因此汇编代码可以调用 printf没有与标头相关的所有其他内容包括代码。

第一个函数具体是做什么的?

__local_stdio_printf_options 返回一个指向静态整数的指针,该指针用作 __stdio_common_vfprintf 的输入。

我们如何在目标文件中拥有 _vprintf_l 函数?

这是因为部分 printf 代码现在包含在头文件中,而之前所有 printf 代码都包含在标准 C 库中。

这是包含在当前本地 printf 实例中的内容的 sn-p。这两个定义我没有外推,但最终结果可以在下面的汇编代码中看到。

__inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void)
{
    static unsigned __int64 _OptionsStorage;
    return &_OptionsStorage;
}

#define _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS (*__local_stdio_printf_options())

_CRT_STDIO_INLINE int __CRTDECL _vfprintf_l(
    _Inout_  FILE*       const _Stream,
    _In_z_   char const* const _Format,
    _In_opt_ _locale_t   const _Locale,
             va_list           _ArgList
    )
{
    return __stdio_common_vfprintf(_CRT_INTERNAL_LOCAL_PRINTF_OPTIONS, _Stream, _Format, _Locale, _ArgList);
}

_CRT_STDIO_INLINE int __CRTDECL printf(
    _In_z_ _Printf_format_string_ char const* const _Format,
    ...)
{
    int _Result;
    va_list _ArgList;
    __crt_va_start(_ArgList, _Format);  /* this is a define */
    _Result = _vfprintf_l(stdout, _Format, NULL, _ArgList);
    __crt_va_end(_ArgList);             /* this is a define */
    return _Result;
}

VS2015 的汇编输出的这个 sn-p 使它更清楚发生了什么。同样,请记住,所有这些都是 printf 内部的,并且可能会随着更高版本的 Visual Studio 发生变化。

CONST   SEGMENT
??_C@_0P@DOOKNNID@Hello?0?5world?$CB?6?$AA@ DB 'Hello, world!', 0aH, 00H
CONST   ENDS

_TEXT   SEGMENT
__local_stdio_printf_options PROC
        lea     rax, OFFSET FLAT:?_OptionsStorage@?1??__local_stdio_printf_options@@9@9 ;static variable
        ret     0
__local_stdio_printf_options ENDP

_vfprintf_l PROC
;       ...
        call    __local_stdio_printf_options            ;local instance
;       ...
        call    QWORD PTR __imp___stdio_common_vfprintf ;library function
;       ...
        ret     0
_vfprintf_l ENDP

printf  PROC
;       ...
        call    QWORD PTR __imp___acrt_iob_func         ;library function
        mov     rbx, rax
        call    __local_stdio_printf_options            ;local instance
;       ...
        call    QWORD PTR __imp___stdio_common_vfprintf ;library function
        ret     0
;       ...
printf  ENDP

main    PROC
        sub     rsp, 40                                 ; 00000028H
        lea     rcx, OFFSET FLAT:??_C@_0P@DOOKNNID@Hello?0?5world?$CB?6?$AA@
        call    printf
        xor     eax, eax
        add     rsp, 40                                 ; 00000028H
        ret     0
main    ENDP
_TEXT   ENDS

这是 VS 早期版本的汇编输出,其中 printf 只是一个库函数 (__imp_printf):

CONST   SEGMENT
??_C@_0P@DOOKNNID@Hello?0?5world?$CB?6?$AA@ DB 'Hello, world!', 0aH, 00H
CONST   ENDS

_TEXT   SEGMENT
main    PROC
        sub     rsp, 40                                 ; 00000028H
        lea     rcx, OFFSET FLAT:??_C@_0P@DOOKNNID@Hello?0?5world?$CB?6?$AA@
        call    QWORD PTR __imp_printf
        xor     eax, eax
        add     rsp, 40                                 ; 00000028H
        ret     0
main    ENDP
_TEXT   ENDS

【讨论】:

  • 这能回答问题的任何部分吗?
  • @prl - 我更新了我的答案,但在我更新之前,是的,它回答了部分问题,因为自 VS2015 以来,printf 的部分代码现在位于包含文件中,这意味着它最终在与用户源代码文件相同的目标文件中。
【解决方案2】:

__acrt_iob_func 是 Microsoft Visual C 中的内部 C runtime (CRT) 函数。

[They] are used internally to implement the standard C and C++ libraries

投机使用__acrt_iob_func

_acrt_iob_func 可能用于设置基本 I/O,例如分配 stdin stdoutstderr 以及一些对

真正不可见的东西

vprintf

printf(3) 通常是vprintf(3) 的包装函数,它以va_list 作为参数

【讨论】:

    【解决方案3】:

    编译器使用内置函数,主要是在linux中通过libc。这个内部版本是为在多个平台上工作而构建的,并且还有一些优化。如果你查询 google 可以查看 libc 的源代码。 您还可以使用调试器跟踪函数并查看它在做什么。

    总而言之,这取决于您的编译器和库的实现。 Hello world 也可以手动缩短。

    【讨论】:

    • 1.这是 Windows 上的 MSVC; libc 源代码(头文件除外)不可用。 2.它没有像gcc那样优化printf()puts();这不是正在发生的事情。
    猜你喜欢
    • 2011-01-11
    • 1970-01-01
    • 2013-10-29
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多