blob: fa4d975e701120d4e5eb6b8da4395eacb2d24e1c [file] [log] [blame]
// 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 "so_file_parser.h"
#include "ir_representation.h"
#include <llvm/Object/Binary.h>
#include <llvm/Object/ELFObjectFile.h>
#include <llvm/Object/ELFTypes.h>
#include <llvm/Object/SymbolSize.h>
namespace abi_util {
template <typename T>
static inline T UnWrap(llvm::Expected<T> value_or_error) {
if (!value_or_error) {
llvm::errs() << "\nerror: " << llvm::toString(value_or_error.takeError())
<< ".\n";
llvm::errs().flush();
exit(1);
}
return std::move(value_or_error.get());
}
static abi_util::ElfSymbolIR::ElfSymbolBinding
LLVMToIRSymbolBinding(unsigned char binding) {
switch (binding) {
case llvm::ELF::STB_GLOBAL:
return abi_util::ElfSymbolIR::ElfSymbolBinding::Global;
case llvm::ELF::STB_WEAK:
return abi_util::ElfSymbolIR::ElfSymbolBinding::Weak;
}
assert(0);
}
template<typename T>
class ELFSoFileParser : public SoFileParser {
private:
LLVM_ELF_IMPORT_TYPES_ELFT(T)
typedef llvm::object::ELFFile<T> ELFO;
typedef typename ELFO::Elf_Sym Elf_Sym;
public:
ELFSoFileParser(const llvm::object::ELFObjectFile<T> *obj);
~ELFSoFileParser() override {}
const std::map<std::string, ElfFunctionIR> &GetFunctions() const override {
return functions_;
}
const std::map<std::string, ElfObjectIR> &GetGlobVars() const override {
return globvars_;
}
private:
bool IsSymbolExported(const Elf_Sym *elf_sym) const {
unsigned char visibility = elf_sym->getVisibility();
unsigned char binding = elf_sym->getBinding();
return ((binding == llvm::ELF::STB_GLOBAL ||
binding == llvm::ELF::STB_WEAK) &&
(visibility == llvm::ELF::STV_DEFAULT ||
visibility == llvm::ELF::STV_PROTECTED));
}
private:
const llvm::object::ELFObjectFile<T> *obj_;
std::map<std::string, abi_util::ElfFunctionIR> functions_;
std::map<std::string, abi_util::ElfObjectIR> globvars_;
};
template<typename T>
ELFSoFileParser<T>::ELFSoFileParser(const llvm::object::ELFObjectFile<T> *obj) {
assert(obj != nullptr);
for (auto symbol_it : obj->getDynamicSymbolIterators()) {
const Elf_Sym *elf_sym = obj->getSymbol(symbol_it.getRawDataRefImpl());
assert (elf_sym != nullptr);
if (!IsSymbolExported(elf_sym) || elf_sym->isUndefined()) {
continue;
}
abi_util::ElfSymbolIR::ElfSymbolBinding symbol_binding =
LLVMToIRSymbolBinding(elf_sym->getBinding());
llvm::object::SymbolRef::Type type = UnWrap(symbol_it.getType());
std::string symbol_name = UnWrap(symbol_it.getName());
if (type == llvm::object::SymbolRef::Type::ST_Function) {
functions_.emplace(symbol_name,
ElfFunctionIR(symbol_name, symbol_binding));
} else if (type == llvm::object::SymbolRef::Type::ST_Data) {
globvars_.emplace(symbol_name, ElfObjectIR(symbol_name, symbol_binding));
}
}
}
template<typename T>
static std::unique_ptr<SoFileParser> CreateELFSoFileParser(
const llvm::object::ELFObjectFile<T> *elfo) {
return llvm::make_unique<ELFSoFileParser<T>>(elfo);
}
std::unique_ptr<SoFileParser> SoFileParser::Create(
const std::string &so_file_path) {
auto binary = llvm::object::createBinary(so_file_path);
if (!binary) {
return nullptr;
}
llvm::object::ObjectFile *obj_file =
llvm::dyn_cast<llvm::object::ObjectFile>(binary.get().getBinary());
if (!obj_file) {
return nullptr;
}
// Little-endian 32-bit
if (auto elf_obj_file =
llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj_file)) {
return CreateELFSoFileParser(elf_obj_file);
}
// Big-endian 32-bit
if (auto elf_obj_file =
llvm::dyn_cast<llvm::object::ELF32BEObjectFile>(obj_file)) {
return CreateELFSoFileParser(elf_obj_file);
}
// Little-endian 64-bit
if (auto elf_obj_file =
llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj_file)) {
return CreateELFSoFileParser(elf_obj_file);
}
// Big-endian 64-bit
if (auto elf_obj_file =
llvm::dyn_cast<llvm::object::ELF64BEObjectFile>(obj_file)) {
return CreateELFSoFileParser(elf_obj_file);
}
return nullptr;
}
} // namespace abi_util