Index: third_party/android_crazy_linker/src/src/crazy_linker_zip.cpp |
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_zip.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_zip.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d0a2ff38284e5a491d42da6400b80649ba431c0a |
--- /dev/null |
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_zip.cpp |
@@ -0,0 +1,252 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "crazy_linker_zip.h" |
+ |
+#include <fcntl.h> |
+#include <sys/mman.h> |
+#include <sys/stat.h> |
+#include <unistd.h> |
+ |
+#include "crazy_linker_debug.h" |
+#include "crazy_linker_system.h" |
+#include "crazy_linker_util.h" |
+ |
+namespace { |
+ |
+// All offsets are given in bytes relative to the start of the header. |
+// Arithmetic is used to indicate the size of small fields that are skipped. |
+ |
+// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.3.16 |
+// This marker appears at the start of the end of central directory record |
+const uint32_t kEndOfCentralDirectoryMarker = 0x06054b50; |
+ |
+// Offsets of fields in End of Central Directory. |
+const int kOffsetNumOfEntriesInEndOfCentralDirectory = 4 + 2 + 2; |
+const int kOffsetOfCentralDirLengthInEndOfCentralDirectory = |
+ kOffsetNumOfEntriesInEndOfCentralDirectory + 2 + 2; |
+const int kOffsetOfStartOfCentralDirInEndOfCentralDirectory = |
+ kOffsetOfCentralDirLengthInEndOfCentralDirectory + 4; |
+ |
+// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.3.12 |
+// This marker appears at the start of the central directory |
+const uint32_t kCentralDirHeaderMarker = 0x2014b50; |
+ |
+// Offsets of fields in Central Directory Header. |
+const int kOffsetFilenameLengthInCentralDirectory = |
+ 4 + 2 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4; |
+const int kOffsetExtraFieldLengthInCentralDirectory = |
+ kOffsetFilenameLengthInCentralDirectory + 2; |
+const int kOffsetCommentLengthInCentralDirectory = |
+ kOffsetExtraFieldLengthInCentralDirectory + 2; |
+const int kOffsetLocalHeaderOffsetInCentralDirectory = |
+ kOffsetCommentLengthInCentralDirectory + 2 + 2 + 2 + 4; |
+const int kOffsetFilenameInCentralDirectory = |
+ kOffsetLocalHeaderOffsetInCentralDirectory + 4; |
+ |
+// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.3.7 |
+// This marker appears at the start of local header |
+const uint32_t kLocalHeaderMarker = 0x04034b50; |
+ |
+// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.4.5 |
+// This value denotes that the file is stored (no compression). |
+const uint32_t kCompressionMethodStored = 0; |
+ |
+// Offsets of fields in the Local Header. |
+const int kOffsetCompressionMethodInLocalHeader = 4 + 2 + 2; |
+const int kOffsetFilenameLengthInLocalHeader = |
+ kOffsetCompressionMethodInLocalHeader + 2 + 2 + 2 + 4 + 4 + 4; |
+const int kOffsetExtraFieldLengthInLocalHeader = |
+ kOffsetFilenameLengthInLocalHeader + 2; |
+const int kOffsetFilenameInLocalHeader = |
+ kOffsetExtraFieldLengthInLocalHeader + 2; |
+ |
+// RAII pattern for unmapping and closing the mapped file. |
+class ScopedMMap { |
+ public: |
+ ScopedMMap(void* mem, uint32_t len) : mem_(mem), len_(len) {} |
+ ~ScopedMMap() { |
+ if (munmap(mem_, len_) == -1) { |
+ LOG_ERRNO("%s: munmap failed when trying to unmap zip file\n", |
+ __FUNCTION__); |
+ } |
+ } |
+ private: |
+ void* mem_; |
+ uint32_t len_; |
+}; |
+ |
+inline uint32_t ReadUInt16(uint8_t* mem_bytes, int offset) { |
+ return |
+ static_cast<uint32_t>(mem_bytes[offset]) | |
+ (static_cast<uint32_t>(mem_bytes[offset + 1]) << 8); |
+} |
+ |
+inline uint32_t ReadUInt32(uint8_t* mem_bytes, int offset) { |
+ return |
+ static_cast<uint32_t>(mem_bytes[offset]) | |
+ (static_cast<uint32_t>(mem_bytes[offset + 1]) << 8) | |
+ (static_cast<uint32_t>(mem_bytes[offset + 2]) << 16) | |
+ (static_cast<uint32_t>(mem_bytes[offset + 3]) << 24); |
+} |
+ |
+} // unnamed namespace |
+ |
+namespace crazy { |
+ |
+const uint32_t kMaxZipFileLength = 1U << 31; // 2GB |
+ |
+int FindStartOffsetOfFileInZipFile(const char* zip_file, const char* filename) { |
+ // Open the file |
+ FileDescriptor fd; |
+ if (!fd.OpenReadOnly(zip_file)) { |
+ LOG_ERRNO("%s: open failed trying to open zip file %s\n", |
+ __FUNCTION__, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ // Find the length of the file. |
+ struct stat stat_buf; |
+ if (stat(zip_file, &stat_buf) == -1) { |
+ LOG_ERRNO("%s: stat failed trying to stat zip file %s\n", |
+ __FUNCTION__, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ if (stat_buf.st_size > kMaxZipFileLength) { |
+ LOG("%s: The size %ld of %s is too large to map\n", |
+ __FUNCTION__, stat_buf.st_size, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ // Map the file into memory. |
+ void* mem = fd.Map(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, 0); |
+ if (mem == MAP_FAILED) { |
+ LOG_ERRNO("%s: mmap failed trying to mmap zip file %s\n", |
+ __FUNCTION__, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ ScopedMMap scoped_mmap(mem, stat_buf.st_size); |
+ |
+ // Scan backwards from the end of the file searching for the end of |
+ // central directory marker. |
+ uint8_t* mem_bytes = static_cast<uint8_t*>(mem); |
+ int off; |
+ for (off = stat_buf.st_size - sizeof(kEndOfCentralDirectoryMarker); |
+ off >= 0; --off) { |
+ if (ReadUInt32(mem_bytes, off) == kEndOfCentralDirectoryMarker) { |
+ break; |
+ } |
+ } |
+ if (off == -1) { |
+ LOG("%s: Failed to find end of central directory in %s\n", |
+ __FUNCTION__, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ // We have located the end of central directory record, now locate |
+ // the central directory by reading the end of central directory record. |
+ |
+ uint32_t length_of_central_dir = ReadUInt32( |
+ mem_bytes, off + kOffsetOfCentralDirLengthInEndOfCentralDirectory); |
+ uint32_t start_of_central_dir = ReadUInt32( |
+ mem_bytes, off + kOffsetOfStartOfCentralDirInEndOfCentralDirectory); |
+ |
+ if (start_of_central_dir > off) { |
+ LOG("%s: Found out of range offset %u for start of directory in %s\n", |
+ __FUNCTION__, start_of_central_dir, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ uint32_t end_of_central_dir = start_of_central_dir + length_of_central_dir; |
+ if (end_of_central_dir > off) { |
+ LOG("%s: Found out of range offset %u for end of directory in %s\n", |
+ __FUNCTION__, end_of_central_dir, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ uint32_t num_entries = ReadUInt16( |
+ mem_bytes, off + kOffsetNumOfEntriesInEndOfCentralDirectory); |
+ |
+ // Read the headers in the central directory and locate the file. |
+ off = start_of_central_dir; |
+ const int target_len = strlen(filename); |
+ int n = 0; |
+ for (; n < num_entries && off < end_of_central_dir; ++n) { |
+ uint32_t marker = ReadUInt32(mem_bytes, off); |
+ if (marker != kCentralDirHeaderMarker) { |
+ LOG("%s: Failed to find central directory header marker in %s. " |
+ "Found 0x%x but expected 0x%x\n", __FUNCTION__, |
+ zip_file, marker, kCentralDirHeaderMarker); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ uint32_t file_name_length = |
+ ReadUInt16(mem_bytes, off + kOffsetFilenameLengthInCentralDirectory); |
+ uint32_t extra_field_length = |
+ ReadUInt16(mem_bytes, off + kOffsetExtraFieldLengthInCentralDirectory); |
+ uint32_t comment_field_length = |
+ ReadUInt16(mem_bytes, off + kOffsetCommentLengthInCentralDirectory); |
+ uint32_t header_length = kOffsetFilenameInCentralDirectory + |
+ file_name_length + extra_field_length + comment_field_length; |
+ |
+ uint32_t local_header_offset = |
+ ReadUInt32(mem_bytes, off + kOffsetLocalHeaderOffsetInCentralDirectory); |
+ |
+ uint8_t* filename_bytes = |
+ mem_bytes + off + kOffsetFilenameInCentralDirectory; |
+ |
+ if (file_name_length == target_len && |
+ memcmp(filename_bytes, filename, target_len) == 0) { |
+ // Filename matches. Read the local header and compute the offset. |
+ uint32_t marker = ReadUInt32(mem_bytes, local_header_offset); |
+ if (marker != kLocalHeaderMarker) { |
+ LOG("%s: Failed to find local file header marker in %s. " |
+ "Found 0x%x but expected 0x%x\n", __FUNCTION__, |
+ zip_file, marker, kLocalHeaderMarker); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ uint32_t compression_method = |
+ ReadUInt16( |
+ mem_bytes, |
+ local_header_offset + kOffsetCompressionMethodInLocalHeader); |
+ if (compression_method != kCompressionMethodStored) { |
+ LOG("%s: %s is compressed within %s. " |
+ "Found compression method %u but expected %u\n", __FUNCTION__, |
+ filename, zip_file, compression_method, kCompressionMethodStored); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ uint32_t file_name_length = |
+ ReadUInt16( |
+ mem_bytes, |
+ local_header_offset + kOffsetFilenameLengthInLocalHeader); |
+ uint32_t extra_field_length = |
+ ReadUInt16( |
+ mem_bytes, |
+ local_header_offset + kOffsetExtraFieldLengthInLocalHeader); |
+ uint32_t header_length = |
+ kOffsetFilenameInLocalHeader + file_name_length + extra_field_length; |
+ |
+ return local_header_offset + header_length; |
+ } |
+ |
+ off += header_length; |
+ } |
+ |
+ if (n < num_entries) { |
+ LOG("%s: Did not find all the expected entries in the central directory. " |
+ "Found %d but expected %d\n", __FUNCTION__, n, num_entries); |
+ } |
+ |
+ if (off < end_of_central_dir) { |
+ LOG("%s: There are %d extra bytes at the end of the central directory.\n", |
+ __FUNCTION__, end_of_central_dir - off); |
+ } |
+ |
+ LOG("%s: Did not find %s in %s\n", __FUNCTION__, filename, zip_file); |
+ return CRAZY_OFFSET_FAILED; |
+} |
+ |
+} // crazy namespace |