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
Module initialization not performed for extern-only lib (? segfault) #6956
Comments
Comment author: @chambart It is not exactly that the module does not perform initialisation. In fact dependencies to external exported in the cmi file does not add a dependency to the implementation. The classical way to fix that kind of problem are to either: I'm not certain we may want to consider externals as real code dependency, but if we do, that would require adding the information in the Primitive.description type the original compilation unit that should contain this function and before emitting the code, add the dependencies of every present externals. |
Comment author: @Chris00
If you decide not to, it should be prominently clear in the manual. I suppose I am not the only one thinking that exporting an "external" in a .mli file is like "val" with the additional possibility of inlining the C call. It is absolutely unexpected that it bypasses the module initialization code. |
Comment author: @garrigue This is a well-known problem (cf. #4166) |
Comment author: @garrigue By the way, since this is a long-standing issue, I wouldn't qualify the priority as "high". |
Comment author: @damiendoligez If you use "val" in your .mli files instead of "external", ocamlopt will compile your calls in exactly the same way. IMO the proper solution to this problem is to add a warning to the documentation to the effect that users should never use "external" in their .mli files. |
Comment author: @Chris00
Then, why not add a warning to the compiler (turned on by default)? |
Comment author: @garrigue As an experiment, I've written a patch which warns on the user side: emit a warning whenever a primitive is called but the unit it belongs to is not referenced by the code. As you can see, this happens in the standard library itself, so I'm not sure this is a good practical solution. |
Comment author: @garrigue After looking at the results of my warning on various libraries (in particular lablgtk), I'm having a few other ideas. Conservative approach (avoiding breakage): Clever approach: This said, I can see plenty of examples which break all of these, without any clear bug. |
Comment author: @garrigue Of course, this also seems to mean that actually the warning approach is not the right one, and we should rather introduce internal dependencies, and force the linking. |
Comment author: @garrigue Added a new patch, require-external.diff, which forces the linking of modules containing used external definitions. |
Comment author: @xavierleroy I'm coming late to this discussion, but the one true way to ensure that initialization code from an OCaml module is always executed is to use the "-linkall" flag when building the library that contains this module. Without -linkall, the compiler is free to discard the module if it sees no use of the code defined in this module. It's a fairly simple policy (-linkall = important side effect in module initialization; no -linkall = module can be discarded), and I'm not sure we need more complicated mechanisms or heuristics. |
Comment author: @garrigue Xavier, while your statement is certainly true at this point (in particular due to the external problem), I'm not sure we want things to stay that way, and I am under the impression that efforts have been made in order to ensure that the "requirement" semantics is clear in most cases. One reason -linkall is not really an option is huge libraries like Core: do you really suggest to use -linkall for such libraries, whereas we have just introduced module aliases to avoid monolithic linking? I understand that -linkall only needs to be enable for side-effecting modules, but identifying them can be painful, and often one only wants the side-effect to occur if some functions in the module are used. When we introduced module aliases we have been careful not to change the "requirement" semantics in the default case: modules that were required at link time should stay required. If you think that this should be left unspecified, and that the compiler should be free to aggressively remove dependencies, then this should be clearly stated, because people are depending on that. |
Comment author: @garrigue Looking back again at this problem (and 4166), I really think that we should do something about it, as it bites even experienced users. The only reasonable alternative I see would be to deprecate the use of external in interfaces. |
Comment author: @alainfrisch Specifying the "requirement" semantics seems a good idea to me. Allowing externals in interfaces improves the bytecode, which could be especially important with js_of_ocaml (several low-level operations are exposed as externals, I'm not sure how clever is js_of_ocaml to detect them if they are not exposed as externals in the interface). Would it be shocking to specify the "requirement" semantics based on identifier resolution, including non-value-like components? (i.e. referring to a type Foo.t would force linking in Foo) This would be quite predictable at least. |
Comment author: @garrigue It's a bit more complex for types, as people do rely on the fact using only types requires no implementation (cf. parsetree.mli). |
Comment author: @xavierleroy |
Original bug ID: 6956
Reporter: @Chris00
Assigned to: @garrigue
Status: resolved (set by @xavierleroy on 2015-11-22T10:44:32Z)
Resolution: fixed
Priority: low
Severity: minor
Platform: amd64
OS: Debian GNU/Linux
OS Version: testing 3.18.16
Version: 4.01.0
Fixed in version: 4.03.0+dev / +beta1
Category: ~DO NOT USE (was: OCaml general)
Duplicate of: #4166
Related to: #7371
Monitored by: @hcarty @Chris00
Bug description
When a module exposes extern functions and only those are used in a program, the module initialization code is not executed. This is a problem because this code may register exceptions that may be raised by the C functions.
Steps to reproduce
A minimal example is attached (use "make" to compile). The "good" example correctly raise the exception, be the exception is explicitly mentioned in the program (which triggers the module initialization code). In the "bad" example, the exception is no longer mentioned and the program segfaults. The reason is because caml_named_value returns NULL and the dereferencing fails.
File attachments
The text was updated successfully, but these errors were encountered: