summaryrefslogtreecommitdiffstats
path: root/vendor/ar_archive_writer
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/ar_archive_writer
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/ar_archive_writer')
-rw-r--r--vendor/ar_archive_writer/.cargo-checksum.json1
-rw-r--r--vendor/ar_archive_writer/Cargo.toml32
-rw-r--r--vendor/ar_archive_writer/LICENSE.txt219
-rw-r--r--vendor/ar_archive_writer/Readme.md14
-rw-r--r--vendor/ar_archive_writer/src/Alignment.h314
-rw-r--r--vendor/ar_archive_writer/src/Archive.h426
-rw-r--r--vendor/ar_archive_writer/src/ArchiveWriter.cpp872
-rw-r--r--vendor/ar_archive_writer/src/ArchiveWriter.h57
-rw-r--r--vendor/ar_archive_writer/src/alignment.rs23
-rw-r--r--vendor/ar_archive_writer/src/archive.rs71
-rw-r--r--vendor/ar_archive_writer/src/archive_writer.rs831
-rw-r--r--vendor/ar_archive_writer/src/lib.rs6
12 files changed, 2866 insertions, 0 deletions
diff --git a/vendor/ar_archive_writer/.cargo-checksum.json b/vendor/ar_archive_writer/.cargo-checksum.json
new file mode 100644
index 000000000..da8ef71d3
--- /dev/null
+++ b/vendor/ar_archive_writer/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"d77e4458f855c3eb93853cd1c88103eeaae424400d5d0ea22432b6ebb5d7a5d8","LICENSE.txt":"f72b120d1385408e9e380acc020756eb6ba1b461d66c328ea67327ba08d7dcfd","Readme.md":"8c0cff5a6a4a4b9d0809de3551f80dd8b5badfaef57f8bb1a8de1595fd04b61b","src/Alignment.h":"f3d4503c7e6486a0b438ac485d1dde82eb6ae8c2b8139fc4bb84cd15c9b72373","src/Archive.h":"925a5cf40a176059a5305de9ab560f4f5d3ff88fd5625ce2f368f70c0f0521ba","src/ArchiveWriter.cpp":"f52ee7968a2e0066eda1d0e622b769a7e704758e12d4514a7095d12b6733fe1f","src/ArchiveWriter.h":"4b4f5e761656f2f521b27c10d7830f7f4754ef3f24671875857beda1bd768d6c","src/alignment.rs":"01d0581fc5125b004b466302a545419d816015b146f30e43482f2183b2cdd9a1","src/archive.rs":"24b987e9878518d72de98057446f95e0bd2060b6e4321361573813bec3108ea5","src/archive_writer.rs":"9711d78417afdd468e2c655f1eec7333fc7ec31ed6d4d50ac6b6053dbe897a1c","src/lib.rs":"24fb03b1d2f801794b95f72a65a5e3eaccac201594431e8f7de3d5252cdb49bc"},"package":"276881980556fdadeb88aa1ffc667e4d2e8fe72531dfabcb7a82bb3c9ea9ba31"} \ No newline at end of file
diff --git a/vendor/ar_archive_writer/Cargo.toml b/vendor/ar_archive_writer/Cargo.toml
new file mode 100644
index 000000000..20ed646c5
--- /dev/null
+++ b/vendor/ar_archive_writer/Cargo.toml
@@ -0,0 +1,32 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "ar_archive_writer"
+version = "0.1.1"
+description = "A writer for object file ar archives"
+readme = "Readme.md"
+keywords = [
+ "ar",
+ "archive",
+]
+license = "Apache-2.0 WITH LLVM-exception"
+repository = "https://github.com/rust-lang/ar_archive_writer"
+resolver = "2"
+
+[dependencies.object]
+version = "0.29.0"
+features = [
+ "std",
+ "read",
+]
+default-features = false
diff --git a/vendor/ar_archive_writer/LICENSE.txt b/vendor/ar_archive_writer/LICENSE.txt
new file mode 100644
index 000000000..f9dc50615
--- /dev/null
+++ b/vendor/ar_archive_writer/LICENSE.txt
@@ -0,0 +1,219 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+--- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
diff --git a/vendor/ar_archive_writer/Readme.md b/vendor/ar_archive_writer/Readme.md
new file mode 100644
index 000000000..cee3ffe3e
--- /dev/null
+++ b/vendor/ar_archive_writer/Readme.md
@@ -0,0 +1,14 @@
+# A writer for object file ar archives
+
+This is based on commit [8ef3e895a](https://github.com/llvm/llvm-project/tree/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2) (15.0.0-rc3) of LLVM's archive writer.
+
+## License
+
+Licensed under Apache License v2.0 with LLVM Exceptions
+([LICENSE.txt](LICENSE.txt) or https://llvm.org/LICENSE.txt)
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you shall be dual licensed as above, without any
+additional terms or conditions.
diff --git a/vendor/ar_archive_writer/src/Alignment.h b/vendor/ar_archive_writer/src/Alignment.h
new file mode 100644
index 000000000..7075dca68
--- /dev/null
+++ b/vendor/ar_archive_writer/src/Alignment.h
@@ -0,0 +1,314 @@
+// Copied from https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/include/llvm/Support/Alignment.h
+
+//===-- llvm/Support/Alignment.h - Useful alignment functions ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains types to represent alignments.
+// They are instrumented to guarantee some invariants are preserved and prevent
+// invalid manipulations.
+//
+// - Align represents an alignment in bytes, it is always set and always a valid
+// power of two, its minimum value is 1 which means no alignment requirements.
+//
+// - MaybeAlign is an optional type, it may be undefined or set. When it's set
+// you can get the underlying Align type by using the getValue() method.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_ALIGNMENT_H_
+#define LLVM_SUPPORT_ALIGNMENT_H_
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/MathExtras.h"
+#include <cassert>
+#ifndef NDEBUG
+#include <string>
+#endif // NDEBUG
+
+namespace llvm {
+
+#define ALIGN_CHECK_ISPOSITIVE(decl) \
+ assert(decl > 0 && (#decl " should be defined"))
+
+/// This struct is a compact representation of a valid (non-zero power of two)
+/// alignment.
+/// It is suitable for use as static global constants.
+struct Align {
+private:
+ uint8_t ShiftValue = 0; /// The log2 of the required alignment.
+ /// ShiftValue is less than 64 by construction.
+
+ friend struct MaybeAlign;
+ friend unsigned Log2(Align);
+ friend bool operator==(Align Lhs, Align Rhs);
+ friend bool operator!=(Align Lhs, Align Rhs);
+ friend bool operator<=(Align Lhs, Align Rhs);
+ friend bool operator>=(Align Lhs, Align Rhs);
+ friend bool operator<(Align Lhs, Align Rhs);
+ friend bool operator>(Align Lhs, Align Rhs);
+ friend unsigned encode(struct MaybeAlign A);
+ friend struct MaybeAlign decodeMaybeAlign(unsigned Value);
+
+ /// A trivial type to allow construction of constexpr Align.
+ /// This is currently needed to workaround a bug in GCC 5.3 which prevents
+ /// definition of constexpr assign operators.
+ /// https://stackoverflow.com/questions/46756288/explicitly-defaulted-function-cannot-be-declared-as-constexpr-because-the-implic
+ /// FIXME: Remove this, make all assign operators constexpr and introduce user
+ /// defined literals when we don't have to support GCC 5.3 anymore.
+ /// https://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain
+ struct LogValue {
+ uint8_t Log;
+ };
+
+public:
+ /// Default is byte-aligned.
+ constexpr Align() = default;
+ /// Do not perform checks in case of copy/move construct/assign, because the
+ /// checks have been performed when building `Other`.
+ constexpr Align(const Align &Other) = default;
+ constexpr Align(Align &&Other) = default;
+ Align &operator=(const Align &Other) = default;
+ Align &operator=(Align &&Other) = default;
+
+ explicit Align(uint64_t Value) {
+ assert(Value > 0 && "Value must not be 0");
+ assert(llvm::isPowerOf2_64(Value) && "Alignment is not a power of 2");
+ ShiftValue = Log2_64(Value);
+ assert(ShiftValue < 64 && "Broken invariant");
+ }
+
+ /// This is a hole in the type system and should not be abused.
+ /// Needed to interact with C for instance.
+ uint64_t value() const { return uint64_t(1) << ShiftValue; }
+
+ // Returns the previous alignment.
+ Align previous() const {
+ assert(ShiftValue != 0 && "Undefined operation");
+ Align Out;
+ Out.ShiftValue = ShiftValue - 1;
+ return Out;
+ }
+
+ /// Allow constructions of constexpr Align.
+ template <size_t kValue> constexpr static LogValue Constant() {
+ return LogValue{static_cast<uint8_t>(CTLog2<kValue>())};
+ }
+
+ /// Allow constructions of constexpr Align from types.
+ /// Compile time equivalent to Align(alignof(T)).
+ template <typename T> constexpr static LogValue Of() {
+ return Constant<std::alignment_of<T>::value>();
+ }
+
+ /// Constexpr constructor from LogValue type.
+ constexpr Align(LogValue CA) : ShiftValue(CA.Log) {}
+};
+
+/// Treats the value 0 as a 1, so Align is always at least 1.
+inline Align assumeAligned(uint64_t Value) {
+ return Value ? Align(Value) : Align();
+}
+
+/// This struct is a compact representation of a valid (power of two) or
+/// undefined (0) alignment.
+struct MaybeAlign : public llvm::Optional<Align> {
+private:
+ using UP = llvm::Optional<Align>;
+
+public:
+ /// Default is undefined.
+ MaybeAlign() = default;
+ /// Do not perform checks in case of copy/move construct/assign, because the
+ /// checks have been performed when building `Other`.
+ MaybeAlign(const MaybeAlign &Other) = default;
+ MaybeAlign &operator=(const MaybeAlign &Other) = default;
+ MaybeAlign(MaybeAlign &&Other) = default;
+ MaybeAlign &operator=(MaybeAlign &&Other) = default;
+
+ /// Use llvm::Optional<Align> constructor.
+ using UP::UP;
+
+ explicit MaybeAlign(uint64_t Value) {
+ assert((Value == 0 || llvm::isPowerOf2_64(Value)) &&
+ "Alignment is neither 0 nor a power of 2");
+ if (Value)
+ emplace(Value);
+ }
+
+ /// For convenience, returns a valid alignment or 1 if undefined.
+ Align valueOrOne() const { return value_or(Align()); }
+};
+
+/// Checks that SizeInBytes is a multiple of the alignment.
+inline bool isAligned(Align Lhs, uint64_t SizeInBytes) {
+ return SizeInBytes % Lhs.value() == 0;
+}
+
+/// Checks that Addr is a multiple of the alignment.
+inline bool isAddrAligned(Align Lhs, const void *Addr) {
+ return isAligned(Lhs, reinterpret_cast<uintptr_t>(Addr));
+}
+
+/// Returns a multiple of A needed to store `Size` bytes.
+inline uint64_t alignTo(uint64_t Size, Align A) {
+ const uint64_t Value = A.value();
+ // The following line is equivalent to `(Size + Value - 1) / Value * Value`.
+
+ // The division followed by a multiplication can be thought of as a right
+ // shift followed by a left shift which zeros out the extra bits produced in
+ // the bump; `~(Value - 1)` is a mask where all those bits being zeroed out
+ // are just zero.
+
+ // Most compilers can generate this code but the pattern may be missed when
+ // multiple functions gets inlined.
+ return (Size + Value - 1) & ~(Value - 1U);
+}
+
+/// If non-zero \p Skew is specified, the return value will be a minimal integer
+/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
+/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
+/// Skew mod \p A'.
+///
+/// Examples:
+/// \code
+/// alignTo(5, Align(8), 7) = 7
+/// alignTo(17, Align(8), 1) = 17
+/// alignTo(~0LL, Align(8), 3) = 3
+/// \endcode
+inline uint64_t alignTo(uint64_t Size, Align A, uint64_t Skew) {
+ const uint64_t Value = A.value();
+ Skew %= Value;
+ return alignTo(Size - Skew, A) + Skew;
+}
+
+/// Aligns `Addr` to `Alignment` bytes, rounding up.
+inline uintptr_t alignAddr(const void *Addr, Align Alignment) {
+ uintptr_t ArithAddr = reinterpret_cast<uintptr_t>(Addr);
+ assert(static_cast<uintptr_t>(ArithAddr + Alignment.value() - 1) >=
+ ArithAddr &&
+ "Overflow");
+ return alignTo(ArithAddr, Alignment);
+}
+
+/// Returns the offset to the next integer (mod 2**64) that is greater than
+/// or equal to \p Value and is a multiple of \p Align.
+inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) {
+ return alignTo(Value, Alignment) - Value;
+}
+
+/// Returns the necessary adjustment for aligning `Addr` to `Alignment`
+/// bytes, rounding up.
+inline uint64_t offsetToAlignedAddr(const void *Addr, Align Alignment) {
+ return offsetToAlignment(reinterpret_cast<uintptr_t>(Addr), Alignment);
+}
+
+/// Returns the log2 of the alignment.
+inline unsigned Log2(Align A) { return A.ShiftValue; }
+
+/// Returns the alignment that satisfies both alignments.
+/// Same semantic as MinAlign.
+inline Align commonAlignment(Align A, uint64_t Offset) {
+ return Align(MinAlign(A.value(), Offset));
+}
+
+/// Returns a representation of the alignment that encodes undefined as 0.
+inline unsigned encode(MaybeAlign A) { return A ? A->ShiftValue + 1 : 0; }
+
+/// Dual operation of the encode function above.
+inline MaybeAlign decodeMaybeAlign(unsigned Value) {
+ if (Value == 0)
+ return MaybeAlign();
+ Align Out;
+ Out.ShiftValue = Value - 1;
+ return Out;
+}
+
+/// Returns a representation of the alignment, the encoded value is positive by
+/// definition.
+inline unsigned encode(Align A) { return encode(MaybeAlign(A)); }
+
+/// Comparisons between Align and scalars. Rhs must be positive.
+inline bool operator==(Align Lhs, uint64_t Rhs) {
+ ALIGN_CHECK_ISPOSITIVE(Rhs);
+ return Lhs.value() == Rhs;
+}
+inline bool operator!=(Align Lhs, uint64_t Rhs) {
+ ALIGN_CHECK_ISPOSITIVE(Rhs);
+ return Lhs.value() != Rhs;
+}
+inline bool operator<=(Align Lhs, uint64_t Rhs) {
+ ALIGN_CHECK_ISPOSITIVE(Rhs);
+ return Lhs.value() <= Rhs;
+}
+inline bool operator>=(Align Lhs, uint64_t Rhs) {
+ ALIGN_CHECK_ISPOSITIVE(Rhs);
+ return Lhs.value() >= Rhs;
+}
+inline bool operator<(Align Lhs, uint64_t Rhs) {
+ ALIGN_CHECK_ISPOSITIVE(Rhs);
+ return Lhs.value() < Rhs;
+}
+inline bool operator>(Align Lhs, uint64_t Rhs) {
+ ALIGN_CHECK_ISPOSITIVE(Rhs);
+ return Lhs.value() > Rhs;
+}
+
+/// Comparisons operators between Align.
+inline bool operator==(Align Lhs, Align Rhs) {
+ return Lhs.ShiftValue == Rhs.ShiftValue;
+}
+inline bool operator!=(Align Lhs, Align Rhs) {
+ return Lhs.ShiftValue != Rhs.ShiftValue;
+}
+inline bool operator<=(Align Lhs, Align Rhs) {
+ return Lhs.ShiftValue <= Rhs.ShiftValue;
+}
+inline bool operator>=(Align Lhs, Align Rhs) {
+ return Lhs.ShiftValue >= Rhs.ShiftValue;
+}
+inline bool operator<(Align Lhs, Align Rhs) {
+ return Lhs.ShiftValue < Rhs.ShiftValue;
+}
+inline bool operator>(Align Lhs, Align Rhs) {
+ return Lhs.ShiftValue > Rhs.ShiftValue;
+}
+
+// Don't allow relational comparisons with MaybeAlign.
+bool operator<=(Align Lhs, MaybeAlign Rhs) = delete;
+bool operator>=(Align Lhs, MaybeAlign Rhs) = delete;
+bool operator<(Align Lhs, MaybeAlign Rhs) = delete;
+bool operator>(Align Lhs, MaybeAlign Rhs) = delete;
+
+bool operator<=(MaybeAlign Lhs, Align Rhs) = delete;
+bool operator>=(MaybeAlign Lhs, Align Rhs) = delete;
+bool operator<(MaybeAlign Lhs, Align Rhs) = delete;
+bool operator>(MaybeAlign Lhs, Align Rhs) = delete;
+
+bool operator<=(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
+bool operator>=(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
+bool operator<(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
+bool operator>(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
+
+#ifndef NDEBUG
+// For usage in LLVM_DEBUG macros.
+inline std::string DebugStr(const Align &A) {
+ return std::to_string(A.value());
+}
+// For usage in LLVM_DEBUG macros.
+inline std::string DebugStr(const MaybeAlign &MA) {
+ if (MA)
+ return std::to_string(MA->value());
+ return "None";
+}
+#endif // NDEBUG
+
+#undef ALIGN_CHECK_ISPOSITIVE
+
+} // namespace llvm
+
+#endif // LLVM_SUPPORT_ALIGNMENT_H_
diff --git a/vendor/ar_archive_writer/src/Archive.h b/vendor/ar_archive_writer/src/Archive.h
new file mode 100644
index 000000000..929839750
--- /dev/null
+++ b/vendor/ar_archive_writer/src/Archive.h
@@ -0,0 +1,426 @@
+// Copied from https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/include/llvm/Object/Archive.h
+
+//===- Archive.h - ar archive file format -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the ar archive file format class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECT_ARCHIVE_H
+#define LLVM_OBJECT_ARCHIVE_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/fallible_iterator.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <cassert>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+template <typename T> class Optional;
+
+namespace object {
+
+const char ArchiveMagic[] = "!<arch>\n";
+const char ThinArchiveMagic[] = "!<thin>\n";
+const char BigArchiveMagic[] = "<bigaf>\n";
+
+class Archive;
+
+class AbstractArchiveMemberHeader {
+protected:
+ AbstractArchiveMemberHeader(const Archive *Parent) : Parent(Parent){};
+
+public:
+ friend class Archive;
+ virtual std::unique_ptr<AbstractArchiveMemberHeader> clone() const = 0;
+ virtual ~AbstractArchiveMemberHeader() = default;
+
+ /// Get the name without looking up long names.
+ virtual Expected<StringRef> getRawName() const = 0;
+ virtual StringRef getRawAccessMode() const = 0;
+ virtual StringRef getRawLastModified() const = 0;
+ virtual StringRef getRawUID() const = 0;
+ virtual StringRef getRawGID() const = 0;
+
+ /// Get the name looking up long names.
+ virtual Expected<StringRef> getName(uint64_t Size) const = 0;
+ virtual Expected<uint64_t> getSize() const = 0;
+ virtual uint64_t getOffset() const = 0;
+
+ /// Get next file member location.
+ virtual Expected<const char *> getNextChildLoc() const = 0;
+ virtual Expected<bool> isThin() const = 0;
+
+ Expected<sys::fs::perms> getAccessMode() const;
+ Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const;
+ Expected<unsigned> getUID() const;
+ Expected<unsigned> getGID() const;
+
+ /// Returns the size in bytes of the format-defined member header of the
+ /// concrete archive type.
+ virtual uint64_t getSizeOf() const = 0;
+
+ const Archive *Parent;
+};
+
+template <typename T>
+class CommonArchiveMemberHeader : public AbstractArchiveMemberHeader {
+public:
+ CommonArchiveMemberHeader(const Archive *Parent, const T *RawHeaderPtr)
+ : AbstractArchiveMemberHeader(Parent), ArMemHdr(RawHeaderPtr){};
+ StringRef getRawAccessMode() const override;
+ StringRef getRawLastModified() const override;
+ StringRef getRawUID() const override;
+ StringRef getRawGID() const override;
+
+ uint64_t getOffset() const override;
+ uint64_t getSizeOf() const override { return sizeof(T); }
+
+ T const *ArMemHdr;
+};
+
+struct UnixArMemHdrType {
+ char Name[16];
+ char LastModified[12];
+ char UID[6];
+ char GID[6];
+ char AccessMode[8];
+ char Size[10]; ///< Size of data, not including header or padding.
+ char Terminator[2];
+};
+
+class ArchiveMemberHeader : public CommonArchiveMemberHeader<UnixArMemHdrType> {
+public:
+ ArchiveMemberHeader(const Archive *Parent, const char *RawHeaderPtr,
+ uint64_t Size, Error *Err);
+
+ std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
+ return std::make_unique<ArchiveMemberHeader>(*this);
+ }
+
+ Expected<StringRef> getRawName() const override;
+
+ Expected<StringRef> getName(uint64_t Size) const override;
+ Expected<uint64_t> getSize() const override;
+ Expected<const char *> getNextChildLoc() const override;
+ Expected<bool> isThin() const override;
+};
+
+// File Member Header
+struct BigArMemHdrType {
+ char Size[20]; // File member size in decimal
+ char NextOffset[20]; // Next member offset in decimal
+ char PrevOffset[20]; // Previous member offset in decimal
+ char LastModified[12];
+ char UID[12];
+ char GID[12];
+ char AccessMode[12];
+ char NameLen[4]; // File member name length in decimal
+ union {
+ char Name[2]; // Start of member name
+ char Terminator[2];
+ };
+};
+
+// Define file member header of AIX big archive.
+class BigArchiveMemberHeader
+ : public CommonArchiveMemberHeader<BigArMemHdrType> {
+
+public:
+ BigArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr,
+ uint64_t Size, Error *Err);
+ std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
+ return std::make_unique<BigArchiveMemberHeader>(*this);
+ }
+
+ Expected<StringRef> getRawName() const override;
+ Expected<uint64_t> getRawNameSize() const;
+
+ Expected<StringRef> getName(uint64_t Size) const override;
+ Expected<uint64_t> getSize() const override;
+ Expected<const char *> getNextChildLoc() const override;
+ Expected<uint64_t> getNextOffset() const;
+ Expected<bool> isThin() const override { return false; }
+};
+
+class Archive : public Binary {
+ virtual void anchor();
+
+public:
+ class Child {
+ friend Archive;
+ friend AbstractArchiveMemberHeader;
+
+ const Archive *Parent;
+ std::unique_ptr<AbstractArchiveMemberHeader> Header;
+ /// Includes header but not padding byte.
+ StringRef Data;
+ /// Offset from Data to the start of the file.
+ uint16_t StartOfFile;
+
+ Expected<bool> isThinMember() const;
+
+ public:
+ Child(const Archive *Parent, const char *Start, Error *Err);
+ Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile);
+
+ Child(const Child &C)
+ : Parent(C.Parent), Data(C.Data), StartOfFile(C.StartOfFile) {
+ if (C.Header)
+ Header = C.Header->clone();
+ }
+
+ Child(Child &&C) {
+ Parent = std::move(C.Parent);
+ Header = std::move(C.Header);
+ Data = C.Data;
+ StartOfFile = C.StartOfFile;
+ }
+
+ Child &operator=(Child &&C) noexcept {
+ if (&C == this)
+ return *this;
+
+ Parent = std::move(C.Parent);
+ Header = std::move(C.Header);
+ Data = C.Data;
+ StartOfFile = C.StartOfFile;
+
+ return *this;
+ }
+
+ Child &operator=(const Child &C) {
+ if (&C == this)
+ return *this;
+
+ Parent = C.Parent;
+ if (C.Header)
+ Header = C.Header->clone();
+ Data = C.Data;
+ StartOfFile = C.StartOfFile;
+
+ return *this;
+ }
+
+ bool operator==(const Child &other) const {
+ assert(!Parent || !other.Parent || Parent == other.Parent);
+ return Data.begin() == other.Data.begin();
+ }
+
+ const Archive *getParent() const { return Parent; }
+ Expected<Child> getNext() const;
+
+ Expected<StringRef> getName() const;
+ Expected<std::string> getFullName() const;
+ Expected<StringRef> getRawName() const { return Header->getRawName(); }
+
+ Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const {
+ return Header->getLastModified();
+ }
+
+ StringRef getRawLastModified() const {
+ return Header->getRawLastModified();
+ }
+
+ Expected<unsigned> getUID() const { return Header->getUID(); }
+ Expected<unsigned> getGID() const { return Header->getGID(); }
+
+ Expected<sys::fs::perms> getAccessMode() const {
+ return Header->getAccessMode();
+ }
+
+ /// \return the size of the archive member without the header or padding.
+ Expected<uint64_t> getSize() const;
+ /// \return the size in the archive header for this member.
+ Expected<uint64_t> getRawSize() const;
+
+ Expected<StringRef> getBuffer() const;
+ uint64_t getChildOffset() const;
+ uint64_t getDataOffset() const { return getChildOffset() + StartOfFile; }
+
+ Expected<MemoryBufferRef> getMemoryBufferRef() const;
+
+ Expected<std::unique_ptr<Binary>>
+ getAsBinary(LLVMContext *Context = nullptr) const;
+ };
+
+ class ChildFallibleIterator {
+ Child C;
+
+ public:
+ ChildFallibleIterator() : C(Child(nullptr, nullptr, nullptr)) {}
+ ChildFallibleIterator(const Child &C) : C(C) {}
+
+ const Child *operator->() const { return &C; }
+ const Child &operator*() const { return C; }
+
+ bool operator==(const ChildFallibleIterator &other) const {
+ // Ignore errors here: If an error occurred during increment then getNext
+ // will have been set to child_end(), and the following comparison should
+ // do the right thing.
+ return C == other.C;
+ }
+
+ bool operator!=(const ChildFallibleIterator &other) const {
+ return !(*this == other);
+ }
+
+ Error inc() {
+ auto NextChild = C.getNext();
+ if (!NextChild)
+ return NextChild.takeError();
+ C = std::move(*NextChild);
+ return Error::success();
+ }
+ };
+
+ using child_iterator = fallible_iterator<ChildFallibleIterator>;
+
+ class Symbol {
+ const Archive *Parent;
+ uint32_t SymbolIndex;
+ uint32_t StringIndex; // Extra index to the string.
+
+ public:
+ Symbol(const Archive *p, uint32_t symi, uint32_t stri)
+ : Parent(p), SymbolIndex(symi), StringIndex(stri) {}
+
+ bool operator==(const Symbol &other) const {
+ return (Parent == other.Parent) && (SymbolIndex == other.SymbolIndex);
+ }
+
+ StringRef getName() const;
+ Expected<Child> getMember() const;
+ Symbol getNext() const;
+ };
+
+ class symbol_iterator {
+ Symbol symbol;
+
+ public:
+ symbol_iterator(const Symbol &s) : symbol(s) {}
+
+ const Symbol *operator->() const { return &symbol; }
+ const Symbol &operator*() const { return symbol; }
+
+ bool operator==(const symbol_iterator &other) const {
+ return symbol == other.symbol;
+ }
+
+ bool operator!=(const symbol_iterator &other) const {
+ return !(*this == other);
+ }
+
+ symbol_iterator &operator++() { // Preincrement
+ symbol = symbol.getNext();
+ return *this;
+ }
+ };
+
+ Archive(MemoryBufferRef Source, Error &Err);
+ static Expected<std::unique_ptr<Archive>> create(MemoryBufferRef Source);
+
+ /// Size field is 10 decimal digits long
+ static const uint64_t MaxMemberSize = 9999999999;
+
+ enum Kind { K_GNU, K_GNU64, K_BSD, K_DARWIN, K_DARWIN64, K_COFF, K_AIXBIG };
+
+ Kind kind() const { return (Kind)Format; }
+ bool isThin() const { return IsThin; }
+ static object::Archive::Kind getDefaultKindForHost();
+
+ child_iterator child_begin(Error &Err, bool SkipInternal = true) const;
+ child_iterator child_end() const;
+ iterator_range<child_iterator> children(Error &Err,
+ bool SkipInternal = true) const {
+ return make_range(child_begin(Err, SkipInternal), child_end());
+ }
+
+ symbol_iterator symbol_begin() const;
+ symbol_iterator symbol_end() const;
+ iterator_range<symbol_iterator> symbols() const {
+ return make_range(symbol_begin(), symbol_end());
+ }
+
+ static bool classof(Binary const *v) { return v->isArchive(); }
+
+ // check if a symbol is in the archive
+ Expected<Optional<Child>> findSym(StringRef name) const;
+
+ virtual bool isEmpty() const;
+ bool hasSymbolTable() const;
+ StringRef getSymbolTable() const { return SymbolTable; }
+ StringRef getStringTable() const { return StringTable; }
+ uint32_t getNumberOfSymbols() const;
+ virtual uint64_t getFirstChildOffset() const { return getArchiveMagicLen(); }
+
+ std::vector<std::unique_ptr<MemoryBuffer>> takeThinBuffers() {
+ return std::move(ThinBuffers);
+ }
+
+ std::unique_ptr<AbstractArchiveMemberHeader>
+ createArchiveMemberHeader(const char *RawHeaderPtr, uint64_t Size,
+ Error *Err) const;
+
+protected:
+ uint64_t getArchiveMagicLen() const;
+ void setFirstRegular(const Child &C);
+
+ StringRef SymbolTable;
+ StringRef StringTable;
+
+private:
+ StringRef FirstRegularData;
+ uint16_t FirstRegularStartOfFile = -1;
+
+ unsigned Format : 3;
+ unsigned IsThin : 1;
+ mutable std::vector<std::unique_ptr<MemoryBuffer>> ThinBuffers;
+};
+
+class BigArchive : public Archive {
+public:
+ /// Fixed-Length Header.
+ struct FixLenHdr {
+ char Magic[sizeof(BigArchiveMagic) - 1]; ///< Big archive magic string.
+ char MemOffset[20]; ///< Offset to member table.
+ char GlobSymOffset[20]; ///< Offset to global symbol table.
+ char
+ GlobSym64Offset[20]; ///< Offset global symbol table for 64-bit objects.
+ char FirstChildOffset[20]; ///< Offset to first archive member.
+ char LastChildOffset[20]; ///< Offset to last archive member.
+ char FreeOffset[20]; ///< Offset to first mem on free list.
+ };
+
+ const FixLenHdr *ArFixLenHdr;
+ uint64_t FirstChildOffset = 0;
+ uint64_t LastChildOffset = 0;
+
+public:
+ BigArchive(MemoryBufferRef Source, Error &Err);
+ uint64_t getFirstChildOffset() const override { return FirstChildOffset; }
+ uint64_t getLastChildOffset() const { return LastChildOffset; }
+ bool isEmpty() const override {
+ return Data.getBufferSize() == sizeof(FixLenHdr);
+ };
+};
+
+} // end namespace object
+} // end namespace llvm
+
+#endif // LLVM_OBJECT_ARCHIVE_H
diff --git a/vendor/ar_archive_writer/src/ArchiveWriter.cpp b/vendor/ar_archive_writer/src/ArchiveWriter.cpp
new file mode 100644
index 000000000..341f1e96f
--- /dev/null
+++ b/vendor/ar_archive_writer/src/ArchiveWriter.cpp
@@ -0,0 +1,872 @@
+// Copied from https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/lib/Object/ArchiveWriter.cpp
+
+//===- ArchiveWriter.cpp - ar File Format implementation --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the writeArchive function.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/SymbolicFile.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <map>
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+using namespace llvm;
+
+NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)
+ : Buf(MemoryBuffer::getMemBuffer(BufRef, false)),
+ MemberName(BufRef.getBufferIdentifier()) {}
+
+object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
+ auto MemBufferRef = this->Buf->getMemBufferRef();
+ Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
+ object::ObjectFile::createObjectFile(MemBufferRef);
+
+ if (OptionalObject)
+ return isa<object::MachOObjectFile>(**OptionalObject)
+ ? object::Archive::K_DARWIN
+ : (isa<object::XCOFFObjectFile>(**OptionalObject)
+ ? object::Archive::K_AIXBIG
+ : object::Archive::K_GNU);
+
+ // Squelch the error in case we had a non-object file.
+ consumeError(OptionalObject.takeError());
+
+ // If we're adding a bitcode file to the archive, detect the Archive kind
+ // based on the target triple.
+ LLVMContext Context;
+ if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {
+ if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
+ MemBufferRef, file_magic::bitcode, &Context)) {
+ auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
+ return Triple(IRObject.getTargetTriple()).isOSDarwin()
+ ? object::Archive::K_DARWIN
+ : object::Archive::K_GNU;
+ } else {
+ // Squelch the error in case this was not a SymbolicFile.
+ consumeError(ObjOrErr.takeError());
+ }
+ }
+
+ return object::Archive::getDefaultKindForHost();
+}
+
+Expected<NewArchiveMember>
+NewArchiveMember::getOldMember(const object::Archive::Child &OldMember,
+ bool Deterministic) {
+ Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef();
+ if (!BufOrErr)
+ return BufOrErr.takeError();
+
+ NewArchiveMember M;
+ M.Buf = MemoryBuffer::getMemBuffer(*BufOrErr, false);
+ M.MemberName = M.Buf->getBufferIdentifier();
+ if (!Deterministic) {
+ auto ModTimeOrErr = OldMember.getLastModified();
+ if (!ModTimeOrErr)
+ return ModTimeOrErr.takeError();
+ M.ModTime = ModTimeOrErr.get();
+ Expected<unsigned> UIDOrErr = OldMember.getUID();
+ if (!UIDOrErr)
+ return UIDOrErr.takeError();
+ M.UID = UIDOrErr.get();
+ Expected<unsigned> GIDOrErr = OldMember.getGID();
+ if (!GIDOrErr)
+ return GIDOrErr.takeError();
+ M.GID = GIDOrErr.get();
+ Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode();
+ if (!AccessModeOrErr)
+ return AccessModeOrErr.takeError();
+ M.Perms = AccessModeOrErr.get();
+ }
+ return std::move(M);
+}
+
+Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName,
+ bool Deterministic) {
+ sys::fs::file_status Status;
+ auto FDOrErr = sys::fs::openNativeFileForRead(FileName);
+ if (!FDOrErr)
+ return FDOrErr.takeError();
+ sys::fs::file_t FD = *FDOrErr;
+ assert(FD != sys::fs::kInvalidFile);
+
+ if (auto EC = sys::fs::status(FD, Status))
+ return errorCodeToError(EC);
+
+ // Opening a directory doesn't make sense. Let it fail.
+ // Linux cannot open directories with open(2), although
+ // cygwin and *bsd can.
+ if (Status.type() == sys::fs::file_type::directory_file)
+ return errorCodeToError(make_error_code(errc::is_a_directory));
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
+ MemoryBuffer::getOpenFile(FD, FileName, Status.getSize(), false);
+ if (!MemberBufferOrErr)
+ return errorCodeToError(MemberBufferOrErr.getError());
+
+ if (auto EC = sys::fs::closeFile(FD))
+ return errorCodeToError(EC);
+
+ NewArchiveMember M;
+ M.Buf = std::move(*MemberBufferOrErr);
+ M.MemberName = M.Buf->getBufferIdentifier();
+ if (!Deterministic) {
+ M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>(
+ Status.getLastModificationTime());
+ M.UID = Status.getUser();
+ M.GID = Status.getGroup();
+ M.Perms = Status.permissions();
+ }
+ return std::move(M);
+}
+
+template <typename T>
+static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) {
+ uint64_t OldPos = OS.tell();
+ OS << Data;
+ unsigned SizeSoFar = OS.tell() - OldPos;
+ assert(SizeSoFar <= Size && "Data doesn't fit in Size");
+ OS.indent(Size - SizeSoFar);
+}
+
+static bool isDarwin(object::Archive::Kind Kind) {
+ return Kind == object::Archive::K_DARWIN ||
+ Kind == object::Archive::K_DARWIN64;
+}
+
+static bool isAIXBigArchive(object::Archive::Kind Kind) {
+ return Kind == object::Archive::K_AIXBIG;
+}
+
+static bool isBSDLike(object::Archive::Kind Kind) {
+ switch (Kind) {
+ case object::Archive::K_GNU:
+ case object::Archive::K_GNU64:
+ case object::Archive::K_AIXBIG:
+ return false;
+ case object::Archive::K_BSD:
+ case object::Archive::K_DARWIN:
+ case object::Archive::K_DARWIN64:
+ return true;
+ case object::Archive::K_COFF:
+ break;
+ }
+ llvm_unreachable("not supported for writting");
+}
+
+template <class T>
+static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) {
+ support::endian::write(Out, Val,
+ isBSDLike(Kind) ? support::little : support::big);
+}
+
+static void printRestOfMemberHeader(
+ raw_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime,
+ unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {
+ printWithSpacePadding(Out, sys::toTimeT(ModTime), 12);
+
+ // The format has only 6 chars for uid and gid. Truncate if the provided
+ // values don't fit.
+ printWithSpacePadding(Out, UID % 1000000, 6);
+ printWithSpacePadding(Out, GID % 1000000, 6);
+
+ printWithSpacePadding(Out, format("%o", Perms), 8);
+ printWithSpacePadding(Out, Size, 10);
+ Out << "`\n";
+}
+
+static void
+printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name,
+ const sys::TimePoint<std::chrono::seconds> &ModTime,
+ unsigned UID, unsigned GID, unsigned Perms,
+ uint64_t Size) {
+ printWithSpacePadding(Out, Twine(Name) + "/", 16);
+ printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
+}
+
+static void
+printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name,
+ const sys::TimePoint<std::chrono::seconds> &ModTime,
+ unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {
+ uint64_t PosAfterHeader = Pos + 60 + Name.size();
+ // Pad so that even 64 bit object files are aligned.
+ unsigned Pad = offsetToAlignment(PosAfterHeader, Align(8));
+ unsigned NameWithPadding = Name.size() + Pad;
+ printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
+ printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
+ NameWithPadding + Size);
+ Out << Name;
+ while (Pad--)
+ Out.write(uint8_t(0));
+}
+
+static void
+printBigArchiveMemberHeader(raw_ostream &Out, StringRef Name,
+ const sys::TimePoint<std::chrono::seconds> &ModTime,
+ unsigned UID, unsigned GID, unsigned Perms,
+ uint64_t Size, unsigned PrevOffset,
+ unsigned NextOffset) {
+ unsigned NameLen = Name.size();
+
+ printWithSpacePadding(Out, Size, 20); // File member size
+ printWithSpacePadding(Out, NextOffset, 20); // Next member header offset
+ printWithSpacePadding(Out, PrevOffset, 20); // Previous member header offset
+ printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); // File member date
+ // The big archive format has 12 chars for uid and gid.
+ printWithSpacePadding(Out, UID % 1000000000000, 12); // UID
+ printWithSpacePadding(Out, GID % 1000000000000, 12); // GID
+ printWithSpacePadding(Out, format("%o", Perms), 12); // Permission
+ printWithSpacePadding(Out, NameLen, 4); // Name length
+ if (NameLen) {
+ printWithSpacePadding(Out, Name, NameLen); // Name
+ if (NameLen % 2)
+ Out.write(uint8_t(0)); // Null byte padding
+ }
+ Out << "`\n"; // Terminator
+}
+
+static bool useStringTable(bool Thin, StringRef Name) {
+ return Thin || Name.size() >= 16 || Name.contains('/');
+}
+
+static bool is64BitKind(object::Archive::Kind Kind) {
+ switch (Kind) {
+ case object::Archive::K_GNU:
+ case object::Archive::K_BSD:
+ case object::Archive::K_DARWIN:
+ case object::Archive::K_COFF:
+ return false;
+ case object::Archive::K_AIXBIG:
+ case object::Archive::K_DARWIN64:
+ case object::Archive::K_GNU64:
+ return true;
+ }
+ llvm_unreachable("not supported for writting");
+}
+
+static void
+printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable,
+ StringMap<uint64_t> &MemberNames, object::Archive::Kind Kind,
+ bool Thin, const NewArchiveMember &M,
+ sys::TimePoint<std::chrono::seconds> ModTime, uint64_t Size) {
+ if (isBSDLike(Kind))
+ return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID,
+ M.Perms, Size);
+ if (!useStringTable(Thin, M.MemberName))
+ return printGNUSmallMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
+ M.Perms, Size);
+ Out << '/';
+ uint64_t NamePos;
+ if (Thin) {
+ NamePos = StringTable.tell();
+ StringTable << M.MemberName << "/\n";
+ } else {
+ auto Insertion = MemberNames.insert({M.MemberName, uint64_t(0)});
+ if (Insertion.second) {
+ Insertion.first->second = StringTable.tell();
+ StringTable << M.MemberName << "/\n";
+ }
+ NamePos = Insertion.first->second;
+ }
+ printWithSpacePadding(Out, NamePos, 15);
+ printRestOfMemberHeader(Out, ModTime, M.UID, M.GID, M.Perms, Size);
+}
+
+namespace {
+struct MemberData {
+ std::vector<unsigned> Symbols;
+ std::string Header;
+ StringRef Data;
+ StringRef Padding;
+};
+} // namespace
+
+static MemberData computeStringTable(StringRef Names) {
+ unsigned Size = Names.size();
+ unsigned Pad = offsetToAlignment(Size, Align(2));
+ std::string Header;
+ raw_string_ostream Out(Header);
+ printWithSpacePadding(Out, "//", 48);
+ printWithSpacePadding(Out, Size + Pad, 10);
+ Out << "`\n";
+ Out.flush();
+ return {{}, std::move(Header), Names, Pad ? "\n" : ""};
+}
+
+static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) {
+ using namespace std::chrono;
+
+ if (!Deterministic)
+ return time_point_cast<seconds>(system_clock::now());
+ return sys::TimePoint<seconds>();
+}
+
+static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
+ Expected<uint32_t> SymFlagsOrErr = S.getFlags();
+ if (!SymFlagsOrErr)
+ // TODO: Actually report errors helpfully.
+ report_fatal_error(SymFlagsOrErr.takeError());
+ if (*SymFlagsOrErr & object::SymbolRef::SF_FormatSpecific)
+ return false;
+ if (!(*SymFlagsOrErr & object::SymbolRef::SF_Global))
+ return false;
+ if (*SymFlagsOrErr & object::SymbolRef::SF_Undefined)
+ return false;
+ return true;
+}
+
+static void printNBits(raw_ostream &Out, object::Archive::Kind Kind,
+ uint64_t Val) {
+ if (is64BitKind(Kind))
+ print<uint64_t>(Out, Kind, Val);
+ else
+ print<uint32_t>(Out, Kind, Val);
+}
+
+static uint64_t computeSymbolTableSize(object::Archive::Kind Kind,
+ uint64_t NumSyms, uint64_t OffsetSize,
+ StringRef StringTable,
+ uint32_t *Padding = nullptr) {
+ assert((OffsetSize == 4 || OffsetSize == 8) && "Unsupported OffsetSize");
+ uint64_t Size = OffsetSize; // Number of entries
+ if (isBSDLike(Kind))
+ Size += NumSyms * OffsetSize * 2; // Table
+ else
+ Size += NumSyms * OffsetSize; // Table
+ if (isBSDLike(Kind))
+ Size += OffsetSize; // byte count
+ Size += StringTable.size();
+ // ld64 expects the members to be 8-byte aligned for 64-bit content and at
+ // least 4-byte aligned for 32-bit content. Opt for the larger encoding
+ // uniformly.
+ // We do this for all bsd formats because it simplifies aligning members.
+ // For the big archive format, the symbol table is the last member, so there
+ // is no need to align.
+ uint32_t Pad = isAIXBigArchive(Kind)
+ ? 0
+ : offsetToAlignment(Size, Align(isBSDLike(Kind) ? 8 : 2));
+ Size += Pad;
+ if (Padding)
+ *Padding = Pad;
+ return Size;
+}
+
+static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
+ bool Deterministic, uint64_t Size,
+ uint64_t PrevMemberOffset = 0) {
+ if (isBSDLike(Kind)) {
+ const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";
+ printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,
+ Size);
+ } else if (isAIXBigArchive(Kind)) {
+ printBigArchiveMemberHeader(Out, "", now(Deterministic), 0, 0,
+ 0, Size, PrevMemberOffset, 0);
+ } else {
+ const char *Name = is64BitKind(Kind) ? "/SYM64" : "";
+ printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);
+ }
+}
+
+static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
+ bool Deterministic, ArrayRef<MemberData> Members,
+ StringRef StringTable,
+ uint64_t PrevMemberOffset = 0) {
+ // We don't write a symbol table on an archive with no members -- except on
+ // Darwin, where the linker will abort unless the archive has a symbol table.
+ if (StringTable.empty() && !isDarwin(Kind))
+ return;
+
+ unsigned NumSyms = 0;
+ for (const MemberData &M : Members)
+ NumSyms += M.Symbols.size();
+
+ uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4;
+ uint32_t Pad;
+ uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize, StringTable, &Pad);
+ writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset);
+
+ uint64_t Pos = isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr)
+ : Out.tell() + Size;
+
+ if (isBSDLike(Kind))
+ printNBits(Out, Kind, NumSyms * 2 * OffsetSize);
+ else
+ printNBits(Out, Kind, NumSyms);
+
+ for (const MemberData &M : Members) {
+ for (unsigned StringOffset : M.Symbols) {
+ if (isBSDLike(Kind))
+ printNBits(Out, Kind, StringOffset);
+ printNBits(Out, Kind, Pos); // member offset
+ }
+ Pos += M.Header.size() + M.Data.size() + M.Padding.size();
+ }
+
+ if (isBSDLike(Kind))
+ // byte count of the string table
+ printNBits(Out, Kind, StringTable.size());
+ Out << StringTable;
+
+ while (Pad--)
+ Out.write(uint8_t(0));
+}
+
+static Expected<std::vector<unsigned>>
+getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) {
+ std::vector<unsigned> Ret;
+
+ // In the scenario when LLVMContext is populated SymbolicFile will contain a
+ // reference to it, thus SymbolicFile should be destroyed first.
+ LLVMContext Context;
+ std::unique_ptr<object::SymbolicFile> Obj;
+
+ const file_magic Type = identify_magic(Buf.getBuffer());
+ // Treat unsupported file types as having no symbols.
+ if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
+ return Ret;
+ if (Type == file_magic::bitcode) {
+ auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
+ Buf, file_magic::bitcode, &Context);
+ if (!ObjOrErr)
+ return ObjOrErr.takeError();
+ Obj = std::move(*ObjOrErr);
+ } else {
+ auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
+ if (!ObjOrErr)
+ return ObjOrErr.takeError();
+ Obj = std::move(*ObjOrErr);
+ }
+
+ HasObject = true;
+ for (const object::BasicSymbolRef &S : Obj->symbols()) {
+ if (!isArchiveSymbol(S))
+ continue;
+ Ret.push_back(SymNames.tell());
+ if (Error E = S.printName(SymNames))
+ return std::move(E);
+ SymNames << '\0';
+ }
+ return Ret;
+}
+
+static Expected<std::vector<MemberData>>
+computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
+ object::Archive::Kind Kind, bool Thin, bool Deterministic,
+ bool NeedSymbols, ArrayRef<NewArchiveMember> NewMembers) {
+ static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
+
+ uint64_t Pos =
+ isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 0;
+
+ std::vector<MemberData> Ret;
+ bool HasObject = false;
+
+ // Deduplicate long member names in the string table and reuse earlier name
+ // offsets. This especially saves space for COFF Import libraries where all
+ // members have the same name.
+ StringMap<uint64_t> MemberNames;
+
+ // UniqueTimestamps is a special case to improve debugging on Darwin:
+ //
+ // The Darwin linker does not link debug info into the final
+ // binary. Instead, it emits entries of type N_OSO in in the output
+ // binary's symbol table, containing references to the linked-in
+ // object files. Using that reference, the debugger can read the
+ // debug data directly from the object files. Alternatively, an
+ // invocation of 'dsymutil' will link the debug data from the object
+ // files into a dSYM bundle, which can be loaded by the debugger,
+ // instead of the object files.
+ //
+ // For an object file, the N_OSO entries contain the absolute path
+ // path to the file, and the file's timestamp. For an object
+ // included in an archive, the path is formatted like
+ // "/absolute/path/to/archive.a(member.o)", and the timestamp is the
+ // archive member's timestamp, rather than the archive's timestamp.
+ //
+ // However, this doesn't always uniquely identify an object within
+ // an archive -- an archive file can have multiple entries with the
+ // same filename. (This will happen commonly if the original object
+ // files started in different directories.) The only way they get
+ // distinguished, then, is via the timestamp. But this process is
+ // unable to find the correct object file in the archive when there
+ // are two files of the same name and timestamp.
+ //
+ // Additionally, timestamp==0 is treated specially, and causes the
+ // timestamp to be ignored as a match criteria.
+ //
+ // That will "usually" work out okay when creating an archive not in
+ // deterministic timestamp mode, because the objects will probably
+ // have been created at different timestamps.
+ //
+ // To ameliorate this problem, in deterministic archive mode (which
+ // is the default), on Darwin we will emit a unique non-zero
+ // timestamp for each entry with a duplicated name. This is still
+ // deterministic: the only thing affecting that timestamp is the
+ // order of the files in the resultant archive.
+ //
+ // See also the functions that handle the lookup:
+ // in lldb: ObjectContainerBSDArchive::Archive::FindObject()
+ // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers().
+ bool UniqueTimestamps = Deterministic && isDarwin(Kind);
+ std::map<StringRef, unsigned> FilenameCount;
+ if (UniqueTimestamps) {
+ for (const NewArchiveMember &M : NewMembers)
+ FilenameCount[M.MemberName]++;
+ for (auto &Entry : FilenameCount)
+ Entry.second = Entry.second > 1 ? 1 : 0;
+ }
+
+ // The big archive format needs to know the offset of the previous member
+ // header.
+ unsigned PrevOffset = 0;
+ for (const NewArchiveMember &M : NewMembers) {
+ std::string Header;
+ raw_string_ostream Out(Header);
+
+ MemoryBufferRef Buf = M.Buf->getMemBufferRef();
+ StringRef Data = Thin ? "" : Buf.getBuffer();
+
+ // ld64 expects the members to be 8-byte aligned for 64-bit content and at
+ // least 4-byte aligned for 32-bit content. Opt for the larger encoding
+ // uniformly. This matches the behaviour with cctools and ensures that ld64
+ // is happy with archives that we generate.
+ unsigned MemberPadding =
+ isDarwin(Kind) ? offsetToAlignment(Data.size(), Align(8)) : 0;
+ unsigned TailPadding =
+ offsetToAlignment(Data.size() + MemberPadding, Align(2));
+ StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding);
+
+ sys::TimePoint<std::chrono::seconds> ModTime;
+ if (UniqueTimestamps)
+ // Increment timestamp for each file of a given name.
+ ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++);
+ else
+ ModTime = M.ModTime;
+
+ uint64_t Size = Buf.getBufferSize() + MemberPadding;
+ if (Size > object::Archive::MaxMemberSize) {
+ std::string StringMsg =
+ "File " + M.MemberName.str() + " exceeds size limit";
+ return make_error<object::GenericBinaryError>(
+ std::move(StringMsg), object::object_error::parse_failed);
+ }
+
+ if (isAIXBigArchive(Kind)) {
+ unsigned NextOffset = Pos + sizeof(object::BigArMemHdrType) +
+ alignTo(M.MemberName.size(), 2) + alignTo(Size, 2);
+ printBigArchiveMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
+ M.Perms, Size, PrevOffset, NextOffset);
+ PrevOffset = Pos;
+ } else {
+ printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M,
+ ModTime, Size);
+ }
+ Out.flush();
+
+ std::vector<unsigned> Symbols;
+ if (NeedSymbols) {
+ Expected<std::vector<unsigned>> SymbolsOrErr =
+ getSymbols(Buf, SymNames, HasObject);
+ if (auto E = SymbolsOrErr.takeError())
+ return std::move(E);
+ Symbols = std::move(*SymbolsOrErr);
+ }
+
+ Pos += Header.size() + Data.size() + Padding.size();
+ Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding});
+ }
+ // If there are no symbols, emit an empty symbol table, to satisfy Solaris
+ // tools, older versions of which expect a symbol table in a non-empty
+ // archive, regardless of whether there are any symbols in it.
+ if (HasObject && SymNames.tell() == 0)
+ SymNames << '\0' << '\0' << '\0';
+ return Ret;
+}
+
+namespace llvm {
+
+static ErrorOr<SmallString<128>> canonicalizePath(StringRef P) {
+ SmallString<128> Ret = P;
+ std::error_code Err = sys::fs::make_absolute(Ret);
+ if (Err)
+ return Err;
+ sys::path::remove_dots(Ret, /*removedotdot*/ true);
+ return Ret;
+}
+
+// Compute the relative path from From to To.
+Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) {
+ ErrorOr<SmallString<128>> PathToOrErr = canonicalizePath(To);
+ ErrorOr<SmallString<128>> DirFromOrErr = canonicalizePath(From);
+ if (!PathToOrErr || !DirFromOrErr)
+ return errorCodeToError(std::error_code(errno, std::generic_category()));
+
+ const SmallString<128> &PathTo = *PathToOrErr;
+ const SmallString<128> &DirFrom = sys::path::parent_path(*DirFromOrErr);
+
+ // Can't construct a relative path between different roots
+ if (sys::path::root_name(PathTo) != sys::path::root_name(DirFrom))
+ return sys::path::convert_to_slash(PathTo);
+
+ // Skip common prefixes
+ auto FromTo =
+ std::mismatch(sys::path::begin(DirFrom), sys::path::end(DirFrom),
+ sys::path::begin(PathTo));
+ auto FromI = FromTo.first;
+ auto ToI = FromTo.second;
+
+ // Construct relative path
+ SmallString<128> Relative;
+ for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
+ sys::path::append(Relative, sys::path::Style::posix, "..");
+
+ for (auto ToE = sys::path::end(PathTo); ToI != ToE; ++ToI)
+ sys::path::append(Relative, sys::path::Style::posix, *ToI);
+
+ return std::string(Relative.str());
+}
+
+static Error writeArchiveToStream(raw_ostream &Out,
+ ArrayRef<NewArchiveMember> NewMembers,
+ bool WriteSymtab, object::Archive::Kind Kind,
+ bool Deterministic, bool Thin) {
+ assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");
+
+ SmallString<0> SymNamesBuf;
+ raw_svector_ostream SymNames(SymNamesBuf);
+ SmallString<0> StringTableBuf;
+ raw_svector_ostream StringTable(StringTableBuf);
+
+ Expected<std::vector<MemberData>> DataOrErr =
+ computeMemberData(StringTable, SymNames, Kind, Thin, Deterministic,
+ WriteSymtab, NewMembers);
+ if (Error E = DataOrErr.takeError())
+ return E;
+ std::vector<MemberData> &Data = *DataOrErr;
+
+ if (!StringTableBuf.empty() && !isAIXBigArchive(Kind))
+ Data.insert(Data.begin(), computeStringTable(StringTableBuf));
+
+ // We would like to detect if we need to switch to a 64-bit symbol table.
+ uint64_t LastMemberEndOffset =
+ isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 8;
+ uint64_t LastMemberHeaderOffset = LastMemberEndOffset;
+ uint64_t NumSyms = 0;
+ for (const auto &M : Data) {
+ // Record the start of the member's offset
+ LastMemberHeaderOffset = LastMemberEndOffset;
+ // Account for the size of each part associated with the member.
+ LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size();
+ NumSyms += M.Symbols.size();
+ }
+
+ // The symbol table is put at the end of the big archive file. The symbol
+ // table is at the start of the archive file for other archive formats.
+ if (WriteSymtab && !isAIXBigArchive(Kind)) {
+ // We assume 32-bit offsets to see if 32-bit symbols are possible or not.
+ uint64_t SymtabSize = computeSymbolTableSize(Kind, NumSyms, 4, SymNamesBuf);
+ auto computeSymbolTableHeaderSize =
+ [=] {
+ SmallString<0> TmpBuf;
+ raw_svector_ostream Tmp(TmpBuf);
+ writeSymbolTableHeader(Tmp, Kind, Deterministic, SymtabSize);
+ return TmpBuf.size();
+ };
+ LastMemberHeaderOffset += computeSymbolTableHeaderSize() + SymtabSize;
+
+ // The SYM64 format is used when an archive's member offsets are larger than
+ // 32-bits can hold. The need for this shift in format is detected by
+ // writeArchive. To test this we need to generate a file with a member that
+ // has an offset larger than 32-bits but this demands a very slow test. To
+ // speed the test up we use this environment variable to pretend like the
+ // cutoff happens before 32-bits and instead happens at some much smaller
+ // value.
+ uint64_t Sym64Threshold = 1ULL << 32;
+ const char *Sym64Env = std::getenv("SYM64_THRESHOLD");
+ if (Sym64Env)
+ StringRef(Sym64Env).getAsInteger(10, Sym64Threshold);
+
+ // If LastMemberHeaderOffset isn't going to fit in a 32-bit varible we need
+ // to switch to 64-bit. Note that the file can be larger than 4GB as long as
+ // the last member starts before the 4GB offset.
+ if (LastMemberHeaderOffset >= Sym64Threshold) {
+ if (Kind == object::Archive::K_DARWIN)
+ Kind = object::Archive::K_DARWIN64;
+ else
+ Kind = object::Archive::K_GNU64;
+ }
+ }
+
+ if (Thin)
+ Out << "!<thin>\n";
+ else if (isAIXBigArchive(Kind))
+ Out << "<bigaf>\n";
+ else
+ Out << "!<arch>\n";
+
+ if (!isAIXBigArchive(Kind)) {
+ if (WriteSymtab)
+ writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf);
+ for (const MemberData &M : Data)
+ Out << M.Header << M.Data << M.Padding;
+ } else {
+ // For the big archive (AIX) format, compute a table of member names and
+ // offsets, used in the member table.
+ uint64_t MemberTableNameStrTblSize = 0;
+ std::vector<size_t> MemberOffsets;
+ std::vector<StringRef> MemberNames;
+ // Loop across object to find offset and names.
+ uint64_t MemberEndOffset = sizeof(object::BigArchive::FixLenHdr);
+ for (size_t I = 0, Size = NewMembers.size(); I != Size; ++I) {
+ const NewArchiveMember &Member = NewMembers[I];
+ MemberTableNameStrTblSize += Member.MemberName.size() + 1;
+ MemberOffsets.push_back(MemberEndOffset);
+ MemberNames.push_back(Member.MemberName);
+ // File member name ended with "`\n". The length is included in
+ // BigArMemHdrType.
+ MemberEndOffset += sizeof(object::BigArMemHdrType) +
+ alignTo(Data[I].Data.size(), 2) +
+ alignTo(Member.MemberName.size(), 2);
+ }
+
+ // AIX member table size.
+ unsigned MemberTableSize = 20 + // Number of members field
+ 20 * MemberOffsets.size() +
+ MemberTableNameStrTblSize;
+
+ unsigned GlobalSymbolOffset =
+ (WriteSymtab && NumSyms > 0)
+ ? LastMemberEndOffset +
+ alignTo(sizeof(object::BigArMemHdrType) + MemberTableSize, 2)
+ : 0;
+
+ // Fixed Sized Header.
+ printWithSpacePadding(Out, NewMembers.size() ? LastMemberEndOffset : 0,
+ 20); // Offset to member table
+ // If there are no file members in the archive, there will be no global
+ // symbol table.
+ printWithSpacePadding(Out, NewMembers.size() ? GlobalSymbolOffset : 0, 20);
+ printWithSpacePadding(
+ Out, 0,
+ 20); // Offset to 64 bits global symbol table - Not supported yet
+ printWithSpacePadding(
+ Out, NewMembers.size() ? sizeof(object::BigArchive::FixLenHdr) : 0,
+ 20); // Offset to first archive member
+ printWithSpacePadding(Out, NewMembers.size() ? LastMemberHeaderOffset : 0,
+ 20); // Offset to last archive member
+ printWithSpacePadding(
+ Out, 0,
+ 20); // Offset to first member of free list - Not supported yet
+
+ for (const MemberData &M : Data) {
+ Out << M.Header << M.Data;
+ if (M.Data.size() % 2)
+ Out << '\0';
+ }
+
+ if (NewMembers.size()) {
+ // Member table.
+ printBigArchiveMemberHeader(Out, "", sys::toTimePoint(0), 0, 0, 0,
+ MemberTableSize, LastMemberHeaderOffset,
+ GlobalSymbolOffset);
+ printWithSpacePadding(Out, MemberOffsets.size(), 20); // Number of members
+ for (uint64_t MemberOffset : MemberOffsets)
+ printWithSpacePadding(Out, MemberOffset,
+ 20); // Offset to member file header.
+ for (StringRef MemberName : MemberNames)
+ Out << MemberName << '\0'; // Member file name, null byte padding.
+
+ if (MemberTableNameStrTblSize % 2)
+ Out << '\0'; // Name table must be tail padded to an even number of
+ // bytes.
+
+ if (WriteSymtab && NumSyms > 0)
+ writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf,
+ LastMemberEndOffset);
+ }
+ }
+ Out.flush();
+ return Error::success();
+}
+
+Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
+ bool WriteSymtab, object::Archive::Kind Kind,
+ bool Deterministic, bool Thin,
+ std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
+ Expected<sys::fs::TempFile> Temp =
+ sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a");
+ if (!Temp)
+ return Temp.takeError();
+ raw_fd_ostream Out(Temp->FD, false);
+
+ if (Error E = writeArchiveToStream(Out, NewMembers, WriteSymtab, Kind,
+ Deterministic, Thin)) {
+ if (Error DiscardError = Temp->discard())
+ return joinErrors(std::move(E), std::move(DiscardError));
+ return E;
+ }
+
+ // At this point, we no longer need whatever backing memory
+ // was used to generate the NewMembers. On Windows, this buffer
+ // could be a mapped view of the file we want to replace (if
+ // we're updating an existing archive, say). In that case, the
+ // rename would still succeed, but it would leave behind a
+ // temporary file (actually the original file renamed) because
+ // a file cannot be deleted while there's a handle open on it,
+ // only renamed. So by freeing this buffer, this ensures that
+ // the last open handle on the destination file, if any, is
+ // closed before we attempt to rename.
+ OldArchiveBuf.reset();
+
+ return Temp->keep(ArcName);
+}
+
+Expected<std::unique_ptr<MemoryBuffer>>
+writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab,
+ object::Archive::Kind Kind, bool Deterministic,
+ bool Thin) {
+ SmallVector<char, 0> ArchiveBufferVector;
+ raw_svector_ostream ArchiveStream(ArchiveBufferVector);
+
+ if (Error E = writeArchiveToStream(ArchiveStream, NewMembers, WriteSymtab,
+ Kind, Deterministic, Thin))
+ return std::move(E);
+
+ return std::make_unique<SmallVectorMemoryBuffer>(
+ std::move(ArchiveBufferVector), /*RequiresNullTerminator=*/false);
+}
+
+} // namespace llvm
diff --git a/vendor/ar_archive_writer/src/ArchiveWriter.h b/vendor/ar_archive_writer/src/ArchiveWriter.h
new file mode 100644
index 000000000..0595d4888
--- /dev/null
+++ b/vendor/ar_archive_writer/src/ArchiveWriter.h
@@ -0,0 +1,57 @@
+// Copied from https://github.com/llvm/llvm-project/blob/3d3ef9d073e1e27ea57480b371b7f5a9f5642ed2/llvm/include/llvm/Object/ArchiveWriter.h
+
+//===- ArchiveWriter.h - ar archive file format writer ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Declares the writeArchive function for writing an archive file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJECT_ARCHIVEWRITER_H
+#define LLVM_OBJECT_ARCHIVEWRITER_H
+
+#include "llvm/Object/Archive.h"
+
+namespace llvm {
+
+struct NewArchiveMember {
+ std::unique_ptr<MemoryBuffer> Buf;
+ StringRef MemberName;
+ sys::TimePoint<std::chrono::seconds> ModTime;
+ unsigned UID = 0, GID = 0, Perms = 0644;
+
+ NewArchiveMember() = default;
+ NewArchiveMember(MemoryBufferRef BufRef);
+
+ // Detect the archive format from the object or bitcode file. This helps
+ // assume the archive format when creating or editing archives in the case
+ // one isn't explicitly set.
+ object::Archive::Kind detectKindFromObject() const;
+
+ static Expected<NewArchiveMember>
+ getOldMember(const object::Archive::Child &OldMember, bool Deterministic);
+
+ static Expected<NewArchiveMember> getFile(StringRef FileName,
+ bool Deterministic);
+};
+
+Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To);
+
+Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
+ bool WriteSymtab, object::Archive::Kind Kind,
+ bool Deterministic, bool Thin,
+ std::unique_ptr<MemoryBuffer> OldArchiveBuf = nullptr);
+
+// writeArchiveToBuffer is similar to writeArchive but returns the Archive in a
+// buffer instead of writing it out to a file.
+Expected<std::unique_ptr<MemoryBuffer>>
+writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab,
+ object::Archive::Kind Kind, bool Deterministic, bool Thin);
+}
+
+#endif
diff --git a/vendor/ar_archive_writer/src/alignment.rs b/vendor/ar_archive_writer/src/alignment.rs
new file mode 100644
index 000000000..816538b49
--- /dev/null
+++ b/vendor/ar_archive_writer/src/alignment.rs
@@ -0,0 +1,23 @@
+// Derived from code in LLVM, which is:
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Derived from https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Support/Alignment.h
+
+/// Returns a multiple of `align` needed to store `size` bytes.
+pub(crate) fn align_to(size: u64, align: u64) -> u64 {
+ (size + align - 1) & !(align - 1)
+}
+
+/*
+/// Returns the offset to the next integer (mod 2**64) that is greater than
+/// or equal to \p Value and is a multiple of \p Align.
+inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) {
+ return alignTo(Value, Alignment) - Value;
+}
+*/
+
+pub(crate) fn offset_to_alignment(value: u64, alignment: u64) -> u64 {
+ align_to(value, alignment) - value
+}
diff --git a/vendor/ar_archive_writer/src/archive.rs b/vendor/ar_archive_writer/src/archive.rs
new file mode 100644
index 000000000..0bc4111c6
--- /dev/null
+++ b/vendor/ar_archive_writer/src/archive.rs
@@ -0,0 +1,71 @@
+// Derived from code in LLVM, which is:
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Derived from https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Object/Archive.h
+
+/// Size field is 10 decimal digits long
+pub(crate) const MAX_MEMBER_SIZE: u64 = 9999999999;
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ArchiveKind {
+ Gnu,
+ Gnu64,
+ Bsd,
+ Darwin,
+ Darwin64,
+ Coff,
+ AixBig,
+}
+
+pub(crate) mod big_archive {
+ #[repr(C)]
+ pub(crate) struct BigArMemHdrType {
+ /// File member size in decimal
+ size: [u8; 20],
+
+ /// Next member offset in decimal
+ next_offset: [u8; 20],
+
+ /// Previous member offset in decimal
+ prev_offset: [u8; 20],
+
+ last_modified: [u8; 12],
+
+ uid: [u8; 12],
+ gid: [u8; 12],
+
+ access_mode: [u8; 12],
+
+ /// File member name length in decimal
+ name_len: [u8; 4],
+
+ terminator: [u8; 2],
+ }
+
+ /// Fixed-Length Header.
+ #[repr(C)]
+ pub(crate) struct FixLenHdr {
+ /// Big archive magic string.
+ magic: [u8; 8],
+
+ /// Offset to member table.
+ mem_offset: [u8; 20],
+
+ /// Offset to global symbol table.
+ glob_sym_offset: [u8; 20],
+
+ /// Offset global symbol table for 64-bit objects.
+ glob_sym64_offset: [u8; 20],
+
+ /// Offset to first archive member.
+ first_child_offset: [u8; 20],
+
+ /// Offset to last archive member.
+ last_child_offset: [u8; 20],
+
+ /// Offset to first mem on free list.
+ free_offset: [u8; 20],
+ }
+}
diff --git a/vendor/ar_archive_writer/src/archive_writer.rs b/vendor/ar_archive_writer/src/archive_writer.rs
new file mode 100644
index 000000000..24dcc3cfc
--- /dev/null
+++ b/vendor/ar_archive_writer/src/archive_writer.rs
@@ -0,0 +1,831 @@
+#![allow(rustc::default_hash_types, rustc::potential_query_instability)]
+
+// Derived from code in LLVM, which is:
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Derived from:
+// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Object/ArchiveWriter.h
+// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/lib/Object/ArchiveWriter.cpp
+
+use std::collections::HashMap;
+use std::io::{self, Cursor, Seek, Write};
+
+use object::{Object, ObjectSymbol};
+
+use crate::alignment::*;
+use crate::archive::*;
+
+pub struct NewArchiveMember<'a> {
+ pub buf: Box<dyn AsRef<[u8]> + 'a>,
+ pub get_symbols: fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
+ pub member_name: String,
+ pub mtime: u64,
+ pub uid: u32,
+ pub gid: u32,
+ pub perms: u32,
+}
+
+fn is_darwin(kind: ArchiveKind) -> bool {
+ matches!(kind, ArchiveKind::Darwin | ArchiveKind::Darwin64)
+}
+
+fn is_aix_big_archive(kind: ArchiveKind) -> bool {
+ kind == ArchiveKind::AixBig
+}
+
+fn is_bsd_like(kind: ArchiveKind) -> bool {
+ match kind {
+ ArchiveKind::Gnu | ArchiveKind::Gnu64 | ArchiveKind::AixBig => false,
+ ArchiveKind::Bsd | ArchiveKind::Darwin | ArchiveKind::Darwin64 => true,
+ ArchiveKind::Coff => panic!("not supported for writing"),
+ }
+}
+
+fn print_rest_of_member_header<W: Write>(
+ w: &mut W,
+ mtime: u64,
+ uid: u32,
+ gid: u32,
+ perms: u32,
+ size: u64,
+) -> io::Result<()> {
+ // The format has only 6 chars for uid and gid. Truncate if the provided
+ // values don't fit.
+ write!(
+ w,
+ "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
+ mtime,
+ uid % 1000000,
+ gid % 1000000,
+ perms,
+ size
+ )
+}
+
+fn print_gnu_small_member_header<W: Write>(
+ w: &mut W,
+ name: String,
+ mtime: u64,
+ uid: u32,
+ gid: u32,
+ perms: u32,
+ size: u64,
+) -> io::Result<()> {
+ write!(w, "{:<16}", name + "/")?;
+ print_rest_of_member_header(w, mtime, uid, gid, perms, size)
+}
+
+fn print_bsd_member_header<W: Write>(
+ w: &mut W,
+ pos: u64,
+ name: &str,
+ mtime: u64,
+ uid: u32,
+ gid: u32,
+ perms: u32,
+ size: u64,
+) -> io::Result<()> {
+ let pos_after_header = pos + 60 + u64::try_from(name.len()).unwrap();
+ // Pad so that even 64 bit object files are aligned.
+ let pad = offset_to_alignment(pos_after_header, 8);
+ let name_with_padding = u64::try_from(name.len()).unwrap() + pad;
+ write!(w, "#1/{:<13}", name_with_padding)?;
+ print_rest_of_member_header(w, mtime, uid, gid, perms, name_with_padding + size)?;
+ write!(w, "{}", name)?;
+ write!(
+ w,
+ "{nil:\0<pad$}",
+ nil = "",
+ pad = usize::try_from(pad).unwrap()
+ )
+}
+
+fn print_big_archive_member_header<W: Write>(
+ w: &mut W,
+ name: &str,
+ mtime: u64,
+ uid: u32,
+ gid: u32,
+ perms: u32,
+ size: u64,
+ prev_offset: u64,
+ next_offset: u64,
+) -> io::Result<()> {
+ write!(
+ w,
+ "{:<20}{:<20}{:<20}{:<12}{:<12}{:<12}{:<12o}{:<4}",
+ size,
+ next_offset,
+ prev_offset,
+ mtime,
+ u64::from(uid) % 1000000000000u64,
+ u64::from(gid) % 1000000000000u64,
+ perms,
+ name.len(),
+ )?;
+
+ if !name.is_empty() {
+ write!(w, "{}", name)?;
+
+ if name.len() % 2 != 0 {
+ write!(w, "\0")?;
+ }
+ }
+
+ write!(w, "`\n")?;
+
+ Ok(())
+}
+
+fn use_string_table(thin: bool, name: &str) -> bool {
+ thin || name.len() >= 16 || name.contains('/')
+}
+
+fn is_64bit_kind(kind: ArchiveKind) -> bool {
+ match kind {
+ ArchiveKind::Gnu | ArchiveKind::Bsd | ArchiveKind::Darwin | ArchiveKind::Coff => false,
+ ArchiveKind::AixBig | ArchiveKind::Darwin64 | ArchiveKind::Gnu64 => true,
+ }
+}
+
+fn print_member_header<'m, W: Write, T: Write + Seek>(
+ w: &mut W,
+ pos: u64,
+ string_table: &mut T,
+ member_names: &mut HashMap<&'m str, u64>,
+ kind: ArchiveKind,
+ thin: bool,
+ m: &'m NewArchiveMember<'m>,
+ mtime: u64,
+ size: u64,
+) -> io::Result<()> {
+ if is_bsd_like(kind) {
+ return print_bsd_member_header(w, pos, &m.member_name, mtime, m.uid, m.gid, m.perms, size);
+ }
+
+ if !use_string_table(thin, &m.member_name) {
+ return print_gnu_small_member_header(
+ w,
+ m.member_name.clone(),
+ mtime,
+ m.uid,
+ m.gid,
+ m.perms,
+ size,
+ );
+ }
+
+ write!(w, "/")?;
+ let name_pos;
+ if thin {
+ name_pos = string_table.stream_position()?;
+ write!(string_table, "{}/\n", m.member_name)?;
+ } else {
+ if let Some(&pos) = member_names.get(&*m.member_name) {
+ name_pos = pos;
+ } else {
+ name_pos = string_table.stream_position()?;
+ member_names.insert(&m.member_name, name_pos);
+ write!(string_table, "{}/\n", m.member_name)?;
+ }
+ }
+ write!(w, "{:<15}", name_pos)?;
+ print_rest_of_member_header(w, mtime, m.uid, m.gid, m.perms, size)
+}
+
+struct MemberData<'a> {
+ symbols: Vec<u64>,
+ header: Vec<u8>,
+ data: &'a [u8],
+ padding: &'static [u8],
+}
+
+fn compute_string_table(names: &[u8]) -> MemberData<'_> {
+ let size = u64::try_from(names.len()).unwrap();
+ let pad = offset_to_alignment(size, 2);
+ let mut header = Vec::new();
+ write!(header, "{:<48}", "//").unwrap();
+ write!(header, "{:<10}", size + pad).unwrap();
+ write!(header, "`\n").unwrap();
+ MemberData {
+ symbols: vec![],
+ header,
+ data: names,
+ padding: if pad != 0 { b"\n" } else { b"" },
+ }
+}
+
+fn now(deterministic: bool) -> u64 {
+ if !deterministic {
+ todo!("non deterministic mode is not yet supported"); // FIXME
+ }
+ 0
+}
+
+fn is_archive_symbol(sym: &object::read::Symbol<'_, '_>) -> bool {
+ // FIXME Use a better equivalent of LLVM's SymbolRef::SF_FormatSpecific
+ if sym.kind() == object::SymbolKind::Null
+ || sym.kind() == object::SymbolKind::File
+ || sym.kind() == object::SymbolKind::Section
+ {
+ return false;
+ }
+ if !sym.is_global() {
+ return false;
+ }
+ if sym.is_undefined() {
+ return false;
+ }
+ true
+}
+
+fn print_n_bits<W: Write>(w: &mut W, kind: ArchiveKind, val: u64) -> io::Result<()> {
+ if is_64bit_kind(kind) {
+ w.write_all(&if is_bsd_like(kind) {
+ u64::to_le_bytes(val)
+ } else {
+ u64::to_be_bytes(val)
+ })
+ } else {
+ w.write_all(&if is_bsd_like(kind) {
+ u32::to_le_bytes(u32::try_from(val).unwrap())
+ } else {
+ u32::to_be_bytes(u32::try_from(val).unwrap())
+ })
+ }
+}
+
+fn compute_symbol_table_size_and_pad(
+ kind: ArchiveKind,
+ num_syms: u64,
+ offset_size: u64,
+ string_table: &[u8],
+) -> (u64, u64) {
+ assert!(
+ offset_size == 4 || offset_size == 8,
+ "Unsupported offset_size"
+ );
+ let mut size = offset_size; // Number of entries
+ if is_bsd_like(kind) {
+ size += num_syms * offset_size * 2; // Table
+ } else {
+ size += num_syms * offset_size; // Table
+ }
+ if is_bsd_like(kind) {
+ size += offset_size; // byte count;
+ }
+ size += u64::try_from(string_table.len()).unwrap();
+ // ld64 expects the members to be 8-byte aligned for 64-bit content and at
+ // least 4-byte aligned for 32-bit content. Opt for the larger encoding
+ // uniformly.
+ // We do this for all bsd formats because it simplifies aligning members.
+ let pad = if is_aix_big_archive(kind) {
+ 0
+ } else {
+ offset_to_alignment(size, if is_bsd_like(kind) { 8 } else { 2 })
+ };
+ size += pad;
+ (size, pad)
+}
+
+fn write_symbol_table_header<W: Write + Seek>(
+ w: &mut W,
+ kind: ArchiveKind,
+ deterministic: bool,
+ size: u64,
+ prev_member_offset: u64,
+) -> io::Result<()> {
+ if is_bsd_like(kind) {
+ let name = if is_64bit_kind(kind) {
+ "__.SYMDEF_64"
+ } else {
+ "__.SYMDEF"
+ };
+ let pos = w.stream_position()?;
+ print_bsd_member_header(w, pos, name, now(deterministic), 0, 0, 0, size)
+ } else if is_aix_big_archive(kind) {
+ print_big_archive_member_header(
+ w,
+ "",
+ now(deterministic),
+ 0,
+ 0,
+ 0,
+ size,
+ prev_member_offset,
+ 0,
+ )
+ } else {
+ let name = if is_64bit_kind(kind) { "/SYM64" } else { "" };
+ print_gnu_small_member_header(w, name.to_string(), now(deterministic), 0, 0, 0, size)
+ }
+}
+
+fn write_symbol_table<W: Write + Seek>(
+ w: &mut W,
+ kind: ArchiveKind,
+ deterministic: bool,
+ members: &[MemberData<'_>],
+ string_table: &[u8],
+ prev_member_offset: u64,
+) -> io::Result<()> {
+ // We don't write a symbol table on an archive with no members -- except on
+ // Darwin, where the linker will abort unless the archive has a symbol table.
+ if string_table.is_empty() && !is_darwin(kind) {
+ return Ok(());
+ }
+
+ let num_syms = u64::try_from(members.iter().map(|m| m.symbols.len()).sum::<usize>()).unwrap();
+
+ let offset_size = if is_64bit_kind(kind) { 8 } else { 4 };
+ let (size, pad) = compute_symbol_table_size_and_pad(kind, num_syms, offset_size, string_table);
+ write_symbol_table_header(w, kind, deterministic, size, prev_member_offset)?;
+
+ let mut pos = if is_aix_big_archive(kind) {
+ u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
+ } else {
+ w.stream_position()? + size
+ };
+
+ if is_bsd_like(kind) {
+ print_n_bits(w, kind, num_syms * 2 * offset_size)?;
+ } else {
+ print_n_bits(w, kind, num_syms)?;
+ }
+
+ for m in members {
+ for &string_offset in &m.symbols {
+ if is_bsd_like(kind) {
+ print_n_bits(w, kind, string_offset)?;
+ }
+ print_n_bits(w, kind, pos)?; // member offset
+ }
+ pos += u64::try_from(m.header.len() + m.data.len() + m.padding.len()).unwrap();
+ }
+
+ if is_bsd_like(kind) {
+ // byte count of the string table
+ print_n_bits(w, kind, u64::try_from(string_table.len()).unwrap())?;
+ }
+
+ w.write_all(string_table)?;
+
+ write!(
+ w,
+ "{nil:\0<pad$}",
+ nil = "",
+ pad = usize::try_from(pad).unwrap()
+ )
+}
+
+pub fn get_native_object_symbols(
+ buf: &[u8],
+ f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
+) -> io::Result<bool> {
+ // FIXME match what LLVM does
+
+ match object::File::parse(buf) {
+ Ok(file) => {
+ for sym in file.symbols() {
+ if !is_archive_symbol(&sym) {
+ continue;
+ }
+ f(sym.name_bytes().expect("FIXME"))?;
+ }
+ Ok(true)
+ }
+ Err(_) => Ok(false),
+ }
+}
+
+// NOTE: LLVM calls this getSymbols and has the get_native_symbols function inlined
+fn write_symbols(
+ buf: &[u8],
+ get_symbols: fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
+ sym_names: &mut Cursor<Vec<u8>>,
+ has_object: &mut bool,
+) -> io::Result<Vec<u64>> {
+ let mut ret = vec![];
+ *has_object = get_symbols(buf, &mut |sym| {
+ ret.push(sym_names.stream_position()?);
+ sym_names.write_all(sym)?;
+ sym_names.write_all(&[0])?;
+ Ok(())
+ })?;
+ Ok(ret)
+}
+
+fn compute_member_data<'a, S: Write + Seek>(
+ string_table: &mut S,
+ sym_names: &mut Cursor<Vec<u8>>,
+ kind: ArchiveKind,
+ thin: bool,
+ deterministic: bool,
+ need_symbols: bool,
+ new_members: &'a [NewArchiveMember<'a>],
+) -> io::Result<Vec<MemberData<'a>>> {
+ const PADDING_DATA: &[u8; 8] = &[b'\n'; 8];
+
+ // This ignores the symbol table, but we only need the value mod 8 and the
+ // symbol table is aligned to be a multiple of 8 bytes
+ let mut pos = if is_aix_big_archive(kind) {
+ u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
+ } else {
+ 0
+ };
+
+ let mut ret = vec![];
+ let mut has_object = false;
+
+ // Deduplicate long member names in the string table and reuse earlier name
+ // offsets. This especially saves space for COFF Import libraries where all
+ // members have the same name.
+ let mut member_names = HashMap::<&str, u64>::new();
+
+ // UniqueTimestamps is a special case to improve debugging on Darwin:
+ //
+ // The Darwin linker does not link debug info into the final
+ // binary. Instead, it emits entries of type N_OSO in in the output
+ // binary's symbol table, containing references to the linked-in
+ // object files. Using that reference, the debugger can read the
+ // debug data directly from the object files. Alternatively, an
+ // invocation of 'dsymutil' will link the debug data from the object
+ // files into a dSYM bundle, which can be loaded by the debugger,
+ // instead of the object files.
+ //
+ // For an object file, the N_OSO entries contain the absolute path
+ // path to the file, and the file's timestamp. For an object
+ // included in an archive, the path is formatted like
+ // "/absolute/path/to/archive.a(member.o)", and the timestamp is the
+ // archive member's timestamp, rather than the archive's timestamp.
+ //
+ // However, this doesn't always uniquely identify an object within
+ // an archive -- an archive file can have multiple entries with the
+ // same filename. (This will happen commonly if the original object
+ // files started in different directories.) The only way they get
+ // distinguished, then, is via the timestamp. But this process is
+ // unable to find the correct object file in the archive when there
+ // are two files of the same name and timestamp.
+ //
+ // Additionally, timestamp==0 is treated specially, and causes the
+ // timestamp to be ignored as a match criteria.
+ //
+ // That will "usually" work out okay when creating an archive not in
+ // deterministic timestamp mode, because the objects will probably
+ // have been created at different timestamps.
+ //
+ // To ameliorate this problem, in deterministic archive mode (which
+ // is the default), on Darwin we will emit a unique non-zero
+ // timestamp for each entry with a duplicated name. This is still
+ // deterministic: the only thing affecting that timestamp is the
+ // order of the files in the resultant archive.
+ //
+ // See also the functions that handle the lookup:
+ // in lldb: ObjectContainerBSDArchive::Archive::FindObject()
+ // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers().
+ let unique_timestamps = deterministic && is_darwin(kind);
+ let mut filename_count = HashMap::new();
+ if unique_timestamps {
+ for m in new_members {
+ *filename_count.entry(&*m.member_name).or_insert(0) += 1;
+ }
+ for (_name, count) in filename_count.iter_mut() {
+ if *count > 1 {
+ *count = 1;
+ }
+ }
+ }
+
+ // The big archive format needs to know the offset of the previous member
+ // header.
+ let mut prev_offset = 0;
+ for m in new_members {
+ let mut header = Vec::new();
+
+ let data: &[u8] = if thin { &[][..] } else { (*m.buf).as_ref() };
+
+ // ld64 expects the members to be 8-byte aligned for 64-bit content and at
+ // least 4-byte aligned for 32-bit content. Opt for the larger encoding
+ // uniformly. This matches the behaviour with cctools and ensures that ld64
+ // is happy with archives that we generate.
+ let member_padding = if is_darwin(kind) {
+ offset_to_alignment(u64::try_from(data.len()).unwrap(), 8)
+ } else {
+ 0
+ };
+ let tail_padding =
+ offset_to_alignment(u64::try_from(data.len()).unwrap() + member_padding, 2);
+ let padding = &PADDING_DATA[..usize::try_from(member_padding + tail_padding).unwrap()];
+
+ let mtime = if unique_timestamps {
+ // Increment timestamp for each file of a given name.
+ *filename_count.get_mut(&*m.member_name).unwrap() += 1;
+ filename_count[&*m.member_name] - 1
+ } else {
+ m.mtime
+ };
+
+ let size = u64::try_from(data.len()).unwrap() + member_padding;
+ if size > MAX_MEMBER_SIZE {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("Archive member {} is too big", m.member_name),
+ ));
+ }
+
+ if is_aix_big_archive(kind) {
+ let next_offset = pos
+ + u64::try_from(std::mem::size_of::<big_archive::BigArMemHdrType>()).unwrap()
+ + align_to(u64::try_from(m.member_name.len()).unwrap(), 2)
+ + align_to(size, 2);
+ print_big_archive_member_header(
+ &mut header,
+ &m.member_name,
+ mtime,
+ m.uid,
+ m.gid,
+ m.perms,
+ size,
+ prev_offset,
+ next_offset,
+ )?;
+ prev_offset = pos;
+ } else {
+ print_member_header(
+ &mut header,
+ pos,
+ string_table,
+ &mut member_names,
+ kind,
+ thin,
+ m,
+ mtime,
+ size,
+ )?;
+ }
+
+ let symbols = if need_symbols {
+ write_symbols(data, m.get_symbols, sym_names, &mut has_object)?
+ } else {
+ vec![]
+ };
+
+ pos += u64::try_from(header.len() + data.len() + padding.len()).unwrap();
+ ret.push(MemberData {
+ symbols,
+ header,
+ data,
+ padding,
+ })
+ }
+
+ // If there are no symbols, emit an empty symbol table, to satisfy Solaris
+ // tools, older versions of which expect a symbol table in a non-empty
+ // archive, regardless of whether there are any symbols in it.
+ if has_object && sym_names.stream_position()? == 0 {
+ write!(sym_names, "\0\0\0")?;
+ }
+
+ Ok(ret)
+}
+
+pub fn write_archive_to_stream<W: Write + Seek>(
+ w: &mut W,
+ new_members: &[NewArchiveMember<'_>],
+ write_symtab: bool,
+ mut kind: ArchiveKind,
+ deterministic: bool,
+ thin: bool,
+) -> io::Result<()> {
+ assert!(
+ !thin || !is_bsd_like(kind),
+ "Only the gnu format has a thin mode"
+ );
+
+ let mut sym_names = Cursor::new(Vec::new());
+ let mut string_table = Cursor::new(Vec::new());
+
+ let mut data = compute_member_data(
+ &mut string_table,
+ &mut sym_names,
+ kind,
+ thin,
+ deterministic,
+ write_symtab,
+ new_members,
+ )?;
+
+ let sym_names = sym_names.into_inner();
+
+ let string_table = string_table.into_inner();
+ if !string_table.is_empty() && !is_aix_big_archive(kind) {
+ data.insert(0, compute_string_table(&string_table));
+ }
+
+ // We would like to detect if we need to switch to a 64-bit symbol table.
+ let mut last_member_end_offset = if is_aix_big_archive(kind) {
+ u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
+ } else {
+ 8
+ };
+ let mut last_member_header_offset = last_member_end_offset;
+ let mut num_syms = 0;
+ for m in &data {
+ // Record the start of the member's offset
+ last_member_header_offset = last_member_end_offset;
+ // Account for the size of each part associated with the member.
+ last_member_end_offset +=
+ u64::try_from(m.header.len() + m.data.len() + m.padding.len()).unwrap();
+ num_syms += u64::try_from(m.symbols.len()).unwrap();
+ }
+
+ // The symbol table is put at the end of the big archive file. The symbol
+ // table is at the start of the archive file for other archive formats.
+ if write_symtab && !is_aix_big_archive(kind) {
+ // We assume 32-bit offsets to see if 32-bit symbols are possible or not.
+ let (symtab_size, _pad) = compute_symbol_table_size_and_pad(kind, num_syms, 4, &sym_names);
+ last_member_header_offset += {
+ // FIXME avoid allocating memory here
+ let mut tmp = Cursor::new(vec![]);
+ write_symbol_table_header(&mut tmp, kind, deterministic, symtab_size, 0).unwrap();
+ u64::try_from(tmp.into_inner().len()).unwrap()
+ } + symtab_size;
+
+ // The SYM64 format is used when an archive's member offsets are larger than
+ // 32-bits can hold. The need for this shift in format is detected by
+ // writeArchive. To test this we need to generate a file with a member that
+ // has an offset larger than 32-bits but this demands a very slow test. To
+ // speed the test up we use this environment variable to pretend like the
+ // cutoff happens before 32-bits and instead happens at some much smaller
+ // value.
+ // FIXME allow lowering the threshold for tests
+ const SYM64_THRESHOLD: u64 = 1 << 32;
+
+ // If LastMemberHeaderOffset isn't going to fit in a 32-bit varible we need
+ // to switch to 64-bit. Note that the file can be larger than 4GB as long as
+ // the last member starts before the 4GB offset.
+ if last_member_header_offset >= SYM64_THRESHOLD {
+ if kind == ArchiveKind::Darwin {
+ kind = ArchiveKind::Darwin64;
+ } else {
+ kind = ArchiveKind::Gnu64;
+ }
+ }
+ }
+
+ if thin {
+ write!(w, "!<thin>\n")?;
+ } else if is_aix_big_archive(kind) {
+ write!(w, "<bigaf>\n")?;
+ } else {
+ write!(w, "!<arch>\n")?;
+ }
+
+ if !is_aix_big_archive(kind) {
+ if write_symtab {
+ write_symbol_table(w, kind, deterministic, &data, &sym_names, 0)?;
+ }
+
+ for m in data {
+ w.write_all(&m.header)?;
+ w.write_all(m.data)?;
+ w.write_all(m.padding)?;
+ }
+ } else {
+ // For the big archive (AIX) format, compute a table of member names and
+ // offsets, used in the member table.
+ let mut member_table_name_str_tbl_size = 0;
+ let mut member_offsets = vec![];
+ let mut member_names = vec![];
+
+ // Loop across object to find offset and names.
+ let mut member_end_offset =
+ u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap();
+ for i in 0..new_members.len() {
+ let member = &new_members[i];
+ member_table_name_str_tbl_size += member.member_name.len() + 1;
+ member_offsets.push(member_end_offset);
+ member_names.push(&member.member_name);
+ // File member name ended with "`\n". The length is included in
+ // BigArMemHdrType.
+ member_end_offset += u64::try_from(std::mem::size_of::<big_archive::BigArMemHdrType>())
+ .unwrap()
+ + align_to(u64::try_from(data[i].data.len()).unwrap(), 2)
+ + align_to(u64::try_from(member.member_name.len()).unwrap(), 2);
+ }
+
+ // AIX member table size.
+ let member_table_size =
+ u64::try_from(20 + 20 * member_offsets.len() + member_table_name_str_tbl_size).unwrap();
+
+ let global_symbol_offset = if write_symtab && num_syms > 0 {
+ last_member_end_offset
+ + align_to(
+ u64::try_from(std::mem::size_of::<big_archive::BigArMemHdrType>()).unwrap()
+ + member_table_size,
+ 2,
+ )
+ } else {
+ 0
+ };
+
+ // Fixed Sized Header.
+ // Offset to member table
+ write!(
+ w,
+ "{:<20}",
+ if !new_members.is_empty() {
+ last_member_end_offset
+ } else {
+ 0
+ }
+ )?;
+ // If there are no file members in the archive, there will be no global
+ // symbol table.
+ write!(
+ w,
+ "{:<20}",
+ if !new_members.is_empty() {
+ global_symbol_offset
+ } else {
+ 0
+ }
+ )?;
+ // Offset to 64 bits global symbol table - Not supported yet
+ write!(w, "{:<20}", 0)?;
+ // Offset to first archive member
+ write!(
+ w,
+ "{:<20}",
+ if !new_members.is_empty() {
+ u64::try_from(std::mem::size_of::<big_archive::FixLenHdr>()).unwrap()
+ } else {
+ 0
+ }
+ )?;
+ // Offset to last archive member
+ write!(
+ w,
+ "{:<20}",
+ if !new_members.is_empty() {
+ last_member_header_offset
+ } else {
+ 0
+ }
+ )?;
+ // Offset to first member of free list - Not supported yet
+ write!(w, "{:<20}", 0)?;
+
+ for m in &data {
+ w.write_all(&m.header)?;
+ w.write_all(m.data)?;
+ if m.data.len() % 2 != 0 {
+ w.write_all(&[0])?;
+ }
+ }
+
+ if !new_members.is_empty() {
+ // Member table.
+ print_big_archive_member_header(
+ w,
+ "",
+ 0,
+ 0,
+ 0,
+ 0,
+ member_table_size,
+ last_member_header_offset,
+ global_symbol_offset,
+ )?;
+ write!(w, "{:<20}", member_offsets.len())?; // Number of members
+ for member_offset in member_offsets {
+ write!(w, "{:<20}", member_offset)?;
+ }
+ for member_name in member_names {
+ w.write_all(member_name.as_bytes())?;
+ w.write_all(&[0])?;
+ }
+
+ if member_table_name_str_tbl_size % 2 != 0 {
+ // Name table must be tail padded to an even number of
+ // bytes.
+ w.write_all(&[0])?;
+ }
+
+ if write_symtab && num_syms > 0 {
+ write_symbol_table(
+ w,
+ kind,
+ deterministic,
+ &data,
+ &sym_names,
+ last_member_end_offset,
+ )?;
+ }
+ }
+ }
+
+ w.flush()
+}
diff --git a/vendor/ar_archive_writer/src/lib.rs b/vendor/ar_archive_writer/src/lib.rs
new file mode 100644
index 000000000..ab2db2f88
--- /dev/null
+++ b/vendor/ar_archive_writer/src/lib.rs
@@ -0,0 +1,6 @@
+mod alignment;
+mod archive;
+mod archive_writer;
+
+pub use archive::ArchiveKind;
+pub use archive_writer::{get_native_object_symbols, write_archive_to_stream, NewArchiveMember};