• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
getMachOFatMemoryBuffers(StringRef Filename,MemoryBuffer & Mem,object::MachOUniversalBinary & Fat)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 
changeBackingMemoryBuffer(std::unique_ptr<MemoryBuffer> && Buf)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>>
GetMemoryBuffersForFile(StringRef Filename,sys::TimeValue Timestamp)45 BinaryHolder::GetMemoryBuffersForFile(StringRef Filename,
46                                       sys::TimeValue Timestamp) {
47   if (Verbose)
48     outs() << "trying to open '" << Filename << "'\n";
49 
50   // Try that first as it doesn't involve any filesystem access.
51   if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp))
52     return *ErrOrArchiveMembers;
53 
54   // If the name ends with a closing paren, there is a huge chance
55   // it is an archive member specification.
56   if (Filename.endswith(")"))
57     if (auto ErrOrArchiveMembers =
58             MapArchiveAndGetMemberBuffers(Filename, Timestamp))
59       return *ErrOrArchiveMembers;
60 
61   // Otherwise, just try opening a standard file. If this is an
62   // archive member specifiaction and any of the above didn't handle it
63   // (either because the archive is not there anymore, or because the
64   // archive doesn't contain the requested member), this will still
65   // provide a sensible error message.
66   auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename);
67   if (auto Err = ErrOrFile.getError())
68     return Err;
69 
70   changeBackingMemoryBuffer(std::move(*ErrOrFile));
71   if (Verbose)
72     outs() << "\tloaded file.\n";
73 
74   auto ErrOrFat = object::MachOUniversalBinary::create(
75       CurrentMemoryBuffer->getMemBufferRef());
76   if (!ErrOrFat) {
77     consumeError(ErrOrFat.takeError());
78     // Not a fat binary must be a standard one. Return a one element vector.
79     return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()};
80   }
81 
82   CurrentFatBinary = std::move(*ErrOrFat);
83   CurrentFatBinaryName = Filename;
84   return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer,
85                                   *CurrentFatBinary);
86 }
87 
88 ErrorOr<std::vector<MemoryBufferRef>>
GetArchiveMemberBuffers(StringRef Filename,sys::TimeValue Timestamp)89 BinaryHolder::GetArchiveMemberBuffers(StringRef Filename,
90                                       sys::TimeValue Timestamp) {
91   if (CurrentArchives.empty())
92     return make_error_code(errc::no_such_file_or_directory);
93 
94   StringRef CurArchiveName = CurrentArchives.front()->getFileName();
95   if (!Filename.startswith(Twine(CurArchiveName, "(").str()))
96     return make_error_code(errc::no_such_file_or_directory);
97 
98   // Remove the archive name and the parens around the archive member name.
99   Filename = Filename.substr(CurArchiveName.size() + 1).drop_back();
100 
101   std::vector<MemoryBufferRef> Buffers;
102   Buffers.reserve(CurrentArchives.size());
103 
104   for (const auto &CurrentArchive : CurrentArchives) {
105     Error Err;
106     for (auto Child : CurrentArchive->children(Err)) {
107       if (auto NameOrErr = Child.getName()) {
108         if (*NameOrErr == Filename) {
109           if (Timestamp != sys::TimeValue::PosixZeroTime() &&
110               Timestamp != Child.getLastModified()) {
111             if (Verbose)
112               outs() << "\tmember had timestamp mismatch.\n";
113             continue;
114           }
115           if (Verbose)
116             outs() << "\tfound member in current archive.\n";
117           auto ErrOrMem = Child.getMemoryBufferRef();
118           if (auto Err = ErrOrMem.getError())
119             return Err;
120           Buffers.push_back(*ErrOrMem);
121         }
122       }
123     }
124     if (Err)
125       return errorToErrorCode(std::move(Err));
126   }
127 
128   if (Buffers.empty())
129     return make_error_code(errc::no_such_file_or_directory);
130   return Buffers;
131 }
132 
133 ErrorOr<std::vector<MemoryBufferRef>>
MapArchiveAndGetMemberBuffers(StringRef Filename,sys::TimeValue Timestamp)134 BinaryHolder::MapArchiveAndGetMemberBuffers(StringRef Filename,
135                                             sys::TimeValue Timestamp) {
136   StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
137 
138   auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
139   if (auto Err = ErrOrBuff.getError())
140     return Err;
141 
142   if (Verbose)
143     outs() << "\topened new archive '" << ArchiveFilename << "'\n";
144 
145   changeBackingMemoryBuffer(std::move(*ErrOrBuff));
146   std::vector<MemoryBufferRef> ArchiveBuffers;
147   auto ErrOrFat = object::MachOUniversalBinary::create(
148       CurrentMemoryBuffer->getMemBufferRef());
149   if (!ErrOrFat) {
150     consumeError(ErrOrFat.takeError());
151     // Not a fat binary must be a standard one.
152     ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
153   } else {
154     CurrentFatBinary = std::move(*ErrOrFat);
155     CurrentFatBinaryName = ArchiveFilename;
156     ArchiveBuffers = getMachOFatMemoryBuffers(
157         CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary);
158   }
159 
160   for (auto MemRef : ArchiveBuffers) {
161     auto ErrOrArchive = object::Archive::create(MemRef);
162     if (!ErrOrArchive)
163       return errorToErrorCode(ErrOrArchive.takeError());
164     CurrentArchives.push_back(std::move(*ErrOrArchive));
165   }
166   return GetArchiveMemberBuffers(Filename, Timestamp);
167 }
168 
169 ErrorOr<const object::ObjectFile &>
getObjfileForArch(const Triple & T)170 BinaryHolder::getObjfileForArch(const Triple &T) {
171   for (const auto &Obj : CurrentObjectFiles) {
172     if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
173       if (MachO->getArchTriple().str() == T.str())
174         return *MachO;
175     } else if (Obj->getArch() == T.getArch())
176       return *Obj;
177   }
178 
179   return make_error_code(object::object_error::arch_not_found);
180 }
181 
182 ErrorOr<std::vector<const object::ObjectFile *>>
GetObjectFiles(StringRef Filename,sys::TimeValue Timestamp)183 BinaryHolder::GetObjectFiles(StringRef Filename, sys::TimeValue Timestamp) {
184   auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp);
185   if (auto Err = ErrOrMemBufferRefs.getError())
186     return Err;
187 
188   std::vector<const object::ObjectFile *> Objects;
189   Objects.reserve(ErrOrMemBufferRefs->size());
190 
191   CurrentObjectFiles.clear();
192   for (auto MemBuf : *ErrOrMemBufferRefs) {
193     auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf);
194     if (!ErrOrObjectFile)
195       return errorToErrorCode(ErrOrObjectFile.takeError());
196 
197     Objects.push_back(ErrOrObjectFile->get());
198     CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
199   }
200 
201   return std::move(Objects);
202 }
203 }
204 }
205