Skip to content

Commit 11f7bc6

Browse files
Dirk MG Seynhaeveaejjehint
andauthored
Fix offload extract (#10257)
- More comments for maintainability. - Extract relevant sections up front to speed up the embedded binary search. - Provide better diagnostics (especially providing info on the result files). - Fix code to work on Windows (COFF files). --------- Co-authored-by: Ejjeh <[email protected]>
1 parent 37bedf8 commit 11f7bc6

File tree

2 files changed

+145
-103
lines changed

2 files changed

+145
-103
lines changed

clang/test/Driver/clang-offload-extract.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
// Extract target images (deprecated use model)
3535
//
3636
// RUN: clang-offload-extract --output=%t.deprecated %t.fat.bin | FileCheck %s --check-prefix CHECK-DEPRECATE
37-
// CHECK-DEPRECATE: Saving target image to
38-
// CHECK-DEPRECATE: Saving target image to
37+
// CHECK-DEPRECATE: Section {{.*}}: Image 1'-> File
38+
// CHECK-DEPRECATE: Section {{.*}}: Image 2'-> File
3939

4040
//
4141
// Check that extracted contents match the original images.
@@ -47,8 +47,8 @@
4747
// Extract target images (new use model)
4848
//
4949
// RUN: clang-offload-extract --stem=%t.extracted %t.fat.bin | FileCheck %s --check-prefix CHECK-EXTRACT
50-
// CHECK-EXTRACT: Saving target image to
51-
// CHECK-EXTRACT: Saving target image to
50+
// CHECK-EXTRACT: Section {{.*}}: Image 1'-> File
51+
// CHECK-EXTRACT: Section {{.*}}: Image 2'-> File
5252

5353
//
5454
// Check that extracted contents match the original images.

clang/tools/clang-offload-extract/ClangOffloadExtract.cpp

Lines changed: 141 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,24 @@
2828
#include "llvm/Support/raw_ostream.h"
2929

3030
#define IMAGE_INFO_SECTION_NAME ".tgtimg"
31+
#define IMAGE_SECTION_NAME_PREFIX "__CLANG_OFFLOAD_BUNDLE__"
32+
// Windows truncates the names of sections to 8 bytes
33+
#define IMAGE_SECTION_NAME_PREFIX_COFF "__CLANG_"
34+
35+
#define DEBUG_TYPE "clang-offload-extract"
3136

3237
using namespace llvm;
3338
using namespace llvm::object;
3439

40+
// Command-line parsing
3541
// Create a category to label utility-specific options; This will allow
3642
// us to distinguish those specific options from generic options and
3743
// irrelevant options
3844
static cl::OptionCategory
3945
ClangOffloadExtractCategory("Utility-specific options");
4046

41-
// Create options for the input and output files, each with an
42-
// appropriate default when not specified
47+
// Create options for the input (fat binary) and output (target images)
48+
// files, each with an appropriate default when not specified
4349
static cl::opt<std::string> Input(cl::Positional, cl::init("a.out"),
4450
cl::desc("<input file>"),
4551
cl::cat(ClangOffloadExtractCategory));
@@ -61,10 +67,8 @@ static cl::alias FileNameStemAlias(
6167
"output", cl::desc("Deprecated option, replaced by option '--stem'"),
6268
cl::aliasopt(FileNameStem), cl::cat(ClangOffloadExtractCategory));
6369

64-
// Path to the current binary
65-
static std::string ToolPath;
66-
6770
// Report error (and handle any deferred errors)
71+
// This is the main diagnostic used by the utility
6872
static void reportError(Error E, Twine message = "\n") {
6973
std::string S;
7074
raw_string_ostream OSS(S);
@@ -80,7 +84,6 @@ static void reportError(Error E, Twine message = "\n") {
8084

8185
int main(int argc, const char **argv) {
8286
sys::PrintStackTraceOnErrorSignal(argv[0]);
83-
ToolPath = argv[0];
8487

8588
// Hide non-generic options that are not in this utility's explicit
8689
// category;
@@ -138,105 +141,144 @@ linked fat binary, and store them in separate files.
138141
}
139142

140143
// We are dealing with an appropriate fat binary;
141-
// Locate the section IMAGE_INFO_SECTION_NAME (which contains the
142-
// metadata on the embedded binaries)
143-
unsigned FileNum = 0;
144-
144+
// * Create an array all the sections that have
145+
// IMAGE_SECTION_NAME in the section name:
146+
auto OffloadSections = SmallVector<SectionRef>();
147+
// * Locate the section that starts with IMAGE_INFO_SECTION_NAME_PREFIX
148+
// and extract the index for all the embedded binaries:
149+
auto OffloadIndex = SmallVector<SectionRef>();
145150
for (const auto &Section : Binary->sections()) {
146-
Expected<StringRef> NameOrErr = Section.getName();
147-
if (auto E = NameOrErr.takeError()) {
151+
Expected<StringRef> InfoSecNameOrErr = Section.getName();
152+
if (auto E = InfoSecNameOrErr.takeError()) {
148153
reportError(std::move(E), "Input File: '" + Input + "'\n");
149154
}
150-
if (*NameOrErr != IMAGE_INFO_SECTION_NAME)
151-
continue;
155+
LLVM_DEBUG(dbgs() << "Section: " << *InfoSecNameOrErr << "\n");
152156

153-
// This is the section we are looking for;
154-
// Extract the meta information:
155-
// The IMAGE_INFO_SECTION_NAME section contains packed <address,
156-
// size> pairs describing target images that are stored in the fat
157-
// binary.
158-
Expected<StringRef> DataOrErr = Section.getContents();
159-
if (auto E = DataOrErr.takeError()) {
160-
reportError(std::move(E), "Input File: '" + Input + "'\n");
157+
// We have a valid section name
158+
std::string SectionNameToCompare = isa<COFFObjectFile>(Binary)
159+
? IMAGE_SECTION_NAME_PREFIX_COFF
160+
: IMAGE_SECTION_NAME_PREFIX;
161+
if (InfoSecNameOrErr->find(SectionNameToCompare) != std::string::npos) {
162+
// This section contains embedded binaries
163+
OffloadSections.push_back(Section);
164+
} else if (*InfoSecNameOrErr == IMAGE_INFO_SECTION_NAME) {
165+
// This section is the index
166+
OffloadIndex.push_back(Section);
161167
}
162-
// Data type to store the metadata for an individual target image
163-
struct ImgInfoType {
164-
uintptr_t Addr;
165-
uintptr_t Size;
166-
};
167-
168-
// Store the metadata for all target images in an array of target
169-
// image information descriptors
170-
auto ImgInfo =
171-
ArrayRef(reinterpret_cast<const ImgInfoType *>(DataOrErr->data()),
172-
DataOrErr->size() / sizeof(ImgInfoType));
173-
174-
// Loop over the image information descriptors to extract each
175-
// target image.
176-
for (const auto &Img : ImgInfo) {
177-
// Ignore zero padding that can be inserted by the linker.
178-
if (!Img.Addr)
179-
continue;
180-
181-
// Find section which contains this image.
182-
// TODO: can use more efficient algorithm than linear search. For
183-
// example sections and images could be sorted by address then one pass
184-
// performed through both at the same time.
185-
// std::find_if
186-
// * searches for a true predicate in [first,last] =~ [first,end)
187-
// * returns end if no predicate is true
188-
// It is probably faster to track success through a bool (ImgFound)
189-
bool ImgFound = false;
190-
auto ImgSec =
191-
find_if(Binary->sections(), [&Img, &ImgFound](SectionRef Sec) {
192-
bool pred = ( //
193-
Sec.isData() //
194-
&& (Img.Addr == Sec.getAddress()) //
195-
&& (Img.Size == Sec.getSize()) //
196-
);
197-
ImgFound = ImgFound || pred;
198-
return pred;
199-
});
200-
if (!ImgFound) {
201-
202-
reportError(
203-
createStringError(
204-
inconvertibleErrorCode(),
205-
"cannot find section containing <0x%lx, 0x%lx> target image",
206-
Img.Addr, Img.Size),
207-
"Input File: '" + Input + "'\n");
208-
}
209-
210-
Expected<StringRef> SecDataOrErr = ImgSec->getContents();
211-
if (auto E = SecDataOrErr.takeError()) {
212-
reportError(std::move(E), "Input File: '" + Input + "'\n");
213-
}
214-
215-
// Output file name is composed from the name prefix provided by the
216-
// user and the image number which is appended to the prefix
217-
std::string FileName = FileNameStem + "." + std::to_string(FileNum++);
218-
219-
// Tell user that we are saving an image.
220-
outs() << "Saving target image to '" << FileName << "'\n";
221-
222-
// Write image data to the output
223-
std::error_code EC;
224-
raw_fd_ostream OS(FileName, EC);
225-
if (EC) {
226-
reportError(createFileError(FileName, EC),
227-
"Specify a different Output File ('--stem' option)\n");
228-
}
229-
230-
OS << SecDataOrErr->substr(Img.Addr - ImgSec->getAddress(), Img.Size);
231-
if (OS.has_error()) {
232-
reportError(createFileError(FileName, OS.error()),
233-
"Try a different Output File ('--stem' option)");
234-
}
235-
} // &Img: ImgInfo
236-
237-
// Fat binary is not expected to have more than one .tgtimg section.
238-
break;
239168
}
240169

170+
// Check if there are any sections with embedded binaries
171+
if (OffloadSections.size() == 0) {
172+
reportError(
173+
createStringError(inconvertibleErrorCode(),
174+
"Could not locate sections with offload binaries"),
175+
"Fat Binary: '" + Input + "'\n");
176+
}
177+
// Check if we found the index section
178+
if (OffloadIndex.size() == 0) {
179+
reportError(
180+
createStringError(inconvertibleErrorCode(),
181+
"Could not locate index for embedded binaries"),
182+
"Fat Binary: '" + Input + "'\n");
183+
}
184+
// Check if we have a valid index section
185+
Expected<StringRef> DataOrErr = OffloadIndex[0].getContents();
186+
if (auto E = DataOrErr.takeError()) {
187+
reportError(std::move(E), "Input File: '" + Input + "'\n");
188+
}
189+
190+
// The input binary contains embedded offload binaries
191+
// The index section contains packed <address, size> pairs describing
192+
// target images that are stored in the fat binary.
193+
// Data type to store the index for an individual target image
194+
struct ImgInfoType {
195+
uintptr_t Addr;
196+
uintptr_t Size;
197+
};
198+
199+
// Store the metadata for all target images in an array of target
200+
// image information descriptors
201+
// This can be done by reinterpreting the content of the section
202+
auto ImgInfo =
203+
ArrayRef(reinterpret_cast<const ImgInfoType *>(DataOrErr->data()),
204+
DataOrErr->size() / sizeof(ImgInfoType));
205+
206+
// Loop over the image information descriptors to extract each
207+
// target image the object file data
208+
unsigned FileNum = 0;
209+
unsigned ImgCnt = 1;
210+
211+
for (const auto &Img : ImgInfo) {
212+
// Ignore zero padding that can be inserted by the linker.
213+
if (!Img.Addr)
214+
continue;
215+
216+
// Find section which contains this image.
217+
// /!\ There might be multiple images in a section
218+
// std::find_if
219+
// * searches for a true predicate in [first,last] =~ [first,end)
220+
// * returns end if no predicate is true
221+
// It is probably faster to track success through a bool (ImgFound)
222+
bool ImgFound = false;
223+
auto ImgSec = find_if(OffloadSections, [&Img, &ImgFound](auto Sec) {
224+
bool pred = ( //
225+
Sec.isData() //
226+
&& (Img.Addr >= Sec.getAddress()) //
227+
&& ((Img.Addr + Img.Size) <= (Sec.getAddress() + Sec.getSize())) //
228+
);
229+
ImgFound = ImgFound || pred;
230+
return pred;
231+
});
232+
if (!ImgFound) {
233+
234+
reportError(
235+
createStringError(inconvertibleErrorCode(),
236+
"Target image (Address:0x%lx, Size:0x%lx>) is "
237+
"not contained in any section",
238+
Img.Addr, Img.Size),
239+
"Fat Binary: '" + Input + "'\n");
240+
}
241+
242+
Expected<StringRef> ImgSecNameOrErr = ImgSec->getName();
243+
if (auto E = ImgSecNameOrErr.takeError()) {
244+
reportError(std::move(E),
245+
"Can not determine section name in Fat Binary '" + Input +
246+
"'\n");
247+
}
248+
Expected<StringRef> SecDataOrErr = ImgSec->getContents();
249+
if (auto E = SecDataOrErr.takeError()) {
250+
reportError(std::move(E), "Can not extract contents for section '" +
251+
*ImgSecNameOrErr + "' in Fat Binary '" +
252+
Input + "'\n");
253+
}
254+
255+
// Output file name is composed from the name prefix provided by the
256+
// user and the image number which is appended to the prefix
257+
std::string FileName = FileNameStem + "." + std::to_string(FileNum++);
258+
std::string OffloadName = ImgSecNameOrErr->data();
259+
std::string OffloadPrefix = isa<COFFObjectFile>(Binary)
260+
? IMAGE_SECTION_NAME_PREFIX_COFF
261+
: IMAGE_SECTION_NAME_PREFIX;
262+
OffloadName.erase(0, OffloadPrefix.length());
263+
264+
// Tell user that we are saving an image.
265+
outs() << "Section '" + OffloadName + "': Image " << ImgCnt++
266+
<< "'-> File '" + FileName + "'\n";
267+
268+
// Write image data to the output
269+
std::error_code EC;
270+
raw_fd_ostream OS(FileName, EC);
271+
if (EC) {
272+
reportError(createFileError(FileName, EC),
273+
"Specify a different Output File ('--stem' option)\n");
274+
}
275+
276+
OS << SecDataOrErr->substr(Img.Addr - ImgSec->getAddress(), Img.Size);
277+
if (OS.has_error()) {
278+
reportError(createFileError(FileName, OS.error()),
279+
"Try a different Output File ('--stem' option)");
280+
}
281+
} // &Img: ImgInfo
282+
241283
return 0;
242284
}

0 commit comments

Comments
 (0)