[Ecls-list] Problem with foreign data pointers from a C library on 64 bits
Philippe Hanrigou
philippe.hanrigou at gmail.com
Thu Sep 1 23:19:50 UTC 2011
Hi,
I am having some problems using ffi to relay pointers between
ECL and a custom C library. It looks as if the pointers end up
being truncated to 32 bits for a reason that completely escapes
me... My C is rusty and I am more than green in Common LISP,
so my hope is that somebody here might be kind enough to help
me out ;-)
I can reproduce the problem with this minimal setup:
- A C file simulating native code:
// == foo-native.c ===================================
#include <stdlib.h>
#include <ecl/ecl.h>
void *create_pointer() {
void *magic;
printf("\n>>> [create pointer] enter\n");
magic = malloc(sizeof *magic);
if (NULL == magic) {
FEerror("Can't allocate memory", 2);
return NULL;
}
printf("<<< [create pointer] got %p \n", magic);
return magic;
}
char *use_pointer(void *p) {
printf("\n>>> [use pointer] got %p\n", p);
return "OK";
}
// ===================================================
- I compile it to the libfoo.a library with
# ===================================================
cflags="`ecl-config --cflags` -I. -I${HOME}/Projects/ecl/deps/gc-7.2alpha6/install/include -Wall -g"
gcc ${cflags} -o foo-native.o -c foo-native.c
ar -rcs libfoo.a foo-native*.o
# ===================================================
- I then try to drive the C library from ECL with
;; == foo.lisp ==========================================
(defpackage :foo
(:use :common-lisp :ffi))
(in-package :foo)
(proclaim '(OPTIMIZE (SAFETY 3) (DEBUG 3) (SPEED 0)))
(ffi:load-foreign-library "foo" :system-library t)
(defun new-p ()
(c-inline () () :pointer-void "@(return)=create_pointer();" :side-effects t))
(defun use-p (p)
(c-inline (p) (:pointer-void) :cstring "@(return)=use_pointer(#0);" :side-effects t))
(use-p (new-p))
;; ===================================================
- Define an ASDF file
;; === foo.asd =========================================
(proclaim '(optimize (safety 3) (debug 3) (speed 2)))
(require 'asdf)
(asdf:defsystem :foo
:name "Foo"
:components ((:file "foo")))
;; ===================================================
- And try to run the whole thing with :
#!/usr/bin/env ecl -shell
;; ===================================================
(require 'asdf)
(push #p"./" asdf:*central-registry*)
(asdf:oos 'asdf:load-op 'foo)
(require 'foo)
;; ===================================================
But then I get:
;; ===================================================
;;; Loading #P"/opt/local/lib/ecl-11.1.1/asdf.fas"
;;; Loading #P"/opt/local/lib/ecl-11.1.1/cmp.fas"
;;; Loading "/Users/ph7/Projects/ECL-Bugs/foo.asd"
;;;
;;; Compiling /Users/ph7/Projects/ECL-Bugs/foo.lisp.
;;; OPTIMIZE levels: Safety=3, Space=0, Speed=2, Debug=3
;;;
;;; Compiling (DEFUN NEW-P ...).
;;; Compiling (DEFUN USE-P ...).
;;; End of Pass 1.
;;; Emitting code for NEW-P.
;;; Emitting code for USE-P.
;;; Note:
;;; Invoking external command:
;;; gcc -I. -I/opt/local/include/ -I/Users/ph7/Projects/ecl/deps/gc-7.2alpha6/install/include -g -O2 -fPIC -fno-common -g -O2 -fPIC -fno-common -D_THREAD_SAFE -Ddarwin -O2 -w -c /Users/ph7/.cache/common-lisp/ecl-11.1.1-cfcfee52-macosx-x86/Users/ph7/Projects/ECL-Bugs/ASDF-TMP-foo.c -o /Users/ph7/.cache/common-lisp/ecl-11.1.1-cfcfee52-macosx-x86/Users/ph7/Projects/ECL-Bugs/ASDF-TMP-foo.o
;;; Finished compiling /Users/ph7/Projects/ECL-Bugs/foo.lisp.
;;;
;;; Note:
;;; Invoking external command:
;;; gcc -I. -I/opt/local/include/ -I/Users/ph7/Projects/ecl/deps/gc-7.2alpha6/install/include -g -O2 -fPIC -fno-common -g -O2 -fPIC -fno-common -D_THREAD_SAFE -Ddarwin -O2 -w -c /Users/ph7/.Trash/eclinitplVKkA.c -o /Users/ph7/.Trash/eclinitplVKkA.o
;;; Note:
;;; Invoking external command:
;;; gcc -o /Users/ph7/.cache/common-lisp/ecl-11.1.1-cfcfee52-macosx-x86/Users/ph7/Projects/ECL-Bugs/foo.fas -L/opt/local/lib/ /Users/ph7/.Trash/eclinitplVKkA.o /Users/ph7/.cache/common-lisp/ecl-11.1.1-cfcfee52-macosx-x86/Users/ph7/Projects/ECL-Bugs/foo.o -bundle -L/Users/ph7/Projects/ecl/deps/gc-7.2alpha6/install/lib -L/Users/ph7/Projects/ecl/deps/gc-7.2alpha6/install/lib -lecl -lgc -lpthread -lm -lfoo
;;; Loading "/Users/ph7/.cache/common-lisp/ecl-11.1.1-cfcfee52-macosx-x86/Users/ph7/Projects/ECL-Bugs/foo.fas"
>>> [create pointer] enter
<<< [create pointer] got 0x7fe519c00d40
>> [ecl_make_foreign_data] enter got 0x19c00d40
>> [ecl_make_foreign_data] output 0x104054d80 pointing to 0x19c00d40
>> [ecl_foreign_data_pointer_safe] object 0x104054d80 pointing to 0x19c00d40
>> [ecl_foreign_data_pointer_safe] return 0x19c00d40
>>> [use pointer] got 0x19c00d40
Condition of type: SEGMENTATION-VIOLATION
Detected access to an invalid or protected memory address.
;; ===================================================
Notice how only the last 32 bits of the original pointer
(0x7fe5 19c0 0d40) are preserved when we try to use the
pointer (0x19c0 0d40).
Since I thought ecl_make_foreign_data / ecl_foreign_data_pointer_safe,
might have something to do with it, I recompiled ECL with the following
modifications:
diff --git a/src/c/ffi.d b/src/c/ffi.d
index a43dcf4..b6a334d 100644
--- a/src/c/ffi.d
+++ b/src/c/ffi.d
@@ -192,10 +192,13 @@ static ffi_type *ecl_type_to_libffi_type[] = {
cl_object
ecl_make_foreign_data(cl_object tag, cl_index size, void *data)
{
+ printf("\n>> [ecl_make_foreign_data] enter got %p", data);
cl_object output = ecl_alloc_object(t_foreign);
output->foreign.tag = tag == Cnil ? @':void' : tag;
output->foreign.size = size;
output->foreign.data = (char*)data;
+
+ printf("\n>> [ecl_make_foreign_data] output %p pointing to %p", output, output->foreign.data);
return output;
}
@@ -212,10 +215,12 @@ ecl_allocate_foreign_data(cl_object tag, cl_index size)
void *
ecl_foreign_data_pointer_safe(cl_object f)
{
+ printf("\n>> [ecl_foreign_data_pointer_safe] object %p pointing to %p", f, f->foreign.data);
if (ecl_unlikely(type_of(f) != t_foreign)) {
FEwrong_type_only_arg(@[si::foreign-data-pointer], f,
@[si::foreign-data]);
}
+ printf("\n>> [ecl_foreign_data_pointer_safe] return %p", f->foreign.data);
return f->foreign.data;
}
But based on the previous output it seems that when we get to
ecl_make_foreign_data the pointer is already truncated.
>>> [create pointer] enter
<<< [create pointer] got 0x7fe519c00d40
>> [ecl_make_foreign_data] enter got 0x19c00d40
I can reproduce the problem on OS X and FreeBSD 8.2 (both
64 bits). I also tried to compile the C file with "-arch x86_64" flag,
but the truncation still happens:
>>> [create pointer] enter
<<< [create pointer] got 0x7ff6a9e00120
>> [ecl_make_foreign_data] enter got 0xffffffffa9e00120
>> [ecl_make_foreign_data] output 0x107019d80 pointing to 0xffffffffa9e00120
>> [ecl_foreign_data_pointer_safe] object 0x107019d80 pointing to 0xffffffffa9e00120
>> [ecl_foreign_data_pointer_safe] return 0xffffffffa9e00120
>>> [use pointer] got 0xffffffffa9e00120
Any idea why?
Thanks in advance,
- Philippe
More information about the ecl-devel
mailing list