• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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