From a74e239e28c7c63fc8bbc7f8ecee94ead4cb8cf9 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Sun, 12 Nov 2006 10:57:18 +0100 Subject: [PATCH] Major cleanup of ElfLinker. --- src/linker.cpp | 330 +++++++++++++++++++++++++++++-------------------- src/linker.h | 22 ++-- src/packer.cpp | 12 +- src/packer.h | 8 +- 4 files changed, 217 insertions(+), 155 deletions(-) diff --git a/src/linker.cpp b/src/linker.cpp index c8c2ad58..d7bf7dc7 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -29,7 +29,7 @@ #include "conf.h" #include "linker.h" -static int hex(unsigned char c) +static unsigned hex(unsigned char c) { return (c & 0xf) + (c > '9' ? 9 : 0); } @@ -45,17 +45,20 @@ static bool update_capacity(unsigned size, unsigned *capacity) return true; } + /************************************************************************* -// +// Section **************************************************************************/ ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a) : - name(strdup(n)), output(NULL), size(s), offset(0), align(a), next(NULL) + name(NULL), output(NULL), size(s), offset(0), p2align(a), next(NULL) { - assert(name); + name = strdup(n); + assert(name != NULL); input = malloc(s + 1); - assert(input); + assert(input != NULL); memcpy(input, i, s); + ((char *)input)[s] = 0; } ElfLinker::Section::~Section() @@ -64,10 +67,17 @@ ElfLinker::Section::~Section() free(input); } + +/************************************************************************* +// Symbol +**************************************************************************/ + ElfLinker::Symbol::Symbol(const char *n, Section *s, unsigned o) : - name(strdup(n)), section(s), offset(o) + name(NULL), section(s), offset(o) { - assert(name); + name = strdup(n); + assert(name != NULL); + assert(section != NULL); } ElfLinker::Symbol::~Symbol() @@ -75,12 +85,77 @@ ElfLinker::Symbol::~Symbol() free(name); } + +/************************************************************************* +// Relocation +**************************************************************************/ + ElfLinker::Relocation::Relocation(const Section *s, unsigned o, const char *t, const Symbol *v, unsigned a) : section(s), offset(o), type(t), value(v), add(a) -{} +{ + assert(section != NULL); +} +/************************************************************************* +// ElfLinker +**************************************************************************/ + +ElfLinker::ElfLinker() : + input(NULL), output(NULL), head(NULL), tail(NULL), + sections(NULL), symbols(NULL), relocations(NULL), + nsections(0), nsections_capacity(0), + nsymbols(0), nsymbols_capacity(0), + nrelocations(0), nrelocations_capacity(0), + reloc_done(false) +{ +} + +ElfLinker::~ElfLinker() +{ + delete [] input; + delete [] output; + + unsigned ic; + for (ic = 0; ic < nsections; ic++) + delete sections[ic]; + free(sections); + for (ic = 0; ic < nsymbols; ic++) + delete symbols[ic]; + free(symbols); + for (ic = 0; ic < nrelocations; ic++) + delete relocations[ic]; + free(relocations); +} + +void ElfLinker::init(const void *pdata, int plen) +{ + upx_byte *i = new upx_byte[plen + 1]; + memcpy(i, pdata, plen); + input = i; + inputlen = plen; + input[plen] = 0; + + output = new upx_byte[plen]; + outputlen = 0; + + int pos = find(input, plen, "Sections:", 9); + assert(pos != -1); + char *psections = (char *) input + pos; + + char *psymbols = strstr(psections, "SYMBOL TABLE:"); + assert(psymbols != NULL); + + char *prelocs = strstr(psymbols, "RELOCATION RECORDS FOR"); + assert(prelocs != NULL); + + preprocessSections(psections, psymbols); + preprocessSymbols(psymbols, prelocs); + preprocessRelocations(prelocs, (char*) input + inputlen); + addLoader("*UND*"); +} + void ElfLinker::preprocessSections(char *start, const char *end) { nsections = 0; @@ -114,22 +189,28 @@ void ElfLinker::preprocessSymbols(char *start, const char *end) { char section[1024]; char symbol[1024]; - unsigned offset; + unsigned value, offset; char *nextl = strchr(start, '\n'); assert(nextl != NULL); - if (sscanf(start, "%x%*8c %1024s %*x %1023s", + if (sscanf(start, "%x g *ABS* %x %1023s", + &value, &offset, symbol) == 3) + { + char *s = strstr(start, symbol); + s[strlen(symbol)] = 0; + addSymbol(s, "*ABS*", value); + assert(offset == 0); + } + else if (sscanf(start, "%x%*8c %1023s %*x %1023s", &offset, section, symbol) == 3) { char *s = strstr(start, symbol); s[strlen(symbol)] = 0; - if (strcmp(section, "*UND*") == 0) offset = 0xdeaddead; + assert(strcmp(section, "*ABS*") != 0); addSymbol(s, section, offset); - - //printf("symbol %s preprocessed o=%x\n", s, offset); } start = nextl + 1; @@ -185,38 +266,54 @@ void ElfLinker::preprocessRelocations(char *start, const char *end) } } -ElfLinker::Section *ElfLinker::findSection(const char *name) const +ElfLinker::Section *ElfLinker::findSection(const char *name, bool fatal) const { for (unsigned ic = 0; ic < nsections; ic++) if (strcmp(sections[ic]->name, name) == 0) return sections[ic]; - - printf("unknown section %s\n", name); - abort(); + if (fatal) + { + printf("unknown section %s\n", name); + abort(); + } return NULL; } -ElfLinker::Symbol *ElfLinker::findSymbol(const char *name) /*const*/ +ElfLinker::Symbol *ElfLinker::findSymbol(const char *name, bool fatal) const { for (unsigned ic = 0; ic < nsymbols; ic++) if (strcmp(symbols[ic]->name, name) == 0) return symbols[ic]; - - // FIXME: this should be a const method ! - if ('*' == name[0]) // *ABS* - return addSymbol(name, name, 0); - - printf("unknown symbol %s\n", name); - abort(); + if (fatal) + { + printf("unknown symbol %s\n", name); + abort(); + } return NULL; } -ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section, - unsigned offset) +ElfLinker::Section *ElfLinker::addSection(const char *sname, const void *sdata, int slen, unsigned p2align) { + //printf("addSection: %s len=%d align=%d\n", sname, slen, p2align); + if (update_capacity(nsections, &nsections_capacity)) + sections = static_cast
(realloc(sections, nsections_capacity * sizeof(Section *))); + assert(sections); + assert(sname); assert(sname[0]); assert(sname[strlen(sname)-1] != ':'); + assert(findSection(sname, false) == NULL); + Section *sec = new Section(sname, sdata, slen, p2align); + sections[nsections++] = sec; + return sec; +} + +ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section, + unsigned offset) +{ + //printf("addSymbol: %s %s 0x%x\n", name, section, offset); if (update_capacity(nsymbols, &nsymbols_capacity)) symbols = static_cast(realloc(symbols, nsymbols_capacity * sizeof(Symbol *))); assert(symbols != NULL); + assert(name); assert(name[0]); assert(name[strlen(name)-1] != ':'); + assert(findSymbol(name, false) == NULL); Symbol *sym = new Symbol(name, findSection(section), offset); symbols[nsymbols++] = sym; return sym; @@ -235,59 +332,6 @@ ElfLinker::Relocation *ElfLinker::addRelocation(const char *section, unsigned of return rel; } -ElfLinker::ElfLinker() : - frozen(false), input(NULL), output(NULL), head(NULL), tail(NULL), - sections(NULL), symbols(NULL), relocations(NULL), - nsections(0), nsections_capacity(0), - nsymbols(0), nsymbols_capacity(0), - nrelocations(0), nrelocations_capacity(0) -{ -} - -ElfLinker::~ElfLinker() -{ - delete [] input; - delete [] output; - - unsigned ic; - for (ic = 0; ic < nsections; ic++) - delete sections[ic]; - free(sections); - for (ic = 0; ic < nsymbols; ic++) - delete symbols[ic]; - free(symbols); - for (ic = 0; ic < nrelocations; ic++) - delete relocations[ic]; - free(relocations); -} - -void ElfLinker::init(const void *pdata, int plen) -{ - upx_byte *i = new upx_byte[plen + 1]; - memcpy(i, pdata, plen); - input = i; - inputlen = plen; - - output = new upx_byte[plen]; - outputlen = 0; - - int pos = find(input, plen, "Sections:", 9); - assert(pos != -1); - char *psections = pos + (char *) input; - - char *psymbols = strstr(psections, "SYMBOL TABLE:"); - assert(psymbols != NULL); - - char *prelocs = strstr(psymbols, "RELOCATION RECORDS FOR"); - assert(prelocs != NULL); - input[plen] = 0; - - preprocessSections(psections, psymbols); - preprocessSymbols(psymbols, prelocs); - preprocessRelocations(prelocs, (char*) input + inputlen); - addLoader("*UND*"); -} - void ElfLinker::setLoaderAlignOffset(int phase) { //assert(phase & 0); @@ -296,7 +340,6 @@ void ElfLinker::setLoaderAlignOffset(int phase) int ElfLinker::addLoader(const char *sname) { - assert(!frozen); if (sname[0] == 0) return outputlen; @@ -311,27 +354,28 @@ int ElfLinker::addLoader(const char *sname) break; } - if (*sect == '+') // alignment + if (sect[0] == '+') // alignment { assert(tail); - if (unsigned l = (hex(sect[2]) - tail->offset - tail->size) - % hex(sect[1])) + unsigned l = (hex(sect[2]) - tail->offset - tail->size) % hex(sect[1]); + if (l) { - alignCode(l); + if (sect[3] == 'D') + alignData(l); + else + alignCode(l); tail->size += l; - outputlen += l; } } else { Section *section = findSection(sect); - if (section->align) { - unsigned const v = ~0u << section->align; + if (section->p2align) { assert(tail); + unsigned const v = ~0u << section->p2align; if (unsigned const l = ~v & -(tail->offset + tail->size)) { alignCode(l); tail->size += l; - outputlen += l; } } memcpy(output + outputlen, section->input, section->size); @@ -354,38 +398,22 @@ int ElfLinker::addLoader(const char *sname) return outputlen; } -void ElfLinker::addSection(const char *sname, const void *sdata, int slen, int align) +int ElfLinker::getSection(const char *sname, int *slen) const { - assert(!frozen); - if (update_capacity(nsections, &nsections_capacity)) - sections = static_cast
(realloc(sections, nsections_capacity * sizeof(Section *))); - assert(sections); - Section *sec = new Section(sname, sdata, slen, align); - sections[nsections++] = sec; - //return sec; -} - -void ElfLinker::freeze() -{ - if (frozen) - return; - - frozen = true; -} - -int ElfLinker::getSection(const char *sname, int *slen) -{ - assert(frozen); - Section *section = findSection(sname); + const Section *section = findSection(sname); if (slen) *slen = section->size; return section->output - output; } -upx_byte *ElfLinker::getLoader(int *llen) +int ElfLinker::getSectionSize(const char *sname) const { - assert(frozen); + const Section *section = findSection(sname); + return section->size; +} +upx_byte *ElfLinker::getLoader(int *llen) const +{ if (llen) *llen = outputlen; return output; @@ -393,31 +421,38 @@ upx_byte *ElfLinker::getLoader(int *llen) void ElfLinker::relocate() { - assert(frozen); - + assert(!reloc_done); + reloc_done = true; for (unsigned ic = 0; ic < nrelocations; ic++) { - Relocation *rel = relocations[ic]; + const Relocation *rel = relocations[ic]; + unsigned value; + if (rel->section->output == NULL) continue; - if (rel->value->section->output == NULL) + if (strcmp(rel->value->section->name, "*ABS*") == 0) + { + value = rel->value->offset; + } + else if (strcmp(rel->value->section->name, "*UND*") == 0 && + rel->value->offset == 0xdeaddead) + { + printf("undefined symbol '%s' referenced\n", rel->value->name); + abort(); + } + else if (rel->value->section->output == NULL) { printf("can not apply reloc '%s:%x' without section '%s'\n", rel->section->name, rel->offset, rel->value->section->name); abort(); } - - if (strcmp(rel->value->section->name, "*UND*") == 0 && - rel->value->offset == 0xdeaddead) + else { - printf("undefined symbol '%s' referenced\n", rel->value->name); - abort(); + value = rel->value->section->offset + + rel->value->offset + rel->add; } - unsigned value = rel->value->section->offset + - rel->value->offset + rel->add; upx_byte *location = rel->section->output + rel->offset; - relocate1(rel, location, value, rel->type); } } @@ -425,7 +460,12 @@ void ElfLinker::relocate() void ElfLinker::defineSymbol(const char *name, unsigned value) { Symbol *symbol = findSymbol(name); - if (strcmp(symbol->section->name, "*UND*") == 0) // for undefined symbols + if (strcmp(symbol->section->name, "*ABS*") == 0) + { + printf("defineSymbol: symbol '%s' is *ABS*\n", name); + abort(); + } + else if (strcmp(symbol->section->name, "*UND*") == 0) // for undefined symbols symbol->offset = value; else if (strcmp(symbol->section->name, name) == 0) // for sections { @@ -438,36 +478,38 @@ void ElfLinker::defineSymbol(const char *name, unsigned value) } else { - printf("symbol '%s' already defined\n", name); + printf("defineSymbol: symbol '%s' already defined\n", name); abort(); } } // debugging support -void ElfLinker::dumpSymbols(FILE *fp) const +void ElfLinker::dumpSymbols(unsigned flags, FILE *fp) const { if (fp == NULL) fp = stdout; for (unsigned ic = 0; ic < nsymbols; ic++) { - Symbol *symbol = symbols[ic]; - fprintf(fp, "%-20s 0x%08x | %-20s 0x%08x\n", + const Symbol *symbol = symbols[ic]; + if ((flags & 1) && symbol->section->output == NULL) + continue; + fprintf(fp, "%-28s 0x%08x | %-28s 0x%08x\n", symbol->name, symbol->offset, symbol->section->name, symbol->section->offset); } } unsigned ElfLinker::getSymbolOffset(const char *name) const { - assert(frozen); - Symbol *symbol = const_cast(this)->findSymbol(name); + const Symbol *symbol = findSymbol(name); if (symbol->section->output == NULL) return 0xdeaddead; return symbol->section->offset + symbol->offset; } -void ElfLinker::alignWithByte(unsigned len, upx_byte b) +void ElfLinker::alignWithByte(unsigned len, unsigned char b) { memset(output + outputlen, b, len); + outputlen += len; } void ElfLinker::relocate1(const Relocation *rel, upx_byte *, @@ -483,6 +525,23 @@ void ElfLinker::relocate1(const Relocation *rel, upx_byte *, // FIXME: add more displacment overflow checks **************************************************************************/ +#if 0 // FIXME +static void check8(const Relocation *rel, const upx_byte *location, int v, int d) +{ + if (v < -128 || v > 127) { + printf("value out of range (%d) in reloc %s:%x\n", + v, rel->section->name, rel->offset); + abort(); + } + if (d < -128 || d > 127) { + printf("displacement target out of range (%d) in reloc %s:%x\n", + v, rel->section->name, rel->offset); + abort(); + } +} +#endif + + void ElfLinkerAMD64::relocate1(const Relocation *rel, upx_byte *location, unsigned value, const char *type) { @@ -580,6 +639,7 @@ void ElfLinkerM68k::alignCode(unsigned len) assert((outputlen & 1) == 0); for (unsigned i = 0; i < len; i += 2) set_be16(output + outputlen + i, 0x4e71); // "nop" + outputlen += len; } void ElfLinkerM68k::relocate1(const Relocation *rel, upx_byte *location, @@ -651,12 +711,12 @@ void ElfLinkerX86::relocate1(const Relocation *rel, upx_byte *location, return super::relocate1(rel, location, value, type); type += 6; - int range_check = 0; + bool range_check = false; if (strncmp(type, "PC", 2) == 0) { value -= rel->section->offset + rel->offset; type += 2; - range_check = 1; + range_check = true; } if (strcmp(type, "8") == 0) diff --git a/src/linker.h b/src/linker.h index 190df537..5db825c4 100644 --- a/src/linker.h +++ b/src/linker.h @@ -41,8 +41,6 @@ protected: struct Symbol; struct Relocation; - bool frozen; // FIXME: can we remove this ? - upx_byte *input; int inputlen; upx_byte *output; @@ -62,11 +60,13 @@ protected: unsigned nrelocations; unsigned nrelocations_capacity; + bool reloc_done; + void preprocessSections(char *start, const char *end); void preprocessSymbols(char *start, const char *end); void preprocessRelocations(char *start, const char *end); - Section *findSection(const char *name) const; - Symbol *findSymbol(const char *name) /*const*/; + Section *findSection(const char *name, bool fatal=true) const; + Symbol *findSymbol(const char *name, bool fatal=true) const; Symbol *addSymbol(const char *name, const char *section, unsigned offset); Relocation *addRelocation(const char *section, unsigned off, const char *type, @@ -79,16 +79,16 @@ public: virtual void init(const void *pdata, int plen); virtual void setLoaderAlignOffset(int phase); virtual int addLoader(const char *sname); - virtual void addSection(const char *sname, const void *sdata, int slen, int align); - virtual void freeze(); - virtual int getSection(const char *sname, int *slen=NULL); - virtual upx_byte *getLoader(int *llen=NULL); + virtual Section *addSection(const char *sname, const void *sdata, int slen, unsigned p2align); + virtual int getSection(const char *sname, int *slen=NULL) const; + virtual int getSectionSize(const char *sname) const; + virtual upx_byte *getLoader(int *llen=NULL) const; virtual void relocate(); virtual void defineSymbol(const char *name, unsigned value); virtual unsigned getSymbolOffset(const char *) const; - virtual void dumpSymbols(FILE *fp=NULL) const; + virtual void dumpSymbols(unsigned flags=0, FILE *fp=NULL) const; - void alignWithByte(unsigned len, upx_byte b); + void alignWithByte(unsigned len, unsigned char b); virtual void alignCode(unsigned len) { alignWithByte(len, 0); } virtual void alignData(unsigned len) { alignWithByte(len, 0); } @@ -104,7 +104,7 @@ struct ElfLinker::Section : private nocopy upx_byte *output; unsigned size; unsigned offset; - unsigned char align; // log2 + unsigned p2align; // log2 Section *next; Section(const char *n, const void *i, unsigned s, unsigned a=0); diff --git a/src/packer.cpp b/src/packer.cpp index 5a3ac0c3..0f9718af 100644 --- a/src/packer.cpp +++ b/src/packer.cpp @@ -932,12 +932,6 @@ unsigned Packer::unoptimizeReloc32(upx_byte **in, upx_byte *image, // loader util **************************************************************************/ -void Packer::freezeLoader() -{ - linker->freeze(); -} - - upx_byte *Packer::getLoader() const { int size = -1; @@ -1034,6 +1028,12 @@ void Packer::initLoader(const void *pdata, int plen, int small) } +void Packer::addLoader(const char *s) +{ + if (s && *s) + linker->addLoader(s); +} + void __acc_cdecl_va Packer::addLoader(const char *s, ...) { va_list ap; diff --git a/src/packer.h b/src/packer.h index dc502d3e..a304af9e 100644 --- a/src/packer.h +++ b/src/packer.h @@ -206,14 +206,16 @@ protected: virtual int buildLoader(const Filter *ft) = 0; virtual Linker* newLinker() const = 0; // loader util for linker - virtual void freezeLoader(); + void freezeLoader() { } virtual upx_byte *getLoader() const; virtual int getLoaderSize() const; virtual void initLoader(const void *pdata, int plen, int small=-1); #if 1 && (ACC_CC_GNUC >= 0x040100) - virtual void __acc_cdecl_va addLoader(const char *s, ...) __attribute__((__sentinel__)); + virtual void addLoader(const char *); + virtual void __acc_cdecl_va addLoader(const char *, ...) __attribute__((__sentinel__)); #else - virtual void __acc_cdecl_va addLoader(const char *s, ...); + virtual void addLoader(const char *); + virtual void __acc_cdecl_va addLoader(const char *, ...); #endif virtual int getLoaderSection(const char *name, int *slen=NULL) const; virtual int getLoaderSectionStart(const char *name, int *slen=NULL) const;