Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure the entrypoint is taken into account when computing the export table #146

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

dra27
Copy link
Member

@dra27 dra27 commented Nov 21, 2024

Presently on trunk and - as far as I can tell - since OCaml 3.11, in Cygwin64:

$ make natruntop
OCaml version 5.4.0+dev0-2024-08-25 - native toplevel
Enter #help;; for help.

# #load "unix.cmxs";;
Error while loading otherlibs/unix/unix.cmxs: error loading shared library: Failure("Cannot resolve caml_debugger_in_use").

This is strange, since ocaml/ocaml@27780d9#diff-3c8eb806a6be5e7baa5af9546b7e12cdfa1220daae562235a450f87b93dd06e7 in OCaml 3.12 ensures that debugger.o is linked into OCaml programs precisely to prevent this problem. The symbol is indeed present:

$ nm ocamlnat.exe | fgrep caml_debugger_in_use
0000000100f89818 B caml_debugger_in_use

the issue is that it is not included in the flexlink export table:

$ PATH=opt/bin:$PATH ./boot/ocamlrun.exe ./ocamlopt.exe -nostdlib -I ./stdlib -I otherlibs/dynlink -I utils -I parsing -I typing -I bytecomp -I file_formats -I lambda -I middle_end -I middle_end/closure -I middle_end/flambda -I middle_end/flambda/base_types -I asmcomp -I driver -I toplevel -I tools -I runtime -I otherlibs/dynlink -I otherlibs/str -I otherlibs/systhreads -I otherlibs/unix -I otherlibs/runtime_events -g -linkall -I toplevel/native   -o ocamlnat.exe compilerlibs/ocamlcommon.cmxa compilerlibs/ocamloptcomp.cmxa compilerlibs/ocamlbytecomp.cmxa otherlibs/dynlink/dynlink.cmxa compilerlibs/ocamltoplevel.cmxa  toplevel/topstart.cmx -cclib -show-exports | fgrep caml_debugger_in_use

The reason is that while -lasmrun is passed to flexlink, the objects passed on the command line give flexlink no reason to link startup_nat.n.o, which would then cause debugger.n.o to be considered and consequently these symbols to be added to the export table.

The fundamental fix here is that when linking an executable, flexlink now considers the entry point when closing over the required list of objects. For Cygwin, this fix is quite simple as it suffices to consider linking main. Cygwin's gcc does link crt0.o, but the full mechanism is technically a bit more involved. This doesn't matter, though, because being a Posix system, the entrypoint is always main (not wmain or WinMain/wWinMain, etc.).

For both MSVC and mingw-w64, the fix is a little more complex as, being non-Posix, there's not a strict requirement for the entrypoint to be main. For mingw-w64, the entrypoint is mainCRTStartup, but whether that calls main or wmain is determined by -municode which selects between using crt2.o or crt2u.o as the startup object. MSVC is affected both by the /ENTRY and /SUBSYSTEM linker options, which select between one of mainCRTStartup, wmainCRTStartup, WinMainCRTStartup or wWinMainCRTStartup. MSVCRT.lib contains each of those symbols in a separate object each of which then has an import reference to either main, wmain, WinMain or wWinMain as required. Since OCaml 4.06, OCaml therefore uses -municode for mingw-w64 and /ENTRY:wMainCRTStartup for MSVC.

This leads to the main part of this PR - the entrypoint for mingw-w64 and MSVC is therefore defined not in one of the objects or libraries passed on the command line (i.e. in libasmrun.a), but in one of the default libraries (either crt2u.o or MSVCRT.lib). The problem is that while these libraries are scanned, they are not analysed when closing over the objects which need to be linked. This is correct (the default libraries are assumed to be being linked in both executable and DLL) - but the objects should be considered in case they import symbols which do come from the objects and libraries passed on the command line. The changes here therefore mean that for mingw-w64, mainCRTStartup gets resolved to crt2u.o. That module imports wmain which then causes main.n.o from libasmrun.a to be linked and, consequently startup_nat.n.o and hence debugger.n.o. This PR happens to be an alternate fix (or, rather, workaround) for ocaml/ocaml#13520.

The defined_in function is used when determining which objects from the
command line will actually be linked. However, this doesn't include the
default libraries, which creates an issue for modules which are linked
as a result of main.

defined_in is augmented to include the mapping of symbols found in the
default libraries, but some additional care is required to ensure that
this doesn't cause imports from those to leak into the export table of
the executable.
If -municode is passed to mingw-w64's gcc, then crt2u.o (which causes
wmain to be selected) should be linked rather than crt2.o. In passing,
the code now sniffs for -nostartfiles, which causes neither crt2.o nor
crt2u.o to be considered.
The export table is determined by looping over the objects included on
the command line. If the main symbol is included in a library (e.g.
libcamlrun or libasmrun) then there may not be an object on the
commandline which causes it, and any transitive dependencies, to be
linked.

The entrypoint symbol for the Cygwin/mingw-w64/MSVC toolchains is now
resolved, and may cause additional objects to be linked.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant