xref: /netbsd-src/external/apache2/llvm/dist/llvm/tools/llvm-elfabi/llvm-elfabi.cpp (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 //===- llvm-elfabi.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 #include "ErrorCollector.h"
10 #include "llvm/InterfaceStub/ELFObjHandler.h"
11 #include "llvm/InterfaceStub/TBEHandler.h"
12 #include "llvm/Support/CommandLine.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/FileOutputBuffer.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/WithColor.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include <string>
20 
21 namespace llvm {
22 namespace elfabi {
23 
24 enum class FileFormat { TBE, ELF };
25 
26 } // end namespace elfabi
27 } // end namespace llvm
28 
29 using namespace llvm;
30 using namespace llvm::elfabi;
31 
32 // Command line flags:
33 cl::opt<FileFormat> InputFileFormat(
34     cl::desc("Force input file format:"),
35     cl::values(clEnumValN(FileFormat::TBE, "tbe",
36                           "Read `input` as text-based ELF stub"),
37                clEnumValN(FileFormat::ELF, "elf",
38                           "Read `input` as ELF binary")));
39 cl::opt<std::string> InputFilePath(cl::Positional, cl::desc("input"),
40                                    cl::Required);
41 cl::opt<std::string>
42     EmitTBE("emit-tbe",
43             cl::desc("Emit a text-based ELF stub (.tbe) from the input file"),
44             cl::value_desc("path"));
45 cl::opt<std::string>
46     SOName("soname",
47            cl::desc("Manually set the DT_SONAME entry of any emitted files"),
48            cl::value_desc("name"));
49 cl::opt<ELFTarget> BinaryOutputTarget(
50     "output-target", cl::desc("Create a binary stub for the specified target"),
51     cl::values(clEnumValN(ELFTarget::ELF32LE, "elf32-little",
52                           "32-bit little-endian ELF stub"),
53                clEnumValN(ELFTarget::ELF32BE, "elf32-big",
54                           "32-bit big-endian ELF stub"),
55                clEnumValN(ELFTarget::ELF64LE, "elf64-little",
56                           "64-bit little-endian ELF stub"),
57                clEnumValN(ELFTarget::ELF64BE, "elf64-big",
58                           "64-bit big-endian ELF stub")));
59 cl::opt<std::string> BinaryOutputFilePath(cl::Positional, cl::desc("output"));
60 cl::opt<bool> WriteIfChanged(
61     "write-if-changed",
62     cl::desc("Write the output file only if it is new or has changed."));
63 
64 /// writeTBE() writes a Text-Based ELF stub to a file using the latest version
65 /// of the YAML parser.
writeTBE(StringRef FilePath,ELFStub & Stub)66 static Error writeTBE(StringRef FilePath, ELFStub &Stub) {
67   // Write TBE to memory first.
68   std::string TBEStr;
69   raw_string_ostream OutStr(TBEStr);
70   Error YAMLErr = writeTBEToOutputStream(OutStr, Stub);
71   if (YAMLErr)
72     return YAMLErr;
73   OutStr.flush();
74 
75   if (WriteIfChanged) {
76     if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
77             MemoryBuffer::getFile(FilePath)) {
78       // Compare TBE output with existing TBE file.
79       // If TBE file unchanged, abort updating.
80       if ((*BufOrError)->getBuffer() == TBEStr)
81         return Error::success();
82     }
83   }
84   // Open TBE file for writing.
85   std::error_code SysErr;
86   raw_fd_ostream Out(FilePath, SysErr);
87   if (SysErr)
88     return createStringError(SysErr, "Couldn't open `%s` for writing",
89                              FilePath.data());
90   Out << TBEStr;
91   return Error::success();
92 }
93 
94 /// readInputFile populates an ELFStub by attempting to read the
95 /// input file using both the TBE and binary ELF parsers.
readInputFile(StringRef FilePath)96 static Expected<std::unique_ptr<ELFStub>> readInputFile(StringRef FilePath) {
97   // Read in file.
98   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
99       MemoryBuffer::getFile(FilePath);
100   if (!BufOrError) {
101     return createStringError(BufOrError.getError(), "Could not open `%s`",
102                              FilePath.data());
103   }
104 
105   std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
106   ErrorCollector EC(/*UseFatalErrors=*/false);
107 
108   // First try to read as a binary (fails fast if not binary).
109   if (InputFileFormat.getNumOccurrences() == 0 ||
110       InputFileFormat == FileFormat::ELF) {
111     Expected<std::unique_ptr<ELFStub>> StubFromELF =
112         readELFFile(FileReadBuffer->getMemBufferRef());
113     if (StubFromELF) {
114       return std::move(*StubFromELF);
115     }
116     EC.addError(StubFromELF.takeError(), "BinaryRead");
117   }
118 
119   // Fall back to reading as a tbe.
120   if (InputFileFormat.getNumOccurrences() == 0 ||
121       InputFileFormat == FileFormat::TBE) {
122     Expected<std::unique_ptr<ELFStub>> StubFromTBE =
123         readTBEFromBuffer(FileReadBuffer->getBuffer());
124     if (StubFromTBE) {
125       return std::move(*StubFromTBE);
126     }
127     EC.addError(StubFromTBE.takeError(), "YamlParse");
128   }
129 
130   // If both readers fail, build a new error that includes all information.
131   EC.addError(createStringError(errc::not_supported,
132                                 "No file readers succeeded reading `%s` "
133                                 "(unsupported/malformed file?)",
134                                 FilePath.data()),
135               "ReadInputFile");
136   EC.escalateToFatal();
137   return EC.makeError();
138 }
139 
fatalError(Error Err)140 static void fatalError(Error Err) {
141   WithColor::defaultErrorHandler(std::move(Err));
142   exit(1);
143 }
144 
main(int argc,char * argv[])145 int main(int argc, char *argv[]) {
146   // Parse arguments.
147   cl::ParseCommandLineOptions(argc, argv);
148 
149   Expected<std::unique_ptr<ELFStub>> StubOrErr = readInputFile(InputFilePath);
150   if (!StubOrErr)
151     fatalError(StubOrErr.takeError());
152 
153   std::unique_ptr<ELFStub> TargetStub = std::move(StubOrErr.get());
154 
155   // Change SoName before emitting stubs.
156   if (SOName.getNumOccurrences() == 1)
157     TargetStub->SoName = SOName;
158 
159   if (EmitTBE.getNumOccurrences() == 1) {
160     TargetStub->TbeVersion = TBEVersionCurrent;
161     Error TBEWriteError = writeTBE(EmitTBE, *TargetStub);
162     if (TBEWriteError)
163       fatalError(std::move(TBEWriteError));
164   }
165 
166   // Write out binary ELF stub.
167   if (BinaryOutputFilePath.getNumOccurrences() == 1) {
168     if (BinaryOutputTarget.getNumOccurrences() == 0)
169       fatalError(createStringError(errc::not_supported,
170                                    "no binary output target specified."));
171     Error BinaryWriteError = writeBinaryStub(
172         BinaryOutputFilePath, *TargetStub, BinaryOutputTarget, WriteIfChanged);
173     if (BinaryWriteError)
174       fatalError(std::move(BinaryWriteError));
175   }
176 }
177