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