1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "crazy_linker_zip.h" 6 7#include <fcntl.h> 8#include <sys/mman.h> 9#include <sys/stat.h> 10#include <unistd.h> 11 12#include "crazy_linker_debug.h" 13#include "crazy_linker_system.h" 14 15namespace { 16 17// All offsets are given in bytes relative to the start of the header. 18// Arithmetic is used to indicate the size of small fields that are skipped. 19 20// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.3.16 21// This marker appears at the start of the end of central directory record 22const uint32_t kEndOfCentralDirectoryMarker = 0x06054b50; 23 24// Offsets of fields in End of Central Directory. 25const int kOffsetNumOfEntriesInEndOfCentralDirectory = 4 + 2 + 2; 26const int kOffsetOfCentralDirLengthInEndOfCentralDirectory = 27 kOffsetNumOfEntriesInEndOfCentralDirectory + 2 + 2; 28const int kOffsetOfStartOfCentralDirInEndOfCentralDirectory = 29 kOffsetOfCentralDirLengthInEndOfCentralDirectory + 4; 30 31// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.3.12 32// This marker appears at the start of the central directory 33const uint32_t kCentralDirHeaderMarker = 0x2014b50; 34 35// Offsets of fields in Central Directory Header. 36const int kOffsetFilenameLengthInCentralDirectory = 37 4 + 2 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4; 38const int kOffsetExtraFieldLengthInCentralDirectory = 39 kOffsetFilenameLengthInCentralDirectory + 2; 40const int kOffsetCommentLengthInCentralDirectory = 41 kOffsetExtraFieldLengthInCentralDirectory + 2; 42const int kOffsetLocalHeaderOffsetInCentralDirectory = 43 kOffsetCommentLengthInCentralDirectory + 2 + 2 + 2 + 4; 44const int kOffsetFilenameInCentralDirectory = 45 kOffsetLocalHeaderOffsetInCentralDirectory + 4; 46 47// http://www.pkware.com/documents/casestudies/APPNOTE.TXT Section 4.3.7 48// This marker appears at the start of local header 49const uint32_t kLocalHeaderMarker = 0x04034b50; 50 51// Offsets of fields in the Local Header. 52const int kOffsetFilenameLengthInLocalHeader = 53 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4; 54const int kOffsetExtraFieldLengthInLocalHeader = 55 kOffsetFilenameLengthInLocalHeader + 2; 56const int kOffsetFilenameInLocalHeader = 57 kOffsetExtraFieldLengthInLocalHeader + 2; 58 59// RAII pattern for unmapping and closing the mapped file. 60class ScopedMMap { 61 public: 62 ScopedMMap(void* mem, uint32_t len) : mem_(mem), len_(len) {} 63 ~ScopedMMap() { 64 if (munmap(mem_, len_) == -1) { 65 LOG_ERRNO("%s: munmap failed when trying to unmap zip file\n", 66 __FUNCTION__); 67 } 68 } 69 private: 70 void* mem_; 71 uint32_t len_; 72}; 73 74inline uint32_t ReadUInt16(uint8_t* mem_bytes, int offset) { 75 return 76 static_cast<uint32_t>(mem_bytes[offset]) | 77 (static_cast<uint32_t>(mem_bytes[offset + 1]) << 8); 78} 79 80inline uint32_t ReadUInt32(uint8_t* mem_bytes, int offset) { 81 return 82 static_cast<uint32_t>(mem_bytes[offset]) | 83 (static_cast<uint32_t>(mem_bytes[offset + 1]) << 8) | 84 (static_cast<uint32_t>(mem_bytes[offset + 2]) << 16) | 85 (static_cast<uint32_t>(mem_bytes[offset + 3]) << 24); 86} 87 88} // unnamed namespace 89 90namespace crazy { 91 92const uint32_t kMaxZipFileLength = 1U << 31; // 2GB 93 94int FindStartOffsetOfFileInZipFile(const char* zip_file, const char* filename) { 95 // Open the file 96 FileDescriptor fd; 97 if (!fd.OpenReadOnly(zip_file)) { 98 LOG_ERRNO("%s: open failed trying to open zip file %s\n", 99 __FUNCTION__, zip_file); 100 return -1; 101 } 102 103 // Find the length of the file. 104 struct stat stat_buf; 105 if (stat(zip_file, &stat_buf) == -1) { 106 LOG_ERRNO("%s: stat failed trying to stat zip file %s\n", 107 __FUNCTION__, zip_file); 108 return -1; 109 } 110 111 if (stat_buf.st_size > kMaxZipFileLength) { 112 LOG("%s: The size %ld of %s is too large to map\n", 113 __FUNCTION__, stat_buf.st_size, zip_file); 114 return -1; 115 } 116 117 // Map the file into memory. 118 void* mem = fd.Map(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, 0); 119 if (mem == MAP_FAILED) { 120 LOG_ERRNO("%s: mmap failed trying to mmap zip file %s\n", 121 __FUNCTION__, zip_file); 122 return -1; 123 } 124 ScopedMMap scoped_mmap(mem, stat_buf.st_size); 125 126 // Scan backwards from the end of the file searching for the end of 127 // central directory marker. 128 uint8_t* mem_bytes = static_cast<uint8_t*>(mem); 129 int off; 130 for (off = stat_buf.st_size - sizeof(kEndOfCentralDirectoryMarker); 131 off >= 0; --off) { 132 if (ReadUInt32(mem_bytes, off) == kEndOfCentralDirectoryMarker) { 133 break; 134 } 135 } 136 if (off == -1) { 137 LOG("%s: Failed to find end of central directory in %s\n", 138 __FUNCTION__, zip_file); 139 return -1; 140 } 141 142 // We have located the end of central directory record, now locate 143 // the central directory by reading the end of central directory record. 144 145 uint32_t length_of_central_dir = ReadUInt32( 146 mem_bytes, off + kOffsetOfCentralDirLengthInEndOfCentralDirectory); 147 uint32_t start_of_central_dir = ReadUInt32( 148 mem_bytes, off + kOffsetOfStartOfCentralDirInEndOfCentralDirectory); 149 150 if (start_of_central_dir > off) { 151 LOG("%s: Found out of range offset %u for start of directory in %s\n", 152 __FUNCTION__, start_of_central_dir, zip_file); 153 return -1; 154 } 155 156 uint32_t end_of_central_dir = start_of_central_dir + length_of_central_dir; 157 if (end_of_central_dir > off) { 158 LOG("%s: Found out of range offset %u for end of directory in %s\n", 159 __FUNCTION__, end_of_central_dir, zip_file); 160 return -1; 161 } 162 163 uint32_t num_entries = ReadUInt16( 164 mem_bytes, off + kOffsetNumOfEntriesInEndOfCentralDirectory); 165 166 // Read the headers in the central directory and locate the file. 167 off = start_of_central_dir; 168 const int target_len = strlen(filename); 169 int n = 0; 170 for (; n < num_entries && off < end_of_central_dir; ++n) { 171 uint32_t marker = ReadUInt32(mem_bytes, off); 172 if (marker != kCentralDirHeaderMarker) { 173 LOG("%s: Failed to find central directory header marker in %s. " 174 "Found 0x%x but expected 0x%x\n", __FUNCTION__, 175 zip_file, marker, kCentralDirHeaderMarker); 176 return -1; 177 } 178 uint32_t file_name_length = 179 ReadUInt16(mem_bytes, off + kOffsetFilenameLengthInCentralDirectory); 180 uint32_t extra_field_length = 181 ReadUInt16(mem_bytes, off + kOffsetExtraFieldLengthInCentralDirectory); 182 uint32_t comment_field_length = 183 ReadUInt16(mem_bytes, off + kOffsetCommentLengthInCentralDirectory); 184 uint32_t header_length = kOffsetFilenameInCentralDirectory + 185 file_name_length + extra_field_length + comment_field_length; 186 187 uint32_t local_header_offset = 188 ReadUInt32(mem_bytes, off + kOffsetLocalHeaderOffsetInCentralDirectory); 189 190 uint8_t* filename_bytes = 191 mem_bytes + off + kOffsetFilenameInCentralDirectory; 192 193 if (file_name_length == target_len && 194 memcmp(filename_bytes, filename, target_len) == 0) { 195 // Filename matches. Read the local header and compute the offset. 196 uint32_t marker = ReadUInt32(mem_bytes, local_header_offset); 197 if (marker != kLocalHeaderMarker) { 198 LOG("%s: Failed to find local file header marker in %s. " 199 "Found 0x%x but expected 0x%x\n", __FUNCTION__, 200 zip_file, marker, kLocalHeaderMarker); 201 return -1; 202 } 203 204 uint32_t file_name_length = 205 ReadUInt16( 206 mem_bytes, 207 local_header_offset + kOffsetFilenameLengthInLocalHeader); 208 uint32_t extra_field_length = 209 ReadUInt16( 210 mem_bytes, 211 local_header_offset + kOffsetExtraFieldLengthInLocalHeader); 212 uint32_t header_length = 213 kOffsetFilenameInLocalHeader + file_name_length + extra_field_length; 214 215 return local_header_offset + header_length; 216 } 217 218 off += header_length; 219 } 220 221 if (n < num_entries) { 222 LOG("%s: Did not find all the expected entries in the central directory. " 223 "Found %d but expected %d\n", __FUNCTION__, n, num_entries); 224 } 225 226 if (off < end_of_central_dir) { 227 LOG("%s: There are %d extra bytes at the end of the central directory.\n", 228 __FUNCTION__, end_of_central_dir - off); 229 } 230 231 LOG("%s: Did not find %s in %s\n", __FUNCTION__, filename, zip_file); 232 return -1; 233} 234 235} // crazy namespace 236