rpm/libelf/cook.c

240 lines
5.8 KiB
C
Executable File

/*
cook.c - read and translate ELF files.
Copyright (C) 1995 Michael Riepe <riepe@ifwsn4.ifw.uni-hannover.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <private.h>
const Elf_Scn _elf_scn_init = INIT_SCN;
const Scn_Data _elf_data_init = INIT_DATA;
const Elf_Type _elf_scn_types[SHT_NUM] = {
/* SHT_NULL */ ELF_T_BYTE,
/* SHT_PROGBITS */ ELF_T_BYTE,
/* SHT_SYMTAB */ ELF_T_SYM,
/* SHT_STRTAB */ ELF_T_BYTE,
/* SHT_RELA */ ELF_T_RELA,
/* SHT_HASH */ ELF_T_WORD,
/* SHT_DYNAMIC */ ELF_T_DYN,
/* SHT_NOTE */ ELF_T_BYTE,
/* SHT_NOBITS */ ELF_T_BYTE,
/* SHT_REL */ ELF_T_REL,
/* SHT_SHLIB */ ELF_T_BYTE,
/* SHT_DYNSYM */ ELF_T_SYM
};
#define truncerr(t) ((t)==ELF_T_EHDR?ERROR_TRUNC_EHDR: \
((t)==ELF_T_PHDR?ERROR_TRUNC_PHDR: \
((t)==ELF_T_SHDR?ERROR_TRUNC_SHDR: \
ERROR_INTERNAL)))
#define memerr(t) ((t)==ELF_T_EHDR?ERROR_MEM_EHDR: \
((t)==ELF_T_PHDR?ERROR_MEM_PHDR: \
((t)==ELF_T_SHDR?ERROR_MEM_SHDR: \
ERROR_INTERNAL)))
static char*
_elf32_item(Elf *elf, Elf_Type type, unsigned n, size_t off, int *flag) {
Elf_Data src, dst;
*flag = 0;
elf_assert(n);
elf_assert(valid_type(type));
if (off < 0 || off > elf->e_size) {
seterr(ERROR_OUTSIDE);
return NULL;
}
src.d_type = type;
src.d_version = elf->e_version;
src.d_size = n * _fsize32(src.d_version, type);
elf_assert(src.d_size);
if (off + src.d_size > elf->e_size) {
seterr(truncerr(type));
return NULL;
}
dst.d_version = _elf_version;
dst.d_size = n * _msize32(dst.d_version, type);
elf_assert(dst.d_size);
elf_assert(elf->e_data);
if (elf->e_rawdata != elf->e_data && dst.d_size <= src.d_size) {
dst.d_buf = elf->e_data + off;
}
else if (!(dst.d_buf = malloc(dst.d_size))) {
seterr(memerr(type));
return NULL;
}
else {
*flag |= 1;
}
if (elf->e_rawdata) {
src.d_buf = elf->e_rawdata + off;
}
else {
if (src.d_size <= dst.d_size) {
src.d_buf = dst.d_buf;
}
else if (!(src.d_buf = malloc(src.d_size))) {
seterr(ERROR_IO_2BIG);
goto fail;
}
else {
*flag |= 2;
}
if (!_elf_read(elf, src.d_buf, off, src.d_size)) {
goto fail;
}
}
if (elf32_xlatetom(&dst, &src, elf->e_encoding)) {
if (*flag & 2) {
free(src.d_buf);
}
if (!(*flag &= 1)) {
elf->e_cooked = 1;
}
return (char*)dst.d_buf;
}
fail:
if (*flag & 2) {
free(src.d_buf);
}
if (*flag & 1) {
free(dst.d_buf);
}
*flag = 0;
return NULL;
}
#undef truncerr
#undef memerr
static int
_elf32_cook(Elf *elf) {
Elf_Scn *head, *scn;
Elf32_Ehdr *ehdr;
Elf32_Shdr *shdr;
Scn_Data *data;
Scn_Data *sd;
unsigned i;
int flag;
elf->e_ehdr = _elf32_item(elf, ELF_T_EHDR, 1, 0, &flag);
if (!(ehdr = (Elf32_Ehdr*)elf->e_ehdr)) {
return 0;
}
if (flag) {
elf->e_free_ehdr = 1;
}
if (ehdr->e_phnum && ehdr->e_phoff) {
elf->e_phdr = _elf32_item(elf, ELF_T_PHDR, ehdr->e_phnum, ehdr->e_phoff, &flag);
if (!elf->e_phdr) {
return 0;
}
if (flag) {
elf->e_free_phdr = 1;
}
elf->e_phnum = ehdr->e_phnum;
}
if (ehdr->e_shnum && ehdr->e_shoff) {
unsigned tmp = _elf_version;
elf_assert(_msize32(_elf_version, ELF_T_SHDR) <= sizeof(*shdr));
_elf_version = EV_CURRENT;
elf_assert(_msize32(_elf_version, ELF_T_SHDR) == sizeof(*shdr));
elf->e_shdr = _elf32_item(elf, ELF_T_SHDR, ehdr->e_shnum, ehdr->e_shoff, &flag);
_elf_version = tmp;
if (!elf->e_shdr) {
return 0;
}
if (flag) {
elf->e_free_shdr = 1;
}
head = (Elf_Scn*)malloc(ehdr->e_shnum * (sizeof(*head) + sizeof(*data)));
if (!head) {
seterr(ERROR_MEM_SCN);
return 0;
}
data = (Scn_Data*)(head + ehdr->e_shnum);
for (scn = NULL, i = ehdr->e_shnum; i-- > 0; ) {
head[i] = _elf_scn_init;
head[i].s_link = scn;
if (!scn) {
elf->e_scn_n = head + i;
}
shdr = (Elf32_Shdr*)elf->e_shdr + i;
scn = head + i;
sd = data + i;
scn->s_elf = elf;
scn->s_shdr = (char*)shdr;
scn->s_index = i;
scn->s_data_1 = sd;
scn->s_data_n = sd;
scn->s_type = shdr->sh_type;
scn->s_size = shdr->sh_size;
scn->s_offset = shdr->sh_offset;
*sd = _elf_data_init;
sd->sd_scn = scn;
if (valid_scntype(shdr->sh_type)) {
sd->sd_data.d_type = _elf_scn_types[shdr->sh_type];
}
else {
sd->sd_data.d_type = ELF_T_BYTE;
}
sd->sd_data.d_size = shdr->sh_size;
sd->sd_data.d_align = shdr->sh_addralign;
sd->sd_data.d_version = _elf_version;
}
elf->e_scn_1 = head;
head->s_freeme = 1;
}
return 1;
}
int
_elf_cook(Elf *elf) {
elf_assert(_elf_scn_init.s_magic == SCN_MAGIC);
elf_assert(_elf_data_init.sd_magic == DATA_MAGIC);
elf_assert(elf);
elf_assert(elf->e_magic == ELF_MAGIC);
elf_assert(elf->e_kind == ELF_K_ELF);
elf_assert(!elf->e_ehdr);
if (!valid_version(elf->e_version)) {
seterr(ERROR_UNKNOWN_VERSION);
}
else if (!valid_encoding(elf->e_encoding)) {
seterr(ERROR_UNKNOWN_ENCODING);
}
else if (elf->e_class == ELFCLASS32) {
return _elf32_cook(elf);
}
else if (valid_class(elf->e_class)) {
seterr(ERROR_UNIMPLEMENTED);
}
else {
seterr(ERROR_UNKNOWN_CLASS);
}
return 0;
}