Working ASLR implementation.

ASLR for shared libraries is controlled by "-a" in ota_from_target_files.
Binary files are self-contained (supported by apriori/soslim).

Signed-off-by: Hristo Bojinov <hristo@google.com>
Change-Id: I500e325bf4a70a8d69a2ab9b2938e83dadb4e65d
diff --git a/tools/apriori/apriori.c b/tools/apriori/apriori.c
index d1807b3..b25ac2b 100644
--- a/tools/apriori/apriori.c
+++ b/tools/apriori/apriori.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <common.h>
 #include <debug.h>
 #include <libelf.h>
@@ -54,6 +55,154 @@
 
 static source_t *sources = NULL;
 
+/* Retouch data is a very concise representation of the resolved relocations.
+   This data is used to randomize the location of prelinked libraries at
+   update time, on the device.
+ */
+
+// We will store retouch entries into this buffer, then dump them at the
+// end of the .so file before setup_prelink_info().
+#define RETOUCH_MAX_SIZE 500000
+static char *retouch_buf;
+static unsigned int retouch_byte_cnt;
+// Compression state.
+static int32_t offs_prev;
+static uint32_t cont_prev;
+
+#define false 0
+#define true 1
+
+void retouch_init(void) {
+    offs_prev = 0;
+    cont_prev = 0;
+    retouch_byte_cnt = 0;
+    retouch_buf = malloc(RETOUCH_MAX_SIZE+12);
+    FAILIF(retouch_buf == NULL,
+           "Could not allocate %d bytes.\n", RETOUCH_MAX_SIZE+12);
+}
+
+//
+// We use three encoding schemes; this takes care of much of the redundancy
+// inherent in lists of relocations:
+//
+//   * two bytes, leading 1, 2b for d_offset ("s"), 13b for d_contents ("c")
+//
+//     76543210 76543210
+//     1ssccccc cccccccc
+//
+//   * three bytes, leading 01, 2b for delta offset, 20b for delta contents
+//
+//     76543210 76543210 76543210
+//     01sscccc cccccccc cccccccc
+//
+//   * eigth bytes, leading 00, 30b for offset, 32b for contents
+//
+//     76543210 76543210 76543210 76543210
+//     00ssssss ssssssss ssssssss ssssssss + 4 bytes contents
+//
+// NOTE 1: All deltas are w.r.t. the previous line in the list.
+// NOTE 2: Two-bit ("ss") offsets mean: "00"=4, "01"=8, "10"=12, and "11"=16.
+// NOTE 3: Delta contents are signed. To map back to a int32 we refill with 1s.
+// NOTE 4: Special encoding for -1 offset. Extended back to 32b when decoded.
+//
+
+void retouch_encode(int32_t offset, uint32_t contents) {
+    int64_t d_offs = offset-offs_prev;
+    int64_t d_cont = (int64_t)contents-(int64_t)cont_prev;
+
+    uint8_t output[8];
+    uint32_t output_size;
+
+    if ((d_offs > 3) &&
+        (d_offs % 4) == 0 &&
+        (d_offs / 4) < 5 &&
+        (d_cont < 4000) &&
+        (d_cont > -4000)) {
+        // we can fit in 2 bytes
+        output[0] =
+          0x80 |
+          (((d_offs/4)-1) << 5) |
+          (((uint64_t)d_cont & 0x1f00) >> 8);
+        output[1] =
+          ((uint64_t)d_cont & 0xff);
+        output_size = 2;
+    } else if ((d_offs > 3) &&
+               (d_offs % 4) == 0 &&
+               (d_offs / 4) < 5 &&
+               (d_cont < 510000) &&
+               (d_cont > -510000)) {
+        // fit in 3 bytes
+        output[0] =
+          0x40 |
+          (((d_offs/4)-1) << 4) |
+          (((uint64_t)d_cont & 0xf0000) >> 16);
+        output[1] =
+          ((uint64_t)d_cont & 0xff00) >> 8;
+        output[2] =
+          ((uint64_t)d_cont & 0xff);
+        output_size = 3;
+    } else {
+        // fit in 8 bytes; we can't support files bigger than (1GB-1)
+        // with this encoding: no library is that big anyway..
+        FAILIF(offset < -1 || offset > 0x3ffffffe, "Offset out of range.\n");
+        output[0] = (offset & 0x3f000000) >> 24;
+        output[1] = (offset & 0xff0000) >> 16;
+        output[2] = (offset & 0xff00) >> 8;
+        output[3] = (offset & 0xff);
+        output[4] = (contents & 0xff000000) >> 24;
+        output[5] = (contents & 0xff0000) >> 16;
+        output[6] = (contents & 0xff00) >> 8;
+        output[7] = (contents & 0xff);
+        output_size = 8;
+    }
+
+    // If this happens, the retouch buffer size can be bumped up.
+    // Currently, the largest case is libwebcore, at about 250K.
+    FAILIF((retouch_byte_cnt+output_size) > RETOUCH_MAX_SIZE,
+           "About to overflow retouch buffer.\n");
+
+    memcpy(retouch_buf+retouch_byte_cnt, output, output_size);
+    retouch_byte_cnt += output_size;
+
+    offs_prev = offset;
+    cont_prev = contents;
+}
+
+void retouch_dump(const char *fname, int elf_little,
+                  unsigned int retouch_byte_cnt, char *retouch_buf) {
+    int fd = open(fname, O_WRONLY);
+    FAILIF(fd < 0,
+           "open(%s, O_WRONLY): %s (%d)\n" ,
+           fname, strerror(errno), errno);
+    off_t sz = lseek(fd, 0, SEEK_END);
+    FAILIF(sz == (off_t)-1,
+           "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+           fd, strerror(errno), errno);
+
+    // The retouch blob ends with "RETOUCH XXXX", where XXXX is the 4-byte
+    // size of the retouch blob, in target endianness.
+    strncpy(retouch_buf+retouch_byte_cnt, "RETOUCH ", 8);
+    if (elf_little ^ is_host_little()) {
+        *(unsigned int *)(retouch_buf+retouch_byte_cnt+8) =
+          switch_endianness(retouch_byte_cnt);
+    } else {
+        *(unsigned int *)(retouch_buf+retouch_byte_cnt+8) =
+          retouch_byte_cnt;
+    }
+
+    int num_written = write(fd, retouch_buf, retouch_byte_cnt+12);
+    FAILIF(num_written < 0,
+           "write(%d, &info, sizeof(info)): %s (%d)\n",
+           fd, strerror(errno), errno);
+    FAILIF((retouch_byte_cnt+12) != num_written,
+           "Could not write %d bytes as expected (wrote %d bytes instead)!\n",
+           retouch_byte_cnt, num_written);
+    FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+}
+
+/* End of retouch code.
+ */
+
 #if defined(DEBUG) && 0
 
 static void print_shdr(source_t *source, Elf_Scn *scn)
@@ -325,6 +474,9 @@
                    basename(source->name));
         }
 
+        /* Save some of the info; needed for retouching (ASLR). */
+        retouch_init();
+
         source->newelf_fd = open(source->output,
                                  O_RDWR | O_CREAT,
                                  0666);
@@ -615,15 +767,15 @@
            strerror(errno),
            errno);
 
-	FAILIF(fstat(source->elf_fd, &source->elf_file_info) < 0,
-		   "fstat(%s(fd %d)): %s (%d)\n",
-		   source->name,
-		   source->elf_fd,
-		   strerror(errno),
-		   errno);
-	INFO("File [%s]'s size is %lld bytes!\n",
-		 source->name,
-		 source->elf_file_info.st_size);
+    FAILIF(fstat(source->elf_fd, &source->elf_file_info) < 0,
+           "fstat(%s(fd %d)): %s (%d)\n",
+           source->name,
+           source->elf_fd,
+           strerror(errno),
+           errno);
+    INFO("File [%s]'s size is %lld bytes!\n",
+         source->name,
+         source->elf_file_info.st_size);
 
     INFO("Calling elf_begin(%s)...\n", full_path);
 
@@ -775,6 +927,11 @@
                function setup_prelink_info() below. */
             INFO("%s: setting up prelink tag at end of file.\n",
                  source->output ? source->output : source->name);
+            retouch_encode(-1, source->base);
+            retouch_dump(source->output ? source->output : source->name,
+                         source->elf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
+                         retouch_byte_cnt,
+                         retouch_buf);
             setup_prelink_info(source->output ? source->output : source->name,
                                source->elf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
                                source->base);
@@ -785,6 +942,7 @@
 #endif/*SUPPORT_ANDROID_PRELINK_TAGS*/
 
     do_destroy_source(source);
+    if (retouch_buf != NULL) { free(retouch_buf); retouch_buf = NULL; }
 
     if (source->shstrtab_data != NULL)
         FREEIF(source->shstrtab_data->d_buf); /* adjust_elf */
@@ -1226,8 +1384,11 @@
                        rel->r_offset,
                        found_sym->st_value,
                        sym_source->base);
-                  if (!dry_run)
+                  if (!dry_run) {
+                    PRINT("WARNING: Relocation type not supported "
+                          "for retouching!");
                     *dest = found_sym->st_value + sym_source->base;
+                  }
                 }
               num_relocations++;
               break;
@@ -1240,8 +1401,15 @@
                    sname,
                    symname ?: "(symbol has no name)",
                    rel->r_offset, *dest, source->base);
-              if (!dry_run)
+              if (!dry_run) {
                 *dest += source->base;
+
+                /* Output an entry for the ASLR touch-up process. */
+                retouch_encode(rel->r_offset
+                               -shdr_mem.sh_addr
+                               +shdr_mem.sh_offset,
+                               *dest);
+              }
               num_relocations++;
               break;
             case R_ARM_COPY:
@@ -1352,15 +1520,21 @@
                             ASSERT(data->d_buf != NULL);
                             ASSERT(data->d_size >= rel->r_offset -
                                    shdr_mem.sh_addr);
-                            if (!dry_run)
-                              memcpy(dest, src, found_sym->st_size);
+                            if (!dry_run) {
+                                PRINT("WARNING: Relocation type not supported "
+                                      "for retouching!");
+                                memcpy(dest, src, found_sym->st_size);
+                            }
                           }
                         else {
                           ASSERT(src == NULL);
                           ASSERT(elf_ndxscn(src_scn) ==
                                  elf_ndxscn(sym_source->bss.scn));
-                          if (!dry_run)
-                            memset(dest, 0, found_sym->st_size);
+                          if (!dry_run) {
+                              PRINT("WARNING: Relocation type not supported "
+                                    "for retouching!");
+                              memset(dest, 0, found_sym->st_size);
+                          }
                         }
                       }
                     }
diff --git a/tools/apriori/source.h b/tools/apriori/source.h
index a5d96bd..5381a29 100644
--- a/tools/apriori/source.h
+++ b/tools/apriori/source.h
@@ -62,7 +62,8 @@
     Elf_Data *shstrtab_data;
     int elf_fd;
     int newelf_fd; /* fd of output file, -1 if output == NULL */
-	struct stat elf_file_info;
+    int newelf_relo_fd; /* fd of relocaion output file */
+    struct stat elf_file_info;
     GElf_Ehdr elf_hdr, oldelf_hdr;
     size_t shstrndx;
     int shnum; /* number of sections */
diff --git a/tools/releasetools/edify_generator.py b/tools/releasetools/edify_generator.py
index 390bd4b..328700f 100644
--- a/tools/releasetools/edify_generator.py
+++ b/tools/releasetools/edify_generator.py
@@ -235,6 +235,20 @@
              ",\0".join(['"' + i + '"' for i in sorted(links)]) + ");")
       self.script.append(self._WordWrap(cmd))
 
+  def RetouchBinaries(self, file_list):
+    """Execute the retouch instructions in files listed."""
+    cmd = ('retouch_binaries(' +
+           ', '.join(['"' + i[0] + '", "' + i[1] + '"' for i in file_list]) +
+           ');')
+    self.script.append(self._WordWrap(cmd))
+
+  def UndoRetouchBinaries(self, file_list):
+    """Undo the retouching (retouch to zero offset)."""
+    cmd = ('undo_retouch_binaries(' +
+           ', '.join(['"' + i[0] + '", "' + i[1] + '"' for i in file_list]) +
+           ');')
+    self.script.append(self._WordWrap(cmd))
+
   def AppendExtra(self, extra):
     """Append text verbatim to the output script."""
     self.script.append(extra)
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 8cd1941..d249214 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -44,6 +44,8 @@
   -e  (--extra_script)  <file>
       Insert the contents of file at the end of the update script.
 
+  -a  (--use_aslr)
+      Specify whether to build the package with ASLR enabled (off by default).
 """
 
 import sys
@@ -75,6 +77,7 @@
 OPTIONS.wipe_user_data = False
 OPTIONS.omit_prereq = False
 OPTIONS.extra_script = None
+OPTIONS.aslr_mode = False
 OPTIONS.worker_threads = 3
 
 def MostPopularKey(d, default):
@@ -91,6 +94,10 @@
   symlink."""
   return (info.external_attr >> 16) == 0120777
 
+def IsRegular(info):
+  """Return true if the zipfile.ZipInfo object passed in represents a
+  symlink."""
+  return (info.external_attr >> 28) == 010
 
 
 class Item:
@@ -246,13 +253,15 @@
                     substitute=None):
   """Copies files underneath system/ in the input zip to the output
   zip.  Populates the Item class with their metadata, and returns a
-  list of symlinks.  output_zip may be None, in which case the copy is
-  skipped (but the other side effects still happen).  substitute is an
-  optional dict of {output filename: contents} to be output instead of
-  certain input files.
+  list of symlinks as well as a list of files that will be retouched.
+  output_zip may be None, in which case the copy is skipped (but the
+  other side effects still happen).  substitute is an optional dict
+  of {output filename: contents} to be output instead of certain input
+  files.
   """
 
   symlinks = []
+  retouch_files = []
 
   for info in input_zip.infolist():
     if info.filename.startswith("SYSTEM/"):
@@ -270,6 +279,9 @@
             data = substitute[fn]
           else:
             data = input_zip.read(info.filename)
+          if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
+            retouch_files.append(("/system/" + basefilename,
+                                  sha.sha(data).hexdigest()))
           output_zip.writestr(info2, data)
         if fn.endswith("/"):
           Item.Get(fn[:-1], dir=True)
@@ -277,7 +289,7 @@
           Item.Get(fn, dir=False)
 
   symlinks.sort()
-  return symlinks
+  return (symlinks, retouch_files)
 
 
 def SignOutput(temp_zip_name, output_zip_name):
@@ -375,8 +387,12 @@
   script.UnpackPackageDir("recovery", "/system")
   script.UnpackPackageDir("system", "/system")
 
-  symlinks = CopySystemFiles(input_zip, output_zip)
+  (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
   script.MakeSymlinks(symlinks)
+  if OPTIONS.aslr_mode:
+    script.RetouchBinaries(retouch_files)
+  else:
+    script.UndoRetouchBinaries(retouch_files)
 
   boot_img = File("boot.img", common.BuildBootableImage(
       os.path.join(OPTIONS.input_tmp, "BOOT")))
@@ -432,12 +448,17 @@
   """Load all the files from SYSTEM/... in a given target-files
   ZipFile, and return a dict of {filename: File object}."""
   out = {}
+  retouch_files = []
   for info in z.infolist():
     if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
-      fn = "system/" + info.filename[7:]
+      basefilename = info.filename[7:]
+      fn = "system/" + basefilename
       data = z.read(info.filename)
       out[fn] = File(fn, data)
-  return out
+      if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
+        retouch_files.append(("/system/" + basefilename,
+                              out[fn].sha1))
+  return (out, retouch_files)
 
 
 DIFF_PROGRAM_BY_EXT = {
@@ -600,9 +621,9 @@
       metadata=metadata)
 
   print "Loading target..."
-  target_data = LoadSystemFiles(target_zip)
+  (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
   print "Loading source..."
-  source_data = LoadSystemFiles(source_zip)
+  (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
 
   verbatim_targets = []
   patch_list = []
@@ -769,7 +790,7 @@
 
   script.ShowProgress(0.1, 10)
 
-  target_symlinks = CopySystemFiles(target_zip, None)
+  (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
 
   target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
   temp_script = script.MakeTemporary()
@@ -778,7 +799,7 @@
 
   # Note that this call will mess up the tree of Items, so make sure
   # we're done with it.
-  source_symlinks = CopySystemFiles(source_zip, None)
+  (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
   source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
 
   # Delete all the symlinks in source that aren't in target.  This
@@ -812,6 +833,10 @@
       to_create.append((dest, link))
   script.DeleteFiles([i[1] for i in to_create])
   script.MakeSymlinks(to_create)
+  if OPTIONS.aslr_mode:
+    script.RetouchBinaries(target_retouch_files)
+  else:
+    script.UndoRetouchBinaries(target_retouch_files)
 
   # Now that the symlinks are created, we can set all the
   # permissions.
@@ -842,6 +867,8 @@
       OPTIONS.omit_prereq = True
     elif o in ("-e", "--extra_script"):
       OPTIONS.extra_script = a
+    elif o in ("-a", "--use_aslr"):
+      OPTIONS.aslr_mode = True
     elif o in ("--worker_threads"):
       OPTIONS.worker_threads = int(a)
     else:
@@ -849,14 +876,15 @@
     return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="b:k:i:d:wne:",
+                             extra_opts="b:k:i:d:wne:a",
                              extra_long_opts=["board_config=",
                                               "package_key=",
                                               "incremental_from=",
                                               "wipe_user_data",
                                               "no_prereq",
                                               "extra_script=",
-                                              "worker_threads="],
+                                              "worker_threads=",
+                                              "use_aslr"],
                              extra_option_handler=option_handler)
 
   if len(args) != 2:
diff --git a/tools/soslim/main.c b/tools/soslim/main.c
index dd8a60b..e23fbce 100644
--- a/tools/soslim/main.c
+++ b/tools/soslim/main.c
@@ -188,9 +188,13 @@
             else INFO("Not building symbol filter, filter file is empty.\n");
         }
 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
-        int prelinked = 0;
+        int prelinked = 0, retouched = 0;
         int elf_little; /* valid if prelinked != 0 */
         long prelink_addr; /* valid if prelinked != 0 */
+#define RETOUCH_MAX_SIZE 500000
+        /* _cnt valid if retouched != 0 */
+        unsigned int retouch_byte_cnt = RETOUCH_MAX_SIZE;
+        char retouch_buf[RETOUCH_MAX_SIZE]; /* valid if retouched != 0 */
 #endif
         clone_elf(elf, newelf,
                   infile, outfile,
@@ -200,7 +204,10 @@
 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
                   , &prelinked,
                   &elf_little,
-                  &prelink_addr
+                  &prelink_addr,
+                  &retouched,
+                  &retouch_byte_cnt,
+                  retouch_buf
 #endif
                   ,
                   true, /* rebuild the section-header-strings table */
@@ -223,6 +230,13 @@
                infile, strerror(errno), errno);
 
 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
+        if (retouched) {
+            INFO("File has retouch data, putting it back in place.\n");
+            retouch_dump(outfile != NULL ? outfile : infile,
+                         elf_little,
+                         retouch_byte_cnt,
+                         retouch_buf);
+        }
         if (prelinked) {
             INFO("File is prelinked, putting prelink TAG back in place.\n");
             setup_prelink_info(outfile != NULL ? outfile : infile,
diff --git a/tools/soslim/prelink_info.c b/tools/soslim/prelink_info.c
index 81d5de3..2600ac7 100644
--- a/tools/soslim/prelink_info.c
+++ b/tools/soslim/prelink_info.c
@@ -11,6 +11,7 @@
 #include <debug.h>
 #include <common.h>
 
+#define RETOUCH_SUFFIX_SIZE 12
 typedef struct {
 	uint32_t mmap_addr;
 	char tag[4]; /* 'P', 'R', 'E', ' ' */
@@ -28,7 +29,7 @@
 		}
 		else {
 			/* Different endianness */
-			*prelink_addr = switch_endianness(info->mmap_addr);
+                        *prelink_addr = switch_endianness(info->mmap_addr);
 		}
 	}
 }
@@ -67,10 +68,104 @@
 		set_prelink(prelink_addr, elf_little, &info);
 		prelinked = 1;
 	}
-	FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+	FAILIF(close(fd) < 0,
+               "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
 	return prelinked;
 }
 
+int check_retouched(const char *fname, int elf_little,
+                    unsigned int *retouch_byte_cnt, char *retouch_buf) {
+    FAILIF(sizeof(prelink_info_t) != 8,
+           "Unexpected sizeof(prelink_info_t) == %d!\n",
+           sizeof(prelink_info_t));
+    int fd = open(fname, O_RDONLY);
+    FAILIF(fd < 0, "open(%s, O_RDONLY): %s (%d)!\n",
+           fname, strerror(errno), errno);
+    off_t end = lseek(fd, 0, SEEK_END);
+    int nr = sizeof(prelink_info_t);
+    off_t sz = lseek(fd, -nr-RETOUCH_SUFFIX_SIZE, SEEK_CUR);
+    ASSERT((long)(end - sz) == (long)(nr+RETOUCH_SUFFIX_SIZE));
+    FAILIF(sz == (off_t)-1,
+           "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+           fd, strerror(errno), errno);
+
+    char retouch_meta[RETOUCH_SUFFIX_SIZE];
+    int num_read = read(fd, &retouch_meta, RETOUCH_SUFFIX_SIZE);
+    FAILIF(num_read < 0,
+           "read(%d, &info, sizeof(prelink_info_t)): %s (%d)!\n",
+           fd, strerror(errno), errno);
+    FAILIF(num_read != RETOUCH_SUFFIX_SIZE,
+           "read(%d, &info, sizeof(prelink_info_t)): did not read %d bytes as "
+           "expected (read %d)!\n",
+           fd, RETOUCH_SUFFIX_SIZE, num_read);
+
+    int retouched = 0;
+    if (!strncmp(retouch_meta, "RETOUCH ", 8)) {
+        unsigned int retouch_byte_cnt_meta;
+        if (!(elf_little ^ is_host_little()))
+            retouch_byte_cnt_meta = *(unsigned int *)(retouch_meta+8);
+        else
+            retouch_byte_cnt_meta =
+              switch_endianness(*(unsigned int *)(retouch_meta+8));
+        FAILIF(*retouch_byte_cnt < retouch_byte_cnt_meta,
+               "Retouch buffer too small at %d bytes (%d needed).",
+               *retouch_byte_cnt, retouch_byte_cnt_meta);
+        *retouch_byte_cnt = retouch_byte_cnt_meta;
+        off_t sz = lseek(fd,
+                         -((long)*retouch_byte_cnt)-RETOUCH_SUFFIX_SIZE-nr,
+                         SEEK_END);
+        ASSERT((long)(end - sz) ==
+               (long)(*retouch_byte_cnt+RETOUCH_SUFFIX_SIZE+nr));
+        FAILIF(sz == (off_t)-1,
+               "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+               fd, strerror(errno), errno);
+        num_read = read(fd, retouch_buf, *retouch_byte_cnt);
+        FAILIF(num_read < 0,
+               "read(%d, &info, sizeof(prelink_info_t)): %s (%d)!\n",
+               fd, strerror(errno), errno);
+        FAILIF(num_read != *retouch_byte_cnt,
+               "read(%d, retouch_buf, %u): did not read %d bytes as "
+               "expected (read %d)!\n",
+               fd, *retouch_byte_cnt, *retouch_byte_cnt, num_read);
+
+        retouched = 1;
+    }
+    FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+    return retouched;
+}
+
+void retouch_dump(const char *fname, int elf_little,
+                  unsigned int retouch_byte_cnt, char *retouch_buf) {
+    int fd = open(fname, O_WRONLY);
+    FAILIF(fd < 0,
+           "open(%s, O_WRONLY): %s (%d)\n",
+           fname, strerror(errno), errno);
+    off_t sz = lseek(fd, 0, SEEK_END);
+    FAILIF(sz == (off_t)-1,
+           "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+           fd, strerror(errno), errno);
+
+    // The retouch blob ends with "RETOUCH XXXX", where XXXX is the 4-byte
+    // size of the retouch blob, in target endianness.
+    strncpy(retouch_buf+retouch_byte_cnt, "RETOUCH ", 8);
+    if (elf_little ^ is_host_little()) {
+        *(unsigned int *)(retouch_buf+retouch_byte_cnt+8) =
+          switch_endianness(retouch_byte_cnt);
+    } else {
+        *(unsigned int *)(retouch_buf+retouch_byte_cnt+8) =
+          retouch_byte_cnt;
+    }
+
+    int num_written = write(fd, retouch_buf, retouch_byte_cnt+12);
+    FAILIF(num_written < 0,
+           "write(%d, &info, sizeof(info)): %s (%d)\n",
+           fd, strerror(errno), errno);
+    FAILIF((retouch_byte_cnt+12) != num_written,
+           "Could not write %d bytes as expected (wrote %d bytes instead)!\n",
+           retouch_byte_cnt, num_written);
+    FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+}
+
 void setup_prelink_info(const char *fname, int elf_little, long base)
 {
     FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %zd!\n", sizeof(prelink_info_t));
@@ -92,7 +187,7 @@
     else {
         /* Different endianness */
         INFO("Host and ELF file [%s] have different endianness.\n", fname);
-		info.mmap_addr = switch_endianness(base);
+        info.mmap_addr = switch_endianness(base);
     }
     strncpy(info.tag, "PRE ", 4);
 
diff --git a/tools/soslim/prelink_info.h b/tools/soslim/prelink_info.h
index e2787cb..efa84fd 100644
--- a/tools/soslim/prelink_info.h
+++ b/tools/soslim/prelink_info.h
@@ -3,6 +3,10 @@
 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
 
 int check_prelinked(const char *fname, int elf_little, long *prelink_addr);
+int check_retouched(const char *fname, int elf_little,
+                    unsigned int *retouch_byte_cnt, char *retouch_buf);
+void retouch_dump(const char *fname, int elf_little,
+                  unsigned int retouch_byte_cnt, char *retouch_buf);
 void setup_prelink_info(const char *fname, int elf_little, long base);
 
 #endif
diff --git a/tools/soslim/soslim.c b/tools/soslim/soslim.c
index 125e29e..33b1ee7 100644
--- a/tools/soslim/soslim.c
+++ b/tools/soslim/soslim.c
@@ -27,7 +27,10 @@
 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
                , int *prelinked,
                int *elf_little,
-               long *prelink_addr
+               long *prelink_addr,
+               int *retouched,
+               unsigned int *retouch_byte_cnt,
+               char *retouch_buf
 #endif
                , bool rebuild_shstrtab,
                bool strip_debug,
@@ -70,6 +73,11 @@
     ASSERT(elf_little);
     *elf_little = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB);
     *prelinked = check_prelinked(elf_name, *elf_little, prelink_addr);
+    ASSERT(retouched);
+    ASSERT(retouch_byte_cnt);
+    ASSERT(retouch_buf);
+    *retouched = check_retouched(elf_name, *elf_little,
+                                 retouch_byte_cnt, retouch_buf);
 #endif
 
     INFO("\n\nCALCULATING MODIFICATIONS\n\n");
diff --git a/tools/soslim/soslim.h b/tools/soslim/soslim.h
index dfcb085..952c960 100644
--- a/tools/soslim/soslim.h
+++ b/tools/soslim/soslim.h
@@ -23,7 +23,10 @@
 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
 			   , int *prelinked,
 			   int *elf_little,
-			   long *prelink_addr
+			   long *prelink_addr,
+                           int *retouched,
+                           unsigned int *retouch_byte_cnt,
+                           char *retouch_buf
 #endif
                , bool rebuild_shstrtab,
                bool strip_debug,