Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,206 @@
//===-- BinaryHolder.cpp --------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This program is a utility that aims to be a dropin replacement for
// Darwin's dsymutil.
//
//===----------------------------------------------------------------------===//
#include "BinaryHolder.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace dsymutil {
static std::vector<MemoryBufferRef>
getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
object::MachOUniversalBinary &Fat) {
std::vector<MemoryBufferRef> Buffers;
StringRef FatData = Fat.getData();
for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
++It) {
StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
Buffers.emplace_back(ObjData, Filename);
}
return Buffers;
}
void BinaryHolder::changeBackingMemoryBuffer(
std::unique_ptr<MemoryBuffer> &&Buf) {
CurrentArchives.clear();
CurrentObjectFiles.clear();
CurrentFatBinary.reset();
CurrentMemoryBuffer = std::move(Buf);
}
ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetMemoryBuffersForFile(
StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
if (Verbose)
outs() << "trying to open '" << Filename << "'\n";
// Try that first as it doesn't involve any filesystem access.
if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp))
return *ErrOrArchiveMembers;
// If the name ends with a closing paren, there is a huge chance
// it is an archive member specification.
if (Filename.endswith(")"))
if (auto ErrOrArchiveMembers =
MapArchiveAndGetMemberBuffers(Filename, Timestamp))
return *ErrOrArchiveMembers;
// Otherwise, just try opening a standard file. If this is an
// archive member specifiaction and any of the above didn't handle it
// (either because the archive is not there anymore, or because the
// archive doesn't contain the requested member), this will still
// provide a sensible error message.
auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename);
if (auto Err = ErrOrFile.getError())
return Err;
changeBackingMemoryBuffer(std::move(*ErrOrFile));
if (Verbose)
outs() << "\tloaded file.\n";
auto ErrOrFat = object::MachOUniversalBinary::create(
CurrentMemoryBuffer->getMemBufferRef());
if (!ErrOrFat) {
consumeError(ErrOrFat.takeError());
// Not a fat binary must be a standard one. Return a one element vector.
return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()};
}
CurrentFatBinary = std::move(*ErrOrFat);
CurrentFatBinaryName = Filename;
return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer,
*CurrentFatBinary);
}
ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetArchiveMemberBuffers(
StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
if (CurrentArchives.empty())
return make_error_code(errc::no_such_file_or_directory);
StringRef CurArchiveName = CurrentArchives.front()->getFileName();
if (!Filename.startswith(Twine(CurArchiveName, "(").str()))
return make_error_code(errc::no_such_file_or_directory);
// Remove the archive name and the parens around the archive member name.
Filename = Filename.substr(CurArchiveName.size() + 1).drop_back();
std::vector<MemoryBufferRef> Buffers;
Buffers.reserve(CurrentArchives.size());
for (const auto &CurrentArchive : CurrentArchives) {
Error Err = Error::success();
for (auto Child : CurrentArchive->children(Err)) {
if (auto NameOrErr = Child.getName()) {
if (*NameOrErr == Filename) {
auto ModTimeOrErr = Child.getLastModified();
if (!ModTimeOrErr)
return errorToErrorCode(ModTimeOrErr.takeError());
if (Timestamp != sys::TimePoint<>() &&
Timestamp != ModTimeOrErr.get()) {
if (Verbose)
outs() << "\tmember had timestamp mismatch.\n";
continue;
}
if (Verbose)
outs() << "\tfound member in current archive.\n";
auto ErrOrMem = Child.getMemoryBufferRef();
if (!ErrOrMem)
return errorToErrorCode(ErrOrMem.takeError());
Buffers.push_back(*ErrOrMem);
}
}
}
if (Err)
return errorToErrorCode(std::move(Err));
}
if (Buffers.empty())
return make_error_code(errc::no_such_file_or_directory);
return Buffers;
}
ErrorOr<std::vector<MemoryBufferRef>>
BinaryHolder::MapArchiveAndGetMemberBuffers(
StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
if (auto Err = ErrOrBuff.getError())
return Err;
if (Verbose)
outs() << "\topened new archive '" << ArchiveFilename << "'\n";
changeBackingMemoryBuffer(std::move(*ErrOrBuff));
std::vector<MemoryBufferRef> ArchiveBuffers;
auto ErrOrFat = object::MachOUniversalBinary::create(
CurrentMemoryBuffer->getMemBufferRef());
if (!ErrOrFat) {
consumeError(ErrOrFat.takeError());
// Not a fat binary must be a standard one.
ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
} else {
CurrentFatBinary = std::move(*ErrOrFat);
CurrentFatBinaryName = ArchiveFilename;
ArchiveBuffers = getMachOFatMemoryBuffers(
CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary);
}
for (auto MemRef : ArchiveBuffers) {
auto ErrOrArchive = object::Archive::create(MemRef);
if (!ErrOrArchive)
return errorToErrorCode(ErrOrArchive.takeError());
CurrentArchives.push_back(std::move(*ErrOrArchive));
}
return GetArchiveMemberBuffers(Filename, Timestamp);
}
ErrorOr<const object::ObjectFile &>
BinaryHolder::getObjfileForArch(const Triple &T) {
for (const auto &Obj : CurrentObjectFiles) {
if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
if (MachO->getArchTriple().str() == T.str())
return *MachO;
} else if (Obj->getArch() == T.getArch())
return *Obj;
}
return make_error_code(object::object_error::arch_not_found);
}
ErrorOr<std::vector<const object::ObjectFile *>>
BinaryHolder::GetObjectFiles(StringRef Filename,
sys::TimePoint<std::chrono::seconds> Timestamp) {
auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp);
if (auto Err = ErrOrMemBufferRefs.getError())
return Err;
std::vector<const object::ObjectFile *> Objects;
Objects.reserve(ErrOrMemBufferRefs->size());
CurrentObjectFiles.clear();
for (auto MemBuf : *ErrOrMemBufferRefs) {
auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf);
if (!ErrOrObjectFile)
return errorToErrorCode(ErrOrObjectFile.takeError());
Objects.push_back(ErrOrObjectFile->get());
CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
}
return std::move(Objects);
}
}
}

View File

@@ -0,0 +1,140 @@
//===-- BinaryHolder.h - Utility class for accessing binaries -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This program is a utility that aims to be a dropin replacement for
// Darwin's dsymutil.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
#define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
#include "llvm/ADT/Triple.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h"
namespace llvm {
namespace dsymutil {
/// \brief The BinaryHolder class is responsible for creating and
/// owning ObjectFile objects and their underlying MemoryBuffer. This
/// is different from a simple OwningBinary in that it handles
/// accessing to archive members.
///
/// As an optimization, this class will reuse an already mapped and
/// parsed Archive object if 2 successive requests target the same
/// archive file (Which is always the case in debug maps).
/// Currently it only owns one memory buffer at any given time,
/// meaning that a mapping request will invalidate the previous memory
/// mapping.
class BinaryHolder {
std::vector<std::unique_ptr<object::Archive>> CurrentArchives;
std::unique_ptr<MemoryBuffer> CurrentMemoryBuffer;
std::vector<std::unique_ptr<object::ObjectFile>> CurrentObjectFiles;
std::unique_ptr<object::MachOUniversalBinary> CurrentFatBinary;
std::string CurrentFatBinaryName;
bool Verbose;
/// Get the MemoryBufferRefs for the file specification in \p
/// Filename from the current archive. Multiple buffers are returned
/// when there are multiple architectures available for the
/// requested file.
///
/// This function performs no system calls, it just looks up a
/// potential match for the given \p Filename in the currently
/// mapped archive if there is one.
ErrorOr<std::vector<MemoryBufferRef>>
GetArchiveMemberBuffers(StringRef Filename,
sys::TimePoint<std::chrono::seconds> Timestamp);
/// Interpret Filename as an archive member specification map the
/// corresponding archive to memory and return the MemoryBufferRefs
/// corresponding to the described member. Multiple buffers are
/// returned when there are multiple architectures available for the
/// requested file.
ErrorOr<std::vector<MemoryBufferRef>>
MapArchiveAndGetMemberBuffers(StringRef Filename,
sys::TimePoint<std::chrono::seconds> Timestamp);
/// Return the MemoryBufferRef that holds the memory mapping for the
/// given \p Filename. This function will try to parse archive
/// member specifications of the form /path/to/archive.a(member.o).
///
/// The returned MemoryBufferRefs points to a buffer owned by this
/// object. The buffer is valid until the next call to
/// GetMemoryBufferForFile() on this object.
/// Multiple buffers are returned when there are multiple
/// architectures available for the requested file.
ErrorOr<std::vector<MemoryBufferRef>>
GetMemoryBuffersForFile(StringRef Filename,
sys::TimePoint<std::chrono::seconds> Timestamp);
void changeBackingMemoryBuffer(std::unique_ptr<MemoryBuffer> &&MemBuf);
ErrorOr<const object::ObjectFile &> getObjfileForArch(const Triple &T);
public:
BinaryHolder(bool Verbose) : Verbose(Verbose) {}
/// Get the ObjectFiles designated by the \p Filename. This
/// might be an archive member specification of the form
/// /path/to/archive.a(member.o).
///
/// Calling this function invalidates the previous mapping owned by
/// the BinaryHolder. Multiple buffers are returned when there are
/// multiple architectures available for the requested file.
ErrorOr<std::vector<const object::ObjectFile *>>
GetObjectFiles(StringRef Filename,
sys::TimePoint<std::chrono::seconds> Timestamp =
sys::TimePoint<std::chrono::seconds>());
/// Wraps GetObjectFiles() to return a derived ObjectFile type.
template <typename ObjectFileType>
ErrorOr<std::vector<const ObjectFileType *>>
GetFilesAs(StringRef Filename,
sys::TimePoint<std::chrono::seconds> Timestamp =
sys::TimePoint<std::chrono::seconds>()) {
auto ErrOrObjFile = GetObjectFiles(Filename, Timestamp);
if (auto Err = ErrOrObjFile.getError())
return Err;
std::vector<const ObjectFileType *> Objects;
Objects.reserve((*ErrOrObjFile).size());
for (const auto &Obj : *ErrOrObjFile) {
const auto *Derived = dyn_cast<ObjectFileType>(Obj);
if (!Derived)
return make_error_code(object::object_error::invalid_file_type);
Objects.push_back(Derived);
}
return std::move(Objects);
}
/// Access the currently owned ObjectFile with architecture \p T. As
/// successfull call to GetObjectFiles() or GetFilesAs() must have
/// been performed before calling this.
ErrorOr<const object::ObjectFile &> Get(const Triple &T) {
return getObjfileForArch(T);
}
/// Access to a derived version of the currently owned
/// ObjectFile. The conversion must be known to be valid.
template <typename ObjectFileType>
ErrorOr<const ObjectFileType &> GetAs(const Triple &T) {
auto ErrOrObj = Get(T);
if (auto Err = ErrOrObj.getError())
return Err;
return cast<ObjectFileType>(*ErrOrObj);
}
};
}
}
#endif

View File

@@ -0,0 +1,187 @@
//===- tools/dsymutil/CFBundle.cpp - CFBundle helper ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "CFBundle.h"
#ifdef __APPLE__
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <CoreFoundation/CoreFoundation.h>
#include <assert.h>
#include <glob.h>
#include <memory>
#endif
namespace llvm {
namespace dsymutil {
#ifdef __APPLE__
/// Deleter that calls CFRelease rather than deleting the pointer.
template <typename T> struct CFDeleter {
void operator()(T *P) {
if (P)
::CFRelease(P);
}
};
/// This helper owns any CoreFoundation pointer and will call CFRelease() on
/// any valid pointer it owns unless that pointer is explicitly released using
/// the release() member function.
template <typename T>
using CFReleaser =
std::unique_ptr<typename std::remove_pointer<T>::type,
CFDeleter<typename std::remove_pointer<T>::type>>;
/// RAII wrapper around CFBundleRef.
class CFString : public CFReleaser<CFStringRef> {
public:
CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {}
const char *UTF8(std::string &Str) const {
return CFString::UTF8(get(), Str);
}
CFIndex GetLength() const {
if (CFStringRef Str = get())
return CFStringGetLength(Str);
return 0;
}
static const char *UTF8(CFStringRef CFStr, std::string &Str);
};
/// Static function that puts a copy of the UTF-8 contents of CFStringRef into
/// std::string and returns the C string pointer that is contained in the
/// std::string when successful, nullptr otherwise.
///
/// This allows the std::string parameter to own the extracted string, and also
/// allows that string to be returned as a C string pointer that can be used.
const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) {
if (!CFStr)
return nullptr;
const CFStringEncoding Encoding = kCFStringEncodingUTF8;
CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr);
MaxUTF8StrLength =
CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding);
if (MaxUTF8StrLength > 0) {
Str.resize(MaxUTF8StrLength);
if (!Str.empty() &&
CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) {
Str.resize(strlen(Str.c_str()));
return Str.c_str();
}
}
return nullptr;
}
/// RAII wrapper around CFBundleRef.
class CFBundle : public CFReleaser<CFBundleRef> {
public:
CFBundle(StringRef Path) : CFReleaser<CFBundleRef>() { SetFromPath(Path); }
CFBundle(CFURLRef Url)
: CFReleaser<CFBundleRef>(Url ? ::CFBundleCreate(nullptr, Url)
: nullptr) {}
/// Return the bundle identifier.
CFStringRef GetIdentifier() const {
if (CFBundleRef bundle = get())
return ::CFBundleGetIdentifier(bundle);
return nullptr;
}
/// Return value for key.
CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const {
if (CFBundleRef bundle = get())
return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
return nullptr;
}
private:
/// Helper to initialize this instance with a new bundle created from the
/// given path. This function will recursively remove components from the
/// path in its search for the nearest Info.plist.
void SetFromPath(StringRef Path);
};
void CFBundle::SetFromPath(StringRef Path) {
// Start from an empty/invalid CFBundle.
reset();
if (Path.empty() || !sys::fs::exists(Path))
return;
SmallString<256> RealPath;
sys::fs::real_path(Path, RealPath, /*expand_tilde*/ true);
do {
// Create a CFURL from the current path and use it to create a CFBundle.
CFReleaser<CFURLRef> BundleURL(::CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault, (const UInt8 *)RealPath.data(), RealPath.size(),
false));
reset(::CFBundleCreate(kCFAllocatorDefault, BundleURL.get()));
// If we have a valid bundle and find its identifier we are done.
if (get() != nullptr) {
if (GetIdentifier() != nullptr)
return;
reset();
}
// Remove the last component of the path and try again until there's
// nothing left but the root.
sys::path::remove_filename(RealPath);
} while (RealPath != sys::path::root_name(RealPath));
}
#endif
/// On Darwin, try and find the original executable's Info.plist to extract
/// information about the bundle. Return default values on other platforms.
CFBundleInfo getBundleInfo(StringRef ExePath) {
CFBundleInfo BundleInfo;
#ifdef __APPLE__
auto PrintError = [&](CFTypeID TypeID) {
CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID));
std::string TypeIDStr;
errs() << "The Info.plist key \"CFBundleShortVersionString\" is"
<< "a " << TypeIDCFStr.UTF8(TypeIDStr)
<< ", but it should be a string in: " << ExePath << ".\n";
};
CFBundle Bundle(ExePath);
if (CFStringRef BundleID = Bundle.GetIdentifier()) {
CFString::UTF8(BundleID, BundleInfo.IDStr);
if (CFTypeRef TypeRef =
Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) {
CFTypeID TypeID = ::CFGetTypeID(TypeRef);
if (TypeID == ::CFStringGetTypeID())
CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr);
else
PrintError(TypeID);
}
if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey(
CFSTR("CFBundleShortVersionString"))) {
CFTypeID TypeID = ::CFGetTypeID(TypeRef);
if (TypeID == ::CFStringGetTypeID())
CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr);
else
PrintError(TypeID);
}
}
#endif
return BundleInfo;
}
} // end namespace dsymutil
} // end namespace llvm

View File

@@ -0,0 +1,26 @@
//===- tools/dsymutil/CFBundle.h - CFBundle helper --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include <string>
namespace llvm {
namespace dsymutil {
struct CFBundleInfo {
std::string VersionStr = "1";
std::string ShortVersionStr = "1.0";
std::string IDStr;
bool OmitShortVersion() const { return ShortVersionStr.empty(); }
};
CFBundleInfo getBundleInfo(llvm::StringRef ExePath);
} // end namespace dsymutil
} // end namespace llvm

View File

@@ -0,0 +1,26 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
AsmPrinter
DebugInfoDWARF
MC
Object
Support
Target
)
add_llvm_tool(llvm-dsymutil
dsymutil.cpp
BinaryHolder.cpp
CFBundle.cpp
DebugMap.cpp
DwarfLinker.cpp
MachODebugMapParser.cpp
MachOUtils.cpp
DEPENDS
intrinsics_gen
)
IF(APPLE)
target_link_libraries(llvm-dsymutil PRIVATE "-framework CoreFoundation")
ENDIF(APPLE)

View File

@@ -0,0 +1,281 @@
//===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "DebugMap.h"
#include "BinaryHolder.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace llvm {
namespace dsymutil {
using namespace llvm::object;
DebugMapObject::DebugMapObject(StringRef ObjectFilename,
sys::TimePoint<std::chrono::seconds> Timestamp,
uint8_t Type)
: Filename(ObjectFilename), Timestamp(Timestamp), Type(Type) {}
bool DebugMapObject::addSymbol(StringRef Name, Optional<uint64_t> ObjectAddress,
uint64_t LinkedAddress, uint32_t Size) {
auto InsertResult = Symbols.insert(
std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size)));
if (ObjectAddress && InsertResult.second)
AddressToMapping[*ObjectAddress] = &*InsertResult.first;
return InsertResult.second;
}
void DebugMapObject::print(raw_ostream &OS) const {
OS << getObjectFilename() << ":\n";
// Sort the symbols in alphabetical order, like llvm-nm (and to get
// deterministic output for testing).
using Entry = std::pair<StringRef, SymbolMapping>;
std::vector<Entry> Entries;
Entries.reserve(Symbols.getNumItems());
for (const auto &Sym : make_range(Symbols.begin(), Symbols.end()))
Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue()));
std::sort(
Entries.begin(), Entries.end(),
[](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; });
for (const auto &Sym : Entries) {
if (Sym.second.ObjectAddress)
OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress));
else
OS << "\t????????????????";
OS << format(" => %016" PRIx64 "+0x%x\t%s\n",
uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size),
Sym.first.data());
}
OS << '\n';
}
#ifndef NDEBUG
void DebugMapObject::dump() const { print(errs()); }
#endif
DebugMapObject &
DebugMap::addDebugMapObject(StringRef ObjectFilePath,
sys::TimePoint<std::chrono::seconds> Timestamp,
uint8_t Type) {
Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp, Type));
return *Objects.back();
}
const DebugMapObject::DebugMapEntry *
DebugMapObject::lookupSymbol(StringRef SymbolName) const {
StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName);
if (Sym == Symbols.end())
return nullptr;
return &*Sym;
}
const DebugMapObject::DebugMapEntry *
DebugMapObject::lookupObjectAddress(uint64_t Address) const {
auto Mapping = AddressToMapping.find(Address);
if (Mapping == AddressToMapping.end())
return nullptr;
return Mapping->getSecond();
}
void DebugMap::print(raw_ostream &OS) const {
yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0);
yout << const_cast<DebugMap &>(*this);
}
#ifndef NDEBUG
void DebugMap::dump() const { print(errs()); }
#endif
namespace {
struct YAMLContext {
StringRef PrependPath;
Triple BinaryTriple;
};
} // end anonymous namespace
ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
bool Verbose) {
auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);
if (auto Err = ErrOrFile.getError())
return Err;
YAMLContext Ctxt;
Ctxt.PrependPath = PrependPath;
std::unique_ptr<DebugMap> Res;
yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt);
yin >> Res;
if (auto EC = yin.error())
return EC;
std::vector<std::unique_ptr<DebugMap>> Result;
Result.push_back(std::move(Res));
return std::move(Result);
}
} // end namespace dsymutil
namespace yaml {
// Normalize/Denormalize between YAML and a DebugMapObject.
struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
YamlDMO(IO &io) { Timestamp = 0; }
YamlDMO(IO &io, dsymutil::DebugMapObject &Obj);
dsymutil::DebugMapObject denormalize(IO &IO);
std::string Filename;
int64_t Timestamp;
std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;
};
void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>::
mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) {
io.mapRequired("sym", s.first);
io.mapOptional("objAddr", s.second.ObjectAddress);
io.mapRequired("binAddr", s.second.BinaryAddress);
io.mapOptional("size", s.second.Size);
}
void MappingTraits<dsymutil::DebugMapObject>::mapping(
IO &io, dsymutil::DebugMapObject &DMO) {
MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO);
io.mapRequired("filename", Norm->Filename);
io.mapOptional("timestamp", Norm->Timestamp);
io.mapRequired("symbols", Norm->Entries);
}
void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) {
out << val.str();
}
StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) {
value = Triple(scalar);
return StringRef();
}
size_t
SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size(
IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) {
return seq.size();
}
dsymutil::DebugMapObject &
SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element(
IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq,
size_t index) {
if (index >= seq.size()) {
seq.resize(index + 1);
seq[index].reset(new dsymutil::DebugMapObject);
}
return *seq[index];
}
void MappingTraits<dsymutil::DebugMap>::mapping(IO &io,
dsymutil::DebugMap &DM) {
io.mapRequired("triple", DM.BinaryTriple);
io.mapOptional("binary-path", DM.BinaryPath);
if (void *Ctxt = io.getContext())
reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple;
io.mapOptional("objects", DM.Objects);
}
void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(
IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) {
if (!DM)
DM.reset(new DebugMap());
io.mapRequired("triple", DM->BinaryTriple);
io.mapOptional("binary-path", DM->BinaryPath);
if (void *Ctxt = io.getContext())
reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple;
io.mapOptional("objects", DM->Objects);
}
MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO(
IO &io, dsymutil::DebugMapObject &Obj) {
Filename = Obj.Filename;
Timestamp = sys::toTimeT(Obj.getTimestamp());
Entries.reserve(Obj.Symbols.size());
for (auto &Entry : Obj.Symbols)
Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue()));
}
dsymutil::DebugMapObject
MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
BinaryHolder BinHolder(/* Verbose =*/false);
const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext());
SmallString<80> Path(Ctxt.PrependPath);
StringMap<uint64_t> SymbolAddresses;
sys::path::append(Path, Filename);
auto ErrOrObjectFiles = BinHolder.GetObjectFiles(Path);
if (auto EC = ErrOrObjectFiles.getError()) {
errs() << "warning: Unable to open " << Path << " " << EC.message() << '\n';
} else if (auto ErrOrObjectFile = BinHolder.Get(Ctxt.BinaryTriple)) {
// Rewrite the object file symbol addresses in the debug map. The
// YAML input is mainly used to test llvm-dsymutil without
// requiring binaries checked-in. If we generate the object files
// during the test, we can't hardcode the symbols addresses, so
// look them up here and rewrite them.
for (const auto &Sym : ErrOrObjectFile->symbols()) {
uint64_t Address = Sym.getValue();
Expected<StringRef> Name = Sym.getName();
if (!Name ||
(Sym.getFlags() & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) {
// TODO: Actually report errors helpfully.
if (!Name)
consumeError(Name.takeError());
continue;
}
SymbolAddresses[*Name] = Address;
}
}
dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO);
for (auto &Entry : Entries) {
auto &Mapping = Entry.second;
Optional<uint64_t> ObjAddress;
if (Mapping.ObjectAddress)
ObjAddress = *Mapping.ObjectAddress;
auto AddressIt = SymbolAddresses.find(Entry.first);
if (AddressIt != SymbolAddresses.end())
ObjAddress = AddressIt->getValue();
Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size);
}
return Res;
}
} // end namespace yaml
} // end namespace llvm

View File

@@ -0,0 +1,258 @@
//=- tools/dsymutil/DebugMap.h - Generic debug map representation -*- C++ -*-=//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
/// \file
///
/// This file contains the class declaration of the DebugMap
/// entity. A DebugMap lists all the object files linked together to
/// produce an executable along with the linked address of all the
/// atoms used in these object files.
/// The DebugMap is an input to the DwarfLinker class that will
/// extract the Dwarf debug information from the referenced object
/// files and link their usefull debug info together.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
#define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/YAMLTraits.h"
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace llvm {
class raw_ostream;
namespace dsymutil {
class DebugMapObject;
/// \brief The DebugMap object stores the list of object files to
/// query for debug information along with the mapping between the
/// symbols' addresses in the object file to their linked address in
/// the linked binary.
///
/// A DebugMap producer could look like this:
/// DebugMap *DM = new DebugMap();
/// for (const auto &Obj: LinkedObjects) {
/// DebugMapObject &DMO = DM->addDebugMapObject(Obj.getPath());
/// for (const auto &Sym: Obj.getLinkedSymbols())
/// DMO.addSymbol(Sym.getName(), Sym.getObjectFileAddress(),
/// Sym.getBinaryAddress());
/// }
///
/// A DebugMap consumer can then use the map to link the debug
/// information. For example something along the lines of:
/// for (const auto &DMO: DM->objects()) {
/// auto Obj = createBinary(DMO.getObjectFilename());
/// for (auto &DIE: Obj.getDwarfDIEs()) {
/// if (SymbolMapping *Sym = DMO.lookup(DIE.getName()))
/// DIE.relocate(Sym->ObjectAddress, Sym->BinaryAddress);
/// else
/// DIE.discardSubtree();
/// }
/// }
class DebugMap {
Triple BinaryTriple;
std::string BinaryPath;
using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>;
ObjectContainer Objects;
/// For YAML IO support.
///@{
friend yaml::MappingTraits<std::unique_ptr<DebugMap>>;
friend yaml::MappingTraits<DebugMap>;
DebugMap() = default;
///@}
public:
DebugMap(const Triple &BinaryTriple, StringRef BinaryPath)
: BinaryTriple(BinaryTriple), BinaryPath(BinaryPath) {}
using const_iterator = ObjectContainer::const_iterator;
iterator_range<const_iterator> objects() const {
return make_range(begin(), end());
}
const_iterator begin() const { return Objects.begin(); }
const_iterator end() const { return Objects.end(); }
/// This function adds an DebugMapObject to the list owned by this
/// debug map.
DebugMapObject &
addDebugMapObject(StringRef ObjectFilePath,
sys::TimePoint<std::chrono::seconds> Timestamp,
uint8_t Type);
const Triple &getTriple() const { return BinaryTriple; }
StringRef getBinaryPath() const { return BinaryPath; }
void print(raw_ostream &OS) const;
#ifndef NDEBUG
void dump() const;
#endif
/// Read a debug map for \a InputFile.
static ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose);
};
/// \brief The DebugMapObject represents one object file described by
/// the DebugMap. It contains a list of mappings between addresses in
/// the object file and in the linked binary for all the linked atoms
/// in this object file.
class DebugMapObject {
public:
struct SymbolMapping {
Optional<yaml::Hex64> ObjectAddress;
yaml::Hex64 BinaryAddress;
yaml::Hex32 Size;
SymbolMapping(Optional<uint64_t> ObjectAddr, uint64_t BinaryAddress,
uint32_t Size)
: BinaryAddress(BinaryAddress), Size(Size) {
if (ObjectAddr)
ObjectAddress = *ObjectAddr;
}
/// For YAML IO support
SymbolMapping() = default;
};
using YAMLSymbolMapping = std::pair<std::string, SymbolMapping>;
using DebugMapEntry = StringMapEntry<SymbolMapping>;
/// \brief Adds a symbol mapping to this DebugMapObject.
/// \returns false if the symbol was already registered. The request
/// is discarded in this case.
bool addSymbol(StringRef SymName, Optional<uint64_t> ObjectAddress,
uint64_t LinkedAddress, uint32_t Size);
/// \brief Lookup a symbol mapping.
/// \returns null if the symbol isn't found.
const DebugMapEntry *lookupSymbol(StringRef SymbolName) const;
/// \brief Lookup an objectfile address.
/// \returns null if the address isn't found.
const DebugMapEntry *lookupObjectAddress(uint64_t Address) const;
StringRef getObjectFilename() const { return Filename; }
sys::TimePoint<std::chrono::seconds> getTimestamp() const {
return Timestamp;
}
uint8_t getType() const { return Type; }
iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const {
return make_range(Symbols.begin(), Symbols.end());
}
void print(raw_ostream &OS) const;
#ifndef NDEBUG
void dump() const;
#endif
private:
friend class DebugMap;
/// DebugMapObjects can only be constructed by the owning DebugMap.
DebugMapObject(StringRef ObjectFilename,
sys::TimePoint<std::chrono::seconds> Timestamp, uint8_t Type);
std::string Filename;
sys::TimePoint<std::chrono::seconds> Timestamp;
StringMap<SymbolMapping> Symbols;
DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
uint8_t Type;
/// For YAMLIO support.
///@{
friend yaml::MappingTraits<dsymutil::DebugMapObject>;
friend yaml::SequenceTraits<std::vector<std::unique_ptr<DebugMapObject>>>;
DebugMapObject() = default;
public:
DebugMapObject(DebugMapObject &&) = default;
DebugMapObject &operator=(DebugMapObject &&) = default;
///@}
};
} // end namespace dsymutil
} // end namespace llvm
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping)
namespace llvm {
namespace yaml {
using namespace llvm::dsymutil;
template <>
struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> {
static void mapping(IO &io,
std::pair<std::string, DebugMapObject::SymbolMapping> &s);
static const bool flow = true;
};
template <> struct MappingTraits<dsymutil::DebugMapObject> {
struct YamlDMO;
static void mapping(IO &io, dsymutil::DebugMapObject &DMO);
};
template <> struct ScalarTraits<Triple> {
static void output(const Triple &val, void *, raw_ostream &out);
static StringRef input(StringRef scalar, void *, Triple &value);
static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
};
template <>
struct SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> {
static size_t
size(IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq);
static dsymutil::DebugMapObject &
element(IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq,
size_t index);
};
template <> struct MappingTraits<dsymutil::DebugMap> {
static void mapping(IO &io, dsymutil::DebugMap &DM);
};
template <> struct MappingTraits<std::unique_ptr<dsymutil::DebugMap>> {
static void mapping(IO &io, std::unique_ptr<dsymutil::DebugMap> &DM);
};
} // end namespace yaml
} // end namespace llvm
#endif // LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H

View File

@@ -0,0 +1 @@
0f5713b6b4fdefcc1ed2a17bf0662312cdb79281

View File

@@ -0,0 +1,22 @@
;===- ./tools/dsymutil/LLVMBuild.txt ---------------------*- Conf -*--===;
;
; The LLVM Compiler Infrastructure
;
; This file is distributed under the University of Illinois Open Source
; License. See LICENSE.TXT for details.
;
;===------------------------------------------------------------------------===;
;
; This is an LLVMBuild description file for the components in this subdirectory.
;
; For more information on the LLVMBuild system, please see:
;
; http://llvm.org/docs/LLVMBuild.html
;
;===------------------------------------------------------------------------===;
[component_0]
type = Tool
name = llvm-dsymutil
parent = Tools
required_libraries = AsmPrinter DebugInfoDWARF MC Object Support all-targets

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
#include <string>
#include "llvm/ADT/StringRef.h"
namespace llvm {
class MCStreamer;
class raw_fd_ostream;
namespace dsymutil {
class DebugMap;
struct LinkOptions;
namespace MachOUtils {
struct ArchAndFilename {
std::string Arch, Path;
ArchAndFilename(StringRef Arch, StringRef Path) : Arch(Arch), Path(Path) {}
};
bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles,
StringRef OutputFileName, const LinkOptions &,
StringRef SDKPath);
bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
raw_fd_ostream &OutFile);
std::string getArchName(StringRef Arch);
}
}
}
#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H

View File

@@ -0,0 +1,75 @@
//===- NonRelocatableStringpool.h - A simple stringpool --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
#define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include <cstdint>
#include <utility>
namespace llvm {
namespace dsymutil {
/// \brief A string table that doesn't need relocations.
///
/// We are doing a final link, no need for a string table that
/// has relocation entries for every reference to it. This class
/// provides this ablitity by just associating offsets with
/// strings.
class NonRelocatableStringpool {
public:
/// \brief Entries are stored into the StringMap and simply linked
/// together through the second element of this pair in order to
/// keep track of insertion order.
using MapTy =
StringMap<std::pair<uint32_t, StringMapEntryBase *>, BumpPtrAllocator>;
NonRelocatableStringpool() : Sentinel(0), Last(&Sentinel) {
// Legacy dsymutil puts an empty string at the start of the line
// table.
getStringOffset("");
}
/// \brief Get the offset of string \p S in the string table. This
/// can insert a new element or return the offset of a preexisitng
/// one.
uint32_t getStringOffset(StringRef S);
/// \brief Get permanent storage for \p S (but do not necessarily
/// emit \p S in the output section).
/// \returns The StringRef that points to permanent storage to use
/// in place of \p S.
StringRef internString(StringRef S);
// \brief Return the first entry of the string table.
const MapTy::MapEntryTy *getFirstEntry() const {
return getNextEntry(&Sentinel);
}
// \brief Get the entry following \p E in the string table or null
// if \p E was the last entry.
const MapTy::MapEntryTy *getNextEntry(const MapTy::MapEntryTy *E) const {
return static_cast<const MapTy::MapEntryTy *>(E->getValue().second);
}
uint64_t getSize() { return CurrentEndOffset; }
private:
MapTy Strings;
uint32_t CurrentEndOffset = 0;
MapTy::MapEntryTy Sentinel, *Last;
};
} // end namespace dsymutil
} // end namespace llvm
#endif // LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H

View File

@@ -0,0 +1,432 @@
//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This program is a utility that aims to be a dropin replacement for
// Darwin's dsymutil.
//
//===----------------------------------------------------------------------===//
#include "dsymutil.h"
#include "CFBundle.h"
#include "DebugMap.h"
#include "MachOUtils.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/thread.h"
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <system_error>
using namespace llvm;
using namespace llvm::cl;
using namespace llvm::dsymutil;
using namespace object;
static OptionCategory DsymCategory("Specific Options");
static opt<bool> Help("h", desc("Alias for -help"), Hidden);
static opt<bool> Version("v", desc("Alias for -version"), Hidden);
static list<std::string> InputFiles(Positional, OneOrMore,
desc("<input files>"), cat(DsymCategory));
static opt<std::string>
OutputFileOpt("o",
desc("Specify the output file. default: <input file>.dwarf"),
value_desc("filename"), cat(DsymCategory));
static opt<std::string> OsoPrependPath(
"oso-prepend-path",
desc("Specify a directory to prepend to the paths of object files."),
value_desc("path"), cat(DsymCategory));
static opt<bool> DumpStab(
"symtab",
desc("Dumps the symbol table found in executable or object file(s) and\n"
"exits."),
init(false), cat(DsymCategory));
static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
static opt<bool> FlatOut("flat",
desc("Produce a flat dSYM file (not a bundle)."),
init(false), cat(DsymCategory));
static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
static opt<unsigned> NumThreads(
"num-threads",
desc("Specifies the maximum number (n) of simultaneous threads to use\n"
"when linking multiple architectures."),
value_desc("n"), init(0), cat(DsymCategory));
static alias NumThreadsA("j", desc("Alias for --num-threads"),
aliasopt(NumThreads));
static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
cat(DsymCategory));
static opt<bool>
NoOutput("no-output",
desc("Do the link in memory, but do not emit the result file."),
init(false), cat(DsymCategory));
static opt<bool>
NoTimestamp("no-swiftmodule-timestamp",
desc("Don't check timestamp for swiftmodule files."),
init(false), cat(DsymCategory));
static list<std::string> ArchFlags(
"arch",
desc("Link DWARF debug information only for specified CPU architecture\n"
"types. This option can be specified multiple times, once for each\n"
"desired architecture. All CPU architectures will be linked by\n"
"default."),
value_desc("arch"), ZeroOrMore, cat(DsymCategory));
static opt<bool>
NoODR("no-odr",
desc("Do not use ODR (One Definition Rule) for type uniquing."),
init(false), cat(DsymCategory));
static opt<bool> DumpDebugMap(
"dump-debug-map",
desc("Parse and dump the debug map to standard output. Not DWARF link "
"will take place."),
init(false), cat(DsymCategory));
static opt<bool> InputIsYAMLDebugMap(
"y", desc("Treat the input file is a YAML debug map rather than a binary."),
init(false), cat(DsymCategory));
static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."),
cat(DsymCategory));
static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
if (NoOutput)
return true;
// Create plist file to write to.
llvm::SmallString<128> InfoPlist(BundleRoot);
llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
std::error_code EC;
llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
if (EC) {
llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
<< EC.message() << '\n';
return false;
}
CFBundleInfo BI = getBundleInfo(Bin);
if (BI.IDStr.empty()) {
llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
BI.IDStr = llvm::sys::path::stem(BundleID);
else
BI.IDStr = BundleID;
}
// Print out information to the plist file.
PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
<< "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
<< "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
<< "<plist version=\"1.0\">\n"
<< "\t<dict>\n"
<< "\t\t<key>CFBundleDevelopmentRegion</key>\n"
<< "\t\t<string>English</string>\n"
<< "\t\t<key>CFBundleIdentifier</key>\n"
<< "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
<< "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
<< "\t\t<string>6.0</string>\n"
<< "\t\t<key>CFBundlePackageType</key>\n"
<< "\t\t<string>dSYM</string>\n"
<< "\t\t<key>CFBundleSignature</key>\n"
<< "\t\t<string>\?\?\?\?</string>\n";
if (!BI.OmitShortVersion())
PL << "\t\t<key>CFBundleShortVersionString</key>\n"
<< "\t\t<string>" << BI.ShortVersionStr << "</string>\n";
PL << "\t\t<key>CFBundleVersion</key>\n"
<< "\t\t<string>" << BI.VersionStr << "</string>\n"
<< "\t</dict>\n"
<< "</plist>\n";
PL.close();
return true;
}
static bool createBundleDir(llvm::StringRef BundleBase) {
if (NoOutput)
return true;
llvm::SmallString<128> Bundle(BundleBase);
llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
if (std::error_code EC = create_directories(Bundle.str(), true,
llvm::sys::fs::perms::all_all)) {
llvm::errs() << "error: cannot create directory " << Bundle << ": "
<< EC.message() << "\n";
return false;
}
return true;
}
static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
if (OutputFile == "-") {
llvm::errs() << "warning: verification skipped for " << Arch
<< "because writing to stdout.\n";
return true;
}
Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
if (!BinOrErr) {
errs() << OutputFile << ": " << toString(BinOrErr.takeError());
return false;
}
Binary &Binary = *BinOrErr.get().getBinary();
if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
raw_ostream &os = Verbose ? errs() : nulls();
os << "Verifying DWARF for architecture: " << Arch << "\n";
std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
DIDumpOptions DumpOpts;
bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
if (!success)
errs() << "error: verification failed for " << Arch << '\n';
return success;
}
return false;
}
static std::string getOutputFileName(llvm::StringRef InputFile) {
if (FlatOut) {
// If a flat dSYM has been requested, things are pretty simple.
if (OutputFileOpt.empty()) {
if (InputFile == "-")
return "a.out.dwarf";
return (InputFile + ".dwarf").str();
}
return OutputFileOpt;
}
// We need to create/update a dSYM bundle.
// A bundle hierarchy looks like this:
// <bundle name>.dSYM/
// Contents/
// Info.plist
// Resources/
// DWARF/
// <DWARF file(s)>
std::string DwarfFile =
InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
llvm::SmallString<128> BundleDir(OutputFileOpt);
if (BundleDir.empty())
BundleDir = DwarfFile + ".dSYM";
if (!createBundleDir(BundleDir) || !createPlistFile(DwarfFile, BundleDir))
return "";
llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
llvm::sys::path::filename(DwarfFile));
return BundleDir.str();
}
static Expected<sys::fs::TempFile> createTempFile() {
llvm::SmallString<128> TmpModel;
llvm::sys::path::system_temp_directory(true, TmpModel);
llvm::sys::path::append(TmpModel, "dsym.tmp%%%%%.dwarf");
return sys::fs::TempFile::create(TmpModel);
}
namespace {
struct TempFileVector {
std::vector<sys::fs::TempFile> Files;
~TempFileVector() {
for (sys::fs::TempFile &Tmp : Files) {
if (Error E = Tmp.discard())
errs() << toString(std::move(E));
}
}
};
} // namespace
int main(int argc, char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
llvm::llvm_shutdown_obj Shutdown;
LinkOptions Options;
void *P = (void *)(intptr_t)getOutputFileName;
std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
SDKPath = llvm::sys::path::parent_path(SDKPath);
HideUnrelatedOptions(DsymCategory);
llvm::cl::ParseCommandLineOptions(
argc, argv,
"manipulate archived DWARF debug symbol files.\n\n"
"dsymutil links the DWARF debug information found in the object files\n"
"for the executable <input file> by using debug symbols information\n"
"contained in its symbol table.\n");
if (Help) {
PrintHelpMessage();
return 0;
}
if (Version) {
llvm::cl::PrintVersionMessage();
return 0;
}
Options.Verbose = Verbose;
Options.NoOutput = NoOutput;
Options.NoODR = NoODR;
Options.NoTimestamp = NoTimestamp;
Options.PrependPath = OsoPrependPath;
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
if (!FlatOut && OutputFileOpt == "-") {
llvm::errs() << "error: cannot emit to standard output without --flat\n";
return 1;
}
if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
return 1;
}
for (const auto &Arch : ArchFlags)
if (Arch != "*" && Arch != "all" &&
!llvm::object::MachOObjectFile::isValidArch(Arch)) {
llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
return 1;
}
for (auto &InputFile : InputFiles) {
// Dump the symbol table for each input file and requested arch
if (DumpStab) {
if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
return 1;
continue;
}
auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
Verbose, InputIsYAMLDebugMap);
if (auto EC = DebugMapPtrsOrErr.getError()) {
llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
<< "\": " << EC.message() << '\n';
return 1;
}
if (DebugMapPtrsOrErr->empty()) {
llvm::errs() << "error: no architecture to link\n";
return 1;
}
if (NumThreads == 0)
NumThreads = llvm::thread::hardware_concurrency();
if (DumpDebugMap || Verbose)
NumThreads = 1;
NumThreads = std::min<unsigned>(NumThreads, DebugMapPtrsOrErr->size());
llvm::ThreadPool Threads(NumThreads);
// If there is more than one link to execute, we need to generate
// temporary files.
bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
TempFileVector TempFileStore;
std::atomic_char AllOK(1);
for (auto &Map : *DebugMapPtrsOrErr) {
if (Verbose || DumpDebugMap)
Map->print(llvm::outs());
if (DumpDebugMap)
continue;
if (Map->begin() == Map->end())
llvm::errs() << "warning: no debug symbols in executable (-arch "
<< MachOUtils::getArchName(Map->getTriple().getArchName())
<< ")\n";
// Using a std::shared_ptr rather than std::unique_ptr because move-only
// types don't work with std::bind in the ThreadPool implementation.
std::shared_ptr<raw_fd_ostream> OS;
std::string OutputFile = getOutputFileName(InputFile);
if (NeedsTempFiles) {
Expected<sys::fs::TempFile> T = createTempFile();
if (!T) {
errs() << toString(T.takeError());
return 1;
}
OS = std::make_shared<raw_fd_ostream>(T->FD, /*shouldClose*/ false);
OutputFile = T->TmpName;
TempFileStore.Files.push_back(std::move(*T));
TempFiles.emplace_back(Map->getTriple().getArchName().str(),
OutputFile);
} else {
std::error_code EC;
OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
sys::fs::F_None);
if (EC) {
errs() << OutputFile << ": " << EC.message();
return 1;
}
}
auto LinkLambda = [&,
OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
AllOK.fetch_and(linkDwarf(*Stream, *Map, Options));
Stream->flush();
if (Verify && !NoOutput)
AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
};
// FIXME: The DwarfLinker can have some very deep recursion that can max
// out the (significantly smaller) stack when using threads. We don't
// want this limitation when we only have a single thread.
if (NumThreads == 1)
LinkLambda(OS);
else
Threads.async(LinkLambda, OS);
}
Threads.wait();
if (!AllOK)
return 1;
if (NeedsTempFiles &&
!MachOUtils::generateUniversalBinary(
TempFiles, getOutputFileName(InputFile), Options, SDKPath))
return 1;
}
return 0;
}

View File

@@ -0,0 +1,74 @@
//===- tools/dsymutil/dsymutil.h - dsymutil high-level functionality ------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
/// \file
///
/// This file contains the class declaration for the code that parses STABS
/// debug maps that are embedded in the binaries symbol tables.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
#define LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
#include "DebugMap.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorOr.h"
#include <memory>
#include <string>
#include <vector>
namespace llvm {
namespace dsymutil {
struct LinkOptions {
/// Verbosity
bool Verbose = false;
/// Skip emitting output
bool NoOutput = false;
/// Do not unique types according to ODR
bool NoODR;
/// Do not check swiftmodule timestamp
bool NoTimestamp = false;
/// -oso-prepend-path
std::string PrependPath;
LinkOptions() = default;
};
/// \brief Extract the DebugMaps from the given file.
/// The file has to be a MachO object file. Multiple debug maps can be
/// returned when the file is universal (aka fat) binary.
ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs,
StringRef PrependPath, bool Verbose, bool InputIsYAML);
/// \brief Dump the symbol table
bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs,
StringRef PrependPath = "");
/// \brief Link the Dwarf debuginfo as directed by the passed DebugMap
/// \p DM into a DwarfFile named \p OutputFilename.
/// \returns false if the link failed.
bool linkDwarf(raw_fd_ostream &OutFile, const DebugMap &DM,
const LinkOptions &Options);
void warn(const Twine &Warning, const Twine &Context);
bool error(const Twine &Error, const Twine &Context);
} // end namespace dsymutil
} // end namespace llvm
#endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H