blob: 385041b310d3ced99d05508b0eb6188ddc4efbb6 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_DWARF_H_
#define RUNTIME_VM_DWARF_H_
#include "vm/allocation.h"
#include "vm/hash_map.h"
#include "vm/object.h"
#include "vm/zone.h"
namespace dart {
#ifdef DART_PRECOMPILER
class Elf;
class InliningNode;
class SnapshotTextObjectNamer;
struct ScriptIndexPair {
// Typedefs needed for the DirectChainedHashMap template.
typedef const Script* Key;
typedef intptr_t Value;
typedef ScriptIndexPair Pair;
static Key KeyOf(Pair kv) { return kv.script_; }
static Value ValueOf(Pair kv) { return kv.index_; }
static inline intptr_t Hashcode(Key key) {
return String::Handle(key->url()).Hash();
}
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair.script_->raw() == key->raw();
}
ScriptIndexPair(const Script* s, intptr_t index) : script_(s), index_(index) {
ASSERT(!s->IsNull());
ASSERT(s->IsNotTemporaryScopedHandle());
}
ScriptIndexPair() : script_(NULL), index_(-1) {}
void Print() const;
const Script* script_;
intptr_t index_;
};
typedef DirectChainedHashMap<ScriptIndexPair> ScriptIndexMap;
struct FunctionIndexPair {
// Typedefs needed for the DirectChainedHashMap template.
typedef const Function* Key;
typedef intptr_t Value;
typedef FunctionIndexPair Pair;
static Key KeyOf(Pair kv) { return kv.function_; }
static Value ValueOf(Pair kv) { return kv.index_; }
static inline intptr_t Hashcode(Key key) { return key->token_pos().value(); }
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair.function_->raw() == key->raw();
}
FunctionIndexPair(const Function* f, intptr_t index)
: function_(f), index_(index) {
ASSERT(!f->IsNull());
ASSERT(f->IsNotTemporaryScopedHandle());
}
FunctionIndexPair() : function_(NULL), index_(-1) {}
void Print() const;
const Function* function_;
intptr_t index_;
};
typedef DirectChainedHashMap<FunctionIndexPair> FunctionIndexMap;
struct CodeAddressPair {
// Typedefs needed for the DirectChainedHashMap template.
typedef const Code* Key;
typedef intptr_t Value;
typedef CodeAddressPair Pair;
static Key KeyOf(Pair kv) { return kv.code_; }
static Value ValueOf(Pair kv) { return kv.address_; }
static inline intptr_t Hashcode(Key key) {
// Code objects are always allocated in old space, so they don't move.
return key->PayloadStart();
}
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair.code_->raw() == key->raw();
}
CodeAddressPair(const Code* c, intptr_t address)
: code_(c), address_(address) {
ASSERT(!c->IsNull());
ASSERT(c->IsNotTemporaryScopedHandle());
ASSERT(address >= 0);
}
CodeAddressPair() : code_(NULL), address_(-1) {}
void Print() const;
const Code* code_;
intptr_t address_;
};
typedef DirectChainedHashMap<CodeAddressPair> CodeAddressMap;
template <typename T>
class Trie : public ZoneAllocated {
public:
// Returns whether [key] is a valid trie key (that is, a C string that
// contains only characters for which charIndex returns a non-negative value).
static bool IsValidKey(const char* key) {
for (intptr_t i = 0; key[i] != '\0'; i++) {
if (ChildIndex(key[i]) < 0) return false;
}
return true;
}
// Adds a binding of [key] to [value] in [trie]. Assumes that the string in
// [key] is a valid trie key and does not already have a value in [trie].
//
// If [trie] is nullptr, then a new trie is created and a pointer to the new
// trie is returned. Otherwise, [trie] will be returned.
static Trie<T>* AddString(Zone* zone,
Trie<T>* trie,
const char* key,
const T* value);
// Adds a binding of [key] to [value]. Assumes that the string in [key] is a
// valid trie key and does not already have a value in this trie.
void AddString(Zone* zone, const char* key, const T* value) {
AddString(zone, this, key, value);
}
// Looks up the value stored for [key] in [trie]. If one is not found, then
// nullptr is returned.
//
// If [end] is not nullptr, then the longest prefix of [key] that is a valid
// trie key prefix will be used for the lookup and the value pointed to by
// [end] is set to the index after that prefix. Otherwise, the whole [key]
// is used.
static const T* Lookup(const Trie<T>* trie,
const char* key,
intptr_t* end = nullptr);
// Looks up the value stored for [key]. If one is not found, then nullptr is
// returned.
//
// If [end] is not nullptr, then the longest prefix of [key] that is a valid
// trie key prefix will be used for the lookup and the value pointed to by
// [end] is set to the index after that prefix. Otherwise, the whole [key]
// is used.
const T* Lookup(const char* key, intptr_t* end = nullptr) const {
return Lookup(this, key, end);
}
private:
// Currently, only the following characters can appear in obfuscated names:
// '_', '@', '0-9', 'a-z', 'A-Z'
static const intptr_t kNumValidChars = 64;
Trie() {
for (intptr_t i = 0; i < kNumValidChars; i++) {
children_[i] = nullptr;
}
}
static intptr_t ChildIndex(char c) {
if (c == '_') return 0;
if (c == '@') return 1;
if (c >= '0' && c <= '9') return ('9' - c) + 2;
if (c >= 'a' && c <= 'z') return ('z' - c) + 12;
if (c >= 'A' && c <= 'Z') return ('Z' - c) + 38;
return -1;
}
const T* value_ = nullptr;
Trie<T>* children_[kNumValidChars];
};
class Dwarf : public ZoneAllocated {
public:
Dwarf(Zone* zone, StreamingWriteStream* stream, Elf* elf);
Elf* elf() const { return elf_; }
// Stores the code object for later creating the line number program.
//
// Should only be called when the output is not ELF.
//
// Returns the stored index of the code object.
intptr_t AddCode(const Code& code);
// Stores the code object for later creating the line number program.
//
// [payload_offset] should be the offset of the payload within the text
// section. [name] is used to create an ELF static symbol for the payload.
//
// Should only be called when the output is ELF.
void AddCode(const Code& code, const char* name, intptr_t payload_offset);
intptr_t AddFunction(const Function& function);
intptr_t AddScript(const Script& script);
intptr_t LookupFunction(const Function& function);
intptr_t LookupScript(const Script& script);
void Write() {
WriteAbbreviations();
WriteCompilationUnit();
WriteLines();
}
private:
// Implements shared functionality for the two AddCode calls. Assumes the
// Code handle is appropriately zoned.
intptr_t AddCodeHelper(const Code& code);
static const intptr_t DW_TAG_compile_unit = 0x11;
static const intptr_t DW_TAG_inlined_subroutine = 0x1d;
static const intptr_t DW_TAG_subprogram = 0x2e;
static const intptr_t DW_CHILDREN_no = 0x0;
static const intptr_t DW_CHILDREN_yes = 0x1;
static const intptr_t DW_AT_sibling = 0x1;
static const intptr_t DW_AT_name = 0x3;
static const intptr_t DW_AT_stmt_list = 0x10;
static const intptr_t DW_AT_low_pc = 0x11;
static const intptr_t DW_AT_high_pc = 0x12;
static const intptr_t DW_AT_comp_dir = 0x1b;
static const intptr_t DW_AT_inline = 0x20;
static const intptr_t DW_AT_producer = 0x25;
static const intptr_t DW_AT_abstract_origin = 0x31;
static const intptr_t DW_AT_decl_column = 0x39;
static const intptr_t DW_AT_decl_file = 0x3a;
static const intptr_t DW_AT_decl_line = 0x3b;
static const intptr_t DW_AT_call_column = 0x57;
static const intptr_t DW_AT_call_file = 0x58;
static const intptr_t DW_AT_call_line = 0x59;
static const intptr_t DW_FORM_addr = 0x01;
static const intptr_t DW_FORM_string = 0x08;
static const intptr_t DW_FORM_udata = 0x0f;
static const intptr_t DW_FORM_ref4 = 0x13;
static const intptr_t DW_FORM_ref_udata = 0x15;
static const intptr_t DW_FORM_sec_offset = 0x17;
static const intptr_t DW_INL_not_inlined = 0x0;
static const intptr_t DW_INL_inlined = 0x1;
static const intptr_t DW_LNS_copy = 0x1;
static const intptr_t DW_LNS_advance_pc = 0x2;
static const intptr_t DW_LNS_advance_line = 0x3;
static const intptr_t DW_LNS_set_file = 0x4;
static const intptr_t DW_LNE_end_sequence = 0x01;
static const intptr_t DW_LNE_set_address = 0x02;
enum {
kCompilationUnit = 1,
kAbstractFunction,
kConcreteFunction,
kInlinedFunction,
};
void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
#if defined(TARGET_ARCH_IS_32_BIT)
#define FORM_ADDR ".4byte"
#elif defined(TARGET_ARCH_IS_64_BIT)
#define FORM_ADDR ".8byte"
#endif
void PrintNamedAddress(const char* name) { Print(FORM_ADDR " %s\n", name); }
void PrintNamedAddressWithOffset(const char* name, intptr_t offset) {
Print(FORM_ADDR " %s + %" Pd "\n", name, offset);
}
#undef FORM_ADDR
void sleb128(intptr_t value) {
if (asm_stream_ != nullptr) {
Print(".sleb128 %" Pd "\n", value);
}
if (elf_ != nullptr) {
bool is_last_part = false;
while (!is_last_part) {
uint8_t part = value & 0x7F;
value >>= 7;
if ((value == 0 && (part & 0x40) == 0) ||
(value == static_cast<intptr_t>(-1) && (part & 0x40) != 0)) {
is_last_part = true;
} else {
part |= 0x80;
}
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&part),
sizeof(part));
}
}
}
void uleb128(uintptr_t value) {
if (asm_stream_ != nullptr) {
Print(".uleb128 %" Pd "\n", value);
}
if (elf_ != nullptr) {
bool is_last_part = false;
while (!is_last_part) {
uint8_t part = value & 0x7F;
value >>= 7;
if (value == 0) {
is_last_part = true;
} else {
part |= 0x80;
}
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&part),
sizeof(part));
}
}
}
void u1(uint8_t value) {
if (asm_stream_ != nullptr) {
Print(".byte %u\n", value);
}
if (elf_ != nullptr) {
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
sizeof(value));
}
}
void u2(uint16_t value) {
if (asm_stream_ != nullptr) {
Print(".2byte %u\n", value);
}
if (elf_ != nullptr) {
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
sizeof(value));
}
}
intptr_t u4(uint32_t value) {
if (asm_stream_ != nullptr) {
Print(".4byte %" Pu32 "\n", value);
}
if (elf_ != nullptr) {
intptr_t fixup = position();
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
sizeof(value));
return fixup;
}
return -1;
}
void fixup_u4(intptr_t position, uint32_t value) {
RELEASE_ASSERT(elf_ != nullptr);
memmove(bin_stream_->buffer() + position, &value, sizeof(value));
}
void u8(uint64_t value) {
if (asm_stream_ != nullptr) {
Print(".8byte %" Pu64 "\n", value);
}
if (elf_ != nullptr) {
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
sizeof(value));
}
}
void addr(uword value) {
RELEASE_ASSERT(elf_ != nullptr);
#if defined(TARGET_ARCH_IS_32_BIT)
u4(value);
#else
u8(value);
#endif
}
void string(const char* cstr) { // NOLINT
if (asm_stream_ != nullptr) {
Print(".string \"%s\"\n", cstr); // NOLINT
}
if (elf_ != nullptr) {
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(cstr),
strlen(cstr) + 1);
}
}
intptr_t position() {
RELEASE_ASSERT(elf_ != nullptr);
return bin_stream_->Position();
}
static constexpr intptr_t kNoLineInformation = 0;
// Returns the line number or kNoLineInformation if there is no line
// information available for the given token position.
static intptr_t TokenPositionToLine(const TokenPosition& token_pos);
void WriteAbbreviations();
void WriteCompilationUnit();
void WriteAbstractFunctions();
void WriteConcreteFunctions();
InliningNode* ExpandInliningTree(const Code& code);
void WriteInliningNode(InliningNode* node,
intptr_t root_code_index,
intptr_t root_code_offset,
const Script& parent_script,
SnapshotTextObjectNamer* namer);
void WriteLines();
const char* Deobfuscate(const char* cstr);
static Trie<const char>* CreateReverseObfuscationTrie(Zone* zone);
Zone* const zone_;
Elf* const elf_;
Trie<const char>* const reverse_obfuscation_trie_;
StreamingWriteStream* asm_stream_;
WriteStream* bin_stream_;
ZoneGrowableArray<const Code*> codes_;
CodeAddressMap code_to_address_;
ZoneGrowableArray<const Function*> functions_;
FunctionIndexMap function_to_index_;
ZoneGrowableArray<const Script*> scripts_;
ScriptIndexMap script_to_index_;
uint32_t* abstract_origins_;
intptr_t temp_;
};
#endif // DART_PRECOMPILER
} // namespace dart
#endif // RUNTIME_VM_DWARF_H_