Arhad Arhad - 2 months ago 13
C Question

Prevent __start entry point from being optimized out

When I have the following

CFLAGS
:

-Wall -Werror -Wextra -pedantic -std=c99 -O3 -nostartfiles -nodefaultlibs


my
__start
entry point is successfully put into an output executable and specified as an entry point of an executable.

However, when I add
-flto
flag, the whole entry point is optimized out (with the rest of the code being invoked via the entry only, either directly on indirectly). Despite of this, linking is performed with no error, bit with some random entry point.

A question is how to prevent
__start
function from being optimized out. I am also curious why a linker “forgets” about an external dependency on
__start
if its default implementation is omitted via
-nostartfiles
.

My GCC version is gcc (i686-posix-dwarf-rev1, Built by MinGW-W64 project) 4.9.2.




UPD:

Source code (fixed with help of @FUZxxl, who wrote about prepended underscores in Windows ABI):

#include <windows.h>

void _start()
{
MessageBox(NULL, TEXT("Hello world."), TEXT(""), MB_OK);
ExitProcess(0);
}


Assembly output:


  1. Non-
    -flto
    version:

    Disassembly of section .text:

    00401000 <__start>:
    401000: 83 ec 1c sub $0x1c,%esp
    401003: c7 44 24 0c 00 00 00 movl $0x0,0xc(%esp)
    40100a: 00
    40100b: c7 44 24 08 00 20 40 movl $0x402000,0x8(%esp)
    401012: 00
    401013: c7 44 24 04 0d 20 40 movl $0x40200d,0x4(%esp)
    40101a: 00
    40101b: c7 04 24 00 00 00 00 movl $0x0,(%esp)
    401022: ff 15 54 40 40 00 call *0x404054
    401028: 83 ec 10 sub $0x10,%esp
    40102b: c7 04 24 00 00 00 00 movl $0x0,(%esp)
    401032: ff 15 4c 40 40 00 call *0x40404c
    401038: 90 nop
    401039: 90 nop
    40103a: 90 nop
    40103b: 90 nop
    40103c: 90 nop
    40103d: 90 nop
    40103e: 90 nop
    40103f: 90 nop

    00401040 <__CTOR_LIST__>:
    401040: ff (bad)
    401041: ff (bad)
    401042: ff (bad)
    401043: ff 00 incl (%eax)
    401045: 00 00 add %al,(%eax)
    ...

    00401048 <__DTOR_LIST__>:
    401048: ff (bad)
    401049: ff (bad)
    40104a: ff (bad)
    40104b: ff 00 incl (%eax)
    40104d: 00 00 add %al,(%eax)

  2. -flto
    version (notice the lack of
    _start
    here, just a bunch of stubs for API entries):

    Disassembly of section .text:

    00401000 <_ExitProcess@4>:
    401000: ff 25 4c 30 40 00 jmp *0x40304c
    401006: 90 nop
    401007: 90 nop

    00401008 <_MessageBoxA@16>:
    401008: ff 25 54 30 40 00 jmp *0x403054
    40100e: 90 nop
    40100f: 90 nop

    00401010 <__CTOR_LIST__>:
    401010: ff (bad)
    401011: ff (bad)
    401012: ff (bad)
    401013: ff 00 incl (%eax)
    401015: 00 00 add %al,(%eax)
    ...

    00401018 <__DTOR_LIST__>:
    401018: ff (bad)
    401019: ff (bad)
    40101a: ff (bad)
    40101b: ff 00 incl (%eax)
    40101d: 00 00 add %al,(%eax)


Answer

With all the exotic/embedded-related options you've set, you have to ensure that your symbol is seen as your entrypoint and not garbage collected by linker optimizations (--gc-sections also does that: collecting "useless" sections)

You can end up with a fully empty .elf file since no section is reachable.

To tell the linker that you are using that symbol as an entrypoint (and avoid that the linker eludes it!), just add

-Wl,-e__start

option to your link command (or write a linker spec file where you declare your symbol, but the command line option is easier)

Comments