/* * 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_); }