Files
sdk/runtime/vm/dwarf.cc
T
Tess Strickland fa03ee7782 [vm] Create a common base class for TextBuffer/ZoneTextBuffer.
Generally, methods that take a *TextBuffer pointer do not care how the
internal buffer is allocated, and so they could be used for either if
both were subclasses of a base class that contained the printing
methods.  This CL makes that base class, and now TextBuffer and
ZoneTextBuffer now share the exact same set of methods for printing to
the internal buffer.

Since the base class is in platform, this does mean dropping the
overload of AddString for Dart String objects that was part of
ZoneTextBuffer.  Instead, this CL just adds an intermediate call to
ToCString() for the small number of callers that used the overload,
keeping the printing interface the same for both.

In addition, one use of TextBuffer that then re-allocated the buffer
contents into the zone manually has been replaced with a ZoneTextBuffer
instead.

Change-Id: I438a085e7e20d55d93987fd7f36afd636f95955f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/157741
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
2020-08-07 18:09:35 +00:00

802 lines
28 KiB
C++

// 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.
#include "vm/dwarf.h"
#include "vm/code_descriptors.h"
#include "vm/elf.h"
#include "vm/image_snapshot.h"
#include "vm/object_store.h"
namespace dart {
#if defined(DART_PRECOMPILER)
class DwarfPosition {
public:
// The DWARF standard uses 0 to denote missing line or column information.
DwarfPosition(intptr_t line, intptr_t column)
: line_(line > 0 ? line : 0), column_(column > 0 ? column : 0) {
// Should only have no line information if also no column information.
ASSERT(line_ > 0 || column_ == 0);
}
explicit DwarfPosition(intptr_t line) : DwarfPosition(line, 0) {}
constexpr DwarfPosition() : line_(0), column_(0) {}
intptr_t line() const { return line_; }
intptr_t column() const { return column_; }
private:
intptr_t line_;
intptr_t column_;
};
static constexpr auto kNoDwarfPositionInfo = DwarfPosition();
class InliningNode : public ZoneAllocated {
public:
InliningNode(const Function& function,
const DwarfPosition& position,
int32_t start_pc_offset)
: function(function),
position(position),
start_pc_offset(start_pc_offset),
end_pc_offset(-1),
children_head(NULL),
children_tail(NULL),
children_next(NULL) {
RELEASE_ASSERT(!function.IsNull());
RELEASE_ASSERT(function.IsNotTemporaryScopedHandle());
}
void AppendChild(InliningNode* child) {
if (children_tail == NULL) {
children_head = children_tail = child;
} else {
children_tail->children_next = child;
children_tail = child;
}
}
const Function& function;
DwarfPosition position;
int32_t start_pc_offset;
int32_t end_pc_offset;
InliningNode* children_head;
InliningNode* children_tail;
InliningNode* children_next;
};
template <typename T>
Trie<T>* Trie<T>::AddString(Zone* zone,
Trie<T>* trie,
const char* key,
const T* value) {
ASSERT(key != nullptr);
if (trie == nullptr) {
trie = new (zone) Trie<T>();
}
if (*key == '\0') {
ASSERT(trie->value_ == nullptr);
trie->value_ = value;
} else {
auto const index = ChildIndex(*key);
ASSERT(index >= 0 && index < kNumValidChars);
trie->children_[index] =
AddString(zone, trie->children_[index], key + 1, value);
}
return trie;
}
template <typename T>
const T* Trie<T>::Lookup(const Trie<T>* trie, const char* key, intptr_t* end) {
intptr_t i = 0;
for (; key[i] != '\0'; i++) {
auto const index = ChildIndex(key[i]);
ASSERT(index < kNumValidChars);
if (index < 0) {
if (end == nullptr) return nullptr;
break;
}
// Still find the longest valid trie prefix when no stored value.
if (trie == nullptr) continue;
trie = trie->children_[index];
}
if (end != nullptr) {
*end = i;
}
if (trie == nullptr) return nullptr;
return trie->value_;
}
Dwarf::Dwarf(Zone* zone)
: zone_(zone),
reverse_obfuscation_trie_(CreateReverseObfuscationTrie(zone)),
codes_(zone, 1024),
code_to_address_(zone),
functions_(zone, 1024),
function_to_index_(zone),
scripts_(zone, 1024),
script_to_index_(zone),
temp_(0) {}
SegmentRelativeOffset Dwarf::CodeAddress(const Code& code) const {
const auto& pair = code_to_address_.LookupValue(&code);
// This is only used by Elf::Finalize(), and the image writers always give a
// text offset when calling AddCode() for an Elf object's Dwarf object. Thus,
// we should have known code offsets for each code object in the map.
ASSERT(pair.offset != SegmentRelativeOffset::kUnknownOffset);
return pair;
}
intptr_t Dwarf::AddCode(const Code& orig_code,
const SegmentRelativeOffset& offset) {
ASSERT(!orig_code.IsNull());
// We should never get the no-argument constructed version here.
ASSERT(offset.offset != SegmentRelativeOffset::kInvalidOffset);
// Generate an appropriately zoned ZoneHandle for storing.
const auto& code = Code::ZoneHandle(zone_, orig_code.raw());
// For now, we assume one of two flows for a given code object:
// ELF: Calls to AddCode(code, vm, offset), vm and offset are the same over
// all calls.
// Assembly: An initial call to AddCode(code, vm) (assembly), possibly
// followed by a later call to AddCode(code, vm, offset)
// (separate debugging info ELF)
if (offset.offset == SegmentRelativeOffset::kUnknownOffset) {
// A call without an address should always come before any calls with
// addresses.
ASSERT(code_to_address_.Lookup(&code) == nullptr);
// Insert a marker so on later calls, we know we've already added to codes_.
code_to_address_.Insert(CodeAddressPair(&code, offset));
} else {
const auto& old_value = code_to_address_.LookupValue(&code);
// ELF does not need to know the index. If we've already added this Code
// object to codes_ in a previous call, don't bother scanning codes_ to find
// the corresponding index, just return -1 instead.
switch (old_value.offset) {
case SegmentRelativeOffset::kInvalidOffset:
code_to_address_.Insert(CodeAddressPair(&code, offset));
break; // Still need to add to codes_.
case SegmentRelativeOffset::kUnknownOffset:
// Code objects should only be associated with either the VM or isolate.
ASSERT_EQUAL(old_value.vm, offset.vm);
code_to_address_.Update(CodeAddressPair(&code, offset));
return -1;
default:
// The information for the code object shouldn't have changed since the
// previous update.
ASSERT(old_value == offset);
return -1;
}
}
const intptr_t index = codes_.length();
codes_.Add(&code);
if (code.IsFunctionCode()) {
const Function& function = Function::Handle(zone_, code.function());
AddFunction(function);
}
const Array& inline_functions =
Array::Handle(zone_, code.inlined_id_to_function());
if (!inline_functions.IsNull()) {
Function& function = Function::Handle(zone_);
for (intptr_t i = 0; i < inline_functions.Length(); i++) {
function ^= inline_functions.At(i);
AddFunction(function);
}
}
return index;
}
intptr_t Dwarf::AddFunction(const Function& function) {
RELEASE_ASSERT(!function.IsNull());
FunctionIndexPair* pair = function_to_index_.Lookup(&function);
if (pair != NULL) {
return pair->index_;
}
intptr_t index = functions_.length();
const Function& zone_func = Function::ZoneHandle(zone_, function.raw());
function_to_index_.Insert(FunctionIndexPair(&zone_func, index));
functions_.Add(&zone_func);
const Script& script = Script::Handle(zone_, function.script());
AddScript(script);
return index;
}
intptr_t Dwarf::AddScript(const Script& script) {
RELEASE_ASSERT(!script.IsNull());
ScriptIndexPair* pair = script_to_index_.Lookup(&script);
if (pair != NULL) {
return pair->index_;
}
// DWARF file numbers start from 1.
intptr_t index = scripts_.length() + 1;
const Script& zone_script = Script::ZoneHandle(zone_, script.raw());
script_to_index_.Insert(ScriptIndexPair(&zone_script, index));
scripts_.Add(&zone_script);
return index;
}
intptr_t Dwarf::LookupFunction(const Function& function) {
RELEASE_ASSERT(!function.IsNull());
FunctionIndexPair* pair = function_to_index_.Lookup(&function);
if (pair == NULL) {
FATAL1("Function detected too late during DWARF generation: %s",
function.ToCString());
}
return pair->index_;
}
intptr_t Dwarf::LookupScript(const Script& script) {
RELEASE_ASSERT(!script.IsNull());
ScriptIndexPair* pair = script_to_index_.Lookup(&script);
if (pair == NULL) {
FATAL1("Script detected too late during DWARF generation: %s",
script.ToCString());
}
return pair->index_;
}
void Dwarf::WriteAbbreviations(DwarfWriteStream* stream) {
// Dwarf data mostly takes the form of a tree, whose nodes are called
// DIEs. Each DIE begins with an abbreviation code, and the abbreviation
// describes the attributes of that DIE and their representation.
stream->uleb128(kCompilationUnit); // Abbrev code.
stream->uleb128(DW_TAG_compile_unit); // Type.
stream->u1(DW_CHILDREN_yes);
stream->uleb128(DW_AT_name); // Start of attributes.
stream->uleb128(DW_FORM_string);
stream->uleb128(DW_AT_producer);
stream->uleb128(DW_FORM_string);
stream->uleb128(DW_AT_comp_dir);
stream->uleb128(DW_FORM_string);
stream->uleb128(DW_AT_low_pc);
stream->uleb128(DW_FORM_addr);
stream->uleb128(DW_AT_high_pc);
stream->uleb128(DW_FORM_addr);
stream->uleb128(DW_AT_stmt_list);
stream->uleb128(DW_FORM_sec_offset);
stream->uleb128(0);
stream->uleb128(0); // End of attributes.
stream->uleb128(kAbstractFunction); // Abbrev code.
stream->uleb128(DW_TAG_subprogram); // Type.
stream->u1(DW_CHILDREN_yes);
stream->uleb128(DW_AT_name); // Start of attributes.
stream->uleb128(DW_FORM_string);
stream->uleb128(DW_AT_decl_file);
stream->uleb128(DW_FORM_udata);
stream->uleb128(DW_AT_inline);
stream->uleb128(DW_FORM_udata);
stream->uleb128(0);
stream->uleb128(0); // End of attributes.
stream->uleb128(kConcreteFunction); // Abbrev code.
stream->uleb128(DW_TAG_subprogram); // Type.
stream->u1(DW_CHILDREN_yes);
stream->uleb128(DW_AT_abstract_origin); // Start of attributes.
stream->uleb128(DW_FORM_ref4);
stream->uleb128(DW_AT_low_pc);
stream->uleb128(DW_FORM_addr);
stream->uleb128(DW_AT_high_pc);
stream->uleb128(DW_FORM_addr);
stream->uleb128(0);
stream->uleb128(0); // End of attributes.
stream->uleb128(kInlinedFunction); // Abbrev code.
stream->uleb128(DW_TAG_inlined_subroutine); // Type.
stream->u1(DW_CHILDREN_yes);
stream->uleb128(DW_AT_abstract_origin); // Start of attributes.
stream->uleb128(DW_FORM_ref4);
stream->uleb128(DW_AT_low_pc);
stream->uleb128(DW_FORM_addr);
stream->uleb128(DW_AT_high_pc);
stream->uleb128(DW_FORM_addr);
stream->uleb128(DW_AT_call_file);
stream->uleb128(DW_FORM_udata);
stream->uleb128(DW_AT_call_line);
stream->uleb128(DW_FORM_udata);
stream->uleb128(DW_AT_call_column);
stream->uleb128(DW_FORM_udata);
stream->uleb128(0);
stream->uleb128(0); // End of attributes.
stream->uleb128(0); // End of abbreviations.
}
void Dwarf::WriteDebugInfo(DwarfWriteStream* stream) {
SnapshotTextObjectNamer namer(zone_);
// 7.5.1.1 Compilation Unit Header
// Unit length.
auto const cu_prefix = "cu";
intptr_t cu_start;
intptr_t cu_size_fixup = stream->ReserveSize(cu_prefix, &cu_start);
stream->u2(2); // DWARF version 2
stream->u4(0); // debug_abbrev_offset
stream->u1(compiler::target::kWordSize); // address_size
// Compilation Unit DIE. We describe the entire Dart program as a single
// compilation unit. Note we write attributes in the same order we declared
// them in our abbreviation above in WriteAbbreviations.
stream->uleb128(kCompilationUnit);
const Library& root_library = Library::Handle(
zone_, Isolate::Current()->object_store()->root_library());
const String& root_uri = String::Handle(zone_, root_library.url());
stream->string(root_uri.ToCString()); // DW_AT_name
stream->string("Dart VM"); // DW_AT_producer
stream->string(""); // DW_AT_comp_dir
// DW_AT_low_pc
// The lowest instruction address in this object file that is part of our
// compilation unit. Dwarf consumers use this to quickly decide which
// compilation unit DIE to consult for a given pc.
stream->OffsetFromSymbol(kIsolateSnapshotInstructionsAsmSymbol, 0);
// DW_AT_high_pc
// The highest instruction address in this object file that is part of our
// compilation unit. Dwarf consumers use this to quickly decide which
// compilation unit DIE to consult for a given pc.
intptr_t last_code_index = codes_.length() - 1;
const Code& last_code = *(codes_[last_code_index]);
auto const last_code_name = namer.SnapshotNameFor(last_code_index, last_code);
stream->OffsetFromSymbol(last_code_name, last_code.Size());
// DW_AT_stmt_list (offset into .debug_line)
// Indicates which line number program is associated with this compilation
// unit. We only emit a single line number program.
stream->u4(0);
WriteAbstractFunctions(stream);
WriteConcreteFunctions(stream);
stream->uleb128(0); // End of children.
stream->uleb128(0); // End of entries.
stream->SetSize(cu_size_fixup, cu_prefix, cu_start);
}
void Dwarf::WriteAbstractFunctions(DwarfWriteStream* stream) {
Script& script = Script::Handle(zone_);
String& name = String::Handle(zone_);
stream->InitializeAbstractOrigins(functions_.length());
// By the point we're creating DWARF information, scripts have already lost
// their token stream and we can't look up their line number or column
// information, hence the lack of DW_AT_decl_line and DW_AT_decl_column.
for (intptr_t i = 0; i < functions_.length(); i++) {
const Function& function = *(functions_[i]);
name = function.QualifiedUserVisibleName();
script = function.script();
const intptr_t file = LookupScript(script);
auto const name_cstr = Deobfuscate(name.ToCString());
stream->RegisterAbstractOrigin(i);
stream->uleb128(kAbstractFunction);
stream->string(name_cstr); // DW_AT_name
stream->uleb128(file); // DW_AT_decl_file
stream->uleb128(DW_INL_inlined); // DW_AT_inline
stream->uleb128(0); // End of children.
}
}
void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
Function& function = Function::Handle(zone_);
Script& script = Script::Handle(zone_);
SnapshotTextObjectNamer namer(zone_);
for (intptr_t i = 0; i < codes_.length(); i++) {
const Code& code = *(codes_[i]);
RELEASE_ASSERT(!code.IsNull());
if (!code.IsFunctionCode()) {
continue;
}
function = code.function();
intptr_t function_index = LookupFunction(function);
script = function.script();
const char* asm_name = namer.SnapshotNameFor(i, code);
stream->uleb128(kConcreteFunction);
// DW_AT_abstract_origin
// References a node written above in WriteAbstractFunctions.
stream->AbstractOrigin(function_index);
// DW_AT_low_pc
stream->OffsetFromSymbol(asm_name, 0);
// DW_AT_high_pc
stream->OffsetFromSymbol(asm_name, code.Size());
InliningNode* node = ExpandInliningTree(code);
if (node != NULL) {
for (InliningNode* child = node->children_head; child != NULL;
child = child->children_next) {
WriteInliningNode(stream, child, asm_name, script, &namer);
}
}
stream->uleb128(0); // End of children.
}
}
static DwarfPosition ReadPosition(ReadStream* stream) {
const intptr_t line = stream->Read<int32_t>();
if (!FLAG_dwarf_stack_traces_mode) {
return DwarfPosition(line);
}
const intptr_t column = stream->Read<int32_t>();
return DwarfPosition(line, column);
}
// Our state machine encodes position metadata such that we don't know the
// end pc for an inlined function until it is popped, but DWARF DIEs encode
// it where the function is pushed. We expand the state transitions into
// an in-memory tree to do the conversion.
InliningNode* Dwarf::ExpandInliningTree(const Code& code) {
const CodeSourceMap& map =
CodeSourceMap::Handle(zone_, code.code_source_map());
if (map.IsNull()) {
return NULL;
}
const Array& functions = Array::Handle(zone_, code.inlined_id_to_function());
const Function& root_function = Function::ZoneHandle(zone_, code.function());
if (root_function.IsNull()) {
FATAL1("Wherefore art thou functionless code, %s?\n", code.ToCString());
}
GrowableArray<InliningNode*> node_stack(zone_, 4);
GrowableArray<DwarfPosition> token_positions(zone_, 4);
NoSafepointScope no_safepoint;
ReadStream stream(map.Data(), map.Length());
int32_t current_pc_offset = 0;
token_positions.Add(kNoDwarfPositionInfo);
InliningNode* root_node =
new (zone_) InliningNode(root_function, token_positions.Last(), 0);
root_node->end_pc_offset = code.Size();
node_stack.Add(root_node);
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
token_positions[token_positions.length() - 1] = ReadPosition(&stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
const Function& child_func =
Function::ZoneHandle(zone_, Function::RawCast(functions.At(func)));
InliningNode* child_node = new (zone_)
InliningNode(child_func, token_positions.Last(), current_pc_offset);
node_stack.Last()->AppendChild(child_node);
node_stack.Add(child_node);
token_positions.Add(kNoDwarfPositionInfo);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(node_stack.length() > 1);
ASSERT(token_positions.length() > 1);
node_stack.Last()->end_pc_offset = current_pc_offset;
node_stack.RemoveLast();
token_positions.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
while (node_stack.length() > 1) {
node_stack.Last()->end_pc_offset = current_pc_offset;
node_stack.RemoveLast();
token_positions.RemoveLast();
}
ASSERT(node_stack[0] == root_node);
return root_node;
}
void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
InliningNode* node,
const char* root_asm_name,
const Script& parent_script,
SnapshotTextObjectNamer* namer) {
intptr_t file = LookupScript(parent_script);
intptr_t function_index = LookupFunction(node->function);
const Script& script = Script::Handle(zone_, node->function.script());
stream->uleb128(kInlinedFunction);
// DW_AT_abstract_origin
// References a node written above in WriteAbstractFunctions.
stream->AbstractOrigin(function_index);
// DW_AT_low_pc
stream->OffsetFromSymbol(root_asm_name, node->start_pc_offset);
// DW_AT_high_pc
stream->OffsetFromSymbol(root_asm_name, node->end_pc_offset);
// DW_AT_call_file
stream->uleb128(file);
// DW_AT_call_line
stream->uleb128(node->position.line());
// DW_at_call_column
stream->uleb128(node->position.column());
for (InliningNode* child = node->children_head; child != NULL;
child = child->children_next) {
WriteInliningNode(stream, child, root_asm_name, script, namer);
}
stream->uleb128(0); // End of children.
}
void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
// 6.2.4 The Line Number Program Header
// 1. unit_length. This encoding implies 32-bit DWARF.
auto const line_prefix = "line";
intptr_t line_start;
intptr_t line_size_fixup = stream->ReserveSize(line_prefix, &line_start);
stream->u2(2); // 2. DWARF version 2
// 3. header_length
auto const lineheader_prefix = "lineheader";
intptr_t lineheader_start;
intptr_t lineheader_size_fixup =
stream->ReserveSize(lineheader_prefix, &lineheader_start);
stream->u1(1); // 4. minimum_instruction_length
stream->u1(1); // 5. default_is_stmt (true for compatibility with dsymutil).
stream->u1(0); // 6. line_base
stream->u1(1); // 7. line_range
stream->u1(13); // 8. opcode_base (12 standard opcodes in Dwarf 2)
// 9. standard_opcode_lengths
stream->u1(0); // DW_LNS_copy, 0 operands
stream->u1(1); // DW_LNS_advance_pc, 1 operands
stream->u1(1); // DW_LNS_advance_list, 1 operands
stream->u1(1); // DW_LNS_set_file, 1 operands
stream->u1(1); // DW_LNS_set_column, 1 operands
stream->u1(0); // DW_LNS_negate_stmt, 0 operands
stream->u1(0); // DW_LNS_set_basic_block, 0 operands
stream->u1(0); // DW_LNS_const_add_pc, 0 operands
stream->u1(1); // DW_LNS_fixed_advance_pc, 1 operands
stream->u1(0); // DW_LNS_set_prolog_end, 0 operands
stream->u1(0); // DW_LNS_set_epligoue_begin, 0 operands
stream->u1(1); // DW_LNS_set_isa, 1 operands
// 10. include_directories (sequence of path names)
// We don't emit any because we use full paths below.
stream->u1(0);
// 11. file_names (sequence of file entries)
String& uri = String::Handle(zone_);
for (intptr_t i = 0; i < scripts_.length(); i++) {
const Script& script = *(scripts_[i]);
uri = script.url();
auto const uri_cstr = Deobfuscate(uri.ToCString());
RELEASE_ASSERT(strlen(uri_cstr) != 0);
stream->string(uri_cstr); // NOLINT
stream->uleb128(0); // Include directory index.
stream->uleb128(0); // File modification time.
stream->uleb128(0); // File length.
}
stream->u1(0); // End of file names.
stream->SetSize(lineheader_size_fixup, lineheader_prefix, lineheader_start);
// 6.2.5 The Line Number Program
// The initial values for the line number program state machine registers
// according to the DWARF standard.
intptr_t previous_pc_offset = 0;
intptr_t previous_file = 1;
intptr_t previous_line = 1;
intptr_t previous_column = 0;
// Other info not stored in the state machine registers.
const char* previous_asm_name = nullptr;
Function& root_function = Function::Handle(zone_);
Script& script = Script::Handle(zone_);
CodeSourceMap& map = CodeSourceMap::Handle(zone_);
Array& functions = Array::Handle(zone_);
GrowableArray<const Function*> function_stack(zone_, 8);
GrowableArray<DwarfPosition> token_positions(zone_, 8);
SnapshotTextObjectNamer namer(zone_);
for (intptr_t i = 0; i < codes_.length(); i++) {
const Code& code = *(codes_[i]);
auto const asm_name = namer.SnapshotNameFor(i, code);
map = code.code_source_map();
if (map.IsNull()) {
continue;
}
root_function = code.function();
functions = code.inlined_id_to_function();
NoSafepointScope no_safepoint;
ReadStream code_map_stream(map.Data(), map.Length());
function_stack.Clear();
token_positions.Clear();
int32_t current_pc_offset = 0;
function_stack.Add(&root_function);
token_positions.Add(kNoDwarfPositionInfo);
while (code_map_stream.PendingBytes() > 0) {
uint8_t opcode = code_map_stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
token_positions[token_positions.length() - 1] =
ReadPosition(&code_map_stream);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = code_map_stream.Read<int32_t>();
current_pc_offset += delta;
const Function& function = *(function_stack.Last());
script = function.script();
intptr_t file = LookupScript(script);
// 1. Update LNP file.
if (file != previous_file) {
stream->u1(DW_LNS_set_file);
stream->uleb128(file);
previous_file = file;
}
// 2. Update LNP line.
const intptr_t line = token_positions.Last().line();
const intptr_t column = token_positions.Last().column();
if (line != previous_line) {
stream->u1(DW_LNS_advance_line);
stream->sleb128(line - previous_line);
previous_line = line;
}
if (column != previous_column) {
stream->u1(DW_LNS_set_column);
stream->uleb128(column);
previous_column = column;
}
// 3. Emit LNP row if the address register has been updated to a
// non-zero value (dartbug.com/41756).
if (previous_asm_name != nullptr) {
stream->u1(DW_LNS_copy);
}
// 4. Update LNP pc.
if (previous_asm_name == nullptr) {
auto const instr_size = 1 + compiler::target::kWordSize;
stream->u1(0); // This is an extended opcode
stream->u1(instr_size); // that is 5 or 9 bytes long
stream->u1(DW_LNE_set_address);
stream->OffsetFromSymbol(asm_name, current_pc_offset);
} else {
stream->u1(DW_LNS_advance_pc);
stream->DistanceBetweenSymbolOffsets(asm_name, current_pc_offset,
previous_asm_name,
previous_pc_offset);
}
previous_asm_name = asm_name;
previous_pc_offset = current_pc_offset;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func_index = code_map_stream.Read<int32_t>();
const Function& child_func = Function::Handle(
zone_, Function::RawCast(functions.At(func_index)));
function_stack.Add(&child_func);
token_positions.Add(kNoDwarfPositionInfo);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack.length() > 1);
ASSERT(token_positions.length() > 1);
function_stack.RemoveLast();
token_positions.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
code_map_stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
}
// Advance pc to end of the compilation unit if not already there.
if (codes_.length() != 0) {
const intptr_t last_code_index = codes_.length() - 1;
const Code& last_code = *(codes_[last_code_index]);
const intptr_t last_pc_offset = last_code.Size();
const char* last_asm_name =
namer.SnapshotNameFor(last_code_index, last_code);
stream->u1(DW_LNS_advance_pc);
if (previous_asm_name != nullptr) {
stream->DistanceBetweenSymbolOffsets(
last_asm_name, last_pc_offset, previous_asm_name, previous_pc_offset);
} else {
// No LNP entries (e.g., only stub code).
ASSERT(previous_pc_offset == 0);
stream->uleb128(last_pc_offset);
}
}
// End of contiguous machine code.
stream->u1(0); // This is an extended opcode
stream->u1(1); // that is 1 byte long
stream->u1(DW_LNE_end_sequence);
stream->SetSize(line_size_fixup, line_prefix, line_start);
}
const char* Dwarf::Deobfuscate(const char* cstr) {
if (reverse_obfuscation_trie_ == nullptr) return cstr;
TextBuffer buffer(256);
// Used to avoid Zone-allocating strings if no deobfuscation was performed.
bool changed = false;
intptr_t i = 0;
while (cstr[i] != '\0') {
intptr_t offset;
auto const value = reverse_obfuscation_trie_->Lookup(cstr + i, &offset);
if (offset == 0) {
// The first character was an invalid key element (that isn't the null
// terminator due to the while condition), copy it and skip to the next.
buffer.AddChar(cstr[i++]);
} else if (value != nullptr) {
changed = true;
buffer.AddString(value);
} else {
buffer.AddRaw(reinterpret_cast<const uint8_t*>(cstr + i), offset);
}
i += offset;
}
if (!changed) return cstr;
return OS::SCreate(zone_, "%s", buffer.buffer());
}
Trie<const char>* Dwarf::CreateReverseObfuscationTrie(Zone* zone) {
auto const I = Thread::Current()->isolate();
auto const map_array = I->obfuscation_map();
if (map_array == nullptr) return nullptr;
Trie<const char>* trie = nullptr;
for (intptr_t i = 0; map_array[i] != nullptr; i += 2) {
auto const key = map_array[i];
auto const value = map_array[i + 1];
ASSERT(value != nullptr);
// Don't include identity mappings.
if (strcmp(key, value) == 0) continue;
// Otherwise, any value in the obfuscation map should be a valid key.
ASSERT(Trie<const char>::IsValidKey(value));
trie = Trie<const char>::AddString(zone, trie, value, key);
}
return trie;
}
#endif // DART_PRECOMPILER
} // namespace dart