Bug 30300 - LTO drops entry point symbol
Summary: LTO drops entry point symbol
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.39
: P2 normal
Target Milestone: ---
Assignee: Nick Clifton
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-04-01 12:21 UTC by Pali Rohár
Modified: 2023-05-04 13:27 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2023-04-12 00:00:00


Attachments
Proposed patch (224 bytes, patch)
2023-04-12 16:09 UTC, Nick Clifton
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Pali Rohár 2023-04-01 12:21:02 UTC
Originally reported on: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109368

LTO for PE executables drops entry point symbol when the default entry point is used. There is no warning and just PE AddressOfEntryPoint is zeroed. Which results in broken PE binary.

When non-default entry point is used and specified via -e option then LTO does not drop entry point symbol and generates working PE executable.



Simple test case which does not use any system library or startup file:

$ cat test-nostartfiles.c
int mainCRTStartup(void) { return 0; }

Default console binary has entry point mainCRTStartup() function (as hardcoded in LD sources).

$ i686-w64-mingw32-gcc -Wall -Wextra -nostartfiles -nodefaultlibs -nostdlib test-nostartfiles.c -o test-nostartfiles.exe

Without LTO it generates working PE binary which correctly returns 0 to system. It also has correct AddressOfEntryPoint field in PE:

$ i686-w64-mingw32-objdump -p test-nostartfiles.exe | grep AddressOfEntryPoint
AddressOfEntryPoint     00001000



When compiling with LTO it does not throw any warning but generates broken PE binary:

$ i686-w64-mingw32-gcc -Wall -Wextra -nostartfiles -nodefaultlibs -nostdlib test-nostartfiles.c -o test-nostartfiles.exe -flto

Trying to run it, it crashes and has zeroed AddressOfEntryPoint:

$ i686-w64-mingw32-objdump -p test-nostartfiles.exe | grep AddressOfEntryPoint
AddressOfEntryPoint     00000000



When non-default entry point is used (specified via -e option) then LTO works correctly and does not drop its entry point.

$ cat test-nostartfiles2.c
int my_entry(void) { return 0; }

$ i686-w64-mingw32-gcc -Wall -Wextra -nostartfiles -nodefaultlibs -nostdlib -e _my_entry test-nostartfiles2.c -o test-nostartfiles2.exe -flto

$ i686-w64-mingw32-objdump -p test-nostartfiles2.exe | grep AddressOfEntryPoint
AddressOfEntryPoint     00001000

Compiled binary works fine.
Comment 1 Nick Clifton 2023-04-12 16:09:51 UTC
Created attachment 14816 [details]
Proposed patch

Hi Pali,

  Please try out this patch and let me know if it works for you.

  I must admit that for the longest time I was blaming the lto plugin
  rather than the linker, but it turns out that I was wrong.  The key
  fact is that if the user adds "-e <foo>" to the linker command line
  then code in ld/ldemul.c adds an unresolved reference to <foo> which
  in turn prevents the LTO compiler from optimizing away any definitions
  of <foo>.  When -e is not used, this undefined reference (to the default
  startup symbol _mainCRTStartup) was not being created and so the LTO
  compiler was dropping the code...

Cheers
  Nick
Comment 2 Pali Rohár 2023-04-15 09:45:03 UTC
Hello! Patch is working fine:

$ x86_64-w64-mingw32-gcc -Wall -Wextra -nostartfiles -nodefaultlibs -nostdlib test-nostartfiles.c -o test-nostartfiles.exe -flto

$ x86_64-w64-mingw32-objdump -p test-nostartfiles.exe | grep AddressOfEntryPoint
AddressOfEntryPoint     0000000000001000

$ x86_64-w64-mingw32-objdump -d test-nostartfiles.exe | head -18

test-nostartfiles2.exe: formát súboru pei-x86-64


Rozloženie sekcie .text:

0000000140001000 <mainCRTStartup>:
   140001000:   55                      push   %rbp
   140001001:   48 89 e5                mov    %rsp,%rbp
   140001004:   b8 00 00 00 00          mov    $0x0,%eax
   140001009:   5d                      pop    %rbp
   14000100a:   c3                      retq
   14000100b:   90                      nop
   14000100c:   90                      nop
   14000100d:   90                      nop
   14000100e:   90                      nop
   14000100f:   90                      nop
Comment 3 Pali Rohár 2023-05-03 20:00:05 UTC
Do you need some more testing?
Comment 4 Sourceware Commits 2023-05-04 13:26:31 UTC
The master branch has been updated by Nick Clifton <nickc@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=3539414584be0094b0a4fe56dfd64ea79d802edc

commit 3539414584be0094b0a4fe56dfd64ea79d802edc
Author: Nick Clifton <nickc@redhat.com>
Date:   Thu May 4 14:24:16 2023 +0100

    Stop the linker from loosing the entry point for COFF/PE code when compiling with LTO enabled.
    
      PR 30300
      * emultempl/pep.em (set_entry_point): Add an undefined reference to the entry point if it has been constructed heuristically.
      * emultempl/pe.em (set_entry_point): Likewise.
Comment 5 Nick Clifton 2023-05-04 13:27:13 UTC
(In reply to Pali Rohár from comment #3)
> Do you need some more testing?

No - just me needing a reminder.  (I have so many balls in the air at the moment..)