[squeak-dev] Re: Re: building Cog / FFiPrims plgin on windows

Alain_Rastoul alr.dev at free.fr
Sun Oct 31 22:13:05 UTC 2010


Skipped content of type multipart/alternative-------------- next part --------------
/* Host windows plugin header file
 * tim at sumeru.stanford.edu
 * All sizes are in pixels; convert from OS abstract units as needed
 * windowHandles etc are expected to be SmallInteger valid values */

/* closeWindow: arg is int windowIndex. Fail (return 0) if anything goes wrong
 * - typically the windowIndex invalid or similar */
extern int closeWindow(int index);

/* createWindow: takes int width, height and origin x/y plus a char* list of
 * as yet undefined attributes. Returns an int window index or 0 for failure
 * Failure may occur because of an inability to add the window, too many
 * windows already extant (platform dependant), the specified size being
 * unreasonable etc. */
extern int createWindowWidthheightoriginXyattrlength(int w, int h, int x, int y,
char * list, int attributeListLength);

/* ioShowDisplayOnWindow: similar to ioShowDisplay but adds the int windowIndex
 * Return true if ok, false if not, but not currently checked */
extern int ioShowDisplayOnWindow( unsigned  *dispBitsIndex, int width, int
height, int depth, int affectedL, int affectedR, int affectedT, int affectedB,
int windowIndex);


/* ioSizeOfWindow: arg is int windowIndex. Return the size of the specified
 * window in (width<<16 || height) format like ioScreenSize.
 * Return -1 for failure - typically invalid windowIndex
 * -1 is chosen since it would correspond to a window size of +/-32k at +/-32k
 * which it is hoped is unlikely for some time to come */
extern int ioSizeOfWindow(int windowIndex);

/* ioSizeOfNativeWindow: arg is void* windowHandle, defined as unsigned long
 * for convenience. Return the size of the specified native window in
 * (width<<16 || height) format like ioScreenSize.
 * Return -1 for failure - typically invalid windowHandle
 * -1 is chosen since it would correspond to a window size of +/-32k at +/-32k
 * which it is hoped is unlikely for some time to come */
extern int ioSizeOfNativeWindow(unsigned long windowHandle);

/* as per ioSizeOfNativeWindow, but answers the size of the drawing
 * surface (the inside) of the window.
 */
extern int ioSizeOfNativeDisplay(unsigned long windowHandle);

/* ioSizeOfWindowSetxy: args are int windowIndex, int w & h for the
 * width / height to make the window. Return the actual size the OS
 * produced in (width<<16 || height) format or -1 for failure as above. */
extern int ioSizeOfWindowSetxy(int windowIndex, int w, int h);

/* ioPositionOfWindow: arg is int windowIndex. Return the pos of the specified
 * window in (left<<16 || top) format like ioScreenSize.
 * Return -1 (as above) for failure - typically invalid windowIndex */
extern int ioPositionOfWindow(int windowIndex);

/* ioPositionOfNativeWindow: arg is void* windowHandle, defined as unsigned long
 * for convenience. Return the pos of the specified native window in
 * (left<<16 || top) format like ioScreenSize.
 * Return -1 (as above) for failure - typically invalid windowHandle */
extern int ioPositionOfNativeWindow(unsigned long windowHandle);

/* as per ioPositionOfNativeWindow, but answers the position of the drawing
 * surface (the inside) of the window.
 */
extern int ioPositionOfNativeDisplay(unsigned long windowHandle);

/* ioPositionOfWindowSetxy: args are int windowIndex, int x & y for the
 * origin x/y for the window. Return the actual origin the OS
 * produced in (left<<16 || top) format or -1 for failure, as above */
extern int ioPositionOfWindowSetxy(int windowIndex, int x, int y);

/* ioSetTitleOfWindow: args are int windowIndex, char* newTitle and
 * int size of new title. Fail with -1 if windowIndex is invalid, string is too
long for platform etc. Leave previous title in place on failure */
int ioSetTitleOfWindow(int windowIndex, char * newTitle, int sizeOfTitle);

/* ioCloseAllWindows: intended for VM shutdown.
 * Close all the windows that appear to be open.
 * No useful return value since we're getting out of Dodge anyway.
 */
extern int ioCloseAllWindows(void);
-------------- next part --------------
/****************************************************************************
*   PROJECT: Common include
*   FILE:    sq.h
*   CONTENT: 
*
*   AUTHOR:  
*   ADDRESS: 
*   EMAIL:   
*   RCSID:   $Id: sq.h 1283 2005-12-31 00:51:12Z rowledge $
*
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "sqConfig.h"
#include "sqMemoryAccess.h"
#include "sqVirtualMachine.h"

#define true	1
#define false	0
#define null	0  /* using "null" because nil is predefined in Think C */

/* Pluggable primitives macros. */

/* Note: All pluggable primitives are defined as
	EXPORT(int) somePrimitive(void)
   If the platform requires special declaration modifiers, the EXPORT
   macro can be redefined.
*/
#define EXPORT(returnType) returnType

/* Image save/restore macros. */

/* Note: The image file save and restore code uses these macros; they
   can be redefined in sqPlatformSpecific.h if desired. These default
   versions are defined in terms of the ANSI Standard C libraries.
*/
#define sqImageFile					   FILE *
#define sqImageFileClose(f)                  		   fclose(f)
#define sqImageFileOpen(fileName, mode)      		   fopen(fileName, mode)
#define sqImageFilePosition(f)               		   ftell(f)
#define sqImageFileRead(ptr, sz, count, f)   		   fread(ptr, sz, count, f)
#define sqImageFileSeek(f, pos)              		   fseek(f, pos, SEEK_SET)
#define sqImageFileWrite(ptr, sz, count, f)  		   fwrite(ptr, sz, count, f)
#define sqImageFileStartLocation(fileRef, fileName, size)  0

/* Platform-dependent macros for handling object memory. */

/* Note: The grow/shrink macros assume that the object memory can be extended
   continuously at its prior end. The garbage collector cannot deal with
   'holes' in the object memory so the support code needs to reserve the
   virtual maximum of pages that can be allocated beforehand. The amount of
   'extra' memory should describe the amount of memory that can be allocated
   from the OS (including swap space if the flag is set to true) and must not
   exceed the prior reserved memory.
   In other words: don't you dare to report more free space then you can
   actually allocate.
   The default implementation assumes a fixed size memory allocated at startup.
*/
#define sqAllocateMemory(minHeapSize, desiredHeapSize)  malloc(desiredHeapSize)
#define sqGrowMemoryBy(oldLimit, delta)			oldLimit
#define sqShrinkMemoryBy(oldLimit, delta)		oldLimit
#define sqMemoryExtraBytesLeft(includingSwap)		0

/* Platform-dependent memory size adjustment macro. */

/* Note: This macro can be redefined to allows platforms with a
   fixed application memory partition (notably, the Macintosh)
   to reserve extra C heap memory for special applications that need
   it (e.g., for a 3D graphics library). Since most platforms can
   extend their application memory partition at run time if needed,
   this macro is defined as a noop here and redefined if necessary
   in sqPlatformSpecific.h.
*/

#define reserveExtraCHeapBytes(origHeapSize, bytesToReserve) origHeapSize

/* Platform-dependent millisecond clock macros. */

/* Note: The Squeak VM uses two different clock functions for timing, and
   the Cog VMs provide a third.

   The primary one, ioMSecs(), is used to implement Delay and Time
   millisecondClockValue. The resolution of this clock
   determines the resolution of these basic timing functions. For
   doing real-time control of music and MIDI, a clock with resolution
   down to one millisecond is preferred, but a coarser clock (say,
   1/60th second) can be used in a pinch.

   The function ioMicroMSecs() is used only to collect timing statistics
   for the garbage collector and other VM facilities. (The function
   name is meant to suggest that the function is based on a clock
   with microsecond accuracy, even though the times it returns are
   in units of milliseconds.) This clock must have enough precision to
   provide accurate timings, and normally isn't called frequently
   enough to slow down the VM. Thus, it can use a more expensive clock
   than ioMSecs(). This function is listed in the sqVirtualMachine plugin
   support mechanism and thus needs to be a real function, even if a macro is
   use to point to it.

   There was a third form that used to be used for quickly timing primitives in
   order to try to keep millisecond delays up to date. That is no longer used.

   By default, the basic ioMSec() clock function is defined
   here as a macro based on the standard C library function clock().
   Any of this can be overridden in sqPlatformSpecific.h.

   The wall clock is answered by ioSeconds, which answers the number of seconds
   since the start of the 20th century (12pm Dec 31, 1900).

   The Cog VMs depend on a heartbeat to cause the VM to check for interrupts at
   regular intervals (of the order of ever milisecond).  The heartbeat on these
   VMs is responsible for updating a 64-bit microsecond clock with the number
   of microseconds since the start of the 20th century (12pm Dec 31, 1900)
   available via ioUTCMicroseconds() and ioLocalMicroseconds().  For cases
   where exact time is required we provide ioUTCMicrosecondsNow that updates
   the clock to return the time right now, rather than of the last heartbeat.
*/

sqInt ioMSecs(void);
/* deprecated out ofexistence sqInt ioLowResMSecs(void); */
sqInt ioMicroMSecs(void);

#define ioMSecs()	((1000 * clock()) / CLOCKS_PER_SEC)

/* this macro cannot be used now that ioMicroMSecs is involved in the
   sqVirtualMachine structures - we must have a function 
#define ioMicroMSecs()	((1000 * clock()) / CLOCKS_PER_SEC)
*/

#if STACKVM
usqLong ioUTCMicrosecondsNow();
usqLong ioUTCMicroseconds();
usqLong ioLocalMicroseconds();
void	ioUpdateVMTimezone();
void	ioSynchronousCheckForEvents();
void	checkHighPriorityTickees(usqLong);
# if ITIMER_HEARTBEAT		/* Hack; allow heartbeat to avoid */
extern int numAsyncTickees; /* prodHighPriorityThread unless necessary */
# endif						/* see platforms/unix/vm/sqUnixHeartbeat.c */
void	ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt*,void**,sqInt*,void**,sqInt*);
#endif

/* this function should return the value of the high performance
   counter if there is such a thing on this platform (otherwise return 0) */
sqLong ioHighResClock(void);

/* New filename converting function; used by the interpreterProxy function 
  ioFilenamefromStringofLengthresolveAliases. Most platforms can ignore the
  resolveAlias boolean - it seems to only be of use by OSX but is crucial there.
*/
sqInt sqGetFilenameFromString(char * aCharBuffer, char * aFilenameString, sqInt filenameLength, sqInt aBoolean);

/* Macro to provide default null behaviour for ftruncate - a non-ansi call
   used in FilePlugin.
   Override in sqPlatformSpecific.h for each platform that implements a
   file truncate, or consider replacing the
   ../Cross/plugins/FilePlugin/sqFilePluginBasicPrims.c
   file with a platform specific version as Win32 and RISC OS do. 
*/
#define sqFTruncate(filenum, fileoffset) true

/* Macros to support Mac browser plugin without ugly code in Interpreter. */

#define insufficientMemorySpecifiedError()	error("Insufficient memory for this image")
#define insufficientMemoryAvailableError()	error("Failed to allocate memory for the heap")
#define unableToReadImageError()		error("Read failed or premature end of image file")
#define browserPluginReturnIfNeeded()
#define browserPluginInitialiseIfNeeded()

/* Platform-specific header file may redefine earlier definitions and macros. */

#include "sqPlatformSpecific.h"

/* Interpreter entry points. */

/* Disable Intel compiler inlining of error which is used for breakpoints */
#pragma auto_inline off
void error(char *s);
#pragma auto_inline on
sqInt checkedByteAt(sqInt byteAddress);
sqInt checkedByteAtput(sqInt byteAddress, sqInt byte);
sqInt checkedLongAt(sqInt byteAddress);
sqInt checkedLongAtput(sqInt byteAddress, sqInt a32BitInteger);
sqInt fullDisplayUpdate(void);
sqInt interpret(void);
sqInt primitiveFail(void);
sqInt signalSemaphoreWithIndex(sqInt semaIndex);
sqInt success(sqInt);

/* Display, mouse, keyboard, time. */

sqInt ioBeep(void);
sqInt ioExit(void);
sqInt ioExitWithErrorCode(int);
sqInt ioForceDisplayUpdate(void);
sqInt ioFormPrint(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth,
		  double hScale, double vScale, sqInt landscapeFlag);
sqInt ioSetFullScreen(sqInt fullScreen);
sqInt ioRelinquishProcessorForMicroseconds(sqInt microSeconds);
sqInt ioScreenSize(void);
sqInt ioScreenDepth(void);
sqInt ioSeconds(void);
sqInt ioSetCursor(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY);
sqInt ioSetCursorWithMask(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY);
sqInt ioShowDisplay(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth,
		    sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB);
sqInt ioHasDisplayDepth(sqInt depth);
sqInt ioSetDisplayMode(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag);

#if STACKVM
/* thread subsystem support for e.g. sqExternalSemaphores.c */
void ioInitThreads();

/* Event polling via periodic heartbeat thread. */
void  ioInitHeartbeat(void);
int   ioHeartbeatMilliseconds(void);
void  ioSetHeartbeatMilliseconds(int);
unsigned long ioHeartbeatFrequency(int);

/* Management of the external semaphore table (max size set at startup) */
#if !defined(INITIAL_EXT_SEM_TABLE_SIZE)
# define INITIAL_EXT_SEM_TABLE_SIZE 256
#endif
int   ioGetMaxExtSemTableSize(void);
void  ioSetMaxExtSemTableSize(int);

/* these are used both in the STACKVM & the COGMTVM */
# if !defined(ioCurrentOSThread)
sqOSThread ioCurrentOSThread(void);
# endif
# if !defined(ioOSThreadsEqual)
int  ioOSThreadsEqual(sqOSThread,sqOSThread);
# endif
# if !COGMTVM
extern sqOSThread ioVMThread;
# define getVMThread() ioVMThread
# endif
#endif /* STACKVM */
#if COGMTVM
extern sqOSThread getVMThread();
/* Please read the comment for CogThreadManager in the VMMaker package for
 * documentation of this API.  N.B. code is included from sqPlatformSpecific.h
 * before the code here.  e.g.
 * # include <pthread.h>
 * # define sqOSThread pthread_t
 * # define sqOSSemaphore pthread_cond_t
 * # define ioOSThreadsEqual(a,b) pthread_equal(a,b)
 */
# if !defined(ioGetThreadLocalThreadIndex)
long ioGetThreadLocalThreadIndex(void);
# endif
# if !defined(ioSetThreadLocalThreadIndex)
void ioSetThreadLocalThreadIndex(long);
# endif

# if !defined(ioNewOSThread)
int  ioNewOSThread(void (*func)(void *), void *);
# endif
# if !defined(ioExitOSThread)
void ioExitOSThread(sqOSThread thread);
# endif
# if !defined(ioReleaseOSThreadState)
void ioReleaseOSThreadState(sqOSThread thread);
# endif
# if !defined(ioOSThreadIsAlive)
int  ioOSThreadIsAlive(sqOSThread);
# endif
int  ioNewOSSemaphore(sqOSSemaphore *);
void ioSignalOSSemaphore(sqOSSemaphore *);
void ioWaitOnOSSemaphore(sqOSSemaphore *);
int  ioNumProcessors(void);
# if !defined(ioTransferTimeslice)
void ioTransferTimeslice(void);
# endif
#endif /* COGMTVM */

/* Profiling. */
void  ioProfileStatus(sqInt *running, void **exestartpc, void **exelimitpc,
					  void **vmhst, long *nvmhbin, void **eahst, long *neahbin);
void  ioControlProfile(int on, void **vhp, long *nvb, void **ehp, long *neb);
long  ioControlNewProfile(int on, unsigned long buffer_size);
void  ioNewProfileStatus(sqInt *running, long *buffersize);
long  ioNewProfileSamplesInto(void *sampleBuffer);
void  ioClearProfile(void);

/* Power management. */

sqInt ioDisablePowerManager(sqInt disableIfNonZero);

/* User input recording I:
   In general, either set of input function can be supported,
   depending on the platform. This (first) set is state based
   and should be supported even on platforms that make use
   of the newer event driven API to support older images 
   without event support.
*/

sqInt ioGetButtonState(void);
sqInt ioGetKeystroke(void);
sqInt ioMousePoint(void);
sqInt ioPeekKeystroke(void);
/* Note: In an event driven architecture, ioProcessEvents is obsolete.
   It can be implemented as a no-op since the image will check for
   events in regular intervals. */
sqInt ioProcessEvents(void);


/* User input recording II:
   The following functions and definition can be used on
   platform supporting events directly.
*/

/* Event types. */
#define EventTypeNone		0
#define EventTypeMouse		1
#define EventTypeKeyboard	2
#define EventTypeDragDropFiles	3
#define EventTypeMenu		4
#define EventTypeWindow		5
#define	EventTypeComplex	6

/* Keypress state for keyboard events. */
#define EventKeyChar	0
#define EventKeyDown	1
#define EventKeyUp	2

/* Button definitions. */
#define RedButtonBit	4
#define YellowButtonBit	2
#define BlueButtonBit	1

/* Modifier definitions. */
#define ShiftKeyBit	1
#define CtrlKeyBit	2
#define OptionKeyBit	4
#define CommandKeyBit	8

/* generic input event */
typedef struct sqInputEvent
{
  int type;			/* type of event; either one of EventTypeXXX */
  unsigned int timeStamp;	/* time stamp */
  /* the interpretation of the following fields depend on the type of the event */
  int unused1;
  int unused2;
  int unused3;
  int unused4;
  int unused5;
  int windowIndex;		/* SmallInteger used in image to identify a host window structure */
} sqInputEvent;

/* mouse input event */
typedef struct sqMouseEvent
{
  int type;			/* EventTypeMouse */
  unsigned int timeStamp;	/* time stamp */
  int x;			/* mouse position x */
  int y;			/* mouse position y */
  int buttons;			/* combination of xxxButtonBit */
  int modifiers;		/* combination of xxxKeyBit */
  int nrClicks;			/* number of clicks in button downs - was reserved1 */
  int windowIndex;		/* host window structure */
} sqMouseEvent;

/* keyboard input event */
typedef struct sqKeyboardEvent
{
  int type;			/* EventTypeKeyboard */
  unsigned int timeStamp;	/* time stamp */
  int charCode;			/* character code in Mac Roman encoding */
  int pressCode;		/* press code; any of EventKeyXXX */
  int modifiers;		/* combination of xxxKeyBit */
  int utf32Code;		/* UTF-32 unicode value */
  int reserved1;		/* reserved for future use */
  int windowIndex;		/* host window structure */
} sqKeyboardEvent;

/* drop files event */
typedef struct sqDragDropFilesEvent
{
  int type;			/* EventTypeDropFiles */
  unsigned int timeStamp;	/* time stamp */
  int dragType;			/* one of DragXXX (see below) */
  int x;			/* mouse position x */
  int y;			/* mouse position y */
  int modifiers;		/* combination of xxxKeyBit */
  int numFiles;			/* number of files in transaction */
  int windowIndex;		/* host window structure */
} sqDragDropFilesEvent;

#define DragEnter	1 /* drag operation from OS entered Squeak window	 */
#define DragMove	2 /* drag operation from OS moved within Squeak window */
#define DragLeave	3 /* drag operation from OS left Squeak window	 */
#define DragDrop	4 /* drag operation dropped contents onto Squeak.      */
#define DragRequest	5 /* data request from other app. */

/* menu event */
typedef struct sqMenuEvent
{
  int type;			/* type of event; EventTypeMenu */
  unsigned int timeStamp;	/* time stamp */
  /* the interpretation of the following fields depend on the type  of the event */
  int menu;			/* platform-dependent to indicate which menu was picked */
  int menuItem;			/* given a menu having 1 to N items this maps to the menu item number */
  int reserved1;		/* reserved for future use */
  int reserved2;		/* reserved for future use */
  int reserved3;		/* reserved for future use */
  int windowIndex;		/* host window structure */
} sqMenuEvent;

/* window action event */
typedef struct sqWindowEvent
{
  int type;			/* type of event;  EventTypeWindow */
  unsigned int timeStamp;	/* time stamp */
  /* the interpretation of the following fields depend on the type  of the event */
  int action;		        /* one of WindowEventXXX (see below) */
  int value1;			/* used for rectangle edges */
  int value2;			/* used for rectangle edges */
  int value3;			/* used for rectangle edges */
  int value4;			/* used for rectangle edges */
  int windowIndex;		/* host window structure */
} sqWindowEvent;

#define WindowEventMetricChange	1 /* size or position of window changed - value1-4 are left/top/right/bottom values */
#define WindowEventClose	2 /* window close icon pressed */
#define WindowEventIconise	3 /* window iconised or hidden etc */
#define WindowEventActivated	4 /* window made active - some platforms only - do not rely upon this */
#define WindowEventPaint	5 /* window area (in value1-4) needs updating. Some platforms do not need to send this, do not rely on it in image */
#define WindowEventStinks	6 /* this window stinks (just to see if people read this stuff) */

typedef struct sqComplexEvent
	{
		int type;			/* type of event;  EventTypeComplex */
		unsigned int timeStamp;	/* time stamp */
		/* the interpretation of the following fields depend on the type  of the event */
		int action;		        /* one of ComplexEventXXX (see below) */
		usqInt objectPointer;	/* used to point to object */
		int unused1;			/*  */
		int unused2;			/*  */
		int unused3;			/*  */
		int windowIndex;	/* host window structure */
	} sqComplexEvent;

#define ComplexEventTypeTouchsDown	1 /*  */
#define ComplexEventTypeTouchsUp	2 /*  */
#define ComplexEventTypeTouchsMoved	3 /*  */
#define ComplexEventTypeTouchsStationary 4 /*  */
#define ComplexEventTypeTouchsCancelled	5 /*  */
#define ComplexEventTypeAccelerationData	6 /*  */
#define ComplexEventTypeLocationData	7 /*  */
#define ComplexEventTypeApplicationData	8 /*  */


/* Set an asynchronous input semaphore index for events. */
sqInt ioSetInputSemaphore(sqInt semaIndex);
/* Retrieve the next input event from the OS. */
sqInt ioGetNextEvent(sqInputEvent *evt);

/* Image file and VM path names. */
extern char imageName[];
char *getImageName(void);
sqInt imageNameGetLength(sqInt sqImageNameIndex, sqInt length);
sqInt imageNamePutLength(sqInt sqImageNameIndex, sqInt length);
sqInt imageNameSize(void);
sqInt vmPathSize(void);
sqInt vmPathGetLength(sqInt sqVMPathIndex, sqInt length);

/* Image security traps. */
sqInt ioCanRenameImage(void);
sqInt ioCanWriteImage(void);
sqInt ioDisableImageWrite(void);

/* Save/restore. */
/* Read the image from the given file starting at the given image offset */
sqInt readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset);
/* NOTE: The following is obsolete - it is only provided for compatibility */
#define readImageFromFileHeapSize(f, s) readImageFromFileHeapSizeStartingAt(f,s,0)

/* Clipboard (cut/copy/paste). */
sqInt clipboardSize(void);
sqInt clipboardReadIntoAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex);
sqInt clipboardWriteFromAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex);


/* Interpreter entry points needed by compiled primitives. */
void *arrayValueOf(sqInt arrayOop);
sqInt checkedIntegerValueOf(sqInt intOop);
void *fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer);
double fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer);
sqInt fetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer);
double floatValueOf(sqInt floatOop);
sqInt pop(sqInt nItems);
sqInt pushInteger(sqInt integerValue);
sqInt sizeOfSTArrayFromCPrimitive(void *cPtr);
sqInt storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objectPointer, sqInt integerValue);

/* System attributes. */
sqInt attributeSize(sqInt indexNumber);
sqInt getAttributeIntoLength(sqInt indexNumber, sqInt byteArrayIndex, sqInt length);

/*** Pluggable primitive support. ***/

/* NOTE: The following functions are those implemented by sqNamedPrims.c */
void *ioLoadExternalFunctionOfLengthFromModuleOfLength(sqInt functionNameIndex, sqInt functionNameLength,
						       sqInt moduleNameIndex, sqInt moduleNameLength);
sqInt  ioUnloadModuleOfLength(sqInt moduleNameIndex, sqInt moduleNameLength);
void  *ioLoadFunctionFrom(char *functionName, char *pluginName);
sqInt  ioShutdownAllModules(void);
sqInt  ioUnloadModule(char *moduleName);
sqInt  ioUnloadModuleOfLength(sqInt moduleNameIndex, sqInt moduleNameLength);
char  *ioListBuiltinModule(sqInt moduleIndex);
char  *ioListLoadedModule(sqInt moduleIndex);
/* The next two are FFI entries! (implemented in sqNamedPrims.c as well) */
void  *ioLoadModuleOfLength(sqInt moduleNameIndex, sqInt moduleNameLength);
void  *ioLoadSymbolOfLengthFromModule(sqInt functionNameIndex, sqInt functionNameLength, void *moduleHandle);

/* The next three functions must be implemented by sqXYZExternalPrims.c */
/* ioLoadModule:
	Load a module from disk.
	WARNING: this always loads a *new* module. Don't even attempt to find
	a loaded one.
	WARNING: never primitiveFail() within, just return 0
*/
void *ioLoadModule(char *pluginName);

/* ioFindExternalFunctionIn:
	Find the function with the given name in the moduleHandle.
	WARNING: never primitiveFail() within, just return 0.
*/
void *ioFindExternalFunctionIn(char *lookupName, void *moduleHandle);

/* ioFreeModule:
	Free the module with the associated handle.
	WARNING: never primitiveFail() within, just return 0.
*/
sqInt ioFreeModule(void *moduleHandle);

/* The Squeak version from which this interpreter was generated. */
extern const char *interpreterVersion;
-------------- next part --------------
/*
 * "quick" hack backtrace specific to x86 and win32 compiled with mingw or MSVC.
 * Extracts symbols and addresses for the main .exe from a .map file.
 * Extracts symbols and addresses for loaded dlls from the dlls themselves.
 *
 * Exports two functions, backtrace which answers an array of return pcs on the
 * call stack of the caller, and symbolic_backtrace which answers an array of
 * function name, module name, offset tuples for the supplied pcs.  These are
 * modelled after the BSD backtrace & backtrace_symbols library functions.
 *
 * Why are we not using StackWalk64 you ask?  StackWalk64 does not work with
 * mingw binaries.  See e.g. the v8 (a.k.a. chromium) source code.
 *
 * Eliot Miranda, 7 january 2010.
 */

#include <windows.h>
#include <stdio.h>
#include <string.h>

//#undef NDEBUG  if you want asserts enabled in the production build
//#define DBGPRINT if you want debug printing turned on

#include "sqAssert.h"
#include "sqWin32Backtrace.h"
#include "sqWin32.h"
#include "interp.h"
#if COGVM
# include "sq.h"
# include "cogmethod.h"
# include "cogit.h"
#endif

typedef struct frame {
	struct frame *savedfp;
	void *retpc;
} Frame;

/* This may be in winnt.h. We know it is not in MinGW 0.3 but is in MinGW v3.17.
 * In between we cannot say.
 */
#if defined(__MINGW32_MAJOR_VERSION) && __MINGW32_MAJOR_VERSION < 2
/* see http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
 * & e.g. http://www.nirsoft.net/kernel_struct/vista/NT_TIB.html
 */
typedef struct _NT_TIB
{
     void *ExceptionList;
     Frame *StackBase;
     Frame *StackLimit;
} ThreadInformationBlock, NT_TIB;
#endif

#define ulong unsigned long

int
backtrace(void *retpcs[], int nrpcs)
{
	void **__fp;
#if defined(_MSC_VER)
	__asm {
		mov EAX, EBP
		mov [__fp], EAX
	}
#elif defined(__MINGW32__)
	asm volatile ("movl %%ebp, %0" : "=r"(__fp) : );
#else
# error "don't know how to derive ebp"
#endif
	return backtrace_from_fp(*__fp, retpcs, nrpcs);
}

int
backtrace_from_fp(void *startfp, void *retpcs[], int nrpcs)
{
	Frame *fp; 
	NT_TIB *tib;
	int i = 0;

#if defined(_MSC_VER)
	__asm {
		mov EAX, FS:[18h]
		mov [tib], EAX
	}
#elif defined(__MINGW32__)
	asm volatile ("movl %%fs:0x18, %0" : "=r" (tib) : );
#else
# error "don't know how to derive tib"
#endif

	fp = startfp;

	while (i < nrpcs) {
		Frame *savedfp;

		if (!fp->retpc)
			break;
		retpcs[i++] = fp->retpc;
		savedfp = fp->savedfp;

#define validfp(fp,sp) (((int)(fp) & 3) == 0 && (char *)(fp) > (char *)(sp))

		if (savedfp >= (Frame *)tib->StackBase
		 || !validfp(savedfp,startfp)
		 || savedfp <= fp)
			break;

		fp = savedfp;
	}
	return i;
}

/* N.B.  This is from psapi.h but we do not link against psapi.dll, so define
 * this ourselves.
 */
typedef struct _MODULEINFO {
	LPVOID lpBaseOfDll;
	DWORD  SizeOfImage;
	LPVOID EntryPoint;
} MODULEINFO, *LPMODULEINFO;
typedef struct _dll_exports {
	HMODULE module;
	char name[MAX_PATH];
	MODULEINFO info;
	void (*find_symbol)(struct _dll_exports *, void *, symbolic_pc *);
	int    n;
	char   initialized;
	ulong *functions;
	union { char **funcNames; ulong *funcNameOffsets; sqInt *methods; } u;
	int   *sorted_ordinals;
} dll_exports;

static dll_exports *dll_exports_for_pc(void *retpc);

/*
 * Answer pairs of function name, offset in symbolic_pc for the n retpcs.
 */
int
symbolic_backtrace(int n, void **retpcs, symbolic_pc *spc)
{
	int i;

	for (i = 0; i < n; i++) {
		dll_exports *exports = dll_exports_for_pc(retpcs[i]);

		if (exports)
			exports->find_symbol(exports, retpcs[i], spc + i);
		else
			spc[i].fnameOrSelector = spc[i].mname = 0, spc[i].offset = 0;
	}
	return n;
}

BOOL (WINAPI *EnumProcessModules)(HANDLE,HMODULE*,DWORD,LPDWORD) = NULL;
BOOL (WINAPI *GetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD)=NULL;

static ulong moduleCount = 0;
static dll_exports *all_exports = 0;

static int
expcmp(const void *a, const void *b)
{ return ((dll_exports *)a)->info.lpBaseOfDll - ((dll_exports *)b)->info.lpBaseOfDll; }

static void find_in_dll(dll_exports *exports, void *pc, symbolic_pc *spc);
static void find_in_exe(dll_exports *exports, void *pc, symbolic_pc *spc);
static void find_in_cog(dll_exports *exports, void *pc, symbolic_pc *spc);

static void
get_modules(void)
{
	ulong moduleCount2, i;
	HANDLE me = GetCurrentProcess();
	HANDLE hPsApi = LoadLibrary("psapi.dll");
	HMODULE *modules;

	EnumProcessModules = (void*)GetProcAddress(hPsApi, "EnumProcessModules");
	GetModuleInformation=(void*)GetProcAddress(hPsApi, "GetModuleInformation");

	if (!EnumProcessModules(me, (HMODULE *)&modules, sizeof(modules), &moduleCount)) {
		printLastError("EnumProcessModules 1");
		return;
	}
	modules = malloc(moduleCount);
#if COGVM
# define EXTRAMODULES 1
#else
# define EXTRAMODULES 0
#endif
	all_exports = calloc(moduleCount / sizeof(HMODULE) + EXTRAMODULES,
						 sizeof(dll_exports));
	if (!modules || !all_exports) {
		printLastError("get_modules out of memory");
		if (modules)
			free(modules);
		return;
	}

	if (!EnumProcessModules(me, modules, moduleCount, &moduleCount2)) {
		printLastError("EnumProcessModules 2");
		free(modules);
		return;
	}
	moduleCount /= sizeof(HMODULE);

	for (i = 0; i < moduleCount; i++) {
		all_exports[i].module = modules[i];
		if (!GetModuleFileName(modules[i], all_exports[i].name, MAX_PATH))
			printLastError("GetModuleFileName");
		if (!GetModuleInformation(me, modules[i], &all_exports[i].info, sizeof(MODULEINFO)))
			printLastError("GetModuleInformation");
		all_exports[i].find_symbol = find_in_dll;
	}
	free(modules);
	assert(GetModuleHandle(0) == all_exports[0].module);
	all_exports[0].find_symbol = find_in_exe;
#if COGVM
	strcpy(all_exports[moduleCount].name,"CogCode");
	all_exports[moduleCount].module = (void *)cogCodeBase();
	all_exports[moduleCount].info.lpBaseOfDll = (void *)cogCodeBase();
	all_exports[moduleCount].info.SizeOfImage = startOfMemory() - cogCodeBase();
	all_exports[moduleCount].find_symbol = find_in_cog;
	++moduleCount;
#endif
	qsort(all_exports, moduleCount, sizeof(dll_exports), expcmp);
}

static dll_exports *
dll_exports_for_pc(void *retpc)
{
	int i;

	if (!all_exports)
		get_modules();

	if (!all_exports)
		return 0;

	for (i = 0; i < moduleCount; i++)
		if (all_exports[i].info.lpBaseOfDll <= retpc
		 && (void *)((ulong)all_exports[i].info.lpBaseOfDll
						  + all_exports[i].info.SizeOfImage) >= retpc)
			return all_exports + i;

	return 0;
}

static void compute_dll_symbols(dll_exports *exports);
static void
find_in_dll(dll_exports *exports, void *pcval, symbolic_pc *spc)
{
	int i;
	ulong pc = (ulong)pcval - (ulong)exports->info.lpBaseOfDll;

	spc->mname = strrchr(exports->name,'\\')
					? strrchr(exports->name,'\\') + 1
					: (char *)&exports->name;

	if (!exports->initialized)
		compute_dll_symbols(exports);

	for (i = 0; i < exports->n; i++) {
		ulong addr = exports->functions[exports->sorted_ordinals[i]];
		if (pc >= addr
		 && (  i + 1 >= exports->n
			|| pc < exports->functions[exports->sorted_ordinals[i + 1]])) {
			spc->fnameOrSelector = (char *)
					(exports->u.funcNameOffsets[exports->sorted_ordinals[i]]
					+ (ulong)exports->module);
			spc->offset = pc - addr;
			return;
		}
	}
	spc->fnameOrSelector = 0;
	spc->offset = pc;
}
static void compute_exe_symbols(dll_exports *exports);

static void
find_in_exe(dll_exports *exports, void *pc, symbolic_pc *spc)
{
	int i;

	spc->mname = strrchr(exports->name,'\\')
					? strrchr(exports->name,'\\') + 1
					: (char *)&exports->name;

	if (!exports->initialized)
		compute_exe_symbols(exports);

	for (i = 0; i < exports->n; i++) {
		ulong addr = exports->functions[i];
		if ((ulong)pc >= addr
		 && (  i + 1 >= exports->n
			|| (ulong)pc < exports->functions[i + 1])) {
			spc->fnameOrSelector = exports->u.funcNames[i];
			spc->offset = (ulong)pc - addr;
			return;
		}
	}
	spc->fnameOrSelector = 0;
	spc->offset = (ulong)pc - (ulong)exports->module;
}

static void
compute_exe_symbols(dll_exports *exports)
{
#if defined(_MSV_VER)
# error parse of MSVC .map file as yet unimplemented
/* Create the file using "cl .... /link /map"
 * Parse it by looking for lines beginning with " 0001:" where 0001 is the
 * segment number of the .text segment.  Representative lines look like

 0000:00000000       __except_list              00000000     <absolute>
 0001:00000000       _printLastError            00401000 f   main.obj
 0001:0000005c       _crashreport               0040105c f   main.obj
 0001:000000ed       _main                      004010ed f   main.obj

 * Be sure to look for global and static symbols.  The character "f" tells you
 * that this is a line for a function.
 */
#else  /* assume a BSD-style nm output as in
		* nm --numeric-sort --defined-only -f bsd $(VMEXE) >$(VMMAP)
		* where typical lines look like
00400000 A __image_base__
00401000 t .text
00401000 t __gnu_exception_handler at 4
00401150 t ___mingw_CRTStartup
00401280 T _mainCRTStartup
004012a0 T _WinMainCRTStartup
004012c0 T _atexit
004012d0 T __onexit
004012e0 t .text
004012e0 T _btext
004012f0 t .text
004012f0 t _genoperandoperand
		* The characters t & T indicate this is a text symbol.  Note duplicates.
		*/

/* Read the entire file into memory.  We can use the string as the string table.
 * Once we've counted the lines we can parse and read the start addresses.
 */
	char filename[MAX_PATH];
	char *contents;
	FILE *f;
	int pos, len, nlines, n;

	strcpy(filename, exports->name);
	strcpy(strrchr(filename,'.')+1,"map");

	if (!(f = fopen(filename,"r"))) {
		printLastError("fopen");
		return;
	}
	fseek(f,0,SEEK_END);
	len = ftell(f);
	fseek(f,0,SEEK_SET);
	if (!(contents = malloc(len))) {
		printLastError("malloc");
		fclose(f);
		return;
	}
	if (fread(contents, sizeof(char), len, f) != len) {
		printLastError("fread");
		fclose(f);
		return;
	}
	fclose(f);

	pos = nlines = 0;
	while (pos < len) {
		if (contents[pos] == '\n') {
			nlines++;
			contents[pos] = 0;
		}
		pos++;
	}

	if (!(exports->functions	= calloc(nlines,sizeof(ulong)))
	 || !(exports->u.funcNames	= calloc(nlines,sizeof(char *)))) {
		printLastError("malloc");
		fclose(f);
		return;
	}
	pos = n = 0;
	while (pos < len) {
		ulong addr;
		char  type, *symname;

		asserta(sscanf(contents + pos, "%lx %c", &addr, &type) == 2);
		symname = strrchr(contents + pos, ' ') + 1;
		if ((type == 't' || type == 'T')
		 && strcmp(symname,".text")) {
			exports->functions[n] = addr;
			exports->u.funcNames[n] = symname;
			++n;
		}
		pos += strlen(contents + pos) + 1;
	}
	exports->n = n;
#endif
#if DBGPRINT
	for (n = 0; n < exports->n; n++)
		printf("exe [%p] %s\n",
				exports->functions[exports->sorted_ordinals[n]],
				exports->u.funcNames[exports->sorted_ordinals[n]]);
#endif
	exports->initialized = 1;
}

/* See e.g. PEDump
	http://msdn.microsoft.com/en-us/library/ms809762.aspx
	http://msdn.microsoft.com/en-us/library/bb985994.aspx
 */

static ulong *funcs_for_ordcmp;
static int
ordcmp(const void *a, const void *b)
{ return funcs_for_ordcmp[*(int *)a] - funcs_for_ordcmp[*(int *)b]; }

static void
compute_dll_symbols(dll_exports *exports)
{
	char *dllbase = (char *)exports->module;
    PIMAGE_EXPORT_DIRECTORY pExportDir;
    int i, j, n;
    PWORD ordinals;
    ulong *functions;
    ulong exportsStartRVA, exportsEndRVA;

    PIMAGE_NT_HEADERS dllhdr;

    dllhdr = (PIMAGE_NT_HEADERS)(dllbase
							  + ((PIMAGE_DOS_HEADER)exports->module)->e_lfanew);

	if (IsBadReadPtr(dllhdr, sizeof(dllhdr->Signature))
	 || dllhdr->Signature != IMAGE_NT_SIGNATURE) {
		fprintf(stderr,"Not a Portable Executable (PE) EXE\n");
		return;
	}

    exportsStartRVA = dllhdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    exportsEndRVA = exportsStartRVA + dllhdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;

    pExportDir = (PIMAGE_EXPORT_DIRECTORY) (dllbase + exportsStartRVA);

#if 0
	time_t timeStamp = pExportDir->TimeDateStamp;
    printf("  TimeDateStamp:   %08X -> %s",
    			pExportDir->TimeDateStamp, ctime(&timeStamp) );
    printf("  Version:         %u.%02u\n", pExportDir->MajorVersion,
            pExportDir->MinorVersion);
    printf("  Ordinal base:    %08X\n", pExportDir->Base);
    printf("  # of functions:  %08X\n", pExportDir->NumberOfFunctions);
    printf("  # of Names:      %08X\n", pExportDir->NumberOfNames);
#endif

    ordinals =	(PWORD)	(dllbase + (DWORD)pExportDir->AddressOfNameOrdinals);
    functions =	(PDWORD)(dllbase + (DWORD)pExportDir->AddressOfFunctions);

    if (!(exports->sorted_ordinals = calloc(pExportDir->NumberOfNames, sizeof(int)))) {
		printLastError("compute_dll_symbols calloc");
		return;
	}
	exports->functions = functions;
	exports->u.funcNameOffsets = (ulong*)(dllbase + (DWORD)pExportDir->AddressOfNames);

    for (i = n = 0; i < pExportDir->NumberOfFunctions; i++)
        if (functions[i]) // Skip over gaps in exported function ordinals
			// Only record if the function has a name.
			for (j=0; j < pExportDir->NumberOfNames; j++)
				if (ordinals[j] == i) {
					exports->sorted_ordinals[n++] = i;
					break;
				}

	exports->n = n;
	exports->initialized = 1;
	funcs_for_ordcmp = exports->functions;
	qsort(exports->sorted_ordinals, n, sizeof(int), ordcmp);

#if DBGPRINT // this to dump the symbols once sorted for checking
	for (i = 0; i < n; i++)
		printf("%3d %lx %s\n",
				exports->sorted_ordinals[i],
				exports->functions[exports->sorted_ordinals[i]]
				+ (ulong)exports->module,
				exports->u.funcNameOffsets[exports->sorted_ordinals[i]]
				+ (ulong)exports->module);
#endif
}

#if COGVM
static void
find_in_cog(dll_exports *exports, void *pc, symbolic_pc *spc)
{
	CogMethod *cogMethod;

	spc->mname = (char *)&exports->name;

	if ((spc->fnameOrSelector = codeEntryNameFor(pc)))
		spc->offset = (ulong)pc - (ulong)codeEntryFor(pc);
	else if ((cogMethod = methodFor(pc))) {
		spc->fnameOrSelector = cogMethod->selector == nilObject()
								? "Cog method with nil selector"
								: (char *)(cogMethod->selector);
		spc->offset = (ulong)pc - (ulong)cogMethod;
	}
	else
		spc->offset = (ulong)pc - (ulong)exports->info.lpBaseOfDll;
}
#endif

void
printModuleInfo(FILE *f)
{
	int i;

	if (!all_exports)
		get_modules();

	fprintf(f, "\nModule information:\n");
	for (i = 0; i < moduleCount; i++) {
		fprintf(f,
				"\t%08x - %08x: %s\n", 
				all_exports[i].info.lpBaseOfDll,
				((ulong)all_exports[i].info.lpBaseOfDll) + all_exports[i].info.SizeOfImage,
				all_exports[i].name);
		fflush(f);
	}
}


More information about the Squeak-dev mailing list