xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/MachO.cpp (revision 7937fe1a17f2c883f41e1bdefd0a1d9c93861532)
1 //===----------------- MachO.cpp - MachO format utilities -----------------===//
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 "llvm/ExecutionEngine/Orc/MachO.h"
10 
11 #include "llvm/ADT/ScopeExit.h"
12 #include "llvm/BinaryFormat/MachO.h"
13 #include "llvm/ExecutionEngine/Orc/Layer.h"
14 #include "llvm/Object/MachOUniversal.h"
15 #include "llvm/Support/FileSystem.h"
16 
17 #define DEBUG_TYPE "orc"
18 
19 namespace llvm {
20 namespace orc {
21 
22 static std::string objDesc(const MemoryBufferRef &Obj, const Triple &TT,
23                            bool ObjIsSlice) {
24   std::string Desc;
25   if (ObjIsSlice)
26     Desc += (TT.getArchName() + " slice of universal binary").str();
27   Desc += Obj.getBufferIdentifier();
28   return Desc;
29 }
30 
31 template <typename HeaderType>
32 static Error checkMachORelocatableObject(MemoryBufferRef Obj,
33                                          bool SwapEndianness, const Triple &TT,
34                                          bool ObjIsSlice) {
35   StringRef Data = Obj.getBuffer();
36 
37   HeaderType Hdr;
38   memcpy(&Hdr, Data.data(), sizeof(HeaderType));
39 
40   if (SwapEndianness)
41     swapStruct(Hdr);
42 
43   if (Hdr.filetype != MachO::MH_OBJECT)
44     return make_error<StringError>(objDesc(Obj, TT, ObjIsSlice) +
45                                        " is not a MachO relocatable object",
46                                    inconvertibleErrorCode());
47 
48   auto ObjArch = object::MachOObjectFile::getArch(Hdr.cputype, Hdr.cpusubtype);
49   if (ObjArch != TT.getArch())
50     return make_error<StringError>(
51         objDesc(Obj, TT, ObjIsSlice) + Triple::getArchTypeName(ObjArch) +
52             ", cannot be loaded into " + TT.str() + " process",
53         inconvertibleErrorCode());
54 
55   return Error::success();
56 }
57 
58 Error checkMachORelocatableObject(MemoryBufferRef Obj, const Triple &TT,
59                                   bool ObjIsSlice) {
60   StringRef Data = Obj.getBuffer();
61 
62   if (Data.size() < 4)
63     return make_error<StringError>(
64         objDesc(Obj, TT, ObjIsSlice) +
65             " is not a valid MachO relocatable object file (truncated header)",
66         inconvertibleErrorCode());
67 
68   uint32_t Magic;
69   memcpy(&Magic, Data.data(), sizeof(uint32_t));
70 
71   switch (Magic) {
72   case MachO::MH_MAGIC:
73   case MachO::MH_CIGAM:
74     return checkMachORelocatableObject<MachO::mach_header>(
75         std::move(Obj), Magic == MachO::MH_CIGAM, TT, ObjIsSlice);
76   case MachO::MH_MAGIC_64:
77   case MachO::MH_CIGAM_64:
78     return checkMachORelocatableObject<MachO::mach_header_64>(
79         std::move(Obj), Magic == MachO::MH_CIGAM_64, TT, ObjIsSlice);
80   default:
81     return make_error<StringError>(
82         objDesc(Obj, TT, ObjIsSlice) +
83             " is not a valid MachO relocatable object (bad magic value)",
84         inconvertibleErrorCode());
85   }
86 }
87 
88 Expected<std::unique_ptr<MemoryBuffer>>
89 checkMachORelocatableObject(std::unique_ptr<MemoryBuffer> Obj, const Triple &TT,
90                             bool ObjIsSlice) {
91   if (auto Err =
92           checkMachORelocatableObject(Obj->getMemBufferRef(), TT, ObjIsSlice))
93     return std::move(Err);
94   return std::move(Obj);
95 }
96 
97 Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>
98 loadMachORelocatableObject(StringRef Path, const Triple &TT, LoadArchives LA,
99                            std::optional<StringRef> IdentifierOverride) {
100   assert((TT.getObjectFormat() == Triple::UnknownObjectFormat ||
101           TT.getObjectFormat() == Triple::MachO) &&
102          "TT must specify MachO or Unknown object format");
103 
104   if (!IdentifierOverride)
105     IdentifierOverride = Path;
106 
107   Expected<sys::fs::file_t> FDOrErr =
108       sys::fs::openNativeFileForRead(Path, sys::fs::OF_None);
109   if (!FDOrErr)
110     return createFileError(Path, FDOrErr.takeError());
111   sys::fs::file_t FD = *FDOrErr;
112   auto CloseFile = make_scope_exit([&]() { sys::fs::closeFile(FD); });
113 
114   auto Buf =
115       MemoryBuffer::getOpenFile(FD, *IdentifierOverride, /*FileSize=*/-1);
116   if (!Buf)
117     return make_error<StringError>(
118         StringRef("Could not load MachO object at path ") + Path,
119         Buf.getError());
120 
121   switch (identify_magic((*Buf)->getBuffer())) {
122   case file_magic::macho_object: {
123     auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, false);
124     if (!CheckedObj)
125       return CheckedObj.takeError();
126     return std::make_pair(std::move(*CheckedObj),
127                           LinkableFileKind::RelocatableObject);
128   }
129   case file_magic::macho_universal_binary:
130     return loadLinkableSliceFromMachOUniversalBinary(FD, std::move(*Buf), TT,
131                                                      LoadArchives::Never, Path,
132                                                      *IdentifierOverride);
133   default:
134     return make_error<StringError>(
135         Path + " does not contain a relocatable object file compatible with " +
136             TT.str(),
137         inconvertibleErrorCode());
138   }
139 }
140 
141 Expected<std::pair<std::unique_ptr<MemoryBuffer>, LinkableFileKind>>
142 loadLinkableSliceFromMachOUniversalBinary(sys::fs::file_t FD,
143                                           std::unique_ptr<MemoryBuffer> UBBuf,
144                                           const Triple &TT, LoadArchives LA,
145                                           StringRef UBPath,
146                                           StringRef Identifier) {
147 
148   auto UniversalBin =
149       object::MachOUniversalBinary::create(UBBuf->getMemBufferRef());
150   if (!UniversalBin)
151     return UniversalBin.takeError();
152 
153   auto SliceRange = getMachOSliceRangeForTriple(**UniversalBin, TT);
154   if (!SliceRange)
155     return SliceRange.takeError();
156 
157   auto Buf = MemoryBuffer::getOpenFileSlice(FD, Identifier, SliceRange->second,
158                                             SliceRange->first);
159   if (!Buf)
160     return make_error<StringError>(
161         "Could not load " + TT.getArchName() +
162             " slice of MachO universal binary at path " + UBPath,
163         Buf.getError());
164 
165   switch (identify_magic((*Buf)->getBuffer())) {
166   case file_magic::archive:
167     if (LA != LoadArchives::Never)
168       return std::make_pair(std::move(*Buf), LinkableFileKind::Archive);
169     break;
170   case file_magic::macho_object: {
171     if (LA != LoadArchives::Required) {
172       auto CheckedObj = checkMachORelocatableObject(std::move(*Buf), TT, true);
173       if (!CheckedObj)
174         return CheckedObj.takeError();
175       return std::make_pair(std::move(*CheckedObj),
176                             LinkableFileKind::RelocatableObject);
177     }
178     break;
179   }
180   default:
181     break;
182   }
183 
184   auto FT = [&] {
185     switch (LA) {
186     case LoadArchives::Never:
187       return "a mach-o relocatable object file";
188     case LoadArchives::Allowed:
189       return "a mach-o relocatable object file or archive";
190     case LoadArchives::Required:
191       return "an archive";
192     }
193     llvm_unreachable("Unknown LoadArchives enum");
194   };
195 
196   return make_error<StringError>(TT.getArchName() + " slice of " + UBPath +
197                                      " does not contain " + FT(),
198                                  inconvertibleErrorCode());
199 }
200 
201 Expected<std::pair<size_t, size_t>>
202 getMachOSliceRangeForTriple(object::MachOUniversalBinary &UB,
203                             const Triple &TT) {
204 
205   for (const auto &Obj : UB.objects()) {
206     auto ObjTT = Obj.getTriple();
207     if (ObjTT.getArch() == TT.getArch() &&
208         ObjTT.getSubArch() == TT.getSubArch() &&
209         (TT.getVendor() == Triple::UnknownVendor ||
210          ObjTT.getVendor() == TT.getVendor())) {
211       // We found a match. Return the range for the slice.
212       return std::make_pair(Obj.getOffset(), Obj.getSize());
213     }
214   }
215 
216   return make_error<StringError>(Twine("Universal binary ") + UB.getFileName() +
217                                      " does not contain a slice for " +
218                                      TT.str(),
219                                  inconvertibleErrorCode());
220 }
221 
222 Expected<std::pair<size_t, size_t>>
223 getMachOSliceRangeForTriple(MemoryBufferRef UBBuf, const Triple &TT) {
224 
225   auto UB = object::MachOUniversalBinary::create(UBBuf);
226   if (!UB)
227     return UB.takeError();
228 
229   return getMachOSliceRangeForTriple(**UB, TT);
230 }
231 
232 Error ForceLoadMachOArchiveMembers::operator()(MemoryBufferRef MemberBuf) {
233   if (!ObjCOnly)
234     return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf));
235 
236   // We need to check whether this archive member contains any Objective-C
237   // or Swift metadata.
238 
239   auto Obj = object::ObjectFile::createObjectFile(MemberBuf);
240   if (!Obj) {
241     // We silently ignore invalid files.
242     consumeError(Obj.takeError());
243     return Error::success();
244   }
245 
246   if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&**Obj)) {
247     // Load the object if any recognized special section is present.
248     for (auto Sec : MachOObj->sections()) {
249       auto SegName =
250           MachOObj->getSectionFinalSegmentName(Sec.getRawDataRefImpl());
251       if (auto SecName = Sec.getName()) {
252         if (*SecName == "__objc_classlist" || *SecName == "__objc_protolist" ||
253             *SecName == "__objc_clsrolist" || *SecName == "__objc_catlist" ||
254             *SecName == "__objc_catlist2" || *SecName == "__objc_nlcatlist" ||
255             (SegName == "__TEXT" && (*SecName).starts_with("__swift") &&
256              *SecName != "__swift_modhash"))
257           return L.add(JD, MemoryBuffer::getMemBuffer(MemberBuf));
258       } else
259         return SecName.takeError();
260     }
261   }
262 
263   return Error::success();
264 }
265 
266 } // End namespace orc.
267 } // End namespace llvm.
268