1 //===- tools/dsymutil/DwarfLinkerForBinary.h --------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
10 #define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
11 
12 #include "BinaryHolder.h"
13 #include "DebugMap.h"
14 #include "LinkUtils.h"
15 #include "llvm/DWARFLinker/DWARFLinker.h"
16 #include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h"
17 #include "llvm/DWARFLinker/DWARFLinkerDeclContext.h"
18 #include "llvm/DWARFLinker/DWARFStreamer.h"
19 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
20 #include "llvm/Remarks/RemarkFormat.h"
21 #include "llvm/Remarks/RemarkLinker.h"
22 
23 namespace llvm {
24 namespace dsymutil {
25 
26 /// The core of the Dsymutil Dwarf linking logic.
27 ///
28 /// The link of the dwarf information from the object files will be
29 /// driven by DWARFLinker. DwarfLinkerForBinary reads DebugMap objects
30 /// and pass information to the DWARFLinker. DWARFLinker
31 /// optimizes DWARF taking into account valid relocations.
32 /// Finally, optimized DWARF is passed to DwarfLinkerForBinary through
33 /// DWARFEmitter interface.
34 class DwarfLinkerForBinary {
35 public:
DwarfLinkerForBinary(raw_fd_ostream & OutFile,BinaryHolder & BinHolder,LinkOptions Options)36   DwarfLinkerForBinary(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
37                        LinkOptions Options)
38       : OutFile(OutFile), BinHolder(BinHolder), Options(std::move(Options)) {}
39 
40   /// Link the contents of the DebugMap.
41   bool link(const DebugMap &);
42 
43   void reportWarning(const Twine &Warning, StringRef Context,
44                      const DWARFDie *DIE = nullptr) const;
45 
46   /// Flags passed to DwarfLinker::lookForDIEsToKeep
47   enum TraversalFlags {
48     TF_Keep = 1 << 0,            ///< Mark the traversed DIEs as kept.
49     TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope.
50     TF_DependencyWalk = 1 << 2,  ///< Walking the dependencies of a kept DIE.
51     TF_ParentWalk = 1 << 3,      ///< Walking up the parents of a kept DIE.
52     TF_ODR = 1 << 4,             ///< Use the ODR while keeping dependents.
53     TF_SkipPC = 1 << 5,          ///< Skip all location attributes.
54   };
55 
56 private:
57 
58   /// Keeps track of relocations.
59   class AddressManager : public AddressesMap {
60     struct ValidReloc {
61       uint64_t Offset;
62       uint32_t Size;
63       uint64_t Addend;
64       const DebugMapObject::DebugMapEntry *Mapping;
65 
ValidRelocValidReloc66       ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend,
67                  const DebugMapObject::DebugMapEntry *Mapping)
68           : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {}
69 
70       bool operator<(const ValidReloc &RHS) const {
71         return Offset < RHS.Offset;
72       }
73       bool operator<(uint64_t RHS) const { return Offset < RHS; }
74     };
75 
76     const DwarfLinkerForBinary &Linker;
77 
78     /// The valid relocations for the current DebugMapObject.
79     /// This vector is sorted by relocation offset.
80     /// {
81     std::vector<ValidReloc> ValidDebugInfoRelocs;
82     std::vector<ValidReloc> ValidDebugAddrRelocs;
83     /// }
84 
85     RangesTy AddressRanges;
86 
87     StringRef SrcFileName;
88 
89     /// Returns list of valid relocations from \p Relocs,
90     /// between \p StartOffset and \p NextOffset.
91     ///
92     /// \returns true if any relocation is found.
93     std::vector<ValidReloc>
94     getRelocations(const std::vector<ValidReloc> &Relocs, uint64_t StartPos,
95                    uint64_t EndPos);
96 
97     /// Resolve specified relocation \p Reloc.
98     ///
99     /// \returns resolved value.
100     uint64_t relocate(const ValidReloc &Reloc) const;
101 
102     /// Fill \p Info with address information for the specified \p Reloc.
103     void fillDieInfo(const ValidReloc &Reloc, CompileUnit::DIEInfo &Info);
104 
105     /// Print contents of debug map entry for the specified \p Reloc.
106     void printReloc(const ValidReloc &Reloc);
107 
108   public:
AddressManager(DwarfLinkerForBinary & Linker,const object::ObjectFile & Obj,const DebugMapObject & DMO)109     AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj,
110                    const DebugMapObject &DMO)
111         : Linker(Linker), SrcFileName(DMO.getObjectFilename()) {
112       findValidRelocsInDebugSections(Obj, DMO);
113 
114       // Iterate over the debug map entries and put all the ones that are
115       // functions (because they have a size) into the Ranges map. This map is
116       // very similar to the FunctionRanges that are stored in each unit, with 2
117       // notable differences:
118       //
119       //  1. Obviously this one is global, while the other ones are per-unit.
120       //
121       //  2. This one contains not only the functions described in the DIE
122       //     tree, but also the ones that are only in the debug map.
123       //
124       // The latter information is required to reproduce dsymutil's logic while
125       // linking line tables. The cases where this information matters look like
126       // bugs that need to be investigated, but for now we need to reproduce
127       // dsymutil's behavior.
128       // FIXME: Once we understood exactly if that information is needed,
129       // maybe totally remove this (or try to use it to do a real
130       // -gline-tables-only on Darwin.
131       for (const auto &Entry : DMO.symbols()) {
132         const auto &Mapping = Entry.getValue();
133         if (Mapping.Size && Mapping.ObjectAddress)
134           AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange(
135               *Mapping.ObjectAddress + Mapping.Size,
136               int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress);
137       }
138     }
~AddressManager()139     virtual ~AddressManager() override { clear(); }
140 
areRelocationsResolved()141     virtual bool areRelocationsResolved() const override { return true; }
142 
hasValidRelocs()143     bool hasValidRelocs() override {
144       return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty();
145     }
146 
147     /// \defgroup FindValidRelocations Translate debug map into a list
148     /// of relevant relocations
149     ///
150     /// @{
151     bool findValidRelocsInDebugSections(const object::ObjectFile &Obj,
152                                         const DebugMapObject &DMO);
153 
154     bool findValidRelocs(const object::SectionRef &Section,
155                          const object::ObjectFile &Obj,
156                          const DebugMapObject &DMO,
157                          std::vector<ValidReloc> &ValidRelocs);
158 
159     void findValidRelocsMachO(const object::SectionRef &Section,
160                               const object::MachOObjectFile &Obj,
161                               const DebugMapObject &DMO,
162                               std::vector<ValidReloc> &ValidRelocs);
163     /// @}
164 
165     /// Checks that there is a relocation in the \p Relocs array against a
166     /// debug map entry between \p StartOffset and \p NextOffset.
167     ///
168     /// \returns true and sets Info.InDebugMap if it is the case.
169     bool hasValidRelocationAt(const std::vector<ValidReloc> &Relocs,
170                               uint64_t StartOffset, uint64_t EndOffset,
171                               CompileUnit::DIEInfo &Info);
172 
173     bool hasLiveMemoryLocation(const DWARFDie &DIE,
174                                CompileUnit::DIEInfo &Info) override;
175     bool hasLiveAddressRange(const DWARFDie &DIE,
176                              CompileUnit::DIEInfo &Info) override;
177 
178     bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
179                           bool IsLittleEndian) override;
180 
181     llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t StartOffset,
182                                                  uint64_t EndOffset) override;
183 
getValidAddressRanges()184     RangesTy &getValidAddressRanges() override { return AddressRanges; }
185 
clear()186     void clear() override {
187       AddressRanges.clear();
188       ValidDebugInfoRelocs.clear();
189       ValidDebugAddrRelocs.clear();
190     }
191   };
192 
193 private:
194   /// \defgroup Helpers Various helper methods.
195   ///
196   /// @{
197   bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile);
198 
199   /// Attempt to load a debug object from disk.
200   ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj,
201                                                  const Triple &triple);
202   ErrorOr<DWARFFile &> loadObject(const DebugMapObject &Obj,
203                                   const DebugMap &DebugMap,
204                                   remarks::RemarkLinker &RL);
205 
206   raw_fd_ostream &OutFile;
207   BinaryHolder &BinHolder;
208   LinkOptions Options;
209   std::unique_ptr<DwarfStreamer> Streamer;
210   std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking;
211   std::vector<std::unique_ptr<DWARFContext>> ContextForLinking;
212   std::vector<std::unique_ptr<AddressManager>> AddressMapForLinking;
213   std::vector<std::string> EmptyWarnings;
214 
215   /// A list of all .swiftinterface files referenced by the debug
216   /// info, mapping Module name to path on disk. The entries need to
217   /// be uniqued and sorted and there are only few entries expected
218   /// per compile unit, which is why this is a std::map.
219   std::map<std::string, std::string> ParseableSwiftInterfaces;
220 
221   bool ModuleCacheHintDisplayed = false;
222   bool ArchiveHintDisplayed = false;
223 };
224 
225 } // end namespace dsymutil
226 } // end namespace llvm
227 
228 #endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
229