* Copyright (c) 1980, 1982, 1983 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
"@(#) Copyright (c) 1980, 1982, 1983 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)pc3.c 5.2 (Berkeley) 4/16/91";
* 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 .o files must be up to date with respect to the
* (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;
* - a subtype descriptor;
* - the file which logically contains the declaration of
* the symbol or which caused the inclusion of 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.
* check each of the argument .o files (or archives of .o files).
for ( argv
++ ; *argv
!= 0 && **argv
== '-' ; argv
++ ) {
error( FATAL
, "pc3: bad flag -%c\n" , **argv
);
for ( /* void */ ; *argv
!= 0 ; argv
++ ) {
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( ERROR
, "cannot open: %s" , ofilep
-> name
);
fstat( fileno( ofilep
-> file
) , &filestat
);
red
= fread( (char *) &mag_un
, 1 , sizeof mag_un
, ofilep
-> file
);
if ( red
!= sizeof mag_un
) {
error( ERROR
, "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( ERROR
, "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 *) &oexec
, 1 , sizeof oexec
, ofilep
-> file
);
if ( red
!= sizeof oexec
) {
error( ERROR
, "error reading struct exec: %s"
if ( N_BADMAG( oexec
) ) {
symoff
= N_SYMOFF( oexec
) - sizeof oexec
;
fseek( ofilep
-> file
, symoff
, 1 );
numsyms
= oexec
.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_desc %x (%s)\n"
, nlp
-> n_un
.n_name
, nlp
-> n_desc
, classify( nlp
-> n_desc
) );
if ( nlp
-> n_type
!= N_PC
) {
/* don't care about the others */
symbolp
= entersymbol( nlp
-> n_un
.n_name
);
if ( symbolp
-> lookup
== NEW
) {
fprintf( stderr
, "[checksymbol] ->name %s is NEW\n"
symbolp
-> desc
= nlp
-> n_desc
;
symbolp
-> fromp
= pfilep
;
switch ( symbolp
-> desc
) {
error( FATAL
, "panic: [checksymbol] NEW" );
symbolp
-> sym_un
.sym_str
.rfilep
= ifilep
;
symbolp
-> sym_un
.sym_str
.rline
= nlp
-> n_value
;
symbolp
-> sym_un
.sym_str
.fromi
= ifilep
;
symbolp
-> sym_un
.sym_str
.iline
= 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
-> desc
)
symbolp
-> sym_un
.sym_str
.fromi
= ifilep
;
symbolp
-> sym_un
.sym_str
.iline
= nlp
-> n_value
;
if ( nlp
-> n_value
< N_FLAGCHECKSUM
) {
"%s is out of date and should be recompiled",
symbolp
-> sym_un
.checksum
= N_FLAGCHECKSUM
;
symbolp
-> sym_un
.checksum
= nlp
-> n_value
;
fprintf( stderr
, "[checksymbol] ->name %s is OLD\n"
switch ( symbolp
-> desc
) {
error( FATAL
, "panic [checksymbol] OLD" );
* finding a file again means you are back
* in it after finishing an include file.
if ( symbolp
-> desc
!= nlp
-> n_desc
) {
error( FATAL
, "panic [checksymbol] PSO" );
* include files can be seen more than once,
* but their checksums are checked if they are
* greater than N_FLAGCHECKSUM.
* PSOL's are seen with checksums as the
* include file is entered, and with
* N_FLAGCHECKSUM as we are back in an
* included file from a nested include.
if ( symbolp
-> desc
!= nlp
-> n_desc
) {
error( FATAL
, "panic [checksymbol] PSOL" );
if ((unsigned) symbolp
->sym_un
.checksum
> N_FLAGCHECKSUM
&& (unsigned) nlp
-> n_value
> N_FLAGCHECKSUM
&& symbolp
-> sym_un
.checksum
!= nlp
-> n_value
) {
"%s included in %s differs from %s included in %s",
symbolp
-> name
, pfilep
-> name
,
symbolp
-> name
, symbolp
-> fromp
-> name
);
* this might be the resolution of the external
* has to match func/proc of external
* and has to have included external
* and has to not have been previously resolved.
if ( ( ( symbolp
-> desc
== N_PEFUNC
&& nlp
-> n_desc
== N_PGFUNC
)
|| ( symbolp
-> desc
== N_PEPROC
&& nlp
-> n_desc
== N_PGPROC
) )
&& ( symbolp
-> fromp
== pfilep
)
&& ( symbolp
-> sym_un
.sym_str
.rfilep
== NIL
) ) {
fprintf( stderr
, "[checksymbol] resolving external\n" );
symbolp
-> sym_un
.sym_str
.rfilep
= ifilep
;
symbolp
-> sym_un
.sym_str
.rline
= nlp
-> n_value
;
* otherwise, it might be another external,
* the same type and from the same include file
if ( ( ( symbolp
-> desc
== N_PEFUNC
&& nlp
-> n_desc
== N_PEFUNC
)
|| ( symbolp
-> desc
== N_PEPROC
&& nlp
-> n_desc
== N_PEPROC
) )
&& ( symbolp
-> sym_un
.sym_str
.fromi
== ifilep
) ) {
* just another pretty external
* make it look like it comes from here.
fprintf( stderr
, "[checksymbol] just another pretty external\n" );
symbolp
-> fromp
= pfilep
;
* if it's not resolved, use the header file
* otherwise, it's just a regular error
if ( symbolp
-> sym_un
.sym_str
.rfilep
== NIL
) {
"%s, line %d: %s is already defined\n\t(%s, line %d)." ,
ifilep
-> name
, nlp
-> n_value
,
symbolp
-> sym_un
.sym_str
.fromi
-> name
,
symbolp
-> sym_un
.sym_str
.iline
);
* 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_desc
!= symbolp
-> desc
|| symbolp
-> sym_un
.sym_str
.fromi
!= ifilep
) {
symbolp
-> fromp
= pfilep
;
switch ( nlp
-> n_desc
) {
error( FATAL
, "pc3: unknown stab 0x%x"
/* these won't conflict with library */
, "%s, line %d: %s %s is already defined\n\t%s%s (%s, line %d)."
, classify( nlp
-> n_desc
)
, ( symbolp
-> desc
== nlp
-> n_desc
? "" : " as " )
, ( symbolp
-> desc
== nlp
-> n_desc
? "" : article( symbolp
-> desc
) )
, 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
;
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
) {
"[entersymbol] calloc'ing table for %d symbols\n" ,
for ( tablep
= &symboltable
; tablep
= tablep
->chain
) {
tablep
->chain
= ( struct symboltableinfo
* )
calloc( 1 , sizeof ( struct symboltableinfo
) );
if ( tablep
->chain
== NIL
) {
error( FATAL
, "ran out of memory (entersymbol)" );
herep
= &( tablep
-> entry
[ hashindex
] );
limitp
= &( tablep
-> entry
[ SYMBOLPRIME
] );
if ( tablep
-> used
> ( ( SYMBOLPRIME
/ 4 ) * 3 ) ) {
/* too full, break for next table */
( *herep
) -> name
= enteredname
;
( *herep
) -> lookup
= NEW
;
"[entersymbol] name %s NEW after %d\n" ,
enteredname
, increment
/ 2 );
if ( ( *herep
) -> name
== enteredname
) {
( *herep
) -> lookup
= OLD
;
fprintf( stderr
, "[entersymbol] name %s OLD at %d\n" ,
enteredname
, increment
/ 2 );
} while ( increment
< SYMBOLPRIME
);
fprintf( stderr
, "[entersymbol] next symboltable\n" );
* allocate a symbol from the dynamically allocated symbol table.
static struct symbol
*nextsymbol
= NIL
;
static long symbolsleft
= 0;
struct symbol
*newsymbol
;
if ( symbolsleft
<= 0 ) {
"[symbolalloc] malloc space for %d symbols\n" ,
SYMBOLALLOC
/ sizeof( struct symbol
) );
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
;
register struct stringtableinfo
*tablep
;
hashindex
= SHORT_ABS( hashstring( string
) ) % STRINGPRIME
;
for ( tablep
= &stringtable
; /*return*/ ; tablep
= tablep
-> chain
) {
"[enterstring] calloc space for %d strings\n" ,
for ( tablep
= &stringtable
; tablep
= tablep
->chain
) {
tablep
->chain
= ( struct stringtableinfo
* )
calloc( 1 , sizeof ( struct stringtableinfo
) );
if ( tablep
->chain
== NIL
) {
error( FATAL
, "ran out of memory (enterstring)" );
herep
= &( tablep
-> entry
[ hashindex
] );
limitp
= &( tablep
-> entry
[ STRINGPRIME
] );
if ( tablep
-> used
> ( ( STRINGPRIME
/ 4 ) * 3 ) ) {
/* too full, break for next table */
*herep
= charalloc( strlen( string
) );
strcpy( *herep
, string
);
"[enterstring] string %s copied after %d\n" ,
*herep
, increment
/ 2 );
/* quick, check the first chars and then the rest */
if ( **herep
== *string
&& strcmp( *herep
, string
) == 0 ) {
"[enterstring] string %s found after %d\n" ,
*herep
, increment
/ 2 );
} while ( increment
< STRINGPRIME
);
fprintf( stderr
, "[enterstring] next stringtable\n" );
* 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 space for %d chars\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( type
, message
, arg1
, arg2
, arg3
, arg4
, arg5
, arg6
, arg7
, arg8
)
errors
= type
> errors
? type
: errors
;
if ( wflag
&& type
== WARNING
) {
fprintf( stderr
, "%s: " , program
);
fprintf( stderr
, "Warning: " );
fprintf( stderr
, "Error: " );
fprintf( stderr
, "Fatal: " );
fprintf( stderr
, "Ooops: " );
fprintf( stderr
, message
, arg1
,arg2
,arg3
,arg4
,arg5
,arg6
,arg7
,arg8
);
fprintf( stderr
, "\n" );
return "external function";
return "external procedure";
return "library variable";
return "library routine";
return "an include file";
return "an external function";
return "an external procedure";
return "a library variable";
return "a library routine";
return "an unknown symbol";