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
61 /// writeTBE() writes a Text-Based ELF stub to a file using the latest version
62 /// of the YAML parser.
writeTBE(StringRef FilePath,ELFStub & Stub)63 static Error writeTBE(StringRef FilePath, ELFStub &Stub) {
64 std::error_code SysErr;
65
66 // Open file for writing.
67 raw_fd_ostream Out(FilePath, SysErr);
68 if (SysErr)
69 return createStringError(SysErr, "Couldn't open `%s` for writing",
70 FilePath.data());
71 // Write file.
72 Error YAMLErr = writeTBEToOutputStream(Out, Stub);
73 if (YAMLErr)
74 return YAMLErr;
75
76 return Error::success();
77 }
78
79 /// readInputFile populates an ELFStub by attempting to read the
80 /// input file using both the TBE and binary ELF parsers.
readInputFile(StringRef FilePath)81 static Expected<std::unique_ptr<ELFStub>> readInputFile(StringRef FilePath) {
82 // Read in file.
83 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
84 MemoryBuffer::getFile(FilePath);
85 if (!BufOrError) {
86 return createStringError(BufOrError.getError(), "Could not open `%s`",
87 FilePath.data());
88 }
89
90 std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
91 ErrorCollector EC(/*UseFatalErrors=*/false);
92
93 // First try to read as a binary (fails fast if not binary).
94 if (InputFileFormat.getNumOccurrences() == 0 ||
95 InputFileFormat == FileFormat::ELF) {
96 Expected<std::unique_ptr<ELFStub>> StubFromELF =
97 readELFFile(FileReadBuffer->getMemBufferRef());
98 if (StubFromELF) {
99 return std::move(*StubFromELF);
100 }
101 EC.addError(StubFromELF.takeError(), "BinaryRead");
102 }
103
104 // Fall back to reading as a tbe.
105 if (InputFileFormat.getNumOccurrences() == 0 ||
106 InputFileFormat == FileFormat::TBE) {
107 Expected<std::unique_ptr<ELFStub>> StubFromTBE =
108 readTBEFromBuffer(FileReadBuffer->getBuffer());
109 if (StubFromTBE) {
110 return std::move(*StubFromTBE);
111 }
112 EC.addError(StubFromTBE.takeError(), "YamlParse");
113 }
114
115 // If both readers fail, build a new error that includes all information.
116 EC.addError(createStringError(errc::not_supported,
117 "No file readers succeeded reading `%s` "
118 "(unsupported/malformed file?)",
119 FilePath.data()),
120 "ReadInputFile");
121 EC.escalateToFatal();
122 return EC.makeError();
123 }
124
fatalError(Error Err)125 static void fatalError(Error Err) {
126 WithColor::defaultErrorHandler(std::move(Err));
127 exit(1);
128 }
129
main(int argc,char * argv[])130 int main(int argc, char *argv[]) {
131 // Parse arguments.
132 cl::ParseCommandLineOptions(argc, argv);
133
134 Expected<std::unique_ptr<ELFStub>> StubOrErr = readInputFile(InputFilePath);
135 if (!StubOrErr)
136 fatalError(StubOrErr.takeError());
137
138 std::unique_ptr<ELFStub> TargetStub = std::move(StubOrErr.get());
139
140 // Change SoName before emitting stubs.
141 if (SOName.getNumOccurrences() == 1)
142 TargetStub->SoName = SOName;
143
144 if (EmitTBE.getNumOccurrences() == 1) {
145 TargetStub->TbeVersion = TBEVersionCurrent;
146 Error TBEWriteError = writeTBE(EmitTBE, *TargetStub);
147 if (TBEWriteError)
148 fatalError(std::move(TBEWriteError));
149 }
150
151 // Write out binary ELF stub.
152 if (BinaryOutputFilePath.getNumOccurrences() == 1) {
153 if (BinaryOutputTarget.getNumOccurrences() == 0)
154 fatalError(createStringError(errc::not_supported,
155 "no binary output target specified."));
156 Error BinaryWriteError =
157 writeBinaryStub(BinaryOutputFilePath, *TargetStub, BinaryOutputTarget);
158 if (BinaryWriteError)
159 fatalError(std::move(BinaryWriteError));
160 }
161 }
162