xref: /llvm-project-15.0.7/lld/MachO/Driver.cpp (revision bf02bcff)
1 //===- Driver.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 "Driver.h"
10 #include "Config.h"
11 #include "InputFiles.h"
12 #include "OutputSection.h"
13 #include "OutputSegment.h"
14 #include "SymbolTable.h"
15 #include "Symbols.h"
16 #include "Target.h"
17 #include "Writer.h"
18 
19 #include "lld/Common/Args.h"
20 #include "lld/Common/Driver.h"
21 #include "lld/Common/ErrorHandler.h"
22 #include "lld/Common/LLVM.h"
23 #include "lld/Common/Memory.h"
24 #include "lld/Common/Version.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/BinaryFormat/MachO.h"
28 #include "llvm/BinaryFormat/Magic.h"
29 #include "llvm/Object/Archive.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/Option.h"
32 #include "llvm/Support/MemoryBuffer.h"
33 #include "llvm/Support/Path.h"
34 
35 using namespace llvm;
36 using namespace llvm::MachO;
37 using namespace llvm::sys;
38 using namespace lld;
39 using namespace lld::macho;
40 
41 Configuration *lld::macho::config;
42 
43 // Create prefix string literals used in Options.td
44 #define PREFIX(NAME, VALUE) const char *NAME[] = VALUE;
45 #include "Options.inc"
46 #undef PREFIX
47 
48 // Create table mapping all options defined in Options.td
49 static const opt::OptTable::Info optInfo[] = {
50 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
51   {X1, X2, X10,         X11,         OPT_##ID, opt::Option::KIND##Class,       \
52    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
53 #include "Options.inc"
54 #undef OPTION
55 };
56 
57 MachOOptTable::MachOOptTable() : OptTable(optInfo) {}
58 
59 opt::InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) {
60   // Make InputArgList from string vectors.
61   unsigned missingIndex;
62   unsigned missingCount;
63   SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
64 
65   opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount);
66 
67   if (missingCount)
68     error(Twine(args.getArgString(missingIndex)) + ": missing argument");
69 
70   for (opt::Arg *arg : args.filtered(OPT_UNKNOWN))
71     error("unknown argument: " + arg->getSpelling());
72   return args;
73 }
74 
75 // This is for -lfoo. We'll look for libfoo.dylib from search paths.
76 static Optional<std::string> findDylib(StringRef name) {
77   for (StringRef dir : config->searchPaths) {
78     std::string path = (dir + "/lib" + name + ".dylib").str();
79     if (fs::exists(path))
80       return path;
81   }
82   error("library not found for -l" + name);
83   return None;
84 }
85 
86 static TargetInfo *createTargetInfo(opt::InputArgList &args) {
87   StringRef s = args.getLastArgValue(OPT_arch, "x86_64");
88   if (s != "x86_64")
89     error("missing or unsupported -arch " + s);
90   return createX86_64TargetInfo();
91 }
92 
93 static std::vector<StringRef> getSearchPaths(opt::InputArgList &args) {
94   std::vector<StringRef> ret{args::getStrings(args, OPT_L)};
95   if (!args.hasArg(OPT_Z)) {
96     ret.push_back("/usr/lib");
97     ret.push_back("/usr/local/lib");
98   }
99   return ret;
100 }
101 
102 static void addFile(StringRef path) {
103   Optional<MemoryBufferRef> buffer = readFile(path);
104   if (!buffer)
105     return;
106   MemoryBufferRef mbref = *buffer;
107 
108   switch (identify_magic(mbref.getBuffer())) {
109   case file_magic::archive: {
110     std::unique_ptr<object::Archive> file = CHECK(
111         object::Archive::create(mbref), path + ": failed to parse archive");
112 
113     if (!file->isEmpty() && !file->hasSymbolTable())
114       error(path + ": archive has no index; run ranlib to add one");
115 
116     inputFiles.push_back(make<ArchiveFile>(std::move(file)));
117     break;
118   }
119   case file_magic::macho_object:
120     inputFiles.push_back(make<ObjFile>(mbref));
121     break;
122   case file_magic::macho_dynamically_linked_shared_lib:
123     inputFiles.push_back(make<DylibFile>(mbref));
124     break;
125   default:
126     error(path + ": unhandled file type");
127   }
128 }
129 
130 // We expect sub-library names of the form "libfoo", which will match a dylib
131 // with a path of .*/libfoo.dylib.
132 static bool markSubLibrary(StringRef searchName) {
133   for (InputFile *file : inputFiles) {
134     if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
135       StringRef filename = path::filename(dylibFile->getName());
136       if (filename.consume_front(searchName) && filename == ".dylib") {
137         dylibFile->reexport = true;
138         return true;
139       }
140     }
141   }
142   return false;
143 }
144 
145 static void handlePlatformVersion(opt::ArgList::iterator &it,
146                                   const opt::ArgList::iterator &end) {
147   // -platform_version takes 3 args, which LLVM's option library doesn't
148   // support directly.  So this explicitly handles that.
149   // FIXME: stash skipped args for later use.
150   for (int i = 0; i < 3; ++i) {
151     ++it;
152     if (it == end || (*it)->getOption().getID() != OPT_INPUT)
153       fatal("usage: -platform_version platform min_version sdk_version");
154   }
155 }
156 
157 bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
158                  raw_ostream &stdoutOS, raw_ostream &stderrOS) {
159   lld::stdoutOS = &stdoutOS;
160   lld::stderrOS = &stderrOS;
161 
162   stderrOS.enable_colors(stderrOS.has_colors());
163   // TODO: Set up error handler properly, e.g. the errorLimitExceededMsg
164 
165   MachOOptTable parser;
166   opt::InputArgList args = parser.parse(argsArr.slice(1));
167 
168   config = make<Configuration>();
169   symtab = make<SymbolTable>();
170   target = createTargetInfo(args);
171 
172   config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"));
173   config->outputFile = args.getLastArgValue(OPT_o, "a.out");
174   config->installName =
175       args.getLastArgValue(OPT_install_name, config->outputFile);
176   config->searchPaths = getSearchPaths(args);
177   config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE;
178 
179   if (args.hasArg(OPT_v)) {
180     message(getLLDVersion());
181     std::vector<StringRef> &searchPaths = config->searchPaths;
182     message("Library search paths:\n" +
183             llvm::join(searchPaths.begin(), searchPaths.end(), "\n"));
184     freeArena();
185     return !errorCount();
186   }
187 
188   for (opt::ArgList::iterator it = args.begin(), end = args.end(); it != end;
189        ++it) {
190     const opt::Arg *arg = *it;
191     switch (arg->getOption().getID()) {
192     case OPT_INPUT:
193       addFile(arg->getValue());
194       break;
195     case OPT_l:
196       if (Optional<std::string> path = findDylib(arg->getValue()))
197         addFile(*path);
198       break;
199     case OPT_platform_version: {
200       handlePlatformVersion(it, end); // Can advance "it".
201       break;
202     }
203     }
204   }
205 
206   // Now that all dylibs have been loaded, search for those that should be
207   // re-exported.
208   for (opt::Arg *arg : args.filtered(OPT_sub_library)) {
209     config->hasReexports = true;
210     StringRef searchName = arg->getValue();
211     if (!markSubLibrary(searchName))
212       error("-sub_library " + searchName + " does not match a supplied dylib");
213   }
214 
215   // dyld requires us to load libSystem. Since we may run tests on non-OSX
216   // systems which do not have libSystem, we mock it out here.
217   // TODO: Replace this with a stub tbd file once we have TAPI support.
218   if (StringRef(getenv("LLD_IN_TEST")) == "1" &&
219       config->outputType == MH_EXECUTE) {
220     inputFiles.push_back(DylibFile::createLibSystemMock());
221   }
222 
223   if (config->outputType == MH_EXECUTE && !isa<Defined>(config->entry)) {
224     error("undefined symbol: " + config->entry->getName());
225     return false;
226   }
227 
228   createSyntheticSections();
229 
230   // Initialize InputSections.
231   for (InputFile *file : inputFiles)
232     for (InputSection *sec : file->sections)
233       inputSections.push_back(sec);
234 
235   // Write to an output file.
236   writeResult();
237 
238   if (canExitEarly)
239     exitLld(errorCount() ? 1 : 0);
240 
241   freeArena();
242   return !errorCount();
243 }
244