[sanitizer] Improve FreeBSD ASLR detection
[llvm-project.git] / llvm / tools / llvm-ifs / llvm-ifs.cpp
blob2dcd0c5ca9e286ff61db86e7fc51d29df3e6fb69
1 //===- llvm-ifs.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 //===-----------------------------------------------------------------------===/
9 #include "ErrorCollector.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/ADT/StringSwitch.h"
12 #include "llvm/ADT/Triple.h"
13 #include "llvm/InterfaceStub/ELFObjHandler.h"
14 #include "llvm/InterfaceStub/IFSHandler.h"
15 #include "llvm/InterfaceStub/IFSStub.h"
16 #include "llvm/ObjectYAML/yaml2obj.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/Debug.h"
19 #include "llvm/Support/Errc.h"
20 #include "llvm/Support/Error.h"
21 #include "llvm/Support/FileOutputBuffer.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/Support/VersionTuple.h"
25 #include "llvm/Support/WithColor.h"
26 #include "llvm/Support/YAMLTraits.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include "llvm/TextAPI/InterfaceFile.h"
29 #include "llvm/TextAPI/TextAPIReader.h"
30 #include "llvm/TextAPI/TextAPIWriter.h"
31 #include <set>
32 #include <string>
33 #include <vector>
35 using namespace llvm;
36 using namespace llvm::yaml;
37 using namespace llvm::MachO;
38 using namespace llvm::ifs;
40 #define DEBUG_TYPE "llvm-ifs"
42 namespace {
43 const VersionTuple IfsVersionCurrent(3, 0);
45 enum class FileFormat { IFS, ELF, TBD };
46 } // end anonymous namespace
48 cl::OptionCategory IfsCategory("Ifs Options");
50 // TODO: Use OptTable for option parsing in the future.
51 // Command line flags:
52 cl::list<std::string> InputFilePaths(cl::Positional, cl::desc("input"),
53 cl::ZeroOrMore, cl::cat(IfsCategory));
54 cl::opt<FileFormat> InputFormat(
55 "input-format", cl::desc("Specify the input file format"),
56 cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"),
57 clEnumValN(FileFormat::ELF, "ELF", "ELF object file")),
58 cl::cat(IfsCategory));
59 cl::opt<FileFormat> OutputFormat(
60 "output-format", cl::desc("Specify the output file format **DEPRECATED**"),
61 cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"),
62 clEnumValN(FileFormat::ELF, "ELF", "ELF stub file"),
63 clEnumValN(FileFormat::TBD, "TBD", "Apple TBD text stub file")),
64 cl::cat(IfsCategory));
65 cl::opt<std::string> OptArch("arch",
66 cl::desc("Specify the architecture, e.g. x86_64"),
67 cl::cat(IfsCategory));
68 cl::opt<IFSBitWidthType>
69 OptBitWidth("bitwidth", cl::desc("Specify the bit width"),
70 cl::values(clEnumValN(IFSBitWidthType::IFS32, "32", "32 bits"),
71 clEnumValN(IFSBitWidthType::IFS64, "64", "64 bits")),
72 cl::cat(IfsCategory));
73 cl::opt<IFSEndiannessType> OptEndianness(
74 "endianness", cl::desc("Specify the endianness"),
75 cl::values(clEnumValN(IFSEndiannessType::Little, "little", "Little Endian"),
76 clEnumValN(IFSEndiannessType::Big, "big", "Big Endian")),
77 cl::cat(IfsCategory));
78 cl::opt<std::string> OptTargetTriple(
79 "target", cl::desc("Specify the target triple, e.g. x86_64-linux-gnu"),
80 cl::cat(IfsCategory));
81 cl::opt<std::string> OptTargetTripleHint(
82 "hint-ifs-target",
83 cl::desc("When --output-format is 'IFS', this flag will hint the expected "
84 "target triple for IFS output"),
85 cl::cat(IfsCategory));
86 cl::opt<bool> StripIFSArch(
87 "strip-ifs-arch",
88 cl::desc("Strip target architecture information away from IFS output"),
89 cl::cat(IfsCategory));
90 cl::opt<bool> StripIFSBitWidth(
91 "strip-ifs-bitwidth",
92 cl::desc("Strip target bit width information away from IFS output"),
93 cl::cat(IfsCategory));
94 cl::opt<bool> StripIFSEndiannessWidth(
95 "strip-ifs-endianness",
96 cl::desc("Strip target endianness information away from IFS output"),
97 cl::cat(IfsCategory));
98 cl::opt<bool> StripIFSTarget(
99 "strip-ifs-target",
100 cl::desc("Strip all target information away from IFS output"),
101 cl::cat(IfsCategory));
102 cl::opt<bool>
103 StripUndefined("strip-undefined",
104 cl::desc("Strip undefined symbols from IFS output"),
105 cl::cat(IfsCategory));
107 cl::opt<std::string>
108 SoName("soname",
109 cl::desc("Manually set the DT_SONAME entry of any emitted files"),
110 cl::value_desc("name"), cl::cat(IfsCategory));
111 cl::opt<std::string> OutputFilePath("output",
112 cl::desc("Output file **DEPRECATED**"),
113 cl::cat(IfsCategory));
114 cl::alias OutputFilePathA("o", cl::desc("Alias for --output"),
115 cl::aliasopt(OutputFilePath), cl::cat(IfsCategory));
116 cl::opt<std::string> OutputELFFilePath("output-elf",
117 cl::desc("Output path for ELF file"),
118 cl::cat(IfsCategory));
119 cl::opt<std::string> OutputIFSFilePath("output-ifs",
120 cl::desc("Output path for IFS file"),
121 cl::cat(IfsCategory));
122 cl::opt<std::string> OutputTBDFilePath("output-tbd",
123 cl::desc("Output path for TBD file"),
124 cl::cat(IfsCategory));
126 cl::opt<bool> WriteIfChanged(
127 "write-if-changed",
128 cl::desc("Write the output file only if it is new or has changed."),
129 cl::cat(IfsCategory));
131 static std::string getTypeName(IFSSymbolType Type) {
132 switch (Type) {
133 case IFSSymbolType::NoType:
134 return "NoType";
135 case IFSSymbolType::Func:
136 return "Func";
137 case IFSSymbolType::Object:
138 return "Object";
139 case IFSSymbolType::TLS:
140 return "TLS";
141 case IFSSymbolType::Unknown:
142 return "Unknown";
144 llvm_unreachable("Unexpected ifs symbol type.");
147 static Expected<std::unique_ptr<IFSStub>> readInputFile(StringRef FilePath) {
148 // Read in file.
149 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
150 MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true);
151 if (!BufOrError)
152 return createStringError(BufOrError.getError(), "Could not open `%s`",
153 FilePath.data());
155 std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
156 ErrorCollector EC(/*UseFatalErrors=*/false);
158 // First try to read as a binary (fails fast if not binary).
159 if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::ELF) {
160 Expected<std::unique_ptr<IFSStub>> StubFromELF =
161 readELFFile(FileReadBuffer->getMemBufferRef());
162 if (StubFromELF) {
163 (*StubFromELF)->IfsVersion = IfsVersionCurrent;
164 return std::move(*StubFromELF);
166 EC.addError(StubFromELF.takeError(), "BinaryRead");
169 // Fall back to reading as a ifs.
170 if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::IFS) {
171 Expected<std::unique_ptr<IFSStub>> StubFromIFS =
172 readIFSFromBuffer(FileReadBuffer->getBuffer());
173 if (StubFromIFS) {
174 if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent)
175 EC.addError(
176 createStringError(errc::not_supported,
177 "IFS version " +
178 (*StubFromIFS)->IfsVersion.getAsString() +
179 " is unsupported."),
180 "ReadInputFile");
181 else
182 return std::move(*StubFromIFS);
183 } else {
184 EC.addError(StubFromIFS.takeError(), "YamlParse");
188 // If both readers fail, build a new error that includes all information.
189 EC.addError(createStringError(errc::not_supported,
190 "No file readers succeeded reading `%s` "
191 "(unsupported/malformed file?)",
192 FilePath.data()),
193 "ReadInputFile");
194 EC.escalateToFatal();
195 return EC.makeError();
198 static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols,
199 const StringRef Format, raw_ostream &Out) {
201 auto PlatformTypeOrError =
202 [](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformType> {
203 if (T.isMacOSX())
204 return llvm::MachO::PLATFORM_MACOS;
205 if (T.isTvOS())
206 return llvm::MachO::PLATFORM_TVOS;
207 if (T.isWatchOS())
208 return llvm::MachO::PLATFORM_WATCHOS;
209 // Note: put isiOS last because tvOS and watchOS are also iOS according
210 // to the Triple.
211 if (T.isiOS())
212 return llvm::MachO::PLATFORM_IOS;
214 return createStringError(errc::not_supported, "Invalid Platform.\n");
215 }(T);
217 if (!PlatformTypeOrError)
218 return -1;
220 PlatformType Plat = PlatformTypeOrError.get();
221 TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)});
223 InterfaceFile File;
224 File.setFileType(FileType::TBD_V3); // Only supporting v3 for now.
225 File.addTargets(Targets);
227 for (const auto &Symbol : Symbols) {
228 auto Name = Symbol.Name;
229 auto Kind = SymbolKind::GlobalSymbol;
230 switch (Symbol.Type) {
231 default:
232 case IFSSymbolType::NoType:
233 Kind = SymbolKind::GlobalSymbol;
234 break;
235 case IFSSymbolType::Object:
236 Kind = SymbolKind::GlobalSymbol;
237 break;
238 case IFSSymbolType::Func:
239 Kind = SymbolKind::GlobalSymbol;
240 break;
242 if (Symbol.Weak)
243 File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined);
244 else
245 File.addSymbol(Kind, Name, Targets);
248 SmallString<4096> Buffer;
249 raw_svector_ostream OS(Buffer);
250 if (Error Result = TextAPIWriter::writeToStream(OS, File))
251 return -1;
252 Out << OS.str();
253 return 0;
256 static void fatalError(Error Err) {
257 WithColor::defaultErrorHandler(std::move(Err));
258 exit(1);
261 /// writeIFS() writes a Text-Based ELF stub to a file using the latest version
262 /// of the YAML parser.
263 static Error writeIFS(StringRef FilePath, IFSStub &Stub) {
264 // Write IFS to memory first.
265 std::string IFSStr;
266 raw_string_ostream OutStr(IFSStr);
267 Error YAMLErr = writeIFSToOutputStream(OutStr, Stub);
268 if (YAMLErr)
269 return YAMLErr;
270 OutStr.flush();
272 if (WriteIfChanged) {
273 if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
274 MemoryBuffer::getFile(FilePath)) {
275 // Compare IFS output with the existing IFS file. If unchanged, avoid changing the file.
276 if ((*BufOrError)->getBuffer() == IFSStr)
277 return Error::success();
280 // Open IFS file for writing.
281 std::error_code SysErr;
282 raw_fd_ostream Out(FilePath, SysErr);
283 if (SysErr)
284 return createStringError(SysErr, "Couldn't open `%s` for writing",
285 FilePath.data());
286 Out << IFSStr;
287 return Error::success();
290 int main(int argc, char *argv[]) {
291 // Parse arguments.
292 cl::HideUnrelatedOptions({&IfsCategory, &getColorCategory()});
293 cl::ParseCommandLineOptions(argc, argv);
295 if (InputFilePaths.empty())
296 InputFilePaths.push_back("-");
298 // If input files are more than one, they can only be IFS files.
299 if (InputFilePaths.size() > 1)
300 InputFormat.setValue(FileFormat::IFS);
302 // Attempt to merge input.
303 IFSStub Stub;
304 std::map<std::string, IFSSymbol> SymbolMap;
305 std::string PreviousInputFilePath;
306 for (const std::string &InputFilePath : InputFilePaths) {
307 Expected<std::unique_ptr<IFSStub>> StubOrErr = readInputFile(InputFilePath);
308 if (!StubOrErr)
309 fatalError(StubOrErr.takeError());
311 std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get());
312 if (PreviousInputFilePath.empty()) {
313 Stub.IfsVersion = TargetStub->IfsVersion;
314 Stub.Target = TargetStub->Target;
315 Stub.SoName = TargetStub->SoName;
316 Stub.NeededLibs = TargetStub->NeededLibs;
317 } else {
318 if (Stub.IfsVersion != TargetStub->IfsVersion) {
319 if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
320 WithColor::error()
321 << "Interface Stub: IfsVersion Mismatch."
322 << "\nFilenames: " << PreviousInputFilePath << " "
323 << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion
324 << " " << TargetStub->IfsVersion << "\n";
325 return -1;
327 if (TargetStub->IfsVersion > Stub.IfsVersion)
328 Stub.IfsVersion = TargetStub->IfsVersion;
330 if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) {
331 WithColor::error() << "Interface Stub: Target Mismatch."
332 << "\nFilenames: " << PreviousInputFilePath << " "
333 << InputFilePath;
334 return -1;
336 if (Stub.SoName != TargetStub->SoName) {
337 WithColor::error() << "Interface Stub: SoName Mismatch."
338 << "\nFilenames: " << PreviousInputFilePath << " "
339 << InputFilePath
340 << "\nSoName Values: " << Stub.SoName << " "
341 << TargetStub->SoName << "\n";
342 return -1;
344 if (Stub.NeededLibs != TargetStub->NeededLibs) {
345 WithColor::error() << "Interface Stub: NeededLibs Mismatch."
346 << "\nFilenames: " << PreviousInputFilePath << " "
347 << InputFilePath << "\n";
348 return -1;
352 for (auto Symbol : TargetStub->Symbols) {
353 auto SI = SymbolMap.find(Symbol.Name);
354 if (SI == SymbolMap.end()) {
355 SymbolMap.insert(
356 std::pair<std::string, IFSSymbol>(Symbol.Name, Symbol));
357 continue;
360 assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match.");
362 // Check conflicts:
363 if (Symbol.Type != SI->second.Type) {
364 WithColor::error() << "Interface Stub: Type Mismatch for "
365 << Symbol.Name << ".\nFilename: " << InputFilePath
366 << "\nType Values: " << getTypeName(SI->second.Type)
367 << " " << getTypeName(Symbol.Type) << "\n";
369 return -1;
371 if (Symbol.Size != SI->second.Size) {
372 WithColor::error() << "Interface Stub: Size Mismatch for "
373 << Symbol.Name << ".\nFilename: " << InputFilePath
374 << "\nSize Values: " << SI->second.Size << " "
375 << Symbol.Size << "\n";
377 return -1;
379 if (Symbol.Weak != SI->second.Weak) {
380 Symbol.Weak = false;
381 continue;
383 // TODO: Not checking Warning. Will be dropped.
386 PreviousInputFilePath = InputFilePath;
389 if (Stub.IfsVersion != IfsVersionCurrent)
390 if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
391 WithColor::error() << "Interface Stub: Bad IfsVersion: "
392 << Stub.IfsVersion << ", llvm-ifs supported version: "
393 << IfsVersionCurrent << ".\n";
394 return -1;
397 for (auto &Entry : SymbolMap)
398 Stub.Symbols.push_back(Entry.second);
400 // Change SoName before emitting stubs.
401 if (SoName.getNumOccurrences() == 1)
402 Stub.SoName = SoName;
403 Optional<IFSArch> OverrideArch;
404 Optional<IFSEndiannessType> OverrideEndianness;
405 Optional<IFSBitWidthType> OverrideBitWidth;
406 Optional<std::string> OverrideTriple;
407 if (OptArch.getNumOccurrences() == 1)
408 OverrideArch = ELF::convertArchNameToEMachine(OptArch.getValue());
409 if (OptEndianness.getNumOccurrences() == 1)
410 OverrideEndianness = OptEndianness.getValue();
411 if (OptBitWidth.getNumOccurrences() == 1)
412 OverrideBitWidth = OptBitWidth.getValue();
413 if (OptTargetTriple.getNumOccurrences() == 1)
414 OverrideTriple = OptTargetTriple.getValue();
415 Error OverrideError = overrideIFSTarget(
416 Stub, OverrideArch, OverrideEndianness, OverrideBitWidth, OverrideTriple);
417 if (OverrideError)
418 fatalError(std::move(OverrideError));
420 if (OutputELFFilePath.getNumOccurrences() == 0 &&
421 OutputIFSFilePath.getNumOccurrences() == 0 &&
422 OutputTBDFilePath.getNumOccurrences() == 0) {
423 if (OutputFormat.getNumOccurrences() == 0) {
424 WithColor::error() << "at least one output should be specified.";
425 return -1;
427 } else if (OutputFormat.getNumOccurrences() == 1) {
428 WithColor::error() << "'--output-format' cannot be used with "
429 "'--output-{FILE_FORMAT}' options at the same time";
430 return -1;
432 if (OutputFormat.getNumOccurrences() == 1) {
433 // TODO: Remove OutputFormat flag in the next revision.
434 WithColor::warning() << "--output-format option is deprecated, please use "
435 "--output-{FILE_FORMAT} options instead\n";
436 switch (OutputFormat.getValue()) {
437 case FileFormat::TBD: {
438 std::error_code SysErr;
439 raw_fd_ostream Out(OutputFilePath, SysErr);
440 if (SysErr) {
441 WithColor::error() << "Couldn't open " << OutputFilePath
442 << " for writing.\n";
443 return -1;
445 if (!Stub.Target.Triple) {
446 WithColor::error()
447 << "Triple should be defined when output format is TBD";
448 return -1;
450 return writeTbdStub(llvm::Triple(Stub.Target.Triple.getValue()),
451 Stub.Symbols, "TBD", Out);
453 case FileFormat::IFS: {
454 Stub.IfsVersion = IfsVersionCurrent;
455 if (InputFormat.getValue() == FileFormat::ELF &&
456 OptTargetTripleHint.getNumOccurrences() == 1) {
457 std::error_code HintEC(1, std::generic_category());
458 IFSTarget HintTarget = parseTriple(OptTargetTripleHint);
459 if (Stub.Target.Arch.getValue() != HintTarget.Arch.getValue())
460 fatalError(make_error<StringError>(
461 "Triple hint does not match the actual architecture", HintEC));
462 if (Stub.Target.Endianness.getValue() !=
463 HintTarget.Endianness.getValue())
464 fatalError(make_error<StringError>(
465 "Triple hint does not match the actual endianness", HintEC));
466 if (Stub.Target.BitWidth.getValue() != HintTarget.BitWidth.getValue())
467 fatalError(make_error<StringError>(
468 "Triple hint does not match the actual bit width", HintEC));
470 stripIFSTarget(Stub, true, false, false, false);
471 Stub.Target.Triple = OptTargetTripleHint.getValue();
472 } else {
473 stripIFSTarget(Stub, StripIFSTarget, StripIFSArch,
474 StripIFSEndiannessWidth, StripIFSBitWidth);
476 if (StripUndefined)
477 stripIFSUndefinedSymbols(Stub);
478 Error IFSWriteError = writeIFS(OutputFilePath.getValue(), Stub);
479 if (IFSWriteError)
480 fatalError(std::move(IFSWriteError));
481 break;
483 case FileFormat::ELF: {
484 Error TargetError = validateIFSTarget(Stub, true);
485 if (TargetError)
486 fatalError(std::move(TargetError));
487 Error BinaryWriteError =
488 writeBinaryStub(OutputFilePath, Stub, WriteIfChanged);
489 if (BinaryWriteError)
490 fatalError(std::move(BinaryWriteError));
491 break;
494 } else {
495 // Check if output path for individual format.
496 if (OutputELFFilePath.getNumOccurrences() == 1) {
497 Error TargetError = validateIFSTarget(Stub, true);
498 if (TargetError)
499 fatalError(std::move(TargetError));
500 Error BinaryWriteError =
501 writeBinaryStub(OutputELFFilePath, Stub, WriteIfChanged);
502 if (BinaryWriteError)
503 fatalError(std::move(BinaryWriteError));
505 if (OutputIFSFilePath.getNumOccurrences() == 1) {
506 Stub.IfsVersion = IfsVersionCurrent;
507 if (InputFormat.getValue() == FileFormat::ELF &&
508 OptTargetTripleHint.getNumOccurrences() == 1) {
509 std::error_code HintEC(1, std::generic_category());
510 IFSTarget HintTarget = parseTriple(OptTargetTripleHint);
511 if (Stub.Target.Arch.getValue() != HintTarget.Arch.getValue())
512 fatalError(make_error<StringError>(
513 "Triple hint does not match the actual architecture", HintEC));
514 if (Stub.Target.Endianness.getValue() !=
515 HintTarget.Endianness.getValue())
516 fatalError(make_error<StringError>(
517 "Triple hint does not match the actual endianness", HintEC));
518 if (Stub.Target.BitWidth.getValue() != HintTarget.BitWidth.getValue())
519 fatalError(make_error<StringError>(
520 "Triple hint does not match the actual bit width", HintEC));
522 stripIFSTarget(Stub, true, false, false, false);
523 Stub.Target.Triple = OptTargetTripleHint.getValue();
524 } else {
525 stripIFSTarget(Stub, StripIFSTarget, StripIFSArch,
526 StripIFSEndiannessWidth, StripIFSBitWidth);
528 if (StripUndefined)
529 stripIFSUndefinedSymbols(Stub);
530 Error IFSWriteError = writeIFS(OutputIFSFilePath.getValue(), Stub);
531 if (IFSWriteError)
532 fatalError(std::move(IFSWriteError));
534 if (OutputTBDFilePath.getNumOccurrences() == 1) {
535 std::error_code SysErr;
536 raw_fd_ostream Out(OutputTBDFilePath, SysErr);
537 if (SysErr) {
538 WithColor::error() << "Couldn't open " << OutputTBDFilePath
539 << " for writing.\n";
540 return -1;
542 if (!Stub.Target.Triple) {
543 WithColor::error()
544 << "Triple should be defined when output format is TBD";
545 return -1;
547 return writeTbdStub(llvm::Triple(Stub.Target.Triple.getValue()),
548 Stub.Symbols, "TBD", Out);
551 return 0;