#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <errno.h>
#include <libelf.h>
#include <stdio.h>
#include <strings.h>
#include <msg.h>
#include <machdep.h>
#include <_libelf.h>
#include <_elfwrap.h>
#if defined(_ELF64)
#define input input64
#define output output64
#else
#define input input32
#define output output32
#endif
static StdSec_t StdSecs[] = {
{ MSG_ORIG(MSG_SCN_SYMTAB), SHT_SYMTAB, 0 },
{ MSG_ORIG(MSG_SCN_STRTAB), SHT_STRTAB, SHF_STRINGS},
{ MSG_ORIG(MSG_SCN_SHSTRTAB), SHT_STRTAB, SHF_STRINGS},
{ NULL, 0, 0 }
};
int
input(int argc, char **argv, const char *prog, const char *ofile,
ObjDesc_t *odp)
{
OutSec_t outsec;
StdSec_t *stdsecs;
size_t ndx, cnt;
int ret = 0, fd = -1;
for (ndx = 1; argc; argc--, argv++, ndx++) {
char *file = *argv;
struct stat status;
size_t namesz;
if (fd != -1)
(void) close(fd);
outsec.os_name = basename(file);
outsec.os_type = SHT_PROGBITS;
outsec.os_flags = SHF_ALLOC;
outsec.os_ndx = ndx;
if ((fd = open(file, O_RDONLY)) == -1) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
prog, file, strerror(err));
ret = 1;
continue;
}
if (fstat(fd, &status) == -1) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_FSTAT),
prog, file, strerror(err));
ret = 1;
continue;
}
if ((outsec.os_size = status.st_size) == 0) {
(void) fprintf(stderr, MSG_INTL(MSG_WARN_ZERO),
prog, file);
continue;
}
if ((outsec.os_addr = mmap(0, outsec.os_size, PROT_READ,
MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_MMAP),
prog, file, strerror(err));
ret = 1;
continue;
}
if (alist_append(&(odp->od_outsecs), &outsec, sizeof (OutSec_t),
AL_CNT_WOSECS) == 0) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_ALLOC),
prog, file, strerror(err));
return (1);
}
namesz = strlen(outsec.os_name) + 1;
odp->od_symtabno += 3;
odp->od_strtabsz += (namesz + MSG_STR_START_SIZE);
odp->od_strtabsz += (namesz + MSG_STR_END_SIZE);
odp->od_shstrtabsz += (namesz + MSG_STR_DOT_SIZE);
}
if (fd != -1)
(void) close(fd);
if (ret || (odp->od_outsecs == NULL))
return (1);
for (cnt = 0, stdsecs = &StdSecs[cnt]; stdsecs->ss_name; cnt++,
ndx++, stdsecs = &StdSecs[cnt]) {
outsec.os_name = stdsecs->ss_name;
outsec.os_type = stdsecs->ss_type;
outsec.os_flags = stdsecs->ss_flags;
outsec.os_ndx = ndx;
outsec.os_size = 0;
outsec.os_addr = 0;
if (alist_append(&(odp->od_outsecs), &outsec, sizeof (OutSec_t),
AL_CNT_WOSECS) == 0) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_ALLOC),
prog, outsec.os_name, strerror(err));
return (1);
}
odp->od_symtabno++;
odp->od_shstrtabsz += (strlen(outsec.os_name) + 1);
}
odp->od_symtabno += 2;
odp->od_strtabsz += strlen(ofile) + 2;
odp->od_shstrtabsz++;
return (0);
}
int
output(const char *prog, int fd, const char *ofile, ushort_t mach,
ObjDesc_t *odp)
{
Aliste off;
Elf *melf, *oelf;
Ehdr *ehdr;
Sym *symtab, *secsymtabent, *glbsymtabent;
char *strtab, *strtabent, *shstrtab, *shstrtabent;
OutSec_t *outsec, *outsymtab, *outstrtab, *outshstrtab;
size_t len;
TargDesc_t tdesc;
if (mach == 0)
mach = M_MACH;
switch (mach) {
#if !defined(lint)
case EM_SPARC:
target_init_sparc(&tdesc);
break;
case EM_SPARCV9:
target_init_sparcv9(&tdesc);
break;
case EM_386:
target_init_i386(&tdesc);
break;
case EM_AMD64:
target_init_amd64(&tdesc);
break;
#else
default:
target_init(&tdesc);
break;
#endif
}
if ((oelf = elf_begin(fd, ELF_C_WRITE, 0)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), prog,
elf_errmsg(elf_errno()));
return (1);
}
if ((ehdr = elf_newehdr(oelf)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWEHDR), prog,
elf_errmsg(elf_errno()));
return (1);
}
ehdr->e_ident[EI_DATA] = M_DATA;
ehdr->e_type = ET_REL;
ehdr->e_version = EV_CURRENT;
for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) {
Elf_Scn *scn;
Elf_Data *data;
Shdr *shdr;
if ((scn = elf_newscn(oelf)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWSCN),
prog, outsec->os_name, elf_errmsg(elf_errno()));
return (1);
}
if ((shdr = elf_getshdr(scn)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSHDR),
prog, outsec->os_name, elf_errmsg(elf_errno()));
return (1);
}
shdr->sh_type = outsec->os_type;
shdr->sh_flags = outsec->os_flags;
if ((data = elf_newdata(scn)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWDATA),
prog, outsec->os_name, elf_errmsg(elf_errno()));
return (1);
}
switch (shdr->sh_type) {
case SHT_PROGBITS:
data->d_buf = outsec->os_addr;
data->d_type = ELF_T_BYTE;
data->d_size = outsec->os_size;
data->d_align = tdesc.td_align;
break;
case SHT_SYMTAB:
data->d_buf = 0;
data->d_type = ELF_T_SYM;
data->d_size = (odp->od_symtabno * tdesc.td_symsz);
data->d_align = tdesc.td_align;
break;
case SHT_STRTAB:
data->d_buf = 0;
data->d_type = ELF_T_BYTE;
if (strcmp(outsec->os_name, MSG_ORIG(MSG_SCN_STRTAB)))
data->d_size = odp->od_shstrtabsz;
else
data->d_size = odp->od_strtabsz;
data->d_align = 1;
break;
}
}
if ((elf_update(oelf, ELF_C_WRIMAGE)) == -1) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_UPDATE), prog,
elf_errmsg(elf_errno()));
return (1);
}
if ((melf = elf_begin(0, ELF_C_IMAGE, oelf)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), prog,
elf_errmsg(elf_errno()));
return (1);
}
if ((ehdr = elf_getehdr(melf)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETEHDR), prog,
elf_errmsg(elf_errno()));
return (1);
}
for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) {
Elf_Scn *scn;
Shdr *shdr;
if ((scn = elf_getscn(melf, outsec->os_ndx)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSCN),
prog, outsec->os_name, elf_errmsg(elf_errno()));
return (1);
}
if ((outsec->os_shdr = shdr = elf_getshdr(scn)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSHDR),
prog, outsec->os_name, elf_errmsg(elf_errno()));
return (1);
}
if ((outsec->os_data = elf_getdata(scn, NULL)) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETDATA),
prog, outsec->os_name, elf_errmsg(elf_errno()));
return (1);
}
if (shdr->sh_type == SHT_PROGBITS)
continue;
if (shdr->sh_type == SHT_SYMTAB) {
outsymtab = outsec;
symtab = (Sym *)outsec->os_data->d_buf;
} else if (shdr->sh_type == SHT_STRTAB) {
if (strcmp(outsec->os_name, MSG_ORIG(MSG_SCN_STRTAB))) {
outshstrtab = outsec;
shstrtab = (char *)outsec->os_data->d_buf;
} else {
outstrtab = outsec;
strtab = (char *)outsec->os_data->d_buf;
}
}
}
ehdr->e_shstrndx = outshstrtab->os_ndx;
strtabent = strtab;
strtabent++;
shstrtabent = shstrtab;
shstrtabent++;
secsymtabent = symtab;
secsymtabent++;
secsymtabent->st_name = (strtabent - strtab);
secsymtabent->st_info = ELF_ST_INFO(STB_LOCAL, STT_NOTYPE);
secsymtabent->st_shndx = SHN_ABS;
secsymtabent++;
glbsymtabent = secsymtabent;
glbsymtabent += alist_nitems(odp->od_outsecs);
outsymtab->os_shdr->sh_link = outstrtab->os_ndx;
len = strlen(ofile) + 1;
(void) memcpy(strtabent, ofile, len);
strtabent += len;
for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) {
size_t alen;
secsymtabent->st_info = ELF_ST_INFO(STB_LOCAL, STT_SECTION);
secsymtabent->st_shndx = outsec->os_ndx;
secsymtabent++;
outsec->os_shdr->sh_name = (shstrtabent - shstrtab);
if (outsec->os_shdr->sh_type == SHT_PROGBITS) {
(void) memcpy(shstrtabent, MSG_ORIG(MSG_STR_DOT),
MSG_STR_DOT_SIZE);
shstrtabent += MSG_STR_DOT_SIZE;
}
len = strlen(outsec->os_name) + 1;
(void) memcpy(shstrtabent, outsec->os_name, len);
shstrtabent += len;
if (outsec->os_shdr->sh_type != SHT_PROGBITS)
continue;
glbsymtabent->st_name = (strtabent - strtab);
glbsymtabent->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
glbsymtabent->st_shndx = outsec->os_ndx;
glbsymtabent->st_size = outsec->os_shdr->sh_size;
glbsymtabent++;
len--;
(void) memcpy(strtabent, outsec->os_name, len);
strtabent += len;
alen = (MSG_STR_START_SIZE + 1);
(void) memcpy(strtabent, MSG_ORIG(MSG_STR_START), alen);
strtabent += alen;
glbsymtabent->st_name = (strtabent - strtab);
glbsymtabent->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
glbsymtabent->st_shndx = outsec->os_ndx;
glbsymtabent->st_value = outsec->os_shdr->sh_size;
glbsymtabent++;
(void) memcpy(strtabent, outsec->os_name, len);
strtabent += len;
alen = (MSG_STR_END_SIZE + 1);
(void) memcpy(strtabent, MSG_ORIG(MSG_STR_END), alen);
strtabent += alen;
}
outsymtab->os_shdr->sh_info = (secsymtabent - symtab);
ehdr->e_ident[EI_CLASS] = tdesc.td_class;
ehdr->e_ident[EI_DATA] = tdesc.td_data;
ehdr->e_machine = tdesc.td_mach;
if ((_elf_sys_encoding() != ehdr->e_ident[EI_DATA]) &&
(_elf_swap_wrimage(melf) != 0)) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_SWAP_WRIMAGE), prog,
elf_errmsg(elf_errno()));
return (1);
}
(void) elf_end(melf);
if ((elf_update(oelf, ELF_C_WRITE)) == -1) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_UPDATE), prog,
elf_errmsg(elf_errno()));
return (1);
}
(void) elf_end(oelf);
return (0);
}