LLDB mainline
ObjectContainerClangOffloadBundle.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10#include "lldb/Core/Module.h"
15#include "lldb/Target/Target.h"
19#include "llvm/ADT/StringMap.h"
20#include "llvm/BinaryFormat/Magic.h"
21#include "llvm/Object/ObjectFile.h"
22#include "llvm/Object/OffloadBundle.h"
23#include "llvm/Support/Chrono.h"
24#include <mutex>
25
26using namespace lldb;
27using namespace lldb_private;
28
30
36
40
42 const ModuleSP &module_sp, DataBufferSP &data_sp,
43 lldb::offset_t data_offset, const FileSpec *file,
44 lldb::offset_t file_offset, lldb::offset_t length)
45 : ObjectContainer(module_sp, file, file_offset, length, data_sp,
46 data_offset) {}
47
49 default;
50
52 const DataExtractor &data) {
53 llvm::StringRef bytes(reinterpret_cast<const char *>(data.GetDataStart()),
54 data.GetByteSize());
55 llvm::file_magic magic = llvm::identify_magic(bytes);
56 switch (magic) {
57 case llvm::file_magic::elf:
58 case llvm::file_magic::elf_relocatable:
59 case llvm::file_magic::elf_executable:
60 case llvm::file_magic::elf_shared_object:
61 case llvm::file_magic::elf_core:
62 return true;
63 default:
64 return false;
65 }
66}
67
68static ArchSpec ParseArchFromBundleEntryID(llvm::StringRef ID) {
69 // Bundle entry IDs use the format: <offload-kind>-<target-triple>
70 // e.g. "hip-amdgcn-amd-amdhsa--gfx906", "host-x86_64-unknown-linux-gnu"
71 auto [Kind, Triple] = ID.split('-');
72 if (Triple.empty())
73 return ArchSpec();
74 return ArchSpec(Triple);
75}
76
78 const FileSpec &file, std::vector<Entry> &entries) {
79 std::string path = file.GetPath();
80 if (path.empty())
81 return false;
82
83 // Cache the parse per file so a bundle with many code objects isn't rescanned
84 // once per object. Keyed by path and validated by mtime, so a changed file
85 // re-parses and overwrites. Locked for concurrent module loads.
86 struct CacheValue {
87 llvm::sys::TimePoint<> mod_time;
88 std::vector<Entry> entries;
89 };
90 static std::mutex cache_mutex;
91 static llvm::StringMap<CacheValue> cache;
92
93 llvm::sys::TimePoint<> mod_time =
95
96 {
97 std::lock_guard<std::mutex> lock(cache_mutex);
98 auto it = cache.find(path);
99 if (it != cache.end() && it->second.mod_time == mod_time) {
100 entries = it->second.entries;
101 return !entries.empty();
102 }
103 }
104
105 // Parse the offload bundle (helper keeps this separate from the caching).
106 auto parse = [&path]() -> std::vector<Entry> {
107 std::vector<Entry> result;
108 auto obj_or_err = llvm::object::ObjectFile::createObjectFile(path);
109 if (!obj_or_err) {
110 llvm::consumeError(obj_or_err.takeError());
111 return result;
112 }
113
114 llvm::SmallVector<llvm::object::OffloadBundleFatBin> bundles;
115 if (auto err = llvm::object::extractOffloadBundleFatBinary(
116 *obj_or_err->getBinary(), bundles)) {
117 llvm::consumeError(std::move(err));
118 return result;
119 }
120
121 for (auto &bundle : bundles) {
122 for (auto &bundle_entry : bundle.getEntries()) {
123 if (bundle_entry.Size == 0)
124 continue;
125
126 DataExtractorSP entry_extractor_sp;
127 uint64_t offset = bundle_entry.Offset;
128 if (bundle.isDecompressed()) {
129 if (!bundle.DecompressedBuffer)
130 continue;
131
132 llvm::StringRef decompressed = bundle.DecompressedBuffer->getBuffer();
133 if (offset > decompressed.size() ||
134 bundle_entry.Size > decompressed.size() - offset)
135 continue;
136
137 auto entry_data_sp = std::make_shared<DataBufferHeap>(
138 decompressed.data() + offset, bundle_entry.Size);
139 entry_extractor_sp = std::make_shared<DataExtractor>(entry_data_sp);
140 // Compressed entries have no corresponding offset in the containing
141 // file. The owned buffer above contains only the selected object.
142 offset = 0;
143 }
144
145 Entry entry;
146 entry.arch = ParseArchFromBundleEntryID(bundle_entry.ID);
147 entry.offset = offset;
148 entry.size = bundle_entry.Size;
149 entry.extractor_sp = std::move(entry_extractor_sp);
150 if (entry.arch.IsValid())
151 result.push_back(std::move(entry));
152 }
153 }
154 return result;
155 };
156
157 std::vector<Entry> parsed = parse();
158
159 // Store/overwrite this path's entry; cache empty results too so non-bundle
160 // files aren't re-parsed.
161 {
162 std::lock_guard<std::mutex> lock(cache_mutex);
163 cache[path] = CacheValue{mod_time, parsed};
164 }
165
166 entries = std::move(parsed);
167 return !entries.empty();
168}
169
171 const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
172 lldb::offset_t data_offset, const FileSpec *file,
173 lldb::offset_t file_offset, lldb::offset_t length) {
174 if (!data_sp || !file)
175 return nullptr;
176
177 DataExtractor data;
178 data.SetData(data_sp, data_offset, length);
179 if (!MagicBytesMatch(data))
180 return nullptr;
181
182 auto container_up = std::make_unique<ObjectContainerClangOffloadBundle>(
183 module_sp, data_sp, data_offset, file, file_offset, length);
184 if (!container_up->ParseHeader())
185 return nullptr;
186
187 return container_up.release();
188}
189
191 m_entries.clear();
193 return false;
194 return true;
195}
196
200
202 uint32_t idx, ArchSpec &arch) const {
203 if (idx < m_entries.size()) {
204 arch = m_entries[idx].arch;
205 return true;
206 }
207 return false;
208}
209
211 const FileSpec &file, DataExtractorSP &extractor_sp,
212 lldb::offset_t /*file_offset*/, lldb::offset_t /*file_size*/) {
213 if (!extractor_sp || !MagicBytesMatch(*extractor_sp))
214 return {};
215
216 std::vector<Entry> entries;
217 if (!FindBundleEntries(file, entries))
218 return {};
219
220 ModuleSpecList specs;
221 for (const Entry &entry : entries) {
222 ModuleSpec spec = entry.extractor_sp
223 ? ModuleSpec(file, UUID(), entry.extractor_sp)
224 : ModuleSpec(file, entry.arch);
225 if (entry.extractor_sp)
226 spec.GetArchitecture() = entry.arch;
227 spec.SetObjectOffset(entry.offset);
228 spec.SetObjectSize(entry.size);
229 specs.Append(spec);
230 }
231 return specs;
232}
233
236 ModuleSP module_sp(GetModule());
237 if (!module_sp)
238 return {};
239
240 ArchSpec arch = module_sp->GetArchitecture();
241 if (!arch.IsValid()) {
243 if (!arch.IsValid())
245 }
246
247 for (int pass = 0; pass < 2; ++pass) {
248 for (const Entry &entry : m_entries) {
249 bool match = (pass == 0) ? arch.IsExactMatch(entry.arch)
250 : arch.IsCompatibleMatch(entry.arch);
251 if (match) {
252 DataExtractorSP extractor_sp = entry.extractor_sp;
253 lldb::offset_t data_offset = 0;
254 return ObjectFile::FindPlugin(module_sp, file, entry.offset, entry.size,
255 extractor_sp, data_offset);
256 }
257 }
258 }
259
260 return {};
261}
static char ID
static ArchSpec ParseArchFromBundleEntryID(llvm::StringRef ID)
#define LLDB_PLUGIN_DEFINE(PluginName)
ObjectContainerClangOffloadBundle(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length)
static bool MagicBytesMatch(const lldb_private::DataExtractor &data)
lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override
Selects an architecture in an object file.
static bool FindBundleEntries(const lldb_private::FileSpec &file, std::vector< Entry > &entries)
bool ParseHeader() override
Attempts to parse the object header.
static lldb_private::ObjectContainer * CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length)
size_t GetNumArchitectures() const override
Get the number of architectures in this object file.
static lldb_private::ModuleSpecList GetModuleSpecifications(const lldb_private::FileSpec &file, lldb::DataExtractorSP &extractor_sp, lldb::offset_t file_offset, lldb::offset_t file_size)
bool GetArchitectureAtIndex(uint32_t idx, lldb_private::ArchSpec &arch) const override
Gets the architecture given an index.
An architecture specification class.
Definition ArchSpec.h:32
bool IsValid() const
Tests if this ArchSpec is valid.
Definition ArchSpec.h:453
bool SetTriple(const llvm::Triple &triple)
Architecture triple setter.
Definition ArchSpec.cpp:949
bool IsCompatibleMatch(const ArchSpec &rhs) const
Shorthand for IsMatch(rhs, CompatibleMatch).
Definition ArchSpec.h:597
bool IsExactMatch(const ArchSpec &rhs) const
Shorthand for IsMatch(rhs, ExactMatch).
Definition ArchSpec.h:592
An data extractor class.
virtual uint64_t GetByteSize() const
Get the number of bytes contained in this object.
const uint8_t * GetDataStart() const
Get the data start pointer.
virtual lldb::offset_t SetData(const void *bytes, lldb::offset_t length, lldb::ByteOrder byte_order)
Set data with a buffer that is caller owned.
A file utility class.
Definition FileSpec.h:56
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:380
llvm::sys::TimePoint GetModificationTime(const FileSpec &file_spec) const
Returns the modification time of the given file.
static FileSystem & Instance()
lldb::ModuleSP GetModule() const
Get const accessor for the module pointer.
void Append(const ModuleSpec &spec)
Definition ModuleSpec.h:371
void SetObjectSize(uint64_t object_size)
Definition ModuleSpec.h:119
ArchSpec & GetArchitecture()
Definition ModuleSpec.h:93
void SetObjectOffset(uint64_t object_offset)
Definition ModuleSpec.h:113
FileSpec m_file
The file that represents this container objects (which can be different from the module's file).
ObjectContainer(const lldb::ModuleSP &module_sp, const FileSpec *file, lldb::offset_t file_offset, lldb::offset_t length, lldb::DataBufferSP data_sp, lldb::offset_t data_offset)
Construct with a parent module, offset, and header data.
static lldb::ObjectFileSP FindPlugin(const lldb::ModuleSP &module_sp, const FileSpec *file_spec, lldb::offset_t file_offset, lldb::offset_t file_size, lldb::DataExtractorSP extractor_sp, lldb::offset_t &data_offset)
Find a ObjectFile plug-in that can parse file_spec.
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
static ArchSpec GetDefaultArchitecture()
Definition Target.cpp:2909
Represents UUID's of various sizes.
Definition UUID.h:27
#define LLDB_ARCH_DEFAULT
CPU Type definitions.
A class that represents a running process on the host machine.
uint64_t offset_t
Definition lldb-types.h:86
std::shared_ptr< lldb_private::ObjectFile > ObjectFileSP
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::DataExtractor > DataExtractorSP
std::shared_ptr< lldb_private::Module > ModuleSP