llvm-project

Форк
0
/
BinaryHolder.cpp 
301 строка · 10.4 Кб
1
//===-- BinaryHolder.cpp --------------------------------------------------===//
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
// This program is a utility that aims to be a dropin replacement for
10
// Darwin's dsymutil.
11
//
12
//===----------------------------------------------------------------------===//
13

14
#include "BinaryHolder.h"
15
#include "llvm/Object/MachO.h"
16
#include "llvm/Support/WithColor.h"
17
#include "llvm/Support/raw_ostream.h"
18

19
namespace llvm {
20
namespace dsymutil {
21

22
static std::pair<StringRef, StringRef>
23
getArchiveAndObjectName(StringRef Filename) {
24
  StringRef Archive = Filename.substr(0, Filename.rfind('('));
25
  StringRef Object = Filename.substr(Archive.size() + 1).drop_back();
26
  return {Archive, Object};
27
}
28

29
static bool isArchive(StringRef Filename) { return Filename.ends_with(")"); }
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
Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
45
                                       StringRef Filename,
46
                                       TimestampTy Timestamp, bool Verbose) {
47
  StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
48

49
  // Try to load archive and force it to be memory mapped.
50
  auto ErrOrBuff = (ArchiveFilename == "-")
51
                       ? MemoryBuffer::getSTDIN()
52
                       : VFS->getBufferForFile(ArchiveFilename, -1, false);
53
  if (auto Err = ErrOrBuff.getError())
54
    return errorCodeToError(Err);
55

56
  MemBuffer = std::move(*ErrOrBuff);
57

58
  if (Verbose)
59
    WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
60

61
  // Load one or more archive buffers, depending on whether we're dealing with
62
  // a fat binary.
63
  std::vector<MemoryBufferRef> ArchiveBuffers;
64

65
  auto ErrOrFat =
66
      object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
67
  if (!ErrOrFat) {
68
    consumeError(ErrOrFat.takeError());
69
    ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
70
  } else {
71
    FatBinary = std::move(*ErrOrFat);
72
    FatBinaryName = std::string(ArchiveFilename);
73
    ArchiveBuffers =
74
        getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
75
  }
76

77
  // Finally, try to load the archives.
78
  Archives.reserve(ArchiveBuffers.size());
79
  for (auto MemRef : ArchiveBuffers) {
80
    auto ErrOrArchive = object::Archive::create(MemRef);
81
    if (!ErrOrArchive)
82
      return ErrOrArchive.takeError();
83
    Archives.push_back(std::move(*ErrOrArchive));
84
  }
85

86
  return Error::success();
87
}
88

89
Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
90
                                      StringRef Filename, TimestampTy Timestamp,
91
                                      bool Verbose) {
92
  // Try to load regular binary and force it to be memory mapped.
93
  auto ErrOrBuff = (Filename == "-")
94
                       ? MemoryBuffer::getSTDIN()
95
                       : VFS->getBufferForFile(Filename, -1, false);
96
  if (auto Err = ErrOrBuff.getError())
97
    return errorCodeToError(Err);
98

99
  if (Filename != "-" && Timestamp != sys::TimePoint<>()) {
100
    llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);
101
    if (!Stat)
102
      return errorCodeToError(Stat.getError());
103
    if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
104
                         Stat->getLastModificationTime()))
105
      WithColor::warning() << Filename
106
                           << ": timestamp mismatch between object file ("
107
                           << Stat->getLastModificationTime()
108
                           << ") and debug map (" << Timestamp << ")\n";
109
  }
110

111
  MemBuffer = std::move(*ErrOrBuff);
112

113
  if (Verbose)
114
    WithColor::note() << "loaded object.\n";
115

116
  // Load one or more object buffers, depending on whether we're dealing with a
117
  // fat binary.
118
  std::vector<MemoryBufferRef> ObjectBuffers;
119

120
  auto ErrOrFat =
121
      object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
122
  if (!ErrOrFat) {
123
    consumeError(ErrOrFat.takeError());
124
    ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
125
  } else {
126
    FatBinary = std::move(*ErrOrFat);
127
    FatBinaryName = std::string(Filename);
128
    ObjectBuffers =
129
        getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
130
  }
131

132
  Objects.reserve(ObjectBuffers.size());
133
  for (auto MemRef : ObjectBuffers) {
134
    auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
135
    if (!ErrOrObjectFile)
136
      return ErrOrObjectFile.takeError();
137
    Objects.push_back(std::move(*ErrOrObjectFile));
138
  }
139

140
  return Error::success();
141
}
142

143
std::vector<const object::ObjectFile *>
144
BinaryHolder::ObjectEntry::getObjects() const {
145
  std::vector<const object::ObjectFile *> Result;
146
  Result.reserve(Objects.size());
147
  for (auto &Object : Objects) {
148
    Result.push_back(Object.get());
149
  }
150
  return Result;
151
}
152
Expected<const object::ObjectFile &>
153
BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
154
  for (const auto &Obj : Objects) {
155
    if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
156
      if (MachO->getArchTriple().str() == T.str())
157
        return *MachO;
158
    } else if (Obj->getArch() == T.getArch())
159
      return *Obj;
160
  }
161
  return errorCodeToError(object::object_error::arch_not_found);
162
}
163

164
Expected<const BinaryHolder::ObjectEntry &>
165
BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
166
                                           TimestampTy Timestamp,
167
                                           bool Verbose) {
168
  StringRef ArchiveFilename;
169
  StringRef ObjectFilename;
170
  std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
171
  KeyTy Key = {ObjectFilename, Timestamp};
172

173
  // Try the cache first.
174
  std::lock_guard<std::mutex> Lock(MemberCacheMutex);
175
  if (MemberCache.count(Key))
176
    return *MemberCache[Key];
177

178
  // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
179
  // the archive members might fail and we don't want to lock the whole archive
180
  // during this operation.
181
  auto OE = std::make_unique<ObjectEntry>();
182

183
  for (const auto &Archive : Archives) {
184
    Error Err = Error::success();
185
    for (const auto &Child : Archive->children(Err)) {
186
      if (auto NameOrErr = Child.getName()) {
187
        if (*NameOrErr == ObjectFilename) {
188
          auto ModTimeOrErr = Child.getLastModified();
189
          if (!ModTimeOrErr)
190
            return ModTimeOrErr.takeError();
191

192
          if (Timestamp != sys::TimePoint<>() &&
193
              Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
194
                               ModTimeOrErr.get())) {
195
            if (Verbose)
196
              WithColor::warning()
197
                  << *NameOrErr
198
                  << ": timestamp mismatch between archive member ("
199
                  << ModTimeOrErr.get() << ") and debug map (" << Timestamp
200
                  << ")\n";
201
            continue;
202
          }
203

204
          if (Verbose)
205
            WithColor::note() << "found member in archive.\n";
206

207
          auto ErrOrMem = Child.getMemoryBufferRef();
208
          if (!ErrOrMem)
209
            return ErrOrMem.takeError();
210

211
          auto ErrOrObjectFile =
212
              object::ObjectFile::createObjectFile(*ErrOrMem);
213
          if (!ErrOrObjectFile)
214
            return ErrOrObjectFile.takeError();
215

216
          OE->Objects.push_back(std::move(*ErrOrObjectFile));
217
        }
218
      }
219
    }
220
    if (Err)
221
      return std::move(Err);
222
  }
223

224
  if (OE->Objects.empty())
225
    return errorCodeToError(errc::no_such_file_or_directory);
226

227
  MemberCache[Key] = std::move(OE);
228
  return *MemberCache[Key];
229
}
230

231
Expected<const BinaryHolder::ObjectEntry &>
232
BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
233
  if (Verbose)
234
    WithColor::note() << "trying to open '" << Filename << "'\n";
235

236
  // If this is an archive, we might have either the object or the archive
237
  // cached. In this case we can load it without accessing the file system.
238
  if (isArchive(Filename)) {
239
    StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
240
    std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
241
    ArchiveRefCounter[ArchiveFilename]++;
242
    if (ArchiveCache.count(ArchiveFilename)) {
243
      return ArchiveCache[ArchiveFilename]->getObjectEntry(Filename, Timestamp,
244
                                                           Verbose);
245
    } else {
246
      auto AE = std::make_unique<ArchiveEntry>();
247
      auto Err = AE->load(VFS, Filename, Timestamp, Verbose);
248
      if (Err) {
249
        // Don't return the error here: maybe the file wasn't an archive.
250
        llvm::consumeError(std::move(Err));
251
      } else {
252
        ArchiveCache[ArchiveFilename] = std::move(AE);
253
        return ArchiveCache[ArchiveFilename]->getObjectEntry(
254
            Filename, Timestamp, Verbose);
255
      }
256
    }
257
  }
258

259
  // If this is an object, we might have it cached. If not we'll have to load
260
  // it from the file system and cache it now.
261
  std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
262
  ObjectRefCounter[Filename]++;
263
  if (!ObjectCache.count(Filename)) {
264
    auto OE = std::make_unique<ObjectEntry>();
265
    auto Err = OE->load(VFS, Filename, Timestamp, Verbose);
266
    if (Err)
267
      return std::move(Err);
268
    ObjectCache[Filename] = std::move(OE);
269
  }
270

271
  return *ObjectCache[Filename];
272
}
273

274
void BinaryHolder::clear() {
275
  std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
276
  std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
277
  ArchiveCache.clear();
278
  ObjectCache.clear();
279
}
280

281
void BinaryHolder::eraseObjectEntry(StringRef Filename) {
282
  if (Verbose)
283
    WithColor::note() << "erasing '" << Filename << "' from cache\n";
284

285
  if (isArchive(Filename)) {
286
    StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
287
    std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
288
    ArchiveRefCounter[ArchiveFilename]--;
289
    if (ArchiveRefCounter[ArchiveFilename] == 0)
290
      ArchiveCache.erase(ArchiveFilename);
291
    return;
292
  }
293

294
  std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
295
  ObjectRefCounter[Filename]--;
296
  if (ObjectRefCounter[Filename] == 0)
297
    ObjectCache.erase(Filename);
298
}
299

300
} // namespace dsymutil
301
} // namespace llvm
302

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.