[Ecls-list] Macro+segfault insight appreciated
Matthew Mondor
mm_lists at pulsar-zone.net
Sat May 1 18:44:02 UTC 2010
Hello,
In attempt to familiarize myself more with macros and eliminate some
redundancy in a library I have, I tried defining in my syscalls library
a macro, DEFSYSCALL to handle many common cases (which if works could
also be expanded to handle other cases ultimately).
However, despite the MACROEXPAND-1 results being promising, a warning
occurs at compilation time for every function generated by DEFSYSCALL,
and subsequently using the offending variable(s) in a list passed to
UNIX-ERROR causes a segmentation fault.
Here is example test code:
;;; The original definition of CHDIR.
;;; This works fine. C-INLINE2 is a macro on top of FFI:C-INLINE which
;;; supports more types and converts from/to native ECL FFI types using
;;; functions previously defined in an EVAL-WHEN form. Using FFI:C-INLINE
;;; should be fine for this test as no non-native types are used for CHDIR.
(defun chdir (path)
"Invokes the chdir(2) syscall on PATH.
Returns NIL on success, or signals a condition of type UNIX-ERROR."
(ffi:clines "#include <errno.h>")
(let ((err (c-inline2 (path)
(:cstring)
:int
"
{
if (chdir(#0) == 0)
@(return) = 0;
else
@(return) = errno;
}" :one-liner nil)))
(if (= 0 err)
nil
(unix-error "chdir" err `(,path))))) ; This is fine
;;; The following is an attempt to produce an equivalent function to the
;;; one above, using a macro to avoid redundancy for a fair number of syscalls
;;; which have a very similar syntax.
(defmacro defsyscall ((fsymbol cfunction) (&rest cargtypes) documentation)
(let* ((nargs (length cargtypes))
(args (loop ; ARG0 ARG1 ARG2...
repeat nargs
collect (gensym "ARG")))
(cargs (let ((s (with-output-to-string (s) ; "#0 #1 #2" ...
(loop
for i from 0 below nargs
do (format s "#~D " i)))))
(subseq s 0 (1- (length s))))))
`(defun ,fsymbol ,args
,documentation
(ffi:clines "#include <errno.h>")
(let ((err (c-inline2 ,args
,cargtypes
:int
,(format nil "
{
if (~A(~A) == 0)
@(return) = 0;
else
@(return) = errno;
}"
cfunction
cargs)
:one-liner nil)))
(if (= 0 err)
nil
(unix-error ,cfunction err (list , at args))))))) ; Problem using ARGS
;;; This example uses the above macro.
;;; The non-error path of CHDIR then works fine, but unfortunately the
;;; error path causes a segmentation fault when attempting to display
;;; the value of ARG0 (path), which is supplied as a list to UNIX-ERROR.
;;; I have tried using EVAL-WHEN at strategic places in DEFSYSCALL or
;;; around this form, or using (SETF (SYMBOL-FUNCTION ... but without
;;; more success so far. When compiling the form below, a warning is issued:
;;;
;;; ! Variable G1 was undefined. Compiler assumes it is a global. <---
;;;
;;; I must be missing something.
(defsyscall (chdir "chdir") (:cstring)
"Invokes the chdir(2) syscall on PATH.
Returns NIL on success, or signals a condition of type UNIX-ERROR.")
;;; The MACROEXPAND-1 of the above is as follows:
(DEFUN CHDIR (#:ARG233)
"Invokes the chdir(2) syscall on PATH.
Returns NIL on success, or signals a condition of type UNIX-ERROR."
(FFI:CLINES "#include <errno.h>")
(LET ((ERR (C-INLINE2 (#:ARG233)
(:CSTRING)
:INT
"
{
if (chdir(#0) == 0)
@(return) = 0;
else
@(return) = errno;
}"
:ONE-LINER NIL)))
(IF (= 0 ERR)
NIL
(UNIX-ERROR "chdir" ERR (LIST #:ARG233))))) ; Problem using #:ARG233
;;; Here is a second variant using INTERN instead of GENSYM:
(defmacro defsyscall ((fsymbol cfunction) (&rest cargtypes) documentation)
(let* ((nargs (length cargtypes))
(args (loop
for i from 0 below nargs
collect (intern (format nil "ARG~D" i))))
(cargs (let ((s (with-output-to-string (s)
(loop
for i from 0 below nargs
do (format s "#~D " i)))))
(subseq s 0 (1- (length s))))))
`(defun ,fsymbol ,args
,documentation
(ffi:clines "#include <errno.h>")
(let ((err (c-inline2 ,args
,cargtypes
:int
,(format nil "
{
if (~A(~A) == 0)
@(return) = 0;
else
@(return) = errno;
}"
cfunction
cargs)
:one-liner nil)))
(if (= 0 err)
nil
(unix-error ,cfunction err (list , at args))))))) ; Problem using ARGS
(defsyscall2 (chdir "chdir") (:cstring)
"Invokes the chdir(2) syscall on PATH.
Returns NIL on success, or signals a condition of type UNIX-ERROR.")
;;; Which MACROEXPAND-1 to the following, which compiles fine separately
;;; but causes the same warning and segfault when DEFSYSCALL2 is used:
(DEFUN CHDIR (ARG0)
"Invokes the chdir(2) syscall on PATH.
Returns NIL on success, or signals a condition of type UNIX-ERROR."
(FFI:CLINES "#include <errno.h>")
(LET ((ERR (C-INLINE2 (ARG0)
(:CSTRING)
:INT
"
{
if (chdir(#0) == 0)
@(return) = 0;
else
@(return) = errno;
}"
:ONE-LINER NIL)))
(IF (= 0 ERR)
NIL
(UNIX-ERROR "chdir" ERR (LIST ARG0))))) ; Problem using ARG0
So after compiling the CHDIR function using DEFSYSCALL:
> (chdir "/tmp")
NIL
> (chdir "/tmpe")
Detected access to an invalid or protected memory address.
[Condition of type SI:SEGMENTATION-VIOLATION]
Restarts:
0: [ABORT] Return to sldb level 1.
1: [RETRY] Retry SLIME REPL evaluation request.
2: [ABORT] Return to SLIME's top level.
3: [CLOSE-CONNECTION] Close SLIME connection
4: [RESTART-TOPLEVEL] Go back to Top-Level REPL.
Backtrace:
0: DEBUG-IN-EMACS
1: INVOKE-SLIME-DEBUGGER
2: G919
3: CALL-WITH-DEBUGGER-HOOK
4: SWANK-DEBUGGER-HOOK
5: INVOKE-DEBUGGER
6: UNIVERSAL-ERROR-HANDLER
...
I am probably missing something important, possibly an EVAL-WHEN
despite my earlier failed tests using it... Any insight would be
appreciated. ECL CVS HEAD was used to test this.
Thanks,
--
Matt
More information about the ecl-devel
mailing list