blob: 328212e4b68477bdd47575fcec5f1fddf6120b73 [file] [log] [blame]
//===-- DWARFASTParserGo.cpp ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "DWARFASTParserGo.h"
#include "DWARFASTParserGo.h"
#include "DWARFDIE.h"
#include "DWARFDIECollection.h"
#include "DWARFDebugInfo.h"
#include "DWARFDeclContext.h"
#include "DWARFDefines.h"
#include "SymbolFileDWARF.h"
#include "SymbolFileDWARFDebugMap.h"
#include "UniqueDWARFASTType.h"
#include "clang/Basic/Specifiers.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/TypeList.h"
//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
#ifdef ENABLE_DEBUG_PRINTF
#include <stdio.h>
#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DEBUG_PRINTF(fmt, ...)
#endif
#define DW_AT_go_kind 0x2900
#define DW_AT_go_key 0x2901
#define DW_AT_go_elem 0x2902
using namespace lldb;
using namespace lldb_private;
DWARFASTParserGo::DWARFASTParserGo(GoASTContext &ast) : m_ast(ast) {}
DWARFASTParserGo::~DWARFASTParserGo() {}
TypeSP DWARFASTParserGo::ParseTypeFromDWARF(
const lldb_private::SymbolContext &sc, const DWARFDIE &die,
lldb_private::Log *log, bool *type_is_new_ptr) {
TypeSP type_sp;
if (type_is_new_ptr)
*type_is_new_ptr = false;
if (die) {
SymbolFileDWARF *dwarf = die.GetDWARF();
if (log) {
dwarf->GetObjectFile()->GetModule()->LogMessage(
log, "DWARFASTParserGo::ParseTypeFromDWARF (die = 0x%8.8x) %s name = "
"'%s')",
die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.GetName());
}
Type *type_ptr = dwarf->m_die_to_type.lookup(die.GetDIE());
TypeList *type_list = dwarf->GetTypeList();
if (type_ptr == NULL) {
if (type_is_new_ptr)
*type_is_new_ptr = true;
const dw_tag_t tag = die.Tag();
bool is_forward_declaration = false;
DWARFAttributes attributes;
const char *type_name_cstr = NULL;
ConstString type_name_const_str;
Type::ResolveState resolve_state = Type::eResolveStateUnresolved;
uint64_t byte_size = 0;
uint64_t go_kind = 0;
Declaration decl;
Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID;
CompilerType compiler_type;
DWARFFormValue form_value;
dw_attr_t attr;
switch (tag) {
case DW_TAG_base_type:
case DW_TAG_pointer_type:
case DW_TAG_typedef:
case DW_TAG_unspecified_type: {
// Set a bit that lets us know that we are currently parsing this
dwarf->m_die_to_type[die.GetDIE()] = DIE_IS_BEING_PARSED;
const size_t num_attributes = die.GetAttributes(attributes);
lldb::user_id_t encoding_uid = LLDB_INVALID_UID;
if (num_attributes > 0) {
uint32_t i;
for (i = 0; i < num_attributes; ++i) {
attr = attributes.AttributeAtIndex(i);
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_name:
type_name_cstr = form_value.AsCString();
if (type_name_cstr)
type_name_const_str.SetCString(type_name_cstr);
break;
case DW_AT_byte_size:
byte_size = form_value.Unsigned();
break;
case DW_AT_encoding:
// = form_value.Unsigned();
break;
case DW_AT_type:
encoding_uid = form_value.Reference();
break;
case DW_AT_go_kind:
go_kind = form_value.Unsigned();
break;
default:
// Do we care about DW_AT_go_key or DW_AT_go_elem?
break;
}
}
}
}
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n",
die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr,
encoding_uid);
switch (tag) {
default:
break;
case DW_TAG_unspecified_type:
resolve_state = Type::eResolveStateFull;
compiler_type = m_ast.CreateVoidType(type_name_const_str);
break;
case DW_TAG_base_type:
resolve_state = Type::eResolveStateFull;
compiler_type =
m_ast.CreateBaseType(go_kind, type_name_const_str, byte_size);
break;
case DW_TAG_pointer_type:
encoding_data_type = Type::eEncodingIsPointerUID;
break;
case DW_TAG_typedef:
encoding_data_type = Type::eEncodingIsTypedefUID;
CompilerType impl;
Type *type = dwarf->ResolveTypeUID(encoding_uid);
if (type) {
if (go_kind == 0 && type->GetName() == type_name_const_str) {
// Go emits extra typedefs as a forward declaration. Ignore
// these.
dwarf->m_die_to_type[die.GetDIE()] = type;
return type->shared_from_this();
}
impl = type->GetForwardCompilerType();
compiler_type =
m_ast.CreateTypedefType(go_kind, type_name_const_str, impl);
}
break;
}
type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str,
byte_size, NULL, encoding_uid,
encoding_data_type, &decl, compiler_type,
resolve_state));
dwarf->m_die_to_type[die.GetDIE()] = type_sp.get();
} break;
case DW_TAG_structure_type: {
// Set a bit that lets us know that we are currently parsing this
dwarf->m_die_to_type[die.GetDIE()] = DIE_IS_BEING_PARSED;
bool byte_size_valid = false;
const size_t num_attributes = die.GetAttributes(attributes);
if (num_attributes > 0) {
uint32_t i;
for (i = 0; i < num_attributes; ++i) {
attr = attributes.AttributeAtIndex(i);
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_name:
type_name_cstr = form_value.AsCString();
type_name_const_str.SetCString(type_name_cstr);
break;
case DW_AT_byte_size:
byte_size = form_value.Unsigned();
byte_size_valid = true;
break;
case DW_AT_go_kind:
go_kind = form_value.Unsigned();
break;
// TODO: Should we use SLICETYPE's DW_AT_go_elem?
default:
break;
}
}
}
}
// TODO(ribrdb): Do we need this?
// UniqueDWARFASTType is large, so don't create a local variables on
// the stack, put it on the heap. This function is often called
// recursively and clang isn't good and sharing the stack space for
// variables in different blocks.
std::unique_ptr<UniqueDWARFASTType> unique_ast_entry_ap(
new UniqueDWARFASTType());
// Only try and unique the type if it has a name.
if (type_name_const_str &&
dwarf->GetUniqueDWARFASTTypeMap().Find(
type_name_const_str, die, decl,
byte_size_valid ? byte_size : -1, *unique_ast_entry_ap)) {
// We have already parsed this type or from another compile unit. GCC
// loves to use the "one definition rule" which can result in
// multiple definitions of the same class over and over in each
// compile unit.
type_sp = unique_ast_entry_ap->m_type_sp;
if (type_sp) {
dwarf->m_die_to_type[die.GetDIE()] = type_sp.get();
return type_sp;
}
}
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
DW_TAG_value_to_name(tag), type_name_cstr);
bool compiler_type_was_created = false;
compiler_type.SetCompilerType(
&m_ast,
dwarf->m_forward_decl_die_to_clang_type.lookup(die.GetDIE()));
if (!compiler_type) {
compiler_type_was_created = true;
compiler_type =
m_ast.CreateStructType(go_kind, type_name_const_str, byte_size);
}
type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str,
byte_size, NULL, LLDB_INVALID_UID,
Type::eEncodingIsUID, &decl, compiler_type,
Type::eResolveStateForward));
// Add our type to the unique type map so we don't end up creating many
// copies of the same type over and over in the ASTContext for our
// module
unique_ast_entry_ap->m_type_sp = type_sp;
unique_ast_entry_ap->m_die = die;
unique_ast_entry_ap->m_declaration = decl;
unique_ast_entry_ap->m_byte_size = byte_size;
dwarf->GetUniqueDWARFASTTypeMap().Insert(type_name_const_str,
*unique_ast_entry_ap);
if (!is_forward_declaration) {
// Always start the definition for a class type so that if the class
// has child classes or types that require the class to be created
// for use as their decl contexts the class will be ready to accept
// these child definitions.
if (die.HasChildren() == false) {
// No children for this struct/union/class, lets finish it
m_ast.CompleteStructType(compiler_type);
} else if (compiler_type_was_created) {
// Leave this as a forward declaration until we need to know the
// details of the type. lldb_private::Type will automatically call
// the SymbolFile virtual function
// "SymbolFileDWARF::CompleteType(Type *)" When the definition
// needs to be defined.
dwarf->m_forward_decl_die_to_clang_type[die.GetDIE()] =
compiler_type.GetOpaqueQualType();
dwarf->m_forward_decl_clang_type_to_die[compiler_type
.GetOpaqueQualType()] =
die.GetDIERef();
// SetHasExternalStorage (compiler_type.GetOpaqueQualType(), true);
}
}
} break;
case DW_TAG_subprogram:
case DW_TAG_subroutine_type: {
// Set a bit that lets us know that we are currently parsing this
dwarf->m_die_to_type[die.GetDIE()] = DIE_IS_BEING_PARSED;
bool is_variadic = false;
clang::StorageClass storage =
clang::SC_None; //, Extern, Static, PrivateExtern
const size_t num_attributes = die.GetAttributes(attributes);
if (num_attributes > 0) {
uint32_t i;
for (i = 0; i < num_attributes; ++i) {
attr = attributes.AttributeAtIndex(i);
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_name:
type_name_cstr = form_value.AsCString();
type_name_const_str.SetCString(type_name_cstr);
break;
case DW_AT_external:
if (form_value.Unsigned()) {
if (storage == clang::SC_None)
storage = clang::SC_Extern;
else
storage = clang::SC_PrivateExtern;
}
break;
case DW_AT_high_pc:
case DW_AT_low_pc:
break;
}
}
}
}
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
DW_TAG_value_to_name(tag), type_name_cstr);
std::vector<CompilerType> function_param_types;
// Parse the function children for the parameters
if (die.HasChildren()) {
ParseChildParameters(sc, die, is_variadic, function_param_types);
}
// compiler_type will get the function prototype clang type after this
// call
compiler_type = m_ast.CreateFunctionType(
type_name_const_str, function_param_types.data(),
function_param_types.size(), is_variadic);
type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, 0, NULL,
LLDB_INVALID_UID, Type::eEncodingIsUID, &decl,
compiler_type, Type::eResolveStateFull));
assert(type_sp.get());
} break;
case DW_TAG_array_type: {
// Set a bit that lets us know that we are currently parsing this
dwarf->m_die_to_type[die.GetDIE()] = DIE_IS_BEING_PARSED;
lldb::user_id_t type_die_offset = DW_INVALID_OFFSET;
int64_t first_index = 0;
uint32_t byte_stride = 0;
uint32_t bit_stride = 0;
const size_t num_attributes = die.GetAttributes(attributes);
if (num_attributes > 0) {
uint32_t i;
for (i = 0; i < num_attributes; ++i) {
attr = attributes.AttributeAtIndex(i);
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_name:
type_name_cstr = form_value.AsCString();
type_name_const_str.SetCString(type_name_cstr);
break;
case DW_AT_type:
type_die_offset = form_value.Reference();
break;
case DW_AT_byte_size:
break; // byte_size = form_value.Unsigned(); break;
case DW_AT_go_kind:
go_kind = form_value.Unsigned();
break;
default:
break;
}
}
}
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
DW_TAG_value_to_name(tag), type_name_cstr);
Type *element_type = dwarf->ResolveTypeUID(type_die_offset);
if (element_type) {
std::vector<uint64_t> element_orders;
ParseChildArrayInfo(sc, die, first_index, element_orders,
byte_stride, bit_stride);
if (byte_stride == 0)
byte_stride = element_type->GetByteSize();
CompilerType array_element_type =
element_type->GetForwardCompilerType();
if (element_orders.size() > 0) {
if (element_orders.size() > 1)
printf("golang: unsupported multi-dimensional array %s\n",
type_name_cstr);
compiler_type = m_ast.CreateArrayType(
type_name_const_str, array_element_type, element_orders[0]);
} else {
compiler_type = m_ast.CreateArrayType(type_name_const_str,
array_element_type, 0);
}
type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str,
byte_stride, NULL, type_die_offset,
Type::eEncodingIsUID, &decl, compiler_type,
Type::eResolveStateFull));
type_sp->SetEncodingType(element_type);
}
}
} break;
default:
dwarf->GetObjectFile()->GetModule()->ReportError(
"{0x%8.8x}: unhandled type tag 0x%4.4x (%s), "
"please file a bug and attach the file at the "
"start of this error message",
die.GetOffset(), tag, DW_TAG_value_to_name(tag));
break;
}
if (type_sp.get()) {
DWARFDIE sc_parent_die =
SymbolFileDWARF::GetParentSymbolContextDIE(die);
dw_tag_t sc_parent_tag = sc_parent_die.Tag();
SymbolContextScope *symbol_context_scope = NULL;
if (sc_parent_tag == DW_TAG_compile_unit ||
sc_parent_tag == DW_TAG_partial_unit) {
symbol_context_scope = sc.comp_unit;
} else if (sc.function != NULL && sc_parent_die) {
symbol_context_scope =
sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID());
if (symbol_context_scope == NULL)
symbol_context_scope = sc.function;
}
if (symbol_context_scope != NULL) {
type_sp->SetSymbolContextScope(symbol_context_scope);
}
// We are ready to put this type into the uniqued list up at the module
// level
type_list->Insert(type_sp);
dwarf->m_die_to_type[die.GetDIE()] = type_sp.get();
}
} else if (type_ptr != DIE_IS_BEING_PARSED) {
type_sp = type_ptr->shared_from_this();
}
}
return type_sp;
}
size_t DWARFASTParserGo::ParseChildParameters(
const SymbolContext &sc,
const DWARFDIE &parent_die, bool &is_variadic,
std::vector<CompilerType> &function_param_types) {
if (!parent_die)
return 0;
size_t arg_idx = 0;
for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
die = die.GetSibling()) {
dw_tag_t tag = die.Tag();
switch (tag) {
case DW_TAG_formal_parameter: {
DWARFAttributes attributes;
const size_t num_attributes = die.GetAttributes(attributes);
if (num_attributes > 0) {
Declaration decl;
DWARFFormValue param_type_die_offset;
uint32_t i;
for (i = 0; i < num_attributes; ++i) {
const dw_attr_t attr = attributes.AttributeAtIndex(i);
DWARFFormValue form_value;
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_name:
// = form_value.AsCString();
break;
case DW_AT_type:
param_type_die_offset = form_value;
break;
case DW_AT_location:
// if (form_value.BlockData())
// {
// const DWARFDataExtractor&
// debug_info_data =
// debug_info();
// uint32_t block_length =
// form_value.Unsigned();
// DWARFDataExtractor
// location(debug_info_data,
// form_value.BlockData() -
// debug_info_data.GetDataStart(),
// block_length);
// }
// else
// {
// }
// break;
default:
break;
}
}
}
Type *type = parent_die.ResolveTypeUID(DIERef(param_type_die_offset));
if (type) {
function_param_types.push_back(type->GetForwardCompilerType());
}
}
arg_idx++;
} break;
case DW_TAG_unspecified_parameters:
is_variadic = true;
break;
default:
break;
}
}
return arg_idx;
}
void DWARFASTParserGo::ParseChildArrayInfo(
const SymbolContext &sc, const DWARFDIE &parent_die, int64_t &first_index,
std::vector<uint64_t> &element_orders, uint32_t &byte_stride,
uint32_t &bit_stride) {
if (!parent_die)
return;
for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
die = die.GetSibling()) {
const dw_tag_t tag = die.Tag();
switch (tag) {
case DW_TAG_subrange_type: {
DWARFAttributes attributes;
const size_t num_child_attributes = die.GetAttributes(attributes);
if (num_child_attributes > 0) {
uint64_t num_elements = 0;
uint32_t i;
for (i = 0; i < num_child_attributes; ++i) {
const dw_attr_t attr = attributes.AttributeAtIndex(i);
DWARFFormValue form_value;
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_count:
num_elements = form_value.Unsigned();
break;
default:
case DW_AT_type:
break;
}
}
}
element_orders.push_back(num_elements);
}
} break;
}
}
}
bool DWARFASTParserGo::CompleteTypeFromDWARF(const DWARFDIE &die,
lldb_private::Type *type,
CompilerType &compiler_type) {
if (!die)
return false;
const dw_tag_t tag = die.Tag();
SymbolFileDWARF *dwarf = die.GetDWARF();
Log *log =
nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION));
if (log)
dwarf->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...",
die.GetID(), DW_TAG_value_to_name(tag), type->GetName().AsCString());
assert(compiler_type);
DWARFAttributes attributes;
switch (tag) {
case DW_TAG_structure_type: {
{
if (die.HasChildren()) {
SymbolContext sc(die.GetLLDBCompileUnit());
ParseChildMembers(sc, die, compiler_type);
}
}
m_ast.CompleteStructType(compiler_type);
return (bool)compiler_type;
}
default:
assert(false && "not a forward go type decl!");
break;
}
return false;
}
size_t DWARFASTParserGo::ParseChildMembers(const SymbolContext &sc,
const DWARFDIE &parent_die,
CompilerType &class_compiler_type) {
size_t count = 0;
uint32_t member_idx = 0;
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
GoASTContext *ast =
llvm::dyn_cast_or_null<GoASTContext>(class_compiler_type.GetTypeSystem());
if (ast == nullptr)
return 0;
for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
die = die.GetSibling()) {
dw_tag_t tag = die.Tag();
switch (tag) {
case DW_TAG_member: {
DWARFAttributes attributes;
const size_t num_attributes = die.GetAttributes(attributes);
if (num_attributes > 0) {
Declaration decl;
const char *name = NULL;
DWARFFormValue encoding_uid;
uint32_t member_byte_offset = UINT32_MAX;
uint32_t i;
for (i = 0; i < num_attributes; ++i) {
const dw_attr_t attr = attributes.AttributeAtIndex(i);
DWARFFormValue form_value;
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
switch (attr) {
case DW_AT_name:
name = form_value.AsCString();
break;
case DW_AT_type:
encoding_uid = form_value;
break;
case DW_AT_data_member_location:
if (form_value.BlockData()) {
Value initialValue(0);
Value memberOffset(0);
const DWARFDataExtractor &debug_info_data = die.GetData();
uint32_t block_length = form_value.Unsigned();
uint32_t block_offset =
form_value.BlockData() - debug_info_data.GetDataStart();
if (DWARFExpression::Evaluate(
NULL, // ExecutionContext *
NULL, // RegisterContext *
module_sp, debug_info_data, die.GetCU(), block_offset,
block_length, eRegisterKindDWARF, &initialValue, NULL,
memberOffset, NULL)) {
member_byte_offset = memberOffset.ResolveValue(NULL).UInt();
}
} else {
// With DWARF 3 and later, if the value is an integer constant,
// this form value is the offset in bytes from the beginning of
// the containing entity.
member_byte_offset = form_value.Unsigned();
}
break;
default:
break;
}
}
}
Type *member_type = die.ResolveTypeUID(DIERef(encoding_uid));
if (member_type) {
CompilerType member_go_type = member_type->GetFullCompilerType();
ConstString name_const_str(name);
m_ast.AddFieldToStruct(class_compiler_type, name_const_str,
member_go_type, member_byte_offset);
}
}
++member_idx;
} break;
default:
break;
}
}
return count;
}
Function *DWARFASTParserGo::ParseFunctionFromDWARF(const SymbolContext &sc,
const DWARFDIE &die) {
DWARFRangeList func_ranges;
const char *name = NULL;
const char *mangled = NULL;
int decl_file = 0;
int decl_line = 0;
int decl_column = 0;
int call_file = 0;
int call_line = 0;
int call_column = 0;
DWARFExpression frame_base(die.GetCU());
assert(die.Tag() == DW_TAG_subprogram);
if (die.Tag() != DW_TAG_subprogram)
return NULL;
if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line,
decl_column, call_file, call_line, call_column,
&frame_base)) {
// Union of all ranges in the function DIE (if the function is
// discontiguous)
AddressRange func_range;
lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0);
lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0);
if (lowest_func_addr != LLDB_INVALID_ADDRESS &&
lowest_func_addr <= highest_func_addr) {
ModuleSP module_sp(die.GetModule());
func_range.GetBaseAddress().ResolveAddressUsingFileSections(
lowest_func_addr, module_sp->GetSectionList());
if (func_range.GetBaseAddress().IsValid())
func_range.SetByteSize(highest_func_addr - lowest_func_addr);
}
if (func_range.GetBaseAddress().IsValid()) {
Mangled func_name;
func_name.SetValue(ConstString(name), false);
FunctionSP func_sp;
std::unique_ptr<Declaration> decl_ap;
if (decl_file != 0 || decl_line != 0 || decl_column != 0)
decl_ap.reset(new Declaration(
sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file),
decl_line, decl_column));
SymbolFileDWARF *dwarf = die.GetDWARF();
// Supply the type _only_ if it has already been parsed
Type *func_type = dwarf->m_die_to_type.lookup(die.GetDIE());
assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED);
if (dwarf->FixupAddress(func_range.GetBaseAddress())) {
const user_id_t func_user_id = die.GetID();
func_sp.reset(new Function(sc.comp_unit,
func_user_id, // UserID is the DIE offset
func_user_id, func_name, func_type,
func_range)); // first address range
if (func_sp.get() != NULL) {
if (frame_base.IsValid())
func_sp->GetFrameBaseExpression() = frame_base;
sc.comp_unit->AddFunction(func_sp);
return func_sp.get();
}
}
}
}
return NULL;
}