[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