Merge "Virtual table dumper which uses llvm's ELF API."
This commit is contained in:
19
vndk/Android.bp
Normal file
19
vndk/Android.bp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) 2017 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
subdirs = [
|
||||||
|
"tools",
|
||||||
|
]
|
||||||
19
vndk/tools/Android.bp
Normal file
19
vndk/tools/Android.bp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) 2017 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
subdirs = [
|
||||||
|
"abides",
|
||||||
|
]
|
||||||
19
vndk/tools/abides/Android.bp
Normal file
19
vndk/tools/abides/Android.bp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) 2017 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
subdirs = [
|
||||||
|
"vtable",
|
||||||
|
]
|
||||||
45
vndk/tools/abides/vtable/Android.bp
Normal file
45
vndk/tools/abides/vtable/Android.bp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) 2016 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
cc_defaults {
|
||||||
|
name: "vndk-vtable-defaults",
|
||||||
|
|
||||||
|
defaults: [
|
||||||
|
"clang-defaults",
|
||||||
|
],
|
||||||
|
|
||||||
|
shared_libs: [
|
||||||
|
"libLLVM",
|
||||||
|
],
|
||||||
|
|
||||||
|
cflags: [
|
||||||
|
"-Wall",
|
||||||
|
"-Werror",
|
||||||
|
"-std=c++11",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_binary_host {
|
||||||
|
name: "llvm-figure-out-vtables",
|
||||||
|
|
||||||
|
defaults: [
|
||||||
|
"vndk-vtable-defaults"
|
||||||
|
],
|
||||||
|
|
||||||
|
srcs: [
|
||||||
|
"llvm_elf_handling.cpp",
|
||||||
|
"llvm_vtable_dump.cpp"],
|
||||||
|
}
|
||||||
453
vndk/tools/abides/vtable/llvm_elf_handling.cpp
Normal file
453
vndk/tools/abides/vtable/llvm_elf_handling.cpp
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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 "llvm_elf_handling.h"
|
||||||
|
|
||||||
|
#include <cxxabi.h>
|
||||||
|
|
||||||
|
using llvm::ELF::ELFDATA2MSB;
|
||||||
|
using llvm::ELF::EM_ARM;
|
||||||
|
using llvm::ELF::EM_MIPS;
|
||||||
|
using llvm::ELF::R_AARCH64_ABS64;
|
||||||
|
using llvm::ELF::R_AARCH64_RELATIVE;
|
||||||
|
using llvm::ELF::R_ARM_ABS32;
|
||||||
|
using llvm::ELF::R_ARM_RELATIVE;
|
||||||
|
using llvm::ELF::R_X86_64_64;
|
||||||
|
using llvm::ELF::R_X86_64_RELATIVE;
|
||||||
|
using llvm::ELF::R_MIPS_64;
|
||||||
|
using llvm::ELF::R_MIPS_REL32;
|
||||||
|
using llvm::ELF::R_MIPS_NONE;
|
||||||
|
using llvm::ELF::SHT_PROGBITS;
|
||||||
|
using llvm::ELF::SHT_REL;
|
||||||
|
using llvm::ELF::SHT_RELA;
|
||||||
|
using llvm::Expected;
|
||||||
|
using llvm::StringRef;
|
||||||
|
using llvm::dyn_cast;
|
||||||
|
using llvm::object::ELF32BEObjectFile;
|
||||||
|
using llvm::object::ELF32LEObjectFile;
|
||||||
|
using llvm::object::ELF64BEObjectFile;
|
||||||
|
using llvm::object::ELF64LEObjectFile;
|
||||||
|
using llvm::object::symbol_iterator;
|
||||||
|
using llvm::support::endian::read;
|
||||||
|
using llvm::outs;
|
||||||
|
using llvm::Error;
|
||||||
|
using llvm::make_unique;
|
||||||
|
|
||||||
|
static std::string demangle(const std::string &MangledName) {
|
||||||
|
char *Str = __cxxabiv1::__cxa_demangle(
|
||||||
|
MangledName.c_str(),
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
nullptr);
|
||||||
|
if (Str) {
|
||||||
|
std::string DemangledString(Str);
|
||||||
|
free(Str);
|
||||||
|
return DemangledString;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedObject::~SharedObject() {}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
static std::unique_ptr<SharedObject> createELFSharedObject(
|
||||||
|
const ELFObjectFile<ELFT> *Objfile) {
|
||||||
|
return make_unique<ELFSharedObject<ELFT>>(Objfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<SharedObject>createELFObjFile(const ObjectFile *Obj) {
|
||||||
|
if (const ELF32LEObjectFile *Objfile = dyn_cast<ELF32LEObjectFile>(Obj))
|
||||||
|
return createELFSharedObject(Objfile);
|
||||||
|
if (const ELF32BEObjectFile *Objfile = dyn_cast<ELF32BEObjectFile>(Obj))
|
||||||
|
return createELFSharedObject(Objfile);
|
||||||
|
if (const ELF64LEObjectFile *Objfile = dyn_cast<ELF64LEObjectFile>(Obj))
|
||||||
|
return createELFSharedObject(Objfile);
|
||||||
|
if (const ELF64BEObjectFile *Objfile = dyn_cast<ELF64BEObjectFile>(Obj))
|
||||||
|
return createELFSharedObject(Objfile);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SharedObject> SharedObject::create(const ObjectFile *Obj) {
|
||||||
|
std::unique_ptr<SharedObject> res(createELFObjFile(Obj));
|
||||||
|
if (res && res->getVTables()) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
ELFSharedObject<ELFT>::~ELFSharedObject() {}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
ELFSharedObject<ELFT>::ELFSharedObject(
|
||||||
|
const ELFObjectFile<ELFT> *Objfile)
|
||||||
|
: mObj(Objfile) {}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
bool ELFSharedObject<ELFT>::cacheELFSections() {
|
||||||
|
for (const SectionRef &ElfSection : mObj->sections()) {
|
||||||
|
const Elf_Shdr *ElfShdr =
|
||||||
|
mObj->getSection(ElfSection.getRawDataRefImpl());
|
||||||
|
if (!ElfShdr) {
|
||||||
|
outs() << "Couldn't create elf shdr \n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (ElfShdr->sh_type) {
|
||||||
|
case SHT_RELA:
|
||||||
|
case SHT_REL:
|
||||||
|
mRelSectionRefs.emplace_back(ElfSection);
|
||||||
|
break;
|
||||||
|
case SHT_PROGBITS:
|
||||||
|
mProgBitSectionRefs.emplace_back(ElfSection);
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
// Any other section won't have information pertinent
|
||||||
|
// to vtables. Relocation entries will have the virtual
|
||||||
|
// functions' relocation information, the PROGBITS sections
|
||||||
|
// will have the vtables themselves.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
void ELFSharedObject<ELFT>::printVTables() const {
|
||||||
|
for (const VTable &Vtable : mVTables) {
|
||||||
|
if (Vtable.getVTableSize() == 0)
|
||||||
|
continue;
|
||||||
|
outs() << Vtable.getDemangledName()
|
||||||
|
<< "\n"
|
||||||
|
<< Vtable.getMangledName()
|
||||||
|
<< ": "
|
||||||
|
<< Vtable.getVTableSize()
|
||||||
|
<< " entries"
|
||||||
|
<< "\n";
|
||||||
|
for (const VFunction &Vfunction : Vtable) {
|
||||||
|
outs() << Vfunction.getOffset()
|
||||||
|
<< " (int (*)(...)) "
|
||||||
|
<< Vfunction.getDemangledName()
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
outs() << "\n"
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
bool ELFSharedObject<ELFT>::getVTables() {
|
||||||
|
if (!cacheELFSections()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!initVTableRanges()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
getVFunctions();
|
||||||
|
for (VTable &Vtable : mVTables) {
|
||||||
|
// Sort the functions by offset before displaying them since the order
|
||||||
|
// of functions appearing in relocation sections might change. That
|
||||||
|
// should not result in the vtable layout changing.
|
||||||
|
Vtable.sortVFunctions();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
bool ELFSharedObject<ELFT>::initVTableRanges() {
|
||||||
|
// Go through all the symbols in the dynsym / symtab sections
|
||||||
|
// and cache all the relevant symbols. i.e: symbols which correspond
|
||||||
|
// to either vtables or functions.
|
||||||
|
|
||||||
|
std::vector<std::pair<SymbolRef, uint64_t>> SymsAndSizes =
|
||||||
|
computeSymbolSizes(*mObj);
|
||||||
|
for (std::pair<SymbolRef, uint64_t> &Pair : SymsAndSizes) {
|
||||||
|
SymbolRef Symbol = Pair.first;
|
||||||
|
SymbolRef::Type SymType = UnWrap(Symbol.getType());
|
||||||
|
uint64_t SymValue = Symbol.getValue();
|
||||||
|
StringRef SymName = UnWrap(Symbol.getName());
|
||||||
|
if (SymName.startswith("__ZTV") || SymName.startswith("_ZTV")) {
|
||||||
|
mVTables.emplace_back(
|
||||||
|
SymName.str(),
|
||||||
|
demangle(SymName.str()),
|
||||||
|
Symbol.getValue(),
|
||||||
|
Symbol.getValue() + Pair.second);
|
||||||
|
} else if (SymType == SymbolRef::ST_Function) {
|
||||||
|
std::map<uint64_t, std::vector<SymbolRef>>::iterator It =
|
||||||
|
mAddrToSymbolRef.find(SymValue);
|
||||||
|
if (It == mAddrToSymbolRef.end()) {
|
||||||
|
mAddrToSymbolRef.insert(std::make_pair(
|
||||||
|
SymValue, std::vector<SymbolRef>(1, Symbol)));
|
||||||
|
} else {
|
||||||
|
std::vector<SymbolRef> &SymVec = It->second;
|
||||||
|
SymVec.emplace_back(Symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mVTables.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::sort(mVTables.begin(), mVTables.end());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
void ELFSharedObject<ELFT>::getVFunctions() {
|
||||||
|
for (const SectionRef &Section : mRelSectionRefs) {
|
||||||
|
for (const RelocationRef &Relocation : Section.relocations()) {
|
||||||
|
VTable *VtPtr = identifyVTable(Relocation.getOffset());
|
||||||
|
if (VtPtr != nullptr) {
|
||||||
|
relocateSym(Relocation, Section, VtPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
VTable *ELFSharedObject<ELFT>::identifyVTable(uint64_t RelOffset) {
|
||||||
|
typename std::vector<VTable>::iterator It;
|
||||||
|
It = std::lower_bound(mVTables.begin(), mVTables.end(), RelOffset);
|
||||||
|
if (It != mVTables.begin() && It->getStartAddr() != RelOffset) {
|
||||||
|
It--;
|
||||||
|
}
|
||||||
|
if (It->getEndAddr() >= RelOffset) {
|
||||||
|
return &(*It);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
void ELFSharedObject<ELFT>::relocateSym(
|
||||||
|
const RelocationRef &Relocation,
|
||||||
|
const SectionRef &Section,
|
||||||
|
VTable *Vtablep) {
|
||||||
|
const Elf_Ehdr *ElfHeader = mObj->getELFFile()->getHeader();
|
||||||
|
if (ElfHeader->e_machine == EM_MIPS) {
|
||||||
|
// bionic/linker/linker_mips.cpp , we handle only one type of
|
||||||
|
// relocation. Depending on if the symbol can be inferred from r_info we
|
||||||
|
// make it an absolute or a relative relocation.
|
||||||
|
if (!absoluteRelocation(Relocation, Vtablep)) {
|
||||||
|
relativeRelocation(Relocation, Section, Vtablep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch(Relocation.getType()) {
|
||||||
|
case R_AARCH64_RELATIVE:
|
||||||
|
case R_X86_64_RELATIVE:
|
||||||
|
case R_ARM_RELATIVE:
|
||||||
|
{
|
||||||
|
// The return value is ignored since failure to relocate
|
||||||
|
// does not mean a fatal error. It might be that the dynsym /
|
||||||
|
// symbol-table does not have enough information to get the
|
||||||
|
// symbol name. Like-wise for absolute relocations.
|
||||||
|
relativeRelocation(Relocation, Section, Vtablep);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case R_AARCH64_ABS64:
|
||||||
|
case R_X86_64_64:
|
||||||
|
case R_ARM_ABS32:
|
||||||
|
{
|
||||||
|
absoluteRelocation(Relocation, Vtablep);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
bool ELFSharedObject<ELFT>::absoluteRelocation(
|
||||||
|
const RelocationRef &Relocation,
|
||||||
|
VTable *Vtablep) {
|
||||||
|
symbol_iterator Symi = Relocation.getSymbol();
|
||||||
|
if (Symi == mObj->symbol_end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SymbolRef Symbol = *Symi;
|
||||||
|
uint64_t RelOffset = Relocation.getOffset();
|
||||||
|
StringRef SymbolName = UnWrap(Symbol.getName());
|
||||||
|
std::string DemangledName = demangle(SymbolName.str());
|
||||||
|
if (!DemangledName.empty()) {
|
||||||
|
Vtablep->addVFunction(SymbolName.str(), DemangledName, RelOffset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
bool ELFSharedObject<ELFT>::relativeRelocation(
|
||||||
|
const RelocationRef &Relocation,
|
||||||
|
const SectionRef &Section,
|
||||||
|
VTable *Vtablep) {
|
||||||
|
uint64_t Addend = 0;
|
||||||
|
uint64_t RelOffset = Relocation.getOffset();
|
||||||
|
if (mObj->getSection(Section.getRawDataRefImpl())->sh_type == SHT_RELA) {
|
||||||
|
const Elf_Rela *Rela = mObj->getRela(Relocation.getRawDataRefImpl());
|
||||||
|
Addend = static_cast<uint64_t>(Rela->r_addend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Addend == 0) {
|
||||||
|
Addend = identifyAddend(Relocation.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<uint64_t, std::vector<SymbolRef>>::iterator It =
|
||||||
|
mAddrToSymbolRef.find(Addend);
|
||||||
|
if (It == mAddrToSymbolRef.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SymbolRef Symbol = matchValueToSymbol(It->second, Vtablep);
|
||||||
|
StringRef SymbolName = UnWrap(Symbol.getName());
|
||||||
|
std::string DemangledName = demangle(SymbolName.str());
|
||||||
|
if (!DemangledName.empty()) {
|
||||||
|
Vtablep->addVFunction(SymbolName.str(), DemangledName, RelOffset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
SymbolRef ELFSharedObject<ELFT>::matchValueToSymbol(
|
||||||
|
std::vector<SymbolRef> &SymVec,
|
||||||
|
VTable *Vtablep) {
|
||||||
|
constexpr size_t pos = sizeof("vtable for ") - 1;
|
||||||
|
const std::string ClassName(Vtablep->getDemangledName().substr(pos));
|
||||||
|
for (const SymbolRef &Symbol : SymVec) {
|
||||||
|
StringRef SymbolName = UnWrap(Symbol.getName());
|
||||||
|
if (SymbolName.str().find(ClassName) != std::string::npos)
|
||||||
|
return Symbol;
|
||||||
|
}
|
||||||
|
// Return the 1st Symbol by default.
|
||||||
|
return SymVec[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
uint64_t ELFSharedObject<ELFT>::identifyAddend(uint64_t ROffset) {
|
||||||
|
for (const SectionRef &Section : mProgBitSectionRefs) {
|
||||||
|
uint64_t Begin = Section.getAddress();
|
||||||
|
uint64_t End = Section.getAddress() + Section.getSize();
|
||||||
|
if (ROffset >= Begin && ROffset <= End) {
|
||||||
|
return getAddendFromSection(Section, ROffset - Begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ELFT>
|
||||||
|
uint64_t ELFSharedObject<ELFT>::getAddendFromSection(
|
||||||
|
const SectionRef &Section,
|
||||||
|
uint64_t Offset) {
|
||||||
|
StringRef Contents;
|
||||||
|
if (Section.getContents(Contents))
|
||||||
|
return 0;
|
||||||
|
const unsigned char *Bytes = Contents.bytes_begin() + Offset;
|
||||||
|
uintX_t Addend = read<uintX_t, ELFT::TargetEndianness>(Bytes);
|
||||||
|
const Elf_Ehdr *ElfHeader = mObj->getELFFile()->getHeader();
|
||||||
|
if (ElfHeader->e_machine == EM_ARM ||
|
||||||
|
ElfHeader->e_machine == EM_MIPS) {
|
||||||
|
// Remove thumb flag as llvm suggests.
|
||||||
|
Addend &= ~1;
|
||||||
|
}
|
||||||
|
return static_cast<uint64_t>(Addend);
|
||||||
|
}
|
||||||
|
|
||||||
|
VFunction::VFunction(
|
||||||
|
const std::string &MangledName,
|
||||||
|
const std::string &DemangledName,
|
||||||
|
uint64_t VFunctionOffset)
|
||||||
|
: mMangledName(MangledName),
|
||||||
|
mDemangledName(DemangledName),
|
||||||
|
mOffset(VFunctionOffset) {}
|
||||||
|
|
||||||
|
uint64_t VFunction::getOffset() const {
|
||||||
|
return mOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &VFunction::getDemangledName() const {
|
||||||
|
return mDemangledName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &VFunction::getMangledName() const {
|
||||||
|
return mMangledName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VFunction::operator<(const VFunction &Vfunction) const {
|
||||||
|
return mOffset < Vfunction.getOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
VTable::VTable(
|
||||||
|
const std::string &MangledName,
|
||||||
|
const std::string &DemangledName,
|
||||||
|
uint64_t Begin,
|
||||||
|
uint64_t End)
|
||||||
|
: mMangledName(MangledName),
|
||||||
|
mDemangledName(DemangledName),
|
||||||
|
mStartAddr(Begin),
|
||||||
|
mEndAddr(End),
|
||||||
|
mBaseOffset(Begin) {}
|
||||||
|
|
||||||
|
void VTable::addVFunction(
|
||||||
|
const std::string &MangledName,
|
||||||
|
const std::string &DemangledName,
|
||||||
|
uint64_t RelOffset) {
|
||||||
|
mFunctions.emplace_back(
|
||||||
|
MangledName,
|
||||||
|
DemangledName,
|
||||||
|
RelOffset - mBaseOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &VTable::getDemangledName() const {
|
||||||
|
return mDemangledName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &VTable::getMangledName() const {
|
||||||
|
return mMangledName;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VTable::getStartAddr() const {
|
||||||
|
return mStartAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VTable::getEndAddr() const {
|
||||||
|
return mEndAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VTable::getBaseOffset() const {
|
||||||
|
return mBaseOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VTable::getVTableSize() const {
|
||||||
|
return mFunctions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
VTable::func_iterator VTable::begin() const {
|
||||||
|
return mFunctions.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
VTable::func_iterator VTable::end() const {
|
||||||
|
return mFunctions.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VTable::operator<(const VTable &Vtable) const {
|
||||||
|
return mStartAddr < Vtable.getStartAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VTable::operator<(const uint64_t ROffset) const {
|
||||||
|
return mStartAddr < ROffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VTable::sortVFunctions() {
|
||||||
|
std::sort(mFunctions.begin(), mFunctions.end());
|
||||||
|
}
|
||||||
170
vndk/tools/abides/vtable/llvm_elf_handling.h
Normal file
170
vndk/tools/abides/vtable/llvm_elf_handling.h
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ELF_HANDLING_H_
|
||||||
|
#define ELF_HANDLING_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <llvm/Object/ELFObjectFile.h>
|
||||||
|
#include <llvm/Object/ELFTypes.h>
|
||||||
|
#include <llvm/Object/SymbolSize.h>
|
||||||
|
#include <llvm/Support/Endian.h>
|
||||||
|
#include <llvm/Support/raw_ostream.h>
|
||||||
|
|
||||||
|
using llvm::object::ObjectFile;
|
||||||
|
using llvm::object::ELFObjectFile;
|
||||||
|
using llvm::object::SectionRef;
|
||||||
|
using llvm::object::RelocationRef;
|
||||||
|
using llvm::object::ELFFile;
|
||||||
|
using llvm::object::ELFType;
|
||||||
|
using llvm::object::ELFDataTypeTypedefHelper;
|
||||||
|
using llvm::object::SymbolRef;
|
||||||
|
using llvm::outs;
|
||||||
|
|
||||||
|
class SharedObject {
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<SharedObject> create(const ObjectFile *);
|
||||||
|
virtual void printVTables() const = 0;
|
||||||
|
virtual ~SharedObject() = 0;
|
||||||
|
private:
|
||||||
|
virtual bool getVTables() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VFunction {
|
||||||
|
public:
|
||||||
|
VFunction(
|
||||||
|
const std::string &,
|
||||||
|
const std::string &,
|
||||||
|
uint64_t);
|
||||||
|
|
||||||
|
uint64_t getOffset() const;
|
||||||
|
bool operator<(const VFunction &) const;
|
||||||
|
const std::string &getMangledName() const;
|
||||||
|
const std::string &getDemangledName() const;
|
||||||
|
private:
|
||||||
|
std::string mMangledName;
|
||||||
|
std::string mDemangledName;
|
||||||
|
uint64_t mOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VTable {
|
||||||
|
public:
|
||||||
|
using func_iterator = std::vector<VFunction>::const_iterator;
|
||||||
|
VTable(
|
||||||
|
const std::string &,
|
||||||
|
const std::string &,
|
||||||
|
uint64_t,
|
||||||
|
uint64_t);
|
||||||
|
|
||||||
|
uint64_t getStartAddr() const;
|
||||||
|
uint64_t getEndAddr() const;
|
||||||
|
uint64_t getBaseOffset() const;
|
||||||
|
uint64_t getVTableSize() const;
|
||||||
|
func_iterator begin() const;
|
||||||
|
func_iterator end() const;
|
||||||
|
const std::string &getMangledName() const;
|
||||||
|
const std::string &getDemangledName() const;
|
||||||
|
void sortVFunctions();
|
||||||
|
void addVFunction(
|
||||||
|
const std::string &,
|
||||||
|
const std::string &,
|
||||||
|
uint64_t);
|
||||||
|
|
||||||
|
bool operator<(const VTable &) const;
|
||||||
|
bool operator<(const uint64_t) const;
|
||||||
|
private:
|
||||||
|
std::vector<VFunction> mFunctions;
|
||||||
|
std::string mMangledName;
|
||||||
|
std::string mDemangledName;
|
||||||
|
/* This holds the range(st_value, st_value) through which the
|
||||||
|
* VTable spans.
|
||||||
|
*/
|
||||||
|
uint64_t mStartAddr;
|
||||||
|
uint64_t mEndAddr;
|
||||||
|
uint64_t mBaseOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ELFT>
|
||||||
|
class ELFSharedObject : public SharedObject {
|
||||||
|
public:
|
||||||
|
void printVTables() const override;
|
||||||
|
bool getVTables() override;
|
||||||
|
~ELFSharedObject();
|
||||||
|
ELFSharedObject(const ELFObjectFile<ELFT> *);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* We need a sym value to SymbolRef map in case the relocation provides
|
||||||
|
* us with an addr instead of a sym index into dynsym / symtab.
|
||||||
|
*/
|
||||||
|
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
|
||||||
|
typedef ELFFile<ELFT> ELFO;
|
||||||
|
typedef typename ELFO::Elf_Shdr Elf_Shdr;
|
||||||
|
typedef typename ELFO::Elf_Ehdr Elf_Ehdr;
|
||||||
|
typedef typename ELFO::Elf_Sym Elf_Sym;
|
||||||
|
typedef typename ELFO::Elf_Rela Elf_Rela;
|
||||||
|
typedef typename ELFO::uintX_t uintX_t;
|
||||||
|
std::map<uint64_t, std::vector<SymbolRef>> mAddrToSymbolRef;
|
||||||
|
const ELFObjectFile<ELFT> *mObj;
|
||||||
|
/* We cache the relocation sections, to look through their relocations for
|
||||||
|
* vfunctions. Sections with type SHT_PROGBITS are cached since they contain
|
||||||
|
* vtables. We might need to peek at the contents of a vtable in cases of
|
||||||
|
* relative relocations.
|
||||||
|
*/
|
||||||
|
std::vector<SectionRef> mRelSectionRefs;
|
||||||
|
std::vector<SectionRef> mProgBitSectionRefs;
|
||||||
|
std::vector<VTable> mVTables;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool cacheELFSections();
|
||||||
|
bool initVTableRanges();
|
||||||
|
void getVFunctions();
|
||||||
|
VTable *identifyVTable(uint64_t);
|
||||||
|
void relocateSym(
|
||||||
|
const RelocationRef &,
|
||||||
|
const SectionRef &,
|
||||||
|
VTable *);
|
||||||
|
|
||||||
|
bool absoluteRelocation(const RelocationRef &, VTable *);
|
||||||
|
bool relativeRelocation(
|
||||||
|
const RelocationRef &,
|
||||||
|
const SectionRef &,
|
||||||
|
VTable *);
|
||||||
|
|
||||||
|
uint64_t identifyAddend(uint64_t);
|
||||||
|
uint64_t getAddendFromSection(const SectionRef &, uint64_t);
|
||||||
|
SymbolRef matchValueToSymbol(std::vector<SymbolRef> &, VTable *);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T UnWrap(llvm::Expected<T> ValueOrError) {
|
||||||
|
if (!ValueOrError) {
|
||||||
|
outs() << "\nError: "
|
||||||
|
<< llvm::toString(ValueOrError.takeError())
|
||||||
|
<< ".\n";
|
||||||
|
outs().flush();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return std::move(ValueOrError.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ELF_HANDLING_H_
|
||||||
|
|
||||||
49
vndk/tools/abides/vtable/llvm_vtable_dump.cpp
Normal file
49
vndk/tools/abides/vtable/llvm_vtable_dump.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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 "llvm_elf_handling.h"
|
||||||
|
|
||||||
|
using llvm::Expected;
|
||||||
|
using llvm::StringRef;
|
||||||
|
using llvm::dyn_cast;
|
||||||
|
using llvm::object::ObjectFile;
|
||||||
|
using llvm::object::OwningBinary;
|
||||||
|
using llvm::outs;
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
outs() << "usage: figure-out-vtables path \n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Expected<OwningBinary<ObjectFile>> Binary =
|
||||||
|
ObjectFile::createObjectFile(StringRef(argv[1]));
|
||||||
|
if (!Binary) {
|
||||||
|
outs() << "Couldn't create object File \n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ObjectFile *Objfile = dyn_cast<ObjectFile>(&(*Binary.get().getBinary()));
|
||||||
|
if (!Objfile) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
auto SoFile = SharedObject::create(Objfile);
|
||||||
|
if (!SoFile) {
|
||||||
|
outs() << "Couldn't create ELFObjectFile \n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
SoFile->printVTables();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
21
vndk/tools/abides/vtable/test/expected/arm/libtest.so.txt
Normal file
21
vndk/tools/abides/vtable/test/expected/arm/libtest.so.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
vtable for Alpha
|
||||||
|
_ZTV5Alpha: 3 entries
|
||||||
|
8 (int (*)(...)) Alpha::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
16 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Beta
|
||||||
|
_ZTV4Beta: 3 entries
|
||||||
|
8 (int (*)(...)) Beta::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Beta::~Beta()
|
||||||
|
16 (int (*)(...)) Beta::~Beta()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Gamma
|
||||||
|
_ZTV5Gamma: 3 entries
|
||||||
|
8 (int (*)(...)) Gamma::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
16 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
|
||||||
|
|
||||||
21
vndk/tools/abides/vtable/test/expected/arm64/libtest.so.txt
Normal file
21
vndk/tools/abides/vtable/test/expected/arm64/libtest.so.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
vtable for Alpha
|
||||||
|
_ZTV5Alpha: 3 entries
|
||||||
|
16 (int (*)(...)) Alpha::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
32 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Beta
|
||||||
|
_ZTV4Beta: 3 entries
|
||||||
|
16 (int (*)(...)) Beta::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Beta::~Beta()
|
||||||
|
32 (int (*)(...)) Beta::~Beta()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Gamma
|
||||||
|
_ZTV5Gamma: 3 entries
|
||||||
|
16 (int (*)(...)) Gamma::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
32 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
|
||||||
|
|
||||||
21
vndk/tools/abides/vtable/test/expected/mips/libtest.so.txt
Normal file
21
vndk/tools/abides/vtable/test/expected/mips/libtest.so.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
vtable for Alpha
|
||||||
|
_ZTV5Alpha: 3 entries
|
||||||
|
8 (int (*)(...)) Alpha::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
16 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Beta
|
||||||
|
_ZTV4Beta: 3 entries
|
||||||
|
8 (int (*)(...)) Beta::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Beta::~Beta()
|
||||||
|
16 (int (*)(...)) Beta::~Beta()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Gamma
|
||||||
|
_ZTV5Gamma: 3 entries
|
||||||
|
8 (int (*)(...)) Gamma::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
16 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
|
||||||
|
|
||||||
21
vndk/tools/abides/vtable/test/expected/mips64/libtest.so.txt
Normal file
21
vndk/tools/abides/vtable/test/expected/mips64/libtest.so.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
vtable for Alpha
|
||||||
|
_ZTV5Alpha: 3 entries
|
||||||
|
16 (int (*)(...)) Alpha::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
32 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Beta
|
||||||
|
_ZTV4Beta: 3 entries
|
||||||
|
16 (int (*)(...)) Beta::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Beta::~Beta()
|
||||||
|
32 (int (*)(...)) Beta::~Beta()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Gamma
|
||||||
|
_ZTV5Gamma: 3 entries
|
||||||
|
16 (int (*)(...)) Gamma::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
32 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
|
||||||
|
|
||||||
21
vndk/tools/abides/vtable/test/expected/x86/libtest.so.txt
Normal file
21
vndk/tools/abides/vtable/test/expected/x86/libtest.so.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
vtable for Alpha
|
||||||
|
_ZTV5Alpha: 3 entries
|
||||||
|
8 (int (*)(...)) Alpha::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
16 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Beta
|
||||||
|
_ZTV4Beta: 3 entries
|
||||||
|
8 (int (*)(...)) Beta::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Beta::~Beta()
|
||||||
|
16 (int (*)(...)) Beta::~Beta()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Gamma
|
||||||
|
_ZTV5Gamma: 3 entries
|
||||||
|
8 (int (*)(...)) Gamma::getData(int*, int*, int)
|
||||||
|
12 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
16 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
|
||||||
|
|
||||||
21
vndk/tools/abides/vtable/test/expected/x86_64/libtest.so.txt
Normal file
21
vndk/tools/abides/vtable/test/expected/x86_64/libtest.so.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
vtable for Alpha
|
||||||
|
_ZTV5Alpha: 3 entries
|
||||||
|
16 (int (*)(...)) Alpha::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
32 (int (*)(...)) Alpha::~Alpha()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Beta
|
||||||
|
_ZTV4Beta: 3 entries
|
||||||
|
16 (int (*)(...)) Beta::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Beta::~Beta()
|
||||||
|
32 (int (*)(...)) Beta::~Beta()
|
||||||
|
|
||||||
|
|
||||||
|
vtable for Gamma
|
||||||
|
_ZTV5Gamma: 3 entries
|
||||||
|
16 (int (*)(...)) Gamma::getData(int*, int*, int)
|
||||||
|
24 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
32 (int (*)(...)) Gamma::~Gamma()
|
||||||
|
|
||||||
|
|
||||||
32
vndk/tools/abides/vtable/test/test1.cpp
Normal file
32
vndk/tools/abides/vtable/test/test1.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
class Alpha {
|
||||||
|
public:
|
||||||
|
virtual void getData(int *src, int *dst, int data);
|
||||||
|
virtual ~Alpha() {};
|
||||||
|
private:
|
||||||
|
int mPdata = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Beta : public Alpha {
|
||||||
|
public:
|
||||||
|
Beta(int data) : mCdata(data) {}
|
||||||
|
virtual void getData(int *src, int *dst, int data);
|
||||||
|
virtual ~Beta() {};
|
||||||
|
private:
|
||||||
|
int mCdata = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Gamma : public Beta {
|
||||||
|
public:
|
||||||
|
Gamma(int data) : mGCdata(data), Beta(data) {}
|
||||||
|
virtual void getData(int *src, int *dst, int data);
|
||||||
|
virtual ~Gamma() {};
|
||||||
|
private:
|
||||||
|
int mGCdata = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Alpha::getData(int *src, int *dst, int data) {}
|
||||||
|
|
||||||
|
void Beta::getData(int *src, int *dst, int data) {}
|
||||||
|
|
||||||
|
void Gamma::getData(int *src, int *dst, int data) {}
|
||||||
|
|
||||||
267
vndk/tools/abides/vtable/test/test_vtable.py
Executable file
267
vndk/tools/abides/vtable/test/test_vtable.py
Executable file
@@ -0,0 +1,267 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (C) 2017 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.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import collections
|
||||||
|
import difflib
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
"""Test vndk vtable dumper"""
|
||||||
|
|
||||||
|
NDK_VERSION = 'r11'
|
||||||
|
API_LEVEL = 'android-24'
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
VNDK_VTABLE_DUMP = os.path.join('llvm-figure-out-vtables')
|
||||||
|
|
||||||
|
def get_dirnames(path, n):
|
||||||
|
"""Get directory, n directories before path"""
|
||||||
|
for i in range(n):
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def get_prebuilts_host():
|
||||||
|
"""Get the host dir for prebuilts"""
|
||||||
|
if sys.platform.startswith('linux'):
|
||||||
|
return 'linux-x86'
|
||||||
|
if sys.platform.startswith('darwin'):
|
||||||
|
return 'darwin-x86'
|
||||||
|
raise NotImplementedError('unknown platform')
|
||||||
|
|
||||||
|
|
||||||
|
def get_prebuilts_gcc(android_build_top, arch, gcc_version):
|
||||||
|
"""Get the path to gcc for the current platform"""
|
||||||
|
return os.path.join(android_build_top, 'prebuilts', 'gcc',
|
||||||
|
get_prebuilts_host(), arch, gcc_version)
|
||||||
|
|
||||||
|
def get_prebuilts_clang(android_build_top):
|
||||||
|
"""Get the path to prebuilt gcc for the current platform"""
|
||||||
|
return os.path.join(android_build_top, 'prebuilts', 'clang', 'host',
|
||||||
|
get_prebuilts_host(), 'clang-stable')
|
||||||
|
|
||||||
|
def get_prebuilts_ndk(android_build_top, subdirs):
|
||||||
|
"""Get the path to prebuilt ndk for the current platform and API level"""
|
||||||
|
return os.path.join(android_build_top, 'prebuilts', 'ndk', NDK_VERSION,
|
||||||
|
'platforms', API_LEVEL, *subdirs)
|
||||||
|
|
||||||
|
def run_cmd(cmd, verbose=False):
|
||||||
|
"""Run the command given and print the command if verbose is True"""
|
||||||
|
if verbose:
|
||||||
|
print('RUN:', ' '.join(cmd), file=sys.stderr)
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def run_output(cmd, verbose=False):
|
||||||
|
"""Run the command given and print output of the command"""
|
||||||
|
if verbose:
|
||||||
|
print('RUN:', ' '.join(cmd), file=sys.stderr)
|
||||||
|
return subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
||||||
|
|
||||||
|
def run_vtable_dump(path, verbose=False):
|
||||||
|
"""Run vndk vtable dumper"""
|
||||||
|
return run_output([VNDK_VTABLE_DUMP, path], verbose)
|
||||||
|
|
||||||
|
|
||||||
|
class Target(object):
|
||||||
|
"""Class representing a target: for eg: x86, arm64 etc"""
|
||||||
|
def __init__(self, name, triple, cflags, ldflags, gcc_toolchain_dir,
|
||||||
|
clang_dir, ndk_include, ndk_lib):
|
||||||
|
"""Parameterized Constructor"""
|
||||||
|
self.name = name
|
||||||
|
self.target_triple = triple
|
||||||
|
self.target_cflags = cflags
|
||||||
|
self.target_ldflags = ldflags
|
||||||
|
|
||||||
|
self.gcc_toolchain_dir = gcc_toolchain_dir
|
||||||
|
self.clang_dir = clang_dir
|
||||||
|
self.ndk_include = ndk_include
|
||||||
|
self.ndk_lib = ndk_lib
|
||||||
|
|
||||||
|
def compile(self, obj_file, src_file, cflags, verbose=False):
|
||||||
|
"""Compiles the given source files and produces a .o at obj_file"""
|
||||||
|
clangpp = os.path.join(self.clang_dir, 'bin', 'clang++')
|
||||||
|
|
||||||
|
cmd = [clangpp, '-o', obj_file, '-c', src_file]
|
||||||
|
cmd.extend(['-fPIE', '-fPIC', '-fno-rtti', '-std=c++11'])
|
||||||
|
cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir])
|
||||||
|
cmd.extend(['-target', self.target_triple])
|
||||||
|
cmd.extend(['-isystem', self.ndk_include])
|
||||||
|
cmd.extend(cflags)
|
||||||
|
cmd.extend(self.target_cflags)
|
||||||
|
run_cmd(cmd, verbose)
|
||||||
|
|
||||||
|
def link(self, out_file, obj_files, ldflags, verbose=False):
|
||||||
|
"""Link the given obj files to form a shared library"""
|
||||||
|
crtbegin = os.path.join(self.ndk_lib, 'crtbegin_so.o')
|
||||||
|
crtend = os.path.join(self.ndk_lib, 'crtend_so.o')
|
||||||
|
clangpp = os.path.join(self.clang_dir, 'bin', 'clang++')
|
||||||
|
|
||||||
|
cmd = [clangpp, '-o', out_file]
|
||||||
|
cmd.extend(['-fPIE', '-fPIC', '-fno-rtti', '-Wl,--no-undefined', '-nostdlib'])
|
||||||
|
cmd.append('-L' + self.ndk_lib)
|
||||||
|
cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir])
|
||||||
|
cmd.extend(['-target', self.target_triple])
|
||||||
|
cmd.append(crtbegin)
|
||||||
|
cmd.extend(obj_files)
|
||||||
|
cmd.append(crtend)
|
||||||
|
cmd.extend(ldflags)
|
||||||
|
cmd.extend(self.target_ldflags)
|
||||||
|
run_cmd(cmd, verbose)
|
||||||
|
|
||||||
|
|
||||||
|
def create_targets(top):
|
||||||
|
"""Create multiple targets objects, one for each architecture supported"""
|
||||||
|
return [
|
||||||
|
Target('arm', 'arm-linux-androideabi', [],[],
|
||||||
|
get_prebuilts_gcc(top, 'arm', 'arm-linux-androideabi-4.9'),
|
||||||
|
get_prebuilts_clang(top),
|
||||||
|
get_prebuilts_ndk(top, ['arch-arm', 'usr', 'include']),
|
||||||
|
get_prebuilts_ndk(top, ['arch-arm', 'usr', 'lib'])),
|
||||||
|
|
||||||
|
Target('arm64', 'aarch64-linux-android', [], [],
|
||||||
|
get_prebuilts_gcc(top, 'aarch64', 'aarch64-linux-android-4.9'),
|
||||||
|
get_prebuilts_clang(top),
|
||||||
|
get_prebuilts_ndk(top, ['arch-arm64', 'usr', 'include']),
|
||||||
|
get_prebuilts_ndk(top, ['arch-arm64', 'usr', 'lib'])),
|
||||||
|
|
||||||
|
Target('mips', 'mipsel-linux-android', [], [],
|
||||||
|
get_prebuilts_gcc(top, 'mips', 'mips64el-linux-android-4.9'),
|
||||||
|
get_prebuilts_clang(top),
|
||||||
|
get_prebuilts_ndk(top, ['arch-mips', 'usr', 'include']),
|
||||||
|
get_prebuilts_ndk(top, ['arch-mips', 'usr', 'lib'])),
|
||||||
|
|
||||||
|
Target('mips64', 'mips64el-linux-android',
|
||||||
|
['-march=mips64el', '-mcpu=mips64r6'],
|
||||||
|
['-march=mips64el', '-mcpu=mips64r6'],
|
||||||
|
get_prebuilts_gcc(top, 'mips', 'mips64el-linux-android-4.9'),
|
||||||
|
get_prebuilts_clang(top),
|
||||||
|
get_prebuilts_ndk(top, ['arch-mips64', 'usr', 'include']),
|
||||||
|
get_prebuilts_ndk(top, ['arch-mips64', 'usr', 'lib64'])),
|
||||||
|
|
||||||
|
Target('x86', 'x86_64-linux-android', ['-m32'], ['-m32'],
|
||||||
|
get_prebuilts_gcc(top, 'x86', 'x86_64-linux-android-4.9'),
|
||||||
|
get_prebuilts_clang(top),
|
||||||
|
get_prebuilts_ndk(top, ['arch-x86', 'usr', 'include']),
|
||||||
|
get_prebuilts_ndk(top, ['arch-x86', 'usr', 'lib'])),
|
||||||
|
|
||||||
|
Target('x86_64', 'x86_64-linux-android', ['-m64'], ['-m64'],
|
||||||
|
get_prebuilts_gcc(top, 'x86', 'x86_64-linux-android-4.9'),
|
||||||
|
get_prebuilts_clang(top),
|
||||||
|
get_prebuilts_ndk(top, ['arch-x86_64', 'usr', 'include']),
|
||||||
|
get_prebuilts_ndk(top, ['arch-x86_64', 'usr', 'lib64'])),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunner(object):
|
||||||
|
"""Class to run the test"""
|
||||||
|
def __init__(self, expected_dir, test_dir, verbose):
|
||||||
|
"""Parameterized constructor"""
|
||||||
|
self.expected_dir = expected_dir
|
||||||
|
self.test_dir = test_dir
|
||||||
|
self.verbose = verbose
|
||||||
|
self.num_errors = 0
|
||||||
|
|
||||||
|
def check_output(self, expected_file_path, actual):
|
||||||
|
"""Compare the output of the test run and the expected output"""
|
||||||
|
actual = actual.splitlines(True)
|
||||||
|
with open(expected_file_path, 'r') as f:
|
||||||
|
expected = f.readlines()
|
||||||
|
if actual == expected:
|
||||||
|
return
|
||||||
|
for line in difflib.context_diff(expected, actual,
|
||||||
|
fromfile=expected_file_path,
|
||||||
|
tofile='actual'):
|
||||||
|
sys.stderr.write(line)
|
||||||
|
self.num_errors += 1
|
||||||
|
|
||||||
|
def run_test_for_target(self, target):
|
||||||
|
"""Run the test for a specific target"""
|
||||||
|
print('Testing target', target.name, '...', file=sys.stderr)
|
||||||
|
|
||||||
|
expected_dir = os.path.join(self.expected_dir, target.name)
|
||||||
|
|
||||||
|
# Create test directory for this target.
|
||||||
|
test_dir = os.path.join(self.test_dir, target.name)
|
||||||
|
os.makedirs(test_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Compile and test "libtest.so".
|
||||||
|
src_file = os.path.join(SCRIPT_DIR, 'test1.cpp')
|
||||||
|
obj_file = os.path.join(test_dir, 'test.o')
|
||||||
|
target.compile(obj_file, src_file, [], self.verbose)
|
||||||
|
|
||||||
|
out_file = os.path.join(test_dir, 'libtest.so')
|
||||||
|
target.link(out_file, [obj_file],
|
||||||
|
['-shared', '-lc', '-lgcc', '-lstdc++'],
|
||||||
|
self.verbose)
|
||||||
|
self.check_output(os.path.join(expected_dir, 'libtest.so.txt'),
|
||||||
|
run_vtable_dump(out_file, self.verbose))
|
||||||
|
|
||||||
|
def run_test(self, targets):
|
||||||
|
"""Run test fo all targets"""
|
||||||
|
for target in targets:
|
||||||
|
self.run_test_for_target(target)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
""" Set up and run test"""
|
||||||
|
# Parse command line arguments.
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--verbose', '-v', action='store_true')
|
||||||
|
parser.add_argument('--android-build-top', help='path to android build top')
|
||||||
|
parser.add_argument('--test-dir',
|
||||||
|
help='directory for temporary files')
|
||||||
|
parser.add_argument('--expected-dir', help='directory with expected output')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Find ${ANDROID_BUILD_TOP}.
|
||||||
|
if args.android_build_top:
|
||||||
|
android_build_top = args.android_build_top
|
||||||
|
else:
|
||||||
|
android_build_top = get_dirnames(SCRIPT_DIR, 6)
|
||||||
|
|
||||||
|
# Find expected output directory.
|
||||||
|
if args.expected_dir:
|
||||||
|
expected_dir = args.expected_dir
|
||||||
|
else:
|
||||||
|
expected_dir = os.path.join(SCRIPT_DIR, 'expected')
|
||||||
|
|
||||||
|
# Load compilation targets.
|
||||||
|
targets = create_targets(android_build_top)
|
||||||
|
|
||||||
|
# Run tests.
|
||||||
|
if args.test_dir:
|
||||||
|
os.makedirs(args.test_dir, exist_ok=True)
|
||||||
|
runner = TestRunner(expected_dir, args.test_dir, args.verbose)
|
||||||
|
runner.run_test(targets)
|
||||||
|
else:
|
||||||
|
with tempfile.TemporaryDirectory() as test_dir:
|
||||||
|
runner = TestRunner(expected_dir, test_dir, args.verbose)
|
||||||
|
runner.run_test(targets)
|
||||||
|
|
||||||
|
if runner.num_errors:
|
||||||
|
print('FAILED:', runner.num_errors, 'test(s) failed', file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print('SUCCESS', file=sys.stderr)
|
||||||
|
|
||||||
|
return 1 if runner.num_errors else 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user