You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
12 KiB
387 lines
12 KiB
/*
|
|
* Copyright 2012, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "bcinfo/Wrap/bitcode_wrapperer.h"
|
|
|
|
#define LOG_TAG "bcinfo"
|
|
|
|
#include <stdio.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <log/log.h>
|
|
|
|
using std::vector;
|
|
|
|
// The number of bytes in a 32 bit integer.
|
|
static const uint32_t kWordSize = 4;
|
|
|
|
// Number of LLVM-defined fixed fields in the header.
|
|
static const uint32_t kLLVMFields = 4;
|
|
|
|
// Total number of fixed fields in the header.
|
|
static const uint32_t kFixedFields = 7;
|
|
|
|
// The magic number that must exist for bitcode wrappers.
|
|
static const uint32_t kWrapperMagicNumber = 0x0B17C0DE;
|
|
|
|
// The version number associated with a wrapper file.
|
|
// Note: llvm currently only allows the value 0. When this changes,
|
|
// we should consider making this a command line option.
|
|
static const uint32_t kLLVMVersionNumber = 0;
|
|
|
|
// Fields defined by Android bitcode header.
|
|
static const uint32_t kAndroidHeaderVersion = 0;
|
|
static const uint32_t kAndroidTargetAPI = 0;
|
|
static const uint32_t kAndroidDefaultCompilerVersion = 0;
|
|
static const uint32_t kAndroidDefaultOptimizationLevel = 3;
|
|
|
|
// PNaCl bitcode version number.
|
|
static const uint32_t kPnaclBitcodeVersion = 0;
|
|
|
|
// Max size for variable fields. Currently only used for writing them
|
|
// out to files (the parsing works for arbitrary sizes).
|
|
static const size_t kMaxVariableFieldSize = 256;
|
|
|
|
BitcodeWrapperer::BitcodeWrapperer(WrapperInput* infile, WrapperOutput* outfile)
|
|
: infile_(infile),
|
|
outfile_(outfile),
|
|
buffer_size_(0),
|
|
cursor_(0),
|
|
infile_at_eof_(false),
|
|
infile_bc_offset_(0),
|
|
wrapper_bc_offset_(0),
|
|
wrapper_bc_size_(0),
|
|
android_header_version_(kAndroidHeaderVersion),
|
|
android_target_api_(kAndroidTargetAPI),
|
|
android_compiler_version_(kAndroidDefaultCompilerVersion),
|
|
android_optimization_level_(kAndroidDefaultOptimizationLevel),
|
|
pnacl_bc_version_(0),
|
|
error_(false) {
|
|
buffer_.resize(kBitcodeWrappererBufferSize);
|
|
if (IsInputBitcodeWrapper()) {
|
|
ParseWrapperHeader();
|
|
} else if (IsInputBitcodeFile()) {
|
|
wrapper_bc_offset_ = kWordSize * kFixedFields;
|
|
wrapper_bc_size_ = GetInFileSize();
|
|
} else {
|
|
ALOGE("Error: input file is not a bitcode file.\n");
|
|
error_ = true;
|
|
}
|
|
}
|
|
|
|
BitcodeWrapperer::~BitcodeWrapperer() {
|
|
for(size_t i = 0; i < variable_field_data_.size(); i++) {
|
|
delete [] variable_field_data_[i];
|
|
}
|
|
}
|
|
|
|
|
|
void BitcodeWrapperer::ClearBuffer() {
|
|
buffer_size_ = 0;
|
|
cursor_ = 0;
|
|
infile_at_eof_ = false;
|
|
}
|
|
|
|
bool BitcodeWrapperer::Seek(uint32_t pos) {
|
|
if (infile_ != nullptr && infile_->Seek(pos)) {
|
|
ClearBuffer();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BitcodeWrapperer::CanReadWord() {
|
|
if (GetBufferUnreadBytes() < kWordSize) {
|
|
FillBuffer();
|
|
return GetBufferUnreadBytes() >= kWordSize;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void BitcodeWrapperer::FillBuffer() {
|
|
if (cursor_ > 0) {
|
|
// Before filling, move any remaining bytes to the
|
|
// front of the buffer. This allows us to assume
|
|
// that after the call to FillBuffer, readable
|
|
// text is contiguous.
|
|
if (cursor_ < buffer_size_) {
|
|
size_t i = 0;
|
|
while (cursor_ < buffer_size_) {
|
|
buffer_[i++] = buffer_[cursor_++];
|
|
}
|
|
cursor_ = 0;
|
|
buffer_size_ = i;
|
|
}
|
|
} else {
|
|
// Assume the buffer contents have been used,
|
|
// and we want to completely refill it.
|
|
buffer_size_ = 0;
|
|
}
|
|
|
|
// If we don't have an input, we can't refill the buffer at all.
|
|
if (infile_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Now fill in remaining space.
|
|
size_t needed = buffer_.size() - buffer_size_;
|
|
|
|
while (buffer_.size() > buffer_size_) {
|
|
int actually_read = infile_->Read(&buffer_[buffer_size_], needed);
|
|
if (infile_->AtEof()) {
|
|
infile_at_eof_ = true;
|
|
}
|
|
if (actually_read) {
|
|
buffer_size_ += actually_read;
|
|
needed -= actually_read;
|
|
} else if (infile_at_eof_) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BitcodeWrapperer::ReadWord(uint32_t& word) {
|
|
if (!CanReadWord()) return false;
|
|
word = (((uint32_t) BufferLookahead(0)) << 0)
|
|
| (((uint32_t) BufferLookahead(1)) << 8)
|
|
| (((uint32_t) BufferLookahead(2)) << 16)
|
|
| (((uint32_t) BufferLookahead(3)) << 24);
|
|
cursor_ += kWordSize;
|
|
return true;
|
|
}
|
|
|
|
bool BitcodeWrapperer::WriteWord(uint32_t value) {
|
|
uint8_t buffer[kWordSize];
|
|
buffer[3] = (value >> 24) & 0xFF;
|
|
buffer[2] = (value >> 16) & 0xFF;
|
|
buffer[1] = (value >> 8) & 0xFF;
|
|
buffer[0] = (value >> 0) & 0xFF;
|
|
return outfile_->Write(buffer, kWordSize);
|
|
}
|
|
|
|
bool BitcodeWrapperer::WriteVariableFields() {
|
|
// This buffer may have to be bigger if we start using the fields
|
|
// for larger things.
|
|
uint8_t buffer[kMaxVariableFieldSize];
|
|
for (vector<BCHeaderField>::iterator it = header_fields_.begin();
|
|
it != header_fields_.end(); ++it) {
|
|
if (!it->Write(buffer, kMaxVariableFieldSize) ||
|
|
!outfile_->Write(buffer, it->GetTotalSize())) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BitcodeWrapperer::ParseWrapperHeader() {
|
|
// Make sure LLVM-defined fields have been parsed
|
|
if (!IsInputBitcodeWrapper()) return false;
|
|
// Check the android/pnacl fields
|
|
if (!ReadWord(android_header_version_) ||
|
|
!ReadWord(android_target_api_) || !ReadWord(pnacl_bc_version_)) {
|
|
ALOGW("Error: file not long enough to contain header\n");
|
|
return false;
|
|
}
|
|
if (pnacl_bc_version_ != kPnaclBitcodeVersion) {
|
|
ALOGW("Error: bad PNaCl Bitcode version\n");
|
|
return false;
|
|
}
|
|
int field_data_total = wrapper_bc_offset_ - kWordSize * kFixedFields;
|
|
if (field_data_total > 0) {
|
|
// Read in the variable fields. We need to allocate space for the data.
|
|
int field_data_read = 0;
|
|
|
|
while (field_data_read < field_data_total) {
|
|
FillBuffer();
|
|
size_t buffer_needed = BCHeaderField::GetDataSizeFromSerialized(
|
|
&buffer_[cursor_]);
|
|
if (buffer_needed > buffer_.size()) {
|
|
buffer_.resize(buffer_needed +
|
|
sizeof(BCHeaderField::FixedSubfield) * 2);
|
|
FillBuffer();
|
|
}
|
|
variable_field_data_.push_back(new uint8_t[buffer_needed]);
|
|
|
|
BCHeaderField field(BCHeaderField::kInvalid, 0,
|
|
variable_field_data_.back());
|
|
field.Read(&buffer_[cursor_], buffer_size_);
|
|
header_fields_.push_back(field);
|
|
size_t field_size = field.GetTotalSize();
|
|
cursor_ += field_size;
|
|
field_data_read += field_size;
|
|
if (field_data_read > field_data_total) {
|
|
// We read too much data, the header is corrupted
|
|
ALOGE("Error: raw bitcode offset inconsistent with "
|
|
"variable field data\n");
|
|
return false;
|
|
}
|
|
|
|
struct IntFieldHelper {
|
|
BCHeaderField::FixedSubfield tag;
|
|
uint16_t len;
|
|
uint32_t val;
|
|
};
|
|
IntFieldHelper tempIntField;
|
|
|
|
switch (field.getID()) {
|
|
case BCHeaderField::kAndroidCompilerVersion:
|
|
if (field.Write((uint8_t*)&tempIntField,
|
|
sizeof(tempIntField))) {
|
|
android_compiler_version_ = tempIntField.val;
|
|
}
|
|
break;
|
|
case BCHeaderField::kAndroidOptimizationLevel:
|
|
if (field.Write((uint8_t*)&tempIntField,
|
|
sizeof(tempIntField))) {
|
|
android_optimization_level_ = tempIntField.val;
|
|
}
|
|
break;
|
|
default:
|
|
// Ignore other field types for now
|
|
break;
|
|
}
|
|
}
|
|
Seek(0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool BitcodeWrapperer::IsInputBitcodeWrapper() {
|
|
ResetCursor();
|
|
// First make sure that there are enough words (LLVM header)
|
|
// to peek at.
|
|
if (GetBufferUnreadBytes() < kLLVMFields * kWordSize) {
|
|
FillBuffer();
|
|
if (GetBufferUnreadBytes() < kLLVMFields * kWordSize) return false;
|
|
}
|
|
|
|
// Now make sure the magic number is right.
|
|
uint32_t first_word;
|
|
if ((!ReadWord(first_word)) ||
|
|
(kWrapperMagicNumber != first_word)) return false;
|
|
|
|
// Make sure the version is right.
|
|
uint32_t second_word;
|
|
if ((!ReadWord(second_word)) ||
|
|
(kLLVMVersionNumber != second_word)) return false;
|
|
|
|
// Make sure that the offset and size (for llvm) is defined.
|
|
uint32_t bc_offset;
|
|
uint32_t bc_size;
|
|
if (ReadWord(bc_offset) &&
|
|
ReadWord(bc_size)) {
|
|
// Before returning, save the extracted values.
|
|
wrapper_bc_offset_ = bc_offset;
|
|
infile_bc_offset_ = bc_offset;
|
|
wrapper_bc_size_ = bc_size;
|
|
return true;
|
|
}
|
|
// If reached, unable to read wrapped header.
|
|
return false;
|
|
}
|
|
|
|
bool BitcodeWrapperer::IsInputBitcodeFile() {
|
|
ResetCursor();
|
|
// First make sure that there are four bytes to peek at.
|
|
if (GetBufferUnreadBytes() < kWordSize) {
|
|
FillBuffer();
|
|
if (GetBufferUnreadBytes() < kWordSize) return false;
|
|
}
|
|
// If reached, Check if first 4 bytes match bitcode
|
|
// file magic number.
|
|
return (BufferLookahead(0) == 'B') &&
|
|
(BufferLookahead(1) == 'C') &&
|
|
(BufferLookahead(2) == 0xc0) &&
|
|
(BufferLookahead(3) == 0xde);
|
|
}
|
|
|
|
bool BitcodeWrapperer::BufferCopyInToOut(uint32_t size) {
|
|
while (size > 0) {
|
|
// Be sure buffer is non-empty before writing.
|
|
if (0 == buffer_size_) {
|
|
FillBuffer();
|
|
if (0 == buffer_size_) {
|
|
return false;
|
|
}
|
|
}
|
|
// copy the buffer to the output file.
|
|
size_t block = (buffer_size_ < size) ? buffer_size_ : size;
|
|
if (!outfile_->Write(&buffer_[cursor_], block)) return false;
|
|
size -= block;
|
|
buffer_size_ = 0;
|
|
}
|
|
// Be sure that there isn't more bytes on the input stream.
|
|
FillBuffer();
|
|
return buffer_size_ == 0;
|
|
}
|
|
|
|
void BitcodeWrapperer::AddHeaderField(BCHeaderField* field) {
|
|
header_fields_.push_back(*field);
|
|
wrapper_bc_offset_ += field->GetTotalSize();
|
|
}
|
|
|
|
bool BitcodeWrapperer::WriteBitcodeWrapperHeader() {
|
|
return
|
|
// Note: This writes out the 4 word header required by llvm wrapped
|
|
// bitcode.
|
|
WriteWord(kWrapperMagicNumber) &&
|
|
WriteWord(kLLVMVersionNumber) &&
|
|
WriteWord(wrapper_bc_offset_) &&
|
|
WriteWord(wrapper_bc_size_) &&
|
|
// 2 fixed fields defined by Android
|
|
WriteWord(android_header_version_) &&
|
|
WriteWord(android_target_api_) &&
|
|
// PNaClBitcode version
|
|
WriteWord(kPnaclBitcodeVersion) &&
|
|
// Common variable-length fields
|
|
WriteVariableFields();
|
|
}
|
|
|
|
void BitcodeWrapperer::PrintWrapperHeader() {
|
|
if (error_) {
|
|
fprintf(stderr, "Error condition exists: the following"
|
|
"data may not be reliable\n");
|
|
}
|
|
fprintf(stderr, "Wrapper magic:\t\t%x\n", kWrapperMagicNumber);
|
|
fprintf(stderr, "LLVM Bitcode version:\t%d\n", kLLVMVersionNumber);
|
|
fprintf(stderr, "Raw bitcode offset:\t%d\n", wrapper_bc_offset_);
|
|
fprintf(stderr, "Raw bitcode size:\t%d\n", wrapper_bc_size_);
|
|
fprintf(stderr, "Android header version:\t%d\n", android_header_version_);
|
|
fprintf(stderr, "Android target API:\t%d\n", android_target_api_);
|
|
fprintf(stderr, "PNaCl bitcode version:\t%d\n", kPnaclBitcodeVersion);
|
|
for (size_t i = 0; i < header_fields_.size(); i++) header_fields_[i].Print();
|
|
}
|
|
|
|
bool BitcodeWrapperer::GenerateWrappedBitcodeFile() {
|
|
if (!error_ &&
|
|
WriteBitcodeWrapperHeader() &&
|
|
Seek(infile_bc_offset_) &&
|
|
BufferCopyInToOut(wrapper_bc_size_)) {
|
|
off_t dangling = wrapper_bc_size_ & 3;
|
|
if (dangling) {
|
|
return outfile_->Write((const uint8_t*) "\0\0\0\0", 4 - dangling);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BitcodeWrapperer::GenerateRawBitcodeFile() {
|
|
return !error_ && Seek(infile_bc_offset_) &&
|
|
BufferCopyInToOut(wrapper_bc_size_);
|
|
}
|