Merge "linker: add experimental support for SHT_RELR sections."
diff --git a/libc/include/elf.h b/libc/include/elf.h
index 37450b2..bda11f5 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -194,6 +194,14 @@
   Elf64_Word vna_next;
 } Elf64_Vernaux;
 
+/* 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
 #define DF_SYMBOLIC   0x00000002
@@ -242,6 +250,13 @@
 #define DT_PREINIT_ARRAY 32
 #define DT_PREINIT_ARRAYSZ 33
 
+/* Experimental support for SHT_RELR sections. For details, see proposal
+   at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg */
+#define DT_RELR 0x6fffe000
+#define DT_RELRSZ 0x6fffe001
+#define DT_RELRENT 0x6fffe003
+#define DT_RELRCOUNT 0x6fffe005
+
 /* Android compressed rel/rela sections */
 #define DT_ANDROID_REL (DT_LOOS + 2)
 #define DT_ANDROID_RELSZ (DT_LOOS + 3)
@@ -494,6 +509,10 @@
 #define SHT_LOOS 0x60000000
 #define SHT_HIOS 0x6fffffff
 
+/* Experimental support for SHT_RELR sections. For details, see proposal
+   at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg */
+#define SHT_RELR 0x6fffff00
+
 /* http://www.sco.com/developers/gabi/latest/ch4.symtab.html */
 #define STN_UNDEF 0
 
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 63415d8..05efc55 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2595,6 +2595,35 @@
   return true;
 }
 
+// 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
+bool soinfo::relocate_relr() {
+  ElfW(Relr)* begin = relr_;
+  ElfW(Relr)* end = relr_ + relr_count_;
+
+  ElfW(Addr) offset = 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(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);
+      }
+      r_offset += sizeof(ElfW(Addr));
+    }
+  }
+  return true;
+}
+
 #if !defined(__mips__)
 #if defined(USE_RELA)
 static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
@@ -3151,7 +3180,7 @@
         }
         break;
 
-      // ignored (see DT_RELCOUNT comments for details)
+      // Ignored (see DT_RELCOUNT comments for details).
       case DT_RELACOUNT:
         break;
 
@@ -3212,6 +3241,25 @@
         return false;
 
 #endif
+      case DT_RELR:
+        relr_ = reinterpret_cast<ElfW(Relr)*>(load_bias + d->d_un.d_ptr);
+        break;
+
+      case DT_RELRSZ:
+        relr_count_ = d->d_un.d_val / sizeof(ElfW(Relr));
+        break;
+
+      case DT_RELRENT:
+        if (d->d_un.d_val != sizeof(ElfW(Relr))) {
+          DL_ERR("invalid DT_RELRENT: %zd", static_cast<size_t>(d->d_un.d_val));
+          return false;
+        }
+        break;
+
+      // Ignored (see DT_RELCOUNT comments for details).
+      case DT_RELRCOUNT:
+        break;
+
       case DT_INIT:
         init_func_ = reinterpret_cast<linker_ctor_function_t>(load_bias + d->d_un.d_ptr);
         DEBUG("%s constructors (DT_INIT) found at %p", get_realpath(), init_func_);
@@ -3504,16 +3552,23 @@
     }
   }
 
+  if (relr_ != nullptr) {
+    DEBUG("[ relocating %s relr ]", get_realpath());
+    if (!relocate_relr()) {
+      return false;
+    }
+  }
+
 #if defined(USE_RELA)
   if (rela_ != nullptr) {
-    DEBUG("[ relocating %s ]", get_realpath());
+    DEBUG("[ relocating %s rela ]", get_realpath());
     if (!relocate(version_tracker,
             plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) {
       return false;
     }
   }
   if (plt_rela_ != nullptr) {
-    DEBUG("[ relocating %s plt ]", get_realpath());
+    DEBUG("[ relocating %s plt rela ]", get_realpath());
     if (!relocate(version_tracker,
             plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) {
       return false;
@@ -3521,14 +3576,14 @@
   }
 #else
   if (rel_ != nullptr) {
-    DEBUG("[ relocating %s ]", get_realpath());
+    DEBUG("[ relocating %s rel ]", get_realpath());
     if (!relocate(version_tracker,
             plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) {
       return false;
     }
   }
   if (plt_rel_ != nullptr) {
-    DEBUG("[ relocating %s plt ]", get_realpath());
+    DEBUG("[ relocating %s plt rel ]", get_realpath());
     if (!relocate(version_tracker,
             plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) {
       return false;
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 91118b0..cb607a9 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -62,7 +62,7 @@
                                          // unset.
 #define FLAG_NEW_SOINFO       0x40000000 // new soinfo format
 
-#define SOINFO_VERSION 3
+#define SOINFO_VERSION 4
 
 typedef void (*linker_dtor_function_t)();
 typedef void (*linker_ctor_function_t)(int, char**, char**);
@@ -309,6 +309,7 @@
   template<typename ElfRelIteratorT>
   bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
                 const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+  bool relocate_relr();
 
  private:
   // This part of the structure is only available
@@ -365,6 +366,10 @@
   friend soinfo* get_libdl_info(const char* linker_path,
                                 const soinfo& linker_si,
                                 const link_map& linker_map);
+
+  // version >= 4
+  ElfW(Relr)* relr_;
+  size_t relr_count_;
 };
 
 // This function is used by dlvsym() to calculate hash of sym_ver