xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/BTF/BTFParser.cpp (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1 //===- BTFParser.cpp ------------------------------------------------------===//
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 // BTFParser reads/interprets .BTF and .BTF.ext ELF sections.
10 // Refer to BTFParser.h for API description.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/DebugInfo/BTF/BTFParser.h"
15 #include "llvm/Support/Errc.h"
16 
17 #define DEBUG_TYPE "debug-info-btf-parser"
18 
19 using namespace llvm;
20 using object::ObjectFile;
21 using object::SectionedAddress;
22 using object::SectionRef;
23 
24 const char BTFSectionName[] = ".BTF";
25 const char BTFExtSectionName[] = ".BTF.ext";
26 
27 // Utility class with API similar to raw_ostream but can be cast
28 // to Error, e.g.:
29 //
30 // Error foo(...) {
31 //   ...
32 //   if (Error E = bar(...))
33 //     return Err("error while foo(): ") << E;
34 //   ...
35 // }
36 //
37 namespace {
38 class Err {
39   std::string Buffer;
40   raw_string_ostream Stream;
41 
42 public:
43   Err(const char *InitialMsg) : Buffer(InitialMsg), Stream(Buffer) {}
44   Err(const char *SectionName, DataExtractor::Cursor &C)
45       : Buffer(), Stream(Buffer) {
46     *this << "error while reading " << SectionName
47           << " section: " << C.takeError();
48   };
49 
50   template <typename T> Err &operator<<(T Val) {
51     Stream << Val;
52     return *this;
53   }
54 
55   Err &write_hex(unsigned long long Val) {
56     Stream.write_hex(Val);
57     return *this;
58   }
59 
60   Err &operator<<(Error Val) {
61     handleAllErrors(std::move(Val),
62                     [=](ErrorInfoBase &Info) { Stream << Info.message(); });
63     return *this;
64   }
65 
66   operator Error() const {
67     return make_error<StringError>(Buffer, errc::invalid_argument);
68   }
69 };
70 } // anonymous namespace
71 
72 // ParseContext wraps information that is only necessary while parsing
73 // ObjectFile and can be discarded once parsing is done.
74 // Used by BTFParser::parse* auxiliary functions.
75 struct BTFParser::ParseContext {
76   const ObjectFile &Obj;
77   // Map from ELF section name to SectionRef
78   DenseMap<StringRef, SectionRef> Sections;
79 
80 public:
81   ParseContext(const ObjectFile &Obj) : Obj(Obj) {}
82 
83   Expected<DataExtractor> makeExtractor(SectionRef Sec) {
84     Expected<StringRef> Contents = Sec.getContents();
85     if (!Contents)
86       return Contents.takeError();
87     return DataExtractor(Contents.get(), Obj.isLittleEndian(),
88                          Obj.getBytesInAddress());
89   }
90 
91   std::optional<SectionRef> findSection(StringRef Name) const {
92     auto It = Sections.find(Name);
93     if (It != Sections.end())
94       return It->second;
95     return std::nullopt;
96   }
97 };
98 
99 Error BTFParser::parseBTF(ParseContext &Ctx, SectionRef BTF) {
100   Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTF);
101   if (!MaybeExtractor)
102     return MaybeExtractor.takeError();
103 
104   DataExtractor &Extractor = MaybeExtractor.get();
105   DataExtractor::Cursor C = DataExtractor::Cursor(0);
106   uint16_t Magic = Extractor.getU16(C);
107   if (!C)
108     return Err(".BTF", C);
109   if (Magic != BTF::MAGIC)
110     return Err("invalid .BTF magic: ").write_hex(Magic);
111   uint8_t Version = Extractor.getU8(C);
112   if (!C)
113     return Err(".BTF", C);
114   if (Version != 1)
115     return Err("unsupported .BTF version: ") << (unsigned)Version;
116   (void)Extractor.getU8(C); // flags
117   uint32_t HdrLen = Extractor.getU32(C);
118   if (!C)
119     return Err(".BTF", C);
120   if (HdrLen < 8)
121     return Err("unexpected .BTF header length: ") << HdrLen;
122   (void)Extractor.getU32(C); // type_off
123   (void)Extractor.getU32(C); // type_len
124   uint32_t StrOff = Extractor.getU32(C);
125   uint32_t StrLen = Extractor.getU32(C);
126   uint32_t StrStart = HdrLen + StrOff;
127   uint32_t StrEnd = StrStart + StrLen;
128   if (!C)
129     return Err(".BTF", C);
130   if (Extractor.getData().size() < StrEnd)
131     return Err("invalid .BTF section size, expecting at-least ")
132            << StrEnd << " bytes";
133 
134   StringsTable = Extractor.getData().substr(StrStart, StrLen);
135   return Error::success();
136 }
137 
138 Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) {
139   Expected<DataExtractor> MaybeExtractor = Ctx.makeExtractor(BTFExt);
140   if (!MaybeExtractor)
141     return MaybeExtractor.takeError();
142 
143   DataExtractor &Extractor = MaybeExtractor.get();
144   DataExtractor::Cursor C = DataExtractor::Cursor(0);
145   uint16_t Magic = Extractor.getU16(C);
146   if (!C)
147     return Err(".BTF.ext", C);
148   if (Magic != BTF::MAGIC)
149     return Err("invalid .BTF.ext magic: ").write_hex(Magic);
150   uint8_t Version = Extractor.getU8(C);
151   if (!C)
152     return Err(".BTF", C);
153   if (Version != 1)
154     return Err("unsupported .BTF.ext version: ") << (unsigned)Version;
155   (void)Extractor.getU8(C); // flags
156   uint32_t HdrLen = Extractor.getU32(C);
157   if (!C)
158     return Err(".BTF.ext", C);
159   if (HdrLen < 8)
160     return Err("unexpected .BTF.ext header length: ") << HdrLen;
161   (void)Extractor.getU32(C); // func_info_off
162   (void)Extractor.getU32(C); // func_info_len
163   uint32_t LineInfoOff = Extractor.getU32(C);
164   uint32_t LineInfoLen = Extractor.getU32(C);
165   if (!C)
166     return Err(".BTF.ext", C);
167   uint32_t LineInfoStart = HdrLen + LineInfoOff;
168   uint32_t LineInfoEnd = LineInfoStart + LineInfoLen;
169   if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd))
170     return E;
171 
172   return Error::success();
173 }
174 
175 Error BTFParser::parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor,
176                                uint64_t LineInfoStart, uint64_t LineInfoEnd) {
177   DataExtractor::Cursor C = DataExtractor::Cursor(LineInfoStart);
178   uint32_t RecSize = Extractor.getU32(C);
179   if (!C)
180     return Err(".BTF.ext", C);
181   if (RecSize < 16)
182     return Err("unexpected .BTF.ext line info record length: ") << RecSize;
183 
184   while (C && C.tell() < LineInfoEnd) {
185     uint32_t SecNameOff = Extractor.getU32(C);
186     uint32_t NumInfo = Extractor.getU32(C);
187     StringRef SecName = findString(SecNameOff);
188     std::optional<SectionRef> Sec = Ctx.findSection(SecName);
189     if (!C)
190       return Err(".BTF.ext", C);
191     if (!Sec)
192       return Err("") << "can't find section '" << SecName
193                      << "' while parsing .BTF.ext line info";
194     BTFLinesVector &Lines = SectionLines[Sec->getIndex()];
195     for (uint32_t I = 0; C && I < NumInfo; ++I) {
196       uint64_t RecStart = C.tell();
197       uint32_t InsnOff = Extractor.getU32(C);
198       uint32_t FileNameOff = Extractor.getU32(C);
199       uint32_t LineOff = Extractor.getU32(C);
200       uint32_t LineCol = Extractor.getU32(C);
201       if (!C)
202         return Err(".BTF.ext", C);
203       Lines.push_back({InsnOff, FileNameOff, LineOff, LineCol});
204       C.seek(RecStart + RecSize);
205     }
206     llvm::stable_sort(Lines,
207                       [](const BTF::BPFLineInfo &L, const BTF::BPFLineInfo &R) {
208                         return L.InsnOffset < R.InsnOffset;
209                       });
210   }
211   if (!C)
212     return Err(".BTF.ext", C);
213 
214   return Error::success();
215 }
216 
217 Error BTFParser::parse(const ObjectFile &Obj) {
218   StringsTable = StringRef();
219   SectionLines.clear();
220 
221   ParseContext Ctx(Obj);
222   std::optional<SectionRef> BTF;
223   std::optional<SectionRef> BTFExt;
224   for (SectionRef Sec : Obj.sections()) {
225     Expected<StringRef> MaybeName = Sec.getName();
226     if (!MaybeName)
227       return Err("error while reading section name: ") << MaybeName.takeError();
228     Ctx.Sections[*MaybeName] = Sec;
229     if (*MaybeName == BTFSectionName)
230       BTF = Sec;
231     if (*MaybeName == BTFExtSectionName)
232       BTFExt = Sec;
233   }
234   if (!BTF)
235     return Err("can't find .BTF section");
236   if (!BTFExt)
237     return Err("can't find .BTF.ext section");
238   if (Error E = parseBTF(Ctx, *BTF))
239     return E;
240   if (Error E = parseBTFExt(Ctx, *BTFExt))
241     return E;
242 
243   return Error::success();
244 }
245 
246 bool BTFParser::hasBTFSections(const ObjectFile &Obj) {
247   bool HasBTF = false;
248   bool HasBTFExt = false;
249   for (SectionRef Sec : Obj.sections()) {
250     Expected<StringRef> Name = Sec.getName();
251     if (Error E = Name.takeError()) {
252       logAllUnhandledErrors(std::move(E), errs());
253       continue;
254     }
255     HasBTF |= *Name == BTFSectionName;
256     HasBTFExt |= *Name == BTFExtSectionName;
257     if (HasBTF && HasBTFExt)
258       return true;
259   }
260   return false;
261 }
262 
263 StringRef BTFParser::findString(uint32_t Offset) const {
264   return StringsTable.slice(Offset, StringsTable.find(0, Offset));
265 }
266 
267 const BTF::BPFLineInfo *
268 BTFParser::findLineInfo(SectionedAddress Address) const {
269   auto MaybeSecInfo = SectionLines.find(Address.SectionIndex);
270   if (MaybeSecInfo == SectionLines.end())
271     return nullptr;
272 
273   const BTFLinesVector &SecInfo = MaybeSecInfo->second;
274   const uint64_t TargetOffset = Address.Address;
275   BTFLinesVector::const_iterator LineInfo =
276       llvm::partition_point(SecInfo, [=](const BTF::BPFLineInfo &Line) {
277         return Line.InsnOffset < TargetOffset;
278       });
279   if (LineInfo == SecInfo.end() || LineInfo->InsnOffset != Address.Address)
280     return nullptr;
281 
282   return LineInfo;
283 }
284