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/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;
 }