1 | //===- TestingSupport.cpp - Convert objects files into test files --------===// |
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 | #include "llvm/Object/COFF.h" |
10 | #include "llvm/Object/ObjectFile.h" |
11 | #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" |
12 | #include "llvm/ProfileData/InstrProf.h" |
13 | #include "llvm/Support/Alignment.h" |
14 | #include "llvm/Support/CommandLine.h" |
15 | #include "llvm/Support/FileSystem.h" |
16 | #include "llvm/Support/LEB128.h" |
17 | #include "llvm/Support/MemoryBuffer.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | #include <functional> |
20 | #include <system_error> |
21 | |
22 | using namespace llvm; |
23 | using namespace object; |
24 | |
25 | int convertForTestingMain(int argc, const char *argv[]) { |
26 | cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required, |
27 | cl::desc("<Source file>" )); |
28 | |
29 | cl::opt<std::string> OutputFilename( |
30 | "o" , cl::Required, |
31 | cl::desc( |
32 | "File with the profile data obtained after an instrumented run" )); |
33 | |
34 | cl::ParseCommandLineOptions(argc, argv, Overview: "LLVM code coverage tool\n" ); |
35 | |
36 | auto ObjErr = llvm::object::ObjectFile::createObjectFile(ObjectPath: InputSourceFile); |
37 | if (!ObjErr) { |
38 | std::string Buf; |
39 | raw_string_ostream OS(Buf); |
40 | logAllUnhandledErrors(E: ObjErr.takeError(), OS); |
41 | OS.flush(); |
42 | errs() << "error: " << Buf; |
43 | return 1; |
44 | } |
45 | ObjectFile *OF = ObjErr.get().getBinary(); |
46 | auto BytesInAddress = OF->getBytesInAddress(); |
47 | if (BytesInAddress != 8) { |
48 | errs() << "error: 64 bit binary expected\n" ; |
49 | return 1; |
50 | } |
51 | |
52 | // Look for the sections that we are interested in. |
53 | int FoundSectionCount = 0; |
54 | SectionRef ProfileNames, CoverageMapping, CoverageRecords; |
55 | auto ObjFormat = OF->getTripleObjectFormat(); |
56 | |
57 | auto ProfileNamesSection = getInstrProfSectionName(IPSK: IPSK_name, OF: ObjFormat, |
58 | /*AddSegmentInfo=*/false); |
59 | auto CoverageMappingSection = |
60 | getInstrProfSectionName(IPSK: IPSK_covmap, OF: ObjFormat, /*AddSegmentInfo=*/false); |
61 | auto CoverageRecordsSection = |
62 | getInstrProfSectionName(IPSK: IPSK_covfun, OF: ObjFormat, /*AddSegmentInfo=*/false); |
63 | if (isa<object::COFFObjectFile>(Val: OF)) { |
64 | // On COFF, the object file section name may end in "$M". This tells the |
65 | // linker to sort these sections between "$A" and "$Z". The linker removes |
66 | // the dollar and everything after it in the final binary. Do the same to |
67 | // match. |
68 | auto Strip = [](std::string &Str) { |
69 | auto Pos = Str.find(c: '$'); |
70 | if (Pos != std::string::npos) |
71 | Str.resize(n: Pos); |
72 | }; |
73 | Strip(ProfileNamesSection); |
74 | Strip(CoverageMappingSection); |
75 | Strip(CoverageRecordsSection); |
76 | } |
77 | |
78 | for (const auto &Section : OF->sections()) { |
79 | StringRef Name; |
80 | if (Expected<StringRef> NameOrErr = Section.getName()) { |
81 | Name = *NameOrErr; |
82 | } else { |
83 | consumeError(Err: NameOrErr.takeError()); |
84 | return 1; |
85 | } |
86 | |
87 | if (Name == ProfileNamesSection) |
88 | ProfileNames = Section; |
89 | else if (Name == CoverageMappingSection) |
90 | CoverageMapping = Section; |
91 | else if (Name == CoverageRecordsSection) |
92 | CoverageRecords = Section; |
93 | else |
94 | continue; |
95 | ++FoundSectionCount; |
96 | } |
97 | if (FoundSectionCount != 3) |
98 | return 1; |
99 | |
100 | // Get the contents of the given sections. |
101 | uint64_t ProfileNamesAddress = ProfileNames.getAddress(); |
102 | StringRef CoverageMappingData; |
103 | StringRef CoverageRecordsData; |
104 | StringRef ProfileNamesData; |
105 | if (Expected<StringRef> E = CoverageMapping.getContents()) |
106 | CoverageMappingData = *E; |
107 | else { |
108 | consumeError(Err: E.takeError()); |
109 | return 1; |
110 | } |
111 | if (Expected<StringRef> E = CoverageRecords.getContents()) |
112 | CoverageRecordsData = *E; |
113 | else { |
114 | consumeError(Err: E.takeError()); |
115 | return 1; |
116 | } |
117 | if (Expected<StringRef> E = ProfileNames.getContents()) |
118 | ProfileNamesData = *E; |
119 | else { |
120 | consumeError(Err: E.takeError()); |
121 | return 1; |
122 | } |
123 | |
124 | // If this is a linked PE/COFF file, then we have to skip over the null byte |
125 | // that is allocated in the .lprfn$A section in the LLVM profiling runtime. |
126 | if (isa<COFFObjectFile>(Val: OF) && !OF->isRelocatableObject()) |
127 | ProfileNamesData = ProfileNamesData.drop_front(N: 1); |
128 | |
129 | int FD; |
130 | if (auto Err = sys::fs::openFileForWrite(Name: OutputFilename, ResultFD&: FD)) { |
131 | errs() << "error: " << Err.message() << "\n" ; |
132 | return 1; |
133 | } |
134 | |
135 | coverage::TestingFormatWriter Writer(ProfileNamesAddress, ProfileNamesData, |
136 | CoverageMappingData, |
137 | CoverageRecordsData); |
138 | raw_fd_ostream OS(FD, true); |
139 | Writer.write(OS); |
140 | |
141 | return 0; |
142 | } |
143 | |