[PM/AA] Extract the interface for GlobalsModRef into a header along with
[oota-llvm.git] / tools / dsymutil / BinaryHolder.cpp
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 Triple BinaryHolder::getTriple(const object::MachOObjectFile &Obj) {
23   // If a ThumbTriple is returned, use it instead of the standard
24   // one. This is because the thumb triple always allows to create a
25   // target, whereas the non-thumb one might not.
26   Triple ThumbTriple;
27   Triple T = Obj.getArch(nullptr, &ThumbTriple);
28   return ThumbTriple.getArch() ? ThumbTriple : T;
29 }
30
31 static std::vector<MemoryBufferRef>
32 getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
33                          object::MachOUniversalBinary &Fat) {
34   std::vector<MemoryBufferRef> Buffers;
35   StringRef FatData = Fat.getData();
36   for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
37        ++It) {
38     StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
39     Buffers.emplace_back(ObjData, Filename);
40   }
41   return Buffers;
42 }
43
44 void BinaryHolder::changeBackingMemoryBuffer(
45     std::unique_ptr<MemoryBuffer> &&Buf) {
46   CurrentArchives.clear();
47   CurrentObjectFiles.clear();
48   CurrentFatBinary.reset();
49
50   CurrentMemoryBuffer = std::move(Buf);
51 }
52
53 ErrorOr<std::vector<MemoryBufferRef>>
54 BinaryHolder::GetMemoryBuffersForFile(StringRef Filename,
55                                       sys::TimeValue Timestamp) {
56   if (Verbose)
57     outs() << "trying to open '" << Filename << "'\n";
58
59   // Try that first as it doesn't involve any filesystem access.
60   if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp))
61     return *ErrOrArchiveMembers;
62
63   // If the name ends with a closing paren, there is a huge chance
64   // it is an archive member specification.
65   if (Filename.endswith(")"))
66     if (auto ErrOrArchiveMembers =
67             MapArchiveAndGetMemberBuffers(Filename, Timestamp))
68       return *ErrOrArchiveMembers;
69
70   // Otherwise, just try opening a standard file. If this is an
71   // archive member specifiaction and any of the above didn't handle it
72   // (either because the archive is not there anymore, or because the
73   // archive doesn't contain the requested member), this will still
74   // provide a sensible error message.
75   auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename);
76   if (auto Err = ErrOrFile.getError())
77     return Err;
78
79   changeBackingMemoryBuffer(std::move(*ErrOrFile));
80   if (Verbose)
81     outs() << "\tloaded file.\n";
82
83   auto ErrOrFat = object::MachOUniversalBinary::create(
84       CurrentMemoryBuffer->getMemBufferRef());
85   if (ErrOrFat.getError()) {
86     // Not a fat binary must be a standard one. Return a one element vector.
87     return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()};
88   }
89
90   CurrentFatBinary = std::move(*ErrOrFat);
91   return getMachOFatMemoryBuffers(Filename, *CurrentMemoryBuffer,
92                                   *CurrentFatBinary);
93 }
94
95 ErrorOr<std::vector<MemoryBufferRef>>
96 BinaryHolder::GetArchiveMemberBuffers(StringRef Filename,
97                                       sys::TimeValue Timestamp) {
98   if (CurrentArchives.empty())
99     return make_error_code(errc::no_such_file_or_directory);
100
101   StringRef CurArchiveName = CurrentArchives.front()->getFileName();
102   if (!Filename.startswith(Twine(CurArchiveName, "(").str()))
103     return make_error_code(errc::no_such_file_or_directory);
104
105   // Remove the archive name and the parens around the archive member name.
106   Filename = Filename.substr(CurArchiveName.size() + 1).drop_back();
107
108   std::vector<MemoryBufferRef> Buffers;
109   Buffers.reserve(CurrentArchives.size());
110
111   for (const auto &CurrentArchive : CurrentArchives) {
112     for (const auto &Child : CurrentArchive->children()) {
113       if (auto NameOrErr = Child.getName()) {
114         if (*NameOrErr == Filename) {
115           if (Timestamp != sys::TimeValue::PosixZeroTime() &&
116               Timestamp != Child.getLastModified()) {
117             if (Verbose)
118               outs() << "\tmember had timestamp mismatch.\n";
119             continue;
120           }
121           if (Verbose)
122             outs() << "\tfound member in current archive.\n";
123           auto ErrOrMem = Child.getMemoryBufferRef();
124           if (auto Err = ErrOrMem.getError())
125             return Err;
126           Buffers.push_back(*ErrOrMem);
127         }
128       }
129     }
130   }
131
132   if (Buffers.empty())
133     return make_error_code(errc::no_such_file_or_directory);
134   return Buffers;
135 }
136
137 ErrorOr<std::vector<MemoryBufferRef>>
138 BinaryHolder::MapArchiveAndGetMemberBuffers(StringRef Filename,
139                                             sys::TimeValue Timestamp) {
140   StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
141
142   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
143   if (auto Err = ErrOrBuff.getError())
144     return Err;
145
146   if (Verbose)
147     outs() << "\topened new archive '" << ArchiveFilename << "'\n";
148
149   changeBackingMemoryBuffer(std::move(*ErrOrBuff));
150   std::vector<MemoryBufferRef> ArchiveBuffers;
151   auto ErrOrFat = object::MachOUniversalBinary::create(
152       CurrentMemoryBuffer->getMemBufferRef());
153   if (ErrOrFat.getError()) {
154     // Not a fat binary must be a standard one.
155     ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
156   } else {
157     CurrentFatBinary = std::move(*ErrOrFat);
158     ArchiveBuffers = getMachOFatMemoryBuffers(
159         ArchiveFilename, *CurrentMemoryBuffer, *CurrentFatBinary);
160   }
161
162   for (auto MemRef : ArchiveBuffers) {
163     auto ErrOrArchive = object::Archive::create(MemRef);
164     if (auto Err = ErrOrArchive.getError())
165       return Err;
166     CurrentArchives.push_back(std::move(*ErrOrArchive));
167   }
168   return GetArchiveMemberBuffers(Filename, Timestamp);
169 }
170
171 ErrorOr<const object::ObjectFile &>
172 BinaryHolder::getObjfileForArch(const Triple &T) {
173   for (const auto &Obj : CurrentObjectFiles) {
174     if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
175       if (getTriple(*MachO).str() == T.str())
176         return *MachO;
177     } else if (Obj->getArch() == T.getArch())
178       return *Obj;
179   }
180
181   return make_error_code(object::object_error::arch_not_found);
182 }
183
184 ErrorOr<std::vector<const object::ObjectFile *>>
185 BinaryHolder::GetObjectFiles(StringRef Filename, sys::TimeValue 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 (auto Err = ErrOrObjectFile.getError())
197       return Err;
198
199     Objects.push_back(ErrOrObjectFile->get());
200     CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
201   }
202
203   return std::move(Objects);
204 }
205 }
206 }