| //===-- godumpspec.cpp - C->Go helper utility for llvm --------------------===// |
| // |
| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This program is a helper for the libgo build. Given an object file |
| // and macros file derived from a given C source file, emit Go translations |
| // for the types/constants/macros in the C file. |
| // |
| // Expected usage mode looks something like this: |
| // |
| // % cc -E -dM -o somefile-macros.txt somefile.c |
| // % cc -g -c -o somefile.o somefile.c |
| // % llvm-godumpspec -object somefile.o \ |
| // -macrotmp somefile-macros.txt \ |
| // -output somefile-types-and-macros.go |
| // |
| // The tool reads DWARF from 'somefile.o' and combines the type/var/constant |
| // info from the DWARF with macro definitions from 'somefile-macros.txt' |
| // to produce Go equivalents for the type/var/constant info in the original |
| // C source file. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/DIContext.h" |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| #include "llvm/Object/Binary.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/ManagedStatic.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/PrettyStackTrace.h" |
| #include "llvm/Support/Signals.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include "macro-parser.h" |
| |
| #include <unordered_set> |
| #include <unordered_map> |
| #include <iostream> |
| #include <fstream> |
| #include <sstream> |
| #include <string> |
| |
| using namespace llvm; |
| using namespace object; |
| |
| namespace { |
| |
| static cl::opt<std::string> |
| InputObjectFile("object", cl::desc("Object file for *.c file")); |
| |
| static cl::opt<std::string> |
| InputMacrosFile("macrotmp", cl::desc("Macros file for *.c file")); |
| |
| static cl::opt<std::string> |
| OutputFilename("output", cl::desc("Output file to write.")); |
| |
| static cl::opt<unsigned> |
| PointerSize("pointersize", cl::desc("Size of a pointer in bytes for " |
| "the target architecture of interest. " |
| "Defaults to host pointer size."), |
| cl::init(sizeof(void*))); |
| |
| static cl::opt<bool> |
| Trace("trace", cl::desc("Enable debug trace output.")); |
| |
| } // namespace |
| |
| // At various points we have to decide whether to use the previously |
| // established DWARF name for a type, or emit it inline. |
| typedef enum { |
| TN_SelectDefault, |
| TN_PreferName, |
| TN_AvoidName |
| } TypeNameDisp; |
| |
| // This helper / mix-in class provides helper routines for capturing |
| // intermediate results via a buffer. |
| |
| class DumpManager { |
| public: |
| |
| // Create a new dump manager, passing it an output ostream. |
| explicit DumpManager(raw_ostream &os); |
| |
| protected: |
| |
| // Output stream |
| raw_ostream &os() { return os_; } |
| |
| // Set up a temporary string buffer (accessed via 'buf()' for |
| // building up intermediate results). |
| void initBuf() { |
| if (buf_.get()) |
| buf_->flush(); |
| str_.reset(new std::string); |
| buf_.reset(new llvm::raw_string_ostream(*str_.get())); |
| } |
| |
| // Return a reference to the current intermediate results buffer. |
| llvm::raw_string_ostream &buf() { |
| assert(buf_.get() != nullptr); |
| return *buf_.get(); |
| } |
| |
| // Save/restore the current intermediate results buffer. |
| // Occasionally when processing a given type there is a need to |
| // pause emission while visiting a sub-type or field, which these |
| // methods enable. Here 'pauseBuf' returns the current intermediate |
| // buffer state (which the caller can then cache away somewhere) and |
| // re-initializes things with a call to initBuf(); restoreBuf takes |
| // the specified string/buf and resets the buffer using those |
| // objects (any current contents of the intermediate results buffer |
| // are lost). |
| std::pair<raw_string_ostream *, std::string *> pauseBuf(); |
| void restoreBuf(raw_string_ostream *stream, std::string *str); |
| |
| // Determines whether a given token is a Go language keyword. |
| bool isGoKeyWord(const char *str) { |
| return keywords_.find(str) != keywords_.end(); |
| } |
| |
| private: |
| std::unordered_set<std::string> keywords_; |
| std::unique_ptr<std::string> str_; |
| std::unique_ptr<llvm::raw_string_ostream> buf_; |
| raw_ostream &os_; // output file we're writing |
| }; |
| |
| DumpManager::DumpManager(raw_ostream &os) |
| : keywords_({"break", "default", "func", "interface", "select", |
| "case", "defer", "go", "map", "struct", "chan", "else", |
| "goto", "package", "switch", "const", "fallthrough", "if", |
| "range", "type", "continue", "for", "import", "return", "var"}), |
| os_(os) |
| { |
| } |
| |
| // Pause output buffering, saving off current state to 'saveTo'. Returns |
| // the state of the current buffer (which the client can presumbably |
| // stash away and later pass to restoreBuf). |
| |
| std::pair<raw_string_ostream *, std::string *> DumpManager::pauseBuf() |
| { |
| raw_string_ostream *r1 = buf_.release(); |
| std::string *r2 = str_.release(); |
| initBuf(); |
| return std::make_pair(r1, r2); |
| } |
| |
| // Restore buffer using previously capturede state from pauseBuf. |
| |
| void DumpManager::restoreBuf(raw_string_ostream *stream, std::string *str) |
| { |
| buf_.reset(stream); |
| str_.reset(str); |
| } |
| |
| // This class manages the overall process of generating Go code from |
| // DWARF and macro info derived from a C compilation. It walks the |
| // DWARF DIE chain from an object file we're looking at, and manages |
| // the process of combining DWARF type info with definitions from a |
| // macro temp file. Expected use here is to construct a helper, then |
| // call the readDwarf() method, then read + process any macro |
| // definitions, and finally called the emit() method. |
| |
| class GoDumpHelper : public MacroParser, public DumpManager { |
| public: |
| explicit GoDumpHelper(raw_ostream &os); |
| void readDwarf(DWARFCompileUnit *cu); |
| void emit(); |
| |
| private: |
| // Visit a type. Each type should be visited twice, first as part of |
| // a discovery/analysis phase (with emit_ == false) and then as part |
| // of an output phase (eith emit_ == true). |
| void visitType(const DWARFDie &die); |
| |
| // Emit a variable. |
| void emitVariable(const DWARFDie &die); |
| |
| // Record a given DWARF DIE for additional processing. |
| void enqueueType(const DWARFDie &die); |
| void enqueueVariable(const DWARFDie &die); |
| |
| // Visit the specified DWARF type DIE, generateing a Go version |
| // of the type into the intermediate results buffer. |
| bool generateType(const DWARFDie &die, TypeNameDisp disp = TN_SelectDefault); |
| |
| // Similar to the above, but returns the Go code as a string without |
| // appending anything to the current buffer. |
| std::pair<bool, std::string> generateTypeToString(const DWARFDie &die); |
| |
| // Helpers to take care of specific type flavors. |
| bool generateBaseType(const DWARFDie &die); |
| bool generateStructType(const DWARFDie &die); |
| bool generateUnionType(const DWARFDie &die); |
| bool generateFcnType(const DWARFDie &die); |
| bool generateArrayType(const DWARFDie &die); |
| bool generateEnumType(const DWARFDie &die); |
| |
| // Postprocess a struct member. |
| bool generateMember(const DWARFDie &die); |
| |
| // Assorted helpers. |
| bool useTypeName(const DWARFDie &die, TypeNameDisp disp); |
| bool isPtrToFunctionType(const DWARFDie &die); |
| bool isSuitableArrayDimTyp(const DWARFDie &die); |
| bool isSpuriousTypedef(const DWARFDie &die); |
| bool isBitField(const DWARFDie &die); |
| bool isAggregate(const DWARFDie &die); |
| const char *dieName(DWARFDie die); |
| DWARFDie forwardedType(DWARFDie die); |
| std::string enumLitString(DWARFFormValue &fvalue); |
| |
| bool isInvalidType(const DWARFDie &die) { |
| return invalidTypes_.find(die.getOffset()) != invalidTypes_.end(); |
| } |
| bool isBaseType(const DWARFDie &die) { |
| return die.getTag() == dwarf::DW_TAG_base_type; |
| } |
| |
| bool typeSizeKnown(const DWARFDie &die) { |
| return typeSize_.find(die.getOffset()) != typeSize_.end(); |
| } |
| |
| uint64_t typeSize(const DWARFDie &die) { |
| auto it = typeSize_.find(die.getOffset()); |
| assert(it != typeSize_.end()); |
| return it->second; |
| } |
| uint64_t typeOfSize(const DWARFDie &die) { |
| DWARFDie typ = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(typ.isValid()); |
| auto it = typeSize_.find(typ.getOffset()); |
| assert(it != typeSize_.end()); |
| return it->second; |
| } |
| |
| void setTypeSize(const DWARFDie &die, uint64_t siz) { |
| auto it = typeSize_.find(die.getOffset()); |
| if (it != typeSize_.end()) { |
| assert(siz == it->second); |
| } else { |
| typeSize_[die.getOffset()] = siz; |
| } |
| } |
| |
| bool typeAlignKnown(const DWARFDie &die) { |
| return typeAlign_.find(die.getOffset()) != typeAlign_.end(); |
| } |
| |
| uint64_t typeAlign(const DWARFDie &die) { |
| auto it = typeAlign_.find(die.getOffset()); |
| if (it != typeAlign_.end()) |
| return it->second; |
| assert(isInvalidType(die)); |
| return 1; |
| } |
| |
| void setTypeAlign(const DWARFDie &die, uint64_t aln) { |
| auto it = typeAlign_.find(die.getOffset()); |
| if (it != typeAlign_.end()) { |
| assert(aln == it->second); |
| } else { |
| typeAlign_[die.getOffset()] = aln; |
| } |
| } |
| |
| private: |
| // Names of types emitted. To avoid clases between macros + types. |
| std::unordered_set<std::string> emittedTypeNames_; |
| |
| // To detect cycles in a type graph. Indexed by DIE offset. |
| std::unordered_set<uint32_t> visited_; |
| |
| // Records types unrepresentable in Go. Indexed by DIE offset. |
| std::unordered_set<uint32_t> invalidTypes_; |
| |
| // Referenced structs with no defined body (eg: "struct X;"). |
| // Indexed by DIE offset. |
| std::unordered_set<uint32_t> externalStructs_; |
| |
| // Anonymous sub-structure types within unions. Indexed by DIE offset. |
| std::unordered_set<uint32_t> anonSubstructure_; |
| |
| // Enumerated type literals. Indexed by enum literal name. |
| std::unordered_map<std::string, std::string> enumLiterals_; |
| |
| // Type size and alignment requirement. Indexed by DIE offset. |
| std::unordered_map<uint32_t, uint64_t> typeSize_; |
| std::unordered_map<uint32_t, uint32_t> typeAlign_; |
| |
| // Queue of interesting DIEs to examine. |
| std::vector<uint32_t> queue_; |
| |
| // Current DWARF compilation unit. |
| DWARFCompileUnit *cu_; // current compilation unit |
| |
| // DWARF die offset of top-level type DIE being visited. |
| uint32_t curDieOffset_; |
| |
| // Count of pad bytes used while processing bitfields. |
| uint32_t padcount_; |
| |
| // Pointer size in bytes. |
| uint32_t ptrSize_; |
| |
| // Set initially to false while we examine all type info, then set to |
| // true for a second pass through to emit types. |
| bool emit_; |
| }; |
| |
| constexpr uint32_t invalidOffset = ((unsigned)-1); |
| |
| GoDumpHelper::GoDumpHelper(raw_ostream &os) |
| : DumpManager(os), |
| curDieOffset_(invalidOffset), |
| padcount_(0), |
| ptrSize_(PointerSize), |
| emit_(false) |
| { |
| } |
| |
| const char *GoDumpHelper::dieName(DWARFDie die) |
| { |
| auto formval = die.find(dwarf::DW_AT_name); |
| if (!formval) |
| return nullptr; |
| auto cstr = formval->getAsCString(); |
| if (!cstr) |
| return nullptr; |
| return *cstr; |
| } |
| |
| void GoDumpHelper::enqueueType(const DWARFDie &die) |
| { |
| queue_.push_back(cu_->getDIEIndex(die)); |
| visitType(die); |
| } |
| |
| void GoDumpHelper::enqueueVariable(const DWARFDie &die) |
| { |
| queue_.push_back(cu_->getDIEIndex(die)); |
| DWARFDie typ = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(typ.isValid()); |
| visitType(typ); |
| } |
| |
| // Walk the DWARF DIE chain for the specified compilation unit, |
| // queuing up interesting DIEs for later post-processing. As each type |
| // or variable is enqueued we'll visit the type associated with it, so |
| // as to discover invalid types and establish the size/alignment of |
| // all interesting types. |
| |
| void GoDumpHelper::readDwarf(DWARFCompileUnit *cu) |
| { |
| assert(cu); |
| cu_ = cu; |
| for (const auto &entry : cu_->dies()) { |
| DWARFDie die(cu_, &entry); |
| if (isType(die.getTag()) && |
| (dieName(die) != nullptr || |
| die.getTag() == dwarf::DW_TAG_enumeration_type)) |
| enqueueType(die); |
| else if (die.getTag() == dwarf::DW_TAG_variable) |
| enqueueVariable(die); |
| if (Trace) |
| die.dump(); |
| } |
| } |
| |
| void GoDumpHelper::visitType(const DWARFDie &die) |
| { |
| // Skip base types at the top level (they will be emitted inline |
| // where needed). |
| if (isBaseType(die)) |
| return; |
| |
| // Skip spurious typedefs ("type X X"), which crop up a fair |
| // amount with structs (ex: "typedef struct A { ... } A;"). |
| if (isSpuriousTypedef(die)) |
| return; |
| |
| if (Trace) { |
| std::cerr << "visit offset " << std::hex << die.getOffset(); |
| if (dieName(die)) |
| std::cerr << " " << dieName(die); |
| std::cerr << "\n"; |
| } |
| |
| initBuf(); |
| const char *cname = dieName(die); |
| curDieOffset_ = die.getOffset(); |
| padcount_ = 0; |
| if (emit_) |
| visited_.clear(); |
| bool ok = generateType(die); |
| curDieOffset_ = invalidOffset; |
| |
| if (emit_ && cname != nullptr) { |
| if (! ok) |
| os() << "// "; |
| else |
| emittedTypeNames_.insert(cname); |
| os() << "type _" << cname << " " << buf().str(); |
| os() << "\n"; |
| |
| if (ok) { |
| // For struct and union types, emit a size constant |
| DWARFDie fwd(forwardedType(die)); |
| if (fwd.getTag() == dwarf::DW_TAG_structure_type || |
| fwd.getTag() == dwarf::DW_TAG_union_type) { |
| assert(typeSizeKnown(fwd)); |
| os() << "const _sizeof_" << cname << " = " << typeSize(fwd) << "\n"; |
| } |
| } |
| } |
| } |
| |
| static const char *bitsTag(unsigned byteSize) { |
| switch(byteSize) { |
| case 1: return "8"; |
| case 2: return "16"; |
| case 4: return "32"; |
| case 8: return "64"; |
| case 16: return "128"; |
| } |
| return nullptr; |
| } |
| |
| std::string GoDumpHelper::enumLitString(DWARFFormValue &fvalue) |
| { |
| std::stringstream ss; |
| auto uval = fvalue.getAsUnsignedConstant(); |
| auto sval = fvalue.getAsSignedConstant(); |
| if (uval) { |
| ss << *uval; |
| } else if (sval) { |
| ss << *sval; |
| } |
| return ss.str(); |
| } |
| |
| bool GoDumpHelper::generateEnumType(const DWARFDie &die) |
| { |
| // Enumerated types wind up as simple uint's in Go. |
| auto byteSize = dwarf::toUnsigned(die.find(dwarf::DW_AT_byte_size)); |
| assert(byteSize); |
| const char *bits = bitsTag(*byteSize); |
| if (!bits) |
| return false; |
| setTypeAlign(die, *byteSize); |
| buf() << "uint" << bits; |
| |
| // Our overall goal is to have enumeration types trump macro |
| // definitions; to enable this, macros and enum literals are |
| // buffered up and then combined/reconciled as part of the |
| // emit process. |
| bool rval = true; |
| DWARFDie child = die.getFirstChild(); |
| while (child && !child.isNULL()) { |
| if (child.getTag() == dwarf::DW_TAG_enumerator) { |
| const char *name = dieName(child); |
| // FIXME: avoid clash with Go keywords here? |
| auto val = child.find(dwarf::DW_AT_const_value); |
| assert(val); |
| std::string s = enumLitString(*val); |
| if (s.empty()) |
| rval = false; |
| else { |
| std::string n(name); |
| if (enumLiterals_.find(n) == enumLiterals_.end()) { |
| enumLiterals_[n] = s; |
| addEnumLiteralPseudoMacro(n, s); |
| } |
| } |
| } |
| child = child.getSibling(); |
| } |
| |
| return rval; |
| } |
| |
| bool GoDumpHelper::isSpuriousTypedef(const DWARFDie &die) |
| { |
| if (die.getTag() != dwarf::DW_TAG_typedef) |
| return false; |
| |
| // For C constructs such as "typedef struct X { ... } X;" in the |
| // DWARF we'll see first a struct type with named type X, followed |
| // by a typedef type with name X, which would result in "type X X", |
| // which is not what we want. |
| DWARFDie tgtDie = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| if (!tgtDie.isValid()) |
| return false; |
| const char *toname = dieName(tgtDie); |
| if (toname) { |
| const char *fromname = dieName(die); |
| if (fromname && !strcmp(fromname, toname)) |
| return true; |
| } |
| return false; |
| } |
| |
| bool GoDumpHelper::isPtrToFunctionType(const DWARFDie &die) |
| { |
| if (die.getTag() != dwarf::DW_TAG_pointer_type) |
| return false; |
| DWARFDie toDie = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| if (! toDie.isValid()) |
| return false; |
| return toDie.getTag() == dwarf::DW_TAG_subroutine_type; |
| } |
| |
| bool GoDumpHelper::isSuitableArrayDimTyp(const DWARFDie &die) |
| { |
| // FIXME: no support yet for enumerated type as array dim. |
| if (!isBaseType(die)) |
| return false; |
| auto byteSize = dwarf::toUnsigned(die.find(dwarf::DW_AT_byte_size)); |
| assert(byteSize); |
| if (*byteSize < 1 || *byteSize > 8) |
| return false; |
| auto encoding = dwarf::toUnsigned(die.find(dwarf::DW_AT_encoding)); |
| assert(encoding); |
| if (*encoding != dwarf::DW_ATE_signed && |
| *encoding != dwarf::DW_ATE_unsigned_char && |
| *encoding != dwarf::DW_ATE_signed_char && |
| *encoding != dwarf::DW_ATE_unsigned) |
| return false; |
| return true; |
| } |
| |
| bool GoDumpHelper::generateArrayType(const DWARFDie &die) |
| { |
| bool rval = true; |
| |
| DWARFDie eltyp = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(eltyp.isValid()); |
| std::pair<bool, std::string> eresult = generateTypeToString(eltyp); |
| if (! eresult.first) |
| rval = false; |
| std::string etgen(eresult.second); |
| setTypeAlign(die, typeAlign(eltyp)); |
| |
| DWARFDie child = die.getFirstChild(); |
| uint64_t totElements = 1; |
| bool zeroDim = false; |
| while (child && !child.isNULL()) { |
| if (child.getTag() == dwarf::DW_TAG_subrange_type) { |
| DWARFDie ctyp = child.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| if (!ctyp.isValid()) { |
| // This corresponds to "[0]" |
| buf() << "[0]"; |
| zeroDim = true; |
| } else { |
| if (! isSuitableArrayDimTyp(ctyp)) |
| rval = false; |
| // NB: don't expect to see a lower bound here or non-constant |
| // upper bound. |
| auto ubval = child.find(dwarf::DW_AT_upper_bound); |
| auto count = child.find(dwarf::DW_AT_count); |
| if (ubval) { |
| auto cval = ubval->getAsUnsignedConstant(); |
| assert(cval); |
| buf() << "[" << *cval << "+1]"; |
| totElements = (*cval+1) * totElements; |
| } else if (count) { |
| auto cval = count->getAsUnsignedConstant(); |
| assert(cval); |
| buf() << "[" << *cval << "]"; |
| totElements = *cval * totElements; |
| } else { |
| // This corresponds to "[0]" |
| buf() << "[0]"; |
| zeroDim = true; |
| } |
| } |
| } |
| child = child.getSibling(); |
| } |
| if (zeroDim) |
| totElements = 0; |
| |
| buf() << etgen; |
| |
| // NB: array types may be lacking a byte size attribute. If so, set |
| // size manually. |
| auto byteSize = dwarf::toUnsigned(die.find(dwarf::DW_AT_byte_size)); |
| if (!byteSize) |
| setTypeSize(die, totElements * typeSize(eltyp)); |
| |
| return rval; |
| } |
| |
| bool GoDumpHelper::generateFcnType(const DWARFDie &die) |
| { |
| bool rval = true; |
| |
| // Params |
| buf() << "func("; |
| bool com = false; |
| for (DWARFDie child : die.children()) { |
| if (com) |
| buf() << ", "; |
| DWARFDie ctyp = child.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(ctyp.isValid()); |
| if (!generateType(ctyp)) |
| rval = false; |
| com = true; |
| } |
| buf() << ") "; |
| |
| // Return type |
| DWARFDie rtyp = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| if (rtyp.isValid()) { |
| if (!generateType(rtyp)) |
| rval = false; |
| } |
| |
| // From a practical perspective the only function types of interest |
| // for us are pointer-to-function types, so here we create fictional |
| // values for size and alignment of this type (this makes things easier |
| // if this DIE is the target of a typedef). |
| setTypeSize(die, 0); |
| setTypeAlign(die, 0); |
| |
| return rval; |
| } |
| |
| bool GoDumpHelper::generateMember(const DWARFDie &die) |
| { |
| bool rval = true; |
| |
| const char *name = dieName(die); |
| bool anonSub = false; |
| if (!name) { |
| // This corresponds to an anonymous sub-union, e.g. something like |
| // |
| // struct x { |
| // union { int q; double z; }; |
| // ... |
| // } |
| // |
| // From the compiler's point of view, "q" and "z" are effectively |
| // fields within x, which has to be reflected in the generated Go code. |
| // Note: to make matters more complicated, anonymous structures are |
| // also allowed. Example: |
| // |
| // union { struct { int x; double z; int y; }; |
| // struct { char c4[4]; |
| // struct { double quix; char kkk; }; double k; }; }; |
| // |
| // For the oddball above, each of the nested fields (ex: kkk) is |
| // considered by the compiler to be a child of the top-level union (in |
| // terms of how a user would reference it). |
| DWARFDie ctyp = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(ctyp.isValid()); |
| assert(ctyp.getTag() == dwarf::DW_TAG_union_type || |
| ctyp.getTag() == dwarf::DW_TAG_structure_type); |
| anonSubstructure_.insert(ctyp.getOffset()); |
| anonSub = true; |
| } else { |
| assert(name); |
| if (isGoKeyWord(name)) |
| buf() << "_"; |
| buf() << name << " "; |
| } |
| auto bitSize = die.find(dwarf::DW_AT_bit_size); |
| if (bitSize) { |
| // This corresponds to the case of a bitfield whose size/alignment |
| // happens to make it appear to be an integral field, e.g. |
| // |
| // struct { |
| // unsigned x:16; |
| // } |
| // |
| // Here we want to treat 'x' as if it were a simple "unsigned |
| // short" and not a bitfield. |
| // |
| auto bsval = bitSize->getAsUnsignedConstant(); |
| assert(bsval); |
| DWARFDie ctyp = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(ctyp.isValid()); |
| assert(isBaseType(ctyp)); |
| auto encoding = dwarf::toUnsigned(ctyp.find(dwarf::DW_AT_encoding)); |
| assert(encoding); |
| if (*encoding == dwarf::DW_ATE_signed_char || |
| *encoding == dwarf::DW_ATE_signed) |
| buf() << "int" << *bsval; |
| else |
| buf() << "uint" << *bsval; |
| } else { |
| DWARFDie ctyp = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(ctyp.isValid()); |
| if (!generateType(ctyp)) |
| rval = false; |
| } |
| if (! anonSub) |
| buf() << "; "; |
| return rval; |
| } |
| |
| bool GoDumpHelper::generateUnionType(const DWARFDie &die) |
| { |
| bool rval = true; |
| if (anonSubstructure_.find(die.getOffset()) == anonSubstructure_.end()) |
| buf() << "struct { "; |
| std::pair<raw_string_ostream *, std::string *> pauseState; |
| |
| // Walk the union members. We want to emit only the first field |
| // (since Go has no unions), so pause buffering after the first |
| // field and resume after we are done. |
| DWARFDie child = die.getFirstChild(); |
| uint64_t csiz = 0; |
| uint64_t calign = 0; |
| uint64_t maxalign = 0; |
| bool firstchild = true; |
| auto padcountsave = 0; |
| while (child && !child.isNULL()) { |
| if (child.getTag() == dwarf::DW_TAG_member) { |
| // Replace bitfields with padding. |
| auto bitsize = child.find(dwarf::DW_AT_bit_size); |
| if (bitsize) |
| continue; |
| |
| rval &= generateMember(child); |
| DWARFDie ctyp = child.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| if (firstchild) { |
| calign = typeAlign(ctyp); |
| csiz = typeSize(ctyp); |
| pauseState = pauseBuf(); |
| padcountsave = padcount_; |
| firstchild = false; |
| } |
| maxalign = std::max(typeAlign(ctyp), maxalign); |
| } |
| child = child.getSibling(); |
| } |
| if (pauseState.first != nullptr) { |
| padcount_ = padcountsave; |
| restoreBuf(pauseState.first, pauseState.second); |
| } |
| |
| // Pad out to the required size |
| auto byteSize = dwarf::toUnsigned(die.find(dwarf::DW_AT_byte_size)); |
| assert(byteSize); |
| if (csiz < *byteSize) { |
| unsigned padAmt = *byteSize - csiz; |
| buf() << "Godump_" << padcount_++ << "_pad [" << padAmt << "]byte; "; |
| } |
| |
| // Enforce alignment |
| if (maxalign > calign && maxalign > 1) { |
| buf() << "Godump_" << padcount_++ << "_align [0]int" |
| << bitsTag(maxalign) << "; "; |
| } |
| setTypeAlign(die, maxalign); |
| |
| if (anonSubstructure_.find(die.getOffset()) == anonSubstructure_.end()) |
| buf() << "}"; |
| return rval; |
| } |
| |
| bool GoDumpHelper::isBitField(const DWARFDie &die) |
| { |
| auto bitSize = die.find(dwarf::DW_AT_bit_size); |
| if (!bitSize) |
| return false; |
| auto byteSize = die.find(dwarf::DW_AT_byte_size); |
| auto bitOffset = die.find(dwarf::DW_AT_bit_offset); |
| assert(bitSize && bitOffset); |
| auto byval = byteSize->getAsUnsignedConstant(); |
| auto bsval = bitSize->getAsUnsignedConstant(); |
| auto boval = bitOffset->getAsUnsignedConstant(); |
| assert(byval && bsval && boval); |
| if (*boval % *bsval == 0 && |
| *bsval % *byval == 0 && |
| (*bsval == 8 || *bsval == 16 || *bsval == 32 || *bsval == 64)) |
| return false; |
| return true; |
| } |
| |
| bool GoDumpHelper::generateStructType(const DWARFDie &die) |
| { |
| if (anonSubstructure_.find(die.getOffset()) == anonSubstructure_.end()) |
| buf() << "struct { "; |
| |
| // Collect members. Note that DWARF allows the producer to include |
| // other things (such as other types) as direct children of the |
| // struct type DIE, so we have to allow for that possibility here. |
| std::vector<DWARFDie> members; |
| DWARFDie child = die.getFirstChild(); |
| while (child && !child.isNULL()) { |
| if (child.getTag() == dwarf::DW_TAG_member) { |
| members.push_back(child); |
| } |
| child = child.getSibling(); |
| } |
| |
| // Walk the members. |
| uint64_t accumSize = 0; |
| uint64_t maxAlign = 0; |
| bool rval = true; |
| bool prevBitField = false; |
| for (unsigned idx = 0; idx < members.size(); ++idx) { |
| auto &member = members[idx]; |
| |
| // Replace bitfields with padding. |
| if (isBitField(member)) { |
| prevBitField = true; |
| continue; |
| } |
| |
| // Padding if needed |
| if (idx != 0) { |
| auto dml = member.find(dwarf::DW_AT_data_member_location); |
| assert(dml); |
| auto dmlval = dml->getAsUnsignedConstant(); |
| assert(dmlval); |
| if (accumSize < dmlval) { |
| unsigned padAmt = *dmlval - accumSize; |
| if (prevBitField) |
| buf() << "Godump_" << padcount_++ << "_pad [" << padAmt << "]byte; "; |
| accumSize += padAmt; |
| } |
| prevBitField = false; |
| } |
| |
| rval &= generateMember(member); |
| DWARFDie mtyp = member.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| maxAlign = std::max(maxAlign, typeAlign(mtyp)); |
| auto memberBitSize = member.find(dwarf::DW_AT_bit_size); |
| if (memberBitSize) { |
| auto bsval = memberBitSize->getAsUnsignedConstant(); |
| assert(bsval); |
| accumSize += *bsval / 8; |
| } else { |
| accumSize += typeSize(mtyp); |
| } |
| } |
| setTypeAlign(die, maxAlign); |
| |
| // Handle the "external" struct case, e.g. something like |
| // |
| // typedef struct definedSomewhereElse btyp; |
| // typedef btyp *pbtyp; |
| // extern pbytp *p; |
| // |
| // There isn't a direct Go equivalent here, so emit a dummy |
| // in such cases and make a record of what's happened. |
| auto isdecl = die.find(dwarf::DW_AT_declaration); |
| if (isdecl) { |
| auto ival = isdecl->getAsUnsignedConstant(); |
| assert(ival); |
| if (*ival) { |
| externalStructs_.insert(die.getOffset()); |
| setTypeSize(die, 0); |
| setTypeAlign(die, 0); |
| } |
| } |
| |
| // Padding if needed |
| auto byteSize = typeSize(die); |
| if (accumSize < byteSize) { |
| unsigned padAmt = byteSize - accumSize; |
| buf() << "Godump_" << padcount_++ << "_pad [" << padAmt << "]byte; "; |
| } |
| |
| if (anonSubstructure_.find(die.getOffset()) == anonSubstructure_.end()) |
| buf() << "}"; |
| |
| return rval; |
| } |
| |
| bool GoDumpHelper::generateBaseType(const DWARFDie &die) |
| { |
| auto byteSize = dwarf::toUnsigned(die.find(dwarf::DW_AT_byte_size)); |
| assert(byteSize); |
| unsigned bytes = *byteSize; |
| const char *bits = bitsTag(bytes); |
| if (!bits) |
| return false; |
| |
| auto encoding = dwarf::toUnsigned(die.find(dwarf::DW_AT_encoding)); |
| assert(encoding); |
| switch(*encoding) { |
| case dwarf::DW_ATE_boolean: |
| setTypeAlign(die, 1); |
| buf() << "bool"; |
| return true; |
| case dwarf::DW_ATE_unsigned_char: { |
| setTypeAlign(die, 1); |
| assert(bytes == 1); |
| buf() << "uint8"; |
| return true; |
| } |
| case dwarf::DW_ATE_signed_char: { |
| setTypeAlign(die, 1); |
| assert(bytes == 1); |
| buf() << "int8"; |
| return true; |
| } |
| case dwarf::DW_ATE_unsigned: { |
| setTypeAlign(die, bytes); |
| buf() << "uint" << bits; |
| return true; |
| } |
| case dwarf::DW_ATE_signed: { |
| setTypeAlign(die, bytes); |
| buf() << "int" << bits; |
| return true; |
| } |
| case dwarf::DW_ATE_float: { |
| setTypeAlign(die, bytes); |
| // Go does not support float128 / long double |
| if (bytes > 8) |
| return false; |
| buf() << "float" << bits; |
| return true; |
| } |
| case dwarf::DW_ATE_complex_float: { |
| setTypeAlign(die, bytes/2); |
| buf() << "complex" << bits; |
| return true; |
| } |
| default: { |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| DWARFDie GoDumpHelper::forwardedType(DWARFDie die) |
| { |
| while (die.getTag() == dwarf::DW_TAG_typedef || |
| die.getTag() == dwarf::DW_TAG_restrict_type || |
| die.getTag() == dwarf::DW_TAG_volatile_type || |
| die.getTag() == dwarf::DW_TAG_const_type) { |
| die = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(die.isValid()); |
| } |
| return die; |
| } |
| |
| bool GoDumpHelper::isAggregate(const DWARFDie &die) |
| { |
| return (die.getTag() == dwarf::DW_TAG_structure_type || |
| die.getTag() == dwarf::DW_TAG_union_type || |
| die.getTag() == dwarf::DW_TAG_array_type); |
| } |
| |
| // When generating a Go representation for a given DWARF type T |
| // that refers to a set of other types { T1, T2, ... TN }, at |
| // various points we have to decide whether to refer to a given child type |
| // TK via TK's names (if it has a name) or whether to emit concrete |
| // definition for TK. This routine helps with making that decision. |
| |
| bool GoDumpHelper::useTypeName(const DWARFDie &die, TypeNameDisp disp) |
| { |
| // Type has to have a name for us to use it. |
| const char *name = dieName(die); |
| if (!name) |
| return false; |
| |
| // Top-level die that we're in the process of emitting? |
| if (die.getOffset() == curDieOffset_) |
| return false; |
| |
| // If we're in the process of visiting this type, we have to |
| // use the emitted name (to avoid infinite recursion). |
| if (visited_.find(die.getOffset()) != visited_.end()) { |
| assert(name); |
| return true; |
| } |
| |
| // Don't try to use the name stored within a base type |
| // (among other things, they are allowed to have spaces) |
| if (isBaseType(die)) |
| return false; |
| |
| // On the first pass (prior to emit) walk as many types as possible. |
| if (!emit_) |
| return false; |
| |
| // Take into account preferences here. |
| if (disp == TN_AvoidName) |
| return false; |
| if (disp == TN_PreferName) |
| return true; |
| |
| // Here we try to mimic the GCC -fgo-dump-spec implementation, which |
| // has specific preferences about whether/where to use a previously |
| // emitted name. |
| DWARFDie fwd(forwardedType(die)); |
| if (!isAggregate(fwd) && !isPtrToFunctionType(fwd)) |
| return false; |
| |
| return true; |
| } |
| |
| bool GoDumpHelper::generateType(const DWARFDie &die, TypeNameDisp disp) |
| { |
| // Invalid? |
| if (isInvalidType(die)) |
| return false; |
| |
| // Record size for posterity. |
| auto byteSize = dwarf::toUnsigned(die.find(dwarf::DW_AT_byte_size)); |
| if (byteSize) |
| setTypeSize(die, *byteSize); |
| |
| // Use a reference to a previously emitted type name if appropriate. |
| if (useTypeName(die, disp)) { |
| const char *name = dieName(die); |
| assert(name); |
| buf() << "_" << name; |
| return true; |
| } |
| |
| // Reset top-level DIE offset. |
| curDieOffset_ = invalidOffset; |
| |
| // Look to see what we're dealing with. |
| bool rval = true; |
| dwarf::Tag tag = die.getTag(); |
| switch(tag) { |
| case dwarf::DW_TAG_base_type: { |
| rval = generateBaseType(die); |
| break; |
| } |
| case dwarf::DW_TAG_pointer_type: { |
| // NB: for "void *" we may see no target type. |
| DWARFDie toDie = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| if (! toDie.isValid()) { |
| // Treat this case as "*byte" |
| buf() << "*byte"; |
| } else { |
| if (toDie.getTag() != dwarf::DW_TAG_subroutine_type) |
| buf() << "*"; |
| bool toDieValid = generateType(toDie); |
| if (!toDieValid) { |
| buf() << "byte"; |
| } |
| } |
| setTypeSize(die, ptrSize_); |
| setTypeAlign(die, ptrSize_); |
| break; |
| } |
| case dwarf::DW_TAG_typedef: { |
| DWARFDie tgtDie = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| // Interestingly, for a construct like: |
| // |
| // typedef void MyOpaque; |
| // typedef MyOpaque *MyOpaquePointer; |
| // |
| // the DIE corresponding to "MyOpaque" will be a typedef with no |
| // type reference attribute; handle this case accordingly. |
| if (!tgtDie.isValid()) { |
| rval = false; |
| } else { |
| rval = generateType(tgtDie, TN_AvoidName); |
| setTypeAlign(die, typeAlign(tgtDie)); |
| setTypeSize(die, typeSize(tgtDie)); |
| } |
| break; |
| } |
| case dwarf::DW_TAG_structure_type: { |
| visited_.insert(die.getOffset()); |
| rval = generateStructType(die); |
| break; |
| } |
| case dwarf::DW_TAG_union_type: { |
| rval = generateUnionType(die); |
| break; |
| } |
| case dwarf::DW_TAG_enumeration_type: { |
| rval = generateEnumType(die); |
| break; |
| } |
| case dwarf::DW_TAG_subroutine_type: { |
| rval = generateFcnType(die); |
| break; |
| } |
| case dwarf::DW_TAG_array_type: { |
| rval = generateArrayType(die); |
| break; |
| } |
| case dwarf::DW_TAG_const_type: |
| case dwarf::DW_TAG_restrict_type: |
| case dwarf::DW_TAG_volatile_type: { |
| // Throw away these qualifiers. |
| DWARFDie qtyp = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(qtyp.isValid()); |
| rval = generateType(qtyp); |
| setTypeAlign(die, typeAlign(qtyp)); |
| setTypeSize(die, typeSize(qtyp)); |
| break; |
| } |
| default: |
| assert(false); |
| } |
| |
| if (!rval) |
| invalidTypes_.insert(die.getOffset()); |
| |
| return rval; |
| } |
| |
| std::pair<bool, std::string> |
| GoDumpHelper::generateTypeToString(const DWARFDie &die) |
| { |
| auto pauseState = pauseBuf(); |
| bool ok = generateType(die); |
| std::string str(buf().str()); |
| restoreBuf(pauseState.first, pauseState.second); |
| return std::make_pair(ok, str); |
| } |
| |
| void GoDumpHelper::emitVariable(const DWARFDie &die) |
| { |
| initBuf(); |
| |
| DWARFDie typ = die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type); |
| assert(typ.isValid()); |
| bool ok = generateType(typ, TN_PreferName); |
| |
| // In cases where there is a clash between a named type and a variable, |
| // we choose the type and skip the variable. |
| const char *name = dieName(die); |
| if (emittedTypeNames_.find(name) != emittedTypeNames_.end()) |
| ok = false; |
| |
| if (! ok) |
| os() << "// "; |
| assert(name); |
| os() << "var _" << name << " " << buf().str() << "\n"; |
| } |
| |
| void GoDumpHelper::emit() |
| { |
| // Tell the visit routines below to emit Go code. |
| emit_ = true; |
| |
| for (auto idx : queue_) { |
| DWARFDie die = cu_->getDIEAtIndex(idx); |
| if (isType(die.getTag())) |
| visitType(die); |
| else if (die.getTag() == dwarf::DW_TAG_variable) |
| emitVariable(die); |
| } |
| |
| // Emit macros once we've finished with types. |
| emitMacros(os(), emittedTypeNames_); |
| } |
| |
| static void error(StringRef Prefix, std::error_code EC) { |
| if (!EC) |
| return; |
| errs() << Prefix << ": " << EC.message() << "\n"; |
| exit(1); |
| } |
| |
| static int visitMacrosFile(const std::string &infile, |
| GoDumpHelper &state, |
| raw_ostream &os) |
| { |
| std::string line; |
| std::ifstream macfile(infile); |
| unsigned lno = 0; |
| if (macfile.is_open()) { |
| while (std::getline(macfile, line)) |
| { |
| lno += 1; |
| state.visitMacroLine(line, lno); |
| } |
| macfile.close(); |
| state.postProcessMacros(); |
| } else { |
| errs() << "error: unable to open macro file " << infile << "\n"; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| struct ObjectState { |
| std::unique_ptr<MemoryBuffer> mbuf_; |
| std::unique_ptr<Binary> binary_; |
| std::unique_ptr<DWARFContext> dwctxt_; |
| }; |
| |
| static int visitObjectFile(const std::string &infile, |
| GoDumpHelper &state, |
| ObjectState &ostate, |
| raw_ostream &os) |
| { |
| ErrorOr<std::unique_ptr<MemoryBuffer>> buffOrErr = |
| MemoryBuffer::getFile(infile); |
| error(infile, buffOrErr.getError()); |
| std::unique_ptr<MemoryBuffer> buffer = std::move(buffOrErr.get()); |
| ostate.mbuf_.reset(buffer.release()); |
| |
| Expected<std::unique_ptr<Binary>> binOrErr = |
| object::createBinary(*ostate.mbuf_); |
| error(infile, errorToErrorCode(binOrErr.takeError())); |
| std::unique_ptr<Binary> binary = std::move(binOrErr.get()); |
| ostate.binary_.reset(binary.release()); |
| |
| // NB: no MachO support at the moment |
| auto *obj = dyn_cast<ObjectFile>(ostate.binary_.get()); |
| if (obj == nullptr) { |
| errs() << "error: problems opening object file " << infile << "\n"; |
| return 1; |
| } |
| ostate.dwctxt_.reset(DWARFContext::create(*obj).release()); |
| |
| // Expect to see exactly one DWARF CU. |
| if (ostate.dwctxt_->getNumCompileUnits() < 1) { |
| errs() << "error: no DWARF compilation units found in " << infile << "\n"; |
| return 1; |
| } else if (ostate.dwctxt_->getNumCompileUnits() > 1) { |
| errs() << "error: unexpected multiple DWARF compilation " |
| << "units found in " << infile << "\n"; |
| return 1; |
| } |
| |
| DWARFCompileUnit *cu = |
| cast<DWARFCompileUnit>(ostate.dwctxt_->getUnitAtIndex(0)); |
| state.readDwarf(cu); |
| |
| return 0; |
| } |
| |
| int main(int argc, char **argv) { |
| |
| // Print a stack trace if we signal out. |
| sys::PrintStackTraceOnErrorSignal(argv[0]); |
| PrettyStackTraceProgram X(argc, argv); |
| llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. |
| |
| llvm::InitializeAllTargetInfos(); |
| llvm::InitializeAllTargetMCs(); |
| |
| cl::ParseCommandLineOptions( |
| argc, argv, |
| "Emit Go translation for type/const/macro information derived " |
| "from compilation of a C file.\n"); |
| |
| if (InputObjectFile.empty()) { |
| errs() << "error: supply input object file using -object option.\n"; |
| return 1; |
| } |
| |
| std::unique_ptr<ToolOutputFile> OutputFile; |
| if (!OutputFilename.empty()) { |
| std::error_code EC; |
| OutputFile = llvm::make_unique<ToolOutputFile>(OutputFilename, EC, |
| sys::fs::F_None); |
| // Don't remove output file if we exit with an error. |
| OutputFile->keep(); |
| error("Unable to open output file" + OutputFilename, EC); |
| } |
| |
| raw_ostream &OS = OutputFile ? OutputFile->os() : outs(); |
| GoDumpHelper state(OS); |
| ObjectState ostate; |
| |
| int rc = 0; |
| if (! InputObjectFile.empty()) { |
| rc |= visitObjectFile(InputObjectFile, state, ostate, OS); |
| } |
| if (! InputMacrosFile.empty()) { |
| rc |= visitMacrosFile(InputMacrosFile, state, OS); |
| } |
| state.emit(); |
| |
| return rc; |
| } |