1 //===-- BinaryHolder.cpp --------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This program is a utility that aims to be a dropin replacement for
11 // Darwin's dsymutil.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "BinaryHolder.h"
16 #include "llvm/Object/MachO.h"
17 #include "llvm/Support/raw_ostream.h"
18 
19 namespace llvm {
20 namespace dsymutil {
21 
22 static std::vector<MemoryBufferRef>
23 getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
24                          object::MachOUniversalBinary &Fat) {
25   std::vector<MemoryBufferRef> Buffers;
26   StringRef FatData = Fat.getData();
27   for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
28        ++It) {
29     StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
30     Buffers.emplace_back(ObjData, Filename);
31   }
32   return Buffers;
33 }
34 
35 void BinaryHolder::changeBackingMemoryBuffer(
36     std::unique_ptr<MemoryBuffer> &&Buf) {
37   CurrentArchives.clear();
38   CurrentObjectFiles.clear();
39   CurrentFatBinary.reset();
40 
41   CurrentMemoryBuffer = std::move(Buf);
42 }
43 
44 ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetMemoryBuffersForFile(
45     StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
46   if (Verbose)
47     outs() << "trying to open '" << Filename << "'\n";
48 
49   // Try that first as it doesn't involve any filesystem access.
50   if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp))
51     return *ErrOrArchiveMembers;
52 
53   // If the name ends with a closing paren, there is a huge chance
54   // it is an archive member specification.
55   if (Filename.endswith(")"))
56     if (auto ErrOrArchiveMembers =
57             MapArchiveAndGetMemberBuffers(Filename, Timestamp))
58       return *ErrOrArchiveMembers;
59 
60   // Otherwise, just try opening a standard file. If this is an
61   // archive member specifiaction and any of the above didn't handle it
62   // (either because the archive is not there anymore, or because the
63   // archive doesn't contain the requested member), this will still
64   // provide a sensible error message.
65   auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename);
66   if (auto Err = ErrOrFile.getError())
67     return Err;
68 
69   changeBackingMemoryBuffer(std::move(*ErrOrFile));
70   if (Verbose)
71     outs() << "\tloaded file.\n";
72 
73   auto ErrOrFat = object::MachOUniversalBinary::create(
74       CurrentMemoryBuffer->getMemBufferRef());
75   if (!ErrOrFat) {
76     consumeError(ErrOrFat.takeError());
77     // Not a fat binary must be a standard one. Return a one element vector.
78     return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()};
79   }
80 
81   CurrentFatBinary = std::move(*ErrOrFat);
82   CurrentFatBinaryName = Filename;
83   return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer,
84                                   *CurrentFatBinary);
85 }
86 
87 ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetArchiveMemberBuffers(
88     StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
89   if (CurrentArchives.empty())
90     return make_error_code(errc::no_such_file_or_directory);
91 
92   StringRef CurArchiveName = CurrentArchives.front()->getFileName();
93   if (!Filename.startswith(Twine(CurArchiveName, "(").str()))
94     return make_error_code(errc::no_such_file_or_directory);
95 
96   // Remove the archive name and the parens around the archive member name.
97   Filename = Filename.substr(CurArchiveName.size() + 1).drop_back();
98 
99   std::vector<MemoryBufferRef> Buffers;
100   Buffers.reserve(CurrentArchives.size());
101 
102   for (const auto &CurrentArchive : CurrentArchives) {
103     Error Err = Error::success();
104     for (auto Child : CurrentArchive->children(Err)) {
105       if (auto NameOrErr = Child.getName()) {
106         if (*NameOrErr == Filename) {
107           auto ModTimeOrErr = Child.getLastModified();
108           if (!ModTimeOrErr)
109             return errorToErrorCode(ModTimeOrErr.takeError());
110           if (Timestamp != sys::TimePoint<>() &&
111               Timestamp != ModTimeOrErr.get()) {
112             if (Verbose)
113               outs() << "\tmember had timestamp mismatch.\n";
114             continue;
115           }
116           if (Verbose)
117             outs() << "\tfound member in current archive.\n";
118           auto ErrOrMem = Child.getMemoryBufferRef();
119           if (!ErrOrMem)
120             return errorToErrorCode(ErrOrMem.takeError());
121           Buffers.push_back(*ErrOrMem);
122         }
123       }
124     }
125     if (Err)
126       return errorToErrorCode(std::move(Err));
127   }
128 
129   if (Buffers.empty())
130     return make_error_code(errc::no_such_file_or_directory);
131   return Buffers;
132 }
133 
134 ErrorOr<std::vector<MemoryBufferRef>>
135 BinaryHolder::MapArchiveAndGetMemberBuffers(
136     StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
137   StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
138 
139   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
140   if (auto Err = ErrOrBuff.getError())
141     return Err;
142 
143   if (Verbose)
144     outs() << "\topened new archive '" << ArchiveFilename << "'\n";
145 
146   changeBackingMemoryBuffer(std::move(*ErrOrBuff));
147   std::vector<MemoryBufferRef> ArchiveBuffers;
148   auto ErrOrFat = object::MachOUniversalBinary::create(
149       CurrentMemoryBuffer->getMemBufferRef());
150   if (!ErrOrFat) {
151     consumeError(ErrOrFat.takeError());
152     // Not a fat binary must be a standard one.
153     ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
154   } else {
155     CurrentFatBinary = std::move(*ErrOrFat);
156     CurrentFatBinaryName = ArchiveFilename;
157     ArchiveBuffers = getMachOFatMemoryBuffers(
158         CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary);
159   }
160 
161   for (auto MemRef : ArchiveBuffers) {
162     auto ErrOrArchive = object::Archive::create(MemRef);
163     if (!ErrOrArchive)
164       return errorToErrorCode(ErrOrArchive.takeError());
165     CurrentArchives.push_back(std::move(*ErrOrArchive));
166   }
167   return GetArchiveMemberBuffers(Filename, Timestamp);
168 }
169 
170 ErrorOr<const object::ObjectFile &>
171 BinaryHolder::getObjfileForArch(const Triple &T) {
172   for (const auto &Obj : CurrentObjectFiles) {
173     if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
174       if (MachO->getArchTriple().str() == T.str())
175         return *MachO;
176     } else if (Obj->getArch() == T.getArch())
177       return *Obj;
178   }
179 
180   return make_error_code(object::object_error::arch_not_found);
181 }
182 
183 ErrorOr<std::vector<const object::ObjectFile *>>
184 BinaryHolder::GetObjectFiles(StringRef Filename,
185                              sys::TimePoint<std::chrono::seconds> Timestamp) {
186   auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp);
187   if (auto Err = ErrOrMemBufferRefs.getError())
188     return Err;
189 
190   std::vector<const object::ObjectFile *> Objects;
191   Objects.reserve(ErrOrMemBufferRefs->size());
192 
193   CurrentObjectFiles.clear();
194   for (auto MemBuf : *ErrOrMemBufferRefs) {
195     auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf);
196     if (!ErrOrObjectFile)
197       return errorToErrorCode(ErrOrObjectFile.takeError());
198 
199     Objects.push_back(ErrOrObjectFile->get());
200     CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
201   }
202 
203   return std::move(Objects);
204 }
205 }
206 }
207