Store native libs aligned to PAGE_SIZE

- Add a new flag to zipalign (-p) that page aligns shared
  libraries (zip entries ending with ".so") in the archive.

- Add a new build variable LOCAL_PAGE_ALIGN_SHARED_LIBRARIES
  to turn on this behaviour in zipalign.

- Add a new LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS to control
  zip behaviour.

Bug: 8076853
Bug: 19330157

Co-Authored-By: Simon Baldwin <simonb@google.com>
Co-Authored-By: Dimitry Ivanov <dimitry@google.com>
Change-Id: I1aa2c039bb2a590ae72f256acc9ba5401c2c59b1
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index db48bd6..cb32a84 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -20,6 +20,7 @@
 LOCAL_MODULE_CLASS:=
 LOCAL_MODULE_SUFFIX:=
 LOCAL_PACKAGE_NAME:=
+LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES:=
 LOCAL_OVERRIDES_PACKAGES:=
 LOCAL_EXPORT_PACKAGE_RESOURCES:=
 LOCAL_MANIFEST_PACKAGE_NAME:=
@@ -94,6 +95,7 @@
 LOCAL_STRIP_MODULE:=
 LOCAL_JNI_SHARED_LIBRARIES:=
 LOCAL_JNI_SHARED_LIBRARIES_ABI:=
+LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS:=
 LOCAL_PREBUILT_JNI_LIBS:=
 LOCAL_JAR_MANIFEST:=
 LOCAL_INSTRUMENTATION_FOR:=
diff --git a/core/definitions.mk b/core/definitions.mk
index 9845556..6951af9 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1837,7 +1837,7 @@
 $(foreach abi,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI),\
   $(call _add-jni-shared-libs-to-package-per-abi,$(abi),\
     $(patsubst $(abi):%,%,$(filter $(abi):%,$(PRIVATE_JNI_SHARED_LIBRARIES)))))
-$(hide) (cd $(dir $@) && zip -r $(notdir $@) lib)
+$(hide) (cd $(dir $@) && zip -r $(PRIVATE_JNI_SHARED_LIBRARIES_ZIP_OPTIONS) $(notdir $@) lib)
 $(hide) rm -rf $(dir $@)lib
 endef
 
@@ -1865,12 +1865,15 @@
 $(hide) mv $@.signed $@
 endef
 
-# Align STORED entries of a package on 4-byte boundaries
-# to make them easier to mmap.
+# Align STORED entries of a package on 4-byte boundaries to make them easier to mmap.
 #
 define align-package
 $(hide) mv $@ $@.unaligned
-$(hide) $(ZIPALIGN) -f 4 $@.unaligned $@.aligned
+$(hide) $(ZIPALIGN) \
+    -f \
+    $(if $(findstring true, $(PRIVATE_PAGE_ALIGN_JNI_SHARED_LIBRARIES)),-p ,) \
+    4 \
+    $@.unaligned $@.aligned
 $(hide) mv $@.aligned $@
 endef
 
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 49a6d9b..f5e6c25 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -63,6 +63,14 @@
 LOCAL_MODULE_TAGS := optional
 endif
 
+ifeq ($(LOCAL_PACKAGE_ALIGNMENT),)
+LOCAL_PACKAGE_ALIGNMENT := $(DEFAULT_PACKAGE_ALIGNMENT)
+endif
+
+ifeq ($(LOCAL_JNI_SHARED_LIBRARIES_PACKAGE_ALIGNMENT),)
+LOCAL_JNI_SHARED_LIBRARIES_PACKAGE_ALIGNMENT := $(DEFAULT_JNI_SHARED_LIBRARIES_PACKAGE_ALIGNMENT)
+endif
+
 ifeq ($(filter tests, $(LOCAL_MODULE_TAGS)),)
 # Force localization check if it's not tagged as tests.
 LOCAL_AAPT_FLAGS := $(LOCAL_AAPT_FLAGS) -z
@@ -373,6 +381,8 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries_with_abis)
 # PRIVATE_JNI_SHARED_LIBRARIES_ABI is a list of ABI names.
 $(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES_ABI := $(jni_shared_libraries_abis)
+$(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := $(LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS)
+$(LOCAL_BUILT_MODULE): PRIVATE_PAGE_ALIGN_JNI_SHARED_LIBRARIES := $(LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES)
 ifneq ($(TARGET_BUILD_APPS),)
     # Include all resources for unbundled apps.
     LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
@@ -485,7 +495,7 @@
 	@rm -rf $(dir $@) && mkdir -p $(dir $@)
 	$(hide) cp $< $@
 	$(hide) zip -d $@ $(foreach f,$(PRIVATE_JNI_SHARED_LIBRARIES),\*/$(f))
-	$(call align-package)
+	$(align-package)
 
 $(call dist-for-goals, apps_only, $(apk_jni_stripped):$(dist_subdir)/$(LOCAL_PACKAGE_NAME).apk)
 
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 2e0d2ef..7359513 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -170,6 +170,7 @@
 include $(BUILD_SYSTEM)/dex_preopt_odex_install.mk
 #######################################
 # Sign and align non-presigned .apks.
+$(built_module) : PRIVATE_PAGE_ALIGN_JNI_SHARED_LIBRARIES := $(LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES)
 $(built_module) : $(my_prebuilt_src_file) | $(ACP) $(ZIPALIGN) $(SIGNAPK_JAR)
 	$(transform-prebuilt-to-target)
 ifdef extracted_jni_libs
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index dc2826b..191dd41 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -32,20 +32,39 @@
     fprintf(stderr, "Zip alignment utility\n");
     fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
     fprintf(stderr,
-        "Usage: zipalign [-f] [-v] [-z] <align> infile.zip outfile.zip\n"
-        "       zipalign -c [-v] <align> infile.zip\n\n" );
+        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
+        "       zipalign -c [-v] <align> infile.zip [list of files]\n\n" );
     fprintf(stderr,
         "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
     fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
     fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
+    fprintf(stderr, "  -p: page align stored shared object: files\n");
     fprintf(stderr, "  -v: verbose output\n");
     fprintf(stderr, "  -z: recompress using Zopfli\n");
 }
 
+static const int kPageAlignment = 4096;
+
+static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,
+    ZipEntry* pEntry) {
+
+    if (!pageAlignSharedLibs) {
+        return defaultAlignment;
+    }
+
+    const char* ext = strrchr(pEntry->getFileName(), '.');
+    if (ext && strcmp(ext, ".so") == 0) {
+        return kPageAlignment;
+    }
+
+    return defaultAlignment;
+}
+
 /*
  * Copy all entries from "pZin" to "pZout", aligning as needed.
  */
-static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli)
+static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli,
+    bool pageAlignSharedLibs)
 {
     int numEntries = pZin->getNumEntries();
     ZipEntry* pEntry;
@@ -75,13 +94,15 @@
                 status = pZout->add(pZin, pEntry, padding, &pNewEntry);
             }
         } else {
+            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
+
             /*
              * Copy the entry, adjusting as required.  We assume that the
              * file position in the new file will be equal to the file
              * position in the original.
              */
             long newOffset = pEntry->getFileOffset() + bias;
-            padding = (alignment - (newOffset % alignment)) % alignment;
+            padding = (alignTo - (newOffset % alignTo)) % alignTo;
 
             //printf("--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\n",
             //    pEntry->getFileName(), (long) pEntry->getFileOffset(),
@@ -105,7 +126,7 @@
  * output file exists and "force" wasn't specified.
  */
 static int process(const char* inFileName, const char* outFileName,
-    int alignment, bool force, bool zopfli)
+    int alignment, bool force, bool zopfli, bool pageAlignSharedLibs)
 {
     ZipFile zin, zout;
 
@@ -136,7 +157,7 @@
         return 1;
     }
 
-    int result = copyAndAlign(&zin, &zout, alignment, zopfli);
+    int result = copyAndAlign(&zin, &zout, alignment, zopfli, pageAlignSharedLibs);
     if (result != 0) {
         printf("zipalign: failed rewriting '%s' to '%s'\n",
             inFileName, outFileName);
@@ -147,7 +168,8 @@
 /*
  * Verify the alignment of a zip archive.
  */
-static int verify(const char* fileName, int alignment, bool verbose)
+static int verify(const char* fileName, int alignment, bool verbose,
+    bool pageAlignSharedLibs)
 {
     ZipFile zipFile;
     bool foundBad = false;
@@ -172,11 +194,12 @@
             }
         } else {
             long offset = pEntry->getFileOffset();
-            if ((offset % alignment) != 0) {
+            const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
+            if ((offset % alignTo) != 0) {
                 if (verbose) {
                     printf("%8ld %s (BAD - %ld)\n",
                         (long) offset, pEntry->getFileName(),
-                        offset % alignment);
+                        offset % alignTo);
                 }
                 foundBad = true;
             } else {
@@ -204,6 +227,7 @@
     bool force = false;
     bool verbose = false;
     bool zopfli = false;
+    bool pageAlignSharedLibs = false;
     int result = 1;
     int alignment;
     char* endp;
@@ -233,6 +257,9 @@
             case 'z':
                 zopfli = true;
                 break;
+            case 'p':
+                pageAlignSharedLibs = true;
+                break;
             default:
                 fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
                 wantUsage = true;
@@ -260,14 +287,15 @@
 
     if (check) {
         /* check existing archive for correct alignment */
-        result = verify(argv[1], alignment, verbose);
+        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
     } else {
         /* create the new archive */
-        result = process(argv[1], argv[2], alignment, force, zopfli);
+        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
 
         /* trust, but verify */
-        if (result == 0)
-            result = verify(argv[2], alignment, verbose);
+        if (result == 0) {
+            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
+        }
     }
 
 bail: