llvm-elfabi.cpp 6.3 KB

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