/* Copyright (c) 1980 Regents of the University of California */
static char sccsid
[] = "@(#)pc3.c 1.1 %G%";
* Pc3 is a pass in the Berkeley Pascal compilation
* process that is performed just prior to linking Pascal
* object files. Its purpose is to enforce the rules of
* separate compilation for Berkeley Pascal. Pc3 is called
* with the same argument list of object files that is sent to
* the loader. These checks are performed by pc3 by examining
* the symbol tables of the object files:
* (1) All source and included files must be "up-to-date" with
* the object files of which they are components.
* (2) Each global Pascal symbol (label, constant, type,
* variable, procedure, or function name) must be uniquely
* declared, i.e. declared in only one included file or
* (3) Each external function (or procedure) may be resolved
* at most once in a source file which included the
* external declaration of the function.
* The symbol table of each object file is scanned and
* each global Pascal symbol is placed in a hashed symbol
* table. The Pascal compiler has been modified to emit all
* Pascal global symbols to the object file symbol table. The
* information stored in the symbol table for each such symbol
* - the name of the symbol;
* - for file symbols, their last modify time;
* - the file which logically contains the declaration of
* the symbol (not an include file);
* - the file which textually contains the declaration of
* the symbol (possibly an include file);
* - the line number at which the symbol is declared;
* - the file which contains the resolution of the symbol.
* - the line number at which the symbol is resolved;
* If a symbol has been previously entered into the symbol
* table, a check is made that the current declaration is of
* the same type and from the same include file as the previous
* one. Except for files and functions and procedures, it is
* an error for a symbol declaration to be encountered more
* than once, unless the re-declarations come from the same
* included file as the original.
* As an include file symbol is encountered in a source
* file, the symbol table entry of each symbol declared in that
* include file is modified to reflect its new logical
* inclusion in the source file. File symbols are also
* encountered as an included file ends, signaling the
* continuation of the enclosing file.
* Functions and procedures which have been declared
* external may be resolved by declarations from source files
* which included the external declaration of the function.
* Functions and procedures may be resolved at most once across
* a set of object files. The loader will complain if a
* function is not resolved at least once.
#include "/usr/src/new/pc0/p.a.out.h"
* check each of the argument .o files (or archives of .o files).
fprintf( stderr
, "[main] *argv = %s\n" , *argv
);
* check the namelist of a file, or all namelists of an archive.
char mag_armag
[ SARMAG
+ 1 ];
ofilep
-> file
= fopen( ofilep
-> name
, "r" );
if ( ofilep
-> file
== NULL
) {
error( WARNING
, "cannot open: %s" , ofilep
-> name
);
fstat( fileno( ofilep
-> file
) , &filestat
);
ofilep
-> modtime
= filestat
.st_mtime
;
red
= fread( (char *) &mag_un
, 1 , sizeof mag_un
, ofilep
-> file
);
if ( red
!= sizeof mag_un
) {
error( WARNING
, "cannot read header: %s" , ofilep
-> name
);
if ( mag_un
.mag_exec
.a_magic
== OARMAG
) {
error( WARNING
, "old archive: %s" , ofilep
-> name
);
if ( strncmp( mag_un
.mag_armag
, ARMAG
, SARMAG
) == 0 ) {
/* archive, iterate through elements */
fprintf( stderr
, "[checkfile] archive %s\n" , ofilep
-> name
);
ofilep
-> nextoffset
= SARMAG
;
while ( nextelement( ofilep
) ) {
} else if ( N_BADMAG( mag_un
.mag_exec
) ) {
error( WARNING
, "bad format: %s" , ofilep
-> name
);
fprintf( stderr
, "[checkfile] .o file %s\n" , ofilep
-> name
);
fseek( ofilep
-> file
, 0L , 0 );
ofilep
-> nextoffset
= filestat
.st_size
;
fclose( ofilep
-> file
);
* check the namelist of this file for conflicts with
* previously entered symbols.
register struct fileinfo
*ofilep
;
register struct nlist
*nlp
;
red
= fread( (char *) &aexec
, 1 , sizeof aexec
, ofilep
-> file
);
if ( red
!= sizeof aexec
) {
error( WARNING
, "error reading struct exec: %s"
if ( N_BADMAG( aexec
) ) {
symoff
= N_SYMOFF( aexec
) - sizeof aexec
;
fseek( ofilep
-> file
, symoff
, 1 );
numsyms
= aexec
.a_syms
/ sizeof ( struct nlist
);
error( WARNING
, "no name list: %s" , ofilep
-> name
);
nlp
= (struct nlist
*) calloc( numsyms
, sizeof ( struct nlist
) );
error( FATAL
, "no room for %d nlists" , numsyms
);
red
= fread( ( char * ) nlp
, numsyms
, sizeof ( struct nlist
)
if ( ftell( ofilep
-> file
) + sizeof ( off_t
)
>= ofilep
-> nextoffset
) {
error( WARNING
, "no string table (old format .o?)"
red
= fread( (char *) &strsize
, sizeof strsize
, 1
error( WARNING
, "no string table (old format .o?)"
stringp
= ( char * ) malloc( strsize
);
error( FATAL
, "no room for %d bytes of strings" , strsize
);
red
= fread( stringp
+ sizeof strsize
, strsize
- sizeof ( strsize
) , 1 , ofilep
-> file
);
error( WARNING
, "error reading string table: %s"
fprintf( stderr
, "[checknl] %s: %d symbols\n"
, ofilep
-> name
, numsyms
);
for ( sym
= 0 ; sym
< numsyms
; sym
++) {
if ( nlp
[ sym
].n_un
.n_strx
) {
nlp
[ sym
].n_un
.n_name
= stringp
+ nlp
[ sym
].n_un
.n_strx
;
nlp
[ sym
].n_un
.n_name
= "";
checksymbol( &nlp
[ sym
] , ofilep
);
* look it up in the hashed symbol table,
* entering it if necessary.
* this maintains a state of which .p and .i files
* it is currently in the midst from the nlist entries
* for source and included files.
* if we are inside a .p but not a .i, pfilep == ifilep.
checksymbol( nlp
, ofilep
)
static struct symbol
*pfilep
= NIL
;
static struct symbol
*ifilep
= NIL
;
register struct symbol
*symbolp
;
if ( pfilep
&& ifilep
) {
fprintf( stderr
, "[checksymbol] pfile %s ifile %s\n"
, pfilep
-> name
, ifilep
-> name
);
fprintf( stderr
, "[checksymbol] ->name %s ->n_type %x (%s)\n"
, nlp
-> n_un
.n_name
, nlp
-> n_type
, classify( nlp
-> n_type
) );
switch ( nlp
-> n_type
) {
symbolp
= entersymbol( nlp
-> n_un
.n_name
);
/* don't care about the others */
if ( symbolp
-> lookup
== NEW
) {
fprintf( stderr
, "[checksymbol] ->name %s is NEW\n"
symbolp
-> type
= nlp
-> n_type
;
switch ( symbolp
-> type
) {
symbolp
-> sym_un
.sym_str
.fromp
= pfilep
;
symbolp
-> sym_un
.sym_str
.fromi
= ifilep
;
symbolp
-> sym_un
.sym_str
.iline
= nlp
-> n_value
;
if ( symbolp
-> type
!= N_PEFUN
&& symbolp
-> type
!= N_PEPRC
) {
symbolp
-> sym_un
.sym_str
.rfilep
= ifilep
;
symbolp
-> sym_un
.sym_str
.rline
= nlp
-> n_value
;
symbolp
-> sym_un
.sym_str
.rfilep
= NIL
;
symbolp
-> sym_un
.sym_str
.rline
= 0;
* functions can only be declared external
if ( pfilep
== ifilep
) {
, "%s, line %d: %s %s must be declared in included file"
, pfilep
-> name
, nlp
-> n_value
, classify( symbolp
-> type
)
symbolp
-> sym_un
.modtime
= mtime( symbolp
-> name
);
if ( symbolp
-> sym_un
.modtime
> ofilep
-> modtime
) {
error( WARNING
, "%s is out of date with %s"
, ofilep
-> name
, symbolp
-> name
);
fprintf( stderr
, "[checksymbol] ->name %s is OLD\n"
switch ( symbolp
-> type
) {
* finding a file again means you are back
* in it after finishing an include file.
* include files can be seen more than once,
* but they still have to be timechecked.
* (this will complain twice for out of date
* include files which include other files.
if ( symbolp
-> sym_un
.modtime
> ofilep
-> modtime
) {
error( WARNING
, "%s is out of date with %s"
, ofilep
-> name
, symbolp
-> name
);
* we may see any number of external declarations,
* but they all have to come
* from the same include file.
if ( nlp
-> n_type
== N_PEFUN
|| nlp
-> n_type
== N_PEPRC
) {
* an external function can be resolved by
* the resolution of the function
* included the external declaration.
if ( ( symbolp
-> type
== N_PEFUN
&& nlp
-> n_type
!= N_PGFUN
)
|| ( symbolp
-> type
== N_PEPRC
&& nlp
-> n_type
!= N_PGPRC
)
|| symbolp
-> sym_un
.sym_str
.fromp
!= pfilep
) {
* an external function can only be resolved once.
if ( symbolp
-> sym_un
.sym_str
.rfilep
!= NIL
) {
symbolp
-> sym_un
.sym_str
.rfilep
= ifilep
;
symbolp
-> sym_un
.sym_str
.rline
= nlp
-> n_value
;
* functions may not be seen more than once.
* the loader will complain about
* `multiply defined', but we can, too.
* labels, constants, types, variables
* and external declarations
* may be seen as many times as they want,
* as long as they come from the same include file.
* make it look like they come from this .p file.
if ( nlp
-> n_type
!= symbolp
-> type
|| symbolp
-> sym_un
.sym_str
.fromi
!= ifilep
) {
symbolp
-> sym_un
.sym_str
.fromp
= pfilep
;
error( WARNING
, "%s, line %d: %s already defined (%s, line %d)."
, ifilep
-> name
, nlp
-> n_value
, nlp
-> n_un
.n_name
, symbolp
-> sym_un
.sym_str
.rfilep
-> name
, symbolp
-> sym_un
.sym_str
.rline
);
* quadratically hashed symbol table.
* things are never deleted from the hash symbol table.
* as more hash table is needed,
* a new one is alloc'ed and chained to the end.
* search is by rehashing within each table,
* traversing chains to next table if unsuccessful.
static struct symboltableinfo
*symboltable
= NIL
;
register struct symboltableinfo
*tablep
;
register struct symbol
**herep
;
register struct symbol
**limitp
;
enteredname
= enterstring( name
);
hashindex
= SHORT_ABS( ( long ) enteredname
) % SYMBOLPRIME
;
for ( tablep
= symboltable
; /*return*/ ; tablep
= tablep
-> chain
) {
fprintf( stderr
, "[entersymbol] calloc\n" );
tablep
= ( struct symboltableinfo
* )
calloc( sizeof ( struct symboltableinfo
) , 1 );
error( FATAL
, "ran out of memory (entersymbol)" );
if ( symboltable
== NIL
) {
herep
= &( tablep
-> entry
[ hashindex
] );
limitp
= &( tablep
-> entry
[ SYMBOLPRIME
] );
fprintf( stderr
, "[entersymbol] increment %d\n"
if ( tablep
-> used
> ( ( SYMBOLPRIME
/ 3 ) * 4 ) ) {
/* too full, break for next table */
( *herep
) -> name
= enteredname
;
( *herep
) -> lookup
= NEW
;
fprintf( stderr
, "[entersymbol] name %s NEW\n"
if ( ( *herep
) -> name
== enteredname
) {
( *herep
) -> lookup
= OLD
;
fprintf( stderr
, "[entersymbol] name %s OLD\n"
} while ( increment
< SYMBOLPRIME
);
* allocate a symbol from the dynamically allocated symbol table.
static struct symbol
*nextsymbol
= NIL
;
static long symbolsleft
= 0;
struct symbol
*newsymbol
;
if ( symbolsleft
<= 0 ) {
fprintf( stderr
, "[symbolalloc] malloc\n" );
nextsymbol
= ( struct symbol
* ) malloc( SYMBOLALLOC
);
error( FATAL
, "ran out of memory (symbolalloc)" );
symbolsleft
= SYMBOLALLOC
/ sizeof( struct symbol
);
* hash a string based on all of its characters.
for ( cp
= string
; *cp
; cp
++ ) {
value
= ( value
* 2 ) + *cp
;
* quadratically hashed string table.
* things are never deleted from the hash string table.
* as more hash table is needed,
* a new one is alloc'ed and chained to the end.
* search is by rehashing within each table,
* traversing chains to next table if unsuccessful.
static struct stringtableinfo
*stringtable
= NIL
;
register struct stringtableinfo
*tablep
;
hashindex
= SHORT_ABS( hashstring( string
) ) % STRINGPRIME
;
for ( tablep
= stringtable
; /*return*/ ; tablep
= tablep
-> chain
) {
fprintf( stderr
, "[enterstring] calloc\n" );
tablep
= ( struct stringtableinfo
* )
calloc( sizeof ( struct stringtableinfo
) , 1 );
error( FATAL
, "ran out of memory (enterstring)" );
if ( stringtable
== NIL
) {
herep
= &( tablep
-> entry
[ hashindex
] );
limitp
= &( tablep
-> entry
[ STRINGPRIME
] );
fprintf( stderr
, "[enterstring] increment %d\n"
if ( tablep
-> used
> ( ( STRINGPRIME
/ 3 ) * 4 ) ) {
/* too full, break for next table */
*herep
= charalloc( strlen( string
) );
strcpy( *herep
, string
);
fprintf( stderr
, "[enterstring] string %s copied\n"
/* quick, check the first chars and then the rest */
if ( **herep
== *string
&& strcmp( *herep
, string
) == 0 ) {
fprintf( stderr
, "[enterstring] string %s found\n"
} while ( increment
< STRINGPRIME
);
* copy a string to the dynamically allocated character table.
static char *nextchar
= NIL
;
static long charsleft
= 0;
register long lengthplus1
= length
+ 1;
if ( charsleft
< lengthplus1
) {
askfor
= lengthplus1
> CHARALLOC
? lengthplus1
: CHARALLOC
;
fprintf( stderr
, "[charalloc] malloc( %d )\n"
nextchar
= ( char * ) malloc( askfor
);
error( FATAL
, "no room for %d characters" , askfor
);
charsleft
-= lengthplus1
;
* read an archive header for the next element
* and find the offset of the one after this.
fseek( ofilep
-> file
, ofilep
-> nextoffset
, 0 );
red
= fread( (char *) &archdr
, 1 , sizeof archdr
, ofilep
-> file
);
if ( red
!= sizeof archdr
) {
/* null terminate the blank-padded name */
cp
= &archdr
.ar_name
[ ( sizeof archdr
.ar_name
) - 1 ];
/* set up the address of the beginning of next element */
arsize
= atol( archdr
.ar_size
);
/* archive elements are aligned on 0 mod 2 boundaries */
ofilep
-> nextoffset
= ftell( ofilep
-> file
) + arsize
;
* variable number of arguments to error, like printf.
error( fatal
, message
, arg1
, arg2
, arg3
, arg4
, arg5
, arg6
)
fprintf( stderr
, "%s: " , program
);
fprintf( stderr
, message
, arg1
, arg2
, arg3
, arg4
, arg5
, arg6
);
fprintf( stderr
, "\n" );
* find the last modify time of a file.
* on error, return the current time.
fprintf( stderr
, "[mtime] filename %s\n"
file
= open( filename
, 0 );
error( WARNING
, "%s: cannot open" , filename
);
return ( (time_t) time( 0 ) );
fstat( file
, &filestat
);
return filestat
.st_mtime
;
return "external function";
return "external procedure";