Enable BCJ filters for X86 and ARM ELF binary.
am: 5288bd128e

Change-Id: I91591226d8006477128c782eb81448302ffb6e75
diff --git a/payload_generator/xz_android.cc b/payload_generator/xz_android.cc
index b2b74b1..41c55f7 100644
--- a/payload_generator/xz_android.cc
+++ b/payload_generator/xz_android.cc
@@ -16,12 +16,14 @@
 
 #include "update_engine/payload_generator/xz.h"
 
-#include <7zCrc.h>
-#include <Xz.h>
-#include <XzEnc.h>
+#include <elf.h>
+#include <endian.h>
 
 #include <algorithm>
 
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzEnc.h>
 #include <base/logging.h>
 
 namespace {
@@ -67,6 +69,37 @@
   brillo::Blob* data_;
 };
 
+// Returns the filter id to be used to compress |data|.
+// Only BCJ filter for x86 and ARM ELF file are supported, returns 0 otherwise.
+int GetFilterID(const brillo::Blob& data) {
+  if (data.size() < sizeof(Elf32_Ehdr) ||
+      memcmp(data.data(), ELFMAG, SELFMAG) != 0)
+    return 0;
+
+  const Elf32_Ehdr* header = reinterpret_cast<const Elf32_Ehdr*>(data.data());
+
+  // Only little-endian is supported.
+  if (header->e_ident[EI_DATA] != ELFDATA2LSB)
+    return 0;
+
+  switch (le16toh(header->e_machine)) {
+    case EM_386:
+    case EM_X86_64:
+      return XZ_ID_X86;
+    case EM_ARM:
+      // Both ARM and ARM Thumb instructions could be found in the same ARM ELF
+      // file. We choose to use the ARM Thumb filter here because testing shows
+      // that it usually works better than the ARM filter.
+      return XZ_ID_ARMT;
+#ifdef EM_AARCH64
+    case EM_AARCH64:
+      // Neither the ARM nor the ARM Thumb filter works well with AArch64.
+      return 0;
+#endif
+  }
+  return 0;
+}
+
 }  // namespace
 
 namespace chromeos_update_engine {
@@ -107,6 +140,8 @@
   Lzma2EncProps_Normalize(&lzma2Props);
   props.lzma2Props = lzma2Props;
 
+  props.filterProps.id = GetFilterID(in);
+
   BlobWriterStream out_writer(out);
   BlobReaderStream in_reader(in);
   SRes res = Xz_Encode(&out_writer, &in_reader, &props, nullptr /* progress */);
diff --git a/payload_generator/zip_unittest.cc b/payload_generator/zip_unittest.cc
index 5b0d5da..29f16d3 100644
--- a/payload_generator/zip_unittest.cc
+++ b/payload_generator/zip_unittest.cc
@@ -135,7 +135,7 @@
   brillo::Blob decompressed;
   EXPECT_TRUE(this->ZipDecompress(out, &decompressed));
   EXPECT_EQ(in.size(), decompressed.size());
-  EXPECT_TRUE(!memcmp(in.data(), decompressed.data(), in.size()));
+  EXPECT_EQ(0, memcmp(in.data(), decompressed.data(), in.size()));
 }
 
 TYPED_TEST(ZipTest, PoorCompressionTest) {
@@ -165,4 +165,18 @@
   EXPECT_EQ(0U, out.size());
 }
 
+TYPED_TEST(ZipTest, CompressELFTest) {
+  string path = test_utils::GetBuildArtifactsPath("delta_generator");
+  brillo::Blob in;
+  utils::ReadFile(path, &in);
+  brillo::Blob out;
+  EXPECT_TRUE(this->ZipCompress(in, &out));
+  EXPECT_LT(out.size(), in.size());
+  EXPECT_GT(out.size(), 0U);
+  brillo::Blob decompressed;
+  EXPECT_TRUE(this->ZipDecompress(out, &decompressed));
+  EXPECT_EQ(in.size(), decompressed.size());
+  EXPECT_EQ(0, memcmp(in.data(), decompressed.data(), in.size()));
+}
+
 }  // namespace chromeos_update_engine