mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
059e346af5
Currently dsymutil will silently fail when processing binaries with Dwarf 5 debug info. This patch adds rudimentary support for Dwarf 5 in dsymutil. - Recognize relocations in the debug_addr section. - Recognize (a subset of) Dwarf 5 form values. - Emits valid Dwarf 5 compile unit header chains. To simplify things (and avoid having to emit indexed sections) I decided to emit the relocated addresses directly in the debug info section. - DW_FORM_strx gets relocated and rewritten to DW_FORM_strp - DW_FORM_addrx gets relocated and rewritten to DW_FORM_addr Obviously there's a lot of work left, but this should be a step in the right direction. rdar://62345491 Differential revision: https://reviews.llvm.org/D94323
221 lines
8.7 KiB
C++
221 lines
8.7 KiB
C++
//===- tools/dsymutil/DwarfLinkerForBinary.h --------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
|
|
#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
|
|
|
|
#include "BinaryHolder.h"
|
|
#include "DebugMap.h"
|
|
#include "LinkUtils.h"
|
|
#include "llvm/DWARFLinker/DWARFLinker.h"
|
|
#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h"
|
|
#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h"
|
|
#include "llvm/DWARFLinker/DWARFStreamer.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/Remarks/RemarkFormat.h"
|
|
#include "llvm/Remarks/RemarkLinker.h"
|
|
|
|
namespace llvm {
|
|
namespace dsymutil {
|
|
|
|
/// The core of the Dsymutil Dwarf linking logic.
|
|
///
|
|
/// The link of the dwarf information from the object files will be
|
|
/// driven by DWARFLinker. DwarfLinkerForBinary reads DebugMap objects
|
|
/// and pass information to the DWARFLinker. DWARFLinker
|
|
/// optimizes DWARF taking into account valid relocations.
|
|
/// Finally, optimized DWARF is passed to DwarfLinkerForBinary through
|
|
/// DWARFEmitter interface.
|
|
class DwarfLinkerForBinary {
|
|
public:
|
|
DwarfLinkerForBinary(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
|
|
LinkOptions Options)
|
|
: OutFile(OutFile), BinHolder(BinHolder), Options(std::move(Options)) {}
|
|
|
|
/// Link the contents of the DebugMap.
|
|
bool link(const DebugMap &);
|
|
|
|
void reportWarning(const Twine &Warning, StringRef Context,
|
|
const DWARFDie *DIE = nullptr) const;
|
|
|
|
/// Flags passed to DwarfLinker::lookForDIEsToKeep
|
|
enum TraversalFlags {
|
|
TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept.
|
|
TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope.
|
|
TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE.
|
|
TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE.
|
|
TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents.
|
|
TF_SkipPC = 1 << 5, ///< Skip all location attributes.
|
|
};
|
|
|
|
private:
|
|
|
|
/// Keeps track of relocations.
|
|
class AddressManager : public AddressesMap {
|
|
struct ValidReloc {
|
|
uint64_t Offset;
|
|
uint32_t Size;
|
|
uint64_t Addend;
|
|
const DebugMapObject::DebugMapEntry *Mapping;
|
|
|
|
ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend,
|
|
const DebugMapObject::DebugMapEntry *Mapping)
|
|
: Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {}
|
|
|
|
bool operator<(const ValidReloc &RHS) const {
|
|
return Offset < RHS.Offset;
|
|
}
|
|
bool operator<(uint64_t RHS) const { return Offset < RHS; }
|
|
};
|
|
|
|
const DwarfLinkerForBinary &Linker;
|
|
|
|
/// The valid relocations for the current DebugMapObject.
|
|
/// This vector is sorted by relocation offset.
|
|
/// {
|
|
std::vector<ValidReloc> ValidDebugInfoRelocs;
|
|
std::vector<ValidReloc> ValidDebugAddrRelocs;
|
|
/// }
|
|
|
|
/// Index into ValidRelocs of the next relocation to consider. As we walk
|
|
/// the DIEs in acsending file offset and as ValidRelocs is sorted by file
|
|
/// offset, keeping this index up to date is all we have to do to have a
|
|
/// cheap lookup during the root DIE selection and during DIE cloning.
|
|
unsigned NextValidReloc = 0;
|
|
|
|
RangesTy AddressRanges;
|
|
|
|
public:
|
|
AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj,
|
|
const DebugMapObject &DMO)
|
|
: Linker(Linker) {
|
|
findValidRelocsInDebugSections(Obj, DMO);
|
|
|
|
// Iterate over the debug map entries and put all the ones that are
|
|
// functions (because they have a size) into the Ranges map. This map is
|
|
// very similar to the FunctionRanges that are stored in each unit, with 2
|
|
// notable differences:
|
|
//
|
|
// 1. Obviously this one is global, while the other ones are per-unit.
|
|
//
|
|
// 2. This one contains not only the functions described in the DIE
|
|
// tree, but also the ones that are only in the debug map.
|
|
//
|
|
// The latter information is required to reproduce dsymutil's logic while
|
|
// linking line tables. The cases where this information matters look like
|
|
// bugs that need to be investigated, but for now we need to reproduce
|
|
// dsymutil's behavior.
|
|
// FIXME: Once we understood exactly if that information is needed,
|
|
// maybe totally remove this (or try to use it to do a real
|
|
// -gline-tables-only on Darwin.
|
|
for (const auto &Entry : DMO.symbols()) {
|
|
const auto &Mapping = Entry.getValue();
|
|
if (Mapping.Size && Mapping.ObjectAddress)
|
|
AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange(
|
|
*Mapping.ObjectAddress + Mapping.Size,
|
|
int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress);
|
|
}
|
|
}
|
|
virtual ~AddressManager() override { clear(); }
|
|
|
|
virtual bool areRelocationsResolved() const override { return true; }
|
|
|
|
bool hasValidRelocs(bool ResetRelocsPtr = true) override {
|
|
if (ResetRelocsPtr)
|
|
NextValidReloc = 0;
|
|
return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty();
|
|
}
|
|
|
|
/// \defgroup FindValidRelocations Translate debug map into a list
|
|
/// of relevant relocations
|
|
///
|
|
/// @{
|
|
bool findValidRelocsInDebugSections(const object::ObjectFile &Obj,
|
|
const DebugMapObject &DMO);
|
|
|
|
bool findValidRelocs(const object::SectionRef &Section,
|
|
const object::ObjectFile &Obj,
|
|
const DebugMapObject &DMO,
|
|
std::vector<ValidReloc> &ValidRelocs);
|
|
|
|
void findValidRelocsMachO(const object::SectionRef &Section,
|
|
const object::MachOObjectFile &Obj,
|
|
const DebugMapObject &DMO,
|
|
std::vector<ValidReloc> &ValidRelocs);
|
|
/// @}
|
|
|
|
/// Checks that there is a relocation in the debug_addr section against a
|
|
/// debug map entry between \p StartOffset and \p NextOffset.
|
|
///
|
|
/// This function must be called with offsets in strictly ascending order
|
|
/// because it never looks back at relocations it already 'went past'.
|
|
/// \returns true and sets Info.InDebugMap if it is the case.
|
|
bool hasValidDebugInfoRelocationAt(uint64_t StartOffset, uint64_t EndOffset,
|
|
CompileUnit::DIEInfo &Info);
|
|
|
|
/// Checks that there is a relocation in the debug_addr section against a
|
|
/// debug map entry at the given offset.
|
|
bool hasValidDebugAddrRelocationAt(uint64_t Offset);
|
|
|
|
bool hasLiveMemoryLocation(const DWARFDie &DIE,
|
|
CompileUnit::DIEInfo &Info) override;
|
|
bool hasLiveAddressRange(const DWARFDie &DIE,
|
|
CompileUnit::DIEInfo &Info) override;
|
|
|
|
bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
|
|
bool IsLittleEndian) override;
|
|
|
|
llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t Offset) override;
|
|
|
|
RangesTy &getValidAddressRanges() override { return AddressRanges; }
|
|
|
|
void clear() override {
|
|
AddressRanges.clear();
|
|
ValidDebugInfoRelocs.clear();
|
|
ValidDebugAddrRelocs.clear();
|
|
NextValidReloc = 0;
|
|
}
|
|
};
|
|
|
|
private:
|
|
/// \defgroup Helpers Various helper methods.
|
|
///
|
|
/// @{
|
|
bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile);
|
|
|
|
/// Attempt to load a debug object from disk.
|
|
ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj,
|
|
const Triple &triple);
|
|
ErrorOr<DWARFFile &> loadObject(const DebugMapObject &Obj,
|
|
const DebugMap &DebugMap,
|
|
remarks::RemarkLinker &RL);
|
|
|
|
raw_fd_ostream &OutFile;
|
|
BinaryHolder &BinHolder;
|
|
LinkOptions Options;
|
|
std::unique_ptr<DwarfStreamer> Streamer;
|
|
std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking;
|
|
std::vector<std::unique_ptr<DWARFContext>> ContextForLinking;
|
|
std::vector<std::unique_ptr<AddressManager>> AddressMapForLinking;
|
|
std::vector<std::string> EmptyWarnings;
|
|
|
|
/// A list of all .swiftinterface files referenced by the debug
|
|
/// info, mapping Module name to path on disk. The entries need to
|
|
/// be uniqued and sorted and there are only few entries expected
|
|
/// per compile unit, which is why this is a std::map.
|
|
std::map<std::string, std::string> ParseableSwiftInterfaces;
|
|
|
|
bool ModuleCacheHintDisplayed = false;
|
|
bool ArchiveHintDisplayed = false;
|
|
};
|
|
|
|
} // end namespace dsymutil
|
|
} // end namespace llvm
|
|
|
|
#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
|