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