1 //===--- llvm-jitlink-coff.cpp -- COFF parsing support for llvm-jitlink ---===//
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 // COFF parsing support for llvm-jitlink.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm-jitlink.h"
14 
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/Path.h"
17 
18 #define DEBUG_TYPE "llvm_jitlink"
19 
20 using namespace llvm;
21 using namespace llvm::jitlink;
22 
isCOFFGOTSection(Section & S)23 static bool isCOFFGOTSection(Section &S) { return S.getName() == "$__GOT"; }
24 
isCOFFStubsSection(Section & S)25 static bool isCOFFStubsSection(Section &S) { return S.getName() == "$__STUBS"; }
26 
getFirstRelocationEdge(LinkGraph & G,Block & B)27 static Expected<Edge &> getFirstRelocationEdge(LinkGraph &G, Block &B) {
28   auto EItr = std::find_if(B.edges().begin(), B.edges().end(),
29                            [](Edge &E) { return E.isRelocation(); });
30   if (EItr == B.edges().end())
31     return make_error<StringError>("GOT entry in " + G.getName() + ", \"" +
32                                        B.getSection().getName() +
33                                        "\" has no relocations",
34                                    inconvertibleErrorCode());
35   return *EItr;
36 }
37 
getCOFFGOTTarget(LinkGraph & G,Block & B)38 static Expected<Symbol &> getCOFFGOTTarget(LinkGraph &G, Block &B) {
39   auto E = getFirstRelocationEdge(G, B);
40   if (!E)
41     return E.takeError();
42   auto &TargetSym = E->getTarget();
43   if (!TargetSym.hasName())
44     return make_error<StringError>(
45         "GOT entry in " + G.getName() + ", \"" +
46             TargetSym.getBlock().getSection().getName() +
47             "\" points to anonymous "
48             "symbol",
49         inconvertibleErrorCode());
50   return TargetSym;
51 }
52 
getCOFFStubTarget(LinkGraph & G,Block & B)53 static Expected<Symbol &> getCOFFStubTarget(LinkGraph &G, Block &B) {
54   auto E = getFirstRelocationEdge(G, B);
55   if (!E)
56     return E.takeError();
57   auto &GOTSym = E->getTarget();
58   if (!GOTSym.isDefined() || !isCOFFGOTSection(GOTSym.getBlock().getSection()))
59     return make_error<StringError>(
60         "Stubs entry in " + G.getName() + ", \"" +
61             GOTSym.getBlock().getSection().getName() +
62             "\" does not point to GOT entry",
63         inconvertibleErrorCode());
64   return getCOFFGOTTarget(G, GOTSym.getBlock());
65 }
66 
67 namespace llvm {
registerCOFFGraphInfo(Session & S,LinkGraph & G)68 Error registerCOFFGraphInfo(Session &S, LinkGraph &G) {
69   auto FileName = sys::path::filename(G.getName());
70   if (S.FileInfos.count(FileName)) {
71     return make_error<StringError>("When -check is passed, file names must be "
72                                    "distinct (duplicate: \"" +
73                                        FileName + "\")",
74                                    inconvertibleErrorCode());
75   }
76 
77   auto &FileInfo = S.FileInfos[FileName];
78   LLVM_DEBUG(
79       { dbgs() << "Registering COFF file info for \"" << FileName << "\"\n"; });
80   for (auto &Sec : G.sections()) {
81     LLVM_DEBUG({
82       dbgs() << "  Section \"" << Sec.getName() << "\": "
83              << (llvm::empty(Sec.symbols()) ? "empty. skipping."
84                                             : "processing...")
85              << "\n";
86     });
87 
88     // Skip empty sections.
89     if (llvm::empty(Sec.symbols()))
90       continue;
91 
92     if (FileInfo.SectionInfos.count(Sec.getName()))
93       return make_error<StringError>("Encountered duplicate section name \"" +
94                                          Sec.getName() + "\" in \"" + FileName +
95                                          "\"",
96                                      inconvertibleErrorCode());
97 
98     bool isGOTSection = isCOFFGOTSection(Sec);
99     bool isStubsSection = isCOFFStubsSection(Sec);
100 
101     bool SectionContainsContent = false;
102     bool SectionContainsZeroFill = false;
103 
104     auto *FirstSym = *Sec.symbols().begin();
105     auto *LastSym = FirstSym;
106     for (auto *Sym : Sec.symbols()) {
107       if (Sym->getAddress() < FirstSym->getAddress())
108         FirstSym = Sym;
109       if (Sym->getAddress() > LastSym->getAddress())
110         LastSym = Sym;
111 
112       if (isGOTSection) {
113         if (Sym->isSymbolZeroFill())
114           return make_error<StringError>("zero-fill atom in GOT section",
115                                          inconvertibleErrorCode());
116 
117         // If this is a GOT symbol with size (i.e. not the GOT start symbol)
118         // then add it to the GOT entry info table.
119         if (Sym->getSize() != 0) {
120           if (auto TS = getCOFFGOTTarget(G, Sym->getBlock()))
121             FileInfo.GOTEntryInfos[TS->getName()] = {
122                 Sym->getSymbolContent(), Sym->getAddress().getValue()};
123           else
124             return TS.takeError();
125         }
126         SectionContainsContent = true;
127       } else if (isStubsSection) {
128         if (Sym->isSymbolZeroFill())
129           return make_error<StringError>("zero-fill atom in Stub section",
130                                          inconvertibleErrorCode());
131 
132         if (auto TS = getCOFFStubTarget(G, Sym->getBlock()))
133           FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
134                                                Sym->getAddress().getValue()};
135         else
136           return TS.takeError();
137         SectionContainsContent = true;
138       }
139 
140       if (Sym->hasName()) {
141         if (Sym->isSymbolZeroFill()) {
142           S.SymbolInfos[Sym->getName()] = {Sym->getSize(),
143                                            Sym->getAddress().getValue()};
144           SectionContainsZeroFill = true;
145         } else {
146           S.SymbolInfos[Sym->getName()] = {Sym->getSymbolContent(),
147                                            Sym->getAddress().getValue()};
148           SectionContainsContent = true;
149         }
150       }
151     }
152 
153     auto SecAddr = FirstSym->getAddress();
154     auto SecSize =
155         (LastSym->getBlock().getAddress() + LastSym->getBlock().getSize()) -
156         SecAddr;
157 
158     if (SectionContainsZeroFill && SectionContainsContent)
159       return make_error<StringError>("Mixed zero-fill and content sections not "
160                                      "supported yet",
161                                      inconvertibleErrorCode());
162 
163     if (SectionContainsZeroFill)
164       FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr.getValue()};
165     else
166       FileInfo.SectionInfos[Sec.getName()] = {
167           ArrayRef<char>(FirstSym->getBlock().getContent().data(), SecSize),
168           SecAddr.getValue()};
169   }
170 
171   return Error::success();
172 }
173 
174 } // end namespace llvm
175