summaryrefslogtreecommitdiffstats
path: root/trunk/src/mdebug.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/mdebug.c')
-rw-r--r--trunk/src/mdebug.c692
1 files changed, 692 insertions, 0 deletions
diff --git a/trunk/src/mdebug.c b/trunk/src/mdebug.c
new file mode 100644
index 0000000..4c22d2f
--- /dev/null
+++ b/trunk/src/mdebug.c
@@ -0,0 +1,692 @@
+/* Copyright (C) 2001 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 <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <error.h>
+#include <stddef.h>
+
+#include "prelink.h"
+
+#define F8(x) unsigned char x[1];
+#define F16(x) unsigned char x[2];
+#define F24(x) unsigned char x[3];
+#define F32(x) unsigned char x[4];
+#define F64(x) unsigned char x[8];
+
+typedef struct
+{
+ F16(magic)
+ F16(vstamp)
+ F32(ilineMax)
+ F32(cbLine)
+ F32(cbLineOffset)
+ F32(idnMax)
+ F32(cbDnOffset)
+ F32(ipdMax)
+ F32(cbPdOffset)
+ F32(isymMax)
+ F32(cbSymOffset)
+ F32(ioptMax)
+ F32(cbOptOffset)
+ F32(iauxMax)
+ F32(cbAuxOffset)
+ F32(issMax)
+ F32(cbSsOffset)
+ F32(issExtMax)
+ F32(cbSsExtOffset)
+ F32(ifdMax)
+ F32(cbFdOffset)
+ F32(crfd)
+ F32(cbRfdOffset)
+ F32(iextMax)
+ F32(cbExtOffset)
+} mdebug_hdr_32;
+
+typedef struct
+{
+ F16(magic)
+ F16(vstamp)
+ F32(ilineMax)
+ F32(idnMax)
+ F32(ipdMax)
+ F32(isymMax)
+ F32(ioptMax)
+ F32(iauxMax)
+ F32(issMax)
+ F32(issExtMax)
+ F32(ifdMax)
+ F32(crfd)
+ F32(iextMax)
+ F64(cbLine)
+ F64(cbLineOffset)
+ F64(cbDnOffset)
+ F64(cbPdOffset)
+ F64(cbSymOffset)
+ F64(cbOptOffset)
+ F64(cbAuxOffset)
+ F64(cbSsOffset)
+ F64(cbSsExtOffset)
+ F64(cbFdOffset)
+ F64(cbRfdOffset)
+ F64(cbExtOffset)
+} mdebug_hdr_64;
+
+typedef struct
+{
+ F32(adr)
+ F32(rss)
+ F32(issBase)
+ F32(cbSs)
+ F32(isymBase)
+ F32(csym)
+ F32(ilineBase)
+ F32(cline)
+ F32(ioptBase)
+ F32(copt)
+ F16(ipdFirst)
+ F16(cpd)
+ F32(iauxBase)
+ F32(caux)
+ F32(rfdBase)
+ F32(crfd)
+ F8(bits1)
+ F24(bits2)
+ F32(cbLineOffset)
+ F32(cbLine)
+} mdebug_fdr_32;
+
+typedef struct
+{
+ F64(adr)
+ F64(cbLineOffset)
+ F64(cbLine)
+ F64(cbSs)
+ F32(rss)
+ F32(issBase)
+ F32(isymBase)
+ F32(csym)
+ F32(ilineBase)
+ F32(cline)
+ F32(ioptBase)
+ F32(copt)
+ F32(ipdFirst)
+ F32(cpd)
+ F32(iauxBase)
+ F32(caux)
+ F32(rfdBase)
+ F32(crfd)
+ F8(bits1)
+ F24(bits2)
+ F32(padding)
+} mdebug_fdr_64;
+
+typedef struct
+{
+ F32(iss)
+ F32(value)
+ F8(bits1)
+ F8(bits2)
+ F8(bits3)
+ F8(bits4)
+} mdebug_sym_32;
+
+typedef struct
+{
+ F64(value)
+ F32(iss)
+ F8(bits1)
+ F8(bits2)
+ F8(bits3)
+ F8(bits4)
+} mdebug_sym_64;
+
+typedef struct
+{
+ F8(bits1)
+ F8(bits2)
+ F16(fd)
+ mdebug_sym_32 asym;
+} mdebug_ext_32;
+
+typedef struct
+{
+ mdebug_sym_64 asym;
+ F8(bits1)
+ F24(bits2)
+ F32(fd)
+} mdebug_ext_64;
+
+typedef struct
+{
+ F32(adr)
+ F32(isym)
+ F32(iline)
+ F32(regmask)
+ F32(regoffset)
+ F32(iopt)
+ F32(fregmask)
+ F32(fregoffset)
+ F32(frameoffset)
+ F16(framereg)
+ F16(pcreg)
+ F32(lnLow)
+ F32(lnHigh)
+ F32(cbLineOffset)
+} mdebug_pdr_32;
+
+typedef struct
+{
+ F64(adr)
+ F64(cbLineOffset)
+ F32(isym)
+ F32(iline)
+ F32(regmask)
+ F32(regoffset)
+ F32(iopt)
+ F32(fregmask)
+ F32(fregoffset)
+ F32(frameoffset)
+ F32(lnLow)
+ F32(lnHigh)
+ F8(gp_prologue)
+ F8(bits1)
+ F8(bits2)
+ F8(localoff)
+ F16(framereg)
+ F16(pcreg)
+} mdebug_pdr_64;
+
+typedef struct
+{
+ F32(bits);
+} mdebug_rndx;
+
+typedef struct
+{
+ F8(bits1)
+ F8(bits2)
+ F8(bits3)
+ F8(bits4)
+ mdebug_rndx rndx;
+ F32(offset)
+} mdebug_opt;
+
+typedef struct
+{
+ F32(rfd)
+ F32(index)
+} mdebug_dnr;
+
+typedef struct
+{
+ F32(rfd)
+} mdebug_rfd;
+
+#define scNil 0
+#define scText 1
+#define scData 2
+#define scBss 3
+#define scRegister 4
+#define scAbs 5
+#define scUndefined 6
+#define scCdbLocal 7
+#define scBits 8
+#define scCdbSystem 9
+#define scDbx 9
+#define scRegImage 10
+#define scInfo 11
+#define scUserStruct 12
+#define scSData 13
+#define scSBss 14
+#define scRData 15
+#define scVar 16
+#define scCommon 17
+#define scSCommon 18
+#define scVarRegister 19
+#define scVariant 20
+#define scSUndefined 21
+#define scInit 22
+#define scBasedVar 23
+#define scXData 24
+#define scPData 25
+#define scFini 26
+#define scRConst 27
+#define scMax 32
+
+#define stNil 0
+#define stGlobal 1
+#define stStatic 2
+#define stParam 3
+#define stLocal 4
+#define stLabel 5
+#define stProc 6
+#define stBlock 7
+#define stEnd 8
+#define stMember 9
+#define stTypedef 10
+#define stFile 11
+#define stRegReloc 12
+#define stForward 13
+#define stStaticProc 14
+#define stConstant 15
+#define stStaParam 16
+#define stStruct 26
+#define stUnion 27
+#define stEnum 28
+#define stIndirect 34
+#define stMax 64
+
+struct mdebug
+{
+ uint32_t (*read_32) (char *);
+ GElf_Addr (*read_ptr) (char *);
+ void (*write_ptr) (char *, GElf_Addr);
+ void (*adjust_sym) (struct mdebug *, unsigned char *, GElf_Addr, GElf_Addr);
+ unsigned char *buf;
+ DSO *dso;
+};
+
+static uint32_t
+read_native_32 (char *p)
+{
+ return *(uint32_t *)p;
+}
+
+static uint32_t
+read_swap_32 (char *p)
+{
+ return bswap_32 (*(uint32_t *)p);
+}
+
+static GElf_Addr
+read_native_ptr32 (char *p)
+{
+ return *(uint32_t *)p;
+}
+
+static GElf_Addr
+read_swap_ptr32 (char *p)
+{
+ return bswap_32 (*(uint32_t *)p);
+}
+
+static void
+write_native_ptr32 (char *p, GElf_Addr v)
+{
+ *(uint32_t *)p = v;
+}
+
+static void
+write_swap_ptr32 (char *p, GElf_Addr v)
+{
+ *(uint32_t *)p = bswap_32 (v);
+}
+
+static GElf_Addr
+read_native_ptr64 (char *p)
+{
+ return *(uint64_t *)p;
+}
+
+static GElf_Addr
+read_swap_ptr64 (char *p)
+{
+ return bswap_64 (*(uint64_t *)p);
+}
+
+static void
+write_native_ptr64 (char *p, GElf_Addr v)
+{
+ *(uint64_t *)p = v;
+}
+
+static void
+write_swap_ptr64 (char *p, GElf_Addr v)
+{
+ *(uint64_t *)p = bswap_64 (v);
+}
+
+static inline int
+mdebug_sym_relocate (unsigned int st, unsigned int sc)
+{
+ switch (sc)
+ {
+ case scData:
+ case scBss:
+ case scAbs:
+ case scSData:
+ case scSBss:
+ case scRData:
+ case scXData:
+ case scPData:
+ return 1;
+ case scText:
+ case scInit:
+ case scFini:
+ case scRConst:
+ if (st != stBlock && st != stEnd && st != stFile)
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void
+adjust_mdebug_sym_le32 (struct mdebug *mdebug, mdebug_sym_32 *symptr,
+ GElf_Addr start, GElf_Addr adjust)
+{
+ unsigned int st, sc;
+ GElf_Addr addr;
+
+ st = symptr->bits1[0] & 0x3f;
+ sc = (symptr->bits1[0] >> 6) | ((symptr->bits2[0] & 7) << 2);
+ if (mdebug_sym_relocate (st, sc))
+ {
+ addr = mdebug->read_ptr (symptr->value);
+ if (addr >= start && (addr || sc != scAbs))
+ mdebug->write_ptr (symptr->value, addr + adjust);
+ }
+}
+
+static void
+adjust_mdebug_sym_be32 (struct mdebug *mdebug, mdebug_sym_32 *symptr,
+ GElf_Addr start, GElf_Addr adjust)
+{
+ unsigned int st, sc;
+ GElf_Addr addr;
+
+ st = symptr->bits1[0] >> 2;
+ sc = ((symptr->bits1[0] & 3) << 3) | (symptr->bits2[0] >> 5);
+ if (mdebug_sym_relocate (st, sc))
+ {
+ addr = mdebug->read_ptr (symptr->value);
+ if (addr >= start && (addr || sc != scAbs))
+ mdebug->write_ptr (symptr->value, addr + adjust);
+ }
+}
+
+static void
+adjust_mdebug_sym_le64 (struct mdebug *mdebug, mdebug_sym_64 *symptr,
+ GElf_Addr start, GElf_Addr adjust)
+{
+ unsigned int st, sc;
+ GElf_Addr addr;
+
+ st = symptr->bits1[0] & 0x3f;
+ sc = (symptr->bits1[0] >> 6) | ((symptr->bits2[0] & 7) << 2);
+ if (mdebug_sym_relocate (st, sc))
+ {
+ addr = mdebug->read_ptr (symptr->value);
+ if (addr >= start && (addr || sc != scAbs))
+ mdebug->write_ptr (symptr->value, addr + adjust);
+ }
+}
+
+static void
+adjust_mdebug_sym_be64 (struct mdebug *mdebug, mdebug_sym_64 *symptr,
+ GElf_Addr start, GElf_Addr adjust)
+{
+ unsigned int st, sc;
+ GElf_Addr addr;
+
+ st = symptr->bits1[0] >> 2;
+ sc = ((symptr->bits1[0] & 3) << 3) | (symptr->bits2[0] >> 5);
+ if (mdebug_sym_relocate (st, sc))
+ {
+ addr = mdebug->read_ptr (symptr->value);
+ if (addr >= start && (addr || sc != scAbs))
+ mdebug->write_ptr (symptr->value, addr + adjust);
+ }
+}
+
+#define SIZEOf(x) \
+ (dso->arch->class == ELFCLASS32 ? sizeof (x##_32) : sizeof (x##_64))
+#define SIZEOF(x) SIZEOf(x)
+#define OFFSETOf(x,y) \
+ (dso->arch->class == ELFCLASS32 ? offsetof (x##_32, y) : offsetof (x##_64, y))
+#define OFFSETOF(x,y) OFFSETOf(x,y)
+
+static int
+start_mdebug (DSO *dso, int n, struct mdebug *mdebug)
+{
+ Elf_Data *data = NULL;
+ Elf_Scn *scn = dso->scn[n];
+
+ data = elf_getdata (scn, NULL);
+ mdebug->buf = data->d_buf;
+ mdebug->dso = dso;
+ assert (data != NULL && data->d_buf != NULL);
+ assert (elf_getdata (scn, data) == NULL);
+ assert (data->d_off == 0 && data->d_size == dso->shdr[n].sh_size);
+ if (dso->mdebug_orig_offset == 0)
+ dso->mdebug_orig_offset = dso->shdr[n].sh_offset;
+#if __BYTE_ORDER == __BIG_ENDIAN
+ if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+#else
+# error Not supported host endianess
+#endif
+ {
+ mdebug->read_32 = read_native_32;
+ if (dso->arch->class == ELFCLASS32)
+ {
+ mdebug->read_ptr = read_native_ptr32;
+ mdebug->write_ptr = write_native_ptr32;
+ }
+ else
+ {
+ mdebug->read_ptr = read_native_ptr64;
+ mdebug->write_ptr = write_native_ptr64;
+ }
+ }
+#if __BYTE_ORDER == __BIG_ENDIAN
+ else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+#endif
+ {
+ mdebug->read_32 = read_swap_32;
+ if (dso->arch->class == ELFCLASS32)
+ {
+ mdebug->read_ptr = read_swap_ptr32;
+ mdebug->write_ptr = write_swap_ptr32;
+ }
+ else
+ {
+ mdebug->read_ptr = read_swap_ptr64;
+ mdebug->write_ptr = write_swap_ptr64;
+ }
+ }
+ else
+ {
+ error (0, 0, "%s: Wrong ELF data enconding", dso->filename);
+ return 1;
+ }
+ if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ {
+ if (dso->arch->class == ELFCLASS32)
+ mdebug->adjust_sym = (void *) adjust_mdebug_sym_le32;
+ else
+ mdebug->adjust_sym = (void *) adjust_mdebug_sym_le64;
+ }
+ else
+ {
+ if (dso->arch->class == ELFCLASS32)
+ mdebug->adjust_sym = (void *) adjust_mdebug_sym_be32;
+ else
+ mdebug->adjust_sym = (void *) adjust_mdebug_sym_be64;
+ }
+
+ if (dso->shdr[n].sh_size < SIZEOF (mdebug_hdr))
+ {
+ error (0, 0, "%s: .mdebug section too small", dso->filename);
+ return 1;
+ }
+ return 0;
+}
+
+int
+adjust_mdebug (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust)
+{
+ struct mdebug mdebug;
+ struct { GElf_Off offset; GElf_Off size; size_t entsize; } regions [11];
+ int i = 0;
+ unsigned char *symptr, *endptr;
+
+ if (start_mdebug (dso, n, &mdebug))
+ return 1;
+
+#define READ(x, y, longsize, sz) \
+do { \
+ unsigned char *tmp; \
+ tmp = mdebug.buf + OFFSETOF (mdebug_hdr, x); \
+ regions[i].offset = mdebug.read_ptr (tmp); \
+ tmp = mdebug.buf + OFFSETOF (mdebug_hdr, y); \
+ if (longsize) \
+ regions[i].size = mdebug.read_ptr (tmp); \
+ else \
+ regions[i].size = mdebug.read_32 (tmp); \
+ regions[i].entsize = sz; \
+ ++i; \
+} while (0)
+
+ READ (cbLineOffset, cbLine, 1, sizeof (char));
+ READ (cbDnOffset, idnMax, 0, sizeof (mdebug_dnr));
+ READ (cbPdOffset, ipdMax, 0, SIZEOF (mdebug_pdr));
+ READ (cbSymOffset, isymMax, 0, SIZEOF (mdebug_sym));
+ READ (cbOptOffset, ioptMax, 0, sizeof (mdebug_opt));
+ READ (cbAuxOffset, iauxMax, 0, 4 * sizeof (char));
+ READ (cbSsOffset, issMax, 0, sizeof (char));
+ READ (cbSsExtOffset, issExtMax, 0, sizeof (char));
+ READ (cbFdOffset, ifdMax, 0, SIZEOF (mdebug_fdr));
+ READ (cbRfdOffset, crfd, 0, sizeof (mdebug_rfd));
+ READ (cbExtOffset, iextMax, 0, SIZEOF (mdebug_ext));
+
+#undef READ
+
+ for (i = 0; i < 11; ++i)
+ {
+ if (regions[i].offset)
+ regions[i].offset -= dso->mdebug_orig_offset;
+ regions[i].size *= regions[i].entsize;
+ if (regions[i].offset >= dso->shdr[n].sh_size
+ || regions[i].offset + regions[i].size > dso->shdr[n].sh_size)
+ {
+ error (0, 0, "%s: File offsets in .mdebug header point outside of .mdebug section",
+ dso->filename);
+ return 1;
+ }
+ }
+
+ /* Adjust symbols. */
+ if (regions[3].offset)
+ for (symptr = mdebug.buf + regions[3].offset,
+ endptr = symptr + regions[3].size;
+ symptr < endptr;
+ symptr += regions[3].entsize)
+ mdebug.adjust_sym (&mdebug, symptr, start, adjust);
+
+ /* Adjust file descriptor's addresses. */
+ if (regions[8].offset)
+ for (symptr = mdebug.buf + regions[8].offset,
+ endptr = symptr + regions[8].size;
+ symptr < endptr;
+ symptr += regions[8].entsize)
+ {
+ GElf_Addr addr;
+
+ assert (offsetof (mdebug_fdr_32, adr) == 0);
+ assert (offsetof (mdebug_fdr_64, adr) == 0);
+ addr = mdebug.read_ptr (symptr);
+ if (addr >= start)
+ mdebug.write_ptr (symptr, addr + adjust);
+ }
+
+ /* Adjust extended symbols. */
+ if (regions[10].offset)
+ for (symptr = mdebug.buf + regions[10].offset
+ + OFFSETOF (mdebug_ext, asym),
+ endptr = symptr + regions[10].size;
+ symptr < endptr;
+ symptr += regions[10].entsize)
+ mdebug.adjust_sym (&mdebug, symptr, start, adjust);
+
+ return 0;
+}
+
+int
+finalize_mdebug (DSO *dso)
+{
+ int i;
+ struct mdebug mdebug;
+ GElf_Addr adj;
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ if ((dso->arch->machine == EM_ALPHA
+ && dso->shdr[i].sh_type == SHT_ALPHA_DEBUG)
+ || (dso->arch->machine == EM_MIPS
+ && dso->shdr[i].sh_type == SHT_MIPS_DEBUG))
+ break;
+
+ assert (i < dso->ehdr.e_shnum);
+
+ /* If .mdebug's file position did not change, there is nothing to do. */
+ adj = dso->shdr[i].sh_offset - dso->mdebug_orig_offset;
+ if (! adj)
+ return 0;
+
+ if (start_mdebug (dso, i, &mdebug))
+ return 1;
+
+#define ADJUST(x) \
+do { \
+ unsigned char *tmp; \
+ GElf_Addr val; \
+ tmp = mdebug.buf + OFFSETOF (mdebug_hdr, x); \
+ val = mdebug.read_ptr (tmp); \
+ if (! val) \
+ break; \
+ val += adj; \
+ if (val < dso->shdr[i].sh_offset \
+ || val >= dso->shdr[i].sh_offset + dso->shdr[i].sh_size) \
+ { \
+ error (0, 0, "%s: File offsets in .mdebug header point outside of .mdebug section", \
+ dso->filename); \
+ return 1; \
+ } \
+ mdebug.write_ptr (tmp, val); \
+} while (0)
+
+ ADJUST (cbLineOffset);
+ ADJUST (cbDnOffset);
+ ADJUST (cbPdOffset);
+ ADJUST (cbSymOffset);
+ ADJUST (cbOptOffset);
+ ADJUST (cbAuxOffset);
+ ADJUST (cbSsOffset);
+ ADJUST (cbSsExtOffset);
+ ADJUST (cbFdOffset);
+ ADJUST (cbRfdOffset);
+ ADJUST (cbExtOffset);
+
+#undef ADJUST
+ return 0;
+}