-3

Here is a sample code of hello world program:

#include <stdio.h>


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

This is a disassembly of object file I've got:

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

In the printf function, we have two more function calls: __acrt_iob_func and _vfprintf_l.

  1. What exactly does the first function do?

  2. How do we have the _vprintf_l function in the object file?

A detailed explanation would be appreciated.

Compiled with: cl /TC main.cpp

Disassembled with: dumpbin /disasm main.obj

PS: I removed assembly code partly so that I can post the question.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Tracy
  • 59
  • 1
  • 8
  • `sub rsp,28h` / `ret` as the entire body of `main` makes zero sense. It can't be what actually runs if you link this object file into an executable and run it, because it would just crash (unless something uses `jmp` to enter main with a return address below the current `rsp` value, but the Windows ABI doesn't have a red-zone...) – Peter Cordes Dec 23 '17 at 00:19
  • @PeterCordes did you read the question carefully? – Tracy Dec 23 '17 at 00:22
  • Oh, did you manual edit it out without leaving a `...` or a `; comment`? Is that what you meant by that "PS"? Oh, I guess so, that would explain the discontinuity in addresses of those instructions in `main`. I'd recommend leaving in the `call` instructions in `main` so that isn't a distraction, and for people that are curious which helper function is actually called in what order. – Peter Cordes Dec 23 '17 at 00:27
  • 1
    They assembly code that you removed has made what you posted non-sensical – David Hoelzer Dec 23 '17 at 01:52
  • 1
    this really isnt a great first program to start with for: I want to look at the assembly of a program. try things like int fun ( int x, int y ) { return (x+y+4); } assemble to object the disassemble. then get more complicated but calling library functions is not all that interesting at first. understand the basics of calling and returning and simple operations. – old_timer Dec 23 '17 at 02:09

3 Answers3

2

VS2015 and later versions now contain part of the code for printf in header files (previously it was just a library based function). For C / C++, it's not an issue, but in my case for what would otherwise be assembly only project, I now include a small C source file in the project that calls printf, which generates the needed code so the assembly code can call printf without all the other stuff related to the header included code.

What exactly does the first function do?

__local_stdio_printf_options returns a pointer to a static integer, which is used as an input for __stdio_common_vfprintf.

How do we have the _vprintf_l function in the object file?

This is because part of printf code is now included in a header file, where previously all of printf was included in the standard C library.

This is a snippet of what is being included for the now local instance of printf. I didn't extrapolate the two defines, but the end result can be seen in the assembly code below.

__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;
}

This snippet of the assembly output from VS2015 makes it a bit clearer what is going on. Again, keep in mind that all of this is internal to printf, and could change with a later version of 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

Here is the assembly output from a prior version of VS, where printf is just a library function (__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
rcgldr
  • 27,407
  • 3
  • 36
  • 61
  • Does this answer any part of the question? – prl Dec 23 '17 at 02:16
  • @prl - I updated my answer, but prior to my update, yes it answered part of the question in that since VS2015, part of the code for printf is now in an include file, which means it ends up in the same object file as the user's source code file. – rcgldr Dec 23 '17 at 03:53
1

__acrt_iob_func is the internal C runtime (CRT) function in Microsoft Visual C.

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

Speculative use of __acrt_iob_func

The _acrt_iob_func is probably used to set up the basic I/O e.g. assigning stdin stdout and stderr and some stuff that's really not visible to the

vprintf

printf(3) is typically a wrapper function around the vprintf(3) which takes va_list as a parameter

Ahmed Masud
  • 21,655
  • 3
  • 33
  • 58
-2

The compiler uses built-in function, mostly in linux by libc. This internal versions are built to work on multiple platforms and also there are some optimizations. You can have a look on the source code of libc if you query google. You could also trace down the functions with a debugger and see exactly what it is doing.

To sum it up, it is up to your implementation of compiler and library. Hello world could also be done shorter by hand.

recycler
  • 1,301
  • 9
  • 9
  • 1. this is MSVC on Windows; libc source code (other than headers) aren't available. 2. it didn't optimize `printf()` to `puts()` the way gcc does; that's not what's going on. – Peter Cordes Dec 23 '17 at 00:22