2000-05-28 13:40:48 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* library.c */
|
|
|
|
/* */
|
|
|
|
/* Library data structures and helpers for the ar65 archiver */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* */
|
2010-08-17 20:47:27 +00:00
|
|
|
/* (C) 1998-2010, Ullrich von Bassewitz */
|
|
|
|
/* Roemerstrasse 52 */
|
|
|
|
/* D-70794 Filderstadt */
|
|
|
|
/* EMail: uz@cc65.org */
|
2000-05-28 13:40:48 +00:00
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* 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>
|
|
|
|
|
2000-08-02 13:23:06 +00:00
|
|
|
/* common */
|
|
|
|
#include "bitops.h"
|
|
|
|
#include "exprdefs.h"
|
|
|
|
#include "filepos.h"
|
|
|
|
#include "libdefs.h"
|
2001-03-10 10:21:03 +00:00
|
|
|
#include "print.h"
|
2000-08-02 13:23:06 +00:00
|
|
|
#include "symdefs.h"
|
|
|
|
#include "xmalloc.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2000-08-02 13:23:06 +00:00
|
|
|
/* ar65 */
|
2000-05-28 13:40:48 +00:00
|
|
|
#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,
|
2001-09-15 12:00:43 +00:00
|
|
|
LIB_VERSION,
|
2003-05-25 17:57:50 +00:00
|
|
|
0,
|
|
|
|
0
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* 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 */
|
|
|
|
{
|
2003-05-25 17:57:50 +00:00
|
|
|
unsigned I;
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* 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);
|
|
|
|
|
2003-05-25 17:57:50 +00:00
|
|
|
/* Strings */
|
|
|
|
O->StringCount = ReadVar (Lib);
|
|
|
|
O->Strings = xmalloc (O->StringCount * sizeof (char*));
|
|
|
|
for (I = 0; I < O->StringCount; ++I) {
|
|
|
|
O->Strings[I] = ReadStr (Lib);
|
|
|
|
}
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Exports */
|
2003-05-25 17:57:50 +00:00
|
|
|
O->ExportSize = ReadVar (Lib);
|
2000-06-14 09:38:07 +00:00
|
|
|
O->Exports = xmalloc (O->ExportSize);
|
2000-05-28 13:40:48 +00:00
|
|
|
ReadData (Lib, O->Exports, O->ExportSize);
|
|
|
|
|
|
|
|
/* Imports */
|
2003-05-25 17:57:50 +00:00
|
|
|
O->ImportSize = ReadVar (Lib);
|
2000-06-14 09:38:07 +00:00
|
|
|
O->Imports = xmalloc (O->ImportSize);
|
2000-05-28 13:40:48 +00:00
|
|
|
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 */
|
2003-05-25 17:57:50 +00:00
|
|
|
Count = ReadVar (Lib);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
{
|
2003-05-25 17:57:50 +00:00
|
|
|
unsigned I;
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* 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);
|
|
|
|
|
2003-05-25 17:57:50 +00:00
|
|
|
/* Strings */
|
|
|
|
WriteVar (NewLib, O->StringCount);
|
|
|
|
for (I = 0; I < O->StringCount; ++I) {
|
|
|
|
WriteStr (NewLib, O->Strings[I]);
|
|
|
|
}
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Exports */
|
2003-05-25 17:57:50 +00:00
|
|
|
WriteVar (NewLib, O->ExportSize);
|
2000-05-28 13:40:48 +00:00
|
|
|
WriteData (NewLib, O->Exports, O->ExportSize);
|
|
|
|
|
|
|
|
/* Imports */
|
2003-05-25 17:57:50 +00:00
|
|
|
WriteVar (NewLib, O->ImportSize);
|
2000-05-28 13:40:48 +00:00
|
|
|
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 */
|
2003-05-25 17:57:50 +00:00
|
|
|
WriteVar (NewLib, ObjCount);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* 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 */
|
2000-06-14 09:38:07 +00:00
|
|
|
LibName = xstrdup (Name);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-08-02 13:23:06 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
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:
|
2003-05-25 17:57:50 +00:00
|
|
|
/* Variable seized symbol index */
|
|
|
|
(void) GetVar (Buf);
|
2000-05-28 13:40:48 +00:00
|
|
|
return;
|
|
|
|
|
2002-12-14 22:57:00 +00:00
|
|
|
case EXPR_SECTION:
|
2000-05-28 13:40:48 +00:00
|
|
|
/* 8 bit segment number */
|
|
|
|
*Buf += 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* What's left are unary and binary nodes */
|
2003-05-25 17:57:50 +00:00
|
|
|
SkipExpr (Buf); /* Skip left */
|
|
|
|
SkipExpr (Buf); /* Skip right */
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-08-02 13:23:06 +00:00
|
|
|
static void SkipFilePos (unsigned char** Buf)
|
|
|
|
/* Skip a file position in Buf */
|
|
|
|
{
|
|
|
|
(void) GetVar (Buf); /* Line */
|
|
|
|
(void) GetVar (Buf); /* Col */
|
|
|
|
(void) GetVar (Buf); /* Name */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
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;
|
|
|
|
|
2000-08-02 13:23:06 +00:00
|
|
|
/* Get the export count */
|
|
|
|
unsigned Count = GetVar (&Exports);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Read the exports */
|
2001-03-10 10:21:03 +00:00
|
|
|
Print (stdout, 1, "Module `%s' (%u exports):\n", O->Name, Count);
|
2000-05-28 13:40:48 +00:00
|
|
|
while (Count--) {
|
2001-03-10 10:21:03 +00:00
|
|
|
|
2003-05-25 17:57:50 +00:00
|
|
|
const char* Name;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-11-28 22:12:14 +00:00
|
|
|
/* Get the export tag and skip the address size */
|
2010-08-17 20:47:27 +00:00
|
|
|
unsigned Type = GetVar (&Exports);
|
2003-11-28 22:12:14 +00:00
|
|
|
++Exports;
|
2000-11-20 22:59:11 +00:00
|
|
|
|
|
|
|
/* condes decls may follow */
|
2010-08-17 20:47:27 +00:00
|
|
|
Exports += SYM_GET_CONDES_COUNT (Type);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-05-25 17:57:50 +00:00
|
|
|
/* Next thing is index of name of symbol */
|
|
|
|
Name = GetObjString (O, GetVar (&Exports));
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Skip value of symbol */
|
2010-08-17 20:47:27 +00:00
|
|
|
if (SYM_IS_EXPR (Type)) {
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Expression tree */
|
|
|
|
SkipExpr (&Exports);
|
|
|
|
} else {
|
|
|
|
/* Constant 32 bit value */
|
|
|
|
Exports += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the position */
|
2000-08-02 13:23:06 +00:00
|
|
|
SkipFilePos (&Exports);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Insert the name into the hash table */
|
2001-03-10 10:21:03 +00:00
|
|
|
Print (stdout, 1, " %s\n", Name);
|
2000-05-28 13:40:48 +00:00
|
|
|
ExpInsert (Name, O->Index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|