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