Subject: Re: bug#1079: GNUstep port segfaults making autoloads on x86_64

From: Yavor Doganov <[email-masked]>
Date: Tue, 07 Oct 2008 22:02:51 +0300

Glenn Morris wrote:
> 
> > Which version of GNUstep Base is this?
> 
> gnustep-startup-0.20.0.tar

That should contain the latest stable releases, so it must be OK (I've
never installed this way, though.)

> base/NSInvocation/general.m:
> FAIL: Can send/return large structs

This is a bit worrisome.  Just to rule out the possibility, can you
compile gnustep-base against libffi in case it was built with
libffcall (and vice-versa -- rebuild it with libffcall if it was with
libffi).  There were numerous problems with both, and libffi became
preferred by upstream a few months ago.

> > Does the naive patch below eliminate this problem?
>
> I'm afraid not.

Oh, of course not; this file is not being read at all at this point.
Nevertheless, still fixes a harmless runtime warning.

> Here is a full backtrace this time:

Thanks.  I can't see how this can be a bug in Emacs.  The arguments of
bootstrap-emacs are evaluated as property lists and since "(setq foo
1)" (or whatever is passed to --batch in such form) is not a validly
formatted array, an NSException is raised here:

#5  0x00002b51204f9fbb in GSPropertyListFromStringsFormat (string=0x1b392730)
    at NSPropertyList.m:1505
        dict = (class NSMutableDictionary *) 0x0
        _pld = {
  ptr = 0x1b281c70 "(setq foo 1)", 
  end = 12, 
  pos = 0, 
  lin = 0, 
  err = 0x2b512095f000, 
  opt = NSPropertyListImmutable, 
  key = 0 '\000', 
  old = 1 '\001'
}

However, this exception should be caught and handled by the parent
exception handler at NSUserDefaults.m:1814 -- note the comment there:

          else
            {                            // Real parameter                      
              /* Parsing the argument as a property list is very                
                 delicate.  We *MUST NOT* crash here just because a             
                 strange parameter (such as `(load "test.scm")`) is             
                 passed, otherwise the whole library is useless in a            
                 foreign environment. */
              NSObject *plist_val;

              NS_DURING
	      [....]

This is precisely the case here, and it's handled properly on i386.

It is getting too complicated for me, but can you compile this test
program (it should never crash):

test.m:
=======
#import	<Foundation/Foundation.h>

int
main (int argc, char** argv, char **env)
{
  NSAutoreleasePool *pool = [NSAutoreleasePool new];
  NSProcessInfo	    *proc = [NSProcessInfo processInfo];
  NSBundle        *bundle = [NSBundle mainBundle];
  NSArray	    *args;
  unsigned		i;
  int              retval = 0;

  [NSProcessInfo initializeWithArguments:argv
		 count:argc
		 environment:env];

  args = [proc arguments];
  GSPrintf (stderr, @"Useless info about bundle: %@\n", bundle);

  if ([args count] <= 1)
    {
      GSPrintf (stderr, @"Please specify some arguments.\n");
      RELEASE (pool);
      exit (EXIT_FAILURE);
    }
  else if ([[proc environment] valueForKey:@"NOHANDLE"] != nil)
    GSPrintf (stderr, @"NOHANDLE defined; should exit with success.\n");
  else
    {
      for (i = 1; i < [args count]; i++)
	{
	  NSString	*opt = [args objectAtIndex: i];

 	  NS_DURING
	    {
	      NSString	*tempString;
	      id	result;

	      tempString = [NSString stringWithString: opt];
	      if (tempString == nil)
		GSPrintf (stderr, @"This should not happen.\n");
	      /* Exception handler taking care here.  */
	      else if (![tempString propertyList]); 
	      else if ((result = [tempString propertyList]) == nil)
	       	GSPrintf (stderr, @"'%@' - a nil property list.\n", opt);
	      else if ([result isKindOfClass: [NSDictionary class]] == YES)
		GSPrintf (stderr, @"'%@' - a dictionary.\n", opt);
	      else if ([result isKindOfClass: [NSArray class]] == YES)
		GSPrintf (stderr, @"'%@' - an array.\n", opt);
	      else if ([result isKindOfClass: [NSString class]] == YES)
		GSPrintf (stderr, @"'%@' - a string.\n", opt);
	      else
		GSPrintf (stderr, @"'%@' - unknown class %@.\n",
		  opt, [[result class] description]);
	    }
	  NS_HANDLER
	    {
	      GSPrintf (stderr, @"'%@' failed with:\n%@.\n", opt,
		[localException reason]);
	      GSPrintf (stderr, @"Failure.\n");
	      retval = 1;
	    }
	  NS_ENDHANDLER
	}
    }
  RELEASE (pool);
  return retval;
}

You can use this simple makefile to build it:

GNUmakefile:
============
include $(GNUSTEP_MAKEFILES)/common.make
TOOL_NAME = test
test_OBJC_FILES = test.m
include $(GNUSTEP_MAKEFILES)/tool.make

Just type `make'.

Then test it as follows (the executable should be /obj/test):

(gdb) r '{a=b; c=d}' '(foo, bar)' --batch '(setq foo 1)'
...
(gdb) set environment NOHANDLE=yes
(gdb) r	  (with the same arguments)

Thanks.