(previously only the scope itself had that size). Pass the size of symbols through the object file to the linker. Bump the object file version and adjust object file reading tools (od65, ar65) to this change. Read the size in the linker and output it in the debug info. Bump the minor version number of the debug info. Read the size and allow to access it via the API. Do better version checking for the debug info and try to be smarter when encountering unknown keywords to improve support for newer minor versions. git-svn-id: svn://svn.cc65.org/cc65/trunk@5057 b7a2c559-68d2-44c3-8de9-860c34a00d81
349 lines
9.6 KiB
C
349 lines
9.6 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* objfile.c */
|
|
/* */
|
|
/* Object file handling for the ar65 archiver */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 1998-2011, Ullrich von Bassewitz */
|
|
/* Roemerstrasse 52 */
|
|
/* D-70794 Filderstadt */
|
|
/* EMail: uz@cc65.org */
|
|
/* */
|
|
/* */
|
|
/* 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 <string.h>
|
|
#include <errno.h>
|
|
#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__MINGW32__)
|
|
/* The Windows compilers have the file in the wrong directory */
|
|
# include <sys/utime.h>
|
|
#else
|
|
# include <sys/types.h> /* FreeBSD needs this */
|
|
# include <utime.h>
|
|
#endif
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* common */
|
|
#include "cddefs.h"
|
|
#include "exprdefs.h"
|
|
#include "fname.h"
|
|
#include "symdefs.h"
|
|
#include "xmalloc.h"
|
|
|
|
/* ar65 */
|
|
#include "error.h"
|
|
#include "objdata.h"
|
|
#include "fileio.h"
|
|
#include "library.h"
|
|
#include "objfile.h"
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Code */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static const char* GetModule (const char* Name)
|
|
/* Get a module name from the file name */
|
|
{
|
|
/* Make a module name from the file name */
|
|
const char* Module = FindName (Name);
|
|
|
|
/* Must not end with a path separator */
|
|
if (*Module == 0) {
|
|
Error ("Cannot make module name from `%s'", Name);
|
|
}
|
|
|
|
/* Done */
|
|
return Module;
|
|
}
|
|
|
|
|
|
|
|
static void ObjReadHeader (FILE* Obj, ObjHeader* H, const char* Name)
|
|
/* Read the header of the object file checking the signature */
|
|
{
|
|
H->Magic = Read32 (Obj);
|
|
if (H->Magic != OBJ_MAGIC) {
|
|
Error ("`%s' is not an object file", Name);
|
|
}
|
|
H->Version = Read16 (Obj);
|
|
if (H->Version != OBJ_VERSION) {
|
|
Error ("Object file `%s' has wrong version", Name);
|
|
}
|
|
H->Flags = Read16 (Obj);
|
|
H->OptionOffs = Read32 (Obj);
|
|
H->OptionSize = Read32 (Obj);
|
|
H->FileOffs = Read32 (Obj);
|
|
H->FileSize = Read32 (Obj);
|
|
H->SegOffs = Read32 (Obj);
|
|
H->SegSize = Read32 (Obj);
|
|
H->ImportOffs = Read32 (Obj);
|
|
H->ImportSize = Read32 (Obj);
|
|
H->ExportOffs = Read32 (Obj);
|
|
H->ExportSize = Read32 (Obj);
|
|
H->DbgSymOffs = Read32 (Obj);
|
|
H->DbgSymSize = Read32 (Obj);
|
|
H->LineInfoOffs = Read32 (Obj);
|
|
H->LineInfoSize = Read32 (Obj);
|
|
H->StrPoolOffs = Read32 (Obj);
|
|
H->StrPoolSize = Read32 (Obj);
|
|
H->AssertOffs = Read32 (Obj);
|
|
H->AssertSize = Read32 (Obj);
|
|
H->ScopeOffs = Read32 (Obj);
|
|
H->ScopeSize = Read32 (Obj);
|
|
}
|
|
|
|
|
|
|
|
static void SkipExpr (FILE* F)
|
|
/* Skip an expression in F */
|
|
{
|
|
/* Get the operation and skip it */
|
|
unsigned char Op = Read8 (F);
|
|
|
|
/* Handle then different expression nodes */
|
|
switch (Op) {
|
|
|
|
case EXPR_NULL:
|
|
break;
|
|
|
|
case EXPR_LITERAL:
|
|
/* 32 bit literal value */
|
|
(void) Read32 (F);
|
|
break;
|
|
|
|
case EXPR_SYMBOL:
|
|
/* Variable seized symbol index */
|
|
(void) ReadVar (F);
|
|
break;
|
|
|
|
case EXPR_SECTION:
|
|
/* 8 bit segment number */
|
|
(void) Read8 (F);
|
|
break;
|
|
|
|
default:
|
|
/* What's left are unary and binary nodes */
|
|
SkipExpr (F); /* Left */
|
|
SkipExpr (F); /* right */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void SkipLineInfoList (FILE* F)
|
|
/* Skip a list of line infos in F */
|
|
{
|
|
/* Number of indices preceeds the list */
|
|
unsigned long Count = ReadVar (F);
|
|
|
|
/* Skip indices */
|
|
while (Count--) {
|
|
(void) ReadVar (F);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ObjReadData (FILE* F, ObjData* O)
|
|
/* Read object file data from the given file. The function expects the Name
|
|
* and Start fields to be valid. Header and basic data are read.
|
|
*/
|
|
{
|
|
unsigned long Count;
|
|
|
|
/* Seek to the start of the object file data */
|
|
fseek (F, O->Start, SEEK_SET);
|
|
|
|
/* Read the object file header */
|
|
ObjReadHeader (F, &O->Header, O->Name);
|
|
|
|
/* Read the string pool */
|
|
fseek (F, O->Start + O->Header.StrPoolOffs, SEEK_SET);
|
|
Count = ReadVar (F);
|
|
CollGrow (&O->Strings, Count);
|
|
while (Count--) {
|
|
CollAppend (&O->Strings, ReadStr (F));
|
|
}
|
|
|
|
/* Read the exports */
|
|
fseek (F, O->Start + O->Header.ExportOffs, SEEK_SET);
|
|
Count = ReadVar (F);
|
|
CollGrow (&O->Exports, Count);
|
|
while (Count--) {
|
|
|
|
unsigned char ConDes[CD_TYPE_COUNT];
|
|
|
|
/* Skip data until we get to the name */
|
|
unsigned Type = ReadVar (F);
|
|
(void) Read8 (F); /* AddrSize */
|
|
ReadData (F, ConDes, SYM_GET_CONDES_COUNT (Type));
|
|
|
|
/* Now this is what we actually need: The name of the export */
|
|
CollAppend (&O->Exports, CollAt (&O->Strings, ReadVar (F)));
|
|
|
|
/* Skip the export value */
|
|
if (SYM_IS_EXPR (Type)) {
|
|
/* Expression tree */
|
|
SkipExpr (F);
|
|
} else {
|
|
/* Literal value */
|
|
(void) Read32 (F);
|
|
}
|
|
|
|
/* Skip the size if necessary */
|
|
if (SYM_HAS_SIZE (Type)) {
|
|
(void) ReadVar (F);
|
|
}
|
|
|
|
/* Line info indices */
|
|
SkipLineInfoList (F);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ObjAdd (const char* Name)
|
|
/* Add an object file to the library */
|
|
{
|
|
struct stat StatBuf;
|
|
const char* Module;
|
|
ObjHeader H;
|
|
ObjData* O;
|
|
|
|
/* Open the object file */
|
|
FILE* Obj = fopen (Name, "rb");
|
|
if (Obj == 0) {
|
|
Error ("Could not open `%s': %s", Name, strerror (errno));
|
|
}
|
|
|
|
/* Get the modification time of the object file. There a race condition
|
|
* here, since we cannot use fileno() (non standard identifier in standard
|
|
* header file), and therefore not fstat. When using stat with the
|
|
* file name, there's a risk that the file was deleted and recreated
|
|
* while it was open. Since mtime and size are only used to check
|
|
* if a file has changed in the debugger, we will ignore this problem
|
|
* here.
|
|
*/
|
|
if (stat (Name, &StatBuf) != 0) {
|
|
Error ("Cannot stat object file `%s': %s", Name, strerror (errno));
|
|
}
|
|
|
|
/* Read and check the header */
|
|
ObjReadHeader (Obj, &H, Name);
|
|
|
|
/* Make a module name from the file name */
|
|
Module = GetModule (Name);
|
|
|
|
/* Check if we already have a module with this name */
|
|
O = FindObjData (Module);
|
|
if (O == 0) {
|
|
/* Not found, create a new entry */
|
|
O = NewObjData ();
|
|
} else {
|
|
/* Found - check the file modification times of the internal copy
|
|
* and the external one.
|
|
*/
|
|
if (difftime ((time_t)O->MTime, StatBuf.st_mtime) > 0.0) {
|
|
Warning ("Replacing module `%s' by older version", O->Name);
|
|
}
|
|
|
|
/* Free data */
|
|
ClearObjData (O);
|
|
}
|
|
|
|
/* Initialize the object module data structure */
|
|
O->Name = xstrdup (Module);
|
|
O->Flags = OBJ_HAVEDATA;
|
|
O->MTime = StatBuf.st_mtime;
|
|
O->Start = 0;
|
|
|
|
/* Determine the file size. Note: Race condition here */
|
|
fseek (Obj, 0, SEEK_END);
|
|
O->Size = ftell (Obj);
|
|
|
|
/* Read the basic data from the object file */
|
|
ObjReadData (Obj, O);
|
|
|
|
/* Copy the complete object data to the library file and update the
|
|
* starting offset
|
|
*/
|
|
fseek (Obj, 0, SEEK_SET);
|
|
O->Start = LibCopyTo (Obj, O->Size);
|
|
|
|
/* Done, close the file (we read it only, so no error check) */
|
|
fclose (Obj);
|
|
}
|
|
|
|
|
|
|
|
void ObjExtract (const char* Name)
|
|
/* Extract a module from the library */
|
|
{
|
|
struct utimbuf U;
|
|
FILE* Obj;
|
|
|
|
/* Make a module name from the file name */
|
|
const char* Module = GetModule (Name);
|
|
|
|
/* Try to find the module in the library */
|
|
const ObjData* O = FindObjData (Module);
|
|
|
|
/* Bail out if the module does not exist */
|
|
if (O == 0) {
|
|
Error ("Module `%s' not found in library", Module);
|
|
}
|
|
|
|
/* Open the output file */
|
|
Obj = fopen (Name, "w+b");
|
|
if (Obj == 0) {
|
|
Error ("Cannot open target file `%s': %s", Name, strerror (errno));
|
|
}
|
|
|
|
/* Copy the complete object file data from the library to the new object
|
|
* file.
|
|
*/
|
|
LibCopyFrom (O->Start, O->Size, Obj);
|
|
|
|
/* Close the file */
|
|
if (fclose (Obj) != 0) {
|
|
Error ("Problem closing object file `%s': %s", Name, strerror (errno));
|
|
}
|
|
|
|
/* Set access and modification time */
|
|
U.actime = O->MTime;
|
|
U.modtime = O->MTime;
|
|
if (utime (Name, &U) != 0) {
|
|
Error ("Cannot set mod time on `%s': %s", Name, strerror (errno));
|
|
}
|
|
}
|
|
|
|
|
|
|