mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 20:56:06 +00:00

In Android API level 23 and above, dynamic loader is able to load .so file directly from APK, which is zip file. https://android.googlesource.com/platform/bionic/+/master/ android-changes-for-ndk-developers.md# opening-shared-libraries-directly-from-an-apk The .so file is page aligned and uncompressed, so ObjectFileELF::GetModuleSpecifications works with .so file offset and size directly from zip file without extracting it. (D152757) GDBRemoteCommunicationServerCommon::GetModuleInfo returns a module spec to LLDB with "zip_path!/so_path" file spec, which is passed through from Android dynamic loader, and the .so file offset and size. PlatformAndroid::DownloadModuleSlice uses 'shell dd' to download the .so file slice from the zip file with the .so file offset and size. Differential Revision: https://reviews.llvm.org/D152759
181 lines
6.7 KiB
C++
181 lines
6.7 KiB
C++
//===-- ZipFile.cpp -------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Utility/ZipFile.h"
|
|
#include "lldb/Utility/DataBuffer.h"
|
|
#include "lldb/Utility/FileSpec.h"
|
|
#include "llvm/Support/Endian.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace llvm::support;
|
|
|
|
namespace {
|
|
|
|
// Zip headers.
|
|
// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
|
|
|
// The end of central directory record.
|
|
struct EocdRecord {
|
|
static constexpr char kSignature[] = {0x50, 0x4b, 0x05, 0x06};
|
|
char signature[sizeof(kSignature)];
|
|
unaligned_uint16_t disks;
|
|
unaligned_uint16_t cd_start_disk;
|
|
unaligned_uint16_t cds_on_this_disk;
|
|
unaligned_uint16_t cd_records;
|
|
unaligned_uint32_t cd_size;
|
|
unaligned_uint32_t cd_offset;
|
|
unaligned_uint16_t comment_length;
|
|
};
|
|
|
|
// Logical find limit for the end of central directory record.
|
|
const size_t kEocdRecordFindLimit =
|
|
sizeof(EocdRecord) +
|
|
std::numeric_limits<decltype(EocdRecord::comment_length)>::max();
|
|
|
|
// Central directory record.
|
|
struct CdRecord {
|
|
static constexpr char kSignature[] = {0x50, 0x4b, 0x01, 0x02};
|
|
char signature[sizeof(kSignature)];
|
|
unaligned_uint16_t version_made_by;
|
|
unaligned_uint16_t version_needed_to_extract;
|
|
unaligned_uint16_t general_purpose_bit_flag;
|
|
unaligned_uint16_t compression_method;
|
|
unaligned_uint16_t last_modification_time;
|
|
unaligned_uint16_t last_modification_date;
|
|
unaligned_uint32_t crc32;
|
|
unaligned_uint32_t compressed_size;
|
|
unaligned_uint32_t uncompressed_size;
|
|
unaligned_uint16_t file_name_length;
|
|
unaligned_uint16_t extra_field_length;
|
|
unaligned_uint16_t comment_length;
|
|
unaligned_uint16_t file_start_disk;
|
|
unaligned_uint16_t internal_file_attributes;
|
|
unaligned_uint32_t external_file_attributes;
|
|
unaligned_uint32_t local_file_header_offset;
|
|
};
|
|
// Immediately after CdRecord,
|
|
// - file name (file_name_length)
|
|
// - extra field (extra_field_length)
|
|
// - comment (comment_length)
|
|
|
|
// Local file header.
|
|
struct LocalFileHeader {
|
|
static constexpr char kSignature[] = {0x50, 0x4b, 0x03, 0x04};
|
|
char signature[sizeof(kSignature)];
|
|
unaligned_uint16_t version_needed_to_extract;
|
|
unaligned_uint16_t general_purpose_bit_flag;
|
|
unaligned_uint16_t compression_method;
|
|
unaligned_uint16_t last_modification_time;
|
|
unaligned_uint16_t last_modification_date;
|
|
unaligned_uint32_t crc32;
|
|
unaligned_uint32_t compressed_size;
|
|
unaligned_uint32_t uncompressed_size;
|
|
unaligned_uint16_t file_name_length;
|
|
unaligned_uint16_t extra_field_length;
|
|
};
|
|
// Immediately after LocalFileHeader,
|
|
// - file name (file_name_length)
|
|
// - extra field (extra_field_length)
|
|
// - file data (should be compressed_size == uncompressed_size, page aligned)
|
|
|
|
const EocdRecord *FindEocdRecord(lldb::DataBufferSP zip_data) {
|
|
// Find backward the end of central directory record from the end of the zip
|
|
// file to the find limit.
|
|
const uint8_t *zip_data_end = zip_data->GetBytes() + zip_data->GetByteSize();
|
|
const uint8_t *find_limit = zip_data_end - kEocdRecordFindLimit;
|
|
const uint8_t *p = zip_data_end - sizeof(EocdRecord);
|
|
for (; p >= zip_data->GetBytes() && p >= find_limit; p--) {
|
|
auto eocd = reinterpret_cast<const EocdRecord *>(p);
|
|
if (::memcmp(eocd->signature, EocdRecord::kSignature,
|
|
sizeof(EocdRecord::kSignature)) == 0) {
|
|
// Found the end of central directory. Sanity check the values.
|
|
if (eocd->cd_records * sizeof(CdRecord) > eocd->cd_size ||
|
|
zip_data->GetBytes() + eocd->cd_offset + eocd->cd_size > p)
|
|
return nullptr;
|
|
|
|
// This is a valid end of central directory record.
|
|
return eocd;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool GetFile(lldb::DataBufferSP zip_data, uint32_t local_file_header_offset,
|
|
lldb::offset_t &file_offset, lldb::offset_t &file_size) {
|
|
auto local_file_header = reinterpret_cast<const LocalFileHeader *>(
|
|
zip_data->GetBytes() + local_file_header_offset);
|
|
// The signature should match.
|
|
if (::memcmp(local_file_header->signature, LocalFileHeader::kSignature,
|
|
sizeof(LocalFileHeader::kSignature)) != 0)
|
|
return false;
|
|
|
|
auto file_data = reinterpret_cast<const uint8_t *>(local_file_header + 1) +
|
|
local_file_header->file_name_length +
|
|
local_file_header->extra_field_length;
|
|
// File should be uncompressed.
|
|
if (local_file_header->compressed_size !=
|
|
local_file_header->uncompressed_size)
|
|
return false;
|
|
|
|
// This file is valid. Return the file offset and size.
|
|
file_offset = file_data - zip_data->GetBytes();
|
|
file_size = local_file_header->uncompressed_size;
|
|
return true;
|
|
}
|
|
|
|
bool FindFile(lldb::DataBufferSP zip_data, const EocdRecord *eocd,
|
|
const llvm::StringRef file_path, lldb::offset_t &file_offset,
|
|
lldb::offset_t &file_size) {
|
|
// Find the file from the central directory records.
|
|
auto cd = reinterpret_cast<const CdRecord *>(zip_data->GetBytes() +
|
|
eocd->cd_offset);
|
|
size_t cd_records = eocd->cd_records;
|
|
for (size_t i = 0; i < cd_records; i++) {
|
|
// The signature should match.
|
|
if (::memcmp(cd->signature, CdRecord::kSignature,
|
|
sizeof(CdRecord::kSignature)) != 0)
|
|
return false;
|
|
|
|
// Sanity check the file name values.
|
|
auto file_name = reinterpret_cast<const char *>(cd + 1);
|
|
size_t file_name_length = cd->file_name_length;
|
|
if (file_name + file_name_length >= reinterpret_cast<const char *>(eocd) ||
|
|
file_name_length == 0)
|
|
return false;
|
|
|
|
// Compare the file name.
|
|
if (file_path == llvm::StringRef(file_name, file_name_length)) {
|
|
// Found the file.
|
|
return GetFile(zip_data, cd->local_file_header_offset, file_offset,
|
|
file_size);
|
|
} else {
|
|
// Skip to the next central directory record.
|
|
cd = reinterpret_cast<const CdRecord *>(
|
|
reinterpret_cast<const char *>(cd) + sizeof(CdRecord) +
|
|
cd->file_name_length + cd->extra_field_length + cd->comment_length);
|
|
// Sanity check the pointer.
|
|
if (reinterpret_cast<const char *>(cd) >=
|
|
reinterpret_cast<const char *>(eocd))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
bool ZipFile::Find(lldb::DataBufferSP zip_data, const llvm::StringRef file_path,
|
|
lldb::offset_t &file_offset, lldb::offset_t &file_size) {
|
|
const EocdRecord *eocd = FindEocdRecord(zip_data);
|
|
if (!eocd)
|
|
return false;
|
|
|
|
return FindFile(zip_data, eocd, file_path, file_offset, file_size);
|
|
}
|