tan  0.0.1
llvm_ar.cpp
1 // Based on https://github.com/llvm/llvm-project/blob/main/llvm/tools/llvm-ar/llvm-ar.cpp
2 
3 //===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
4 //
5 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6 // See https://llvm.org/LICENSE.txt for license information.
7 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 //
9 //===----------------------------------------------------------------------===//
10 //
11 // Builds up (relatively) standard unix archive files (.a) containing LLVM
12 // bitcode or other files.
13 //
14 //===----------------------------------------------------------------------===//
15 #include "llvm_api/llvm_ar.h"
16 
17 #include <llvm/ADT/StringSwitch.h>
18 #include <llvm/BinaryFormat/Magic.h>
19 #include <llvm/IR/LLVMContext.h>
20 #include <llvm/Object/Archive.h>
21 #include <llvm/Object/ArchiveWriter.h>
22 #include <llvm/Object/SymbolicFile.h>
23 #include <llvm/Support/Chrono.h>
24 #include <llvm/Support/CommandLine.h>
25 #include <llvm/Support/ConvertUTF.h>
26 #include <llvm/Support/Errc.h>
27 #include <llvm/Support/Format.h>
28 #include <llvm/Support/FormatVariadic.h>
29 #include <llvm/Support/MemoryBuffer.h>
30 #include <llvm/Support/Path.h>
31 #include <llvm/Support/TargetSelect.h>
32 #include <llvm/Support/WithColor.h>
33 #include <llvm/Support/raw_ostream.h>
34 #include <llvm/ToolDrivers/llvm-lib/LibDriver.h>
35 #include <llvm/Support/Casting.h>
36 #include <llvm/Object/ObjectFile.h>
37 #include <llvm/Object/MachO.h>
38 #include <llvm/Object/IRObjectFile.h>
39 #include <llvm/Support/Host.h>
40 
41 #if !defined(_MSC_VER) && !defined(__MINGW32__)
42 #include <unistd.h>
43 #else
44 #include <io.h>
45 #include <stringapiset.h>
46 #endif
47 
48 using namespace llvm;
49 using llvm::Error;
50 
51 /// The name this program was invoked as.
52 static StringRef ToolName;
53 static unsigned MRILineNumber;
54 static bool ParsingMRIScript;
55 
56 /// Show the error message and exit.
57 [[noreturn]] static void fail(Twine Error) {
58  if (ParsingMRIScript) {
59  WithColor::error(errs(), ToolName) << "script line " << MRILineNumber << ": " << Error << "\n";
60  } else {
61  WithColor::error(errs(), ToolName) << Error << "\n";
62  }
63  exit(1);
64 }
65 
66 // DON'T delete, used on Windows
67 [[maybe_unused]] static void failIfError(std::error_code EC, Twine Context = "") {
68  if (!EC)
69  return;
70 
71  std::string ContextStr = Context.str();
72  if (ContextStr.empty())
73  fail(EC.message());
74  fail(Context + ": " + EC.message());
75 }
76 
77 static void failIfError(Error E, Twine Context = "") {
78  if (!E) {
79  return;
80  }
81 
82  handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
83  std::string ContextStr = Context.str();
84  if (ContextStr.empty()) {
85  fail(EIB.message());
86  }
87  fail(Context + ": " + EIB.message());
88  });
89 }
90 
91 static bool Symtab = true; ///< 's' modifier
92 static bool Deterministic = true; ///< 'D' and 'U' modifiers
93 
94 // Relative Positional Argument (for insert/move). This variable holds
95 // the name of the archive member to which the 'a', 'b' or 'i' modifier
96 // refers. Only one of 'a', 'b' or 'i' can be specified so we only need
97 // one variable.
98 static std::string RelPos;
99 
100 ///
101 static std::string ArchiveName;
102 static std::vector<str> Members;
103 ///
104 
105 inline std::string normalizePath(StringRef Path) { return std::string(sys::path::filename(Path)); }
106 
107 static bool comparePaths(StringRef Path1, StringRef Path2) {
108 // When on Windows this function calls CompareStringOrdinal
109 // as Windows file paths are case-insensitive.
110 // CompareStringOrdinal compares two Unicode strings for
111 // binary equivalence and allows for case insensitivity.
112 #ifdef _WIN32
113  SmallVector<wchar_t, 128> WPath1, WPath2;
114  failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1));
115  failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2));
116 
117  return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(), WPath2.size(), true) == CSTR_EQUAL;
118 #else
119  return normalizePath(Path1) == normalizePath(Path2);
120 #endif
121 }
122 
123 static void addChildMember(std::vector<NewArchiveMember> &members, const object::Archive::Child &M) {
124  Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getOldMember(M, Deterministic);
125  failIfError(NMOrErr.takeError());
126  members.push_back(std::move(*NMOrErr));
127 }
128 
129 static void addMember(std::vector<NewArchiveMember> &members, StringRef FileName) {
130  Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getFile(FileName, Deterministic);
131  failIfError(NMOrErr.takeError(), FileName);
132  NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
133  members.push_back(std::move(*NMOrErr));
134 }
135 
136 enum InsertAction { IA_AddOldMember, IA_AddNewMember, IA_MoveOldMember, IA_MoveNewMember };
137 
138 static InsertAction computeInsertAction(StringRef Name, std::vector<str>::iterator &Pos) {
139  auto MI = find_if(Members, [Name](StringRef Path) { return comparePaths(Name, Path); });
140  if (MI == Members.end()) {
141  return IA_AddOldMember;
142  }
143  Pos = MI;
144  if (RelPos.empty()) {
145  return IA_AddNewMember;
146  }
147  return IA_MoveNewMember;
148 }
149 
150 static std::vector<NewArchiveMember> computeNewArchiveMembers(object::Archive *OldArchive) {
151  /// we have to walk this twice and computing it is not trivial, so creating an
152  /// explicit std::vector is actually fairly efficient
153  std::vector<NewArchiveMember> Ret;
154  std::vector<NewArchiveMember> Moved;
155  int InsertPos = -1;
156  if (OldArchive) {
157  Error Err = Error::success();
158  for (auto &Child : OldArchive->children(Err)) {
159  int Pos = (int)Ret.size();
160  Expected<StringRef> NameOrErr = Child.getName();
161  failIfError(NameOrErr.takeError());
162  std::string Name = std::string(NameOrErr.get());
163  if (comparePaths(Name, RelPos)) {
164  InsertPos = Pos + 1;
165  }
166  auto MemberI = Members.end();
167  InsertAction Action = computeInsertAction(Name, MemberI);
168  switch (Action) {
169  case IA_AddOldMember:
170  addChildMember(Ret, Child);
171  break;
172  case IA_AddNewMember:
173  addMember(Ret, *MemberI);
174  break;
175  case IA_MoveOldMember:
176  addChildMember(Moved, Child);
177  break;
178  case IA_MoveNewMember:
179  addMember(Moved, *MemberI);
180  break;
181  default:
182  break;
183  }
184  if (MemberI != Members.end()) {
185  Members.erase(MemberI);
186  }
187  }
188  failIfError(std::move(Err));
189  }
190 
191  if (!RelPos.empty() && InsertPos == -1) {
192  fail("insertion point not found");
193  }
194  if (RelPos.empty()) {
195  InsertPos = (int)Ret.size();
196  }
197 
198  assert(unsigned(InsertPos) <= Ret.size());
199  int Pos = InsertPos;
200  for (auto &M : Moved) {
201  Ret.insert(Ret.begin() + Pos, std::move(M));
202  ++Pos;
203  }
204 
205  std::vector<NewArchiveMember> NewMembers;
206  for (auto &Member : Members) {
207  addMember(NewMembers, Member);
208  }
209  Ret.reserve(Ret.size() + NewMembers.size());
210  std::move(NewMembers.begin(), NewMembers.end(), std::inserter(Ret, std::next(Ret.begin(), InsertPos)));
211  return Ret;
212 }
213 
214 static object::Archive::Kind getDefaultForHost() {
215  return Triple(sys::getProcessTriple()).isOSDarwin() ? object::Archive::K_DARWIN : object::Archive::K_GNU;
216 }
217 
218 static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
219  auto MemBufferRef = Member.Buf->getMemBufferRef();
220  Expected<std::unique_ptr<object::ObjectFile>> OptionalObject = object::ObjectFile::createObjectFile(MemBufferRef);
221 
222  if (OptionalObject) {
223  return isa<object::MachOObjectFile>(**OptionalObject) ? object::Archive::K_DARWIN : object::Archive::K_GNU;
224  }
225 
226  // squelch the error in case we had a non-object file
227  consumeError(OptionalObject.takeError());
228 
229  // If we're adding a bitcode file to the archive, detect the Archive kind
230  // based on the target triple.
231  LLVMContext Context;
232  if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {
233  if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(MemBufferRef, file_magic::bitcode, &Context)) {
234  auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
235  return Triple(IRObject.getTargetTriple()).isOSDarwin() ? object::Archive::K_DARWIN : object::Archive::K_GNU;
236  } else {
237  // Squelch the error in case this was not a SymbolicFile.
238  consumeError(ObjOrErr.takeError());
239  }
240  }
241 
242  return getDefaultForHost();
243 }
244 
245 static void performWriteOperation(object::Archive *OldArchive, std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
246  std::vector<NewArchiveMember> NewMembers = computeNewArchiveMembers(OldArchive);
247  object::Archive::Kind Kind;
248  if (OldArchive) {
249  Kind = OldArchive->kind();
250  } else {
251  Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front()) : getDefaultForHost();
252  }
253  Error E = writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic, false, std::move(OldArchiveBuf));
254  failIfError(std::move(E), ArchiveName);
255 }
256 
257 extern void llvm_ar_create_static_lib(const str &archive_name, const vector<str> &objects) {
258  ArchiveName = archive_name;
259  Members = objects;
260  llvm::InitializeAllTargetInfos();
261  llvm::InitializeAllTargetMCs();
262  llvm::InitializeAllAsmParsers();
263  /// create or open the archive object.
264  ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(archive_name, -1, false);
265  std::error_code EC = Buf.getError();
266  if (EC && EC != errc::no_such_file_or_directory) {
267  fail("error opening '" + archive_name + "': " + EC.message());
268  }
269  if (!EC) {
270  Error Err = Error::success();
271  object::Archive Archive(Buf.get()->getMemBufferRef(), Err);
272  failIfError(std::move(Err), "unable to load '" + archive_name + "'");
273  performWriteOperation(&Archive, std::move(Buf.get()));
274  return;
275  }
276  assert(EC == errc::no_such_file_or_directory);
277  performWriteOperation(nullptr, nullptr);
278 }