\begindata{text,538119136} \textdsversion{12} \template{symbol} \define{global attr:[FontSize ConstantFontSize Point 12]} \define{italic menu:[Font~1,Italic~11] attr:[FontFace Italic Int Set]} \define{bold menu:[Font~1,Bold~10] attr:[FontFace Bold Int Set]} \define{chapter menu:[Title~3,Chapter~20] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set] attr:[FontSize PreviousFontSize Point 4]} \define{section menu:[Title~3,Section~21] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set] attr:[FontSize PreviousFontSize Point 2]} \define{subsection menu:[Title~3,Subsection~22] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set]} \define{paragraph menu:[Title~3,Paragraph~23] attr:[Justification LeftJustified Point 0] attr:[FontFace Italic Int Set]} \define{bigger menu:[Font~1,Bigger~20] attr:[FontSize PreviousFontSize Point 2]} \define{indent menu:[Region~4,Indent~20] attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768]} \define{typewriter menu:[Font~1,Typewriter~40] attr:[FontFace FixedFace Int Set] attr:[FontFamily AndyType Int 0]} \define{underline menu:[Font~1,Underline~41] attr:[Flags Underline Int Set]} \define{display menu:[Region~4,Display~14] attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768] attr:[Justification LeftJustified Point 0]} \define{example menu:[Region~4,Example~12] attr:[LeftMargin LeftMargin Inch 32768] attr:[Justification LeftJustified Point 0] attr:[FontFace FixedFace Int Set] attr:[FontFamily AndyType Int 0]} \define{itemize menu:[Region~4,Itemize~31] attr:[LeftMargin LeftMargin Inch 32768]} \define{description menu:[Region~4,Description~11] attr:[LeftMargin LeftMargin Inch 32768] attr:[Indent LeftMargin Inch -32768]} \define{enumerate menu:[Region~4,Enumerate~30] attr:[LeftMargin LeftMargin Inch 32768]} \define{programexample menu:[Region~4,ProgramExample~13] attr:[LeftMargin LeftMargin Inch 32768] attr:[Justification LeftJustified Point 0] attr:[FontFace FixedFace Int Set] attr:[FontFamily AndyType Int 0]} \define{quotation menu:[Region~4,Quotation~10] attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768] attr:[FontFace Italic Int Set]} \define{excerptedcaption attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768] attr:[FontFace Bold Int Set] attr:[FontFace Italic Int Set]} \define{subscript menu:[Font~1,Subscript~30] attr:[Script PreviousScriptMovement Point 2] attr:[FontSize PreviousFontSize Point -2]} \define{superscript menu:[Font~1,Superscript~31] attr:[Script PreviousScriptMovement Point -6] attr:[FontSize PreviousFontSize Point -2]} \define{smaller menu:[Font~1,Smaller~21] attr:[FontSize PreviousFontSize Point -2]} \define{heading menu:[Title~3,Heading~11] attr:[LeftMargin LeftMargin Inch -13107] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set]} \define{majorheading menu:[Title~3,MajorHeading~10] attr:[Justification Centered Point 0] attr:[FontSize PreviousFontSize Point 4]} \define{formatnote menu:[Region~4,FormatNote~60] attr:[Flags PassThru Int Set] attr:['color' 'Pink']} \define{literal menu:[Region~4,Literal~62] attr:[Flags PassThru Int Set]} \define{subheading menu:[Title~3,Subheading~12] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set]} \define{center menu:[Justify~2,Center~10] attr:[Justification Centered Point 0]} \define{flushleft menu:[Justify~2,FlushLeft~20] attr:[Justification LeftJustified Point 0]} \define{flushright menu:[Justify~2,FlushRight~21] attr:[Justification RightJustified Point 0]} \define{leftindent menu:[Region~4,LeftIndent~21] attr:[LeftMargin LeftMargin Inch 32768]} \define{black menu:[Color,Black] attr:['color' 'Black']} \define{red menu:[Color,Red] attr:['color' 'Red']} \define{green menu:[Color,Green] attr:['color' 'Green']} \define{blue menu:[Color,Blue] attr:['color' 'Blue']} \define{magenta menu:[Color,Magenta] attr:['color' 'Magenta']} \define{cyan menu:[Color,Cyan] attr:['color' 'Cyan']} \define{yellow menu:[Color,Yellow] attr:['color' 'Yellow']} \flushright{March, 1994} \majorheading{AUIS Porting Guidelines } Manufacturers continue to produce new hardware/operating system platforms on which people would like to utilize AUIS, the Andrew User Interface System. This document describes some of the tasks essential in doing a port. Throughout, a file name beginning with ./ refers to a file in the AUIS source distribution. Installed files are referred to as if they are installed in /usr/andrew, though a site may install the tree elsewhere. \ In general, the term <platform> below refers to a name chosen to refer to the platform. Often it is the name used by Transarc to identify the platform and therefore available as the `sys` command or the value of @sys as a path component. Usually the name is a compound listing first the hardwar and then the operating system; for example, the <platform>s for the IBM RT/PC are rt_aix221, rt_ aos4, and rt_mach. The platform name must be #defined as the value of the variable SYS_NAME in ./config/<platform>/system.h. ________________________________________________________ \bold{\bigger{General rules regarding porting AUIS to a new platform:}} \leftindent{Make as few changes as is possible; the fewer additional characters, lines and files, the better. Try to restrict your changes to: ./config/<platform>/system.\{h,mcr\} ./overhead/class/machdep/<platform>/... ./atk/console/stats/<platform>/... }\heading{ }________________________________________________________ \bold{\bigger{The config Directory}} The config directory contains the imake configurations files, including the imake "rules" files, the .mcr files, and the .h files. These are used to generate Makefiles from Imakefiles and to produce the andrewos.h include file which is included in all source code files. When generating a Makefile from an Imakefile, the file ./config/imake.tmpl is preprocessed. It defines various Makefile variables and has #includes for platform.tmpl (which #includes system.mcr) andrew.rls (which #includes site.rls) the-Imakefile-itself Platform.tmpl is responsible only for determining what platform this is. It then #includes the system.mcr file for the platform. \ System.mcr defines Makefile variables with '='. It #includes system.h, allsys.mcr, and site.mcr. A flag is set so the system.h does not #include header files for the operating system. System.h #defines various preprocessor variables and also #includes allsys.h, various operating system include files, and site.h. Site.mcr and site.h should be empty in a distribution. They can be utilized at a given site to override definitions given in the other files. The andrew.rls files (and site.rls) #define preprocessor macros which are invoked in the Imakefile. We do not expect you to add new rules; if you feel you must, please contact the Consortium staff. Each .c source file includes andrewos.h, which does nothing more than #include system.h, but this time the flag is not set and the various operating system header files are #included. The latter include approprately named versions of \ types.h, syslog.h, unistd.h, signal.h, param.h, fcntl.h, file.h dirXXX.h, stringX.h, timeX.h When adding support for a new platform, you must (1) create a new subdirectory of ./config whose name identifies the new system type, (2) populate that new directory with a system.h and system.mcr file, and (3) add the proper "vendor block" to the file ./config/platform.tmpl, so that the new files are used under the proper platform. \ == \bold{Adding New Config Files} Suppose you are adding support for a variant of UNIX, called SPINIX, that runs on the Intel 80386 chip. You typically create the new directory config/spinix_i386 and populate it with system files borrowed from an already-ported i386 system, like sco_i386. Then add a test in ./config/platform.tmpl to check for a cpp symbol unique to SPINIX, so that the correct files are used: \example{#if defined(i386) && defined(spinix) #include <i386_spinix/system.mcr> #define MacroIncludeFile i386_spinix/system.mcr #endif /* Spinix */ } \ With the above "bootstrap" code, it is assume that the two symbols "i386" and "spinix" are defined by the cpp. You can generally determine what symbols are exported by cpp by running the strings command on the cpp executable: \typewriter{ % strings /lib/cpp | more } .. or, if you use gcc, use the verbose flag (-v) to see what arguments gcc passes to cpp: \typewriter{ % touch foo.c; gcc -v -o foo foo.c }The symbols defined for the compilation are given after -D on the line showing the call to cpp. == \bold{What goes in a system.h file} The system.h file initializes the cpp state seen by each source module and each Imakefile. Each system.h file has this general form: #include <allsys.h> XXXX #ifndef In_Imake XYXY #endif /* In_Imake */ XXXX #include <site.h> The token XXXX denotes that arbitrary #defines are used here. XYXY denotes that a combination of #defines, additonal file-inclusion directives and C declarations may be used here. The contents of the whole file is available to source modules via inclusion of the header andrewos.h while only the XXXX sections are included in the final Makefiles. In the config header files you should *only*: \ 1) define or redefine cpp symbols, \ 2) #include additional header files constructs) (protected by #ifndef In_Imake if they include arbitrary C \ 3) Insert C declarations, if absolutely necessary (again, place them between #ifndef In_Imake and #endif /* In_Imake */) == \bold{What goes in a system.mcr file} Tthe system.mcr file is included in all Makefiles, after being processed by cpp. Macros defined in system.mcr are related to such issues as, "where is the X11 bin directory" and "what are the debug flags to be passed to the compiler." XBINDIR = /usr/local/bin CDEBUGFLAGS = -g In the config macro files, you should *only* set the values of various Make variables. It is OK to set these Make macros based on the state of the cpp, for instance: #ifdef POSIX_ENV STD_DEFINES = -D_POSIX_SOURCE -D_ALL_SOURCE #endif ________________________________________________________ \bold{\bigger{CLASS -- ./overhead/class/machdep}} The machine-dependent class directory implements the dynamic loading feature of the class system in whatever way is proper for a given platform. \ == \bold{An overview of the dynamic loading implemented by Class} Dynamic loading is a process whereby the code that implements a particular class is automatically loaded into a running process upon the first call to one if its classprocedures. For instance, if I have a multimedia filesystem browser it is likely that during the course of browsing I will run across multimedia compound documents containing references to objects of arbitrary classes. If those classes have already been used within the process, then a description of them exists and instances can be created. If the class description is not in the current process, the class has not been previously used and the system needs to find the proper "dynamic object" to load, to obtain a description of the class so that a copy can be instantiated. \ The Andrew Class system is fairly complicated but can be described by the following points: A class-header file (.ch) contains an ASCII description of a class in the Class preprocessor language. The Class Preprocessor translates a class-header file into a class-import file (.ih) and a class-export file (.eh) that are included by various source modules. The class-export file should be the last file included by the defining module or modules. The class-import file is included by any module explicitly using the class. The class-export file contains support routines for the class implementation. Each method or classprocedure for a class is initially defined as being one of an array of "jumper" functions that are defined in the Class library. The entry points for these "jumper" functions are defined by an assembly module located in the machine-dependent class directory, called entry.spp. It is the purpose of the jumper routines to remember which original classprocedure was called, load the approprate module if it hasn't already been loaded, and call the original classprocedure. These jumper must be implemented in assembler because (1) it's important that the stack and registers be cleaned up such that it looks like the classprocedure was called directly and not through the jumpers and (2) declaring the jumpers as C functions limits the number and types of arguments that a classprocedure can have. Once a class description has been loaded into the running process, the remaining classprocedures and methods are re-defined to be their original routines, and the jumper assembly entry points are no longer used. The rest of the dynamic loading system in Class is dependent on what services are offered by the OS. Newer OS's provide shared library support. A shared library is similar to a dynamically loaded class in that the code in the shared library is not attached to the process until it is actually called for. Even when it is attached, the text portion of the library code is shared among all the processes using the library while each process has it's own copy of the data segment. On systems that support shared libraries (IBM RS/6000, HPUX, SunOS) the jumper routines are still needed but the task of actually loading the code into the running process is greatly simplified as it is provided by a system call. In more archaic OS's (BSD), the task of actually loading an external object module is complicated and calls for additonal steps. \ == \bold{Class with Shared Libraries} On platforms that support shared libraries, system calls are provided that make it possible to attach a module to the running process and to lookup the address of symbols within that attach module. On SYSV platforms the set of system calls are dlopen, dlsym, dlerror, and dlclose. The modules that can be manipulated by these shared library services must be compiled such that they contain no symbols that absolutely reference other elements of the object file. This type of module contains what is called position-independent code (pic). On the RS/6000, all code generated by the stock compiler is pic by default. Symbol references are all made through a table of contents (TOC) -- one TOC is maintained for each object file. On other platforms you must specify to the compiler that pic is desired by setting the PICFLAG macro in that platform's system.mcr file. It would be nice if we could just compile each and every module as positionindependent but there are compiler & runtime limits under certain platforms that prevent that simple-minded solution. One notable example of this problem is under SunOS4.1.x. where we must compile both pic and non-pic of each module to avoid a stub table overflow. For this platform, each directory that contains source also contains a subdirectory called "shared" where the pic versions of the modules reside. Under this scheme, when a dynamic object is created by makedo, it's underlying object modules are retrieved from the shared directory while that non-pic versions are used for archive libraries. This results in a larger build area but ensures that relevent compiler/linker limits are not reached. == \bold{Class without Shared Libraries} On platforms that do not support shared libraries, the symbols found in a module to be dynamically loaded must all be manually relocated in the running processes address space. This is a very complicated matter as it gets into the details of the object file format for a particular operating system. Luckily there are some defacto-standards for object file formats: a.out, COFF, XCOFF, and ELF. Of these, only a.out is any trouble because the others come from OS's that support shared libraries. In the machine-dependent class directory, there are several implementations of symbol relocation for a.out format (see ./overhead/class/machdep/aos_rt/class.c). There are certain routines in the C library that rely on local data. Malloc is one of these routines. The code for these routines must be gathered up and linked into the main process (called runapp) and also these routines must be excluded from the dynamic objects. The rest of the routines in the C library must be wrapped up and put into an auxillary library known as libcx.a. It is libcx.a that is linked against the object files that implement a class in the machine-dependent file makedo.csh. Makedo's purpose is to run the link-editor (aka: ld) on the the object files that implement a class, explicitly specifying the resultant file's entrypoint, and resolving any external references to symbols in either libcx.a or the list of globals that will be found in runapp. This list of global symbols and libcx.a are both created in the machine-dependent Imakefile via various nm-related hacks that are very ugly. When a class is loaded (attached to a running process), it's entrypoint is looked up and called. This entrypoint is a routine called <classname>__GetClassInfo. This routine is defined in <classname>.eh, the class's export-header file, and it's purpose is to return a pointer to a structure that contains information about the classes superclass, an array of pointers to the class's methods and classprocedures, and the classes instance data. The structure returned from GetClassInfo is, in effect, an instance of the class. Next, the classes Initializers are called. The first time a class is loaded it's InitializeClass routine is called to setup data common to all instances of the class. Then the InitializeObject method is called, to initialize the object's instance data. ________________________________________________________ \bold{\bigger{Console Statistics -./atk/console/stats/<platform>/getstats.c}} Getstats runs as a separate process extracting information from the kernel and passing it to the 'console' application. (A separate process is needed because it must be setuid to root on some platforms.) Information reported includes disk occupancy (% full) and a host of statistics from /dev/kmem including CPU load, I/O load, and VM occupancy. Getstats extracts info at fixed intervals; for each cycle and each datum, it prints a line to stdout. The parent program (Console, or any other program which wishes to get at this information) is responsible for setting up a pipe, binding the child's (this program) stdout to one end of a pipe, and parsing the strings which are passed back. The format of each line is an ID (int), a colon, a value (int), and optionally another colon followed by a string. The ID is coded from the included file "getstats.h". \ The main program in ./atk/console/stats/common/gsmain.c calls on three functions which must be defined in ./atk/console/stats/<platform>/getstats.c: \leftindent{ InitGVMStats() Do any required initialization. Typically, this means reading the namelist associated with the kernel to find the locations of the information. GetGVMStats(int UsersID) Send lines to stdout for each relevant statistic in ../../lib/getstats.h. The UserId canbe used to distinguish processes belonging to this user from other users. GetDiskStats(int Init) If Init is TRUE, send lines to stdout giving the mount point of each disk. Otherwise send lines giving the percent of each idsk which is occupied. The ID in these messages will be DISK1, DISK2, ... .} ________________________________________________________ \bold{\bigger{Portability Macros}} We have introduced a few portability macros in order to keep the sources as clean as possible and to ease the burden of porting the sources to a new platform. \ NEWPGRP \leftindent{When a program forks off another process, the child often is disconnected from its parent's process group so that signals generated by the child aren't received by the parent. On BSD-derived platforms, a process can make itself the process group leader by issuing the following system call: setpgrp(0, getpid()); while on sysv derived platforms (including all POSIX systems we have encountered), it is done this way: setpgrp(); Now there is a macro called NEWPGRP() that is to be used for this purpose. The macro is defined in the system.h platform-dependent config file. \ } DIRENT_TYPE DIRENT_NAMELEN(dirEnt) \leftindent{To open a directory and read it's entries: \example{#include <andrewos.h> char *dirName; DIR *dir; DIRENT_TYPE *dirEnt; if(dir = opendir(dirName)) \{ while (dirEnt = readdir(Dir)) \ printf("%s\\t%d\\n", dirEnt->d_name, DIRENT_NAMELEN(dirEnt)); \} } DIRENT_TYPE: entry a portability type that represents a directory DIRENT_NAMELEN: a portability macro that takes a (DIRENT_TYPE *) and returns the length of the directory entry name string }SIGSET_TYPE FILE_HAS_IO MANDIR TMACMANFILE TMACPREFIX MIN MAX ________________________________________________________ \bold{\bigger{System definition variables}} In addition to the variables described in ./README (and ./README.ez), the following may be #defined in ./config/<platform>/system.h file \leftindent{ #define LIBDL_ENV 1 This variable should be defined iff dynamic loading is to be implemented with the SunOS or SYSV dlopen interface. (See ./overhead/class/machdep/sun_sparc_\{41,51\}) #define HAVE_SHARED_LIBRARIES 1 HAVE_SHARED_LIBRARIES apparently influences how the X library is linked into dynamic objects or programs which load dynamic objects. Probably by linking with a full path when it is not defined, and with -L$(XLIBDIR) -lX11 when it is defined. #define NO_SHARED_DIR 1 When not defined every C file will be compiled twice, and one compiled with the contents of $(PICFLAG) will be placed in a directory called shared. When defined every file will be compiled once with the contents of $(PICFLAG) and no shared directory will be used. } Settable in ./config/<platform>/system.mcr: \leftindent{PICFLAG = \ $(PICFLAG) should have cc switches appropriate to compiling a .c file for inclusion in a shared library or dlopenable object. \ }\enddata{text,538119136}