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

Segfault when called from C on 64bit -fPIC (function with more than 8 parameters) #5707

Closed
vicuna opened this issue Jul 31, 2012 · 6 comments
Assignees
Milestone

Comments

@vicuna
Copy link

vicuna commented Jul 31, 2012

Original bug ID: 5707
Reporter: georg
Assigned to: @xavierleroy
Status: closed (set by @xavierleroy on 2015-12-11T18:08:30Z)
Resolution: fixed
Priority: normal
Severity: crash
Platform: x86_64
OS: Linux
OS Version: 3.2.0
Version: 3.12.1
Target version: 4.00.1+dev
Fixed in version: 4.00.1+dev
Category: back end (clambda to assembly)
Has duplicate: #5717
Monitored by: @lefessan @ygrek

Bug description

Dear developers,

I found a severe BUG in ocaml connected with ocaml code as a libary called from C++ under 64bit.
I compiled ocaml 3.12.1 version with -fPIC as in the INSTALL.

The problem occurs when a function with more than 8 parameters is called. The last parameters do have the right value and if
afterwards a reference is accessed than a segmentation fault occurs, see the code below.

Some Observations:
Calling it natively as an ocaml executable works.
Commenting the access to !v removes the segmentation fault but still the values are wrong.
Uncommenting line "test 10", causes the error already at this call and the subsequent call from C runs fine!

Here the code: also attached as a tar file:
---- m1.ml ------------------
module M2 = struct
let v = ref 0;;

let foo p1 p2 p3 p4 p5 p6 p7 p8 p9 =
prerr_endline "start";
print_endline (string_of_int p1);
print_endline (string_of_int p2);
print_endline (string_of_int p3);
print_endline (string_of_int p4);
print_endline (string_of_int p5);
print_endline (string_of_int p6);
print_endline (string_of_int p7);
print_endline (string_of_int p8);
print_endline (string_of_int p9);
print_endline (string_of_int !v);
prerr_endline "end";
;;
end;;

let test i : unit =
print_endline ("Initialising: " ^ (string_of_int i));
M2.foo 1 2 3 4 5 6 7 8 9;
;;

(* test 10;;*)

let _ = Callback.register "test" test;;
----- end m1.ml

----- interface.c
#include <caml/mlvalues.h>
#include <caml/callback.h>

#include "interface.h"

void ocaml_initialize(char** argv){
caml_main(argv);
}

void ocaml_test(){
value* test_pointer = caml_named_value("test");
caml_callback(*test_pointer, Val_int(11));
}
------ end interface.c

----- interface.h
void ocaml_initialize(char** argv);

void ocaml_test();
----- end interface.h

Steps to reproduce

You need ocaml to be compiled with -fPIC on order to be able to generate a shared libary on 64bit, as written in the INSTALL
./configure -cc "gcc -fPIC" -aspp "gcc -c -fPIC"

ocamlopt -o libmytest.so -ccopt -shared interface.c m1.ml
g++ -Wall main.cpp -lmytest -o test
./test

or unpack the tar file and call make.

Additional information

g++ : (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
ocaml: The Objective Caml toplevel, version 3.12.1
uname: Linux 3.2.0-27-generic #43-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux
CPU: Intel(R) Core(TM)2 Duo CPU E7600

File attachments

@vicuna
Copy link
Author

vicuna commented Jul 31, 2012

Comment author: @lefessan

Use "caml_startup(argv)" instead of "caml_main(argv)".

See http://caml.inria.fr/pub/docs/manual-ocaml/manual033.html#toc148

@vicuna
Copy link
Author

vicuna commented Jul 31, 2012

Comment author: @xavierleroy

Use "caml_startup(argv)" instead of "caml_main(argv)".

In native code, caml_startup and caml_main do exactly the same things, so this doesn't explain the observed crash. Reverting this PR to "new" and trying to reproduce.

@vicuna
Copy link
Author

vicuna commented Jul 31, 2012

Comment author: @xavierleroy

Here is what happens. Since m1.ml was compiled in shared library mode, the tail call from M1.test to M1.M2.foo goes through the dynamic loader, which clobbers registers r10 and r11, which ocamlopt uses to pass parameter "p9" and the implicit environment parameter to M1.M2.foo.

We might be able to fix this by using two C-callee-save registers for passing parameters #9 and #10, e.g. r12 and r13, instead of r10 and r11. That would work around the issue for Linux at least, but no guarantees for other platforms, esp. Win64.

@vicuna
Copy link
Author

vicuna commented Jul 31, 2012

Comment author: @lefessan

Doesn't that mean that ocamlopt should strictly obey C calling conventions regarding caller-save registers, at least when compiling with -shared ?

@vicuna
Copy link
Author

vicuna commented Jul 31, 2012

Comment author: @xavierleroy

Doesn't that mean that ocamlopt should strictly obey C calling conventions regarding caller-save registers, at least when compiling with -shared ?

No. It means that for parameter passing, ocamlopt should only use registers that the dynamic loader must preserve: either

  • registers that are used for parameter passing in the C calling convention, or
  • C callee-save registers (there are no callee-save registers in ocamlopt's conventions)

The AMD64 SVR4 ABI specification explicitly allows r11 to be destroyed by the dynamic loader. If my reading of fig 3.4 is correct, all other integer registers should be preserved, including r10 because it can be used to pass a static link to a function. The Linux dynamic loader destroys r10 (hmmmph) and r11, but seems to preserve all other regs.

@vicuna
Copy link
Author

vicuna commented Sep 8, 2012

Comment author: @xavierleroy

Tentative fix in 4.00 bugfix branch (r12907) and on trunk (r12908). The repro case works. The MSVC64 port needs testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants