summaryrefslogtreecommitdiffstats
path: root/third_party/android_crazy_linker
diff options
context:
space:
mode:
authorsimonb@chromium.org <simonb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-12 14:17:47 +0000
committersimonb@chromium.org <simonb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-12 14:17:47 +0000
commit095b95c5e17d8b202c7fa3474bfecf83286f40c1 (patch)
treefd87644c8b9c22316876451ed6ea7f20025792bf /third_party/android_crazy_linker
parent76eeaa14e47061f2559789973ea0d70163579c91 (diff)
downloadchromium_src-095b95c5e17d8b202c7fa3474bfecf83286f40c1.zip
chromium_src-095b95c5e17d8b202c7fa3474bfecf83286f40c1.tar.gz
chromium_src-095b95c5e17d8b202c7fa3474bfecf83286f40c1.tar.bz2
Roll to android/platform/ndk/681f1b744aec1b0888f4c7a68165720db9670300
BUG= Review URL: https://codereview.chromium.org/328153002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276663 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/android_crazy_linker')
-rw-r--r--third_party/android_crazy_linker/README.chromium5
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp688
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.h51
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.cpp2
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.h4
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_elf_view.cpp4
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_elf_view.h8
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_proc_maps_unittest.cpp1
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp1
-rw-r--r--third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h6
-rw-r--r--third_party/android_crazy_linker/src/src/elf_traits.h24
-rw-r--r--third_party/android_crazy_linker/src/src/linker_phdr.cpp2
-rw-r--r--third_party/android_crazy_linker/src/tests/bar_with_relro.cpp2
-rw-r--r--third_party/android_crazy_linker/src/tests/foo_with_relro.cpp2
-rw-r--r--third_party/android_crazy_linker/src/tests/foo_with_static_constructor.cpp15
-rw-r--r--third_party/android_crazy_linker/src/tests/test_util.h8
16 files changed, 593 insertions, 230 deletions
diff --git a/third_party/android_crazy_linker/README.chromium b/third_party/android_crazy_linker/README.chromium
index 8626404..58d2ef0 100644
--- a/third_party/android_crazy_linker/README.chromium
+++ b/third_party/android_crazy_linker/README.chromium
@@ -1,6 +1,6 @@
Name: Android Crazy Linker
URL: https://android.googlesource.com/platform/ndk/+/master/sources/android/crazy_linker/
-Version: f96c7168b688f2d8cc99c220e7a40e3739476848
+Version: 681f1b744aec1b0888f4c7a68165720db9670300
Security Critical: no
License: Apache Version 2.0
@@ -25,3 +25,6 @@ index 5fa16df..bc03739 100644
#include <errno.h>
+- Rolled to:
+ android/platform/ndk/681f1b744aec1b0888f4c7a68165720db9670300
+ (Re-add license header to crazy_linker_elf_view.cpp)
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp
index 7525f62..591a1fd 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.cpp
@@ -33,6 +33,7 @@
// Processor-specific relocation types supported by the linker.
#ifdef __arm__
+/* arm32 relocations */
#define R_ARM_ABS32 2
#define R_ARM_REL32 3
#define R_ARM_GLOB_DAT 21
@@ -42,6 +43,17 @@
#endif // __arm__
+#ifdef __aarch64__
+
+/* arm64 relocations */
+#define R_AARCH64_ABS64 257
+#define R_AARCH64_COPY 1024
+#define R_AARCH64_GLOB_DAT 1025
+#define R_AARCH64_JUMP_SLOT 1026
+#define R_AARCH64_RELATIVE 1027
+
+#endif // __aarch64__
+
#ifdef __i386__
/* i386 relocations */
@@ -67,7 +79,7 @@ enum RelocationType {
};
// Convert an ELF relocation type info a RelocationType value.
-RelocationType GetRelocationType(unsigned r_type) {
+RelocationType GetRelocationType(ELF::Word r_type) {
switch (r_type) {
#ifdef __arm__
case R_ARM_JUMP_SLOT:
@@ -83,6 +95,19 @@ RelocationType GetRelocationType(unsigned r_type) {
return RELOCATION_TYPE_COPY;
#endif
+#ifdef __aarch64__
+ case R_AARCH64_JUMP_SLOT:
+ case R_AARCH64_GLOB_DAT:
+ case R_AARCH64_ABS64:
+ return RELOCATION_TYPE_ABSOLUTE;
+
+ case R_AARCH64_RELATIVE:
+ return RELOCATION_TYPE_RELATIVE;
+
+ case R_AARCH64_COPY:
+ return RELOCATION_TYPE_COPY;
+#endif
+
#ifdef __i386__
case R_386_JMP_SLOT:
case R_386_GLOB_DAT:
@@ -114,49 +139,71 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) {
phdr_count_ = view->phdr_count();
load_bias_ = view->load_bias();
+ // We handle only Rel or Rela, but not both. If DT_RELA or DT_RELASZ
+ // then we require DT_PLTREL to agree.
+ bool has_rela_relocations = false;
+ bool has_rel_relocations = false;
+
// Parse the dynamic table.
ElfView::DynamicIterator dyn(view);
for (; dyn.HasNext(); dyn.GetNext()) {
ELF::Addr dyn_value = dyn.GetValue();
uintptr_t dyn_addr = dyn.GetAddress(view->load_bias());
- switch (dyn.GetTag()) {
+ const ELF::Addr tag = dyn.GetTag();
+ switch (tag) {
case DT_PLTREL:
- // NOTE: Yes, there is nothing to record here, the content of
- // plt_rel_ will come from DT_JMPREL instead.
- RLOG(" DT_PLTREL\n");
- if (dyn_value != DT_REL) {
- *error = "Unsupported DT_RELA entry in dynamic section";
+ RLOG(" DT_PLTREL value=%d\n", dyn_value);
+ if (dyn_value != DT_REL && dyn_value != DT_RELA) {
+ *error = "Invalid DT_PLTREL value in dynamic section";
return false;
}
+ relocations_type_ = dyn_value;
break;
case DT_JMPREL:
RLOG(" DT_JMPREL addr=%p\n", dyn_addr);
- plt_relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
+ plt_relocations_ = dyn_addr;
break;
case DT_PLTRELSZ:
- plt_relocations_count_ = dyn_value / sizeof(ELF::Rel);
- RLOG(" DT_PLTRELSZ size=%d count=%d\n",
- dyn_value,
- plt_relocations_count_);
+ plt_relocations_size_ = dyn_value;
+ RLOG(" DT_PLTRELSZ size=%d\n", dyn_value);
break;
+ case DT_RELA:
case DT_REL:
- RLOG(" DT_REL addr=%p\n", dyn_addr);
- relocations_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
+ RLOG(" %s addr=%p\n",
+ (tag == DT_RELA) ? "DT_RELA" : "DT_REL",
+ dyn_addr);
+ if (relocations_) {
+ *error = "Unsupported DT_RELA/DT_REL combination in dynamic section";
+ return false;
+ }
+ relocations_ = dyn_addr;
+ if (tag == DT_RELA)
+ has_rela_relocations = true;
+ else
+ has_rel_relocations = true;
break;
+ case DT_RELASZ:
case DT_RELSZ:
- relocations_count_ = dyn_value / sizeof(ELF::Rel);
- RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, relocations_count_);
+ RLOG(" %s size=%d\n",
+ (tag == DT_RELASZ) ? "DT_RELASZ" : "DT_RELSZ",
+ dyn_addr);
+ if (relocations_size_) {
+ *error = "Unsupported DT_RELASZ/DT_RELSZ combination in dyn section";
+ return false;
+ }
+ relocations_size_ = dyn_value;
+ if (tag == DT_RELASZ)
+ has_rela_relocations = true;
+ else
+ has_rel_relocations = true;
break;
case DT_PLTGOT:
// Only used on MIPS currently. Could also be used on other platforms
// when lazy binding (i.e. RTLD_LAZY) is implemented.
RLOG(" DT_PLTGOT addr=%p\n", dyn_addr);
- plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr);
+ plt_got_ = reinterpret_cast<ELF::Addr*>(dyn_addr);
break;
- case DT_RELA:
- *error = "Unsupported DT_RELA entry in dynamic section";
- return false;
case DT_TEXTREL:
RLOG(" DT_TEXTREL\n");
has_text_relocations_ = true;
@@ -195,6 +242,20 @@ bool ElfRelocations::Init(const ElfView* view, Error* error) {
}
}
+ if (relocations_type_ != DT_REL && relocations_type_ != DT_RELA) {
+ *error = "Unsupported or missing DT_PLTREL in dynamic section";
+ return false;
+ }
+
+ if (relocations_type_ == DT_REL && has_rela_relocations) {
+ *error = "Found DT_RELA in dyn section, but DT_PLTREL is DT_REL";
+ return false;
+ }
+ if (relocations_type_ == DT_RELA && has_rel_relocations) {
+ *error = "Found DT_REL in dyn section, but DT_PLTREL is DT_RELA";
+ return false;
+ }
+
return true;
}
@@ -210,14 +271,34 @@ bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
}
}
- if (!ApplyRelocs(plt_relocations_,
- plt_relocations_count_,
- symbols,
- resolver,
- error) ||
- !ApplyRelocs(
- relocations_, relocations_count_, symbols, resolver, error)) {
- return false;
+ if (relocations_type_ == DT_REL) {
+ if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(plt_relocations_),
+ plt_relocations_size_ / sizeof(ELF::Rel),
+ symbols,
+ resolver,
+ error))
+ return false;
+ if (!ApplyRelRelocs(reinterpret_cast<ELF::Rel*>(relocations_),
+ relocations_size_ / sizeof(ELF::Rel),
+ symbols,
+ resolver,
+ error))
+ return false;
+ }
+
+ else if (relocations_type_ == DT_RELA) {
+ if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(plt_relocations_),
+ plt_relocations_size_ / sizeof(ELF::Rela),
+ symbols,
+ resolver,
+ error))
+ return false;
+ if (!ApplyRelaRelocs(reinterpret_cast<ELF::Rela*>(relocations_),
+ relocations_size_ / sizeof(ELF::Rela),
+ symbols,
+ resolver,
+ error))
+ return false;
}
#ifdef __mips__
@@ -236,19 +317,249 @@ bool ElfRelocations::ApplyAll(const ElfSymbols* symbols,
return true;
}
-bool ElfRelocations::ApplyRelocs(const ELF::Rel* rel,
- size_t rel_count,
- const ElfSymbols* symbols,
- SymbolResolver* resolver,
- Error* error) {
+bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela,
+ ELF::Addr sym_addr,
+ bool resolved CRAZY_UNUSED,
+ Error* error) {
+ const ELF::Word rela_type = ELF_R_TYPE(rela->r_info);
+ const ELF::Word CRAZY_UNUSED rela_symbol = ELF_R_SYM(rela->r_info);
+ const ELF::Sword CRAZY_UNUSED addend = rela->r_addend;
+
+ const ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_);
+
+ RLOG(" rela reloc=%p offset=%p type=%d addend=%p\n",
+ reloc,
+ rela->r_offset,
+ rela_type,
+ addend);
+
+ // Apply the relocation.
+ ELF::Addr* CRAZY_UNUSED target = reinterpret_cast<ELF::Addr*>(reloc);
+ switch (rela_type) {
+#ifdef __aarch64__
+ case R_AARCH64_JUMP_SLOT:
+ RLOG(" R_AARCH64_JUMP_SLOT target=%p addr=%p\n",
+ target,
+ sym_addr + addend);
+ *target = sym_addr + addend;
+ break;
+
+ case R_AARCH64_GLOB_DAT:
+ RLOG(" R_AARCH64_GLOB_DAT target=%p addr=%p\n",
+ target,
+ sym_addr + addend);
+ *target = sym_addr + addend;
+ break;
+
+ case R_AARCH64_ABS64:
+ RLOG(" R_AARCH64_ABS64 target=%p (%p) addr=%p\n",
+ target,
+ *target,
+ sym_addr + addend);
+ *target += sym_addr + addend;
+ break;
+
+ case R_AARCH64_RELATIVE:
+ RLOG(" R_AARCH64_RELATIVE target=%p (%p) bias=%p\n",
+ target,
+ *target,
+ load_bias_ + addend);
+ if (__builtin_expect(rela_symbol, 0)) {
+ *error = "Invalid relative relocation with symbol";
+ return false;
+ }
+ *target = load_bias_ + addend;
+ break;
+
+ case R_AARCH64_COPY:
+ // NOTE: These relocations are forbidden in shared libraries.
+ RLOG(" R_AARCH64_COPY\n");
+ *error = "Invalid R_AARCH64_COPY relocation in shared library";
+ return false;
+#endif // __aarch64__
+
+ default:
+ error->Format("Invalid relocation type (%d)", rela_type);
+ return false;
+ }
+
+ return true;
+}
+
+bool ElfRelocations::ApplyRelReloc(const ELF::Rel* rel,
+ ELF::Addr sym_addr,
+ bool resolved CRAZY_UNUSED,
+ Error* error) {
+ const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
+ const ELF::Word CRAZY_UNUSED rel_symbol = ELF_R_SYM(rel->r_info);
+
+ const ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
+
+ RLOG(" rel reloc=%p offset=%p type=%d\n", reloc, rel->r_offset, rel_type);
+
+ // Apply the relocation.
+ ELF::Addr* CRAZY_UNUSED target = reinterpret_cast<ELF::Addr*>(reloc);
+ switch (rel_type) {
+#ifdef __arm__
+ case R_ARM_JUMP_SLOT:
+ RLOG(" R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr);
+ *target = sym_addr;
+ break;
+
+ case R_ARM_GLOB_DAT:
+ RLOG(" R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr);
+ *target = sym_addr;
+ break;
+
+ case R_ARM_ABS32:
+ RLOG(" R_ARM_ABS32 target=%p (%p) addr=%p\n",
+ target,
+ *target,
+ sym_addr);
+ *target += sym_addr;
+ break;
+
+ case R_ARM_REL32:
+ RLOG(" R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n",
+ target,
+ *target,
+ sym_addr,
+ rel->r_offset);
+ *target += sym_addr - rel->r_offset;
+ break;
+
+ case R_ARM_RELATIVE:
+ RLOG(" R_ARM_RELATIVE target=%p (%p) bias=%p\n",
+ target,
+ *target,
+ load_bias_);
+ if (__builtin_expect(rel_symbol, 0)) {
+ *error = "Invalid relative relocation with symbol";
+ return false;
+ }
+ *target += load_bias_;
+ break;
+
+ case R_ARM_COPY:
+ // NOTE: These relocations are forbidden in shared libraries.
+ // The Android linker has special code to deal with this, which
+ // is not needed here.
+ RLOG(" R_ARM_COPY\n");
+ *error = "Invalid R_ARM_COPY relocation in shared library";
+ return false;
+#endif // __arm__
+
+#ifdef __i386__
+ case R_386_JMP_SLOT:
+ *target = sym_addr;
+ break;
+
+ case R_386_GLOB_DAT:
+ *target = sym_addr;
+ break;
+
+ case R_386_RELATIVE:
+ if (rel_symbol) {
+ *error = "Invalid relative relocation with symbol";
+ return false;
+ }
+ *target += load_bias_;
+ break;
+
+ case R_386_32:
+ *target += sym_addr;
+ break;
+
+ case R_386_PC32:
+ *target += (sym_addr - reloc);
+ break;
+#endif // __i386__
+
+#ifdef __mips__
+ case R_MIPS_REL32:
+ if (resolved)
+ *target += sym_addr;
+ else
+ *target += load_bias_;
+ break;
+#endif // __mips__
+
+ default:
+ error->Format("Invalid relocation type (%d)", rel_type);
+ return false;
+ }
+
+ return true;
+}
+
+bool ElfRelocations::ResolveSymbol(ELF::Word rel_type,
+ ELF::Word rel_symbol,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ ELF::Addr reloc,
+ ELF::Addr* sym_addr,
+ Error* error) {
+ const char* sym_name = symbols->LookupNameById(rel_symbol);
+ RLOG(" symbol name='%s'\n", sym_name);
+ void* address = resolver->Lookup(sym_name);
+
+ if (address) {
+ // The symbol was found, so compute its address.
+ RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, sym_name, address);
+ *sym_addr = reinterpret_cast<ELF::Addr>(address);
+ return true;
+ }
+
+ // The symbol was not found. Normally this is an error except
+ // if this is a weak reference.
+ if (!symbols->IsWeakById(rel_symbol)) {
+ error->Format("Could not find symbol '%s'", sym_name);
+ return false;
+ }
+
+ RLOG("%s: weak reference to unresolved symbol %s\n", __FUNCTION__, sym_name);
+
+ // IHI0044C AAELF 4.5.1.1:
+ // Libraries are not searched to resolve weak references.
+ // It is not an error for a weak reference to remain
+ // unsatisfied.
+ //
+ // During linking, the value of an undefined weak reference is:
+ // - Zero if the relocation type is absolute
+ // - The address of the place if the relocation is pc-relative
+ // - The address of nominal base address if the relocation
+ // type is base-relative.
+ RelocationType r = GetRelocationType(rel_type);
+ if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE) {
+ *sym_addr = 0;
+ return true;
+ }
+
+ if (r == RELOCATION_TYPE_PC_RELATIVE) {
+ *sym_addr = reloc;
+ return true;
+ }
+
+ error->Format(
+ "Invalid weak relocation type (%d) for unknown symbol '%s'",
+ r,
+ sym_name);
+ return false;
+}
+
+bool ElfRelocations::ApplyRelRelocs(const ELF::Rel* rel,
+ size_t rel_count,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error) {
RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count);
if (!rel)
return true;
for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
- unsigned rel_type = ELF_R_TYPE(rel->r_info);
- unsigned rel_symbol = ELF_R_SYM(rel->r_info);
+ const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
+ const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info);
ELF::Addr sym_addr = 0;
ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
@@ -263,147 +574,68 @@ bool ElfRelocations::ApplyRelocs(const ELF::Rel* rel,
if (rel_type == 0)
continue;
- bool CRAZY_UNUSED resolved = false;
+ bool resolved = false;
// If this is a symbolic relocation, compute the symbol's address.
if (__builtin_expect(rel_symbol != 0, 0)) {
- const char* sym_name = symbols->LookupNameById(rel_symbol);
- RLOG(" symbol name='%s'\n", sym_name);
- void* address = resolver->Lookup(sym_name);
- if (address) {
- // The symbol was found, so compute its address.
- RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, sym_name, address);
- resolved = true;
- sym_addr = reinterpret_cast<ELF::Addr>(address);
- } else {
- // The symbol was not found. Normally this is an error except
- // if this is a weak reference.
- if (!symbols->IsWeakById(rel_symbol)) {
- error->Format("Could not find symbol '%s'", sym_name);
- return false;
- }
-
- resolved = true;
- RLOG("%s: weak reference to unresolved symbol %s\n",
- __FUNCTION__,
- sym_name);
-
- // IHI0044C AAELF 4.5.1.1:
- // Libraries are not searched to resolve weak references.
- // It is not an error for a weak reference to remain
- // unsatisfied.
- //
- // During linking, the value of an undefined weak reference is:
- // - Zero if the relocation type is absolute
- // - The address of the place if the relocation is pc-relative
- // - The address of nominal base address if the relocation
- // type is base-relative.
- RelocationType r = GetRelocationType(rel_type);
- if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE)
- sym_addr = 0;
- else if (r == RELOCATION_TYPE_PC_RELATIVE)
- sym_addr = reloc;
- else {
- error->Format(
- "Invalid weak relocation type (%d) for unknown symbol '%s'",
- r,
- sym_name);
- return false;
- }
- }
+ resolved = ResolveSymbol(rel_type,
+ rel_symbol,
+ symbols,
+ resolver,
+ reloc,
+ &sym_addr,
+ error);
}
- // Apply the relocation.
- ELF::Addr* target = reinterpret_cast<ELF::Addr*>(reloc);
- switch (rel_type) {
-#ifdef __arm__
- case R_ARM_JUMP_SLOT:
- RLOG(" R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr);
- *target = sym_addr;
- break;
-
- case R_ARM_GLOB_DAT:
- RLOG(" R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr);
- *target = sym_addr;
- break;
-
- case R_ARM_ABS32:
- RLOG(" R_ARM_ABS32 target=%p (%p) addr=%p\n",
- target,
- *target,
- sym_addr);
- *target += sym_addr;
- break;
-
- case R_ARM_REL32:
- RLOG(" R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n",
- target,
- *target,
- sym_addr,
- rel->r_offset);
- *target += sym_addr - rel->r_offset;
- break;
-
- case R_ARM_RELATIVE:
- RLOG(" R_ARM_RELATIVE target=%p (%p) bias=%p\n",
- target,
- *target,
- load_bias_);
- if (__builtin_expect(rel_symbol, 0)) {
- *error = "Invalid relative relocation with symbol";
- return false;
- }
- *target += load_bias_;
- break;
+ if (!ApplyRelReloc(rel, sym_addr, resolved, error))
+ return false;
+ }
- case R_ARM_COPY:
- // NOTE: These relocations are forbidden in shared libraries.
- // The Android linker has special code to deal with this, which
- // is not needed here.
- RLOG(" R_ARM_COPY\n");
- *error = "Invalid R_ARM_COPY relocation in shared library";
- return false;
-#endif // __arm__
+ return true;
+}
-#ifdef __i386__
- case R_386_JMP_SLOT:
- *target = sym_addr;
- break;
+bool ElfRelocations::ApplyRelaRelocs(const ELF::Rela* rela,
+ size_t rela_count,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error) {
+ RLOG("%s: rela=%p rela_count=%d\n", __FUNCTION__, rela, rela_count);
- case R_386_GLOB_DAT:
- *target = sym_addr;
- break;
+ if (!rela)
+ return true;
- case R_386_RELATIVE:
- if (rel_symbol) {
- *error = "Invalid relative relocation with symbol";
- return false;
- }
- *target += load_bias_;
- break;
+ for (size_t rel_n = 0; rel_n < rela_count; rela++, rel_n++) {
+ const ELF::Word rel_type = ELF_R_TYPE(rela->r_info);
+ const ELF::Word rel_symbol = ELF_R_SYM(rela->r_info);
- case R_386_32:
- *target += sym_addr;
- break;
+ ELF::Addr sym_addr = 0;
+ ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_);
+ RLOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n",
+ rel_n + 1,
+ rela_count,
+ reloc,
+ rela->r_offset,
+ rel_type,
+ rel_symbol);
- case R_386_PC32:
- *target += (sym_addr - reloc);
- break;
-#endif // __i386__
+ if (rel_type == 0)
+ continue;
-#ifdef __mips__
- case R_MIPS_REL32:
- if (resolved)
- *target += sym_addr;
- else
- *target += load_bias_;
- break;
-#endif // __mips__
+ bool resolved = false;
- default:
- error->Format("Invalid relocation type (%d)", rel_type);
- return false;
+ // If this is a symbolic relocation, compute the symbol's address.
+ if (__builtin_expect(rel_symbol != 0, 0)) {
+ resolved = ResolveSymbol(rel_type,
+ rel_symbol,
+ symbols,
+ resolver,
+ reloc,
+ &sym_addr,
+ error);
}
+
+ if (!ApplyRelaReloc(rela, sym_addr, resolved, error))
+ return false;
}
return true;
@@ -457,27 +689,58 @@ bool ElfRelocations::RelocateMipsGot(const ElfSymbols* symbols,
}
#endif // __mips__
-void ElfRelocations::CopyAndRelocate(size_t src_addr,
- size_t dst_addr,
- size_t map_addr,
- size_t size) {
- // First, a straight copy.
- ::memcpy(reinterpret_cast<void*>(dst_addr),
- reinterpret_cast<void*>(src_addr),
- size);
+void ElfRelocations::AdjustRelocation(ELF::Word rel_type,
+ ELF::Addr src_reloc,
+ size_t dst_delta,
+ size_t map_delta) {
+ ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta);
+
+ switch (rel_type) {
+#ifdef __arm__
+ case R_ARM_RELATIVE:
+ *dst_ptr += map_delta;
+ break;
+#endif // __arm__
+#ifdef __aarch64__
+ case R_AARCH64_RELATIVE:
+ *dst_ptr += map_delta;
+ break;
+#endif // __aarch64__
+
+#ifdef __i386__
+ case R_386_RELATIVE:
+ *dst_ptr += map_delta;
+ break;
+#endif
+
+#ifdef __mips__
+ case R_MIPS_REL32:
+ *dst_ptr += map_delta;
+ break;
+#endif
+ default:
+ ;
+ }
+}
+
+void ElfRelocations::RelocateRela(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size) {
// Add this value to each source address to get the corresponding
// destination address.
- size_t dst_delta = dst_addr - src_addr;
- size_t map_delta = map_addr - src_addr;
+ const size_t dst_delta = dst_addr - src_addr;
+ const size_t map_delta = map_addr - src_addr;
// Ignore PLT relocations, which all target symbols (ignored here).
- const ELF::Rel* rel = relocations_;
- const ELF::Rel* rel_limit = rel + relocations_count_;
+ const ELF::Rela* rel = reinterpret_cast<ELF::Rela*>(relocations_);
+ const size_t relocations_count = relocations_size_ / sizeof(ELF::Rela);
+ const ELF::Rela* rel_limit = rel + relocations_count;
for (; rel < rel_limit; ++rel) {
- unsigned rel_type = ELF_R_TYPE(rel->r_info);
- unsigned rel_symbol = ELF_R_SYM(rel->r_info);
+ const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
+ const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info);
ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
if (rel_type == 0 || rel_symbol != 0) {
@@ -490,32 +753,65 @@ void ElfRelocations::CopyAndRelocate(size_t src_addr,
continue;
}
- ELF::Addr* dst_ptr = reinterpret_cast<ELF::Addr*>(src_reloc + dst_delta);
+ AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta);
+ }
+}
- switch (rel_type) {
-#ifdef __arm__
- case R_ARM_RELATIVE:
- *dst_ptr += map_delta;
- break;
-#endif // __arm__
+void ElfRelocations::RelocateRel(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size) {
+ // Add this value to each source address to get the corresponding
+ // destination address.
+ const size_t dst_delta = dst_addr - src_addr;
+ const size_t map_delta = map_addr - src_addr;
-#ifdef __i386__
- case R_386_RELATIVE:
- *dst_ptr += map_delta;
- break;
-#endif
+ // Ignore PLT relocations, which all target symbols (ignored here).
+ const ELF::Rel* rel = reinterpret_cast<ELF::Rel*>(relocations_);
+ const size_t relocations_count = relocations_size_ / sizeof(ELF::Rel);
+ const ELF::Rel* rel_limit = rel + relocations_count;
-#ifdef __mips__
- case R_MIPS_REL32:
- *dst_ptr += map_delta;
- break;
-#endif
- default:
- ;
+ for (; rel < rel_limit; ++rel) {
+ const ELF::Word rel_type = ELF_R_TYPE(rel->r_info);
+ const ELF::Word rel_symbol = ELF_R_SYM(rel->r_info);
+ ELF::Addr src_reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
+
+ if (rel_type == 0 || rel_symbol != 0) {
+ // Ignore empty and symbolic relocations
+ continue;
}
+
+ if (src_reloc < src_addr || src_reloc >= src_addr + size) {
+ // Ignore entries that don't relocate addresses inside the source section.
+ continue;
+ }
+
+ AdjustRelocation(rel_type, src_reloc, dst_delta, map_delta);
}
+}
+
+void ElfRelocations::CopyAndRelocate(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size) {
+ // First, a straight copy.
+ ::memcpy(reinterpret_cast<void*>(dst_addr),
+ reinterpret_cast<void*>(src_addr),
+ size);
+
+ // Relocate relocations.
+ if (relocations_type_ == DT_REL)
+ RelocateRel(src_addr, dst_addr, map_addr, size);
+
+ else if (relocations_type_ == DT_RELA)
+ RelocateRela(src_addr, dst_addr, map_addr, size);
#ifdef __mips__
+ // Add this value to each source address to get the corresponding
+ // destination address.
+ const size_t dst_delta = dst_addr - src_addr;
+ const size_t map_delta = map_addr - src_addr;
+
// Only relocate local GOT entries.
ELF::Addr* got = plt_got_;
if (got) {
@@ -528,8 +824,6 @@ void ElfRelocations::CopyAndRelocate(size_t src_addr,
}
}
#endif
-
- // Done
}
} // namespace crazy
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.h b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.h
index e3ef893..edf2f4b 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_relocations.h
@@ -56,11 +56,43 @@ class ElfRelocations {
size_t size);
private:
- bool ApplyRelocs(const ELF::Rel* relocs,
- size_t relocs_count,
- const ElfSymbols* symbols,
- SymbolResolver* resolver,
- Error* error);
+ bool ResolveSymbol(unsigned rel_type,
+ unsigned rel_symbol,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ ELF::Addr reloc,
+ ELF::Addr* sym_addr,
+ Error* error);
+ bool ApplyRelaReloc(const ELF::Rela* rela,
+ ELF::Addr sym_addr,
+ bool resolved,
+ Error* error);
+ bool ApplyRelReloc(const ELF::Rel* rel,
+ ELF::Addr sym_addr,
+ bool resolved,
+ Error* error);
+ bool ApplyRelaRelocs(const ELF::Rela* relocs,
+ size_t relocs_count,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error);
+ bool ApplyRelRelocs(const ELF::Rel* relocs,
+ size_t relocs_count,
+ const ElfSymbols* symbols,
+ SymbolResolver* resolver,
+ Error* error);
+ void AdjustRelocation(ELF::Word rel_type,
+ ELF::Addr src_reloc,
+ size_t dst_delta,
+ size_t map_delta);
+ void RelocateRela(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size);
+ void RelocateRel(size_t src_addr,
+ size_t dst_addr,
+ size_t map_addr,
+ size_t size);
#if defined(__mips__)
bool RelocateMipsGot(const ElfSymbols* symbols,
@@ -72,12 +104,13 @@ class ElfRelocations {
size_t phdr_count_;
size_t load_bias_;
- const ELF::Rel* plt_relocations_;
- size_t plt_relocations_count_;
+ ELF::Addr relocations_type_;
+ ELF::Addr plt_relocations_;
+ size_t plt_relocations_size_;
ELF::Addr* plt_got_;
- const ELF::Rel* relocations_;
- size_t relocations_count_;
+ ELF::Addr relocations_;
+ size_t relocations_size_;
#if defined(__mips__)
// MIPS-specific relocation fields.
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.cpp
index 28c39ce..b03e017 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.cpp
@@ -35,7 +35,7 @@ bool ElfSymbols::Init(const ElfView* view) {
case DT_HASH:
LOG(" DT_HASH addr=%p\n", dyn_addr);
{
- ELF::Addr* data = reinterpret_cast<uintptr_t*>(dyn_addr);
+ ELF::Word* data = reinterpret_cast<ELF::Word*>(dyn_addr);
hash_bucket_size_ = data[0];
hash_chain_size_ = data[1];
hash_bucket_ = data + 2;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.h b/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.h
index 13692a0..5c148b6 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_symbols.h
@@ -66,9 +66,9 @@ class ElfSymbols {
private:
const ELF::Sym* symbol_table_;
const char* string_table_;
- ELF::Addr* hash_bucket_;
+ ELF::Word* hash_bucket_;
size_t hash_bucket_size_;
- ELF::Addr* hash_chain_;
+ ELF::Word* hash_chain_;
size_t hash_chain_size_;
};
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.cpp
index 9066ef5..89d66e9 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.cpp
@@ -12,12 +12,12 @@
namespace crazy {
-bool ElfView::InitUnmapped(size_t load_address,
+bool ElfView::InitUnmapped(ELF::Addr load_address,
const ELF::Phdr* phdr,
size_t phdr_count,
Error* error) {
// Compute load size and bias.
- size_t min_vaddr = 0;
+ ELF::Addr min_vaddr = 0;
load_size_ = phdr_table_get_load_size(phdr, phdr_count, &min_vaddr, NULL);
if (load_size_ == 0) {
*error = "Invalid program header table";
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.h b/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.h
index 68cc219..6c62d1d 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_view.h
@@ -35,7 +35,7 @@ class ElfView {
// appropriate values. Note that functions phdr() or dynamic() will always
// return an address relative to |load_address|, even if the binary was
// not loaded yet in the process.
- bool InitUnmapped(size_t load_address,
+ bool InitUnmapped(ELF::Addr load_address,
const ELF::Phdr* phdr,
size_t phdr_count,
Error* error);
@@ -75,7 +75,7 @@ class ElfView {
ELF::Addr GetValue() const { return dyn_->d_un.d_val; }
ELF::Addr* GetValuePointer() const {
- return const_cast<ELF::Addr*>(&dyn_->d_un.d_val);
+ return const_cast<ELF::Addr*>(&dyn_->d_un.d_ptr);
}
uintptr_t GetOffset() const { return dyn_->d_un.d_ptr; }
@@ -98,8 +98,8 @@ class ElfView {
size_t phdr_count_;
const ELF::Dyn* dynamic_;
size_t dynamic_count_;
- size_t dynamic_flags_;
- size_t load_address_;
+ ELF::Word dynamic_flags_;
+ ELF::Addr load_address_;
size_t load_size_;
size_t load_bias_;
};
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_proc_maps_unittest.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_proc_maps_unittest.cpp
index 852f59c..7276523 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_proc_maps_unittest.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_proc_maps_unittest.cpp
@@ -4,6 +4,7 @@
#include "crazy_linker_proc_maps.h"
+#include <limits.h>
#include <minitest/minitest.h>
#include "crazy_linker_system_mock.h"
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
index 92ade0b0..e016499 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
@@ -8,7 +8,6 @@
#include <stdlib.h>
#include <sys/mman.h>
#include <elf.h>
-#include <sys/exec_elf.h>
#include "crazy_linker_ashmem.h"
#include "crazy_linker_debug.h"
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
index 031a5d6..0f730a5 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
@@ -170,8 +170,8 @@ class SharedLibrary {
ElfView view_;
ElfSymbols symbols_;
- size_t relro_start_;
- size_t relro_size_;
+ ELF::Addr relro_start_;
+ ELF::Addr relro_size_;
bool relro_used_;
SharedLibrary* list_next_;
@@ -205,4 +205,4 @@ class SharedLibrary {
} // namespace crazy
-#endif // CRAZY_LINKER_SHARED_LIBRARY_H \ No newline at end of file
+#endif // CRAZY_LINKER_SHARED_LIBRARY_H
diff --git a/third_party/android_crazy_linker/src/src/elf_traits.h b/third_party/android_crazy_linker/src/src/elf_traits.h
index ae038be..fa6b780 100644
--- a/third_party/android_crazy_linker/src/src/elf_traits.h
+++ b/third_party/android_crazy_linker/src/src/elf_traits.h
@@ -16,28 +16,48 @@ struct ELF {
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Phdr Phdr;
typedef Elf32_Word Word;
+ typedef Elf32_Sword Sword;
typedef Elf32_Addr Addr;
typedef Elf32_Dyn Dyn;
typedef Elf32_Sym Sym;
typedef Elf32_Rel Rel;
+ typedef Elf32_Rela Rela;
typedef Elf32_auxv_t auxv_t;
enum { kElfClass = ELFCLASS32 };
enum { kElfBits = 32 };
+
+#ifndef ELF_R_TYPE
+#define ELF_R_TYPE ELF32_R_TYPE
+#endif
+
+#ifndef ELF_R_SYM
+#define ELF_R_SYM ELF32_R_SYM
+#endif
};
#elif __SIZEOF_POINTER__ == 8
struct ELF {
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Phdr Phdr;
typedef Elf64_Word Word;
+ typedef Elf64_Sword Sword;
typedef Elf64_Addr Addr;
typedef Elf64_Dyn Dyn;
typedef Elf64_Sym Sym;
typedef Elf64_Rel Rel;
+ typedef Elf64_Rela Rela;
typedef Elf64_auxv_t auxv_t;
enum { kElfClass = ELFCLASS64 };
enum { kElfBits = 64 };
+
+#ifndef ELF_R_TYPE
+#define ELF_R_TYPE ELF64_R_TYPE
+#endif
+
+#ifndef ELF_R_SYM
+#define ELF_R_SYM ELF64_R_SYM
+#endif
};
#else
#error "Unsupported target CPU bitness"
@@ -47,8 +67,10 @@ struct ELF {
#define ELF_MACHINE EM_ARM
#elif defined(__i386__)
#define ELF_MACHINE EM_386
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__) // mips64el defines __mips__ too
#define ELF_MACHINE EM_MIPS
+#elif defined(__aarch64__)
+#define ELF_MACHINE EM_AARCH64
#else
#error "Unsupported target CPU architecture"
#endif
diff --git a/third_party/android_crazy_linker/src/src/linker_phdr.cpp b/third_party/android_crazy_linker/src/src/linker_phdr.cpp
index 53955d4..d43a358 100644
--- a/third_party/android_crazy_linker/src/src/linker_phdr.cpp
+++ b/third_party/android_crazy_linker/src/src/linker_phdr.cpp
@@ -142,7 +142,7 @@ size_t phdr_table_get_load_size(const ELF::Phdr* phdr_table,
size_t phdr_count,
ELF::Addr* out_min_vaddr,
ELF::Addr* out_max_vaddr) {
- ELF::Addr min_vaddr = 0xFFFFFFFFU;
+ ELF::Addr min_vaddr = ~static_cast<ELF::Addr>(0);
ELF::Addr max_vaddr = 0x00000000U;
bool found_pt_load = false;
diff --git a/third_party/android_crazy_linker/src/tests/bar_with_relro.cpp b/third_party/android_crazy_linker/src/tests/bar_with_relro.cpp
index be5681c..a0377c9 100644
--- a/third_party/android_crazy_linker/src/tests/bar_with_relro.cpp
+++ b/third_party/android_crazy_linker/src/tests/bar_with_relro.cpp
@@ -30,7 +30,7 @@ extern "C" void Bar() {
for (size_t n = 0; n < sizeof(kStrings) / sizeof(kStrings[0]); ++n) {
const char* ptr = kStrings[n];
if (strcmp(ptr, "another example string")) {
- printf("%s: Bad string at offset=%d\n", __FUNCTION__, n);
+ printf("%s: Bad string at offset=%zu\n", __FUNCTION__, n);
exit(1);
}
}
diff --git a/third_party/android_crazy_linker/src/tests/foo_with_relro.cpp b/third_party/android_crazy_linker/src/tests/foo_with_relro.cpp
index 2980ba2..3e447a9 100644
--- a/third_party/android_crazy_linker/src/tests/foo_with_relro.cpp
+++ b/third_party/android_crazy_linker/src/tests/foo_with_relro.cpp
@@ -21,7 +21,7 @@ extern "C" void Foo() {
for (size_t n = 0; n < sizeof(kStrings) / sizeof(kStrings[0]); ++n) {
const char* ptr = kStrings[n];
if (strcmp(ptr, "some example string")) {
- printf("%s: Bad string at offset=%d\n", __FUNCTION__, n);
+ printf("%s: Bad string at offset=%zu\n", __FUNCTION__, n);
exit(1);
}
}
diff --git a/third_party/android_crazy_linker/src/tests/foo_with_static_constructor.cpp b/third_party/android_crazy_linker/src/tests/foo_with_static_constructor.cpp
index b5ff3d2..0f43eaa 100644
--- a/third_party/android_crazy_linker/src/tests/foo_with_static_constructor.cpp
+++ b/third_party/android_crazy_linker/src/tests/foo_with_static_constructor.cpp
@@ -7,9 +7,16 @@
// like __aeabi_atexit(), which are not normally returned by
// a call to dlsym().
+// Libc is not required to copy strings passed to putenv(). If it does
+// not then env pointers become invalid when rodata is unmapped on
+// library unload. To guard against this, putenv() strings are first
+// strdup()'ed. This is a mild memory leak.
+
#include <stdlib.h>
+#ifdef __arm__
extern "C" void __aeabi_atexit(void*);
+#endif
class A {
public:
@@ -17,17 +24,17 @@ class A {
x_ = rand();
const char* env = getenv("TEST_VAR");
if (!env || strcmp(env, "INIT"))
- putenv("TEST_VAR=LOAD_ERROR");
+ putenv(strdup("TEST_VAR=LOAD_ERROR"));
else
- putenv("TEST_VAR=LOADED");
+ putenv(strdup("TEST_VAR=LOADED"));
}
~A() {
const char* env = getenv("TEST_VAR");
if (!env || strcmp(env, "LOADED"))
- putenv("TEST_VAR=UNLOAD_ERROR");
+ putenv(strdup("TEST_VAR=UNLOAD_ERROR"));
else
- putenv("TEST_VAR=UNLOADED");
+ putenv(strdup("TEST_VAR=UNLOADED"));
}
int Get() const { return x_; }
diff --git a/third_party/android_crazy_linker/src/tests/test_util.h b/third_party/android_crazy_linker/src/tests/test_util.h
index e6e722c..dd50129 100644
--- a/third_party/android_crazy_linker/src/tests/test_util.h
+++ b/third_party/android_crazy_linker/src/tests/test_util.h
@@ -23,6 +23,10 @@
#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS // to get PRI and SCN in 32-bit inttypes.h
+#endif
+#include <inttypes.h>
namespace {
@@ -341,8 +345,8 @@ inline void CheckRelroMaps(int expected_count) {
if (!strstr(line, " r--"))
Panic("Shared RELRO mapping is not readonly!\n");
// Check that they can't be remapped read-write.
- unsigned vma_start, vma_end;
- if (sscanf(line, "%x-%x", &vma_start, &vma_end) != 2)
+ uint64_t vma_start, vma_end;
+ if (sscanf(line, "%" SCNx64 "-%" SCNx64, &vma_start, &vma_end) != 2)
Panic("Could not parse VM address range!\n");
int ret = ::mprotect(
(void*)vma_start, vma_end - vma_start, PROT_READ | PROT_WRITE);