diff options
Diffstat (limited to 'trunk/src/data.c')
-rw-r--r-- | trunk/src/data.c | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/trunk/src/data.c b/trunk/src/data.c new file mode 100644 index 0000000..13380da --- /dev/null +++ b/trunk/src/data.c @@ -0,0 +1,339 @@ +/* Copyright (C) 2001, 2002 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include "prelink.h" + +#define UREAD(le,nn) \ +uint##nn##_t \ +read_u##le##nn (DSO *dso, GElf_Addr addr) \ +{ \ + Elf_Type type; \ + unsigned char *data = get_data (dso, addr, NULL, &type); \ + \ + if (data == NULL) \ + return 0; \ + \ + if (type == ELF_T_BYTE) \ + return buf_read_u##le##nn (data); \ + else \ + return *(uint##nn##_t *)data; \ +} + +#define WRITE(le,nn) \ +int \ +write_##le##nn (DSO *dso, GElf_Addr addr, uint##nn##_t val) \ +{ \ + int sec; \ + Elf_Type type; \ + unsigned char *data = get_data (dso, addr, &sec, &type); \ + \ + if (data == NULL) \ + return -1; \ + \ + if (type == ELF_T_BYTE) \ + buf_write_##le##nn (data, val); \ + else \ + *(uint##nn##_t *)data = val; \ + elf_flagscn (dso->scn[sec], ELF_C_SET, ELF_F_DIRTY); \ + return 0; \ +} + +#define BUFREADUNE(nn) \ +uint##nn##_t \ +buf_read_une##nn (DSO *dso, unsigned char *buf) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + return buf_read_ule32 (buf); \ + else \ + return buf_read_ube32 (buf); \ +} + +#define READUNE(nn) \ +uint##nn##_t \ +read_une##nn (DSO *dso, GElf_Addr addr) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + return read_ule##nn (dso, addr); \ + else \ + return read_ube##nn (dso, addr); \ +} + +#define WRITENE(nn) \ +void \ +write_ne##nn (DSO *dso, GElf_Addr addr, uint##nn##_t val) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + write_le##nn (dso, addr, val); \ + else \ + write_be##nn (dso, addr, val); \ +} + +#define BUFWRITENE(nn) \ +void \ +buf_write_ne##nn (DSO *dso, unsigned char *buf, \ + uint##nn##_t val) \ +{ \ + if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) \ + buf_write_le##nn (buf, val); \ + else \ + buf_write_be##nn (buf, val); \ +} + +#define READWRITE(le,nn) UREAD(le,nn) WRITE(le,nn) +#define READWRITESIZE(nn) \ + READWRITE(le,nn) READWRITE(be,nn) \ + BUFREADUNE(nn) READUNE(nn) \ + WRITENE(nn) BUFWRITENE(nn) + +unsigned char * +get_data (DSO *dso, GElf_Addr addr, int *secp, Elf_Type *typep) +{ + int sec = addr_to_sec (dso, addr); + Elf_Data *data = NULL; + + if (sec == -1) + return NULL; + + if (secp) + *secp = sec; + + addr -= dso->shdr[sec].sh_addr; + while ((data = elf_getdata (dso->scn[sec], data)) != NULL) + if (data->d_off <= addr && data->d_off + data->d_size > addr) + { + if (typep) *typep = data->d_type; + return (unsigned char *) data->d_buf + (addr - data->d_off); + } + return NULL; +} + +/* Initialize IT so that the first byte it provides is address ADDR + of DSO. */ + +void +init_data_iterator (struct data_iterator *it, DSO *dso, GElf_Addr addr) +{ + it->dso = dso; + it->data = NULL; + it->addr = addr; +} + +/* Return a pointer to the next SIZE bytes pointed to by IT, and move + IT to the end of the returned block. Return null if the data could + not be read for some reason. */ + +unsigned char * +get_data_from_iterator (struct data_iterator *it, GElf_Addr size) +{ + unsigned char *ptr; + + /* If we're at the end of a data block, move onto the next. */ + if (it->data && it->data->d_off + it->data->d_size == it->sec_offset) + it->data = elf_getdata (it->dso->scn[it->sec], it->data); + + if (it->data == NULL) + { + /* Find out which section contains the next byte. */ + it->sec = addr_to_sec (it->dso, it->addr); + if (it->sec < 0) + return NULL; + + /* Fast-forward to the block that contains ADDR, if any. */ + it->sec_offset = it->addr - it->dso->shdr[it->sec].sh_addr; + do + it->data = elf_getdata (it->dso->scn[it->sec], it->data); + while (it->data && it->data->d_off + it->data->d_size <= it->sec_offset); + } + + /* Make sure that all the data we want is included in this block. */ + if (it->data == NULL + || it->data->d_off > it->sec_offset + || it->data->d_off + it->data->d_size < it->sec_offset + size) + return NULL; + + ptr = (unsigned char *) it->data->d_buf + (it->sec_offset - it->data->d_off); + it->sec_offset += size; + it->addr += size; + return ptr; +} + +/* Read the symbol pointed to by IT into SYM and move IT onto the + next symbol. Return true on success. */ + +int +get_sym_from_iterator (struct data_iterator *it, GElf_Sym *sym) +{ + GElf_Addr offset, size; + unsigned char *ptr; + + size = gelf_fsize (it->dso->elf, ELF_T_SYM, 1, EV_CURRENT); + ptr = get_data_from_iterator (it, size); + if (ptr != NULL) + { + offset = ptr - (unsigned char *) it->data->d_buf; + if (offset % size == 0) + { + gelfx_getsym (it->dso->elf, it->data, offset / size, sym); + return 1; + } + } + return 0; +} + +inline uint8_t +buf_read_u8 (unsigned char *data) +{ + return *data; +} + +inline uint16_t +buf_read_ule16 (unsigned char *data) +{ + return data[0] | (data[1] << 8); +} + +inline uint16_t +buf_read_ube16 (unsigned char *data) +{ + return data[1] | (data[0] << 8); +} + +inline uint32_t +buf_read_ule32 (unsigned char *data) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + +inline uint32_t +buf_read_ube32 (unsigned char *data) +{ + return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); +} + +inline uint64_t +buf_read_ule64 (unsigned char *data) +{ + return (data[0] | (data[1] << 8) | (data[2] << 16)) + | (((uint64_t)data[3]) << 24) + | (((uint64_t)data[4]) << 32) + | (((uint64_t)data[5]) << 40) + | (((uint64_t)data[6]) << 48) + | (((uint64_t)data[7]) << 56); +} + +inline uint64_t +buf_read_ube64 (unsigned char *data) +{ + return (data[7] | (data[6] << 8) | (data[5] << 16)) + | (((uint64_t)data[4]) << 24) + | (((uint64_t)data[3]) << 32) + | (((uint64_t)data[2]) << 40) + | (((uint64_t)data[1]) << 48) + | (((uint64_t)data[0]) << 56); +} + +inline void +buf_write_8 (unsigned char *data, uint8_t val) +{ + *data = val; +} + +inline void +buf_write_le16 (unsigned char *data, uint16_t val) +{ + data[0] = val; + data[1] = val >> 8; +} + +inline void +buf_write_be16 (unsigned char *data, uint16_t val) +{ + data[1] = val; + data[0] = val >> 8; +} + +inline void +buf_write_le32 (unsigned char *data, uint32_t val) +{ + data[0] = val; + data[1] = val >> 8; + data[2] = val >> 16; + data[3] = val >> 24; +} + +inline void +buf_write_be32 (unsigned char *data, uint32_t val) +{ + data[3] = val; + data[2] = val >> 8; + data[1] = val >> 16; + data[0] = val >> 24; +} + +inline void +buf_write_le64 (unsigned char *data, uint64_t val) +{ + data[0] = val; + data[1] = val >> 8; + data[2] = val >> 16; + data[3] = val >> 24; + data[4] = val >> 32; + data[5] = val >> 40; + data[6] = val >> 48; + data[7] = val >> 56; +} + +inline void +buf_write_be64 (unsigned char *data, uint64_t val) +{ + data[7] = val; + data[6] = val >> 8; + data[5] = val >> 16; + data[4] = val >> 24; + data[3] = val >> 32; + data[2] = val >> 40; + data[1] = val >> 48; + data[0] = val >> 56; +} + +READWRITE(,8) +READWRITESIZE(16) +READWRITESIZE(32) +READWRITESIZE(64) + +const char * +strptr (DSO *dso, int sec, off_t offset) +{ + Elf_Scn *scn; + Elf_Data *data; + + scn = dso->scn[sec]; + if (offset >= 0 && offset < dso->shdr[sec].sh_size) + { + data = NULL; + while ((data = elf_getdata (scn, data)) != NULL) + { + if (data->d_buf + && offset >= data->d_off + && offset < data->d_off + data->d_size) + return (const char *) data->d_buf + (offset - data->d_off); + } + } + + return NULL; +} |