Files
sigmaker-ida/idasdk76/ldr/ar/arcmn.cpp
2021-10-31 21:20:46 +02:00

239 lines
6.9 KiB
C++

#include <diskio.hpp>
#define MAGICLEN ((SARMAG > SAIAMAG) ? SARMAG : SAIAMAG)
//------------------------------------------------------------------------
bool is_ar_file(linput_t *li, qoff64_t offset, bool include_aix)
{
char magic[MAGICLEN];
qlseek(li, offset);
if ( qlread(li, magic, sizeof(magic)) != sizeof(magic) )
return false;
return memcmp(magic, ARMAG, SARMAG) == 0
|| memcmp(magic, ARMAGB, SARMAG) == 0
|| memcmp(magic, ARMAGE, SARMAG) == 0
|| include_aix
&& (memcmp(magic,AIAMAG,SAIAMAG) == 0
|| memcmp(magic,AIAMAGBIG,SAIAMAG) == 0);
}
//------------------------------------------------------------------------
static char *get_msft_module_name(
const char *ahname,
const char *ahend,
char *name,
size_t size)
{
if ( size == 0 )
return NULL;
char *ptr = name;
for ( size_t i=0; ahname < ahend; i++,ptr++ )
{
char chr = *ahname++;
if ( chr == '\n' || i == size-1 )
chr = '\0';
*ptr = chr;
if ( chr == '\0' )
break;
}
while ( ptr > name && qisspace(ptr[-1]) )
*--ptr = '\0';
if ( ptr > name && ptr[-1] == '/' )
ptr[-1] = '\0';
return name;
}
//------------------------------------------------------------------------
static const char *get_ar_modname(
const char *lnames,
const char *end,
const char *name,
char *buf,
size_t bufsize)
{
if ( lnames != NULL && *name == '/' )
{
name++;
size_t off = size_t(atol(name));
while ( qisdigit(*name) )
name++;
if ( *name == '\0' )
{
if ( lnames+off < lnames )
return qstrncpy(buf, "?", bufsize);
return get_msft_module_name(lnames+off, end, buf, bufsize);
}
}
return qstrncpy(buf, name, bufsize);
}
//------------------------------------------------------------------------
// return codes:
// 0: ok
// 1: no input file
// 2: read error
// 3: bad archive
// 4: not enough memory
// 5: maxpos reached
struct ar_visitor_t
{
virtual ssize_t idaapi visit_ar_module(
qoff64_t offset,
ar_hdr *ah,
uint64 size,
char *filename) = 0;
};
// Enumerate modules in AR archive.
// \param maxpos Max position in file. Typically is used when
// archive is embedded within other file.
ssize_t enum_ar_contents(linput_t *li, ar_visitor_t &av, int32 maxpos = -1)
{
ssize_t code = 0;
char *names = NULL;
size_t names_size = 0;
while ( true )
{
ar_hdr ah;
qoff64_t filepos = qltell(li);
if ( filepos & 1 )
qlseek(li, filepos+1);
if ( maxpos > -1 && filepos >= maxpos )
{
code = 5;
break;
}
ssize_t bytes = qlread(li, &ah, sizeof(ah));
if ( bytes == 0 )
break; // end of archive, no error
if ( bytes != sizeof(ah) )
{
code = 2; // read error
break;
}
if ( memcmp(ah.ar_fmag, ARFMAG, sizeof(ah.ar_fmag)) != 0 )
{
code = 3; // bad archive
break;
}
char name[sizeof(ah.ar_name)+1];
get_msft_module_name(ah.ar_name, ah.ar_name+sizeof(ah.ar_name), name, sizeof(name));
uint64 size = qatoll(ah.ar_size);
filepos = qltell(li);
if ( names == NULL && name[0] == '/' && name[1] == '\0' )
{
if ( size != 0 )
{
names = (char *)qalloc(size);
if ( names == NULL )
{
code = 4; // not enough memory
break;
}
names_size = size;
if ( qlread(li, names, size) != size )
{
code = 2; // read error
break;
}
}
continue;
}
if ( memcmp(name, AR_EFMT1, 3) == 0 )
{
// BSD/Apple archive: the length of long name follows
// #1/nnn
size_t extralen = size_t(atol(name+3));
char *modname = (char *)qalloc(extralen+1);
if ( modname == NULL )
{
code = 4; // not enough memory
break;
}
if ( qlread(li, modname, extralen) != extralen )
{
code = 2; // read error
break;
}
modname[extralen]='\0';
// skip special files
if ( !strneq(modname, "__.SYMDEF", sizeof("__.SYMDEF")-1) )
{
code = av.visit_ar_module(qoff64_t(filepos+extralen), &ah, size-extralen, modname);
if ( code != 0 )
break;
}
qfree(modname);
}
else if ( name[0] != '\0' )
{
char modname[MAXSTR];
get_ar_modname(names, names+names_size, name, modname, sizeof(modname));
code = av.visit_ar_module(filepos, &ah, size, modname);
if ( code != 0 )
break;
}
qlseek(li, qoff64_t(filepos+size));
}
qfree(names);
return code;
}
//--------------------------------------------------------------------------
// convert small archive header to big one
bool upgrade_aix_fl_hdr(fl_hdr *fh, const fl_hdr_small *fh_small)
{
if ( memcmp(fh_small->fl_magic, AIAMAG, SAIAMAG) != 0 )
return false; // not small archive
qstrncpy(fh->fl_memoff, fh_small->fl_memoff, sizeof(fh->fl_memoff));
qstrncpy(fh->fl_gstoff, fh_small->fl_gstoff, sizeof(fh->fl_gstoff));
fh->fl_gst64off[0] = '\0';
qstrncpy(fh->fl_fstmoff, fh_small->fl_fstmoff, sizeof(fh->fl_fstmoff));
qstrncpy(fh->fl_lstmoff, fh_small->fl_lstmoff, sizeof(fh->fl_lstmoff));
qstrncpy(fh->fl_freeoff, fh_small->fl_freeoff, sizeof(fh->fl_freeoff));
return true;
}
//--------------------------------------------------------------------------
bool read_aix_fl_hdr(fl_hdr *fh, linput_t *li)
{
size_t nread = qlread(li, fh, sizeof(*fh));
if ( nread == sizeof(*fh) && memcmp(fh->fl_magic, AIAMAGBIG, SAIAMAG) == 0 )
return true;
if ( nread < sizeof(fl_hdr_small) )
return false;
fl_hdr_small fh_small;
memcpy(&fh_small, fh, sizeof(fh_small)); //-V512 call of the 'memcpy' function will lead to underflow of the buffer
return upgrade_aix_fl_hdr(fh, &fh_small);
}
//--------------------------------------------------------------------------
// convert small member header to big one
void upgrade_aix_ar_hdr(aix_ar_hdr *ah, const aix_ar_hdr_small *ah_small)
{
qstrncpy(ah->ar_size, ah_small->ar_size, sizeof(ah->ar_size));
qstrncpy(ah->ar_nxtmem, ah_small->ar_nxtmem, sizeof(ah->ar_nxtmem));
qstrncpy(ah->ar_prvmem, ah_small->ar_prvmem, sizeof(ah->ar_prvmem));
qstrncpy(ah->ar_date, ah_small->ar_date, sizeof(ah->ar_date));
qstrncpy(ah->ar_uid, ah_small->ar_uid, sizeof(ah->ar_uid));
qstrncpy(ah->ar_gid, ah_small->ar_gid, sizeof(ah->ar_gid));
qstrncpy(ah->ar_mode, ah_small->ar_mode, sizeof(ah->ar_mode));
qstrncpy(ah->ar_namlen, ah_small->ar_namlen, sizeof(ah->ar_namlen));
}
//--------------------------------------------------------------------------
bool read_aix_ar_hdr(aix_ar_hdr *ah, const fl_hdr *fh, linput_t *li)
{
if ( memcmp(fh->fl_magic, AIAMAGBIG, SAIAMAG) == 0 )
return qlread(li, ah, sizeof(*ah)) == sizeof(*ah);
aix_ar_hdr_small ah_small;
if ( qlread(li, &ah_small, sizeof(ah_small)) != sizeof(ah_small) )
return false;
upgrade_aix_ar_hdr(ah, &ah_small);
return true;
}