diff options
Diffstat (limited to 'vendor/ar_archive_writer')
-rw-r--r-- | vendor/ar_archive_writer/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/ar_archive_writer/Cargo.toml | 32 | ||||
-rw-r--r-- | vendor/ar_archive_writer/LICENSE.txt | 219 | ||||
-rw-r--r-- | vendor/ar_archive_writer/Readme.md | 14 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/Alignment.h | 314 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/Archive.h | 426 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/ArchiveWriter.cpp | 872 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/ArchiveWriter.h | 57 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/alignment.rs | 23 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/archive.rs | 71 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/archive_writer.rs | 831 | ||||
-rw-r--r-- | vendor/ar_archive_writer/src/lib.rs | 6 |
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}; |