Files
cc65/src/ar65/library.c
cuz 097a01094e Added a method to write variable sized unsigned values. Use this method for
all sorts of things in the object files. This does not only make the object
files smaller, but does also remove several limits (strings may be longer
than 255 bytes, several counters no longer have 8 or 16 bit limits).


git-svn-id: svn://svn.cc65.org/cc65/trunk@260 b7a2c559-68d2-44c3-8de9-860c34a00d81
2000-08-02 13:23:06 +00:00

503 lines
13 KiB
C

/*****************************************************************************/
/* */
/* library.c */
/* */
/* Library data structures and helpers for the ar65 archiver */
/* */
/* */
/* */
/* (C) 1998-2000 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* common */
#include "bitops.h"
#include "exprdefs.h"
#include "filepos.h"
#include "libdefs.h"
#include "symdefs.h"
#include "xmalloc.h"
/* ar65 */
#include "error.h"
#include "global.h"
#include "fileio.h"
#include "objdata.h"
#include "exports.h"
#include "library.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* File descriptor for the library file */
FILE* NewLib = 0;
static FILE* Lib = 0;
static const char* LibName = 0;
/* The library header */
static LibHeader Header = {
LIB_MAGIC,
LIB_VERSION
};
/*****************************************************************************/
/* Writing file data structures */
/*****************************************************************************/
static void ReadHeader (void)
/* Read the header of a library file */
{
/* Seek to position zero */
fseek (Lib, 0, SEEK_SET);
/* Read the header fields, checking magic and version */
Header.Magic = Read32 (Lib);
if (Header.Magic != LIB_MAGIC) {
Error ("`%s' is not a valid library file", LibName);
}
Header.Version = Read16 (Lib);
if (Header.Version != LIB_VERSION) {
Error ("Wrong data version in `%s'", LibName);
}
Header.Flags = Read16 (Lib);
Header.IndexOffs = Read32 (Lib);
}
static void ReadIndexEntry (void)
/* Read one entry in the index */
{
/* Create a new entry and insert it into the list */
ObjData* O = NewObjData ();
/* Module name/flags/MTime/Start/Size */
O->Name = ReadStr (Lib);
O->Flags = Read16 (Lib);
O->MTime = Read32 (Lib);
O->Start = Read32 (Lib);
O->Size = Read32 (Lib);
/* Exports */
O->ExportSize = Read16 (Lib);
O->Exports = xmalloc (O->ExportSize);
ReadData (Lib, O->Exports, O->ExportSize);
/* Imports */
O->ImportSize = Read16 (Lib);
O->Imports = xmalloc (O->ImportSize);
ReadData (Lib, O->Imports, O->ImportSize);
}
static void ReadIndex (void)
/* Read the index of a library file */
{
unsigned Count;
/* Seek to the start of the index */
fseek (Lib, Header.IndexOffs, SEEK_SET);
/* Read the object file count and calculate the cross ref size */
Count = Read16 (Lib);
/* Read all entries in the index */
while (Count--) {
ReadIndexEntry ();
}
}
/*****************************************************************************/
/* Writing file data structures */
/*****************************************************************************/
static void WriteHeader (void)
/* Write the header to the library file */
{
/* Seek to position zero */
fseek (NewLib, 0, SEEK_SET);
/* Write the header fields */
Write32 (NewLib, Header.Magic);
Write16 (NewLib, Header.Version);
Write16 (NewLib, Header.Flags);
Write32 (NewLib, Header.IndexOffs);
}
static void WriteIndexEntry (ObjData* O)
/* Write one index entry */
{
/* Module name/flags/MTime/start/size */
WriteStr (NewLib, O->Name);
Write16 (NewLib, O->Flags & ~OBJ_HAVEDATA);
Write32 (NewLib, O->MTime);
Write32 (NewLib, O->Start);
Write32 (NewLib, O->Size);
/* Exports */
Write16 (NewLib, O->ExportSize);
WriteData (NewLib, O->Exports, O->ExportSize);
/* Imports */
Write16 (NewLib, O->ImportSize);
WriteData (NewLib, O->Imports, O->ImportSize);
}
static void WriteIndex (void)
/* Write the index of a library file */
{
ObjData* O;
/* Sync I/O in case the last operation was a read */
fseek (NewLib, 0, SEEK_CUR);
/* Remember the current offset in the header */
Header.IndexOffs = ftell (NewLib);
/* Write the object file count */
Write16 (NewLib, ObjCount);
/* Write the object files */
O = ObjRoot;
while (O) {
WriteIndexEntry (O);
O = O->Next;
}
}
/*****************************************************************************/
/* High level stuff */
/*****************************************************************************/
void LibOpen (const char* Name, int MustExist, int NeedTemp)
/* Open an existing library and a temporary copy. If MustExist is true, the
* old library is expected to exist. If NeedTemp is true, a temporary library
* is created.
*/
{
/* Remember the name */
LibName = xstrdup (Name);
/* Open the existing library for reading */
Lib = fopen (Name, "rb");
if (Lib == 0) {
/* File does not exist */
if (MustExist) {
Error ("Library `%s' does not exist", Name);
} else {
Warning ("Library `%s' not found - will be created", Name);
}
} else {
/* We have an existing file: Read the header */
ReadHeader ();
/* Now read the existing index */
ReadIndex ();
}
if (NeedTemp) {
/* Create the temporary library */
NewLib = tmpfile ();
if (NewLib == 0) {
Error ("Cannot create temporary file: %s", strerror (errno));
}
/* Write a dummy header to the temp file */
WriteHeader ();
}
}
unsigned long LibCopyTo (FILE* F, unsigned long Bytes)
/* Copy data from F to the temp library file, return the start position in
* the temporary library file.
*/
{
unsigned char Buf [4096];
/* Remember the position */
unsigned long Pos = ftell (NewLib);
/* Copy loop */
while (Bytes) {
unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes;
ReadData (F, Buf, Count);
WriteData (NewLib, Buf, Count);
Bytes -= Count;
}
/* Return the start position */
return Pos;
}
void LibCopyFrom (unsigned long Pos, unsigned long Bytes, FILE* F)
/* Copy data from the library file into another file */
{
unsigned char Buf [4096];
/* Seek to the correct position */
fseek (Lib, Pos, SEEK_SET);
/* Copy loop */
while (Bytes) {
unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes;
ReadData (Lib, Buf, Count);
WriteData (F, Buf, Count);
Bytes -= Count;
}
}
static unsigned long GetVar (unsigned char** Buf)
/* Get a variable sized value from Buf */
{
unsigned char C;
unsigned long V = 0;
unsigned Shift = 0;
do {
/* Read one byte */
C = **Buf;
++(*Buf);
/* Add this char to the value */
V |= ((unsigned long)(C & 0x7F)) << Shift;
/* Next value */
Shift += 7;
} while (C & 0x80);
/* Return the result */
return V;
}
static void SkipExpr (unsigned char** Buf)
/* Skip an expression in Buf */
{
/* Get the operation and skip it */
unsigned char Op = **Buf;
++(*Buf);
/* Filter leaf nodes */
switch (Op) {
case EXPR_NULL:
return;
case EXPR_LITERAL:
/* 32 bit literal value */
*Buf += 4;
return;
case EXPR_SYMBOL:
/* 16 bit symbol index */
*Buf += 2;
return;
case EXPR_SEGMENT:
/* 8 bit segment number */
*Buf += 1;
return;
}
/* What's left are unary and binary nodes */
SkipExpr (Buf); /* Skip left */
SkipExpr (Buf); /* Skip right */
}
static void SkipFilePos (unsigned char** Buf)
/* Skip a file position in Buf */
{
(void) GetVar (Buf); /* Line */
(void) GetVar (Buf); /* Col */
(void) GetVar (Buf); /* Name */
}
static void LibCheckExports (ObjData* O)
/* Insert all exports from the given object file into the global list
* checking for duplicates.
*/
{
/* Get a pointer to the buffer */
unsigned char* Exports = O->Exports;
/* Get the export count */
unsigned Count = GetVar (&Exports);
/* Read the exports */
if (Verbose > 1) {
printf ("Module `%s' (%u exports):\n", O->Name, Count);
}
while (Count--) {
/* Get the export tag */
unsigned char Tag = *Exports++;
/* Next thing is name of symbol */
unsigned Len = GetVar (&Exports);
char* Name = xmalloc (Len + 1);
memcpy (Name, Exports, Len);
Name [Len] = '\0';
Exports += Len;
/* Skip value of symbol */
if (Tag & EXP_EXPR) {
/* Expression tree */
SkipExpr (&Exports);
} else {
/* Constant 32 bit value */
Exports += 4;
}
/* Skip the position */
SkipFilePos (&Exports);
/* Insert the name into the hash table */
if (Verbose > 1) {
printf (" %s\n", Name);
}
ExpInsert (Name, O->Index);
/* Free the name */
xfree (Name);
}
}
void LibClose (void)
/* Write remaining data, close both files and copy the temp file to the old
* filename
*/
{
/* Do we have a temporary library? */
if (NewLib) {
unsigned I;
unsigned char Buf [4096];
size_t Count;
/* Index the object files and make an array containing the objects */
MakeObjPool ();
/* Walk through the object file list, inserting exports into the
* export list checking for duplicates. Copy any data that is still
* in the old library into the new one.
*/
for (I = 0; I < ObjCount; ++I) {
/* Get a pointer to the object */
ObjData* O = ObjPool [I];
/* Check exports, make global export table */
LibCheckExports (O);
/* Copy data if needed */
if ((O->Flags & OBJ_HAVEDATA) == 0) {
/* Data is still in the old library */
fseek (Lib, O->Start, SEEK_SET);
O->Start = ftell (NewLib);
LibCopyTo (Lib, O->Size);
O->Flags |= OBJ_HAVEDATA;
}
}
/* Write the index */
WriteIndex ();
/* Write the updated header */
WriteHeader ();
/* Close the file */
if (Lib && fclose (Lib) != 0) {
Error ("Error closing library: %s", strerror (errno));
}
/* Reopen the library and truncate it */
Lib = fopen (LibName, "wb");
if (Lib == 0) {
Error ("Cannot open library `%s' for writing: %s",
LibName, strerror (errno));
}
/* Copy the new library to the new one */
fseek (NewLib, 0, SEEK_SET);
while ((Count = fread (Buf, 1, sizeof (Buf), NewLib)) != 0) {
if (fwrite (Buf, 1, Count, Lib) != Count) {
Error ("Cannot write to `%s': %s", LibName, strerror (errno));
}
}
}
/* Close both files */
if (Lib && fclose (Lib) != 0) {
Error ("Problem closing `%s': %s", LibName, strerror (errno));
}
if (NewLib && fclose (NewLib) != 0) {
Error ("Problem closing temporary library file: %s", strerror (errno));
}
}