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
22using namespace llvm;
23using namespace object;
24
25int 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