linker: simpler encoding for SHT_RELR sections.

This change modifies the encoding used in SHT_RELR sections to a simpler
version that gives better results. This encoding was suggested by Andrew
Grieve and is described in this post on generic-abi@googlegroups.com:
    https://groups.google.com/d/msg/generic-abi/bX460iggiKg/Pi9aSwwABgAJ

Bug: None
Test: Built image for marlin, flashed on device, ran arm and
      aarch64 binaries containing '.relr.dyn' sections using
      the new encoding.

Change-Id: I266affe0fbad91dc375995985a221cb02499447b
diff --git a/libc/include/elf.h b/libc/include/elf.h
index bda11f5..a8d62db 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -194,13 +194,9 @@
   Elf64_Word vna_next;
 } Elf64_Vernaux;
 
-/* Relocation table entry for relative (in section of type SHT_RELR).  */
+/* Relocation table entry for relative (in section of type SHT_RELR). */
 typedef Elf32_Word Elf32_Relr;
 typedef Elf64_Xword Elf64_Relr;
-#define ELF32_R_JUMP(val) ((val) >> 24)
-#define ELF32_R_BITS(val) ((val) & 0xffffff)
-#define ELF64_R_JUMP(val) ((val) >> 56)
-#define ELF64_R_BITS(val) ((val) & 0xffffffffffffff)
 
 /* http://www.sco.com/developers/gabi/latest/ch5.dynamic.html */
 #define DF_ORIGIN     0x00000001
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 05efc55..7489721 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2595,31 +2595,46 @@
   return true;
 }
 
+void soinfo::apply_relr_reloc(ElfW(Addr) offset) {
+  ElfW(Addr) address = offset + load_bias;
+  *reinterpret_cast<ElfW(Addr)*>(address) += load_bias;
+}
+
 // Process relocations in SHT_RELR section (experimental).
-// See the original proposal for details of the encoding:
-// - https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
+// Details of the encoding are described in this post:
+//   https://groups.google.com/d/msg/generic-abi/bX460iggiKg/Pi9aSwwABgAJ
 bool soinfo::relocate_relr() {
   ElfW(Relr)* begin = relr_;
   ElfW(Relr)* end = relr_ + relr_count_;
+  constexpr size_t wordsize = sizeof(ElfW(Addr));
 
-  ElfW(Addr) offset = 0;
+  ElfW(Addr) base = 0;
   for (ElfW(Relr)* current = begin; current < end; ++current) {
-    ElfW(Addr) jump = ELFW(R_JUMP)(*current);
-    ElfW(Addr) bits = ELFW(R_BITS)(*current);
-    offset += jump * sizeof(ElfW(Addr));
-    if (jump == 0) {
-      ++current;
-      offset = *current;
+    ElfW(Relr) entry = *current;
+    ElfW(Addr) offset;
+
+    if ((entry&1) == 0) {
+      // Even entry: encodes the offset for next relocation.
+      offset = static_cast<ElfW(Addr)>(entry);
+      apply_relr_reloc(offset);
+      // Set base offset for subsequent bitmap entries.
+      base = offset + wordsize;
+      continue;
     }
-    ElfW(Addr) r_offset = offset;
-    for (; bits != 0; bits >>= 1) {
-      if ((bits&1) != 0) {
-        ElfW(Addr) reloc = static_cast<ElfW(Addr)>(r_offset + load_bias);
-        ElfW(Addr) addend = *reinterpret_cast<ElfW(Addr)*>(reloc);
-        *reinterpret_cast<ElfW(Addr)*>(reloc) = (load_bias + addend);
+
+    // Odd entry: encodes bitmap for relocations starting at base.
+    offset = base;
+    while (entry != 0) {
+      entry >>= 1;
+      if ((entry&1) != 0) {
+        apply_relr_reloc(offset);
       }
-      r_offset += sizeof(ElfW(Addr));
+      offset += wordsize;
     }
+
+    // Advance base offset by 63 words for 64-bit platforms,
+    // or 31 words for 32-bit platforms.
+    base += (8*wordsize - 1) * wordsize;
   }
   return true;
 }
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index cb607a9..447c7c3 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -310,6 +310,7 @@
   bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
                 const soinfo_list_t& global_group, const soinfo_list_t& local_group);
   bool relocate_relr();
+  void apply_relr_reloc(ElfW(Addr) offset);
 
  private:
   // This part of the structure is only available