/* linker.cpp -- This file is part of the UPX executable compressor. Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2017 Laszlo Molnar All Rights Reserved. UPX and the UCL library are free software; you can redistribute them and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Markus F.X.J. Oberhumer Laszlo Molnar */ #include "conf.h" #include "linker.h" static unsigned hex(unsigned char c) { return (c & 0xf) + (c > '9' ? 9 : 0); } static bool update_capacity(unsigned size, unsigned *capacity) { if (size < *capacity) return false; if (*capacity == 0) *capacity = 16; while (size >= *capacity) *capacity *= 2; return true; } static void __acc_cdecl_va internal_error(const char *format, ...) { static char buf[1024]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); throwInternalError(buf); } /************************************************************************* // Section **************************************************************************/ ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a) : name(NULL), output(NULL), size(s), offset(0), p2align(a), next(NULL) { name = strdup(n); assert(name != NULL); input = malloc(s + 1); assert(input != NULL); if (s != 0) memcpy(input, i, s); ((char *) input)[s] = 0; } ElfLinker::Section::~Section() { free(name); free(input); } /************************************************************************* // Symbol **************************************************************************/ ElfLinker::Symbol::Symbol(const char *n, Section *s, upx_uint64_t o) : name(NULL), section(s), offset(o) { name = strdup(n); assert(name != NULL); assert(section != NULL); } ElfLinker::Symbol::~Symbol() { free(name); } /************************************************************************* // Relocation **************************************************************************/ ElfLinker::Relocation::Relocation(const Section *s, unsigned o, const char *t, const Symbol *v, upx_uint64_t a) : section(s), offset(o), type(t), value(v), add(a) { assert(section != NULL); } /************************************************************************* // ElfLinker **************************************************************************/ ElfLinker::ElfLinker() : bele(&N_BELE_RTP::le_policy), 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_v, int plen) { const upx_byte *pdata = (const upx_byte *) pdata_v; if (plen >= 16 && memcmp(pdata, "UPX#", 4) == 0) { // decompress pre-compressed stub-loader int method; unsigned u_len, c_len; if (pdata[4]) { method = pdata[4]; u_len = get_le16(pdata + 5); c_len = get_le16(pdata + 7); pdata += 9; assert(9 + c_len == (unsigned) plen); } else { method = pdata[5]; u_len = get_le32(pdata + 6); c_len = get_le32(pdata + 10); pdata += 14; assert(14 + c_len == (unsigned) plen); } assert((unsigned) plen < u_len); inputlen = u_len; input = new upx_byte[inputlen + 1]; unsigned new_len = u_len; int r = upx_decompress(pdata, c_len, input, &new_len, method, NULL); if (r == UPX_E_OUT_OF_MEMORY) throwOutOfMemoryException(); if (r != UPX_E_OK || new_len != u_len) throwBadLoader(); } else { inputlen = plen; input = new upx_byte[inputlen + 1]; if (inputlen) memcpy(input, pdata, inputlen); } input[inputlen] = 0; // NUL terminate output = new upx_byte[inputlen ? inputlen : 0x4000]; outputlen = 0; if ((int) strlen("Sections:\n" "SYMBOL TABLE:\n" "RELOCATION RECORDS FOR ") < inputlen) { int pos = find(input, inputlen, "Sections:\n", 10); assert(pos != -1); char *psections = (char *) input + pos; char *psymbols = strstr(psections, "SYMBOL TABLE:\n"); 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, char *end) { char *nextl; for (nsections = 0; start < end; start = 1 + nextl) { nextl = strchr(start, '\n'); assert(nextl != NULL); *nextl = '\0'; // a record is a line unsigned offset, size, align; char name[1024]; if (sscanf(start, "%*d %1023s %x %*d %*d %x 2**%d", name, &size, &offset, &align) == 4) { char *n = strstr(start, name); n[strlen(name)] = 0; addSection(n, input + offset, size, align); // printf("section %s preprocessed\n", n); } } addSection("*ABS*", NULL, 0, 0); addSection("*UND*", NULL, 0, 0); } void ElfLinker::preprocessSymbols(char *start, char *end) { char *nextl; for (nsymbols = 0; start < end; start = 1 + nextl) { nextl = strchr(start, '\n'); assert(nextl != NULL); *nextl = '\0'; // a record is a line unsigned value, offset; char section[1024]; char symbol[1024]; 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); } #if 0 else if (sscanf(start, "%x%*8c %1023s %*x %1023s", #else // work around broken scanf() implementations // http://bugs.winehq.org/show_bug.cgi?id=10401 (fixed in Wine 0.9.58) else if (sscanf(start, "%x%*c%*c%*c%*c%*c%*c%*c%*c %1023s %*x %1023s", #endif &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); } } } void ElfLinker::preprocessRelocations(char *start, char *end) { Section *section = NULL; char *nextl; for (nrelocations = 0; start < end; start = 1 + nextl) { nextl = strchr(start, '\n'); assert(nextl != NULL); *nextl = '\0'; // a record is a line { char sect[1024]; if (sscanf(start, "RELOCATION RECORDS FOR [%[^]]", sect) == 1) section = findSection(sect); } unsigned offset; char type[100]; char symbol[1024]; if (sscanf(start, "%x %99s %1023s", &offset, type, symbol) == 3) { char *t = strstr(start, type); t[strlen(type)] = 0; upx_uint64_t add = 0; char *p = strstr(symbol, "+0x"); if (p == NULL) p = strstr(symbol, "-0x"); if (p) { char sign = *p; *p = 0; // terminate the symbol name p += 3; assert(strlen(p) == 8 || strlen(p) == 16); #if (ACC_CC_MSC && (_MSC_VER < 1800)) unsigned a = 0, b = 0; if (sscanf(p, "%08x%08x", &a, &b) == 2) add = ((upx_uint64_t) a << 32) | b; else add = a; #else char *endptr = NULL; add = strtoull(p, &endptr, 16); assert(endptr && *endptr == '\0'); #endif if (sign == '-') add = 0 - add; } if (section) { addRelocation(section->name, offset, t, symbol, add); // printf("relocation %s %s %x %llu preprocessed\n", section->name, symbol, offset, // (unsigned long long) add); } } } } 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]; if (fatal) internal_error("unknown section %s\n", name); return NULL; } 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]; if (fatal) internal_error("unknown symbol %s\n", name); return NULL; } 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, upx_uint64_t 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; } ElfLinker::Relocation *ElfLinker::addRelocation(const char *section, unsigned off, const char *type, const char *symbol, upx_uint64_t add) { if (update_capacity(nrelocations, &nrelocations_capacity)) relocations = static_cast( realloc(relocations, (nrelocations_capacity) * sizeof(Relocation *))); assert(relocations != NULL); Relocation *rel = new Relocation(findSection(section), off, type, findSymbol(symbol), add); relocations[nrelocations++] = rel; return rel; } #if 0 void ElfLinker::setLoaderAlignOffset(int phase) { //assert(phase & 0); printf("\nFIXME: ElfLinker::setLoaderAlignOffset %d\n", phase); } #endif int ElfLinker::addLoader(const char *sname) { assert(sname != NULL); if (!sname[0]) return outputlen; char *begin = strdup(sname); char *end = begin + strlen(begin); for (char *sect = begin; sect < end;) { for (char *tokend = sect; *tokend; tokend++) if (*tokend == ' ' || *tokend == ',') { *tokend = 0; break; } if (sect[0] == '+') // alignment { assert(tail); unsigned l = hex(sect[2]) - tail->offset - tail->size; unsigned m = hex(sect[1]); if (m) { l %= hex(sect[1]); } if (l) { if (sect[3] == 'D') alignData(l); else alignCode(l); tail->size += l; } } else { Section *section = findSection(sect); if (section->p2align) { assert(tail); assert(tail != section); // .p2align must be < 32 unsigned const v = ~0u << section->p2align; if (unsigned const l = ~v & (0u - (unsigned) (tail->offset + tail->size))) { alignCode(l); tail->size += l; } } memcpy(output + outputlen, section->input, section->size); section->output = output + outputlen; // printf("section added: 0x%04x %3d %s\n", outputlen, section->size, section->name); outputlen += section->size; if (head) { tail->next = section; section->offset = tail->offset + tail->size; } else head = section; tail = section; } sect += strlen(sect) + 1; } free(begin); return outputlen; } void ElfLinker::addLoader(const char *s, va_list ap) { while (s != NULL) { addLoader(s); s = va_arg(ap, const char *); } } void __acc_cdecl_va ElfLinker::addLoaderVA(const char *s, ...) { va_list ap; va_start(ap, s); addLoader(s, ap); va_end(ap); } int ElfLinker::getSection(const char *sname, int *slen) const { const Section *section = findSection(sname); if (slen) *slen = section->size; return (int) (section->output - output); } int ElfLinker::getSectionSize(const char *sname) const { const Section *section = findSection(sname); return section->size; } upx_byte *ElfLinker::getLoader(int *llen) const { if (llen) *llen = outputlen; return output; } void ElfLinker::relocate() { assert(!reloc_done); reloc_done = true; for (unsigned ic = 0; ic < nrelocations; ic++) { const Relocation *rel = relocations[ic]; upx_uint64_t value = 0; if (rel->section->output == NULL) continue; 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) internal_error("undefined symbol '%s' referenced\n", rel->value->name); else if (rel->value->section->output == NULL) internal_error("can not apply reloc '%s:%x' without section '%s'\n", rel->section->name, rel->offset, rel->value->section->name); else { value = rel->value->section->offset + rel->value->offset + rel->add; } upx_byte *location = rel->section->output + rel->offset; // printf("%-28s %-28s %-10s 0x%16llx 0x%16llx\n", rel->section->name, rel->value->name, // rel->type, (long long) value, (long long) value - rel->section->offset - rel->offset); // printf(" %llx %d %llx %d %llx : %d\n", (long long) value, rel->value->section->offset, // rel->value->offset, rel->offset, (long long) rel->add, *location); relocate1(rel, location, value, rel->type); } } void ElfLinker::defineSymbol(const char *name, upx_uint64_t value) { Symbol *symbol = findSymbol(name); if (strcmp(symbol->section->name, "*ABS*") == 0) internal_error("defineSymbol: symbol '%s' is *ABS*\n", name); else if (strcmp(symbol->section->name, "*UND*") == 0) // for undefined symbols symbol->offset = value; else if (strcmp(symbol->section->name, name) == 0) // for sections { for (Section *section = symbol->section; section; section = section->next) { assert(section->offset < value); section->offset = value; value += section->size; } } else internal_error("defineSymbol: symbol '%s' already defined\n", name); } // debugging support void ElfLinker::dumpSymbol(const Symbol *symbol, unsigned flags, FILE *fp) const { if ((flags & 1) && symbol->section->output == NULL) return; char d0[16 + 1], d1[16 + 1]; upx_snprintf(d0, sizeof(d0), "%016llx", (upx_uint64_t) symbol->offset); upx_snprintf(d1, sizeof(d1), "%016llx", (upx_uint64_t) symbol->section->offset); fprintf(fp, "%-28s 0x%-16s | %-28s 0x%-16s\n", symbol->name, d0, symbol->section->name, d1); } void ElfLinker::dumpSymbols(unsigned flags, FILE *fp) const { if (fp == NULL) fp = stdout; if ((flags & 2) == 0) { // default: dump symbols in used section order for (const Section *section = head; section; section = section->next) { char d1[16 + 1]; upx_snprintf(d1, sizeof(d1), "%016llx", (upx_uint64_t) section->offset); fprintf(fp, "%-42s%-28s 0x%-16s\n", "", section->name, d1); for (unsigned ic = 0; ic < nsymbols; ic++) { const Symbol *symbol = symbols[ic]; if (symbol->section == section && strcmp(symbol->name, section->name) != 0) dumpSymbol(symbol, flags, fp); } } } else { // dump all symbols for (unsigned ic = 0; ic < nsymbols; ic++) dumpSymbol(symbols[ic], flags, fp); } } upx_uint64_t ElfLinker::getSymbolOffset(const char *name) const { const Symbol *symbol = findSymbol(name); if (symbol->section->output == NULL) return 0xdeaddead; return symbol->section->offset + symbol->offset; } void ElfLinker::alignWithByte(unsigned len, unsigned char b) { memset(output + outputlen, b, len); outputlen += len; } void ElfLinker::relocate1(const Relocation *rel, upx_byte *, upx_uint64_t, const char *) { internal_error("unknown relocation type '%s\n", rel->type); } /************************************************************************* // ElfLinker arch subclasses // FIXME: add more displacment overflow checks // FIXME: add support for our special "ignore_reloc_overflow" section **************************************************************************/ #if 0 // FIXME static void check8(const Relocation *rel, const upx_byte *location, int v, int d) { if (v < -128 || v > 127) internal_error("value out of range (%d) in reloc %s:%x\n", v, rel->section->name, rel->offset); if (d < -128 || d > 127) internal_error("displacement target out of range (%d) in reloc %s:%x\n", v, rel->section->name, rel->offset); } #endif void ElfLinkerAMD64::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strncmp(type, "R_X86_64_", 9)) return super::relocate1(rel, location, value, type); type += 9; bool range_check = false; if (strncmp(type, "PC", 2) == 0) { value -= rel->section->offset + rel->offset; type += 2; range_check = true; } if (strcmp(type, "8") == 0) { int displ = (signed char) *location + (int) value; if (range_check && (displ < -128 || displ > 127)) internal_error("target out of range (%d) in reloc %s:%x\n", displ, rel->section->name, rel->offset); *location += value; } else if (strcmp(type, "16") == 0) set_le16(location, get_le16(location) + value); else if (strncmp(type, "32", 2) == 0) // for "32" and "32S" set_le32(location, get_le32(location) + value); else if (strcmp(type, "64") == 0) set_le64(location, get_le64(location) + value); else super::relocate1(rel, location, value, type); } void ElfLinkerArmBE::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strcmp(type, "R_ARM_PC24") == 0) { value -= rel->section->offset + rel->offset; set_be24(1 + location, get_be24(1 + location) + value / 4); } else if (strcmp(type, "R_ARM_ABS32") == 0) { set_be32(location, get_be32(location) + value); } else if (strcmp(type, "R_ARM_THM_CALL") == 0 || strcmp(type, "R_ARM_THM_XPC22") == 0 || strcmp(type, "R_ARM_THM_PC22") == 0) { value -= rel->section->offset + rel->offset; value += ((get_be16(location) & 0x7ff) << 12); value += (get_be16(location + 2) & 0x7ff) << 1; set_be16(location, 0xf000 + ((value >> 12) & 0x7ff)); set_be16(location + 2, 0xf800 + ((value >> 1) & 0x7ff)); //(b, 0xF000 + ((v - 1) / 2) * 0x10000); // set_be32(location, get_be32(location) + value / 4); } else if (0 == strcmp("R_ARM_ABS8", type)) { *location += value; } else super::relocate1(rel, location, value, type); } void ElfLinkerArmLE::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strcmp(type, "R_ARM_PC24") == 0) { value -= rel->section->offset + rel->offset; set_le24(location, get_le24(location) + value / 4); } else if (strcmp(type, "R_ARM_ABS32") == 0) { set_le32(location, get_le32(location) + value); } else if (strcmp(type, "R_ARM_THM_CALL") == 0 || strcmp(type, "R_ARM_THM_XPC22") == 0 || strcmp(type, "R_ARM_THM_PC22") == 0) { value -= rel->section->offset + rel->offset; value += ((get_le16(location) & 0x7ff) << 12); value += (get_le16(location + 2) & 0x7ff) << 1; set_le16(location, 0xf000 + ((value >> 12) & 0x7ff)); set_le16(location + 2, 0xf800 + ((value >> 1) & 0x7ff)); //(b, 0xF000 + ((v - 1) / 2) * 0x10000); // set_le32(location, get_le32(location) + value / 4); } else if (0 == strcmp("R_ARM_ABS8", type)) { *location += value; } else super::relocate1(rel, location, value, type); } void ElfLinkerArm64LE::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strcmp(type, "R_AARCH64_CALL26") == 0) { value -= rel->section->offset + rel->offset; set_le24(location, get_le24(location) + value / 4); // FIXME set_le26 } else if (strcmp(type, "R_AARCH64_ABS32") == 0) { set_le32(location, get_le32(location) + value); } else super::relocate1(rel, location, value, type); } void ElfLinkerM68k::alignCode(unsigned len) { assert((len & 1) == 0); 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, upx_uint64_t value, const char *type) { if (strncmp(type, "R_68K_", 6)) return super::relocate1(rel, location, value, type); type += 6; if (strncmp(type, "PC", 2) == 0) { value -= rel->section->offset + rel->offset; type += 2; } if (strcmp(type, "8") == 0) { *location += value; } else if (strcmp(type, "16") == 0) { set_be16(location, get_be16(location) + value); } else if (strcmp(type, "32") == 0) { set_be32(location, get_be32(location) + value); } else super::relocate1(rel, location, value, type); } void ElfLinkerMipsBE::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { #define MIPS_HI(a) (((a) >> 16) + (((a) &0x8000) >> 15)) #define MIPS_LO(a) ((a) &0xffff) #define MIPS_PC16(a) ((a) >> 2) #define MIPS_PC26(a) (((a) &0x0fffffff) >> 2) if (strcmp(type, "R_MIPS_HI16") == 0) set_be16(2 + location, get_be16(2 + location) + MIPS_HI(value)); else if (strcmp(type, "R_MIPS_LO16") == 0) set_be16(2 + location, get_be16(2 + location) + MIPS_LO(value)); else if (strcmp(type, "R_MIPS_PC16") == 0) { value -= rel->section->offset + rel->offset; set_be16(2 + location, get_be16(2 + location) + MIPS_PC16(value)); } else if (strcmp(type, "R_MIPS_26") == 0) set_be32(location, get_be32(location) + MIPS_PC26(value)); else if (strcmp(type, "R_MIPS_32") == 0) set_be32(location, get_be32(location) + value); else super::relocate1(rel, location, value, type); #undef MIPS_HI #undef MIPS_LO #undef MIPS_PC16 #undef MIPS_PC26 } void ElfLinkerMipsLE::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { #define MIPS_HI(a) (((a) >> 16) + (((a) &0x8000) >> 15)) #define MIPS_LO(a) ((a) &0xffff) #define MIPS_PC16(a) ((a) >> 2) #define MIPS_PC26(a) (((a) &0x0fffffff) >> 2) if (strcmp(type, "R_MIPS_HI16") == 0) set_le16(location, get_le16(location) + MIPS_HI(value)); else if (strcmp(type, "R_MIPS_LO16") == 0) set_le16(location, get_le16(location) + MIPS_LO(value)); else if (strcmp(type, "R_MIPS_PC16") == 0) { value -= rel->section->offset + rel->offset; set_le16(location, get_le16(location) + MIPS_PC16(value)); } else if (strcmp(type, "R_MIPS_26") == 0) set_le32(location, get_le32(location) + MIPS_PC26(value)); else if (strcmp(type, "R_MIPS_32") == 0) set_le32(location, get_le32(location) + value); else super::relocate1(rel, location, value, type); #undef MIPS_HI #undef MIPS_LO #undef MIPS_PC16 #undef MIPS_PC26 } void ElfLinkerPpc32::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strncmp(type, "R_PPC_", 6)) return super::relocate1(rel, location, value, type); type += 6; if (strcmp(type, "ADDR32") == 0) { set_be32(location, get_be32(location) + value); return; } if (strncmp(type, "REL", 3) == 0) { value -= rel->section->offset + rel->offset; type += 3; } // FIXME: more relocs // Note that original (*location).displ is ignored. if (strcmp(type, "24") == 0) { if (3 & value) internal_error("unaligned word diplacement"); // FIXME: displacment overflow? set_be32(location, (0xfc000003 & get_be32(location)) + (0x03fffffc & value)); } else if (strcmp(type, "14") == 0) { if (3 & value) internal_error("unaligned word diplacement"); // FIXME: displacment overflow? set_be32(location, (0xffff0003 & get_be32(location)) + (0x0000fffc & value)); } else super::relocate1(rel, location, value, type); } void ElfLinkerPpc64le::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strncmp(type, "R_PPC64_REL", 11)) return super::relocate1(rel, location, value, type); type += 11; bool range_check = false; if (strncmp(type, "PC", 2) == 0) { type += 2; range_check = true; } /* value will hold relative displacement */ value -= rel->section->offset + rel->offset; if (strcmp(type, "8") == 0) { int displ = (signed char) *location + (int) value; if (range_check && (displ < -128 || displ > 127)) internal_error("target out of range (%d) in reloc %s:%x\n", displ, rel->section->name, rel->offset); *location += value; } else if (strncmp(type, "14", 2) == 0) // for "32" and "32S" set_le16(location, get_le16(location) + value); else if (strcmp(type, "16") == 0) set_le16(location, get_le16(location) + value); else if (strncmp(type, "24", 2) == 0) // for "32" and "32S" set_le24(location, get_le24(location) + value); else if (strncmp(type, "32", 2) == 0) // for "32" and "32S" set_le32(location, get_le32(location) + value); else if (strcmp(type, "64") == 0) set_le64(location, get_le64(location) + value); else super::relocate1(rel, location, value, type); } void ElfLinkerX86::relocate1(const Relocation *rel, upx_byte *location, upx_uint64_t value, const char *type) { if (strncmp(type, "R_386_", 6)) return super::relocate1(rel, location, value, type); type += 6; bool range_check = false; if (strncmp(type, "PC", 2) == 0) { value -= rel->section->offset + rel->offset; type += 2; range_check = true; } if (strcmp(type, "8") == 0) { int displ = (signed char) *location + (int) value; if (range_check && (displ < -128 || displ > 127)) internal_error("target out of range (%d,%d,%d) in reloc %s:%x\n", displ, *location, value, rel->section->name, rel->offset); *location += value; } else if (strcmp(type, "16") == 0) set_le16(location, get_le16(location) + value); else if (strcmp(type, "32") == 0) set_le32(location, get_le32(location) + value); else super::relocate1(rel, location, value, type); } /* vim:set ts=4 sw=4 et: */