/* lkar.c - ar library format handling
Copyright (C) 1989-1995 Alan R. Baldwin
721 Berkeley St., Kent, Ohio 44240
Copyright (C) 2008-2009 Borut Razem, borut dot razem at gmail dot com
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/*
* With contributions for the
* object libraries from
* Ken Hornstein
* kenh@cmf.nrl.navy.mil
*
*/
/*
* Extensions: P. Felber
*/
#include
#include "aslink.h"
#include "lklibr.h"
#include "lkrel.h"
#include "lkar.h"
#ifndef max
# define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef HAVE_STRNDUP
char *
strndup (const char *str, size_t len)
{
char *s = (char *) malloc (len + 1);
memcpy (s, str, len);
s[len] = '\0';
return s;
}
#endif
static int
is_ar (FILE * libfp)
{
char buf[SARMAG];
int ret;
if (!(ret = fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, ARMAG, SARMAG) == 0))
rewind (libfp);
return ret;
}
static char *str_tab; /* string table */
static int str_tab_size; /* string table size */
static char *
get_long_name (const char *name)
{
assert ('/' == name[0]);
if (NULL != str_tab)
{
char *p;
int name_offset = strtol (++name, &p, 0);
if (p != name && name_offset < str_tab_size)
{
int len = p - name + 1;
while (len < AR_NAME_LEN && name[len++] == ' ')
;
if (len == AR_NAME_LEN)
{
const char *n;
/* long name: get it from the symbol table */
name = &str_tab[name_offset];
for (n = name; *n != '/' && *n != '\n'; ++n)
assert (n < &str_tab[str_tab_size]);
if (n[0] != '/' || n[1] != '\n')
while (*++n != '\n')
assert (n < &str_tab[str_tab_size]);
return strndup (name, n - name);
}
}
}
return NULL;
}
static char *
get_member_name (char *name, size_t *p_size, int allocate, FILE * libfp)
{
if (p_size != NULL)
*p_size = 0;
if (0 == memcmp (name, "#1/", 3))
{
char *p;
size_t len = strtoul (&name [3], &p, 10);
if (p > &name [3])
{
/* BSD appends real file name to the file header */
if (p_size != NULL)
*p_size = len;
if (allocate)
{
char *n = (char *) malloc (len);
if (fread (n, 1, len, libfp) != len)
{
/* not an ar archive or broken ar archive */
free (n);
return NULL;
}
else
return n;
}
else
{
/* just advance the file pointer */
fseek (libfp, len, SEEK_CUR);
return NULL;
}
}
else
{
/* not an ar archive or broken ar archive */
return NULL;
}
}
else if (allocate)
{
if (name[0] == '/')
{
char *n = get_long_name (name);
if (NULL != n)
return n;
}
else
{
const char *p = strrchr (name + 1, '/');
if (NULL != p)
{
int len = p - name;
while (name[++len] == ' ')
;
if (len == AR_NAME_LEN)
return strndup (name, p - name);
}
else
{
/* BSD formed member name:
trim trailing spaces */
p = name + AR_NAME_LEN;
while (*--p == ' ' && p >= name)
;
return strndup (name, p - name + 1);
}
}
/* bad formed member name or long name not found:
just return it */
return strdup (name);
}
else
return NULL;
}
static size_t
ar_get_header (struct ar_hdr *hdr, FILE * libfp, char **p_obj_name)
{
char header[ARHDR_LEN];
char buf[AR_DATE_LEN + 1];
char *obj_name;
size_t size;
if (fread (header, 1, sizeof (header), libfp) != sizeof (header)
|| memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
{
/* not an ar archive */
return 0;
}
memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
hdr->ar_name[AR_NAME_LEN] = '\0';
memcpy (buf, &header[AR_DATE_OFFSET], AR_DATE_LEN);
buf[AR_DATE_LEN] = '\0';
hdr->ar_date = strtol (buf, NULL, 0);
memcpy (buf, &header[AR_UID_OFFSET], AR_GID_LEN);
buf[AR_GID_LEN] = '\0';
hdr->ar_uid = (uid_t) strtol (buf, NULL, 0);
memcpy (buf, &header[AR_GID_OFFSET], AR_DATE_LEN);
buf[AR_DATE_LEN] = '\0';
hdr->ar_gid = (gid_t) strtol (buf, NULL, 0);
memcpy (buf, &header[AR_MODE_OFFSET], AR_MODE_LEN);
buf[AR_MODE_LEN] = '\0';
hdr->ar_mode = (mode_t) strtoul (buf, NULL, 0);
memcpy (buf, &header[AR_SIZE_OFFSET], AR_SIZE_LEN);
buf[AR_SIZE_LEN] = '\0';
hdr->ar_size = strtol (buf, NULL, 0);
obj_name = get_member_name (hdr->ar_name, &size, p_obj_name != NULL, libfp);
if (p_obj_name != NULL)
*p_obj_name = obj_name;
/* treat BSD appended real file name as a part of the header */
hdr->ar_size -= size;
return size + ARHDR_LEN;
}
#ifdef INDEXLIB
static char *
get_member_name_by_offset (FILE * fp, long offset)
{
struct ar_hdr hdr;
char *name;
fseek (fp, offset, SEEK_SET);
return (ar_get_header (&hdr, fp, &name) != 0) ? name : NULL;
}
static pmlibraryfile
find_member_by_offset (const char *libspc, long offset)
{
pmlibraryfile p;
/* walk trough all archive members */
for (p = libr; p; p = p->next)
{
if (0 == strcmp (libspc, p->libspc) && p->offset == offset)
return p;
}
return NULL;
}
static pmlibraryfile
buildlibraryindex_ar (struct lbname *lbnh, FILE * libfp, pmlibraryfile This, int type)
{
struct ar_hdr hdr;
char *obj_name;
size_t hdr_size;
int sym_found = 0;
/* walk trough all archive members */
while ((hdr_size = ar_get_header (&hdr, libfp, &obj_name)) != 0)
{
long pos = ftell (libfp);
if (AR_IS_SYMBOL_TABLE (obj_name))
{
char *buf, *po, *ps;
int i;
long nsym;
/* duplicated symbol table */
assert (!sym_found);
free (obj_name);
buf = (char *) new (hdr.ar_size);
if (fread (buf, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (buf);
return This;
}
nsym = sgetl (buf);
po = buf + 4;
ps = po + nsym * 4;
for (i = 0; i < nsym; ++i)
{
pmlibrarysymbol ThisSym;
char *sym;
long offset;
pmlibraryfile entry;
offset = sgetl (po);
po += 4;
sym = strdup (ps);
ps += strlen (ps) + 1;
if ((entry = find_member_by_offset (lbnh->libspc, offset)) != NULL)
{
for (ThisSym = entry->symbols; ThisSym->next != NULL; ThisSym = ThisSym->next)
;
}
else
{
/* Opened OK - create a new libraryfile object for it */
if (This == NULL)
{
assert (libr == NULL);
libr = This = (pmlibraryfile) new (sizeof (mlibraryfile));
}
else
{
This->next = (pmlibraryfile) new (sizeof (mlibraryfile));
This = This->next;
}
This->next = NULL;
This->loaded = 0;
This->libspc = lbnh->libspc;
This->offset = offset;
This->relfil = get_member_name_by_offset (libfp, offset); /* member name */
This->filspc = strdup (This->relfil); /* member file name */
This->type = type;
/* start a new linked list of symbols for this module. */
This->symbols = ThisSym = NULL;
}
if (ThisSym == NULL)
ThisSym = This->symbols = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
else
{
ThisSym->next = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
ThisSym = ThisSym->next;
}
ThisSym->next = NULL;
ThisSym->name = sym;
}
free (buf);
sym_found = 1;
/* string table already found: finish */
if (str_tab)
break;
}
else if (AR_IS_BSD_SYMBOL_TABLE (obj_name))
{
char *buf, *po, *ps;
int i;
long nsym, tablesize;
/* duplicated symbol table */
assert (!sym_found);
free (obj_name);
buf = (char *) new (hdr.ar_size);
if (fread (buf, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (buf);
return This;
}
tablesize = sgetl (buf);
nsym = tablesize / 8;
po = buf + 4;
ps = po + tablesize + 4;
for (i = 0; i < nsym; ++i)
{
pmlibrarysymbol ThisSym;
char *sym;
long offset;
pmlibraryfile entry;
sym = ps + sgetl (po);
po += 4;
offset = sgetl (po);
po += 4;
sym = strdup (ps);
if ((entry = find_member_by_offset (lbnh->libspc, offset)) != NULL)
{
for (ThisSym = entry->symbols; ThisSym->next != NULL; ThisSym = ThisSym->next)
;
}
else
{
/* Opened OK - create a new libraryfile object for it */
if (This == NULL)
{
assert (libr == NULL);
libr = This = (pmlibraryfile) new (sizeof (mlibraryfile));
}
else
{
This->next = (pmlibraryfile) new (sizeof (mlibraryfile));
This = This->next;
}
This->next = NULL;
This->loaded = 0;
This->libspc = lbnh->libspc;
This->offset = offset;
This->relfil = get_member_name_by_offset (libfp, offset); /* member name */
This->filspc = strdup (This->relfil); /* member file name */
This->type = type;
/* start a new linked list of symbols for this module. */
This->symbols = ThisSym = NULL;
}
if (ThisSym == NULL)
ThisSym = This->symbols = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
else
{
ThisSym->next = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
ThisSym = ThisSym->next;
}
ThisSym->next = NULL;
ThisSym->name = sym;
}
free (buf);
sym_found = 1;
/* string table already found: finish */
if (str_tab)
break;
}
else if (AR_IS_STRING_TABLE (obj_name))
{
free (obj_name);
/* duplicated string table */
assert (NULL == str_tab);
str_tab = (char *) new (hdr.ar_size);
if (fread (str_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (str_tab);
str_tab_size = 0;
return This;
}
str_tab_size = hdr.ar_size;
/* sybol table already found: finish */
if (sym_found)
break;
}
else
{
if (NULL == libr)
{
/* Opened OK - create a new libraryfile object for it */
if (This == NULL)
{
assert (libr == NULL);
libr = This = (pmlibraryfile) new (sizeof (mlibraryfile));
}
else
{
This->next = (pmlibraryfile) new (sizeof (mlibraryfile));
This = This->next;
}
This->next = NULL;
This->loaded = -1;
This->libspc = lbnh->libspc;
This->offset = pos - hdr_size;
This->relfil = obj_name; /* member name */
This->filspc = strdup (This->relfil); /* member file name */
D (" Indexing module: %s\n", This->relfil);
This->type = type;
/* start a new linked list of symbols for this module. */
This->symbols = NULL;
add_rel_index (libfp, hdr.ar_size, This);
}
}
fseek (libfp, pos + hdr.ar_size + (hdr.ar_size & 1), SEEK_SET);
}
if (NULL != str_tab)
{
/* has a symbol table: walk through modules and replace offsets with names */
pmlibraryfile lfp;
for (lfp = libr; lfp; lfp = lfp->next)
{
char *name = lfp->relfil;
if (name[0] == '/')
{
char *p = get_long_name (name);
if (NULL != p)
{
free (lfp->relfil);
lfp->relfil = p;
free (lfp->filspc);
lfp->filspc = strdup (p);
}
}
}
free (str_tab);
str_tab = NULL;
str_tab_size = 0;
}
return This;
}
#else
#if 0
static int
load_adb (FILE * libfp, struct lbfile *lbfh)
{
struct ar_hdr hdr;
char *adb_name;
char *obj_name;
size_t hdr_size;
/* check if it is a .rel file */
if (0 != stricmp (&lbfh->relfil[strlen (lbfh->relfil) - 4], ".rel"))
return 0;
adb_name = (char *) new (strlen (lbfh->relfil) + 1);
memcpy (adb_name, lbfh->relfil, strlen (lbfh->relfil) - 4);
memcpy (&adb_name[strlen (lbfh->relfil) - 4], ".adb", 5);
if (!is_ar (libfp))
{
fprintf (stderr, "?ASlink-Error-%s is not an archive\n", lbfh->libspc);
fclose (libfp);
lkexit (1);
}
/* walk trough all archive members */
while ((hdr_size = ar_get_header (&hdr, libfp, &obj_name)) != 0)
{
if (AR_IS_STRING_TABLE (obj_name))
{
free (obj_name);
if (str_tab)
free (str_tab);
str_tab = (char *) new (hdr.ar_size);
if ((off_t) fread (str_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (str_tab);
str_tab_size = 0;
return 0;
}
str_tab_size = hdr.ar_size;
}
if (AR_IS_SYMBOL_TABLE (obj_name) || 0 != stricmp (obj_name, adb_name))
{
free (obj_name);
/* skip the mamber */
fseek (libfp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
}
else
{
long left = hdr.ar_size;
char buf[4096];
free (obj_name);
while (left)
{
size_t n = min (left, sizeof buf);
if (fread (buf, 1, n, libfp) != n)
{
assert (0);
}
fwrite (buf, 1, n, yfp);
left -= n;
}
if (hdr.ar_size & 1)
getc (libfp);
free (adb_name);
return 1;
}
}
free (adb_name);
return 0;
}
#endif
static void
load_str_tab (FILE * libfp)
{
struct ar_hdr hdr;
char *obj_name;
size_t hdr_size;
/* walk trough all archive members */
while ((hdr_size = ar_get_header (&hdr, libfp, &obj_name)) != 0)
{
long pos = ftell (libfp);
if (AR_IS_STRING_TABLE (obj_name))
{
free (obj_name);
/* duplicated string table */
assert (NULL == str_tab);
str_tab = (char *) new (hdr.ar_size);
if (fread (str_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (str_tab);
str_tab_size = 0;
return;
}
str_tab_size = hdr.ar_size;
return;
}
fseek (libfp, pos + hdr.ar_size + (hdr.ar_size & 1), SEEK_SET);
}
}
static int
fndsym_ar (const char *name, struct lbname *lbnh, FILE * libfp, int type)
{
struct ar_hdr hdr;
int ret = 0;
size_t hdr_size;
char *obj_name;
long pos;
pos = ftell (libfp);
load_str_tab (libfp);
fseek (libfp, pos, SEEK_SET);
/* walk trough all archive members */
while ((hdr_size = ar_get_header (&hdr, libfp, &obj_name)) != 0)
{
char filspc[PATH_MAX] = { 0 };
if (lbnh->path != NULL)
{
strcpy (filspc, lbnh->path);
if (*filspc != '\0' && (filspc[strlen (filspc) - 1] != '/') && (filspc[strlen (filspc) - 1] != LKDIRSEP))
{
strcat (filspc, LKDIRSEPSTR);
}
}
if (AR_IS_SYMBOL_TABLE (obj_name))
{
char *buf, *po, *ps;
int i;
long nsym;
free (obj_name);
buf = (char *) new (hdr.ar_size);
if (fread (buf, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (buf);
return 0;
}
nsym = sgetl (buf);
po = buf + 4;
ps = po + nsym * 4;
for (i = 0; i < nsym; ++i)
{
char *sym;
long offset;
offset = sgetl (po);
po += 4;
sym = ps;
while (*ps++ != '\0')
;
if (0 == strcmp (name, sym))
{
fseek (libfp, offset, SEEK_SET);
if (ar_get_header (&hdr, libfp, &obj_name))
{
sprintf (&filspc[strlen (filspc)], "%s", hdr.ar_name);
/* If this module has been loaded already don't load it again. */
if (!is_module_loaded (filspc))
{
struct lbfile *lbfh, *lbf;
lbfh = (struct lbfile *) new (sizeof (struct lbfile));
lbfh->libspc = strdup (lbnh->libspc);
lbfh->relfil = obj_name;
lbfh->filspc = strdup (filspc);
lbfh->offset = offset;
lbfh->type = type;
if (lbfhead == NULL)
{
lbfhead = lbfh;
}
else
{
for (lbf = lbfhead; lbf->next != NULL; lbf = lbf->next)
;
lbf->next = lbfh;
}
D ("Loading module %s from file %s.\n", hdr.ar_name, lbfh->libspc);
load_rel (libfp, hdr.ar_size);
///* if cdb information required & .adb file present */
//if (yflag && yfp)
// {
// if (load_adb(FILE *libfp, struct lbfile *lbfh))
// SaveLinkedFilePath (filspc);
// }
ret = 1;
break;
}
}
else
{
fprintf (stderr, "?ASlink-Error-Bad offset in library file %s(%s)\n", lbnh->libspc, name);
fclose (libfp);
lkexit (1);
}
}
}
free (buf);
break;
}
else if (AR_IS_BSD_SYMBOL_TABLE (obj_name))
{
char *buf, *po, *ps;
int i;
long nsym, tablesize;
free (obj_name);
buf = (char *) new (hdr.ar_size);
if (fread (buf, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (buf);
return 0;
}
tablesize = sgetl (buf);
nsym = tablesize / 8;
po = buf + 4;
ps = po + tablesize + 4;
for (i = 0; i < nsym; ++i)
{
char *sym;
long offset;
sym = ps + sgetl (po);
po += 4;
offset = sgetl (po);
po += 4;
if (0 == strcmp (name, sym))
{
fseek (libfp, offset, SEEK_SET);
if (ar_get_header (&hdr, libfp, &obj_name))
{
sprintf (&filspc[strlen (filspc)], "%s", hdr.ar_name);
/* If this module has been loaded already don't load it again. */
if (!is_module_loaded (filspc))
{
struct lbfile *lbfh, *lbf;
lbfh = (struct lbfile *) new (sizeof (struct lbfile));
lbfh->libspc = strdup (lbnh->libspc);
lbfh->relfil = obj_name;
lbfh->filspc = strdup (filspc);
lbfh->offset = offset;
lbfh->type = type;
if (lbfhead == NULL)
{
lbfhead = lbfh;
}
else
{
for (lbf = lbfhead; lbf->next != NULL; lbf = lbf->next)
;
lbf->next = lbfh;
}
D ("Loading module %s from file %s.\n", hdr.ar_name, lbfh->libspc);
load_rel (libfp, hdr.ar_size);
///* if cdb information required & .adb file present */
//if (yflag && yfp)
// {
// if (load_adb(FILE *libfp, struct lbfile *lbfh))
// SaveLinkedFilePath (filspc);
// }
ret = 1;
break;
}
}
else
{
fprintf (stderr, "?ASlink-Error-Bad offset in library file %s(%s)\n", lbnh->libspc, name);
fclose (libfp);
lkexit (1);
}
}
}
free (buf);
break;
}
else if (AR_IS_STRING_TABLE (obj_name))
{
free (obj_name);
if (str_tab)
free (str_tab);
str_tab = (char *) new (hdr.ar_size);
if (fread (str_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
{
free (str_tab);
str_tab = NULL;
str_tab_size = 0;
return 0;
}
str_tab_size = hdr.ar_size;
}
else
{
long pos = ftell (libfp);
free (obj_name);
D (" Module: %s\n", hdr.ar_name);
sprintf (&filspc[strlen (filspc)], "%s", hdr.ar_name);
/* Opened OK - create a new libraryfile object for it */
ret = add_rel_file (name, lbnh, hdr.ar_name, filspc, pos - hdr_size, libfp, hdr.ar_size, type);
///* if cdb information required & .adb file present */
//if (yflag && yfp)
// {
// if (load_adb(FILE *libfp, struct lbfile *lbfh))
// SaveLinkedFilePath (filspc);
// }
if (ret)
break;
fseek (libfp, pos + hdr.ar_size + (hdr.ar_size & 1), SEEK_SET);
}
}
if (NULL != str_tab)
{
free (str_tab);
str_tab = NULL;
str_tab_size = 0;
}
return ret;
}
#endif
static void
loadfile_ar (struct lbfile *lbfh)
{
FILE *fp;
fp = fopen (lbfh->libspc, "rb");
if (fp != NULL)
{
struct ar_hdr hdr;
fseek (fp, lbfh->offset, SEEK_SET);
if (ar_get_header (&hdr, fp, NULL) != 0)
{
D ("Loading module %s from file %s.\n", hdr.ar_name, lbfh->libspc);
load_rel (fp, hdr.ar_size);
fclose (fp);
}
else
{
fprintf (stderr, "?ASlink-Error-Bad offset in library file %s(%s)\n", lbfh->libspc, lbfh->relfil);
fclose (fp);
lkexit (1);
}
}
else
{
fprintf (stderr, "?ASlink-Error-Opening library '%s'\n", lbfh->libspc);
lkexit (1);
}
}
struct aslib_target aslib_target_ar = {
&is_ar,
#ifdef INDEXLIB
&buildlibraryindex_ar,
#else
&fndsym_ar,
#endif
&loadfile_ar,
};