Skip to content

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

Open
dra27 wants to merge 4 commits into
ocaml:masterfrom
dra27:entrypoint
Open

Ensure the entrypoint is taken into account when computing the export table#146
dra27 wants to merge 4 commits into
ocaml:masterfrom
dra27:entrypoint

Conversation

@dra27

@dra27 dra27 commented Nov 21, 2024

Copy link
Copy Markdown
Member

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.

dra27 added 4 commits July 21, 2026 18:23
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.

@nojb nojb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only glanced at the code, but LGTM

Comment thread reloc.ml
Some "main"
| `MINGW | `MINGW64 -> begin
let entry_point s =
String.length s > 7 && String.sub s 0 7 = "-Wl,-e,"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't -Wl,-e rewritten into -link arguments (cf

flexdll/cmdline.ml

Lines 281 to 285 in ab8161f

| s :: rest when String.length s >= 6 && String.sub s 0 5 = "-Wl,-" ->
let args =
String.split_on_char ',' (String.sub s 4 (String.length s - 4))
in
List.fold_right (fun arg args -> "-link" :: arg :: args) args (tr rest)
) ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Urgh - good catch, and unfortunately it's because this part is completely broken. The problem here is that when we link MSVC we actually call the linker, but when we link mingw-w64, we call the C compiler! I added this bit here for completeness - for MSVC, we of course select Unicode with -link /entry:wmainCRTStartup and that works because flexlink passes the /entry: directly to /link. For mingw-w64, we of course do -link -municode and that survives being passed to the C compiler to do the linking, but -Wl,-e,foo won't work being passed to the C compiler as -e foo. I only added this handling for -Wl,-e because I just written the /entry: part for MSVC (which I knew was needed).

Hmm. Given that you can't specify the entry point at the moment on mingw-w64, I'm tempted to remove this bit of code and just add a comment that you can't change the entrypoint on mingw-w64?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh - coffee kicks in! I remember how this works - the -Wl, translation to -link is a shim to allow common options to pass through. You can specify the entry point on mingw-w64, it's just that you have to say -link -Wl,-e,entrypoint at which point the code above does work as intended!

Comment thread CHANGES
Comment on lines +40 to +42
- GPR#146: Take the entrypoint (main, wmainCRTStartup, etc.) into account when
determining the modules which will be linked. For mingw-w64, select crt2u.o
instead of crt2.o if -link -municode is specified (David Allsopp)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be moved in the section above.

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.

3 participants