/****************************************************************/
/* mdso: midipix dso scavenger */
/* Copyright (C) 2015--2017 Z. Gilboa */
/* Released under GPLv2 and GPLv3; see COPYING.MDSO. */
/****************************************************************/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <mdso/mdso.h>
#include "mdso_errinfo_impl.h"
#include "perk_structs.h"
static void mdso_argen_common_hdr(
struct pe_raw_archive_common_hdr * arhdr,
char * file_id,
size_t size)
{
size_t slen;
char sbuf[10];
memset(arhdr,0x20,sizeof(*arhdr));
slen = strlen(file_id);
memcpy(arhdr->ar_file_id,file_id,slen);
arhdr->ar_file_id[slen] = '/';
arhdr->ar_uid[0] = '0';
arhdr->ar_gid[0] = '0';
arhdr->ar_file_mode[0] = '0';
slen = sprintf(sbuf,"%zu",size);
memcpy(arhdr->ar_file_size,sbuf,slen);
arhdr->ar_end_tag[0] = 0x60;
arhdr->ar_end_tag[1] = 0x0a;
}
static void mdso_write_big_endian_long(unsigned char * ch, uint32_t val)
{
ch[3] = val;
ch[2] = val >> 8;
ch[1] = val >> 16;
ch[0] = val >> 24;
}
int mdso_argen_common(
const struct mdso_driver_ctx * dctx,
const char ** symv,
const int * stype,
struct mdso_object * vobj)
{
int ret;
const char ** psym;
int nsym;
int nobj;
int ndata;
uint32_t objlen;
uint32_t hdrlen;
uint32_t mapstrsnum;
uint32_t mapstrslen;
uint32_t symidx;
void * addr;
unsigned char * ar;
unsigned char * mark;
unsigned char * idx;
char * mapstrs;
struct pe_raw_archive_common_hdr * arhdr;
struct mdso_object * pobj;
struct mdso_object * aobj;
struct mdso_object sobj[256];
char objname[16];
/* init */
memset(sobj,0,sizeof(sobj));
for (nsym=0,ndata=0,psym=symv; *psym; psym++,nsym++)
ndata += (stype[psym-symv] == MDSO_SYMBOL_TYPE_DATA);
if ((nobj = 1 + (2*nsym) - ndata) < 256)
aobj = sobj;
else if (nobj > 1024*1024)
return MDSO_CUSTOM_ERROR(dctx,MDSO_ERR_INVALID_VECTOR);
else if (!(aobj = calloc(1,nobj*sizeof(*aobj))))
return MDSO_SYSTEM_ERROR(dctx);
/* objlen: archive signature, index header */
objlen = 8;
objlen += sizeof(struct pe_raw_archive_common_hdr);
/* objlen: member headers */
ret = mdso_objgen_dsometa(dctx,aobj);
aobj->size += 1;
aobj->size |= 1;
aobj->size ^= 1;
objlen += aobj->size;
objlen += sizeof(struct pe_raw_archive_common_hdr);
mapstrslen = aobj->mapstrslen;
mapstrsnum = aobj->mapstrsnum;
/* objlen: symfn, symentry */
for (psym=symv,pobj=&aobj[1]; *psym && !ret; psym++) {
if (stype[psym-symv] == MDSO_SYMBOL_TYPE_CODE) {
ret = mdso_objgen_symfn(dctx,*psym,pobj);
pobj->size += 1;
pobj->size |= 1;
pobj->size ^= 1;
objlen += pobj->size;
objlen += sizeof(struct pe_raw_archive_common_hdr);
mapstrslen += pobj->mapstrslen;
mapstrsnum += pobj->mapstrsnum;
pobj++;
}
ret |= mdso_objgen_symentry(dctx,*psym,pobj);
pobj->size += 1;
pobj->size |= 1;
pobj->size ^= 1;
objlen += pobj->size;
objlen += sizeof(struct pe_raw_archive_common_hdr);
mapstrslen += pobj->mapstrslen;
mapstrsnum += pobj->mapstrsnum;
pobj++;
}
/* verify logic */
if (ret && (aobj == sobj))
return MDSO_NESTED_ERROR(dctx);
if (ret) {
free(aobj);
return MDSO_NESTED_ERROR(dctx);
}
/* index: string block alignment */
mapstrslen += 1;
mapstrslen |= 1;
mapstrslen ^= 1;
/* objlen: index size, padding */
objlen += sizeof(uint32_t) * (1 + mapstrsnum);
objlen += mapstrslen;
objlen += 15;
objlen |= 15;
objlen ^= 15;
/* archive meta info, in-memory mapping */
if (vobj->addr && (vobj->size < objlen))
return MDSO_BUFFER_ERROR(dctx);
if ((addr = vobj->addr)) {
(void)0;
} else {
vobj->size = objlen;
vobj->mapstrslen = mapstrslen;
vobj->mapstrsnum = mapstrsnum;
if (!vobj->name)
return 0;
else if (mdso_create_archive(dctx,vobj) < 0)
return MDSO_NESTED_ERROR(dctx);
}
ar = (unsigned char *)vobj->addr;
/* archive signature */
memcpy(ar,"!<arch>\n",8);
mark = &ar[8];
/* archive header */
arhdr = (struct pe_raw_archive_common_hdr *)mark;
hdrlen = sizeof(uint32_t) * (1 + mapstrsnum) + mapstrslen;
mdso_argen_common_hdr(arhdr,"",hdrlen);
mark += sizeof(*arhdr);
/* archive index initialization */
mdso_write_big_endian_long(mark,mapstrsnum);
mark += sizeof(uint32_t);
idx = mark;
mark += sizeof(uint32_t) * mapstrsnum;
mapstrs = (char *)mark;
mark += mapstrslen;
/* .dsometa object */
aobj->mapstrs = mapstrs;
aobj->arhdrpos = (uint32_t)(mark - ar);
aobj->arhdrlen = sizeof(struct pe_raw_archive_common_hdr);
aobj->addr = &mark[aobj->arhdrlen];
for (symidx=0; symidx<aobj->mapstrsnum; symidx++) {
mdso_write_big_endian_long(idx,aobj->arhdrpos);
idx += sizeof(uint32_t);
}
ret = mdso_objgen_dsometa(dctx,aobj);
mdso_argen_common_hdr(
(struct pe_raw_archive_common_hdr *)mark,
".dsometa.o",aobj->size);
mark += aobj->arhdrlen + aobj->size;
mapstrs += aobj->mapstrslen;
/* archive symfn and symentry objects */
for (psym=symv,pobj=&aobj[1]; *psym && !ret; psym++) {
/* symfn object */
if (stype[psym-symv] == MDSO_SYMBOL_TYPE_CODE) {
pobj->mapstrs = mapstrs;
pobj->arhdrpos = (uint32_t)(mark - ar);
pobj->arhdrlen = sizeof(struct pe_raw_archive_common_hdr);
pobj->addr = &mark[pobj->arhdrlen];
for (symidx=0; symidx<pobj->mapstrsnum; symidx++) {
mdso_write_big_endian_long(idx,pobj->arhdrpos);
idx += sizeof(uint32_t);
}
ret = mdso_objgen_symfn(dctx,*psym,pobj);
sprintf(
objname,"f%06zu.o",
psym - symv);
mdso_argen_common_hdr(
(struct pe_raw_archive_common_hdr *)mark,
objname,pobj->size);
mark += pobj->arhdrlen + pobj->size;
mapstrs += pobj->mapstrslen;
pobj++;
}
/* symentry object */
pobj->mapstrs = mapstrs;
pobj->arhdrpos = (uint32_t)(mark - ar);
pobj->arhdrlen = sizeof(struct pe_raw_archive_common_hdr);
pobj->addr = &mark[pobj->arhdrlen];
for (symidx=0; symidx<pobj->mapstrsnum; symidx++) {
mdso_write_big_endian_long(idx,pobj->arhdrpos);
idx += sizeof(uint32_t);
}
sprintf(
objname,"s%06zu.o",
psym - symv);
ret = mdso_objgen_symentry(dctx,*psym,pobj);
mdso_argen_common_hdr(
(struct pe_raw_archive_common_hdr *)mark,
objname,pobj->size);
mark += pobj->arhdrlen + pobj->size;
mapstrs += pobj->mapstrslen;
pobj++;
}
/* aobj */
if (aobj != sobj)
free(aobj);
/* fs object unmap */
if (!addr)
munmap(vobj->addr,vobj->size);
/* verify */
if (ret)
return MDSO_NESTED_ERROR(dctx);
/* tada */
return 0;
}