diff options
author | orivej <orivej@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/libs/llvm12/tools | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) | |
download | ydb-718c552901d703c502ccbefdfc3c9028d608b947.tar.gz |
Restoring authorship annotation for <orivej@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/llvm12/tools')
93 files changed, 21958 insertions, 21958 deletions
diff --git a/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.cpp b/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.cpp index f83521346c..6a90549be5 100644 --- a/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.cpp @@ -1,101 +1,101 @@ -//===-- BinaryHolder.cpp --------------------------------------------------===// -// -// 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 program is a utility that aims to be a dropin replacement for -// Darwin's dsymutil. -// -//===----------------------------------------------------------------------===// - -#include "BinaryHolder.h" -#include "llvm/Object/MachO.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { -namespace dsymutil { - -static std::pair<StringRef, StringRef> -getArchiveAndObjectName(StringRef Filename) { - StringRef Archive = Filename.substr(0, Filename.rfind('(')); - StringRef Object = Filename.substr(Archive.size() + 1).drop_back(); - return {Archive, Object}; -} - -static bool isArchive(StringRef Filename) { return Filename.endswith(")"); } - -static std::vector<MemoryBufferRef> -getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem, - object::MachOUniversalBinary &Fat) { - std::vector<MemoryBufferRef> Buffers; - StringRef FatData = Fat.getData(); - for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End; - ++It) { - StringRef ObjData = FatData.substr(It->getOffset(), It->getSize()); - Buffers.emplace_back(ObjData, Filename); - } - return Buffers; -} - -Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, - StringRef Filename, - TimestampTy Timestamp, bool Verbose) { - StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; - - // Try to load archive and force it to be memory mapped. - auto ErrOrBuff = (ArchiveFilename == "-") - ? MemoryBuffer::getSTDIN() - : VFS->getBufferForFile(ArchiveFilename, -1, false); - if (auto Err = ErrOrBuff.getError()) - return errorCodeToError(Err); - - MemBuffer = std::move(*ErrOrBuff); - - if (Verbose) - WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n"; - - // Load one or more archive buffers, depending on whether we're dealing with - // a fat binary. - std::vector<MemoryBufferRef> ArchiveBuffers; - - auto ErrOrFat = - object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); - if (!ErrOrFat) { - consumeError(ErrOrFat.takeError()); - ArchiveBuffers.push_back(MemBuffer->getMemBufferRef()); - } else { - FatBinary = std::move(*ErrOrFat); - FatBinaryName = std::string(ArchiveFilename); - ArchiveBuffers = - getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); - } - - // Finally, try to load the archives. - Archives.reserve(ArchiveBuffers.size()); - for (auto MemRef : ArchiveBuffers) { - auto ErrOrArchive = object::Archive::create(MemRef); - if (!ErrOrArchive) - return ErrOrArchive.takeError(); - Archives.push_back(std::move(*ErrOrArchive)); - } - - return Error::success(); -} - -Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, +//===-- BinaryHolder.cpp --------------------------------------------------===// +// +// 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 program is a utility that aims to be a dropin replacement for +// Darwin's dsymutil. +// +//===----------------------------------------------------------------------===// + +#include "BinaryHolder.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dsymutil { + +static std::pair<StringRef, StringRef> +getArchiveAndObjectName(StringRef Filename) { + StringRef Archive = Filename.substr(0, Filename.rfind('(')); + StringRef Object = Filename.substr(Archive.size() + 1).drop_back(); + return {Archive, Object}; +} + +static bool isArchive(StringRef Filename) { return Filename.endswith(")"); } + +static std::vector<MemoryBufferRef> +getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem, + object::MachOUniversalBinary &Fat) { + std::vector<MemoryBufferRef> Buffers; + StringRef FatData = Fat.getData(); + for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End; + ++It) { + StringRef ObjData = FatData.substr(It->getOffset(), It->getSize()); + Buffers.emplace_back(ObjData, Filename); + } + return Buffers; +} + +Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, + StringRef Filename, + TimestampTy Timestamp, bool Verbose) { + StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; + + // Try to load archive and force it to be memory mapped. + auto ErrOrBuff = (ArchiveFilename == "-") + ? MemoryBuffer::getSTDIN() + : VFS->getBufferForFile(ArchiveFilename, -1, false); + if (auto Err = ErrOrBuff.getError()) + return errorCodeToError(Err); + + MemBuffer = std::move(*ErrOrBuff); + + if (Verbose) + WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n"; + + // Load one or more archive buffers, depending on whether we're dealing with + // a fat binary. + std::vector<MemoryBufferRef> ArchiveBuffers; + + auto ErrOrFat = + object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); + if (!ErrOrFat) { + consumeError(ErrOrFat.takeError()); + ArchiveBuffers.push_back(MemBuffer->getMemBufferRef()); + } else { + FatBinary = std::move(*ErrOrFat); + FatBinaryName = std::string(ArchiveFilename); + ArchiveBuffers = + getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); + } + + // Finally, try to load the archives. + Archives.reserve(ArchiveBuffers.size()); + for (auto MemRef : ArchiveBuffers) { + auto ErrOrArchive = object::Archive::create(MemRef); + if (!ErrOrArchive) + return ErrOrArchive.takeError(); + Archives.push_back(std::move(*ErrOrArchive)); + } + + return Error::success(); +} + +Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename, TimestampTy Timestamp, bool Verbose) { - // Try to load regular binary and force it to be memory mapped. - auto ErrOrBuff = (Filename == "-") - ? MemoryBuffer::getSTDIN() - : VFS->getBufferForFile(Filename, -1, false); - if (auto Err = ErrOrBuff.getError()) - return errorCodeToError(Err); - + // Try to load regular binary and force it to be memory mapped. + auto ErrOrBuff = (Filename == "-") + ? MemoryBuffer::getSTDIN() + : VFS->getBufferForFile(Filename, -1, false); + if (auto Err = ErrOrBuff.getError()) + return errorCodeToError(Err); + if (Filename != "-" && Timestamp != sys::TimePoint<>()) { llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename); if (!Stat) @@ -108,178 +108,178 @@ Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, << ") and debug map (" << Timestamp << ")\n"; } - MemBuffer = std::move(*ErrOrBuff); - - if (Verbose) - WithColor::note() << "loaded object.\n"; - - // Load one or more object buffers, depending on whether we're dealing with a - // fat binary. - std::vector<MemoryBufferRef> ObjectBuffers; - - auto ErrOrFat = - object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); - if (!ErrOrFat) { - consumeError(ErrOrFat.takeError()); - ObjectBuffers.push_back(MemBuffer->getMemBufferRef()); - } else { - FatBinary = std::move(*ErrOrFat); - FatBinaryName = std::string(Filename); - ObjectBuffers = - getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); - } - - Objects.reserve(ObjectBuffers.size()); - for (auto MemRef : ObjectBuffers) { - auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef); - if (!ErrOrObjectFile) - return ErrOrObjectFile.takeError(); - Objects.push_back(std::move(*ErrOrObjectFile)); - } - - return Error::success(); -} - -std::vector<const object::ObjectFile *> -BinaryHolder::ObjectEntry::getObjects() const { - std::vector<const object::ObjectFile *> Result; - Result.reserve(Objects.size()); - for (auto &Object : Objects) { - Result.push_back(Object.get()); - } - return Result; -} -Expected<const object::ObjectFile &> -BinaryHolder::ObjectEntry::getObject(const Triple &T) const { - for (const auto &Obj : Objects) { - if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { - if (MachO->getArchTriple().str() == T.str()) - return *MachO; - } else if (Obj->getArch() == T.getArch()) - return *Obj; - } - return errorCodeToError(object::object_error::arch_not_found); -} - -Expected<const BinaryHolder::ObjectEntry &> -BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename, - TimestampTy Timestamp, - bool Verbose) { - StringRef ArchiveFilename; - StringRef ObjectFilename; - std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename); - - // Try the cache first. - KeyTy Key = {ObjectFilename, Timestamp}; - - { - std::lock_guard<std::mutex> Lock(MemberCacheMutex); - if (MemberCache.count(Key)) - return MemberCache[Key]; - } - - // Create a new ObjectEntry, but don't add it to the cache yet. Loading of - // the archive members might fail and we don't want to lock the whole archive - // during this operation. - ObjectEntry OE; - - for (const auto &Archive : Archives) { - Error Err = Error::success(); - for (auto Child : Archive->children(Err)) { - if (auto NameOrErr = Child.getName()) { - if (*NameOrErr == ObjectFilename) { - auto ModTimeOrErr = Child.getLastModified(); - if (!ModTimeOrErr) - return ModTimeOrErr.takeError(); - - if (Timestamp != sys::TimePoint<>() && + MemBuffer = std::move(*ErrOrBuff); + + if (Verbose) + WithColor::note() << "loaded object.\n"; + + // Load one or more object buffers, depending on whether we're dealing with a + // fat binary. + std::vector<MemoryBufferRef> ObjectBuffers; + + auto ErrOrFat = + object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); + if (!ErrOrFat) { + consumeError(ErrOrFat.takeError()); + ObjectBuffers.push_back(MemBuffer->getMemBufferRef()); + } else { + FatBinary = std::move(*ErrOrFat); + FatBinaryName = std::string(Filename); + ObjectBuffers = + getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); + } + + Objects.reserve(ObjectBuffers.size()); + for (auto MemRef : ObjectBuffers) { + auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef); + if (!ErrOrObjectFile) + return ErrOrObjectFile.takeError(); + Objects.push_back(std::move(*ErrOrObjectFile)); + } + + return Error::success(); +} + +std::vector<const object::ObjectFile *> +BinaryHolder::ObjectEntry::getObjects() const { + std::vector<const object::ObjectFile *> Result; + Result.reserve(Objects.size()); + for (auto &Object : Objects) { + Result.push_back(Object.get()); + } + return Result; +} +Expected<const object::ObjectFile &> +BinaryHolder::ObjectEntry::getObject(const Triple &T) const { + for (const auto &Obj : Objects) { + if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { + if (MachO->getArchTriple().str() == T.str()) + return *MachO; + } else if (Obj->getArch() == T.getArch()) + return *Obj; + } + return errorCodeToError(object::object_error::arch_not_found); +} + +Expected<const BinaryHolder::ObjectEntry &> +BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename, + TimestampTy Timestamp, + bool Verbose) { + StringRef ArchiveFilename; + StringRef ObjectFilename; + std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename); + + // Try the cache first. + KeyTy Key = {ObjectFilename, Timestamp}; + + { + std::lock_guard<std::mutex> Lock(MemberCacheMutex); + if (MemberCache.count(Key)) + return MemberCache[Key]; + } + + // Create a new ObjectEntry, but don't add it to the cache yet. Loading of + // the archive members might fail and we don't want to lock the whole archive + // during this operation. + ObjectEntry OE; + + for (const auto &Archive : Archives) { + Error Err = Error::success(); + for (auto Child : Archive->children(Err)) { + if (auto NameOrErr = Child.getName()) { + if (*NameOrErr == ObjectFilename) { + auto ModTimeOrErr = Child.getLastModified(); + if (!ModTimeOrErr) + return ModTimeOrErr.takeError(); + + if (Timestamp != sys::TimePoint<>() && Timestamp != std::chrono::time_point_cast<std::chrono::seconds>( ModTimeOrErr.get())) { - if (Verbose) + if (Verbose) WithColor::warning() << *NameOrErr << ": timestamp mismatch between archive member (" << ModTimeOrErr.get() << ") and debug map (" << Timestamp << ")\n"; - continue; - } - - if (Verbose) - WithColor::note() << "found member in archive.\n"; - - auto ErrOrMem = Child.getMemoryBufferRef(); - if (!ErrOrMem) - return ErrOrMem.takeError(); - - auto ErrOrObjectFile = - object::ObjectFile::createObjectFile(*ErrOrMem); - if (!ErrOrObjectFile) - return ErrOrObjectFile.takeError(); - - OE.Objects.push_back(std::move(*ErrOrObjectFile)); - } - } - } - if (Err) - return std::move(Err); - } - - if (OE.Objects.empty()) - return errorCodeToError(errc::no_such_file_or_directory); - - std::lock_guard<std::mutex> Lock(MemberCacheMutex); - MemberCache.try_emplace(Key, std::move(OE)); - return MemberCache[Key]; -} - -Expected<const BinaryHolder::ObjectEntry &> -BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) { - if (Verbose) - WithColor::note() << "trying to open '" << Filename << "'\n"; - - // If this is an archive, we might have either the object or the archive - // cached. In this case we can load it without accessing the file system. - if (isArchive(Filename)) { - StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; - std::lock_guard<std::mutex> Lock(ArchiveCacheMutex); - if (ArchiveCache.count(ArchiveFilename)) { - return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, - Verbose); - } else { - ArchiveEntry &AE = ArchiveCache[ArchiveFilename]; - auto Err = AE.load(VFS, Filename, Timestamp, Verbose); - if (Err) { - ArchiveCache.erase(ArchiveFilename); - // Don't return the error here: maybe the file wasn't an archive. - llvm::consumeError(std::move(Err)); - } else { - return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, - Verbose); - } - } - } - - // If this is an object, we might have it cached. If not we'll have to load - // it from the file system and cache it now. - std::lock_guard<std::mutex> Lock(ObjectCacheMutex); - if (!ObjectCache.count(Filename)) { - ObjectEntry &OE = ObjectCache[Filename]; + continue; + } + + if (Verbose) + WithColor::note() << "found member in archive.\n"; + + auto ErrOrMem = Child.getMemoryBufferRef(); + if (!ErrOrMem) + return ErrOrMem.takeError(); + + auto ErrOrObjectFile = + object::ObjectFile::createObjectFile(*ErrOrMem); + if (!ErrOrObjectFile) + return ErrOrObjectFile.takeError(); + + OE.Objects.push_back(std::move(*ErrOrObjectFile)); + } + } + } + if (Err) + return std::move(Err); + } + + if (OE.Objects.empty()) + return errorCodeToError(errc::no_such_file_or_directory); + + std::lock_guard<std::mutex> Lock(MemberCacheMutex); + MemberCache.try_emplace(Key, std::move(OE)); + return MemberCache[Key]; +} + +Expected<const BinaryHolder::ObjectEntry &> +BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) { + if (Verbose) + WithColor::note() << "trying to open '" << Filename << "'\n"; + + // If this is an archive, we might have either the object or the archive + // cached. In this case we can load it without accessing the file system. + if (isArchive(Filename)) { + StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; + std::lock_guard<std::mutex> Lock(ArchiveCacheMutex); + if (ArchiveCache.count(ArchiveFilename)) { + return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, + Verbose); + } else { + ArchiveEntry &AE = ArchiveCache[ArchiveFilename]; + auto Err = AE.load(VFS, Filename, Timestamp, Verbose); + if (Err) { + ArchiveCache.erase(ArchiveFilename); + // Don't return the error here: maybe the file wasn't an archive. + llvm::consumeError(std::move(Err)); + } else { + return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, + Verbose); + } + } + } + + // If this is an object, we might have it cached. If not we'll have to load + // it from the file system and cache it now. + std::lock_guard<std::mutex> Lock(ObjectCacheMutex); + if (!ObjectCache.count(Filename)) { + ObjectEntry &OE = ObjectCache[Filename]; auto Err = OE.load(VFS, Filename, Timestamp, Verbose); - if (Err) { - ObjectCache.erase(Filename); - return std::move(Err); - } - } - - return ObjectCache[Filename]; -} - -void BinaryHolder::clear() { - std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex); - std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex); - ArchiveCache.clear(); - ObjectCache.clear(); -} - -} // namespace dsymutil -} // namespace llvm + if (Err) { + ObjectCache.erase(Filename); + return std::move(Err); + } + } + + return ObjectCache[Filename]; +} + +void BinaryHolder::clear() { + std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex); + std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex); + ArchiveCache.clear(); + ObjectCache.clear(); +} + +} // namespace dsymutil +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.h b/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.h index 5e81fe4b93..bf4bcb63b6 100644 --- a/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.h +++ b/contrib/libs/llvm12/tools/dsymutil/BinaryHolder.h @@ -1,172 +1,172 @@ -//===-- BinaryHolder.h - Utility class for accessing binaries -------------===// -// -// 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 program is a utility that aims to be a dropin replacement for -// Darwin's dsymutil. -// -//===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H -#define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/Triple.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/Error.h" -#include "llvm/Object/MachOUniversal.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Support/Chrono.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/VirtualFileSystem.h" - -#include <mutex> - -namespace llvm { -namespace dsymutil { - -/// The BinaryHolder class is responsible for creating and owning -/// ObjectFiles and their underlying MemoryBuffers. It differs from a simple -/// OwningBinary in that it handles accessing and caching of archives and its -/// members. -class BinaryHolder { -public: - using TimestampTy = sys::TimePoint<std::chrono::seconds>; - - BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS, bool Verbose = false) - : VFS(VFS), Verbose(Verbose) {} - - // Forward declarations for friend declaration. - class ObjectEntry; - class ArchiveEntry; - - /// Base class shared by cached entries, representing objects and archives. - class EntryBase { - protected: - std::unique_ptr<MemoryBuffer> MemBuffer; - std::unique_ptr<object::MachOUniversalBinary> FatBinary; - std::string FatBinaryName; - }; - - /// Cached entry holding one or more (in case of a fat binary) object files. - class ObjectEntry : public EntryBase { - public: - /// Load the given object binary in memory. - Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename, +//===-- BinaryHolder.h - Utility class for accessing binaries -------------===// +// +// 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 program is a utility that aims to be a dropin replacement for +// Darwin's dsymutil. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H +#define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/Error.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include <mutex> + +namespace llvm { +namespace dsymutil { + +/// The BinaryHolder class is responsible for creating and owning +/// ObjectFiles and their underlying MemoryBuffers. It differs from a simple +/// OwningBinary in that it handles accessing and caching of archives and its +/// members. +class BinaryHolder { +public: + using TimestampTy = sys::TimePoint<std::chrono::seconds>; + + BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS, bool Verbose = false) + : VFS(VFS), Verbose(Verbose) {} + + // Forward declarations for friend declaration. + class ObjectEntry; + class ArchiveEntry; + + /// Base class shared by cached entries, representing objects and archives. + class EntryBase { + protected: + std::unique_ptr<MemoryBuffer> MemBuffer; + std::unique_ptr<object::MachOUniversalBinary> FatBinary; + std::string FatBinaryName; + }; + + /// Cached entry holding one or more (in case of a fat binary) object files. + class ObjectEntry : public EntryBase { + public: + /// Load the given object binary in memory. + Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename, TimestampTy Timestamp, bool Verbose = false); - - /// Access all owned ObjectFiles. - std::vector<const object::ObjectFile *> getObjects() const; - - /// Access to a derived version of all the currently owned ObjectFiles. The - /// conversion might be invalid, in which case an Error is returned. - template <typename ObjectFileType> - Expected<std::vector<const ObjectFileType *>> getObjectsAs() const { - std::vector<const ObjectFileType *> Result; - Result.reserve(Objects.size()); - for (auto &Object : Objects) { - const auto *Derived = dyn_cast<ObjectFileType>(Object.get()); - if (!Derived) - return errorCodeToError(object::object_error::invalid_file_type); - Result.push_back(Derived); - } - return Result; - } - - /// Access the owned ObjectFile with architecture \p T. - Expected<const object::ObjectFile &> getObject(const Triple &T) const; - - /// Access to a derived version of the currently owned ObjectFile with - /// architecture \p T. The conversion must be known to be valid. - template <typename ObjectFileType> - Expected<const ObjectFileType &> getObjectAs(const Triple &T) const { - auto Object = getObject(T); - if (!Object) - return Object.takeError(); - return cast<ObjectFileType>(*Object); - } - - private: - std::vector<std::unique_ptr<object::ObjectFile>> Objects; - friend ArchiveEntry; - }; - - /// Cached entry holding one or more (in the of a fat binary) archive files. - class ArchiveEntry : public EntryBase { - public: - struct KeyTy { - std::string Filename; - TimestampTy Timestamp; - - KeyTy() : Filename(), Timestamp() {} - KeyTy(StringRef Filename, TimestampTy Timestamp) - : Filename(Filename.str()), Timestamp(Timestamp) {} - }; - - /// Load the given object binary in memory. - Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename, - TimestampTy Timestamp, bool Verbose = false); - - Expected<const ObjectEntry &> getObjectEntry(StringRef Filename, - TimestampTy Timestamp, - bool Verbose = false); - - private: - std::vector<std::unique_ptr<object::Archive>> Archives; - DenseMap<KeyTy, ObjectEntry> MemberCache; - std::mutex MemberCacheMutex; - }; - - Expected<const ObjectEntry &> - getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy()); - - void clear(); - -private: - /// Cache of static archives. Objects that are part of a static archive are - /// stored under this object, rather than in the map below. - StringMap<ArchiveEntry> ArchiveCache; - std::mutex ArchiveCacheMutex; - - /// Object entries for objects that are not in a static archive. - StringMap<ObjectEntry> ObjectCache; - std::mutex ObjectCacheMutex; - - /// Virtual File System instance. - IntrusiveRefCntPtr<vfs::FileSystem> VFS; - - bool Verbose; -}; - -} // namespace dsymutil - -template <> struct DenseMapInfo<dsymutil::BinaryHolder::ArchiveEntry::KeyTy> { - - static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getEmptyKey() { - return dsymutil::BinaryHolder::ArchiveEntry::KeyTy(); - } - - static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getTombstoneKey() { - return dsymutil::BinaryHolder::ArchiveEntry::KeyTy("/", {}); - } - - static unsigned - getHashValue(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &K) { - return hash_combine(DenseMapInfo<StringRef>::getHashValue(K.Filename), - DenseMapInfo<unsigned>::getHashValue( - K.Timestamp.time_since_epoch().count())); - } - - static bool isEqual(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &LHS, - const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &RHS) { - return LHS.Filename == RHS.Filename && LHS.Timestamp == RHS.Timestamp; - } -}; - -} // namespace llvm -#endif + + /// Access all owned ObjectFiles. + std::vector<const object::ObjectFile *> getObjects() const; + + /// Access to a derived version of all the currently owned ObjectFiles. The + /// conversion might be invalid, in which case an Error is returned. + template <typename ObjectFileType> + Expected<std::vector<const ObjectFileType *>> getObjectsAs() const { + std::vector<const ObjectFileType *> Result; + Result.reserve(Objects.size()); + for (auto &Object : Objects) { + const auto *Derived = dyn_cast<ObjectFileType>(Object.get()); + if (!Derived) + return errorCodeToError(object::object_error::invalid_file_type); + Result.push_back(Derived); + } + return Result; + } + + /// Access the owned ObjectFile with architecture \p T. + Expected<const object::ObjectFile &> getObject(const Triple &T) const; + + /// Access to a derived version of the currently owned ObjectFile with + /// architecture \p T. The conversion must be known to be valid. + template <typename ObjectFileType> + Expected<const ObjectFileType &> getObjectAs(const Triple &T) const { + auto Object = getObject(T); + if (!Object) + return Object.takeError(); + return cast<ObjectFileType>(*Object); + } + + private: + std::vector<std::unique_ptr<object::ObjectFile>> Objects; + friend ArchiveEntry; + }; + + /// Cached entry holding one or more (in the of a fat binary) archive files. + class ArchiveEntry : public EntryBase { + public: + struct KeyTy { + std::string Filename; + TimestampTy Timestamp; + + KeyTy() : Filename(), Timestamp() {} + KeyTy(StringRef Filename, TimestampTy Timestamp) + : Filename(Filename.str()), Timestamp(Timestamp) {} + }; + + /// Load the given object binary in memory. + Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename, + TimestampTy Timestamp, bool Verbose = false); + + Expected<const ObjectEntry &> getObjectEntry(StringRef Filename, + TimestampTy Timestamp, + bool Verbose = false); + + private: + std::vector<std::unique_ptr<object::Archive>> Archives; + DenseMap<KeyTy, ObjectEntry> MemberCache; + std::mutex MemberCacheMutex; + }; + + Expected<const ObjectEntry &> + getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy()); + + void clear(); + +private: + /// Cache of static archives. Objects that are part of a static archive are + /// stored under this object, rather than in the map below. + StringMap<ArchiveEntry> ArchiveCache; + std::mutex ArchiveCacheMutex; + + /// Object entries for objects that are not in a static archive. + StringMap<ObjectEntry> ObjectCache; + std::mutex ObjectCacheMutex; + + /// Virtual File System instance. + IntrusiveRefCntPtr<vfs::FileSystem> VFS; + + bool Verbose; +}; + +} // namespace dsymutil + +template <> struct DenseMapInfo<dsymutil::BinaryHolder::ArchiveEntry::KeyTy> { + + static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getEmptyKey() { + return dsymutil::BinaryHolder::ArchiveEntry::KeyTy(); + } + + static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getTombstoneKey() { + return dsymutil::BinaryHolder::ArchiveEntry::KeyTy("/", {}); + } + + static unsigned + getHashValue(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &K) { + return hash_combine(DenseMapInfo<StringRef>::getHashValue(K.Filename), + DenseMapInfo<unsigned>::getHashValue( + K.Timestamp.time_since_epoch().count())); + } + + static bool isEqual(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &LHS, + const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &RHS) { + return LHS.Filename == RHS.Filename && LHS.Timestamp == RHS.Timestamp; + } +}; + +} // namespace llvm +#endif diff --git a/contrib/libs/llvm12/tools/dsymutil/CFBundle.cpp b/contrib/libs/llvm12/tools/dsymutil/CFBundle.cpp index 0625afb18a..406c35cc59 100644 --- a/contrib/libs/llvm12/tools/dsymutil/CFBundle.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/CFBundle.cpp @@ -1,185 +1,185 @@ -//===- tools/dsymutil/CFBundle.cpp - CFBundle helper ------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "CFBundle.h" - -#ifdef __APPLE__ -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" -#include <CoreFoundation/CoreFoundation.h> -#include <assert.h> -#include <glob.h> -#include <memory> -#endif - -namespace llvm { -namespace dsymutil { - -#ifdef __APPLE__ -/// Deleter that calls CFRelease rather than deleting the pointer. -template <typename T> struct CFDeleter { - void operator()(T *P) { - if (P) - ::CFRelease(P); - } -}; - -/// This helper owns any CoreFoundation pointer and will call CFRelease() on -/// any valid pointer it owns unless that pointer is explicitly released using -/// the release() member function. -template <typename T> -using CFReleaser = std::unique_ptr<std::remove_pointer_t<T>, - CFDeleter<std::remove_pointer_t<T>>>; - -/// RAII wrapper around CFBundleRef. -class CFString : public CFReleaser<CFStringRef> { -public: - CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {} - - const char *UTF8(std::string &Str) const { - return CFString::UTF8(get(), Str); - } - - CFIndex GetLength() const { - if (CFStringRef Str = get()) - return CFStringGetLength(Str); - return 0; - } - - static const char *UTF8(CFStringRef CFStr, std::string &Str); -}; - -/// Static function that puts a copy of the UTF-8 contents of CFStringRef into -/// std::string and returns the C string pointer that is contained in the -/// std::string when successful, nullptr otherwise. -/// -/// This allows the std::string parameter to own the extracted string, and also -/// allows that string to be returned as a C string pointer that can be used. -const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) { - if (!CFStr) - return nullptr; - - const CFStringEncoding Encoding = kCFStringEncodingUTF8; - CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr); - MaxUTF8StrLength = - CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding); - if (MaxUTF8StrLength > 0) { - Str.resize(MaxUTF8StrLength); - if (!Str.empty() && - CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) { - Str.resize(strlen(Str.c_str())); - return Str.c_str(); - } - } - - return nullptr; -} - -/// RAII wrapper around CFBundleRef. -class CFBundle : public CFReleaser<CFBundleRef> { -public: - CFBundle(StringRef Path) : CFReleaser<CFBundleRef>() { SetFromPath(Path); } - - CFBundle(CFURLRef Url) - : CFReleaser<CFBundleRef>(Url ? ::CFBundleCreate(nullptr, Url) - : nullptr) {} - - /// Return the bundle identifier. - CFStringRef GetIdentifier() const { - if (CFBundleRef bundle = get()) - return ::CFBundleGetIdentifier(bundle); - return nullptr; - } - - /// Return value for key. - CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const { - if (CFBundleRef bundle = get()) - return ::CFBundleGetValueForInfoDictionaryKey(bundle, key); - return nullptr; - } - -private: - /// Helper to initialize this instance with a new bundle created from the - /// given path. This function will recursively remove components from the - /// path in its search for the nearest Info.plist. - void SetFromPath(StringRef Path); -}; - -void CFBundle::SetFromPath(StringRef Path) { - // Start from an empty/invalid CFBundle. - reset(); - - if (Path.empty() || !sys::fs::exists(Path)) - return; - - SmallString<256> RealPath; - sys::fs::real_path(Path, RealPath, /*expand_tilde*/ true); - - do { - // Create a CFURL from the current path and use it to create a CFBundle. - CFReleaser<CFURLRef> BundleURL(::CFURLCreateFromFileSystemRepresentation( - kCFAllocatorDefault, (const UInt8 *)RealPath.data(), RealPath.size(), - false)); - reset(::CFBundleCreate(kCFAllocatorDefault, BundleURL.get())); - - // If we have a valid bundle and find its identifier we are done. - if (get() != nullptr) { - if (GetIdentifier() != nullptr) - return; - reset(); - } - - // Remove the last component of the path and try again until there's - // nothing left but the root. - sys::path::remove_filename(RealPath); - } while (RealPath != sys::path::root_name(RealPath)); -} -#endif - -/// On Darwin, try and find the original executable's Info.plist to extract -/// information about the bundle. Return default values on other platforms. -CFBundleInfo getBundleInfo(StringRef ExePath) { - CFBundleInfo BundleInfo; - -#ifdef __APPLE__ - auto PrintError = [&](CFTypeID TypeID) { - CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID)); - std::string TypeIDStr; - errs() << "The Info.plist key \"CFBundleShortVersionString\" is" - << "a " << TypeIDCFStr.UTF8(TypeIDStr) - << ", but it should be a string in: " << ExePath << ".\n"; - }; - - CFBundle Bundle(ExePath); - if (CFStringRef BundleID = Bundle.GetIdentifier()) { - CFString::UTF8(BundleID, BundleInfo.IDStr); - if (CFTypeRef TypeRef = - Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) { - CFTypeID TypeID = ::CFGetTypeID(TypeRef); - if (TypeID == ::CFStringGetTypeID()) - CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr); - else - PrintError(TypeID); - } - if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey( - CFSTR("CFBundleShortVersionString"))) { - CFTypeID TypeID = ::CFGetTypeID(TypeRef); - if (TypeID == ::CFStringGetTypeID()) - CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr); - else - PrintError(TypeID); - } - } -#endif - - return BundleInfo; -} - -} // end namespace dsymutil -} // end namespace llvm +//===- tools/dsymutil/CFBundle.cpp - CFBundle helper ------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "CFBundle.h" + +#ifdef __APPLE__ +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <CoreFoundation/CoreFoundation.h> +#include <assert.h> +#include <glob.h> +#include <memory> +#endif + +namespace llvm { +namespace dsymutil { + +#ifdef __APPLE__ +/// Deleter that calls CFRelease rather than deleting the pointer. +template <typename T> struct CFDeleter { + void operator()(T *P) { + if (P) + ::CFRelease(P); + } +}; + +/// This helper owns any CoreFoundation pointer and will call CFRelease() on +/// any valid pointer it owns unless that pointer is explicitly released using +/// the release() member function. +template <typename T> +using CFReleaser = std::unique_ptr<std::remove_pointer_t<T>, + CFDeleter<std::remove_pointer_t<T>>>; + +/// RAII wrapper around CFBundleRef. +class CFString : public CFReleaser<CFStringRef> { +public: + CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {} + + const char *UTF8(std::string &Str) const { + return CFString::UTF8(get(), Str); + } + + CFIndex GetLength() const { + if (CFStringRef Str = get()) + return CFStringGetLength(Str); + return 0; + } + + static const char *UTF8(CFStringRef CFStr, std::string &Str); +}; + +/// Static function that puts a copy of the UTF-8 contents of CFStringRef into +/// std::string and returns the C string pointer that is contained in the +/// std::string when successful, nullptr otherwise. +/// +/// This allows the std::string parameter to own the extracted string, and also +/// allows that string to be returned as a C string pointer that can be used. +const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) { + if (!CFStr) + return nullptr; + + const CFStringEncoding Encoding = kCFStringEncodingUTF8; + CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr); + MaxUTF8StrLength = + CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding); + if (MaxUTF8StrLength > 0) { + Str.resize(MaxUTF8StrLength); + if (!Str.empty() && + CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) { + Str.resize(strlen(Str.c_str())); + return Str.c_str(); + } + } + + return nullptr; +} + +/// RAII wrapper around CFBundleRef. +class CFBundle : public CFReleaser<CFBundleRef> { +public: + CFBundle(StringRef Path) : CFReleaser<CFBundleRef>() { SetFromPath(Path); } + + CFBundle(CFURLRef Url) + : CFReleaser<CFBundleRef>(Url ? ::CFBundleCreate(nullptr, Url) + : nullptr) {} + + /// Return the bundle identifier. + CFStringRef GetIdentifier() const { + if (CFBundleRef bundle = get()) + return ::CFBundleGetIdentifier(bundle); + return nullptr; + } + + /// Return value for key. + CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const { + if (CFBundleRef bundle = get()) + return ::CFBundleGetValueForInfoDictionaryKey(bundle, key); + return nullptr; + } + +private: + /// Helper to initialize this instance with a new bundle created from the + /// given path. This function will recursively remove components from the + /// path in its search for the nearest Info.plist. + void SetFromPath(StringRef Path); +}; + +void CFBundle::SetFromPath(StringRef Path) { + // Start from an empty/invalid CFBundle. + reset(); + + if (Path.empty() || !sys::fs::exists(Path)) + return; + + SmallString<256> RealPath; + sys::fs::real_path(Path, RealPath, /*expand_tilde*/ true); + + do { + // Create a CFURL from the current path and use it to create a CFBundle. + CFReleaser<CFURLRef> BundleURL(::CFURLCreateFromFileSystemRepresentation( + kCFAllocatorDefault, (const UInt8 *)RealPath.data(), RealPath.size(), + false)); + reset(::CFBundleCreate(kCFAllocatorDefault, BundleURL.get())); + + // If we have a valid bundle and find its identifier we are done. + if (get() != nullptr) { + if (GetIdentifier() != nullptr) + return; + reset(); + } + + // Remove the last component of the path and try again until there's + // nothing left but the root. + sys::path::remove_filename(RealPath); + } while (RealPath != sys::path::root_name(RealPath)); +} +#endif + +/// On Darwin, try and find the original executable's Info.plist to extract +/// information about the bundle. Return default values on other platforms. +CFBundleInfo getBundleInfo(StringRef ExePath) { + CFBundleInfo BundleInfo; + +#ifdef __APPLE__ + auto PrintError = [&](CFTypeID TypeID) { + CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID)); + std::string TypeIDStr; + errs() << "The Info.plist key \"CFBundleShortVersionString\" is" + << "a " << TypeIDCFStr.UTF8(TypeIDStr) + << ", but it should be a string in: " << ExePath << ".\n"; + }; + + CFBundle Bundle(ExePath); + if (CFStringRef BundleID = Bundle.GetIdentifier()) { + CFString::UTF8(BundleID, BundleInfo.IDStr); + if (CFTypeRef TypeRef = + Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) { + CFTypeID TypeID = ::CFGetTypeID(TypeRef); + if (TypeID == ::CFStringGetTypeID()) + CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr); + else + PrintError(TypeID); + } + if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey( + CFSTR("CFBundleShortVersionString"))) { + CFTypeID TypeID = ::CFGetTypeID(TypeRef); + if (TypeID == ::CFStringGetTypeID()) + CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr); + else + PrintError(TypeID); + } + } +#endif + + return BundleInfo; +} + +} // end namespace dsymutil +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/CFBundle.h b/contrib/libs/llvm12/tools/dsymutil/CFBundle.h index 103db03516..12eb64ac0b 100644 --- a/contrib/libs/llvm12/tools/dsymutil/CFBundle.h +++ b/contrib/libs/llvm12/tools/dsymutil/CFBundle.h @@ -1,30 +1,30 @@ -//===- tools/dsymutil/CFBundle.h - CFBundle helper --------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H -#define LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H - -#include "llvm/ADT/StringRef.h" -#include <string> - -namespace llvm { -namespace dsymutil { - -struct CFBundleInfo { - std::string VersionStr = "1"; - std::string ShortVersionStr = "1.0"; - std::string IDStr; - bool OmitShortVersion() const { return ShortVersionStr.empty(); } -}; - -CFBundleInfo getBundleInfo(llvm::StringRef ExePath); - -} // end namespace dsymutil -} // end namespace llvm - -#endif +//===- tools/dsymutil/CFBundle.h - CFBundle helper --------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H +#define LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H + +#include "llvm/ADT/StringRef.h" +#include <string> + +namespace llvm { +namespace dsymutil { + +struct CFBundleInfo { + std::string VersionStr = "1"; + std::string ShortVersionStr = "1.0"; + std::string IDStr; + bool OmitShortVersion() const { return ShortVersionStr.empty(); } +}; + +CFBundleInfo getBundleInfo(llvm::StringRef ExePath); + +} // end namespace dsymutil +} // end namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/dsymutil/DebugMap.cpp b/contrib/libs/llvm12/tools/dsymutil/DebugMap.cpp index 605c1317b9..ba763e65c5 100644 --- a/contrib/libs/llvm12/tools/dsymutil/DebugMap.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/DebugMap.cpp @@ -1,294 +1,294 @@ -//===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "DebugMap.h" -#include "BinaryHolder.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Triple.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Support/Chrono.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/YAMLTraits.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cinttypes> -#include <cstdint> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -namespace llvm { - -namespace dsymutil { - -using namespace llvm::object; - -DebugMapObject::DebugMapObject(StringRef ObjectFilename, - sys::TimePoint<std::chrono::seconds> Timestamp, - uint8_t Type) - : Filename(std::string(ObjectFilename)), Timestamp(Timestamp), Type(Type) {} - -bool DebugMapObject::addSymbol(StringRef Name, Optional<uint64_t> ObjectAddress, - uint64_t LinkedAddress, uint32_t Size) { - auto InsertResult = Symbols.insert( - std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size))); - - if (ObjectAddress && InsertResult.second) - AddressToMapping[*ObjectAddress] = &*InsertResult.first; - return InsertResult.second; -} - -void DebugMapObject::print(raw_ostream &OS) const { - OS << getObjectFilename() << ":\n"; - // Sort the symbols in alphabetical order, like llvm-nm (and to get - // deterministic output for testing). - using Entry = std::pair<StringRef, SymbolMapping>; - std::vector<Entry> Entries; - Entries.reserve(Symbols.getNumItems()); +//===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "DebugMap.h" +#include "BinaryHolder.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstdint> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +namespace llvm { + +namespace dsymutil { + +using namespace llvm::object; + +DebugMapObject::DebugMapObject(StringRef ObjectFilename, + sys::TimePoint<std::chrono::seconds> Timestamp, + uint8_t Type) + : Filename(std::string(ObjectFilename)), Timestamp(Timestamp), Type(Type) {} + +bool DebugMapObject::addSymbol(StringRef Name, Optional<uint64_t> ObjectAddress, + uint64_t LinkedAddress, uint32_t Size) { + auto InsertResult = Symbols.insert( + std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size))); + + if (ObjectAddress && InsertResult.second) + AddressToMapping[*ObjectAddress] = &*InsertResult.first; + return InsertResult.second; +} + +void DebugMapObject::print(raw_ostream &OS) const { + OS << getObjectFilename() << ":\n"; + // Sort the symbols in alphabetical order, like llvm-nm (and to get + // deterministic output for testing). + using Entry = std::pair<StringRef, SymbolMapping>; + std::vector<Entry> Entries; + Entries.reserve(Symbols.getNumItems()); for (const auto &Sym : Symbols) - Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue())); - llvm::sort(Entries, [](const Entry &LHS, const Entry &RHS) { - return LHS.first < RHS.first; - }); - for (const auto &Sym : Entries) { - if (Sym.second.ObjectAddress) - OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress)); - else - OS << "\t????????????????"; - OS << format(" => %016" PRIx64 "+0x%x\t%s\n", - uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size), - Sym.first.data()); - } - OS << '\n'; -} - -#ifndef NDEBUG -void DebugMapObject::dump() const { print(errs()); } -#endif - -DebugMapObject & -DebugMap::addDebugMapObject(StringRef ObjectFilePath, - sys::TimePoint<std::chrono::seconds> Timestamp, - uint8_t Type) { - Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp, Type)); - return *Objects.back(); -} - -const DebugMapObject::DebugMapEntry * -DebugMapObject::lookupSymbol(StringRef SymbolName) const { - StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName); - if (Sym == Symbols.end()) - return nullptr; - return &*Sym; -} - -const DebugMapObject::DebugMapEntry * -DebugMapObject::lookupObjectAddress(uint64_t Address) const { - auto Mapping = AddressToMapping.find(Address); - if (Mapping == AddressToMapping.end()) - return nullptr; - return Mapping->getSecond(); -} - -void DebugMap::print(raw_ostream &OS) const { - yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0); - yout << const_cast<DebugMap &>(*this); -} - -#ifndef NDEBUG -void DebugMap::dump() const { print(errs()); } -#endif - -namespace { - -struct YAMLContext { - StringRef PrependPath; - Triple BinaryTriple; -}; - -} // end anonymous namespace - -ErrorOr<std::vector<std::unique_ptr<DebugMap>>> -DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, - bool Verbose) { - auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); - if (auto Err = ErrOrFile.getError()) - return Err; - - YAMLContext Ctxt; - - Ctxt.PrependPath = PrependPath; - - std::unique_ptr<DebugMap> Res; - yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt); - yin >> Res; - - if (auto EC = yin.error()) - return EC; - std::vector<std::unique_ptr<DebugMap>> Result; - Result.push_back(std::move(Res)); - return std::move(Result); -} - -} // end namespace dsymutil - -namespace yaml { - -// Normalize/Denormalize between YAML and a DebugMapObject. -struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { - YamlDMO(IO &io) { Timestamp = 0; } - YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); - dsymutil::DebugMapObject denormalize(IO &IO); - - std::string Filename; - int64_t Timestamp; - std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; -}; - -void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>:: - mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) { - io.mapRequired("sym", s.first); - io.mapOptional("objAddr", s.second.ObjectAddress); - io.mapRequired("binAddr", s.second.BinaryAddress); - io.mapOptional("size", s.second.Size); -} - -void MappingTraits<dsymutil::DebugMapObject>::mapping( - IO &io, dsymutil::DebugMapObject &DMO) { - MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); - io.mapRequired("filename", Norm->Filename); - io.mapOptional("timestamp", Norm->Timestamp); - io.mapRequired("symbols", Norm->Entries); -} - -void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) { - out << val.str(); -} - -StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) { - value = Triple(scalar); - return StringRef(); -} - -size_t -SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size( - IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) { - return seq.size(); -} - -dsymutil::DebugMapObject & -SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element( - IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, - size_t index) { - if (index >= seq.size()) { - seq.resize(index + 1); - seq[index].reset(new dsymutil::DebugMapObject); - } - return *seq[index]; -} - -void MappingTraits<dsymutil::DebugMap>::mapping(IO &io, - dsymutil::DebugMap &DM) { - io.mapRequired("triple", DM.BinaryTriple); - io.mapOptional("binary-path", DM.BinaryPath); - if (void *Ctxt = io.getContext()) - reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple; - io.mapOptional("objects", DM.Objects); -} - -void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( - IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) { - if (!DM) - DM.reset(new DebugMap()); - io.mapRequired("triple", DM->BinaryTriple); - io.mapOptional("binary-path", DM->BinaryPath); - if (void *Ctxt = io.getContext()) - reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple; - io.mapOptional("objects", DM->Objects); -} - -MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( - IO &io, dsymutil::DebugMapObject &Obj) { - Filename = Obj.Filename; - Timestamp = sys::toTimeT(Obj.getTimestamp()); - Entries.reserve(Obj.Symbols.size()); - for (auto &Entry : Obj.Symbols) - Entries.push_back( - std::make_pair(std::string(Entry.getKey()), Entry.getValue())); -} - -dsymutil::DebugMapObject -MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { - BinaryHolder BinHolder(vfs::getRealFileSystem(), /* Verbose =*/false); - const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext()); - SmallString<80> Path(Ctxt.PrependPath); - StringMap<uint64_t> SymbolAddresses; - - sys::path::append(Path, Filename); - - auto ObjectEntry = BinHolder.getObjectEntry(Path); - if (!ObjectEntry) { - auto Err = ObjectEntry.takeError(); - WithColor::warning() << "Unable to open " << Path << " " - << toString(std::move(Err)) << '\n'; - } else { - auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple); - if (!Object) { - auto Err = Object.takeError(); - WithColor::warning() << "Unable to open " << Path << " " - << toString(std::move(Err)) << '\n'; - } else { - for (const auto &Sym : Object->symbols()) { - Expected<uint64_t> AddressOrErr = Sym.getValue(); - if (!AddressOrErr) { - // TODO: Actually report errors helpfully. - consumeError(AddressOrErr.takeError()); - continue; - } - Expected<StringRef> Name = Sym.getName(); - Expected<uint32_t> FlagsOrErr = Sym.getFlags(); - if (!Name || !FlagsOrErr || - (*FlagsOrErr & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { - // TODO: Actually report errors helpfully. - if (!FlagsOrErr) - consumeError(FlagsOrErr.takeError()); - if (!Name) - consumeError(Name.takeError()); - continue; - } - SymbolAddresses[*Name] = *AddressOrErr; - } - } - } - - dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO); - for (auto &Entry : Entries) { - auto &Mapping = Entry.second; - Optional<uint64_t> ObjAddress; - if (Mapping.ObjectAddress) - ObjAddress = *Mapping.ObjectAddress; - auto AddressIt = SymbolAddresses.find(Entry.first); - if (AddressIt != SymbolAddresses.end()) - ObjAddress = AddressIt->getValue(); - Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size); - } - return Res; -} - -} // end namespace yaml -} // end namespace llvm + Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue())); + llvm::sort(Entries, [](const Entry &LHS, const Entry &RHS) { + return LHS.first < RHS.first; + }); + for (const auto &Sym : Entries) { + if (Sym.second.ObjectAddress) + OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress)); + else + OS << "\t????????????????"; + OS << format(" => %016" PRIx64 "+0x%x\t%s\n", + uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size), + Sym.first.data()); + } + OS << '\n'; +} + +#ifndef NDEBUG +void DebugMapObject::dump() const { print(errs()); } +#endif + +DebugMapObject & +DebugMap::addDebugMapObject(StringRef ObjectFilePath, + sys::TimePoint<std::chrono::seconds> Timestamp, + uint8_t Type) { + Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp, Type)); + return *Objects.back(); +} + +const DebugMapObject::DebugMapEntry * +DebugMapObject::lookupSymbol(StringRef SymbolName) const { + StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName); + if (Sym == Symbols.end()) + return nullptr; + return &*Sym; +} + +const DebugMapObject::DebugMapEntry * +DebugMapObject::lookupObjectAddress(uint64_t Address) const { + auto Mapping = AddressToMapping.find(Address); + if (Mapping == AddressToMapping.end()) + return nullptr; + return Mapping->getSecond(); +} + +void DebugMap::print(raw_ostream &OS) const { + yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0); + yout << const_cast<DebugMap &>(*this); +} + +#ifndef NDEBUG +void DebugMap::dump() const { print(errs()); } +#endif + +namespace { + +struct YAMLContext { + StringRef PrependPath; + Triple BinaryTriple; +}; + +} // end anonymous namespace + +ErrorOr<std::vector<std::unique_ptr<DebugMap>>> +DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, + bool Verbose) { + auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); + if (auto Err = ErrOrFile.getError()) + return Err; + + YAMLContext Ctxt; + + Ctxt.PrependPath = PrependPath; + + std::unique_ptr<DebugMap> Res; + yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt); + yin >> Res; + + if (auto EC = yin.error()) + return EC; + std::vector<std::unique_ptr<DebugMap>> Result; + Result.push_back(std::move(Res)); + return std::move(Result); +} + +} // end namespace dsymutil + +namespace yaml { + +// Normalize/Denormalize between YAML and a DebugMapObject. +struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { + YamlDMO(IO &io) { Timestamp = 0; } + YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); + dsymutil::DebugMapObject denormalize(IO &IO); + + std::string Filename; + int64_t Timestamp; + std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; +}; + +void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>:: + mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) { + io.mapRequired("sym", s.first); + io.mapOptional("objAddr", s.second.ObjectAddress); + io.mapRequired("binAddr", s.second.BinaryAddress); + io.mapOptional("size", s.second.Size); +} + +void MappingTraits<dsymutil::DebugMapObject>::mapping( + IO &io, dsymutil::DebugMapObject &DMO) { + MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); + io.mapRequired("filename", Norm->Filename); + io.mapOptional("timestamp", Norm->Timestamp); + io.mapRequired("symbols", Norm->Entries); +} + +void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) { + out << val.str(); +} + +StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) { + value = Triple(scalar); + return StringRef(); +} + +size_t +SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size( + IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) { + return seq.size(); +} + +dsymutil::DebugMapObject & +SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element( + IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, + size_t index) { + if (index >= seq.size()) { + seq.resize(index + 1); + seq[index].reset(new dsymutil::DebugMapObject); + } + return *seq[index]; +} + +void MappingTraits<dsymutil::DebugMap>::mapping(IO &io, + dsymutil::DebugMap &DM) { + io.mapRequired("triple", DM.BinaryTriple); + io.mapOptional("binary-path", DM.BinaryPath); + if (void *Ctxt = io.getContext()) + reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple; + io.mapOptional("objects", DM.Objects); +} + +void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( + IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) { + if (!DM) + DM.reset(new DebugMap()); + io.mapRequired("triple", DM->BinaryTriple); + io.mapOptional("binary-path", DM->BinaryPath); + if (void *Ctxt = io.getContext()) + reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple; + io.mapOptional("objects", DM->Objects); +} + +MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( + IO &io, dsymutil::DebugMapObject &Obj) { + Filename = Obj.Filename; + Timestamp = sys::toTimeT(Obj.getTimestamp()); + Entries.reserve(Obj.Symbols.size()); + for (auto &Entry : Obj.Symbols) + Entries.push_back( + std::make_pair(std::string(Entry.getKey()), Entry.getValue())); +} + +dsymutil::DebugMapObject +MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { + BinaryHolder BinHolder(vfs::getRealFileSystem(), /* Verbose =*/false); + const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext()); + SmallString<80> Path(Ctxt.PrependPath); + StringMap<uint64_t> SymbolAddresses; + + sys::path::append(Path, Filename); + + auto ObjectEntry = BinHolder.getObjectEntry(Path); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + WithColor::warning() << "Unable to open " << Path << " " + << toString(std::move(Err)) << '\n'; + } else { + auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple); + if (!Object) { + auto Err = Object.takeError(); + WithColor::warning() << "Unable to open " << Path << " " + << toString(std::move(Err)) << '\n'; + } else { + for (const auto &Sym : Object->symbols()) { + Expected<uint64_t> AddressOrErr = Sym.getValue(); + if (!AddressOrErr) { + // TODO: Actually report errors helpfully. + consumeError(AddressOrErr.takeError()); + continue; + } + Expected<StringRef> Name = Sym.getName(); + Expected<uint32_t> FlagsOrErr = Sym.getFlags(); + if (!Name || !FlagsOrErr || + (*FlagsOrErr & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { + // TODO: Actually report errors helpfully. + if (!FlagsOrErr) + consumeError(FlagsOrErr.takeError()); + if (!Name) + consumeError(Name.takeError()); + continue; + } + SymbolAddresses[*Name] = *AddressOrErr; + } + } + } + + dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO); + for (auto &Entry : Entries) { + auto &Mapping = Entry.second; + Optional<uint64_t> ObjAddress; + if (Mapping.ObjectAddress) + ObjAddress = *Mapping.ObjectAddress; + auto AddressIt = SymbolAddresses.find(Entry.first); + if (AddressIt != SymbolAddresses.end()) + ObjAddress = AddressIt->getValue(); + Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size); + } + return Res; +} + +} // end namespace yaml +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/DebugMap.h b/contrib/libs/llvm12/tools/dsymutil/DebugMap.h index ee552ed983..3ce8a33a0a 100644 --- a/contrib/libs/llvm12/tools/dsymutil/DebugMap.h +++ b/contrib/libs/llvm12/tools/dsymutil/DebugMap.h @@ -1,272 +1,272 @@ -//=- tools/dsymutil/DebugMap.h - Generic debug map representation -*- 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 -// -//===----------------------------------------------------------------------===// -// -/// \file -/// -/// This file contains the class declaration of the DebugMap -/// entity. A DebugMap lists all the object files linked together to -/// produce an executable along with the linked address of all the -/// atoms used in these object files. -/// The DebugMap is an input to the DwarfLinker class that will -/// extract the Dwarf debug information from the referenced object -/// files and link their usefull debug info together. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H -#define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Triple.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/Object/MachO.h" -#include "llvm/Support/Chrono.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/YAMLTraits.h" -#include <chrono> -#include <cstddef> -#include <cstdint> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -namespace llvm { - -class raw_ostream; - -namespace dsymutil { - -class DebugMapObject; - -/// The DebugMap object stores the list of object files to query for debug -/// information along with the mapping between the symbols' addresses in the -/// object file to their linked address in the linked binary. -/// -/// A DebugMap producer could look like this: -/// DebugMap *DM = new DebugMap(); -/// for (const auto &Obj: LinkedObjects) { -/// DebugMapObject &DMO = DM->addDebugMapObject(Obj.getPath()); -/// for (const auto &Sym: Obj.getLinkedSymbols()) -/// DMO.addSymbol(Sym.getName(), Sym.getObjectFileAddress(), -/// Sym.getBinaryAddress()); -/// } -/// -/// A DebugMap consumer can then use the map to link the debug -/// information. For example something along the lines of: -/// for (const auto &DMO: DM->objects()) { -/// auto Obj = createBinary(DMO.getObjectFilename()); -/// for (auto &DIE: Obj.getDwarfDIEs()) { -/// if (SymbolMapping *Sym = DMO.lookup(DIE.getName())) -/// DIE.relocate(Sym->ObjectAddress, Sym->BinaryAddress); -/// else -/// DIE.discardSubtree(); -/// } -/// } -class DebugMap { - Triple BinaryTriple; - std::string BinaryPath; - std::vector<uint8_t> BinaryUUID; - using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>; - - ObjectContainer Objects; - - /// For YAML IO support. - ///@{ - friend yaml::MappingTraits<std::unique_ptr<DebugMap>>; - friend yaml::MappingTraits<DebugMap>; - - DebugMap() = default; - ///@} - -public: - DebugMap(const Triple &BinaryTriple, StringRef BinaryPath, - ArrayRef<uint8_t> BinaryUUID = ArrayRef<uint8_t>()) - : BinaryTriple(BinaryTriple), BinaryPath(std::string(BinaryPath)), - BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {} - - using const_iterator = ObjectContainer::const_iterator; - - iterator_range<const_iterator> objects() const { - return make_range(begin(), end()); - } - - const_iterator begin() const { return Objects.begin(); } - - const_iterator end() const { return Objects.end(); } - - unsigned getNumberOfObjects() const { return Objects.size(); } - - /// This function adds an DebugMapObject to the list owned by this - /// debug map. - DebugMapObject & - addDebugMapObject(StringRef ObjectFilePath, - sys::TimePoint<std::chrono::seconds> Timestamp, - uint8_t Type = llvm::MachO::N_OSO); - - const Triple &getTriple() const { return BinaryTriple; } - - const ArrayRef<uint8_t> getUUID() const { - return ArrayRef<uint8_t>(BinaryUUID); - } - - StringRef getBinaryPath() const { return BinaryPath; } - - void print(raw_ostream &OS) const; - -#ifndef NDEBUG - void dump() const; -#endif - - /// Read a debug map for \a InputFile. - static ErrorOr<std::vector<std::unique_ptr<DebugMap>>> - parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose); -}; - -/// The DebugMapObject represents one object file described by the DebugMap. It -/// contains a list of mappings between addresses in the object file and in the -/// linked binary for all the linked atoms in this object file. -class DebugMapObject { -public: - struct SymbolMapping { - Optional<yaml::Hex64> ObjectAddress; - yaml::Hex64 BinaryAddress; - yaml::Hex32 Size; - - SymbolMapping(Optional<uint64_t> ObjectAddr, uint64_t BinaryAddress, - uint32_t Size) - : BinaryAddress(BinaryAddress), Size(Size) { - if (ObjectAddr) - ObjectAddress = *ObjectAddr; - } - - /// For YAML IO support - SymbolMapping() = default; - }; - - using YAMLSymbolMapping = std::pair<std::string, SymbolMapping>; - using DebugMapEntry = StringMapEntry<SymbolMapping>; - - /// Adds a symbol mapping to this DebugMapObject. - /// \returns false if the symbol was already registered. The request - /// is discarded in this case. - bool addSymbol(StringRef SymName, Optional<uint64_t> ObjectAddress, - uint64_t LinkedAddress, uint32_t Size); - - /// Lookup a symbol mapping. - /// \returns null if the symbol isn't found. - const DebugMapEntry *lookupSymbol(StringRef SymbolName) const; - - /// Lookup an object file address. - /// \returns null if the address isn't found. - const DebugMapEntry *lookupObjectAddress(uint64_t Address) const; - - StringRef getObjectFilename() const { return Filename; } - - sys::TimePoint<std::chrono::seconds> getTimestamp() const { - return Timestamp; - } - - uint8_t getType() const { return Type; } - - iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const { - return make_range(Symbols.begin(), Symbols.end()); - } - - bool empty() const { return Symbols.empty(); } - - void addWarning(StringRef Warning) { - Warnings.push_back(std::string(Warning)); - } - const std::vector<std::string> &getWarnings() const { return Warnings; } - - void print(raw_ostream &OS) const; -#ifndef NDEBUG - void dump() const; -#endif - -private: - friend class DebugMap; - - /// DebugMapObjects can only be constructed by the owning DebugMap. - DebugMapObject(StringRef ObjectFilename, - sys::TimePoint<std::chrono::seconds> Timestamp, uint8_t Type); - - std::string Filename; - sys::TimePoint<std::chrono::seconds> Timestamp; - StringMap<SymbolMapping> Symbols; - DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; - uint8_t Type; - - std::vector<std::string> Warnings; - - /// For YAMLIO support. - ///@{ - friend yaml::MappingTraits<dsymutil::DebugMapObject>; - friend yaml::SequenceTraits<std::vector<std::unique_ptr<DebugMapObject>>>; - - DebugMapObject() = default; - -public: - DebugMapObject(DebugMapObject &&) = default; - DebugMapObject &operator=(DebugMapObject &&) = default; - ///@} -}; - -} // end namespace dsymutil -} // end namespace llvm - -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping) - -namespace llvm { -namespace yaml { - -using namespace llvm::dsymutil; - -template <> -struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> { - static void mapping(IO &io, - std::pair<std::string, DebugMapObject::SymbolMapping> &s); - static const bool flow = true; -}; - -template <> struct MappingTraits<dsymutil::DebugMapObject> { - struct YamlDMO; - static void mapping(IO &io, dsymutil::DebugMapObject &DMO); -}; - -template <> struct ScalarTraits<Triple> { - static void output(const Triple &val, void *, raw_ostream &out); - static StringRef input(StringRef scalar, void *, Triple &value); - static QuotingType mustQuote(StringRef) { return QuotingType::Single; } -}; - -template <> -struct SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> { - static size_t - size(IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq); - static dsymutil::DebugMapObject & - element(IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, - size_t index); -}; - -template <> struct MappingTraits<dsymutil::DebugMap> { - static void mapping(IO &io, dsymutil::DebugMap &DM); -}; - -template <> struct MappingTraits<std::unique_ptr<dsymutil::DebugMap>> { - static void mapping(IO &io, std::unique_ptr<dsymutil::DebugMap> &DM); -}; - -} // end namespace yaml -} // end namespace llvm - -#endif // LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H +//=- tools/dsymutil/DebugMap.h - Generic debug map representation -*- 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// +/// This file contains the class declaration of the DebugMap +/// entity. A DebugMap lists all the object files linked together to +/// produce an executable along with the linked address of all the +/// atoms used in these object files. +/// The DebugMap is an input to the DwarfLinker class that will +/// extract the Dwarf debug information from the referenced object +/// files and link their usefull debug info together. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H +#define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/YAMLTraits.h" +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +namespace llvm { + +class raw_ostream; + +namespace dsymutil { + +class DebugMapObject; + +/// The DebugMap object stores the list of object files to query for debug +/// information along with the mapping between the symbols' addresses in the +/// object file to their linked address in the linked binary. +/// +/// A DebugMap producer could look like this: +/// DebugMap *DM = new DebugMap(); +/// for (const auto &Obj: LinkedObjects) { +/// DebugMapObject &DMO = DM->addDebugMapObject(Obj.getPath()); +/// for (const auto &Sym: Obj.getLinkedSymbols()) +/// DMO.addSymbol(Sym.getName(), Sym.getObjectFileAddress(), +/// Sym.getBinaryAddress()); +/// } +/// +/// A DebugMap consumer can then use the map to link the debug +/// information. For example something along the lines of: +/// for (const auto &DMO: DM->objects()) { +/// auto Obj = createBinary(DMO.getObjectFilename()); +/// for (auto &DIE: Obj.getDwarfDIEs()) { +/// if (SymbolMapping *Sym = DMO.lookup(DIE.getName())) +/// DIE.relocate(Sym->ObjectAddress, Sym->BinaryAddress); +/// else +/// DIE.discardSubtree(); +/// } +/// } +class DebugMap { + Triple BinaryTriple; + std::string BinaryPath; + std::vector<uint8_t> BinaryUUID; + using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>; + + ObjectContainer Objects; + + /// For YAML IO support. + ///@{ + friend yaml::MappingTraits<std::unique_ptr<DebugMap>>; + friend yaml::MappingTraits<DebugMap>; + + DebugMap() = default; + ///@} + +public: + DebugMap(const Triple &BinaryTriple, StringRef BinaryPath, + ArrayRef<uint8_t> BinaryUUID = ArrayRef<uint8_t>()) + : BinaryTriple(BinaryTriple), BinaryPath(std::string(BinaryPath)), + BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {} + + using const_iterator = ObjectContainer::const_iterator; + + iterator_range<const_iterator> objects() const { + return make_range(begin(), end()); + } + + const_iterator begin() const { return Objects.begin(); } + + const_iterator end() const { return Objects.end(); } + + unsigned getNumberOfObjects() const { return Objects.size(); } + + /// This function adds an DebugMapObject to the list owned by this + /// debug map. + DebugMapObject & + addDebugMapObject(StringRef ObjectFilePath, + sys::TimePoint<std::chrono::seconds> Timestamp, + uint8_t Type = llvm::MachO::N_OSO); + + const Triple &getTriple() const { return BinaryTriple; } + + const ArrayRef<uint8_t> getUUID() const { + return ArrayRef<uint8_t>(BinaryUUID); + } + + StringRef getBinaryPath() const { return BinaryPath; } + + void print(raw_ostream &OS) const; + +#ifndef NDEBUG + void dump() const; +#endif + + /// Read a debug map for \a InputFile. + static ErrorOr<std::vector<std::unique_ptr<DebugMap>>> + parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose); +}; + +/// The DebugMapObject represents one object file described by the DebugMap. It +/// contains a list of mappings between addresses in the object file and in the +/// linked binary for all the linked atoms in this object file. +class DebugMapObject { +public: + struct SymbolMapping { + Optional<yaml::Hex64> ObjectAddress; + yaml::Hex64 BinaryAddress; + yaml::Hex32 Size; + + SymbolMapping(Optional<uint64_t> ObjectAddr, uint64_t BinaryAddress, + uint32_t Size) + : BinaryAddress(BinaryAddress), Size(Size) { + if (ObjectAddr) + ObjectAddress = *ObjectAddr; + } + + /// For YAML IO support + SymbolMapping() = default; + }; + + using YAMLSymbolMapping = std::pair<std::string, SymbolMapping>; + using DebugMapEntry = StringMapEntry<SymbolMapping>; + + /// Adds a symbol mapping to this DebugMapObject. + /// \returns false if the symbol was already registered. The request + /// is discarded in this case. + bool addSymbol(StringRef SymName, Optional<uint64_t> ObjectAddress, + uint64_t LinkedAddress, uint32_t Size); + + /// Lookup a symbol mapping. + /// \returns null if the symbol isn't found. + const DebugMapEntry *lookupSymbol(StringRef SymbolName) const; + + /// Lookup an object file address. + /// \returns null if the address isn't found. + const DebugMapEntry *lookupObjectAddress(uint64_t Address) const; + + StringRef getObjectFilename() const { return Filename; } + + sys::TimePoint<std::chrono::seconds> getTimestamp() const { + return Timestamp; + } + + uint8_t getType() const { return Type; } + + iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const { + return make_range(Symbols.begin(), Symbols.end()); + } + + bool empty() const { return Symbols.empty(); } + + void addWarning(StringRef Warning) { + Warnings.push_back(std::string(Warning)); + } + const std::vector<std::string> &getWarnings() const { return Warnings; } + + void print(raw_ostream &OS) const; +#ifndef NDEBUG + void dump() const; +#endif + +private: + friend class DebugMap; + + /// DebugMapObjects can only be constructed by the owning DebugMap. + DebugMapObject(StringRef ObjectFilename, + sys::TimePoint<std::chrono::seconds> Timestamp, uint8_t Type); + + std::string Filename; + sys::TimePoint<std::chrono::seconds> Timestamp; + StringMap<SymbolMapping> Symbols; + DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; + uint8_t Type; + + std::vector<std::string> Warnings; + + /// For YAMLIO support. + ///@{ + friend yaml::MappingTraits<dsymutil::DebugMapObject>; + friend yaml::SequenceTraits<std::vector<std::unique_ptr<DebugMapObject>>>; + + DebugMapObject() = default; + +public: + DebugMapObject(DebugMapObject &&) = default; + DebugMapObject &operator=(DebugMapObject &&) = default; + ///@} +}; + +} // end namespace dsymutil +} // end namespace llvm + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping) + +namespace llvm { +namespace yaml { + +using namespace llvm::dsymutil; + +template <> +struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> { + static void mapping(IO &io, + std::pair<std::string, DebugMapObject::SymbolMapping> &s); + static const bool flow = true; +}; + +template <> struct MappingTraits<dsymutil::DebugMapObject> { + struct YamlDMO; + static void mapping(IO &io, dsymutil::DebugMapObject &DMO); +}; + +template <> struct ScalarTraits<Triple> { + static void output(const Triple &val, void *, raw_ostream &out); + static StringRef input(StringRef scalar, void *, Triple &value); + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } +}; + +template <> +struct SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> { + static size_t + size(IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq); + static dsymutil::DebugMapObject & + element(IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, + size_t index); +}; + +template <> struct MappingTraits<dsymutil::DebugMap> { + static void mapping(IO &io, dsymutil::DebugMap &DM); +}; + +template <> struct MappingTraits<std::unique_ptr<dsymutil::DebugMap>> { + static void mapping(IO &io, std::unique_ptr<dsymutil::DebugMap> &DM); +}; + +} // end namespace yaml +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H diff --git a/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.cpp b/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.cpp index 29408e7c49..1cb561dbac 100644 --- a/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -1,616 +1,616 @@ -//===- tools/dsymutil/DwarfLinkerForBinary.cpp ----------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "DwarfLinkerForBinary.h" -#include "BinaryHolder.h" -#include "DebugMap.h" -#include "MachOUtils.h" -#include "dsymutil.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitVector.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseMapInfo.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/Hashing.h" -#include "llvm/ADT/IntervalMap.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/PointerIntPair.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Triple.h" -#include "llvm/ADT/Twine.h" -#include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/CodeGen/AccelTable.h" -#include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/CodeGen/DIE.h" -#include "llvm/CodeGen/NonRelocatableStringpool.h" -#include "llvm/Config/config.h" -#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" -#include "llvm/DebugInfo/DIContext.h" -#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" -#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" -#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" -#include "llvm/DebugInfo/DWARF/DWARFDie.h" -#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "llvm/DebugInfo/DWARF/DWARFSection.h" -#include "llvm/DebugInfo/DWARF/DWARFUnit.h" -#include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCCodeEmitter.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCDwarf.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCObjectFileInfo.h" -#include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCSection.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptions.h" -#include "llvm/Object/MachO.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Object/SymbolicFile.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/DJB.h" -#include "llvm/Support/DataExtractor.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/ToolOutputFile.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.h" -#include <algorithm> -#include <cassert> -#include <cinttypes> -#include <climits> -#include <cstdint> -#include <cstdlib> -#include <cstring> -#include <limits> -#include <map> -#include <memory> -#include <string> -#include <system_error> -#include <tuple> -#include <utility> -#include <vector> - -namespace llvm { - -static mc::RegisterMCTargetOptionsFlags MOF; - -namespace dsymutil { - -static Error copySwiftInterfaces( - const std::map<std::string, std::string> &ParseableSwiftInterfaces, - StringRef Architecture, const LinkOptions &Options) { - std::error_code EC; - SmallString<128> InputPath; - SmallString<128> Path; - sys::path::append(Path, *Options.ResourceDir, "Swift", Architecture); - if ((EC = sys::fs::create_directories(Path.str(), true, - sys::fs::perms::all_all))) - return make_error<StringError>( - "cannot create directory: " + toString(errorCodeToError(EC)), EC); - unsigned BaseLength = Path.size(); - - for (auto &I : ParseableSwiftInterfaces) { - StringRef ModuleName = I.first; - StringRef InterfaceFile = I.second; - if (!Options.PrependPath.empty()) { - InputPath.clear(); - sys::path::append(InputPath, Options.PrependPath, InterfaceFile); - InterfaceFile = InputPath; - } - sys::path::append(Path, ModuleName); - Path.append(".swiftinterface"); - if (Options.Verbose) - outs() << "copy parseable Swift interface " << InterfaceFile << " -> " - << Path.str() << '\n'; - - // copy_file attempts an APFS clone first, so this should be cheap. - if ((EC = sys::fs::copy_file(InterfaceFile, Path.str()))) - warn(Twine("cannot copy parseable Swift interface ") + InterfaceFile + - ": " + toString(errorCodeToError(EC))); - Path.resize(BaseLength); - } - return Error::success(); -} - -/// Report a warning to the user, optionally including information about a -/// specific \p DIE related to the warning. -void DwarfLinkerForBinary::reportWarning(const Twine &Warning, - StringRef Context, - const DWARFDie *DIE) const { - - warn(Warning, Context); - - if (!Options.Verbose || !DIE) - return; - - DIDumpOptions DumpOpts; - DumpOpts.ChildRecurseDepth = 0; - DumpOpts.Verbose = Options.Verbose; - - WithColor::note() << " in DIE:\n"; - DIE->dump(errs(), 6 /* Indent */, DumpOpts); -} - -bool DwarfLinkerForBinary::createStreamer(const Triple &TheTriple, - raw_fd_ostream &OutFile) { - if (Options.NoOutput) - return true; - - Streamer = std::make_unique<DwarfStreamer>( - Options.FileType, OutFile, Options.Translator, Options.Minimize, - [&](const Twine &Error, StringRef Context, const DWARFDie *) { - error(Error, Context); - }, - [&](const Twine &Warning, StringRef Context, const DWARFDie *) { - warn(Warning, Context); - }); - return Streamer->init(TheTriple); -} - -ErrorOr<const object::ObjectFile &> -DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, - const Triple &Triple) { - auto ObjectEntry = - BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp()); - if (!ObjectEntry) { - auto Err = ObjectEntry.takeError(); - reportWarning(Twine(Obj.getObjectFilename()) + ": " + - toString(std::move(Err)), - Obj.getObjectFilename()); - return errorToErrorCode(std::move(Err)); - } - - auto Object = ObjectEntry->getObject(Triple); - if (!Object) { - auto Err = Object.takeError(); - reportWarning(Twine(Obj.getObjectFilename()) + ": " + - toString(std::move(Err)), - Obj.getObjectFilename()); - return errorToErrorCode(std::move(Err)); - } - - return *Object; -} - -static Error remarksErrorHandler(const DebugMapObject &DMO, - DwarfLinkerForBinary &Linker, - std::unique_ptr<FileError> FE) { - bool IsArchive = DMO.getObjectFilename().endswith(")"); - // Don't report errors for missing remark files from static - // archives. - if (!IsArchive) - return Error(std::move(FE)); - - std::string Message = FE->message(); - Error E = FE->takeError(); - Error NewE = handleErrors(std::move(E), [&](std::unique_ptr<ECError> EC) { - if (EC->convertToErrorCode() != std::errc::no_such_file_or_directory) - return Error(std::move(EC)); - - Linker.reportWarning(Message, DMO.getObjectFilename()); - return Error(Error::success()); - }); - - if (!NewE) - return Error::success(); - - return createFileError(FE->getFileName(), std::move(NewE)); -} - -static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath, - StringRef ArchName, const remarks::RemarkLinker &RL) { - // Make sure we don't create the directories and the file if there is nothing - // to serialize. - if (RL.empty()) - return Error::success(); - - SmallString<128> InputPath; - SmallString<128> Path; - // Create the "Remarks" directory in the "Resources" directory. - sys::path::append(Path, *Options.ResourceDir, "Remarks"); - if (std::error_code EC = sys::fs::create_directories(Path.str(), true, - sys::fs::perms::all_all)) - return errorCodeToError(EC); - - // Append the file name. - // For fat binaries, also append a dash and the architecture name. - sys::path::append(Path, sys::path::filename(BinaryPath)); - if (Options.NumDebugMaps > 1) { - // More than one debug map means we have a fat binary. - Path += '-'; - Path += ArchName; - } - - std::error_code EC; - raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, sys::fs::OF_None); - if (EC) - return errorCodeToError(EC); - - if (Error E = RL.serialize(OS, Options.RemarksFormat)) - return E; - - return Error::success(); -} - +//===- tools/dsymutil/DwarfLinkerForBinary.cpp ----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "DwarfLinkerForBinary.h" +#include "BinaryHolder.h" +#include "DebugMap.h" +#include "MachOUtils.h" +#include "dsymutil.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/CodeGen/AccelTable.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/Config/config.h" +#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/SymbolicFile.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <climits> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <limits> +#include <map> +#include <memory> +#include <string> +#include <system_error> +#include <tuple> +#include <utility> +#include <vector> + +namespace llvm { + +static mc::RegisterMCTargetOptionsFlags MOF; + +namespace dsymutil { + +static Error copySwiftInterfaces( + const std::map<std::string, std::string> &ParseableSwiftInterfaces, + StringRef Architecture, const LinkOptions &Options) { + std::error_code EC; + SmallString<128> InputPath; + SmallString<128> Path; + sys::path::append(Path, *Options.ResourceDir, "Swift", Architecture); + if ((EC = sys::fs::create_directories(Path.str(), true, + sys::fs::perms::all_all))) + return make_error<StringError>( + "cannot create directory: " + toString(errorCodeToError(EC)), EC); + unsigned BaseLength = Path.size(); + + for (auto &I : ParseableSwiftInterfaces) { + StringRef ModuleName = I.first; + StringRef InterfaceFile = I.second; + if (!Options.PrependPath.empty()) { + InputPath.clear(); + sys::path::append(InputPath, Options.PrependPath, InterfaceFile); + InterfaceFile = InputPath; + } + sys::path::append(Path, ModuleName); + Path.append(".swiftinterface"); + if (Options.Verbose) + outs() << "copy parseable Swift interface " << InterfaceFile << " -> " + << Path.str() << '\n'; + + // copy_file attempts an APFS clone first, so this should be cheap. + if ((EC = sys::fs::copy_file(InterfaceFile, Path.str()))) + warn(Twine("cannot copy parseable Swift interface ") + InterfaceFile + + ": " + toString(errorCodeToError(EC))); + Path.resize(BaseLength); + } + return Error::success(); +} + +/// Report a warning to the user, optionally including information about a +/// specific \p DIE related to the warning. +void DwarfLinkerForBinary::reportWarning(const Twine &Warning, + StringRef Context, + const DWARFDie *DIE) const { + + warn(Warning, Context); + + if (!Options.Verbose || !DIE) + return; + + DIDumpOptions DumpOpts; + DumpOpts.ChildRecurseDepth = 0; + DumpOpts.Verbose = Options.Verbose; + + WithColor::note() << " in DIE:\n"; + DIE->dump(errs(), 6 /* Indent */, DumpOpts); +} + +bool DwarfLinkerForBinary::createStreamer(const Triple &TheTriple, + raw_fd_ostream &OutFile) { + if (Options.NoOutput) + return true; + + Streamer = std::make_unique<DwarfStreamer>( + Options.FileType, OutFile, Options.Translator, Options.Minimize, + [&](const Twine &Error, StringRef Context, const DWARFDie *) { + error(Error, Context); + }, + [&](const Twine &Warning, StringRef Context, const DWARFDie *) { + warn(Warning, Context); + }); + return Streamer->init(TheTriple); +} + +ErrorOr<const object::ObjectFile &> +DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, + const Triple &Triple) { + auto ObjectEntry = + BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp()); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + reportWarning(Twine(Obj.getObjectFilename()) + ": " + + toString(std::move(Err)), + Obj.getObjectFilename()); + return errorToErrorCode(std::move(Err)); + } + + auto Object = ObjectEntry->getObject(Triple); + if (!Object) { + auto Err = Object.takeError(); + reportWarning(Twine(Obj.getObjectFilename()) + ": " + + toString(std::move(Err)), + Obj.getObjectFilename()); + return errorToErrorCode(std::move(Err)); + } + + return *Object; +} + +static Error remarksErrorHandler(const DebugMapObject &DMO, + DwarfLinkerForBinary &Linker, + std::unique_ptr<FileError> FE) { + bool IsArchive = DMO.getObjectFilename().endswith(")"); + // Don't report errors for missing remark files from static + // archives. + if (!IsArchive) + return Error(std::move(FE)); + + std::string Message = FE->message(); + Error E = FE->takeError(); + Error NewE = handleErrors(std::move(E), [&](std::unique_ptr<ECError> EC) { + if (EC->convertToErrorCode() != std::errc::no_such_file_or_directory) + return Error(std::move(EC)); + + Linker.reportWarning(Message, DMO.getObjectFilename()); + return Error(Error::success()); + }); + + if (!NewE) + return Error::success(); + + return createFileError(FE->getFileName(), std::move(NewE)); +} + +static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath, + StringRef ArchName, const remarks::RemarkLinker &RL) { + // Make sure we don't create the directories and the file if there is nothing + // to serialize. + if (RL.empty()) + return Error::success(); + + SmallString<128> InputPath; + SmallString<128> Path; + // Create the "Remarks" directory in the "Resources" directory. + sys::path::append(Path, *Options.ResourceDir, "Remarks"); + if (std::error_code EC = sys::fs::create_directories(Path.str(), true, + sys::fs::perms::all_all)) + return errorCodeToError(EC); + + // Append the file name. + // For fat binaries, also append a dash and the architecture name. + sys::path::append(Path, sys::path::filename(BinaryPath)); + if (Options.NumDebugMaps > 1) { + // More than one debug map means we have a fat binary. + Path += '-'; + Path += ArchName; + } + + std::error_code EC; + raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC, sys::fs::OF_None); + if (EC) + return errorCodeToError(EC); + + if (Error E = RL.serialize(OS, Options.RemarksFormat)) + return E; + + return Error::success(); +} + ErrorOr<DWARFFile &> -DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, - const DebugMap &DebugMap, - remarks::RemarkLinker &RL) { - auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple()); - - if (ErrorOrObj) { - ContextForLinking.push_back( - std::unique_ptr<DWARFContext>(DWARFContext::create(*ErrorOrObj))); - AddressMapForLinking.push_back( - std::make_unique<AddressManager>(*this, *ErrorOrObj, Obj)); - +DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj, + const DebugMap &DebugMap, + remarks::RemarkLinker &RL) { + auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple()); + + if (ErrorOrObj) { + ContextForLinking.push_back( + std::unique_ptr<DWARFContext>(DWARFContext::create(*ErrorOrObj))); + AddressMapForLinking.push_back( + std::make_unique<AddressManager>(*this, *ErrorOrObj, Obj)); + ObjectsForLinking.push_back(std::make_unique<DWARFFile>( - Obj.getObjectFilename(), ContextForLinking.back().get(), - AddressMapForLinking.back().get(), - Obj.empty() ? Obj.getWarnings() : EmptyWarnings)); - - Error E = RL.link(*ErrorOrObj); - if (Error NewE = handleErrors( - std::move(E), [&](std::unique_ptr<FileError> EC) -> Error { - return remarksErrorHandler(Obj, *this, std::move(EC)); - })) - return errorToErrorCode(std::move(NewE)); - - return *ObjectsForLinking.back(); - } - - return ErrorOrObj.getError(); -} - -bool DwarfLinkerForBinary::link(const DebugMap &Map) { - if (!createStreamer(Map.getTriple(), OutFile)) - return false; - - ObjectsForLinking.clear(); - ContextForLinking.clear(); - AddressMapForLinking.clear(); - - DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); - - DWARFLinker GeneralLinker(Streamer.get(), DwarfLinkerClient::Dsymutil); - - remarks::RemarkLinker RL; - if (!Options.RemarksPrependPath.empty()) - RL.setExternalFilePrependPath(Options.RemarksPrependPath); - GeneralLinker.setObjectPrefixMap(&Options.ObjectPrefixMap); - - std::function<StringRef(StringRef)> TranslationLambda = [&](StringRef Input) { - assert(Options.Translator); - return Options.Translator(Input); - }; - - GeneralLinker.setVerbosity(Options.Verbose); - GeneralLinker.setStatistics(Options.Statistics); - GeneralLinker.setNoOutput(Options.NoOutput); - GeneralLinker.setNoODR(Options.NoODR); - GeneralLinker.setUpdate(Options.Update); - GeneralLinker.setNumThreads(Options.Threads); - GeneralLinker.setAccelTableKind(Options.TheAccelTableKind); - GeneralLinker.setPrependPath(Options.PrependPath); - if (Options.Translator) - GeneralLinker.setStringsTranslator(TranslationLambda); - GeneralLinker.setWarningHandler( - [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { - reportWarning(Warning, Context, DIE); - }); - GeneralLinker.setErrorHandler( - [&](const Twine &Error, StringRef Context, const DWARFDie *) { - error(Error, Context); - }); - GeneralLinker.setObjFileLoader( - [&DebugMap, &RL, this](StringRef ContainerName, + Obj.getObjectFilename(), ContextForLinking.back().get(), + AddressMapForLinking.back().get(), + Obj.empty() ? Obj.getWarnings() : EmptyWarnings)); + + Error E = RL.link(*ErrorOrObj); + if (Error NewE = handleErrors( + std::move(E), [&](std::unique_ptr<FileError> EC) -> Error { + return remarksErrorHandler(Obj, *this, std::move(EC)); + })) + return errorToErrorCode(std::move(NewE)); + + return *ObjectsForLinking.back(); + } + + return ErrorOrObj.getError(); +} + +bool DwarfLinkerForBinary::link(const DebugMap &Map) { + if (!createStreamer(Map.getTriple(), OutFile)) + return false; + + ObjectsForLinking.clear(); + ContextForLinking.clear(); + AddressMapForLinking.clear(); + + DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath()); + + DWARFLinker GeneralLinker(Streamer.get(), DwarfLinkerClient::Dsymutil); + + remarks::RemarkLinker RL; + if (!Options.RemarksPrependPath.empty()) + RL.setExternalFilePrependPath(Options.RemarksPrependPath); + GeneralLinker.setObjectPrefixMap(&Options.ObjectPrefixMap); + + std::function<StringRef(StringRef)> TranslationLambda = [&](StringRef Input) { + assert(Options.Translator); + return Options.Translator(Input); + }; + + GeneralLinker.setVerbosity(Options.Verbose); + GeneralLinker.setStatistics(Options.Statistics); + GeneralLinker.setNoOutput(Options.NoOutput); + GeneralLinker.setNoODR(Options.NoODR); + GeneralLinker.setUpdate(Options.Update); + GeneralLinker.setNumThreads(Options.Threads); + GeneralLinker.setAccelTableKind(Options.TheAccelTableKind); + GeneralLinker.setPrependPath(Options.PrependPath); + if (Options.Translator) + GeneralLinker.setStringsTranslator(TranslationLambda); + GeneralLinker.setWarningHandler( + [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) { + reportWarning(Warning, Context, DIE); + }); + GeneralLinker.setErrorHandler( + [&](const Twine &Error, StringRef Context, const DWARFDie *) { + error(Error, Context); + }); + GeneralLinker.setObjFileLoader( + [&DebugMap, &RL, this](StringRef ContainerName, StringRef Path) -> ErrorOr<DWARFFile &> { - auto &Obj = DebugMap.addDebugMapObject( - Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO); - - if (auto ErrorOrObj = loadObject(Obj, DebugMap, RL)) { - return *ErrorOrObj; - } else { - // Try and emit more helpful warnings by applying some heuristics. - StringRef ObjFile = ContainerName; - bool IsClangModule = sys::path::extension(Path).equals(".pcm"); - bool IsArchive = ObjFile.endswith(")"); - - if (IsClangModule) { - StringRef ModuleCacheDir = sys::path::parent_path(Path); - if (sys::fs::exists(ModuleCacheDir)) { - // If the module's parent directory exists, we assume that the - // module cache has expired and was pruned by clang. A more - // adventurous dsymutil would invoke clang to rebuild the module - // now. - if (!ModuleCacheHintDisplayed) { - WithColor::note() - << "The clang module cache may have expired since " - "this object file was built. Rebuilding the " - "object file will rebuild the module cache.\n"; - ModuleCacheHintDisplayed = true; - } - } else if (IsArchive) { - // If the module cache directory doesn't exist at all and the - // object file is inside a static library, we assume that the - // static library was built on a different machine. We don't want - // to discourage module debugging for convenience libraries within - // a project though. - if (!ArchiveHintDisplayed) { - WithColor::note() - << "Linking a static library that was built with " - "-gmodules, but the module cache was not found. " - "Redistributable static libraries should never be " - "built with module debugging enabled. The debug " - "experience will be degraded due to incomplete " - "debug information.\n"; - ArchiveHintDisplayed = true; - } - } - } - - return ErrorOrObj.getError(); - } - - llvm_unreachable("Unhandled DebugMap object"); - }); - GeneralLinker.setSwiftInterfacesMap(&ParseableSwiftInterfaces); - - for (const auto &Obj : Map.objects()) { - // N_AST objects (swiftmodule files) should get dumped directly into the - // appropriate DWARF section. - if (Obj->getType() == MachO::N_AST) { - if (Options.Verbose) - outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; - - StringRef File = Obj->getObjectFilename(); - auto ErrorOrMem = MemoryBuffer::getFile(File); - if (!ErrorOrMem) { - warn("Could not open '" + File + "'\n"); - continue; - } - sys::fs::file_status Stat; - if (auto Err = sys::fs::status(File, Stat)) { - warn(Err.message()); - continue; - } - if (!Options.NoTimestamp) { - // The modification can have sub-second precision so we need to cast - // away the extra precision that's not present in the debug map. - auto ModificationTime = - std::chrono::time_point_cast<std::chrono::seconds>( - Stat.getLastModificationTime()); - if (ModificationTime != Obj->getTimestamp()) { - // Not using the helper here as we can easily stream TimePoint<>. + auto &Obj = DebugMap.addDebugMapObject( + Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO); + + if (auto ErrorOrObj = loadObject(Obj, DebugMap, RL)) { + return *ErrorOrObj; + } else { + // Try and emit more helpful warnings by applying some heuristics. + StringRef ObjFile = ContainerName; + bool IsClangModule = sys::path::extension(Path).equals(".pcm"); + bool IsArchive = ObjFile.endswith(")"); + + if (IsClangModule) { + StringRef ModuleCacheDir = sys::path::parent_path(Path); + if (sys::fs::exists(ModuleCacheDir)) { + // If the module's parent directory exists, we assume that the + // module cache has expired and was pruned by clang. A more + // adventurous dsymutil would invoke clang to rebuild the module + // now. + if (!ModuleCacheHintDisplayed) { + WithColor::note() + << "The clang module cache may have expired since " + "this object file was built. Rebuilding the " + "object file will rebuild the module cache.\n"; + ModuleCacheHintDisplayed = true; + } + } else if (IsArchive) { + // If the module cache directory doesn't exist at all and the + // object file is inside a static library, we assume that the + // static library was built on a different machine. We don't want + // to discourage module debugging for convenience libraries within + // a project though. + if (!ArchiveHintDisplayed) { + WithColor::note() + << "Linking a static library that was built with " + "-gmodules, but the module cache was not found. " + "Redistributable static libraries should never be " + "built with module debugging enabled. The debug " + "experience will be degraded due to incomplete " + "debug information.\n"; + ArchiveHintDisplayed = true; + } + } + } + + return ErrorOrObj.getError(); + } + + llvm_unreachable("Unhandled DebugMap object"); + }); + GeneralLinker.setSwiftInterfacesMap(&ParseableSwiftInterfaces); + + for (const auto &Obj : Map.objects()) { + // N_AST objects (swiftmodule files) should get dumped directly into the + // appropriate DWARF section. + if (Obj->getType() == MachO::N_AST) { + if (Options.Verbose) + outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; + + StringRef File = Obj->getObjectFilename(); + auto ErrorOrMem = MemoryBuffer::getFile(File); + if (!ErrorOrMem) { + warn("Could not open '" + File + "'\n"); + continue; + } + sys::fs::file_status Stat; + if (auto Err = sys::fs::status(File, Stat)) { + warn(Err.message()); + continue; + } + if (!Options.NoTimestamp) { + // The modification can have sub-second precision so we need to cast + // away the extra precision that's not present in the debug map. + auto ModificationTime = + std::chrono::time_point_cast<std::chrono::seconds>( + Stat.getLastModificationTime()); + if (ModificationTime != Obj->getTimestamp()) { + // Not using the helper here as we can easily stream TimePoint<>. WithColor::warning() << File << ": timestamp mismatch between swift interface file (" << sys::TimePoint<>(Obj->getTimestamp()) << ") and debug map (" << sys::TimePoint<>(Obj->getTimestamp()) << ")\n"; - continue; - } - } - - // Copy the module into the .swift_ast section. - if (!Options.NoOutput) - Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer()); - - continue; - } - - if (auto ErrorOrObj = loadObject(*Obj, Map, RL)) - GeneralLinker.addObjectFile(*ErrorOrObj); - else { + continue; + } + } + + // Copy the module into the .swift_ast section. + if (!Options.NoOutput) + Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer()); + + continue; + } + + if (auto ErrorOrObj = loadObject(*Obj, Map, RL)) + GeneralLinker.addObjectFile(*ErrorOrObj); + else { ObjectsForLinking.push_back(std::make_unique<DWARFFile>( - Obj->getObjectFilename(), nullptr, nullptr, - Obj->empty() ? Obj->getWarnings() : EmptyWarnings)); - GeneralLinker.addObjectFile(*ObjectsForLinking.back()); - } - } - - // link debug info for loaded object files. - GeneralLinker.link(); - - StringRef ArchName = Map.getTriple().getArchName(); - if (Error E = emitRemarks(Options, Map.getBinaryPath(), ArchName, RL)) - return error(toString(std::move(E))); - - if (Options.NoOutput) - return true; - - if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { - StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); - if (auto E = - copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options)) - return error(toString(std::move(E))); - } - - if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() && - Options.FileType == OutputFileType::Object) - return MachOUtils::generateDsymCompanion( - Options.VFS, Map, Options.Translator, - *Streamer->getAsmPrinter().OutStreamer, OutFile); - - Streamer->finish(); - return true; -} - -static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) { - switch (Arch) { - case Triple::x86: - return RelocType == MachO::GENERIC_RELOC_SECTDIFF || - RelocType == MachO::GENERIC_RELOC_LOCAL_SECTDIFF; - case Triple::x86_64: - return RelocType == MachO::X86_64_RELOC_SUBTRACTOR; - case Triple::arm: - case Triple::thumb: - return RelocType == MachO::ARM_RELOC_SECTDIFF || - RelocType == MachO::ARM_RELOC_LOCAL_SECTDIFF || - RelocType == MachO::ARM_RELOC_HALF || - RelocType == MachO::ARM_RELOC_HALF_SECTDIFF; - case Triple::aarch64: - return RelocType == MachO::ARM64_RELOC_SUBTRACTOR; - default: - return false; - } -} - -/// Iterate over the relocations of the given \p Section and -/// store the ones that correspond to debug map entries into the -/// ValidRelocs array. -void DwarfLinkerForBinary::AddressManager::findValidRelocsMachO( - const object::SectionRef &Section, const object::MachOObjectFile &Obj, + Obj->getObjectFilename(), nullptr, nullptr, + Obj->empty() ? Obj->getWarnings() : EmptyWarnings)); + GeneralLinker.addObjectFile(*ObjectsForLinking.back()); + } + } + + // link debug info for loaded object files. + GeneralLinker.link(); + + StringRef ArchName = Map.getTriple().getArchName(); + if (Error E = emitRemarks(Options, Map.getBinaryPath(), ArchName, RL)) + return error(toString(std::move(E))); + + if (Options.NoOutput) + return true; + + if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { + StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); + if (auto E = + copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options)) + return error(toString(std::move(E))); + } + + if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() && + Options.FileType == OutputFileType::Object) + return MachOUtils::generateDsymCompanion( + Options.VFS, Map, Options.Translator, + *Streamer->getAsmPrinter().OutStreamer, OutFile); + + Streamer->finish(); + return true; +} + +static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) { + switch (Arch) { + case Triple::x86: + return RelocType == MachO::GENERIC_RELOC_SECTDIFF || + RelocType == MachO::GENERIC_RELOC_LOCAL_SECTDIFF; + case Triple::x86_64: + return RelocType == MachO::X86_64_RELOC_SUBTRACTOR; + case Triple::arm: + case Triple::thumb: + return RelocType == MachO::ARM_RELOC_SECTDIFF || + RelocType == MachO::ARM_RELOC_LOCAL_SECTDIFF || + RelocType == MachO::ARM_RELOC_HALF || + RelocType == MachO::ARM_RELOC_HALF_SECTDIFF; + case Triple::aarch64: + return RelocType == MachO::ARM64_RELOC_SUBTRACTOR; + default: + return false; + } +} + +/// Iterate over the relocations of the given \p Section and +/// store the ones that correspond to debug map entries into the +/// ValidRelocs array. +void DwarfLinkerForBinary::AddressManager::findValidRelocsMachO( + const object::SectionRef &Section, const object::MachOObjectFile &Obj, const DebugMapObject &DMO, std::vector<ValidReloc> &ValidRelocs) { - Expected<StringRef> ContentsOrErr = Section.getContents(); - if (!ContentsOrErr) { - consumeError(ContentsOrErr.takeError()); - Linker.reportWarning("error reading section", DMO.getObjectFilename()); - return; - } - DataExtractor Data(*ContentsOrErr, Obj.isLittleEndian(), 0); - bool SkipNext = false; - - for (const object::RelocationRef &Reloc : Section.relocations()) { - if (SkipNext) { - SkipNext = false; - continue; - } - - object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl(); - MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef); - - if (isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc), - Obj.getArch())) { - SkipNext = true; + Expected<StringRef> ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) { + consumeError(ContentsOrErr.takeError()); + Linker.reportWarning("error reading section", DMO.getObjectFilename()); + return; + } + DataExtractor Data(*ContentsOrErr, Obj.isLittleEndian(), 0); + bool SkipNext = false; + + for (const object::RelocationRef &Reloc : Section.relocations()) { + if (SkipNext) { + SkipNext = false; + continue; + } + + object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl(); + MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef); + + if (isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc), + Obj.getArch())) { + SkipNext = true; Linker.reportWarning("unsupported relocation in " + *Section.getName() + " section.", - DMO.getObjectFilename()); - continue; - } - - unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); - uint64_t Offset64 = Reloc.getOffset(); - if ((RelocSize != 4 && RelocSize != 8)) { + DMO.getObjectFilename()); + continue; + } + + unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); + uint64_t Offset64 = Reloc.getOffset(); + if ((RelocSize != 4 && RelocSize != 8)) { Linker.reportWarning("unsupported relocation in " + *Section.getName() + " section.", - DMO.getObjectFilename()); - continue; - } - uint64_t OffsetCopy = Offset64; - // Mach-o uses REL relocations, the addend is at the relocation offset. - uint64_t Addend = Data.getUnsigned(&OffsetCopy, RelocSize); - uint64_t SymAddress; - int64_t SymOffset; - - if (Obj.isRelocationScattered(MachOReloc)) { - // The address of the base symbol for scattered relocations is - // stored in the reloc itself. The actual addend will store the - // base address plus the offset. - SymAddress = Obj.getScatteredRelocationValue(MachOReloc); - SymOffset = int64_t(Addend) - SymAddress; - } else { - SymAddress = Addend; - SymOffset = 0; - } - - auto Sym = Reloc.getSymbol(); - if (Sym != Obj.symbol_end()) { - Expected<StringRef> SymbolName = Sym->getName(); - if (!SymbolName) { - consumeError(SymbolName.takeError()); - Linker.reportWarning("error getting relocation symbol name.", - DMO.getObjectFilename()); - continue; - } - if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) - ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping); - } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) { - // Do not store the addend. The addend was the address of the symbol in - // the object file, the address in the binary that is stored in the debug - // map doesn't need to be offset. - ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping); - } - } -} - -/// Dispatch the valid relocation finding logic to the -/// appropriate handler depending on the object file format. -bool DwarfLinkerForBinary::AddressManager::findValidRelocs( - const object::SectionRef &Section, const object::ObjectFile &Obj, + DMO.getObjectFilename()); + continue; + } + uint64_t OffsetCopy = Offset64; + // Mach-o uses REL relocations, the addend is at the relocation offset. + uint64_t Addend = Data.getUnsigned(&OffsetCopy, RelocSize); + uint64_t SymAddress; + int64_t SymOffset; + + if (Obj.isRelocationScattered(MachOReloc)) { + // The address of the base symbol for scattered relocations is + // stored in the reloc itself. The actual addend will store the + // base address plus the offset. + SymAddress = Obj.getScatteredRelocationValue(MachOReloc); + SymOffset = int64_t(Addend) - SymAddress; + } else { + SymAddress = Addend; + SymOffset = 0; + } + + auto Sym = Reloc.getSymbol(); + if (Sym != Obj.symbol_end()) { + Expected<StringRef> SymbolName = Sym->getName(); + if (!SymbolName) { + consumeError(SymbolName.takeError()); + Linker.reportWarning("error getting relocation symbol name.", + DMO.getObjectFilename()); + continue; + } + if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) + ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping); + } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) { + // Do not store the addend. The addend was the address of the symbol in + // the object file, the address in the binary that is stored in the debug + // map doesn't need to be offset. + ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping); + } + } +} + +/// Dispatch the valid relocation finding logic to the +/// appropriate handler depending on the object file format. +bool DwarfLinkerForBinary::AddressManager::findValidRelocs( + const object::SectionRef &Section, const object::ObjectFile &Obj, const DebugMapObject &DMO, std::vector<ValidReloc> &Relocs) { - // Dispatch to the right handler depending on the file type. - if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) + // Dispatch to the right handler depending on the file type. + if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) findValidRelocsMachO(Section, *MachOObj, DMO, Relocs); - else - Linker.reportWarning(Twine("unsupported object file type: ") + - Obj.getFileName(), - DMO.getObjectFilename()); + else + Linker.reportWarning(Twine("unsupported object file type: ") + + Obj.getFileName(), + DMO.getObjectFilename()); if (Relocs.empty()) - return false; - - // Sort the relocations by offset. We will walk the DIEs linearly in - // the file, this allows us to just keep an index in the relocation - // array that we advance during our walk, rather than resorting to - // some associative container. See DwarfLinkerForBinary::NextValidReloc. + return false; + + // Sort the relocations by offset. We will walk the DIEs linearly in + // the file, this allows us to just keep an index in the relocation + // array that we advance during our walk, rather than resorting to + // some associative container. See DwarfLinkerForBinary::NextValidReloc. llvm::sort(Relocs); - return true; -} - + return true; +} + /// Look for relocations in the debug_info and debug_addr section that match /// entries in the debug map. These relocations will drive the Dwarf link by /// indicating which DIEs refer to symbols present in the linked binary. -/// \returns whether there are any valid relocations in the debug info. +/// \returns whether there are any valid relocations in the debug info. bool DwarfLinkerForBinary::AddressManager::findValidRelocsInDebugSections( - const object::ObjectFile &Obj, const DebugMapObject &DMO) { - // Find the debug_info section. + const object::ObjectFile &Obj, const DebugMapObject &DMO) { + // Find the debug_info section. bool FoundValidRelocs = false; - for (const object::SectionRef &Section : Obj.sections()) { - StringRef SectionName; - if (Expected<StringRef> NameOrErr = Section.getName()) - SectionName = *NameOrErr; - else - consumeError(NameOrErr.takeError()); - - SectionName = SectionName.substr(SectionName.find_first_not_of("._")); + for (const object::SectionRef &Section : Obj.sections()) { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Section.getName()) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + SectionName = SectionName.substr(SectionName.find_first_not_of("._")); if (SectionName == "debug_info") FoundValidRelocs |= findValidRelocs(Section, Obj, DMO, ValidDebugInfoRelocs); if (SectionName == "debug_addr") FoundValidRelocs |= findValidRelocs(Section, Obj, DMO, ValidDebugAddrRelocs); - } + } return FoundValidRelocs; -} - +} + bool DwarfLinkerForBinary::AddressManager::hasValidDebugAddrRelocationAt( uint64_t Offset) { auto It = std::lower_bound(ValidDebugAddrRelocs.begin(), @@ -619,44 +619,44 @@ bool DwarfLinkerForBinary::AddressManager::hasValidDebugAddrRelocationAt( } bool DwarfLinkerForBinary::AddressManager::hasValidDebugInfoRelocationAt( - uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info) { - assert(NextValidReloc == 0 || + uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info) { + assert(NextValidReloc == 0 || StartOffset > ValidDebugInfoRelocs[NextValidReloc - 1].Offset); if (NextValidReloc >= ValidDebugInfoRelocs.size()) - return false; - + return false; + uint64_t RelocOffset = ValidDebugInfoRelocs[NextValidReloc].Offset; - - // We might need to skip some relocs that we didn't consider. For - // example the high_pc of a discarded DIE might contain a reloc that - // is in the list because it actually corresponds to the start of a - // function that is in the debug map. + + // We might need to skip some relocs that we didn't consider. For + // example the high_pc of a discarded DIE might contain a reloc that + // is in the list because it actually corresponds to the start of a + // function that is in the debug map. while (RelocOffset < StartOffset && NextValidReloc < ValidDebugInfoRelocs.size() - 1) RelocOffset = ValidDebugInfoRelocs[++NextValidReloc].Offset; - - if (RelocOffset < StartOffset || RelocOffset >= EndOffset) - return false; - + + if (RelocOffset < StartOffset || RelocOffset >= EndOffset) + return false; + const auto &ValidReloc = ValidDebugInfoRelocs[NextValidReloc++]; - const auto &Mapping = ValidReloc.Mapping->getValue(); - const uint64_t BinaryAddress = Mapping.BinaryAddress; - const uint64_t ObjectAddress = Mapping.ObjectAddress - ? uint64_t(*Mapping.ObjectAddress) - : std::numeric_limits<uint64_t>::max(); - if (Linker.Options.Verbose) - outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() - << "\t" - << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, - BinaryAddress); - - Info.AddrAdjust = BinaryAddress + ValidReloc.Addend; - if (Mapping.ObjectAddress) - Info.AddrAdjust -= ObjectAddress; - Info.InDebugMap = true; - return true; -} - + const auto &Mapping = ValidReloc.Mapping->getValue(); + const uint64_t BinaryAddress = Mapping.BinaryAddress; + const uint64_t ObjectAddress = Mapping.ObjectAddress + ? uint64_t(*Mapping.ObjectAddress) + : std::numeric_limits<uint64_t>::max(); + if (Linker.Options.Verbose) + outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() + << "\t" + << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress, + BinaryAddress); + + Info.AddrAdjust = BinaryAddress + ValidReloc.Addend; + if (Mapping.ObjectAddress) + Info.AddrAdjust -= ObjectAddress; + Info.InDebugMap = true; + return true; +} + /// Get the starting and ending (exclusive) offset for the /// attribute with index \p Idx descibed by \p Abbrev. \p Offset is /// supposed to point to the position of the first attribute described @@ -722,51 +722,51 @@ bool DwarfLinkerForBinary::AddressManager::hasLiveAddressRange( return false; } -/// Apply the valid relocations found by findValidRelocs() to -/// the buffer \p Data, taking into account that Data is at \p BaseOffset -/// in the debug_info section. -/// -/// Like for findValidRelocs(), this function must be called with -/// monotonic \p BaseOffset values. -/// -/// \returns whether any reloc has been applied. -bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( - MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) { - assert(areRelocationsResolved()); - assert((NextValidReloc == 0 || +/// Apply the valid relocations found by findValidRelocs() to +/// the buffer \p Data, taking into account that Data is at \p BaseOffset +/// in the debug_info section. +/// +/// Like for findValidRelocs(), this function must be called with +/// monotonic \p BaseOffset values. +/// +/// \returns whether any reloc has been applied. +bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( + MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) { + assert(areRelocationsResolved()); + assert((NextValidReloc == 0 || BaseOffset > ValidDebugInfoRelocs[NextValidReloc - 1].Offset) && - "BaseOffset should only be increasing."); + "BaseOffset should only be increasing."); if (NextValidReloc >= ValidDebugInfoRelocs.size()) - return false; - - // Skip relocs that haven't been applied. + return false; + + // Skip relocs that haven't been applied. while (NextValidReloc < ValidDebugInfoRelocs.size() && ValidDebugInfoRelocs[NextValidReloc].Offset < BaseOffset) - ++NextValidReloc; - - bool Applied = false; - uint64_t EndOffset = BaseOffset + Data.size(); + ++NextValidReloc; + + bool Applied = false; + uint64_t EndOffset = BaseOffset + Data.size(); while (NextValidReloc < ValidDebugInfoRelocs.size() && ValidDebugInfoRelocs[NextValidReloc].Offset >= BaseOffset && ValidDebugInfoRelocs[NextValidReloc].Offset < EndOffset) { const auto &ValidReloc = ValidDebugInfoRelocs[NextValidReloc++]; - assert(ValidReloc.Offset - BaseOffset < Data.size()); - assert(ValidReloc.Offset - BaseOffset + ValidReloc.Size <= Data.size()); - char Buf[8]; - uint64_t Value = ValidReloc.Mapping->getValue().BinaryAddress; - Value += ValidReloc.Addend; - for (unsigned I = 0; I != ValidReloc.Size; ++I) { - unsigned Index = IsLittleEndian ? I : (ValidReloc.Size - I - 1); - Buf[I] = uint8_t(Value >> (Index * 8)); - } - assert(ValidReloc.Size <= sizeof(Buf)); - memcpy(&Data[ValidReloc.Offset - BaseOffset], Buf, ValidReloc.Size); - Applied = true; - } - - return Applied; -} - + assert(ValidReloc.Offset - BaseOffset < Data.size()); + assert(ValidReloc.Offset - BaseOffset + ValidReloc.Size <= Data.size()); + char Buf[8]; + uint64_t Value = ValidReloc.Mapping->getValue().BinaryAddress; + Value += ValidReloc.Addend; + for (unsigned I = 0; I != ValidReloc.Size; ++I) { + unsigned Index = IsLittleEndian ? I : (ValidReloc.Size - I - 1); + Buf[I] = uint8_t(Value >> (Index * 8)); + } + assert(ValidReloc.Size <= sizeof(Buf)); + memcpy(&Data[ValidReloc.Offset - BaseOffset], Buf, ValidReloc.Size); + Applied = true; + } + + return Applied; +} + llvm::Expected<uint64_t> DwarfLinkerForBinary::AddressManager::relocateIndexedAddr(uint64_t Offset) { auto It = std::lower_bound(ValidDebugAddrRelocs.begin(), @@ -778,11 +778,11 @@ DwarfLinkerForBinary::AddressManager::relocateIndexedAddr(uint64_t Offset) { return It->Mapping->getValue().BinaryAddress + It->Addend; } -bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, - const DebugMap &DM, LinkOptions Options) { - DwarfLinkerForBinary Linker(OutFile, BinHolder, std::move(Options)); - return Linker.link(DM); -} - -} // namespace dsymutil -} // namespace llvm +bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, + const DebugMap &DM, LinkOptions Options) { + DwarfLinkerForBinary Linker(OutFile, BinHolder, std::move(Options)); + return Linker.link(DM); +} + +} // namespace dsymutil +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.h b/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.h index c6c07d689f..849d2c7fa5 100644 --- a/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.h +++ b/contrib/libs/llvm12/tools/dsymutil/DwarfLinkerForBinary.h @@ -1,154 +1,154 @@ -//===- tools/dsymutil/DwarfLinkerForBinary.h --------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H -#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H - -#include "BinaryHolder.h" -#include "DebugMap.h" -#include "LinkUtils.h" -#include "llvm/DWARFLinker/DWARFLinker.h" -#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" -#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" -#include "llvm/DWARFLinker/DWARFStreamer.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/Remarks/RemarkFormat.h" -#include "llvm/Remarks/RemarkLinker.h" - -namespace llvm { -namespace dsymutil { - -/// The core of the Dsymutil Dwarf linking logic. -/// -/// The link of the dwarf information from the object files will be -/// driven by DWARFLinker. DwarfLinkerForBinary reads DebugMap objects -/// and pass information to the DWARFLinker. DWARFLinker -/// optimizes DWARF taking into account valid relocations. -/// Finally, optimized DWARF is passed to DwarfLinkerForBinary through -/// DWARFEmitter interface. -class DwarfLinkerForBinary { -public: - DwarfLinkerForBinary(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, - LinkOptions Options) - : OutFile(OutFile), BinHolder(BinHolder), Options(std::move(Options)) {} - - /// Link the contents of the DebugMap. - bool link(const DebugMap &); - - void reportWarning(const Twine &Warning, StringRef Context, - const DWARFDie *DIE = nullptr) const; - - /// Flags passed to DwarfLinker::lookForDIEsToKeep - enum TraversalFlags { - TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. - TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope. - TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. - TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. - TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents. - TF_SkipPC = 1 << 5, ///< Skip all location attributes. - }; - -private: - - /// Keeps track of relocations. - class AddressManager : public AddressesMap { - struct ValidReloc { - uint64_t Offset; - uint32_t Size; - uint64_t Addend; - const DebugMapObject::DebugMapEntry *Mapping; - - ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend, - const DebugMapObject::DebugMapEntry *Mapping) - : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} - - bool operator<(const ValidReloc &RHS) const { - return Offset < RHS.Offset; - } +//===- tools/dsymutil/DwarfLinkerForBinary.h --------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H +#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H + +#include "BinaryHolder.h" +#include "DebugMap.h" +#include "LinkUtils.h" +#include "llvm/DWARFLinker/DWARFLinker.h" +#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" +#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Remarks/RemarkLinker.h" + +namespace llvm { +namespace dsymutil { + +/// The core of the Dsymutil Dwarf linking logic. +/// +/// The link of the dwarf information from the object files will be +/// driven by DWARFLinker. DwarfLinkerForBinary reads DebugMap objects +/// and pass information to the DWARFLinker. DWARFLinker +/// optimizes DWARF taking into account valid relocations. +/// Finally, optimized DWARF is passed to DwarfLinkerForBinary through +/// DWARFEmitter interface. +class DwarfLinkerForBinary { +public: + DwarfLinkerForBinary(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, + LinkOptions Options) + : OutFile(OutFile), BinHolder(BinHolder), Options(std::move(Options)) {} + + /// Link the contents of the DebugMap. + bool link(const DebugMap &); + + void reportWarning(const Twine &Warning, StringRef Context, + const DWARFDie *DIE = nullptr) const; + + /// Flags passed to DwarfLinker::lookForDIEsToKeep + enum TraversalFlags { + TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. + TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope. + TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. + TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. + TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents. + TF_SkipPC = 1 << 5, ///< Skip all location attributes. + }; + +private: + + /// Keeps track of relocations. + class AddressManager : public AddressesMap { + struct ValidReloc { + uint64_t Offset; + uint32_t Size; + uint64_t Addend; + const DebugMapObject::DebugMapEntry *Mapping; + + ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend, + const DebugMapObject::DebugMapEntry *Mapping) + : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} + + bool operator<(const ValidReloc &RHS) const { + return Offset < RHS.Offset; + } bool operator<(uint64_t RHS) const { return Offset < RHS; } - }; - - const DwarfLinkerForBinary &Linker; - - /// The valid relocations for the current DebugMapObject. - /// This vector is sorted by relocation offset. + }; + + const DwarfLinkerForBinary &Linker; + + /// The valid relocations for the current DebugMapObject. + /// This vector is sorted by relocation offset. /// { std::vector<ValidReloc> ValidDebugInfoRelocs; std::vector<ValidReloc> ValidDebugAddrRelocs; /// } - - /// Index into ValidRelocs of the next relocation to consider. As we walk - /// the DIEs in acsending file offset and as ValidRelocs is sorted by file - /// offset, keeping this index up to date is all we have to do to have a - /// cheap lookup during the root DIE selection and during DIE cloning. - unsigned NextValidReloc = 0; - - RangesTy AddressRanges; - - public: - AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj, - const DebugMapObject &DMO) - : Linker(Linker) { + + /// Index into ValidRelocs of the next relocation to consider. As we walk + /// the DIEs in acsending file offset and as ValidRelocs is sorted by file + /// offset, keeping this index up to date is all we have to do to have a + /// cheap lookup during the root DIE selection and during DIE cloning. + unsigned NextValidReloc = 0; + + RangesTy AddressRanges; + + public: + AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj, + const DebugMapObject &DMO) + : Linker(Linker) { findValidRelocsInDebugSections(Obj, DMO); - - // Iterate over the debug map entries and put all the ones that are - // functions (because they have a size) into the Ranges map. This map is - // very similar to the FunctionRanges that are stored in each unit, with 2 - // notable differences: - // - // 1. Obviously this one is global, while the other ones are per-unit. - // - // 2. This one contains not only the functions described in the DIE - // tree, but also the ones that are only in the debug map. - // - // The latter information is required to reproduce dsymutil's logic while - // linking line tables. The cases where this information matters look like - // bugs that need to be investigated, but for now we need to reproduce - // dsymutil's behavior. - // FIXME: Once we understood exactly if that information is needed, - // maybe totally remove this (or try to use it to do a real - // -gline-tables-only on Darwin. - for (const auto &Entry : DMO.symbols()) { - const auto &Mapping = Entry.getValue(); - if (Mapping.Size && Mapping.ObjectAddress) - AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( - *Mapping.ObjectAddress + Mapping.Size, - int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); - } - } - virtual ~AddressManager() override { clear(); } - - virtual bool areRelocationsResolved() const override { return true; } - - bool hasValidRelocs(bool ResetRelocsPtr = true) override { - if (ResetRelocsPtr) - NextValidReloc = 0; + + // Iterate over the debug map entries and put all the ones that are + // functions (because they have a size) into the Ranges map. This map is + // very similar to the FunctionRanges that are stored in each unit, with 2 + // notable differences: + // + // 1. Obviously this one is global, while the other ones are per-unit. + // + // 2. This one contains not only the functions described in the DIE + // tree, but also the ones that are only in the debug map. + // + // The latter information is required to reproduce dsymutil's logic while + // linking line tables. The cases where this information matters look like + // bugs that need to be investigated, but for now we need to reproduce + // dsymutil's behavior. + // FIXME: Once we understood exactly if that information is needed, + // maybe totally remove this (or try to use it to do a real + // -gline-tables-only on Darwin. + for (const auto &Entry : DMO.symbols()) { + const auto &Mapping = Entry.getValue(); + if (Mapping.Size && Mapping.ObjectAddress) + AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( + *Mapping.ObjectAddress + Mapping.Size, + int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + } + } + virtual ~AddressManager() override { clear(); } + + virtual bool areRelocationsResolved() const override { return true; } + + bool hasValidRelocs(bool ResetRelocsPtr = true) override { + if (ResetRelocsPtr) + NextValidReloc = 0; return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty(); - } - - /// \defgroup FindValidRelocations Translate debug map into a list - /// of relevant relocations - /// - /// @{ + } + + /// \defgroup FindValidRelocations Translate debug map into a list + /// of relevant relocations + /// + /// @{ bool findValidRelocsInDebugSections(const object::ObjectFile &Obj, const DebugMapObject &DMO); - - bool findValidRelocs(const object::SectionRef &Section, - const object::ObjectFile &Obj, + + bool findValidRelocs(const object::SectionRef &Section, + const object::ObjectFile &Obj, const DebugMapObject &DMO, std::vector<ValidReloc> &ValidRelocs); - - void findValidRelocsMachO(const object::SectionRef &Section, - const object::MachOObjectFile &Obj, + + void findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, const DebugMapObject &DMO, std::vector<ValidReloc> &ValidRelocs); - /// @} - + /// @} + /// Checks that there is a relocation in the debug_addr section against a /// debug map entry between \p StartOffset and \p NextOffset. /// @@ -157,7 +157,7 @@ private: /// \returns true and sets Info.InDebugMap if it is the case. bool hasValidDebugInfoRelocationAt(uint64_t StartOffset, uint64_t EndOffset, CompileUnit::DIEInfo &Info); - + /// Checks that there is a relocation in the debug_addr section against a /// debug map entry at the given offset. bool hasValidDebugAddrRelocationAt(uint64_t Offset); @@ -167,54 +167,54 @@ private: bool hasLiveAddressRange(const DWARFDie &DIE, CompileUnit::DIEInfo &Info) override; - bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset, - bool IsLittleEndian) override; - + bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset, + bool IsLittleEndian) override; + llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t Offset) override; - RangesTy &getValidAddressRanges() override { return AddressRanges; } - - void clear() override { - AddressRanges.clear(); + RangesTy &getValidAddressRanges() override { return AddressRanges; } + + void clear() override { + AddressRanges.clear(); ValidDebugInfoRelocs.clear(); ValidDebugAddrRelocs.clear(); - NextValidReloc = 0; - } - }; - -private: - /// \defgroup Helpers Various helper methods. - /// - /// @{ - bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); - - /// Attempt to load a debug object from disk. - ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj, - const Triple &triple); + NextValidReloc = 0; + } + }; + +private: + /// \defgroup Helpers Various helper methods. + /// + /// @{ + bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); + + /// Attempt to load a debug object from disk. + ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj, + const Triple &triple); ErrorOr<DWARFFile &> loadObject(const DebugMapObject &Obj, - const DebugMap &DebugMap, - remarks::RemarkLinker &RL); - - raw_fd_ostream &OutFile; - BinaryHolder &BinHolder; - LinkOptions Options; - std::unique_ptr<DwarfStreamer> Streamer; + const DebugMap &DebugMap, + remarks::RemarkLinker &RL); + + raw_fd_ostream &OutFile; + BinaryHolder &BinHolder; + LinkOptions Options; + std::unique_ptr<DwarfStreamer> Streamer; std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking; - std::vector<std::unique_ptr<DWARFContext>> ContextForLinking; - std::vector<std::unique_ptr<AddressManager>> AddressMapForLinking; - std::vector<std::string> EmptyWarnings; - - /// A list of all .swiftinterface files referenced by the debug - /// info, mapping Module name to path on disk. The entries need to - /// be uniqued and sorted and there are only few entries expected - /// per compile unit, which is why this is a std::map. - std::map<std::string, std::string> ParseableSwiftInterfaces; - - bool ModuleCacheHintDisplayed = false; - bool ArchiveHintDisplayed = false; -}; - -} // end namespace dsymutil -} // end namespace llvm - -#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H + std::vector<std::unique_ptr<DWARFContext>> ContextForLinking; + std::vector<std::unique_ptr<AddressManager>> AddressMapForLinking; + std::vector<std::string> EmptyWarnings; + + /// A list of all .swiftinterface files referenced by the debug + /// info, mapping Module name to path on disk. The entries need to + /// be uniqued and sorted and there are only few entries expected + /// per compile unit, which is why this is a std::map. + std::map<std::string, std::string> ParseableSwiftInterfaces; + + bool ModuleCacheHintDisplayed = false; + bool ArchiveHintDisplayed = false; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H diff --git a/contrib/libs/llvm12/tools/dsymutil/LinkUtils.h b/contrib/libs/llvm12/tools/dsymutil/LinkUtils.h index 52b36353c6..5b2e372547 100644 --- a/contrib/libs/llvm12/tools/dsymutil/LinkUtils.h +++ b/contrib/libs/llvm12/tools/dsymutil/LinkUtils.h @@ -1,107 +1,107 @@ -//===- tools/dsymutil/LinkUtils.h - Dwarf linker utilities ------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H -#define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H - -#include "SymbolMap.h" - -#include "llvm/ADT/Twine.h" -#include "llvm/Remarks/RemarkFormat.h" -#include "llvm/Support/VirtualFileSystem.h" -#include "llvm/Support/WithColor.h" - -#include "llvm/DWARFLinker/DWARFLinker.h" -#include "llvm/DWARFLinker/DWARFStreamer.h" -#include <string> - -namespace llvm { -namespace dsymutil { - -struct LinkOptions { - /// Verbosity - bool Verbose = false; - - /// Statistics - bool Statistics = false; - - /// Skip emitting output - bool NoOutput = false; - - /// Do not unique types according to ODR - bool NoODR = false; - - /// Update - bool Update = false; - - /// Minimize - bool Minimize = false; - - /// Do not check swiftmodule timestamp - bool NoTimestamp = false; - - /// Number of threads. - unsigned Threads = 1; - - // Output file type. - OutputFileType FileType = OutputFileType::Object; - - /// The accelerator table kind - AccelTableKind TheAccelTableKind; - - /// -oso-prepend-path - std::string PrependPath; - - /// The -object-prefix-map. - std::map<std::string, std::string> ObjectPrefixMap; - - /// The Resources directory in the .dSYM bundle. - Optional<std::string> ResourceDir; - - /// Symbol map translator. - SymbolMapTranslator Translator; - - /// Virtual File System. - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = - vfs::getRealFileSystem(); - - /// Fields used for linking and placing remarks into the .dSYM bundle. - /// @{ - - /// Number of debug maps processed in total. - unsigned NumDebugMaps = 0; - - /// -remarks-prepend-path: prepend a path to all the external remark file - /// paths found in remark metadata. - std::string RemarksPrependPath; - - /// The output format of the remarks. - remarks::Format RemarksFormat = remarks::Format::Bitstream; - - /// @} - - LinkOptions() = default; -}; - -inline void warn(Twine Warning, Twine Context = {}) { - WithColor::warning() << Warning + "\n"; - if (!Context.isTriviallyEmpty()) - WithColor::note() << Twine("while processing ") + Context + "\n"; -} - -inline bool error(Twine Error, Twine Context = {}) { - WithColor::error() << Error + "\n"; - if (!Context.isTriviallyEmpty()) - WithColor::note() << Twine("while processing ") + Context + "\n"; - return false; -} - -} // end namespace dsymutil -} // end namespace llvm - -#endif // LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H +//===- tools/dsymutil/LinkUtils.h - Dwarf linker utilities ------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H +#define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H + +#include "SymbolMap.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/WithColor.h" + +#include "llvm/DWARFLinker/DWARFLinker.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" +#include <string> + +namespace llvm { +namespace dsymutil { + +struct LinkOptions { + /// Verbosity + bool Verbose = false; + + /// Statistics + bool Statistics = false; + + /// Skip emitting output + bool NoOutput = false; + + /// Do not unique types according to ODR + bool NoODR = false; + + /// Update + bool Update = false; + + /// Minimize + bool Minimize = false; + + /// Do not check swiftmodule timestamp + bool NoTimestamp = false; + + /// Number of threads. + unsigned Threads = 1; + + // Output file type. + OutputFileType FileType = OutputFileType::Object; + + /// The accelerator table kind + AccelTableKind TheAccelTableKind; + + /// -oso-prepend-path + std::string PrependPath; + + /// The -object-prefix-map. + std::map<std::string, std::string> ObjectPrefixMap; + + /// The Resources directory in the .dSYM bundle. + Optional<std::string> ResourceDir; + + /// Symbol map translator. + SymbolMapTranslator Translator; + + /// Virtual File System. + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = + vfs::getRealFileSystem(); + + /// Fields used for linking and placing remarks into the .dSYM bundle. + /// @{ + + /// Number of debug maps processed in total. + unsigned NumDebugMaps = 0; + + /// -remarks-prepend-path: prepend a path to all the external remark file + /// paths found in remark metadata. + std::string RemarksPrependPath; + + /// The output format of the remarks. + remarks::Format RemarksFormat = remarks::Format::Bitstream; + + /// @} + + LinkOptions() = default; +}; + +inline void warn(Twine Warning, Twine Context = {}) { + WithColor::warning() << Warning + "\n"; + if (!Context.isTriviallyEmpty()) + WithColor::note() << Twine("while processing ") + Context + "\n"; +} + +inline bool error(Twine Error, Twine Context = {}) { + WithColor::error() << Error + "\n"; + if (!Context.isTriviallyEmpty()) + WithColor::note() << Twine("while processing ") + Context + "\n"; + return false; +} + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H diff --git a/contrib/libs/llvm12/tools/dsymutil/MachODebugMapParser.cpp b/contrib/libs/llvm12/tools/dsymutil/MachODebugMapParser.cpp index 37848c561a..4c05326dc2 100644 --- a/contrib/libs/llvm12/tools/dsymutil/MachODebugMapParser.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/MachODebugMapParser.cpp @@ -1,609 +1,609 @@ -//===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "BinaryHolder.h" -#include "DebugMap.h" -#include "MachOUtils.h" -#include "llvm/ADT/Optional.h" -#include "llvm/Object/MachO.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include <vector> - -namespace { -using namespace llvm; -using namespace llvm::dsymutil; -using namespace llvm::object; - -class MachODebugMapParser { -public: - MachODebugMapParser(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - StringRef BinaryPath, ArrayRef<std::string> Archs, - StringRef PathPrefix = "", - bool PaperTrailWarnings = false, bool Verbose = false) - : BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()), - PathPrefix(std::string(PathPrefix)), - PaperTrailWarnings(PaperTrailWarnings), BinHolder(VFS, Verbose), - CurrentDebugMapObject(nullptr) {} - - /// Parses and returns the DebugMaps of the input binary. The binary contains - /// multiple maps in case it is a universal binary. - /// \returns an error in case the provided BinaryPath doesn't exist - /// or isn't of a supported type. - ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse(); - - /// Walk the symbol table and dump it. - bool dumpStab(); - -private: - std::string BinaryPath; - SmallVector<StringRef, 1> Archs; - std::string PathPrefix; - bool PaperTrailWarnings; - - /// Owns the MemoryBuffer for the main binary. - BinaryHolder BinHolder; - /// Map of the binary symbol addresses. - StringMap<uint64_t> MainBinarySymbolAddresses; - StringRef MainBinaryStrings; - /// The constructed DebugMap. - std::unique_ptr<DebugMap> Result; - /// List of common symbols that need to be added to the debug map. - std::vector<std::string> CommonSymbols; - - /// Map of the currently processed object file symbol addresses. - StringMap<Optional<uint64_t>> CurrentObjectAddresses; - /// Element of the debug map corresponding to the current object file. - DebugMapObject *CurrentDebugMapObject; - - /// Holds function info while function scope processing. - const char *CurrentFunctionName; - uint64_t CurrentFunctionAddress; - - std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary, - StringRef BinaryPath); - - void - switchToNewDebugMapObject(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp); - void resetParserState(); - uint64_t getMainBinarySymbolAddress(StringRef Name); - std::vector<StringRef> getMainBinarySymbolNames(uint64_t Value); - void loadMainBinarySymbols(const MachOObjectFile &MainBinary); - void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj); - void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, - uint8_t SectionIndex, uint16_t Flags, - uint64_t Value); - - template <typename STEType> void handleStabDebugMapEntry(const STEType &STE) { - handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, - STE.n_value); - } - - void addCommonSymbols(); - - /// Dump the symbol table output header. - void dumpSymTabHeader(raw_ostream &OS, StringRef Arch); - - /// Dump the contents of nlist entries. - void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex, - uint8_t Type, uint8_t SectionIndex, uint16_t Flags, - uint64_t Value); - - template <typename STEType> - void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) { - dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, - STE.n_value); - } - void dumpOneBinaryStab(const MachOObjectFile &MainBinary, - StringRef BinaryPath); - - void Warning(const Twine &Msg, StringRef File = StringRef()) { - WithColor::warning() << "(" - << MachOUtils::getArchName( - Result->getTriple().getArchName()) - << ") " << File << " " << Msg << "\n"; - - if (PaperTrailWarnings) { - if (!File.empty()) - Result->addDebugMapObject(File, sys::TimePoint<std::chrono::seconds>()); - if (Result->end() != Result->begin()) { - auto it = Result->end(); - (*--it)->addWarning(Msg.str()); - } - } - } -}; - -} // anonymous namespace - -/// Reset the parser state corresponding to the current object -/// file. This is to be called after an object file is finished -/// processing. -void MachODebugMapParser::resetParserState() { - CommonSymbols.clear(); - CurrentObjectAddresses.clear(); - CurrentDebugMapObject = nullptr; -} - -/// Commons symbols won't show up in the symbol map but might need to be -/// relocated. We can add them to the symbol table ourselves by combining the -/// information in the object file (the symbol name) and the main binary (the -/// address). -void MachODebugMapParser::addCommonSymbols() { - for (auto &CommonSymbol : CommonSymbols) { - uint64_t CommonAddr = getMainBinarySymbolAddress(CommonSymbol); - if (CommonAddr == 0) { - // The main binary doesn't have an address for the given symbol. - continue; - } - if (!CurrentDebugMapObject->addSymbol(CommonSymbol, None /*ObjectAddress*/, - CommonAddr, 0 /*size*/)) { - // The symbol is already present. - continue; - } - } -} - -/// Create a new DebugMapObject. This function resets the state of the -/// parser that was referring to the last object file and sets -/// everything up to add symbols to the new one. -void MachODebugMapParser::switchToNewDebugMapObject( - StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { - addCommonSymbols(); - resetParserState(); - - SmallString<80> Path(PathPrefix); - sys::path::append(Path, Filename); - - auto ObjectEntry = BinHolder.getObjectEntry(Path, Timestamp); - if (!ObjectEntry) { - auto Err = ObjectEntry.takeError(); - Warning("unable to open object file: " + toString(std::move(Err)), - Path.str()); - return; - } - - auto Object = ObjectEntry->getObjectAs<MachOObjectFile>(Result->getTriple()); - if (!Object) { - auto Err = Object.takeError(); - Warning("unable to open object file: " + toString(std::move(Err)), - Path.str()); - return; - } - - CurrentDebugMapObject = - &Result->addDebugMapObject(Path, Timestamp, MachO::N_OSO); - loadCurrentObjectFileSymbols(*Object); -} - -static std::string getArchName(const object::MachOObjectFile &Obj) { - Triple T = Obj.getArchTriple(); - return std::string(T.getArchName()); -} - -std::unique_ptr<DebugMap> -MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, - StringRef BinaryPath) { - loadMainBinarySymbols(MainBinary); - ArrayRef<uint8_t> UUID = MainBinary.getUuid(); - Result = - std::make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath, UUID); - MainBinaryStrings = MainBinary.getStringTableData(); - for (const SymbolRef &Symbol : MainBinary.symbols()) { - const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); - if (MainBinary.is64Bit()) - handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI)); - else - handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI)); - } - - resetParserState(); - return std::move(Result); -} - -// Table that maps Darwin's Mach-O stab constants to strings to allow printing. -// llvm-nm has very similar code, the strings used here are however slightly -// different and part of the interface of dsymutil (some project's build-systems -// parse the ouptut of dsymutil -s), thus they shouldn't be changed. -struct DarwinStabName { - uint8_t NType; - const char *Name; -}; - -static const struct DarwinStabName DarwinStabNames[] = { - {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"}, - {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"}, - {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"}, - {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"}, - {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"}, - {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"}, - {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"}, - {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"}, - {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"}, - {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"}, - {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"}, - {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"}, - {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"}, - {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"}, - {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"}, - {MachO::N_LENG, "N_LENG"}, {0, nullptr}}; - -static const char *getDarwinStabString(uint8_t NType) { - for (unsigned i = 0; DarwinStabNames[i].Name; i++) { - if (DarwinStabNames[i].NType == NType) - return DarwinStabNames[i].Name; - } - return nullptr; -} - -void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) { - OS << "-----------------------------------" - "-----------------------------------\n"; - OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n"; - OS << "-----------------------------------" - "-----------------------------------\n"; - OS << "Index n_strx n_type n_sect n_desc n_value\n"; - OS << "======== -------- ------------------ ------ ------ ----------------\n"; -} - -void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, - uint32_t StringIndex, uint8_t Type, - uint8_t SectionIndex, uint16_t Flags, - uint64_t Value) { - // Index - OS << '[' << format_decimal(Index, 6) - << "] " - // n_strx - << format_hex_no_prefix(StringIndex, 8) - << ' ' - // n_type... - << format_hex_no_prefix(Type, 2) << " ("; - - if (Type & MachO::N_STAB) - OS << left_justify(getDarwinStabString(Type), 13); - else { - if (Type & MachO::N_PEXT) - OS << "PEXT "; - else - OS << " "; - switch (Type & MachO::N_TYPE) { - case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT - OS << "UNDF"; - break; - case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT - OS << "ABS "; - break; - case MachO::N_SECT: // 0xe defined in section number n_sect - OS << "SECT"; - break; - case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib) - OS << "PBUD"; - break; - case MachO::N_INDR: // 0xa indirect - OS << "INDR"; - break; - default: - OS << format_hex_no_prefix(Type, 2) << " "; - break; - } - if (Type & MachO::N_EXT) - OS << " EXT"; - else - OS << " "; - } - - OS << ") " - // n_sect - << format_hex_no_prefix(SectionIndex, 2) - << " " - // n_desc - << format_hex_no_prefix(Flags, 4) - << " " - // n_value - << format_hex_no_prefix(Value, 16); - - const char *Name = &MainBinaryStrings.data()[StringIndex]; - if (Name && Name[0]) - OS << " '" << Name << "'"; - - OS << "\n"; -} - -void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary, - StringRef BinaryPath) { - loadMainBinarySymbols(MainBinary); - MainBinaryStrings = MainBinary.getStringTableData(); - raw_ostream &OS(llvm::outs()); - - dumpSymTabHeader(OS, getArchName(MainBinary)); - uint64_t Idx = 0; - for (const SymbolRef &Symbol : MainBinary.symbols()) { - const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); - if (MainBinary.is64Bit()) - dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI)); - else - dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI)); - Idx++; - } - - OS << "\n\n"; - resetParserState(); -} - -static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) { - if (Archs.empty() || is_contained(Archs, "all") || is_contained(Archs, "*")) - return true; - - if (Arch.startswith("arm") && Arch != "arm64" && is_contained(Archs, "arm")) - return true; - - SmallString<16> ArchName = Arch; - if (Arch.startswith("thumb")) - ArchName = ("arm" + Arch.substr(5)).str(); - - return is_contained(Archs, ArchName); -} - -bool MachODebugMapParser::dumpStab() { - auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); - if (!ObjectEntry) { - auto Err = ObjectEntry.takeError(); - WithColor::error() << "cannot load '" << BinaryPath - << "': " << toString(std::move(Err)) << '\n'; - return false; - } - - auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); - if (!Objects) { - auto Err = Objects.takeError(); - WithColor::error() << "cannot get '" << BinaryPath - << "' as MachO file: " << toString(std::move(Err)) - << "\n"; - return false; - } - - for (const auto *Object : *Objects) - if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) - dumpOneBinaryStab(*Object, BinaryPath); - - return true; -} - -/// This main parsing routine tries to open the main binary and if -/// successful iterates over the STAB entries. The real parsing is -/// done in handleStabSymbolTableEntry. -ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() { - auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); - if (!ObjectEntry) { - return errorToErrorCode(ObjectEntry.takeError()); - } - - auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); - if (!Objects) { - return errorToErrorCode(Objects.takeError()); - } - - std::vector<std::unique_ptr<DebugMap>> Results; - for (const auto *Object : *Objects) - if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) - Results.push_back(parseOneBinary(*Object, BinaryPath)); - - return std::move(Results); -} - -/// Interpret the STAB entries to fill the DebugMap. -void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, - uint8_t Type, - uint8_t SectionIndex, - uint16_t Flags, - uint64_t Value) { - if (!(Type & MachO::N_STAB)) - return; - - const char *Name = &MainBinaryStrings.data()[StringIndex]; - - // An N_OSO entry represents the start of a new object file description. - if (Type == MachO::N_OSO) - return switchToNewDebugMapObject(Name, sys::toTimePoint(Value)); - - if (Type == MachO::N_AST) { - SmallString<80> Path(PathPrefix); - sys::path::append(Path, Name); - Result->addDebugMapObject(Path, sys::toTimePoint(Value), Type); - return; - } - - // If the last N_OSO object file wasn't found, CurrentDebugMapObject will be - // null. Do not update anything until we find the next valid N_OSO entry. - if (!CurrentDebugMapObject) - return; - - uint32_t Size = 0; - switch (Type) { - case MachO::N_GSYM: - // This is a global variable. We need to query the main binary - // symbol table to find its address as it might not be in the - // debug map (for common symbols). - Value = getMainBinarySymbolAddress(Name); - break; - case MachO::N_FUN: - // Functions are scopes in STABS. They have an end marker that - // contains the function size. - if (Name[0] == '\0') { - Size = Value; - Value = CurrentFunctionAddress; - Name = CurrentFunctionName; - break; - } else { - CurrentFunctionName = Name; - CurrentFunctionAddress = Value; - return; - } - case MachO::N_STSYM: - break; - default: - return; - } - - auto ObjectSymIt = CurrentObjectAddresses.find(Name); - - // If the name of a (non-static) symbol is not in the current object, we - // check all its aliases from the main binary. - if (ObjectSymIt == CurrentObjectAddresses.end() && Type != MachO::N_STSYM) { - for (const auto &Alias : getMainBinarySymbolNames(Value)) { - ObjectSymIt = CurrentObjectAddresses.find(Alias); - if (ObjectSymIt != CurrentObjectAddresses.end()) - break; - } - } - - if (ObjectSymIt == CurrentObjectAddresses.end()) { - Warning("could not find object file symbol for symbol " + Twine(Name)); - return; - } - - if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value, - Size)) { - Warning(Twine("failed to insert symbol '") + Name + "' in the debug map."); - return; - } -} - -/// Load the current object file symbols into CurrentObjectAddresses. -void MachODebugMapParser::loadCurrentObjectFileSymbols( - const object::MachOObjectFile &Obj) { - CurrentObjectAddresses.clear(); - - for (auto Sym : Obj.symbols()) { - uint64_t Addr = cantFail(Sym.getValue()); - Expected<StringRef> Name = Sym.getName(); - if (!Name) { - // TODO: Actually report errors helpfully. - consumeError(Name.takeError()); - continue; - } - // The value of some categories of symbols isn't meaningful. For - // example common symbols store their size in the value field, not - // their address. Absolute symbols have a fixed address that can - // conflict with standard symbols. These symbols (especially the - // common ones), might still be referenced by relocations. These - // relocations will use the symbol itself, and won't need an - // object file address. The object file address field is optional - // in the DebugMap, leave it unassigned for these symbols. - uint32_t Flags = cantFail(Sym.getFlags()); - if (Flags & SymbolRef::SF_Absolute) { - CurrentObjectAddresses[*Name] = None; - } else if (Flags & SymbolRef::SF_Common) { - CurrentObjectAddresses[*Name] = None; - CommonSymbols.push_back(std::string(*Name)); - } else { - CurrentObjectAddresses[*Name] = Addr; - } - } -} - -/// Lookup a symbol address in the main binary symbol table. The -/// parser only needs to query common symbols, thus not every symbol's -/// address is available through this function. -uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) { - auto Sym = MainBinarySymbolAddresses.find(Name); - if (Sym == MainBinarySymbolAddresses.end()) - return 0; - return Sym->second; -} - -/// Get all symbol names in the main binary for the given value. -std::vector<StringRef> -MachODebugMapParser::getMainBinarySymbolNames(uint64_t Value) { - std::vector<StringRef> Names; - for (const auto &Entry : MainBinarySymbolAddresses) { - if (Entry.second == Value) - Names.push_back(Entry.first()); - } - return Names; -} - -/// Load the interesting main binary symbols' addresses into -/// MainBinarySymbolAddresses. -void MachODebugMapParser::loadMainBinarySymbols( - const MachOObjectFile &MainBinary) { - section_iterator Section = MainBinary.section_end(); - MainBinarySymbolAddresses.clear(); - for (const auto &Sym : MainBinary.symbols()) { - Expected<SymbolRef::Type> TypeOrErr = Sym.getType(); - if (!TypeOrErr) { - // TODO: Actually report errors helpfully. - consumeError(TypeOrErr.takeError()); - continue; - } - SymbolRef::Type Type = *TypeOrErr; - // Skip undefined and STAB entries. - if ((Type == SymbolRef::ST_Debug) || (Type == SymbolRef::ST_Unknown)) - continue; - // In theory, the only symbols of interest are the global variables. These - // are the only ones that need to be queried because the address of common - // data won't be described in the debug map. All other addresses should be - // fetched for the debug map. In reality, by playing with 'ld -r' and - // export lists, you can get symbols described as N_GSYM in the debug map, - // but associated with a local symbol. Gather all the symbols, but prefer - // the global ones. - uint8_t SymType = - MainBinary.getSymbolTableEntry(Sym.getRawDataRefImpl()).n_type; - bool Extern = SymType & (MachO::N_EXT | MachO::N_PEXT); - Expected<section_iterator> SectionOrErr = Sym.getSection(); - if (!SectionOrErr) { - // TODO: Actually report errors helpfully. - consumeError(SectionOrErr.takeError()); - continue; - } - Section = *SectionOrErr; +//===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "BinaryHolder.h" +#include "DebugMap.h" +#include "MachOUtils.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <vector> + +namespace { +using namespace llvm; +using namespace llvm::dsymutil; +using namespace llvm::object; + +class MachODebugMapParser { +public: + MachODebugMapParser(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + StringRef BinaryPath, ArrayRef<std::string> Archs, + StringRef PathPrefix = "", + bool PaperTrailWarnings = false, bool Verbose = false) + : BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()), + PathPrefix(std::string(PathPrefix)), + PaperTrailWarnings(PaperTrailWarnings), BinHolder(VFS, Verbose), + CurrentDebugMapObject(nullptr) {} + + /// Parses and returns the DebugMaps of the input binary. The binary contains + /// multiple maps in case it is a universal binary. + /// \returns an error in case the provided BinaryPath doesn't exist + /// or isn't of a supported type. + ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse(); + + /// Walk the symbol table and dump it. + bool dumpStab(); + +private: + std::string BinaryPath; + SmallVector<StringRef, 1> Archs; + std::string PathPrefix; + bool PaperTrailWarnings; + + /// Owns the MemoryBuffer for the main binary. + BinaryHolder BinHolder; + /// Map of the binary symbol addresses. + StringMap<uint64_t> MainBinarySymbolAddresses; + StringRef MainBinaryStrings; + /// The constructed DebugMap. + std::unique_ptr<DebugMap> Result; + /// List of common symbols that need to be added to the debug map. + std::vector<std::string> CommonSymbols; + + /// Map of the currently processed object file symbol addresses. + StringMap<Optional<uint64_t>> CurrentObjectAddresses; + /// Element of the debug map corresponding to the current object file. + DebugMapObject *CurrentDebugMapObject; + + /// Holds function info while function scope processing. + const char *CurrentFunctionName; + uint64_t CurrentFunctionAddress; + + std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary, + StringRef BinaryPath); + + void + switchToNewDebugMapObject(StringRef Filename, + sys::TimePoint<std::chrono::seconds> Timestamp); + void resetParserState(); + uint64_t getMainBinarySymbolAddress(StringRef Name); + std::vector<StringRef> getMainBinarySymbolNames(uint64_t Value); + void loadMainBinarySymbols(const MachOObjectFile &MainBinary); + void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj); + void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, + uint64_t Value); + + template <typename STEType> void handleStabDebugMapEntry(const STEType &STE) { + handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, + STE.n_value); + } + + void addCommonSymbols(); + + /// Dump the symbol table output header. + void dumpSymTabHeader(raw_ostream &OS, StringRef Arch); + + /// Dump the contents of nlist entries. + void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex, + uint8_t Type, uint8_t SectionIndex, uint16_t Flags, + uint64_t Value); + + template <typename STEType> + void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) { + dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, + STE.n_value); + } + void dumpOneBinaryStab(const MachOObjectFile &MainBinary, + StringRef BinaryPath); + + void Warning(const Twine &Msg, StringRef File = StringRef()) { + WithColor::warning() << "(" + << MachOUtils::getArchName( + Result->getTriple().getArchName()) + << ") " << File << " " << Msg << "\n"; + + if (PaperTrailWarnings) { + if (!File.empty()) + Result->addDebugMapObject(File, sys::TimePoint<std::chrono::seconds>()); + if (Result->end() != Result->begin()) { + auto it = Result->end(); + (*--it)->addWarning(Msg.str()); + } + } + } +}; + +} // anonymous namespace + +/// Reset the parser state corresponding to the current object +/// file. This is to be called after an object file is finished +/// processing. +void MachODebugMapParser::resetParserState() { + CommonSymbols.clear(); + CurrentObjectAddresses.clear(); + CurrentDebugMapObject = nullptr; +} + +/// Commons symbols won't show up in the symbol map but might need to be +/// relocated. We can add them to the symbol table ourselves by combining the +/// information in the object file (the symbol name) and the main binary (the +/// address). +void MachODebugMapParser::addCommonSymbols() { + for (auto &CommonSymbol : CommonSymbols) { + uint64_t CommonAddr = getMainBinarySymbolAddress(CommonSymbol); + if (CommonAddr == 0) { + // The main binary doesn't have an address for the given symbol. + continue; + } + if (!CurrentDebugMapObject->addSymbol(CommonSymbol, None /*ObjectAddress*/, + CommonAddr, 0 /*size*/)) { + // The symbol is already present. + continue; + } + } +} + +/// Create a new DebugMapObject. This function resets the state of the +/// parser that was referring to the last object file and sets +/// everything up to add symbols to the new one. +void MachODebugMapParser::switchToNewDebugMapObject( + StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { + addCommonSymbols(); + resetParserState(); + + SmallString<80> Path(PathPrefix); + sys::path::append(Path, Filename); + + auto ObjectEntry = BinHolder.getObjectEntry(Path, Timestamp); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + Path.str()); + return; + } + + auto Object = ObjectEntry->getObjectAs<MachOObjectFile>(Result->getTriple()); + if (!Object) { + auto Err = Object.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + Path.str()); + return; + } + + CurrentDebugMapObject = + &Result->addDebugMapObject(Path, Timestamp, MachO::N_OSO); + loadCurrentObjectFileSymbols(*Object); +} + +static std::string getArchName(const object::MachOObjectFile &Obj) { + Triple T = Obj.getArchTriple(); + return std::string(T.getArchName()); +} + +std::unique_ptr<DebugMap> +MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, + StringRef BinaryPath) { + loadMainBinarySymbols(MainBinary); + ArrayRef<uint8_t> UUID = MainBinary.getUuid(); + Result = + std::make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath, UUID); + MainBinaryStrings = MainBinary.getStringTableData(); + for (const SymbolRef &Symbol : MainBinary.symbols()) { + const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); + if (MainBinary.is64Bit()) + handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI)); + else + handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI)); + } + + resetParserState(); + return std::move(Result); +} + +// Table that maps Darwin's Mach-O stab constants to strings to allow printing. +// llvm-nm has very similar code, the strings used here are however slightly +// different and part of the interface of dsymutil (some project's build-systems +// parse the ouptut of dsymutil -s), thus they shouldn't be changed. +struct DarwinStabName { + uint8_t NType; + const char *Name; +}; + +static const struct DarwinStabName DarwinStabNames[] = { + {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"}, + {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"}, + {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"}, + {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"}, + {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"}, + {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"}, + {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"}, + {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"}, + {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"}, + {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"}, + {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"}, + {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"}, + {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"}, + {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"}, + {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"}, + {MachO::N_LENG, "N_LENG"}, {0, nullptr}}; + +static const char *getDarwinStabString(uint8_t NType) { + for (unsigned i = 0; DarwinStabNames[i].Name; i++) { + if (DarwinStabNames[i].NType == NType) + return DarwinStabNames[i].Name; + } + return nullptr; +} + +void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) { + OS << "-----------------------------------" + "-----------------------------------\n"; + OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n"; + OS << "-----------------------------------" + "-----------------------------------\n"; + OS << "Index n_strx n_type n_sect n_desc n_value\n"; + OS << "======== -------- ------------------ ------ ------ ----------------\n"; +} + +void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, + uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, + uint64_t Value) { + // Index + OS << '[' << format_decimal(Index, 6) + << "] " + // n_strx + << format_hex_no_prefix(StringIndex, 8) + << ' ' + // n_type... + << format_hex_no_prefix(Type, 2) << " ("; + + if (Type & MachO::N_STAB) + OS << left_justify(getDarwinStabString(Type), 13); + else { + if (Type & MachO::N_PEXT) + OS << "PEXT "; + else + OS << " "; + switch (Type & MachO::N_TYPE) { + case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT + OS << "UNDF"; + break; + case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT + OS << "ABS "; + break; + case MachO::N_SECT: // 0xe defined in section number n_sect + OS << "SECT"; + break; + case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib) + OS << "PBUD"; + break; + case MachO::N_INDR: // 0xa indirect + OS << "INDR"; + break; + default: + OS << format_hex_no_prefix(Type, 2) << " "; + break; + } + if (Type & MachO::N_EXT) + OS << " EXT"; + else + OS << " "; + } + + OS << ") " + // n_sect + << format_hex_no_prefix(SectionIndex, 2) + << " " + // n_desc + << format_hex_no_prefix(Flags, 4) + << " " + // n_value + << format_hex_no_prefix(Value, 16); + + const char *Name = &MainBinaryStrings.data()[StringIndex]; + if (Name && Name[0]) + OS << " '" << Name << "'"; + + OS << "\n"; +} + +void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary, + StringRef BinaryPath) { + loadMainBinarySymbols(MainBinary); + MainBinaryStrings = MainBinary.getStringTableData(); + raw_ostream &OS(llvm::outs()); + + dumpSymTabHeader(OS, getArchName(MainBinary)); + uint64_t Idx = 0; + for (const SymbolRef &Symbol : MainBinary.symbols()) { + const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); + if (MainBinary.is64Bit()) + dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI)); + else + dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI)); + Idx++; + } + + OS << "\n\n"; + resetParserState(); +} + +static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) { + if (Archs.empty() || is_contained(Archs, "all") || is_contained(Archs, "*")) + return true; + + if (Arch.startswith("arm") && Arch != "arm64" && is_contained(Archs, "arm")) + return true; + + SmallString<16> ArchName = Arch; + if (Arch.startswith("thumb")) + ArchName = ("arm" + Arch.substr(5)).str(); + + return is_contained(Archs, ArchName); +} + +bool MachODebugMapParser::dumpStab() { + auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + WithColor::error() << "cannot load '" << BinaryPath + << "': " << toString(std::move(Err)) << '\n'; + return false; + } + + auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); + if (!Objects) { + auto Err = Objects.takeError(); + WithColor::error() << "cannot get '" << BinaryPath + << "' as MachO file: " << toString(std::move(Err)) + << "\n"; + return false; + } + + for (const auto *Object : *Objects) + if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) + dumpOneBinaryStab(*Object, BinaryPath); + + return true; +} + +/// This main parsing routine tries to open the main binary and if +/// successful iterates over the STAB entries. The real parsing is +/// done in handleStabSymbolTableEntry. +ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() { + auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); + if (!ObjectEntry) { + return errorToErrorCode(ObjectEntry.takeError()); + } + + auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); + if (!Objects) { + return errorToErrorCode(Objects.takeError()); + } + + std::vector<std::unique_ptr<DebugMap>> Results; + for (const auto *Object : *Objects) + if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) + Results.push_back(parseOneBinary(*Object, BinaryPath)); + + return std::move(Results); +} + +/// Interpret the STAB entries to fill the DebugMap. +void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, + uint8_t Type, + uint8_t SectionIndex, + uint16_t Flags, + uint64_t Value) { + if (!(Type & MachO::N_STAB)) + return; + + const char *Name = &MainBinaryStrings.data()[StringIndex]; + + // An N_OSO entry represents the start of a new object file description. + if (Type == MachO::N_OSO) + return switchToNewDebugMapObject(Name, sys::toTimePoint(Value)); + + if (Type == MachO::N_AST) { + SmallString<80> Path(PathPrefix); + sys::path::append(Path, Name); + Result->addDebugMapObject(Path, sys::toTimePoint(Value), Type); + return; + } + + // If the last N_OSO object file wasn't found, CurrentDebugMapObject will be + // null. Do not update anything until we find the next valid N_OSO entry. + if (!CurrentDebugMapObject) + return; + + uint32_t Size = 0; + switch (Type) { + case MachO::N_GSYM: + // This is a global variable. We need to query the main binary + // symbol table to find its address as it might not be in the + // debug map (for common symbols). + Value = getMainBinarySymbolAddress(Name); + break; + case MachO::N_FUN: + // Functions are scopes in STABS. They have an end marker that + // contains the function size. + if (Name[0] == '\0') { + Size = Value; + Value = CurrentFunctionAddress; + Name = CurrentFunctionName; + break; + } else { + CurrentFunctionName = Name; + CurrentFunctionAddress = Value; + return; + } + case MachO::N_STSYM: + break; + default: + return; + } + + auto ObjectSymIt = CurrentObjectAddresses.find(Name); + + // If the name of a (non-static) symbol is not in the current object, we + // check all its aliases from the main binary. + if (ObjectSymIt == CurrentObjectAddresses.end() && Type != MachO::N_STSYM) { + for (const auto &Alias : getMainBinarySymbolNames(Value)) { + ObjectSymIt = CurrentObjectAddresses.find(Alias); + if (ObjectSymIt != CurrentObjectAddresses.end()) + break; + } + } + + if (ObjectSymIt == CurrentObjectAddresses.end()) { + Warning("could not find object file symbol for symbol " + Twine(Name)); + return; + } + + if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value, + Size)) { + Warning(Twine("failed to insert symbol '") + Name + "' in the debug map."); + return; + } +} + +/// Load the current object file symbols into CurrentObjectAddresses. +void MachODebugMapParser::loadCurrentObjectFileSymbols( + const object::MachOObjectFile &Obj) { + CurrentObjectAddresses.clear(); + + for (auto Sym : Obj.symbols()) { + uint64_t Addr = cantFail(Sym.getValue()); + Expected<StringRef> Name = Sym.getName(); + if (!Name) { + // TODO: Actually report errors helpfully. + consumeError(Name.takeError()); + continue; + } + // The value of some categories of symbols isn't meaningful. For + // example common symbols store their size in the value field, not + // their address. Absolute symbols have a fixed address that can + // conflict with standard symbols. These symbols (especially the + // common ones), might still be referenced by relocations. These + // relocations will use the symbol itself, and won't need an + // object file address. The object file address field is optional + // in the DebugMap, leave it unassigned for these symbols. + uint32_t Flags = cantFail(Sym.getFlags()); + if (Flags & SymbolRef::SF_Absolute) { + CurrentObjectAddresses[*Name] = None; + } else if (Flags & SymbolRef::SF_Common) { + CurrentObjectAddresses[*Name] = None; + CommonSymbols.push_back(std::string(*Name)); + } else { + CurrentObjectAddresses[*Name] = Addr; + } + } +} + +/// Lookup a symbol address in the main binary symbol table. The +/// parser only needs to query common symbols, thus not every symbol's +/// address is available through this function. +uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) { + auto Sym = MainBinarySymbolAddresses.find(Name); + if (Sym == MainBinarySymbolAddresses.end()) + return 0; + return Sym->second; +} + +/// Get all symbol names in the main binary for the given value. +std::vector<StringRef> +MachODebugMapParser::getMainBinarySymbolNames(uint64_t Value) { + std::vector<StringRef> Names; + for (const auto &Entry : MainBinarySymbolAddresses) { + if (Entry.second == Value) + Names.push_back(Entry.first()); + } + return Names; +} + +/// Load the interesting main binary symbols' addresses into +/// MainBinarySymbolAddresses. +void MachODebugMapParser::loadMainBinarySymbols( + const MachOObjectFile &MainBinary) { + section_iterator Section = MainBinary.section_end(); + MainBinarySymbolAddresses.clear(); + for (const auto &Sym : MainBinary.symbols()) { + Expected<SymbolRef::Type> TypeOrErr = Sym.getType(); + if (!TypeOrErr) { + // TODO: Actually report errors helpfully. + consumeError(TypeOrErr.takeError()); + continue; + } + SymbolRef::Type Type = *TypeOrErr; + // Skip undefined and STAB entries. + if ((Type == SymbolRef::ST_Debug) || (Type == SymbolRef::ST_Unknown)) + continue; + // In theory, the only symbols of interest are the global variables. These + // are the only ones that need to be queried because the address of common + // data won't be described in the debug map. All other addresses should be + // fetched for the debug map. In reality, by playing with 'ld -r' and + // export lists, you can get symbols described as N_GSYM in the debug map, + // but associated with a local symbol. Gather all the symbols, but prefer + // the global ones. + uint8_t SymType = + MainBinary.getSymbolTableEntry(Sym.getRawDataRefImpl()).n_type; + bool Extern = SymType & (MachO::N_EXT | MachO::N_PEXT); + Expected<section_iterator> SectionOrErr = Sym.getSection(); + if (!SectionOrErr) { + // TODO: Actually report errors helpfully. + consumeError(SectionOrErr.takeError()); + continue; + } + Section = *SectionOrErr; if ((Section == MainBinary.section_end() || Section->isText()) && !Extern) - continue; - uint64_t Addr = cantFail(Sym.getValue()); - Expected<StringRef> NameOrErr = Sym.getName(); - if (!NameOrErr) { - // TODO: Actually report errors helpfully. - consumeError(NameOrErr.takeError()); - continue; - } - StringRef Name = *NameOrErr; - if (Name.size() == 0 || Name[0] == '\0') - continue; - // Override only if the new key is global. - if (Extern) - MainBinarySymbolAddresses[Name] = Addr; - else - MainBinarySymbolAddresses.try_emplace(Name, Addr); - } -} - -namespace llvm { -namespace dsymutil { -llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>> -parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - StringRef InputFile, ArrayRef<std::string> Archs, - StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, - bool InputIsYAML) { - if (InputIsYAML) - return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); - - MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, - PaperTrailWarnings, Verbose); - return Parser.parse(); -} - -bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - StringRef InputFile, ArrayRef<std::string> Archs, - StringRef PrependPath) { - MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, false); - return Parser.dumpStab(); -} -} // namespace dsymutil -} // namespace llvm + continue; + uint64_t Addr = cantFail(Sym.getValue()); + Expected<StringRef> NameOrErr = Sym.getName(); + if (!NameOrErr) { + // TODO: Actually report errors helpfully. + consumeError(NameOrErr.takeError()); + continue; + } + StringRef Name = *NameOrErr; + if (Name.size() == 0 || Name[0] == '\0') + continue; + // Override only if the new key is global. + if (Extern) + MainBinarySymbolAddresses[Name] = Addr; + else + MainBinarySymbolAddresses.try_emplace(Name, Addr); + } +} + +namespace llvm { +namespace dsymutil { +llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>> +parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, + bool InputIsYAML) { + if (InputIsYAML) + return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); + + MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, + PaperTrailWarnings, Verbose); + return Parser.parse(); +} + +bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath) { + MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, false); + return Parser.dumpStab(); +} +} // namespace dsymutil +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/MachOUtils.cpp b/contrib/libs/llvm12/tools/dsymutil/MachOUtils.cpp index 943af43058..c91be23ac6 100644 --- a/contrib/libs/llvm12/tools/dsymutil/MachOUtils.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/MachOUtils.cpp @@ -1,436 +1,436 @@ -//===-- MachOUtils.cpp - Mach-o specific helpers for dsymutil ------------===// +//===-- MachOUtils.cpp - Mach-o specific helpers for dsymutil ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MachOUtils.h" +#include "BinaryHolder.h" +#include "DebugMap.h" +#include "LinkUtils.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" +#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCMachObjectWriter.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dsymutil { +namespace MachOUtils { + +llvm::Error ArchAndFile::createTempFile() { + llvm::SmallString<128> TmpModel; + llvm::sys::path::system_temp_directory(true, TmpModel); + llvm::sys::path::append(TmpModel, "dsym.tmp%%%%%.dwarf"); + Expected<sys::fs::TempFile> T = sys::fs::TempFile::create(TmpModel); + + if (!T) + return T.takeError(); + + File = std::make_unique<sys::fs::TempFile>(std::move(*T)); + return Error::success(); +} + +llvm::StringRef ArchAndFile::path() const { return File->TmpName; } + +ArchAndFile::~ArchAndFile() { + if (File) + if (auto E = File->discard()) + llvm::consumeError(std::move(E)); +} + +std::string getArchName(StringRef Arch) { + if (Arch.startswith("thumb")) + return (llvm::Twine("arm") + Arch.drop_front(5)).str(); + return std::string(Arch); +} + +static bool runLipo(StringRef SDKPath, SmallVectorImpl<StringRef> &Args) { + auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath)); + if (!Path) + Path = sys::findProgramByName("lipo"); + + if (!Path) { + WithColor::error() << "lipo: " << Path.getError().message() << "\n"; + return false; + } + + std::string ErrMsg; + int result = sys::ExecuteAndWait(*Path, Args, None, {}, 0, 0, &ErrMsg); + if (result) { + WithColor::error() << "lipo: " << ErrMsg << "\n"; + return false; + } + + return true; +} + +bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles, + StringRef OutputFileName, + const LinkOptions &Options, StringRef SDKPath) { + // No need to merge one file into a universal fat binary. + if (ArchFiles.size() == 1) { + if (auto E = ArchFiles.front().File->keep(OutputFileName)) { + WithColor::error() << "while keeping " << ArchFiles.front().path() + << " as " << OutputFileName << ": " + << toString(std::move(E)) << "\n"; + return false; + } + return true; + } + + SmallVector<StringRef, 8> Args; + Args.push_back("lipo"); + Args.push_back("-create"); + + for (auto &Thin : ArchFiles) + Args.push_back(Thin.path()); + + // Align segments to match dsymutil-classic alignment + for (auto &Thin : ArchFiles) { + Thin.Arch = getArchName(Thin.Arch); + Args.push_back("-segalign"); + Args.push_back(Thin.Arch); + Args.push_back("20"); + } + + Args.push_back("-output"); + Args.push_back(OutputFileName.data()); + + if (Options.Verbose) { + outs() << "Running lipo\n"; + for (auto Arg : Args) + outs() << ' ' << Arg; + outs() << "\n"; + } + + return Options.NoOutput ? true : runLipo(SDKPath, Args); +} + +// Return a MachO::segment_command_64 that holds the same values as the passed +// MachO::segment_command. We do that to avoid having to duplicate the logic +// for 32bits and 64bits segments. +struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) { + MachO::segment_command_64 Seg64; + Seg64.cmd = Seg.cmd; + Seg64.cmdsize = Seg.cmdsize; + memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname)); + Seg64.vmaddr = Seg.vmaddr; + Seg64.vmsize = Seg.vmsize; + Seg64.fileoff = Seg.fileoff; + Seg64.filesize = Seg.filesize; + Seg64.maxprot = Seg.maxprot; + Seg64.initprot = Seg.initprot; + Seg64.nsects = Seg.nsects; + Seg64.flags = Seg.flags; + return Seg64; +} + +// Iterate on all \a Obj segments, and apply \a Handler to them. +template <typename FunctionTy> +static void iterateOnSegments(const object::MachOObjectFile &Obj, + FunctionTy Handler) { + for (const auto &LCI : Obj.load_commands()) { + MachO::segment_command_64 Segment; + if (LCI.C.cmd == MachO::LC_SEGMENT) + Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI)); + else if (LCI.C.cmd == MachO::LC_SEGMENT_64) + Segment = Obj.getSegment64LoadCommand(LCI); + else + continue; + + Handler(Segment); + } +} + +// Transfer the symbols described by \a NList to \a NewSymtab which is just the +// raw contents of the symbol table for the dSYM companion file. \returns +// whether the symbol was transferred or not. +template <typename NListTy> +static bool transferSymbol(NListTy NList, bool IsLittleEndian, + StringRef Strings, SmallVectorImpl<char> &NewSymtab, + NonRelocatableStringpool &NewStrings, + bool &InDebugNote) { + // Do not transfer undefined symbols, we want real addresses. + if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF) + return false; + + // Do not transfer N_AST symbols as their content is copied into a section of + // the Mach-O companion file. + if (NList.n_type == MachO::N_AST) + return false; + + StringRef Name = StringRef(Strings.begin() + NList.n_strx); + + // An N_SO with a filename opens a debugging scope and another one without a + // name closes it. Don't transfer anything in the debugging scope. + if (InDebugNote) { + InDebugNote = + (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0'); + return false; + } else if (NList.n_type == MachO::N_SO) { + InDebugNote = true; + return false; + } + + // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty + // strings at the start of the generated string table (There is + // corresponding code in the string table emission). + NList.n_strx = NewStrings.getStringOffset(Name) + 1; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(NList); + + NewSymtab.append(reinterpret_cast<char *>(&NList), + reinterpret_cast<char *>(&NList + 1)); + return true; +} + +// Wrapper around transferSymbol to transfer all of \a Obj symbols +// to \a NewSymtab. This function does not write in the output file. +// \returns the number of symbols in \a NewSymtab. +static unsigned transferSymbols(const object::MachOObjectFile &Obj, + SmallVectorImpl<char> &NewSymtab, + NonRelocatableStringpool &NewStrings) { + unsigned Syms = 0; + StringRef Strings = Obj.getStringTableData(); + bool IsLittleEndian = Obj.isLittleEndian(); + bool InDebugNote = false; + + if (Obj.is64Bit()) { + for (const object::SymbolRef &Symbol : Obj.symbols()) { + object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); + if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian, + Strings, NewSymtab, NewStrings, InDebugNote)) + ++Syms; + } + } else { + for (const object::SymbolRef &Symbol : Obj.symbols()) { + object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); + if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings, + NewSymtab, NewStrings, InDebugNote)) + ++Syms; + } + } + return Syms; +} + +static MachO::section +getSection(const object::MachOObjectFile &Obj, + const MachO::segment_command &Seg, + const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { + return Obj.getSection(LCI, Idx); +} + +static MachO::section_64 +getSection(const object::MachOObjectFile &Obj, + const MachO::segment_command_64 &Seg, + const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { + return Obj.getSection64(LCI, Idx); +} + +// Transfer \a Segment from \a Obj to the output file. This calls into \a Writer +// to write these load commands directly in the output file at the current +// position. // -// 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 +// The function also tries to find a hole in the address map to fit the __DWARF +// segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the +// highest segment address. // -//===----------------------------------------------------------------------===// - -#include "MachOUtils.h" -#include "BinaryHolder.h" -#include "DebugMap.h" -#include "LinkUtils.h" -#include "llvm/CodeGen/NonRelocatableStringpool.h" -#include "llvm/MC/MCAsmLayout.h" -#include "llvm/MC/MCMachObjectWriter.h" -#include "llvm/MC/MCObjectStreamer.h" -#include "llvm/MC/MCSectionMachO.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/Object/MachO.h" -#include "llvm/Support/FileUtilities.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { -namespace dsymutil { -namespace MachOUtils { - -llvm::Error ArchAndFile::createTempFile() { - llvm::SmallString<128> TmpModel; - llvm::sys::path::system_temp_directory(true, TmpModel); - llvm::sys::path::append(TmpModel, "dsym.tmp%%%%%.dwarf"); - Expected<sys::fs::TempFile> T = sys::fs::TempFile::create(TmpModel); - - if (!T) - return T.takeError(); - - File = std::make_unique<sys::fs::TempFile>(std::move(*T)); - return Error::success(); -} - -llvm::StringRef ArchAndFile::path() const { return File->TmpName; } - -ArchAndFile::~ArchAndFile() { - if (File) - if (auto E = File->discard()) - llvm::consumeError(std::move(E)); -} - -std::string getArchName(StringRef Arch) { - if (Arch.startswith("thumb")) - return (llvm::Twine("arm") + Arch.drop_front(5)).str(); - return std::string(Arch); -} - -static bool runLipo(StringRef SDKPath, SmallVectorImpl<StringRef> &Args) { - auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath)); - if (!Path) - Path = sys::findProgramByName("lipo"); - - if (!Path) { - WithColor::error() << "lipo: " << Path.getError().message() << "\n"; - return false; - } - - std::string ErrMsg; - int result = sys::ExecuteAndWait(*Path, Args, None, {}, 0, 0, &ErrMsg); - if (result) { - WithColor::error() << "lipo: " << ErrMsg << "\n"; - return false; - } - - return true; -} - -bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles, - StringRef OutputFileName, - const LinkOptions &Options, StringRef SDKPath) { - // No need to merge one file into a universal fat binary. - if (ArchFiles.size() == 1) { - if (auto E = ArchFiles.front().File->keep(OutputFileName)) { - WithColor::error() << "while keeping " << ArchFiles.front().path() - << " as " << OutputFileName << ": " - << toString(std::move(E)) << "\n"; - return false; - } - return true; - } - - SmallVector<StringRef, 8> Args; - Args.push_back("lipo"); - Args.push_back("-create"); - - for (auto &Thin : ArchFiles) - Args.push_back(Thin.path()); - - // Align segments to match dsymutil-classic alignment - for (auto &Thin : ArchFiles) { - Thin.Arch = getArchName(Thin.Arch); - Args.push_back("-segalign"); - Args.push_back(Thin.Arch); - Args.push_back("20"); - } - - Args.push_back("-output"); - Args.push_back(OutputFileName.data()); - - if (Options.Verbose) { - outs() << "Running lipo\n"; - for (auto Arg : Args) - outs() << ' ' << Arg; - outs() << "\n"; - } - - return Options.NoOutput ? true : runLipo(SDKPath, Args); -} - -// Return a MachO::segment_command_64 that holds the same values as the passed -// MachO::segment_command. We do that to avoid having to duplicate the logic -// for 32bits and 64bits segments. -struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) { - MachO::segment_command_64 Seg64; - Seg64.cmd = Seg.cmd; - Seg64.cmdsize = Seg.cmdsize; - memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname)); - Seg64.vmaddr = Seg.vmaddr; - Seg64.vmsize = Seg.vmsize; - Seg64.fileoff = Seg.fileoff; - Seg64.filesize = Seg.filesize; - Seg64.maxprot = Seg.maxprot; - Seg64.initprot = Seg.initprot; - Seg64.nsects = Seg.nsects; - Seg64.flags = Seg.flags; - return Seg64; -} - -// Iterate on all \a Obj segments, and apply \a Handler to them. -template <typename FunctionTy> -static void iterateOnSegments(const object::MachOObjectFile &Obj, - FunctionTy Handler) { - for (const auto &LCI : Obj.load_commands()) { - MachO::segment_command_64 Segment; - if (LCI.C.cmd == MachO::LC_SEGMENT) - Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI)); - else if (LCI.C.cmd == MachO::LC_SEGMENT_64) - Segment = Obj.getSegment64LoadCommand(LCI); - else - continue; - - Handler(Segment); - } -} - -// Transfer the symbols described by \a NList to \a NewSymtab which is just the -// raw contents of the symbol table for the dSYM companion file. \returns -// whether the symbol was transferred or not. -template <typename NListTy> -static bool transferSymbol(NListTy NList, bool IsLittleEndian, - StringRef Strings, SmallVectorImpl<char> &NewSymtab, - NonRelocatableStringpool &NewStrings, - bool &InDebugNote) { - // Do not transfer undefined symbols, we want real addresses. - if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF) - return false; - - // Do not transfer N_AST symbols as their content is copied into a section of - // the Mach-O companion file. - if (NList.n_type == MachO::N_AST) - return false; - - StringRef Name = StringRef(Strings.begin() + NList.n_strx); - - // An N_SO with a filename opens a debugging scope and another one without a - // name closes it. Don't transfer anything in the debugging scope. - if (InDebugNote) { - InDebugNote = - (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0'); - return false; - } else if (NList.n_type == MachO::N_SO) { - InDebugNote = true; - return false; - } - - // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty - // strings at the start of the generated string table (There is - // corresponding code in the string table emission). - NList.n_strx = NewStrings.getStringOffset(Name) + 1; - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(NList); - - NewSymtab.append(reinterpret_cast<char *>(&NList), - reinterpret_cast<char *>(&NList + 1)); - return true; -} - -// Wrapper around transferSymbol to transfer all of \a Obj symbols -// to \a NewSymtab. This function does not write in the output file. -// \returns the number of symbols in \a NewSymtab. -static unsigned transferSymbols(const object::MachOObjectFile &Obj, - SmallVectorImpl<char> &NewSymtab, - NonRelocatableStringpool &NewStrings) { - unsigned Syms = 0; - StringRef Strings = Obj.getStringTableData(); - bool IsLittleEndian = Obj.isLittleEndian(); - bool InDebugNote = false; - - if (Obj.is64Bit()) { - for (const object::SymbolRef &Symbol : Obj.symbols()) { - object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); - if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian, - Strings, NewSymtab, NewStrings, InDebugNote)) - ++Syms; - } - } else { - for (const object::SymbolRef &Symbol : Obj.symbols()) { - object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); - if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings, - NewSymtab, NewStrings, InDebugNote)) - ++Syms; - } - } - return Syms; -} - -static MachO::section -getSection(const object::MachOObjectFile &Obj, - const MachO::segment_command &Seg, - const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { - return Obj.getSection(LCI, Idx); -} - -static MachO::section_64 -getSection(const object::MachOObjectFile &Obj, - const MachO::segment_command_64 &Seg, - const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { - return Obj.getSection64(LCI, Idx); -} - -// Transfer \a Segment from \a Obj to the output file. This calls into \a Writer -// to write these load commands directly in the output file at the current -// position. -// -// The function also tries to find a hole in the address map to fit the __DWARF -// segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the -// highest segment address. -// -// When the __LINKEDIT segment is transferred, its offset and size are set resp. -// to \a LinkeditOffset and \a LinkeditSize. +// When the __LINKEDIT segment is transferred, its offset and size are set resp. +// to \a LinkeditOffset and \a LinkeditSize. // // When the eh_frame section is transferred, its offset and size are set resp. // to \a EHFrameOffset and \a EHFrameSize. -template <typename SegmentTy> -static void transferSegmentAndSections( - const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment, - const object::MachOObjectFile &Obj, MachObjectWriter &Writer, +template <typename SegmentTy> +static void transferSegmentAndSections( + const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment, + const object::MachOObjectFile &Obj, MachObjectWriter &Writer, uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t EHFrameOffset, uint64_t EHFrameSize, uint64_t DwarfSegmentSize, uint64_t &GapForDwarf, uint64_t &EndAddress) { - if (StringRef("__DWARF") == Segment.segname) - return; - + if (StringRef("__DWARF") == Segment.segname) + return; + if (StringRef("__TEXT") == Segment.segname && EHFrameSize > 0) { Segment.fileoff = EHFrameOffset; Segment.filesize = EHFrameSize; } else if (StringRef("__LINKEDIT") == Segment.segname) { - Segment.fileoff = LinkeditOffset; - Segment.filesize = LinkeditSize; - // Resize vmsize by rounding to the page size. - Segment.vmsize = alignTo(LinkeditSize, 0x1000); + Segment.fileoff = LinkeditOffset; + Segment.filesize = LinkeditSize; + // Resize vmsize by rounding to the page size. + Segment.vmsize = alignTo(LinkeditSize, 0x1000); } else { Segment.fileoff = Segment.filesize = 0; - } - - // Check if the end address of the last segment and our current - // start address leave a sufficient gap to store the __DWARF - // segment. - uint64_t PrevEndAddress = EndAddress; - EndAddress = alignTo(EndAddress, 0x1000); - if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress && - Segment.vmaddr - EndAddress >= DwarfSegmentSize) - GapForDwarf = EndAddress; - - // The segments are not necessarily sorted by their vmaddr. - EndAddress = - std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize); - unsigned nsects = Segment.nsects; - if (Obj.isLittleEndian() != sys::IsLittleEndianHost) - MachO::swapStruct(Segment); - Writer.W.OS.write(reinterpret_cast<char *>(&Segment), sizeof(Segment)); - for (unsigned i = 0; i < nsects; ++i) { - auto Sect = getSection(Obj, Segment, LCI, i); + } + + // Check if the end address of the last segment and our current + // start address leave a sufficient gap to store the __DWARF + // segment. + uint64_t PrevEndAddress = EndAddress; + EndAddress = alignTo(EndAddress, 0x1000); + if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress && + Segment.vmaddr - EndAddress >= DwarfSegmentSize) + GapForDwarf = EndAddress; + + // The segments are not necessarily sorted by their vmaddr. + EndAddress = + std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize); + unsigned nsects = Segment.nsects; + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Segment); + Writer.W.OS.write(reinterpret_cast<char *>(&Segment), sizeof(Segment)); + for (unsigned i = 0; i < nsects; ++i) { + auto Sect = getSection(Obj, Segment, LCI, i); if (StringRef("__eh_frame") == Sect.sectname) { Sect.offset = EHFrameOffset; Sect.reloff = Sect.nreloc = 0; } else { Sect.offset = Sect.reloff = Sect.nreloc = 0; } - if (Obj.isLittleEndian() != sys::IsLittleEndianHost) - MachO::swapStruct(Sect); - Writer.W.OS.write(reinterpret_cast<char *>(&Sect), sizeof(Sect)); - } -} - -// Write the __DWARF segment load command to the output file. -static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset, - uint64_t FileSize, unsigned NumSections, - MCAsmLayout &Layout, MachObjectWriter &Writer) { - Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr, - alignTo(FileSize, 0x1000), FileOffset, - FileSize, /* MaxProt */ 7, - /* InitProt =*/3); - - for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { - MCSection *Sec = Layout.getSectionOrder()[i]; - if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec)) - continue; - - unsigned Align = Sec->getAlignment(); - if (Align > 1) { - VMAddr = alignTo(VMAddr, Align); - FileOffset = alignTo(FileOffset, Align); - } - Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0); - - FileOffset += Layout.getSectionAddressSize(Sec); - VMAddr += Layout.getSectionAddressSize(Sec); - } -} - -static bool isExecutable(const object::MachOObjectFile &Obj) { - if (Obj.is64Bit()) - return Obj.getHeader64().filetype != MachO::MH_OBJECT; - else - return Obj.getHeader().filetype != MachO::MH_OBJECT; -} - -static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) { - bool HasLinkEditSegment = false; - iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) { - if (StringRef("__LINKEDIT") == Segment.segname) - HasLinkEditSegment = true; - }); - return HasLinkEditSegment; -} - -static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) { - if (Is64Bit) - return sizeof(MachO::segment_command_64) + - NumSections * sizeof(MachO::section_64); - - return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section); -} - -// Stream a dSYM companion binary file corresponding to the binary referenced -// by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to -// \a OutFile and it must be using a MachObjectWriter object to do so. -bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - const DebugMap &DM, SymbolMapTranslator &Translator, - MCStreamer &MS, raw_fd_ostream &OutFile) { - auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS); - MCAssembler &MCAsm = ObjectStreamer.getAssembler(); - auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter()); - - // Layout but don't emit. - ObjectStreamer.flushPendingLabels(); - MCAsmLayout Layout(MCAsm); - MCAsm.layout(Layout); - - BinaryHolder InputBinaryHolder(VFS, false); - - auto ObjectEntry = InputBinaryHolder.getObjectEntry(DM.getBinaryPath()); - if (!ObjectEntry) { - auto Err = ObjectEntry.takeError(); - return error(Twine("opening ") + DM.getBinaryPath() + ": " + - toString(std::move(Err)), - "output file streaming"); - } - - auto Object = - ObjectEntry->getObjectAs<object::MachOObjectFile>(DM.getTriple()); - if (!Object) { - auto Err = Object.takeError(); - return error(Twine("opening ") + DM.getBinaryPath() + ": " + - toString(std::move(Err)), - "output file streaming"); - } - - auto &InputBinary = *Object; - - bool Is64Bit = Writer.is64Bit(); - MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand(); - - // Compute the number of load commands we will need. - unsigned LoadCommandSize = 0; - unsigned NumLoadCommands = 0; - - // Get LC_UUID and LC_BUILD_VERSION. - MachO::uuid_command UUIDCmd; - SmallVector<MachO::build_version_command, 2> BuildVersionCmd; - memset(&UUIDCmd, 0, sizeof(UUIDCmd)); - for (auto &LCI : InputBinary.load_commands()) { - switch (LCI.C.cmd) { - case MachO::LC_UUID: - if (UUIDCmd.cmd) - return error("Binary contains more than one UUID"); - UUIDCmd = InputBinary.getUuidCommand(LCI); - ++NumLoadCommands; - LoadCommandSize += sizeof(UUIDCmd); - break; - case MachO::LC_BUILD_VERSION: { - MachO::build_version_command Cmd; - memset(&Cmd, 0, sizeof(Cmd)); - Cmd = InputBinary.getBuildVersionLoadCommand(LCI); - ++NumLoadCommands; - LoadCommandSize += sizeof(Cmd); - // LLDB doesn't care about the build tools for now. - Cmd.ntools = 0; - BuildVersionCmd.push_back(Cmd); - break; - } - default: - break; - } - } - - // If we have a valid symtab to copy, do it. - bool ShouldEmitSymtab = - isExecutable(InputBinary) && hasLinkEditSegment(InputBinary); - if (ShouldEmitSymtab) { - LoadCommandSize += sizeof(MachO::symtab_command); - ++NumLoadCommands; - } - + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Sect); + Writer.W.OS.write(reinterpret_cast<char *>(&Sect), sizeof(Sect)); + } +} + +// Write the __DWARF segment load command to the output file. +static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset, + uint64_t FileSize, unsigned NumSections, + MCAsmLayout &Layout, MachObjectWriter &Writer) { + Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr, + alignTo(FileSize, 0x1000), FileOffset, + FileSize, /* MaxProt */ 7, + /* InitProt =*/3); + + for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { + MCSection *Sec = Layout.getSectionOrder()[i]; + if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec)) + continue; + + unsigned Align = Sec->getAlignment(); + if (Align > 1) { + VMAddr = alignTo(VMAddr, Align); + FileOffset = alignTo(FileOffset, Align); + } + Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0); + + FileOffset += Layout.getSectionAddressSize(Sec); + VMAddr += Layout.getSectionAddressSize(Sec); + } +} + +static bool isExecutable(const object::MachOObjectFile &Obj) { + if (Obj.is64Bit()) + return Obj.getHeader64().filetype != MachO::MH_OBJECT; + else + return Obj.getHeader().filetype != MachO::MH_OBJECT; +} + +static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) { + bool HasLinkEditSegment = false; + iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) { + if (StringRef("__LINKEDIT") == Segment.segname) + HasLinkEditSegment = true; + }); + return HasLinkEditSegment; +} + +static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) { + if (Is64Bit) + return sizeof(MachO::segment_command_64) + + NumSections * sizeof(MachO::section_64); + + return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section); +} + +// Stream a dSYM companion binary file corresponding to the binary referenced +// by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to +// \a OutFile and it must be using a MachObjectWriter object to do so. +bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + const DebugMap &DM, SymbolMapTranslator &Translator, + MCStreamer &MS, raw_fd_ostream &OutFile) { + auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS); + MCAssembler &MCAsm = ObjectStreamer.getAssembler(); + auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter()); + + // Layout but don't emit. + ObjectStreamer.flushPendingLabels(); + MCAsmLayout Layout(MCAsm); + MCAsm.layout(Layout); + + BinaryHolder InputBinaryHolder(VFS, false); + + auto ObjectEntry = InputBinaryHolder.getObjectEntry(DM.getBinaryPath()); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + return error(Twine("opening ") + DM.getBinaryPath() + ": " + + toString(std::move(Err)), + "output file streaming"); + } + + auto Object = + ObjectEntry->getObjectAs<object::MachOObjectFile>(DM.getTriple()); + if (!Object) { + auto Err = Object.takeError(); + return error(Twine("opening ") + DM.getBinaryPath() + ": " + + toString(std::move(Err)), + "output file streaming"); + } + + auto &InputBinary = *Object; + + bool Is64Bit = Writer.is64Bit(); + MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand(); + + // Compute the number of load commands we will need. + unsigned LoadCommandSize = 0; + unsigned NumLoadCommands = 0; + + // Get LC_UUID and LC_BUILD_VERSION. + MachO::uuid_command UUIDCmd; + SmallVector<MachO::build_version_command, 2> BuildVersionCmd; + memset(&UUIDCmd, 0, sizeof(UUIDCmd)); + for (auto &LCI : InputBinary.load_commands()) { + switch (LCI.C.cmd) { + case MachO::LC_UUID: + if (UUIDCmd.cmd) + return error("Binary contains more than one UUID"); + UUIDCmd = InputBinary.getUuidCommand(LCI); + ++NumLoadCommands; + LoadCommandSize += sizeof(UUIDCmd); + break; + case MachO::LC_BUILD_VERSION: { + MachO::build_version_command Cmd; + memset(&Cmd, 0, sizeof(Cmd)); + Cmd = InputBinary.getBuildVersionLoadCommand(LCI); + ++NumLoadCommands; + LoadCommandSize += sizeof(Cmd); + // LLDB doesn't care about the build tools for now. + Cmd.ntools = 0; + BuildVersionCmd.push_back(Cmd); + break; + } + default: + break; + } + } + + // If we have a valid symtab to copy, do it. + bool ShouldEmitSymtab = + isExecutable(InputBinary) && hasLinkEditSegment(InputBinary); + if (ShouldEmitSymtab) { + LoadCommandSize += sizeof(MachO::symtab_command); + ++NumLoadCommands; + } + // If we have a valid eh_frame to copy, do it. uint64_t EHFrameSize = 0; StringRef EHFrameData; @@ -452,148 +452,148 @@ bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, } } - unsigned HeaderSize = - Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - // We will copy every segment that isn't __DWARF. - iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) { - if (StringRef("__DWARF") == Segment.segname) - return; - - ++NumLoadCommands; - LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects); - }); - - // We will add our own brand new __DWARF segment if we have debug - // info. - unsigned NumDwarfSections = 0; - uint64_t DwarfSegmentSize = 0; - - for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { - MCSection *Sec = Layout.getSectionOrder()[i]; - if (Sec->begin() == Sec->end()) - continue; - - if (uint64_t Size = Layout.getSectionFileSize(Sec)) { - DwarfSegmentSize = alignTo(DwarfSegmentSize, Sec->getAlignment()); - DwarfSegmentSize += Size; - ++NumDwarfSections; - } - } - - if (NumDwarfSections) { - ++NumLoadCommands; - LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections); - } - - SmallString<0> NewSymtab; - std::function<StringRef(StringRef)> TranslationLambda = - Translator ? [&](StringRef Input) { return Translator(Input); } - : static_cast<std::function<StringRef(StringRef)>>(nullptr); - // Legacy dsymutil puts an empty string at the start of the line table. - // thus we set NonRelocatableStringpool(,PutEmptyString=true) - NonRelocatableStringpool NewStrings(TranslationLambda, true); - unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); - unsigned NumSyms = 0; - uint64_t NewStringsSize = 0; - if (ShouldEmitSymtab) { - NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2); - NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings); - NewStringsSize = NewStrings.getSize() + 1; - } - - uint64_t SymtabStart = LoadCommandSize; - SymtabStart += HeaderSize; - SymtabStart = alignTo(SymtabStart, 0x1000); - - // We gathered all the information we need, start emitting the output file. - Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false); - - // Write the load commands. - assert(OutFile.tell() == HeaderSize); - if (UUIDCmd.cmd != 0) { - Writer.W.write<uint32_t>(UUIDCmd.cmd); - Writer.W.write<uint32_t>(sizeof(UUIDCmd)); - OutFile.write(reinterpret_cast<const char *>(UUIDCmd.uuid), 16); - assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd)); - } - for (auto Cmd : BuildVersionCmd) { - Writer.W.write<uint32_t>(Cmd.cmd); - Writer.W.write<uint32_t>(sizeof(Cmd)); - Writer.W.write<uint32_t>(Cmd.platform); - Writer.W.write<uint32_t>(Cmd.minos); - Writer.W.write<uint32_t>(Cmd.sdk); - Writer.W.write<uint32_t>(Cmd.ntools); - } - - assert(SymtabCmd.cmd && "No symbol table."); - uint64_t StringStart = SymtabStart + NumSyms * NListSize; - if (ShouldEmitSymtab) - Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart, - NewStringsSize); - + unsigned HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + // We will copy every segment that isn't __DWARF. + iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) { + if (StringRef("__DWARF") == Segment.segname) + return; + + ++NumLoadCommands; + LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects); + }); + + // We will add our own brand new __DWARF segment if we have debug + // info. + unsigned NumDwarfSections = 0; + uint64_t DwarfSegmentSize = 0; + + for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { + MCSection *Sec = Layout.getSectionOrder()[i]; + if (Sec->begin() == Sec->end()) + continue; + + if (uint64_t Size = Layout.getSectionFileSize(Sec)) { + DwarfSegmentSize = alignTo(DwarfSegmentSize, Sec->getAlignment()); + DwarfSegmentSize += Size; + ++NumDwarfSections; + } + } + + if (NumDwarfSections) { + ++NumLoadCommands; + LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections); + } + + SmallString<0> NewSymtab; + std::function<StringRef(StringRef)> TranslationLambda = + Translator ? [&](StringRef Input) { return Translator(Input); } + : static_cast<std::function<StringRef(StringRef)>>(nullptr); + // Legacy dsymutil puts an empty string at the start of the line table. + // thus we set NonRelocatableStringpool(,PutEmptyString=true) + NonRelocatableStringpool NewStrings(TranslationLambda, true); + unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); + unsigned NumSyms = 0; + uint64_t NewStringsSize = 0; + if (ShouldEmitSymtab) { + NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2); + NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings); + NewStringsSize = NewStrings.getSize() + 1; + } + + uint64_t SymtabStart = LoadCommandSize; + SymtabStart += HeaderSize; + SymtabStart = alignTo(SymtabStart, 0x1000); + + // We gathered all the information we need, start emitting the output file. + Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false); + + // Write the load commands. + assert(OutFile.tell() == HeaderSize); + if (UUIDCmd.cmd != 0) { + Writer.W.write<uint32_t>(UUIDCmd.cmd); + Writer.W.write<uint32_t>(sizeof(UUIDCmd)); + OutFile.write(reinterpret_cast<const char *>(UUIDCmd.uuid), 16); + assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd)); + } + for (auto Cmd : BuildVersionCmd) { + Writer.W.write<uint32_t>(Cmd.cmd); + Writer.W.write<uint32_t>(sizeof(Cmd)); + Writer.W.write<uint32_t>(Cmd.platform); + Writer.W.write<uint32_t>(Cmd.minos); + Writer.W.write<uint32_t>(Cmd.sdk); + Writer.W.write<uint32_t>(Cmd.ntools); + } + + assert(SymtabCmd.cmd && "No symbol table."); + uint64_t StringStart = SymtabStart + NumSyms * NListSize; + if (ShouldEmitSymtab) + Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart, + NewStringsSize); + uint64_t EHFrameStart = StringStart + NewStringsSize; EHFrameStart = alignTo(EHFrameStart, 0x1000); uint64_t DwarfSegmentStart = EHFrameStart + EHFrameSize; - DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000); - - // Write the load commands for the segments and sections we 'import' from - // the original binary. - uint64_t EndAddress = 0; - uint64_t GapForDwarf = UINT64_MAX; - for (auto &LCI : InputBinary.load_commands()) { - if (LCI.C.cmd == MachO::LC_SEGMENT) + DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000); + + // Write the load commands for the segments and sections we 'import' from + // the original binary. + uint64_t EndAddress = 0; + uint64_t GapForDwarf = UINT64_MAX; + for (auto &LCI : InputBinary.load_commands()) { + if (LCI.C.cmd == MachO::LC_SEGMENT) transferSegmentAndSections( LCI, InputBinary.getSegmentLoadCommand(LCI), InputBinary, Writer, SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart, EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress); - else if (LCI.C.cmd == MachO::LC_SEGMENT_64) + else if (LCI.C.cmd == MachO::LC_SEGMENT_64) transferSegmentAndSections( LCI, InputBinary.getSegment64LoadCommand(LCI), InputBinary, Writer, SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart, EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress); - } - - uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000); - uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX; - if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax || - DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) { - // There is no room for the __DWARF segment at the end of the - // address space. Look through segments to find a gap. - DwarfVMAddr = GapForDwarf; - if (DwarfVMAddr == UINT64_MAX) - warn("not enough VM space for the __DWARF segment.", - "output file streaming"); - } - - // Write the load command for the __DWARF segment. - createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize, - NumDwarfSections, Layout, Writer); - - assert(OutFile.tell() == LoadCommandSize + HeaderSize); - OutFile.write_zeros(SymtabStart - (LoadCommandSize + HeaderSize)); - assert(OutFile.tell() == SymtabStart); - - // Transfer symbols. - if (ShouldEmitSymtab) { - OutFile << NewSymtab.str(); - assert(OutFile.tell() == StringStart); - - // Transfer string table. - // FIXME: The NonRelocatableStringpool starts with an empty string, but - // dsymutil-classic starts the reconstructed string table with 2 of these. - // Reproduce that behavior for now (there is corresponding code in - // transferSymbol). - OutFile << '\0'; - std::vector<DwarfStringPoolEntryRef> Strings = - NewStrings.getEntriesForEmission(); - for (auto EntryRef : Strings) { - OutFile.write(EntryRef.getString().data(), - EntryRef.getString().size() + 1); - } - } - assert(OutFile.tell() == StringStart + NewStringsSize); - + } + + uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000); + uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX; + if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax || + DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) { + // There is no room for the __DWARF segment at the end of the + // address space. Look through segments to find a gap. + DwarfVMAddr = GapForDwarf; + if (DwarfVMAddr == UINT64_MAX) + warn("not enough VM space for the __DWARF segment.", + "output file streaming"); + } + + // Write the load command for the __DWARF segment. + createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize, + NumDwarfSections, Layout, Writer); + + assert(OutFile.tell() == LoadCommandSize + HeaderSize); + OutFile.write_zeros(SymtabStart - (LoadCommandSize + HeaderSize)); + assert(OutFile.tell() == SymtabStart); + + // Transfer symbols. + if (ShouldEmitSymtab) { + OutFile << NewSymtab.str(); + assert(OutFile.tell() == StringStart); + + // Transfer string table. + // FIXME: The NonRelocatableStringpool starts with an empty string, but + // dsymutil-classic starts the reconstructed string table with 2 of these. + // Reproduce that behavior for now (there is corresponding code in + // transferSymbol). + OutFile << '\0'; + std::vector<DwarfStringPoolEntryRef> Strings = + NewStrings.getEntriesForEmission(); + for (auto EntryRef : Strings) { + OutFile.write(EntryRef.getString().data(), + EntryRef.getString().size() + 1); + } + } + assert(OutFile.tell() == StringStart + NewStringsSize); + // Pad till the EH frame start. OutFile.write_zeros(EHFrameStart - (StringStart + NewStringsSize)); assert(OutFile.tell() == EHFrameStart); @@ -603,22 +603,22 @@ bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, OutFile << EHFrameData; assert(OutFile.tell() == EHFrameStart + EHFrameSize); - // Pad till the Dwarf segment start. + // Pad till the Dwarf segment start. OutFile.write_zeros(DwarfSegmentStart - (EHFrameStart + EHFrameSize)); - assert(OutFile.tell() == DwarfSegmentStart); - - // Emit the Dwarf sections contents. - for (const MCSection &Sec : MCAsm) { - if (Sec.begin() == Sec.end()) - continue; - - uint64_t Pos = OutFile.tell(); - OutFile.write_zeros(alignTo(Pos, Sec.getAlignment()) - Pos); - MCAsm.writeSectionData(OutFile, &Sec, Layout); - } - - return true; -} -} // namespace MachOUtils -} // namespace dsymutil -} // namespace llvm + assert(OutFile.tell() == DwarfSegmentStart); + + // Emit the Dwarf sections contents. + for (const MCSection &Sec : MCAsm) { + if (Sec.begin() == Sec.end()) + continue; + + uint64_t Pos = OutFile.tell(); + OutFile.write_zeros(alignTo(Pos, Sec.getAlignment()) - Pos); + MCAsm.writeSectionData(OutFile, &Sec, Layout); + } + + return true; +} +} // namespace MachOUtils +} // namespace dsymutil +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/MachOUtils.h b/contrib/libs/llvm12/tools/dsymutil/MachOUtils.h index b1cdd44d38..109da802b0 100644 --- a/contrib/libs/llvm12/tools/dsymutil/MachOUtils.h +++ b/contrib/libs/llvm12/tools/dsymutil/MachOUtils.h @@ -1,52 +1,52 @@ -//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H -#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H - -#include "SymbolMap.h" - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/VirtualFileSystem.h" - -#include <string> - -namespace llvm { -class MCStreamer; -class raw_fd_ostream; -namespace dsymutil { -class DebugMap; -struct LinkOptions; -namespace MachOUtils { - -struct ArchAndFile { - std::string Arch; - std::unique_ptr<llvm::sys::fs::TempFile> File; - - llvm::Error createTempFile(); - llvm::StringRef path() const; - - ArchAndFile(StringRef Arch) : Arch(std::string(Arch)) {} - ArchAndFile(ArchAndFile &&A) = default; - ArchAndFile &operator=(ArchAndFile &&A) = default; - ~ArchAndFile(); -}; - -bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles, - StringRef OutputFileName, const LinkOptions &, - StringRef SDKPath); - -bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - const DebugMap &DM, SymbolMapTranslator &Translator, - MCStreamer &MS, raw_fd_ostream &OutFile); - -std::string getArchName(StringRef Arch); -} // namespace MachOUtils -} // namespace dsymutil -} // namespace llvm -#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H +//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H +#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H + +#include "SymbolMap.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include <string> + +namespace llvm { +class MCStreamer; +class raw_fd_ostream; +namespace dsymutil { +class DebugMap; +struct LinkOptions; +namespace MachOUtils { + +struct ArchAndFile { + std::string Arch; + std::unique_ptr<llvm::sys::fs::TempFile> File; + + llvm::Error createTempFile(); + llvm::StringRef path() const; + + ArchAndFile(StringRef Arch) : Arch(std::string(Arch)) {} + ArchAndFile(ArchAndFile &&A) = default; + ArchAndFile &operator=(ArchAndFile &&A) = default; + ~ArchAndFile(); +}; + +bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles, + StringRef OutputFileName, const LinkOptions &, + StringRef SDKPath); + +bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + const DebugMap &DM, SymbolMapTranslator &Translator, + MCStreamer &MS, raw_fd_ostream &OutFile); + +std::string getArchName(StringRef Arch); +} // namespace MachOUtils +} // namespace dsymutil +} // namespace llvm +#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H diff --git a/contrib/libs/llvm12/tools/dsymutil/Options.td b/contrib/libs/llvm12/tools/dsymutil/Options.td index 7bd21b0d43..f704467115 100644 --- a/contrib/libs/llvm12/tools/dsymutil/Options.td +++ b/contrib/libs/llvm12/tools/dsymutil/Options.td @@ -1,187 +1,187 @@ -include "llvm/Option/OptParser.td" - -class F<string name>: Flag<["--", "-"], name>; - -def grp_general : OptionGroup<"Dsymutil">, HelpText<"Dsymutil Options">; - -def help: F<"help">, - HelpText<"Prints this help output.">, - Group<grp_general>; -def: Flag<["-"], "h">, - Alias<help>, - HelpText<"Alias for --help">, - Group<grp_general>; - -def version: F<"version">, - HelpText<"Prints the dsymutil version.">, - Group<grp_general>; -def: Flag<["-"], "v">, - Alias<version>, - HelpText<"Alias for --version">, - Group<grp_general>; - -def verbose: F<"verbose">, - HelpText<"Enable verbose mode.">, - Group<grp_general>; - -def statistics: F<"statistics">, - HelpText<"Print statistics about the contribution of each object file to " - "the linked debug info. This prints a table after linking with the " - "object file name, the size of the debug info in the object file " - "(in bytes) and the size contributed (in bytes) to the linked dSYM. " - "The table is sorted by the output size listing the object files " - "with the largest contribution first.">, - Group<grp_general>; - -def verify: F<"verify">, - HelpText<"Run the DWARF verifier on the linked DWARF debug info.">, - Group<grp_general>; - -def no_output: F<"no-output">, - HelpText<"Do the link in memory, but do not emit the result file.">, - Group<grp_general>; - -def no_swiftmodule_timestamp: F<"no-swiftmodule-timestamp">, - HelpText<"Don't check timestamp for swiftmodule files.">, - Group<grp_general>; - -def no_odr: F<"no-odr">, - HelpText<"Do not use ODR (One Definition Rule) for type uniquing.">, - Group<grp_general>; - -def dump_debug_map: F<"dump-debug-map">, - HelpText<"Parse and dump the debug map to standard output. Not DWARF link will take place.">, - Group<grp_general>; - -def yaml_input: Flag<["-", "--"], "y">, - HelpText<"Treat the input file is a YAML debug map rather than a binary.">, - Group<grp_general>; - -def papertrail: F<"papertrail">, - HelpText<"Embed warnings in the linked DWARF debug info.">, - Group<grp_general>; - -def assembly: Flag<["-", "--"], "S">, - HelpText<"Output textual assembly instead of a binary dSYM companion file.">, - Group<grp_general>; - -def symtab: F<"symtab">, - HelpText<"Dumps the symbol table found in executable or object file(s) and exits.">, - Group<grp_general>; -def: Flag<["-"], "s">, - Alias<symtab>, - HelpText<"Alias for --symtab">, - Group<grp_general>; - -def flat: F<"flat">, - HelpText<"Produce a flat dSYM file (not a bundle).">, - Group<grp_general>; -def: Flag<["-"], "f">, - Alias<flat>, - HelpText<"Alias for --flat">, - Group<grp_general>; - -def minimize: F<"minimize">, - HelpText<"When used when creating a dSYM file with Apple accelerator tables, " - "this option will suppress the emission of the .debug_inlines, " - ".debug_pubnames, and .debug_pubtypes sections since dsymutil " - "has better equivalents: .apple_names and .apple_types. When used in " - "conjunction with --update option, this option will cause redundant " - "accelerator tables to be removed.">, - Group<grp_general>; -def: Flag<["-"], "z">, - Alias<minimize>, - HelpText<"Alias for --minimize">, - Group<grp_general>; - -def update: F<"update">, - HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">, - Group<grp_general>; -def: Flag<["-"], "u">, - Alias<update>, - HelpText<"Alias for --update">, - Group<grp_general>; - -def output: Separate<["-", "--"], "o">, - MetaVarName<"<filename>">, - HelpText<"Specify the output file. Defaults to <input file>.dwarf">, - Group<grp_general>; -def: Separate<["--", "-"], "out">, - MetaVarName<"<filename>">, - Alias<output>, - HelpText<"Alias for -o">, - Group<grp_general>; -def: Joined<["--", "-"], "out=">, Alias<output>; -def: Joined<["-", "--"], "o=">, Alias<output>; - -def oso_prepend_path: Separate<["--", "-"], "oso-prepend-path">, - MetaVarName<"<path>">, - HelpText<"Specify a directory to prepend to the paths of object files.">, - Group<grp_general>; -def: Joined<["--", "-"], "oso-prepend-path=">, Alias<oso_prepend_path>; - -def object_prefix_map: Separate<["--", "-"], "object-prefix-map">, - MetaVarName<"<prefix=remapped>">, - HelpText<"Remap object file paths (but no source paths) before processing." - "Use this for Clang objects where the module cache location was" - "remapped using -fdebug-prefix-map; to help dsymutil" - "find the Clang module cache.">, - Group<grp_general>; -def: Joined<["--", "-"], "object-prefix-map=">, Alias<object_prefix_map>; - -def symbolmap: Separate<["--", "-"], "symbol-map">, - MetaVarName<"<bcsymbolmap>">, - HelpText<"Updates the existing dSYMs inplace using symbol map specified.">, - Group<grp_general>; -def: Joined<["--", "-"], "symbol-map=">, Alias<symbolmap>; - -def arch: Separate<["--", "-"], "arch">, - MetaVarName<"<arch>">, - HelpText<"Link DWARF debug information only for specified CPU architecture" - "types. This option can be specified multiple times, once for each" - "desired architecture. All CPU architectures will be linked by" - "default.">, - Group<grp_general>; -def: Joined<["--", "-"], "arch=">, Alias<arch>; - -def accelerator: Separate<["--", "-"], "accelerator">, - MetaVarName<"<accelerator type>">, - HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple', 'Dwarf' and 'Default'">, - Group<grp_general>; -def: Joined<["--", "-"], "accelerator=">, Alias<accelerator>; - -def toolchain: Separate<["--", "-"], "toolchain">, - MetaVarName<"<toolchain>">, - HelpText<"Embed toolchain information in dSYM bundle.">, - Group<grp_general>; - -def threads: Separate<["--", "-"], "num-threads">, - MetaVarName<"<threads>">, - HelpText<"Specifies the maximum number of simultaneous threads to use when linking multiple architectures.">, - Group<grp_general>; -def: Separate<["-"], "j">, - MetaVarName<"<threads>">, - HelpText<"Alias for --num-threads">, - Group<grp_general>; - -def gen_reproducer: F<"gen-reproducer">, - HelpText<"Generate a reproducer consisting of the input object files.">, - Group<grp_general>; - -def use_reproducer: Separate<["--", "-"], "use-reproducer">, - MetaVarName<"<path>">, - HelpText<"Use the object files from the given reproducer path.">, - Group<grp_general>; -def: Joined<["--", "-"], "use-reproducer=">, Alias<use_reproducer>; - -def remarks_prepend_path: Separate<["--", "-"], "remarks-prepend-path">, - MetaVarName<"<path>">, - HelpText<"Specify a directory to prepend to the paths of the external remark files.">, - Group<grp_general>; -def: Joined<["--", "-"], "remarks-prepend-path=">, Alias<remarks_prepend_path>; - -def remarks_output_format: Separate<["--", "-"], "remarks-output-format">, - MetaVarName<"<format>">, - HelpText<"Specify the format to be used when serializing the linked remarks.">, - Group<grp_general>; -def: Joined<["--", "-"], "remarks-output-format=">, Alias<remarks_output_format>; +include "llvm/Option/OptParser.td" + +class F<string name>: Flag<["--", "-"], name>; + +def grp_general : OptionGroup<"Dsymutil">, HelpText<"Dsymutil Options">; + +def help: F<"help">, + HelpText<"Prints this help output.">, + Group<grp_general>; +def: Flag<["-"], "h">, + Alias<help>, + HelpText<"Alias for --help">, + Group<grp_general>; + +def version: F<"version">, + HelpText<"Prints the dsymutil version.">, + Group<grp_general>; +def: Flag<["-"], "v">, + Alias<version>, + HelpText<"Alias for --version">, + Group<grp_general>; + +def verbose: F<"verbose">, + HelpText<"Enable verbose mode.">, + Group<grp_general>; + +def statistics: F<"statistics">, + HelpText<"Print statistics about the contribution of each object file to " + "the linked debug info. This prints a table after linking with the " + "object file name, the size of the debug info in the object file " + "(in bytes) and the size contributed (in bytes) to the linked dSYM. " + "The table is sorted by the output size listing the object files " + "with the largest contribution first.">, + Group<grp_general>; + +def verify: F<"verify">, + HelpText<"Run the DWARF verifier on the linked DWARF debug info.">, + Group<grp_general>; + +def no_output: F<"no-output">, + HelpText<"Do the link in memory, but do not emit the result file.">, + Group<grp_general>; + +def no_swiftmodule_timestamp: F<"no-swiftmodule-timestamp">, + HelpText<"Don't check timestamp for swiftmodule files.">, + Group<grp_general>; + +def no_odr: F<"no-odr">, + HelpText<"Do not use ODR (One Definition Rule) for type uniquing.">, + Group<grp_general>; + +def dump_debug_map: F<"dump-debug-map">, + HelpText<"Parse and dump the debug map to standard output. Not DWARF link will take place.">, + Group<grp_general>; + +def yaml_input: Flag<["-", "--"], "y">, + HelpText<"Treat the input file is a YAML debug map rather than a binary.">, + Group<grp_general>; + +def papertrail: F<"papertrail">, + HelpText<"Embed warnings in the linked DWARF debug info.">, + Group<grp_general>; + +def assembly: Flag<["-", "--"], "S">, + HelpText<"Output textual assembly instead of a binary dSYM companion file.">, + Group<grp_general>; + +def symtab: F<"symtab">, + HelpText<"Dumps the symbol table found in executable or object file(s) and exits.">, + Group<grp_general>; +def: Flag<["-"], "s">, + Alias<symtab>, + HelpText<"Alias for --symtab">, + Group<grp_general>; + +def flat: F<"flat">, + HelpText<"Produce a flat dSYM file (not a bundle).">, + Group<grp_general>; +def: Flag<["-"], "f">, + Alias<flat>, + HelpText<"Alias for --flat">, + Group<grp_general>; + +def minimize: F<"minimize">, + HelpText<"When used when creating a dSYM file with Apple accelerator tables, " + "this option will suppress the emission of the .debug_inlines, " + ".debug_pubnames, and .debug_pubtypes sections since dsymutil " + "has better equivalents: .apple_names and .apple_types. When used in " + "conjunction with --update option, this option will cause redundant " + "accelerator tables to be removed.">, + Group<grp_general>; +def: Flag<["-"], "z">, + Alias<minimize>, + HelpText<"Alias for --minimize">, + Group<grp_general>; + +def update: F<"update">, + HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">, + Group<grp_general>; +def: Flag<["-"], "u">, + Alias<update>, + HelpText<"Alias for --update">, + Group<grp_general>; + +def output: Separate<["-", "--"], "o">, + MetaVarName<"<filename>">, + HelpText<"Specify the output file. Defaults to <input file>.dwarf">, + Group<grp_general>; +def: Separate<["--", "-"], "out">, + MetaVarName<"<filename>">, + Alias<output>, + HelpText<"Alias for -o">, + Group<grp_general>; +def: Joined<["--", "-"], "out=">, Alias<output>; +def: Joined<["-", "--"], "o=">, Alias<output>; + +def oso_prepend_path: Separate<["--", "-"], "oso-prepend-path">, + MetaVarName<"<path>">, + HelpText<"Specify a directory to prepend to the paths of object files.">, + Group<grp_general>; +def: Joined<["--", "-"], "oso-prepend-path=">, Alias<oso_prepend_path>; + +def object_prefix_map: Separate<["--", "-"], "object-prefix-map">, + MetaVarName<"<prefix=remapped>">, + HelpText<"Remap object file paths (but no source paths) before processing." + "Use this for Clang objects where the module cache location was" + "remapped using -fdebug-prefix-map; to help dsymutil" + "find the Clang module cache.">, + Group<grp_general>; +def: Joined<["--", "-"], "object-prefix-map=">, Alias<object_prefix_map>; + +def symbolmap: Separate<["--", "-"], "symbol-map">, + MetaVarName<"<bcsymbolmap>">, + HelpText<"Updates the existing dSYMs inplace using symbol map specified.">, + Group<grp_general>; +def: Joined<["--", "-"], "symbol-map=">, Alias<symbolmap>; + +def arch: Separate<["--", "-"], "arch">, + MetaVarName<"<arch>">, + HelpText<"Link DWARF debug information only for specified CPU architecture" + "types. This option can be specified multiple times, once for each" + "desired architecture. All CPU architectures will be linked by" + "default.">, + Group<grp_general>; +def: Joined<["--", "-"], "arch=">, Alias<arch>; + +def accelerator: Separate<["--", "-"], "accelerator">, + MetaVarName<"<accelerator type>">, + HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple', 'Dwarf' and 'Default'">, + Group<grp_general>; +def: Joined<["--", "-"], "accelerator=">, Alias<accelerator>; + +def toolchain: Separate<["--", "-"], "toolchain">, + MetaVarName<"<toolchain>">, + HelpText<"Embed toolchain information in dSYM bundle.">, + Group<grp_general>; + +def threads: Separate<["--", "-"], "num-threads">, + MetaVarName<"<threads>">, + HelpText<"Specifies the maximum number of simultaneous threads to use when linking multiple architectures.">, + Group<grp_general>; +def: Separate<["-"], "j">, + MetaVarName<"<threads>">, + HelpText<"Alias for --num-threads">, + Group<grp_general>; + +def gen_reproducer: F<"gen-reproducer">, + HelpText<"Generate a reproducer consisting of the input object files.">, + Group<grp_general>; + +def use_reproducer: Separate<["--", "-"], "use-reproducer">, + MetaVarName<"<path>">, + HelpText<"Use the object files from the given reproducer path.">, + Group<grp_general>; +def: Joined<["--", "-"], "use-reproducer=">, Alias<use_reproducer>; + +def remarks_prepend_path: Separate<["--", "-"], "remarks-prepend-path">, + MetaVarName<"<path>">, + HelpText<"Specify a directory to prepend to the paths of the external remark files.">, + Group<grp_general>; +def: Joined<["--", "-"], "remarks-prepend-path=">, Alias<remarks_prepend_path>; + +def remarks_output_format: Separate<["--", "-"], "remarks-output-format">, + MetaVarName<"<format>">, + HelpText<"Specify the format to be used when serializing the linked remarks.">, + Group<grp_general>; +def: Joined<["--", "-"], "remarks-output-format=">, Alias<remarks_output_format>; diff --git a/contrib/libs/llvm12/tools/dsymutil/Reproducer.cpp b/contrib/libs/llvm12/tools/dsymutil/Reproducer.cpp index 5c60758c6f..d064086b0f 100644 --- a/contrib/libs/llvm12/tools/dsymutil/Reproducer.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/Reproducer.cpp @@ -1,85 +1,85 @@ -//===- Reproducer.cpp -----------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Reproducer.h" -#include "llvm/Support/Path.h" - -using namespace llvm; -using namespace llvm::dsymutil; - -static std::string createReproducerDir(std::error_code &EC) { - SmallString<128> Root; - if (const char *Path = getenv("DSYMUTIL_REPRODUCER_PATH")) { - Root.assign(Path); - EC = sys::fs::create_directory(Root); - } else { - EC = sys::fs::createUniqueDirectory("dsymutil", Root); - } - return EC ? "" : std::string(Root); -} - -Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {} -Reproducer::~Reproducer() = default; - -ReproducerGenerate::ReproducerGenerate(std::error_code &EC) - : Root(createReproducerDir(EC)), FC() { - if (!Root.empty()) - FC = std::make_shared<FileCollector>(Root, Root); - VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC); -} - -ReproducerGenerate::~ReproducerGenerate() { - if (!FC) - return; - FC->copyFiles(false); - SmallString<128> Mapping(Root); - sys::path::append(Mapping, "mapping.yaml"); - FC->writeMapping(Mapping.str()); - outs() << "reproducer written to " << Root << '\n'; -} - -ReproducerUse::~ReproducerUse() = default; - -ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) { - SmallString<128> Mapping(Root); - sys::path::append(Mapping, "mapping.yaml"); - ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = - vfs::getRealFileSystem()->getBufferForFile(Mapping.str()); - - if (!Buffer) { - EC = Buffer.getError(); - return; - } - - VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, Mapping); -} - -llvm::Expected<std::unique_ptr<Reproducer>> -Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) { - switch (Mode) { - case ReproducerMode::Generate: { - std::error_code EC; - std::unique_ptr<Reproducer> Repro = - std::make_unique<ReproducerGenerate>(EC); - if (EC) - return errorCodeToError(EC); - return std::move(Repro); - } - case ReproducerMode::Use: { - std::error_code EC; - std::unique_ptr<Reproducer> Repro = - std::make_unique<ReproducerUse>(Root, EC); - if (EC) - return errorCodeToError(EC); - return std::move(Repro); - } - case ReproducerMode::Off: - return std::make_unique<Reproducer>(); - } - llvm_unreachable("All cases handled above."); -} +//===- Reproducer.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Reproducer.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::dsymutil; + +static std::string createReproducerDir(std::error_code &EC) { + SmallString<128> Root; + if (const char *Path = getenv("DSYMUTIL_REPRODUCER_PATH")) { + Root.assign(Path); + EC = sys::fs::create_directory(Root); + } else { + EC = sys::fs::createUniqueDirectory("dsymutil", Root); + } + return EC ? "" : std::string(Root); +} + +Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {} +Reproducer::~Reproducer() = default; + +ReproducerGenerate::ReproducerGenerate(std::error_code &EC) + : Root(createReproducerDir(EC)), FC() { + if (!Root.empty()) + FC = std::make_shared<FileCollector>(Root, Root); + VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC); +} + +ReproducerGenerate::~ReproducerGenerate() { + if (!FC) + return; + FC->copyFiles(false); + SmallString<128> Mapping(Root); + sys::path::append(Mapping, "mapping.yaml"); + FC->writeMapping(Mapping.str()); + outs() << "reproducer written to " << Root << '\n'; +} + +ReproducerUse::~ReproducerUse() = default; + +ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) { + SmallString<128> Mapping(Root); + sys::path::append(Mapping, "mapping.yaml"); + ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = + vfs::getRealFileSystem()->getBufferForFile(Mapping.str()); + + if (!Buffer) { + EC = Buffer.getError(); + return; + } + + VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, Mapping); +} + +llvm::Expected<std::unique_ptr<Reproducer>> +Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) { + switch (Mode) { + case ReproducerMode::Generate: { + std::error_code EC; + std::unique_ptr<Reproducer> Repro = + std::make_unique<ReproducerGenerate>(EC); + if (EC) + return errorCodeToError(EC); + return std::move(Repro); + } + case ReproducerMode::Use: { + std::error_code EC; + std::unique_ptr<Reproducer> Repro = + std::make_unique<ReproducerUse>(Root, EC); + if (EC) + return errorCodeToError(EC); + return std::move(Repro); + } + case ReproducerMode::Off: + return std::make_unique<Reproducer>(); + } + llvm_unreachable("All cases handled above."); +} diff --git a/contrib/libs/llvm12/tools/dsymutil/Reproducer.h b/contrib/libs/llvm12/tools/dsymutil/Reproducer.h index e965e1ceda..8b3adf63f3 100644 --- a/contrib/libs/llvm12/tools/dsymutil/Reproducer.h +++ b/contrib/libs/llvm12/tools/dsymutil/Reproducer.h @@ -1,77 +1,77 @@ -//===- tools/dsymutil/Reproducer.h ------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_REPRODUCER_H -#define LLVM_TOOLS_DSYMUTIL_REPRODUCER_H - -#include "llvm/Support/Error.h" -#include "llvm/Support/FileCollector.h" -#include "llvm/Support/VirtualFileSystem.h" - -namespace llvm { -namespace dsymutil { - -/// The reproducer mode. -enum class ReproducerMode { - Generate, - Use, - Off, -}; - -/// The reproducer class manages the sate related to reproducers in dsymutil. -/// Instances should be created with Reproducer::createReproducer. An instance -/// of this class is returned when reproducers are off. The VFS returned by -/// this instance is the real file system. -class Reproducer { -public: - Reproducer(); - virtual ~Reproducer(); - - IntrusiveRefCntPtr<vfs::FileSystem> getVFS() const { return VFS; } - - /// Create a Reproducer instance based on the given mode. - static llvm::Expected<std::unique_ptr<Reproducer>> - createReproducer(ReproducerMode Mode, StringRef Root); - -protected: - IntrusiveRefCntPtr<vfs::FileSystem> VFS; -}; - -/// Reproducer instance used to generate a new reproducer. The VFS returned by -/// this instance is a FileCollectorFileSystem that tracks every file used by -/// dsymutil. -class ReproducerGenerate : public Reproducer { -public: - ReproducerGenerate(std::error_code &EC); - ~ReproducerGenerate() override; - -private: - /// The path to the reproducer. - std::string Root; - - /// The FileCollector used by the FileCollectorFileSystem. - std::shared_ptr<FileCollector> FC; -}; - -/// Reproducer instance used to use an existing reproducer. The VFS returned by -/// this instance is a RedirectingFileSystem that remaps paths to their -/// counterpart in the reproducer. -class ReproducerUse : public Reproducer { -public: - ReproducerUse(StringRef Root, std::error_code &EC); - ~ReproducerUse() override; - -private: - /// The path to the reproducer. - std::string Root; -}; - -} // end namespace dsymutil -} // end namespace llvm - -#endif // LLVM_TOOLS_DSYMUTIL_REPRODUCER_H +//===- tools/dsymutil/Reproducer.h ------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_REPRODUCER_H +#define LLVM_TOOLS_DSYMUTIL_REPRODUCER_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/FileCollector.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace llvm { +namespace dsymutil { + +/// The reproducer mode. +enum class ReproducerMode { + Generate, + Use, + Off, +}; + +/// The reproducer class manages the sate related to reproducers in dsymutil. +/// Instances should be created with Reproducer::createReproducer. An instance +/// of this class is returned when reproducers are off. The VFS returned by +/// this instance is the real file system. +class Reproducer { +public: + Reproducer(); + virtual ~Reproducer(); + + IntrusiveRefCntPtr<vfs::FileSystem> getVFS() const { return VFS; } + + /// Create a Reproducer instance based on the given mode. + static llvm::Expected<std::unique_ptr<Reproducer>> + createReproducer(ReproducerMode Mode, StringRef Root); + +protected: + IntrusiveRefCntPtr<vfs::FileSystem> VFS; +}; + +/// Reproducer instance used to generate a new reproducer. The VFS returned by +/// this instance is a FileCollectorFileSystem that tracks every file used by +/// dsymutil. +class ReproducerGenerate : public Reproducer { +public: + ReproducerGenerate(std::error_code &EC); + ~ReproducerGenerate() override; + +private: + /// The path to the reproducer. + std::string Root; + + /// The FileCollector used by the FileCollectorFileSystem. + std::shared_ptr<FileCollector> FC; +}; + +/// Reproducer instance used to use an existing reproducer. The VFS returned by +/// this instance is a RedirectingFileSystem that remaps paths to their +/// counterpart in the reproducer. +class ReproducerUse : public Reproducer { +public: + ReproducerUse(StringRef Root, std::error_code &EC); + ~ReproducerUse() override; + +private: + /// The path to the reproducer. + std::string Root; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_REPRODUCER_H diff --git a/contrib/libs/llvm12/tools/dsymutil/SymbolMap.cpp b/contrib/libs/llvm12/tools/dsymutil/SymbolMap.cpp index 07a54795a8..ed0c70888d 100644 --- a/contrib/libs/llvm12/tools/dsymutil/SymbolMap.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/SymbolMap.cpp @@ -1,161 +1,161 @@ -//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "SymbolMap.h" -#include "DebugMap.h" -#include "MachOUtils.h" - -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/WithColor.h" - -#ifdef __APPLE__ -#include <CoreFoundation/CoreFoundation.h> -#include <uuid/uuid.h> -#endif - -namespace llvm { -namespace dsymutil { - -StringRef SymbolMapTranslator::operator()(StringRef Input) { - if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#")) - return Input; - - bool MightNeedUnderscore = false; - StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); - if (Line[0] == '#') { - Line = Line.drop_front(); - MightNeedUnderscore = true; - } - - std::size_t LineNumber = std::numeric_limits<std::size_t>::max(); - Line.split('_').first.getAsInteger(10, LineNumber); - if (LineNumber >= UnobfuscatedStrings.size()) { - WithColor::warning() << "reference to a unexisting unobfuscated string " - << Input << ": symbol map mismatch?\n" - << Line << '\n'; - return Input; - } - - const std::string &Translation = UnobfuscatedStrings[LineNumber]; - if (!MightNeedUnderscore || !MangleNames) - return Translation; - - // Objective-C symbols for the MachO symbol table start with a \1. Please see +//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "SymbolMap.h" +#include "DebugMap.h" +#include "MachOUtils.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" + +#ifdef __APPLE__ +#include <CoreFoundation/CoreFoundation.h> +#include <uuid/uuid.h> +#endif + +namespace llvm { +namespace dsymutil { + +StringRef SymbolMapTranslator::operator()(StringRef Input) { + if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#")) + return Input; + + bool MightNeedUnderscore = false; + StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); + if (Line[0] == '#') { + Line = Line.drop_front(); + MightNeedUnderscore = true; + } + + std::size_t LineNumber = std::numeric_limits<std::size_t>::max(); + Line.split('_').first.getAsInteger(10, LineNumber); + if (LineNumber >= UnobfuscatedStrings.size()) { + WithColor::warning() << "reference to a unexisting unobfuscated string " + << Input << ": symbol map mismatch?\n" + << Line << '\n'; + return Input; + } + + const std::string &Translation = UnobfuscatedStrings[LineNumber]; + if (!MightNeedUnderscore || !MangleNames) + return Translation; + + // Objective-C symbols for the MachO symbol table start with a \1. Please see // `MangleContext::mangleObjCMethodName` in clang. - if (Translation[0] == 1) - return StringRef(Translation).drop_front(); - - // We need permanent storage for the string we are about to create. Just - // append it to the vector containing translations. This should only happen - // during MachO symbol table translation, thus there should be no risk on - // exponential growth. - UnobfuscatedStrings.emplace_back("_" + Translation); - return UnobfuscatedStrings.back(); -} - -SymbolMapTranslator SymbolMapLoader::Load(StringRef InputFile, - const DebugMap &Map) const { - if (SymbolMap.empty()) - return {}; - - std::string SymbolMapPath = SymbolMap; - -#if __APPLE__ - // Look through the UUID Map. - if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) { - uuid_string_t UUIDString; - uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString); - - SmallString<256> PlistPath( - sys::path::parent_path(sys::path::parent_path(InputFile))); - sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist"); - - CFStringRef plistFile = CFStringCreateWithCString( - kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8); - CFURLRef fileURL = CFURLCreateWithFileSystemPath( - kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false); - CFReadStreamRef resourceData = - CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); - if (resourceData) { - CFReadStreamOpen(resourceData); - CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream( - kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable, - nullptr, nullptr); - - if (plist) { - if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) { - CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue( - plist, CFSTR("DBGOriginalUUID")); - - StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8)); - SmallString<256> BCSymbolMapPath(SymbolMapPath); - sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap"); - SymbolMapPath = std::string(BCSymbolMapPath); - } - CFRelease(plist); - } - CFReadStreamClose(resourceData); - CFRelease(resourceData); - } - CFRelease(fileURL); - CFRelease(plistFile); - } -#endif - - if (sys::fs::is_directory(SymbolMapPath)) { - SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" + - MachOUtils::getArchName(Map.getTriple().getArchName()) + - ".bcsymbolmap") - .str(); - } - - auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath); - if (auto EC = ErrOrMemBuffer.getError()) { - WithColor::warning() << SymbolMapPath << ": " << EC.message() - << ": not unobfuscating.\n"; - return {}; - } - - std::vector<std::string> UnobfuscatedStrings; - auto &MemBuf = **ErrOrMemBuffer; - StringRef Data(MemBuf.getBufferStart(), - MemBuf.getBufferEnd() - MemBuf.getBufferStart()); - StringRef LHS; - std::tie(LHS, Data) = Data.split('\n'); - bool MangleNames = false; - - // Check version string first. - if (!LHS.startswith("BCSymbolMap Version:")) { - // Version string not present, warns but try to parse it. - WithColor::warning() << SymbolMapPath - << " is missing version string: assuming 1.0.\n"; - UnobfuscatedStrings.emplace_back(LHS); - } else if (LHS.equals("BCSymbolMap Version: 1.0")) { - MangleNames = true; - } else if (LHS.equals("BCSymbolMap Version: 2.0")) { - MangleNames = false; - } else { - StringRef VersionNum; - std::tie(LHS, VersionNum) = LHS.split(':'); - WithColor::warning() << SymbolMapPath - << " has unsupported symbol map version" << VersionNum - << ": not unobfuscating.\n"; - return {}; - } - - while (!Data.empty()) { - std::tie(LHS, Data) = Data.split('\n'); - UnobfuscatedStrings.emplace_back(LHS); - } - - return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames); -} - -} // namespace dsymutil -} // namespace llvm + if (Translation[0] == 1) + return StringRef(Translation).drop_front(); + + // We need permanent storage for the string we are about to create. Just + // append it to the vector containing translations. This should only happen + // during MachO symbol table translation, thus there should be no risk on + // exponential growth. + UnobfuscatedStrings.emplace_back("_" + Translation); + return UnobfuscatedStrings.back(); +} + +SymbolMapTranslator SymbolMapLoader::Load(StringRef InputFile, + const DebugMap &Map) const { + if (SymbolMap.empty()) + return {}; + + std::string SymbolMapPath = SymbolMap; + +#if __APPLE__ + // Look through the UUID Map. + if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) { + uuid_string_t UUIDString; + uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString); + + SmallString<256> PlistPath( + sys::path::parent_path(sys::path::parent_path(InputFile))); + sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist"); + + CFStringRef plistFile = CFStringCreateWithCString( + kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false); + CFReadStreamRef resourceData = + CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); + if (resourceData) { + CFReadStreamOpen(resourceData); + CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream( + kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable, + nullptr, nullptr); + + if (plist) { + if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) { + CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue( + plist, CFSTR("DBGOriginalUUID")); + + StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8)); + SmallString<256> BCSymbolMapPath(SymbolMapPath); + sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap"); + SymbolMapPath = std::string(BCSymbolMapPath); + } + CFRelease(plist); + } + CFReadStreamClose(resourceData); + CFRelease(resourceData); + } + CFRelease(fileURL); + CFRelease(plistFile); + } +#endif + + if (sys::fs::is_directory(SymbolMapPath)) { + SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" + + MachOUtils::getArchName(Map.getTriple().getArchName()) + + ".bcsymbolmap") + .str(); + } + + auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath); + if (auto EC = ErrOrMemBuffer.getError()) { + WithColor::warning() << SymbolMapPath << ": " << EC.message() + << ": not unobfuscating.\n"; + return {}; + } + + std::vector<std::string> UnobfuscatedStrings; + auto &MemBuf = **ErrOrMemBuffer; + StringRef Data(MemBuf.getBufferStart(), + MemBuf.getBufferEnd() - MemBuf.getBufferStart()); + StringRef LHS; + std::tie(LHS, Data) = Data.split('\n'); + bool MangleNames = false; + + // Check version string first. + if (!LHS.startswith("BCSymbolMap Version:")) { + // Version string not present, warns but try to parse it. + WithColor::warning() << SymbolMapPath + << " is missing version string: assuming 1.0.\n"; + UnobfuscatedStrings.emplace_back(LHS); + } else if (LHS.equals("BCSymbolMap Version: 1.0")) { + MangleNames = true; + } else if (LHS.equals("BCSymbolMap Version: 2.0")) { + MangleNames = false; + } else { + StringRef VersionNum; + std::tie(LHS, VersionNum) = LHS.split(':'); + WithColor::warning() << SymbolMapPath + << " has unsupported symbol map version" << VersionNum + << ": not unobfuscating.\n"; + return {}; + } + + while (!Data.empty()) { + std::tie(LHS, Data) = Data.split('\n'); + UnobfuscatedStrings.emplace_back(LHS); + } + + return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames); +} + +} // namespace dsymutil +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/dsymutil/SymbolMap.h b/contrib/libs/llvm12/tools/dsymutil/SymbolMap.h index 977de31a5a..636a6126b7 100644 --- a/contrib/libs/llvm12/tools/dsymutil/SymbolMap.h +++ b/contrib/libs/llvm12/tools/dsymutil/SymbolMap.h @@ -1,53 +1,53 @@ -//=- tools/dsymutil/SymbolMap.h -----------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H -#define LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H - -#include "llvm/ADT/StringRef.h" - -#include <string> -#include <vector> - -namespace llvm { -namespace dsymutil { -class DebugMap; - -/// Callable class to unobfuscate strings based on a BCSymbolMap. -class SymbolMapTranslator { -public: - SymbolMapTranslator() : MangleNames(false) {} - - SymbolMapTranslator(std::vector<std::string> UnobfuscatedStrings, - bool MangleNames) - : UnobfuscatedStrings(std::move(UnobfuscatedStrings)), - MangleNames(MangleNames) {} - - StringRef operator()(StringRef Input); - - operator bool() const { return !UnobfuscatedStrings.empty(); } - -private: - std::vector<std::string> UnobfuscatedStrings; - bool MangleNames; -}; - -/// Class to initialize SymbolMapTranslators from a BCSymbolMap. -class SymbolMapLoader { -public: - SymbolMapLoader(std::string SymbolMap) : SymbolMap(std::move(SymbolMap)) {} - - SymbolMapTranslator Load(StringRef InputFile, const DebugMap &Map) const; - -private: - const std::string SymbolMap; -}; -} // namespace dsymutil -} // namespace llvm - -#endif // LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H +//=- tools/dsymutil/SymbolMap.h -----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H +#define LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H + +#include "llvm/ADT/StringRef.h" + +#include <string> +#include <vector> + +namespace llvm { +namespace dsymutil { +class DebugMap; + +/// Callable class to unobfuscate strings based on a BCSymbolMap. +class SymbolMapTranslator { +public: + SymbolMapTranslator() : MangleNames(false) {} + + SymbolMapTranslator(std::vector<std::string> UnobfuscatedStrings, + bool MangleNames) + : UnobfuscatedStrings(std::move(UnobfuscatedStrings)), + MangleNames(MangleNames) {} + + StringRef operator()(StringRef Input); + + operator bool() const { return !UnobfuscatedStrings.empty(); } + +private: + std::vector<std::string> UnobfuscatedStrings; + bool MangleNames; +}; + +/// Class to initialize SymbolMapTranslators from a BCSymbolMap. +class SymbolMapLoader { +public: + SymbolMapLoader(std::string SymbolMap) : SymbolMap(std::move(SymbolMap)) {} + + SymbolMapTranslator Load(StringRef InputFile, const DebugMap &Map) const; + +private: + const std::string SymbolMap; +}; +} // namespace dsymutil +} // namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H diff --git a/contrib/libs/llvm12/tools/dsymutil/dsymutil.cpp b/contrib/libs/llvm12/tools/dsymutil/dsymutil.cpp index 347b2dd916..bd73a45cca 100644 --- a/contrib/libs/llvm12/tools/dsymutil/dsymutil.cpp +++ b/contrib/libs/llvm12/tools/dsymutil/dsymutil.cpp @@ -1,691 +1,691 @@ -//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===// -// -// 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 program is a utility that aims to be a dropin replacement for Darwin's -// dsymutil. -//===----------------------------------------------------------------------===// - -#include "dsymutil.h" -#include "BinaryHolder.h" -#include "CFBundle.h" -#include "DebugMap.h" -#include "LinkUtils.h" -#include "MachOUtils.h" -#include "Reproducer.h" +//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===// +// +// 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 program is a utility that aims to be a dropin replacement for Darwin's +// dsymutil. +//===----------------------------------------------------------------------===// + +#include "dsymutil.h" +#include "BinaryHolder.h" +#include "CFBundle.h" +#include "DebugMap.h" +#include "LinkUtils.h" +#include "MachOUtils.h" +#include "Reproducer.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Triple.h" -#include "llvm/DebugInfo/DIContext.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" -#include "llvm/Object/Binary.h" -#include "llvm/Object/MachO.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FileCollector.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/thread.h" -#include <algorithm> -#include <cstdint> -#include <cstdlib> -#include <string> -#include <system_error> - -using namespace llvm; -using namespace llvm::dsymutil; -using namespace object; - -namespace { -enum ID { - OPT_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - OPT_##ID, -#include "Options.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "Options.inc" -#undef PREFIX - -const opt::OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - { \ - PREFIX, NAME, HELPTEXT, \ - METAVAR, OPT_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, \ - OPT_##ALIAS, ALIASARGS, VALUES}, -#include "Options.inc" -#undef OPTION -}; - -class DsymutilOptTable : public opt::OptTable { -public: - DsymutilOptTable() : OptTable(InfoTable) {} -}; -} // namespace - -struct DsymutilOptions { - bool DumpDebugMap = false; - bool DumpStab = false; - bool Flat = false; - bool InputIsYAMLDebugMap = false; - bool PaperTrailWarnings = false; - bool Verify = false; - std::string SymbolMap; - std::string OutputFile; - std::string Toolchain; - std::string ReproducerPath; - std::vector<std::string> Archs; - std::vector<std::string> InputFiles; - unsigned NumThreads; - ReproducerMode ReproMode = ReproducerMode::Off; - dsymutil::LinkOptions LinkOpts; -}; - -/// Return a list of input files. This function has logic for dealing with the -/// special case where we might have dSYM bundles as input. The function -/// returns an error when the directory structure doesn't match that of a dSYM -/// bundle. -static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args, - bool DsymAsInput) { - std::vector<std::string> InputFiles; - for (auto *File : Args.filtered(OPT_INPUT)) - InputFiles.push_back(File->getValue()); - - if (!DsymAsInput) - return InputFiles; - - // If we are updating, we might get dSYM bundles as input. - std::vector<std::string> Inputs; - for (const auto &Input : InputFiles) { - if (!sys::fs::is_directory(Input)) { - Inputs.push_back(Input); - continue; - } - - // Make sure that we're dealing with a dSYM bundle. - SmallString<256> BundlePath(Input); - sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); - if (!sys::fs::is_directory(BundlePath)) - return make_error<StringError>( - Input + " is a directory, but doesn't look like a dSYM bundle.", - inconvertibleErrorCode()); - - // Create a directory iterator to iterate over all the entries in the - // bundle. - std::error_code EC; - sys::fs::directory_iterator DirIt(BundlePath, EC); - sys::fs::directory_iterator DirEnd; - if (EC) - return errorCodeToError(EC); - - // Add each entry to the list of inputs. - while (DirIt != DirEnd) { - Inputs.push_back(DirIt->path()); - DirIt.increment(EC); - if (EC) - return errorCodeToError(EC); - } - } - return Inputs; -} - -// Verify that the given combination of options makes sense. -static Error verifyOptions(const DsymutilOptions &Options) { - if (Options.InputFiles.empty()) { - return make_error<StringError>("no input files specified", - errc::invalid_argument); - } - +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/MachO.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileCollector.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/thread.h" +#include <algorithm> +#include <cstdint> +#include <cstdlib> +#include <string> +#include <system_error> + +using namespace llvm; +using namespace llvm::dsymutil; +using namespace object; + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class DsymutilOptTable : public opt::OptTable { +public: + DsymutilOptTable() : OptTable(InfoTable) {} +}; +} // namespace + +struct DsymutilOptions { + bool DumpDebugMap = false; + bool DumpStab = false; + bool Flat = false; + bool InputIsYAMLDebugMap = false; + bool PaperTrailWarnings = false; + bool Verify = false; + std::string SymbolMap; + std::string OutputFile; + std::string Toolchain; + std::string ReproducerPath; + std::vector<std::string> Archs; + std::vector<std::string> InputFiles; + unsigned NumThreads; + ReproducerMode ReproMode = ReproducerMode::Off; + dsymutil::LinkOptions LinkOpts; +}; + +/// Return a list of input files. This function has logic for dealing with the +/// special case where we might have dSYM bundles as input. The function +/// returns an error when the directory structure doesn't match that of a dSYM +/// bundle. +static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args, + bool DsymAsInput) { + std::vector<std::string> InputFiles; + for (auto *File : Args.filtered(OPT_INPUT)) + InputFiles.push_back(File->getValue()); + + if (!DsymAsInput) + return InputFiles; + + // If we are updating, we might get dSYM bundles as input. + std::vector<std::string> Inputs; + for (const auto &Input : InputFiles) { + if (!sys::fs::is_directory(Input)) { + Inputs.push_back(Input); + continue; + } + + // Make sure that we're dealing with a dSYM bundle. + SmallString<256> BundlePath(Input); + sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); + if (!sys::fs::is_directory(BundlePath)) + return make_error<StringError>( + Input + " is a directory, but doesn't look like a dSYM bundle.", + inconvertibleErrorCode()); + + // Create a directory iterator to iterate over all the entries in the + // bundle. + std::error_code EC; + sys::fs::directory_iterator DirIt(BundlePath, EC); + sys::fs::directory_iterator DirEnd; + if (EC) + return errorCodeToError(EC); + + // Add each entry to the list of inputs. + while (DirIt != DirEnd) { + Inputs.push_back(DirIt->path()); + DirIt.increment(EC); + if (EC) + return errorCodeToError(EC); + } + } + return Inputs; +} + +// Verify that the given combination of options makes sense. +static Error verifyOptions(const DsymutilOptions &Options) { + if (Options.InputFiles.empty()) { + return make_error<StringError>("no input files specified", + errc::invalid_argument); + } + if (Options.LinkOpts.Update && llvm::is_contained(Options.InputFiles, "-")) { - // FIXME: We cannot use stdin for an update because stdin will be - // consumed by the BinaryHolder during the debugmap parsing, and - // then we will want to consume it again in DwarfLinker. If we - // used a unique BinaryHolder object that could cache multiple - // binaries this restriction would go away. - return make_error<StringError>( - "standard input cannot be used as input for a dSYM update.", - errc::invalid_argument); - } - - if (!Options.Flat && Options.OutputFile == "-") - return make_error<StringError>( - "cannot emit to standard output without --flat.", - errc::invalid_argument); - - if (Options.InputFiles.size() > 1 && Options.Flat && - !Options.OutputFile.empty()) - return make_error<StringError>( - "cannot use -o with multiple inputs in flat mode.", - errc::invalid_argument); - - if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap) - return make_error<StringError>( - "paper trail warnings are not supported for YAML input.", - errc::invalid_argument); - - if (!Options.ReproducerPath.empty() && - Options.ReproMode != ReproducerMode::Use) - return make_error<StringError>( - "cannot combine --gen-reproducer and --use-reproducer.", - errc::invalid_argument); - - return Error::success(); -} - -static Expected<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) { - if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) { - StringRef S = Accelerator->getValue(); - if (S == "Apple") - return AccelTableKind::Apple; - if (S == "Dwarf") - return AccelTableKind::Dwarf; - if (S == "Default") - return AccelTableKind::Default; - return make_error<StringError>( - "invalid accelerator type specified: '" + S + - "'. Support values are 'Apple', 'Dwarf' and 'Default'.", - inconvertibleErrorCode()); - } - return AccelTableKind::Default; -} - -/// Parses the command line options into the LinkOptions struct and performs -/// some sanity checking. Returns an error in case the latter fails. -static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) { - DsymutilOptions Options; - - Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map); - Options.DumpStab = Args.hasArg(OPT_symtab); - Options.Flat = Args.hasArg(OPT_flat); - Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input); - Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail); - Options.Verify = Args.hasArg(OPT_verify); - - Options.LinkOpts.Minimize = Args.hasArg(OPT_minimize); - Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr); - Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output); - Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); - Options.LinkOpts.Update = Args.hasArg(OPT_update); - Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); - Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics); - - if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) { - Options.ReproMode = ReproducerMode::Use; - Options.ReproducerPath = ReproducerPath->getValue(); - } - - if (Args.hasArg(OPT_gen_reproducer)) - Options.ReproMode = ReproducerMode::Generate; - - if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) { - Options.LinkOpts.TheAccelTableKind = *AccelKind; - } else { - return AccelKind.takeError(); - } - - if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap)) - Options.SymbolMap = SymbolMap->getValue(); - - if (Args.hasArg(OPT_symbolmap)) - Options.LinkOpts.Update = true; - - if (Expected<std::vector<std::string>> InputFiles = - getInputs(Args, Options.LinkOpts.Update)) { - Options.InputFiles = std::move(*InputFiles); - } else { - return InputFiles.takeError(); - } - - for (auto *Arch : Args.filtered(OPT_arch)) - Options.Archs.push_back(Arch->getValue()); - - if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path)) - Options.LinkOpts.PrependPath = OsoPrependPath->getValue(); - - for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) { - auto Split = StringRef(Arg).split('='); - Options.LinkOpts.ObjectPrefixMap.insert( - {std::string(Split.first), std::string(Split.second)}); - } - - if (opt::Arg *OutputFile = Args.getLastArg(OPT_output)) - Options.OutputFile = OutputFile->getValue(); - - if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain)) - Options.Toolchain = Toolchain->getValue(); - - if (Args.hasArg(OPT_assembly)) - Options.LinkOpts.FileType = OutputFileType::Assembly; - - if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) - Options.LinkOpts.Threads = atoi(NumThreads->getValue()); - else - Options.LinkOpts.Threads = 0; // Use all available hardware threads - - if (Options.DumpDebugMap || Options.LinkOpts.Verbose) - Options.LinkOpts.Threads = 1; - - if (getenv("RC_DEBUG_OPTIONS")) - Options.PaperTrailWarnings = true; - - if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path)) - Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue(); - - if (opt::Arg *RemarksOutputFormat = - Args.getLastArg(OPT_remarks_output_format)) { - if (Expected<remarks::Format> FormatOrErr = - remarks::parseFormat(RemarksOutputFormat->getValue())) - Options.LinkOpts.RemarksFormat = *FormatOrErr; - else - return FormatOrErr.takeError(); - } - - if (Error E = verifyOptions(Options)) - return std::move(E); - return Options; -} - -static Error createPlistFile(StringRef Bin, StringRef BundleRoot, - StringRef Toolchain) { - // Create plist file to write to. - SmallString<128> InfoPlist(BundleRoot); - sys::path::append(InfoPlist, "Contents/Info.plist"); - std::error_code EC; - raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_Text); - if (EC) - return make_error<StringError>( - "cannot create Plist: " + toString(errorCodeToError(EC)), EC); - - CFBundleInfo BI = getBundleInfo(Bin); - - if (BI.IDStr.empty()) { - StringRef BundleID = *sys::path::rbegin(BundleRoot); - if (sys::path::extension(BundleRoot) == ".dSYM") - BI.IDStr = std::string(sys::path::stem(BundleID)); - else - BI.IDStr = std::string(BundleID); - } - - // Print out information to the plist file. - PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" - << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " - << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" - << "<plist version=\"1.0\">\n" - << "\t<dict>\n" - << "\t\t<key>CFBundleDevelopmentRegion</key>\n" - << "\t\t<string>English</string>\n" - << "\t\t<key>CFBundleIdentifier</key>\n" - << "\t\t<string>com.apple.xcode.dsym."; - printHTMLEscaped(BI.IDStr, PL); - PL << "</string>\n" - << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n" - << "\t\t<string>6.0</string>\n" - << "\t\t<key>CFBundlePackageType</key>\n" - << "\t\t<string>dSYM</string>\n" - << "\t\t<key>CFBundleSignature</key>\n" - << "\t\t<string>\?\?\?\?</string>\n"; - - if (!BI.OmitShortVersion()) { - PL << "\t\t<key>CFBundleShortVersionString</key>\n"; - PL << "\t\t<string>"; - printHTMLEscaped(BI.ShortVersionStr, PL); - PL << "</string>\n"; - } - - PL << "\t\t<key>CFBundleVersion</key>\n"; - PL << "\t\t<string>"; - printHTMLEscaped(BI.VersionStr, PL); - PL << "</string>\n"; - - if (!Toolchain.empty()) { - PL << "\t\t<key>Toolchain</key>\n"; - PL << "\t\t<string>"; - printHTMLEscaped(Toolchain, PL); - PL << "</string>\n"; - } - - PL << "\t</dict>\n" - << "</plist>\n"; - - PL.close(); - return Error::success(); -} - -static Error createBundleDir(StringRef BundleBase) { - SmallString<128> Bundle(BundleBase); - sys::path::append(Bundle, "Contents", "Resources", "DWARF"); - if (std::error_code EC = - create_directories(Bundle.str(), true, sys::fs::perms::all_all)) - return make_error<StringError>( - "cannot create bundle: " + toString(errorCodeToError(EC)), EC); - - return Error::success(); -} - -static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) { - if (OutputFile == "-") { - WithColor::warning() << "verification skipped for " << Arch - << "because writing to stdout.\n"; - return true; - } - - Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile); - if (!BinOrErr) { - WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError()); - return false; - } - - Binary &Binary = *BinOrErr.get().getBinary(); - if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) { - raw_ostream &os = Verbose ? errs() : nulls(); - os << "Verifying DWARF for architecture: " << Arch << "\n"; - std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); - DIDumpOptions DumpOpts; - bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion()); - if (!success) - WithColor::error() << "verification failed for " << Arch << '\n'; - return success; - } - - return false; -} - -namespace { -struct OutputLocation { - OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {}) - : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {} - /// This method is a workaround for older compilers. - Optional<std::string> getResourceDir() const { return ResourceDir; } - std::string DWARFFile; - Optional<std::string> ResourceDir; -}; -} // namespace - -static Expected<OutputLocation> -getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) { - if (Options.OutputFile == "-") - return OutputLocation(Options.OutputFile); - - // When updating, do in place replacement. - if (Options.OutputFile.empty() && - (Options.LinkOpts.Update || !Options.SymbolMap.empty())) - return OutputLocation(std::string(InputFile)); - - // If a flat dSYM has been requested, things are pretty simple. - if (Options.Flat) { - if (Options.OutputFile.empty()) { - if (InputFile == "-") - return OutputLocation{"a.out.dwarf", {}}; - return OutputLocation((InputFile + ".dwarf").str()); - } - - return OutputLocation(Options.OutputFile); - } - - // We need to create/update a dSYM bundle. - // A bundle hierarchy looks like this: - // <bundle name>.dSYM/ - // Contents/ - // Info.plist - // Resources/ - // DWARF/ - // <DWARF file(s)> - std::string DwarfFile = - std::string(InputFile == "-" ? StringRef("a.out") : InputFile); - SmallString<128> Path(Options.OutputFile); - if (Path.empty()) - Path = DwarfFile + ".dSYM"; - if (!Options.LinkOpts.NoOutput) { - if (auto E = createBundleDir(Path)) - return std::move(E); - if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain)) - return std::move(E); - } - - sys::path::append(Path, "Contents", "Resources"); - std::string ResourceDir = std::string(Path.str()); - sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile)); - return OutputLocation(std::string(Path.str()), ResourceDir); -} - -int main(int argc, char **argv) { - InitLLVM X(argc, argv); - - // Parse arguments. - DsymutilOptTable T; - unsigned MAI; - unsigned MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1); - opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); - - void *P = (void *)(intptr_t)getOutputFileName; - std::string SDKPath = sys::fs::getMainExecutable(argv[0], P); - SDKPath = std::string(sys::path::parent_path(SDKPath)); - - for (auto *Arg : Args.filtered(OPT_UNKNOWN)) { - WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling() - << '\n'; - } - - if (Args.hasArg(OPT_help)) { - T.PrintHelp( - outs(), (std::string(argv[0]) + " [options] <input files>").c_str(), - "manipulate archived DWARF debug symbol files.\n\n" - "dsymutil links the DWARF debug information found in the object files\n" - "for the executable <input file> by using debug symbols information\n" - "contained in its symbol table.\n", - false); - return 0; - } - - if (Args.hasArg(OPT_version)) { - cl::PrintVersionMessage(); - return 0; - } - - auto OptionsOrErr = getOptions(Args); - if (!OptionsOrErr) { - WithColor::error() << toString(OptionsOrErr.takeError()); - return 1; - } - - auto &Options = *OptionsOrErr; - - InitializeAllTargetInfos(); - InitializeAllTargetMCs(); - InitializeAllTargets(); - InitializeAllAsmPrinters(); - - auto Repro = - Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath); - if (!Repro) { - WithColor::error() << toString(Repro.takeError()); - return 1; - } - - Options.LinkOpts.VFS = (*Repro)->getVFS(); - - for (const auto &Arch : Options.Archs) - if (Arch != "*" && Arch != "all" && - !object::MachOObjectFile::isValidArch(Arch)) { - WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; - return 1; - } - - SymbolMapLoader SymMapLoader(Options.SymbolMap); - - for (auto &InputFile : Options.InputFiles) { - // Dump the symbol table for each input file and requested arch - if (Options.DumpStab) { - if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs, - Options.LinkOpts.PrependPath)) - return 1; - continue; - } - - auto DebugMapPtrsOrErr = - parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs, - Options.LinkOpts.PrependPath, Options.PaperTrailWarnings, - Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap); - - if (auto EC = DebugMapPtrsOrErr.getError()) { - WithColor::error() << "cannot parse the debug map for '" << InputFile - << "': " << EC.message() << '\n'; - return 1; - } - - // Remember the number of debug maps that are being processed to decide how - // to name the remark files. - Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size(); - - if (Options.LinkOpts.Update) { - // The debug map should be empty. Add one object file corresponding to - // the input file. - for (auto &Map : *DebugMapPtrsOrErr) - Map->addDebugMapObject(InputFile, - sys::TimePoint<std::chrono::seconds>()); - } - - // Ensure that the debug map is not empty (anymore). - if (DebugMapPtrsOrErr->empty()) { - WithColor::error() << "no architecture to link\n"; - return 1; - } - - // Shared a single binary holder for all the link steps. - BinaryHolder BinHolder(Options.LinkOpts.VFS); - - // Statistics only require different architectures to be processed - // sequentially, the link itself can still happen in parallel. Change the - // thread pool strategy here instead of modifying LinkOpts.Threads. - ThreadPoolStrategy S = hardware_concurrency( - Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads); - if (Options.LinkOpts.Threads == 0) { - // If NumThreads is not specified, create one thread for each input, up to - // the number of hardware threads. - S.ThreadsRequested = DebugMapPtrsOrErr->size(); - S.Limit = true; - } - ThreadPool Threads(S); - - // If there is more than one link to execute, we need to generate - // temporary files. - const bool NeedsTempFiles = - !Options.DumpDebugMap && (Options.OutputFile != "-") && - (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update); - const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput; - - SmallVector<MachOUtils::ArchAndFile, 4> TempFiles; - std::atomic_char AllOK(1); - for (auto &Map : *DebugMapPtrsOrErr) { - if (Options.LinkOpts.Verbose || Options.DumpDebugMap) - Map->print(outs()); - - if (Options.DumpDebugMap) - continue; - - if (!Options.SymbolMap.empty()) - Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map); - - if (Map->begin() == Map->end()) - WithColor::warning() - << "no debug symbols in executable (-arch " - << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n"; - - // Using a std::shared_ptr rather than std::unique_ptr because move-only - // types don't work with std::bind in the ThreadPool implementation. - std::shared_ptr<raw_fd_ostream> OS; - - Expected<OutputLocation> OutputLocationOrErr = - getOutputFileName(InputFile, Options); - if (!OutputLocationOrErr) { - WithColor::error() << toString(OutputLocationOrErr.takeError()); - return 1; - } - Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir(); - - std::string OutputFile = OutputLocationOrErr->DWARFFile; - if (NeedsTempFiles) { - TempFiles.emplace_back(Map->getTriple().getArchName().str()); - - auto E = TempFiles.back().createTempFile(); - if (E) { - WithColor::error() << toString(std::move(E)); - return 1; - } - - auto &TempFile = *(TempFiles.back().File); - OS = std::make_shared<raw_fd_ostream>(TempFile.FD, - /*shouldClose*/ false); - OutputFile = TempFile.TmpName; - } else { - std::error_code EC; - OS = std::make_shared<raw_fd_ostream>( - Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None); - if (EC) { - WithColor::error() << OutputFile << ": " << EC.message(); - return 1; - } - } - - auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream, - LinkOptions Options) { - AllOK.fetch_and( - linkDwarf(*Stream, BinHolder, *Map, std::move(Options))); - Stream->flush(); - if (Verify) - AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(), - Options.Verbose)); - }; - - // FIXME: The DwarfLinker can have some very deep recursion that can max - // out the (significantly smaller) stack when using threads. We don't - // want this limitation when we only have a single thread. - if (S.ThreadsRequested == 1) - LinkLambda(OS, Options.LinkOpts); - else - Threads.async(LinkLambda, OS, Options.LinkOpts); - } - - Threads.wait(); - - if (!AllOK) - return 1; - - if (NeedsTempFiles) { - Expected<OutputLocation> OutputLocationOrErr = - getOutputFileName(InputFile, Options); - if (!OutputLocationOrErr) { - WithColor::error() << toString(OutputLocationOrErr.takeError()); - return 1; - } - if (!MachOUtils::generateUniversalBinary(TempFiles, - OutputLocationOrErr->DWARFFile, - Options.LinkOpts, SDKPath)) - return 1; - } - } - - return 0; -} + // FIXME: We cannot use stdin for an update because stdin will be + // consumed by the BinaryHolder during the debugmap parsing, and + // then we will want to consume it again in DwarfLinker. If we + // used a unique BinaryHolder object that could cache multiple + // binaries this restriction would go away. + return make_error<StringError>( + "standard input cannot be used as input for a dSYM update.", + errc::invalid_argument); + } + + if (!Options.Flat && Options.OutputFile == "-") + return make_error<StringError>( + "cannot emit to standard output without --flat.", + errc::invalid_argument); + + if (Options.InputFiles.size() > 1 && Options.Flat && + !Options.OutputFile.empty()) + return make_error<StringError>( + "cannot use -o with multiple inputs in flat mode.", + errc::invalid_argument); + + if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap) + return make_error<StringError>( + "paper trail warnings are not supported for YAML input.", + errc::invalid_argument); + + if (!Options.ReproducerPath.empty() && + Options.ReproMode != ReproducerMode::Use) + return make_error<StringError>( + "cannot combine --gen-reproducer and --use-reproducer.", + errc::invalid_argument); + + return Error::success(); +} + +static Expected<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) { + if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) { + StringRef S = Accelerator->getValue(); + if (S == "Apple") + return AccelTableKind::Apple; + if (S == "Dwarf") + return AccelTableKind::Dwarf; + if (S == "Default") + return AccelTableKind::Default; + return make_error<StringError>( + "invalid accelerator type specified: '" + S + + "'. Support values are 'Apple', 'Dwarf' and 'Default'.", + inconvertibleErrorCode()); + } + return AccelTableKind::Default; +} + +/// Parses the command line options into the LinkOptions struct and performs +/// some sanity checking. Returns an error in case the latter fails. +static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) { + DsymutilOptions Options; + + Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map); + Options.DumpStab = Args.hasArg(OPT_symtab); + Options.Flat = Args.hasArg(OPT_flat); + Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input); + Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail); + Options.Verify = Args.hasArg(OPT_verify); + + Options.LinkOpts.Minimize = Args.hasArg(OPT_minimize); + Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr); + Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output); + Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); + Options.LinkOpts.Update = Args.hasArg(OPT_update); + Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); + Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics); + + if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) { + Options.ReproMode = ReproducerMode::Use; + Options.ReproducerPath = ReproducerPath->getValue(); + } + + if (Args.hasArg(OPT_gen_reproducer)) + Options.ReproMode = ReproducerMode::Generate; + + if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) { + Options.LinkOpts.TheAccelTableKind = *AccelKind; + } else { + return AccelKind.takeError(); + } + + if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap)) + Options.SymbolMap = SymbolMap->getValue(); + + if (Args.hasArg(OPT_symbolmap)) + Options.LinkOpts.Update = true; + + if (Expected<std::vector<std::string>> InputFiles = + getInputs(Args, Options.LinkOpts.Update)) { + Options.InputFiles = std::move(*InputFiles); + } else { + return InputFiles.takeError(); + } + + for (auto *Arch : Args.filtered(OPT_arch)) + Options.Archs.push_back(Arch->getValue()); + + if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path)) + Options.LinkOpts.PrependPath = OsoPrependPath->getValue(); + + for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) { + auto Split = StringRef(Arg).split('='); + Options.LinkOpts.ObjectPrefixMap.insert( + {std::string(Split.first), std::string(Split.second)}); + } + + if (opt::Arg *OutputFile = Args.getLastArg(OPT_output)) + Options.OutputFile = OutputFile->getValue(); + + if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain)) + Options.Toolchain = Toolchain->getValue(); + + if (Args.hasArg(OPT_assembly)) + Options.LinkOpts.FileType = OutputFileType::Assembly; + + if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) + Options.LinkOpts.Threads = atoi(NumThreads->getValue()); + else + Options.LinkOpts.Threads = 0; // Use all available hardware threads + + if (Options.DumpDebugMap || Options.LinkOpts.Verbose) + Options.LinkOpts.Threads = 1; + + if (getenv("RC_DEBUG_OPTIONS")) + Options.PaperTrailWarnings = true; + + if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path)) + Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue(); + + if (opt::Arg *RemarksOutputFormat = + Args.getLastArg(OPT_remarks_output_format)) { + if (Expected<remarks::Format> FormatOrErr = + remarks::parseFormat(RemarksOutputFormat->getValue())) + Options.LinkOpts.RemarksFormat = *FormatOrErr; + else + return FormatOrErr.takeError(); + } + + if (Error E = verifyOptions(Options)) + return std::move(E); + return Options; +} + +static Error createPlistFile(StringRef Bin, StringRef BundleRoot, + StringRef Toolchain) { + // Create plist file to write to. + SmallString<128> InfoPlist(BundleRoot); + sys::path::append(InfoPlist, "Contents/Info.plist"); + std::error_code EC; + raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_Text); + if (EC) + return make_error<StringError>( + "cannot create Plist: " + toString(errorCodeToError(EC)), EC); + + CFBundleInfo BI = getBundleInfo(Bin); + + if (BI.IDStr.empty()) { + StringRef BundleID = *sys::path::rbegin(BundleRoot); + if (sys::path::extension(BundleRoot) == ".dSYM") + BI.IDStr = std::string(sys::path::stem(BundleID)); + else + BI.IDStr = std::string(BundleID); + } + + // Print out information to the plist file. + PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" + << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " + << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + << "<plist version=\"1.0\">\n" + << "\t<dict>\n" + << "\t\t<key>CFBundleDevelopmentRegion</key>\n" + << "\t\t<string>English</string>\n" + << "\t\t<key>CFBundleIdentifier</key>\n" + << "\t\t<string>com.apple.xcode.dsym."; + printHTMLEscaped(BI.IDStr, PL); + PL << "</string>\n" + << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n" + << "\t\t<string>6.0</string>\n" + << "\t\t<key>CFBundlePackageType</key>\n" + << "\t\t<string>dSYM</string>\n" + << "\t\t<key>CFBundleSignature</key>\n" + << "\t\t<string>\?\?\?\?</string>\n"; + + if (!BI.OmitShortVersion()) { + PL << "\t\t<key>CFBundleShortVersionString</key>\n"; + PL << "\t\t<string>"; + printHTMLEscaped(BI.ShortVersionStr, PL); + PL << "</string>\n"; + } + + PL << "\t\t<key>CFBundleVersion</key>\n"; + PL << "\t\t<string>"; + printHTMLEscaped(BI.VersionStr, PL); + PL << "</string>\n"; + + if (!Toolchain.empty()) { + PL << "\t\t<key>Toolchain</key>\n"; + PL << "\t\t<string>"; + printHTMLEscaped(Toolchain, PL); + PL << "</string>\n"; + } + + PL << "\t</dict>\n" + << "</plist>\n"; + + PL.close(); + return Error::success(); +} + +static Error createBundleDir(StringRef BundleBase) { + SmallString<128> Bundle(BundleBase); + sys::path::append(Bundle, "Contents", "Resources", "DWARF"); + if (std::error_code EC = + create_directories(Bundle.str(), true, sys::fs::perms::all_all)) + return make_error<StringError>( + "cannot create bundle: " + toString(errorCodeToError(EC)), EC); + + return Error::success(); +} + +static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) { + if (OutputFile == "-") { + WithColor::warning() << "verification skipped for " << Arch + << "because writing to stdout.\n"; + return true; + } + + Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile); + if (!BinOrErr) { + WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError()); + return false; + } + + Binary &Binary = *BinOrErr.get().getBinary(); + if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) { + raw_ostream &os = Verbose ? errs() : nulls(); + os << "Verifying DWARF for architecture: " << Arch << "\n"; + std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); + DIDumpOptions DumpOpts; + bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion()); + if (!success) + WithColor::error() << "verification failed for " << Arch << '\n'; + return success; + } + + return false; +} + +namespace { +struct OutputLocation { + OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {}) + : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {} + /// This method is a workaround for older compilers. + Optional<std::string> getResourceDir() const { return ResourceDir; } + std::string DWARFFile; + Optional<std::string> ResourceDir; +}; +} // namespace + +static Expected<OutputLocation> +getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) { + if (Options.OutputFile == "-") + return OutputLocation(Options.OutputFile); + + // When updating, do in place replacement. + if (Options.OutputFile.empty() && + (Options.LinkOpts.Update || !Options.SymbolMap.empty())) + return OutputLocation(std::string(InputFile)); + + // If a flat dSYM has been requested, things are pretty simple. + if (Options.Flat) { + if (Options.OutputFile.empty()) { + if (InputFile == "-") + return OutputLocation{"a.out.dwarf", {}}; + return OutputLocation((InputFile + ".dwarf").str()); + } + + return OutputLocation(Options.OutputFile); + } + + // We need to create/update a dSYM bundle. + // A bundle hierarchy looks like this: + // <bundle name>.dSYM/ + // Contents/ + // Info.plist + // Resources/ + // DWARF/ + // <DWARF file(s)> + std::string DwarfFile = + std::string(InputFile == "-" ? StringRef("a.out") : InputFile); + SmallString<128> Path(Options.OutputFile); + if (Path.empty()) + Path = DwarfFile + ".dSYM"; + if (!Options.LinkOpts.NoOutput) { + if (auto E = createBundleDir(Path)) + return std::move(E); + if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain)) + return std::move(E); + } + + sys::path::append(Path, "Contents", "Resources"); + std::string ResourceDir = std::string(Path.str()); + sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile)); + return OutputLocation(std::string(Path.str()), ResourceDir); +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + // Parse arguments. + DsymutilOptTable T; + unsigned MAI; + unsigned MAC; + ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1); + opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); + + void *P = (void *)(intptr_t)getOutputFileName; + std::string SDKPath = sys::fs::getMainExecutable(argv[0], P); + SDKPath = std::string(sys::path::parent_path(SDKPath)); + + for (auto *Arg : Args.filtered(OPT_UNKNOWN)) { + WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling() + << '\n'; + } + + if (Args.hasArg(OPT_help)) { + T.PrintHelp( + outs(), (std::string(argv[0]) + " [options] <input files>").c_str(), + "manipulate archived DWARF debug symbol files.\n\n" + "dsymutil links the DWARF debug information found in the object files\n" + "for the executable <input file> by using debug symbols information\n" + "contained in its symbol table.\n", + false); + return 0; + } + + if (Args.hasArg(OPT_version)) { + cl::PrintVersionMessage(); + return 0; + } + + auto OptionsOrErr = getOptions(Args); + if (!OptionsOrErr) { + WithColor::error() << toString(OptionsOrErr.takeError()); + return 1; + } + + auto &Options = *OptionsOrErr; + + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllTargets(); + InitializeAllAsmPrinters(); + + auto Repro = + Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath); + if (!Repro) { + WithColor::error() << toString(Repro.takeError()); + return 1; + } + + Options.LinkOpts.VFS = (*Repro)->getVFS(); + + for (const auto &Arch : Options.Archs) + if (Arch != "*" && Arch != "all" && + !object::MachOObjectFile::isValidArch(Arch)) { + WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; + return 1; + } + + SymbolMapLoader SymMapLoader(Options.SymbolMap); + + for (auto &InputFile : Options.InputFiles) { + // Dump the symbol table for each input file and requested arch + if (Options.DumpStab) { + if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs, + Options.LinkOpts.PrependPath)) + return 1; + continue; + } + + auto DebugMapPtrsOrErr = + parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs, + Options.LinkOpts.PrependPath, Options.PaperTrailWarnings, + Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap); + + if (auto EC = DebugMapPtrsOrErr.getError()) { + WithColor::error() << "cannot parse the debug map for '" << InputFile + << "': " << EC.message() << '\n'; + return 1; + } + + // Remember the number of debug maps that are being processed to decide how + // to name the remark files. + Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size(); + + if (Options.LinkOpts.Update) { + // The debug map should be empty. Add one object file corresponding to + // the input file. + for (auto &Map : *DebugMapPtrsOrErr) + Map->addDebugMapObject(InputFile, + sys::TimePoint<std::chrono::seconds>()); + } + + // Ensure that the debug map is not empty (anymore). + if (DebugMapPtrsOrErr->empty()) { + WithColor::error() << "no architecture to link\n"; + return 1; + } + + // Shared a single binary holder for all the link steps. + BinaryHolder BinHolder(Options.LinkOpts.VFS); + + // Statistics only require different architectures to be processed + // sequentially, the link itself can still happen in parallel. Change the + // thread pool strategy here instead of modifying LinkOpts.Threads. + ThreadPoolStrategy S = hardware_concurrency( + Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads); + if (Options.LinkOpts.Threads == 0) { + // If NumThreads is not specified, create one thread for each input, up to + // the number of hardware threads. + S.ThreadsRequested = DebugMapPtrsOrErr->size(); + S.Limit = true; + } + ThreadPool Threads(S); + + // If there is more than one link to execute, we need to generate + // temporary files. + const bool NeedsTempFiles = + !Options.DumpDebugMap && (Options.OutputFile != "-") && + (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update); + const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput; + + SmallVector<MachOUtils::ArchAndFile, 4> TempFiles; + std::atomic_char AllOK(1); + for (auto &Map : *DebugMapPtrsOrErr) { + if (Options.LinkOpts.Verbose || Options.DumpDebugMap) + Map->print(outs()); + + if (Options.DumpDebugMap) + continue; + + if (!Options.SymbolMap.empty()) + Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map); + + if (Map->begin() == Map->end()) + WithColor::warning() + << "no debug symbols in executable (-arch " + << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n"; + + // Using a std::shared_ptr rather than std::unique_ptr because move-only + // types don't work with std::bind in the ThreadPool implementation. + std::shared_ptr<raw_fd_ostream> OS; + + Expected<OutputLocation> OutputLocationOrErr = + getOutputFileName(InputFile, Options); + if (!OutputLocationOrErr) { + WithColor::error() << toString(OutputLocationOrErr.takeError()); + return 1; + } + Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir(); + + std::string OutputFile = OutputLocationOrErr->DWARFFile; + if (NeedsTempFiles) { + TempFiles.emplace_back(Map->getTriple().getArchName().str()); + + auto E = TempFiles.back().createTempFile(); + if (E) { + WithColor::error() << toString(std::move(E)); + return 1; + } + + auto &TempFile = *(TempFiles.back().File); + OS = std::make_shared<raw_fd_ostream>(TempFile.FD, + /*shouldClose*/ false); + OutputFile = TempFile.TmpName; + } else { + std::error_code EC; + OS = std::make_shared<raw_fd_ostream>( + Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None); + if (EC) { + WithColor::error() << OutputFile << ": " << EC.message(); + return 1; + } + } + + auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream, + LinkOptions Options) { + AllOK.fetch_and( + linkDwarf(*Stream, BinHolder, *Map, std::move(Options))); + Stream->flush(); + if (Verify) + AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(), + Options.Verbose)); + }; + + // FIXME: The DwarfLinker can have some very deep recursion that can max + // out the (significantly smaller) stack when using threads. We don't + // want this limitation when we only have a single thread. + if (S.ThreadsRequested == 1) + LinkLambda(OS, Options.LinkOpts); + else + Threads.async(LinkLambda, OS, Options.LinkOpts); + } + + Threads.wait(); + + if (!AllOK) + return 1; + + if (NeedsTempFiles) { + Expected<OutputLocation> OutputLocationOrErr = + getOutputFileName(InputFile, Options); + if (!OutputLocationOrErr) { + WithColor::error() << toString(OutputLocationOrErr.takeError()); + return 1; + } + if (!MachOUtils::generateUniversalBinary(TempFiles, + OutputLocationOrErr->DWARFFile, + Options.LinkOpts, SDKPath)) + return 1; + } + } + + return 0; +} diff --git a/contrib/libs/llvm12/tools/dsymutil/dsymutil.h b/contrib/libs/llvm12/tools/dsymutil/dsymutil.h index f88f57bb20..7080b1bf3b 100644 --- a/contrib/libs/llvm12/tools/dsymutil/dsymutil.h +++ b/contrib/libs/llvm12/tools/dsymutil/dsymutil.h @@ -1,56 +1,56 @@ -//===- tools/dsymutil/dsymutil.h - dsymutil high-level functionality ------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -/// \file -/// -/// This file contains the class declaration for the code that parses STABS -/// debug maps that are embedded in the binaries symbol tables. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H -#define LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H - -#include "DebugMap.h" -#include "LinkUtils.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/ErrorOr.h" -#include <memory> -#include <string> -#include <vector> - -namespace llvm { -namespace dsymutil { - -class BinaryHolder; - -/// Extract the DebugMaps from the given file. -/// The file has to be a MachO object file. Multiple debug maps can be -/// returned when the file is universal (aka fat) binary. -ErrorOr<std::vector<std::unique_ptr<DebugMap>>> -parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - StringRef InputFile, ArrayRef<std::string> Archs, - StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, - bool InputIsYAML); - -/// Dump the symbol table. -bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - StringRef InputFile, ArrayRef<std::string> Archs, - StringRef PrependPath = ""); - -/// Link the Dwarf debug info as directed by the passed DebugMap \p DM into a -/// DwarfFile named \p OutputFilename. \returns false if the link failed. -bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, - const DebugMap &DM, LinkOptions Options); - -} // end namespace dsymutil -} // end namespace llvm - -#endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H +//===- tools/dsymutil/dsymutil.h - dsymutil high-level functionality ------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// +/// This file contains the class declaration for the code that parses STABS +/// debug maps that are embedded in the binaries symbol tables. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H +#define LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H + +#include "DebugMap.h" +#include "LinkUtils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorOr.h" +#include <memory> +#include <string> +#include <vector> + +namespace llvm { +namespace dsymutil { + +class BinaryHolder; + +/// Extract the DebugMaps from the given file. +/// The file has to be a MachO object file. Multiple debug maps can be +/// returned when the file is universal (aka fat) binary. +ErrorOr<std::vector<std::unique_ptr<DebugMap>>> +parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, + bool InputIsYAML); + +/// Dump the symbol table. +bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath = ""); + +/// Link the Dwarf debug info as directed by the passed DebugMap \p DM into a +/// DwarfFile named \p OutputFilename. \returns false if the link failed. +bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, + const DebugMap &DM, LinkOptions Options); + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H diff --git a/contrib/libs/llvm12/tools/dsymutil/ya.make b/contrib/libs/llvm12/tools/dsymutil/ya.make index 8a3b747e01..2ebc7666d2 100644 --- a/contrib/libs/llvm12/tools/dsymutil/ya.make +++ b/contrib/libs/llvm12/tools/dsymutil/ya.make @@ -1,17 +1,17 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/Analysis @@ -70,29 +70,29 @@ PEERDIR( contrib/libs/llvm12/lib/Transforms/Scalar contrib/libs/llvm12/lib/Transforms/Utils contrib/libs/llvm12/lib/Transforms/Vectorize -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/dsymutil contrib/libs/llvm12/tools/dsymutil -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - BinaryHolder.cpp - CFBundle.cpp - DebugMap.cpp - DwarfLinkerForBinary.cpp - MachODebugMapParser.cpp - MachOUtils.cpp - Reproducer.cpp - SymbolMap.cpp - dsymutil.cpp -) - +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + BinaryHolder.cpp + CFBundle.cpp + DebugMap.cpp + DwarfLinkerForBinary.cpp + MachODebugMapParser.cpp + MachOUtils.cpp + Reproducer.cpp + SymbolMap.cpp + dsymutil.cpp +) + IF (OS_DARWIN AND ARCH_AARCH64) LDFLAGS( -framework @@ -100,4 +100,4 @@ IF (OS_DARWIN AND ARCH_AARCH64) ) ENDIF() -END() +END() diff --git a/contrib/libs/llvm12/tools/llvm-cvtres/Opts.td b/contrib/libs/llvm12/tools/llvm-cvtres/Opts.td index 8687d4701c..e418c30346 100644 --- a/contrib/libs/llvm12/tools/llvm-cvtres/Opts.td +++ b/contrib/libs/llvm12/tools/llvm-cvtres/Opts.td @@ -1,19 +1,19 @@ -include "llvm/Option/OptParser.td" - -// All the switches can be preceded by either '/' or '-'. - -def DEFINE : Joined<["/", "-"], "DEFINE:">, HelpText<"">, MetaVarName<"symbol">; -def FOLDDUPS : Flag<["/", "-"], "FOLDDUPS:">, HelpText<"">; -def MACHINE : Joined<["/", "-"], "MACHINE:">, HelpText<"">, MetaVarName<"{ARM|ARM64|EBC|IA64|X64|X86}">; -def NOLOGO : Flag<["/", "-"], "NOLOGO">, HelpText<"">; -def OUT : Joined<["/", "-"], "OUT:">, HelpText<"">, MetaVarName<"filename">; -def READONLY : Flag<["/", "-"], "READONLY">, HelpText<"">; -def VERBOSE : Flag<["/", "-"], "VERBOSE">, HelpText<"">; -def HELP : Flag<["/", "-"], "HELP">; -def H : Flag<["/", "-"], "H">, Alias<HELP>; -def HELP_Q : Flag<["/?", "-?"], "">, Alias<HELP>; - -// Extensions. - -def TIMESTAMP : Joined<["/", "-"], "TIMESTAMP:">, - HelpText<"Timestamp for coff header, defaults to current time">; +include "llvm/Option/OptParser.td" + +// All the switches can be preceded by either '/' or '-'. + +def DEFINE : Joined<["/", "-"], "DEFINE:">, HelpText<"">, MetaVarName<"symbol">; +def FOLDDUPS : Flag<["/", "-"], "FOLDDUPS:">, HelpText<"">; +def MACHINE : Joined<["/", "-"], "MACHINE:">, HelpText<"">, MetaVarName<"{ARM|ARM64|EBC|IA64|X64|X86}">; +def NOLOGO : Flag<["/", "-"], "NOLOGO">, HelpText<"">; +def OUT : Joined<["/", "-"], "OUT:">, HelpText<"">, MetaVarName<"filename">; +def READONLY : Flag<["/", "-"], "READONLY">, HelpText<"">; +def VERBOSE : Flag<["/", "-"], "VERBOSE">, HelpText<"">; +def HELP : Flag<["/", "-"], "HELP">; +def H : Flag<["/", "-"], "H">, Alias<HELP>; +def HELP_Q : Flag<["/?", "-?"], "">, Alias<HELP>; + +// Extensions. + +def TIMESTAMP : Joined<["/", "-"], "TIMESTAMP:">, + HelpText<"Timestamp for coff header, defaults to current time">; diff --git a/contrib/libs/llvm12/tools/llvm-cvtres/llvm-cvtres.cpp b/contrib/libs/llvm12/tools/llvm-cvtres/llvm-cvtres.cpp index 11cfb466e1..0c43c923b0 100644 --- a/contrib/libs/llvm12/tools/llvm-cvtres/llvm-cvtres.cpp +++ b/contrib/libs/llvm12/tools/llvm-cvtres/llvm-cvtres.cpp @@ -1,211 +1,211 @@ -//===- llvm-cvtres.cpp - Serialize .res files into .obj ---------*- 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 -// -//===----------------------------------------------------------------------===// -// -// Serialize .res files into .obj files. This is intended to be a -// platform-independent port of Microsoft's cvtres.exe. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Object/Binary.h" -#include "llvm/Object/WindowsMachineFlag.h" -#include "llvm/Object/WindowsResource.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/BinaryStreamError.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/ScopedPrinter.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/raw_ostream.h" - -#include <system_error> - -using namespace llvm; -using namespace object; - -namespace { - -enum ID { - OPT_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - OPT_##ID, -#include "Opts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "Opts.inc" -#undef PREFIX - -static const opt::OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - { \ - PREFIX, NAME, HELPTEXT, \ - METAVAR, OPT_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, \ - OPT_##ALIAS, ALIASARGS, VALUES}, -#include "Opts.inc" -#undef OPTION -}; - -class CvtResOptTable : public opt::OptTable { -public: - CvtResOptTable() : OptTable(InfoTable, true) {} -}; -} - +//===- llvm-cvtres.cpp - Serialize .res files into .obj ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Serialize .res files into .obj files. This is intended to be a +// platform-independent port of Microsoft's cvtres.exe. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/Binary.h" +#include "llvm/Object/WindowsMachineFlag.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +#include <system_error> + +using namespace llvm; +using namespace object; + +namespace { + +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class CvtResOptTable : public opt::OptTable { +public: + CvtResOptTable() : OptTable(InfoTable, true) {} +}; +} + static LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { - errs() << Msg; - exit(1); -} - -static void reportError(StringRef Input, std::error_code EC) { - reportError(Twine(Input) + ": " + EC.message() + ".\n"); -} - + errs() << Msg; + exit(1); +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Twine(Input) + ": " + EC.message() + ".\n"); +} + static void error(Error EC) { - if (!EC) - return; - handleAllErrors(std::move(EC), - [&](const ErrorInfoBase &EI) { reportError(EI.message()); }); -} - -static uint32_t getTime() { - std::time_t Now = time(nullptr); - if (Now < 0 || !isUInt<32>(Now)) - return UINT32_MAX; - return static_cast<uint32_t>(Now); -} - -template <typename T> T error(Expected<T> EC) { - if (!EC) - error(EC.takeError()); - return std::move(EC.get()); -} - -int main(int Argc, const char **Argv) { - InitLLVM X(Argc, Argv); - - CvtResOptTable T; - unsigned MAI, MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); - opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); - - if (InputArgs.hasArg(OPT_HELP)) { - T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter"); - return 0; - } - - bool Verbose = InputArgs.hasArg(OPT_VERBOSE); - - COFF::MachineTypes MachineType; - - if (opt::Arg *Arg = InputArgs.getLastArg(OPT_MACHINE)) { - MachineType = getMachineType(Arg->getValue()); - if (MachineType == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { - reportError(Twine("Unsupported machine architecture ") + Arg->getValue() + - "\n"); - } - } else { - if (Verbose) - outs() << "Machine architecture not specified; assumed X64.\n"; - MachineType = COFF::IMAGE_FILE_MACHINE_AMD64; - } - - std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_INPUT); - - if (InputFiles.size() == 0) { - reportError("No input file specified.\n"); - } - - SmallString<128> OutputFile; - - if (opt::Arg *Arg = InputArgs.getLastArg(OPT_OUT)) { - OutputFile = Arg->getValue(); - } else { - OutputFile = sys::path::filename(StringRef(InputFiles[0])); - sys::path::replace_extension(OutputFile, ".obj"); - } - - uint32_t DateTimeStamp; - if (llvm::opt::Arg *Arg = InputArgs.getLastArg(OPT_TIMESTAMP)) { - StringRef Value(Arg->getValue()); - if (Value.getAsInteger(0, DateTimeStamp)) - reportError(Twine("invalid timestamp: ") + Value + - ". Expected 32-bit integer\n"); - } else { - DateTimeStamp = getTime(); - } - - if (Verbose) - outs() << "Machine: " << machineToStr(MachineType) << '\n'; - - WindowsResourceParser Parser; - - for (const auto &File : InputFiles) { - Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File); - if (!BinaryOrErr) - reportError(File, errorToErrorCode(BinaryOrErr.takeError())); - - Binary &Binary = *BinaryOrErr.get().getBinary(); - - WindowsResource *RF = dyn_cast<WindowsResource>(&Binary); - if (!RF) - reportError(File + ": unrecognized file format.\n"); - - if (Verbose) { - int EntryNumber = 0; - ResourceEntryRef Entry = error(RF->getHeadEntry()); - bool End = false; - while (!End) { - error(Entry.moveNext(End)); - EntryNumber++; - } - outs() << "Number of resources: " << EntryNumber << "\n"; - } - - std::vector<std::string> Duplicates; - error(Parser.parse(RF, Duplicates)); - for (const auto& DupeDiag : Duplicates) - reportError(DupeDiag); - } - - if (Verbose) { - Parser.printTree(outs()); - } - - std::unique_ptr<MemoryBuffer> OutputBuffer = - error(llvm::object::writeWindowsResourceCOFF(MachineType, Parser, - DateTimeStamp)); - auto FileOrErr = - FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize()); - if (!FileOrErr) - reportError(OutputFile, errorToErrorCode(FileOrErr.takeError())); - std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr); - std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), - FileBuffer->getBufferStart()); - error(FileBuffer->commit()); - - if (Verbose) { - Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(OutputFile); - if (!BinaryOrErr) - reportError(OutputFile, errorToErrorCode(BinaryOrErr.takeError())); - Binary &Binary = *BinaryOrErr.get().getBinary(); - ScopedPrinter W(errs()); - W.printBinaryBlock("Output File Raw Data", Binary.getData()); - } - - return 0; -} + if (!EC) + return; + handleAllErrors(std::move(EC), + [&](const ErrorInfoBase &EI) { reportError(EI.message()); }); +} + +static uint32_t getTime() { + std::time_t Now = time(nullptr); + if (Now < 0 || !isUInt<32>(Now)) + return UINT32_MAX; + return static_cast<uint32_t>(Now); +} + +template <typename T> T error(Expected<T> EC) { + if (!EC) + error(EC.takeError()); + return std::move(EC.get()); +} + +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); + + CvtResOptTable T; + unsigned MAI, MAC; + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + + if (InputArgs.hasArg(OPT_HELP)) { + T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter"); + return 0; + } + + bool Verbose = InputArgs.hasArg(OPT_VERBOSE); + + COFF::MachineTypes MachineType; + + if (opt::Arg *Arg = InputArgs.getLastArg(OPT_MACHINE)) { + MachineType = getMachineType(Arg->getValue()); + if (MachineType == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + reportError(Twine("Unsupported machine architecture ") + Arg->getValue() + + "\n"); + } + } else { + if (Verbose) + outs() << "Machine architecture not specified; assumed X64.\n"; + MachineType = COFF::IMAGE_FILE_MACHINE_AMD64; + } + + std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_INPUT); + + if (InputFiles.size() == 0) { + reportError("No input file specified.\n"); + } + + SmallString<128> OutputFile; + + if (opt::Arg *Arg = InputArgs.getLastArg(OPT_OUT)) { + OutputFile = Arg->getValue(); + } else { + OutputFile = sys::path::filename(StringRef(InputFiles[0])); + sys::path::replace_extension(OutputFile, ".obj"); + } + + uint32_t DateTimeStamp; + if (llvm::opt::Arg *Arg = InputArgs.getLastArg(OPT_TIMESTAMP)) { + StringRef Value(Arg->getValue()); + if (Value.getAsInteger(0, DateTimeStamp)) + reportError(Twine("invalid timestamp: ") + Value + + ". Expected 32-bit integer\n"); + } else { + DateTimeStamp = getTime(); + } + + if (Verbose) + outs() << "Machine: " << machineToStr(MachineType) << '\n'; + + WindowsResourceParser Parser; + + for (const auto &File : InputFiles) { + Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File); + if (!BinaryOrErr) + reportError(File, errorToErrorCode(BinaryOrErr.takeError())); + + Binary &Binary = *BinaryOrErr.get().getBinary(); + + WindowsResource *RF = dyn_cast<WindowsResource>(&Binary); + if (!RF) + reportError(File + ": unrecognized file format.\n"); + + if (Verbose) { + int EntryNumber = 0; + ResourceEntryRef Entry = error(RF->getHeadEntry()); + bool End = false; + while (!End) { + error(Entry.moveNext(End)); + EntryNumber++; + } + outs() << "Number of resources: " << EntryNumber << "\n"; + } + + std::vector<std::string> Duplicates; + error(Parser.parse(RF, Duplicates)); + for (const auto& DupeDiag : Duplicates) + reportError(DupeDiag); + } + + if (Verbose) { + Parser.printTree(outs()); + } + + std::unique_ptr<MemoryBuffer> OutputBuffer = + error(llvm::object::writeWindowsResourceCOFF(MachineType, Parser, + DateTimeStamp)); + auto FileOrErr = + FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize()); + if (!FileOrErr) + reportError(OutputFile, errorToErrorCode(FileOrErr.takeError())); + std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr); + std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), + FileBuffer->getBufferStart()); + error(FileBuffer->commit()); + + if (Verbose) { + Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(OutputFile); + if (!BinaryOrErr) + reportError(OutputFile, errorToErrorCode(BinaryOrErr.takeError())); + Binary &Binary = *BinaryOrErr.get().getBinary(); + ScopedPrinter W(errs()); + W.printBinaryBlock("Output File Raw Data", Binary.getData()); + } + + return 0; +} diff --git a/contrib/libs/llvm12/tools/llvm-cvtres/ya.make b/contrib/libs/llvm12/tools/llvm-cvtres/ya.make index fc7b01fdb7..ab0eff49cc 100644 --- a/contrib/libs/llvm12/tools/llvm-cvtres/ya.make +++ b/contrib/libs/llvm12/tools/llvm-cvtres/ya.make @@ -1,17 +1,17 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/BinaryFormat @@ -26,19 +26,19 @@ PEERDIR( contrib/libs/llvm12/lib/Remarks contrib/libs/llvm12/lib/Support contrib/libs/llvm12/lib/TextAPI/MachO -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-cvtres contrib/libs/llvm12/tools/llvm-cvtres -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - llvm-cvtres.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + llvm-cvtres.cpp +) + +END() diff --git a/contrib/libs/llvm12/tools/llvm-lipo/LipoOpts.td b/contrib/libs/llvm12/tools/llvm-lipo/LipoOpts.td index 866353573c..f1a8d1140b 100644 --- a/contrib/libs/llvm12/tools/llvm-lipo/LipoOpts.td +++ b/contrib/libs/llvm12/tools/llvm-lipo/LipoOpts.td @@ -1,60 +1,60 @@ -include "llvm/Option/OptParser.td" - -def help : Flag<["-", "--"], "help">; -def h : Flag<["-"], "h">, Alias<help>; - -def version : Flag<["-", "--"], "version">, - HelpText<"Print the version and exit.">; - -def segalign - : MultiArg<["-", "--"], "segalign", 2>, - HelpText<"Specifies the segment alignment for the specified " - "architecture when creating a universal binary file. The " - "alignment is a hexadecimal number that is a power of 2.">; - -def arch - : MultiArg<["-", "--"], "arch", 2>, - HelpText<"Specifies the architecture and the corresponding input file">; - -def action_group : OptionGroup<"action group">; - -def verify_arch - : Option<["-", "--"], "verify_arch", KIND_REMAINING_ARGS>, - Group<action_group>, - HelpText< - "Verify that the specified arch_types are present in the input file">; - -def archs : Option<["-", "--"], "archs", KIND_FLAG>, - Group<action_group>, - HelpText<"Display the arch_types present in the input file">; - -def info : Option<["-", "--"], "info", KIND_FLAG>, - Group<action_group>, - HelpText<"Display descriptions of each input file including " - "filename and arch_types. Groups universal binaries " - "together followed by thin files">; - -def thin : Option<["-", "--"], "thin", KIND_SEPARATE>, - Group<action_group>, - HelpText<"Create a thin output file of specified arch_type from the " - "fat input file. Requires -output option">; - -def extract : Option<["-", "--"], "extract", KIND_SEPARATE>, - Group<action_group>, - HelpText<"Create a universal output file containing only the specified " - "arch_type from the fat input file. Requires -output option">; - -def create : Option<["-", "--"], "create", KIND_FLAG>, - Group<action_group>, - HelpText<"Create a universal binary output file from the input " - "files. Requires -output option">; - -def replace - : MultiArg<["-", "--"], "replace", 2>, - Group<action_group>, - HelpText<"Replace the specified arch type with the contents of the " - "input_file in a universal binary. Requires -output option">; - -def output : Option<["-", "--"], "output", KIND_SEPARATE>, - HelpText<"Create output file with specified name">; -def o : JoinedOrSeparate<["-"], "o">, Alias<output>; +include "llvm/Option/OptParser.td" + +def help : Flag<["-", "--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def version : Flag<["-", "--"], "version">, + HelpText<"Print the version and exit.">; + +def segalign + : MultiArg<["-", "--"], "segalign", 2>, + HelpText<"Specifies the segment alignment for the specified " + "architecture when creating a universal binary file. The " + "alignment is a hexadecimal number that is a power of 2.">; + +def arch + : MultiArg<["-", "--"], "arch", 2>, + HelpText<"Specifies the architecture and the corresponding input file">; + +def action_group : OptionGroup<"action group">; + +def verify_arch + : Option<["-", "--"], "verify_arch", KIND_REMAINING_ARGS>, + Group<action_group>, + HelpText< + "Verify that the specified arch_types are present in the input file">; + +def archs : Option<["-", "--"], "archs", KIND_FLAG>, + Group<action_group>, + HelpText<"Display the arch_types present in the input file">; + +def info : Option<["-", "--"], "info", KIND_FLAG>, + Group<action_group>, + HelpText<"Display descriptions of each input file including " + "filename and arch_types. Groups universal binaries " + "together followed by thin files">; + +def thin : Option<["-", "--"], "thin", KIND_SEPARATE>, + Group<action_group>, + HelpText<"Create a thin output file of specified arch_type from the " + "fat input file. Requires -output option">; + +def extract : Option<["-", "--"], "extract", KIND_SEPARATE>, + Group<action_group>, + HelpText<"Create a universal output file containing only the specified " + "arch_type from the fat input file. Requires -output option">; + +def create : Option<["-", "--"], "create", KIND_FLAG>, + Group<action_group>, + HelpText<"Create a universal binary output file from the input " + "files. Requires -output option">; + +def replace + : MultiArg<["-", "--"], "replace", 2>, + Group<action_group>, + HelpText<"Replace the specified arch type with the contents of the " + "input_file in a universal binary. Requires -output option">; + +def output : Option<["-", "--"], "output", KIND_SEPARATE>, + HelpText<"Create output file with specified name">; +def o : JoinedOrSeparate<["-"], "o">, Alias<output>; diff --git a/contrib/libs/llvm12/tools/llvm-lipo/llvm-lipo.cpp b/contrib/libs/llvm12/tools/llvm-lipo/llvm-lipo.cpp index 7fbe489ecc..e1881745b1 100644 --- a/contrib/libs/llvm12/tools/llvm-lipo/llvm-lipo.cpp +++ b/contrib/libs/llvm12/tools/llvm-lipo/llvm-lipo.cpp @@ -1,47 +1,47 @@ -//===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// A utility for creating / splitting / inspecting universal binaries. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/Triple.h" +//===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// A utility for creating / splitting / inspecting universal binaries. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" -#include "llvm/Object/Binary.h" +#include "llvm/Object/Binary.h" #include "llvm/Object/IRObjectFile.h" -#include "llvm/Object/MachO.h" -#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/MachOUniversalWriter.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/WithColor.h" -#include "llvm/TextAPI/MachO/Architecture.h" - -using namespace llvm; -using namespace llvm::object; - -static const StringRef ToolName = "llvm-lipo"; +#include "llvm/Object/ObjectFile.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" +#include "llvm/TextAPI/MachO/Architecture.h" + +using namespace llvm; +using namespace llvm::object; + +static const StringRef ToolName = "llvm-lipo"; static LLVMContext LLVMCtx; - -LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) { - WithColor::error(errs(), ToolName) << Message << "\n"; - errs().flush(); - exit(EXIT_FAILURE); -} - + +LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) { + WithColor::error(errs(), ToolName) << Message << "\n"; + errs().flush(); + exit(EXIT_FAILURE); +} + LLVM_ATTRIBUTE_NORETURN static void reportError(Error E) { assert(E); std::string Buf; @@ -51,347 +51,347 @@ LLVM_ATTRIBUTE_NORETURN static void reportError(Error E) { reportError(Buf); } -LLVM_ATTRIBUTE_NORETURN static void reportError(StringRef File, Error E) { - assert(E); - std::string Buf; - raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS); - OS.flush(); - WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; - exit(EXIT_FAILURE); -} - -namespace { -enum LipoID { - LIPO_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - LIPO_##ID, -#include "LipoOpts.inc" -#undef OPTION -}; - -// LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr. -const char *const *LIPO_nullptr = nullptr; -#define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE; -#include "LipoOpts.inc" -#undef PREFIX - -static const opt::OptTable::Info LipoInfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - {LIPO_##PREFIX, NAME, HELPTEXT, \ - METAVAR, LIPO_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, LIPO_##GROUP, \ - LIPO_##ALIAS, ALIASARGS, VALUES}, -#include "LipoOpts.inc" -#undef OPTION -}; - -class LipoOptTable : public opt::OptTable { -public: - LipoOptTable() : OptTable(LipoInfoTable) {} -}; - -enum class LipoAction { - PrintArchs, - PrintInfo, - VerifyArch, - ThinArch, - ExtractArch, - CreateUniversal, - ReplaceArch, -}; - -struct InputFile { - Optional<StringRef> ArchType; - StringRef FileName; -}; - -struct Config { - SmallVector<InputFile, 1> InputFiles; - SmallVector<std::string, 1> VerifyArchList; - SmallVector<InputFile, 1> ReplacementFiles; - StringMap<const uint32_t> SegmentAlignments; - std::string ArchType; - std::string OutputFile; - LipoAction ActionToPerform; -}; - +LLVM_ATTRIBUTE_NORETURN static void reportError(StringRef File, Error E) { + assert(E); + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(E), OS); + OS.flush(); + WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; + exit(EXIT_FAILURE); +} + +namespace { +enum LipoID { + LIPO_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + LIPO_##ID, +#include "LipoOpts.inc" +#undef OPTION +}; + +// LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr. +const char *const *LIPO_nullptr = nullptr; +#define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE; +#include "LipoOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info LipoInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {LIPO_##PREFIX, NAME, HELPTEXT, \ + METAVAR, LIPO_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, LIPO_##GROUP, \ + LIPO_##ALIAS, ALIASARGS, VALUES}, +#include "LipoOpts.inc" +#undef OPTION +}; + +class LipoOptTable : public opt::OptTable { +public: + LipoOptTable() : OptTable(LipoInfoTable) {} +}; + +enum class LipoAction { + PrintArchs, + PrintInfo, + VerifyArch, + ThinArch, + ExtractArch, + CreateUniversal, + ReplaceArch, +}; + +struct InputFile { + Optional<StringRef> ArchType; + StringRef FileName; +}; + +struct Config { + SmallVector<InputFile, 1> InputFiles; + SmallVector<std::string, 1> VerifyArchList; + SmallVector<InputFile, 1> ReplacementFiles; + StringMap<const uint32_t> SegmentAlignments; + std::string ArchType; + std::string OutputFile; + LipoAction ActionToPerform; +}; + static Slice createSliceFromArchive(const Archive &A) { Expected<Slice> ArchiveOrSlice = Slice::create(A, &LLVMCtx); if (!ArchiveOrSlice) reportError(A.getFileName(), ArchiveOrSlice.takeError()); return *ArchiveOrSlice; -} - +} + static Slice createSliceFromIR(const IRObjectFile &IRO, unsigned Align) { Expected<Slice> IROrErr = Slice::create(IRO, Align); if (!IROrErr) reportError(IRO.getFileName(), IROrErr.takeError()); return *IROrErr; -} - -} // end namespace - -static void validateArchitectureName(StringRef ArchitectureName) { - if (!MachOObjectFile::isValidArch(ArchitectureName)) { - std::string Buf; - raw_string_ostream OS(Buf); - OS << "Invalid architecture: " << ArchitectureName - << "\nValid architecture names are:"; - for (auto arch : MachOObjectFile::getValidArchs()) - OS << " " << arch; - reportError(OS.str()); - } -} - -static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) { - Config C; - LipoOptTable T; - unsigned MissingArgumentIndex, MissingArgumentCount; - opt::InputArgList InputArgs = - T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - - if (MissingArgumentCount) - reportError("missing argument to " + - StringRef(InputArgs.getArgString(MissingArgumentIndex)) + - " option"); - - if (InputArgs.size() == 0) { - // PrintHelp does not accept Twine. - T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); - exit(EXIT_FAILURE); - } - - if (InputArgs.hasArg(LIPO_help)) { - // PrintHelp does not accept Twine. - T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); - exit(EXIT_SUCCESS); - } - - if (InputArgs.hasArg(LIPO_version)) { - outs() << ToolName + "\n"; - cl::PrintVersionMessage(); - exit(EXIT_SUCCESS); - } - - for (auto Arg : InputArgs.filtered(LIPO_UNKNOWN)) - reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'"); - - for (auto Arg : InputArgs.filtered(LIPO_INPUT)) - C.InputFiles.push_back({None, Arg->getValue()}); - for (auto Arg : InputArgs.filtered(LIPO_arch)) { - validateArchitectureName(Arg->getValue(0)); - if (!Arg->getValue(1)) - reportError( - "arch is missing an argument: expects -arch arch_type file_name"); - C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)}); - } - - if (C.InputFiles.empty()) - reportError("at least one input file should be specified"); - - if (InputArgs.hasArg(LIPO_output)) - C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output)); - - for (auto Segalign : InputArgs.filtered(LIPO_segalign)) { - if (!Segalign->getValue(1)) - reportError("segalign is missing an argument: expects -segalign " - "arch_type alignment_value"); - - validateArchitectureName(Segalign->getValue(0)); - - uint32_t AlignmentValue; - if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16)) - reportError("argument to -segalign <arch_type> " + - Twine(Segalign->getValue(1)) + - " (hex) is not a proper hexadecimal number"); - if (!isPowerOf2_32(AlignmentValue)) - reportError("argument to -segalign <arch_type> " + - Twine(Segalign->getValue(1)) + - " (hex) must be a non-zero power of two"); - if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment) - reportError( - "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) + - " (hex) must be less than or equal to the maximum section align 2^" + - Twine(MachOUniversalBinary::MaxSectionAlignment)); - auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0), - Log2_32(AlignmentValue)); - if (!Entry.second) - reportError("-segalign " + Twine(Segalign->getValue(0)) + - " <alignment_value> specified multiple times: " + - Twine(1 << Entry.first->second) + ", " + - Twine(AlignmentValue)); - } - - SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group)); - if (ActionArgs.empty()) - reportError("at least one action should be specified"); - // errors if multiple actions specified other than replace - // multiple replace flags may be specified, as long as they are not mixed with - // other action flags - auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace); - if (ActionArgs.size() > 1 && - ActionArgs.size() != - static_cast<size_t>(std::distance(ReplacementArgsRange.begin(), - ReplacementArgsRange.end()))) { - std::string Buf; - raw_string_ostream OS(Buf); - OS << "only one of the following actions can be specified:"; - for (auto Arg : ActionArgs) - OS << " " << Arg->getSpelling(); - reportError(OS.str()); - } - - switch (ActionArgs[0]->getOption().getID()) { - case LIPO_verify_arch: - for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch)) - C.VerifyArchList.push_back(A); - if (C.VerifyArchList.empty()) - reportError( - "verify_arch requires at least one architecture to be specified"); - if (C.InputFiles.size() > 1) - reportError("verify_arch expects a single input file"); - C.ActionToPerform = LipoAction::VerifyArch; - return C; - - case LIPO_archs: - if (C.InputFiles.size() > 1) - reportError("archs expects a single input file"); - C.ActionToPerform = LipoAction::PrintArchs; - return C; - - case LIPO_info: - C.ActionToPerform = LipoAction::PrintInfo; - return C; - - case LIPO_thin: - if (C.InputFiles.size() > 1) - reportError("thin expects a single input file"); - if (C.OutputFile.empty()) - reportError("thin expects a single output file"); - C.ArchType = ActionArgs[0]->getValue(); - validateArchitectureName(C.ArchType); - C.ActionToPerform = LipoAction::ThinArch; - return C; - - case LIPO_extract: - if (C.InputFiles.size() > 1) - reportError("extract expects a single input file"); - if (C.OutputFile.empty()) - reportError("extract expects a single output file"); - C.ArchType = ActionArgs[0]->getValue(); - validateArchitectureName(C.ArchType); - C.ActionToPerform = LipoAction::ExtractArch; - return C; - - case LIPO_create: - if (C.OutputFile.empty()) - reportError("create expects a single output file to be specified"); - C.ActionToPerform = LipoAction::CreateUniversal; - return C; - - case LIPO_replace: - for (auto Action : ActionArgs) { - if (!Action->getValue(1)) - reportError( - "replace is missing an argument: expects -replace arch_type " - "file_name"); - validateArchitectureName(Action->getValue(0)); - C.ReplacementFiles.push_back( - {StringRef(Action->getValue(0)), Action->getValue(1)}); - } - - if (C.OutputFile.empty()) - reportError("replace expects a single output file to be specified"); - if (C.InputFiles.size() > 1) - reportError("replace expects a single input file"); - C.ActionToPerform = LipoAction::ReplaceArch; - return C; - - default: - reportError("llvm-lipo action unspecified"); - } -} - -static SmallVector<OwningBinary<Binary>, 1> -readInputBinaries(ArrayRef<InputFile> InputFiles) { - SmallVector<OwningBinary<Binary>, 1> InputBinaries; - for (const InputFile &IF : InputFiles) { +} + +} // end namespace + +static void validateArchitectureName(StringRef ArchitectureName) { + if (!MachOObjectFile::isValidArch(ArchitectureName)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS << "Invalid architecture: " << ArchitectureName + << "\nValid architecture names are:"; + for (auto arch : MachOObjectFile::getValidArchs()) + OS << " " << arch; + reportError(OS.str()); + } +} + +static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) { + Config C; + LipoOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (MissingArgumentCount) + reportError("missing argument to " + + StringRef(InputArgs.getArgString(MissingArgumentIndex)) + + " option"); + + if (InputArgs.size() == 0) { + // PrintHelp does not accept Twine. + T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); + exit(EXIT_FAILURE); + } + + if (InputArgs.hasArg(LIPO_help)) { + // PrintHelp does not accept Twine. + T.PrintHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo"); + exit(EXIT_SUCCESS); + } + + if (InputArgs.hasArg(LIPO_version)) { + outs() << ToolName + "\n"; + cl::PrintVersionMessage(); + exit(EXIT_SUCCESS); + } + + for (auto Arg : InputArgs.filtered(LIPO_UNKNOWN)) + reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'"); + + for (auto Arg : InputArgs.filtered(LIPO_INPUT)) + C.InputFiles.push_back({None, Arg->getValue()}); + for (auto Arg : InputArgs.filtered(LIPO_arch)) { + validateArchitectureName(Arg->getValue(0)); + if (!Arg->getValue(1)) + reportError( + "arch is missing an argument: expects -arch arch_type file_name"); + C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)}); + } + + if (C.InputFiles.empty()) + reportError("at least one input file should be specified"); + + if (InputArgs.hasArg(LIPO_output)) + C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output)); + + for (auto Segalign : InputArgs.filtered(LIPO_segalign)) { + if (!Segalign->getValue(1)) + reportError("segalign is missing an argument: expects -segalign " + "arch_type alignment_value"); + + validateArchitectureName(Segalign->getValue(0)); + + uint32_t AlignmentValue; + if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16)) + reportError("argument to -segalign <arch_type> " + + Twine(Segalign->getValue(1)) + + " (hex) is not a proper hexadecimal number"); + if (!isPowerOf2_32(AlignmentValue)) + reportError("argument to -segalign <arch_type> " + + Twine(Segalign->getValue(1)) + + " (hex) must be a non-zero power of two"); + if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment) + reportError( + "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) + + " (hex) must be less than or equal to the maximum section align 2^" + + Twine(MachOUniversalBinary::MaxSectionAlignment)); + auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0), + Log2_32(AlignmentValue)); + if (!Entry.second) + reportError("-segalign " + Twine(Segalign->getValue(0)) + + " <alignment_value> specified multiple times: " + + Twine(1 << Entry.first->second) + ", " + + Twine(AlignmentValue)); + } + + SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group)); + if (ActionArgs.empty()) + reportError("at least one action should be specified"); + // errors if multiple actions specified other than replace + // multiple replace flags may be specified, as long as they are not mixed with + // other action flags + auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace); + if (ActionArgs.size() > 1 && + ActionArgs.size() != + static_cast<size_t>(std::distance(ReplacementArgsRange.begin(), + ReplacementArgsRange.end()))) { + std::string Buf; + raw_string_ostream OS(Buf); + OS << "only one of the following actions can be specified:"; + for (auto Arg : ActionArgs) + OS << " " << Arg->getSpelling(); + reportError(OS.str()); + } + + switch (ActionArgs[0]->getOption().getID()) { + case LIPO_verify_arch: + for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch)) + C.VerifyArchList.push_back(A); + if (C.VerifyArchList.empty()) + reportError( + "verify_arch requires at least one architecture to be specified"); + if (C.InputFiles.size() > 1) + reportError("verify_arch expects a single input file"); + C.ActionToPerform = LipoAction::VerifyArch; + return C; + + case LIPO_archs: + if (C.InputFiles.size() > 1) + reportError("archs expects a single input file"); + C.ActionToPerform = LipoAction::PrintArchs; + return C; + + case LIPO_info: + C.ActionToPerform = LipoAction::PrintInfo; + return C; + + case LIPO_thin: + if (C.InputFiles.size() > 1) + reportError("thin expects a single input file"); + if (C.OutputFile.empty()) + reportError("thin expects a single output file"); + C.ArchType = ActionArgs[0]->getValue(); + validateArchitectureName(C.ArchType); + C.ActionToPerform = LipoAction::ThinArch; + return C; + + case LIPO_extract: + if (C.InputFiles.size() > 1) + reportError("extract expects a single input file"); + if (C.OutputFile.empty()) + reportError("extract expects a single output file"); + C.ArchType = ActionArgs[0]->getValue(); + validateArchitectureName(C.ArchType); + C.ActionToPerform = LipoAction::ExtractArch; + return C; + + case LIPO_create: + if (C.OutputFile.empty()) + reportError("create expects a single output file to be specified"); + C.ActionToPerform = LipoAction::CreateUniversal; + return C; + + case LIPO_replace: + for (auto Action : ActionArgs) { + if (!Action->getValue(1)) + reportError( + "replace is missing an argument: expects -replace arch_type " + "file_name"); + validateArchitectureName(Action->getValue(0)); + C.ReplacementFiles.push_back( + {StringRef(Action->getValue(0)), Action->getValue(1)}); + } + + if (C.OutputFile.empty()) + reportError("replace expects a single output file to be specified"); + if (C.InputFiles.size() > 1) + reportError("replace expects a single input file"); + C.ActionToPerform = LipoAction::ReplaceArch; + return C; + + default: + reportError("llvm-lipo action unspecified"); + } +} + +static SmallVector<OwningBinary<Binary>, 1> +readInputBinaries(ArrayRef<InputFile> InputFiles) { + SmallVector<OwningBinary<Binary>, 1> InputBinaries; + for (const InputFile &IF : InputFiles) { Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(IF.FileName, &LLVMCtx); - if (!BinaryOrErr) - reportError(IF.FileName, BinaryOrErr.takeError()); - const Binary *B = BinaryOrErr->getBinary(); + if (!BinaryOrErr) + reportError(IF.FileName, BinaryOrErr.takeError()); + const Binary *B = BinaryOrErr->getBinary(); if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary() && !B->isIR()) - reportError("File " + IF.FileName + " has unsupported binary format"); + reportError("File " + IF.FileName + " has unsupported binary format"); if (IF.ArchType && (B->isMachO() || B->isArchive() || B->isIR())) { const auto S = B->isMachO() ? Slice(*cast<MachOObjectFile>(B)) : B->isArchive() ? createSliceFromArchive(*cast<Archive>(B)) : createSliceFromIR(*cast<IRObjectFile>(B), 0); - const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture( - MachO::getArchitectureFromName( - Triple(*IF.ArchType).getArchName())) - .first; - // For compatibility with cctools' lipo the comparison is relaxed just to - // checking cputypes. - if (S.getCPUType() != SpecifiedCPUType) - reportError("specified architecture: " + *IF.ArchType + - " for file: " + B->getFileName() + - " does not match the file's architecture (" + - S.getArchString() + ")"); - } - InputBinaries.push_back(std::move(*BinaryOrErr)); - } - return InputBinaries; -} - -LLVM_ATTRIBUTE_NORETURN -static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries, - ArrayRef<std::string> VerifyArchList) { - assert(!VerifyArchList.empty() && - "The list of architectures should be non-empty"); - assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); - - for (StringRef Arch : VerifyArchList) - validateArchitectureName(Arch); - - if (auto UO = - dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) { - for (StringRef Arch : VerifyArchList) { - Expected<MachOUniversalBinary::ObjectForArch> Obj = - UO->getObjectForArch(Arch); - if (!Obj) - exit(EXIT_FAILURE); - } - } else if (auto O = - dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) { - const Triple::ArchType ObjectArch = O->getArch(); - for (StringRef Arch : VerifyArchList) - if (ObjectArch != Triple(Arch).getArch()) - exit(EXIT_FAILURE); - } else { - llvm_unreachable("Unexpected binary format"); - } - exit(EXIT_SUCCESS); -} - -static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) { - // Prints trailing space for compatibility with cctools lipo. - if (auto UO = dyn_cast<MachOUniversalBinary>(Binary)) { - for (const auto &O : UO->objects()) { + const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture( + MachO::getArchitectureFromName( + Triple(*IF.ArchType).getArchName())) + .first; + // For compatibility with cctools' lipo the comparison is relaxed just to + // checking cputypes. + if (S.getCPUType() != SpecifiedCPUType) + reportError("specified architecture: " + *IF.ArchType + + " for file: " + B->getFileName() + + " does not match the file's architecture (" + + S.getArchString() + ")"); + } + InputBinaries.push_back(std::move(*BinaryOrErr)); + } + return InputBinaries; +} + +LLVM_ATTRIBUTE_NORETURN +static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries, + ArrayRef<std::string> VerifyArchList) { + assert(!VerifyArchList.empty() && + "The list of architectures should be non-empty"); + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + + for (StringRef Arch : VerifyArchList) + validateArchitectureName(Arch); + + if (auto UO = + dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) { + for (StringRef Arch : VerifyArchList) { + Expected<MachOUniversalBinary::ObjectForArch> Obj = + UO->getObjectForArch(Arch); + if (!Obj) + exit(EXIT_FAILURE); + } + } else if (auto O = + dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) { + const Triple::ArchType ObjectArch = O->getArch(); + for (StringRef Arch : VerifyArchList) + if (ObjectArch != Triple(Arch).getArch()) + exit(EXIT_FAILURE); + } else { + llvm_unreachable("Unexpected binary format"); + } + exit(EXIT_SUCCESS); +} + +static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) { + // Prints trailing space for compatibility with cctools lipo. + if (auto UO = dyn_cast<MachOUniversalBinary>(Binary)) { + for (const auto &O : UO->objects()) { // Order here is important, because both MachOObjectFile and // IRObjectFile can be created with a binary that has embedded bitcode. - Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError = - O.getAsObjectFile(); - if (MachOObjOrError) { + Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError = + O.getAsObjectFile(); + if (MachOObjOrError) { OS << Slice(*(MachOObjOrError->get())).getArchString() << " "; - continue; - } + continue; + } Expected<std::unique_ptr<IRObjectFile>> IROrError = O.getAsIRObject(LLVMCtx); if (IROrError) { @@ -404,20 +404,20 @@ static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) { OS << SliceOrErr.get().getArchString() << " "; continue; } - Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive(); - if (ArchiveOrError) { - consumeError(MachOObjOrError.takeError()); + Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive(); + if (ArchiveOrError) { + consumeError(MachOObjOrError.takeError()); consumeError(IROrError.takeError()); OS << createSliceFromArchive(**ArchiveOrError).getArchString() << " "; - continue; - } - consumeError(ArchiveOrError.takeError()); - reportError(Binary->getFileName(), MachOObjOrError.takeError()); + continue; + } + consumeError(ArchiveOrError.takeError()); + reportError(Binary->getFileName(), MachOObjOrError.takeError()); reportError(Binary->getFileName(), IROrError.takeError()); - } - OS << "\n"; - return; - } + } + OS << "\n"; + return; + } if (const auto *MachO = dyn_cast<MachOObjectFile>(Binary)) { OS << Slice(*MachO).getArchString() << " \n"; @@ -431,62 +431,62 @@ static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) { reportError(IR->getFileName(), SliceOrErr.takeError()); OS << SliceOrErr->getArchString() << " \n"; -} - -LLVM_ATTRIBUTE_NORETURN -static void printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) { - assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); - printBinaryArchs(InputBinaries.front().getBinary(), outs()); - exit(EXIT_SUCCESS); -} - -LLVM_ATTRIBUTE_NORETURN -static void printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries) { - // Group universal and thin files together for compatibility with cctools lipo - for (auto &IB : InputBinaries) { - const Binary *Binary = IB.getBinary(); - if (Binary->isMachOUniversalBinary()) { - outs() << "Architectures in the fat file: " << Binary->getFileName() - << " are: "; - printBinaryArchs(Binary, outs()); - } - } - for (auto &IB : InputBinaries) { - const Binary *Binary = IB.getBinary(); - if (!Binary->isMachOUniversalBinary()) { - assert(Binary->isMachO() && "expected MachO binary"); - outs() << "Non-fat file: " << Binary->getFileName() - << " is architecture: "; - printBinaryArchs(Binary, outs()); - } - } - exit(EXIT_SUCCESS); -} - -LLVM_ATTRIBUTE_NORETURN -static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries, - StringRef ArchType, StringRef OutputFileName) { - assert(!ArchType.empty() && "The architecture type should be non-empty"); - assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); - assert(!OutputFileName.empty() && "Thin expects a single output file"); - - if (InputBinaries.front().getBinary()->isMachO()) { - reportError("input file " + - InputBinaries.front().getBinary()->getFileName() + - " must be a fat file when the -thin option is specified"); - exit(EXIT_FAILURE); - } - - auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary()); - Expected<std::unique_ptr<MachOObjectFile>> Obj = - UO->getMachOObjectForArch(ArchType); +} + +LLVM_ATTRIBUTE_NORETURN +static void printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) { + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + printBinaryArchs(InputBinaries.front().getBinary(), outs()); + exit(EXIT_SUCCESS); +} + +LLVM_ATTRIBUTE_NORETURN +static void printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries) { + // Group universal and thin files together for compatibility with cctools lipo + for (auto &IB : InputBinaries) { + const Binary *Binary = IB.getBinary(); + if (Binary->isMachOUniversalBinary()) { + outs() << "Architectures in the fat file: " << Binary->getFileName() + << " are: "; + printBinaryArchs(Binary, outs()); + } + } + for (auto &IB : InputBinaries) { + const Binary *Binary = IB.getBinary(); + if (!Binary->isMachOUniversalBinary()) { + assert(Binary->isMachO() && "expected MachO binary"); + outs() << "Non-fat file: " << Binary->getFileName() + << " is architecture: "; + printBinaryArchs(Binary, outs()); + } + } + exit(EXIT_SUCCESS); +} + +LLVM_ATTRIBUTE_NORETURN +static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries, + StringRef ArchType, StringRef OutputFileName) { + assert(!ArchType.empty() && "The architecture type should be non-empty"); + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + assert(!OutputFileName.empty() && "Thin expects a single output file"); + + if (InputBinaries.front().getBinary()->isMachO()) { + reportError("input file " + + InputBinaries.front().getBinary()->getFileName() + + " must be a fat file when the -thin option is specified"); + exit(EXIT_FAILURE); + } + + auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary()); + Expected<std::unique_ptr<MachOObjectFile>> Obj = + UO->getMachOObjectForArch(ArchType); Expected<std::unique_ptr<IRObjectFile>> IRObj = UO->getIRObjectForArch(ArchType, LLVMCtx); - Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType); + Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType); if (!Obj && !IRObj && !Ar) - reportError("fat input file " + UO->getFileName() + - " does not contain the specified architecture " + ArchType + - " to thin it to"); + reportError("fat input file " + UO->getFileName() + + " does not contain the specified architecture " + ArchType + + " to thin it to"); Binary *B; // Order here is important, because both Obj and IRObj will be valid with a // binary that has embedded bitcode. @@ -497,72 +497,72 @@ static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries, else B = Ar->get(); - Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError = - FileOutputBuffer::create(OutputFileName, - B->getMemoryBufferRef().getBufferSize(), - sys::fs::can_execute(UO->getFileName()) - ? FileOutputBuffer::F_executable - : 0); - if (!OutFileOrError) - reportError(OutputFileName, OutFileOrError.takeError()); - std::copy(B->getMemoryBufferRef().getBufferStart(), - B->getMemoryBufferRef().getBufferEnd(), - OutFileOrError.get()->getBufferStart()); - if (Error E = OutFileOrError.get()->commit()) - reportError(OutputFileName, std::move(E)); - exit(EXIT_SUCCESS); -} - -static void checkArchDuplicates(ArrayRef<Slice> Slices) { - DenseMap<uint64_t, const Binary *> CPUIds; - for (const auto &S : Slices) { - auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary()); - if (!Entry.second) - reportError(Entry.first->second->getFileName() + " and " + - S.getBinary()->getFileName() + - " have the same architecture " + S.getArchString() + - " and therefore cannot be in the same universal binary"); - } -} - -template <typename Range> -static void updateAlignments(Range &Slices, - const StringMap<const uint32_t> &Alignments) { - for (auto &Slice : Slices) { - auto Alignment = Alignments.find(Slice.getArchString()); - if (Alignment != Alignments.end()) - Slice.setP2Alignment(Alignment->second); - } -} - -static void checkUnusedAlignments(ArrayRef<Slice> Slices, - const StringMap<const uint32_t> &Alignments) { - auto HasArch = [&](StringRef Arch) { + Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError = + FileOutputBuffer::create(OutputFileName, + B->getMemoryBufferRef().getBufferSize(), + sys::fs::can_execute(UO->getFileName()) + ? FileOutputBuffer::F_executable + : 0); + if (!OutFileOrError) + reportError(OutputFileName, OutFileOrError.takeError()); + std::copy(B->getMemoryBufferRef().getBufferStart(), + B->getMemoryBufferRef().getBufferEnd(), + OutFileOrError.get()->getBufferStart()); + if (Error E = OutFileOrError.get()->commit()) + reportError(OutputFileName, std::move(E)); + exit(EXIT_SUCCESS); +} + +static void checkArchDuplicates(ArrayRef<Slice> Slices) { + DenseMap<uint64_t, const Binary *> CPUIds; + for (const auto &S : Slices) { + auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary()); + if (!Entry.second) + reportError(Entry.first->second->getFileName() + " and " + + S.getBinary()->getFileName() + + " have the same architecture " + S.getArchString() + + " and therefore cannot be in the same universal binary"); + } +} + +template <typename Range> +static void updateAlignments(Range &Slices, + const StringMap<const uint32_t> &Alignments) { + for (auto &Slice : Slices) { + auto Alignment = Alignments.find(Slice.getArchString()); + if (Alignment != Alignments.end()) + Slice.setP2Alignment(Alignment->second); + } +} + +static void checkUnusedAlignments(ArrayRef<Slice> Slices, + const StringMap<const uint32_t> &Alignments) { + auto HasArch = [&](StringRef Arch) { return llvm::any_of(Slices, [Arch](Slice S) { return S.getArchString() == Arch; }); - }; - for (StringRef Arch : Alignments.keys()) - if (!HasArch(Arch)) - reportError("-segalign " + Arch + - " <value> specified but resulting fat file does not contain " - "that architecture "); -} - -// Updates vector ExtractedObjects with the MachOObjectFiles extracted from -// Universal Binary files to transfer ownership. + }; + for (StringRef Arch : Alignments.keys()) + if (!HasArch(Arch)) + reportError("-segalign " + Arch + + " <value> specified but resulting fat file does not contain " + "that architecture "); +} + +// Updates vector ExtractedObjects with the MachOObjectFiles extracted from +// Universal Binary files to transfer ownership. static SmallVector<Slice, 2> buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries, const StringMap<const uint32_t> &Alignments, SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) { - SmallVector<Slice, 2> Slices; - for (auto &IB : InputBinaries) { - const Binary *InputBinary = IB.getBinary(); - if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) { - for (const auto &O : UO->objects()) { + SmallVector<Slice, 2> Slices; + for (auto &IB : InputBinaries) { + const Binary *InputBinary = IB.getBinary(); + if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) { + for (const auto &O : UO->objects()) { // Order here is important, because both MachOObjectFile and // IRObjectFile can be created with a binary that has embedded bitcode. - Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError = - O.getAsObjectFile(); + Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError = + O.getAsObjectFile(); if (BinaryOrError) { Slices.emplace_back(*(BinaryOrError.get()), O.getAlign()); ExtractedObjects.push_back(std::move(BinaryOrError.get())); @@ -578,7 +578,7 @@ buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries, continue; } reportError(InputBinary->getFileName(), BinaryOrError.takeError()); - } + } } else if (const auto *O = dyn_cast<MachOObjectFile>(InputBinary)) { Slices.emplace_back(*O); } else if (const auto *A = dyn_cast<Archive>(InputBinary)) { @@ -591,166 +591,166 @@ buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries, continue; } Slices.emplace_back(std::move(SliceOrErr.get())); - } else { - llvm_unreachable("Unexpected binary format"); - } - } - updateAlignments(Slices, Alignments); - return Slices; -} - -LLVM_ATTRIBUTE_NORETURN -static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries, - const StringMap<const uint32_t> &Alignments, - StringRef OutputFileName) { - assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries"); - assert(!OutputFileName.empty() && "Create expects a single output file"); - + } else { + llvm_unreachable("Unexpected binary format"); + } + } + updateAlignments(Slices, Alignments); + return Slices; +} + +LLVM_ATTRIBUTE_NORETURN +static void createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries, + const StringMap<const uint32_t> &Alignments, + StringRef OutputFileName) { + assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries"); + assert(!OutputFileName.empty() && "Create expects a single output file"); + SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects; - SmallVector<Slice, 1> Slices = - buildSlices(InputBinaries, Alignments, ExtractedObjects); - checkArchDuplicates(Slices); - checkUnusedAlignments(Slices, Alignments); - + SmallVector<Slice, 1> Slices = + buildSlices(InputBinaries, Alignments, ExtractedObjects); + checkArchDuplicates(Slices); + checkUnusedAlignments(Slices, Alignments); + llvm::stable_sort(Slices); if (Error E = writeUniversalBinary(Slices, OutputFileName)) reportError(std::move(E)); - exit(EXIT_SUCCESS); -} - -LLVM_ATTRIBUTE_NORETURN -static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries, - const StringMap<const uint32_t> &Alignments, - StringRef ArchType, StringRef OutputFileName) { - assert(!ArchType.empty() && - "The architecture type should be non-empty"); - assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); - assert(!OutputFileName.empty() && "Thin expects a single output file"); - - if (InputBinaries.front().getBinary()->isMachO()) { - reportError("input file " + - InputBinaries.front().getBinary()->getFileName() + - " must be a fat file when the -extract option is specified"); - } - + exit(EXIT_SUCCESS); +} + +LLVM_ATTRIBUTE_NORETURN +static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries, + const StringMap<const uint32_t> &Alignments, + StringRef ArchType, StringRef OutputFileName) { + assert(!ArchType.empty() && + "The architecture type should be non-empty"); + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + assert(!OutputFileName.empty() && "Thin expects a single output file"); + + if (InputBinaries.front().getBinary()->isMachO()) { + reportError("input file " + + InputBinaries.front().getBinary()->getFileName() + + " must be a fat file when the -extract option is specified"); + } + SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects; - SmallVector<Slice, 2> Slices = - buildSlices(InputBinaries, Alignments, ExtractedObjects); - erase_if(Slices, [ArchType](const Slice &S) { - return ArchType != S.getArchString(); - }); - - if (Slices.empty()) - reportError( - "fat input file " + InputBinaries.front().getBinary()->getFileName() + - " does not contain the specified architecture " + ArchType); + SmallVector<Slice, 2> Slices = + buildSlices(InputBinaries, Alignments, ExtractedObjects); + erase_if(Slices, [ArchType](const Slice &S) { + return ArchType != S.getArchString(); + }); + + if (Slices.empty()) + reportError( + "fat input file " + InputBinaries.front().getBinary()->getFileName() + + " does not contain the specified architecture " + ArchType); llvm::stable_sort(Slices); if (Error E = writeUniversalBinary(Slices, OutputFileName)) reportError(std::move(E)); - exit(EXIT_SUCCESS); -} - -static StringMap<Slice> -buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries, - const StringMap<const uint32_t> &Alignments) { - StringMap<Slice> Slices; - // populates StringMap of slices to replace with; error checks for mismatched - // replace flag args, fat files, and duplicate arch_types - for (const auto &OB : ReplacementBinaries) { - const Binary *ReplacementBinary = OB.getBinary(); - auto O = dyn_cast<MachOObjectFile>(ReplacementBinary); - if (!O) - reportError("replacement file: " + ReplacementBinary->getFileName() + - " is a fat file (must be a thin file)"); + exit(EXIT_SUCCESS); +} + +static StringMap<Slice> +buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries, + const StringMap<const uint32_t> &Alignments) { + StringMap<Slice> Slices; + // populates StringMap of slices to replace with; error checks for mismatched + // replace flag args, fat files, and duplicate arch_types + for (const auto &OB : ReplacementBinaries) { + const Binary *ReplacementBinary = OB.getBinary(); + auto O = dyn_cast<MachOObjectFile>(ReplacementBinary); + if (!O) + reportError("replacement file: " + ReplacementBinary->getFileName() + + " is a fat file (must be a thin file)"); Slice S(*O); - auto Entry = Slices.try_emplace(S.getArchString(), S); - if (!Entry.second) - reportError("-replace " + S.getArchString() + - " <file_name> specified multiple times: " + - Entry.first->second.getBinary()->getFileName() + ", " + - O->getFileName()); - } - auto SlicesMapRange = map_range( - Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); }); - updateAlignments(SlicesMapRange, Alignments); - return Slices; -} - -LLVM_ATTRIBUTE_NORETURN -static void replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries, - const StringMap<const uint32_t> &Alignments, - StringRef OutputFileName, - ArrayRef<InputFile> ReplacementFiles) { - assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); - assert(!OutputFileName.empty() && "Replace expects a single output file"); - - if (InputBinaries.front().getBinary()->isMachO()) - reportError("input file " + - InputBinaries.front().getBinary()->getFileName() + - " must be a fat file when the -replace option is specified"); - - SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries = - readInputBinaries(ReplacementFiles); - - StringMap<Slice> ReplacementSlices = - buildReplacementSlices(ReplacementBinaries, Alignments); + auto Entry = Slices.try_emplace(S.getArchString(), S); + if (!Entry.second) + reportError("-replace " + S.getArchString() + + " <file_name> specified multiple times: " + + Entry.first->second.getBinary()->getFileName() + ", " + + O->getFileName()); + } + auto SlicesMapRange = map_range( + Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); }); + updateAlignments(SlicesMapRange, Alignments); + return Slices; +} + +LLVM_ATTRIBUTE_NORETURN +static void replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries, + const StringMap<const uint32_t> &Alignments, + StringRef OutputFileName, + ArrayRef<InputFile> ReplacementFiles) { + assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); + assert(!OutputFileName.empty() && "Replace expects a single output file"); + + if (InputBinaries.front().getBinary()->isMachO()) + reportError("input file " + + InputBinaries.front().getBinary()->getFileName() + + " must be a fat file when the -replace option is specified"); + + SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries = + readInputBinaries(ReplacementFiles); + + StringMap<Slice> ReplacementSlices = + buildReplacementSlices(ReplacementBinaries, Alignments); SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects; - SmallVector<Slice, 2> Slices = - buildSlices(InputBinaries, Alignments, ExtractedObjects); - - for (auto &Slice : Slices) { - auto It = ReplacementSlices.find(Slice.getArchString()); - if (It != ReplacementSlices.end()) { - Slice = It->second; - ReplacementSlices.erase(It); // only keep remaining replacing arch_types - } - } - - if (!ReplacementSlices.empty()) - reportError("-replace " + ReplacementSlices.begin()->first() + - " <file_name> specified but fat file: " + - InputBinaries.front().getBinary()->getFileName() + - " does not contain that architecture"); - - checkUnusedAlignments(Slices, Alignments); + SmallVector<Slice, 2> Slices = + buildSlices(InputBinaries, Alignments, ExtractedObjects); + + for (auto &Slice : Slices) { + auto It = ReplacementSlices.find(Slice.getArchString()); + if (It != ReplacementSlices.end()) { + Slice = It->second; + ReplacementSlices.erase(It); // only keep remaining replacing arch_types + } + } + + if (!ReplacementSlices.empty()) + reportError("-replace " + ReplacementSlices.begin()->first() + + " <file_name> specified but fat file: " + + InputBinaries.front().getBinary()->getFileName() + + " does not contain that architecture"); + + checkUnusedAlignments(Slices, Alignments); llvm::stable_sort(Slices); if (Error E = writeUniversalBinary(Slices, OutputFileName)) reportError(std::move(E)); - exit(EXIT_SUCCESS); -} - -int main(int argc, char **argv) { - InitLLVM X(argc, argv); - Config C = parseLipoOptions(makeArrayRef(argv + 1, argc)); - SmallVector<OwningBinary<Binary>, 1> InputBinaries = - readInputBinaries(C.InputFiles); - - switch (C.ActionToPerform) { - case LipoAction::VerifyArch: - verifyArch(InputBinaries, C.VerifyArchList); - break; - case LipoAction::PrintArchs: - printArchs(InputBinaries); - break; - case LipoAction::PrintInfo: - printInfo(InputBinaries); - break; - case LipoAction::ThinArch: - thinSlice(InputBinaries, C.ArchType, C.OutputFile); - break; - case LipoAction::ExtractArch: - extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile); - break; - case LipoAction::CreateUniversal: - createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile); - break; - case LipoAction::ReplaceArch: - replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile, - C.ReplacementFiles); - break; - } - return EXIT_SUCCESS; -} + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + Config C = parseLipoOptions(makeArrayRef(argv + 1, argc)); + SmallVector<OwningBinary<Binary>, 1> InputBinaries = + readInputBinaries(C.InputFiles); + + switch (C.ActionToPerform) { + case LipoAction::VerifyArch: + verifyArch(InputBinaries, C.VerifyArchList); + break; + case LipoAction::PrintArchs: + printArchs(InputBinaries); + break; + case LipoAction::PrintInfo: + printInfo(InputBinaries); + break; + case LipoAction::ThinArch: + thinSlice(InputBinaries, C.ArchType, C.OutputFile); + break; + case LipoAction::ExtractArch: + extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile); + break; + case LipoAction::CreateUniversal: + createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile); + break; + case LipoAction::ReplaceArch: + replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile, + C.ReplacementFiles); + break; + } + return EXIT_SUCCESS; +} diff --git a/contrib/libs/llvm12/tools/llvm-lipo/ya.make b/contrib/libs/llvm12/tools/llvm-lipo/ya.make index afb28b31ce..157bfe611e 100644 --- a/contrib/libs/llvm12/tools/llvm-lipo/ya.make +++ b/contrib/libs/llvm12/tools/llvm-lipo/ya.make @@ -1,17 +1,17 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/BinaryFormat @@ -56,19 +56,19 @@ PEERDIR( contrib/libs/llvm12/lib/Target/X86/MCTargetDesc contrib/libs/llvm12/lib/Target/X86/TargetInfo contrib/libs/llvm12/lib/TextAPI/MachO -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-lipo contrib/libs/llvm12/tools/llvm-lipo -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - llvm-lipo.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + llvm-lipo.cpp +) + +END() diff --git a/contrib/libs/llvm12/tools/llvm-ml/Disassembler.cpp b/contrib/libs/llvm12/tools/llvm-ml/Disassembler.cpp index 8eeddb7179..a7fec3c35d 100644 --- a/contrib/libs/llvm12/tools/llvm-ml/Disassembler.cpp +++ b/contrib/libs/llvm12/tools/llvm-ml/Disassembler.cpp @@ -1,203 +1,203 @@ -//===- Disassembler.cpp - Disassembler for hex strings --------------------===// -// -// 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 class implements the disassembler of strings of bytes written in -// hexadecimal, from standard input or from a file. -// -//===----------------------------------------------------------------------===// - -#include "Disassembler.h" -#include "llvm/ADT/Triple.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCDisassembler/MCDisassembler.h" -#include "llvm/MC/MCInst.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -typedef std::pair<std::vector<unsigned char>, std::vector<const char *>> - ByteArrayTy; - -static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes, - SourceMgr &SM, raw_ostream &Out, MCStreamer &Streamer, - bool InAtomicBlock, const MCSubtargetInfo &STI) { - ArrayRef<uint8_t> Data(Bytes.first.data(), Bytes.first.size()); - - // Disassemble it to strings. - uint64_t Size; - uint64_t Index; - - for (Index = 0; Index < Bytes.first.size(); Index += Size) { - MCInst Inst; - - MCDisassembler::DecodeStatus S; - S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls()); - switch (S) { - case MCDisassembler::Fail: - SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]), - SourceMgr::DK_Warning, "invalid instruction encoding"); - // Don't try to resynchronise the stream in a block - if (InAtomicBlock) - return true; - - if (Size == 0) - Size = 1; // skip illegible bytes - - break; - - case MCDisassembler::SoftFail: - SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]), - SourceMgr::DK_Warning, - "potentially undefined instruction encoding"); - LLVM_FALLTHROUGH; - - case MCDisassembler::Success: - Streamer.emitInstruction(Inst, STI); - break; - } - } - - return false; -} - -static bool SkipToToken(StringRef &Str) { - for (;;) { - if (Str.empty()) - return false; - - // Strip horizontal whitespace and commas. - if (size_t Pos = Str.find_first_not_of(" \t\r\n,")) { - Str = Str.substr(Pos); - continue; - } - - // If this is the start of a comment, remove the rest of the line. - if (Str[0] == '#') { - Str = Str.substr(Str.find_first_of('\n')); - continue; - } - return true; - } -} - -static bool ByteArrayFromString(ByteArrayTy &ByteArray, StringRef &Str, - SourceMgr &SM) { - while (SkipToToken(Str)) { - // Handled by higher level - if (Str[0] == '[' || Str[0] == ']') - return false; - - // Get the current token. - size_t Next = Str.find_first_of(" \t\n\r,#[]"); - StringRef Value = Str.substr(0, Next); - - // Convert to a byte and add to the byte vector. - unsigned ByteVal; - if (Value.getAsInteger(0, ByteVal) || ByteVal > 255) { - // If we have an error, print it and skip to the end of line. - SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error, - "invalid input token"); - Str = Str.substr(Str.find('\n')); - ByteArray.first.clear(); - ByteArray.second.clear(); - continue; - } - - ByteArray.first.push_back(ByteVal); - ByteArray.second.push_back(Value.data()); - Str = Str.substr(Next); - } - - return false; -} - -int Disassembler::disassemble(const Target &T, const std::string &Triple, - MCSubtargetInfo &STI, MCStreamer &Streamer, - MemoryBuffer &Buffer, SourceMgr &SM, - raw_ostream &Out) { - std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(Triple)); - if (!MRI) { - errs() << "error: no register info for target " << Triple << "\n"; - return -1; - } - - MCTargetOptions MCOptions; - std::unique_ptr<const MCAsmInfo> MAI( - T.createMCAsmInfo(*MRI, Triple, MCOptions)); - if (!MAI) { - errs() << "error: no assembly info for target " << Triple << "\n"; - return -1; - } - - // Set up the MCContext for creating symbols and MCExpr's. - MCContext Ctx(MAI.get(), MRI.get(), nullptr); - - std::unique_ptr<const MCDisassembler> DisAsm( - T.createMCDisassembler(STI, Ctx)); - if (!DisAsm) { - errs() << "error: no disassembler for target " << Triple << "\n"; - return -1; - } - - // Set up initial section manually here - Streamer.InitSections(false); - - bool ErrorOccurred = false; - - // Convert the input to a vector for disassembly. - ByteArrayTy ByteArray; - StringRef Str = Buffer.getBuffer(); - bool InAtomicBlock = false; - - while (SkipToToken(Str)) { - ByteArray.first.clear(); - ByteArray.second.clear(); - - if (Str[0] == '[') { - if (InAtomicBlock) { - SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, - "nested atomic blocks make no sense"); - ErrorOccurred = true; - } - InAtomicBlock = true; - Str = Str.drop_front(); - continue; - } else if (Str[0] == ']') { - if (!InAtomicBlock) { - SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, - "attempt to close atomic block without opening"); - ErrorOccurred = true; - } - InAtomicBlock = false; - Str = Str.drop_front(); - continue; - } - - // It's a real token, get the bytes and emit them - ErrorOccurred |= ByteArrayFromString(ByteArray, Str, SM); - - if (!ByteArray.first.empty()) - ErrorOccurred |= - PrintInsts(*DisAsm, ByteArray, SM, Out, Streamer, InAtomicBlock, STI); - } - - if (InAtomicBlock) { - SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, - "unclosed atomic block"); - ErrorOccurred = true; - } - - return ErrorOccurred; -} +//===- Disassembler.cpp - Disassembler for hex strings --------------------===// +// +// 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 class implements the disassembler of strings of bytes written in +// hexadecimal, from standard input or from a file. +// +//===----------------------------------------------------------------------===// + +#include "Disassembler.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +typedef std::pair<std::vector<unsigned char>, std::vector<const char *>> + ByteArrayTy; + +static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes, + SourceMgr &SM, raw_ostream &Out, MCStreamer &Streamer, + bool InAtomicBlock, const MCSubtargetInfo &STI) { + ArrayRef<uint8_t> Data(Bytes.first.data(), Bytes.first.size()); + + // Disassemble it to strings. + uint64_t Size; + uint64_t Index; + + for (Index = 0; Index < Bytes.first.size(); Index += Size) { + MCInst Inst; + + MCDisassembler::DecodeStatus S; + S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls()); + switch (S) { + case MCDisassembler::Fail: + SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]), + SourceMgr::DK_Warning, "invalid instruction encoding"); + // Don't try to resynchronise the stream in a block + if (InAtomicBlock) + return true; + + if (Size == 0) + Size = 1; // skip illegible bytes + + break; + + case MCDisassembler::SoftFail: + SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]), + SourceMgr::DK_Warning, + "potentially undefined instruction encoding"); + LLVM_FALLTHROUGH; + + case MCDisassembler::Success: + Streamer.emitInstruction(Inst, STI); + break; + } + } + + return false; +} + +static bool SkipToToken(StringRef &Str) { + for (;;) { + if (Str.empty()) + return false; + + // Strip horizontal whitespace and commas. + if (size_t Pos = Str.find_first_not_of(" \t\r\n,")) { + Str = Str.substr(Pos); + continue; + } + + // If this is the start of a comment, remove the rest of the line. + if (Str[0] == '#') { + Str = Str.substr(Str.find_first_of('\n')); + continue; + } + return true; + } +} + +static bool ByteArrayFromString(ByteArrayTy &ByteArray, StringRef &Str, + SourceMgr &SM) { + while (SkipToToken(Str)) { + // Handled by higher level + if (Str[0] == '[' || Str[0] == ']') + return false; + + // Get the current token. + size_t Next = Str.find_first_of(" \t\n\r,#[]"); + StringRef Value = Str.substr(0, Next); + + // Convert to a byte and add to the byte vector. + unsigned ByteVal; + if (Value.getAsInteger(0, ByteVal) || ByteVal > 255) { + // If we have an error, print it and skip to the end of line. + SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error, + "invalid input token"); + Str = Str.substr(Str.find('\n')); + ByteArray.first.clear(); + ByteArray.second.clear(); + continue; + } + + ByteArray.first.push_back(ByteVal); + ByteArray.second.push_back(Value.data()); + Str = Str.substr(Next); + } + + return false; +} + +int Disassembler::disassemble(const Target &T, const std::string &Triple, + MCSubtargetInfo &STI, MCStreamer &Streamer, + MemoryBuffer &Buffer, SourceMgr &SM, + raw_ostream &Out) { + std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(Triple)); + if (!MRI) { + errs() << "error: no register info for target " << Triple << "\n"; + return -1; + } + + MCTargetOptions MCOptions; + std::unique_ptr<const MCAsmInfo> MAI( + T.createMCAsmInfo(*MRI, Triple, MCOptions)); + if (!MAI) { + errs() << "error: no assembly info for target " << Triple << "\n"; + return -1; + } + + // Set up the MCContext for creating symbols and MCExpr's. + MCContext Ctx(MAI.get(), MRI.get(), nullptr); + + std::unique_ptr<const MCDisassembler> DisAsm( + T.createMCDisassembler(STI, Ctx)); + if (!DisAsm) { + errs() << "error: no disassembler for target " << Triple << "\n"; + return -1; + } + + // Set up initial section manually here + Streamer.InitSections(false); + + bool ErrorOccurred = false; + + // Convert the input to a vector for disassembly. + ByteArrayTy ByteArray; + StringRef Str = Buffer.getBuffer(); + bool InAtomicBlock = false; + + while (SkipToToken(Str)) { + ByteArray.first.clear(); + ByteArray.second.clear(); + + if (Str[0] == '[') { + if (InAtomicBlock) { + SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, + "nested atomic blocks make no sense"); + ErrorOccurred = true; + } + InAtomicBlock = true; + Str = Str.drop_front(); + continue; + } else if (Str[0] == ']') { + if (!InAtomicBlock) { + SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, + "attempt to close atomic block without opening"); + ErrorOccurred = true; + } + InAtomicBlock = false; + Str = Str.drop_front(); + continue; + } + + // It's a real token, get the bytes and emit them + ErrorOccurred |= ByteArrayFromString(ByteArray, Str, SM); + + if (!ByteArray.first.empty()) + ErrorOccurred |= + PrintInsts(*DisAsm, ByteArray, SM, Out, Streamer, InAtomicBlock, STI); + } + + if (InAtomicBlock) { + SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, + "unclosed atomic block"); + ErrorOccurred = true; + } + + return ErrorOccurred; +} diff --git a/contrib/libs/llvm12/tools/llvm-ml/Disassembler.h b/contrib/libs/llvm12/tools/llvm-ml/Disassembler.h index d3565089e2..f54bab3239 100644 --- a/contrib/libs/llvm12/tools/llvm-ml/Disassembler.h +++ b/contrib/libs/llvm12/tools/llvm-ml/Disassembler.h @@ -1,37 +1,37 @@ -//===- Disassembler.h - Text File Disassembler ----------------------------===// -// -// 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 class implements the disassembler of strings of bytes written in -// hexadecimal, from standard input or from a file. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H -#define LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H - -#include <string> - -namespace llvm { - -class MemoryBuffer; -class Target; -class raw_ostream; -class SourceMgr; -class MCSubtargetInfo; -class MCStreamer; - -class Disassembler { -public: - static int disassemble(const Target &T, const std::string &Triple, - MCSubtargetInfo &STI, MCStreamer &Streamer, - MemoryBuffer &Buffer, SourceMgr &SM, raw_ostream &Out); -}; - -} // namespace llvm - -#endif +//===- Disassembler.h - Text File Disassembler ----------------------------===// +// +// 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 class implements the disassembler of strings of bytes written in +// hexadecimal, from standard input or from a file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H +#define LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H + +#include <string> + +namespace llvm { + +class MemoryBuffer; +class Target; +class raw_ostream; +class SourceMgr; +class MCSubtargetInfo; +class MCStreamer; + +class Disassembler { +public: + static int disassemble(const Target &T, const std::string &Triple, + MCSubtargetInfo &STI, MCStreamer &Streamer, + MemoryBuffer &Buffer, SourceMgr &SM, raw_ostream &Out); +}; + +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-ml/llvm-ml.cpp b/contrib/libs/llvm12/tools/llvm-ml/llvm-ml.cpp index 1733dcd472..8ac253723e 100644 --- a/contrib/libs/llvm12/tools/llvm-ml/llvm-ml.cpp +++ b/contrib/libs/llvm12/tools/llvm-ml/llvm-ml.cpp @@ -1,52 +1,52 @@ -//===-- llvm-ml.cpp - masm-compatible assembler -----------------*- 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 -// -//===----------------------------------------------------------------------===// -// -// A simple driver around MasmParser; based on llvm-mc. -// -//===----------------------------------------------------------------------===// - +//===-- llvm-ml.cpp - masm-compatible assembler -----------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// A simple driver around MasmParser; based on llvm-mc. +// +//===----------------------------------------------------------------------===// + #include "llvm/ADT/StringSwitch.h" -#include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCCodeEmitter.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCInstPrinter.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCObjectFileInfo.h" -#include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCParser/AsmLexer.h" -#include "llvm/MC/MCParser/MCTargetAsmParser.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCParser/AsmLexer.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" -#include "llvm/Support/Compression.h" -#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Support/ToolOutputFile.h" -#include "llvm/Support/WithColor.h" - -using namespace llvm; +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; using namespace llvm::opt; - + namespace { - + enum ID { OPT_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ @@ -54,12 +54,12 @@ enum ID { OPT_##ID, #include "Opts.inc" #undef OPTION -}; - +}; + #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "Opts.inc" #undef PREFIX - + static const opt::OptTable::Info InfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ @@ -70,83 +70,83 @@ static const opt::OptTable::Info InfoTable[] = { OPT_##ALIAS, ALIASARGS, VALUES}, #include "Opts.inc" #undef OPTION -}; - +}; + class MLOptTable : public opt::OptTable { public: MLOptTable() : OptTable(InfoTable, /*IgnoreCase=*/false) {} -}; +}; } // namespace - + static Triple GetTriple(StringRef ProgName, opt::InputArgList &Args) { - // Figure out the target triple. + // Figure out the target triple. StringRef DefaultBitness = "32"; SmallString<255> Program = ProgName; sys::path::replace_extension(Program, ""); if (Program.endswith("ml64")) DefaultBitness = "64"; - + StringRef TripleName = StringSwitch<StringRef>(Args.getLastArgValue(OPT_bitness, DefaultBitness)) .Case("32", "i386-pc-windows") .Case("64", "x86_64-pc-windows") .Default(""); return Triple(Triple::normalize(TripleName)); -} - -static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) { - std::error_code EC; - auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::F_None); - if (EC) { - WithColor::error() << EC.message() << '\n'; - return nullptr; - } - - return Out; -} - -static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) { - AsmLexer Lexer(MAI); - Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer()); +} + +static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) { + std::error_code EC; + auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::F_None); + if (EC) { + WithColor::error() << EC.message() << '\n'; + return nullptr; + } + + return Out; +} + +static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) { + AsmLexer Lexer(MAI); + Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer()); Lexer.setLexMasmIntegers(true); Lexer.useMasmDefaultRadix(true); Lexer.setLexMasmHexFloats(true); Lexer.setLexMasmStrings(true); - - bool Error = false; - while (Lexer.Lex().isNot(AsmToken::Eof)) { - Lexer.getTok().dump(OS); - OS << "\n"; - if (Lexer.getTok().getKind() == AsmToken::Error) - Error = true; - } - - return Error; -} - + + bool Error = false; + while (Lexer.Lex().isNot(AsmToken::Eof)) { + Lexer.getTok().dump(OS); + OS << "\n"; + if (Lexer.getTok().getKind() == AsmToken::Error) + Error = true; + } + + return Error; +} + static int AssembleInput(StringRef ProgName, const Target *TheTarget, - SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, - MCAsmInfo &MAI, MCSubtargetInfo &STI, + SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, + MCAsmInfo &MAI, MCSubtargetInfo &STI, MCInstrInfo &MCII, MCTargetOptions &MCOptions, const opt::ArgList &InputArgs) { - std::unique_ptr<MCAsmParser> Parser( + std::unique_ptr<MCAsmParser> Parser( createMCMasmParser(SrcMgr, Ctx, Str, MAI, 0)); - std::unique_ptr<MCTargetAsmParser> TAP( - TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); - - if (!TAP) { - WithColor::error(errs(), ProgName) - << "this target does not support assembly parsing.\n"; - return 1; - } - + std::unique_ptr<MCTargetAsmParser> TAP( + TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions)); + + if (!TAP) { + WithColor::error(errs(), ProgName) + << "this target does not support assembly parsing.\n"; + return 1; + } + Parser->setShowParsedOperands(InputArgs.hasArg(OPT_show_inst_operands)); - Parser->setTargetParser(*TAP); - Parser->getLexer().setLexMasmIntegers(true); + Parser->setTargetParser(*TAP); + Parser->getLexer().setLexMasmIntegers(true); Parser->getLexer().useMasmDefaultRadix(true); Parser->getLexer().setLexMasmHexFloats(true); Parser->getLexer().setLexMasmStrings(true); - + auto Defines = InputArgs.getAllArgValues(OPT_define); for (StringRef Define : Defines) { const auto NameValue = Define.split('='); @@ -158,27 +158,27 @@ static int AssembleInput(StringRef ProgName, const Target *TheTarget, } } - int Res = Parser->Run(/*NoInitialTextSection=*/true); - - return Res; -} - + int Res = Parser->Run(/*NoInitialTextSection=*/true); + + return Res; +} + int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); StringRef ProgName = sys::path::filename(Argv[0]); - - // Initialize targets and assembly printers/parsers. - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmParsers(); - llvm::InitializeAllDisassemblers(); - + + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); + MLOptTable T; unsigned MissingArgIndex, MissingArgCount; ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgIndex, MissingArgCount); - + std::string InputFilename; for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { std::string ArgString = Arg->getAsString(InputArgs); @@ -230,17 +230,17 @@ int main(int Argc, char **Argv) { } MCTargetOptions MCOptions; - MCOptions.AssemblyLanguage = "masm"; - + MCOptions.AssemblyLanguage = "masm"; + Triple TheTriple = GetTriple(ProgName, InputArgs); std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error); if (!TheTarget) { WithColor::error(errs(), ProgName) << Error; - return 1; + return 1; } const std::string &TripleName = TheTriple.getTriple(); - + bool SafeSEH = InputArgs.hasArg(OPT_safeseh); if (SafeSEH && !(TheTriple.isArch32Bit() && TheTriple.isX86())) { WithColor::warning() @@ -248,42 +248,42 @@ int main(int Argc, char **Argv) { SafeSEH = false; } - ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = - MemoryBuffer::getFileOrSTDIN(InputFilename); - if (std::error_code EC = BufferPtr.getError()) { - WithColor::error(errs(), ProgName) - << InputFilename << ": " << EC.message() << '\n'; - return 1; - } - - SourceMgr SrcMgr; - - // Tell SrcMgr about this buffer, which is what the parser will pick up. - SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); - - // Record the location of the include directories so that the lexer can find - // it later. + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = + MemoryBuffer::getFileOrSTDIN(InputFilename); + if (std::error_code EC = BufferPtr.getError()) { + WithColor::error(errs(), ProgName) + << InputFilename << ": " << EC.message() << '\n'; + return 1; + } + + SourceMgr SrcMgr; + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); + + // Record the location of the include directories so that the lexer can find + // it later. SrcMgr.setIncludeDirs(InputArgs.getAllArgValues(OPT_include_path)); - - std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); - assert(MRI && "Unable to create target register info!"); - - std::unique_ptr<MCAsmInfo> MAI( - TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); - assert(MAI && "Unable to create target asm info!"); - + + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); + assert(MRI && "Unable to create target register info!"); + + std::unique_ptr<MCAsmInfo> MAI( + TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + assert(MAI && "Unable to create target asm info!"); + MAI->setPreserveAsmComments(InputArgs.hasArg(OPT_preserve_comments)); - - // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and - // MCObjectFileInfo needs a MCContext reference in order to initialize itself. - MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); - MOFI.InitMCObjectFileInfo(TheTriple, /*PIC=*/false, Ctx, - /*LargeCodeModel=*/true); - + + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and + // MCObjectFileInfo needs a MCContext reference in order to initialize itself. + MCObjectFileInfo MOFI; + MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); + MOFI.InitMCObjectFileInfo(TheTriple, /*PIC=*/false, Ctx, + /*LargeCodeModel=*/true); + if (InputArgs.hasArg(OPT_save_temp_labels)) - Ctx.setAllowTemporaryLabels(false); - + Ctx.setAllowTemporaryLabels(false); + // Set compilation information. SmallString<128> CWD; if (!sys::fs::current_path(CWD)) @@ -294,78 +294,78 @@ int main(int Argc, char **Argv) { SmallString<255> DefaultOutputFilename; if (InputArgs.hasArg(OPT_as_lex)) { DefaultOutputFilename = "-"; - } else { + } else { DefaultOutputFilename = InputFilename; sys::path::replace_extension(DefaultOutputFilename, FileType); - } + } const StringRef OutputFilename = InputArgs.getLastArgValue(OPT_output_file, DefaultOutputFilename); - std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename); - if (!Out) - return 1; - - std::unique_ptr<buffer_ostream> BOS; - raw_pwrite_stream *OS = &Out->os(); - std::unique_ptr<MCStreamer> Str; - - std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); + std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename); + if (!Out) + return 1; + + std::unique_ptr<buffer_ostream> BOS; + raw_pwrite_stream *OS = &Out->os(); + std::unique_ptr<MCStreamer> Str; + + std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); assert(MCII && "Unable to create instruction info!"); - std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo( - TripleName, /*CPU=*/"", /*Features=*/"")); + std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo( + TripleName, /*CPU=*/"", /*Features=*/"")); assert(STI && "Unable to create subtarget info!"); - - MCInstPrinter *IP = nullptr; + + MCInstPrinter *IP = nullptr; if (FileType == "s") { const bool OutputATTAsm = InputArgs.hasArg(OPT_output_att_asm); - const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect - : 1U; // Intel dialect + const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect + : 1U; // Intel dialect IP = TheTarget->createMCInstPrinter(TheTriple, OutputAsmVariant, *MAI, *MCII, *MRI); - - if (!IP) { - WithColor::error() - << "unable to create instruction printer for target triple '" - << TheTriple.normalize() << "' with " - << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n"; - return 1; - } - - // Set the display preference for hex vs. decimal immediates. + + if (!IP) { + WithColor::error() + << "unable to create instruction printer for target triple '" + << TheTriple.normalize() << "' with " + << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n"; + return 1; + } + + // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(InputArgs.hasArg(OPT_print_imm_hex)); - - // Set up the AsmStreamer. - std::unique_ptr<MCCodeEmitter> CE; + + // Set up the AsmStreamer. + std::unique_ptr<MCCodeEmitter> CE; if (InputArgs.hasArg(OPT_show_encoding)) - CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); - - std::unique_ptr<MCAsmBackend> MAB( - TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); - auto FOut = std::make_unique<formatted_raw_ostream>(*OS); + CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + + std::unique_ptr<MCAsmBackend> MAB( + TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); + auto FOut = std::make_unique<formatted_raw_ostream>(*OS); Str.reset(TheTarget->createAsmStreamer( Ctx, std::move(FOut), /*asmverbose*/ true, /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB), InputArgs.hasArg(OPT_show_inst))); - + } else if (FileType == "null") { - Str.reset(TheTarget->createNullStreamer(Ctx)); + Str.reset(TheTarget->createNullStreamer(Ctx)); } else if (FileType == "obj") { - if (!Out->os().supportsSeeking()) { - BOS = std::make_unique<buffer_ostream>(Out->os()); - OS = BOS.get(); - } - - MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); - MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); - Str.reset(TheTarget->createMCObjectStreamer( - TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), - MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI, - MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, - /*DWARFMustBeAtTheEnd*/ false)); + if (!Out->os().supportsSeeking()) { + BOS = std::make_unique<buffer_ostream>(Out->os()); + OS = BOS.get(); + } + + MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); + MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); + Str.reset(TheTarget->createMCObjectStreamer( + TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), + MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI, + MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false)); } else { llvm_unreachable("Invalid file type!"); - } - + } + if (TheTriple.isOSBinFormatCOFF()) { // Emit an absolute @feat.00 symbol. This is a features bitfield read by // link.exe. @@ -383,20 +383,20 @@ int main(int Argc, char **Argv) { Str->emitAssignment(Feat00Sym, MCConstantExpr::create(Feat00Flags, Ctx)); } - // Use Assembler information for parsing. - Str->setUseAssemblerInfoForParsing(true); - - int Res = 1; + // Use Assembler information for parsing. + Str->setUseAssemblerInfoForParsing(true); + + int Res = 1; if (InputArgs.hasArg(OPT_as_lex)) { // -as-lex; Lex only, and output a stream of tokens - Res = AsLexInput(SrcMgr, *MAI, Out->os()); + Res = AsLexInput(SrcMgr, *MAI, Out->os()); } else { - Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, + Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, *MCII, MCOptions, InputArgs); - } - - // Keep output if no errors. - if (Res == 0) - Out->keep(); - return Res; -} + } + + // Keep output if no errors. + if (Res == 0) + Out->keep(); + return Res; +} diff --git a/contrib/libs/llvm12/tools/llvm-ml/ya.make b/contrib/libs/llvm12/tools/llvm-ml/ya.make index 5371cfd851..8566abf283 100644 --- a/contrib/libs/llvm12/tools/llvm-ml/ya.make +++ b/contrib/libs/llvm12/tools/llvm-ml/ya.make @@ -1,17 +1,17 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/BinaryFormat @@ -45,20 +45,20 @@ PEERDIR( contrib/libs/llvm12/lib/Target/X86/Disassembler contrib/libs/llvm12/lib/Target/X86/MCTargetDesc contrib/libs/llvm12/lib/Target/X86/TargetInfo -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-ml contrib/libs/llvm12/tools/llvm-ml -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - Disassembler.cpp - llvm-ml.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + Disassembler.cpp + llvm-ml.cpp +) + +END() diff --git a/contrib/libs/llvm12/tools/llvm-mt/Opts.td b/contrib/libs/llvm12/tools/llvm-mt/Opts.td index da5b2c992e..4c9b5def88 100644 --- a/contrib/libs/llvm12/tools/llvm-mt/Opts.td +++ b/contrib/libs/llvm12/tools/llvm-mt/Opts.td @@ -1,30 +1,30 @@ -include "llvm/Option/OptParser.td" - -def unsupported : OptionGroup<"unsupported">; -def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">; -def identity : Joined<["/", "-"], "identity:">, HelpText<"Not supported">, MetaVarName<"identity">, Group<unsupported>; -def rgs : Joined<["/", "-"], "rgs:">, HelpText<"Not supported">, MetaVarName<"script">, Group<unsupported>; -def tlb : Joined<["/", "-"], "tlb:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; -def dll : Joined<["/", "-"], "dll:">, HelpText<"Not supported">, MetaVarName<"dll">, Group<unsupported>; -def replacements : Joined<["/", "-"], "replacements:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; -def managed_assembly_name : Joined<["/", "-"], "managedassemblyname:">, HelpText<"Not supported">, MetaVarName<"assembly">, Group<unsupported>; -def no_dependency : Flag<["/", "-"], "nodependency">, HelpText<"Not supported">, Group<unsupported>; -def category : Flag<["/", "-"], "category">, HelpText<"Not supported">, Group<unsupported>; -def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">; -def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">; -def input_resource : Joined<["/", "-"], "inputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; -def output_resource : Joined<["/", "-"], "outputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; -def output_resource_flag : Flag<["/", "-"], "outputresource">, Alias<output_resource>, HelpText<"Not supported">, Group<unsupported>; -def update_resource : Joined<["/", "-"], "updateresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; -def hash_update : Joined<["/", "-"], "hashupdate:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; -def hash_update_flag : Flag<["/", "-"], "hashupdate">, Alias<hash_update>, HelpText<"Not supported">, Group<unsupported>; -def validate_manifest : Flag<["/", "-"], "validate_manifest">, HelpText<"Not supported">, Group<unsupported>; -def validate_file_hashes : Joined<["/", "-"], "validate_file_hashes:">, HelpText<"Not supported">, MetaVarName<"">, Group<unsupported>; -def canonicalize : Flag<["/", "-"], "canonicalize:">, HelpText<"Not supported">, Group<unsupported>; -def check_for_duplicates : Flag<["/", "-"], "check_for_duplicates:">, HelpText<"Not supported">, Group<unsupported>; -def make_cdfs : Flag<["/", "-"], "makecdfs:">, HelpText<"Not supported">, Group<unsupported>; -def notify_update : Flag<["/", "-"], "notify_update">, HelpText<"Not supported">, Group<unsupported>; -def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group<unsupported>; -def help : Flag<["/", "-"], "?">; -def help_long : Flag<["/", "-"], "help">, Alias<help>; -def h : Flag<["/", "-"], "h">, Alias<help>; +include "llvm/Option/OptParser.td" + +def unsupported : OptionGroup<"unsupported">; +def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">; +def identity : Joined<["/", "-"], "identity:">, HelpText<"Not supported">, MetaVarName<"identity">, Group<unsupported>; +def rgs : Joined<["/", "-"], "rgs:">, HelpText<"Not supported">, MetaVarName<"script">, Group<unsupported>; +def tlb : Joined<["/", "-"], "tlb:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; +def dll : Joined<["/", "-"], "dll:">, HelpText<"Not supported">, MetaVarName<"dll">, Group<unsupported>; +def replacements : Joined<["/", "-"], "replacements:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; +def managed_assembly_name : Joined<["/", "-"], "managedassemblyname:">, HelpText<"Not supported">, MetaVarName<"assembly">, Group<unsupported>; +def no_dependency : Flag<["/", "-"], "nodependency">, HelpText<"Not supported">, Group<unsupported>; +def category : Flag<["/", "-"], "category">, HelpText<"Not supported">, Group<unsupported>; +def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">; +def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">; +def input_resource : Joined<["/", "-"], "inputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; +def output_resource : Joined<["/", "-"], "outputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; +def output_resource_flag : Flag<["/", "-"], "outputresource">, Alias<output_resource>, HelpText<"Not supported">, Group<unsupported>; +def update_resource : Joined<["/", "-"], "updateresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; +def hash_update : Joined<["/", "-"], "hashupdate:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>; +def hash_update_flag : Flag<["/", "-"], "hashupdate">, Alias<hash_update>, HelpText<"Not supported">, Group<unsupported>; +def validate_manifest : Flag<["/", "-"], "validate_manifest">, HelpText<"Not supported">, Group<unsupported>; +def validate_file_hashes : Joined<["/", "-"], "validate_file_hashes:">, HelpText<"Not supported">, MetaVarName<"">, Group<unsupported>; +def canonicalize : Flag<["/", "-"], "canonicalize:">, HelpText<"Not supported">, Group<unsupported>; +def check_for_duplicates : Flag<["/", "-"], "check_for_duplicates:">, HelpText<"Not supported">, Group<unsupported>; +def make_cdfs : Flag<["/", "-"], "makecdfs:">, HelpText<"Not supported">, Group<unsupported>; +def notify_update : Flag<["/", "-"], "notify_update">, HelpText<"Not supported">, Group<unsupported>; +def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group<unsupported>; +def help : Flag<["/", "-"], "?">; +def help_long : Flag<["/", "-"], "help">, Alias<help>; +def h : Flag<["/", "-"], "h">, Alias<help>; diff --git a/contrib/libs/llvm12/tools/llvm-mt/llvm-mt.cpp b/contrib/libs/llvm12/tools/llvm-mt/llvm-mt.cpp index 997e5acbe2..59458ae8c3 100644 --- a/contrib/libs/llvm12/tools/llvm-mt/llvm-mt.cpp +++ b/contrib/libs/llvm12/tools/llvm-mt/llvm-mt.cpp @@ -1,154 +1,154 @@ -//===- llvm-mt.cpp - Merge .manifest files ---------------------*- 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 -// -//===---------------------------------------------------------------------===// -// -// Merge .manifest files. This is intended to be a platform-independent port -// of Microsoft's mt.exe. -// -//===---------------------------------------------------------------------===// - -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/WindowsManifest/WindowsManifestMerger.h" - -#include <system_error> - -using namespace llvm; - -namespace { - -enum ID { - OPT_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - OPT_##ID, -#include "Opts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "Opts.inc" -#undef PREFIX - -static const opt::OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ -{ \ - PREFIX, NAME, HELPTEXT, \ - METAVAR, OPT_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, \ - OPT_##ALIAS, ALIASARGS, VALUES}, -#include "Opts.inc" -#undef OPTION -}; - -class CvtResOptTable : public opt::OptTable { -public: - CvtResOptTable() : OptTable(InfoTable, true) {} -}; -} // namespace - +//===- llvm-mt.cpp - Merge .manifest files ---------------------*- 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 +// +//===---------------------------------------------------------------------===// +// +// Merge .manifest files. This is intended to be a platform-independent port +// of Microsoft's mt.exe. +// +//===---------------------------------------------------------------------===// + +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/WindowsManifest/WindowsManifestMerger.h" + +#include <system_error> + +using namespace llvm; + +namespace { + +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ +{ \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class CvtResOptTable : public opt::OptTable { +public: + CvtResOptTable() : OptTable(InfoTable, true) {} +}; +} // namespace + LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Msg) { - WithColor::error(errs(), "llvm-mt") << Msg << '\n'; - exit(1); -} - -static void reportError(StringRef Input, std::error_code EC) { - reportError(Twine(Input) + ": " + EC.message()); -} - + WithColor::error(errs(), "llvm-mt") << Msg << '\n'; + exit(1); +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Twine(Input) + ": " + EC.message()); +} + static void error(Error EC) { - if (EC) - handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) { - reportError(EI.message()); - }); -} - -int main(int Argc, const char **Argv) { - InitLLVM X(Argc, Argv); - - CvtResOptTable T; - unsigned MAI, MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); - opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); - - for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { - auto ArgString = Arg->getAsString(InputArgs); - std::string Diag; - raw_string_ostream OS(Diag); - OS << "invalid option '" << ArgString << "'"; - - std::string Nearest; - if (T.findNearest(ArgString, Nearest) < 2) - OS << ", did you mean '" << Nearest << "'?"; - - reportError(OS.str()); - } - - for (auto &Arg : InputArgs) { - if (Arg->getOption().matches(OPT_unsupported)) { - outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName() - << "' option\n"; - } - } - - if (InputArgs.hasArg(OPT_help)) { - T.PrintHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false); - return 0; - } - - std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_manifest); - - if (InputFiles.size() == 0) { - reportError("no input file specified"); - } - - StringRef OutputFile; - if (InputArgs.hasArg(OPT_out)) { - OutputFile = InputArgs.getLastArgValue(OPT_out); - } else if (InputFiles.size() == 1) { - OutputFile = InputFiles[0]; - } else { - reportError("no output file specified"); - } - - windows_manifest::WindowsManifestMerger Merger; - - for (const auto &File : InputFiles) { - ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr = - MemoryBuffer::getFile(File); - if (!ManifestOrErr) - reportError(File, ManifestOrErr.getError()); - MemoryBuffer &Manifest = *ManifestOrErr.get(); - error(Merger.merge(Manifest)); - } - - std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest(); - if (!OutputBuffer) - reportError("empty manifest not written"); - Expected<std::unique_ptr<FileOutputBuffer>> FileOrErr = - FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize()); - if (!FileOrErr) - reportError(OutputFile, errorToErrorCode(FileOrErr.takeError())); - std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr); - std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), - FileBuffer->getBufferStart()); - error(FileBuffer->commit()); - return 0; -} + if (EC) + handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) { + reportError(EI.message()); + }); +} + +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); + + CvtResOptTable T; + unsigned MAI, MAC; + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + + for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { + auto ArgString = Arg->getAsString(InputArgs); + std::string Diag; + raw_string_ostream OS(Diag); + OS << "invalid option '" << ArgString << "'"; + + std::string Nearest; + if (T.findNearest(ArgString, Nearest) < 2) + OS << ", did you mean '" << Nearest << "'?"; + + reportError(OS.str()); + } + + for (auto &Arg : InputArgs) { + if (Arg->getOption().matches(OPT_unsupported)) { + outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName() + << "' option\n"; + } + } + + if (InputArgs.hasArg(OPT_help)) { + T.PrintHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false); + return 0; + } + + std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_manifest); + + if (InputFiles.size() == 0) { + reportError("no input file specified"); + } + + StringRef OutputFile; + if (InputArgs.hasArg(OPT_out)) { + OutputFile = InputArgs.getLastArgValue(OPT_out); + } else if (InputFiles.size() == 1) { + OutputFile = InputFiles[0]; + } else { + reportError("no output file specified"); + } + + windows_manifest::WindowsManifestMerger Merger; + + for (const auto &File : InputFiles) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr = + MemoryBuffer::getFile(File); + if (!ManifestOrErr) + reportError(File, ManifestOrErr.getError()); + MemoryBuffer &Manifest = *ManifestOrErr.get(); + error(Merger.merge(Manifest)); + } + + std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest(); + if (!OutputBuffer) + reportError("empty manifest not written"); + Expected<std::unique_ptr<FileOutputBuffer>> FileOrErr = + FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize()); + if (!FileOrErr) + reportError(OutputFile, errorToErrorCode(FileOrErr.takeError())); + std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr); + std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), + FileBuffer->getBufferStart()); + error(FileBuffer->commit()); + return 0; +} diff --git a/contrib/libs/llvm12/tools/llvm-mt/ya.make b/contrib/libs/llvm12/tools/llvm-mt/ya.make index 83a363a067..4bea81adf1 100644 --- a/contrib/libs/llvm12/tools/llvm-mt/ya.make +++ b/contrib/libs/llvm12/tools/llvm-mt/ya.make @@ -1,36 +1,36 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/Demangle contrib/libs/llvm12/lib/Option contrib/libs/llvm12/lib/Support contrib/libs/llvm12/lib/WindowsManifest -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-mt contrib/libs/llvm12/tools/llvm-mt -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - llvm-mt.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + llvm-mt.cpp +) + +END() diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.cpp index 06b2a20a76..f71d1bfacb 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.cpp @@ -1,79 +1,79 @@ -//===- Buffer.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Buffer.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Process.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -Buffer::~Buffer() {} - -static Error createEmptyFile(StringRef FileName) { - // Create an empty tempfile and atomically swap it in place with the desired - // output file. - Expected<sys::fs::TempFile> Temp = - sys::fs::TempFile::create(FileName + ".temp-empty-%%%%%%%"); - return Temp ? Temp->keep(FileName) : Temp.takeError(); -} - -Error FileBuffer::allocate(size_t Size) { - // When a 0-sized file is requested, skip allocation but defer file - // creation/truncation until commit() to avoid side effects if something - // happens between allocate() and commit(). - if (Size == 0) { - EmptyFile = true; - return Error::success(); - } - - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); - // FileOutputBuffer::create() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - if (!BufferOrErr) - return createFileError(getName(), BufferOrErr.takeError()); - Buf = std::move(*BufferOrErr); - return Error::success(); -} - -Error FileBuffer::commit() { - if (EmptyFile) - return createEmptyFile(getName()); - - assert(Buf && "allocate() not called before commit()!"); - Error Err = Buf->commit(); - // FileOutputBuffer::commit() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - return Err ? createFileError(getName(), std::move(Err)) : std::move(Err); -} - -uint8_t *FileBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -Error MemBuffer::allocate(size_t Size) { - Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); - return Error::success(); -} - -Error MemBuffer::commit() { return Error::success(); } - -uint8_t *MemBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() { - return std::move(Buf); -} - -} // end namespace objcopy -} // end namespace llvm +//===- Buffer.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Buffer.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +Buffer::~Buffer() {} + +static Error createEmptyFile(StringRef FileName) { + // Create an empty tempfile and atomically swap it in place with the desired + // output file. + Expected<sys::fs::TempFile> Temp = + sys::fs::TempFile::create(FileName + ".temp-empty-%%%%%%%"); + return Temp ? Temp->keep(FileName) : Temp.takeError(); +} + +Error FileBuffer::allocate(size_t Size) { + // When a 0-sized file is requested, skip allocation but defer file + // creation/truncation until commit() to avoid side effects if something + // happens between allocate() and commit(). + if (Size == 0) { + EmptyFile = true; + return Error::success(); + } + + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); + // FileOutputBuffer::create() returns an Error that is just a wrapper around + // std::error_code. Wrap it in FileError to include the actual filename. + if (!BufferOrErr) + return createFileError(getName(), BufferOrErr.takeError()); + Buf = std::move(*BufferOrErr); + return Error::success(); +} + +Error FileBuffer::commit() { + if (EmptyFile) + return createEmptyFile(getName()); + + assert(Buf && "allocate() not called before commit()!"); + Error Err = Buf->commit(); + // FileOutputBuffer::commit() returns an Error that is just a wrapper around + // std::error_code. Wrap it in FileError to include the actual filename. + return Err ? createFileError(getName(), std::move(Err)) : std::move(Err); +} + +uint8_t *FileBuffer::getBufferStart() { + return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); +} + +Error MemBuffer::allocate(size_t Size) { + Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); + return Error::success(); +} + +Error MemBuffer::commit() { return Error::success(); } + +uint8_t *MemBuffer::getBufferStart() { + return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); +} + +std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() { + return std::move(Buf); +} + +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.h b/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.h index 487d5585c3..7782463ac5 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/Buffer.h @@ -1,68 +1,68 @@ -//===- Buffer.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H -#define LLVM_TOOLS_OBJCOPY_BUFFER_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/MemoryBuffer.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -// The class Buffer abstracts out the common interface of FileOutputBuffer and -// WritableMemoryBuffer so that the hierarchy of Writers depends on this -// abstract interface and doesn't depend on a particular implementation. -// TODO: refactor the buffer classes in LLVM to enable us to use them here -// directly. -class Buffer { - StringRef Name; - -public: - virtual ~Buffer(); - virtual Error allocate(size_t Size) = 0; - virtual uint8_t *getBufferStart() = 0; - virtual Error commit() = 0; - - explicit Buffer(StringRef Name) : Name(Name) {} - StringRef getName() const { return Name; } -}; - -class FileBuffer : public Buffer { - std::unique_ptr<FileOutputBuffer> Buf; - // Indicates that allocate(0) was called, and commit() should create or - // truncate a file instead of using a FileOutputBuffer. - bool EmptyFile = false; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} -}; - -class MemBuffer : public Buffer { - std::unique_ptr<WritableMemoryBuffer> Buf; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit MemBuffer(StringRef Name) : Buffer(Name) {} - - std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer(); -}; - -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H +//===- Buffer.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H +#define LLVM_TOOLS_OBJCOPY_BUFFER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +// The class Buffer abstracts out the common interface of FileOutputBuffer and +// WritableMemoryBuffer so that the hierarchy of Writers depends on this +// abstract interface and doesn't depend on a particular implementation. +// TODO: refactor the buffer classes in LLVM to enable us to use them here +// directly. +class Buffer { + StringRef Name; + +public: + virtual ~Buffer(); + virtual Error allocate(size_t Size) = 0; + virtual uint8_t *getBufferStart() = 0; + virtual Error commit() = 0; + + explicit Buffer(StringRef Name) : Name(Name) {} + StringRef getName() const { return Name; } +}; + +class FileBuffer : public Buffer { + std::unique_ptr<FileOutputBuffer> Buf; + // Indicates that allocate(0) was called, and commit() should create or + // truncate a file instead of using a FileOutputBuffer. + bool EmptyFile = false; + +public: + Error allocate(size_t Size) override; + uint8_t *getBufferStart() override; + Error commit() override; + + explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} +}; + +class MemBuffer : public Buffer { + std::unique_ptr<WritableMemoryBuffer> Buf; + +public: + Error allocate(size_t Size) override; + uint8_t *getBufferStart() override; + Error commit() override; + + explicit MemBuffer(StringRef Name) : Buffer(Name) {} + + std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer(); +}; + +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index b5de8a45a8..3c058e4785 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -1,292 +1,292 @@ -//===- COFFObjcopy.cpp ----------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "COFFObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" -#include "Object.h" -#include "Reader.h" -#include "Writer.h" - -#include "llvm/Object/Binary.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/CRC.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Path.h" -#include <cassert> - -namespace llvm { -namespace objcopy { -namespace coff { - -using namespace object; -using namespace COFF; - -static bool isDebugSection(const Section &Sec) { - return Sec.Name.startswith(".debug"); -} - -static uint64_t getNextRVA(const Object &Obj) { - if (Obj.getSections().empty()) - return 0; - const Section &Last = Obj.getSections().back(); - return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize, - Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1); -} - +//===- COFFObjcopy.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "COFFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "Reader.h" +#include "Writer.h" + +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Path.h" +#include <cassert> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +static bool isDebugSection(const Section &Sec) { + return Sec.Name.startswith(".debug"); +} + +static uint64_t getNextRVA(const Object &Obj) { + if (Obj.getSections().empty()) + return 0; + const Section &Last = Obj.getSections().back(); + return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize, + Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1); +} + static Expected<std::vector<uint8_t>> createGnuDebugLinkSectionContents(StringRef File) { - ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr = - MemoryBuffer::getFile(File); - if (!LinkTargetOrErr) + ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr = + MemoryBuffer::getFile(File); + if (!LinkTargetOrErr) return createFileError(File, LinkTargetOrErr.getError()); - auto LinkTarget = std::move(*LinkTargetOrErr); - uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer())); - - StringRef FileName = sys::path::filename(File); - size_t CRCPos = alignTo(FileName.size() + 1, 4); - std::vector<uint8_t> Data(CRCPos + 4); - memcpy(Data.data(), FileName.data(), FileName.size()); - support::endian::write32le(Data.data() + CRCPos, CRC32); - return Data; -} - -// Adds named section with given contents to the object. -static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents, - uint32_t Characteristics) { - bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | - IMAGE_SCN_MEM_WRITE); - - Section Sec; - Sec.setOwnedContents(Contents); - Sec.Name = Name; - Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u; - Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u; - Sec.Header.SizeOfRawData = - NeedVA ? alignTo(Sec.Header.VirtualSize, - Obj.IsPE ? Obj.PeHeader.FileAlignment : 1) - : Sec.getContents().size(); - // Sec.Header.PointerToRawData is filled in by the writer. - Sec.Header.PointerToRelocations = 0; - Sec.Header.PointerToLinenumbers = 0; - // Sec.Header.NumberOfRelocations is filled in by the writer. - Sec.Header.NumberOfLinenumbers = 0; - Sec.Header.Characteristics = Characteristics; - - Obj.addSections(Sec); -} - + auto LinkTarget = std::move(*LinkTargetOrErr); + uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer())); + + StringRef FileName = sys::path::filename(File); + size_t CRCPos = alignTo(FileName.size() + 1, 4); + std::vector<uint8_t> Data(CRCPos + 4); + memcpy(Data.data(), FileName.data(), FileName.size()); + support::endian::write32le(Data.data() + CRCPos, CRC32); + return Data; +} + +// Adds named section with given contents to the object. +static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents, + uint32_t Characteristics) { + bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE); + + Section Sec; + Sec.setOwnedContents(Contents); + Sec.Name = Name; + Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u; + Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u; + Sec.Header.SizeOfRawData = + NeedVA ? alignTo(Sec.Header.VirtualSize, + Obj.IsPE ? Obj.PeHeader.FileAlignment : 1) + : Sec.getContents().size(); + // Sec.Header.PointerToRawData is filled in by the writer. + Sec.Header.PointerToRelocations = 0; + Sec.Header.PointerToLinenumbers = 0; + // Sec.Header.NumberOfRelocations is filled in by the writer. + Sec.Header.NumberOfLinenumbers = 0; + Sec.Header.Characteristics = Characteristics; + + Obj.addSections(Sec); +} + static Error addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { Expected<std::vector<uint8_t>> Contents = - createGnuDebugLinkSectionContents(DebugLinkFile); + createGnuDebugLinkSectionContents(DebugLinkFile); if (!Contents) return Contents.takeError(); addSection(Obj, ".gnu_debuglink", *Contents, - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | - IMAGE_SCN_MEM_DISCARDABLE); + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_DISCARDABLE); return Error::success(); -} - -static void setSectionFlags(Section &Sec, SectionFlag AllFlags) { - // Need to preserve alignment flags. - const uint32_t PreserveMask = - IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES | - IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES | - IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES | - IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES | - IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES | - IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES | - IMAGE_SCN_ALIGN_8192BYTES; - - // Setup new section characteristics based on the flags provided in command - // line. - uint32_t NewCharacteristics = - (Sec.Header.Characteristics & PreserveMask) | IMAGE_SCN_MEM_READ; - - if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad)) - NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; - if (AllFlags & SectionFlag::SecNoload) - NewCharacteristics |= IMAGE_SCN_LNK_REMOVE; - if (!(AllFlags & SectionFlag::SecReadonly)) - NewCharacteristics |= IMAGE_SCN_MEM_WRITE; - if (AllFlags & SectionFlag::SecDebug) - NewCharacteristics |= - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE; - if (AllFlags & SectionFlag::SecCode) - NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE; - if (AllFlags & SectionFlag::SecData) - NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; - if (AllFlags & SectionFlag::SecShare) - NewCharacteristics |= IMAGE_SCN_MEM_SHARED; - if (AllFlags & SectionFlag::SecExclude) - NewCharacteristics |= IMAGE_SCN_LNK_REMOVE; - - Sec.Header.Characteristics = NewCharacteristics; -} - -static Error handleArgs(const CopyConfig &Config, Object &Obj) { - // Perform the actual section removals. - Obj.removeSections([&Config](const Section &Sec) { - // Contrary to --only-keep-debug, --only-section fully removes sections that - // aren't mentioned. - if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name)) - return true; - - if (Config.StripDebug || Config.StripAll || Config.StripAllGNU || - Config.DiscardMode == DiscardType::All || Config.StripUnneeded) { - if (isDebugSection(Sec) && - (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) - return true; - } - - if (Config.ToRemove.matches(Sec.Name)) - return true; - - return false; - }); - - if (Config.OnlyKeepDebug) { - // For --only-keep-debug, we keep all other sections, but remove their - // content. The VirtualSize field in the section header is kept intact. - Obj.truncateSections([](const Section &Sec) { - return !isDebugSection(Sec) && Sec.Name != ".buildid" && - ((Sec.Header.Characteristics & - (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0); - }); - } - - // StripAll removes all symbols and thus also removes all relocations. - if (Config.StripAll || Config.StripAllGNU) - for (Section &Sec : Obj.getMutableSections()) - Sec.Relocs.clear(); - - // If we need to do per-symbol removals, initialize the Referenced field. - if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All || - !Config.SymbolsToRemove.empty()) - if (Error E = Obj.markSymbols()) - return E; - - for (Symbol &Sym : Obj.getMutableSymbols()) { - auto I = Config.SymbolsToRename.find(Sym.Name); - if (I != Config.SymbolsToRename.end()) - Sym.Name = I->getValue(); - } - +} + +static void setSectionFlags(Section &Sec, SectionFlag AllFlags) { + // Need to preserve alignment flags. + const uint32_t PreserveMask = + IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES | + IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES | + IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES | + IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES | + IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES | + IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES | + IMAGE_SCN_ALIGN_8192BYTES; + + // Setup new section characteristics based on the flags provided in command + // line. + uint32_t NewCharacteristics = + (Sec.Header.Characteristics & PreserveMask) | IMAGE_SCN_MEM_READ; + + if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad)) + NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; + if (AllFlags & SectionFlag::SecNoload) + NewCharacteristics |= IMAGE_SCN_LNK_REMOVE; + if (!(AllFlags & SectionFlag::SecReadonly)) + NewCharacteristics |= IMAGE_SCN_MEM_WRITE; + if (AllFlags & SectionFlag::SecDebug) + NewCharacteristics |= + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE; + if (AllFlags & SectionFlag::SecCode) + NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE; + if (AllFlags & SectionFlag::SecData) + NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; + if (AllFlags & SectionFlag::SecShare) + NewCharacteristics |= IMAGE_SCN_MEM_SHARED; + if (AllFlags & SectionFlag::SecExclude) + NewCharacteristics |= IMAGE_SCN_LNK_REMOVE; + + Sec.Header.Characteristics = NewCharacteristics; +} + +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // Perform the actual section removals. + Obj.removeSections([&Config](const Section &Sec) { + // Contrary to --only-keep-debug, --only-section fully removes sections that + // aren't mentioned. + if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name)) + return true; + + if (Config.StripDebug || Config.StripAll || Config.StripAllGNU || + Config.DiscardMode == DiscardType::All || Config.StripUnneeded) { + if (isDebugSection(Sec) && + (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) + return true; + } + + if (Config.ToRemove.matches(Sec.Name)) + return true; + + return false; + }); + + if (Config.OnlyKeepDebug) { + // For --only-keep-debug, we keep all other sections, but remove their + // content. The VirtualSize field in the section header is kept intact. + Obj.truncateSections([](const Section &Sec) { + return !isDebugSection(Sec) && Sec.Name != ".buildid" && + ((Sec.Header.Characteristics & + (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0); + }); + } + + // StripAll removes all symbols and thus also removes all relocations. + if (Config.StripAll || Config.StripAllGNU) + for (Section &Sec : Obj.getMutableSections()) + Sec.Relocs.clear(); + + // If we need to do per-symbol removals, initialize the Referenced field. + if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All || + !Config.SymbolsToRemove.empty()) + if (Error E = Obj.markSymbols()) + return E; + + for (Symbol &Sym : Obj.getMutableSymbols()) { + auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = I->getValue(); + } + auto ToRemove = [&](const Symbol &Sym) -> Expected<bool> { - // For StripAll, all relocations have been stripped and we remove all - // symbols. - if (Config.StripAll || Config.StripAllGNU) - return true; - - if (Config.SymbolsToRemove.matches(Sym.Name)) { - // Explicitly removing a referenced symbol is an error. - if (Sym.Referenced) + // For StripAll, all relocations have been stripped and we remove all + // symbols. + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (Config.SymbolsToRemove.matches(Sym.Name)) { + // Explicitly removing a referenced symbol is an error. + if (Sym.Referenced) return createStringError( llvm::errc::invalid_argument, "'" + Config.OutputFilename + "': not stripping symbol '" + Sym.Name.str() + "' because it is named in a relocation"); - return true; - } - - if (!Sym.Referenced) { - // With --strip-unneeded, GNU objcopy removes all unreferenced local - // symbols, and any unreferenced undefined external. - // With --strip-unneeded-symbol we strip only specific unreferenced - // local symbol instead of removing all of such. - if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || - Sym.Sym.SectionNumber == 0) - if (Config.StripUnneeded || - Config.UnneededSymbolsToRemove.matches(Sym.Name)) - return true; - - // GNU objcopy keeps referenced local symbols and external symbols - // if --discard-all is set, similar to what --strip-unneeded does, - // but undefined local symbols are kept when --discard-all is set. - if (Config.DiscardMode == DiscardType::All && - Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && - Sym.Sym.SectionNumber != 0) - return true; - } - - return false; + return true; + } + + if (!Sym.Referenced) { + // With --strip-unneeded, GNU objcopy removes all unreferenced local + // symbols, and any unreferenced undefined external. + // With --strip-unneeded-symbol we strip only specific unreferenced + // local symbol instead of removing all of such. + if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || + Sym.Sym.SectionNumber == 0) + if (Config.StripUnneeded || + Config.UnneededSymbolsToRemove.matches(Sym.Name)) + return true; + + // GNU objcopy keeps referenced local symbols and external symbols + // if --discard-all is set, similar to what --strip-unneeded does, + // but undefined local symbols are kept when --discard-all is set. + if (Config.DiscardMode == DiscardType::All && + Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && + Sym.Sym.SectionNumber != 0) + return true; + } + + return false; }; - + // Actually do removals of symbols. if (Error Err = Obj.removeSymbols(ToRemove)) return Err; - if (!Config.SetSectionFlags.empty()) - for (Section &Sec : Obj.getMutableSections()) { - const auto It = Config.SetSectionFlags.find(Sec.Name); - if (It != Config.SetSectionFlags.end()) - setSectionFlags(Sec, It->second.NewFlags); - } - - for (const auto &Flag : Config.AddSection) { - StringRef SecName, FileName; - std::tie(SecName, FileName) = Flag.split("="); - - auto BufOrErr = MemoryBuffer::getFile(FileName); - if (!BufOrErr) - return createFileError(FileName, errorCodeToError(BufOrErr.getError())); - auto Buf = std::move(*BufOrErr); - - addSection( - Obj, SecName, - makeArrayRef(reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), - Buf->getBufferSize()), - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES); - } - - if (!Config.AddGnuDebugLink.empty()) + if (!Config.SetSectionFlags.empty()) + for (Section &Sec : Obj.getMutableSections()) { + const auto It = Config.SetSectionFlags.find(Sec.Name); + if (It != Config.SetSectionFlags.end()) + setSectionFlags(Sec, It->second.NewFlags); + } + + for (const auto &Flag : Config.AddSection) { + StringRef SecName, FileName; + std::tie(SecName, FileName) = Flag.split("="); + + auto BufOrErr = MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return createFileError(FileName, errorCodeToError(BufOrErr.getError())); + auto Buf = std::move(*BufOrErr); + + addSection( + Obj, SecName, + makeArrayRef(reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()), + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES); + } + + if (!Config.AddGnuDebugLink.empty()) if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink)) return E; - - if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || - !Config.KeepSection.empty() || Config.NewSymbolVisibility || - !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || - !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || - !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SetSectionAlignment.empty() || Config.ExtractDWO || - Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || - Config.StripNonAlloc || Config.StripSections || - Config.StripSwiftSymbols || Config.Weaken || - Config.DecompressDebugSections || - Config.DiscardMode == DiscardType::Locals || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for COFF"); - } - - return Error::success(); -} - -Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In, - Buffer &Out) { - COFFReader Reader(In); - Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); - if (!ObjOrErr) - return createFileError(Config.InputFilename, ObjOrErr.takeError()); - Object *Obj = ObjOrErr->get(); - assert(Obj && "Unable to deserialize COFF object"); - if (Error E = handleArgs(Config, *Obj)) - return createFileError(Config.InputFilename, std::move(E)); - COFFWriter Writer(*Obj, Out); - if (Error E = Writer.write()) - return createFileError(Config.OutputFilename, std::move(E)); - return Error::success(); -} - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm + + if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || + Config.BuildIdLinkInput || Config.BuildIdLinkOutput || + !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || + !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || + !Config.KeepSection.empty() || Config.NewSymbolVisibility || + !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || + !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || + !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || + !Config.SetSectionAlignment.empty() || Config.ExtractDWO || + Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || + Config.StripNonAlloc || Config.StripSections || + Config.StripSwiftSymbols || Config.Weaken || + Config.DecompressDebugSections || + Config.DiscardMode == DiscardType::Locals || + !Config.SymbolsToAdd.empty() || Config.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for COFF"); + } + + return Error::success(); +} + +Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In, + Buffer &Out) { + COFFReader Reader(In); + Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); + if (!ObjOrErr) + return createFileError(Config.InputFilename, ObjOrErr.takeError()); + Object *Obj = ObjOrErr->get(); + assert(Obj && "Unable to deserialize COFF object"); + if (Error E = handleArgs(Config, *Obj)) + return createFileError(Config.InputFilename, std::move(E)); + COFFWriter Writer(*Obj, Out); + if (Error E = Writer.write()) + return createFileError(Config.OutputFilename, std::move(E)); + return Error::success(); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.h b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.h index 858759e52c..6b59f44946 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -1,31 +1,31 @@ -//===- COFFObjcopy.h --------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H -#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H - -namespace llvm { -class Error; - -namespace object { -class COFFObjectFile; -} // end namespace object - -namespace objcopy { -struct CopyConfig; -class Buffer; - -namespace coff { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::COFFObjectFile &In, Buffer &Out); - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H +//===- COFFObjcopy.h --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H + +namespace llvm { +class Error; + +namespace object { +class COFFObjectFile; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace coff { +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out); + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.cpp index 1c17b8408e..fc67f1cb4d 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.cpp @@ -1,39 +1,39 @@ -//===- Object.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Object.h" -#include "llvm/ADT/DenseSet.h" -#include <algorithm> - -namespace llvm { -namespace objcopy { -namespace coff { - -using namespace object; - -void Object::addSymbols(ArrayRef<Symbol> NewSymbols) { - for (Symbol S : NewSymbols) { - S.UniqueId = NextSymbolUniqueId++; - Symbols.emplace_back(S); - } - updateSymbols(); -} - -void Object::updateSymbols() { - SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size()); - for (Symbol &Sym : Symbols) - SymbolMap[Sym.UniqueId] = &Sym; -} - -const Symbol *Object::findSymbol(size_t UniqueId) const { +//===- Object.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Object.h" +#include "llvm/ADT/DenseSet.h" +#include <algorithm> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +void Object::addSymbols(ArrayRef<Symbol> NewSymbols) { + for (Symbol S : NewSymbols) { + S.UniqueId = NextSymbolUniqueId++; + Symbols.emplace_back(S); + } + updateSymbols(); +} + +void Object::updateSymbols() { + SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size()); + for (Symbol &Sym : Symbols) + SymbolMap[Sym.UniqueId] = &Sym; +} + +const Symbol *Object::findSymbol(size_t UniqueId) const { return SymbolMap.lookup(UniqueId); -} - +} + Error Object::removeSymbols( function_ref<Expected<bool>(const Symbol &)> ToRemove) { Error Errs = Error::success(); @@ -46,61 +46,61 @@ Error Object::removeSymbols( return *ShouldRemove; }); - updateSymbols(); + updateSymbols(); return Errs; -} - -Error Object::markSymbols() { - for (Symbol &Sym : Symbols) - Sym.Referenced = false; - for (const Section &Sec : Sections) { - for (const Relocation &R : Sec.Relocs) { - auto It = SymbolMap.find(R.Target); - if (It == SymbolMap.end()) - return createStringError(object_error::invalid_symbol_index, - "relocation target %zu not found", R.Target); - It->second->Referenced = true; - } - } - return Error::success(); -} - -void Object::addSections(ArrayRef<Section> NewSections) { - for (Section S : NewSections) { - S.UniqueId = NextSectionUniqueId++; - Sections.emplace_back(S); - } - updateSections(); -} - -void Object::updateSections() { - SectionMap = DenseMap<ssize_t, Section *>(Sections.size()); - size_t Index = 1; - for (Section &S : Sections) { - SectionMap[S.UniqueId] = &S; - S.Index = Index++; - } -} - -const Section *Object::findSection(ssize_t UniqueId) const { +} + +Error Object::markSymbols() { + for (Symbol &Sym : Symbols) + Sym.Referenced = false; + for (const Section &Sec : Sections) { + for (const Relocation &R : Sec.Relocs) { + auto It = SymbolMap.find(R.Target); + if (It == SymbolMap.end()) + return createStringError(object_error::invalid_symbol_index, + "relocation target %zu not found", R.Target); + It->second->Referenced = true; + } + } + return Error::success(); +} + +void Object::addSections(ArrayRef<Section> NewSections) { + for (Section S : NewSections) { + S.UniqueId = NextSectionUniqueId++; + Sections.emplace_back(S); + } + updateSections(); +} + +void Object::updateSections() { + SectionMap = DenseMap<ssize_t, Section *>(Sections.size()); + size_t Index = 1; + for (Section &S : Sections) { + SectionMap[S.UniqueId] = &S; + S.Index = Index++; + } +} + +const Section *Object::findSection(ssize_t UniqueId) const { return SectionMap.lookup(UniqueId); -} - -void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { - DenseSet<ssize_t> AssociatedSections; - auto RemoveAssociated = [&AssociatedSections](const Section &Sec) { +} + +void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { + DenseSet<ssize_t> AssociatedSections; + auto RemoveAssociated = [&AssociatedSections](const Section &Sec) { return AssociatedSections.contains(Sec.UniqueId); - }; - do { - DenseSet<ssize_t> RemovedSections; + }; + do { + DenseSet<ssize_t> RemovedSections; llvm::erase_if(Sections, [ToRemove, &RemovedSections](const Section &Sec) { bool Remove = ToRemove(Sec); if (Remove) RemovedSections.insert(Sec.UniqueId); return Remove; }); - // Remove all symbols referring to the removed sections. - AssociatedSections.clear(); + // Remove all symbols referring to the removed sections. + AssociatedSections.clear(); llvm::erase_if( Symbols, [&RemovedSections, &AssociatedSections](const Symbol &Sym) { // If there are sections that are associative to a removed @@ -111,22 +111,22 @@ void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { AssociatedSections.insert(Sym.TargetSectionId); return RemovedSections.contains(Sym.TargetSectionId); }); - ToRemove = RemoveAssociated; - } while (!AssociatedSections.empty()); - updateSections(); - updateSymbols(); -} - -void Object::truncateSections(function_ref<bool(const Section &)> ToTruncate) { - for (Section &Sec : Sections) { - if (ToTruncate(Sec)) { - Sec.clearContents(); - Sec.Relocs.clear(); - Sec.Header.SizeOfRawData = 0; - } - } -} - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm + ToRemove = RemoveAssociated; + } while (!AssociatedSections.empty()); + updateSections(); + updateSymbols(); +} + +void Object::truncateSections(function_ref<bool(const Section &)> ToTruncate) { + for (Section &Sec : Sections) { + if (ToTruncate(Sec)) { + Sec.clearContents(); + Sec.Relocs.clear(); + Sec.Header.SizeOfRawData = 0; + } + } +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.h b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.h index 0e854b58cb..9602f56e63 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Object.h @@ -1,211 +1,211 @@ -//===- Object.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H -#define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/BinaryFormat/COFF.h" -#include "llvm/Object/COFF.h" -#include <cstddef> -#include <cstdint> -#include <vector> - -namespace llvm { -namespace objcopy { -namespace coff { - -struct Relocation { - Relocation() = default; +//===- Object.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H +#define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include <cstddef> +#include <cstdint> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Relocation { + Relocation() = default; Relocation(const object::coff_relocation &R) : Reloc(R) {} - - object::coff_relocation Reloc; - size_t Target = 0; - StringRef TargetName; // Used for diagnostics only -}; - -struct Section { - object::coff_section Header; - std::vector<Relocation> Relocs; - StringRef Name; - ssize_t UniqueId; - size_t Index; - - ArrayRef<uint8_t> getContents() const { - if (!OwnedContents.empty()) - return OwnedContents; - return ContentsRef; - } - - void setContentsRef(ArrayRef<uint8_t> Data) { - OwnedContents.clear(); - ContentsRef = Data; - } - - void setOwnedContents(std::vector<uint8_t> &&Data) { - ContentsRef = ArrayRef<uint8_t>(); - OwnedContents = std::move(Data); - } - - void clearContents() { - ContentsRef = ArrayRef<uint8_t>(); - OwnedContents.clear(); - } - -private: - ArrayRef<uint8_t> ContentsRef; - std::vector<uint8_t> OwnedContents; -}; - -struct AuxSymbol { - AuxSymbol(ArrayRef<uint8_t> In) { - assert(In.size() == sizeof(Opaque)); - std::copy(In.begin(), In.end(), Opaque); - } - - ArrayRef<uint8_t> getRef() const { - return ArrayRef<uint8_t>(Opaque, sizeof(Opaque)); - } - - uint8_t Opaque[sizeof(object::coff_symbol16)]; -}; - -struct Symbol { - object::coff_symbol32 Sym; - StringRef Name; - std::vector<AuxSymbol> AuxData; - StringRef AuxFile; - ssize_t TargetSectionId; - ssize_t AssociativeComdatTargetSectionId = 0; - Optional<size_t> WeakTargetSymbolId; - size_t UniqueId; - size_t RawIndex; - bool Referenced; -}; - -struct Object { - bool IsPE = false; - - object::dos_header DosHeader; - ArrayRef<uint8_t> DosStub; - - object::coff_file_header CoffFileHeader; - - bool Is64 = false; - object::pe32plus_header PeHeader; - uint32_t BaseOfData = 0; // pe32plus_header lacks this field. - - std::vector<object::data_directory> DataDirectories; - - ArrayRef<Symbol> getSymbols() const { return Symbols; } - // This allows mutating individual Symbols, but not mutating the list - // of symbols itself. - iterator_range<std::vector<Symbol>::iterator> getMutableSymbols() { - return make_range(Symbols.begin(), Symbols.end()); - } - - const Symbol *findSymbol(size_t UniqueId) const; - - void addSymbols(ArrayRef<Symbol> NewSymbols); + + object::coff_relocation Reloc; + size_t Target = 0; + StringRef TargetName; // Used for diagnostics only +}; + +struct Section { + object::coff_section Header; + std::vector<Relocation> Relocs; + StringRef Name; + ssize_t UniqueId; + size_t Index; + + ArrayRef<uint8_t> getContents() const { + if (!OwnedContents.empty()) + return OwnedContents; + return ContentsRef; + } + + void setContentsRef(ArrayRef<uint8_t> Data) { + OwnedContents.clear(); + ContentsRef = Data; + } + + void setOwnedContents(std::vector<uint8_t> &&Data) { + ContentsRef = ArrayRef<uint8_t>(); + OwnedContents = std::move(Data); + } + + void clearContents() { + ContentsRef = ArrayRef<uint8_t>(); + OwnedContents.clear(); + } + +private: + ArrayRef<uint8_t> ContentsRef; + std::vector<uint8_t> OwnedContents; +}; + +struct AuxSymbol { + AuxSymbol(ArrayRef<uint8_t> In) { + assert(In.size() == sizeof(Opaque)); + std::copy(In.begin(), In.end(), Opaque); + } + + ArrayRef<uint8_t> getRef() const { + return ArrayRef<uint8_t>(Opaque, sizeof(Opaque)); + } + + uint8_t Opaque[sizeof(object::coff_symbol16)]; +}; + +struct Symbol { + object::coff_symbol32 Sym; + StringRef Name; + std::vector<AuxSymbol> AuxData; + StringRef AuxFile; + ssize_t TargetSectionId; + ssize_t AssociativeComdatTargetSectionId = 0; + Optional<size_t> WeakTargetSymbolId; + size_t UniqueId; + size_t RawIndex; + bool Referenced; +}; + +struct Object { + bool IsPE = false; + + object::dos_header DosHeader; + ArrayRef<uint8_t> DosStub; + + object::coff_file_header CoffFileHeader; + + bool Is64 = false; + object::pe32plus_header PeHeader; + uint32_t BaseOfData = 0; // pe32plus_header lacks this field. + + std::vector<object::data_directory> DataDirectories; + + ArrayRef<Symbol> getSymbols() const { return Symbols; } + // This allows mutating individual Symbols, but not mutating the list + // of symbols itself. + iterator_range<std::vector<Symbol>::iterator> getMutableSymbols() { + return make_range(Symbols.begin(), Symbols.end()); + } + + const Symbol *findSymbol(size_t UniqueId) const; + + void addSymbols(ArrayRef<Symbol> NewSymbols); Error removeSymbols(function_ref<Expected<bool>(const Symbol &)> ToRemove); - - // Set the Referenced field on all Symbols, based on relocations in - // all sections. - Error markSymbols(); - - ArrayRef<Section> getSections() const { return Sections; } - // This allows mutating individual Sections, but not mutating the list - // of sections itself. - iterator_range<std::vector<Section>::iterator> getMutableSections() { - return make_range(Sections.begin(), Sections.end()); - } - - const Section *findSection(ssize_t UniqueId) const; - - void addSections(ArrayRef<Section> NewSections); - void removeSections(function_ref<bool(const Section &)> ToRemove); - void truncateSections(function_ref<bool(const Section &)> ToTruncate); - -private: - std::vector<Symbol> Symbols; - DenseMap<size_t, Symbol *> SymbolMap; - - size_t NextSymbolUniqueId = 0; - - std::vector<Section> Sections; - DenseMap<ssize_t, Section *> SectionMap; - - ssize_t NextSectionUniqueId = 1; // Allow a UniqueId 0 to mean undefined. - - // Update SymbolMap. - void updateSymbols(); - - // Update SectionMap and Index in each Section. - void updateSections(); -}; - -// Copy between coff_symbol16 and coff_symbol32. -// The source and destination files can use either coff_symbol16 or -// coff_symbol32, while we always store them as coff_symbol32 in the -// intermediate data structure. -template <class Symbol1Ty, class Symbol2Ty> -void copySymbol(Symbol1Ty &Dest, const Symbol2Ty &Src) { - static_assert(sizeof(Dest.Name.ShortName) == sizeof(Src.Name.ShortName), - "Mismatched name sizes"); - memcpy(Dest.Name.ShortName, Src.Name.ShortName, sizeof(Dest.Name.ShortName)); - Dest.Value = Src.Value; - Dest.SectionNumber = Src.SectionNumber; - Dest.Type = Src.Type; - Dest.StorageClass = Src.StorageClass; - Dest.NumberOfAuxSymbols = Src.NumberOfAuxSymbols; -} - -// Copy between pe32_header and pe32plus_header. -// We store the intermediate state in a pe32plus_header. -template <class PeHeader1Ty, class PeHeader2Ty> -void copyPeHeader(PeHeader1Ty &Dest, const PeHeader2Ty &Src) { - Dest.Magic = Src.Magic; - Dest.MajorLinkerVersion = Src.MajorLinkerVersion; - Dest.MinorLinkerVersion = Src.MinorLinkerVersion; - Dest.SizeOfCode = Src.SizeOfCode; - Dest.SizeOfInitializedData = Src.SizeOfInitializedData; - Dest.SizeOfUninitializedData = Src.SizeOfUninitializedData; - Dest.AddressOfEntryPoint = Src.AddressOfEntryPoint; - Dest.BaseOfCode = Src.BaseOfCode; - Dest.ImageBase = Src.ImageBase; - Dest.SectionAlignment = Src.SectionAlignment; - Dest.FileAlignment = Src.FileAlignment; - Dest.MajorOperatingSystemVersion = Src.MajorOperatingSystemVersion; - Dest.MinorOperatingSystemVersion = Src.MinorOperatingSystemVersion; - Dest.MajorImageVersion = Src.MajorImageVersion; - Dest.MinorImageVersion = Src.MinorImageVersion; - Dest.MajorSubsystemVersion = Src.MajorSubsystemVersion; - Dest.MinorSubsystemVersion = Src.MinorSubsystemVersion; - Dest.Win32VersionValue = Src.Win32VersionValue; - Dest.SizeOfImage = Src.SizeOfImage; - Dest.SizeOfHeaders = Src.SizeOfHeaders; - Dest.CheckSum = Src.CheckSum; - Dest.Subsystem = Src.Subsystem; - Dest.DLLCharacteristics = Src.DLLCharacteristics; - Dest.SizeOfStackReserve = Src.SizeOfStackReserve; - Dest.SizeOfStackCommit = Src.SizeOfStackCommit; - Dest.SizeOfHeapReserve = Src.SizeOfHeapReserve; - Dest.SizeOfHeapCommit = Src.SizeOfHeapCommit; - Dest.LoaderFlags = Src.LoaderFlags; - Dest.NumberOfRvaAndSize = Src.NumberOfRvaAndSize; -} - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H + + // Set the Referenced field on all Symbols, based on relocations in + // all sections. + Error markSymbols(); + + ArrayRef<Section> getSections() const { return Sections; } + // This allows mutating individual Sections, but not mutating the list + // of sections itself. + iterator_range<std::vector<Section>::iterator> getMutableSections() { + return make_range(Sections.begin(), Sections.end()); + } + + const Section *findSection(ssize_t UniqueId) const; + + void addSections(ArrayRef<Section> NewSections); + void removeSections(function_ref<bool(const Section &)> ToRemove); + void truncateSections(function_ref<bool(const Section &)> ToTruncate); + +private: + std::vector<Symbol> Symbols; + DenseMap<size_t, Symbol *> SymbolMap; + + size_t NextSymbolUniqueId = 0; + + std::vector<Section> Sections; + DenseMap<ssize_t, Section *> SectionMap; + + ssize_t NextSectionUniqueId = 1; // Allow a UniqueId 0 to mean undefined. + + // Update SymbolMap. + void updateSymbols(); + + // Update SectionMap and Index in each Section. + void updateSections(); +}; + +// Copy between coff_symbol16 and coff_symbol32. +// The source and destination files can use either coff_symbol16 or +// coff_symbol32, while we always store them as coff_symbol32 in the +// intermediate data structure. +template <class Symbol1Ty, class Symbol2Ty> +void copySymbol(Symbol1Ty &Dest, const Symbol2Ty &Src) { + static_assert(sizeof(Dest.Name.ShortName) == sizeof(Src.Name.ShortName), + "Mismatched name sizes"); + memcpy(Dest.Name.ShortName, Src.Name.ShortName, sizeof(Dest.Name.ShortName)); + Dest.Value = Src.Value; + Dest.SectionNumber = Src.SectionNumber; + Dest.Type = Src.Type; + Dest.StorageClass = Src.StorageClass; + Dest.NumberOfAuxSymbols = Src.NumberOfAuxSymbols; +} + +// Copy between pe32_header and pe32plus_header. +// We store the intermediate state in a pe32plus_header. +template <class PeHeader1Ty, class PeHeader2Ty> +void copyPeHeader(PeHeader1Ty &Dest, const PeHeader2Ty &Src) { + Dest.Magic = Src.Magic; + Dest.MajorLinkerVersion = Src.MajorLinkerVersion; + Dest.MinorLinkerVersion = Src.MinorLinkerVersion; + Dest.SizeOfCode = Src.SizeOfCode; + Dest.SizeOfInitializedData = Src.SizeOfInitializedData; + Dest.SizeOfUninitializedData = Src.SizeOfUninitializedData; + Dest.AddressOfEntryPoint = Src.AddressOfEntryPoint; + Dest.BaseOfCode = Src.BaseOfCode; + Dest.ImageBase = Src.ImageBase; + Dest.SectionAlignment = Src.SectionAlignment; + Dest.FileAlignment = Src.FileAlignment; + Dest.MajorOperatingSystemVersion = Src.MajorOperatingSystemVersion; + Dest.MinorOperatingSystemVersion = Src.MinorOperatingSystemVersion; + Dest.MajorImageVersion = Src.MajorImageVersion; + Dest.MinorImageVersion = Src.MinorImageVersion; + Dest.MajorSubsystemVersion = Src.MajorSubsystemVersion; + Dest.MinorSubsystemVersion = Src.MinorSubsystemVersion; + Dest.Win32VersionValue = Src.Win32VersionValue; + Dest.SizeOfImage = Src.SizeOfImage; + Dest.SizeOfHeaders = Src.SizeOfHeaders; + Dest.CheckSum = Src.CheckSum; + Dest.Subsystem = Src.Subsystem; + Dest.DLLCharacteristics = Src.DLLCharacteristics; + Dest.SizeOfStackReserve = Src.SizeOfStackReserve; + Dest.SizeOfStackCommit = Src.SizeOfStackCommit; + Dest.SizeOfHeapReserve = Src.SizeOfHeapReserve; + Dest.SizeOfHeapCommit = Src.SizeOfHeapCommit; + Dest.LoaderFlags = Src.LoaderFlags; + Dest.NumberOfRvaAndSize = Src.NumberOfRvaAndSize; +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.cpp index d1beacb3bd..aafdc0f728 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.cpp @@ -1,226 +1,226 @@ -//===- Reader.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Reader.h" -#include "Object.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/BinaryFormat/COFF.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/ErrorHandling.h" -#include <cstddef> -#include <cstdint> - -namespace llvm { -namespace objcopy { -namespace coff { - -using namespace object; -using namespace COFF; - -Error COFFReader::readExecutableHeaders(Object &Obj) const { - const dos_header *DH = COFFObj.getDOSHeader(); - Obj.Is64 = COFFObj.is64(); - if (!DH) - return Error::success(); - - Obj.IsPE = true; - Obj.DosHeader = *DH; - if (DH->AddressOfNewExeHeader > sizeof(*DH)) - Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]), - DH->AddressOfNewExeHeader - sizeof(*DH)); - - if (COFFObj.is64()) { - Obj.PeHeader = *COFFObj.getPE32PlusHeader(); - } else { - const pe32_header *PE32 = COFFObj.getPE32Header(); - copyPeHeader(Obj.PeHeader, *PE32); - // The pe32plus_header (stored in Object) lacks the BaseOfData field. - Obj.BaseOfData = PE32->BaseOfData; - } - - for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) { - const data_directory *Dir = COFFObj.getDataDirectory(I); - if (!Dir) - return errorCodeToError(object_error::parse_failed); - Obj.DataDirectories.emplace_back(*Dir); - } - return Error::success(); -} - -Error COFFReader::readSections(Object &Obj) const { - std::vector<Section> Sections; - // Section indexing starts from 1. - for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) { - Expected<const coff_section *> SecOrErr = COFFObj.getSection(I); - if (!SecOrErr) - return SecOrErr.takeError(); - const coff_section *Sec = *SecOrErr; - Sections.push_back(Section()); - Section &S = Sections.back(); - S.Header = *Sec; - S.Header.Characteristics &= ~COFF::IMAGE_SCN_LNK_NRELOC_OVFL; - ArrayRef<uint8_t> Contents; - if (Error E = COFFObj.getSectionContents(Sec, Contents)) - return E; - S.setContentsRef(Contents); - ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec); - for (const coff_relocation &R : Relocs) - S.Relocs.push_back(R); - if (Expected<StringRef> NameOrErr = COFFObj.getSectionName(Sec)) - S.Name = *NameOrErr; - else - return NameOrErr.takeError(); - } - Obj.addSections(Sections); - return Error::success(); -} - -Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { - std::vector<Symbol> Symbols; - Symbols.reserve(COFFObj.getRawNumberOfSymbols()); - ArrayRef<Section> Sections = Obj.getSections(); - for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) { - Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I); - if (!SymOrErr) - return SymOrErr.takeError(); - COFFSymbolRef SymRef = *SymOrErr; - - Symbols.push_back(Symbol()); - Symbol &Sym = Symbols.back(); - // Copy symbols from the original form into an intermediate coff_symbol32. - if (IsBigObj) - copySymbol(Sym.Sym, - *reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr())); - else - copySymbol(Sym.Sym, - *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); - auto NameOrErr = COFFObj.getSymbolName(SymRef); - if (!NameOrErr) - return NameOrErr.takeError(); - Sym.Name = *NameOrErr; - - ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef); - size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16); - assert(AuxData.size() == SymSize * SymRef.getNumberOfAuxSymbols()); - // The auxillary symbols are structs of sizeof(coff_symbol16) each. - // In the big object format (where symbols are coff_symbol32), each - // auxillary symbol is padded with 2 bytes at the end. Copy each - // auxillary symbol to the Sym.AuxData vector. For file symbols, - // the whole range of aux symbols are interpreted as one null padded - // string instead. - if (SymRef.isFileRecord()) - Sym.AuxFile = StringRef(reinterpret_cast<const char *>(AuxData.data()), - AuxData.size()) - .rtrim('\0'); - else - for (size_t I = 0; I < SymRef.getNumberOfAuxSymbols(); I++) - Sym.AuxData.push_back(AuxData.slice(I * SymSize, sizeof(AuxSymbol))); - - // Find the unique id of the section - if (SymRef.getSectionNumber() <= - 0) // Special symbol (undefined/absolute/debug) - Sym.TargetSectionId = SymRef.getSectionNumber(); - else if (static_cast<uint32_t>(SymRef.getSectionNumber() - 1) < - Sections.size()) - Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId; - else - return createStringError(object_error::parse_failed, - "section number out of range"); - // For section definitions, check if it is comdat associative, and if - // it is, find the target section unique id. - const coff_aux_section_definition *SD = SymRef.getSectionDefinition(); - const coff_aux_weak_external *WE = SymRef.getWeakExternal(); - if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) { - int32_t Index = SD->getNumber(IsBigObj); - if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size()) - return createStringError(object_error::parse_failed, - "unexpected associative section index"); - Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId; - } else if (WE) { - // This is a raw symbol index for now, but store it in the Symbol - // until we've added them to the Object, which assigns the final - // unique ids. - Sym.WeakTargetSymbolId = WE->TagIndex; - } - I += 1 + SymRef.getNumberOfAuxSymbols(); - } - Obj.addSymbols(Symbols); - return Error::success(); -} - -Error COFFReader::setSymbolTargets(Object &Obj) const { - std::vector<const Symbol *> RawSymbolTable; - for (const Symbol &Sym : Obj.getSymbols()) { - RawSymbolTable.push_back(&Sym); - for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++) - RawSymbolTable.push_back(nullptr); - } - for (Symbol &Sym : Obj.getMutableSymbols()) { - // Convert WeakTargetSymbolId from the original raw symbol index to - // a proper unique id. - if (Sym.WeakTargetSymbolId) { - if (*Sym.WeakTargetSymbolId >= RawSymbolTable.size()) - return createStringError(object_error::parse_failed, - "weak external reference out of range"); - const Symbol *Target = RawSymbolTable[*Sym.WeakTargetSymbolId]; - if (Target == nullptr) - return createStringError(object_error::parse_failed, - "invalid SymbolTableIndex"); - Sym.WeakTargetSymbolId = Target->UniqueId; - } - } - for (Section &Sec : Obj.getMutableSections()) { - for (Relocation &R : Sec.Relocs) { - if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size()) - return createStringError(object_error::parse_failed, - "SymbolTableIndex out of range"); - const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex]; - if (Sym == nullptr) - return createStringError(object_error::parse_failed, - "invalid SymbolTableIndex"); - R.Target = Sym->UniqueId; - R.TargetName = Sym->Name; - } - } - return Error::success(); -} - -Expected<std::unique_ptr<Object>> COFFReader::create() const { - auto Obj = std::make_unique<Object>(); - - bool IsBigObj = false; - if (const coff_file_header *CFH = COFFObj.getCOFFHeader()) { - Obj->CoffFileHeader = *CFH; - } else { - const coff_bigobj_file_header *CBFH = COFFObj.getCOFFBigObjHeader(); - if (!CBFH) - return createStringError(object_error::parse_failed, - "no COFF file header returned"); - // Only copying the few fields from the bigobj header that we need - // and won't recreate in the end. - Obj->CoffFileHeader.Machine = CBFH->Machine; - Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp; - IsBigObj = true; - } - - if (Error E = readExecutableHeaders(*Obj)) - return std::move(E); - if (Error E = readSections(*Obj)) - return std::move(E); - if (Error E = readSymbols(*Obj, IsBigObj)) - return std::move(E); - if (Error E = setSymbolTargets(*Obj)) - return std::move(E); - - return std::move(Obj); -} - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm +//===- Reader.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Reader.h" +#include "Object.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +Error COFFReader::readExecutableHeaders(Object &Obj) const { + const dos_header *DH = COFFObj.getDOSHeader(); + Obj.Is64 = COFFObj.is64(); + if (!DH) + return Error::success(); + + Obj.IsPE = true; + Obj.DosHeader = *DH; + if (DH->AddressOfNewExeHeader > sizeof(*DH)) + Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]), + DH->AddressOfNewExeHeader - sizeof(*DH)); + + if (COFFObj.is64()) { + Obj.PeHeader = *COFFObj.getPE32PlusHeader(); + } else { + const pe32_header *PE32 = COFFObj.getPE32Header(); + copyPeHeader(Obj.PeHeader, *PE32); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + Obj.BaseOfData = PE32->BaseOfData; + } + + for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) { + const data_directory *Dir = COFFObj.getDataDirectory(I); + if (!Dir) + return errorCodeToError(object_error::parse_failed); + Obj.DataDirectories.emplace_back(*Dir); + } + return Error::success(); +} + +Error COFFReader::readSections(Object &Obj) const { + std::vector<Section> Sections; + // Section indexing starts from 1. + for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) { + Expected<const coff_section *> SecOrErr = COFFObj.getSection(I); + if (!SecOrErr) + return SecOrErr.takeError(); + const coff_section *Sec = *SecOrErr; + Sections.push_back(Section()); + Section &S = Sections.back(); + S.Header = *Sec; + S.Header.Characteristics &= ~COFF::IMAGE_SCN_LNK_NRELOC_OVFL; + ArrayRef<uint8_t> Contents; + if (Error E = COFFObj.getSectionContents(Sec, Contents)) + return E; + S.setContentsRef(Contents); + ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec); + for (const coff_relocation &R : Relocs) + S.Relocs.push_back(R); + if (Expected<StringRef> NameOrErr = COFFObj.getSectionName(Sec)) + S.Name = *NameOrErr; + else + return NameOrErr.takeError(); + } + Obj.addSections(Sections); + return Error::success(); +} + +Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { + std::vector<Symbol> Symbols; + Symbols.reserve(COFFObj.getRawNumberOfSymbols()); + ArrayRef<Section> Sections = Obj.getSections(); + for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) { + Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I); + if (!SymOrErr) + return SymOrErr.takeError(); + COFFSymbolRef SymRef = *SymOrErr; + + Symbols.push_back(Symbol()); + Symbol &Sym = Symbols.back(); + // Copy symbols from the original form into an intermediate coff_symbol32. + if (IsBigObj) + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr())); + else + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); + auto NameOrErr = COFFObj.getSymbolName(SymRef); + if (!NameOrErr) + return NameOrErr.takeError(); + Sym.Name = *NameOrErr; + + ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef); + size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16); + assert(AuxData.size() == SymSize * SymRef.getNumberOfAuxSymbols()); + // The auxillary symbols are structs of sizeof(coff_symbol16) each. + // In the big object format (where symbols are coff_symbol32), each + // auxillary symbol is padded with 2 bytes at the end. Copy each + // auxillary symbol to the Sym.AuxData vector. For file symbols, + // the whole range of aux symbols are interpreted as one null padded + // string instead. + if (SymRef.isFileRecord()) + Sym.AuxFile = StringRef(reinterpret_cast<const char *>(AuxData.data()), + AuxData.size()) + .rtrim('\0'); + else + for (size_t I = 0; I < SymRef.getNumberOfAuxSymbols(); I++) + Sym.AuxData.push_back(AuxData.slice(I * SymSize, sizeof(AuxSymbol))); + + // Find the unique id of the section + if (SymRef.getSectionNumber() <= + 0) // Special symbol (undefined/absolute/debug) + Sym.TargetSectionId = SymRef.getSectionNumber(); + else if (static_cast<uint32_t>(SymRef.getSectionNumber() - 1) < + Sections.size()) + Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId; + else + return createStringError(object_error::parse_failed, + "section number out of range"); + // For section definitions, check if it is comdat associative, and if + // it is, find the target section unique id. + const coff_aux_section_definition *SD = SymRef.getSectionDefinition(); + const coff_aux_weak_external *WE = SymRef.getWeakExternal(); + if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + int32_t Index = SD->getNumber(IsBigObj); + if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size()) + return createStringError(object_error::parse_failed, + "unexpected associative section index"); + Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId; + } else if (WE) { + // This is a raw symbol index for now, but store it in the Symbol + // until we've added them to the Object, which assigns the final + // unique ids. + Sym.WeakTargetSymbolId = WE->TagIndex; + } + I += 1 + SymRef.getNumberOfAuxSymbols(); + } + Obj.addSymbols(Symbols); + return Error::success(); +} + +Error COFFReader::setSymbolTargets(Object &Obj) const { + std::vector<const Symbol *> RawSymbolTable; + for (const Symbol &Sym : Obj.getSymbols()) { + RawSymbolTable.push_back(&Sym); + for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++) + RawSymbolTable.push_back(nullptr); + } + for (Symbol &Sym : Obj.getMutableSymbols()) { + // Convert WeakTargetSymbolId from the original raw symbol index to + // a proper unique id. + if (Sym.WeakTargetSymbolId) { + if (*Sym.WeakTargetSymbolId >= RawSymbolTable.size()) + return createStringError(object_error::parse_failed, + "weak external reference out of range"); + const Symbol *Target = RawSymbolTable[*Sym.WeakTargetSymbolId]; + if (Target == nullptr) + return createStringError(object_error::parse_failed, + "invalid SymbolTableIndex"); + Sym.WeakTargetSymbolId = Target->UniqueId; + } + } + for (Section &Sec : Obj.getMutableSections()) { + for (Relocation &R : Sec.Relocs) { + if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size()) + return createStringError(object_error::parse_failed, + "SymbolTableIndex out of range"); + const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex]; + if (Sym == nullptr) + return createStringError(object_error::parse_failed, + "invalid SymbolTableIndex"); + R.Target = Sym->UniqueId; + R.TargetName = Sym->Name; + } + } + return Error::success(); +} + +Expected<std::unique_ptr<Object>> COFFReader::create() const { + auto Obj = std::make_unique<Object>(); + + bool IsBigObj = false; + if (const coff_file_header *CFH = COFFObj.getCOFFHeader()) { + Obj->CoffFileHeader = *CFH; + } else { + const coff_bigobj_file_header *CBFH = COFFObj.getCOFFBigObjHeader(); + if (!CBFH) + return createStringError(object_error::parse_failed, + "no COFF file header returned"); + // Only copying the few fields from the bigobj header that we need + // and won't recreate in the end. + Obj->CoffFileHeader.Machine = CBFH->Machine; + Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp; + IsBigObj = true; + } + + if (Error E = readExecutableHeaders(*Obj)) + return std::move(E); + if (Error E = readSections(*Obj)) + return std::move(E); + if (Error E = readSymbols(*Obj, IsBigObj)) + return std::move(E); + if (Error E = setSymbolTargets(*Obj)) + return std::move(E); + + return std::move(Obj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.h b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.h index ec15369db0..573b768c06 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Reader.h @@ -1,42 +1,42 @@ -//===- Reader.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H -#define LLVM_TOOLS_OBJCOPY_COFF_READER_H - -#include "Buffer.h" -#include "llvm/BinaryFormat/COFF.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace objcopy { -namespace coff { - -struct Object; - -using object::COFFObjectFile; - -class COFFReader { - const COFFObjectFile &COFFObj; - - Error readExecutableHeaders(Object &Obj) const; - Error readSections(Object &Obj) const; - Error readSymbols(Object &Obj, bool IsBigObj) const; - Error setSymbolTargets(Object &Obj) const; - -public: - explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {} - Expected<std::unique_ptr<Object>> create() const; -}; - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_COFF_READER_H +//===- Reader.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H +#define LLVM_TOOLS_OBJCOPY_COFF_READER_H + +#include "Buffer.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Object; + +using object::COFFObjectFile; + +class COFFReader { + const COFFObjectFile &COFFObj; + + Error readExecutableHeaders(Object &Obj) const; + Error readSections(Object &Obj) const; + Error readSymbols(Object &Obj, bool IsBigObj) const; + Error setSymbolTargets(Object &Obj) const; + +public: + explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {} + Expected<std::unique_ptr<Object>> create() const; +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_READER_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.cpp index 6b560890a4..3fff721dcb 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.cpp @@ -1,449 +1,449 @@ -//===- Writer.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Writer.h" -#include "Object.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/BinaryFormat/COFF.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/ErrorHandling.h" -#include <cstddef> -#include <cstdint> - -namespace llvm { -namespace objcopy { -namespace coff { - -using namespace object; -using namespace COFF; - -Error COFFWriter::finalizeRelocTargets() { - for (Section &Sec : Obj.getMutableSections()) { - for (Relocation &R : Sec.Relocs) { - const Symbol *Sym = Obj.findSymbol(R.Target); - if (Sym == nullptr) - return createStringError(object_error::invalid_symbol_index, - "relocation target '%s' (%zu) not found", - R.TargetName.str().c_str(), R.Target); - R.Reloc.SymbolTableIndex = Sym->RawIndex; - } - } - return Error::success(); -} - -Error COFFWriter::finalizeSymbolContents() { - for (Symbol &Sym : Obj.getMutableSymbols()) { - if (Sym.TargetSectionId <= 0) { - // Undefined, or a special kind of symbol. These negative values - // are stored in the SectionNumber field which is unsigned. - Sym.Sym.SectionNumber = static_cast<uint32_t>(Sym.TargetSectionId); - } else { - const Section *Sec = Obj.findSection(Sym.TargetSectionId); - if (Sec == nullptr) - return createStringError(object_error::invalid_symbol_index, - "symbol '%s' points to a removed section", - Sym.Name.str().c_str()); - Sym.Sym.SectionNumber = Sec->Index; - - if (Sym.Sym.NumberOfAuxSymbols == 1 && - Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) { - coff_aux_section_definition *SD = - reinterpret_cast<coff_aux_section_definition *>( - Sym.AuxData[0].Opaque); - uint32_t SDSectionNumber; - if (Sym.AssociativeComdatTargetSectionId == 0) { - // Not a comdat associative section; just set the Number field to - // the number of the section itself. - SDSectionNumber = Sec->Index; - } else { - Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId); - if (Sec == nullptr) - return createStringError( - object_error::invalid_symbol_index, - "symbol '%s' is associative to a removed section", - Sym.Name.str().c_str()); - SDSectionNumber = Sec->Index; - } - // Update the section definition with the new section number. - SD->NumberLowPart = static_cast<uint16_t>(SDSectionNumber); - SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16); - } - } - // Check that we actually have got AuxData to match the weak symbol target - // we want to set. Only >= 1 would be required, but only == 1 makes sense. - if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) { - coff_aux_weak_external *WE = - reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData[0].Opaque); - const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId); - if (Target == nullptr) - return createStringError(object_error::invalid_symbol_index, - "symbol '%s' is missing its weak target", - Sym.Name.str().c_str()); - WE->TagIndex = Target->RawIndex; - } - } - return Error::success(); -} - -void COFFWriter::layoutSections() { - for (auto &S : Obj.getMutableSections()) { - if (S.Header.SizeOfRawData > 0) - S.Header.PointerToRawData = FileSize; - FileSize += S.Header.SizeOfRawData; // For executables, this is already - // aligned to FileAlignment. - if (S.Relocs.size() >= 0xffff) { - S.Header.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL; - S.Header.NumberOfRelocations = 0xffff; - S.Header.PointerToRelocations = FileSize; - FileSize += sizeof(coff_relocation); - } else { - S.Header.NumberOfRelocations = S.Relocs.size(); - S.Header.PointerToRelocations = S.Relocs.size() ? FileSize : 0; - } - - FileSize += S.Relocs.size() * sizeof(coff_relocation); - FileSize = alignTo(FileSize, FileAlignment); - - if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) - SizeOfInitializedData += S.Header.SizeOfRawData; - } -} - -size_t COFFWriter::finalizeStringTable() { - for (const auto &S : Obj.getSections()) - if (S.Name.size() > COFF::NameSize) - StrTabBuilder.add(S.Name); - - for (const auto &S : Obj.getSymbols()) - if (S.Name.size() > COFF::NameSize) - StrTabBuilder.add(S.Name); - - StrTabBuilder.finalize(); - - for (auto &S : Obj.getMutableSections()) { - memset(S.Header.Name, 0, sizeof(S.Header.Name)); - if (S.Name.size() > COFF::NameSize) { - snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", - (int)StrTabBuilder.getOffset(S.Name)); - } else { - memcpy(S.Header.Name, S.Name.data(), S.Name.size()); - } - } - for (auto &S : Obj.getMutableSymbols()) { - if (S.Name.size() > COFF::NameSize) { - S.Sym.Name.Offset.Zeroes = 0; - S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name); - } else { - strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize); - } - } - return StrTabBuilder.getSize(); -} - -template <class SymbolTy> -std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() { - size_t RawSymIndex = 0; - for (auto &S : Obj.getMutableSymbols()) { - // Symbols normally have NumberOfAuxSymbols set correctly all the time. - // For file symbols, we need to know the output file's symbol size to be - // able to calculate the number of slots it occupies. - if (!S.AuxFile.empty()) - S.Sym.NumberOfAuxSymbols = - alignTo(S.AuxFile.size(), sizeof(SymbolTy)) / sizeof(SymbolTy); - S.RawIndex = RawSymIndex; - RawSymIndex += 1 + S.Sym.NumberOfAuxSymbols; - } - return std::make_pair(RawSymIndex * sizeof(SymbolTy), sizeof(SymbolTy)); -} - -Error COFFWriter::finalize(bool IsBigObj) { - size_t SymTabSize, SymbolSize; - std::tie(SymTabSize, SymbolSize) = IsBigObj - ? finalizeSymbolTable<coff_symbol32>() - : finalizeSymbolTable<coff_symbol16>(); - - if (Error E = finalizeRelocTargets()) - return E; - if (Error E = finalizeSymbolContents()) - return E; - - size_t SizeOfHeaders = 0; - FileAlignment = 1; - size_t PeHeaderSize = 0; - if (Obj.IsPE) { - Obj.DosHeader.AddressOfNewExeHeader = - sizeof(Obj.DosHeader) + Obj.DosStub.size(); - SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic); - - FileAlignment = Obj.PeHeader.FileAlignment; - Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size(); - - PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header); - SizeOfHeaders += - PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); - } - Obj.CoffFileHeader.NumberOfSections = Obj.getSections().size(); - SizeOfHeaders += - IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header); - SizeOfHeaders += sizeof(coff_section) * Obj.getSections().size(); - SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment); - - Obj.CoffFileHeader.SizeOfOptionalHeader = - PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); - - FileSize = SizeOfHeaders; - SizeOfInitializedData = 0; - - layoutSections(); - - if (Obj.IsPE) { - Obj.PeHeader.SizeOfHeaders = SizeOfHeaders; - Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData; - - if (!Obj.getSections().empty()) { - const Section &S = Obj.getSections().back(); - Obj.PeHeader.SizeOfImage = - alignTo(S.Header.VirtualAddress + S.Header.VirtualSize, - Obj.PeHeader.SectionAlignment); - } - - // If the PE header had a checksum, clear it, since it isn't valid - // any longer. (We don't calculate a new one.) - Obj.PeHeader.CheckSum = 0; - } - - size_t StrTabSize = finalizeStringTable(); - - size_t PointerToSymbolTable = FileSize; - // StrTabSize <= 4 is the size of an empty string table, only consisting - // of the length field. - if (SymTabSize == 0 && StrTabSize <= 4 && Obj.IsPE) { - // For executables, don't point to the symbol table and skip writing - // the length field, if both the symbol and string tables are empty. - PointerToSymbolTable = 0; - StrTabSize = 0; - } - - size_t NumRawSymbols = SymTabSize / SymbolSize; - Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable; - Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols; - FileSize += SymTabSize + StrTabSize; - FileSize = alignTo(FileSize, FileAlignment); - - return Error::success(); -} - -void COFFWriter::writeHeaders(bool IsBigObj) { - uint8_t *Ptr = Buf.getBufferStart(); - if (Obj.IsPE) { - memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); - Ptr += sizeof(Obj.DosHeader); - memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size()); - Ptr += Obj.DosStub.size(); - memcpy(Ptr, PEMagic, sizeof(PEMagic)); - Ptr += sizeof(PEMagic); - } - if (!IsBigObj) { - memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader)); - Ptr += sizeof(Obj.CoffFileHeader); - } else { - // Generate a coff_bigobj_file_header, filling it in with the values - // from Obj.CoffFileHeader. All extra fields that don't exist in - // coff_file_header can be set to hardcoded values. - coff_bigobj_file_header BigObjHeader; - BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN; - BigObjHeader.Sig2 = 0xffff; - BigObjHeader.Version = BigObjHeader::MinBigObjectVersion; - BigObjHeader.Machine = Obj.CoffFileHeader.Machine; - BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp; - memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic)); - BigObjHeader.unused1 = 0; - BigObjHeader.unused2 = 0; - BigObjHeader.unused3 = 0; - BigObjHeader.unused4 = 0; - // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus - // get the original one instead. - BigObjHeader.NumberOfSections = Obj.getSections().size(); - BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable; - BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols; - - memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader)); - Ptr += sizeof(BigObjHeader); - } - if (Obj.IsPE) { - if (Obj.Is64) { - memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader)); - Ptr += sizeof(Obj.PeHeader); - } else { - pe32_header PeHeader; - copyPeHeader(PeHeader, Obj.PeHeader); - // The pe32plus_header (stored in Object) lacks the BaseOfData field. - PeHeader.BaseOfData = Obj.BaseOfData; - - memcpy(Ptr, &PeHeader, sizeof(PeHeader)); - Ptr += sizeof(PeHeader); - } - for (const auto &DD : Obj.DataDirectories) { - memcpy(Ptr, &DD, sizeof(DD)); - Ptr += sizeof(DD); - } - } - for (const auto &S : Obj.getSections()) { - memcpy(Ptr, &S.Header, sizeof(S.Header)); - Ptr += sizeof(S.Header); - } -} - -void COFFWriter::writeSections() { - for (const auto &S : Obj.getSections()) { - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; - ArrayRef<uint8_t> Contents = S.getContents(); - std::copy(Contents.begin(), Contents.end(), Ptr); - - // For executable sections, pad the remainder of the raw data size with - // 0xcc, which is int3 on x86. - if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) && - S.Header.SizeOfRawData > Contents.size()) - memset(Ptr + Contents.size(), 0xcc, - S.Header.SizeOfRawData - Contents.size()); - - Ptr += S.Header.SizeOfRawData; - - if (S.Relocs.size() >= 0xffff) { - object::coff_relocation R; - R.VirtualAddress = S.Relocs.size() + 1; - R.SymbolTableIndex = 0; - R.Type = 0; - memcpy(Ptr, &R, sizeof(R)); - Ptr += sizeof(R); - } - for (const auto &R : S.Relocs) { - memcpy(Ptr, &R.Reloc, sizeof(R.Reloc)); - Ptr += sizeof(R.Reloc); - } - } -} - -template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { - uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; - for (const auto &S : Obj.getSymbols()) { - // Convert symbols back to the right size, from coff_symbol32. - copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), - S.Sym); - Ptr += sizeof(SymbolTy); - if (!S.AuxFile.empty()) { - // For file symbols, just write the string into the aux symbol slots, - // assuming that the unwritten parts are initialized to zero in the memory - // mapped file. - std::copy(S.AuxFile.begin(), S.AuxFile.end(), Ptr); - Ptr += S.Sym.NumberOfAuxSymbols * sizeof(SymbolTy); - } else { - // For other auxillary symbols, write their opaque payload into one symbol - // table slot each. For big object files, the symbols are larger than the - // opaque auxillary symbol struct and we leave padding at the end of each - // entry. - for (const AuxSymbol &AuxSym : S.AuxData) { - ArrayRef<uint8_t> Ref = AuxSym.getRef(); - std::copy(Ref.begin(), Ref.end(), Ptr); - Ptr += sizeof(SymbolTy); - } - } - } - if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) { - // Always write a string table in object files, even an empty one. - StrTabBuilder.write(Ptr); - Ptr += StrTabBuilder.getSize(); - } -} - -Error COFFWriter::write(bool IsBigObj) { - if (Error E = finalize(IsBigObj)) - return E; - - if (Error E = Buf.allocate(FileSize)) - return E; - - writeHeaders(IsBigObj); - writeSections(); - if (IsBigObj) - writeSymbolStringTables<coff_symbol32>(); - else - writeSymbolStringTables<coff_symbol16>(); - - if (Obj.IsPE) - if (Error E = patchDebugDirectory()) - return E; - - return Buf.commit(); -} - -Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) { - for (const auto &S : Obj.getSections()) { - if (RVA >= S.Header.VirtualAddress && - RVA < S.Header.VirtualAddress + S.Header.SizeOfRawData) - return S.Header.PointerToRawData + RVA - S.Header.VirtualAddress; - } - return createStringError(object_error::parse_failed, - "debug directory payload not found"); -} - -// Locate which sections contain the debug directories, iterate over all -// the debug_directory structs in there, and set the PointerToRawData field -// in all of them, according to their new physical location in the file. -Error COFFWriter::patchDebugDirectory() { - if (Obj.DataDirectories.size() < DEBUG_DIRECTORY) - return Error::success(); - const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY]; - if (Dir->Size <= 0) - return Error::success(); - for (const auto &S : Obj.getSections()) { - if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress && - Dir->RelativeVirtualAddress < - S.Header.VirtualAddress + S.Header.SizeOfRawData) { - if (Dir->RelativeVirtualAddress + Dir->Size > - S.Header.VirtualAddress + S.Header.SizeOfRawData) - return createStringError(object_error::parse_failed, - "debug directory extends past end of section"); - - size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; - uint8_t *End = Ptr + Dir->Size; - while (Ptr < End) { - debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); - if (!Debug->AddressOfRawData) - return createStringError(object_error::parse_failed, - "debug directory payload outside of " - "mapped sections not supported"); - if (Expected<uint32_t> FilePosOrErr = - virtualAddressToFileAddress(Debug->AddressOfRawData)) - Debug->PointerToRawData = *FilePosOrErr; - else - return FilePosOrErr.takeError(); - Ptr += sizeof(debug_directory); - Offset += sizeof(debug_directory); - } - // Debug directory found and patched, all done. - return Error::success(); - } - } - return createStringError(object_error::parse_failed, - "debug directory not found"); -} - -Error COFFWriter::write() { - bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16; - if (IsBigObj && Obj.IsPE) - return createStringError(object_error::parse_failed, - "too many sections for executable"); - return write(IsBigObj); -} - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm +//===- Writer.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "Object.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +Error COFFWriter::finalizeRelocTargets() { + for (Section &Sec : Obj.getMutableSections()) { + for (Relocation &R : Sec.Relocs) { + const Symbol *Sym = Obj.findSymbol(R.Target); + if (Sym == nullptr) + return createStringError(object_error::invalid_symbol_index, + "relocation target '%s' (%zu) not found", + R.TargetName.str().c_str(), R.Target); + R.Reloc.SymbolTableIndex = Sym->RawIndex; + } + } + return Error::success(); +} + +Error COFFWriter::finalizeSymbolContents() { + for (Symbol &Sym : Obj.getMutableSymbols()) { + if (Sym.TargetSectionId <= 0) { + // Undefined, or a special kind of symbol. These negative values + // are stored in the SectionNumber field which is unsigned. + Sym.Sym.SectionNumber = static_cast<uint32_t>(Sym.TargetSectionId); + } else { + const Section *Sec = Obj.findSection(Sym.TargetSectionId); + if (Sec == nullptr) + return createStringError(object_error::invalid_symbol_index, + "symbol '%s' points to a removed section", + Sym.Name.str().c_str()); + Sym.Sym.SectionNumber = Sec->Index; + + if (Sym.Sym.NumberOfAuxSymbols == 1 && + Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) { + coff_aux_section_definition *SD = + reinterpret_cast<coff_aux_section_definition *>( + Sym.AuxData[0].Opaque); + uint32_t SDSectionNumber; + if (Sym.AssociativeComdatTargetSectionId == 0) { + // Not a comdat associative section; just set the Number field to + // the number of the section itself. + SDSectionNumber = Sec->Index; + } else { + Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId); + if (Sec == nullptr) + return createStringError( + object_error::invalid_symbol_index, + "symbol '%s' is associative to a removed section", + Sym.Name.str().c_str()); + SDSectionNumber = Sec->Index; + } + // Update the section definition with the new section number. + SD->NumberLowPart = static_cast<uint16_t>(SDSectionNumber); + SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16); + } + } + // Check that we actually have got AuxData to match the weak symbol target + // we want to set. Only >= 1 would be required, but only == 1 makes sense. + if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) { + coff_aux_weak_external *WE = + reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData[0].Opaque); + const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId); + if (Target == nullptr) + return createStringError(object_error::invalid_symbol_index, + "symbol '%s' is missing its weak target", + Sym.Name.str().c_str()); + WE->TagIndex = Target->RawIndex; + } + } + return Error::success(); +} + +void COFFWriter::layoutSections() { + for (auto &S : Obj.getMutableSections()) { + if (S.Header.SizeOfRawData > 0) + S.Header.PointerToRawData = FileSize; + FileSize += S.Header.SizeOfRawData; // For executables, this is already + // aligned to FileAlignment. + if (S.Relocs.size() >= 0xffff) { + S.Header.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL; + S.Header.NumberOfRelocations = 0xffff; + S.Header.PointerToRelocations = FileSize; + FileSize += sizeof(coff_relocation); + } else { + S.Header.NumberOfRelocations = S.Relocs.size(); + S.Header.PointerToRelocations = S.Relocs.size() ? FileSize : 0; + } + + FileSize += S.Relocs.size() * sizeof(coff_relocation); + FileSize = alignTo(FileSize, FileAlignment); + + if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + SizeOfInitializedData += S.Header.SizeOfRawData; + } +} + +size_t COFFWriter::finalizeStringTable() { + for (const auto &S : Obj.getSections()) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + for (const auto &S : Obj.getSymbols()) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + StrTabBuilder.finalize(); + + for (auto &S : Obj.getMutableSections()) { + memset(S.Header.Name, 0, sizeof(S.Header.Name)); + if (S.Name.size() > COFF::NameSize) { + snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", + (int)StrTabBuilder.getOffset(S.Name)); + } else { + memcpy(S.Header.Name, S.Name.data(), S.Name.size()); + } + } + for (auto &S : Obj.getMutableSymbols()) { + if (S.Name.size() > COFF::NameSize) { + S.Sym.Name.Offset.Zeroes = 0; + S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name); + } else { + strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize); + } + } + return StrTabBuilder.getSize(); +} + +template <class SymbolTy> +std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() { + size_t RawSymIndex = 0; + for (auto &S : Obj.getMutableSymbols()) { + // Symbols normally have NumberOfAuxSymbols set correctly all the time. + // For file symbols, we need to know the output file's symbol size to be + // able to calculate the number of slots it occupies. + if (!S.AuxFile.empty()) + S.Sym.NumberOfAuxSymbols = + alignTo(S.AuxFile.size(), sizeof(SymbolTy)) / sizeof(SymbolTy); + S.RawIndex = RawSymIndex; + RawSymIndex += 1 + S.Sym.NumberOfAuxSymbols; + } + return std::make_pair(RawSymIndex * sizeof(SymbolTy), sizeof(SymbolTy)); +} + +Error COFFWriter::finalize(bool IsBigObj) { + size_t SymTabSize, SymbolSize; + std::tie(SymTabSize, SymbolSize) = IsBigObj + ? finalizeSymbolTable<coff_symbol32>() + : finalizeSymbolTable<coff_symbol16>(); + + if (Error E = finalizeRelocTargets()) + return E; + if (Error E = finalizeSymbolContents()) + return E; + + size_t SizeOfHeaders = 0; + FileAlignment = 1; + size_t PeHeaderSize = 0; + if (Obj.IsPE) { + Obj.DosHeader.AddressOfNewExeHeader = + sizeof(Obj.DosHeader) + Obj.DosStub.size(); + SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic); + + FileAlignment = Obj.PeHeader.FileAlignment; + Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size(); + + PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header); + SizeOfHeaders += + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + } + Obj.CoffFileHeader.NumberOfSections = Obj.getSections().size(); + SizeOfHeaders += + IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header); + SizeOfHeaders += sizeof(coff_section) * Obj.getSections().size(); + SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment); + + Obj.CoffFileHeader.SizeOfOptionalHeader = + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + + FileSize = SizeOfHeaders; + SizeOfInitializedData = 0; + + layoutSections(); + + if (Obj.IsPE) { + Obj.PeHeader.SizeOfHeaders = SizeOfHeaders; + Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData; + + if (!Obj.getSections().empty()) { + const Section &S = Obj.getSections().back(); + Obj.PeHeader.SizeOfImage = + alignTo(S.Header.VirtualAddress + S.Header.VirtualSize, + Obj.PeHeader.SectionAlignment); + } + + // If the PE header had a checksum, clear it, since it isn't valid + // any longer. (We don't calculate a new one.) + Obj.PeHeader.CheckSum = 0; + } + + size_t StrTabSize = finalizeStringTable(); + + size_t PointerToSymbolTable = FileSize; + // StrTabSize <= 4 is the size of an empty string table, only consisting + // of the length field. + if (SymTabSize == 0 && StrTabSize <= 4 && Obj.IsPE) { + // For executables, don't point to the symbol table and skip writing + // the length field, if both the symbol and string tables are empty. + PointerToSymbolTable = 0; + StrTabSize = 0; + } + + size_t NumRawSymbols = SymTabSize / SymbolSize; + Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable; + Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols; + FileSize += SymTabSize + StrTabSize; + FileSize = alignTo(FileSize, FileAlignment); + + return Error::success(); +} + +void COFFWriter::writeHeaders(bool IsBigObj) { + uint8_t *Ptr = Buf.getBufferStart(); + if (Obj.IsPE) { + memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); + Ptr += sizeof(Obj.DosHeader); + memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size()); + Ptr += Obj.DosStub.size(); + memcpy(Ptr, PEMagic, sizeof(PEMagic)); + Ptr += sizeof(PEMagic); + } + if (!IsBigObj) { + memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader)); + Ptr += sizeof(Obj.CoffFileHeader); + } else { + // Generate a coff_bigobj_file_header, filling it in with the values + // from Obj.CoffFileHeader. All extra fields that don't exist in + // coff_file_header can be set to hardcoded values. + coff_bigobj_file_header BigObjHeader; + BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN; + BigObjHeader.Sig2 = 0xffff; + BigObjHeader.Version = BigObjHeader::MinBigObjectVersion; + BigObjHeader.Machine = Obj.CoffFileHeader.Machine; + BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp; + memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic)); + BigObjHeader.unused1 = 0; + BigObjHeader.unused2 = 0; + BigObjHeader.unused3 = 0; + BigObjHeader.unused4 = 0; + // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus + // get the original one instead. + BigObjHeader.NumberOfSections = Obj.getSections().size(); + BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable; + BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols; + + memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader)); + Ptr += sizeof(BigObjHeader); + } + if (Obj.IsPE) { + if (Obj.Is64) { + memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader)); + Ptr += sizeof(Obj.PeHeader); + } else { + pe32_header PeHeader; + copyPeHeader(PeHeader, Obj.PeHeader); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + PeHeader.BaseOfData = Obj.BaseOfData; + + memcpy(Ptr, &PeHeader, sizeof(PeHeader)); + Ptr += sizeof(PeHeader); + } + for (const auto &DD : Obj.DataDirectories) { + memcpy(Ptr, &DD, sizeof(DD)); + Ptr += sizeof(DD); + } + } + for (const auto &S : Obj.getSections()) { + memcpy(Ptr, &S.Header, sizeof(S.Header)); + Ptr += sizeof(S.Header); + } +} + +void COFFWriter::writeSections() { + for (const auto &S : Obj.getSections()) { + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + ArrayRef<uint8_t> Contents = S.getContents(); + std::copy(Contents.begin(), Contents.end(), Ptr); + + // For executable sections, pad the remainder of the raw data size with + // 0xcc, which is int3 on x86. + if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) && + S.Header.SizeOfRawData > Contents.size()) + memset(Ptr + Contents.size(), 0xcc, + S.Header.SizeOfRawData - Contents.size()); + + Ptr += S.Header.SizeOfRawData; + + if (S.Relocs.size() >= 0xffff) { + object::coff_relocation R; + R.VirtualAddress = S.Relocs.size() + 1; + R.SymbolTableIndex = 0; + R.Type = 0; + memcpy(Ptr, &R, sizeof(R)); + Ptr += sizeof(R); + } + for (const auto &R : S.Relocs) { + memcpy(Ptr, &R.Reloc, sizeof(R.Reloc)); + Ptr += sizeof(R.Reloc); + } + } +} + +template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { + uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + for (const auto &S : Obj.getSymbols()) { + // Convert symbols back to the right size, from coff_symbol32. + copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), + S.Sym); + Ptr += sizeof(SymbolTy); + if (!S.AuxFile.empty()) { + // For file symbols, just write the string into the aux symbol slots, + // assuming that the unwritten parts are initialized to zero in the memory + // mapped file. + std::copy(S.AuxFile.begin(), S.AuxFile.end(), Ptr); + Ptr += S.Sym.NumberOfAuxSymbols * sizeof(SymbolTy); + } else { + // For other auxillary symbols, write their opaque payload into one symbol + // table slot each. For big object files, the symbols are larger than the + // opaque auxillary symbol struct and we leave padding at the end of each + // entry. + for (const AuxSymbol &AuxSym : S.AuxData) { + ArrayRef<uint8_t> Ref = AuxSym.getRef(); + std::copy(Ref.begin(), Ref.end(), Ptr); + Ptr += sizeof(SymbolTy); + } + } + } + if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) { + // Always write a string table in object files, even an empty one. + StrTabBuilder.write(Ptr); + Ptr += StrTabBuilder.getSize(); + } +} + +Error COFFWriter::write(bool IsBigObj) { + if (Error E = finalize(IsBigObj)) + return E; + + if (Error E = Buf.allocate(FileSize)) + return E; + + writeHeaders(IsBigObj); + writeSections(); + if (IsBigObj) + writeSymbolStringTables<coff_symbol32>(); + else + writeSymbolStringTables<coff_symbol16>(); + + if (Obj.IsPE) + if (Error E = patchDebugDirectory()) + return E; + + return Buf.commit(); +} + +Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) { + for (const auto &S : Obj.getSections()) { + if (RVA >= S.Header.VirtualAddress && + RVA < S.Header.VirtualAddress + S.Header.SizeOfRawData) + return S.Header.PointerToRawData + RVA - S.Header.VirtualAddress; + } + return createStringError(object_error::parse_failed, + "debug directory payload not found"); +} + +// Locate which sections contain the debug directories, iterate over all +// the debug_directory structs in there, and set the PointerToRawData field +// in all of them, according to their new physical location in the file. +Error COFFWriter::patchDebugDirectory() { + if (Obj.DataDirectories.size() < DEBUG_DIRECTORY) + return Error::success(); + const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY]; + if (Dir->Size <= 0) + return Error::success(); + for (const auto &S : Obj.getSections()) { + if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress && + Dir->RelativeVirtualAddress < + S.Header.VirtualAddress + S.Header.SizeOfRawData) { + if (Dir->RelativeVirtualAddress + Dir->Size > + S.Header.VirtualAddress + S.Header.SizeOfRawData) + return createStringError(object_error::parse_failed, + "debug directory extends past end of section"); + + size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *End = Ptr + Dir->Size; + while (Ptr < End) { + debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); + if (!Debug->AddressOfRawData) + return createStringError(object_error::parse_failed, + "debug directory payload outside of " + "mapped sections not supported"); + if (Expected<uint32_t> FilePosOrErr = + virtualAddressToFileAddress(Debug->AddressOfRawData)) + Debug->PointerToRawData = *FilePosOrErr; + else + return FilePosOrErr.takeError(); + Ptr += sizeof(debug_directory); + Offset += sizeof(debug_directory); + } + // Debug directory found and patched, all done. + return Error::success(); + } + } + return createStringError(object_error::parse_failed, + "debug directory not found"); +} + +Error COFFWriter::write() { + bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16; + if (IsBigObj && Obj.IsPE) + return createStringError(object_error::parse_failed, + "too many sections for executable"); + return write(IsBigObj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.h b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.h index 3c0bdcbd5d..cd6a17984c 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/COFF/Writer.h @@ -1,62 +1,62 @@ -//===- Writer.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H -#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H - -#include "Buffer.h" -#include "llvm/MC/StringTableBuilder.h" -#include "llvm/Support/Error.h" -#include <cstddef> -#include <utility> - -namespace llvm { -namespace objcopy { -namespace coff { - -struct Object; - -class COFFWriter { - Object &Obj; - Buffer &Buf; - - size_t FileSize; - size_t FileAlignment; - size_t SizeOfInitializedData; - StringTableBuilder StrTabBuilder; - - template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); - Error finalizeRelocTargets(); - Error finalizeSymbolContents(); - void layoutSections(); - size_t finalizeStringTable(); - - Error finalize(bool IsBigObj); - - void writeHeaders(bool IsBigObj); - void writeSections(); - template <class SymbolTy> void writeSymbolStringTables(); - - Error write(bool IsBigObj); - - Error patchDebugDirectory(); - Expected<uint32_t> virtualAddressToFileAddress(uint32_t RVA); - -public: - virtual ~COFFWriter() {} - Error write(); - - COFFWriter(Object &Obj, Buffer &Buf) - : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} -}; - -} // end namespace coff -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H +//===- Writer.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H +#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H + +#include "Buffer.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Support/Error.h" +#include <cstddef> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Object; + +class COFFWriter { + Object &Obj; + Buffer &Buf; + + size_t FileSize; + size_t FileAlignment; + size_t SizeOfInitializedData; + StringTableBuilder StrTabBuilder; + + template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); + Error finalizeRelocTargets(); + Error finalizeSymbolContents(); + void layoutSections(); + size_t finalizeStringTable(); + + Error finalize(bool IsBigObj); + + void writeHeaders(bool IsBigObj); + void writeSections(); + template <class SymbolTy> void writeSymbolStringTables(); + + Error write(bool IsBigObj); + + Error patchDebugDirectory(); + Expected<uint32_t> virtualAddressToFileAddress(uint32_t RVA); + +public: + virtual ~COFFWriter() {} + Error write(); + + COFFWriter(Object &Obj, Buffer &Buf) + : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/CommonOpts.td b/contrib/libs/llvm12/tools/llvm-objcopy/CommonOpts.td index 6481d1d1df..8f29e1346d 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/CommonOpts.td +++ b/contrib/libs/llvm12/tools/llvm-objcopy/CommonOpts.td @@ -1,125 +1,125 @@ -include "llvm/Option/OptParser.td" - -multiclass Eq<string name, string help> { - def NAME : Separate<["--"], name>; - def NAME #_eq : Joined<["--"], name #"=">, - Alias<!cast<Separate>(NAME)>, - HelpText<help>; -} - -def help : Flag<["--"], "help">; -def h : Flag<["-"], "h">, Alias<help>; - -def allow_broken_links - : Flag<["--"], "allow-broken-links">, - HelpText<"Allow the tool to remove sections even if it would leave " - "invalid section references. The appropriate sh_link fields " - "will be set to zero.">; - -def enable_deterministic_archives - : Flag<["--"], "enable-deterministic-archives">, - HelpText<"Enable deterministic mode when operating on archives (use " - "zero for UIDs, GIDs, and timestamps).">; -def D : Flag<["-"], "D">, - Alias<enable_deterministic_archives>, - HelpText<"Alias for --enable-deterministic-archives">; - -def disable_deterministic_archives - : Flag<["--"], "disable-deterministic-archives">, - HelpText<"Disable deterministic mode when operating on archives (use " - "real values for UIDs, GIDs, and timestamps).">; -def U : Flag<["-"], "U">, - Alias<disable_deterministic_archives>, - HelpText<"Alias for --disable-deterministic-archives">; - -def preserve_dates : Flag<["--"], "preserve-dates">, - HelpText<"Preserve access and modification timestamps">; -def p : Flag<["-"], "p">, - Alias<preserve_dates>, - HelpText<"Alias for --preserve-dates">; - -def strip_all : Flag<["--"], "strip-all">, - HelpText<"Remove non-allocated sections outside segments. " - ".gnu.warning* and .ARM.attribute sections are not " - "removed">; - -def strip_all_gnu - : Flag<["--"], "strip-all-gnu">, - HelpText<"Compatible with GNU's --strip-all">; - -def strip_debug : Flag<["--"], "strip-debug">, - HelpText<"Remove all debug sections">; -def g : Flag<["-"], "g">, - Alias<strip_debug>, - HelpText<"Alias for --strip-debug">; - -def strip_unneeded : Flag<["--"], "strip-unneeded">, - HelpText<"Remove all symbols not needed by relocations">; - -defm remove_section : Eq<"remove-section", "Remove <section>">, - MetaVarName<"section">; -def R : JoinedOrSeparate<["-"], "R">, - Alias<remove_section>, - HelpText<"Alias for --remove-section">; - -def strip_sections - : Flag<["--"], "strip-sections">, - HelpText<"Remove all section headers and all sections not in segments">; - -defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">, - MetaVarName<"symbol">; -def N : JoinedOrSeparate<["-"], "N">, - Alias<strip_symbol>, - HelpText<"Alias for --strip-symbol">; - -defm keep_section : Eq<"keep-section", "Keep <section>">, - MetaVarName<"section">; - -defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, - MetaVarName<"symbol">; -def K : JoinedOrSeparate<["-"], "K">, - Alias<keep_symbol>, - HelpText<"Alias for --keep-symbol">; - -def keep_file_symbols : Flag<["--"], "keep-file-symbols">, - HelpText<"Do not remove file symbols">; - -def only_keep_debug - : Flag<["--"], "only-keep-debug">, - HelpText< - "Produce a debug file as the output that only preserves contents of " - "sections useful for debugging purposes">; - -def discard_locals : Flag<["--"], "discard-locals">, - HelpText<"Remove compiler-generated local symbols, (e.g. " - "symbols starting with .L)">; -def X : Flag<["-"], "X">, - Alias<discard_locals>, - HelpText<"Alias for --discard-locals">; - -def discard_all - : Flag<["--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; -def x : Flag<["-"], "x">, - Alias<discard_all>, - HelpText<"Alias for --discard-all">; - -def regex - : Flag<["--"], "regex">, - HelpText<"Permit regular expressions in name comparison">; - -def version : Flag<["--"], "version">, - HelpText<"Print the version and exit.">; -def V : Flag<["-"], "V">, - Alias<version>, - HelpText<"Alias for --version">; - -def wildcard - : Flag<["--"], "wildcard">, - HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible " - "with --regex. Allows using '*' to match any number of " - "characters, '?' to match any single character, '\' to escape " - "special characters, and '[]' to define character classes. " - "Wildcards beginning with '!' will prevent a match, for example " - "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">; -def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">; +include "llvm/Option/OptParser.td" + +multiclass Eq<string name, string help> { + def NAME : Separate<["--"], name>; + def NAME #_eq : Joined<["--"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; +} + +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def allow_broken_links + : Flag<["--"], "allow-broken-links">, + HelpText<"Allow the tool to remove sections even if it would leave " + "invalid section references. The appropriate sh_link fields " + "will be set to zero.">; + +def enable_deterministic_archives + : Flag<["--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when operating on archives (use " + "zero for UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; + +def disable_deterministic_archives + : Flag<["--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when operating on archives (use " + "real values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; + +def preserve_dates : Flag<["--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, + Alias<preserve_dates>, + HelpText<"Alias for --preserve-dates">; + +def strip_all : Flag<["--"], "strip-all">, + HelpText<"Remove non-allocated sections outside segments. " + ".gnu.warning* and .ARM.attribute sections are not " + "removed">; + +def strip_all_gnu + : Flag<["--"], "strip-all-gnu">, + HelpText<"Compatible with GNU's --strip-all">; + +def strip_debug : Flag<["--"], "strip-debug">, + HelpText<"Remove all debug sections">; +def g : Flag<["-"], "g">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; + +def strip_unneeded : Flag<["--"], "strip-unneeded">, + HelpText<"Remove all symbols not needed by relocations">; + +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, + Alias<remove_section>, + HelpText<"Alias for --remove-section">; + +def strip_sections + : Flag<["--"], "strip-sections">, + HelpText<"Remove all section headers and all sections not in segments">; + +defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">, + MetaVarName<"symbol">; +def N : JoinedOrSeparate<["-"], "N">, + Alias<strip_symbol>, + HelpText<"Alias for --strip-symbol">; + +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; + +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"symbol">; +def K : JoinedOrSeparate<["-"], "K">, + Alias<keep_symbol>, + HelpText<"Alias for --keep-symbol">; + +def keep_file_symbols : Flag<["--"], "keep-file-symbols">, + HelpText<"Do not remove file symbols">; + +def only_keep_debug + : Flag<["--"], "only-keep-debug">, + HelpText< + "Produce a debug file as the output that only preserves contents of " + "sections useful for debugging purposes">; + +def discard_locals : Flag<["--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, + Alias<discard_locals>, + HelpText<"Alias for --discard-locals">; + +def discard_all + : Flag<["--"], "discard-all">, + HelpText<"Remove all local symbols except file and section symbols">; +def x : Flag<["-"], "x">, + Alias<discard_all>, + HelpText<"Alias for --discard-all">; + +def regex + : Flag<["--"], "regex">, + HelpText<"Permit regular expressions in name comparison">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, + Alias<version>, + HelpText<"Alias for --version">; + +def wildcard + : Flag<["--"], "wildcard">, + HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible " + "with --regex. Allows using '*' to match any number of " + "characters, '?' to match any single character, '\' to escape " + "special characters, and '[]' to define character classes. " + "Wildcards beginning with '!' will prevent a match, for example " + "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">; +def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">; diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.cpp index ba74759a34..8fca9b62cd 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.cpp @@ -1,106 +1,106 @@ -//===- CopyConfig.cpp -----------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "CopyConfig.h" - -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSet.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Support/CRC.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compression.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/StringSaver.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -namespace { -enum ObjcopyID { - OBJCOPY_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - OBJCOPY_##ID, -#include "ObjcopyOpts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE; -#include "ObjcopyOpts.inc" -#undef PREFIX - -static const opt::OptTable::Info ObjcopyInfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - {OBJCOPY_##PREFIX, \ - NAME, \ - HELPTEXT, \ - METAVAR, \ - OBJCOPY_##ID, \ - opt::Option::KIND##Class, \ - PARAM, \ - FLAGS, \ - OBJCOPY_##GROUP, \ - OBJCOPY_##ALIAS, \ - ALIASARGS, \ - VALUES}, -#include "ObjcopyOpts.inc" -#undef OPTION -}; - -class ObjcopyOptTable : public opt::OptTable { -public: - ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} -}; - -enum InstallNameToolID { - INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - INSTALL_NAME_TOOL_##ID, -#include "InstallNameToolOpts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) \ - const char *const INSTALL_NAME_TOOL_##NAME[] = VALUE; -#include "InstallNameToolOpts.inc" -#undef PREFIX - -static const opt::OptTable::Info InstallNameToolInfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - {INSTALL_NAME_TOOL_##PREFIX, \ - NAME, \ - HELPTEXT, \ - METAVAR, \ - INSTALL_NAME_TOOL_##ID, \ - opt::Option::KIND##Class, \ - PARAM, \ - FLAGS, \ - INSTALL_NAME_TOOL_##GROUP, \ - INSTALL_NAME_TOOL_##ALIAS, \ - ALIASARGS, \ - VALUES}, -#include "InstallNameToolOpts.inc" -#undef OPTION -}; - -class InstallNameToolOptTable : public opt::OptTable { -public: - InstallNameToolOptTable() : OptTable(InstallNameToolInfoTable) {} -}; - +//===- CopyConfig.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "CopyConfig.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/StringSaver.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +namespace { +enum ObjcopyID { + OBJCOPY_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OBJCOPY_##ID, +#include "ObjcopyOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE; +#include "ObjcopyOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info ObjcopyInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {OBJCOPY_##PREFIX, \ + NAME, \ + HELPTEXT, \ + METAVAR, \ + OBJCOPY_##ID, \ + opt::Option::KIND##Class, \ + PARAM, \ + FLAGS, \ + OBJCOPY_##GROUP, \ + OBJCOPY_##ALIAS, \ + ALIASARGS, \ + VALUES}, +#include "ObjcopyOpts.inc" +#undef OPTION +}; + +class ObjcopyOptTable : public opt::OptTable { +public: + ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} +}; + +enum InstallNameToolID { + INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + INSTALL_NAME_TOOL_##ID, +#include "InstallNameToolOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) \ + const char *const INSTALL_NAME_TOOL_##NAME[] = VALUE; +#include "InstallNameToolOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info InstallNameToolInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {INSTALL_NAME_TOOL_##PREFIX, \ + NAME, \ + HELPTEXT, \ + METAVAR, \ + INSTALL_NAME_TOOL_##ID, \ + opt::Option::KIND##Class, \ + PARAM, \ + FLAGS, \ + INSTALL_NAME_TOOL_##GROUP, \ + INSTALL_NAME_TOOL_##ALIAS, \ + ALIASARGS, \ + VALUES}, +#include "InstallNameToolOpts.inc" +#undef OPTION +}; + +class InstallNameToolOptTable : public opt::OptTable { +public: + InstallNameToolOptTable() : OptTable(InstallNameToolInfoTable) {} +}; + enum BitcodeStripID { BITCODE_STRIP_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ @@ -138,816 +138,816 @@ public: BitcodeStripOptTable() : OptTable(BitcodeStripInfoTable) {} }; -enum StripID { - STRIP_INVALID = 0, // This is not an option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - STRIP_##ID, -#include "StripOpts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE; -#include "StripOpts.inc" -#undef PREFIX - -static const opt::OptTable::Info StripInfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - {STRIP_##PREFIX, NAME, HELPTEXT, \ - METAVAR, STRIP_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, STRIP_##GROUP, \ - STRIP_##ALIAS, ALIASARGS, VALUES}, -#include "StripOpts.inc" -#undef OPTION -}; - -class StripOptTable : public opt::OptTable { -public: - StripOptTable() : OptTable(StripInfoTable) {} -}; - -} // namespace - -static SectionFlag parseSectionRenameFlag(StringRef SectionName) { - return llvm::StringSwitch<SectionFlag>(SectionName) - .CaseLower("alloc", SectionFlag::SecAlloc) - .CaseLower("load", SectionFlag::SecLoad) - .CaseLower("noload", SectionFlag::SecNoload) - .CaseLower("readonly", SectionFlag::SecReadonly) - .CaseLower("debug", SectionFlag::SecDebug) - .CaseLower("code", SectionFlag::SecCode) - .CaseLower("data", SectionFlag::SecData) - .CaseLower("rom", SectionFlag::SecRom) - .CaseLower("merge", SectionFlag::SecMerge) - .CaseLower("strings", SectionFlag::SecStrings) - .CaseLower("contents", SectionFlag::SecContents) - .CaseLower("share", SectionFlag::SecShare) - .CaseLower("exclude", SectionFlag::SecExclude) - .Default(SectionFlag::SecNone); -} - -static Expected<SectionFlag> -parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) { - SectionFlag ParsedFlags = SectionFlag::SecNone; - for (StringRef Flag : SectionFlags) { - SectionFlag ParsedFlag = parseSectionRenameFlag(Flag); - if (ParsedFlag == SectionFlag::SecNone) - return createStringError( - errc::invalid_argument, - "unrecognized section flag '%s'. Flags supported for GNU " - "compatibility: alloc, load, noload, readonly, exclude, debug, " - "code, data, rom, share, contents, merge, strings", - Flag.str().c_str()); - ParsedFlags |= ParsedFlag; - } - - return ParsedFlags; -} - -static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) { - if (!FlagValue.contains('=')) - return createStringError(errc::invalid_argument, - "bad format for --rename-section: missing '='"); - - // Initial split: ".foo" = ".bar,f1,f2,..." - auto Old2New = FlagValue.split('='); - SectionRename SR; - SR.OriginalName = Old2New.first; - - // Flags split: ".bar" "f1" "f2" ... - SmallVector<StringRef, 6> NameAndFlags; - Old2New.second.split(NameAndFlags, ','); - SR.NewName = NameAndFlags[0]; - - if (NameAndFlags.size() > 1) { - Expected<SectionFlag> ParsedFlagSet = - parseSectionFlagSet(makeArrayRef(NameAndFlags).drop_front()); - if (!ParsedFlagSet) - return ParsedFlagSet.takeError(); - SR.NewFlags = *ParsedFlagSet; - } - - return SR; -} - -static Expected<std::pair<StringRef, uint64_t>> -parseSetSectionAlignment(StringRef FlagValue) { - if (!FlagValue.contains('=')) - return createStringError( - errc::invalid_argument, - "bad format for --set-section-alignment: missing '='"); - auto Split = StringRef(FlagValue).split('='); - if (Split.first.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --set-section-alignment: missing section name"); - uint64_t NewAlign; - if (Split.second.getAsInteger(0, NewAlign)) - return createStringError(errc::invalid_argument, - "invalid alignment for --set-section-alignment: '%s'", - Split.second.str().c_str()); - return std::make_pair(Split.first, NewAlign); -} - -static Expected<SectionFlagsUpdate> -parseSetSectionFlagValue(StringRef FlagValue) { - if (!StringRef(FlagValue).contains('=')) - return createStringError(errc::invalid_argument, - "bad format for --set-section-flags: missing '='"); - - // Initial split: ".foo" = "f1,f2,..." - auto Section2Flags = StringRef(FlagValue).split('='); - SectionFlagsUpdate SFU; - SFU.Name = Section2Flags.first; - - // Flags split: "f1" "f2" ... - SmallVector<StringRef, 6> SectionFlags; - Section2Flags.second.split(SectionFlags, ','); - Expected<SectionFlag> ParsedFlagSet = parseSectionFlagSet(SectionFlags); - if (!ParsedFlagSet) - return ParsedFlagSet.takeError(); - SFU.NewFlags = *ParsedFlagSet; - - return SFU; -} - -struct TargetInfo { - FileFormat Format; - MachineInfo Machine; -}; - -// FIXME: consolidate with the bfd parsing used by lld. -static const StringMap<MachineInfo> TargetMap{ - // Name, {EMachine, 64bit, LittleEndian} - // x86 - {"elf32-i386", {ELF::EM_386, false, true}}, - {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, - {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, - // Intel MCU - {"elf32-iamcu", {ELF::EM_IAMCU, false, true}}, - // ARM - {"elf32-littlearm", {ELF::EM_ARM, false, true}}, - // ARM AArch64 - {"elf64-aarch64", {ELF::EM_AARCH64, true, true}}, - {"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}}, - // RISC-V - {"elf32-littleriscv", {ELF::EM_RISCV, false, true}}, - {"elf64-littleriscv", {ELF::EM_RISCV, true, true}}, - // PowerPC - {"elf32-powerpc", {ELF::EM_PPC, false, false}}, - {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, - {"elf64-powerpc", {ELF::EM_PPC64, true, false}}, - {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, - // MIPS - {"elf32-bigmips", {ELF::EM_MIPS, false, false}}, - {"elf32-ntradbigmips", {ELF::EM_MIPS, false, false}}, - {"elf32-ntradlittlemips", {ELF::EM_MIPS, false, true}}, - {"elf32-tradbigmips", {ELF::EM_MIPS, false, false}}, - {"elf32-tradlittlemips", {ELF::EM_MIPS, false, true}}, - {"elf64-tradbigmips", {ELF::EM_MIPS, true, false}}, - {"elf64-tradlittlemips", {ELF::EM_MIPS, true, true}}, - // SPARC - {"elf32-sparc", {ELF::EM_SPARC, false, false}}, - {"elf32-sparcel", {ELF::EM_SPARC, false, true}}, - {"elf32-hexagon", {ELF::EM_HEXAGON, false, true}}, -}; - -static Expected<TargetInfo> -getOutputTargetInfoByTargetName(StringRef TargetName) { - StringRef OriginalTargetName = TargetName; - bool IsFreeBSD = TargetName.consume_back("-freebsd"); - auto Iter = TargetMap.find(TargetName); - if (Iter == std::end(TargetMap)) - return createStringError(errc::invalid_argument, - "invalid output format: '%s'", - OriginalTargetName.str().c_str()); - MachineInfo MI = Iter->getValue(); - if (IsFreeBSD) - MI.OSABI = ELF::ELFOSABI_FREEBSD; - - FileFormat Format; - if (TargetName.startswith("elf")) - Format = FileFormat::ELF; - else - // This should never happen because `TargetName` is valid (it certainly - // exists in the TargetMap). - llvm_unreachable("unknown target prefix"); - - return {TargetInfo{Format, MI}}; -} - -static Error -addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, - StringRef Filename, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback) { - StringSaver Saver(Alloc); - SmallVector<StringRef, 16> Lines; - auto BufOrErr = MemoryBuffer::getFile(Filename); - if (!BufOrErr) - return createFileError(Filename, BufOrErr.getError()); - - BufOrErr.get()->getBuffer().split(Lines, '\n'); - for (StringRef Line : Lines) { - // Ignore everything after '#', trim whitespace, and only add the symbol if - // it's not empty. - auto TrimmedLine = Line.split('#').first.trim(); - if (!TrimmedLine.empty()) - if (Error E = Symbols.addMatcher(NameOrPattern::create( - Saver.save(TrimmedLine), MS, ErrorCallback))) - return E; - } - - return Error::success(); -} - -Expected<NameOrPattern> -NameOrPattern::create(StringRef Pattern, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback) { - switch (MS) { - case MatchStyle::Literal: - return NameOrPattern(Pattern); - case MatchStyle::Wildcard: { - SmallVector<char, 32> Data; - bool IsPositiveMatch = true; - if (Pattern[0] == '!') { - IsPositiveMatch = false; - Pattern = Pattern.drop_front(); - } - Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern); - - // If we couldn't create it as a glob, report the error, but try again with - // a literal if the error reporting is non-fatal. - if (!GlobOrErr) { - if (Error E = ErrorCallback(GlobOrErr.takeError())) - return std::move(E); - return create(Pattern, MatchStyle::Literal, ErrorCallback); - } - - return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr), - IsPositiveMatch); - } - case MatchStyle::Regex: { - SmallVector<char, 32> Data; - return NameOrPattern(std::make_shared<Regex>( - ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data))); - } - } - llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum"); -} - -static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename, - BumpPtrAllocator &Alloc, - StringRef Filename) { - StringSaver Saver(Alloc); - SmallVector<StringRef, 16> Lines; - auto BufOrErr = MemoryBuffer::getFile(Filename); - if (!BufOrErr) - return createFileError(Filename, BufOrErr.getError()); - - BufOrErr.get()->getBuffer().split(Lines, '\n'); - size_t NumLines = Lines.size(); - for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) { - StringRef TrimmedLine = Lines[LineNo].split('#').first.trim(); - if (TrimmedLine.empty()) - continue; - - std::pair<StringRef, StringRef> Pair = Saver.save(TrimmedLine).split(' '); - StringRef NewName = Pair.second.trim(); - if (NewName.empty()) - return createStringError(errc::invalid_argument, - "%s:%zu: missing new symbol name", - Filename.str().c_str(), LineNo + 1); - SymbolsToRename.insert({Pair.first, NewName}); - } - return Error::success(); -} - -template <class T> static ErrorOr<T> getAsInteger(StringRef Val) { - T Result; - if (Val.getAsInteger(0, Result)) - return errc::invalid_argument; - return Result; -} - -namespace { - +enum StripID { + STRIP_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + STRIP_##ID, +#include "StripOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE; +#include "StripOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info StripInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {STRIP_##PREFIX, NAME, HELPTEXT, \ + METAVAR, STRIP_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, STRIP_##GROUP, \ + STRIP_##ALIAS, ALIASARGS, VALUES}, +#include "StripOpts.inc" +#undef OPTION +}; + +class StripOptTable : public opt::OptTable { +public: + StripOptTable() : OptTable(StripInfoTable) {} +}; + +} // namespace + +static SectionFlag parseSectionRenameFlag(StringRef SectionName) { + return llvm::StringSwitch<SectionFlag>(SectionName) + .CaseLower("alloc", SectionFlag::SecAlloc) + .CaseLower("load", SectionFlag::SecLoad) + .CaseLower("noload", SectionFlag::SecNoload) + .CaseLower("readonly", SectionFlag::SecReadonly) + .CaseLower("debug", SectionFlag::SecDebug) + .CaseLower("code", SectionFlag::SecCode) + .CaseLower("data", SectionFlag::SecData) + .CaseLower("rom", SectionFlag::SecRom) + .CaseLower("merge", SectionFlag::SecMerge) + .CaseLower("strings", SectionFlag::SecStrings) + .CaseLower("contents", SectionFlag::SecContents) + .CaseLower("share", SectionFlag::SecShare) + .CaseLower("exclude", SectionFlag::SecExclude) + .Default(SectionFlag::SecNone); +} + +static Expected<SectionFlag> +parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) { + SectionFlag ParsedFlags = SectionFlag::SecNone; + for (StringRef Flag : SectionFlags) { + SectionFlag ParsedFlag = parseSectionRenameFlag(Flag); + if (ParsedFlag == SectionFlag::SecNone) + return createStringError( + errc::invalid_argument, + "unrecognized section flag '%s'. Flags supported for GNU " + "compatibility: alloc, load, noload, readonly, exclude, debug, " + "code, data, rom, share, contents, merge, strings", + Flag.str().c_str()); + ParsedFlags |= ParsedFlag; + } + + return ParsedFlags; +} + +static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) { + if (!FlagValue.contains('=')) + return createStringError(errc::invalid_argument, + "bad format for --rename-section: missing '='"); + + // Initial split: ".foo" = ".bar,f1,f2,..." + auto Old2New = FlagValue.split('='); + SectionRename SR; + SR.OriginalName = Old2New.first; + + // Flags split: ".bar" "f1" "f2" ... + SmallVector<StringRef, 6> NameAndFlags; + Old2New.second.split(NameAndFlags, ','); + SR.NewName = NameAndFlags[0]; + + if (NameAndFlags.size() > 1) { + Expected<SectionFlag> ParsedFlagSet = + parseSectionFlagSet(makeArrayRef(NameAndFlags).drop_front()); + if (!ParsedFlagSet) + return ParsedFlagSet.takeError(); + SR.NewFlags = *ParsedFlagSet; + } + + return SR; +} + +static Expected<std::pair<StringRef, uint64_t>> +parseSetSectionAlignment(StringRef FlagValue) { + if (!FlagValue.contains('=')) + return createStringError( + errc::invalid_argument, + "bad format for --set-section-alignment: missing '='"); + auto Split = StringRef(FlagValue).split('='); + if (Split.first.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --set-section-alignment: missing section name"); + uint64_t NewAlign; + if (Split.second.getAsInteger(0, NewAlign)) + return createStringError(errc::invalid_argument, + "invalid alignment for --set-section-alignment: '%s'", + Split.second.str().c_str()); + return std::make_pair(Split.first, NewAlign); +} + +static Expected<SectionFlagsUpdate> +parseSetSectionFlagValue(StringRef FlagValue) { + if (!StringRef(FlagValue).contains('=')) + return createStringError(errc::invalid_argument, + "bad format for --set-section-flags: missing '='"); + + // Initial split: ".foo" = "f1,f2,..." + auto Section2Flags = StringRef(FlagValue).split('='); + SectionFlagsUpdate SFU; + SFU.Name = Section2Flags.first; + + // Flags split: "f1" "f2" ... + SmallVector<StringRef, 6> SectionFlags; + Section2Flags.second.split(SectionFlags, ','); + Expected<SectionFlag> ParsedFlagSet = parseSectionFlagSet(SectionFlags); + if (!ParsedFlagSet) + return ParsedFlagSet.takeError(); + SFU.NewFlags = *ParsedFlagSet; + + return SFU; +} + +struct TargetInfo { + FileFormat Format; + MachineInfo Machine; +}; + +// FIXME: consolidate with the bfd parsing used by lld. +static const StringMap<MachineInfo> TargetMap{ + // Name, {EMachine, 64bit, LittleEndian} + // x86 + {"elf32-i386", {ELF::EM_386, false, true}}, + {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, + {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, + // Intel MCU + {"elf32-iamcu", {ELF::EM_IAMCU, false, true}}, + // ARM + {"elf32-littlearm", {ELF::EM_ARM, false, true}}, + // ARM AArch64 + {"elf64-aarch64", {ELF::EM_AARCH64, true, true}}, + {"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}}, + // RISC-V + {"elf32-littleriscv", {ELF::EM_RISCV, false, true}}, + {"elf64-littleriscv", {ELF::EM_RISCV, true, true}}, + // PowerPC + {"elf32-powerpc", {ELF::EM_PPC, false, false}}, + {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, + {"elf64-powerpc", {ELF::EM_PPC64, true, false}}, + {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, + // MIPS + {"elf32-bigmips", {ELF::EM_MIPS, false, false}}, + {"elf32-ntradbigmips", {ELF::EM_MIPS, false, false}}, + {"elf32-ntradlittlemips", {ELF::EM_MIPS, false, true}}, + {"elf32-tradbigmips", {ELF::EM_MIPS, false, false}}, + {"elf32-tradlittlemips", {ELF::EM_MIPS, false, true}}, + {"elf64-tradbigmips", {ELF::EM_MIPS, true, false}}, + {"elf64-tradlittlemips", {ELF::EM_MIPS, true, true}}, + // SPARC + {"elf32-sparc", {ELF::EM_SPARC, false, false}}, + {"elf32-sparcel", {ELF::EM_SPARC, false, true}}, + {"elf32-hexagon", {ELF::EM_HEXAGON, false, true}}, +}; + +static Expected<TargetInfo> +getOutputTargetInfoByTargetName(StringRef TargetName) { + StringRef OriginalTargetName = TargetName; + bool IsFreeBSD = TargetName.consume_back("-freebsd"); + auto Iter = TargetMap.find(TargetName); + if (Iter == std::end(TargetMap)) + return createStringError(errc::invalid_argument, + "invalid output format: '%s'", + OriginalTargetName.str().c_str()); + MachineInfo MI = Iter->getValue(); + if (IsFreeBSD) + MI.OSABI = ELF::ELFOSABI_FREEBSD; + + FileFormat Format; + if (TargetName.startswith("elf")) + Format = FileFormat::ELF; + else + // This should never happen because `TargetName` is valid (it certainly + // exists in the TargetMap). + llvm_unreachable("unknown target prefix"); + + return {TargetInfo{Format, MI}}; +} + +static Error +addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, + StringRef Filename, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback) { + StringSaver Saver(Alloc); + SmallVector<StringRef, 16> Lines; + auto BufOrErr = MemoryBuffer::getFile(Filename); + if (!BufOrErr) + return createFileError(Filename, BufOrErr.getError()); + + BufOrErr.get()->getBuffer().split(Lines, '\n'); + for (StringRef Line : Lines) { + // Ignore everything after '#', trim whitespace, and only add the symbol if + // it's not empty. + auto TrimmedLine = Line.split('#').first.trim(); + if (!TrimmedLine.empty()) + if (Error E = Symbols.addMatcher(NameOrPattern::create( + Saver.save(TrimmedLine), MS, ErrorCallback))) + return E; + } + + return Error::success(); +} + +Expected<NameOrPattern> +NameOrPattern::create(StringRef Pattern, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback) { + switch (MS) { + case MatchStyle::Literal: + return NameOrPattern(Pattern); + case MatchStyle::Wildcard: { + SmallVector<char, 32> Data; + bool IsPositiveMatch = true; + if (Pattern[0] == '!') { + IsPositiveMatch = false; + Pattern = Pattern.drop_front(); + } + Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern); + + // If we couldn't create it as a glob, report the error, but try again with + // a literal if the error reporting is non-fatal. + if (!GlobOrErr) { + if (Error E = ErrorCallback(GlobOrErr.takeError())) + return std::move(E); + return create(Pattern, MatchStyle::Literal, ErrorCallback); + } + + return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr), + IsPositiveMatch); + } + case MatchStyle::Regex: { + SmallVector<char, 32> Data; + return NameOrPattern(std::make_shared<Regex>( + ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data))); + } + } + llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum"); +} + +static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename, + BumpPtrAllocator &Alloc, + StringRef Filename) { + StringSaver Saver(Alloc); + SmallVector<StringRef, 16> Lines; + auto BufOrErr = MemoryBuffer::getFile(Filename); + if (!BufOrErr) + return createFileError(Filename, BufOrErr.getError()); + + BufOrErr.get()->getBuffer().split(Lines, '\n'); + size_t NumLines = Lines.size(); + for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) { + StringRef TrimmedLine = Lines[LineNo].split('#').first.trim(); + if (TrimmedLine.empty()) + continue; + + std::pair<StringRef, StringRef> Pair = Saver.save(TrimmedLine).split(' '); + StringRef NewName = Pair.second.trim(); + if (NewName.empty()) + return createStringError(errc::invalid_argument, + "%s:%zu: missing new symbol name", + Filename.str().c_str(), LineNo + 1); + SymbolsToRename.insert({Pair.first, NewName}); + } + return Error::success(); +} + +template <class T> static ErrorOr<T> getAsInteger(StringRef Val) { + T Result; + if (Val.getAsInteger(0, Result)) + return errc::invalid_argument; + return Result; +} + +namespace { + enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip }; - -} // anonymous namespace - -static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, - ToolType Tool) { - StringRef HelpText, ToolName; - switch (Tool) { - case ToolType::Objcopy: - ToolName = "llvm-objcopy"; - HelpText = " [options] input [output]"; - break; - case ToolType::Strip: - ToolName = "llvm-strip"; - HelpText = " [options] inputs..."; - break; - case ToolType::InstallNameTool: - ToolName = "llvm-install-name-tool"; - HelpText = " [options] input"; - break; + +} // anonymous namespace + +static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, + ToolType Tool) { + StringRef HelpText, ToolName; + switch (Tool) { + case ToolType::Objcopy: + ToolName = "llvm-objcopy"; + HelpText = " [options] input [output]"; + break; + case ToolType::Strip: + ToolName = "llvm-strip"; + HelpText = " [options] inputs..."; + break; + case ToolType::InstallNameTool: + ToolName = "llvm-install-name-tool"; + HelpText = " [options] input"; + break; case ToolType::BitcodeStrip: ToolName = "llvm-bitcode-strip"; HelpText = " [options] input"; break; - } - OptTable.PrintHelp(OS, (ToolName + HelpText).str().c_str(), - (ToolName + " tool").str().c_str()); - // TODO: Replace this with libOption call once it adds extrahelp support. - // The CommandLine library has a cl::extrahelp class to support this, - // but libOption does not have that yet. - OS << "\nPass @FILE as argument to read options from FILE.\n"; -} - -// ParseObjcopyOptions returns the config and sets the input arguments. If a -// help flag is set then ParseObjcopyOptions will print the help messege and -// exit. -Expected<DriverConfig> -parseObjcopyOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback) { - DriverConfig DC; - ObjcopyOptTable T; - unsigned MissingArgumentIndex, MissingArgumentCount; - llvm::opt::InputArgList InputArgs = - T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - - if (InputArgs.size() == 0) { - printHelp(T, errs(), ToolType::Objcopy); - exit(1); - } - - if (InputArgs.hasArg(OBJCOPY_help)) { - printHelp(T, outs(), ToolType::Objcopy); - exit(0); - } - - if (InputArgs.hasArg(OBJCOPY_version)) { - outs() << "llvm-objcopy, compatible with GNU objcopy\n"; - cl::PrintVersionMessage(); - exit(0); - } - - SmallVector<const char *, 2> Positional; - - for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) - return createStringError(errc::invalid_argument, "unknown argument '%s'", - Arg->getAsString(InputArgs).c_str()); - - for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) - Positional.push_back(Arg->getValue()); - - if (Positional.empty()) - return createStringError(errc::invalid_argument, "no input file specified"); - - if (Positional.size() > 2) - return createStringError(errc::invalid_argument, - "too many positional arguments"); - - CopyConfig Config; - Config.InputFilename = Positional[0]; - Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; - if (InputArgs.hasArg(OBJCOPY_target) && - (InputArgs.hasArg(OBJCOPY_input_target) || - InputArgs.hasArg(OBJCOPY_output_target))) - return createStringError( - errc::invalid_argument, - "--target cannot be used with --input-target or --output-target"); - - if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard)) - return createStringError(errc::invalid_argument, - "--regex and --wildcard are incompatible"); - - MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex) - ? MatchStyle::Regex - : MatchStyle::Wildcard; - MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex) - ? MatchStyle::Regex - : InputArgs.hasArg(OBJCOPY_wildcard) - ? MatchStyle::Wildcard - : MatchStyle::Literal; - StringRef InputFormat, OutputFormat; - if (InputArgs.hasArg(OBJCOPY_target)) { - InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); - OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); - } else { - InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); - OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); - } - - // FIXME: Currently, we ignore the target for non-binary/ihex formats - // explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the - // format by llvm::object::createBinary regardless of the option value. - Config.InputFormat = StringSwitch<FileFormat>(InputFormat) - .Case("binary", FileFormat::Binary) - .Case("ihex", FileFormat::IHex) - .Default(FileFormat::Unspecified); - - if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) - Config.NewSymbolVisibility = - InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); - - Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) - .Case("binary", FileFormat::Binary) - .Case("ihex", FileFormat::IHex) - .Default(FileFormat::Unspecified); - if (Config.OutputFormat == FileFormat::Unspecified) { - if (OutputFormat.empty()) { - Config.OutputFormat = Config.InputFormat; - } else { - Expected<TargetInfo> Target = - getOutputTargetInfoByTargetName(OutputFormat); - if (!Target) - return Target.takeError(); - Config.OutputFormat = Target->Format; - Config.OutputArch = Target->Machine; - } - } - - if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, - OBJCOPY_compress_debug_sections_eq)) { - Config.CompressionType = DebugCompressionType::Z; - - if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) { - Config.CompressionType = - StringSwitch<DebugCompressionType>( - InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)) - .Case("zlib-gnu", DebugCompressionType::GNU) - .Case("zlib", DebugCompressionType::Z) - .Default(DebugCompressionType::None); - if (Config.CompressionType == DebugCompressionType::None) - return createStringError( - errc::invalid_argument, - "invalid or unsupported --compress-debug-sections format: %s", - InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq) - .str() - .c_str()); - } - if (!zlib::isAvailable()) - return createStringError( - errc::invalid_argument, - "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); - } - - Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); - // The gnu_debuglink's target is expected to not change or else its CRC would - // become invalidated and get rejected. We can avoid recalculating the - // checksum for every target file inside an archive by precomputing the CRC - // here. This prevents a significant amount of I/O. - if (!Config.AddGnuDebugLink.empty()) { - auto DebugOrErr = MemoryBuffer::getFile(Config.AddGnuDebugLink); - if (!DebugOrErr) - return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError()); - auto Debug = std::move(*DebugOrErr); - Config.GnuDebugLinkCRC32 = - llvm::crc32(arrayRefFromStringRef(Debug->getBuffer())); - } - Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); - if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) - Config.BuildIdLinkInput = - InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); - if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) - Config.BuildIdLinkOutput = - InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); - Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); - Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); - Config.AllocSectionsPrefix = - InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections); - if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) - Config.ExtractPartition = Arg->getValue(); - - for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { - if (!StringRef(Arg->getValue()).contains('=')) - return createStringError(errc::invalid_argument, - "bad format for --redefine-sym"); - auto Old2New = StringRef(Arg->getValue()).split('='); - if (!Config.SymbolsToRename.insert(Old2New).second) - return createStringError(errc::invalid_argument, - "multiple redefinition of symbol '%s'", - Old2New.first.str().c_str()); - } - - for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbols)) - if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc, - Arg->getValue())) - return std::move(E); - - for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { - Expected<SectionRename> SR = - parseRenameSectionValue(StringRef(Arg->getValue())); - if (!SR) - return SR.takeError(); - if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second) - return createStringError(errc::invalid_argument, - "multiple renames of section '%s'", - SR->OriginalName.str().c_str()); - } - for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) { - Expected<std::pair<StringRef, uint64_t>> NameAndAlign = - parseSetSectionAlignment(Arg->getValue()); - if (!NameAndAlign) - return NameAndAlign.takeError(); - Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second; - } - for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { - Expected<SectionFlagsUpdate> SFU = - parseSetSectionFlagValue(Arg->getValue()); - if (!SFU) - return SFU.takeError(); - if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second) - return createStringError( - errc::invalid_argument, - "--set-section-flags set multiple times for section '%s'", - SFU->Name.str().c_str()); - } - // Prohibit combinations of --set-section-flags when the section name is used - // by --rename-section, either as a source or a destination. - for (const auto &E : Config.SectionsToRename) { - const SectionRename &SR = E.second; - if (Config.SetSectionFlags.count(SR.OriginalName)) - return createStringError( - errc::invalid_argument, - "--set-section-flags=%s conflicts with --rename-section=%s=%s", - SR.OriginalName.str().c_str(), SR.OriginalName.str().c_str(), - SR.NewName.str().c_str()); - if (Config.SetSectionFlags.count(SR.NewName)) - return createStringError( - errc::invalid_argument, - "--set-section-flags=%s conflicts with --rename-section=%s=%s", - SR.NewName.str().c_str(), SR.OriginalName.str().c_str(), - SR.NewName.str().c_str()); - } - - for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) - if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( - Arg->getValue(), SectionMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) - if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( - Arg->getValue(), SectionMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) - if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create( - Arg->getValue(), SectionMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) { - StringRef ArgValue(Arg->getValue()); - if (!ArgValue.contains('=')) - return createStringError(errc::invalid_argument, - "bad format for --add-section: missing '='"); - if (ArgValue.split("=").second.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-section: missing file name"); - Config.AddSection.push_back(ArgValue); - } - for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) - Config.DumpSection.push_back(Arg->getValue()); - Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); - Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); - Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); - Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo); - Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections); - Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); - Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); - Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); - Config.ExtractMainPartition = - InputArgs.hasArg(OBJCOPY_extract_main_partition); - Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); - Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); - if (InputArgs.hasArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) - Config.DiscardMode = - InputArgs.hasFlag(OBJCOPY_discard_all, OBJCOPY_discard_locals) - ? DiscardType::All - : DiscardType::Locals; - Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); - Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); - Config.DecompressDebugSections = - InputArgs.hasArg(OBJCOPY_decompress_debug_sections); - if (Config.DiscardMode == DiscardType::All) { - Config.StripDebug = true; - Config.KeepFileSymbols = true; - } - for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) - if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, - Arg->getValue(), SymbolMatchStyle, - ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) - if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, - Arg->getValue(), SymbolMatchStyle, - ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) - if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, - Arg->getValue(), SymbolMatchStyle, - ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) - if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, - Arg->getValue(), SymbolMatchStyle, - ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) - if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, - Arg->getValue(), SymbolMatchStyle, - ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) - if (Error E = - Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) - if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, - Arg->getValue(), SymbolMatchStyle, - ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) - if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) - if (Error E = - addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), - SymbolMatchStyle, ErrorCallback)) - return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) - Config.SymbolsToAdd.push_back(Arg->getValue()); - - Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); - - Config.DeterministicArchives = InputArgs.hasFlag( - OBJCOPY_enable_deterministic_archives, - OBJCOPY_disable_deterministic_archives, /*default=*/true); - - Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); - - if (Config.PreserveDates && - (Config.OutputFilename == "-" || Config.InputFilename == "-")) - return createStringError(errc::invalid_argument, - "--preserve-dates requires a file"); - - for (auto Arg : InputArgs) - if (Arg->getOption().matches(OBJCOPY_set_start)) { - auto EAddr = getAsInteger<uint64_t>(Arg->getValue()); - if (!EAddr) - return createStringError( - EAddr.getError(), "bad entry point address: '%s'", Arg->getValue()); - - Config.EntryExpr = [EAddr](uint64_t) { return *EAddr; }; - } else if (Arg->getOption().matches(OBJCOPY_change_start)) { - auto EIncr = getAsInteger<int64_t>(Arg->getValue()); - if (!EIncr) - return createStringError(EIncr.getError(), - "bad entry point increment: '%s'", - Arg->getValue()); - auto Expr = Config.EntryExpr ? std::move(Config.EntryExpr) - : [](uint64_t A) { return A; }; - Config.EntryExpr = [Expr, EIncr](uint64_t EAddr) { - return Expr(EAddr) + *EIncr; - }; - } - - if (Config.DecompressDebugSections && - Config.CompressionType != DebugCompressionType::None) { - return createStringError( - errc::invalid_argument, - "cannot specify both --compress-debug-sections and " - "--decompress-debug-sections"); - } - - if (Config.DecompressDebugSections && !zlib::isAvailable()) - return createStringError( - errc::invalid_argument, - "LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress"); - - if (Config.ExtractPartition && Config.ExtractMainPartition) - return createStringError(errc::invalid_argument, - "cannot specify --extract-partition together with " - "--extract-main-partition"); - - DC.CopyConfigs.push_back(std::move(Config)); - return std::move(DC); -} - -// ParseInstallNameToolOptions returns the config and sets the input arguments. -// If a help flag is set then ParseInstallNameToolOptions will print the help -// messege and exit. -Expected<DriverConfig> -parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { - DriverConfig DC; - CopyConfig Config; - InstallNameToolOptTable T; - unsigned MissingArgumentIndex, MissingArgumentCount; - llvm::opt::InputArgList InputArgs = - T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - - if (MissingArgumentCount) - return createStringError( - errc::invalid_argument, - "missing argument to " + - StringRef(InputArgs.getArgString(MissingArgumentIndex)) + - " option"); - - if (InputArgs.size() == 0) { - printHelp(T, errs(), ToolType::InstallNameTool); - exit(1); - } - - if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) { - printHelp(T, outs(), ToolType::InstallNameTool); - exit(0); - } - - if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) { - outs() << "llvm-install-name-tool, compatible with cctools " - "install_name_tool\n"; - cl::PrintVersionMessage(); - exit(0); - } - - for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) - Config.RPathToAdd.push_back(Arg->getValue()); - + } + OptTable.PrintHelp(OS, (ToolName + HelpText).str().c_str(), + (ToolName + " tool").str().c_str()); + // TODO: Replace this with libOption call once it adds extrahelp support. + // The CommandLine library has a cl::extrahelp class to support this, + // but libOption does not have that yet. + OS << "\nPass @FILE as argument to read options from FILE.\n"; +} + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback) { + DriverConfig DC; + ObjcopyOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (InputArgs.size() == 0) { + printHelp(T, errs(), ToolType::Objcopy); + exit(1); + } + + if (InputArgs.hasArg(OBJCOPY_help)) { + printHelp(T, outs(), ToolType::Objcopy); + exit(0); + } + + if (InputArgs.hasArg(OBJCOPY_version)) { + outs() << "llvm-objcopy, compatible with GNU objcopy\n"; + cl::PrintVersionMessage(); + exit(0); + } + + SmallVector<const char *, 2> Positional; + + for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) + return createStringError(errc::invalid_argument, "unknown argument '%s'", + Arg->getAsString(InputArgs).c_str()); + + for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) + Positional.push_back(Arg->getValue()); + + if (Positional.empty()) + return createStringError(errc::invalid_argument, "no input file specified"); + + if (Positional.size() > 2) + return createStringError(errc::invalid_argument, + "too many positional arguments"); + + CopyConfig Config; + Config.InputFilename = Positional[0]; + Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; + if (InputArgs.hasArg(OBJCOPY_target) && + (InputArgs.hasArg(OBJCOPY_input_target) || + InputArgs.hasArg(OBJCOPY_output_target))) + return createStringError( + errc::invalid_argument, + "--target cannot be used with --input-target or --output-target"); + + if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard)) + return createStringError(errc::invalid_argument, + "--regex and --wildcard are incompatible"); + + MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex) + ? MatchStyle::Regex + : MatchStyle::Wildcard; + MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex) + ? MatchStyle::Regex + : InputArgs.hasArg(OBJCOPY_wildcard) + ? MatchStyle::Wildcard + : MatchStyle::Literal; + StringRef InputFormat, OutputFormat; + if (InputArgs.hasArg(OBJCOPY_target)) { + InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + } else { + InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); + OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); + } + + // FIXME: Currently, we ignore the target for non-binary/ihex formats + // explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the + // format by llvm::object::createBinary regardless of the option value. + Config.InputFormat = StringSwitch<FileFormat>(InputFormat) + .Case("binary", FileFormat::Binary) + .Case("ihex", FileFormat::IHex) + .Default(FileFormat::Unspecified); + + if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) + Config.NewSymbolVisibility = + InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); + + Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) + .Case("binary", FileFormat::Binary) + .Case("ihex", FileFormat::IHex) + .Default(FileFormat::Unspecified); + if (Config.OutputFormat == FileFormat::Unspecified) { + if (OutputFormat.empty()) { + Config.OutputFormat = Config.InputFormat; + } else { + Expected<TargetInfo> Target = + getOutputTargetInfoByTargetName(OutputFormat); + if (!Target) + return Target.takeError(); + Config.OutputFormat = Target->Format; + Config.OutputArch = Target->Machine; + } + } + + if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, + OBJCOPY_compress_debug_sections_eq)) { + Config.CompressionType = DebugCompressionType::Z; + + if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) { + Config.CompressionType = + StringSwitch<DebugCompressionType>( + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)) + .Case("zlib-gnu", DebugCompressionType::GNU) + .Case("zlib", DebugCompressionType::Z) + .Default(DebugCompressionType::None); + if (Config.CompressionType == DebugCompressionType::None) + return createStringError( + errc::invalid_argument, + "invalid or unsupported --compress-debug-sections format: %s", + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq) + .str() + .c_str()); + } + if (!zlib::isAvailable()) + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); + } + + Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); + // The gnu_debuglink's target is expected to not change or else its CRC would + // become invalidated and get rejected. We can avoid recalculating the + // checksum for every target file inside an archive by precomputing the CRC + // here. This prevents a significant amount of I/O. + if (!Config.AddGnuDebugLink.empty()) { + auto DebugOrErr = MemoryBuffer::getFile(Config.AddGnuDebugLink); + if (!DebugOrErr) + return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError()); + auto Debug = std::move(*DebugOrErr); + Config.GnuDebugLinkCRC32 = + llvm::crc32(arrayRefFromStringRef(Debug->getBuffer())); + } + Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); + if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) + Config.BuildIdLinkInput = + InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); + if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) + Config.BuildIdLinkOutput = + InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); + Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); + Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); + Config.AllocSectionsPrefix = + InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections); + if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) + Config.ExtractPartition = Arg->getValue(); + + for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { + if (!StringRef(Arg->getValue()).contains('=')) + return createStringError(errc::invalid_argument, + "bad format for --redefine-sym"); + auto Old2New = StringRef(Arg->getValue()).split('='); + if (!Config.SymbolsToRename.insert(Old2New).second) + return createStringError(errc::invalid_argument, + "multiple redefinition of symbol '%s'", + Old2New.first.str().c_str()); + } + + for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbols)) + if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc, + Arg->getValue())) + return std::move(E); + + for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { + Expected<SectionRename> SR = + parseRenameSectionValue(StringRef(Arg->getValue())); + if (!SR) + return SR.takeError(); + if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second) + return createStringError(errc::invalid_argument, + "multiple renames of section '%s'", + SR->OriginalName.str().c_str()); + } + for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) { + Expected<std::pair<StringRef, uint64_t>> NameAndAlign = + parseSetSectionAlignment(Arg->getValue()); + if (!NameAndAlign) + return NameAndAlign.takeError(); + Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second; + } + for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { + Expected<SectionFlagsUpdate> SFU = + parseSetSectionFlagValue(Arg->getValue()); + if (!SFU) + return SFU.takeError(); + if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second) + return createStringError( + errc::invalid_argument, + "--set-section-flags set multiple times for section '%s'", + SFU->Name.str().c_str()); + } + // Prohibit combinations of --set-section-flags when the section name is used + // by --rename-section, either as a source or a destination. + for (const auto &E : Config.SectionsToRename) { + const SectionRename &SR = E.second; + if (Config.SetSectionFlags.count(SR.OriginalName)) + return createStringError( + errc::invalid_argument, + "--set-section-flags=%s conflicts with --rename-section=%s=%s", + SR.OriginalName.str().c_str(), SR.OriginalName.str().c_str(), + SR.NewName.str().c_str()); + if (Config.SetSectionFlags.count(SR.NewName)) + return createStringError( + errc::invalid_argument, + "--set-section-flags=%s conflicts with --rename-section=%s=%s", + SR.NewName.str().c_str(), SR.OriginalName.str().c_str(), + SR.NewName.str().c_str()); + } + + for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) + if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) + if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) + if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) { + StringRef ArgValue(Arg->getValue()); + if (!ArgValue.contains('=')) + return createStringError(errc::invalid_argument, + "bad format for --add-section: missing '='"); + if (ArgValue.split("=").second.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-section: missing file name"); + Config.AddSection.push_back(ArgValue); + } + for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) + Config.DumpSection.push_back(Arg->getValue()); + Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); + Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); + Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); + Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo); + Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections); + Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); + Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); + Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); + Config.ExtractMainPartition = + InputArgs.hasArg(OBJCOPY_extract_main_partition); + Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); + Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); + if (InputArgs.hasArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) + Config.DiscardMode = + InputArgs.hasFlag(OBJCOPY_discard_all, OBJCOPY_discard_locals) + ? DiscardType::All + : DiscardType::Locals; + Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); + Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); + Config.DecompressDebugSections = + InputArgs.hasArg(OBJCOPY_decompress_debug_sections); + if (Config.DiscardMode == DiscardType::All) { + Config.StripDebug = true; + Config.KeepFileSymbols = true; + } + for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) + if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) + if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) + if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) + if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) + if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) + if (Error E = + Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) + if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) + if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) + if (Error E = + addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), + SymbolMatchStyle, ErrorCallback)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) + Config.SymbolsToAdd.push_back(Arg->getValue()); + + Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); + + Config.DeterministicArchives = InputArgs.hasFlag( + OBJCOPY_enable_deterministic_archives, + OBJCOPY_disable_deterministic_archives, /*default=*/true); + + Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); + + if (Config.PreserveDates && + (Config.OutputFilename == "-" || Config.InputFilename == "-")) + return createStringError(errc::invalid_argument, + "--preserve-dates requires a file"); + + for (auto Arg : InputArgs) + if (Arg->getOption().matches(OBJCOPY_set_start)) { + auto EAddr = getAsInteger<uint64_t>(Arg->getValue()); + if (!EAddr) + return createStringError( + EAddr.getError(), "bad entry point address: '%s'", Arg->getValue()); + + Config.EntryExpr = [EAddr](uint64_t) { return *EAddr; }; + } else if (Arg->getOption().matches(OBJCOPY_change_start)) { + auto EIncr = getAsInteger<int64_t>(Arg->getValue()); + if (!EIncr) + return createStringError(EIncr.getError(), + "bad entry point increment: '%s'", + Arg->getValue()); + auto Expr = Config.EntryExpr ? std::move(Config.EntryExpr) + : [](uint64_t A) { return A; }; + Config.EntryExpr = [Expr, EIncr](uint64_t EAddr) { + return Expr(EAddr) + *EIncr; + }; + } + + if (Config.DecompressDebugSections && + Config.CompressionType != DebugCompressionType::None) { + return createStringError( + errc::invalid_argument, + "cannot specify both --compress-debug-sections and " + "--decompress-debug-sections"); + } + + if (Config.DecompressDebugSections && !zlib::isAvailable()) + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress"); + + if (Config.ExtractPartition && Config.ExtractMainPartition) + return createStringError(errc::invalid_argument, + "cannot specify --extract-partition together with " + "--extract-main-partition"); + + DC.CopyConfigs.push_back(std::move(Config)); + return std::move(DC); +} + +// ParseInstallNameToolOptions returns the config and sets the input arguments. +// If a help flag is set then ParseInstallNameToolOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { + DriverConfig DC; + CopyConfig Config; + InstallNameToolOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (MissingArgumentCount) + return createStringError( + errc::invalid_argument, + "missing argument to " + + StringRef(InputArgs.getArgString(MissingArgumentIndex)) + + " option"); + + if (InputArgs.size() == 0) { + printHelp(T, errs(), ToolType::InstallNameTool); + exit(1); + } + + if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) { + printHelp(T, outs(), ToolType::InstallNameTool); + exit(0); + } + + if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) { + outs() << "llvm-install-name-tool, compatible with cctools " + "install_name_tool\n"; + cl::PrintVersionMessage(); + exit(0); + } + + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) + Config.RPathToAdd.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_prepend_rpath)) Config.RPathToPrepend.push_back(Arg->getValue()); - for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { - StringRef RPath = Arg->getValue(); - - // Cannot add and delete the same rpath at the same time. - if (is_contained(Config.RPathToAdd, RPath)) - return createStringError( - errc::invalid_argument, + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { + StringRef RPath = Arg->getValue(); + + // Cannot add and delete the same rpath at the same time. + if (is_contained(Config.RPathToAdd, RPath)) + return createStringError( + errc::invalid_argument, "cannot specify both -add_rpath '%s' and -delete_rpath '%s'", - RPath.str().c_str(), RPath.str().c_str()); + RPath.str().c_str(), RPath.str().c_str()); if (is_contained(Config.RPathToPrepend, RPath)) return createStringError( errc::invalid_argument, "cannot specify both -prepend_rpath '%s' and -delete_rpath '%s'", RPath.str().c_str(), RPath.str().c_str()); - - Config.RPathsToRemove.insert(RPath); - } - - for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) { - StringRef Old = Arg->getValue(0); - StringRef New = Arg->getValue(1); - - auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; }; - - // Cannot specify duplicate -rpath entries - auto It1 = find_if( - Config.RPathsToUpdate, - [&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) { - return Match(OldNew.getFirst()) || Match(OldNew.getSecond()); - }); - if (It1 != Config.RPathsToUpdate.end()) - return createStringError(errc::invalid_argument, + + Config.RPathsToRemove.insert(RPath); + } + + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) { + StringRef Old = Arg->getValue(0); + StringRef New = Arg->getValue(1); + + auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; }; + + // Cannot specify duplicate -rpath entries + auto It1 = find_if( + Config.RPathsToUpdate, + [&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) { + return Match(OldNew.getFirst()) || Match(OldNew.getSecond()); + }); + if (It1 != Config.RPathsToUpdate.end()) + return createStringError(errc::invalid_argument, "cannot specify both -rpath '" + It1->getFirst() + "' '" + It1->getSecond() + "' and -rpath '" + Old + "' '" + New + "'"); - - // Cannot specify the same rpath under both -delete_rpath and -rpath - auto It2 = find_if(Config.RPathsToRemove, Match); - if (It2 != Config.RPathsToRemove.end()) - return createStringError(errc::invalid_argument, + + // Cannot specify the same rpath under both -delete_rpath and -rpath + auto It2 = find_if(Config.RPathsToRemove, Match); + if (It2 != Config.RPathsToRemove.end()) + return createStringError(errc::invalid_argument, "cannot specify both -delete_rpath '" + *It2 + "' and -rpath '" + Old + "' '" + New + "'"); - - // Cannot specify the same rpath under both -add_rpath and -rpath - auto It3 = find_if(Config.RPathToAdd, Match); - if (It3 != Config.RPathToAdd.end()) - return createStringError(errc::invalid_argument, + + // Cannot specify the same rpath under both -add_rpath and -rpath + auto It3 = find_if(Config.RPathToAdd, Match); + if (It3 != Config.RPathToAdd.end()) + return createStringError(errc::invalid_argument, "cannot specify both -add_rpath '" + *It3 + "' and -rpath '" + Old + "' '" + New + "'"); - + // Cannot specify the same rpath under both -prepend_rpath and -rpath. auto It4 = find_if(Config.RPathToPrepend, Match); if (It4 != Config.RPathToPrepend.end()) @@ -955,41 +955,41 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { "cannot specify both -prepend_rpath '" + *It4 + "' and -rpath '" + Old + "' '" + New + "'"); - Config.RPathsToUpdate.insert({Old, New}); - } - + Config.RPathsToUpdate.insert({Old, New}); + } + if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) { - Config.SharedLibId = Arg->getValue(); + Config.SharedLibId = Arg->getValue(); if (Config.SharedLibId->empty()) return createStringError(errc::invalid_argument, "cannot specify an empty id"); } - + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change)) - Config.InstallNamesToUpdate.insert({Arg->getValue(0), Arg->getValue(1)}); - + Config.InstallNamesToUpdate.insert({Arg->getValue(0), Arg->getValue(1)}); + Config.RemoveAllRpaths = InputArgs.hasArg(INSTALL_NAME_TOOL_delete_all_rpaths); - SmallVector<StringRef, 2> Positional; - for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) - return createStringError(errc::invalid_argument, "unknown argument '%s'", - Arg->getAsString(InputArgs).c_str()); - for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT)) - Positional.push_back(Arg->getValue()); - if (Positional.empty()) - return createStringError(errc::invalid_argument, "no input file specified"); - if (Positional.size() > 1) - return createStringError( - errc::invalid_argument, - "llvm-install-name-tool expects a single input file"); - Config.InputFilename = Positional[0]; - Config.OutputFilename = Positional[0]; - - DC.CopyConfigs.push_back(std::move(Config)); - return std::move(DC); -} - + SmallVector<StringRef, 2> Positional; + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) + return createStringError(errc::invalid_argument, "unknown argument '%s'", + Arg->getAsString(InputArgs).c_str()); + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT)) + Positional.push_back(Arg->getValue()); + if (Positional.empty()) + return createStringError(errc::invalid_argument, "no input file specified"); + if (Positional.size() > 1) + return createStringError( + errc::invalid_argument, + "llvm-install-name-tool expects a single input file"); + Config.InputFilename = Positional[0]; + Config.OutputFilename = Positional[0]; + + DC.CopyConfigs.push_back(std::move(Config)); + return std::move(DC); +} + Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { DriverConfig DC; @@ -1034,147 +1034,147 @@ parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { return std::move(DC); } -// ParseStripOptions returns the config and sets the input arguments. If a -// help flag is set then ParseStripOptions will print the help messege and -// exit. -Expected<DriverConfig> -parseStripOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback) { - StripOptTable T; - unsigned MissingArgumentIndex, MissingArgumentCount; - llvm::opt::InputArgList InputArgs = - T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - - if (InputArgs.size() == 0) { - printHelp(T, errs(), ToolType::Strip); - exit(1); - } - - if (InputArgs.hasArg(STRIP_help)) { - printHelp(T, outs(), ToolType::Strip); - exit(0); - } - - if (InputArgs.hasArg(STRIP_version)) { - outs() << "llvm-strip, compatible with GNU strip\n"; - cl::PrintVersionMessage(); - exit(0); - } - - SmallVector<StringRef, 2> Positional; - for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN)) - return createStringError(errc::invalid_argument, "unknown argument '%s'", - Arg->getAsString(InputArgs).c_str()); - for (auto Arg : InputArgs.filtered(STRIP_INPUT)) - Positional.push_back(Arg->getValue()); - - if (Positional.empty()) - return createStringError(errc::invalid_argument, "no input file specified"); - - if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output)) - return createStringError( - errc::invalid_argument, - "multiple input files cannot be used in combination with -o"); - - CopyConfig Config; - - if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) - return createStringError(errc::invalid_argument, - "--regex and --wildcard are incompatible"); - MatchStyle SectionMatchStyle = - InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; - MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex) - ? MatchStyle::Regex - : InputArgs.hasArg(STRIP_wildcard) - ? MatchStyle::Wildcard - : MatchStyle::Literal; - Config.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); - Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); - - if (InputArgs.hasArg(STRIP_discard_all, STRIP_discard_locals)) - Config.DiscardMode = - InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals) - ? DiscardType::All - : DiscardType::Locals; - Config.StripSections = InputArgs.hasArg(STRIP_strip_sections); - Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); - if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all)) - Config.StripAll = Arg->getOption().getID() == STRIP_strip_all; - Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); - Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); - Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); - Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); - - for (auto Arg : InputArgs.filtered(STRIP_keep_section)) - if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( - Arg->getValue(), SectionMatchStyle, ErrorCallback))) - return std::move(E); - - for (auto Arg : InputArgs.filtered(STRIP_remove_section)) - if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( - Arg->getValue(), SectionMatchStyle, ErrorCallback))) - return std::move(E); - - for (auto Arg : InputArgs.filtered(STRIP_strip_symbol)) - if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - - for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) - if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( - Arg->getValue(), SymbolMatchStyle, ErrorCallback))) - return std::move(E); - - if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && - !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && - !Config.StripAllGNU && Config.SymbolsToRemove.empty()) - Config.StripAll = true; - - if (Config.DiscardMode == DiscardType::All) { - Config.StripDebug = true; - Config.KeepFileSymbols = true; - } - - Config.DeterministicArchives = - InputArgs.hasFlag(STRIP_enable_deterministic_archives, - STRIP_disable_deterministic_archives, /*default=*/true); - - Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); - Config.InputFormat = FileFormat::Unspecified; - Config.OutputFormat = FileFormat::Unspecified; - - DriverConfig DC; - if (Positional.size() == 1) { - Config.InputFilename = Positional[0]; - Config.OutputFilename = - InputArgs.getLastArgValue(STRIP_output, Positional[0]); - DC.CopyConfigs.push_back(std::move(Config)); - } else { - StringMap<unsigned> InputFiles; - for (StringRef Filename : Positional) { - if (InputFiles[Filename]++ == 1) { - if (Filename == "-") - return createStringError( - errc::invalid_argument, - "cannot specify '-' as an input file more than once"); - if (Error E = ErrorCallback(createStringError( - errc::invalid_argument, "'%s' was already specified", - Filename.str().c_str()))) - return std::move(E); - } - Config.InputFilename = Filename; - Config.OutputFilename = Filename; - DC.CopyConfigs.push_back(Config); - } - } - - if (Config.PreserveDates && (is_contained(Positional, "-") || - InputArgs.getLastArgValue(STRIP_output) == "-")) - return createStringError(errc::invalid_argument, - "--preserve-dates requires a file"); - - return std::move(DC); -} - -} // namespace objcopy -} // namespace llvm +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. +Expected<DriverConfig> +parseStripOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback) { + StripOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (InputArgs.size() == 0) { + printHelp(T, errs(), ToolType::Strip); + exit(1); + } + + if (InputArgs.hasArg(STRIP_help)) { + printHelp(T, outs(), ToolType::Strip); + exit(0); + } + + if (InputArgs.hasArg(STRIP_version)) { + outs() << "llvm-strip, compatible with GNU strip\n"; + cl::PrintVersionMessage(); + exit(0); + } + + SmallVector<StringRef, 2> Positional; + for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN)) + return createStringError(errc::invalid_argument, "unknown argument '%s'", + Arg->getAsString(InputArgs).c_str()); + for (auto Arg : InputArgs.filtered(STRIP_INPUT)) + Positional.push_back(Arg->getValue()); + + if (Positional.empty()) + return createStringError(errc::invalid_argument, "no input file specified"); + + if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output)) + return createStringError( + errc::invalid_argument, + "multiple input files cannot be used in combination with -o"); + + CopyConfig Config; + + if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) + return createStringError(errc::invalid_argument, + "--regex and --wildcard are incompatible"); + MatchStyle SectionMatchStyle = + InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; + MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex) + ? MatchStyle::Regex + : InputArgs.hasArg(STRIP_wildcard) + ? MatchStyle::Wildcard + : MatchStyle::Literal; + Config.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); + Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); + + if (InputArgs.hasArg(STRIP_discard_all, STRIP_discard_locals)) + Config.DiscardMode = + InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals) + ? DiscardType::All + : DiscardType::Locals; + Config.StripSections = InputArgs.hasArg(STRIP_strip_sections); + Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); + if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all)) + Config.StripAll = Arg->getOption().getID() == STRIP_strip_all; + Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); + Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); + Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); + Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); + + for (auto Arg : InputArgs.filtered(STRIP_keep_section)) + if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); + + for (auto Arg : InputArgs.filtered(STRIP_remove_section)) + if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); + + for (auto Arg : InputArgs.filtered(STRIP_strip_symbol)) + if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + + for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) + if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); + + if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && + !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && + !Config.StripAllGNU && Config.SymbolsToRemove.empty()) + Config.StripAll = true; + + if (Config.DiscardMode == DiscardType::All) { + Config.StripDebug = true; + Config.KeepFileSymbols = true; + } + + Config.DeterministicArchives = + InputArgs.hasFlag(STRIP_enable_deterministic_archives, + STRIP_disable_deterministic_archives, /*default=*/true); + + Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); + Config.InputFormat = FileFormat::Unspecified; + Config.OutputFormat = FileFormat::Unspecified; + + DriverConfig DC; + if (Positional.size() == 1) { + Config.InputFilename = Positional[0]; + Config.OutputFilename = + InputArgs.getLastArgValue(STRIP_output, Positional[0]); + DC.CopyConfigs.push_back(std::move(Config)); + } else { + StringMap<unsigned> InputFiles; + for (StringRef Filename : Positional) { + if (InputFiles[Filename]++ == 1) { + if (Filename == "-") + return createStringError( + errc::invalid_argument, + "cannot specify '-' as an input file more than once"); + if (Error E = ErrorCallback(createStringError( + errc::invalid_argument, "'%s' was already specified", + Filename.str().c_str()))) + return std::move(E); + } + Config.InputFilename = Filename; + Config.OutputFilename = Filename; + DC.CopyConfigs.push_back(Config); + } + } + + if (Config.PreserveDates && (is_contained(Positional, "-") || + InputArgs.getLastArgValue(STRIP_output) == "-")) + return createStringError(errc::invalid_argument, + "--preserve-dates requires a file"); + + return std::move(DC); +} + +} // namespace objcopy +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.h b/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.h index 07eac9d2bb..008a138de7 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/CopyConfig.h @@ -1,289 +1,289 @@ -//===- CopyConfig.h -------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H -#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H - -#include "ELF/ELFConfig.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitmaskEnum.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Object/ELFTypes.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/GlobPattern.h" -#include "llvm/Support/Regex.h" -// Necessary for llvm::DebugCompressionType::None -#include "llvm/Target/TargetOptions.h" -#include <vector> - -namespace llvm { -namespace objcopy { - -enum class FileFormat { - Unspecified, - ELF, - Binary, - IHex, -}; - -// This type keeps track of the machine info for various architectures. This -// lets us map architecture names to ELF types and the e_machine value of the -// ELF file. -struct MachineInfo { - MachineInfo(uint16_t EM, uint8_t ABI, bool Is64, bool IsLittle) - : EMachine(EM), OSABI(ABI), Is64Bit(Is64), IsLittleEndian(IsLittle) {} - // Alternative constructor that defaults to NONE for OSABI. - MachineInfo(uint16_t EM, bool Is64, bool IsLittle) - : MachineInfo(EM, ELF::ELFOSABI_NONE, Is64, IsLittle) {} - // Default constructor for unset fields. - MachineInfo() : MachineInfo(0, 0, false, false) {} - uint16_t EMachine; - uint8_t OSABI; - bool Is64Bit; - bool IsLittleEndian; -}; - -// Flags set by --set-section-flags or --rename-section. Interpretation of these -// is format-specific and not all flags are meaningful for all object file -// formats. This is a bitmask; many section flags may be set. -enum SectionFlag { - SecNone = 0, - SecAlloc = 1 << 0, - SecLoad = 1 << 1, - SecNoload = 1 << 2, - SecReadonly = 1 << 3, - SecDebug = 1 << 4, - SecCode = 1 << 5, - SecData = 1 << 6, - SecRom = 1 << 7, - SecMerge = 1 << 8, - SecStrings = 1 << 9, - SecContents = 1 << 10, - SecShare = 1 << 11, - SecExclude = 1 << 12, - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecExclude) -}; - -struct SectionRename { - StringRef OriginalName; - StringRef NewName; - Optional<SectionFlag> NewFlags; -}; - -struct SectionFlagsUpdate { - StringRef Name; - SectionFlag NewFlags; -}; - -enum class DiscardType { - None, // Default - All, // --discard-all (-x) - Locals, // --discard-locals (-X) -}; - -enum class MatchStyle { - Literal, // Default for symbols. - Wildcard, // Default for sections, or enabled with --wildcard (-w). - Regex, // Enabled with --regex. -}; - -class NameOrPattern { - StringRef Name; - // Regex is shared between multiple CopyConfig instances. - std::shared_ptr<Regex> R; - std::shared_ptr<GlobPattern> G; - bool IsPositiveMatch = true; - - NameOrPattern(StringRef N) : Name(N) {} - NameOrPattern(std::shared_ptr<Regex> R) : R(R) {} - NameOrPattern(std::shared_ptr<GlobPattern> G, bool IsPositiveMatch) - : G(G), IsPositiveMatch(IsPositiveMatch) {} - -public: - // ErrorCallback is used to handle recoverable errors. An Error returned - // by the callback aborts the parsing and is then returned by this function. - static Expected<NameOrPattern> - create(StringRef Pattern, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback); - - bool isPositiveMatch() const { return IsPositiveMatch; } - bool operator==(StringRef S) const { - return R ? R->match(S) : G ? G->match(S) : Name == S; - } - bool operator!=(StringRef S) const { return !operator==(S); } -}; - -// Matcher that checks symbol or section names against the command line flags -// provided for that option. -class NameMatcher { - std::vector<NameOrPattern> PosMatchers; - std::vector<NameOrPattern> NegMatchers; - -public: - Error addMatcher(Expected<NameOrPattern> Matcher) { - if (!Matcher) - return Matcher.takeError(); - if (Matcher->isPositiveMatch()) - PosMatchers.push_back(std::move(*Matcher)); - else - NegMatchers.push_back(std::move(*Matcher)); - return Error::success(); - } - bool matches(StringRef S) const { - return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S); - } - bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } -}; - -// Configuration for copying/stripping a single file. -struct CopyConfig { - // Format-specific options to be initialized lazily when needed. - Optional<elf::ELFCopyConfig> ELF; - - // Main input/output options - StringRef InputFilename; - FileFormat InputFormat = FileFormat::Unspecified; - StringRef OutputFilename; - FileFormat OutputFormat = FileFormat::Unspecified; - - // Only applicable when --output-format!=binary (e.g. elf64-x86-64). - Optional<MachineInfo> OutputArch; - - // Advanced options - StringRef AddGnuDebugLink; - // Cached gnu_debuglink's target CRC - uint32_t GnuDebugLinkCRC32; - StringRef BuildIdLinkDir; - Optional<StringRef> BuildIdLinkInput; - Optional<StringRef> BuildIdLinkOutput; - Optional<StringRef> ExtractPartition; - StringRef SplitDWO; - StringRef SymbolsPrefix; - StringRef AllocSectionsPrefix; - DiscardType DiscardMode = DiscardType::None; - Optional<StringRef> NewSymbolVisibility; - - // Repeated options - std::vector<StringRef> AddSection; - std::vector<StringRef> DumpSection; - std::vector<StringRef> SymbolsToAdd; - std::vector<StringRef> RPathToAdd; +//===- CopyConfig.h -------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H + +#include "ELF/ELFConfig.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/GlobPattern.h" +#include "llvm/Support/Regex.h" +// Necessary for llvm::DebugCompressionType::None +#include "llvm/Target/TargetOptions.h" +#include <vector> + +namespace llvm { +namespace objcopy { + +enum class FileFormat { + Unspecified, + ELF, + Binary, + IHex, +}; + +// This type keeps track of the machine info for various architectures. This +// lets us map architecture names to ELF types and the e_machine value of the +// ELF file. +struct MachineInfo { + MachineInfo(uint16_t EM, uint8_t ABI, bool Is64, bool IsLittle) + : EMachine(EM), OSABI(ABI), Is64Bit(Is64), IsLittleEndian(IsLittle) {} + // Alternative constructor that defaults to NONE for OSABI. + MachineInfo(uint16_t EM, bool Is64, bool IsLittle) + : MachineInfo(EM, ELF::ELFOSABI_NONE, Is64, IsLittle) {} + // Default constructor for unset fields. + MachineInfo() : MachineInfo(0, 0, false, false) {} + uint16_t EMachine; + uint8_t OSABI; + bool Is64Bit; + bool IsLittleEndian; +}; + +// Flags set by --set-section-flags or --rename-section. Interpretation of these +// is format-specific and not all flags are meaningful for all object file +// formats. This is a bitmask; many section flags may be set. +enum SectionFlag { + SecNone = 0, + SecAlloc = 1 << 0, + SecLoad = 1 << 1, + SecNoload = 1 << 2, + SecReadonly = 1 << 3, + SecDebug = 1 << 4, + SecCode = 1 << 5, + SecData = 1 << 6, + SecRom = 1 << 7, + SecMerge = 1 << 8, + SecStrings = 1 << 9, + SecContents = 1 << 10, + SecShare = 1 << 11, + SecExclude = 1 << 12, + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecExclude) +}; + +struct SectionRename { + StringRef OriginalName; + StringRef NewName; + Optional<SectionFlag> NewFlags; +}; + +struct SectionFlagsUpdate { + StringRef Name; + SectionFlag NewFlags; +}; + +enum class DiscardType { + None, // Default + All, // --discard-all (-x) + Locals, // --discard-locals (-X) +}; + +enum class MatchStyle { + Literal, // Default for symbols. + Wildcard, // Default for sections, or enabled with --wildcard (-w). + Regex, // Enabled with --regex. +}; + +class NameOrPattern { + StringRef Name; + // Regex is shared between multiple CopyConfig instances. + std::shared_ptr<Regex> R; + std::shared_ptr<GlobPattern> G; + bool IsPositiveMatch = true; + + NameOrPattern(StringRef N) : Name(N) {} + NameOrPattern(std::shared_ptr<Regex> R) : R(R) {} + NameOrPattern(std::shared_ptr<GlobPattern> G, bool IsPositiveMatch) + : G(G), IsPositiveMatch(IsPositiveMatch) {} + +public: + // ErrorCallback is used to handle recoverable errors. An Error returned + // by the callback aborts the parsing and is then returned by this function. + static Expected<NameOrPattern> + create(StringRef Pattern, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback); + + bool isPositiveMatch() const { return IsPositiveMatch; } + bool operator==(StringRef S) const { + return R ? R->match(S) : G ? G->match(S) : Name == S; + } + bool operator!=(StringRef S) const { return !operator==(S); } +}; + +// Matcher that checks symbol or section names against the command line flags +// provided for that option. +class NameMatcher { + std::vector<NameOrPattern> PosMatchers; + std::vector<NameOrPattern> NegMatchers; + +public: + Error addMatcher(Expected<NameOrPattern> Matcher) { + if (!Matcher) + return Matcher.takeError(); + if (Matcher->isPositiveMatch()) + PosMatchers.push_back(std::move(*Matcher)); + else + NegMatchers.push_back(std::move(*Matcher)); + return Error::success(); + } + bool matches(StringRef S) const { + return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S); + } + bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } +}; + +// Configuration for copying/stripping a single file. +struct CopyConfig { + // Format-specific options to be initialized lazily when needed. + Optional<elf::ELFCopyConfig> ELF; + + // Main input/output options + StringRef InputFilename; + FileFormat InputFormat = FileFormat::Unspecified; + StringRef OutputFilename; + FileFormat OutputFormat = FileFormat::Unspecified; + + // Only applicable when --output-format!=binary (e.g. elf64-x86-64). + Optional<MachineInfo> OutputArch; + + // Advanced options + StringRef AddGnuDebugLink; + // Cached gnu_debuglink's target CRC + uint32_t GnuDebugLinkCRC32; + StringRef BuildIdLinkDir; + Optional<StringRef> BuildIdLinkInput; + Optional<StringRef> BuildIdLinkOutput; + Optional<StringRef> ExtractPartition; + StringRef SplitDWO; + StringRef SymbolsPrefix; + StringRef AllocSectionsPrefix; + DiscardType DiscardMode = DiscardType::None; + Optional<StringRef> NewSymbolVisibility; + + // Repeated options + std::vector<StringRef> AddSection; + std::vector<StringRef> DumpSection; + std::vector<StringRef> SymbolsToAdd; + std::vector<StringRef> RPathToAdd; std::vector<StringRef> RPathToPrepend; - DenseMap<StringRef, StringRef> RPathsToUpdate; - DenseMap<StringRef, StringRef> InstallNamesToUpdate; - DenseSet<StringRef> RPathsToRemove; - - // install-name-tool's id option - Optional<StringRef> SharedLibId; - - // Section matchers - NameMatcher KeepSection; - NameMatcher OnlySection; - NameMatcher ToRemove; - - // Symbol matchers - NameMatcher SymbolsToGlobalize; - NameMatcher SymbolsToKeep; - NameMatcher SymbolsToLocalize; - NameMatcher SymbolsToRemove; - NameMatcher UnneededSymbolsToRemove; - NameMatcher SymbolsToWeaken; - NameMatcher SymbolsToKeepGlobal; - - // Map options - StringMap<SectionRename> SectionsToRename; - StringMap<uint64_t> SetSectionAlignment; - StringMap<SectionFlagsUpdate> SetSectionFlags; - StringMap<StringRef> SymbolsToRename; - - // ELF entry point address expression. The input parameter is an entry point - // address in the input ELF file. The entry address in the output file is - // calculated with EntryExpr(input_address), when either --set-start or - // --change-start is used. - std::function<uint64_t(uint64_t)> EntryExpr; - - // Boolean options - bool AllowBrokenLinks = false; - bool DeterministicArchives = true; - bool ExtractDWO = false; - bool ExtractMainPartition = false; - bool KeepFileSymbols = false; - bool LocalizeHidden = false; - bool OnlyKeepDebug = false; - bool PreserveDates = false; - bool StripAll = false; - bool StripAllGNU = false; - bool StripDWO = false; - bool StripDebug = false; - bool StripNonAlloc = false; - bool StripSections = false; - bool StripSwiftSymbols = false; - bool StripUnneeded = false; - bool Weaken = false; - bool DecompressDebugSections = false; + DenseMap<StringRef, StringRef> RPathsToUpdate; + DenseMap<StringRef, StringRef> InstallNamesToUpdate; + DenseSet<StringRef> RPathsToRemove; + + // install-name-tool's id option + Optional<StringRef> SharedLibId; + + // Section matchers + NameMatcher KeepSection; + NameMatcher OnlySection; + NameMatcher ToRemove; + + // Symbol matchers + NameMatcher SymbolsToGlobalize; + NameMatcher SymbolsToKeep; + NameMatcher SymbolsToLocalize; + NameMatcher SymbolsToRemove; + NameMatcher UnneededSymbolsToRemove; + NameMatcher SymbolsToWeaken; + NameMatcher SymbolsToKeepGlobal; + + // Map options + StringMap<SectionRename> SectionsToRename; + StringMap<uint64_t> SetSectionAlignment; + StringMap<SectionFlagsUpdate> SetSectionFlags; + StringMap<StringRef> SymbolsToRename; + + // ELF entry point address expression. The input parameter is an entry point + // address in the input ELF file. The entry address in the output file is + // calculated with EntryExpr(input_address), when either --set-start or + // --change-start is used. + std::function<uint64_t(uint64_t)> EntryExpr; + + // Boolean options + bool AllowBrokenLinks = false; + bool DeterministicArchives = true; + bool ExtractDWO = false; + bool ExtractMainPartition = false; + bool KeepFileSymbols = false; + bool LocalizeHidden = false; + bool OnlyKeepDebug = false; + bool PreserveDates = false; + bool StripAll = false; + bool StripAllGNU = false; + bool StripDWO = false; + bool StripDebug = false; + bool StripNonAlloc = false; + bool StripSections = false; + bool StripSwiftSymbols = false; + bool StripUnneeded = false; + bool Weaken = false; + bool DecompressDebugSections = false; // install-name-tool's --delete_all_rpaths bool RemoveAllRpaths = false; - DebugCompressionType CompressionType = DebugCompressionType::None; - - // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on - // success or returns an Error otherwise. - Error parseELFConfig() { - if (!ELF) { - Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this); - if (!ELFConfig) - return ELFConfig.takeError(); - ELF = *ELFConfig; - } - return Error::success(); - } -}; - -// Configuration for the overall invocation of this tool. When invoked as -// objcopy, will always contain exactly one CopyConfig. When invoked as strip, -// will contain one or more CopyConfigs. -struct DriverConfig { - SmallVector<CopyConfig, 1> CopyConfigs; - BumpPtrAllocator Alloc; -}; - -// ParseObjcopyOptions returns the config and sets the input arguments. If a -// help flag is set then ParseObjcopyOptions will print the help messege and -// exit. ErrorCallback is used to handle recoverable errors. An Error returned -// by the callback aborts the parsing and is then returned by this function. -Expected<DriverConfig> -parseObjcopyOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback); - -// ParseInstallNameToolOptions returns the config and sets the input arguments. -// If a help flag is set then ParseInstallNameToolOptions will print the help -// messege and exit. -Expected<DriverConfig> -parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); - + DebugCompressionType CompressionType = DebugCompressionType::None; + + // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on + // success or returns an Error otherwise. + Error parseELFConfig() { + if (!ELF) { + Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this); + if (!ELFConfig) + return ELFConfig.takeError(); + ELF = *ELFConfig; + } + return Error::success(); + } +}; + +// Configuration for the overall invocation of this tool. When invoked as +// objcopy, will always contain exactly one CopyConfig. When invoked as strip, +// will contain one or more CopyConfigs. +struct DriverConfig { + SmallVector<CopyConfig, 1> CopyConfigs; + BumpPtrAllocator Alloc; +}; + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); + +// ParseInstallNameToolOptions returns the config and sets the input arguments. +// If a help flag is set then ParseInstallNameToolOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); + // ParseBitcodeStripOptions returns the config and sets the input arguments. // If a help flag is set then ParseBitcodeStripOptions will print the help // messege and exit. Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr); -// ParseStripOptions returns the config and sets the input arguments. If a -// help flag is set then ParseStripOptions will print the help messege and -// exit. ErrorCallback is used to handle recoverable errors. An Error returned -// by the callback aborts the parsing and is then returned by this function. -Expected<DriverConfig> -parseStripOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback); -} // namespace objcopy -} // namespace llvm - -#endif +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseStripOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); +} // namespace objcopy +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.cpp index 40993760ad..fcedf8ee11 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.cpp @@ -1,133 +1,133 @@ -//===- ELFConfig.cpp ------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "CopyConfig.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace objcopy { -namespace elf { - -static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue, - uint8_t DefaultVisibility) { - // Parse value given with --add-symbol option and create the - // new symbol if possible. The value format for --add-symbol is: - // - // <name>=[<section>:]<value>[,<flags>] - // - // where: - // <name> - symbol name, can be empty string - // <section> - optional section name. If not given ABS symbol is created - // <value> - symbol value, can be decimal or hexadecimal number prefixed - // with 0x. - // <flags> - optional flags affecting symbol type, binding or visibility: - // The following are currently supported: - // - // global, local, weak, default, hidden, file, section, object, - // indirect-function. - // - // The following flags are ignored and provided for GNU - // compatibility only: - // - // warning, debug, constructor, indirect, synthetic, - // unique-object, before=<symbol>. - NewSymbolInfo SI; - StringRef Value; - std::tie(SI.SymbolName, Value) = FlagValue.split('='); - if (Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing '=' after '%s'", - SI.SymbolName.str().c_str()); - - if (Value.contains(':')) { - std::tie(SI.SectionName, Value) = Value.split(':'); - if (SI.SectionName.empty() || Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing section name or symbol value"); - } - - SmallVector<StringRef, 6> Flags; - Value.split(Flags, ','); - if (Flags[0].getAsInteger(0, SI.Value)) - return createStringError(errc::invalid_argument, "bad symbol value: '%s'", - Flags[0].str().c_str()); - - SI.Visibility = DefaultVisibility; - - using Functor = std::function<void(void)>; - SmallVector<StringRef, 6> UnsupportedFlags; - for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) - static_cast<Functor>( - StringSwitch<Functor>(Flags[I]) - .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) - .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) - .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) - .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) - .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) - .CaseLower("protected", - [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) - .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) - .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) - .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) - .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) - .CaseLower("indirect-function", - [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) - .CaseLower("debug", [] {}) - .CaseLower("constructor", [] {}) - .CaseLower("warning", [] {}) - .CaseLower("indirect", [] {}) - .CaseLower("synthetic", [] {}) - .CaseLower("unique-object", [] {}) - .StartsWithLower("before", [] {}) - .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); - if (!UnsupportedFlags.empty()) - return createStringError(errc::invalid_argument, - "unsupported flag%s for --add-symbol: '%s'", - UnsupportedFlags.size() > 1 ? "s" : "", - join(UnsupportedFlags, "', '").c_str()); - return SI; -} - -Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) { - ELFCopyConfig ELFConfig; - if (Config.NewSymbolVisibility) { - const uint8_t Invalid = 0xff; - ELFConfig.NewSymbolVisibility = - StringSwitch<uint8_t>(*Config.NewSymbolVisibility) - .Case("default", ELF::STV_DEFAULT) - .Case("hidden", ELF::STV_HIDDEN) - .Case("internal", ELF::STV_INTERNAL) - .Case("protected", ELF::STV_PROTECTED) - .Default(Invalid); - - if (ELFConfig.NewSymbolVisibility == Invalid) - return createStringError(errc::invalid_argument, - "'%s' is not a valid symbol visibility", - Config.NewSymbolVisibility->str().c_str()); - } - - for (StringRef Arg : Config.SymbolsToAdd) { - Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo( - Arg, - ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); - if (!NSI) - return NSI.takeError(); - ELFConfig.SymbolsToAdd.push_back(*NSI); - } - - return ELFConfig; -} - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm +//===- ELFConfig.cpp ------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "CopyConfig.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace elf { + +static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue, + uint8_t DefaultVisibility) { + // Parse value given with --add-symbol option and create the + // new symbol if possible. The value format for --add-symbol is: + // + // <name>=[<section>:]<value>[,<flags>] + // + // where: + // <name> - symbol name, can be empty string + // <section> - optional section name. If not given ABS symbol is created + // <value> - symbol value, can be decimal or hexadecimal number prefixed + // with 0x. + // <flags> - optional flags affecting symbol type, binding or visibility: + // The following are currently supported: + // + // global, local, weak, default, hidden, file, section, object, + // indirect-function. + // + // The following flags are ignored and provided for GNU + // compatibility only: + // + // warning, debug, constructor, indirect, synthetic, + // unique-object, before=<symbol>. + NewSymbolInfo SI; + StringRef Value; + std::tie(SI.SymbolName, Value) = FlagValue.split('='); + if (Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing '=' after '%s'", + SI.SymbolName.str().c_str()); + + if (Value.contains(':')) { + std::tie(SI.SectionName, Value) = Value.split(':'); + if (SI.SectionName.empty() || Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing section name or symbol value"); + } + + SmallVector<StringRef, 6> Flags; + Value.split(Flags, ','); + if (Flags[0].getAsInteger(0, SI.Value)) + return createStringError(errc::invalid_argument, "bad symbol value: '%s'", + Flags[0].str().c_str()); + + SI.Visibility = DefaultVisibility; + + using Functor = std::function<void(void)>; + SmallVector<StringRef, 6> UnsupportedFlags; + for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) + static_cast<Functor>( + StringSwitch<Functor>(Flags[I]) + .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) + .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) + .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) + .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) + .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) + .CaseLower("protected", + [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) + .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) + .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) + .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) + .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) + .CaseLower("indirect-function", + [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) + .CaseLower("debug", [] {}) + .CaseLower("constructor", [] {}) + .CaseLower("warning", [] {}) + .CaseLower("indirect", [] {}) + .CaseLower("synthetic", [] {}) + .CaseLower("unique-object", [] {}) + .StartsWithLower("before", [] {}) + .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); + if (!UnsupportedFlags.empty()) + return createStringError(errc::invalid_argument, + "unsupported flag%s for --add-symbol: '%s'", + UnsupportedFlags.size() > 1 ? "s" : "", + join(UnsupportedFlags, "', '").c_str()); + return SI; +} + +Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) { + ELFCopyConfig ELFConfig; + if (Config.NewSymbolVisibility) { + const uint8_t Invalid = 0xff; + ELFConfig.NewSymbolVisibility = + StringSwitch<uint8_t>(*Config.NewSymbolVisibility) + .Case("default", ELF::STV_DEFAULT) + .Case("hidden", ELF::STV_HIDDEN) + .Case("internal", ELF::STV_INTERNAL) + .Case("protected", ELF::STV_PROTECTED) + .Default(Invalid); + + if (ELFConfig.NewSymbolVisibility == Invalid) + return createStringError(errc::invalid_argument, + "'%s' is not a valid symbol visibility", + Config.NewSymbolVisibility->str().c_str()); + } + + for (StringRef Arg : Config.SymbolsToAdd) { + Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo( + Arg, + ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); + if (!NSI) + return NSI.takeError(); + ELFConfig.SymbolsToAdd.push_back(*NSI); + } + + return ELFConfig; +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.h b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.h index 977efbc416..a51ac07546 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFConfig.h @@ -1,44 +1,44 @@ -//===- ELFConfig.h ----------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H -#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H - -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Object/ELFTypes.h" -#include "llvm/Support/Error.h" -#include <vector> - -namespace llvm { -namespace objcopy { -struct CopyConfig; - -namespace elf { - -struct NewSymbolInfo { - StringRef SymbolName; - StringRef SectionName; - uint64_t Value = 0; - uint8_t Type = ELF::STT_NOTYPE; - uint8_t Bind = ELF::STB_GLOBAL; - uint8_t Visibility = ELF::STV_DEFAULT; -}; - -struct ELFCopyConfig { - Optional<uint8_t> NewSymbolVisibility; - std::vector<NewSymbolInfo> SymbolsToAdd; -}; - -Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config); - -} // namespace elf -} // namespace objcopy -} // namespace llvm - -#endif +//===- ELFConfig.h ----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H +#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Error.h" +#include <vector> + +namespace llvm { +namespace objcopy { +struct CopyConfig; + +namespace elf { + +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + uint64_t Value = 0; + uint8_t Type = ELF::STT_NOTYPE; + uint8_t Bind = ELF::STB_GLOBAL; + uint8_t Visibility = ELF::STV_DEFAULT; +}; + +struct ELFCopyConfig { + Optional<uint8_t> NewSymbolVisibility; + std::vector<NewSymbolInfo> SymbolsToAdd; +}; + +Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config); + +} // namespace elf +} // namespace objcopy +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index c53a34bc46..6067ed6d26 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -1,592 +1,592 @@ -//===- ELFObjcopy.cpp -----------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "ELFObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" -#include "Object.h" -#include "llvm/ADT/BitmaskEnum.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/BinaryFormat/ELF.h" -#include "llvm/MC/MCTargetOptions.h" -#include "llvm/Object/Binary.h" -#include "llvm/Object/ELFObjectFile.h" -#include "llvm/Object/ELFTypes.h" -#include "llvm/Object/Error.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Compression.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cassert> -#include <cstdlib> -#include <functional> -#include <iterator> -#include <memory> -#include <string> -#include <system_error> -#include <utility> - -namespace llvm { -namespace objcopy { -namespace elf { - -using namespace object; -using namespace ELF; -using SectionPred = std::function<bool(const SectionBase &Sec)>; - -static bool isDebugSection(const SectionBase &Sec) { - return StringRef(Sec.Name).startswith(".debug") || - StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index"; -} - -static bool isDWOSection(const SectionBase &Sec) { - return StringRef(Sec.Name).endswith(".dwo"); -} - -static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { - // We can't remove the section header string table. - if (&Sec == Obj.SectionNames) - return false; - // Short of keeping the string table we want to keep everything that is a DWO - // section and remove everything else. - return !isDWOSection(Sec); -} - -uint64_t getNewShfFlags(SectionFlag AllFlags) { - uint64_t NewFlags = 0; - if (AllFlags & SectionFlag::SecAlloc) - NewFlags |= ELF::SHF_ALLOC; - if (!(AllFlags & SectionFlag::SecReadonly)) - NewFlags |= ELF::SHF_WRITE; - if (AllFlags & SectionFlag::SecCode) - NewFlags |= ELF::SHF_EXECINSTR; - if (AllFlags & SectionFlag::SecMerge) - NewFlags |= ELF::SHF_MERGE; - if (AllFlags & SectionFlag::SecStrings) - NewFlags |= ELF::SHF_STRINGS; - if (AllFlags & SectionFlag::SecExclude) - NewFlags |= ELF::SHF_EXCLUDE; - return NewFlags; -} - -static uint64_t getSectionFlagsPreserveMask(uint64_t OldFlags, - uint64_t NewFlags) { - // Preserve some flags which should not be dropped when setting flags. - // Also, preserve anything OS/processor dependant. - const uint64_t PreserveMask = - (ELF::SHF_COMPRESSED | ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | - ELF::SHF_MASKOS | ELF::SHF_MASKPROC | ELF::SHF_TLS | - ELF::SHF_INFO_LINK) & - ~ELF::SHF_EXCLUDE; - return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask); -} - -static void setSectionFlagsAndType(SectionBase &Sec, SectionFlag Flags) { - Sec.Flags = getSectionFlagsPreserveMask(Sec.Flags, getNewShfFlags(Flags)); - - // In GNU objcopy, certain flags promote SHT_NOBITS to SHT_PROGBITS. This rule - // may promote more non-ALLOC sections than GNU objcopy, but it is fine as - // non-ALLOC SHT_NOBITS sections do not make much sense. - if (Sec.Type == SHT_NOBITS && - (!(Sec.Flags & ELF::SHF_ALLOC) || - Flags & (SectionFlag::SecContents | SectionFlag::SecLoad))) - Sec.Type = SHT_PROGBITS; -} - -static ElfType getOutputElfType(const Binary &Bin) { - // Infer output ELF type from the input ELF object - if (isa<ELFObjectFile<ELF32LE>>(Bin)) - return ELFT_ELF32LE; - if (isa<ELFObjectFile<ELF64LE>>(Bin)) - return ELFT_ELF64LE; - if (isa<ELFObjectFile<ELF32BE>>(Bin)) - return ELFT_ELF32BE; - if (isa<ELFObjectFile<ELF64BE>>(Bin)) - return ELFT_ELF64BE; - llvm_unreachable("Invalid ELFType"); -} - -static ElfType getOutputElfType(const MachineInfo &MI) { - // Infer output ELF type from the binary arch specified - if (MI.Is64Bit) - return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; - else - return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; -} - -static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, - ElfType OutputElfType) { - // Depending on the initial ELFT and OutputFormat we need a different Writer. - switch (OutputElfType) { - case ELFT_ELF32LE: - return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections, - Config.OnlyKeepDebug); - case ELFT_ELF64LE: - return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections, - Config.OnlyKeepDebug); - case ELFT_ELF32BE: - return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections, - Config.OnlyKeepDebug); - case ELFT_ELF64BE: - return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections, - Config.OnlyKeepDebug); - } - llvm_unreachable("Invalid output format"); -} - -static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, - ElfType OutputElfType) { - switch (Config.OutputFormat) { - case FileFormat::Binary: - return std::make_unique<BinaryWriter>(Obj, Buf); - case FileFormat::IHex: - return std::make_unique<IHexWriter>(Obj, Buf); - default: - return createELFWriter(Config, Obj, Buf, OutputElfType); - } -} - -template <class ELFT> -static Expected<ArrayRef<uint8_t>> -findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) { - auto PhdrsOrErr = In.program_headers(); - if (auto Err = PhdrsOrErr.takeError()) - return createFileError(Config.InputFilename, std::move(Err)); - - for (const auto &Phdr : *PhdrsOrErr) { - if (Phdr.p_type != PT_NOTE) - continue; - Error Err = Error::success(); - for (auto Note : In.notes(Phdr, Err)) - if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) - return Note.getDesc(); - if (Err) - return createFileError(Config.InputFilename, std::move(Err)); - } - +//===- ELFObjcopy.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "ELFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace elf { + +using namespace object; +using namespace ELF; +using SectionPred = std::function<bool(const SectionBase &Sec)>; + +static bool isDebugSection(const SectionBase &Sec) { + return StringRef(Sec.Name).startswith(".debug") || + StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index"; +} + +static bool isDWOSection(const SectionBase &Sec) { + return StringRef(Sec.Name).endswith(".dwo"); +} + +static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { + // We can't remove the section header string table. + if (&Sec == Obj.SectionNames) + return false; + // Short of keeping the string table we want to keep everything that is a DWO + // section and remove everything else. + return !isDWOSection(Sec); +} + +uint64_t getNewShfFlags(SectionFlag AllFlags) { + uint64_t NewFlags = 0; + if (AllFlags & SectionFlag::SecAlloc) + NewFlags |= ELF::SHF_ALLOC; + if (!(AllFlags & SectionFlag::SecReadonly)) + NewFlags |= ELF::SHF_WRITE; + if (AllFlags & SectionFlag::SecCode) + NewFlags |= ELF::SHF_EXECINSTR; + if (AllFlags & SectionFlag::SecMerge) + NewFlags |= ELF::SHF_MERGE; + if (AllFlags & SectionFlag::SecStrings) + NewFlags |= ELF::SHF_STRINGS; + if (AllFlags & SectionFlag::SecExclude) + NewFlags |= ELF::SHF_EXCLUDE; + return NewFlags; +} + +static uint64_t getSectionFlagsPreserveMask(uint64_t OldFlags, + uint64_t NewFlags) { + // Preserve some flags which should not be dropped when setting flags. + // Also, preserve anything OS/processor dependant. + const uint64_t PreserveMask = + (ELF::SHF_COMPRESSED | ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | ELF::SHF_TLS | + ELF::SHF_INFO_LINK) & + ~ELF::SHF_EXCLUDE; + return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask); +} + +static void setSectionFlagsAndType(SectionBase &Sec, SectionFlag Flags) { + Sec.Flags = getSectionFlagsPreserveMask(Sec.Flags, getNewShfFlags(Flags)); + + // In GNU objcopy, certain flags promote SHT_NOBITS to SHT_PROGBITS. This rule + // may promote more non-ALLOC sections than GNU objcopy, but it is fine as + // non-ALLOC SHT_NOBITS sections do not make much sense. + if (Sec.Type == SHT_NOBITS && + (!(Sec.Flags & ELF::SHF_ALLOC) || + Flags & (SectionFlag::SecContents | SectionFlag::SecLoad))) + Sec.Type = SHT_PROGBITS; +} + +static ElfType getOutputElfType(const Binary &Bin) { + // Infer output ELF type from the input ELF object + if (isa<ELFObjectFile<ELF32LE>>(Bin)) + return ELFT_ELF32LE; + if (isa<ELFObjectFile<ELF64LE>>(Bin)) + return ELFT_ELF64LE; + if (isa<ELFObjectFile<ELF32BE>>(Bin)) + return ELFT_ELF32BE; + if (isa<ELFObjectFile<ELF64BE>>(Bin)) + return ELFT_ELF64BE; + llvm_unreachable("Invalid ELFType"); +} + +static ElfType getOutputElfType(const MachineInfo &MI) { + // Infer output ELF type from the binary arch specified + if (MI.Is64Bit) + return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; + else + return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; +} + +static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { + // Depending on the initial ELFT and OutputFormat we need a different Writer. + switch (OutputElfType) { + case ELFT_ELF32LE: + return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); + case ELFT_ELF64LE: + return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); + case ELFT_ELF32BE: + return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); + case ELFT_ELF64BE: + return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); + } + llvm_unreachable("Invalid output format"); +} + +static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { + switch (Config.OutputFormat) { + case FileFormat::Binary: + return std::make_unique<BinaryWriter>(Obj, Buf); + case FileFormat::IHex: + return std::make_unique<IHexWriter>(Obj, Buf); + default: + return createELFWriter(Config, Obj, Buf, OutputElfType); + } +} + +template <class ELFT> +static Expected<ArrayRef<uint8_t>> +findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) { + auto PhdrsOrErr = In.program_headers(); + if (auto Err = PhdrsOrErr.takeError()) + return createFileError(Config.InputFilename, std::move(Err)); + + for (const auto &Phdr : *PhdrsOrErr) { + if (Phdr.p_type != PT_NOTE) + continue; + Error Err = Error::success(); + for (auto Note : In.notes(Phdr, Err)) + if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) + return Note.getDesc(); + if (Err) + return createFileError(Config.InputFilename, std::move(Err)); + } + return createFileError(Config.InputFilename, createStringError(llvm::errc::invalid_argument, "could not find build ID")); -} - -static Expected<ArrayRef<uint8_t>> -findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { - if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) +} + +static Expected<ArrayRef<uint8_t>> +findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { + if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) + else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) + else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) + else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) return findBuildID(Config, O->getELFFile()); - - llvm_unreachable("Bad file format"); -} - -template <class... Ts> + + llvm_unreachable("Bad file format"); +} + +template <class... Ts> static Error makeStringError(std::error_code EC, const Twine &Msg, Ts &&... Args) { - std::string FullMsg = (EC.message() + ": " + Msg).str(); - return createStringError(EC, FullMsg.c_str(), std::forward<Ts>(Args)...); -} - -#define MODEL_8 "%%%%%%%%" -#define MODEL_16 MODEL_8 MODEL_8 -#define MODEL_32 (MODEL_16 MODEL_16) - -static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, - StringRef Suffix, - ArrayRef<uint8_t> BuildIdBytes) { - SmallString<128> Path = Config.BuildIdLinkDir; - sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true)); - if (auto EC = sys::fs::create_directories(Path)) - return createFileError( - Path.str(), - makeStringError(EC, "cannot create build ID link directory")); - - sys::path::append(Path, - llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); - Path += Suffix; - SmallString<128> TmpPath; - // create_hard_link races so we need to link to a temporary path but - // we want to make sure that we choose a filename that does not exist. - // By using 32 model characters we get 128-bits of entropy. It is - // unlikely that this string has ever existed before much less exists - // on this disk or in the current working directory. - // Additionally we prepend the original Path for debugging but also - // because it ensures that we're linking within a directory on the same - // partition on the same device which is critical. It has the added - // win of yet further decreasing the odds of a conflict. - sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath, - /*MakeAbsolute*/ false); - if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // We then atomically rename the link into place which will just move the - // link. If rename fails something is more seriously wrong so just return - // an error. - if (auto EC = sys::fs::rename(TmpPath, Path)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // If `Path` was already a hard-link to the same underlying file then the - // temp file will be left so we need to remove it. Remove will not cause - // an error by default if the file is already gone so just blindly remove - // it rather than checking. - if (auto EC = sys::fs::remove(TmpPath)) { - TmpPath.push_back('\0'); - return makeStringError(EC, "could not remove '%s'", TmpPath.data()); - } - return Error::success(); -} - -static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, - StringRef File, ElfType OutputElfType) { + std::string FullMsg = (EC.message() + ": " + Msg).str(); + return createStringError(EC, FullMsg.c_str(), std::forward<Ts>(Args)...); +} + +#define MODEL_8 "%%%%%%%%" +#define MODEL_16 MODEL_8 MODEL_8 +#define MODEL_32 (MODEL_16 MODEL_16) + +static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, + StringRef Suffix, + ArrayRef<uint8_t> BuildIdBytes) { + SmallString<128> Path = Config.BuildIdLinkDir; + sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true)); + if (auto EC = sys::fs::create_directories(Path)) + return createFileError( + Path.str(), + makeStringError(EC, "cannot create build ID link directory")); + + sys::path::append(Path, + llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); + Path += Suffix; + SmallString<128> TmpPath; + // create_hard_link races so we need to link to a temporary path but + // we want to make sure that we choose a filename that does not exist. + // By using 32 model characters we get 128-bits of entropy. It is + // unlikely that this string has ever existed before much less exists + // on this disk or in the current working directory. + // Additionally we prepend the original Path for debugging but also + // because it ensures that we're linking within a directory on the same + // partition on the same device which is critical. It has the added + // win of yet further decreasing the odds of a conflict. + sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath, + /*MakeAbsolute*/ false); + if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) { + Path.push_back('\0'); + return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), + Path.data()); + } + // We then atomically rename the link into place which will just move the + // link. If rename fails something is more seriously wrong so just return + // an error. + if (auto EC = sys::fs::rename(TmpPath, Path)) { + Path.push_back('\0'); + return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), + Path.data()); + } + // If `Path` was already a hard-link to the same underlying file then the + // temp file will be left so we need to remove it. Remove will not cause + // an error by default if the file is already gone so just blindly remove + // it rather than checking. + if (auto EC = sys::fs::remove(TmpPath)) { + TmpPath.push_back('\0'); + return makeStringError(EC, "could not remove '%s'", TmpPath.data()); + } + return Error::success(); +} + +static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, + StringRef File, ElfType OutputElfType) { Expected<std::unique_ptr<Object>> DWOFile = Reader.create(false); if (!DWOFile) return DWOFile.takeError(); - auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) { + auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) { return onlyKeepDWOPred(**DWOFile, Sec); - }; + }; if (Error E = (*DWOFile)->removeSections(Config.AllowBrokenLinks, OnlyKeepDWOPred)) - return E; - if (Config.OutputArch) { + return E; + if (Config.OutputArch) { (*DWOFile)->Machine = Config.OutputArch.getValue().EMachine; (*DWOFile)->OSABI = Config.OutputArch.getValue().OSABI; - } - FileBuffer FB(File); + } + FileBuffer FB(File); std::unique_ptr<Writer> Writer = createWriter(Config, **DWOFile, FB, OutputElfType); - if (Error E = Writer->finalize()) - return E; - return Writer->write(); -} - -static Error dumpSectionToFile(StringRef SecName, StringRef Filename, - Object &Obj) { - for (auto &Sec : Obj.sections()) { - if (Sec.Name == SecName) { - if (Sec.Type == SHT_NOBITS) - return createStringError(object_error::parse_failed, - "cannot dump section '%s': it has no contents", - SecName.str().c_str()); - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(Filename, Sec.OriginalData.size()); - if (!BufferOrErr) - return BufferOrErr.takeError(); - std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); - std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), - Buf->getBufferStart()); - if (Error E = Buf->commit()) - return E; - return Error::success(); - } - } - return createStringError(object_error::parse_failed, "section '%s' not found", - SecName.str().c_str()); -} - -static bool isCompressable(const SectionBase &Sec) { - return !(Sec.Flags & ELF::SHF_COMPRESSED) && - StringRef(Sec.Name).startswith(".debug"); -} - + if (Error E = Writer->finalize()) + return E; + return Writer->write(); +} + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (auto &Sec : Obj.sections()) { + if (Sec.Name == SecName) { + if (Sec.Type == SHT_NOBITS) + return createStringError(object_error::parse_failed, + "cannot dump section '%s': it has no contents", + SecName.str().c_str()); + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Sec.OriginalData.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), + Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return createStringError(object_error::parse_failed, "section '%s' not found", + SecName.str().c_str()); +} + +static bool isCompressable(const SectionBase &Sec) { + return !(Sec.Flags & ELF::SHF_COMPRESSED) && + StringRef(Sec.Name).startswith(".debug"); +} + static Error replaceDebugSections( - Object &Obj, SectionPred &RemovePred, + Object &Obj, SectionPred &RemovePred, function_ref<bool(const SectionBase &)> ShouldReplace, function_ref<Expected<SectionBase *>(const SectionBase *)> AddSection) { - // Build a list of the debug sections we are going to replace. + // Build a list of the debug sections we are going to replace. // We can't call `AddSection` while iterating over sections, - // because it would mutate the sections array. - SmallVector<SectionBase *, 13> ToReplace; - for (auto &Sec : Obj.sections()) + // because it would mutate the sections array. + SmallVector<SectionBase *, 13> ToReplace; + for (auto &Sec : Obj.sections()) if (ShouldReplace(Sec)) - ToReplace.push_back(&Sec); - - // Build a mapping from original section to a new one. - DenseMap<SectionBase *, SectionBase *> FromTo; + ToReplace.push_back(&Sec); + + // Build a mapping from original section to a new one. + DenseMap<SectionBase *, SectionBase *> FromTo; for (SectionBase *S : ToReplace) { Expected<SectionBase *> NewSection = AddSection(S); if (!NewSection) return NewSection.takeError(); - + FromTo[S] = *NewSection; } - // Now we want to update the target sections of relocation - // sections. Also we will update the relocations themselves - // to update the symbol references. - for (auto &Sec : Obj.sections()) - Sec.replaceSectionReferences(FromTo); - + // Now we want to update the target sections of relocation + // sections. Also we will update the relocations themselves + // to update the symbol references. + for (auto &Sec : Obj.sections()) + Sec.replaceSectionReferences(FromTo); + RemovePred = [ShouldReplace, RemovePred](const SectionBase &Sec) { return ShouldReplace(Sec) || RemovePred(Sec); - }; + }; return Error::success(); -} - -static bool isUnneededSymbol(const Symbol &Sym) { - return !Sym.Referenced && - (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && - Sym.Type != STT_SECTION; -} - -static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { - // TODO: update or remove symbols only if there is an option that affects - // them. - if (!Obj.SymbolTable) - return Error::success(); - - Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { - // Common and undefined symbols don't make sense as local symbols, and can - // even cause crashes if we localize those, so skip them. - if (!Sym.isCommon() && Sym.getShndx() != SHN_UNDEF && - ((Config.LocalizeHidden && - (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || - Config.SymbolsToLocalize.matches(Sym.Name))) - Sym.Binding = STB_LOCAL; - - // Note: these two globalize flags have very similar names but different - // meanings: - // - // --globalize-symbol: promote a symbol to global - // --keep-global-symbol: all symbols except for these should be made local - // - // If --globalize-symbol is specified for a given symbol, it will be - // global in the output file even if it is not included via - // --keep-global-symbol. Because of that, make sure to check - // --globalize-symbol second. - if (!Config.SymbolsToKeepGlobal.empty() && - !Config.SymbolsToKeepGlobal.matches(Sym.Name) && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_LOCAL; - - if (Config.SymbolsToGlobalize.matches(Sym.Name) && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_GLOBAL; - - if (Config.SymbolsToWeaken.matches(Sym.Name) && Sym.Binding == STB_GLOBAL) - Sym.Binding = STB_WEAK; - - if (Config.Weaken && Sym.Binding == STB_GLOBAL && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_WEAK; - - const auto I = Config.SymbolsToRename.find(Sym.Name); - if (I != Config.SymbolsToRename.end()) - Sym.Name = std::string(I->getValue()); - - if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) - Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); - }); - - // The purpose of this loop is to mark symbols referenced by sections - // (like GroupSection or RelocationSection). This way, we know which - // symbols are still 'needed' and which are not. - if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty() || - !Config.OnlySection.empty()) { - for (SectionBase &Sec : Obj.sections()) - Sec.markSymbols(); - } - - auto RemoveSymbolsPred = [&](const Symbol &Sym) { - if (Config.SymbolsToKeep.matches(Sym.Name) || - (Config.KeepFileSymbols && Sym.Type == STT_FILE)) - return false; - - if ((Config.DiscardMode == DiscardType::All || - (Config.DiscardMode == DiscardType::Locals && - StringRef(Sym.Name).startswith(".L"))) && - Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF && - Sym.Type != STT_FILE && Sym.Type != STT_SECTION) - return true; - - if (Config.StripAll || Config.StripAllGNU) - return true; - - if (Config.StripDebug && Sym.Type == STT_FILE) - return true; - - if (Config.SymbolsToRemove.matches(Sym.Name)) - return true; - - if ((Config.StripUnneeded || - Config.UnneededSymbolsToRemove.matches(Sym.Name)) && - (!Obj.isRelocatable() || isUnneededSymbol(Sym))) - return true; - - // We want to remove undefined symbols if all references have been stripped. - if (!Config.OnlySection.empty() && !Sym.Referenced && - Sym.getShndx() == SHN_UNDEF) - return true; - - return false; - }; - - return Obj.removeSymbols(RemoveSymbolsPred); -} - -static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { - SectionPred RemovePred = [](const SectionBase &) { return false; }; - - // Removes: - if (!Config.ToRemove.empty()) { - RemovePred = [&Config](const SectionBase &Sec) { - return Config.ToRemove.matches(Sec.Name); - }; - } - - if (Config.StripDWO || !Config.SplitDWO.empty()) - RemovePred = [RemovePred](const SectionBase &Sec) { - return isDWOSection(Sec) || RemovePred(Sec); - }; - - if (Config.ExtractDWO) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); - }; - - if (Config.StripAllGNU) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if ((Sec.Flags & SHF_ALLOC) != 0) - return false; - if (&Sec == Obj.SectionNames) - return false; - switch (Sec.Type) { - case SHT_SYMTAB: - case SHT_REL: - case SHT_RELA: - case SHT_STRTAB: - return true; - } - return isDebugSection(Sec); - }; - - if (Config.StripSections) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || Sec.ParentSegment == nullptr; - }; - } - - if (Config.StripDebug || Config.StripUnneeded) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || isDebugSection(Sec); - }; - } - - if (Config.StripNonAlloc) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - return (Sec.Flags & SHF_ALLOC) == 0 && Sec.ParentSegment == nullptr; - }; - - if (Config.StripAll) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - if (StringRef(Sec.Name).startswith(".gnu.warning")) - return false; - // We keep the .ARM.attribute section to maintain compatibility - // with Debian derived distributions. This is a bug in their - // patchset as documented here: - // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=943798 - if (Sec.Type == SHT_ARM_ATTRIBUTES) - return false; - if (Sec.ParentSegment != nullptr) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - if (Config.ExtractPartition || Config.ExtractMainPartition) { - RemovePred = [RemovePred](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (Sec.Type == SHT_LLVM_PART_EHDR || Sec.Type == SHT_LLVM_PART_PHDR) - return true; - return (Sec.Flags & SHF_ALLOC) != 0 && !Sec.ParentSegment; - }; - } - - // Explicit copies: - if (!Config.OnlySection.empty()) { - RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (Config.OnlySection.matches(Sec.Name)) - return false; - - // Allow all implicit removes. - if (RemovePred(Sec)) - return true; - - // Keep special sections. - if (Obj.SectionNames == &Sec) - return false; - if (Obj.SymbolTable == &Sec || - (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) - return false; - - // Remove everything else. - return true; - }; - } - - if (!Config.KeepSection.empty()) { - RemovePred = [&Config, RemovePred](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (Config.KeepSection.matches(Sec.Name)) - return false; - // Otherwise defer to RemovePred. - return RemovePred(Sec); - }; - } - - // This has to be the last predicate assignment. - // If the option --keep-symbol has been specified - // and at least one of those symbols is present - // (equivalently, the updated symbol table is not empty) - // the symbol table and the string table should not be removed. - if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) && - Obj.SymbolTable && !Obj.SymbolTable->empty()) { - RemovePred = [&Obj, RemovePred](const SectionBase &Sec) { - if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab()) - return false; - return RemovePred(Sec); - }; - } - +} + +static bool isUnneededSymbol(const Symbol &Sym) { + return !Sym.Referenced && + (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && + Sym.Type != STT_SECTION; +} + +static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { + // TODO: update or remove symbols only if there is an option that affects + // them. + if (!Obj.SymbolTable) + return Error::success(); + + Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { + // Common and undefined symbols don't make sense as local symbols, and can + // even cause crashes if we localize those, so skip them. + if (!Sym.isCommon() && Sym.getShndx() != SHN_UNDEF && + ((Config.LocalizeHidden && + (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || + Config.SymbolsToLocalize.matches(Sym.Name))) + Sym.Binding = STB_LOCAL; + + // Note: these two globalize flags have very similar names but different + // meanings: + // + // --globalize-symbol: promote a symbol to global + // --keep-global-symbol: all symbols except for these should be made local + // + // If --globalize-symbol is specified for a given symbol, it will be + // global in the output file even if it is not included via + // --keep-global-symbol. Because of that, make sure to check + // --globalize-symbol second. + if (!Config.SymbolsToKeepGlobal.empty() && + !Config.SymbolsToKeepGlobal.matches(Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_LOCAL; + + if (Config.SymbolsToGlobalize.matches(Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_GLOBAL; + + if (Config.SymbolsToWeaken.matches(Sym.Name) && Sym.Binding == STB_GLOBAL) + Sym.Binding = STB_WEAK; + + if (Config.Weaken && Sym.Binding == STB_GLOBAL && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_WEAK; + + const auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = std::string(I->getValue()); + + if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) + Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); + }); + + // The purpose of this loop is to mark symbols referenced by sections + // (like GroupSection or RelocationSection). This way, we know which + // symbols are still 'needed' and which are not. + if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty() || + !Config.OnlySection.empty()) { + for (SectionBase &Sec : Obj.sections()) + Sec.markSymbols(); + } + + auto RemoveSymbolsPred = [&](const Symbol &Sym) { + if (Config.SymbolsToKeep.matches(Sym.Name) || + (Config.KeepFileSymbols && Sym.Type == STT_FILE)) + return false; + + if ((Config.DiscardMode == DiscardType::All || + (Config.DiscardMode == DiscardType::Locals && + StringRef(Sym.Name).startswith(".L"))) && + Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF && + Sym.Type != STT_FILE && Sym.Type != STT_SECTION) + return true; + + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (Config.StripDebug && Sym.Type == STT_FILE) + return true; + + if (Config.SymbolsToRemove.matches(Sym.Name)) + return true; + + if ((Config.StripUnneeded || + Config.UnneededSymbolsToRemove.matches(Sym.Name)) && + (!Obj.isRelocatable() || isUnneededSymbol(Sym))) + return true; + + // We want to remove undefined symbols if all references have been stripped. + if (!Config.OnlySection.empty() && !Sym.Referenced && + Sym.getShndx() == SHN_UNDEF) + return true; + + return false; + }; + + return Obj.removeSymbols(RemoveSymbolsPred); +} + +static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const SectionBase &) { return false; }; + + // Removes: + if (!Config.ToRemove.empty()) { + RemovePred = [&Config](const SectionBase &Sec) { + return Config.ToRemove.matches(Sec.Name); + }; + } + + if (Config.StripDWO || !Config.SplitDWO.empty()) + RemovePred = [RemovePred](const SectionBase &Sec) { + return isDWOSection(Sec) || RemovePred(Sec); + }; + + if (Config.ExtractDWO) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); + }; + + if (Config.StripAllGNU) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if ((Sec.Flags & SHF_ALLOC) != 0) + return false; + if (&Sec == Obj.SectionNames) + return false; + switch (Sec.Type) { + case SHT_SYMTAB: + case SHT_REL: + case SHT_RELA: + case SHT_STRTAB: + return true; + } + return isDebugSection(Sec); + }; + + if (Config.StripSections) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || Sec.ParentSegment == nullptr; + }; + } + + if (Config.StripDebug || Config.StripUnneeded) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripNonAlloc) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + return (Sec.Flags & SHF_ALLOC) == 0 && Sec.ParentSegment == nullptr; + }; + + if (Config.StripAll) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + if (StringRef(Sec.Name).startswith(".gnu.warning")) + return false; + // We keep the .ARM.attribute section to maintain compatibility + // with Debian derived distributions. This is a bug in their + // patchset as documented here: + // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=943798 + if (Sec.Type == SHT_ARM_ATTRIBUTES) + return false; + if (Sec.ParentSegment != nullptr) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + if (Config.ExtractPartition || Config.ExtractMainPartition) { + RemovePred = [RemovePred](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (Sec.Type == SHT_LLVM_PART_EHDR || Sec.Type == SHT_LLVM_PART_PHDR) + return true; + return (Sec.Flags & SHF_ALLOC) != 0 && !Sec.ParentSegment; + }; + } + + // Explicit copies: + if (!Config.OnlySection.empty()) { + RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (Config.OnlySection.matches(Sec.Name)) + return false; + + // Allow all implicit removes. + if (RemovePred(Sec)) + return true; + + // Keep special sections. + if (Obj.SectionNames == &Sec) + return false; + if (Obj.SymbolTable == &Sec || + (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) + return false; + + // Remove everything else. + return true; + }; + } + + if (!Config.KeepSection.empty()) { + RemovePred = [&Config, RemovePred](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (Config.KeepSection.matches(Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + // This has to be the last predicate assignment. + // If the option --keep-symbol has been specified + // and at least one of those symbols is present + // (equivalently, the updated symbol table is not empty) + // the symbol table and the string table should not be removed. + if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) && + Obj.SymbolTable && !Obj.SymbolTable->empty()) { + RemovePred = [&Obj, RemovePred](const SectionBase &Sec) { + if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab()) + return false; + return RemovePred(Sec); + }; + } + if (Config.CompressionType != DebugCompressionType::None) { if (Error Err = replaceDebugSections( Obj, RemovePred, isCompressable, @@ -595,7 +595,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { CompressedSection::create(*S, Config.CompressionType); if (!NewSection) return NewSection.takeError(); - + return &Obj.addSection<CompressedSection>(std::move(*NewSection)); })) return Err; @@ -610,156 +610,156 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { return Err; } - return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); -} - -// This function handles the high level operations of GNU objcopy including -// handling command line options. It's important to outline certain properties -// we expect to hold of the command line operations. Any operation that "keeps" -// should keep regardless of a remove. Additionally any removal should respect -// any previous removals. Lastly whether or not something is removed shouldn't -// depend a) on the order the options occur in or b) on some opaque priority -// system. The only priority is that keeps/copies overrule removes. -static Error handleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - if (Config.StripSwiftSymbols) - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for ELF"); - if (!Config.SplitDWO.empty()) - if (Error E = - splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType)) - return E; - - if (Config.OutputArch) { - Obj.Machine = Config.OutputArch.getValue().EMachine; - Obj.OSABI = Config.OutputArch.getValue().OSABI; - } - - // Dump sections before add/remove for compatibility with GNU objcopy. - for (StringRef Flag : Config.DumpSection) { - StringRef SectionName; - StringRef FileName; - std::tie(SectionName, FileName) = Flag.split('='); - if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) - return E; - } - - // It is important to remove the sections first. For example, we want to - // remove the relocation sections before removing the symbols. That allows - // us to avoid reporting the inappropriate errors about removing symbols - // named in relocations. - if (Error E = replaceAndRemoveSections(Config, Obj)) - return E; - - if (Error E = updateAndRemoveSymbols(Config, Obj)) - return E; - - if (!Config.SectionsToRename.empty()) { - for (SectionBase &Sec : Obj.sections()) { - const auto Iter = Config.SectionsToRename.find(Sec.Name); - if (Iter != Config.SectionsToRename.end()) { - const SectionRename &SR = Iter->second; - Sec.Name = std::string(SR.NewName); - if (SR.NewFlags.hasValue()) - setSectionFlagsAndType(Sec, SR.NewFlags.getValue()); - } - } - } - - // Add a prefix to allocated sections and their relocation sections. This - // should be done after renaming the section by Config.SectionToRename to - // imitate the GNU objcopy behavior. - if (!Config.AllocSectionsPrefix.empty()) { - DenseSet<SectionBase *> PrefixedSections; - for (SectionBase &Sec : Obj.sections()) { - if (Sec.Flags & SHF_ALLOC) { - Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str(); - PrefixedSections.insert(&Sec); - } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) { - // Rename relocation sections associated to the allocated sections. - // For example, if we rename .text to .prefix.text, we also rename - // .rel.text to .rel.prefix.text. - // - // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled - // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not - // .rela.prefix.plt since GNU objcopy does so. - const SectionBase *TargetSec = RelocSec->getSection(); - if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) { - StringRef prefix; - switch (Sec.Type) { - case SHT_REL: - prefix = ".rel"; - break; - case SHT_RELA: - prefix = ".rela"; - break; - default: - llvm_unreachable("not a relocation section"); - } - - // If the relocation section comes *after* the target section, we - // don't add Config.AllocSectionsPrefix because we've already added - // the prefix to TargetSec->Name. Otherwise, if the relocation - // section comes *before* the target section, we add the prefix. - if (PrefixedSections.count(TargetSec)) - Sec.Name = (prefix + TargetSec->Name).str(); - else - Sec.Name = - (prefix + Config.AllocSectionsPrefix + TargetSec->Name).str(); - } - } - } - } - - if (!Config.SetSectionAlignment.empty()) { - for (SectionBase &Sec : Obj.sections()) { - auto I = Config.SetSectionAlignment.find(Sec.Name); - if (I != Config.SetSectionAlignment.end()) - Sec.Align = I->second; - } - } - - if (Config.OnlyKeepDebug) - for (auto &Sec : Obj.sections()) - if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE) - Sec.Type = SHT_NOBITS; - - for (const auto &Flag : Config.AddSection) { - std::pair<StringRef, StringRef> SecPair = Flag.split("="); - StringRef SecName = SecPair.first; - StringRef File = SecPair.second; - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFile(File); - if (!BufOrErr) - return createFileError(File, errorCodeToError(BufOrErr.getError())); - std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); - ArrayRef<uint8_t> Data( - reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), - Buf->getBufferSize()); - OwnedDataSection &NewSection = - Obj.addSection<OwnedDataSection>(SecName, Data); - if (SecName.startswith(".note") && SecName != ".note.GNU-stack") - NewSection.Type = SHT_NOTE; - } - - if (!Config.AddGnuDebugLink.empty()) - Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink, - Config.GnuDebugLinkCRC32); - - // If the symbol table was previously removed, we need to create a new one - // before adding new symbols. + return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); +} + +// This function handles the high level operations of GNU objcopy including +// handling command line options. It's important to outline certain properties +// we expect to hold of the command line operations. Any operation that "keeps" +// should keep regardless of a remove. Additionally any removal should respect +// any previous removals. Lastly whether or not something is removed shouldn't +// depend a) on the order the options occur in or b) on some opaque priority +// system. The only priority is that keeps/copies overrule removes. +static Error handleArgs(const CopyConfig &Config, Object &Obj, + const Reader &Reader, ElfType OutputElfType) { + if (Config.StripSwiftSymbols) + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for ELF"); + if (!Config.SplitDWO.empty()) + if (Error E = + splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType)) + return E; + + if (Config.OutputArch) { + Obj.Machine = Config.OutputArch.getValue().EMachine; + Obj.OSABI = Config.OutputArch.getValue().OSABI; + } + + // Dump sections before add/remove for compatibility with GNU objcopy. + for (StringRef Flag : Config.DumpSection) { + StringRef SectionName; + StringRef FileName; + std::tie(SectionName, FileName) = Flag.split('='); + if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) + return E; + } + + // It is important to remove the sections first. For example, we want to + // remove the relocation sections before removing the symbols. That allows + // us to avoid reporting the inappropriate errors about removing symbols + // named in relocations. + if (Error E = replaceAndRemoveSections(Config, Obj)) + return E; + + if (Error E = updateAndRemoveSymbols(Config, Obj)) + return E; + + if (!Config.SectionsToRename.empty()) { + for (SectionBase &Sec : Obj.sections()) { + const auto Iter = Config.SectionsToRename.find(Sec.Name); + if (Iter != Config.SectionsToRename.end()) { + const SectionRename &SR = Iter->second; + Sec.Name = std::string(SR.NewName); + if (SR.NewFlags.hasValue()) + setSectionFlagsAndType(Sec, SR.NewFlags.getValue()); + } + } + } + + // Add a prefix to allocated sections and their relocation sections. This + // should be done after renaming the section by Config.SectionToRename to + // imitate the GNU objcopy behavior. + if (!Config.AllocSectionsPrefix.empty()) { + DenseSet<SectionBase *> PrefixedSections; + for (SectionBase &Sec : Obj.sections()) { + if (Sec.Flags & SHF_ALLOC) { + Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str(); + PrefixedSections.insert(&Sec); + } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) { + // Rename relocation sections associated to the allocated sections. + // For example, if we rename .text to .prefix.text, we also rename + // .rel.text to .rel.prefix.text. + // + // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled + // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not + // .rela.prefix.plt since GNU objcopy does so. + const SectionBase *TargetSec = RelocSec->getSection(); + if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) { + StringRef prefix; + switch (Sec.Type) { + case SHT_REL: + prefix = ".rel"; + break; + case SHT_RELA: + prefix = ".rela"; + break; + default: + llvm_unreachable("not a relocation section"); + } + + // If the relocation section comes *after* the target section, we + // don't add Config.AllocSectionsPrefix because we've already added + // the prefix to TargetSec->Name. Otherwise, if the relocation + // section comes *before* the target section, we add the prefix. + if (PrefixedSections.count(TargetSec)) + Sec.Name = (prefix + TargetSec->Name).str(); + else + Sec.Name = + (prefix + Config.AllocSectionsPrefix + TargetSec->Name).str(); + } + } + } + } + + if (!Config.SetSectionAlignment.empty()) { + for (SectionBase &Sec : Obj.sections()) { + auto I = Config.SetSectionAlignment.find(Sec.Name); + if (I != Config.SetSectionAlignment.end()) + Sec.Align = I->second; + } + } + + if (Config.OnlyKeepDebug) + for (auto &Sec : Obj.sections()) + if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE) + Sec.Type = SHT_NOBITS; + + for (const auto &Flag : Config.AddSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(File); + if (!BufOrErr) + return createFileError(File, errorCodeToError(BufOrErr.getError())); + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + ArrayRef<uint8_t> Data( + reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()); + OwnedDataSection &NewSection = + Obj.addSection<OwnedDataSection>(SecName, Data); + if (SecName.startswith(".note") && SecName != ".note.GNU-stack") + NewSection.Type = SHT_NOTE; + } + + if (!Config.AddGnuDebugLink.empty()) + Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink, + Config.GnuDebugLinkCRC32); + + // If the symbol table was previously removed, we need to create a new one + // before adding new symbols. if (!Obj.SymbolTable && !Config.ELF->SymbolsToAdd.empty()) if (Error E = Obj.addNewSymbolTable()) return E; - - for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { - SectionBase *Sec = Obj.findSection(SI.SectionName); - uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; - Obj.SymbolTable->addSymbol( - SI.SymbolName, SI.Bind, SI.Type, Sec, Value, SI.Visibility, - Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); - } - + + for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { + SectionBase *Sec = Obj.findSection(SI.SectionName); + uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; + Obj.SymbolTable->addSymbol( + SI.SymbolName, SI.Bind, SI.Type, Sec, Value, SI.Visibility, + Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); + } + // --set-section-flags works with sections added by --add-section. if (!Config.SetSectionFlags.empty()) { for (auto &Sec : Obj.sections()) { @@ -771,98 +771,98 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, } } - if (Config.EntryExpr) - Obj.Entry = Config.EntryExpr(Obj.Entry); - return Error::success(); -} - -static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, - ElfType OutputElfType) { - std::unique_ptr<Writer> Writer = - createWriter(Config, Obj, Out, OutputElfType); - if (Error E = Writer->finalize()) - return E; - return Writer->write(); -} - -Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - IHexReader Reader(&In); + if (Config.EntryExpr) + Obj.Entry = Config.EntryExpr(Obj.Entry); + return Error::success(); +} + +static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, + ElfType OutputElfType) { + std::unique_ptr<Writer> Writer = + createWriter(Config, Obj, Out, OutputElfType); + if (Error E = Writer->finalize()) + return E; + return Writer->write(); +} + +Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + IHexReader Reader(&In); Expected<std::unique_ptr<Object>> Obj = Reader.create(true); if (!Obj) return Obj.takeError(); - const ElfType OutputElfType = + const ElfType OutputElfType = getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) - return E; + return E; return writeOutput(Config, **Obj, Out, OutputElfType); -} - -Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - uint8_t NewSymbolVisibility = - Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); - BinaryReader Reader(&In, NewSymbolVisibility); +} + +Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + uint8_t NewSymbolVisibility = + Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); + BinaryReader Reader(&In, NewSymbolVisibility); Expected<std::unique_ptr<Object>> Obj = Reader.create(true); if (!Obj) return Obj.takeError(); - - // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch - // (-B<arch>). - const ElfType OutputElfType = - getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); + + // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch + // (-B<arch>). + const ElfType OutputElfType = + getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) - return E; + return E; return writeOutput(Config, **Obj, Out, OutputElfType); -} - -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out) { - ELFReader Reader(&In, Config.ExtractPartition); +} + +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out) { + ELFReader Reader(&In, Config.ExtractPartition); Expected<std::unique_ptr<Object>> Obj = Reader.create(!Config.SymbolsToAdd.empty()); if (!Obj) return Obj.takeError(); - // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input. - const ElfType OutputElfType = - Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) - : getOutputElfType(In); - ArrayRef<uint8_t> BuildIdBytes; - - if (!Config.BuildIdLinkDir.empty()) { - auto BuildIdBytesOrErr = findBuildID(Config, In); - if (auto E = BuildIdBytesOrErr.takeError()) - return E; - BuildIdBytes = *BuildIdBytesOrErr; - - if (BuildIdBytes.size() < 2) - return createFileError( - Config.InputFilename, - createStringError(object_error::parse_failed, - "build ID is smaller than two bytes")); - } - - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) - if (Error E = - linkToBuildIdDir(Config, Config.InputFilename, - Config.BuildIdLinkInput.getValue(), BuildIdBytes)) - return E; - + // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input. + const ElfType OutputElfType = + Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) + : getOutputElfType(In); + ArrayRef<uint8_t> BuildIdBytes; + + if (!Config.BuildIdLinkDir.empty()) { + auto BuildIdBytesOrErr = findBuildID(Config, In); + if (auto E = BuildIdBytesOrErr.takeError()) + return E; + BuildIdBytes = *BuildIdBytesOrErr; + + if (BuildIdBytes.size() < 2) + return createFileError( + Config.InputFilename, + createStringError(object_error::parse_failed, + "build ID is smaller than two bytes")); + } + + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) + if (Error E = + linkToBuildIdDir(Config, Config.InputFilename, + Config.BuildIdLinkInput.getValue(), BuildIdBytes)) + return E; + if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) - return createFileError(Config.InputFilename, std::move(E)); - + return createFileError(Config.InputFilename, std::move(E)); + if (Error E = writeOutput(Config, **Obj, Out, OutputElfType)) - return createFileError(Config.InputFilename, std::move(E)); - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) - if (Error E = - linkToBuildIdDir(Config, Config.OutputFilename, - Config.BuildIdLinkOutput.getValue(), BuildIdBytes)) - return createFileError(Config.OutputFilename, std::move(E)); - - return Error::success(); -} - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm + return createFileError(Config.InputFilename, std::move(E)); + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) + if (Error E = + linkToBuildIdDir(Config, Config.OutputFilename, + Config.BuildIdLinkOutput.getValue(), BuildIdBytes)) + return createFileError(Config.OutputFilename, std::move(E)); + + return Error::success(); +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.h b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.h index e13e237e29..619a0bd908 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -1,36 +1,36 @@ -//===- ELFObjcopy.h ---------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H -#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H - -namespace llvm { -class Error; -class MemoryBuffer; - -namespace object { -class ELFObjectFileBase; -} // end namespace object - -namespace objcopy { -struct CopyConfig; -class Buffer; - -namespace elf { -Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out); - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H +//===- ELFObjcopy.h ---------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H + +namespace llvm { +class Error; +class MemoryBuffer; + +namespace object { +class ELFObjectFileBase; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace elf { +Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out); +Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out); +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out); + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.cpp index 0ff82f951b..d3094d351b 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.cpp @@ -1,551 +1,551 @@ -//===- Object.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Object.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/BinaryFormat/ELF.h" -#include "llvm/MC/MCTargetOptions.h" +//===- Object.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Object.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/ELF.h" -#include "llvm/Object/ELFObjectFile.h" -#include "llvm/Support/Compression.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/Path.h" -#include <algorithm> -#include <cstddef> -#include <cstdint> -#include <iterator> -#include <unordered_set> -#include <utility> -#include <vector> - -namespace llvm { -namespace objcopy { -namespace elf { - -using namespace object; -using namespace ELF; - -template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { - uint8_t *B = Buf.getBufferStart() + Obj.ProgramHdrSegment.Offset + - Seg.Index * sizeof(Elf_Phdr); - Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B); - Phdr.p_type = Seg.Type; - Phdr.p_flags = Seg.Flags; - Phdr.p_offset = Seg.Offset; - Phdr.p_vaddr = Seg.VAddr; - Phdr.p_paddr = Seg.PAddr; - Phdr.p_filesz = Seg.FileSize; - Phdr.p_memsz = Seg.MemSize; - Phdr.p_align = Seg.Align; -} - -Error SectionBase::removeSectionReferences( +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace elf { + +using namespace object; +using namespace ELF; + +template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { + uint8_t *B = Buf.getBufferStart() + Obj.ProgramHdrSegment.Offset + + Seg.Index * sizeof(Elf_Phdr); + Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B); + Phdr.p_type = Seg.Type; + Phdr.p_flags = Seg.Flags; + Phdr.p_offset = Seg.Offset; + Phdr.p_vaddr = Seg.VAddr; + Phdr.p_paddr = Seg.PAddr; + Phdr.p_filesz = Seg.FileSize; + Phdr.p_memsz = Seg.MemSize; + Phdr.p_align = Seg.Align; +} + +Error SectionBase::removeSectionReferences( bool, function_ref<bool(const SectionBase *)>) { - return Error::success(); -} - + return Error::success(); +} + Error SectionBase::removeSymbols(function_ref<bool(const Symbol &)>) { - return Error::success(); -} - + return Error::success(); +} + Error SectionBase::initialize(SectionTableRef) { return Error::success(); } -void SectionBase::finalize() {} -void SectionBase::markSymbols() {} -void SectionBase::replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &) {} -void SectionBase::onRemove() {} - -template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { - uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; - Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); - Shdr.sh_name = Sec.NameIndex; - Shdr.sh_type = Sec.Type; - Shdr.sh_flags = Sec.Flags; - Shdr.sh_addr = Sec.Addr; - Shdr.sh_offset = Sec.Offset; - Shdr.sh_size = Sec.Size; - Shdr.sh_link = Sec.Link; - Shdr.sh_info = Sec.Info; - Shdr.sh_addralign = Sec.Align; - Shdr.sh_entsize = Sec.EntrySize; -} - +void SectionBase::finalize() {} +void SectionBase::markSymbols() {} +void SectionBase::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &) {} +void SectionBase::onRemove() {} + +template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { + uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; + Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); + Shdr.sh_name = Sec.NameIndex; + Shdr.sh_type = Sec.Type; + Shdr.sh_flags = Sec.Flags; + Shdr.sh_addr = Sec.Addr; + Shdr.sh_offset = Sec.Offset; + Shdr.sh_size = Sec.Size; + Shdr.sh_link = Sec.Link; + Shdr.sh_info = Sec.Info; + Shdr.sh_addralign = Sec.Align; + Shdr.sh_entsize = Sec.EntrySize; +} + template <class ELFT> Error ELFSectionSizer<ELFT>::visit(Section &) { return Error::success(); } - + template <class ELFT> Error ELFSectionSizer<ELFT>::visit(OwnedDataSection &) { return Error::success(); } - + template <class ELFT> Error ELFSectionSizer<ELFT>::visit(StringTableSection &) { return Error::success(); } - -template <class ELFT> + +template <class ELFT> Error ELFSectionSizer<ELFT>::visit(DynamicRelocationSection &) { return Error::success(); } - -template <class ELFT> + +template <class ELFT> Error ELFSectionSizer<ELFT>::visit(SymbolTableSection &Sec) { - Sec.EntrySize = sizeof(Elf_Sym); - Sec.Size = Sec.Symbols.size() * Sec.EntrySize; - // Align to the largest field in Elf_Sym. - Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); + Sec.EntrySize = sizeof(Elf_Sym); + Sec.Size = Sec.Symbols.size() * Sec.EntrySize; + // Align to the largest field in Elf_Sym. + Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); return Error::success(); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionSizer<ELFT>::visit(RelocationSection &Sec) { - Sec.EntrySize = Sec.Type == SHT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela); - Sec.Size = Sec.Relocations.size() * Sec.EntrySize; - // Align to the largest field in Elf_Rel(a). - Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); + Sec.EntrySize = Sec.Type == SHT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela); + Sec.Size = Sec.Relocations.size() * Sec.EntrySize; + // Align to the largest field in Elf_Rel(a). + Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); return Error::success(); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &) { return Error::success(); } - + template <class ELFT> Error ELFSectionSizer<ELFT>::visit(GroupSection &Sec) { - Sec.Size = sizeof(Elf_Word) + Sec.GroupMembers.size() * sizeof(Elf_Word); + Sec.Size = sizeof(Elf_Word) + Sec.GroupMembers.size() * sizeof(Elf_Word); return Error::success(); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionSizer<ELFT>::visit(SectionIndexSection &) { return Error::success(); } - + template <class ELFT> Error ELFSectionSizer<ELFT>::visit(CompressedSection &) { return Error::success(); } - -template <class ELFT> + +template <class ELFT> Error ELFSectionSizer<ELFT>::visit(DecompressedSection &) { return Error::success(); } - + Error BinarySectionWriter::visit(const SectionIndexSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write symbol section index table '" + Sec.Name + "' "); -} - +} + Error BinarySectionWriter::visit(const SymbolTableSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write symbol table '" + Sec.Name + "' out to binary"); -} - +} + Error BinarySectionWriter::visit(const RelocationSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write relocation section '" + Sec.Name + "' out to binary"); -} - +} + Error BinarySectionWriter::visit(const GnuDebugLinkSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write '" + Sec.Name + "' out to binary"); -} - +} + Error BinarySectionWriter::visit(const GroupSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write '" + Sec.Name + "' out to binary"); -} - +} + Error SectionWriter::visit(const Section &Sec) { - if (Sec.Type != SHT_NOBITS) - llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); + if (Sec.Type != SHT_NOBITS) + llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); return Error::success(); -} - -static bool addressOverflows32bit(uint64_t Addr) { - // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok - return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX; -} - -template <class T> static T checkedGetHex(StringRef S) { - T Value; - bool Fail = S.getAsInteger(16, Value); - assert(!Fail); - (void)Fail; - return Value; -} - -// Fills exactly Len bytes of buffer with hexadecimal characters -// representing value 'X' -template <class T, class Iterator> -static Iterator utohexstr(T X, Iterator It, size_t Len) { - // Fill range with '0' - std::fill(It, It + Len, '0'); - - for (long I = Len - 1; I >= 0; --I) { - unsigned char Mod = static_cast<unsigned char>(X) & 15; - *(It + I) = hexdigit(Mod, false); - X >>= 4; - } - assert(X == 0); - return It + Len; -} - -uint8_t IHexRecord::getChecksum(StringRef S) { - assert((S.size() & 1) == 0); - uint8_t Checksum = 0; - while (!S.empty()) { - Checksum += checkedGetHex<uint8_t>(S.take_front(2)); - S = S.drop_front(2); - } - return -Checksum; -} - -IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr, - ArrayRef<uint8_t> Data) { - IHexLineData Line(getLineLength(Data.size())); - assert(Line.size()); - auto Iter = Line.begin(); - *Iter++ = ':'; - Iter = utohexstr(Data.size(), Iter, 2); - Iter = utohexstr(Addr, Iter, 4); - Iter = utohexstr(Type, Iter, 2); - for (uint8_t X : Data) - Iter = utohexstr(X, Iter, 2); - StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter)); - Iter = utohexstr(getChecksum(S), Iter, 2); - *Iter++ = '\r'; - *Iter++ = '\n'; - assert(Iter == Line.end()); - return Line; -} - -static Error checkRecord(const IHexRecord &R) { - switch (R.Type) { - case IHexRecord::Data: - if (R.HexData.size() == 0) - return createStringError( - errc::invalid_argument, - "zero data length is not allowed for data records"); - break; - case IHexRecord::EndOfFile: - break; - case IHexRecord::SegmentAddr: - // 20-bit segment address. Data length must be 2 bytes - // (4 bytes in hex) - if (R.HexData.size() != 4) - return createStringError( - errc::invalid_argument, - "segment address data should be 2 bytes in size"); - break; - case IHexRecord::StartAddr80x86: - case IHexRecord::StartAddr: - if (R.HexData.size() != 8) - return createStringError(errc::invalid_argument, - "start address data should be 4 bytes in size"); - // According to Intel HEX specification '03' record - // only specifies the code address within the 20-bit - // segmented address space of the 8086/80186. This - // means 12 high order bits should be zeroes. - if (R.Type == IHexRecord::StartAddr80x86 && - R.HexData.take_front(3) != "000") - return createStringError(errc::invalid_argument, - "start address exceeds 20 bit for 80x86"); - break; - case IHexRecord::ExtendedAddr: - // 16-31 bits of linear base address - if (R.HexData.size() != 4) - return createStringError( - errc::invalid_argument, - "extended address data should be 2 bytes in size"); - break; - default: - // Unknown record type - return createStringError(errc::invalid_argument, "unknown record type: %u", - static_cast<unsigned>(R.Type)); - } - return Error::success(); -} - -// Checks that IHEX line contains valid characters. -// This allows converting hexadecimal data to integers -// without extra verification. -static Error checkChars(StringRef Line) { - assert(!Line.empty()); - if (Line[0] != ':') - return createStringError(errc::invalid_argument, - "missing ':' in the beginning of line."); - - for (size_t Pos = 1; Pos < Line.size(); ++Pos) - if (hexDigitValue(Line[Pos]) == -1U) - return createStringError(errc::invalid_argument, - "invalid character at position %zu.", Pos + 1); - return Error::success(); -} - -Expected<IHexRecord> IHexRecord::parse(StringRef Line) { - assert(!Line.empty()); - - // ':' + Length + Address + Type + Checksum with empty data ':LLAAAATTCC' - if (Line.size() < 11) - return createStringError(errc::invalid_argument, - "line is too short: %zu chars.", Line.size()); - - if (Error E = checkChars(Line)) - return std::move(E); - - IHexRecord Rec; - size_t DataLen = checkedGetHex<uint8_t>(Line.substr(1, 2)); - if (Line.size() != getLength(DataLen)) - return createStringError(errc::invalid_argument, - "invalid line length %zu (should be %zu)", - Line.size(), getLength(DataLen)); - - Rec.Addr = checkedGetHex<uint16_t>(Line.substr(3, 4)); - Rec.Type = checkedGetHex<uint8_t>(Line.substr(7, 2)); - Rec.HexData = Line.substr(9, DataLen * 2); - - if (getChecksum(Line.drop_front(1)) != 0) - return createStringError(errc::invalid_argument, "incorrect checksum."); - if (Error E = checkRecord(Rec)) - return std::move(E); - return Rec; -} - -static uint64_t sectionPhysicalAddr(const SectionBase *Sec) { - Segment *Seg = Sec->ParentSegment; - if (Seg && Seg->Type != ELF::PT_LOAD) - Seg = nullptr; - return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset - : Sec->Addr; -} - -void IHexSectionWriterBase::writeSection(const SectionBase *Sec, - ArrayRef<uint8_t> Data) { - assert(Data.size() == Sec->Size); - const uint32_t ChunkSize = 16; - uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU; - while (!Data.empty()) { - uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize); - if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) { - if (Addr > 0xFFFFFU) { - // Write extended address record, zeroing segment address - // if needed. - if (SegmentAddr != 0) - SegmentAddr = writeSegmentAddr(0U); - BaseAddr = writeBaseAddr(Addr); - } else { - // We can still remain 16-bit - SegmentAddr = writeSegmentAddr(Addr); - } - } - uint64_t SegOffset = Addr - BaseAddr - SegmentAddr; - assert(SegOffset <= 0xFFFFU); - DataSize = std::min(DataSize, 0x10000U - SegOffset); - writeData(0, SegOffset, Data.take_front(DataSize)); - Addr += DataSize; - Data = Data.drop_front(DataSize); - } -} - -uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) { - assert(Addr <= 0xFFFFFU); - uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0}; - writeData(2, 0, Data); - return Addr & 0xF0000U; -} - -uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) { - assert(Addr <= 0xFFFFFFFFU); - uint64_t Base = Addr & 0xFFFF0000U; - uint8_t Data[] = {static_cast<uint8_t>(Base >> 24), - static_cast<uint8_t>((Base >> 16) & 0xFF)}; - writeData(4, 0, Data); - return Base; -} - +} + +static bool addressOverflows32bit(uint64_t Addr) { + // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok + return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX; +} + +template <class T> static T checkedGetHex(StringRef S) { + T Value; + bool Fail = S.getAsInteger(16, Value); + assert(!Fail); + (void)Fail; + return Value; +} + +// Fills exactly Len bytes of buffer with hexadecimal characters +// representing value 'X' +template <class T, class Iterator> +static Iterator utohexstr(T X, Iterator It, size_t Len) { + // Fill range with '0' + std::fill(It, It + Len, '0'); + + for (long I = Len - 1; I >= 0; --I) { + unsigned char Mod = static_cast<unsigned char>(X) & 15; + *(It + I) = hexdigit(Mod, false); + X >>= 4; + } + assert(X == 0); + return It + Len; +} + +uint8_t IHexRecord::getChecksum(StringRef S) { + assert((S.size() & 1) == 0); + uint8_t Checksum = 0; + while (!S.empty()) { + Checksum += checkedGetHex<uint8_t>(S.take_front(2)); + S = S.drop_front(2); + } + return -Checksum; +} + +IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data) { + IHexLineData Line(getLineLength(Data.size())); + assert(Line.size()); + auto Iter = Line.begin(); + *Iter++ = ':'; + Iter = utohexstr(Data.size(), Iter, 2); + Iter = utohexstr(Addr, Iter, 4); + Iter = utohexstr(Type, Iter, 2); + for (uint8_t X : Data) + Iter = utohexstr(X, Iter, 2); + StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter)); + Iter = utohexstr(getChecksum(S), Iter, 2); + *Iter++ = '\r'; + *Iter++ = '\n'; + assert(Iter == Line.end()); + return Line; +} + +static Error checkRecord(const IHexRecord &R) { + switch (R.Type) { + case IHexRecord::Data: + if (R.HexData.size() == 0) + return createStringError( + errc::invalid_argument, + "zero data length is not allowed for data records"); + break; + case IHexRecord::EndOfFile: + break; + case IHexRecord::SegmentAddr: + // 20-bit segment address. Data length must be 2 bytes + // (4 bytes in hex) + if (R.HexData.size() != 4) + return createStringError( + errc::invalid_argument, + "segment address data should be 2 bytes in size"); + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + if (R.HexData.size() != 8) + return createStringError(errc::invalid_argument, + "start address data should be 4 bytes in size"); + // According to Intel HEX specification '03' record + // only specifies the code address within the 20-bit + // segmented address space of the 8086/80186. This + // means 12 high order bits should be zeroes. + if (R.Type == IHexRecord::StartAddr80x86 && + R.HexData.take_front(3) != "000") + return createStringError(errc::invalid_argument, + "start address exceeds 20 bit for 80x86"); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + if (R.HexData.size() != 4) + return createStringError( + errc::invalid_argument, + "extended address data should be 2 bytes in size"); + break; + default: + // Unknown record type + return createStringError(errc::invalid_argument, "unknown record type: %u", + static_cast<unsigned>(R.Type)); + } + return Error::success(); +} + +// Checks that IHEX line contains valid characters. +// This allows converting hexadecimal data to integers +// without extra verification. +static Error checkChars(StringRef Line) { + assert(!Line.empty()); + if (Line[0] != ':') + return createStringError(errc::invalid_argument, + "missing ':' in the beginning of line."); + + for (size_t Pos = 1; Pos < Line.size(); ++Pos) + if (hexDigitValue(Line[Pos]) == -1U) + return createStringError(errc::invalid_argument, + "invalid character at position %zu.", Pos + 1); + return Error::success(); +} + +Expected<IHexRecord> IHexRecord::parse(StringRef Line) { + assert(!Line.empty()); + + // ':' + Length + Address + Type + Checksum with empty data ':LLAAAATTCC' + if (Line.size() < 11) + return createStringError(errc::invalid_argument, + "line is too short: %zu chars.", Line.size()); + + if (Error E = checkChars(Line)) + return std::move(E); + + IHexRecord Rec; + size_t DataLen = checkedGetHex<uint8_t>(Line.substr(1, 2)); + if (Line.size() != getLength(DataLen)) + return createStringError(errc::invalid_argument, + "invalid line length %zu (should be %zu)", + Line.size(), getLength(DataLen)); + + Rec.Addr = checkedGetHex<uint16_t>(Line.substr(3, 4)); + Rec.Type = checkedGetHex<uint8_t>(Line.substr(7, 2)); + Rec.HexData = Line.substr(9, DataLen * 2); + + if (getChecksum(Line.drop_front(1)) != 0) + return createStringError(errc::invalid_argument, "incorrect checksum."); + if (Error E = checkRecord(Rec)) + return std::move(E); + return Rec; +} + +static uint64_t sectionPhysicalAddr(const SectionBase *Sec) { + Segment *Seg = Sec->ParentSegment; + if (Seg && Seg->Type != ELF::PT_LOAD) + Seg = nullptr; + return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset + : Sec->Addr; +} + +void IHexSectionWriterBase::writeSection(const SectionBase *Sec, + ArrayRef<uint8_t> Data) { + assert(Data.size() == Sec->Size); + const uint32_t ChunkSize = 16; + uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU; + while (!Data.empty()) { + uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize); + if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) { + if (Addr > 0xFFFFFU) { + // Write extended address record, zeroing segment address + // if needed. + if (SegmentAddr != 0) + SegmentAddr = writeSegmentAddr(0U); + BaseAddr = writeBaseAddr(Addr); + } else { + // We can still remain 16-bit + SegmentAddr = writeSegmentAddr(Addr); + } + } + uint64_t SegOffset = Addr - BaseAddr - SegmentAddr; + assert(SegOffset <= 0xFFFFU); + DataSize = std::min(DataSize, 0x10000U - SegOffset); + writeData(0, SegOffset, Data.take_front(DataSize)); + Addr += DataSize; + Data = Data.drop_front(DataSize); + } +} + +uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) { + assert(Addr <= 0xFFFFFU); + uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0}; + writeData(2, 0, Data); + return Addr & 0xF0000U; +} + +uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) { + assert(Addr <= 0xFFFFFFFFU); + uint64_t Base = Addr & 0xFFFF0000U; + uint8_t Data[] = {static_cast<uint8_t>(Base >> 24), + static_cast<uint8_t>((Base >> 16) & 0xFF)}; + writeData(4, 0, Data); + return Base; +} + void IHexSectionWriterBase::writeData(uint8_t, uint16_t, - ArrayRef<uint8_t> Data) { - Offset += IHexRecord::getLineLength(Data.size()); -} - + ArrayRef<uint8_t> Data) { + Offset += IHexRecord::getLineLength(Data.size()); +} + Error IHexSectionWriterBase::visit(const Section &Sec) { - writeSection(&Sec, Sec.Contents); + writeSection(&Sec, Sec.Contents); return Error::success(); -} - +} + Error IHexSectionWriterBase::visit(const OwnedDataSection &Sec) { - writeSection(&Sec, Sec.Data); + writeSection(&Sec, Sec.Data); return Error::success(); -} - +} + Error IHexSectionWriterBase::visit(const StringTableSection &Sec) { - // Check that sizer has already done its work - assert(Sec.Size == Sec.StrTabBuilder.getSize()); - // We are free to pass an invalid pointer to writeSection as long - // as we don't actually write any data. The real writer class has - // to override this method . - writeSection(&Sec, {nullptr, static_cast<size_t>(Sec.Size)}); + // Check that sizer has already done its work + assert(Sec.Size == Sec.StrTabBuilder.getSize()); + // We are free to pass an invalid pointer to writeSection as long + // as we don't actually write any data. The real writer class has + // to override this method . + writeSection(&Sec, {nullptr, static_cast<size_t>(Sec.Size)}); return Error::success(); -} - +} + Error IHexSectionWriterBase::visit(const DynamicRelocationSection &Sec) { - writeSection(&Sec, Sec.Contents); + writeSection(&Sec, Sec.Contents); return Error::success(); -} - -void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr, - ArrayRef<uint8_t> Data) { - IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data); - memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size()); - Offset += HexData.size(); -} - +} + +void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data) { + IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data); + memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size()); + Offset += HexData.size(); +} + Error IHexSectionWriter::visit(const StringTableSection &Sec) { - assert(Sec.Size == Sec.StrTabBuilder.getSize()); - std::vector<uint8_t> Data(Sec.Size); - Sec.StrTabBuilder.write(Data.data()); - writeSection(&Sec, Data); + assert(Sec.Size == Sec.StrTabBuilder.getSize()); + std::vector<uint8_t> Data(Sec.Size); + Sec.StrTabBuilder.write(Data.data()); + writeSection(&Sec, Data); return Error::success(); -} - +} + Error Section::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); } - + Error Section::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); } - + Error SectionWriter::visit(const OwnedDataSection &Sec) { - llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset); + llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset); return Error::success(); -} - -static constexpr std::array<uint8_t, 4> ZlibGnuMagic = {{'Z', 'L', 'I', 'B'}}; - -static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) { - return Data.size() > ZlibGnuMagic.size() && - std::equal(ZlibGnuMagic.begin(), ZlibGnuMagic.end(), Data.data()); -} - -template <class ELFT> -static std::tuple<uint64_t, uint64_t> -getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) { - const bool IsGnuDebug = isDataGnuCompressed(Data); - const uint64_t DecompressedSize = - IsGnuDebug - ? support::endian::read64be(Data.data() + ZlibGnuMagic.size()) - : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())->ch_size; - const uint64_t DecompressedAlign = - IsGnuDebug ? 1 - : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data()) - ->ch_addralign; - - return std::make_tuple(DecompressedSize, DecompressedAlign); -} - -template <class ELFT> +} + +static constexpr std::array<uint8_t, 4> ZlibGnuMagic = {{'Z', 'L', 'I', 'B'}}; + +static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) { + return Data.size() > ZlibGnuMagic.size() && + std::equal(ZlibGnuMagic.begin(), ZlibGnuMagic.end(), Data.data()); +} + +template <class ELFT> +static std::tuple<uint64_t, uint64_t> +getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) { + const bool IsGnuDebug = isDataGnuCompressed(Data); + const uint64_t DecompressedSize = + IsGnuDebug + ? support::endian::read64be(Data.data() + ZlibGnuMagic.size()) + : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())->ch_size; + const uint64_t DecompressedAlign = + IsGnuDebug ? 1 + : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data()) + ->ch_addralign; + + return std::make_tuple(DecompressedSize, DecompressedAlign); +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) { - const size_t DataOffset = isDataGnuCompressed(Sec.OriginalData) - ? (ZlibGnuMagic.size() + sizeof(Sec.Size)) - : sizeof(Elf_Chdr_Impl<ELFT>); - - StringRef CompressedContent( - reinterpret_cast<const char *>(Sec.OriginalData.data()) + DataOffset, - Sec.OriginalData.size() - DataOffset); - - SmallVector<char, 128> DecompressedContent; + const size_t DataOffset = isDataGnuCompressed(Sec.OriginalData) + ? (ZlibGnuMagic.size() + sizeof(Sec.Size)) + : sizeof(Elf_Chdr_Impl<ELFT>); + + StringRef CompressedContent( + reinterpret_cast<const char *>(Sec.OriginalData.data()) + DataOffset, + Sec.OriginalData.size() - DataOffset); + + SmallVector<char, 128> DecompressedContent; if (Error Err = zlib::uncompress(CompressedContent, DecompressedContent, static_cast<size_t>(Sec.Size))) return createStringError(errc::invalid_argument, "'" + Sec.Name + "': " + toString(std::move(Err))); - - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); + + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); return Error::success(); -} - +} + Error BinarySectionWriter::visit(const DecompressedSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write compressed section '" + Sec.Name + "' "); -} - +} + Error DecompressedSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error DecompressedSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - +} + Error OwnedDataSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error OwnedDataSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -void OwnedDataSection::appendHexData(StringRef HexData) { - assert((HexData.size() & 1) == 0); - while (!HexData.empty()) { - Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2))); - HexData = HexData.drop_front(2); - } - Size = Data.size(); -} - +} + +void OwnedDataSection::appendHexData(StringRef HexData) { + assert((HexData.size() & 1) == 0); + while (!HexData.empty()) { + Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2))); + HexData = HexData.drop_front(2); + } + Size = Data.size(); +} + Error BinarySectionWriter::visit(const CompressedSection &Sec) { return createStringError(errc::operation_not_permitted, "cannot write compressed section '" + Sec.Name + "' "); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - if (Sec.CompressionType == DebugCompressionType::None) { - std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + if (Sec.CompressionType == DebugCompressionType::None) { + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); return Error::success(); - } - - if (Sec.CompressionType == DebugCompressionType::GNU) { - const char *Magic = "ZLIB"; - memcpy(Buf, Magic, strlen(Magic)); - Buf += strlen(Magic); - const uint64_t DecompressedSize = - support::endian::read64be(&Sec.DecompressedSize); - memcpy(Buf, &DecompressedSize, sizeof(DecompressedSize)); - Buf += sizeof(DecompressedSize); - } else { - Elf_Chdr_Impl<ELFT> Chdr; - Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB; - Chdr.ch_size = Sec.DecompressedSize; - Chdr.ch_addralign = Sec.DecompressedAlign; - memcpy(Buf, &Chdr, sizeof(Chdr)); - Buf += sizeof(Chdr); - } - - std::copy(Sec.CompressedData.begin(), Sec.CompressedData.end(), Buf); + } + + if (Sec.CompressionType == DebugCompressionType::GNU) { + const char *Magic = "ZLIB"; + memcpy(Buf, Magic, strlen(Magic)); + Buf += strlen(Magic); + const uint64_t DecompressedSize = + support::endian::read64be(&Sec.DecompressedSize); + memcpy(Buf, &DecompressedSize, sizeof(DecompressedSize)); + Buf += sizeof(DecompressedSize); + } else { + Elf_Chdr_Impl<ELFT> Chdr; + Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB; + Chdr.ch_size = Sec.DecompressedSize; + Chdr.ch_addralign = Sec.DecompressedAlign; + memcpy(Buf, &Chdr, sizeof(Chdr)); + Buf += sizeof(Chdr); + } + + std::copy(Sec.CompressedData.begin(), Sec.CompressedData.end(), Buf); return Error::success(); -} - +} + Expected<CompressedSection> CompressedSection::create(const SectionBase &Sec, DebugCompressionType CompressionType) { @@ -564,87 +564,87 @@ CompressedSection::create(ArrayRef<uint8_t> CompressedData, return CompressedSection(CompressedData, DecompressedSize, DecompressedAlign); } -CompressedSection::CompressedSection(const SectionBase &Sec, +CompressedSection::CompressedSection(const SectionBase &Sec, DebugCompressionType CompressionType, Error &OutErr) - : SectionBase(Sec), CompressionType(CompressionType), - DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { + : SectionBase(Sec), CompressionType(CompressionType), + DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { ErrorAsOutParameter EAO(&OutErr); if (Error Err = zlib::compress( - StringRef(reinterpret_cast<const char *>(OriginalData.data()), - OriginalData.size()), + StringRef(reinterpret_cast<const char *>(OriginalData.data()), + OriginalData.size()), CompressedData)) { OutErr = createStringError(llvm::errc::invalid_argument, "'" + Name + "': " + toString(std::move(Err))); return; } - - size_t ChdrSize; - if (CompressionType == DebugCompressionType::GNU) { - Name = ".z" + Sec.Name.substr(1); - ChdrSize = sizeof("ZLIB") - 1 + sizeof(uint64_t); - } else { - Flags |= ELF::SHF_COMPRESSED; - ChdrSize = - std::max(std::max(sizeof(object::Elf_Chdr_Impl<object::ELF64LE>), - sizeof(object::Elf_Chdr_Impl<object::ELF64BE>)), - std::max(sizeof(object::Elf_Chdr_Impl<object::ELF32LE>), - sizeof(object::Elf_Chdr_Impl<object::ELF32BE>))); - } - Size = ChdrSize + CompressedData.size(); - Align = 8; -} - -CompressedSection::CompressedSection(ArrayRef<uint8_t> CompressedData, - uint64_t DecompressedSize, - uint64_t DecompressedAlign) - : CompressionType(DebugCompressionType::None), - DecompressedSize(DecompressedSize), DecompressedAlign(DecompressedAlign) { - OriginalData = CompressedData; -} - + + size_t ChdrSize; + if (CompressionType == DebugCompressionType::GNU) { + Name = ".z" + Sec.Name.substr(1); + ChdrSize = sizeof("ZLIB") - 1 + sizeof(uint64_t); + } else { + Flags |= ELF::SHF_COMPRESSED; + ChdrSize = + std::max(std::max(sizeof(object::Elf_Chdr_Impl<object::ELF64LE>), + sizeof(object::Elf_Chdr_Impl<object::ELF64BE>)), + std::max(sizeof(object::Elf_Chdr_Impl<object::ELF32LE>), + sizeof(object::Elf_Chdr_Impl<object::ELF32BE>))); + } + Size = ChdrSize + CompressedData.size(); + Align = 8; +} + +CompressedSection::CompressedSection(ArrayRef<uint8_t> CompressedData, + uint64_t DecompressedSize, + uint64_t DecompressedAlign) + : CompressionType(DebugCompressionType::None), + DecompressedSize(DecompressedSize), DecompressedAlign(DecompressedAlign) { + OriginalData = CompressedData; +} + Error CompressedSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error CompressedSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -void StringTableSection::addString(StringRef Name) { StrTabBuilder.add(Name); } - -uint32_t StringTableSection::findIndex(StringRef Name) const { - return StrTabBuilder.getOffset(Name); -} - -void StringTableSection::prepareForLayout() { - StrTabBuilder.finalize(); - Size = StrTabBuilder.getSize(); -} - +} + +void StringTableSection::addString(StringRef Name) { StrTabBuilder.add(Name); } + +uint32_t StringTableSection::findIndex(StringRef Name) const { + return StrTabBuilder.getOffset(Name); +} + +void StringTableSection::prepareForLayout() { + StrTabBuilder.finalize(); + Size = StrTabBuilder.getSize(); +} + Error SectionWriter::visit(const StringTableSection &Sec) { - Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset); + Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset); return Error::success(); -} - +} + Error StringTableSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error StringTableSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf)); + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf)); return Error::success(); -} - +} + Error SectionIndexSection::initialize(SectionTableRef SecTable) { - Size = 0; + Size = 0; Expected<SymbolTableSection *> Sec = SecTable.getSectionOfType<SymbolTableSection>( Link, @@ -656,145 +656,145 @@ Error SectionIndexSection::initialize(SectionTableRef SecTable) { return Sec.takeError(); setSymTab(*Sec); - Symbols->setShndxTable(this); + Symbols->setShndxTable(this); return Error::success(); -} - -void SectionIndexSection::finalize() { Link = Symbols->Index; } - +} + +void SectionIndexSection::finalize() { Link = Symbols->Index; } + Error SectionIndexSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error SectionIndexSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) { - switch (Index) { - case SHN_ABS: - case SHN_COMMON: - return true; - } - - if (Machine == EM_AMDGPU) { - return Index == SHN_AMDGPU_LDS; - } - - if (Machine == EM_HEXAGON) { - switch (Index) { - case SHN_HEXAGON_SCOMMON: - case SHN_HEXAGON_SCOMMON_1: - case SHN_HEXAGON_SCOMMON_2: - case SHN_HEXAGON_SCOMMON_4: - case SHN_HEXAGON_SCOMMON_8: - return true; - } - } - return false; -} - -// Large indexes force us to clarify exactly what this function should do. This -// function should return the value that will appear in st_shndx when written -// out. -uint16_t Symbol::getShndx() const { - if (DefinedIn != nullptr) { - if (DefinedIn->Index >= SHN_LORESERVE) - return SHN_XINDEX; - return DefinedIn->Index; - } - - if (ShndxType == SYMBOL_SIMPLE_INDEX) { - // This means that we don't have a defined section but we do need to - // output a legitimate section index. - return SHN_UNDEF; - } - - assert(ShndxType == SYMBOL_ABS || ShndxType == SYMBOL_COMMON || - (ShndxType >= SYMBOL_LOPROC && ShndxType <= SYMBOL_HIPROC) || - (ShndxType >= SYMBOL_LOOS && ShndxType <= SYMBOL_HIOS)); - return static_cast<uint16_t>(ShndxType); -} - -bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; } - -void SymbolTableSection::assignIndices() { - uint32_t Index = 0; - for (auto &Sym : Symbols) - Sym->Index = Index++; -} - -void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, - SectionBase *DefinedIn, uint64_t Value, - uint8_t Visibility, uint16_t Shndx, - uint64_t SymbolSize) { - Symbol Sym; - Sym.Name = Name.str(); - Sym.Binding = Bind; - Sym.Type = Type; - Sym.DefinedIn = DefinedIn; - if (DefinedIn != nullptr) - DefinedIn->HasSymbol = true; - if (DefinedIn == nullptr) { - if (Shndx >= SHN_LORESERVE) - Sym.ShndxType = static_cast<SymbolShndxType>(Shndx); - else - Sym.ShndxType = SYMBOL_SIMPLE_INDEX; - } - Sym.Value = Value; - Sym.Visibility = Visibility; - Sym.Size = SymbolSize; - Sym.Index = Symbols.size(); - Symbols.emplace_back(std::make_unique<Symbol>(Sym)); - Size += this->EntrySize; -} - -Error SymbolTableSection::removeSectionReferences( +} + +static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) { + switch (Index) { + case SHN_ABS: + case SHN_COMMON: + return true; + } + + if (Machine == EM_AMDGPU) { + return Index == SHN_AMDGPU_LDS; + } + + if (Machine == EM_HEXAGON) { + switch (Index) { + case SHN_HEXAGON_SCOMMON: + case SHN_HEXAGON_SCOMMON_1: + case SHN_HEXAGON_SCOMMON_2: + case SHN_HEXAGON_SCOMMON_4: + case SHN_HEXAGON_SCOMMON_8: + return true; + } + } + return false; +} + +// Large indexes force us to clarify exactly what this function should do. This +// function should return the value that will appear in st_shndx when written +// out. +uint16_t Symbol::getShndx() const { + if (DefinedIn != nullptr) { + if (DefinedIn->Index >= SHN_LORESERVE) + return SHN_XINDEX; + return DefinedIn->Index; + } + + if (ShndxType == SYMBOL_SIMPLE_INDEX) { + // This means that we don't have a defined section but we do need to + // output a legitimate section index. + return SHN_UNDEF; + } + + assert(ShndxType == SYMBOL_ABS || ShndxType == SYMBOL_COMMON || + (ShndxType >= SYMBOL_LOPROC && ShndxType <= SYMBOL_HIPROC) || + (ShndxType >= SYMBOL_LOOS && ShndxType <= SYMBOL_HIOS)); + return static_cast<uint16_t>(ShndxType); +} + +bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; } + +void SymbolTableSection::assignIndices() { + uint32_t Index = 0; + for (auto &Sym : Symbols) + Sym->Index = Index++; +} + +void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, + SectionBase *DefinedIn, uint64_t Value, + uint8_t Visibility, uint16_t Shndx, + uint64_t SymbolSize) { + Symbol Sym; + Sym.Name = Name.str(); + Sym.Binding = Bind; + Sym.Type = Type; + Sym.DefinedIn = DefinedIn; + if (DefinedIn != nullptr) + DefinedIn->HasSymbol = true; + if (DefinedIn == nullptr) { + if (Shndx >= SHN_LORESERVE) + Sym.ShndxType = static_cast<SymbolShndxType>(Shndx); + else + Sym.ShndxType = SYMBOL_SIMPLE_INDEX; + } + Sym.Value = Value; + Sym.Visibility = Visibility; + Sym.Size = SymbolSize; + Sym.Index = Symbols.size(); + Symbols.emplace_back(std::make_unique<Symbol>(Sym)); + Size += this->EntrySize; +} + +Error SymbolTableSection::removeSectionReferences( bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { - if (ToRemove(SectionIndexTable)) - SectionIndexTable = nullptr; - if (ToRemove(SymbolNames)) { - if (!AllowBrokenLinks) - return createStringError( - llvm::errc::invalid_argument, - "string table '%s' cannot be removed because it is " - "referenced by the symbol table '%s'", - SymbolNames->Name.data(), this->Name.data()); - SymbolNames = nullptr; - } - return removeSymbols( - [ToRemove](const Symbol &Sym) { return ToRemove(Sym.DefinedIn); }); -} - -void SymbolTableSection::updateSymbols(function_ref<void(Symbol &)> Callable) { - std::for_each(std::begin(Symbols) + 1, std::end(Symbols), - [Callable](SymPtr &Sym) { Callable(*Sym); }); - std::stable_partition( - std::begin(Symbols), std::end(Symbols), - [](const SymPtr &Sym) { return Sym->Binding == STB_LOCAL; }); - assignIndices(); -} - -Error SymbolTableSection::removeSymbols( - function_ref<bool(const Symbol &)> ToRemove) { - Symbols.erase( - std::remove_if(std::begin(Symbols) + 1, std::end(Symbols), - [ToRemove](const SymPtr &Sym) { return ToRemove(*Sym); }), - std::end(Symbols)); - Size = Symbols.size() * EntrySize; - assignIndices(); - return Error::success(); -} - -void SymbolTableSection::replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &FromTo) { - for (std::unique_ptr<Symbol> &Sym : Symbols) - if (SectionBase *To = FromTo.lookup(Sym->DefinedIn)) - Sym->DefinedIn = To; -} - + if (ToRemove(SectionIndexTable)) + SectionIndexTable = nullptr; + if (ToRemove(SymbolNames)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "string table '%s' cannot be removed because it is " + "referenced by the symbol table '%s'", + SymbolNames->Name.data(), this->Name.data()); + SymbolNames = nullptr; + } + return removeSymbols( + [ToRemove](const Symbol &Sym) { return ToRemove(Sym.DefinedIn); }); +} + +void SymbolTableSection::updateSymbols(function_ref<void(Symbol &)> Callable) { + std::for_each(std::begin(Symbols) + 1, std::end(Symbols), + [Callable](SymPtr &Sym) { Callable(*Sym); }); + std::stable_partition( + std::begin(Symbols), std::end(Symbols), + [](const SymPtr &Sym) { return Sym->Binding == STB_LOCAL; }); + assignIndices(); +} + +Error SymbolTableSection::removeSymbols( + function_ref<bool(const Symbol &)> ToRemove) { + Symbols.erase( + std::remove_if(std::begin(Symbols) + 1, std::end(Symbols), + [ToRemove](const SymPtr &Sym) { return ToRemove(*Sym); }), + std::end(Symbols)); + Size = Symbols.size() * EntrySize; + assignIndices(); + return Error::success(); +} + +void SymbolTableSection::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) { + for (std::unique_ptr<Symbol> &Sym : Symbols) + if (SectionBase *To = FromTo.lookup(Sym->DefinedIn)) + Sym->DefinedIn = To; +} + Error SymbolTableSection::initialize(SectionTableRef SecTable) { - Size = 0; + Size = 0; Expected<StringTableSection *> Sec = SecTable.getSectionOfType<StringTableSection>( Link, @@ -807,58 +807,58 @@ Error SymbolTableSection::initialize(SectionTableRef SecTable) { setStrTab(*Sec); return Error::success(); -} - -void SymbolTableSection::finalize() { - uint32_t MaxLocalIndex = 0; - for (std::unique_ptr<Symbol> &Sym : Symbols) { - Sym->NameIndex = - SymbolNames == nullptr ? 0 : SymbolNames->findIndex(Sym->Name); - if (Sym->Binding == STB_LOCAL) - MaxLocalIndex = std::max(MaxLocalIndex, Sym->Index); - } - // Now we need to set the Link and Info fields. - Link = SymbolNames == nullptr ? 0 : SymbolNames->Index; - Info = MaxLocalIndex + 1; -} - -void SymbolTableSection::prepareForLayout() { - // Reserve proper amount of space in section index table, so we can - // layout sections correctly. We will fill the table with correct - // indexes later in fillShdnxTable. - if (SectionIndexTable) - SectionIndexTable->reserve(Symbols.size()); - - // Add all of our strings to SymbolNames so that SymbolNames has the right - // size before layout is decided. - // If the symbol names section has been removed, don't try to add strings to - // the table. - if (SymbolNames != nullptr) - for (std::unique_ptr<Symbol> &Sym : Symbols) - SymbolNames->addString(Sym->Name); -} - -void SymbolTableSection::fillShndxTable() { - if (SectionIndexTable == nullptr) - return; - // Fill section index table with real section indexes. This function must - // be called after assignOffsets. - for (const std::unique_ptr<Symbol> &Sym : Symbols) { - if (Sym->DefinedIn != nullptr && Sym->DefinedIn->Index >= SHN_LORESERVE) - SectionIndexTable->addIndex(Sym->DefinedIn->Index); - else - SectionIndexTable->addIndex(SHN_UNDEF); - } -} - +} + +void SymbolTableSection::finalize() { + uint32_t MaxLocalIndex = 0; + for (std::unique_ptr<Symbol> &Sym : Symbols) { + Sym->NameIndex = + SymbolNames == nullptr ? 0 : SymbolNames->findIndex(Sym->Name); + if (Sym->Binding == STB_LOCAL) + MaxLocalIndex = std::max(MaxLocalIndex, Sym->Index); + } + // Now we need to set the Link and Info fields. + Link = SymbolNames == nullptr ? 0 : SymbolNames->Index; + Info = MaxLocalIndex + 1; +} + +void SymbolTableSection::prepareForLayout() { + // Reserve proper amount of space in section index table, so we can + // layout sections correctly. We will fill the table with correct + // indexes later in fillShdnxTable. + if (SectionIndexTable) + SectionIndexTable->reserve(Symbols.size()); + + // Add all of our strings to SymbolNames so that SymbolNames has the right + // size before layout is decided. + // If the symbol names section has been removed, don't try to add strings to + // the table. + if (SymbolNames != nullptr) + for (std::unique_ptr<Symbol> &Sym : Symbols) + SymbolNames->addString(Sym->Name); +} + +void SymbolTableSection::fillShndxTable() { + if (SectionIndexTable == nullptr) + return; + // Fill section index table with real section indexes. This function must + // be called after assignOffsets. + for (const std::unique_ptr<Symbol> &Sym : Symbols) { + if (Sym->DefinedIn != nullptr && Sym->DefinedIn->Index >= SHN_LORESERVE) + SectionIndexTable->addIndex(Sym->DefinedIn->Index); + else + SectionIndexTable->addIndex(SHN_UNDEF); + } +} + Expected<const Symbol *> SymbolTableSection::getSymbolByIndex(uint32_t Index) const { - if (Symbols.size() <= Index) + if (Symbols.size() <= Index) return createStringError(errc::invalid_argument, "invalid symbol index: " + Twine(Index)); - return Symbols[Index].get(); -} - + return Symbols[Index].get(); +} + Expected<Symbol *> SymbolTableSection::getSymbolByIndex(uint32_t Index) { Expected<const Symbol *> Sym = static_cast<const SymbolTableSection *>(this)->getSymbolByIndex(Index); @@ -866,73 +866,73 @@ Expected<Symbol *> SymbolTableSection::getSymbolByIndex(uint32_t Index) { return Sym.takeError(); return const_cast<Symbol *>(*Sym); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) { - Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Out.getBufferStart() + Sec.Offset); - // Loop though symbols setting each entry of the symbol table. - for (const std::unique_ptr<Symbol> &Symbol : Sec.Symbols) { - Sym->st_name = Symbol->NameIndex; - Sym->st_value = Symbol->Value; - Sym->st_size = Symbol->Size; - Sym->st_other = Symbol->Visibility; - Sym->setBinding(Symbol->Binding); - Sym->setType(Symbol->Type); - Sym->st_shndx = Symbol->getShndx(); - ++Sym; - } + Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Out.getBufferStart() + Sec.Offset); + // Loop though symbols setting each entry of the symbol table. + for (const std::unique_ptr<Symbol> &Symbol : Sec.Symbols) { + Sym->st_name = Symbol->NameIndex; + Sym->st_value = Symbol->Value; + Sym->st_size = Symbol->Size; + Sym->st_other = Symbol->Visibility; + Sym->setBinding(Symbol->Binding); + Sym->setType(Symbol->Type); + Sym->st_shndx = Symbol->getShndx(); + ++Sym; + } return Error::success(); -} - +} + Error SymbolTableSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error SymbolTableSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -Error RelocationSection::removeSectionReferences( +} + +Error RelocationSection::removeSectionReferences( bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { - if (ToRemove(Symbols)) { - if (!AllowBrokenLinks) - return createStringError( - llvm::errc::invalid_argument, - "symbol table '%s' cannot be removed because it is " - "referenced by the relocation section '%s'", - Symbols->Name.data(), this->Name.data()); - Symbols = nullptr; - } - - for (const Relocation &R : Relocations) { - if (!R.RelocSymbol || !R.RelocSymbol->DefinedIn || - !ToRemove(R.RelocSymbol->DefinedIn)) - continue; - return createStringError(llvm::errc::invalid_argument, - "section '%s' cannot be removed: (%s+0x%" PRIx64 - ") has relocation against symbol '%s'", - R.RelocSymbol->DefinedIn->Name.data(), - SecToApplyRel->Name.data(), R.Offset, - R.RelocSymbol->Name.c_str()); - } - - return Error::success(); -} - -template <class SymTabType> + if (ToRemove(Symbols)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "symbol table '%s' cannot be removed because it is " + "referenced by the relocation section '%s'", + Symbols->Name.data(), this->Name.data()); + Symbols = nullptr; + } + + for (const Relocation &R : Relocations) { + if (!R.RelocSymbol || !R.RelocSymbol->DefinedIn || + !ToRemove(R.RelocSymbol->DefinedIn)) + continue; + return createStringError(llvm::errc::invalid_argument, + "section '%s' cannot be removed: (%s+0x%" PRIx64 + ") has relocation against symbol '%s'", + R.RelocSymbol->DefinedIn->Name.data(), + SecToApplyRel->Name.data(), R.Offset, + R.RelocSymbol->Name.c_str()); + } + + return Error::success(); +} + +template <class SymTabType> Error RelocSectionWithSymtabBase<SymTabType>::initialize( - SectionTableRef SecTable) { + SectionTableRef SecTable) { if (Link != SHN_UNDEF) { Expected<SymTabType *> Sec = SecTable.getSectionOfType<SymTabType>( - Link, - "Link field value " + Twine(Link) + " in section " + Name + - " is invalid", - "Link field value " + Twine(Link) + " in section " + Name + + Link, + "Link field value " + Twine(Link) + " in section " + Name + + " is invalid", + "Link field value " + Twine(Link) + " in section " + Name + " is not a symbol table"); if (!Sec) return Sec.takeError(); - + setSymTab(*Sec); } @@ -945,467 +945,467 @@ Error RelocSectionWithSymtabBase<SymTabType>::initialize( setSection(*Sec); } else - setSection(nullptr); + setSection(nullptr); return Error::success(); -} - -template <class SymTabType> -void RelocSectionWithSymtabBase<SymTabType>::finalize() { - this->Link = Symbols ? Symbols->Index : 0; - - if (SecToApplyRel != nullptr) - this->Info = SecToApplyRel->Index; -} - -template <class ELFT> +} + +template <class SymTabType> +void RelocSectionWithSymtabBase<SymTabType>::finalize() { + this->Link = Symbols ? Symbols->Index : 0; + + if (SecToApplyRel != nullptr) + this->Info = SecToApplyRel->Index; +} + +template <class ELFT> static void setAddend(Elf_Rel_Impl<ELFT, false> &, uint64_t) {} - -template <class ELFT> -static void setAddend(Elf_Rel_Impl<ELFT, true> &Rela, uint64_t Addend) { - Rela.r_addend = Addend; -} - -template <class RelRange, class T> -static void writeRel(const RelRange &Relocations, T *Buf) { - for (const auto &Reloc : Relocations) { - Buf->r_offset = Reloc.Offset; - setAddend(*Buf, Reloc.Addend); - Buf->setSymbolAndType(Reloc.RelocSymbol ? Reloc.RelocSymbol->Index : 0, - Reloc.Type, false); - ++Buf; - } -} - -template <class ELFT> + +template <class ELFT> +static void setAddend(Elf_Rel_Impl<ELFT, true> &Rela, uint64_t Addend) { + Rela.r_addend = Addend; +} + +template <class RelRange, class T> +static void writeRel(const RelRange &Relocations, T *Buf) { + for (const auto &Reloc : Relocations) { + Buf->r_offset = Reloc.Offset; + setAddend(*Buf, Reloc.Addend); + Buf->setSymbolAndType(Reloc.RelocSymbol ? Reloc.RelocSymbol->Index : 0, + Reloc.Type, false); + ++Buf; + } +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - if (Sec.Type == SHT_REL) - writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf)); - else - writeRel(Sec.Relocations, reinterpret_cast<Elf_Rela *>(Buf)); + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + if (Sec.Type == SHT_REL) + writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf)); + else + writeRel(Sec.Relocations, reinterpret_cast<Elf_Rela *>(Buf)); return Error::success(); -} - +} + Error RelocationSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error RelocationSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -Error RelocationSection::removeSymbols( - function_ref<bool(const Symbol &)> ToRemove) { - for (const Relocation &Reloc : Relocations) - if (Reloc.RelocSymbol && ToRemove(*Reloc.RelocSymbol)) - return createStringError( - llvm::errc::invalid_argument, - "not stripping symbol '%s' because it is named in a relocation", - Reloc.RelocSymbol->Name.data()); - return Error::success(); -} - -void RelocationSection::markSymbols() { - for (const Relocation &Reloc : Relocations) - if (Reloc.RelocSymbol) - Reloc.RelocSymbol->Referenced = true; -} - -void RelocationSection::replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &FromTo) { - // Update the target section if it was replaced. - if (SectionBase *To = FromTo.lookup(SecToApplyRel)) - SecToApplyRel = To; -} - +} + +Error RelocationSection::removeSymbols( + function_ref<bool(const Symbol &)> ToRemove) { + for (const Relocation &Reloc : Relocations) + if (Reloc.RelocSymbol && ToRemove(*Reloc.RelocSymbol)) + return createStringError( + llvm::errc::invalid_argument, + "not stripping symbol '%s' because it is named in a relocation", + Reloc.RelocSymbol->Name.data()); + return Error::success(); +} + +void RelocationSection::markSymbols() { + for (const Relocation &Reloc : Relocations) + if (Reloc.RelocSymbol) + Reloc.RelocSymbol->Referenced = true; +} + +void RelocationSection::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) { + // Update the target section if it was replaced. + if (SectionBase *To = FromTo.lookup(SecToApplyRel)) + SecToApplyRel = To; +} + Error SectionWriter::visit(const DynamicRelocationSection &Sec) { - llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); + llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); return Error::success(); -} - +} + Error DynamicRelocationSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -Error DynamicRelocationSection::removeSectionReferences( - bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { - if (ToRemove(Symbols)) { - if (!AllowBrokenLinks) - return createStringError( - llvm::errc::invalid_argument, - "symbol table '%s' cannot be removed because it is " - "referenced by the relocation section '%s'", - Symbols->Name.data(), this->Name.data()); - Symbols = nullptr; - } - - // SecToApplyRel contains a section referenced by sh_info field. It keeps - // a section to which the relocation section applies. When we remove any - // sections we also remove their relocation sections. Since we do that much - // earlier, this assert should never be triggered. - assert(!SecToApplyRel || !ToRemove(SecToApplyRel)); - return Error::success(); -} - -Error Section::removeSectionReferences( - bool AllowBrokenDependency, - function_ref<bool(const SectionBase *)> ToRemove) { - if (ToRemove(LinkSection)) { - if (!AllowBrokenDependency) - return createStringError(llvm::errc::invalid_argument, - "section '%s' cannot be removed because it is " - "referenced by the section '%s'", - LinkSection->Name.data(), this->Name.data()); - LinkSection = nullptr; - } - return Error::success(); -} - -void GroupSection::finalize() { - this->Info = Sym ? Sym->Index : 0; - this->Link = SymTab ? SymTab->Index : 0; -} - -Error GroupSection::removeSectionReferences( - bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { - if (ToRemove(SymTab)) { - if (!AllowBrokenLinks) - return createStringError( - llvm::errc::invalid_argument, - "section '.symtab' cannot be removed because it is " - "referenced by the group section '%s'", - this->Name.data()); - SymTab = nullptr; - Sym = nullptr; - } - llvm::erase_if(GroupMembers, ToRemove); - return Error::success(); -} - -Error GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { - if (ToRemove(*Sym)) - return createStringError(llvm::errc::invalid_argument, - "symbol '%s' cannot be removed because it is " - "referenced by the section '%s[%d]'", - Sym->Name.data(), this->Name.data(), this->Index); - return Error::success(); -} - -void GroupSection::markSymbols() { - if (Sym) - Sym->Referenced = true; -} - -void GroupSection::replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &FromTo) { - for (SectionBase *&Sec : GroupMembers) - if (SectionBase *To = FromTo.lookup(Sec)) - Sec = To; -} - -void GroupSection::onRemove() { - // As the header section of the group is removed, drop the Group flag in its - // former members. - for (SectionBase *Sec : GroupMembers) - Sec->Flags &= ~SHF_GROUP; -} - +} + +Error DynamicRelocationSection::removeSectionReferences( + bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(Symbols)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "symbol table '%s' cannot be removed because it is " + "referenced by the relocation section '%s'", + Symbols->Name.data(), this->Name.data()); + Symbols = nullptr; + } + + // SecToApplyRel contains a section referenced by sh_info field. It keeps + // a section to which the relocation section applies. When we remove any + // sections we also remove their relocation sections. Since we do that much + // earlier, this assert should never be triggered. + assert(!SecToApplyRel || !ToRemove(SecToApplyRel)); + return Error::success(); +} + +Error Section::removeSectionReferences( + bool AllowBrokenDependency, + function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(LinkSection)) { + if (!AllowBrokenDependency) + return createStringError(llvm::errc::invalid_argument, + "section '%s' cannot be removed because it is " + "referenced by the section '%s'", + LinkSection->Name.data(), this->Name.data()); + LinkSection = nullptr; + } + return Error::success(); +} + +void GroupSection::finalize() { + this->Info = Sym ? Sym->Index : 0; + this->Link = SymTab ? SymTab->Index : 0; +} + +Error GroupSection::removeSectionReferences( + bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(SymTab)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "section '.symtab' cannot be removed because it is " + "referenced by the group section '%s'", + this->Name.data()); + SymTab = nullptr; + Sym = nullptr; + } + llvm::erase_if(GroupMembers, ToRemove); + return Error::success(); +} + +Error GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + if (ToRemove(*Sym)) + return createStringError(llvm::errc::invalid_argument, + "symbol '%s' cannot be removed because it is " + "referenced by the section '%s[%d]'", + Sym->Name.data(), this->Name.data(), this->Index); + return Error::success(); +} + +void GroupSection::markSymbols() { + if (Sym) + Sym->Referenced = true; +} + +void GroupSection::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) { + for (SectionBase *&Sec : GroupMembers) + if (SectionBase *To = FromTo.lookup(Sec)) + Sec = To; +} + +void GroupSection::onRemove() { + // As the header section of the group is removed, drop the Group flag in its + // former members. + for (SectionBase *Sec : GroupMembers) + Sec->Flags &= ~SHF_GROUP; +} + Error Section::initialize(SectionTableRef SecTable) { - if (Link == ELF::SHN_UNDEF) + if (Link == ELF::SHN_UNDEF) return Error::success(); Expected<SectionBase *> Sec = - SecTable.getSection(Link, "Link field value " + Twine(Link) + - " in section " + Name + " is invalid"); + SecTable.getSection(Link, "Link field value " + Twine(Link) + + " in section " + Name + " is invalid"); if (!Sec) return Sec.takeError(); LinkSection = *Sec; - if (LinkSection->Type == ELF::SHT_SYMTAB) - LinkSection = nullptr; + if (LinkSection->Type == ELF::SHT_SYMTAB) + LinkSection = nullptr; return Error::success(); -} - -void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; } - -void GnuDebugLinkSection::init(StringRef File) { - FileName = sys::path::filename(File); - // The format for the .gnu_debuglink starts with the file name and is - // followed by a null terminator and then the CRC32 of the file. The CRC32 - // should be 4 byte aligned. So we add the FileName size, a 1 for the null - // byte, and then finally push the size to alignment and add 4. - Size = alignTo(FileName.size() + 1, 4) + 4; - // The CRC32 will only be aligned if we align the whole section. - Align = 4; - Type = OriginalType = ELF::SHT_PROGBITS; - Name = ".gnu_debuglink"; - // For sections not found in segments, OriginalOffset is only used to - // establish the order that sections should go in. By using the maximum - // possible offset we cause this section to wind up at the end. - OriginalOffset = std::numeric_limits<uint64_t>::max(); -} - -GnuDebugLinkSection::GnuDebugLinkSection(StringRef File, - uint32_t PrecomputedCRC) - : FileName(File), CRC32(PrecomputedCRC) { - init(File); -} - -template <class ELFT> +} + +void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; } + +void GnuDebugLinkSection::init(StringRef File) { + FileName = sys::path::filename(File); + // The format for the .gnu_debuglink starts with the file name and is + // followed by a null terminator and then the CRC32 of the file. The CRC32 + // should be 4 byte aligned. So we add the FileName size, a 1 for the null + // byte, and then finally push the size to alignment and add 4. + Size = alignTo(FileName.size() + 1, 4) + 4; + // The CRC32 will only be aligned if we align the whole section. + Align = 4; + Type = OriginalType = ELF::SHT_PROGBITS; + Name = ".gnu_debuglink"; + // For sections not found in segments, OriginalOffset is only used to + // establish the order that sections should go in. By using the maximum + // possible offset we cause this section to wind up at the end. + OriginalOffset = std::numeric_limits<uint64_t>::max(); +} + +GnuDebugLinkSection::GnuDebugLinkSection(StringRef File, + uint32_t PrecomputedCRC) + : FileName(File), CRC32(PrecomputedCRC) { + init(File); +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) { - unsigned char *Buf = Out.getBufferStart() + Sec.Offset; - Elf_Word *CRC = - reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word)); - *CRC = Sec.CRC32; - llvm::copy(Sec.FileName, Buf); + unsigned char *Buf = Out.getBufferStart() + Sec.Offset; + Elf_Word *CRC = + reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word)); + *CRC = Sec.CRC32; + llvm::copy(Sec.FileName, Buf); return Error::success(); -} - +} + Error GnuDebugLinkSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error GnuDebugLinkSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) { - ELF::Elf32_Word *Buf = - reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset); - *Buf++ = Sec.FlagWord; - for (SectionBase *S : Sec.GroupMembers) - support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index); + ELF::Elf32_Word *Buf = + reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset); + *Buf++ = Sec.FlagWord; + for (SectionBase *S : Sec.GroupMembers) + support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index); return Error::success(); -} - +} + Error GroupSection::accept(SectionVisitor &Visitor) const { return Visitor.visit(*this); -} - +} + Error GroupSection::accept(MutableSectionVisitor &Visitor) { return Visitor.visit(*this); -} - -// Returns true IFF a section is wholly inside the range of a segment -static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) { - // If a section is empty it should be treated like it has a size of 1. This is - // to clarify the case when an empty section lies on a boundary between two - // segments and ensures that the section "belongs" to the second segment and - // not the first. - uint64_t SecSize = Sec.Size ? Sec.Size : 1; - - if (Sec.Type == SHT_NOBITS) { - if (!(Sec.Flags & SHF_ALLOC)) - return false; - - bool SectionIsTLS = Sec.Flags & SHF_TLS; - bool SegmentIsTLS = Seg.Type == PT_TLS; - if (SectionIsTLS != SegmentIsTLS) - return false; - - return Seg.VAddr <= Sec.Addr && - Seg.VAddr + Seg.MemSize >= Sec.Addr + SecSize; - } - - return Seg.Offset <= Sec.OriginalOffset && - Seg.Offset + Seg.FileSize >= Sec.OriginalOffset + SecSize; -} - -// Returns true IFF a segment's original offset is inside of another segment's -// range. -static bool segmentOverlapsSegment(const Segment &Child, - const Segment &Parent) { - - return Parent.OriginalOffset <= Child.OriginalOffset && - Parent.OriginalOffset + Parent.FileSize > Child.OriginalOffset; -} - -static bool compareSegmentsByOffset(const Segment *A, const Segment *B) { - // Any segment without a parent segment should come before a segment - // that has a parent segment. - if (A->OriginalOffset < B->OriginalOffset) - return true; - if (A->OriginalOffset > B->OriginalOffset) - return false; - return A->Index < B->Index; -} - -void BasicELFBuilder::initFileHeader() { - Obj->Flags = 0x0; - Obj->Type = ET_REL; - Obj->OSABI = ELFOSABI_NONE; - Obj->ABIVersion = 0; - Obj->Entry = 0x0; - Obj->Machine = EM_NONE; - Obj->Version = 1; -} - -void BasicELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } - -StringTableSection *BasicELFBuilder::addStrTab() { - auto &StrTab = Obj->addSection<StringTableSection>(); - StrTab.Name = ".strtab"; - - Obj->SectionNames = &StrTab; - return &StrTab; -} - -SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) { - auto &SymTab = Obj->addSection<SymbolTableSection>(); - - SymTab.Name = ".symtab"; - SymTab.Link = StrTab->Index; - - // The symbol table always needs a null symbol - SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); - - Obj->SymbolTable = &SymTab; - return &SymTab; -} - +} + +// Returns true IFF a section is wholly inside the range of a segment +static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) { + // If a section is empty it should be treated like it has a size of 1. This is + // to clarify the case when an empty section lies on a boundary between two + // segments and ensures that the section "belongs" to the second segment and + // not the first. + uint64_t SecSize = Sec.Size ? Sec.Size : 1; + + if (Sec.Type == SHT_NOBITS) { + if (!(Sec.Flags & SHF_ALLOC)) + return false; + + bool SectionIsTLS = Sec.Flags & SHF_TLS; + bool SegmentIsTLS = Seg.Type == PT_TLS; + if (SectionIsTLS != SegmentIsTLS) + return false; + + return Seg.VAddr <= Sec.Addr && + Seg.VAddr + Seg.MemSize >= Sec.Addr + SecSize; + } + + return Seg.Offset <= Sec.OriginalOffset && + Seg.Offset + Seg.FileSize >= Sec.OriginalOffset + SecSize; +} + +// Returns true IFF a segment's original offset is inside of another segment's +// range. +static bool segmentOverlapsSegment(const Segment &Child, + const Segment &Parent) { + + return Parent.OriginalOffset <= Child.OriginalOffset && + Parent.OriginalOffset + Parent.FileSize > Child.OriginalOffset; +} + +static bool compareSegmentsByOffset(const Segment *A, const Segment *B) { + // Any segment without a parent segment should come before a segment + // that has a parent segment. + if (A->OriginalOffset < B->OriginalOffset) + return true; + if (A->OriginalOffset > B->OriginalOffset) + return false; + return A->Index < B->Index; +} + +void BasicELFBuilder::initFileHeader() { + Obj->Flags = 0x0; + Obj->Type = ET_REL; + Obj->OSABI = ELFOSABI_NONE; + Obj->ABIVersion = 0; + Obj->Entry = 0x0; + Obj->Machine = EM_NONE; + Obj->Version = 1; +} + +void BasicELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } + +StringTableSection *BasicELFBuilder::addStrTab() { + auto &StrTab = Obj->addSection<StringTableSection>(); + StrTab.Name = ".strtab"; + + Obj->SectionNames = &StrTab; + return &StrTab; +} + +SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) { + auto &SymTab = Obj->addSection<SymbolTableSection>(); + + SymTab.Name = ".symtab"; + SymTab.Link = StrTab->Index; + + // The symbol table always needs a null symbol + SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); + + Obj->SymbolTable = &SymTab; + return &SymTab; +} + Error BasicELFBuilder::initSections() { - for (SectionBase &Sec : Obj->sections()) + for (SectionBase &Sec : Obj->sections()) if (Error Err = Sec.initialize(Obj->sections())) return Err; return Error::success(); -} - -void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { - auto Data = ArrayRef<uint8_t>( - reinterpret_cast<const uint8_t *>(MemBuf->getBufferStart()), - MemBuf->getBufferSize()); - auto &DataSection = Obj->addSection<Section>(Data); - DataSection.Name = ".data"; - DataSection.Type = ELF::SHT_PROGBITS; - DataSection.Size = Data.size(); - DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; - - std::string SanitizedFilename = MemBuf->getBufferIdentifier().str(); - std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename), - [](char C) { return !isalnum(C); }, '_'); - Twine Prefix = Twine("_binary_") + SanitizedFilename; - - SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, - /*Value=*/0, NewSymbolVisibility, 0, 0); - SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection, - /*Value=*/DataSection.Size, NewSymbolVisibility, 0, 0); - SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr, - /*Value=*/DataSection.Size, NewSymbolVisibility, SHN_ABS, - 0); -} - +} + +void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { + auto Data = ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(MemBuf->getBufferStart()), + MemBuf->getBufferSize()); + auto &DataSection = Obj->addSection<Section>(Data); + DataSection.Name = ".data"; + DataSection.Type = ELF::SHT_PROGBITS; + DataSection.Size = Data.size(); + DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; + + std::string SanitizedFilename = MemBuf->getBufferIdentifier().str(); + std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename), + [](char C) { return !isalnum(C); }, '_'); + Twine Prefix = Twine("_binary_") + SanitizedFilename; + + SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, + /*Value=*/0, NewSymbolVisibility, 0, 0); + SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection, + /*Value=*/DataSection.Size, NewSymbolVisibility, 0, 0); + SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr, + /*Value=*/DataSection.Size, NewSymbolVisibility, SHN_ABS, + 0); +} + Expected<std::unique_ptr<Object>> BinaryELFBuilder::build() { - initFileHeader(); - initHeaderSegment(); - - SymbolTableSection *SymTab = addSymTab(addStrTab()); + initFileHeader(); + initHeaderSegment(); + + SymbolTableSection *SymTab = addSymTab(addStrTab()); if (Error Err = initSections()) return std::move(Err); - addData(SymTab); - - return std::move(Obj); -} - -// Adds sections from IHEX data file. Data should have been -// fully validated by this time. -void IHexELFBuilder::addDataSections() { - OwnedDataSection *Section = nullptr; - uint64_t SegmentAddr = 0, BaseAddr = 0; - uint32_t SecNo = 1; - - for (const IHexRecord &R : Records) { - uint64_t RecAddr; - switch (R.Type) { - case IHexRecord::Data: - // Ignore empty data records - if (R.HexData.empty()) - continue; - RecAddr = R.Addr + SegmentAddr + BaseAddr; - if (!Section || Section->Addr + Section->Size != RecAddr) - // OriginalOffset field is only used to sort section properly, so - // instead of keeping track of real offset in IHEX file, we use - // section number. - Section = &Obj->addSection<OwnedDataSection>( - ".sec" + std::to_string(SecNo++), RecAddr, - ELF::SHF_ALLOC | ELF::SHF_WRITE, SecNo); - Section->appendHexData(R.HexData); - break; - case IHexRecord::EndOfFile: - break; - case IHexRecord::SegmentAddr: - // 20-bit segment address. - SegmentAddr = checkedGetHex<uint16_t>(R.HexData) << 4; - break; - case IHexRecord::StartAddr80x86: - case IHexRecord::StartAddr: - Obj->Entry = checkedGetHex<uint32_t>(R.HexData); - assert(Obj->Entry <= 0xFFFFFU); - break; - case IHexRecord::ExtendedAddr: - // 16-31 bits of linear base address - BaseAddr = checkedGetHex<uint16_t>(R.HexData) << 16; - break; - default: - llvm_unreachable("unknown record type"); - } - } -} - + addData(SymTab); + + return std::move(Obj); +} + +// Adds sections from IHEX data file. Data should have been +// fully validated by this time. +void IHexELFBuilder::addDataSections() { + OwnedDataSection *Section = nullptr; + uint64_t SegmentAddr = 0, BaseAddr = 0; + uint32_t SecNo = 1; + + for (const IHexRecord &R : Records) { + uint64_t RecAddr; + switch (R.Type) { + case IHexRecord::Data: + // Ignore empty data records + if (R.HexData.empty()) + continue; + RecAddr = R.Addr + SegmentAddr + BaseAddr; + if (!Section || Section->Addr + Section->Size != RecAddr) + // OriginalOffset field is only used to sort section properly, so + // instead of keeping track of real offset in IHEX file, we use + // section number. + Section = &Obj->addSection<OwnedDataSection>( + ".sec" + std::to_string(SecNo++), RecAddr, + ELF::SHF_ALLOC | ELF::SHF_WRITE, SecNo); + Section->appendHexData(R.HexData); + break; + case IHexRecord::EndOfFile: + break; + case IHexRecord::SegmentAddr: + // 20-bit segment address. + SegmentAddr = checkedGetHex<uint16_t>(R.HexData) << 4; + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + Obj->Entry = checkedGetHex<uint32_t>(R.HexData); + assert(Obj->Entry <= 0xFFFFFU); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + BaseAddr = checkedGetHex<uint16_t>(R.HexData) << 16; + break; + default: + llvm_unreachable("unknown record type"); + } + } +} + Expected<std::unique_ptr<Object>> IHexELFBuilder::build() { - initFileHeader(); - initHeaderSegment(); - StringTableSection *StrTab = addStrTab(); - addSymTab(StrTab); + initFileHeader(); + initHeaderSegment(); + StringTableSection *StrTab = addStrTab(); + addSymTab(StrTab); if (Error Err = initSections()) return std::move(Err); - addDataSections(); - - return std::move(Obj); -} - -template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) { - for (Segment &Parent : Obj.segments()) { - // Every segment will overlap with itself but we don't want a segment to - // be its own parent so we avoid that situation. - if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) { - // We want a canonical "most parental" segment but this requires - // inspecting the ParentSegment. - if (compareSegmentsByOffset(&Parent, &Child)) - if (Child.ParentSegment == nullptr || - compareSegmentsByOffset(&Parent, Child.ParentSegment)) { - Child.ParentSegment = &Parent; - } - } - } -} - + addDataSections(); + + return std::move(Obj); +} + +template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) { + for (Segment &Parent : Obj.segments()) { + // Every segment will overlap with itself but we don't want a segment to + // be its own parent so we avoid that situation. + if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) { + // We want a canonical "most parental" segment but this requires + // inspecting the ParentSegment. + if (compareSegmentsByOffset(&Parent, &Child)) + if (Child.ParentSegment == nullptr || + compareSegmentsByOffset(&Parent, Child.ParentSegment)) { + Child.ParentSegment = &Parent; + } + } + } +} + template <class ELFT> Error ELFBuilder<ELFT>::findEhdrOffset() { - if (!ExtractPartition) + if (!ExtractPartition) return Error::success(); - - for (const SectionBase &Sec : Obj.sections()) { - if (Sec.Type == SHT_LLVM_PART_EHDR && Sec.Name == *ExtractPartition) { - EhdrOffset = Sec.Offset; + + for (const SectionBase &Sec : Obj.sections()) { + if (Sec.Type == SHT_LLVM_PART_EHDR && Sec.Name == *ExtractPartition) { + EhdrOffset = Sec.Offset; return Error::success(); - } - } + } + } return createStringError(errc::invalid_argument, "could not find partition named '" + *ExtractPartition + "'"); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFBuilder<ELFT>::readProgramHeaders(const ELFFile<ELFT> &HeadersFile) { - uint32_t Index = 0; + uint32_t Index = 0; Expected<typename ELFFile<ELFT>::Elf_Phdr_Range> Headers = HeadersFile.program_headers(); @@ -1413,114 +1413,114 @@ Error ELFBuilder<ELFT>::readProgramHeaders(const ELFFile<ELFT> &HeadersFile) { return Headers.takeError(); for (const typename ELFFile<ELFT>::Elf_Phdr &Phdr : *Headers) { - if (Phdr.p_offset + Phdr.p_filesz > HeadersFile.getBufSize()) + if (Phdr.p_offset + Phdr.p_filesz > HeadersFile.getBufSize()) return createStringError( errc::invalid_argument, "program header with offset 0x" + Twine::utohexstr(Phdr.p_offset) + " and file size 0x" + Twine::utohexstr(Phdr.p_filesz) + " goes past the end of the file"); - - ArrayRef<uint8_t> Data{HeadersFile.base() + Phdr.p_offset, - (size_t)Phdr.p_filesz}; - Segment &Seg = Obj.addSegment(Data); - Seg.Type = Phdr.p_type; - Seg.Flags = Phdr.p_flags; - Seg.OriginalOffset = Phdr.p_offset + EhdrOffset; - Seg.Offset = Phdr.p_offset + EhdrOffset; - Seg.VAddr = Phdr.p_vaddr; - Seg.PAddr = Phdr.p_paddr; - Seg.FileSize = Phdr.p_filesz; - Seg.MemSize = Phdr.p_memsz; - Seg.Align = Phdr.p_align; - Seg.Index = Index++; - for (SectionBase &Sec : Obj.sections()) - if (sectionWithinSegment(Sec, Seg)) { - Seg.addSection(&Sec); - if (!Sec.ParentSegment || Sec.ParentSegment->Offset > Seg.Offset) - Sec.ParentSegment = &Seg; - } - } - - auto &ElfHdr = Obj.ElfHdrSegment; - ElfHdr.Index = Index++; - ElfHdr.OriginalOffset = ElfHdr.Offset = EhdrOffset; - + + ArrayRef<uint8_t> Data{HeadersFile.base() + Phdr.p_offset, + (size_t)Phdr.p_filesz}; + Segment &Seg = Obj.addSegment(Data); + Seg.Type = Phdr.p_type; + Seg.Flags = Phdr.p_flags; + Seg.OriginalOffset = Phdr.p_offset + EhdrOffset; + Seg.Offset = Phdr.p_offset + EhdrOffset; + Seg.VAddr = Phdr.p_vaddr; + Seg.PAddr = Phdr.p_paddr; + Seg.FileSize = Phdr.p_filesz; + Seg.MemSize = Phdr.p_memsz; + Seg.Align = Phdr.p_align; + Seg.Index = Index++; + for (SectionBase &Sec : Obj.sections()) + if (sectionWithinSegment(Sec, Seg)) { + Seg.addSection(&Sec); + if (!Sec.ParentSegment || Sec.ParentSegment->Offset > Seg.Offset) + Sec.ParentSegment = &Seg; + } + } + + auto &ElfHdr = Obj.ElfHdrSegment; + ElfHdr.Index = Index++; + ElfHdr.OriginalOffset = ElfHdr.Offset = EhdrOffset; + const typename ELFT::Ehdr &Ehdr = HeadersFile.getHeader(); - auto &PrHdr = Obj.ProgramHdrSegment; - PrHdr.Type = PT_PHDR; - PrHdr.Flags = 0; - // The spec requires us to have p_vaddr % p_align == p_offset % p_align. - // Whereas this works automatically for ElfHdr, here OriginalOffset is - // always non-zero and to ensure the equation we assign the same value to - // VAddr as well. - PrHdr.OriginalOffset = PrHdr.Offset = PrHdr.VAddr = EhdrOffset + Ehdr.e_phoff; - PrHdr.PAddr = 0; - PrHdr.FileSize = PrHdr.MemSize = Ehdr.e_phentsize * Ehdr.e_phnum; - // The spec requires us to naturally align all the fields. - PrHdr.Align = sizeof(Elf_Addr); - PrHdr.Index = Index++; - - // Now we do an O(n^2) loop through the segments in order to match up - // segments. - for (Segment &Child : Obj.segments()) - setParentSegment(Child); - setParentSegment(ElfHdr); - setParentSegment(PrHdr); + auto &PrHdr = Obj.ProgramHdrSegment; + PrHdr.Type = PT_PHDR; + PrHdr.Flags = 0; + // The spec requires us to have p_vaddr % p_align == p_offset % p_align. + // Whereas this works automatically for ElfHdr, here OriginalOffset is + // always non-zero and to ensure the equation we assign the same value to + // VAddr as well. + PrHdr.OriginalOffset = PrHdr.Offset = PrHdr.VAddr = EhdrOffset + Ehdr.e_phoff; + PrHdr.PAddr = 0; + PrHdr.FileSize = PrHdr.MemSize = Ehdr.e_phentsize * Ehdr.e_phnum; + // The spec requires us to naturally align all the fields. + PrHdr.Align = sizeof(Elf_Addr); + PrHdr.Index = Index++; + + // Now we do an O(n^2) loop through the segments in order to match up + // segments. + for (Segment &Child : Obj.segments()) + setParentSegment(Child); + setParentSegment(ElfHdr); + setParentSegment(PrHdr); return Error::success(); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) { - if (GroupSec->Align % sizeof(ELF::Elf32_Word) != 0) + if (GroupSec->Align % sizeof(ELF::Elf32_Word) != 0) return createStringError(errc::invalid_argument, "invalid alignment " + Twine(GroupSec->Align) + " of group section '" + GroupSec->Name + "'"); - SectionTableRef SecTable = Obj.sections(); - if (GroupSec->Link != SHN_UNDEF) { - auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>( - GroupSec->Link, - "link field value '" + Twine(GroupSec->Link) + "' in section '" + - GroupSec->Name + "' is invalid", - "link field value '" + Twine(GroupSec->Link) + "' in section '" + - GroupSec->Name + "' is not a symbol table"); + SectionTableRef SecTable = Obj.sections(); + if (GroupSec->Link != SHN_UNDEF) { + auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>( + GroupSec->Link, + "link field value '" + Twine(GroupSec->Link) + "' in section '" + + GroupSec->Name + "' is invalid", + "link field value '" + Twine(GroupSec->Link) + "' in section '" + + GroupSec->Name + "' is not a symbol table"); if (!SymTab) return SymTab.takeError(); Expected<Symbol *> Sym = (*SymTab)->getSymbolByIndex(GroupSec->Info); - if (!Sym) + if (!Sym) return createStringError(errc::invalid_argument, "info field value '" + Twine(GroupSec->Info) + "' in section '" + GroupSec->Name + "' is not a valid symbol index"); GroupSec->setSymTab(*SymTab); GroupSec->setSymbol(*Sym); - } - if (GroupSec->Contents.size() % sizeof(ELF::Elf32_Word) || - GroupSec->Contents.empty()) + } + if (GroupSec->Contents.size() % sizeof(ELF::Elf32_Word) || + GroupSec->Contents.empty()) return createStringError(errc::invalid_argument, "the content of the section " + GroupSec->Name + " is malformed"); - const ELF::Elf32_Word *Word = - reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data()); - const ELF::Elf32_Word *End = - Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word); - GroupSec->setFlagWord(*Word++); - for (; Word != End; ++Word) { - uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word); + const ELF::Elf32_Word *Word = + reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data()); + const ELF::Elf32_Word *End = + Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word); + GroupSec->setFlagWord(*Word++); + for (; Word != End; ++Word) { + uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word); Expected<SectionBase *> Sec = SecTable.getSection( - Index, "group member index " + Twine(Index) + " in section '" + + Index, "group member index " + Twine(Index) + " in section '" + GroupSec->Name + "' is invalid"); if (!Sec) return Sec.takeError(); GroupSec->addMember(*Sec); - } + } return Error::success(); -} - -template <class ELFT> +} + +template <class ELFT> Error ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) { Expected<const Elf_Shdr *> Shdr = ElfFile.getSection(SymTab->Index); if (!Shdr) @@ -1530,27 +1530,27 @@ Error ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) { if (!StrTabData) return StrTabData.takeError(); - ArrayRef<Elf_Word> ShndxData; - + ArrayRef<Elf_Word> ShndxData; + Expected<typename ELFFile<ELFT>::Elf_Sym_Range> Symbols = ElfFile.symbols(*Shdr); if (!Symbols) return Symbols.takeError(); for (const typename ELFFile<ELFT>::Elf_Sym &Sym : *Symbols) { - SectionBase *DefSection = nullptr; - + SectionBase *DefSection = nullptr; + Expected<StringRef> Name = Sym.getName(*StrTabData); if (!Name) return Name.takeError(); - if (Sym.st_shndx == SHN_XINDEX) { - if (SymTab->getShndxTable() == nullptr) + if (Sym.st_shndx == SHN_XINDEX) { + if (SymTab->getShndxTable() == nullptr) return createStringError(errc::invalid_argument, "symbol '" + *Name + "' has index SHN_XINDEX but no " "SHT_SYMTAB_SHNDX section exists"); - if (ShndxData.data() == nullptr) { + if (ShndxData.data() == nullptr) { Expected<const Elf_Shdr *> ShndxSec = ElfFile.getSection(SymTab->getShndxTable()->Index); if (!ShndxSec) @@ -1567,61 +1567,61 @@ Error ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) { errc::invalid_argument, "symbol section index table does not have the same number of " "entries as the symbol table"); - } + } Elf_Word Index = ShndxData[&Sym - Symbols->begin()]; Expected<SectionBase *> Sec = Obj.sections().getSection( - Index, + Index, "symbol '" + *Name + "' has invalid section index " + Twine(Index)); if (!Sec) return Sec.takeError(); DefSection = *Sec; - } else if (Sym.st_shndx >= SHN_LORESERVE) { - if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) { + } else if (Sym.st_shndx >= SHN_LORESERVE) { + if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) { return createStringError( errc::invalid_argument, "symbol '" + *Name + "' has unsupported value greater than or equal " "to SHN_LORESERVE: " + Twine(Sym.st_shndx)); - } - } else if (Sym.st_shndx != SHN_UNDEF) { + } + } else if (Sym.st_shndx != SHN_UNDEF) { Expected<SectionBase *> Sec = Obj.sections().getSection( Sym.st_shndx, "symbol '" + *Name + - "' is defined has invalid section index " + - Twine(Sym.st_shndx)); + "' is defined has invalid section index " + + Twine(Sym.st_shndx)); if (!Sec) return Sec.takeError(); DefSection = *Sec; - } - + } + SymTab->addSymbol(*Name, Sym.getBinding(), Sym.getType(), DefSection, - Sym.getValue(), Sym.st_other, Sym.st_shndx, Sym.st_size); - } + Sym.getValue(), Sym.st_other, Sym.st_shndx, Sym.st_size); + } return Error::success(); -} - -template <class ELFT> +} + +template <class ELFT> static void getAddend(uint64_t &, const Elf_Rel_Impl<ELFT, false> &) {} - -template <class ELFT> -static void getAddend(uint64_t &ToSet, const Elf_Rel_Impl<ELFT, true> &Rela) { - ToSet = Rela.r_addend; -} - -template <class T> + +template <class ELFT> +static void getAddend(uint64_t &ToSet, const Elf_Rel_Impl<ELFT, true> &Rela) { + ToSet = Rela.r_addend; +} + +template <class T> static Error initRelocations(RelocationSection *Relocs, SymbolTableSection *SymbolTable, T RelRange) { - for (const auto &Rel : RelRange) { - Relocation ToAdd; - ToAdd.Offset = Rel.r_offset; - getAddend(ToAdd.Addend, Rel); - ToAdd.Type = Rel.getType(false); - - if (uint32_t Sym = Rel.getSymbol(false)) { - if (!SymbolTable) + for (const auto &Rel : RelRange) { + Relocation ToAdd; + ToAdd.Offset = Rel.r_offset; + getAddend(ToAdd.Addend, Rel); + ToAdd.Type = Rel.getType(false); + + if (uint32_t Sym = Rel.getSymbol(false)) { + if (!SymbolTable) return createStringError( errc::invalid_argument, "'" + Relocs->Name + "': relocation references symbol with index " + @@ -1631,22 +1631,22 @@ static Error initRelocations(RelocationSection *Relocs, return SymByIndex.takeError(); ToAdd.RelocSymbol = *SymByIndex; - } - - Relocs->addRelocation(ToAdd); - } + } + + Relocs->addRelocation(ToAdd); + } return Error::success(); -} - +} + Expected<SectionBase *> SectionTableRef::getSection(uint32_t Index, Twine ErrMsg) { - if (Index == SHN_UNDEF || Index > Sections.size()) + if (Index == SHN_UNDEF || Index > Sections.size()) return createStringError(errc::invalid_argument, ErrMsg); - return Sections[Index - 1].get(); -} - -template <class T> + return Sections[Index - 1].get(); +} + +template <class T> Expected<T *> SectionTableRef::getSectionOfType(uint32_t Index, Twine IndexErrMsg, Twine TypeErrMsg) { @@ -1655,81 +1655,81 @@ Expected<T *> SectionTableRef::getSectionOfType(uint32_t Index, return BaseSec.takeError(); if (T *Sec = dyn_cast<T>(*BaseSec)) - return Sec; + return Sec; return createStringError(errc::invalid_argument, TypeErrMsg); -} - -template <class ELFT> +} + +template <class ELFT> Expected<SectionBase &> ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) { - switch (Shdr.sh_type) { - case SHT_REL: - case SHT_RELA: - if (Shdr.sh_flags & SHF_ALLOC) { + switch (Shdr.sh_type) { + case SHT_REL: + case SHT_RELA: + if (Shdr.sh_flags & SHF_ALLOC) { if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr)) return Obj.addSection<DynamicRelocationSection>(*Data); else return Data.takeError(); - } - return Obj.addSection<RelocationSection>(); - case SHT_STRTAB: - // If a string table is allocated we don't want to mess with it. That would - // mean altering the memory image. There are no special link types or - // anything so we can just use a Section. - if (Shdr.sh_flags & SHF_ALLOC) { + } + return Obj.addSection<RelocationSection>(); + case SHT_STRTAB: + // If a string table is allocated we don't want to mess with it. That would + // mean altering the memory image. There are no special link types or + // anything so we can just use a Section. + if (Shdr.sh_flags & SHF_ALLOC) { if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr)) return Obj.addSection<Section>(*Data); else return Data.takeError(); - } - return Obj.addSection<StringTableSection>(); - case SHT_HASH: - case SHT_GNU_HASH: - // Hash tables should refer to SHT_DYNSYM which we're not going to change. - // Because of this we don't need to mess with the hash tables either. + } + return Obj.addSection<StringTableSection>(); + case SHT_HASH: + case SHT_GNU_HASH: + // Hash tables should refer to SHT_DYNSYM which we're not going to change. + // Because of this we don't need to mess with the hash tables either. if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr)) return Obj.addSection<Section>(*Data); else return Data.takeError(); - case SHT_GROUP: + case SHT_GROUP: if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr)) return Obj.addSection<GroupSection>(*Data); else return Data.takeError(); - case SHT_DYNSYM: + case SHT_DYNSYM: if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr)) return Obj.addSection<DynamicSymbolTableSection>(*Data); else return Data.takeError(); - case SHT_DYNAMIC: + case SHT_DYNAMIC: if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr)) return Obj.addSection<DynamicSection>(*Data); else return Data.takeError(); - case SHT_SYMTAB: { - auto &SymTab = Obj.addSection<SymbolTableSection>(); - Obj.SymbolTable = &SymTab; - return SymTab; - } - case SHT_SYMTAB_SHNDX: { - auto &ShndxSection = Obj.addSection<SectionIndexSection>(); - Obj.SectionIndexTable = &ShndxSection; - return ShndxSection; - } - case SHT_NOBITS: + case SHT_SYMTAB: { + auto &SymTab = Obj.addSection<SymbolTableSection>(); + Obj.SymbolTable = &SymTab; + return SymTab; + } + case SHT_SYMTAB_SHNDX: { + auto &ShndxSection = Obj.addSection<SectionIndexSection>(); + Obj.SectionIndexTable = &ShndxSection; + return ShndxSection; + } + case SHT_NOBITS: return Obj.addSection<Section>(ArrayRef<uint8_t>()); - default: { + default: { Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr); if (!Data) return Data.takeError(); - + Expected<StringRef> Name = ElfFile.getSectionName(Shdr); if (!Name) return Name.takeError(); if (Name->startswith(".zdebug") || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) { - uint64_t DecompressedSize, DecompressedAlign; - std::tie(DecompressedSize, DecompressedAlign) = + uint64_t DecompressedSize, DecompressedAlign; + std::tie(DecompressedSize, DecompressedAlign) = getDecompressedSizeAndAlignment<ELFT>(*Data); Expected<CompressedSection> NewSection = CompressedSection::create(*Data, DecompressedSize, DecompressedAlign); @@ -1737,25 +1737,25 @@ Expected<SectionBase &> ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) { return NewSection.takeError(); return Obj.addSection<CompressedSection>(std::move(*NewSection)); - } - + } + return Obj.addSection<Section>(*Data); - } - } -} - + } + } +} + template <class ELFT> Error ELFBuilder<ELFT>::readSectionHeaders() { - uint32_t Index = 0; + uint32_t Index = 0; Expected<typename ELFFile<ELFT>::Elf_Shdr_Range> Sections = ElfFile.sections(); if (!Sections) return Sections.takeError(); for (const typename ELFFile<ELFT>::Elf_Shdr &Shdr : *Sections) { - if (Index == 0) { - ++Index; - continue; - } + if (Index == 0) { + ++Index; + continue; + } Expected<SectionBase &> Sec = makeSection(Shdr); if (!Sec) return Sec.takeError(); @@ -1777,68 +1777,68 @@ template <class ELFT> Error ELFBuilder<ELFT>::readSectionHeaders() { Sec->Index = Index++; Sec->OriginalIndex = Sec->Index; Sec->OriginalData = - ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, - (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); - } + ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, + (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); + } return Error::success(); -} - +} + template <class ELFT> Error ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { uint32_t ShstrIndex = ElfFile.getHeader().e_shstrndx; if (ShstrIndex == SHN_XINDEX) { Expected<const Elf_Shdr *> Sec = ElfFile.getSection(0); if (!Sec) return Sec.takeError(); - + ShstrIndex = (*Sec)->sh_link; } - if (ShstrIndex == SHN_UNDEF) - Obj.HadShdrs = false; + if (ShstrIndex == SHN_UNDEF) + Obj.HadShdrs = false; else { Expected<StringTableSection *> Sec = - Obj.sections().template getSectionOfType<StringTableSection>( - ShstrIndex, - "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + - " is invalid", - "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + - " does not reference a string table"); + Obj.sections().template getSectionOfType<StringTableSection>( + ShstrIndex, + "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + + " is invalid", + "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + + " does not reference a string table"); if (!Sec) return Sec.takeError(); - + Obj.SectionNames = *Sec; } - // If a section index table exists we'll need to initialize it before we - // initialize the symbol table because the symbol table might need to - // reference it. - if (Obj.SectionIndexTable) + // If a section index table exists we'll need to initialize it before we + // initialize the symbol table because the symbol table might need to + // reference it. + if (Obj.SectionIndexTable) if (Error Err = Obj.SectionIndexTable->initialize(Obj.sections())) return Err; - - // Now that all of the sections have been added we can fill out some extra - // details about symbol tables. We need the symbol table filled out before - // any relocations. - if (Obj.SymbolTable) { + + // Now that all of the sections have been added we can fill out some extra + // details about symbol tables. We need the symbol table filled out before + // any relocations. + if (Obj.SymbolTable) { if (Error Err = Obj.SymbolTable->initialize(Obj.sections())) return Err; if (Error Err = initSymbolTable(Obj.SymbolTable)) return Err; - } else if (EnsureSymtab) { + } else if (EnsureSymtab) { if (Error Err = Obj.addNewSymbolTable()) return Err; - } - - // Now that all sections and symbols have been added we can add - // relocations that reference symbols and set the link and info fields for - // relocation sections. + } + + // Now that all sections and symbols have been added we can add + // relocations that reference symbols and set the link and info fields for + // relocation sections. for (SectionBase &Sec : Obj.sections()) { - if (&Sec == Obj.SymbolTable) - continue; + if (&Sec == Obj.SymbolTable) + continue; if (Error Err = Sec.initialize(Obj.sections())) return Err; - if (auto RelSec = dyn_cast<RelocationSection>(&Sec)) { + if (auto RelSec = dyn_cast<RelocationSection>(&Sec)) { Expected<typename ELFFile<ELFT>::Elf_Shdr_Range> Sections = ElfFile.sections(); if (!Sections) @@ -1863,78 +1863,78 @@ template <class ELFT> Error ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { if (Error Err = initRelocations(RelSec, Obj.SymbolTable, *Relas)) return Err; } - } else if (auto GroupSec = dyn_cast<GroupSection>(&Sec)) { + } else if (auto GroupSec = dyn_cast<GroupSection>(&Sec)) { if (Error Err = initGroupSection(GroupSec)) return Err; - } - } + } + } return Error::success(); -} - +} + template <class ELFT> Error ELFBuilder<ELFT>::build(bool EnsureSymtab) { if (Error E = readSectionHeaders()) return E; if (Error E = findEhdrOffset()) return E; - - // The ELFFile whose ELF headers and program headers are copied into the - // output file. Normally the same as ElfFile, but if we're extracting a - // loadable partition it will point to the partition's headers. + + // The ELFFile whose ELF headers and program headers are copied into the + // output file. Normally the same as ElfFile, but if we're extracting a + // loadable partition it will point to the partition's headers. Expected<ELFFile<ELFT>> HeadersFile = ELFFile<ELFT>::create(toStringRef( {ElfFile.base() + EhdrOffset, ElfFile.getBufSize() - EhdrOffset})); if (!HeadersFile) return HeadersFile.takeError(); - + const typename ELFFile<ELFT>::Elf_Ehdr &Ehdr = HeadersFile->getHeader(); - Obj.OSABI = Ehdr.e_ident[EI_OSABI]; - Obj.ABIVersion = Ehdr.e_ident[EI_ABIVERSION]; - Obj.Type = Ehdr.e_type; - Obj.Machine = Ehdr.e_machine; - Obj.Version = Ehdr.e_version; - Obj.Entry = Ehdr.e_entry; - Obj.Flags = Ehdr.e_flags; - + Obj.OSABI = Ehdr.e_ident[EI_OSABI]; + Obj.ABIVersion = Ehdr.e_ident[EI_ABIVERSION]; + Obj.Type = Ehdr.e_type; + Obj.Machine = Ehdr.e_machine; + Obj.Version = Ehdr.e_version; + Obj.Entry = Ehdr.e_entry; + Obj.Flags = Ehdr.e_flags; + if (Error E = readSections(EnsureSymtab)) return E; return readProgramHeaders(*HeadersFile); -} - -Writer::~Writer() {} - -Reader::~Reader() {} - +} + +Writer::~Writer() {} + +Reader::~Reader() {} + Expected<std::unique_ptr<Object>> BinaryReader::create(bool /*EnsureSymtab*/) const { - return BinaryELFBuilder(MemBuf, NewSymbolVisibility).build(); -} - -Expected<std::vector<IHexRecord>> IHexReader::parse() const { - SmallVector<StringRef, 16> Lines; - std::vector<IHexRecord> Records; - bool HasSections = false; - - MemBuf->getBuffer().split(Lines, '\n'); - Records.reserve(Lines.size()); - for (size_t LineNo = 1; LineNo <= Lines.size(); ++LineNo) { - StringRef Line = Lines[LineNo - 1].trim(); - if (Line.empty()) - continue; - - Expected<IHexRecord> R = IHexRecord::parse(Line); - if (!R) - return parseError(LineNo, R.takeError()); - if (R->Type == IHexRecord::EndOfFile) - break; - HasSections |= (R->Type == IHexRecord::Data); - Records.push_back(*R); - } - if (!HasSections) - return parseError(-1U, "no sections"); - - return std::move(Records); -} - + return BinaryELFBuilder(MemBuf, NewSymbolVisibility).build(); +} + +Expected<std::vector<IHexRecord>> IHexReader::parse() const { + SmallVector<StringRef, 16> Lines; + std::vector<IHexRecord> Records; + bool HasSections = false; + + MemBuf->getBuffer().split(Lines, '\n'); + Records.reserve(Lines.size()); + for (size_t LineNo = 1; LineNo <= Lines.size(); ++LineNo) { + StringRef Line = Lines[LineNo - 1].trim(); + if (Line.empty()) + continue; + + Expected<IHexRecord> R = IHexRecord::parse(Line); + if (!R) + return parseError(LineNo, R.takeError()); + if (R->Type == IHexRecord::EndOfFile) + break; + HasSections |= (R->Type == IHexRecord::Data); + Records.push_back(*R); + } + if (!HasSections) + return parseError(-1U, "no sections"); + + return std::move(Records); +} + Expected<std::unique_ptr<Object>> IHexReader::create(bool /*EnsureSymtab*/) const { Expected<std::vector<IHexRecord>> Records = parse(); @@ -1942,375 +1942,375 @@ IHexReader::create(bool /*EnsureSymtab*/) const { return Records.takeError(); return IHexELFBuilder(*Records).build(); -} - +} + Expected<std::unique_ptr<Object>> ELFReader::create(bool EnsureSymtab) const { - auto Obj = std::make_unique<Object>(); - if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { - ELFBuilder<ELF32LE> Builder(*O, *Obj, ExtractPartition); + auto Obj = std::make_unique<Object>(); + if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { + ELFBuilder<ELF32LE> Builder(*O, *Obj, ExtractPartition); if (Error Err = Builder.build(EnsureSymtab)) return std::move(Err); return std::move(Obj); - } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { - ELFBuilder<ELF64LE> Builder(*O, *Obj, ExtractPartition); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { + ELFBuilder<ELF64LE> Builder(*O, *Obj, ExtractPartition); if (Error Err = Builder.build(EnsureSymtab)) return std::move(Err); return std::move(Obj); - } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { - ELFBuilder<ELF32BE> Builder(*O, *Obj, ExtractPartition); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { + ELFBuilder<ELF32BE> Builder(*O, *Obj, ExtractPartition); if (Error Err = Builder.build(EnsureSymtab)) return std::move(Err); return std::move(Obj); - } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { - ELFBuilder<ELF64BE> Builder(*O, *Obj, ExtractPartition); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { + ELFBuilder<ELF64BE> Builder(*O, *Obj, ExtractPartition); if (Error Err = Builder.build(EnsureSymtab)) return std::move(Err); return std::move(Obj); - } + } return createStringError(errc::invalid_argument, "invalid file type"); -} - -template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { - Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf.getBufferStart()); - std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); - Ehdr.e_ident[EI_MAG0] = 0x7f; - Ehdr.e_ident[EI_MAG1] = 'E'; - Ehdr.e_ident[EI_MAG2] = 'L'; - Ehdr.e_ident[EI_MAG3] = 'F'; - Ehdr.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; - Ehdr.e_ident[EI_DATA] = - ELFT::TargetEndianness == support::big ? ELFDATA2MSB : ELFDATA2LSB; - Ehdr.e_ident[EI_VERSION] = EV_CURRENT; - Ehdr.e_ident[EI_OSABI] = Obj.OSABI; - Ehdr.e_ident[EI_ABIVERSION] = Obj.ABIVersion; - - Ehdr.e_type = Obj.Type; - Ehdr.e_machine = Obj.Machine; - Ehdr.e_version = Obj.Version; - Ehdr.e_entry = Obj.Entry; - // We have to use the fully-qualified name llvm::size - // since some compilers complain on ambiguous resolution. - Ehdr.e_phnum = llvm::size(Obj.segments()); - Ehdr.e_phoff = (Ehdr.e_phnum != 0) ? Obj.ProgramHdrSegment.Offset : 0; - Ehdr.e_phentsize = (Ehdr.e_phnum != 0) ? sizeof(Elf_Phdr) : 0; - Ehdr.e_flags = Obj.Flags; - Ehdr.e_ehsize = sizeof(Elf_Ehdr); - if (WriteSectionHeaders && Obj.sections().size() != 0) { - Ehdr.e_shentsize = sizeof(Elf_Shdr); - Ehdr.e_shoff = Obj.SHOff; - // """ - // If the number of sections is greater than or equal to - // SHN_LORESERVE (0xff00), this member has the value zero and the actual - // number of section header table entries is contained in the sh_size field - // of the section header at index 0. - // """ - auto Shnum = Obj.sections().size() + 1; - if (Shnum >= SHN_LORESERVE) - Ehdr.e_shnum = 0; - else - Ehdr.e_shnum = Shnum; - // """ - // If the section name string table section index is greater than or equal - // to SHN_LORESERVE (0xff00), this member has the value SHN_XINDEX (0xffff) - // and the actual index of the section name string table section is - // contained in the sh_link field of the section header at index 0. - // """ - if (Obj.SectionNames->Index >= SHN_LORESERVE) - Ehdr.e_shstrndx = SHN_XINDEX; - else - Ehdr.e_shstrndx = Obj.SectionNames->Index; - } else { - Ehdr.e_shentsize = 0; - Ehdr.e_shoff = 0; - Ehdr.e_shnum = 0; - Ehdr.e_shstrndx = 0; - } -} - -template <class ELFT> void ELFWriter<ELFT>::writePhdrs() { - for (auto &Seg : Obj.segments()) - writePhdr(Seg); -} - -template <class ELFT> void ELFWriter<ELFT>::writeShdrs() { - // This reference serves to write the dummy section header at the begining - // of the file. It is not used for anything else - Elf_Shdr &Shdr = - *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff); - Shdr.sh_name = 0; - Shdr.sh_type = SHT_NULL; - Shdr.sh_flags = 0; - Shdr.sh_addr = 0; - Shdr.sh_offset = 0; - // See writeEhdr for why we do this. - uint64_t Shnum = Obj.sections().size() + 1; - if (Shnum >= SHN_LORESERVE) - Shdr.sh_size = Shnum; - else - Shdr.sh_size = 0; - // See writeEhdr for why we do this. - if (Obj.SectionNames != nullptr && Obj.SectionNames->Index >= SHN_LORESERVE) - Shdr.sh_link = Obj.SectionNames->Index; - else - Shdr.sh_link = 0; - Shdr.sh_info = 0; - Shdr.sh_addralign = 0; - Shdr.sh_entsize = 0; - - for (SectionBase &Sec : Obj.sections()) - writeShdr(Sec); -} - +} + +template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { + Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf.getBufferStart()); + std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); + Ehdr.e_ident[EI_MAG0] = 0x7f; + Ehdr.e_ident[EI_MAG1] = 'E'; + Ehdr.e_ident[EI_MAG2] = 'L'; + Ehdr.e_ident[EI_MAG3] = 'F'; + Ehdr.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; + Ehdr.e_ident[EI_DATA] = + ELFT::TargetEndianness == support::big ? ELFDATA2MSB : ELFDATA2LSB; + Ehdr.e_ident[EI_VERSION] = EV_CURRENT; + Ehdr.e_ident[EI_OSABI] = Obj.OSABI; + Ehdr.e_ident[EI_ABIVERSION] = Obj.ABIVersion; + + Ehdr.e_type = Obj.Type; + Ehdr.e_machine = Obj.Machine; + Ehdr.e_version = Obj.Version; + Ehdr.e_entry = Obj.Entry; + // We have to use the fully-qualified name llvm::size + // since some compilers complain on ambiguous resolution. + Ehdr.e_phnum = llvm::size(Obj.segments()); + Ehdr.e_phoff = (Ehdr.e_phnum != 0) ? Obj.ProgramHdrSegment.Offset : 0; + Ehdr.e_phentsize = (Ehdr.e_phnum != 0) ? sizeof(Elf_Phdr) : 0; + Ehdr.e_flags = Obj.Flags; + Ehdr.e_ehsize = sizeof(Elf_Ehdr); + if (WriteSectionHeaders && Obj.sections().size() != 0) { + Ehdr.e_shentsize = sizeof(Elf_Shdr); + Ehdr.e_shoff = Obj.SHOff; + // """ + // If the number of sections is greater than or equal to + // SHN_LORESERVE (0xff00), this member has the value zero and the actual + // number of section header table entries is contained in the sh_size field + // of the section header at index 0. + // """ + auto Shnum = Obj.sections().size() + 1; + if (Shnum >= SHN_LORESERVE) + Ehdr.e_shnum = 0; + else + Ehdr.e_shnum = Shnum; + // """ + // If the section name string table section index is greater than or equal + // to SHN_LORESERVE (0xff00), this member has the value SHN_XINDEX (0xffff) + // and the actual index of the section name string table section is + // contained in the sh_link field of the section header at index 0. + // """ + if (Obj.SectionNames->Index >= SHN_LORESERVE) + Ehdr.e_shstrndx = SHN_XINDEX; + else + Ehdr.e_shstrndx = Obj.SectionNames->Index; + } else { + Ehdr.e_shentsize = 0; + Ehdr.e_shoff = 0; + Ehdr.e_shnum = 0; + Ehdr.e_shstrndx = 0; + } +} + +template <class ELFT> void ELFWriter<ELFT>::writePhdrs() { + for (auto &Seg : Obj.segments()) + writePhdr(Seg); +} + +template <class ELFT> void ELFWriter<ELFT>::writeShdrs() { + // This reference serves to write the dummy section header at the begining + // of the file. It is not used for anything else + Elf_Shdr &Shdr = + *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff); + Shdr.sh_name = 0; + Shdr.sh_type = SHT_NULL; + Shdr.sh_flags = 0; + Shdr.sh_addr = 0; + Shdr.sh_offset = 0; + // See writeEhdr for why we do this. + uint64_t Shnum = Obj.sections().size() + 1; + if (Shnum >= SHN_LORESERVE) + Shdr.sh_size = Shnum; + else + Shdr.sh_size = 0; + // See writeEhdr for why we do this. + if (Obj.SectionNames != nullptr && Obj.SectionNames->Index >= SHN_LORESERVE) + Shdr.sh_link = Obj.SectionNames->Index; + else + Shdr.sh_link = 0; + Shdr.sh_info = 0; + Shdr.sh_addralign = 0; + Shdr.sh_entsize = 0; + + for (SectionBase &Sec : Obj.sections()) + writeShdr(Sec); +} + template <class ELFT> Error ELFWriter<ELFT>::writeSectionData() { - for (SectionBase &Sec : Obj.sections()) - // Segments are responsible for writing their contents, so only write the - // section data if the section is not in a segment. Note that this renders - // sections in segments effectively immutable. - if (Sec.ParentSegment == nullptr) + for (SectionBase &Sec : Obj.sections()) + // Segments are responsible for writing their contents, so only write the + // section data if the section is not in a segment. Note that this renders + // sections in segments effectively immutable. + if (Sec.ParentSegment == nullptr) if (Error Err = Sec.accept(*SecWriter)) return Err; return Error::success(); -} - -template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { - for (Segment &Seg : Obj.segments()) { - size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size()); - std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(), - Size); - } - - // Iterate over removed sections and overwrite their old data with zeroes. - for (auto &Sec : Obj.removedSections()) { - Segment *Parent = Sec.ParentSegment; - if (Parent == nullptr || Sec.Type == SHT_NOBITS || Sec.Size == 0) - continue; - uint64_t Offset = - Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset; - std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size); - } -} - -template <class ELFT> -ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH, - bool OnlyKeepDebug) - : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs), - OnlyKeepDebug(OnlyKeepDebug) {} - +} + +template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { + for (Segment &Seg : Obj.segments()) { + size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size()); + std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(), + Size); + } + + // Iterate over removed sections and overwrite their old data with zeroes. + for (auto &Sec : Obj.removedSections()) { + Segment *Parent = Sec.ParentSegment; + if (Parent == nullptr || Sec.Type == SHT_NOBITS || Sec.Size == 0) + continue; + uint64_t Offset = + Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset; + std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size); + } +} + +template <class ELFT> +ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH, + bool OnlyKeepDebug) + : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs), + OnlyKeepDebug(OnlyKeepDebug) {} + Error Object::removeSections( bool AllowBrokenLinks, std::function<bool(const SectionBase &)> ToRemove) { - - auto Iter = std::stable_partition( - std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) { - if (ToRemove(*Sec)) - return false; - if (auto RelSec = dyn_cast<RelocationSectionBase>(Sec.get())) { - if (auto ToRelSec = RelSec->getSection()) - return !ToRemove(*ToRelSec); - } - return true; - }); - if (SymbolTable != nullptr && ToRemove(*SymbolTable)) - SymbolTable = nullptr; - if (SectionNames != nullptr && ToRemove(*SectionNames)) - SectionNames = nullptr; - if (SectionIndexTable != nullptr && ToRemove(*SectionIndexTable)) - SectionIndexTable = nullptr; - // Now make sure there are no remaining references to the sections that will - // be removed. Sometimes it is impossible to remove a reference so we emit - // an error here instead. - std::unordered_set<const SectionBase *> RemoveSections; - RemoveSections.reserve(std::distance(Iter, std::end(Sections))); - for (auto &RemoveSec : make_range(Iter, std::end(Sections))) { - for (auto &Segment : Segments) - Segment->removeSection(RemoveSec.get()); - RemoveSec->onRemove(); - RemoveSections.insert(RemoveSec.get()); - } - - // For each section that remains alive, we want to remove the dead references. - // This either might update the content of the section (e.g. remove symbols - // from symbol table that belongs to removed section) or trigger an error if - // a live section critically depends on a section being removed somehow - // (e.g. the removed section is referenced by a relocation). - for (auto &KeepSec : make_range(std::begin(Sections), Iter)) { + + auto Iter = std::stable_partition( + std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) { + if (ToRemove(*Sec)) + return false; + if (auto RelSec = dyn_cast<RelocationSectionBase>(Sec.get())) { + if (auto ToRelSec = RelSec->getSection()) + return !ToRemove(*ToRelSec); + } + return true; + }); + if (SymbolTable != nullptr && ToRemove(*SymbolTable)) + SymbolTable = nullptr; + if (SectionNames != nullptr && ToRemove(*SectionNames)) + SectionNames = nullptr; + if (SectionIndexTable != nullptr && ToRemove(*SectionIndexTable)) + SectionIndexTable = nullptr; + // Now make sure there are no remaining references to the sections that will + // be removed. Sometimes it is impossible to remove a reference so we emit + // an error here instead. + std::unordered_set<const SectionBase *> RemoveSections; + RemoveSections.reserve(std::distance(Iter, std::end(Sections))); + for (auto &RemoveSec : make_range(Iter, std::end(Sections))) { + for (auto &Segment : Segments) + Segment->removeSection(RemoveSec.get()); + RemoveSec->onRemove(); + RemoveSections.insert(RemoveSec.get()); + } + + // For each section that remains alive, we want to remove the dead references. + // This either might update the content of the section (e.g. remove symbols + // from symbol table that belongs to removed section) or trigger an error if + // a live section critically depends on a section being removed somehow + // (e.g. the removed section is referenced by a relocation). + for (auto &KeepSec : make_range(std::begin(Sections), Iter)) { if (Error E = KeepSec->removeSectionReferences( AllowBrokenLinks, [&RemoveSections](const SectionBase *Sec) { - return RemoveSections.find(Sec) != RemoveSections.end(); - })) - return E; - } - - // Transfer removed sections into the Object RemovedSections container for use - // later. - std::move(Iter, Sections.end(), std::back_inserter(RemovedSections)); - // Now finally get rid of them all together. - Sections.erase(Iter, std::end(Sections)); - return Error::success(); -} - -Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { - if (SymbolTable) - for (const SecPtr &Sec : Sections) - if (Error E = Sec->removeSymbols(ToRemove)) - return E; - return Error::success(); -} - + return RemoveSections.find(Sec) != RemoveSections.end(); + })) + return E; + } + + // Transfer removed sections into the Object RemovedSections container for use + // later. + std::move(Iter, Sections.end(), std::back_inserter(RemovedSections)); + // Now finally get rid of them all together. + Sections.erase(Iter, std::end(Sections)); + return Error::success(); +} + +Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + if (SymbolTable) + for (const SecPtr &Sec : Sections) + if (Error E = Sec->removeSymbols(ToRemove)) + return E; + return Error::success(); +} + Error Object::addNewSymbolTable() { - assert(!SymbolTable && "Object must not has a SymbolTable."); - - // Reuse an existing SHT_STRTAB section if it exists. - StringTableSection *StrTab = nullptr; - for (SectionBase &Sec : sections()) { - if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) { - StrTab = static_cast<StringTableSection *>(&Sec); - - // Prefer a string table that is not the section header string table, if - // such a table exists. - if (SectionNames != &Sec) - break; - } - } - if (!StrTab) - StrTab = &addSection<StringTableSection>(); - - SymbolTableSection &SymTab = addSection<SymbolTableSection>(); - SymTab.Name = ".symtab"; - SymTab.Link = StrTab->Index; + assert(!SymbolTable && "Object must not has a SymbolTable."); + + // Reuse an existing SHT_STRTAB section if it exists. + StringTableSection *StrTab = nullptr; + for (SectionBase &Sec : sections()) { + if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) { + StrTab = static_cast<StringTableSection *>(&Sec); + + // Prefer a string table that is not the section header string table, if + // such a table exists. + if (SectionNames != &Sec) + break; + } + } + if (!StrTab) + StrTab = &addSection<StringTableSection>(); + + SymbolTableSection &SymTab = addSection<SymbolTableSection>(); + SymTab.Name = ".symtab"; + SymTab.Link = StrTab->Index; if (Error Err = SymTab.initialize(sections())) return Err; - SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); - - SymbolTable = &SymTab; + SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); + + SymbolTable = &SymTab; return Error::success(); -} - -void Object::sortSections() { - // Use stable_sort to maintain the original ordering as closely as possible. - llvm::stable_sort(Sections, [](const SecPtr &A, const SecPtr &B) { - // Put SHT_GROUP sections first, since group section headers must come - // before the sections they contain. This also matches what GNU objcopy - // does. - if (A->Type != B->Type && - (A->Type == ELF::SHT_GROUP || B->Type == ELF::SHT_GROUP)) - return A->Type == ELF::SHT_GROUP; - // For all other sections, sort by offset order. - return A->OriginalOffset < B->OriginalOffset; - }); -} - -// Orders segments such that if x = y->ParentSegment then y comes before x. -static void orderSegments(std::vector<Segment *> &Segments) { - llvm::stable_sort(Segments, compareSegmentsByOffset); -} - -// This function finds a consistent layout for a list of segments starting from -// an Offset. It assumes that Segments have been sorted by orderSegments and -// returns an Offset one past the end of the last segment. -static uint64_t layoutSegments(std::vector<Segment *> &Segments, - uint64_t Offset) { - assert(llvm::is_sorted(Segments, compareSegmentsByOffset)); - // The only way a segment should move is if a section was between two - // segments and that section was removed. If that section isn't in a segment - // then it's acceptable, but not ideal, to simply move it to after the - // segments. So we can simply layout segments one after the other accounting - // for alignment. - for (Segment *Seg : Segments) { - // We assume that segments have been ordered by OriginalOffset and Index - // such that a parent segment will always come before a child segment in - // OrderedSegments. This means that the Offset of the ParentSegment should - // already be set and we can set our offset relative to it. - if (Seg->ParentSegment != nullptr) { - Segment *Parent = Seg->ParentSegment; - Seg->Offset = - Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset; - } else { - Seg->Offset = - alignTo(Offset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr); - } - Offset = std::max(Offset, Seg->Offset + Seg->FileSize); - } - return Offset; -} - -// This function finds a consistent layout for a list of sections. It assumes -// that the ->ParentSegment of each section has already been laid out. The -// supplied starting Offset is used for the starting offset of any section that -// does not have a ParentSegment. It returns either the offset given if all -// sections had a ParentSegment or an offset one past the last section if there -// was a section that didn't have a ParentSegment. -template <class Range> -static uint64_t layoutSections(Range Sections, uint64_t Offset) { - // Now the offset of every segment has been set we can assign the offsets - // of each section. For sections that are covered by a segment we should use - // the segment's original offset and the section's original offset to compute - // the offset from the start of the segment. Using the offset from the start - // of the segment we can assign a new offset to the section. For sections not - // covered by segments we can just bump Offset to the next valid location. - uint32_t Index = 1; - for (auto &Sec : Sections) { - Sec.Index = Index++; - if (Sec.ParentSegment != nullptr) { - auto Segment = *Sec.ParentSegment; - Sec.Offset = - Segment.Offset + (Sec.OriginalOffset - Segment.OriginalOffset); - } else { - Offset = alignTo(Offset, Sec.Align == 0 ? 1 : Sec.Align); - Sec.Offset = Offset; - if (Sec.Type != SHT_NOBITS) - Offset += Sec.Size; - } - } - return Offset; -} - -// Rewrite sh_offset after some sections are changed to SHT_NOBITS and thus -// occupy no space in the file. -static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) { - uint32_t Index = 1; - for (auto &Sec : Obj.sections()) { - Sec.Index = Index++; - - auto *FirstSec = Sec.ParentSegment && Sec.ParentSegment->Type == PT_LOAD - ? Sec.ParentSegment->firstSection() - : nullptr; - - // The first section in a PT_LOAD has to have congruent offset and address - // modulo the alignment, which usually equals the maximum page size. - if (FirstSec && FirstSec == &Sec) - Off = alignTo(Off, Sec.ParentSegment->Align, Sec.Addr); - - // sh_offset is not significant for SHT_NOBITS sections, but the congruence - // rule must be followed if it is the first section in a PT_LOAD. Do not - // advance Off. - if (Sec.Type == SHT_NOBITS) { - Sec.Offset = Off; - continue; - } - - if (!FirstSec) { - // FirstSec being nullptr generally means that Sec does not have the - // SHF_ALLOC flag. - Off = Sec.Align ? alignTo(Off, Sec.Align) : Off; - } else if (FirstSec != &Sec) { - // The offset is relative to the first section in the PT_LOAD segment. Use - // sh_offset for non-SHF_ALLOC sections. - Off = Sec.OriginalOffset - FirstSec->OriginalOffset + FirstSec->Offset; - } - Sec.Offset = Off; - Off += Sec.Size; - } - return Off; -} - +} + +void Object::sortSections() { + // Use stable_sort to maintain the original ordering as closely as possible. + llvm::stable_sort(Sections, [](const SecPtr &A, const SecPtr &B) { + // Put SHT_GROUP sections first, since group section headers must come + // before the sections they contain. This also matches what GNU objcopy + // does. + if (A->Type != B->Type && + (A->Type == ELF::SHT_GROUP || B->Type == ELF::SHT_GROUP)) + return A->Type == ELF::SHT_GROUP; + // For all other sections, sort by offset order. + return A->OriginalOffset < B->OriginalOffset; + }); +} + +// Orders segments such that if x = y->ParentSegment then y comes before x. +static void orderSegments(std::vector<Segment *> &Segments) { + llvm::stable_sort(Segments, compareSegmentsByOffset); +} + +// This function finds a consistent layout for a list of segments starting from +// an Offset. It assumes that Segments have been sorted by orderSegments and +// returns an Offset one past the end of the last segment. +static uint64_t layoutSegments(std::vector<Segment *> &Segments, + uint64_t Offset) { + assert(llvm::is_sorted(Segments, compareSegmentsByOffset)); + // The only way a segment should move is if a section was between two + // segments and that section was removed. If that section isn't in a segment + // then it's acceptable, but not ideal, to simply move it to after the + // segments. So we can simply layout segments one after the other accounting + // for alignment. + for (Segment *Seg : Segments) { + // We assume that segments have been ordered by OriginalOffset and Index + // such that a parent segment will always come before a child segment in + // OrderedSegments. This means that the Offset of the ParentSegment should + // already be set and we can set our offset relative to it. + if (Seg->ParentSegment != nullptr) { + Segment *Parent = Seg->ParentSegment; + Seg->Offset = + Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset; + } else { + Seg->Offset = + alignTo(Offset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr); + } + Offset = std::max(Offset, Seg->Offset + Seg->FileSize); + } + return Offset; +} + +// This function finds a consistent layout for a list of sections. It assumes +// that the ->ParentSegment of each section has already been laid out. The +// supplied starting Offset is used for the starting offset of any section that +// does not have a ParentSegment. It returns either the offset given if all +// sections had a ParentSegment or an offset one past the last section if there +// was a section that didn't have a ParentSegment. +template <class Range> +static uint64_t layoutSections(Range Sections, uint64_t Offset) { + // Now the offset of every segment has been set we can assign the offsets + // of each section. For sections that are covered by a segment we should use + // the segment's original offset and the section's original offset to compute + // the offset from the start of the segment. Using the offset from the start + // of the segment we can assign a new offset to the section. For sections not + // covered by segments we can just bump Offset to the next valid location. + uint32_t Index = 1; + for (auto &Sec : Sections) { + Sec.Index = Index++; + if (Sec.ParentSegment != nullptr) { + auto Segment = *Sec.ParentSegment; + Sec.Offset = + Segment.Offset + (Sec.OriginalOffset - Segment.OriginalOffset); + } else { + Offset = alignTo(Offset, Sec.Align == 0 ? 1 : Sec.Align); + Sec.Offset = Offset; + if (Sec.Type != SHT_NOBITS) + Offset += Sec.Size; + } + } + return Offset; +} + +// Rewrite sh_offset after some sections are changed to SHT_NOBITS and thus +// occupy no space in the file. +static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) { + uint32_t Index = 1; + for (auto &Sec : Obj.sections()) { + Sec.Index = Index++; + + auto *FirstSec = Sec.ParentSegment && Sec.ParentSegment->Type == PT_LOAD + ? Sec.ParentSegment->firstSection() + : nullptr; + + // The first section in a PT_LOAD has to have congruent offset and address + // modulo the alignment, which usually equals the maximum page size. + if (FirstSec && FirstSec == &Sec) + Off = alignTo(Off, Sec.ParentSegment->Align, Sec.Addr); + + // sh_offset is not significant for SHT_NOBITS sections, but the congruence + // rule must be followed if it is the first section in a PT_LOAD. Do not + // advance Off. + if (Sec.Type == SHT_NOBITS) { + Sec.Offset = Off; + continue; + } + + if (!FirstSec) { + // FirstSec being nullptr generally means that Sec does not have the + // SHF_ALLOC flag. + Off = Sec.Align ? alignTo(Off, Sec.Align) : Off; + } else if (FirstSec != &Sec) { + // The offset is relative to the first section in the PT_LOAD segment. Use + // sh_offset for non-SHF_ALLOC sections. + Off = Sec.OriginalOffset - FirstSec->OriginalOffset + FirstSec->Offset; + } + Sec.Offset = Off; + Off += Sec.Size; + } + return Off; +} + // Rewrite p_offset and p_filesz of non-PT_PHDR segments after sh_offset values // have been updated. -static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments, - uint64_t HdrEnd) { - uint64_t MaxOffset = 0; - for (Segment *Seg : Segments) { +static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments, + uint64_t HdrEnd) { + uint64_t MaxOffset = 0; + for (Segment *Seg : Segments) { // An empty segment contains no section (see sectionWithinSegment). If it // has a parent segment, copy the parent segment's offset field. This works // for empty PT_TLS. We don't handle empty segments without a parent for @@ -2318,377 +2318,377 @@ static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments, if (Seg->ParentSegment != nullptr && Seg->MemSize == 0) Seg->Offset = Seg->ParentSegment->Offset; - const SectionBase *FirstSec = Seg->firstSection(); - if (Seg->Type == PT_PHDR || !FirstSec) - continue; - - uint64_t Offset = FirstSec->Offset; - uint64_t FileSize = 0; - for (const SectionBase *Sec : Seg->Sections) { - uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size; - if (Sec->Offset + Size > Offset) - FileSize = std::max(FileSize, Sec->Offset + Size - Offset); - } - - // If the segment includes EHDR and program headers, don't make it smaller - // than the headers. - if (Seg->Offset < HdrEnd && HdrEnd <= Seg->Offset + Seg->FileSize) { - FileSize += Offset - Seg->Offset; - Offset = Seg->Offset; - FileSize = std::max(FileSize, HdrEnd - Offset); - } - - Seg->Offset = Offset; - Seg->FileSize = FileSize; - MaxOffset = std::max(MaxOffset, Offset + FileSize); - } - return MaxOffset; -} - -template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() { - Segment &ElfHdr = Obj.ElfHdrSegment; - ElfHdr.Type = PT_PHDR; - ElfHdr.Flags = 0; - ElfHdr.VAddr = 0; - ElfHdr.PAddr = 0; - ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr); - ElfHdr.Align = 0; -} - -template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { - // We need a temporary list of segments that has a special order to it - // so that we know that anytime ->ParentSegment is set that segment has - // already had its offset properly set. - std::vector<Segment *> OrderedSegments; - for (Segment &Segment : Obj.segments()) - OrderedSegments.push_back(&Segment); - OrderedSegments.push_back(&Obj.ElfHdrSegment); - OrderedSegments.push_back(&Obj.ProgramHdrSegment); - orderSegments(OrderedSegments); - - uint64_t Offset; - if (OnlyKeepDebug) { - // For --only-keep-debug, the sections that did not preserve contents were - // changed to SHT_NOBITS. We now rewrite sh_offset fields of sections, and - // then rewrite p_offset/p_filesz of program headers. - uint64_t HdrEnd = - sizeof(Elf_Ehdr) + llvm::size(Obj.segments()) * sizeof(Elf_Phdr); - Offset = layoutSectionsForOnlyKeepDebug(Obj, HdrEnd); - Offset = std::max(Offset, - layoutSegmentsForOnlyKeepDebug(OrderedSegments, HdrEnd)); - } else { - // Offset is used as the start offset of the first segment to be laid out. - // Since the ELF Header (ElfHdrSegment) must be at the start of the file, - // we start at offset 0. - Offset = layoutSegments(OrderedSegments, 0); - Offset = layoutSections(Obj.sections(), Offset); - } - // If we need to write the section header table out then we need to align the - // Offset so that SHOffset is valid. - if (WriteSectionHeaders) - Offset = alignTo(Offset, sizeof(Elf_Addr)); - Obj.SHOff = Offset; -} - -template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const { - // We already have the section header offset so we can calculate the total - // size by just adding up the size of each section header. - if (!WriteSectionHeaders) - return Obj.SHOff; - size_t ShdrCount = Obj.sections().size() + 1; // Includes null shdr. - return Obj.SHOff + ShdrCount * sizeof(Elf_Shdr); -} - -template <class ELFT> Error ELFWriter<ELFT>::write() { - // Segment data must be written first, so that the ELF header and program - // header tables can overwrite it, if covered by a segment. - writeSegmentData(); - writeEhdr(); - writePhdrs(); + const SectionBase *FirstSec = Seg->firstSection(); + if (Seg->Type == PT_PHDR || !FirstSec) + continue; + + uint64_t Offset = FirstSec->Offset; + uint64_t FileSize = 0; + for (const SectionBase *Sec : Seg->Sections) { + uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size; + if (Sec->Offset + Size > Offset) + FileSize = std::max(FileSize, Sec->Offset + Size - Offset); + } + + // If the segment includes EHDR and program headers, don't make it smaller + // than the headers. + if (Seg->Offset < HdrEnd && HdrEnd <= Seg->Offset + Seg->FileSize) { + FileSize += Offset - Seg->Offset; + Offset = Seg->Offset; + FileSize = std::max(FileSize, HdrEnd - Offset); + } + + Seg->Offset = Offset; + Seg->FileSize = FileSize; + MaxOffset = std::max(MaxOffset, Offset + FileSize); + } + return MaxOffset; +} + +template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() { + Segment &ElfHdr = Obj.ElfHdrSegment; + ElfHdr.Type = PT_PHDR; + ElfHdr.Flags = 0; + ElfHdr.VAddr = 0; + ElfHdr.PAddr = 0; + ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr); + ElfHdr.Align = 0; +} + +template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { + // We need a temporary list of segments that has a special order to it + // so that we know that anytime ->ParentSegment is set that segment has + // already had its offset properly set. + std::vector<Segment *> OrderedSegments; + for (Segment &Segment : Obj.segments()) + OrderedSegments.push_back(&Segment); + OrderedSegments.push_back(&Obj.ElfHdrSegment); + OrderedSegments.push_back(&Obj.ProgramHdrSegment); + orderSegments(OrderedSegments); + + uint64_t Offset; + if (OnlyKeepDebug) { + // For --only-keep-debug, the sections that did not preserve contents were + // changed to SHT_NOBITS. We now rewrite sh_offset fields of sections, and + // then rewrite p_offset/p_filesz of program headers. + uint64_t HdrEnd = + sizeof(Elf_Ehdr) + llvm::size(Obj.segments()) * sizeof(Elf_Phdr); + Offset = layoutSectionsForOnlyKeepDebug(Obj, HdrEnd); + Offset = std::max(Offset, + layoutSegmentsForOnlyKeepDebug(OrderedSegments, HdrEnd)); + } else { + // Offset is used as the start offset of the first segment to be laid out. + // Since the ELF Header (ElfHdrSegment) must be at the start of the file, + // we start at offset 0. + Offset = layoutSegments(OrderedSegments, 0); + Offset = layoutSections(Obj.sections(), Offset); + } + // If we need to write the section header table out then we need to align the + // Offset so that SHOffset is valid. + if (WriteSectionHeaders) + Offset = alignTo(Offset, sizeof(Elf_Addr)); + Obj.SHOff = Offset; +} + +template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const { + // We already have the section header offset so we can calculate the total + // size by just adding up the size of each section header. + if (!WriteSectionHeaders) + return Obj.SHOff; + size_t ShdrCount = Obj.sections().size() + 1; // Includes null shdr. + return Obj.SHOff + ShdrCount * sizeof(Elf_Shdr); +} + +template <class ELFT> Error ELFWriter<ELFT>::write() { + // Segment data must be written first, so that the ELF header and program + // header tables can overwrite it, if covered by a segment. + writeSegmentData(); + writeEhdr(); + writePhdrs(); if (Error E = writeSectionData()) return E; - if (WriteSectionHeaders) - writeShdrs(); - return Buf.commit(); -} - -static Error removeUnneededSections(Object &Obj) { - // We can remove an empty symbol table from non-relocatable objects. - // Relocatable objects typically have relocation sections whose - // sh_link field points to .symtab, so we can't remove .symtab - // even if it is empty. - if (Obj.isRelocatable() || Obj.SymbolTable == nullptr || - !Obj.SymbolTable->empty()) - return Error::success(); - - // .strtab can be used for section names. In such a case we shouldn't - // remove it. - auto *StrTab = Obj.SymbolTable->getStrTab() == Obj.SectionNames - ? nullptr - : Obj.SymbolTable->getStrTab(); - return Obj.removeSections(false, [&](const SectionBase &Sec) { - return &Sec == Obj.SymbolTable || &Sec == StrTab; - }); -} - -template <class ELFT> Error ELFWriter<ELFT>::finalize() { - // It could happen that SectionNames has been removed and yet the user wants - // a section header table output. We need to throw an error if a user tries - // to do that. - if (Obj.SectionNames == nullptr && WriteSectionHeaders) - return createStringError(llvm::errc::invalid_argument, - "cannot write section header table because " - "section header string table was removed"); - - if (Error E = removeUnneededSections(Obj)) - return E; - Obj.sortSections(); - - // We need to assign indexes before we perform layout because we need to know - // if we need large indexes or not. We can assign indexes first and check as - // we go to see if we will actully need large indexes. - bool NeedsLargeIndexes = false; - if (Obj.sections().size() >= SHN_LORESERVE) { - SectionTableRef Sections = Obj.sections(); - NeedsLargeIndexes = + if (WriteSectionHeaders) + writeShdrs(); + return Buf.commit(); +} + +static Error removeUnneededSections(Object &Obj) { + // We can remove an empty symbol table from non-relocatable objects. + // Relocatable objects typically have relocation sections whose + // sh_link field points to .symtab, so we can't remove .symtab + // even if it is empty. + if (Obj.isRelocatable() || Obj.SymbolTable == nullptr || + !Obj.SymbolTable->empty()) + return Error::success(); + + // .strtab can be used for section names. In such a case we shouldn't + // remove it. + auto *StrTab = Obj.SymbolTable->getStrTab() == Obj.SectionNames + ? nullptr + : Obj.SymbolTable->getStrTab(); + return Obj.removeSections(false, [&](const SectionBase &Sec) { + return &Sec == Obj.SymbolTable || &Sec == StrTab; + }); +} + +template <class ELFT> Error ELFWriter<ELFT>::finalize() { + // It could happen that SectionNames has been removed and yet the user wants + // a section header table output. We need to throw an error if a user tries + // to do that. + if (Obj.SectionNames == nullptr && WriteSectionHeaders) + return createStringError(llvm::errc::invalid_argument, + "cannot write section header table because " + "section header string table was removed"); + + if (Error E = removeUnneededSections(Obj)) + return E; + Obj.sortSections(); + + // We need to assign indexes before we perform layout because we need to know + // if we need large indexes or not. We can assign indexes first and check as + // we go to see if we will actully need large indexes. + bool NeedsLargeIndexes = false; + if (Obj.sections().size() >= SHN_LORESERVE) { + SectionTableRef Sections = Obj.sections(); + NeedsLargeIndexes = any_of(drop_begin(Sections, SHN_LORESERVE), [](const SectionBase &Sec) { return Sec.HasSymbol; }); - // TODO: handle case where only one section needs the large index table but - // only needs it because the large index table hasn't been removed yet. - } - - if (NeedsLargeIndexes) { - // This means we definitely need to have a section index table but if we - // already have one then we should use it instead of making a new one. - if (Obj.SymbolTable != nullptr && Obj.SectionIndexTable == nullptr) { - // Addition of a section to the end does not invalidate the indexes of - // other sections and assigns the correct index to the new section. - auto &Shndx = Obj.addSection<SectionIndexSection>(); - Obj.SymbolTable->setShndxTable(&Shndx); - Shndx.setSymTab(Obj.SymbolTable); - } - } else { - // Since we don't need SectionIndexTable we should remove it and all - // references to it. - if (Obj.SectionIndexTable != nullptr) { - // We do not support sections referring to the section index table. - if (Error E = Obj.removeSections(false /*AllowBrokenLinks*/, - [this](const SectionBase &Sec) { - return &Sec == Obj.SectionIndexTable; - })) - return E; - } - } - - // Make sure we add the names of all the sections. Importantly this must be - // done after we decide to add or remove SectionIndexes. - if (Obj.SectionNames != nullptr) - for (const SectionBase &Sec : Obj.sections()) - Obj.SectionNames->addString(Sec.Name); - - initEhdrSegment(); - - // Before we can prepare for layout the indexes need to be finalized. - // Also, the output arch may not be the same as the input arch, so fix up - // size-related fields before doing layout calculations. - uint64_t Index = 0; - auto SecSizer = std::make_unique<ELFSectionSizer<ELFT>>(); - for (SectionBase &Sec : Obj.sections()) { - Sec.Index = Index++; + // TODO: handle case where only one section needs the large index table but + // only needs it because the large index table hasn't been removed yet. + } + + if (NeedsLargeIndexes) { + // This means we definitely need to have a section index table but if we + // already have one then we should use it instead of making a new one. + if (Obj.SymbolTable != nullptr && Obj.SectionIndexTable == nullptr) { + // Addition of a section to the end does not invalidate the indexes of + // other sections and assigns the correct index to the new section. + auto &Shndx = Obj.addSection<SectionIndexSection>(); + Obj.SymbolTable->setShndxTable(&Shndx); + Shndx.setSymTab(Obj.SymbolTable); + } + } else { + // Since we don't need SectionIndexTable we should remove it and all + // references to it. + if (Obj.SectionIndexTable != nullptr) { + // We do not support sections referring to the section index table. + if (Error E = Obj.removeSections(false /*AllowBrokenLinks*/, + [this](const SectionBase &Sec) { + return &Sec == Obj.SectionIndexTable; + })) + return E; + } + } + + // Make sure we add the names of all the sections. Importantly this must be + // done after we decide to add or remove SectionIndexes. + if (Obj.SectionNames != nullptr) + for (const SectionBase &Sec : Obj.sections()) + Obj.SectionNames->addString(Sec.Name); + + initEhdrSegment(); + + // Before we can prepare for layout the indexes need to be finalized. + // Also, the output arch may not be the same as the input arch, so fix up + // size-related fields before doing layout calculations. + uint64_t Index = 0; + auto SecSizer = std::make_unique<ELFSectionSizer<ELFT>>(); + for (SectionBase &Sec : Obj.sections()) { + Sec.Index = Index++; if (Error Err = Sec.accept(*SecSizer)) return Err; - } - - // The symbol table does not update all other sections on update. For - // instance, symbol names are not added as new symbols are added. This means - // that some sections, like .strtab, don't yet have their final size. - if (Obj.SymbolTable != nullptr) - Obj.SymbolTable->prepareForLayout(); - - // Now that all strings are added we want to finalize string table builders, - // because that affects section sizes which in turn affects section offsets. - for (SectionBase &Sec : Obj.sections()) - if (auto StrTab = dyn_cast<StringTableSection>(&Sec)) - StrTab->prepareForLayout(); - - assignOffsets(); - - // layoutSections could have modified section indexes, so we need - // to fill the index table after assignOffsets. - if (Obj.SymbolTable != nullptr) - Obj.SymbolTable->fillShndxTable(); - - // Finally now that all offsets and indexes have been set we can finalize any - // remaining issues. - uint64_t Offset = Obj.SHOff + sizeof(Elf_Shdr); - for (SectionBase &Sec : Obj.sections()) { - Sec.HeaderOffset = Offset; - Offset += sizeof(Elf_Shdr); - if (WriteSectionHeaders) - Sec.NameIndex = Obj.SectionNames->findIndex(Sec.Name); - Sec.finalize(); - } - - if (Error E = Buf.allocate(totalSize())) - return E; - SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf); - return Error::success(); -} - -Error BinaryWriter::write() { - for (const SectionBase &Sec : Obj.allocSections()) + } + + // The symbol table does not update all other sections on update. For + // instance, symbol names are not added as new symbols are added. This means + // that some sections, like .strtab, don't yet have their final size. + if (Obj.SymbolTable != nullptr) + Obj.SymbolTable->prepareForLayout(); + + // Now that all strings are added we want to finalize string table builders, + // because that affects section sizes which in turn affects section offsets. + for (SectionBase &Sec : Obj.sections()) + if (auto StrTab = dyn_cast<StringTableSection>(&Sec)) + StrTab->prepareForLayout(); + + assignOffsets(); + + // layoutSections could have modified section indexes, so we need + // to fill the index table after assignOffsets. + if (Obj.SymbolTable != nullptr) + Obj.SymbolTable->fillShndxTable(); + + // Finally now that all offsets and indexes have been set we can finalize any + // remaining issues. + uint64_t Offset = Obj.SHOff + sizeof(Elf_Shdr); + for (SectionBase &Sec : Obj.sections()) { + Sec.HeaderOffset = Offset; + Offset += sizeof(Elf_Shdr); + if (WriteSectionHeaders) + Sec.NameIndex = Obj.SectionNames->findIndex(Sec.Name); + Sec.finalize(); + } + + if (Error E = Buf.allocate(totalSize())) + return E; + SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf); + return Error::success(); +} + +Error BinaryWriter::write() { + for (const SectionBase &Sec : Obj.allocSections()) if (Error Err = Sec.accept(*SecWriter)) return Err; - return Buf.commit(); -} - -Error BinaryWriter::finalize() { - // Compute the section LMA based on its sh_offset and the containing segment's - // p_offset and p_paddr. Also compute the minimum LMA of all non-empty - // sections as MinAddr. In the output, the contents between address 0 and - // MinAddr will be skipped. - uint64_t MinAddr = UINT64_MAX; - for (SectionBase &Sec : Obj.allocSections()) { - if (Sec.ParentSegment != nullptr) - Sec.Addr = - Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr; - if (Sec.Size > 0) - MinAddr = std::min(MinAddr, Sec.Addr); - } - - // Now that every section has been laid out we just need to compute the total - // file size. This might not be the same as the offset returned by - // layoutSections, because we want to truncate the last segment to the end of - // its last non-empty section, to match GNU objcopy's behaviour. - TotalSize = 0; - for (SectionBase &Sec : Obj.allocSections()) - if (Sec.Type != SHT_NOBITS && Sec.Size > 0) { - Sec.Offset = Sec.Addr - MinAddr; - TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); - } - - if (Error E = Buf.allocate(TotalSize)) - return E; - SecWriter = std::make_unique<BinarySectionWriter>(Buf); - return Error::success(); -} - -bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs, - const SectionBase *Rhs) const { - return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) < - (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU); -} - -uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) { - IHexLineData HexData; - uint8_t Data[4] = {}; - // We don't write entry point record if entry is zero. - if (Obj.Entry == 0) - return 0; - - if (Obj.Entry <= 0xFFFFFU) { - Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF; - support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry), - support::big); - HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data); - } else { - support::endian::write(Data, static_cast<uint32_t>(Obj.Entry), - support::big); - HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data); - } - memcpy(Buf, HexData.data(), HexData.size()); - return HexData.size(); -} - -uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) { - IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0, {}); - memcpy(Buf, HexData.data(), HexData.size()); - return HexData.size(); -} - -Error IHexWriter::write() { - IHexSectionWriter Writer(Buf); - // Write sections. - for (const SectionBase *Sec : Sections) + return Buf.commit(); +} + +Error BinaryWriter::finalize() { + // Compute the section LMA based on its sh_offset and the containing segment's + // p_offset and p_paddr. Also compute the minimum LMA of all non-empty + // sections as MinAddr. In the output, the contents between address 0 and + // MinAddr will be skipped. + uint64_t MinAddr = UINT64_MAX; + for (SectionBase &Sec : Obj.allocSections()) { + if (Sec.ParentSegment != nullptr) + Sec.Addr = + Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr; + if (Sec.Size > 0) + MinAddr = std::min(MinAddr, Sec.Addr); + } + + // Now that every section has been laid out we just need to compute the total + // file size. This might not be the same as the offset returned by + // layoutSections, because we want to truncate the last segment to the end of + // its last non-empty section, to match GNU objcopy's behaviour. + TotalSize = 0; + for (SectionBase &Sec : Obj.allocSections()) + if (Sec.Type != SHT_NOBITS && Sec.Size > 0) { + Sec.Offset = Sec.Addr - MinAddr; + TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); + } + + if (Error E = Buf.allocate(TotalSize)) + return E; + SecWriter = std::make_unique<BinarySectionWriter>(Buf); + return Error::success(); +} + +bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs, + const SectionBase *Rhs) const { + return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) < + (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU); +} + +uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) { + IHexLineData HexData; + uint8_t Data[4] = {}; + // We don't write entry point record if entry is zero. + if (Obj.Entry == 0) + return 0; + + if (Obj.Entry <= 0xFFFFFU) { + Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF; + support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry), + support::big); + HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data); + } else { + support::endian::write(Data, static_cast<uint32_t>(Obj.Entry), + support::big); + HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data); + } + memcpy(Buf, HexData.data(), HexData.size()); + return HexData.size(); +} + +uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) { + IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0, {}); + memcpy(Buf, HexData.data(), HexData.size()); + return HexData.size(); +} + +Error IHexWriter::write() { + IHexSectionWriter Writer(Buf); + // Write sections. + for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(Writer)) return Err; - - uint64_t Offset = Writer.getBufferOffset(); - // Write entry point address. - Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset); - // Write EOF. - Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset); - assert(Offset == TotalSize); - return Buf.commit(); -} - -Error IHexWriter::checkSection(const SectionBase &Sec) { - uint64_t Addr = sectionPhysicalAddr(&Sec); - if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1)) - return createStringError( - errc::invalid_argument, + + uint64_t Offset = Writer.getBufferOffset(); + // Write entry point address. + Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset); + // Write EOF. + Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset); + assert(Offset == TotalSize); + return Buf.commit(); +} + +Error IHexWriter::checkSection(const SectionBase &Sec) { + uint64_t Addr = sectionPhysicalAddr(&Sec); + if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1)) + return createStringError( + errc::invalid_argument, "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit", Sec.Name.c_str(), Addr, Addr + Sec.Size - 1); - return Error::success(); -} - -Error IHexWriter::finalize() { - bool UseSegments = false; - auto ShouldWrite = [](const SectionBase &Sec) { - return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS); - }; - auto IsInPtLoad = [](const SectionBase &Sec) { - return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD; - }; - - // We can't write 64-bit addresses. - if (addressOverflows32bit(Obj.Entry)) - return createStringError(errc::invalid_argument, - "Entry point address 0x%llx overflows 32 bits.", - Obj.Entry); - - // If any section we're to write has segment then we - // switch to using physical addresses. Otherwise we - // use section virtual address. - for (const SectionBase &Sec : Obj.sections()) - if (ShouldWrite(Sec) && IsInPtLoad(Sec)) { - UseSegments = true; - break; - } - - for (const SectionBase &Sec : Obj.sections()) - if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) { - if (Error E = checkSection(Sec)) - return E; - Sections.insert(&Sec); - } - - IHexSectionWriterBase LengthCalc(Buf); - for (const SectionBase *Sec : Sections) + return Error::success(); +} + +Error IHexWriter::finalize() { + bool UseSegments = false; + auto ShouldWrite = [](const SectionBase &Sec) { + return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS); + }; + auto IsInPtLoad = [](const SectionBase &Sec) { + return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD; + }; + + // We can't write 64-bit addresses. + if (addressOverflows32bit(Obj.Entry)) + return createStringError(errc::invalid_argument, + "Entry point address 0x%llx overflows 32 bits.", + Obj.Entry); + + // If any section we're to write has segment then we + // switch to using physical addresses. Otherwise we + // use section virtual address. + for (const SectionBase &Sec : Obj.sections()) + if (ShouldWrite(Sec) && IsInPtLoad(Sec)) { + UseSegments = true; + break; + } + + for (const SectionBase &Sec : Obj.sections()) + if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) { + if (Error E = checkSection(Sec)) + return E; + Sections.insert(&Sec); + } + + IHexSectionWriterBase LengthCalc(Buf); + for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(LengthCalc)) return Err; - - // We need space to write section records + StartAddress record - // (if start adress is not zero) + EndOfFile record. - TotalSize = LengthCalc.getBufferOffset() + - (Obj.Entry ? IHexRecord::getLineLength(4) : 0) + - IHexRecord::getLineLength(0); - if (Error E = Buf.allocate(TotalSize)) - return E; - return Error::success(); -} - -template class ELFBuilder<ELF64LE>; -template class ELFBuilder<ELF64BE>; -template class ELFBuilder<ELF32LE>; -template class ELFBuilder<ELF32BE>; - -template class ELFWriter<ELF64LE>; -template class ELFWriter<ELF64BE>; -template class ELFWriter<ELF32LE>; -template class ELFWriter<ELF32BE>; - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm + + // We need space to write section records + StartAddress record + // (if start adress is not zero) + EndOfFile record. + TotalSize = LengthCalc.getBufferOffset() + + (Obj.Entry ? IHexRecord::getLineLength(4) : 0) + + IHexRecord::getLineLength(0); + if (Error E = Buf.allocate(TotalSize)) + return E; + return Error::success(); +} + +template class ELFBuilder<ELF64LE>; +template class ELFBuilder<ELF64BE>; +template class ELFBuilder<ELF32LE>; +template class ELFBuilder<ELF32BE>; + +template class ELFWriter<ELF64LE>; +template class ELFWriter<ELF64BE>; +template class ELFWriter<ELF32LE>; +template class ELFWriter<ELF32BE>; + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.h b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.h index 0205c2d4f3..8a0ceb67d7 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ELF/Object.h @@ -1,79 +1,79 @@ -//===- Object.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H -#define LLVM_TOOLS_OBJCOPY_OBJECT_H - -#include "Buffer.h" -#include "CopyConfig.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/BinaryFormat/ELF.h" -#include "llvm/MC/StringTableBuilder.h" -#include "llvm/Object/ELFObjectFile.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileOutputBuffer.h" -#include <cstddef> -#include <cstdint> -#include <functional> -#include <memory> -#include <set> -#include <vector> - -namespace llvm { -enum class DebugCompressionType; -namespace objcopy { -namespace elf { - -class SectionBase; -class Section; -class OwnedDataSection; -class StringTableSection; -class SymbolTableSection; -class RelocationSection; -class DynamicRelocationSection; -class GnuDebugLinkSection; -class GroupSection; -class SectionIndexSection; -class CompressedSection; -class DecompressedSection; -class Segment; -class Object; -struct Symbol; - -class SectionTableRef { - MutableArrayRef<std::unique_ptr<SectionBase>> Sections; - -public: - using iterator = pointee_iterator<std::unique_ptr<SectionBase> *>; - - explicit SectionTableRef(MutableArrayRef<std::unique_ptr<SectionBase>> Secs) - : Sections(Secs) {} - SectionTableRef(const SectionTableRef &) = default; - - iterator begin() const { return iterator(Sections.data()); } - iterator end() const { return iterator(Sections.data() + Sections.size()); } - size_t size() const { return Sections.size(); } - +//===- Object.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H +#define LLVM_TOOLS_OBJCOPY_OBJECT_H + +#include "Buffer.h" +#include "CopyConfig.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <set> +#include <vector> + +namespace llvm { +enum class DebugCompressionType; +namespace objcopy { +namespace elf { + +class SectionBase; +class Section; +class OwnedDataSection; +class StringTableSection; +class SymbolTableSection; +class RelocationSection; +class DynamicRelocationSection; +class GnuDebugLinkSection; +class GroupSection; +class SectionIndexSection; +class CompressedSection; +class DecompressedSection; +class Segment; +class Object; +struct Symbol; + +class SectionTableRef { + MutableArrayRef<std::unique_ptr<SectionBase>> Sections; + +public: + using iterator = pointee_iterator<std::unique_ptr<SectionBase> *>; + + explicit SectionTableRef(MutableArrayRef<std::unique_ptr<SectionBase>> Secs) + : Sections(Secs) {} + SectionTableRef(const SectionTableRef &) = default; + + iterator begin() const { return iterator(Sections.data()); } + iterator end() const { return iterator(Sections.data() + Sections.size()); } + size_t size() const { return Sections.size(); } + Expected<SectionBase *> getSection(uint32_t Index, Twine ErrMsg); - - template <class T> + + template <class T> Expected<T *> getSectionOfType(uint32_t Index, Twine IndexErrMsg, Twine TypeErrMsg); -}; - -enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE }; - -class SectionVisitor { -public: - virtual ~SectionVisitor() = default; - +}; + +enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE }; + +class SectionVisitor { +public: + virtual ~SectionVisitor() = default; + virtual Error visit(const Section &Sec) = 0; virtual Error visit(const OwnedDataSection &Sec) = 0; virtual Error visit(const StringTableSection &Sec) = 0; @@ -85,12 +85,12 @@ public: virtual Error visit(const SectionIndexSection &Sec) = 0; virtual Error visit(const CompressedSection &Sec) = 0; virtual Error visit(const DecompressedSection &Sec) = 0; -}; - -class MutableSectionVisitor { -public: - virtual ~MutableSectionVisitor() = default; - +}; + +class MutableSectionVisitor { +public: + virtual ~MutableSectionVisitor() = default; + virtual Error visit(Section &Sec) = 0; virtual Error visit(OwnedDataSection &Sec) = 0; virtual Error visit(StringTableSection &Sec) = 0; @@ -102,15 +102,15 @@ public: virtual Error visit(SectionIndexSection &Sec) = 0; virtual Error visit(CompressedSection &Sec) = 0; virtual Error visit(DecompressedSection &Sec) = 0; -}; - -class SectionWriter : public SectionVisitor { -protected: - Buffer &Out; - -public: - virtual ~SectionWriter() = default; - +}; + +class SectionWriter : public SectionVisitor { +protected: + Buffer &Out; + +public: + virtual ~SectionWriter() = default; + Error visit(const Section &Sec) override; Error visit(const OwnedDataSection &Sec) override; Error visit(const StringTableSection &Sec) override; @@ -122,19 +122,19 @@ public: virtual Error visit(const SectionIndexSection &Sec) override = 0; virtual Error visit(const CompressedSection &Sec) override = 0; virtual Error visit(const DecompressedSection &Sec) override = 0; - - explicit SectionWriter(Buffer &Buf) : Out(Buf) {} -}; - -template <class ELFT> class ELFSectionWriter : public SectionWriter { -private: - using Elf_Word = typename ELFT::Word; - using Elf_Rel = typename ELFT::Rel; - using Elf_Rela = typename ELFT::Rela; - using Elf_Sym = typename ELFT::Sym; - -public: - virtual ~ELFSectionWriter() {} + + explicit SectionWriter(Buffer &Buf) : Out(Buf) {} +}; + +template <class ELFT> class ELFSectionWriter : public SectionWriter { +private: + using Elf_Word = typename ELFT::Word; + using Elf_Rel = typename ELFT::Rel; + using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; + +public: + virtual ~ELFSectionWriter() {} Error visit(const SymbolTableSection &Sec) override; Error visit(const RelocationSection &Sec) override; Error visit(const GnuDebugLinkSection &Sec) override; @@ -142,19 +142,19 @@ public: Error visit(const SectionIndexSection &Sec) override; Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - - explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} -}; - -template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor { -private: - using Elf_Rel = typename ELFT::Rel; - using Elf_Rela = typename ELFT::Rela; - using Elf_Sym = typename ELFT::Sym; - using Elf_Word = typename ELFT::Word; - using Elf_Xword = typename ELFT::Xword; - -public: + + explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} +}; + +template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor { +private: + using Elf_Rel = typename ELFT::Rel; + using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; + using Elf_Word = typename ELFT::Word; + using Elf_Xword = typename ELFT::Xword; + +public: Error visit(Section &Sec) override; Error visit(OwnedDataSection &Sec) override; Error visit(StringTableSection &Sec) override; @@ -166,19 +166,19 @@ public: Error visit(SectionIndexSection &Sec) override; Error visit(CompressedSection &Sec) override; Error visit(DecompressedSection &Sec) override; -}; - -#define MAKE_SEC_WRITER_FRIEND \ - friend class SectionWriter; \ - friend class IHexSectionWriterBase; \ - friend class IHexSectionWriter; \ - template <class ELFT> friend class ELFSectionWriter; \ - template <class ELFT> friend class ELFSectionSizer; - -class BinarySectionWriter : public SectionWriter { -public: - virtual ~BinarySectionWriter() {} - +}; + +#define MAKE_SEC_WRITER_FRIEND \ + friend class SectionWriter; \ + friend class IHexSectionWriterBase; \ + friend class IHexSectionWriter; \ + template <class ELFT> friend class ELFSectionWriter; \ + template <class ELFT> friend class ELFSectionSizer; + +class BinarySectionWriter : public SectionWriter { +public: + virtual ~BinarySectionWriter() {} + Error visit(const SymbolTableSection &Sec) override; Error visit(const RelocationSection &Sec) override; Error visit(const GnuDebugLinkSection &Sec) override; @@ -186,766 +186,766 @@ public: Error visit(const SectionIndexSection &Sec) override; Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - - explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} -}; - -using IHexLineData = SmallVector<char, 64>; - -struct IHexRecord { - // Memory address of the record. - uint16_t Addr; - // Record type (see below). - uint16_t Type; - // Record data in hexadecimal form. - StringRef HexData; - - // Helper method to get file length of the record - // including newline character - static size_t getLength(size_t DataSize) { - // :LLAAAATT[DD...DD]CC' - return DataSize * 2 + 11; - } - - // Gets length of line in a file (getLength + CRLF). - static size_t getLineLength(size_t DataSize) { - return getLength(DataSize) + 2; - } - - // Given type, address and data returns line which can - // be written to output file. - static IHexLineData getLine(uint8_t Type, uint16_t Addr, - ArrayRef<uint8_t> Data); - - // Parses the line and returns record if possible. - // Line should be trimmed from whitespace characters. - static Expected<IHexRecord> parse(StringRef Line); - - // Calculates checksum of stringified record representation - // S must NOT contain leading ':' and trailing whitespace - // characters - static uint8_t getChecksum(StringRef S); - - enum Type { - // Contains data and a 16-bit starting address for the data. - // The byte count specifies number of data bytes in the record. - Data = 0, - // Must occur exactly once per file in the last line of the file. - // The data field is empty (thus byte count is 00) and the address - // field is typically 0000. - EndOfFile = 1, - // The data field contains a 16-bit segment base address (thus byte - // count is always 02) compatible with 80x86 real mode addressing. - // The address field (typically 0000) is ignored. The segment address - // from the most recent 02 record is multiplied by 16 and added to each - // subsequent data record address to form the physical starting address - // for the data. This allows addressing up to one megabyte of address - // space. - SegmentAddr = 2, - // or 80x86 processors, specifies the initial content of the CS:IP - // registers. The address field is 0000, the byte count is always 04, - // the first two data bytes are the CS value, the latter two are the - // IP value. - StartAddr80x86 = 3, - // Allows for 32 bit addressing (up to 4GiB). The record's address field - // is ignored (typically 0000) and its byte count is always 02. The two - // data bytes (big endian) specify the upper 16 bits of the 32 bit - // absolute address for all subsequent type 00 records - ExtendedAddr = 4, - // The address field is 0000 (not used) and the byte count is always 04. - // The four data bytes represent a 32-bit address value. In the case of - // 80386 and higher CPUs, this address is loaded into the EIP register. - StartAddr = 5, - // We have no other valid types - InvalidType = 6 - }; -}; - -// Base class for IHexSectionWriter. This class implements writing algorithm, -// but doesn't actually write records. It is used for output buffer size -// calculation in IHexWriter::finalize. -class IHexSectionWriterBase : public BinarySectionWriter { - // 20-bit segment address - uint32_t SegmentAddr = 0; - // Extended linear address - uint32_t BaseAddr = 0; - - // Write segment address corresponding to 'Addr' - uint64_t writeSegmentAddr(uint64_t Addr); - // Write extended linear (base) address corresponding to 'Addr' - uint64_t writeBaseAddr(uint64_t Addr); - -protected: - // Offset in the output buffer - uint64_t Offset = 0; - - void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data); - virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data); - -public: - explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {} - - uint64_t getBufferOffset() const { return Offset; } + + explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} +}; + +using IHexLineData = SmallVector<char, 64>; + +struct IHexRecord { + // Memory address of the record. + uint16_t Addr; + // Record type (see below). + uint16_t Type; + // Record data in hexadecimal form. + StringRef HexData; + + // Helper method to get file length of the record + // including newline character + static size_t getLength(size_t DataSize) { + // :LLAAAATT[DD...DD]CC' + return DataSize * 2 + 11; + } + + // Gets length of line in a file (getLength + CRLF). + static size_t getLineLength(size_t DataSize) { + return getLength(DataSize) + 2; + } + + // Given type, address and data returns line which can + // be written to output file. + static IHexLineData getLine(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data); + + // Parses the line and returns record if possible. + // Line should be trimmed from whitespace characters. + static Expected<IHexRecord> parse(StringRef Line); + + // Calculates checksum of stringified record representation + // S must NOT contain leading ':' and trailing whitespace + // characters + static uint8_t getChecksum(StringRef S); + + enum Type { + // Contains data and a 16-bit starting address for the data. + // The byte count specifies number of data bytes in the record. + Data = 0, + // Must occur exactly once per file in the last line of the file. + // The data field is empty (thus byte count is 00) and the address + // field is typically 0000. + EndOfFile = 1, + // The data field contains a 16-bit segment base address (thus byte + // count is always 02) compatible with 80x86 real mode addressing. + // The address field (typically 0000) is ignored. The segment address + // from the most recent 02 record is multiplied by 16 and added to each + // subsequent data record address to form the physical starting address + // for the data. This allows addressing up to one megabyte of address + // space. + SegmentAddr = 2, + // or 80x86 processors, specifies the initial content of the CS:IP + // registers. The address field is 0000, the byte count is always 04, + // the first two data bytes are the CS value, the latter two are the + // IP value. + StartAddr80x86 = 3, + // Allows for 32 bit addressing (up to 4GiB). The record's address field + // is ignored (typically 0000) and its byte count is always 02. The two + // data bytes (big endian) specify the upper 16 bits of the 32 bit + // absolute address for all subsequent type 00 records + ExtendedAddr = 4, + // The address field is 0000 (not used) and the byte count is always 04. + // The four data bytes represent a 32-bit address value. In the case of + // 80386 and higher CPUs, this address is loaded into the EIP register. + StartAddr = 5, + // We have no other valid types + InvalidType = 6 + }; +}; + +// Base class for IHexSectionWriter. This class implements writing algorithm, +// but doesn't actually write records. It is used for output buffer size +// calculation in IHexWriter::finalize. +class IHexSectionWriterBase : public BinarySectionWriter { + // 20-bit segment address + uint32_t SegmentAddr = 0; + // Extended linear address + uint32_t BaseAddr = 0; + + // Write segment address corresponding to 'Addr' + uint64_t writeSegmentAddr(uint64_t Addr); + // Write extended linear (base) address corresponding to 'Addr' + uint64_t writeBaseAddr(uint64_t Addr); + +protected: + // Offset in the output buffer + uint64_t Offset = 0; + + void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data); + virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data); + +public: + explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {} + + uint64_t getBufferOffset() const { return Offset; } Error visit(const Section &Sec) final; Error visit(const OwnedDataSection &Sec) final; Error visit(const StringTableSection &Sec) override; Error visit(const DynamicRelocationSection &Sec) final; - using BinarySectionWriter::visit; -}; - -// Real IHEX section writer -class IHexSectionWriter : public IHexSectionWriterBase { -public: - IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {} - - void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override; + using BinarySectionWriter::visit; +}; + +// Real IHEX section writer +class IHexSectionWriter : public IHexSectionWriterBase { +public: + IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {} + + void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override; Error visit(const StringTableSection &Sec) override; -}; - -class Writer { -protected: - Object &Obj; - Buffer &Buf; - -public: - virtual ~Writer(); - virtual Error finalize() = 0; - virtual Error write() = 0; - - Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} -}; - -template <class ELFT> class ELFWriter : public Writer { -private: - using Elf_Addr = typename ELFT::Addr; - using Elf_Shdr = typename ELFT::Shdr; - using Elf_Phdr = typename ELFT::Phdr; - using Elf_Ehdr = typename ELFT::Ehdr; - - void initEhdrSegment(); - - void writeEhdr(); - void writePhdr(const Segment &Seg); - void writeShdr(const SectionBase &Sec); - - void writePhdrs(); - void writeShdrs(); +}; + +class Writer { +protected: + Object &Obj; + Buffer &Buf; + +public: + virtual ~Writer(); + virtual Error finalize() = 0; + virtual Error write() = 0; + + Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} +}; + +template <class ELFT> class ELFWriter : public Writer { +private: + using Elf_Addr = typename ELFT::Addr; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Phdr = typename ELFT::Phdr; + using Elf_Ehdr = typename ELFT::Ehdr; + + void initEhdrSegment(); + + void writeEhdr(); + void writePhdr(const Segment &Seg); + void writeShdr(const SectionBase &Sec); + + void writePhdrs(); + void writeShdrs(); Error writeSectionData(); - void writeSegmentData(); - - void assignOffsets(); - - std::unique_ptr<ELFSectionWriter<ELFT>> SecWriter; - - size_t totalSize() const; - -public: - virtual ~ELFWriter() {} - bool WriteSectionHeaders; - - // For --only-keep-debug, select an alternative section/segment layout - // algorithm. - bool OnlyKeepDebug; - - Error finalize() override; - Error write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug); -}; - -class BinaryWriter : public Writer { -private: - std::unique_ptr<BinarySectionWriter> SecWriter; - - uint64_t TotalSize = 0; - -public: - ~BinaryWriter() {} - Error finalize() override; - Error write() override; - BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} -}; - -class IHexWriter : public Writer { - struct SectionCompare { - bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const; - }; - - std::set<const SectionBase *, SectionCompare> Sections; - size_t TotalSize = 0; - - Error checkSection(const SectionBase &Sec); - uint64_t writeEntryPointRecord(uint8_t *Buf); - uint64_t writeEndOfFileRecord(uint8_t *Buf); - -public: - ~IHexWriter() {} - Error finalize() override; - Error write() override; - IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} -}; - -class SectionBase { -public: - std::string Name; - Segment *ParentSegment = nullptr; - uint64_t HeaderOffset = 0; - uint32_t Index = 0; - + void writeSegmentData(); + + void assignOffsets(); + + std::unique_ptr<ELFSectionWriter<ELFT>> SecWriter; + + size_t totalSize() const; + +public: + virtual ~ELFWriter() {} + bool WriteSectionHeaders; + + // For --only-keep-debug, select an alternative section/segment layout + // algorithm. + bool OnlyKeepDebug; + + Error finalize() override; + Error write() override; + ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug); +}; + +class BinaryWriter : public Writer { +private: + std::unique_ptr<BinarySectionWriter> SecWriter; + + uint64_t TotalSize = 0; + +public: + ~BinaryWriter() {} + Error finalize() override; + Error write() override; + BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} +}; + +class IHexWriter : public Writer { + struct SectionCompare { + bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const; + }; + + std::set<const SectionBase *, SectionCompare> Sections; + size_t TotalSize = 0; + + Error checkSection(const SectionBase &Sec); + uint64_t writeEntryPointRecord(uint8_t *Buf); + uint64_t writeEndOfFileRecord(uint8_t *Buf); + +public: + ~IHexWriter() {} + Error finalize() override; + Error write() override; + IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} +}; + +class SectionBase { +public: + std::string Name; + Segment *ParentSegment = nullptr; + uint64_t HeaderOffset = 0; + uint32_t Index = 0; + uint32_t OriginalIndex = 0; - uint64_t OriginalFlags = 0; - uint64_t OriginalType = ELF::SHT_NULL; - uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max(); - - uint64_t Addr = 0; - uint64_t Align = 1; - uint32_t EntrySize = 0; - uint64_t Flags = 0; - uint64_t Info = 0; - uint64_t Link = ELF::SHN_UNDEF; - uint64_t NameIndex = 0; - uint64_t Offset = 0; - uint64_t Size = 0; - uint64_t Type = ELF::SHT_NULL; - ArrayRef<uint8_t> OriginalData; + uint64_t OriginalFlags = 0; + uint64_t OriginalType = ELF::SHT_NULL; + uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max(); + + uint64_t Addr = 0; + uint64_t Align = 1; + uint32_t EntrySize = 0; + uint64_t Flags = 0; + uint64_t Info = 0; + uint64_t Link = ELF::SHN_UNDEF; + uint64_t NameIndex = 0; + uint64_t Offset = 0; + uint64_t Size = 0; + uint64_t Type = ELF::SHT_NULL; + ArrayRef<uint8_t> OriginalData; bool HasSymbol = false; - - SectionBase() = default; - SectionBase(const SectionBase &) = default; - - virtual ~SectionBase() = default; - + + SectionBase() = default; + SectionBase(const SectionBase &) = default; + + virtual ~SectionBase() = default; + virtual Error initialize(SectionTableRef SecTable); - virtual void finalize(); - // Remove references to these sections. The list of sections must be sorted. - virtual Error - removeSectionReferences(bool AllowBrokenLinks, - function_ref<bool(const SectionBase *)> ToRemove); - virtual Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); + virtual void finalize(); + // Remove references to these sections. The list of sections must be sorted. + virtual Error + removeSectionReferences(bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove); + virtual Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); virtual Error accept(SectionVisitor &Visitor) const = 0; virtual Error accept(MutableSectionVisitor &Visitor) = 0; - virtual void markSymbols(); - virtual void - replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &); - // Notify the section that it is subject to removal. - virtual void onRemove(); -}; - -class Segment { -private: - struct SectionCompare { - bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const { - // Some sections might have the same address if one of them is empty. To - // fix this we can use the lexicographic ordering on ->Addr and the + virtual void markSymbols(); + virtual void + replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &); + // Notify the section that it is subject to removal. + virtual void onRemove(); +}; + +class Segment { +private: + struct SectionCompare { + bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const { + // Some sections might have the same address if one of them is empty. To + // fix this we can use the lexicographic ordering on ->Addr and the // original index. - if (Lhs->OriginalOffset == Rhs->OriginalOffset) + if (Lhs->OriginalOffset == Rhs->OriginalOffset) return Lhs->OriginalIndex < Rhs->OriginalIndex; - return Lhs->OriginalOffset < Rhs->OriginalOffset; - } - }; - -public: - uint32_t Type = 0; - uint32_t Flags = 0; - uint64_t Offset = 0; - uint64_t VAddr = 0; - uint64_t PAddr = 0; - uint64_t FileSize = 0; - uint64_t MemSize = 0; - uint64_t Align = 0; - - uint32_t Index = 0; - uint64_t OriginalOffset = 0; - Segment *ParentSegment = nullptr; - ArrayRef<uint8_t> Contents; - std::set<const SectionBase *, SectionCompare> Sections; - - explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {} - Segment() = default; - - const SectionBase *firstSection() const { - if (!Sections.empty()) - return *Sections.begin(); - return nullptr; - } - - void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } - void addSection(const SectionBase *Sec) { Sections.insert(Sec); } - - ArrayRef<uint8_t> getContents() const { return Contents; } -}; - -class Section : public SectionBase { - MAKE_SEC_WRITER_FRIEND - - ArrayRef<uint8_t> Contents; - SectionBase *LinkSection = nullptr; - -public: - explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {} - + return Lhs->OriginalOffset < Rhs->OriginalOffset; + } + }; + +public: + uint32_t Type = 0; + uint32_t Flags = 0; + uint64_t Offset = 0; + uint64_t VAddr = 0; + uint64_t PAddr = 0; + uint64_t FileSize = 0; + uint64_t MemSize = 0; + uint64_t Align = 0; + + uint32_t Index = 0; + uint64_t OriginalOffset = 0; + Segment *ParentSegment = nullptr; + ArrayRef<uint8_t> Contents; + std::set<const SectionBase *, SectionCompare> Sections; + + explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {} + Segment() = default; + + const SectionBase *firstSection() const { + if (!Sections.empty()) + return *Sections.begin(); + return nullptr; + } + + void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } + void addSection(const SectionBase *Sec) { Sections.insert(Sec); } + + ArrayRef<uint8_t> getContents() const { return Contents; } +}; + +class Section : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + ArrayRef<uint8_t> Contents; + SectionBase *LinkSection = nullptr; + +public: + explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {} + Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; Error removeSectionReferences( bool AllowBrokenLinks, - function_ref<bool(const SectionBase *)> ToRemove) override; + function_ref<bool(const SectionBase *)> ToRemove) override; Error initialize(SectionTableRef SecTable) override; - void finalize() override; -}; - -class OwnedDataSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - - std::vector<uint8_t> Data; - -public: - OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data) - : Data(std::begin(Data), std::end(Data)) { - Name = SecName.str(); - Type = OriginalType = ELF::SHT_PROGBITS; - Size = Data.size(); - OriginalOffset = std::numeric_limits<uint64_t>::max(); - } - - OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags, - uint64_t SecOff) { - Name = SecName.str(); - Type = OriginalType = ELF::SHT_PROGBITS; - Addr = SecAddr; - Flags = OriginalFlags = SecFlags; - OriginalOffset = SecOff; - } - - void appendHexData(StringRef HexData); + void finalize() override; +}; + +class OwnedDataSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + std::vector<uint8_t> Data; + +public: + OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data) + : Data(std::begin(Data), std::end(Data)) { + Name = SecName.str(); + Type = OriginalType = ELF::SHT_PROGBITS; + Size = Data.size(); + OriginalOffset = std::numeric_limits<uint64_t>::max(); + } + + OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags, + uint64_t SecOff) { + Name = SecName.str(); + Type = OriginalType = ELF::SHT_PROGBITS; + Addr = SecAddr; + Flags = OriginalFlags = SecFlags; + OriginalOffset = SecOff; + } + + void appendHexData(StringRef HexData); Error accept(SectionVisitor &Sec) const override; Error accept(MutableSectionVisitor &Visitor) override; -}; - -class CompressedSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - - DebugCompressionType CompressionType; - uint64_t DecompressedSize; - uint64_t DecompressedAlign; - SmallVector<char, 128> CompressedData; - -public: +}; + +class CompressedSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + DebugCompressionType CompressionType; + uint64_t DecompressedSize; + uint64_t DecompressedAlign; + SmallVector<char, 128> CompressedData; + +public: static Expected<CompressedSection> create(const SectionBase &Sec, DebugCompressionType CompressionType); static Expected<CompressedSection> create(ArrayRef<uint8_t> CompressedData, uint64_t DecompressedSize, uint64_t DecompressedAlign); - - uint64_t getDecompressedSize() const { return DecompressedSize; } - uint64_t getDecompressedAlign() const { return DecompressedAlign; } - + + uint64_t getDecompressedSize() const { return DecompressedSize; } + uint64_t getDecompressedAlign() const { return DecompressedAlign; } + Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; - - static bool classof(const SectionBase *S) { - return (S->OriginalFlags & ELF::SHF_COMPRESSED) || - (StringRef(S->Name).startswith(".zdebug")); - } + + static bool classof(const SectionBase *S) { + return (S->OriginalFlags & ELF::SHF_COMPRESSED) || + (StringRef(S->Name).startswith(".zdebug")); + } private: CompressedSection(const SectionBase &Sec, DebugCompressionType CompressionType, Error &Err); CompressedSection(ArrayRef<uint8_t> CompressedData, uint64_t DecompressedSize, uint64_t DecompressedAlign); -}; - -class DecompressedSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - -public: - explicit DecompressedSection(const CompressedSection &Sec) - : SectionBase(Sec) { - Size = Sec.getDecompressedSize(); - Align = Sec.getDecompressedAlign(); - Flags = OriginalFlags = (Flags & ~ELF::SHF_COMPRESSED); - if (StringRef(Name).startswith(".zdebug")) - Name = "." + Name.substr(2); - } - +}; + +class DecompressedSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + +public: + explicit DecompressedSection(const CompressedSection &Sec) + : SectionBase(Sec) { + Size = Sec.getDecompressedSize(); + Align = Sec.getDecompressedAlign(); + Flags = OriginalFlags = (Flags & ~ELF::SHF_COMPRESSED); + if (StringRef(Name).startswith(".zdebug")) + Name = "." + Name.substr(2); + } + Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; -}; - -// There are two types of string tables that can exist, dynamic and not dynamic. -// In the dynamic case the string table is allocated. Changing a dynamic string -// table would mean altering virtual addresses and thus the memory image. So -// dynamic string tables should not have an interface to modify them or -// reconstruct them. This type lets us reconstruct a string table. To avoid -// this class being used for dynamic string tables (which has happened) the -// classof method checks that the particular instance is not allocated. This -// then agrees with the makeSection method used to construct most sections. -class StringTableSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - - StringTableBuilder StrTabBuilder; - -public: - StringTableSection() : StrTabBuilder(StringTableBuilder::ELF) { - Type = OriginalType = ELF::SHT_STRTAB; - } - - void addString(StringRef Name); - uint32_t findIndex(StringRef Name) const; - void prepareForLayout(); +}; + +// There are two types of string tables that can exist, dynamic and not dynamic. +// In the dynamic case the string table is allocated. Changing a dynamic string +// table would mean altering virtual addresses and thus the memory image. So +// dynamic string tables should not have an interface to modify them or +// reconstruct them. This type lets us reconstruct a string table. To avoid +// this class being used for dynamic string tables (which has happened) the +// classof method checks that the particular instance is not allocated. This +// then agrees with the makeSection method used to construct most sections. +class StringTableSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + StringTableBuilder StrTabBuilder; + +public: + StringTableSection() : StrTabBuilder(StringTableBuilder::ELF) { + Type = OriginalType = ELF::SHT_STRTAB; + } + + void addString(StringRef Name); + uint32_t findIndex(StringRef Name) const; + void prepareForLayout(); Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; - - static bool classof(const SectionBase *S) { - if (S->OriginalFlags & ELF::SHF_ALLOC) - return false; - return S->OriginalType == ELF::SHT_STRTAB; - } -}; - -// Symbols have a st_shndx field that normally stores an index but occasionally -// stores a different special value. This enum keeps track of what the st_shndx -// field means. Most of the values are just copies of the special SHN_* values. -// SYMBOL_SIMPLE_INDEX means that the st_shndx is just an index of a section. -enum SymbolShndxType { - SYMBOL_SIMPLE_INDEX = 0, - SYMBOL_ABS = ELF::SHN_ABS, - SYMBOL_COMMON = ELF::SHN_COMMON, - SYMBOL_LOPROC = ELF::SHN_LOPROC, - SYMBOL_AMDGPU_LDS = ELF::SHN_AMDGPU_LDS, - SYMBOL_HEXAGON_SCOMMON = ELF::SHN_HEXAGON_SCOMMON, - SYMBOL_HEXAGON_SCOMMON_2 = ELF::SHN_HEXAGON_SCOMMON_2, - SYMBOL_HEXAGON_SCOMMON_4 = ELF::SHN_HEXAGON_SCOMMON_4, - SYMBOL_HEXAGON_SCOMMON_8 = ELF::SHN_HEXAGON_SCOMMON_8, - SYMBOL_HIPROC = ELF::SHN_HIPROC, - SYMBOL_LOOS = ELF::SHN_LOOS, - SYMBOL_HIOS = ELF::SHN_HIOS, - SYMBOL_XINDEX = ELF::SHN_XINDEX, -}; - -struct Symbol { - uint8_t Binding; - SectionBase *DefinedIn = nullptr; - SymbolShndxType ShndxType; - uint32_t Index; - std::string Name; - uint32_t NameIndex; - uint64_t Size; - uint8_t Type; - uint64_t Value; - uint8_t Visibility; - bool Referenced = false; - - uint16_t getShndx() const; - bool isCommon() const; -}; - -class SectionIndexSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - -private: - std::vector<uint32_t> Indexes; - SymbolTableSection *Symbols = nullptr; - -public: - virtual ~SectionIndexSection() {} - void addIndex(uint32_t Index) { - assert(Size > 0); + + static bool classof(const SectionBase *S) { + if (S->OriginalFlags & ELF::SHF_ALLOC) + return false; + return S->OriginalType == ELF::SHT_STRTAB; + } +}; + +// Symbols have a st_shndx field that normally stores an index but occasionally +// stores a different special value. This enum keeps track of what the st_shndx +// field means. Most of the values are just copies of the special SHN_* values. +// SYMBOL_SIMPLE_INDEX means that the st_shndx is just an index of a section. +enum SymbolShndxType { + SYMBOL_SIMPLE_INDEX = 0, + SYMBOL_ABS = ELF::SHN_ABS, + SYMBOL_COMMON = ELF::SHN_COMMON, + SYMBOL_LOPROC = ELF::SHN_LOPROC, + SYMBOL_AMDGPU_LDS = ELF::SHN_AMDGPU_LDS, + SYMBOL_HEXAGON_SCOMMON = ELF::SHN_HEXAGON_SCOMMON, + SYMBOL_HEXAGON_SCOMMON_2 = ELF::SHN_HEXAGON_SCOMMON_2, + SYMBOL_HEXAGON_SCOMMON_4 = ELF::SHN_HEXAGON_SCOMMON_4, + SYMBOL_HEXAGON_SCOMMON_8 = ELF::SHN_HEXAGON_SCOMMON_8, + SYMBOL_HIPROC = ELF::SHN_HIPROC, + SYMBOL_LOOS = ELF::SHN_LOOS, + SYMBOL_HIOS = ELF::SHN_HIOS, + SYMBOL_XINDEX = ELF::SHN_XINDEX, +}; + +struct Symbol { + uint8_t Binding; + SectionBase *DefinedIn = nullptr; + SymbolShndxType ShndxType; + uint32_t Index; + std::string Name; + uint32_t NameIndex; + uint64_t Size; + uint8_t Type; + uint64_t Value; + uint8_t Visibility; + bool Referenced = false; + + uint16_t getShndx() const; + bool isCommon() const; +}; + +class SectionIndexSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + +private: + std::vector<uint32_t> Indexes; + SymbolTableSection *Symbols = nullptr; + +public: + virtual ~SectionIndexSection() {} + void addIndex(uint32_t Index) { + assert(Size > 0); Indexes.push_back(Index); + } + + void reserve(size_t NumSymbols) { + Indexes.reserve(NumSymbols); + Size = NumSymbols * 4; } - - void reserve(size_t NumSymbols) { - Indexes.reserve(NumSymbols); - Size = NumSymbols * 4; - } - void setSymTab(SymbolTableSection *SymTab) { Symbols = SymTab; } + void setSymTab(SymbolTableSection *SymTab) { Symbols = SymTab; } Error initialize(SectionTableRef SecTable) override; - void finalize() override; + void finalize() override; Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; - - SectionIndexSection() { - Name = ".symtab_shndx"; - Align = 4; - EntrySize = 4; - Type = OriginalType = ELF::SHT_SYMTAB_SHNDX; - } -}; - -class SymbolTableSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - - void setStrTab(StringTableSection *StrTab) { SymbolNames = StrTab; } - void assignIndices(); - -protected: - std::vector<std::unique_ptr<Symbol>> Symbols; - StringTableSection *SymbolNames = nullptr; - SectionIndexSection *SectionIndexTable = nullptr; - - using SymPtr = std::unique_ptr<Symbol>; - -public: - SymbolTableSection() { Type = OriginalType = ELF::SHT_SYMTAB; } - - void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, - uint64_t Value, uint8_t Visibility, uint16_t Shndx, - uint64_t SymbolSize); - void prepareForLayout(); - // An 'empty' symbol table still contains a null symbol. - bool empty() const { return Symbols.size() == 1; } - void setShndxTable(SectionIndexSection *ShndxTable) { - SectionIndexTable = ShndxTable; - } - const SectionIndexSection *getShndxTable() const { return SectionIndexTable; } - void fillShndxTable(); - const SectionBase *getStrTab() const { return SymbolNames; } + + SectionIndexSection() { + Name = ".symtab_shndx"; + Align = 4; + EntrySize = 4; + Type = OriginalType = ELF::SHT_SYMTAB_SHNDX; + } +}; + +class SymbolTableSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + void setStrTab(StringTableSection *StrTab) { SymbolNames = StrTab; } + void assignIndices(); + +protected: + std::vector<std::unique_ptr<Symbol>> Symbols; + StringTableSection *SymbolNames = nullptr; + SectionIndexSection *SectionIndexTable = nullptr; + + using SymPtr = std::unique_ptr<Symbol>; + +public: + SymbolTableSection() { Type = OriginalType = ELF::SHT_SYMTAB; } + + void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, + uint64_t Value, uint8_t Visibility, uint16_t Shndx, + uint64_t SymbolSize); + void prepareForLayout(); + // An 'empty' symbol table still contains a null symbol. + bool empty() const { return Symbols.size() == 1; } + void setShndxTable(SectionIndexSection *ShndxTable) { + SectionIndexTable = ShndxTable; + } + const SectionIndexSection *getShndxTable() const { return SectionIndexTable; } + void fillShndxTable(); + const SectionBase *getStrTab() const { return SymbolNames; } Expected<const Symbol *> getSymbolByIndex(uint32_t Index) const; Expected<Symbol *> getSymbolByIndex(uint32_t Index); - void updateSymbols(function_ref<void(Symbol &)> Callable); - + void updateSymbols(function_ref<void(Symbol &)> Callable); + Error removeSectionReferences( bool AllowBrokenLinks, - function_ref<bool(const SectionBase *)> ToRemove) override; + function_ref<bool(const SectionBase *)> ToRemove) override; Error initialize(SectionTableRef SecTable) override; - void finalize() override; + void finalize() override; Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; - Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; - void replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &FromTo) override; - - static bool classof(const SectionBase *S) { - return S->OriginalType == ELF::SHT_SYMTAB; - } -}; - -struct Relocation { - Symbol *RelocSymbol = nullptr; - uint64_t Offset; - uint64_t Addend; - uint32_t Type; -}; - -// All relocation sections denote relocations to apply to another section. -// However, some relocation sections use a dynamic symbol table and others use -// a regular symbol table. Because the types of the two symbol tables differ in -// our system (because they should behave differently) we can't uniformly -// represent all relocations with the same base class if we expose an interface -// that mentions the symbol table type. So we split the two base types into two -// different classes, one which handles the section the relocation is applied to -// and another which handles the symbol table type. The symbol table type is -// taken as a type parameter to the class (see RelocSectionWithSymtabBase). -class RelocationSectionBase : public SectionBase { -protected: - SectionBase *SecToApplyRel = nullptr; - -public: - const SectionBase *getSection() const { return SecToApplyRel; } - void setSection(SectionBase *Sec) { SecToApplyRel = Sec; } - - static bool classof(const SectionBase *S) { - return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; - } -}; - -// Takes the symbol table type to use as a parameter so that we can deduplicate -// that code between the two symbol table types. -template <class SymTabType> -class RelocSectionWithSymtabBase : public RelocationSectionBase { - void setSymTab(SymTabType *SymTab) { Symbols = SymTab; } - -protected: - RelocSectionWithSymtabBase() = default; - - SymTabType *Symbols = nullptr; - -public: + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + void replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) override; + + static bool classof(const SectionBase *S) { + return S->OriginalType == ELF::SHT_SYMTAB; + } +}; + +struct Relocation { + Symbol *RelocSymbol = nullptr; + uint64_t Offset; + uint64_t Addend; + uint32_t Type; +}; + +// All relocation sections denote relocations to apply to another section. +// However, some relocation sections use a dynamic symbol table and others use +// a regular symbol table. Because the types of the two symbol tables differ in +// our system (because they should behave differently) we can't uniformly +// represent all relocations with the same base class if we expose an interface +// that mentions the symbol table type. So we split the two base types into two +// different classes, one which handles the section the relocation is applied to +// and another which handles the symbol table type. The symbol table type is +// taken as a type parameter to the class (see RelocSectionWithSymtabBase). +class RelocationSectionBase : public SectionBase { +protected: + SectionBase *SecToApplyRel = nullptr; + +public: + const SectionBase *getSection() const { return SecToApplyRel; } + void setSection(SectionBase *Sec) { SecToApplyRel = Sec; } + + static bool classof(const SectionBase *S) { + return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; + } +}; + +// Takes the symbol table type to use as a parameter so that we can deduplicate +// that code between the two symbol table types. +template <class SymTabType> +class RelocSectionWithSymtabBase : public RelocationSectionBase { + void setSymTab(SymTabType *SymTab) { Symbols = SymTab; } + +protected: + RelocSectionWithSymtabBase() = default; + + SymTabType *Symbols = nullptr; + +public: Error initialize(SectionTableRef SecTable) override; - void finalize() override; -}; - -class RelocationSection - : public RelocSectionWithSymtabBase<SymbolTableSection> { - MAKE_SEC_WRITER_FRIEND - - std::vector<Relocation> Relocations; - -public: - void addRelocation(Relocation Rel) { Relocations.push_back(Rel); } + void finalize() override; +}; + +class RelocationSection + : public RelocSectionWithSymtabBase<SymbolTableSection> { + MAKE_SEC_WRITER_FRIEND + + std::vector<Relocation> Relocations; + +public: + void addRelocation(Relocation Rel) { Relocations.push_back(Rel); } Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; Error removeSectionReferences( bool AllowBrokenLinks, - function_ref<bool(const SectionBase *)> ToRemove) override; - Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; - void markSymbols() override; - void replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &FromTo) override; - - static bool classof(const SectionBase *S) { - if (S->OriginalFlags & ELF::SHF_ALLOC) - return false; - return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; - } -}; - -// TODO: The way stripping and groups interact is complicated -// and still needs to be worked on. - -class GroupSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - const SymbolTableSection *SymTab = nullptr; - Symbol *Sym = nullptr; - ELF::Elf32_Word FlagWord; - SmallVector<SectionBase *, 3> GroupMembers; - -public: - // TODO: Contents is present in several classes of the hierarchy. - // This needs to be refactored to avoid duplication. - ArrayRef<uint8_t> Contents; - - explicit GroupSection(ArrayRef<uint8_t> Data) : Contents(Data) {} - - void setSymTab(const SymbolTableSection *SymTabSec) { SymTab = SymTabSec; } - void setSymbol(Symbol *S) { Sym = S; } - void setFlagWord(ELF::Elf32_Word W) { FlagWord = W; } - void addMember(SectionBase *Sec) { GroupMembers.push_back(Sec); } - + function_ref<bool(const SectionBase *)> ToRemove) override; + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + void markSymbols() override; + void replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) override; + + static bool classof(const SectionBase *S) { + if (S->OriginalFlags & ELF::SHF_ALLOC) + return false; + return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; + } +}; + +// TODO: The way stripping and groups interact is complicated +// and still needs to be worked on. + +class GroupSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + const SymbolTableSection *SymTab = nullptr; + Symbol *Sym = nullptr; + ELF::Elf32_Word FlagWord; + SmallVector<SectionBase *, 3> GroupMembers; + +public: + // TODO: Contents is present in several classes of the hierarchy. + // This needs to be refactored to avoid duplication. + ArrayRef<uint8_t> Contents; + + explicit GroupSection(ArrayRef<uint8_t> Data) : Contents(Data) {} + + void setSymTab(const SymbolTableSection *SymTabSec) { SymTab = SymTabSec; } + void setSymbol(Symbol *S) { Sym = S; } + void setFlagWord(ELF::Elf32_Word W) { FlagWord = W; } + void addMember(SectionBase *Sec) { GroupMembers.push_back(Sec); } + Error accept(SectionVisitor &) const override; Error accept(MutableSectionVisitor &Visitor) override; - void finalize() override; - Error removeSectionReferences( - bool AllowBrokenLinks, - function_ref<bool(const SectionBase *)> ToRemove) override; - Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; - void markSymbols() override; - void replaceSectionReferences( - const DenseMap<SectionBase *, SectionBase *> &FromTo) override; - void onRemove() override; - - static bool classof(const SectionBase *S) { - return S->OriginalType == ELF::SHT_GROUP; - } -}; - -class DynamicSymbolTableSection : public Section { -public: - explicit DynamicSymbolTableSection(ArrayRef<uint8_t> Data) : Section(Data) {} - - static bool classof(const SectionBase *S) { - return S->OriginalType == ELF::SHT_DYNSYM; - } -}; - -class DynamicSection : public Section { -public: - explicit DynamicSection(ArrayRef<uint8_t> Data) : Section(Data) {} - - static bool classof(const SectionBase *S) { - return S->OriginalType == ELF::SHT_DYNAMIC; - } -}; - -class DynamicRelocationSection - : public RelocSectionWithSymtabBase<DynamicSymbolTableSection> { - MAKE_SEC_WRITER_FRIEND - -private: - ArrayRef<uint8_t> Contents; - -public: - explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {} - + void finalize() override; + Error removeSectionReferences( + bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) override; + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + void markSymbols() override; + void replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) override; + void onRemove() override; + + static bool classof(const SectionBase *S) { + return S->OriginalType == ELF::SHT_GROUP; + } +}; + +class DynamicSymbolTableSection : public Section { +public: + explicit DynamicSymbolTableSection(ArrayRef<uint8_t> Data) : Section(Data) {} + + static bool classof(const SectionBase *S) { + return S->OriginalType == ELF::SHT_DYNSYM; + } +}; + +class DynamicSection : public Section { +public: + explicit DynamicSection(ArrayRef<uint8_t> Data) : Section(Data) {} + + static bool classof(const SectionBase *S) { + return S->OriginalType == ELF::SHT_DYNAMIC; + } +}; + +class DynamicRelocationSection + : public RelocSectionWithSymtabBase<DynamicSymbolTableSection> { + MAKE_SEC_WRITER_FRIEND + +private: + ArrayRef<uint8_t> Contents; + +public: + explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {} + Error accept(SectionVisitor &) const override; Error accept(MutableSectionVisitor &Visitor) override; - Error removeSectionReferences( - bool AllowBrokenLinks, - function_ref<bool(const SectionBase *)> ToRemove) override; - - static bool classof(const SectionBase *S) { - if (!(S->OriginalFlags & ELF::SHF_ALLOC)) - return false; - return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; - } -}; - -class GnuDebugLinkSection : public SectionBase { - MAKE_SEC_WRITER_FRIEND - -private: - StringRef FileName; - uint32_t CRC32; - - void init(StringRef File); - -public: - // If we add this section from an external source we can use this ctor. - explicit GnuDebugLinkSection(StringRef File, uint32_t PrecomputedCRC); + Error removeSectionReferences( + bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) override; + + static bool classof(const SectionBase *S) { + if (!(S->OriginalFlags & ELF::SHF_ALLOC)) + return false; + return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; + } +}; + +class GnuDebugLinkSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + +private: + StringRef FileName; + uint32_t CRC32; + + void init(StringRef File); + +public: + // If we add this section from an external source we can use this ctor. + explicit GnuDebugLinkSection(StringRef File, uint32_t PrecomputedCRC); Error accept(SectionVisitor &Visitor) const override; Error accept(MutableSectionVisitor &Visitor) override; -}; - -class Reader { -public: - virtual ~Reader(); +}; + +class Reader { +public: + virtual ~Reader(); virtual Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const = 0; -}; - -using object::Binary; -using object::ELFFile; -using object::ELFObjectFile; -using object::OwningBinary; - -class BasicELFBuilder { -protected: - std::unique_ptr<Object> Obj; - - void initFileHeader(); - void initHeaderSegment(); - StringTableSection *addStrTab(); - SymbolTableSection *addSymTab(StringTableSection *StrTab); +}; + +using object::Binary; +using object::ELFFile; +using object::ELFObjectFile; +using object::OwningBinary; + +class BasicELFBuilder { +protected: + std::unique_ptr<Object> Obj; + + void initFileHeader(); + void initHeaderSegment(); + StringTableSection *addStrTab(); + SymbolTableSection *addSymTab(StringTableSection *StrTab); Error initSections(); - -public: - BasicELFBuilder() : Obj(std::make_unique<Object>()) {} -}; - -class BinaryELFBuilder : public BasicELFBuilder { - MemoryBuffer *MemBuf; - uint8_t NewSymbolVisibility; - void addData(SymbolTableSection *SymTab); - -public: - BinaryELFBuilder(MemoryBuffer *MB, uint8_t NewSymbolVisibility) - : BasicELFBuilder(), MemBuf(MB), - NewSymbolVisibility(NewSymbolVisibility) {} - + +public: + BasicELFBuilder() : Obj(std::make_unique<Object>()) {} +}; + +class BinaryELFBuilder : public BasicELFBuilder { + MemoryBuffer *MemBuf; + uint8_t NewSymbolVisibility; + void addData(SymbolTableSection *SymTab); + +public: + BinaryELFBuilder(MemoryBuffer *MB, uint8_t NewSymbolVisibility) + : BasicELFBuilder(), MemBuf(MB), + NewSymbolVisibility(NewSymbolVisibility) {} + Expected<std::unique_ptr<Object>> build(); -}; - -class IHexELFBuilder : public BasicELFBuilder { - const std::vector<IHexRecord> &Records; - - void addDataSections(); - -public: - IHexELFBuilder(const std::vector<IHexRecord> &Records) - : BasicELFBuilder(), Records(Records) {} - +}; + +class IHexELFBuilder : public BasicELFBuilder { + const std::vector<IHexRecord> &Records; + + void addDataSections(); + +public: + IHexELFBuilder(const std::vector<IHexRecord> &Records) + : BasicELFBuilder(), Records(Records) {} + Expected<std::unique_ptr<Object>> build(); -}; - -template <class ELFT> class ELFBuilder { -private: - using Elf_Addr = typename ELFT::Addr; - using Elf_Shdr = typename ELFT::Shdr; - using Elf_Word = typename ELFT::Word; - - const ELFFile<ELFT> &ElfFile; - Object &Obj; - size_t EhdrOffset = 0; - Optional<StringRef> ExtractPartition; - - void setParentSegment(Segment &Child); +}; + +template <class ELFT> class ELFBuilder { +private: + using Elf_Addr = typename ELFT::Addr; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Word = typename ELFT::Word; + + const ELFFile<ELFT> &ElfFile; + Object &Obj; + size_t EhdrOffset = 0; + Optional<StringRef> ExtractPartition; + + void setParentSegment(Segment &Child); Error readProgramHeaders(const ELFFile<ELFT> &HeadersFile); Error initGroupSection(GroupSection *GroupSec); Error initSymbolTable(SymbolTableSection *SymTab); @@ -953,149 +953,149 @@ private: Error readSections(bool EnsureSymtab); Error findEhdrOffset(); Expected<SectionBase &> makeSection(const Elf_Shdr &Shdr); - -public: - ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj, - Optional<StringRef> ExtractPartition) + +public: + ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj, + Optional<StringRef> ExtractPartition) : ElfFile(ElfObj.getELFFile()), Obj(Obj), - ExtractPartition(ExtractPartition) {} - + ExtractPartition(ExtractPartition) {} + Error build(bool EnsureSymtab); -}; - -class BinaryReader : public Reader { - MemoryBuffer *MemBuf; - uint8_t NewSymbolVisibility; - -public: - BinaryReader(MemoryBuffer *MB, const uint8_t NewSymbolVisibility) - : MemBuf(MB), NewSymbolVisibility(NewSymbolVisibility) {} +}; + +class BinaryReader : public Reader { + MemoryBuffer *MemBuf; + uint8_t NewSymbolVisibility; + +public: + BinaryReader(MemoryBuffer *MB, const uint8_t NewSymbolVisibility) + : MemBuf(MB), NewSymbolVisibility(NewSymbolVisibility) {} Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const override; -}; - -class IHexReader : public Reader { - MemoryBuffer *MemBuf; - - Expected<std::vector<IHexRecord>> parse() const; - Error parseError(size_t LineNo, Error E) const { - return LineNo == -1U - ? createFileError(MemBuf->getBufferIdentifier(), std::move(E)) - : createFileError(MemBuf->getBufferIdentifier(), LineNo, - std::move(E)); - } - template <typename... Ts> - Error parseError(size_t LineNo, char const *Fmt, const Ts &... Vals) const { - Error E = createStringError(errc::invalid_argument, Fmt, Vals...); - return parseError(LineNo, std::move(E)); - } - -public: - IHexReader(MemoryBuffer *MB) : MemBuf(MB) {} - +}; + +class IHexReader : public Reader { + MemoryBuffer *MemBuf; + + Expected<std::vector<IHexRecord>> parse() const; + Error parseError(size_t LineNo, Error E) const { + return LineNo == -1U + ? createFileError(MemBuf->getBufferIdentifier(), std::move(E)) + : createFileError(MemBuf->getBufferIdentifier(), LineNo, + std::move(E)); + } + template <typename... Ts> + Error parseError(size_t LineNo, char const *Fmt, const Ts &... Vals) const { + Error E = createStringError(errc::invalid_argument, Fmt, Vals...); + return parseError(LineNo, std::move(E)); + } + +public: + IHexReader(MemoryBuffer *MB) : MemBuf(MB) {} + Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const override; -}; - -class ELFReader : public Reader { - Binary *Bin; - Optional<StringRef> ExtractPartition; - -public: +}; + +class ELFReader : public Reader { + Binary *Bin; + Optional<StringRef> ExtractPartition; + +public: Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const override; - explicit ELFReader(Binary *B, Optional<StringRef> ExtractPartition) - : Bin(B), ExtractPartition(ExtractPartition) {} -}; - -class Object { -private: - using SecPtr = std::unique_ptr<SectionBase>; - using SegPtr = std::unique_ptr<Segment>; - - std::vector<SecPtr> Sections; - std::vector<SegPtr> Segments; - std::vector<SecPtr> RemovedSections; - - static bool sectionIsAlloc(const SectionBase &Sec) { - return Sec.Flags & ELF::SHF_ALLOC; - }; - -public: - template <class T> - using Range = iterator_range< - pointee_iterator<typename std::vector<std::unique_ptr<T>>::iterator>>; - - template <class T> - using ConstRange = iterator_range<pointee_iterator< - typename std::vector<std::unique_ptr<T>>::const_iterator>>; - - // It is often the case that the ELF header and the program header table are - // not present in any segment. This could be a problem during file layout, - // because other segments may get assigned an offset where either of the - // two should reside, which will effectively corrupt the resulting binary. - // Other than that we use these segments to track program header offsets - // when they may not follow the ELF header. - Segment ElfHdrSegment; - Segment ProgramHdrSegment; - - uint8_t OSABI; - uint8_t ABIVersion; - uint64_t Entry; - uint64_t SHOff; - uint32_t Type; - uint32_t Machine; - uint32_t Version; - uint32_t Flags; - - bool HadShdrs = true; - bool MustBeRelocatable = false; - StringTableSection *SectionNames = nullptr; - SymbolTableSection *SymbolTable = nullptr; - SectionIndexSection *SectionIndexTable = nullptr; - - void sortSections(); - SectionTableRef sections() { return SectionTableRef(Sections); } - ConstRange<SectionBase> sections() const { - return make_pointee_range(Sections); - } - iterator_range< - filter_iterator<pointee_iterator<std::vector<SecPtr>::const_iterator>, - decltype(§ionIsAlloc)>> - allocSections() const { - return make_filter_range(make_pointee_range(Sections), sectionIsAlloc); - } - - SectionBase *findSection(StringRef Name) { - auto SecIt = - find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; }); - return SecIt == Sections.end() ? nullptr : SecIt->get(); - } - SectionTableRef removedSections() { return SectionTableRef(RemovedSections); } - - Range<Segment> segments() { return make_pointee_range(Segments); } - ConstRange<Segment> segments() const { return make_pointee_range(Segments); } - - Error removeSections(bool AllowBrokenLinks, - std::function<bool(const SectionBase &)> ToRemove); - Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); - template <class T, class... Ts> T &addSection(Ts &&... Args) { - auto Sec = std::make_unique<T>(std::forward<Ts>(Args)...); - auto Ptr = Sec.get(); - MustBeRelocatable |= isa<RelocationSection>(*Ptr); - Sections.emplace_back(std::move(Sec)); - Ptr->Index = Sections.size(); - return *Ptr; - } + explicit ELFReader(Binary *B, Optional<StringRef> ExtractPartition) + : Bin(B), ExtractPartition(ExtractPartition) {} +}; + +class Object { +private: + using SecPtr = std::unique_ptr<SectionBase>; + using SegPtr = std::unique_ptr<Segment>; + + std::vector<SecPtr> Sections; + std::vector<SegPtr> Segments; + std::vector<SecPtr> RemovedSections; + + static bool sectionIsAlloc(const SectionBase &Sec) { + return Sec.Flags & ELF::SHF_ALLOC; + }; + +public: + template <class T> + using Range = iterator_range< + pointee_iterator<typename std::vector<std::unique_ptr<T>>::iterator>>; + + template <class T> + using ConstRange = iterator_range<pointee_iterator< + typename std::vector<std::unique_ptr<T>>::const_iterator>>; + + // It is often the case that the ELF header and the program header table are + // not present in any segment. This could be a problem during file layout, + // because other segments may get assigned an offset where either of the + // two should reside, which will effectively corrupt the resulting binary. + // Other than that we use these segments to track program header offsets + // when they may not follow the ELF header. + Segment ElfHdrSegment; + Segment ProgramHdrSegment; + + uint8_t OSABI; + uint8_t ABIVersion; + uint64_t Entry; + uint64_t SHOff; + uint32_t Type; + uint32_t Machine; + uint32_t Version; + uint32_t Flags; + + bool HadShdrs = true; + bool MustBeRelocatable = false; + StringTableSection *SectionNames = nullptr; + SymbolTableSection *SymbolTable = nullptr; + SectionIndexSection *SectionIndexTable = nullptr; + + void sortSections(); + SectionTableRef sections() { return SectionTableRef(Sections); } + ConstRange<SectionBase> sections() const { + return make_pointee_range(Sections); + } + iterator_range< + filter_iterator<pointee_iterator<std::vector<SecPtr>::const_iterator>, + decltype(§ionIsAlloc)>> + allocSections() const { + return make_filter_range(make_pointee_range(Sections), sectionIsAlloc); + } + + SectionBase *findSection(StringRef Name) { + auto SecIt = + find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; }); + return SecIt == Sections.end() ? nullptr : SecIt->get(); + } + SectionTableRef removedSections() { return SectionTableRef(RemovedSections); } + + Range<Segment> segments() { return make_pointee_range(Segments); } + ConstRange<Segment> segments() const { return make_pointee_range(Segments); } + + Error removeSections(bool AllowBrokenLinks, + std::function<bool(const SectionBase &)> ToRemove); + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); + template <class T, class... Ts> T &addSection(Ts &&... Args) { + auto Sec = std::make_unique<T>(std::forward<Ts>(Args)...); + auto Ptr = Sec.get(); + MustBeRelocatable |= isa<RelocationSection>(*Ptr); + Sections.emplace_back(std::move(Sec)); + Ptr->Index = Sections.size(); + return *Ptr; + } Error addNewSymbolTable(); - Segment &addSegment(ArrayRef<uint8_t> Data) { - Segments.emplace_back(std::make_unique<Segment>(Data)); - return *Segments.back(); - } - bool isRelocatable() const { - return (Type != ELF::ET_DYN && Type != ELF::ET_EXEC) || MustBeRelocatable; - } -}; - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_OBJECT_H + Segment &addSegment(ArrayRef<uint8_t> Data) { + Segments.emplace_back(std::make_unique<Segment>(Data)); + return *Segments.back(); + } + bool isRelocatable() const { + return (Type != ELF::ET_DYN && Type != ELF::ET_EXEC) || MustBeRelocatable; + } +}; + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_OBJECT_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/InstallNameToolOpts.td b/contrib/libs/llvm12/tools/llvm-objcopy/InstallNameToolOpts.td index 88dea84400..bada17fdb2 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/InstallNameToolOpts.td +++ b/contrib/libs/llvm12/tools/llvm-objcopy/InstallNameToolOpts.td @@ -1,43 +1,43 @@ -//===-- InstallNameToolOpts.td - llvm-install-name-tool options --------*-===// -// -// 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 describes the command line options of llvm-install-name. -// -//===----------------------------------------------------------------------===// - -include "llvm/Option/OptParser.td" - -def help : Flag<["--"], "help">; -def h : Flag<["-"], "h">, Alias<help>; - -def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, - HelpText<"Add new rpath">; - +//===-- InstallNameToolOpts.td - llvm-install-name-tool options --------*-===// +// +// 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 describes the command line options of llvm-install-name. +// +//===----------------------------------------------------------------------===// + +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, + HelpText<"Add new rpath">; + def prepend_rpath : Option<["-", "--"], "prepend_rpath", KIND_SEPARATE>, HelpText<"Add new rpath before other rpaths">; -def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>, - HelpText<"Delete specified rpath">; - +def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>, + HelpText<"Delete specified rpath">; + def delete_all_rpaths: Flag<["-", "--"], "delete_all_rpaths">, HelpText<"Delete all rpath directives">; -def rpath: MultiArg<["-", "--"], "rpath", 2>, - HelpText<"Change rpath path name">; - -def id : Option<["-","--"], "id", KIND_SEPARATE>, - HelpText<"Change dynamic shared library id">; - -def change: MultiArg<["-", "--"], "change", 2>, - HelpText<"Change dependent shared library install name">; - -def version : Flag<["--"], "version">, - HelpText<"Print the version and exit.">; +def rpath: MultiArg<["-", "--"], "rpath", 2>, + HelpText<"Change rpath path name">; + +def id : Option<["-","--"], "id", KIND_SEPARATE>, + HelpText<"Change dynamic shared library id">; + +def change: MultiArg<["-", "--"], "change", 2>, + HelpText<"Change dependent shared library install name">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit.">; def V : Flag<["-"], "V">, Alias<version>, diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp index 8e2bf36238..0688e79297 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -1,20 +1,20 @@ -//===- MachOLayoutBuilder.cpp -----------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "MachOLayoutBuilder.h" -#include "llvm/Support/Alignment.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/ErrorHandling.h" - -namespace llvm { -namespace objcopy { -namespace macho { - +//===- MachOLayoutBuilder.cpp -----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "MachOLayoutBuilder.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { +namespace objcopy { +namespace macho { + StringTableBuilder::Kind MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) { if (O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) @@ -23,376 +23,376 @@ MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) { : StringTableBuilder::MachOLinked; } -uint32_t MachOLayoutBuilder::computeSizeOfCmds() const { - uint32_t Size = 0; - for (const LoadCommand &LC : O.LoadCommands) { - const MachO::macho_load_command &MLC = LC.MachOLoadCommand; - auto cmd = MLC.load_command_data.cmd; - switch (cmd) { - case MachO::LC_SEGMENT: - Size += sizeof(MachO::segment_command) + - sizeof(MachO::section) * LC.Sections.size(); - continue; - case MachO::LC_SEGMENT_64: - Size += sizeof(MachO::segment_command_64) + - sizeof(MachO::section_64) * LC.Sections.size(); - continue; - } - - switch (cmd) { -#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ - case MachO::LCName: \ - Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \ - break; -#include "llvm/BinaryFormat/MachO.def" -#undef HANDLE_LOAD_COMMAND - } - } - - return Size; -} - -void MachOLayoutBuilder::constructStringTable() { - for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols) - StrTableBuilder.add(Sym->Name); - StrTableBuilder.finalize(); -} - -void MachOLayoutBuilder::updateSymbolIndexes() { - uint32_t Index = 0; - for (auto &Symbol : O.SymTable.Symbols) - Symbol->Index = Index++; -} - -// Updates the index and the number of local/external/undefined symbols. -void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) { - assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB); - // Make sure that nlist entries in the symbol table are sorted by the those - // types. The order is: local < defined external < undefined external. - assert(llvm::is_sorted(O.SymTable.Symbols, - [](const std::unique_ptr<SymbolEntry> &A, - const std::unique_ptr<SymbolEntry> &B) { - bool AL = A->isLocalSymbol(), - BL = B->isLocalSymbol(); - if (AL != BL) - return AL; - return !AL && !A->isUndefinedSymbol() && - B->isUndefinedSymbol(); - }) && - "Symbols are not sorted by their types."); - - uint32_t NumLocalSymbols = 0; - auto Iter = O.SymTable.Symbols.begin(); - auto End = O.SymTable.Symbols.end(); - for (; Iter != End; ++Iter) { - if ((*Iter)->isExternalSymbol()) - break; - - ++NumLocalSymbols; - } - - uint32_t NumExtDefSymbols = 0; - for (; Iter != End; ++Iter) { - if ((*Iter)->isUndefinedSymbol()) - break; - - ++NumExtDefSymbols; - } - - MLC.dysymtab_command_data.ilocalsym = 0; - MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols; - MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols; - MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols; - MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols; - MLC.dysymtab_command_data.nundefsym = - O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols); -} - -// Recomputes and updates offset and size fields in load commands and sections -// since they could be modified. -uint64_t MachOLayoutBuilder::layoutSegments() { - auto HeaderSize = - Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - const bool IsObjectFile = - O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; - uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; - for (LoadCommand &LC : O.LoadCommands) { - auto &MLC = LC.MachOLoadCommand; - StringRef Segname; - uint64_t SegmentVmAddr; - uint64_t SegmentVmSize; - switch (MLC.load_command_data.cmd) { - case MachO::LC_SEGMENT: - SegmentVmAddr = MLC.segment_command_data.vmaddr; - SegmentVmSize = MLC.segment_command_data.vmsize; - Segname = StringRef(MLC.segment_command_data.segname, - strnlen(MLC.segment_command_data.segname, - sizeof(MLC.segment_command_data.segname))); - break; - case MachO::LC_SEGMENT_64: - SegmentVmAddr = MLC.segment_command_64_data.vmaddr; - SegmentVmSize = MLC.segment_command_64_data.vmsize; - Segname = StringRef(MLC.segment_command_64_data.segname, - strnlen(MLC.segment_command_64_data.segname, - sizeof(MLC.segment_command_64_data.segname))); - break; - default: - continue; - } - - if (Segname == "__LINKEDIT") { - // We update the __LINKEDIT segment later (in layoutTail). - assert(LC.Sections.empty() && "__LINKEDIT segment has sections"); - LinkEditLoadCommand = &MLC; - continue; - } - - // Update file offsets and sizes of sections. - uint64_t SegOffset = Offset; - uint64_t SegFileSize = 0; - uint64_t VMSize = 0; - for (std::unique_ptr<Section> &Sec : LC.Sections) { - assert(SegmentVmAddr <= Sec->Addr && - "Section's address cannot be smaller than Segment's one"); - uint32_t SectOffset = Sec->Addr - SegmentVmAddr; - if (IsObjectFile) { +uint32_t MachOLayoutBuilder::computeSizeOfCmds() const { + uint32_t Size = 0; + for (const LoadCommand &LC : O.LoadCommands) { + const MachO::macho_load_command &MLC = LC.MachOLoadCommand; + auto cmd = MLC.load_command_data.cmd; + switch (cmd) { + case MachO::LC_SEGMENT: + Size += sizeof(MachO::segment_command) + + sizeof(MachO::section) * LC.Sections.size(); + continue; + case MachO::LC_SEGMENT_64: + Size += sizeof(MachO::segment_command_64) + + sizeof(MachO::section_64) * LC.Sections.size(); + continue; + } + + switch (cmd) { +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \ + break; +#include "llvm/BinaryFormat/MachO.def" +#undef HANDLE_LOAD_COMMAND + } + } + + return Size; +} + +void MachOLayoutBuilder::constructStringTable() { + for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols) + StrTableBuilder.add(Sym->Name); + StrTableBuilder.finalize(); +} + +void MachOLayoutBuilder::updateSymbolIndexes() { + uint32_t Index = 0; + for (auto &Symbol : O.SymTable.Symbols) + Symbol->Index = Index++; +} + +// Updates the index and the number of local/external/undefined symbols. +void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) { + assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB); + // Make sure that nlist entries in the symbol table are sorted by the those + // types. The order is: local < defined external < undefined external. + assert(llvm::is_sorted(O.SymTable.Symbols, + [](const std::unique_ptr<SymbolEntry> &A, + const std::unique_ptr<SymbolEntry> &B) { + bool AL = A->isLocalSymbol(), + BL = B->isLocalSymbol(); + if (AL != BL) + return AL; + return !AL && !A->isUndefinedSymbol() && + B->isUndefinedSymbol(); + }) && + "Symbols are not sorted by their types."); + + uint32_t NumLocalSymbols = 0; + auto Iter = O.SymTable.Symbols.begin(); + auto End = O.SymTable.Symbols.end(); + for (; Iter != End; ++Iter) { + if ((*Iter)->isExternalSymbol()) + break; + + ++NumLocalSymbols; + } + + uint32_t NumExtDefSymbols = 0; + for (; Iter != End; ++Iter) { + if ((*Iter)->isUndefinedSymbol()) + break; + + ++NumExtDefSymbols; + } + + MLC.dysymtab_command_data.ilocalsym = 0; + MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols; + MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols; + MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols; + MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols; + MLC.dysymtab_command_data.nundefsym = + O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols); +} + +// Recomputes and updates offset and size fields in load commands and sections +// since they could be modified. +uint64_t MachOLayoutBuilder::layoutSegments() { + auto HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + const bool IsObjectFile = + O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; + uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; + for (LoadCommand &LC : O.LoadCommands) { + auto &MLC = LC.MachOLoadCommand; + StringRef Segname; + uint64_t SegmentVmAddr; + uint64_t SegmentVmSize; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + SegmentVmAddr = MLC.segment_command_data.vmaddr; + SegmentVmSize = MLC.segment_command_data.vmsize; + Segname = StringRef(MLC.segment_command_data.segname, + strnlen(MLC.segment_command_data.segname, + sizeof(MLC.segment_command_data.segname))); + break; + case MachO::LC_SEGMENT_64: + SegmentVmAddr = MLC.segment_command_64_data.vmaddr; + SegmentVmSize = MLC.segment_command_64_data.vmsize; + Segname = StringRef(MLC.segment_command_64_data.segname, + strnlen(MLC.segment_command_64_data.segname, + sizeof(MLC.segment_command_64_data.segname))); + break; + default: + continue; + } + + if (Segname == "__LINKEDIT") { + // We update the __LINKEDIT segment later (in layoutTail). + assert(LC.Sections.empty() && "__LINKEDIT segment has sections"); + LinkEditLoadCommand = &MLC; + continue; + } + + // Update file offsets and sizes of sections. + uint64_t SegOffset = Offset; + uint64_t SegFileSize = 0; + uint64_t VMSize = 0; + for (std::unique_ptr<Section> &Sec : LC.Sections) { + assert(SegmentVmAddr <= Sec->Addr && + "Section's address cannot be smaller than Segment's one"); + uint32_t SectOffset = Sec->Addr - SegmentVmAddr; + if (IsObjectFile) { if (!Sec->hasValidOffset()) { - Sec->Offset = 0; - } else { - uint64_t PaddingSize = - offsetToAlignment(SegFileSize, Align(1ull << Sec->Align)); - Sec->Offset = SegOffset + SegFileSize + PaddingSize; - Sec->Size = Sec->Content.size(); - SegFileSize += PaddingSize + Sec->Size; - } - } else { + Sec->Offset = 0; + } else { + uint64_t PaddingSize = + offsetToAlignment(SegFileSize, Align(1ull << Sec->Align)); + Sec->Offset = SegOffset + SegFileSize + PaddingSize; + Sec->Size = Sec->Content.size(); + SegFileSize += PaddingSize + Sec->Size; + } + } else { if (!Sec->hasValidOffset()) { - Sec->Offset = 0; - } else { - Sec->Offset = SegOffset + SectOffset; - Sec->Size = Sec->Content.size(); - SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size); - } - } - VMSize = std::max(VMSize, SectOffset + Sec->Size); - } - - if (IsObjectFile) { - Offset += SegFileSize; - } else { - Offset = alignTo(Offset + SegFileSize, PageSize); - SegFileSize = alignTo(SegFileSize, PageSize); - // Use the original vmsize if the segment is __PAGEZERO. - VMSize = - Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize); - } - - switch (MLC.load_command_data.cmd) { - case MachO::LC_SEGMENT: - MLC.segment_command_data.cmdsize = - sizeof(MachO::segment_command) + - sizeof(MachO::section) * LC.Sections.size(); - MLC.segment_command_data.nsects = LC.Sections.size(); - MLC.segment_command_data.fileoff = SegOffset; - MLC.segment_command_data.vmsize = VMSize; - MLC.segment_command_data.filesize = SegFileSize; - break; - case MachO::LC_SEGMENT_64: - MLC.segment_command_64_data.cmdsize = - sizeof(MachO::segment_command_64) + - sizeof(MachO::section_64) * LC.Sections.size(); - MLC.segment_command_64_data.nsects = LC.Sections.size(); - MLC.segment_command_64_data.fileoff = SegOffset; - MLC.segment_command_64_data.vmsize = VMSize; - MLC.segment_command_64_data.filesize = SegFileSize; - break; - } - } - - return Offset; -} - -uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) { - for (LoadCommand &LC : O.LoadCommands) - for (std::unique_ptr<Section> &Sec : LC.Sections) { - Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset; - Sec->NReloc = Sec->Relocations.size(); - Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc; - } - - return Offset; -} - -Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { - // If we are building the layout of an executable or dynamic library - // which does not have any segments other than __LINKEDIT, - // the Offset can be equal to zero by this time. It happens because of the - // convention that in such cases the file offsets specified by LC_SEGMENT - // start with zero (unlike the case of a relocatable object file). - const uint64_t HeaderSize = - Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) || - Offset >= HeaderSize + O.Header.SizeOfCmds) && - "Incorrect tail offset"); - Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds); - - // The order of LINKEDIT elements is as follows: - // rebase info, binding info, weak binding info, lazy binding info, export - // trie, data-in-code, symbol table, indirect symbol table, symbol table - // strings, code signature. - uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); - uint64_t StartOfLinkEdit = Offset; - uint64_t StartOfRebaseInfo = StartOfLinkEdit; - uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size(); - uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size(); - uint64_t StartOfLazyBindingInfo = - StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size(); - uint64_t StartOfExportTrie = - StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size(); - uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size(); - uint64_t StartOfDataInCode = - StartOfFunctionStarts + O.FunctionStarts.Data.size(); - uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size(); - uint64_t StartOfIndirectSymbols = - StartOfSymbols + NListSize * O.SymTable.Symbols.size(); - uint64_t StartOfSymbolStrings = - StartOfIndirectSymbols + - sizeof(uint32_t) * O.IndirectSymTable.Symbols.size(); - uint64_t StartOfCodeSignature = - StartOfSymbolStrings + StrTableBuilder.getSize(); + Sec->Offset = 0; + } else { + Sec->Offset = SegOffset + SectOffset; + Sec->Size = Sec->Content.size(); + SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size); + } + } + VMSize = std::max(VMSize, SectOffset + Sec->Size); + } + + if (IsObjectFile) { + Offset += SegFileSize; + } else { + Offset = alignTo(Offset + SegFileSize, PageSize); + SegFileSize = alignTo(SegFileSize, PageSize); + // Use the original vmsize if the segment is __PAGEZERO. + VMSize = + Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize); + } + + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + MLC.segment_command_data.cmdsize = + sizeof(MachO::segment_command) + + sizeof(MachO::section) * LC.Sections.size(); + MLC.segment_command_data.nsects = LC.Sections.size(); + MLC.segment_command_data.fileoff = SegOffset; + MLC.segment_command_data.vmsize = VMSize; + MLC.segment_command_data.filesize = SegFileSize; + break; + case MachO::LC_SEGMENT_64: + MLC.segment_command_64_data.cmdsize = + sizeof(MachO::segment_command_64) + + sizeof(MachO::section_64) * LC.Sections.size(); + MLC.segment_command_64_data.nsects = LC.Sections.size(); + MLC.segment_command_64_data.fileoff = SegOffset; + MLC.segment_command_64_data.vmsize = VMSize; + MLC.segment_command_64_data.filesize = SegFileSize; + break; + } + } + + return Offset; +} + +uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) { + for (LoadCommand &LC : O.LoadCommands) + for (std::unique_ptr<Section> &Sec : LC.Sections) { + Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset; + Sec->NReloc = Sec->Relocations.size(); + Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc; + } + + return Offset; +} + +Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { + // If we are building the layout of an executable or dynamic library + // which does not have any segments other than __LINKEDIT, + // the Offset can be equal to zero by this time. It happens because of the + // convention that in such cases the file offsets specified by LC_SEGMENT + // start with zero (unlike the case of a relocatable object file). + const uint64_t HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) || + Offset >= HeaderSize + O.Header.SizeOfCmds) && + "Incorrect tail offset"); + Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds); + + // The order of LINKEDIT elements is as follows: + // rebase info, binding info, weak binding info, lazy binding info, export + // trie, data-in-code, symbol table, indirect symbol table, symbol table + // strings, code signature. + uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); + uint64_t StartOfLinkEdit = Offset; + uint64_t StartOfRebaseInfo = StartOfLinkEdit; + uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size(); + uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size(); + uint64_t StartOfLazyBindingInfo = + StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size(); + uint64_t StartOfExportTrie = + StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size(); + uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size(); + uint64_t StartOfDataInCode = + StartOfFunctionStarts + O.FunctionStarts.Data.size(); + uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size(); + uint64_t StartOfIndirectSymbols = + StartOfSymbols + NListSize * O.SymTable.Symbols.size(); + uint64_t StartOfSymbolStrings = + StartOfIndirectSymbols + + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size(); + uint64_t StartOfCodeSignature = + StartOfSymbolStrings + StrTableBuilder.getSize(); if (O.CodeSignatureCommandIndex) StartOfCodeSignature = alignTo(StartOfCodeSignature, 16); - uint64_t LinkEditSize = - (StartOfCodeSignature + O.CodeSignature.Data.size()) - StartOfLinkEdit; - - // Now we have determined the layout of the contents of the __LINKEDIT - // segment. Update its load command. - if (LinkEditLoadCommand) { - MachO::macho_load_command *MLC = LinkEditLoadCommand; - switch (LinkEditLoadCommand->load_command_data.cmd) { - case MachO::LC_SEGMENT: - MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command); - MLC->segment_command_data.fileoff = StartOfLinkEdit; - MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize); - MLC->segment_command_data.filesize = LinkEditSize; - break; - case MachO::LC_SEGMENT_64: - MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64); - MLC->segment_command_64_data.fileoff = StartOfLinkEdit; - MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize); - MLC->segment_command_64_data.filesize = LinkEditSize; - break; - } - } - - for (LoadCommand &LC : O.LoadCommands) { - auto &MLC = LC.MachOLoadCommand; - auto cmd = MLC.load_command_data.cmd; - switch (cmd) { - case MachO::LC_CODE_SIGNATURE: - MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature; - MLC.linkedit_data_command_data.datasize = O.CodeSignature.Data.size(); - break; - case MachO::LC_SYMTAB: - MLC.symtab_command_data.symoff = StartOfSymbols; - MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); - MLC.symtab_command_data.stroff = StartOfSymbolStrings; - MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); - break; - case MachO::LC_DYSYMTAB: { - if (MLC.dysymtab_command_data.ntoc != 0 || - MLC.dysymtab_command_data.nmodtab != 0 || - MLC.dysymtab_command_data.nextrefsyms != 0 || - MLC.dysymtab_command_data.nlocrel != 0 || - MLC.dysymtab_command_data.nextrel != 0) - return createStringError(llvm::errc::not_supported, - "shared library is not yet supported"); - - if (!O.IndirectSymTable.Symbols.empty()) { - MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols; - MLC.dysymtab_command_data.nindirectsyms = - O.IndirectSymTable.Symbols.size(); - } - - updateDySymTab(MLC); - break; - } - case MachO::LC_DATA_IN_CODE: - MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; - MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); - break; - case MachO::LC_FUNCTION_STARTS: - MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; - MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); - break; - case MachO::LC_DYLD_INFO: - case MachO::LC_DYLD_INFO_ONLY: - MLC.dyld_info_command_data.rebase_off = - O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo; - MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size(); - MLC.dyld_info_command_data.bind_off = - O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo; - MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size(); - MLC.dyld_info_command_data.weak_bind_off = - O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo; - MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size(); - MLC.dyld_info_command_data.lazy_bind_off = - O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo; - MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size(); - MLC.dyld_info_command_data.export_off = - O.Exports.Trie.empty() ? 0 : StartOfExportTrie; - MLC.dyld_info_command_data.export_size = O.Exports.Trie.size(); - break; - // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in - // <mach-o/loader.h> is not an offset in the binary file, instead, it is a - // relative virtual address. At the moment modification of the __TEXT - // segment of executables isn't supported anyway (e.g. data in code entries - // are not recalculated). Moreover, in general - // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because - // without making additional assumptions (e.g. that the entire __TEXT - // segment should be encrypted) we do not know how to recalculate the - // boundaries of the encrypted part. For now just copy over these load - // commands until we encounter a real world usecase where - // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted. - case MachO::LC_ENCRYPTION_INFO: - case MachO::LC_ENCRYPTION_INFO_64: - case MachO::LC_LOAD_DYLINKER: - case MachO::LC_MAIN: - case MachO::LC_RPATH: - case MachO::LC_SEGMENT: - case MachO::LC_SEGMENT_64: - case MachO::LC_VERSION_MIN_MACOSX: - case MachO::LC_VERSION_MIN_IPHONEOS: - case MachO::LC_VERSION_MIN_TVOS: - case MachO::LC_VERSION_MIN_WATCHOS: - case MachO::LC_BUILD_VERSION: - case MachO::LC_ID_DYLIB: - case MachO::LC_LOAD_DYLIB: - case MachO::LC_LOAD_WEAK_DYLIB: - case MachO::LC_UUID: - case MachO::LC_SOURCE_VERSION: - // Nothing to update. - break; - default: - // Abort if it's unsupported in order to prevent corrupting the object. - return createStringError(llvm::errc::not_supported, - "unsupported load command (cmd=0x%x)", cmd); - } - } - - return Error::success(); -} - -Error MachOLayoutBuilder::layout() { - O.Header.NCmds = O.LoadCommands.size(); - O.Header.SizeOfCmds = computeSizeOfCmds(); - constructStringTable(); - updateSymbolIndexes(); - uint64_t Offset = layoutSegments(); - Offset = layoutRelocations(Offset); - return layoutTail(Offset); -} - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm + uint64_t LinkEditSize = + (StartOfCodeSignature + O.CodeSignature.Data.size()) - StartOfLinkEdit; + + // Now we have determined the layout of the contents of the __LINKEDIT + // segment. Update its load command. + if (LinkEditLoadCommand) { + MachO::macho_load_command *MLC = LinkEditLoadCommand; + switch (LinkEditLoadCommand->load_command_data.cmd) { + case MachO::LC_SEGMENT: + MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command); + MLC->segment_command_data.fileoff = StartOfLinkEdit; + MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize); + MLC->segment_command_data.filesize = LinkEditSize; + break; + case MachO::LC_SEGMENT_64: + MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64); + MLC->segment_command_64_data.fileoff = StartOfLinkEdit; + MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize); + MLC->segment_command_64_data.filesize = LinkEditSize; + break; + } + } + + for (LoadCommand &LC : O.LoadCommands) { + auto &MLC = LC.MachOLoadCommand; + auto cmd = MLC.load_command_data.cmd; + switch (cmd) { + case MachO::LC_CODE_SIGNATURE: + MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature; + MLC.linkedit_data_command_data.datasize = O.CodeSignature.Data.size(); + break; + case MachO::LC_SYMTAB: + MLC.symtab_command_data.symoff = StartOfSymbols; + MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); + MLC.symtab_command_data.stroff = StartOfSymbolStrings; + MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); + break; + case MachO::LC_DYSYMTAB: { + if (MLC.dysymtab_command_data.ntoc != 0 || + MLC.dysymtab_command_data.nmodtab != 0 || + MLC.dysymtab_command_data.nextrefsyms != 0 || + MLC.dysymtab_command_data.nlocrel != 0 || + MLC.dysymtab_command_data.nextrel != 0) + return createStringError(llvm::errc::not_supported, + "shared library is not yet supported"); + + if (!O.IndirectSymTable.Symbols.empty()) { + MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols; + MLC.dysymtab_command_data.nindirectsyms = + O.IndirectSymTable.Symbols.size(); + } + + updateDySymTab(MLC); + break; + } + case MachO::LC_DATA_IN_CODE: + MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; + MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); + break; + case MachO::LC_FUNCTION_STARTS: + MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; + MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + MLC.dyld_info_command_data.rebase_off = + O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo; + MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size(); + MLC.dyld_info_command_data.bind_off = + O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo; + MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size(); + MLC.dyld_info_command_data.weak_bind_off = + O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo; + MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size(); + MLC.dyld_info_command_data.lazy_bind_off = + O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo; + MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size(); + MLC.dyld_info_command_data.export_off = + O.Exports.Trie.empty() ? 0 : StartOfExportTrie; + MLC.dyld_info_command_data.export_size = O.Exports.Trie.size(); + break; + // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in + // <mach-o/loader.h> is not an offset in the binary file, instead, it is a + // relative virtual address. At the moment modification of the __TEXT + // segment of executables isn't supported anyway (e.g. data in code entries + // are not recalculated). Moreover, in general + // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because + // without making additional assumptions (e.g. that the entire __TEXT + // segment should be encrypted) we do not know how to recalculate the + // boundaries of the encrypted part. For now just copy over these load + // commands until we encounter a real world usecase where + // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted. + case MachO::LC_ENCRYPTION_INFO: + case MachO::LC_ENCRYPTION_INFO_64: + case MachO::LC_LOAD_DYLINKER: + case MachO::LC_MAIN: + case MachO::LC_RPATH: + case MachO::LC_SEGMENT: + case MachO::LC_SEGMENT_64: + case MachO::LC_VERSION_MIN_MACOSX: + case MachO::LC_VERSION_MIN_IPHONEOS: + case MachO::LC_VERSION_MIN_TVOS: + case MachO::LC_VERSION_MIN_WATCHOS: + case MachO::LC_BUILD_VERSION: + case MachO::LC_ID_DYLIB: + case MachO::LC_LOAD_DYLIB: + case MachO::LC_LOAD_WEAK_DYLIB: + case MachO::LC_UUID: + case MachO::LC_SOURCE_VERSION: + // Nothing to update. + break; + default: + // Abort if it's unsupported in order to prevent corrupting the object. + return createStringError(llvm::errc::not_supported, + "unsupported load command (cmd=0x%x)", cmd); + } + } + + return Error::success(); +} + +Error MachOLayoutBuilder::layout() { + O.Header.NCmds = O.LoadCommands.size(); + O.Header.SizeOfCmds = computeSizeOfCmds(); + constructStringTable(); + updateSymbolIndexes(); + uint64_t Offset = layoutSegments(); + Offset = layoutRelocations(Offset); + return layoutTail(Offset); +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h index 5fe6683e27..960b1af432 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h @@ -1,54 +1,54 @@ -//===- MachOLayoutBuilder.h -------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H -#define LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H - -#include "MachOObjcopy.h" -#include "Object.h" - -namespace llvm { -namespace objcopy { -namespace macho { - -class MachOLayoutBuilder { - Object &O; - bool Is64Bit; - uint64_t PageSize; - - // Points to the __LINKEDIT segment if it exists. - MachO::macho_load_command *LinkEditLoadCommand = nullptr; +//===- MachOLayoutBuilder.h -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H +#define LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H + +#include "MachOObjcopy.h" +#include "Object.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +class MachOLayoutBuilder { + Object &O; + bool Is64Bit; + uint64_t PageSize; + + // Points to the __LINKEDIT segment if it exists. + MachO::macho_load_command *LinkEditLoadCommand = nullptr; StringTableBuilder StrTableBuilder; - - uint32_t computeSizeOfCmds() const; - void constructStringTable(); - void updateSymbolIndexes(); - void updateDySymTab(MachO::macho_load_command &MLC); - uint64_t layoutSegments(); - uint64_t layoutRelocations(uint64_t Offset); - Error layoutTail(uint64_t Offset); - + + uint32_t computeSizeOfCmds() const; + void constructStringTable(); + void updateSymbolIndexes(); + void updateDySymTab(MachO::macho_load_command &MLC); + uint64_t layoutSegments(); + uint64_t layoutRelocations(uint64_t Offset); + Error layoutTail(uint64_t Offset); + static StringTableBuilder::Kind getStringTableBuilderKind(const Object &O, bool Is64Bit); -public: - MachOLayoutBuilder(Object &O, bool Is64Bit, uint64_t PageSize) +public: + MachOLayoutBuilder(Object &O, bool Is64Bit, uint64_t PageSize) : O(O), Is64Bit(Is64Bit), PageSize(PageSize), StrTableBuilder(getStringTableBuilderKind(O, Is64Bit)) {} - - // Recomputes and updates fields in the given object such as file offsets. - Error layout(); - - StringTableBuilder &getStringTableBuilder() { return StrTableBuilder; } -}; - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H + + // Recomputes and updates fields in the given object such as file offsets. + Error layout(); + + StringTableBuilder &getStringTableBuilder() { return StrTableBuilder; } +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.cpp index fef4a0ae55..42f223b600 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -1,233 +1,233 @@ -//===- MachOObjcopy.cpp -----------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "MachOObjcopy.h" -#include "../CopyConfig.h" +//===- MachOObjcopy.cpp -----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "MachOObjcopy.h" +#include "../CopyConfig.h" #include "../llvm-objcopy.h" -#include "MachOReader.h" -#include "MachOWriter.h" -#include "llvm/ADT/DenseSet.h" +#include "MachOReader.h" +#include "MachOWriter.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/MachOUniversalWriter.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace objcopy { -namespace macho { - -using namespace object; -using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; -using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; - -#ifndef NDEBUG -static bool isLoadCommandWithPayloadString(const LoadCommand &LC) { - // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and - // LC_LAZY_LOAD_DYLIB - return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH || - LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB || - LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB || - LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB; -} -#endif - -static StringRef getPayloadString(const LoadCommand &LC) { - assert(isLoadCommandWithPayloadString(LC) && - "unsupported load command encountered"); - - return StringRef(reinterpret_cast<const char *>(LC.Payload.data()), - LC.Payload.size()) - .rtrim('\0'); -} - -static Error removeSections(const CopyConfig &Config, Object &Obj) { - SectionPred RemovePred = [](const std::unique_ptr<Section> &) { - return false; - }; - - if (!Config.ToRemove.empty()) { - RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) { - return Config.ToRemove.matches(Sec->CanonicalName); - }; - } - - if (Config.StripAll || Config.StripDebug) { - // Remove all debug sections. - RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) { - if (Sec->Segname == "__DWARF") - return true; - - return RemovePred(Sec); - }; - } - - if (!Config.OnlySection.empty()) { - // Overwrite RemovePred because --only-section takes priority. - RemovePred = [&Config](const std::unique_ptr<Section> &Sec) { - return !Config.OnlySection.matches(Sec->CanonicalName); - }; - } - - return Obj.removeSections(RemovePred); -} - -static void markSymbols(const CopyConfig &Config, Object &Obj) { - // Symbols referenced from the indirect symbol table must not be removed. - for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) - if (ISE.Symbol) - (*ISE.Symbol)->Referenced = true; -} - -static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { - for (SymbolEntry &Sym : Obj.SymTable) { - auto I = Config.SymbolsToRename.find(Sym.Name); - if (I != Config.SymbolsToRename.end()) - Sym.Name = std::string(I->getValue()); - } - - auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) { - if (N->Referenced) - return false; - if (Config.StripAll) - return true; - if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) - return true; - // This behavior is consistent with cctools' strip. - if (Config.StripSwiftSymbols && (Obj.Header.Flags & MachO::MH_DYLDLINK) && - Obj.SwiftVersion && *Obj.SwiftVersion && N->isSwiftSymbol()) - return true; - return false; - }; - - Obj.SymTable.removeSymbols(RemovePred); -} - -template <typename LCType> -static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) { - assert(isLoadCommandWithPayloadString(LC) && - "unsupported load command encountered"); - - uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8); - - LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize; - LC.Payload.assign(NewCmdsize - sizeof(LCType), 0); - std::copy(S.begin(), S.end(), LC.Payload.begin()); -} - -static LoadCommand buildRPathLoadCommand(StringRef Path) { - LoadCommand LC; - MachO::rpath_command RPathLC; - RPathLC.cmd = MachO::LC_RPATH; - RPathLC.path = sizeof(MachO::rpath_command); - RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8); - LC.MachOLoadCommand.rpath_command_data = RPathLC; - LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0); - std::copy(Path.begin(), Path.end(), LC.Payload.begin()); - return LC; -} - -static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { - // Remove RPaths. - DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(), - Config.RPathsToRemove.end()); - +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +using namespace object; +using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; +using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; + +#ifndef NDEBUG +static bool isLoadCommandWithPayloadString(const LoadCommand &LC) { + // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and + // LC_LAZY_LOAD_DYLIB + return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH || + LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB || + LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB || + LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB; +} +#endif + +static StringRef getPayloadString(const LoadCommand &LC) { + assert(isLoadCommandWithPayloadString(LC) && + "unsupported load command encountered"); + + return StringRef(reinterpret_cast<const char *>(LC.Payload.data()), + LC.Payload.size()) + .rtrim('\0'); +} + +static Error removeSections(const CopyConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const std::unique_ptr<Section> &) { + return false; + }; + + if (!Config.ToRemove.empty()) { + RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) { + return Config.ToRemove.matches(Sec->CanonicalName); + }; + } + + if (Config.StripAll || Config.StripDebug) { + // Remove all debug sections. + RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) { + if (Sec->Segname == "__DWARF") + return true; + + return RemovePred(Sec); + }; + } + + if (!Config.OnlySection.empty()) { + // Overwrite RemovePred because --only-section takes priority. + RemovePred = [&Config](const std::unique_ptr<Section> &Sec) { + return !Config.OnlySection.matches(Sec->CanonicalName); + }; + } + + return Obj.removeSections(RemovePred); +} + +static void markSymbols(const CopyConfig &Config, Object &Obj) { + // Symbols referenced from the indirect symbol table must not be removed. + for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) + if (ISE.Symbol) + (*ISE.Symbol)->Referenced = true; +} + +static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { + for (SymbolEntry &Sym : Obj.SymTable) { + auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = std::string(I->getValue()); + } + + auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) { + if (N->Referenced) + return false; + if (Config.StripAll) + return true; + if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) + return true; + // This behavior is consistent with cctools' strip. + if (Config.StripSwiftSymbols && (Obj.Header.Flags & MachO::MH_DYLDLINK) && + Obj.SwiftVersion && *Obj.SwiftVersion && N->isSwiftSymbol()) + return true; + return false; + }; + + Obj.SymTable.removeSymbols(RemovePred); +} + +template <typename LCType> +static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) { + assert(isLoadCommandWithPayloadString(LC) && + "unsupported load command encountered"); + + uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8); + + LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize; + LC.Payload.assign(NewCmdsize - sizeof(LCType), 0); + std::copy(S.begin(), S.end(), LC.Payload.begin()); +} + +static LoadCommand buildRPathLoadCommand(StringRef Path) { + LoadCommand LC; + MachO::rpath_command RPathLC; + RPathLC.cmd = MachO::LC_RPATH; + RPathLC.path = sizeof(MachO::rpath_command); + RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8); + LC.MachOLoadCommand.rpath_command_data = RPathLC; + LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0); + std::copy(Path.begin(), Path.end(), LC.Payload.begin()); + return LC; +} + +static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { + // Remove RPaths. + DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(), + Config.RPathsToRemove.end()); + LoadCommandPred RemovePred = [&RPathsToRemove, &Config](const LoadCommand &LC) { - if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) { + if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) { // When removing all RPaths we don't need to care // about what it contains if (Config.RemoveAllRpaths) return true; - StringRef RPath = getPayloadString(LC); - if (RPathsToRemove.count(RPath)) { - RPathsToRemove.erase(RPath); - return true; - } - } - return false; - }; - - if (Error E = Obj.removeLoadCommands(RemovePred)) - return E; - - // Emit an error if the Mach-O binary does not contain an rpath path name - // specified in -delete_rpath. - for (StringRef RPath : Config.RPathsToRemove) { - if (RPathsToRemove.count(RPath)) - return createStringError(errc::invalid_argument, - "no LC_RPATH load command with path: %s", - RPath.str().c_str()); - } - - DenseSet<StringRef> RPaths; - - // Get all existing RPaths. - for (LoadCommand &LC : Obj.LoadCommands) { - if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) - RPaths.insert(getPayloadString(LC)); - } - - // Throw errors for invalid RPaths. - for (const auto &OldNew : Config.RPathsToUpdate) { - StringRef Old = OldNew.getFirst(); - StringRef New = OldNew.getSecond(); + StringRef RPath = getPayloadString(LC); + if (RPathsToRemove.count(RPath)) { + RPathsToRemove.erase(RPath); + return true; + } + } + return false; + }; + + if (Error E = Obj.removeLoadCommands(RemovePred)) + return E; + + // Emit an error if the Mach-O binary does not contain an rpath path name + // specified in -delete_rpath. + for (StringRef RPath : Config.RPathsToRemove) { + if (RPathsToRemove.count(RPath)) + return createStringError(errc::invalid_argument, + "no LC_RPATH load command with path: %s", + RPath.str().c_str()); + } + + DenseSet<StringRef> RPaths; + + // Get all existing RPaths. + for (LoadCommand &LC : Obj.LoadCommands) { + if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) + RPaths.insert(getPayloadString(LC)); + } + + // Throw errors for invalid RPaths. + for (const auto &OldNew : Config.RPathsToUpdate) { + StringRef Old = OldNew.getFirst(); + StringRef New = OldNew.getSecond(); if (!RPaths.contains(Old)) - return createStringError(errc::invalid_argument, - "no LC_RPATH load command with path: " + Old); + return createStringError(errc::invalid_argument, + "no LC_RPATH load command with path: " + Old); if (RPaths.contains(New)) - return createStringError(errc::invalid_argument, + return createStringError(errc::invalid_argument, "rpath '" + New + "' would create a duplicate load command"); - } - - // Update load commands. - for (LoadCommand &LC : Obj.LoadCommands) { - switch (LC.MachOLoadCommand.load_command_data.cmd) { - case MachO::LC_ID_DYLIB: + } + + // Update load commands. + for (LoadCommand &LC : Obj.LoadCommands) { + switch (LC.MachOLoadCommand.load_command_data.cmd) { + case MachO::LC_ID_DYLIB: if (Config.SharedLibId) updateLoadCommandPayloadString<MachO::dylib_command>( LC, *Config.SharedLibId); - break; - - case MachO::LC_RPATH: { - StringRef RPath = getPayloadString(LC); - StringRef NewRPath = Config.RPathsToUpdate.lookup(RPath); - if (!NewRPath.empty()) - updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath); - break; - } - - // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB - // here once llvm-objcopy supports them. - case MachO::LC_LOAD_DYLIB: - case MachO::LC_LOAD_WEAK_DYLIB: - StringRef InstallName = getPayloadString(LC); - StringRef NewInstallName = - Config.InstallNamesToUpdate.lookup(InstallName); - if (!NewInstallName.empty()) - updateLoadCommandPayloadString<MachO::dylib_command>(LC, - NewInstallName); - break; - } - } - - // Add new RPaths. - for (StringRef RPath : Config.RPathToAdd) { + break; + + case MachO::LC_RPATH: { + StringRef RPath = getPayloadString(LC); + StringRef NewRPath = Config.RPathsToUpdate.lookup(RPath); + if (!NewRPath.empty()) + updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath); + break; + } + + // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB + // here once llvm-objcopy supports them. + case MachO::LC_LOAD_DYLIB: + case MachO::LC_LOAD_WEAK_DYLIB: + StringRef InstallName = getPayloadString(LC); + StringRef NewInstallName = + Config.InstallNamesToUpdate.lookup(InstallName); + if (!NewInstallName.empty()) + updateLoadCommandPayloadString<MachO::dylib_command>(LC, + NewInstallName); + break; + } + } + + // Add new RPaths. + for (StringRef RPath : Config.RPathToAdd) { if (RPaths.contains(RPath)) - return createStringError(errc::invalid_argument, + return createStringError(errc::invalid_argument, "rpath '" + RPath + "' would create a duplicate load command"); - RPaths.insert(RPath); + RPaths.insert(RPath); Obj.LoadCommands.push_back(buildRPathLoadCommand(RPath)); - } - + } + for (StringRef RPath : Config.RPathToPrepend) { if (RPaths.contains(RPath)) return createStringError(errc::invalid_argument, @@ -244,176 +244,176 @@ static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { if (!Config.RPathToPrepend.empty()) Obj.updateLoadCommandIndexes(); - return Error::success(); -} - -static Error dumpSectionToFile(StringRef SecName, StringRef Filename, - Object &Obj) { - for (LoadCommand &LC : Obj.LoadCommands) - for (const std::unique_ptr<Section> &Sec : LC.Sections) { - if (Sec->CanonicalName == SecName) { - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(Filename, Sec->Content.size()); - if (!BufferOrErr) - return BufferOrErr.takeError(); - std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); - llvm::copy(Sec->Content, Buf->getBufferStart()); - - if (Error E = Buf->commit()) - return E; - return Error::success(); - } - } - - return createStringError(object_error::parse_failed, "section '%s' not found", - SecName.str().c_str()); -} - -static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFile(Filename); - if (!BufOrErr) - return createFileError(Filename, errorCodeToError(BufOrErr.getError())); - std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); - - std::pair<StringRef, StringRef> Pair = SecName.split(','); - StringRef TargetSegName = Pair.first; - Section Sec(TargetSegName, Pair.second); - Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer()); + return Error::success(); +} + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (LoadCommand &LC : Obj.LoadCommands) + for (const std::unique_ptr<Section> &Sec : LC.Sections) { + if (Sec->CanonicalName == SecName) { + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Sec->Content.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + llvm::copy(Sec->Content, Buf->getBufferStart()); + + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + + return createStringError(object_error::parse_failed, "section '%s' not found", + SecName.str().c_str()); +} + +static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(Filename); + if (!BufOrErr) + return createFileError(Filename, errorCodeToError(BufOrErr.getError())); + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + + std::pair<StringRef, StringRef> Pair = SecName.split(','); + StringRef TargetSegName = Pair.first; + Section Sec(TargetSegName, Pair.second); + Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer()); Sec.Size = Sec.Content.size(); - - // Add the a section into an existing segment. - for (LoadCommand &LC : Obj.LoadCommands) { - Optional<StringRef> SegName = LC.getSegmentName(); - if (SegName && SegName == TargetSegName) { + + // Add the a section into an existing segment. + for (LoadCommand &LC : Obj.LoadCommands) { + Optional<StringRef> SegName = LC.getSegmentName(); + if (SegName && SegName == TargetSegName) { uint64_t Addr = *LC.getSegmentVMAddr(); for (const std::unique_ptr<Section> &S : LC.Sections) Addr = std::max(Addr, S->Addr + S->Size); - LC.Sections.push_back(std::make_unique<Section>(Sec)); + LC.Sections.push_back(std::make_unique<Section>(Sec)); LC.Sections.back()->Addr = Addr; - return Error::success(); - } - } - - // There's no segment named TargetSegName. Create a new load command and - // Insert a new section into it. + return Error::success(); + } + } + + // There's no segment named TargetSegName. Create a new load command and + // Insert a new section into it. LoadCommand &NewSegment = Obj.addSegment(TargetSegName, alignTo(Sec.Size, 16384)); - NewSegment.Sections.push_back(std::make_unique<Section>(Sec)); + NewSegment.Sections.push_back(std::make_unique<Section>(Sec)); NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr(); - return Error::success(); -} - -// isValidMachOCannonicalName returns success if Name is a MachO cannonical name -// ("<segment>,<section>") and lengths of both segment and section names are -// valid. + return Error::success(); +} + +// isValidMachOCannonicalName returns success if Name is a MachO cannonical name +// ("<segment>,<section>") and lengths of both segment and section names are +// valid. static Error isValidMachOCannonicalName(StringRef Name) { - if (Name.count(',') != 1) - return createStringError(errc::invalid_argument, - "invalid section name '%s' (should be formatted " - "as '<segment name>,<section name>')", - Name.str().c_str()); - - std::pair<StringRef, StringRef> Pair = Name.split(','); - if (Pair.first.size() > 16) - return createStringError(errc::invalid_argument, - "too long segment name: '%s'", - Pair.first.str().c_str()); - if (Pair.second.size() > 16) - return createStringError(errc::invalid_argument, - "too long section name: '%s'", - Pair.second.str().c_str()); - return Error::success(); -} - -static Error handleArgs(const CopyConfig &Config, Object &Obj) { - if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() || - Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || - !Config.UnneededSymbolsToRemove.empty() || - !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || - Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates || - Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + if (Name.count(',') != 1) + return createStringError(errc::invalid_argument, + "invalid section name '%s' (should be formatted " + "as '<segment name>,<section name>')", + Name.str().c_str()); + + std::pair<StringRef, StringRef> Pair = Name.split(','); + if (Pair.first.size() > 16) + return createStringError(errc::invalid_argument, + "too long segment name: '%s'", + Pair.first.str().c_str()); + if (Pair.second.size() > 16) + return createStringError(errc::invalid_argument, + "too long section name: '%s'", + Pair.second.str().c_str()); + return Error::success(); +} + +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || + Config.BuildIdLinkInput || Config.BuildIdLinkOutput || + !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || + !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() || + Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || + !Config.UnneededSymbolsToRemove.empty() || + !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || + Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates || + Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || + Config.StripSections || Config.Weaken || Config.DecompressDebugSections || Config.StripUnneeded || Config.DiscardMode == DiscardType::Locals || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for MachO"); - } - - // Dump sections before add/remove for compatibility with GNU objcopy. - for (StringRef Flag : Config.DumpSection) { - StringRef SectionName; - StringRef FileName; - std::tie(SectionName, FileName) = Flag.split('='); - if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) - return E; - } - - if (Error E = removeSections(Config, Obj)) - return E; - - // Mark symbols to determine which symbols are still needed. - if (Config.StripAll) - markSymbols(Config, Obj); - - updateAndRemoveSymbols(Config, Obj); - - if (Config.StripAll) - for (LoadCommand &LC : Obj.LoadCommands) - for (std::unique_ptr<Section> &Sec : LC.Sections) - Sec->Relocations.clear(); - - for (const auto &Flag : Config.AddSection) { - std::pair<StringRef, StringRef> SecPair = Flag.split("="); - StringRef SecName = SecPair.first; - StringRef File = SecPair.second; - if (Error E = isValidMachOCannonicalName(SecName)) - return E; - if (Error E = addSection(SecName, File, Obj)) - return E; - } - - if (Error E = processLoadCommands(Config, Obj)) - return E; - - return Error::success(); -} - -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out) { - MachOReader Reader(In); + !Config.SymbolsToAdd.empty() || Config.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for MachO"); + } + + // Dump sections before add/remove for compatibility with GNU objcopy. + for (StringRef Flag : Config.DumpSection) { + StringRef SectionName; + StringRef FileName; + std::tie(SectionName, FileName) = Flag.split('='); + if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) + return E; + } + + if (Error E = removeSections(Config, Obj)) + return E; + + // Mark symbols to determine which symbols are still needed. + if (Config.StripAll) + markSymbols(Config, Obj); + + updateAndRemoveSymbols(Config, Obj); + + if (Config.StripAll) + for (LoadCommand &LC : Obj.LoadCommands) + for (std::unique_ptr<Section> &Sec : LC.Sections) + Sec->Relocations.clear(); + + for (const auto &Flag : Config.AddSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = isValidMachOCannonicalName(SecName)) + return E; + if (Error E = addSection(SecName, File, Obj)) + return E; + } + + if (Error E = processLoadCommands(Config, Obj)) + return E; + + return Error::success(); +} + +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::MachOObjectFile &In, Buffer &Out) { + MachOReader Reader(In); Expected<std::unique_ptr<Object>> O = Reader.create(); - if (!O) + if (!O) return createFileError(Config.InputFilename, O.takeError()); - + if (Error E = handleArgs(Config, **O)) - return createFileError(Config.InputFilename, std::move(E)); - - // Page size used for alignment of segment sizes in Mach-O executables and - // dynamic libraries. - uint64_t PageSize; - switch (In.getArch()) { - case Triple::ArchType::arm: - case Triple::ArchType::aarch64: - case Triple::ArchType::aarch64_32: - PageSize = 16384; - break; - default: - PageSize = 4096; - } - + return createFileError(Config.InputFilename, std::move(E)); + + // Page size used for alignment of segment sizes in Mach-O executables and + // dynamic libraries. + uint64_t PageSize; + switch (In.getArch()) { + case Triple::ArchType::arm: + case Triple::ArchType::aarch64: + case Triple::ArchType::aarch64_32: + PageSize = 16384; + break; + default: + PageSize = 4096; + } + MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), PageSize, Out); - if (auto E = Writer.finalize()) - return E; - return Writer.write(); -} - + if (auto E = Writer.finalize()) + return E; + return Writer.write(); +} + Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, const MachOUniversalBinary &In, Buffer &Out) { @@ -483,6 +483,6 @@ Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, return Out.commit(); } -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.h b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.h index c3f5391f79..ae96b8329d 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOObjcopy.h @@ -1,35 +1,35 @@ -//===- MachOObjcopy.h -------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H -#define LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H - -namespace llvm { -class Error; - -namespace object { -class MachOObjectFile; -class MachOUniversalBinary; -} // end namespace object - -namespace objcopy { -struct CopyConfig; -class Buffer; - -namespace macho { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out); +//===- MachOObjcopy.h -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H + +namespace llvm { +class Error; + +namespace object { +class MachOObjectFile; +class MachOUniversalBinary; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace macho { +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::MachOObjectFile &In, Buffer &Out); Error executeObjcopyOnMachOUniversalBinary( CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out); -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.cpp index 548a85bd49..85e738667c 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -1,341 +1,341 @@ -//===- MachOReader.cpp ------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "MachOReader.h" -#include "Object.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Object/MachO.h" +//===- MachOReader.cpp ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "MachOReader.h" +#include "Object.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/Errc.h" -#include <memory> - -namespace llvm { -namespace objcopy { -namespace macho { - -void MachOReader::readHeader(Object &O) const { - O.Header.Magic = MachOObj.getHeader().magic; - O.Header.CPUType = MachOObj.getHeader().cputype; - O.Header.CPUSubType = MachOObj.getHeader().cpusubtype; - O.Header.FileType = MachOObj.getHeader().filetype; - O.Header.NCmds = MachOObj.getHeader().ncmds; - O.Header.SizeOfCmds = MachOObj.getHeader().sizeofcmds; - O.Header.Flags = MachOObj.getHeader().flags; -} - -template <typename SectionType> -Section constructSectionCommon(SectionType Sec, uint32_t Index) { - StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname))); - StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); - Section S(SegName, SectName); - S.Index = Index; - S.Addr = Sec.addr; - S.Size = Sec.size; +#include <memory> + +namespace llvm { +namespace objcopy { +namespace macho { + +void MachOReader::readHeader(Object &O) const { + O.Header.Magic = MachOObj.getHeader().magic; + O.Header.CPUType = MachOObj.getHeader().cputype; + O.Header.CPUSubType = MachOObj.getHeader().cpusubtype; + O.Header.FileType = MachOObj.getHeader().filetype; + O.Header.NCmds = MachOObj.getHeader().ncmds; + O.Header.SizeOfCmds = MachOObj.getHeader().sizeofcmds; + O.Header.Flags = MachOObj.getHeader().flags; +} + +template <typename SectionType> +Section constructSectionCommon(SectionType Sec, uint32_t Index) { + StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname))); + StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); + Section S(SegName, SectName); + S.Index = Index; + S.Addr = Sec.addr; + S.Size = Sec.size; S.OriginalOffset = Sec.offset; - S.Align = Sec.align; - S.RelOff = Sec.reloff; - S.NReloc = Sec.nreloc; - S.Flags = Sec.flags; - S.Reserved1 = Sec.reserved1; - S.Reserved2 = Sec.reserved2; - S.Reserved3 = 0; - return S; -} - -template <typename SectionType> -Section constructSection(SectionType Sec, uint32_t Index); - -template <> Section constructSection(MachO::section Sec, uint32_t Index) { - return constructSectionCommon(Sec, Index); -} - -template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) { - Section S = constructSectionCommon(Sec, Index); - S.Reserved3 = Sec.reserved3; - return S; -} - -template <typename SectionType, typename SegmentType> + S.Align = Sec.align; + S.RelOff = Sec.reloff; + S.NReloc = Sec.nreloc; + S.Flags = Sec.flags; + S.Reserved1 = Sec.reserved1; + S.Reserved2 = Sec.reserved2; + S.Reserved3 = 0; + return S; +} + +template <typename SectionType> +Section constructSection(SectionType Sec, uint32_t Index); + +template <> Section constructSection(MachO::section Sec, uint32_t Index) { + return constructSectionCommon(Sec, Index); +} + +template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) { + Section S = constructSectionCommon(Sec, Index); + S.Reserved3 = Sec.reserved3; + return S; +} + +template <typename SectionType, typename SegmentType> Expected<std::vector<std::unique_ptr<Section>>> -extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, - const object::MachOObjectFile &MachOObj, - uint32_t &NextSectionIndex) { - auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; - const SectionType *Curr = - reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); - std::vector<std::unique_ptr<Section>> Sections; - for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { - if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { - SectionType Sec; - memcpy((void *)&Sec, Curr, sizeof(SectionType)); - MachO::swapStruct(Sec); - Sections.push_back( - std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); - } else { - Sections.push_back( - std::make_unique<Section>(constructSection(*Curr, NextSectionIndex))); - } - - Section &S = *Sections.back(); - - Expected<object::SectionRef> SecRef = - MachOObj.getSection(NextSectionIndex++); - if (!SecRef) +extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, + const object::MachOObjectFile &MachOObj, + uint32_t &NextSectionIndex) { + auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; + const SectionType *Curr = + reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); + std::vector<std::unique_ptr<Section>> Sections; + for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { + SectionType Sec; + memcpy((void *)&Sec, Curr, sizeof(SectionType)); + MachO::swapStruct(Sec); + Sections.push_back( + std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); + } else { + Sections.push_back( + std::make_unique<Section>(constructSection(*Curr, NextSectionIndex))); + } + + Section &S = *Sections.back(); + + Expected<object::SectionRef> SecRef = + MachOObj.getSection(NextSectionIndex++); + if (!SecRef) return SecRef.takeError(); - + Expected<ArrayRef<uint8_t>> Data = MachOObj.getSectionContents(SecRef->getRawDataRefImpl()); if (!Data) return Data.takeError(); - + S.Content = StringRef(reinterpret_cast<const char *>(Data->data()), Data->size()); - S.Relocations.reserve(S.NReloc); - for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), - RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); - RI != RE; ++RI) { - RelocationInfo R; - R.Symbol = nullptr; // We'll fill this field later. - R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); - R.Scattered = MachOObj.isRelocationScattered(R.Info); - R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); - S.Relocations.push_back(R); - } - - assert(S.NReloc == S.Relocations.size() && - "Incorrect number of relocations"); - } + S.Relocations.reserve(S.NReloc); + for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), + RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); + RI != RE; ++RI) { + RelocationInfo R; + R.Symbol = nullptr; // We'll fill this field later. + R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); + R.Scattered = MachOObj.isRelocationScattered(R.Info); + R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); + S.Relocations.push_back(R); + } + + assert(S.NReloc == S.Relocations.size() && + "Incorrect number of relocations"); + } return std::move(Sections); -} - +} + Error MachOReader::readLoadCommands(Object &O) const { - // For MachO sections indices start from 1. - uint32_t NextSectionIndex = 1; - for (auto LoadCmd : MachOObj.load_commands()) { - LoadCommand LC; - switch (LoadCmd.C.cmd) { - case MachO::LC_CODE_SIGNATURE: - O.CodeSignatureCommandIndex = O.LoadCommands.size(); - break; - case MachO::LC_SEGMENT: + // For MachO sections indices start from 1. + uint32_t NextSectionIndex = 1; + for (auto LoadCmd : MachOObj.load_commands()) { + LoadCommand LC; + switch (LoadCmd.C.cmd) { + case MachO::LC_CODE_SIGNATURE: + O.CodeSignatureCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_SEGMENT: if (Expected<std::vector<std::unique_ptr<Section>>> Sections = extractSections<MachO::section, MachO::segment_command>( LoadCmd, MachOObj, NextSectionIndex)) LC.Sections = std::move(*Sections); else return Sections.takeError(); - break; - case MachO::LC_SEGMENT_64: + break; + case MachO::LC_SEGMENT_64: if (Expected<std::vector<std::unique_ptr<Section>>> Sections = extractSections<MachO::section_64, MachO::segment_command_64>( LoadCmd, MachOObj, NextSectionIndex)) LC.Sections = std::move(*Sections); else return Sections.takeError(); - break; - case MachO::LC_SYMTAB: - O.SymTabCommandIndex = O.LoadCommands.size(); - break; - case MachO::LC_DYSYMTAB: - O.DySymTabCommandIndex = O.LoadCommands.size(); - break; - case MachO::LC_DYLD_INFO: - case MachO::LC_DYLD_INFO_ONLY: - O.DyLdInfoCommandIndex = O.LoadCommands.size(); - break; - case MachO::LC_DATA_IN_CODE: - O.DataInCodeCommandIndex = O.LoadCommands.size(); - break; - case MachO::LC_FUNCTION_STARTS: - O.FunctionStartsCommandIndex = O.LoadCommands.size(); - break; - } -#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ - case MachO::LCName: \ - memcpy((void *)&(LC.MachOLoadCommand.LCStruct##_data), LoadCmd.Ptr, \ - sizeof(MachO::LCStruct)); \ - if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \ - MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \ - if (LoadCmd.C.cmdsize > sizeof(MachO::LCStruct)) \ - LC.Payload = ArrayRef<uint8_t>( \ - reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \ - sizeof(MachO::LCStruct), \ - LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \ - break; - - switch (LoadCmd.C.cmd) { - default: - memcpy((void *)&(LC.MachOLoadCommand.load_command_data), LoadCmd.Ptr, - sizeof(MachO::load_command)); - if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) - MachO::swapStruct(LC.MachOLoadCommand.load_command_data); - if (LoadCmd.C.cmdsize > sizeof(MachO::load_command)) - LC.Payload = ArrayRef<uint8_t>( - reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + - sizeof(MachO::load_command), - LoadCmd.C.cmdsize - sizeof(MachO::load_command)); - break; -#include "llvm/BinaryFormat/MachO.def" - } - O.LoadCommands.push_back(std::move(LC)); - } + break; + case MachO::LC_SYMTAB: + O.SymTabCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_DYSYMTAB: + O.DySymTabCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + O.DyLdInfoCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_DATA_IN_CODE: + O.DataInCodeCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_FUNCTION_STARTS: + O.FunctionStartsCommandIndex = O.LoadCommands.size(); + break; + } +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + memcpy((void *)&(LC.MachOLoadCommand.LCStruct##_data), LoadCmd.Ptr, \ + sizeof(MachO::LCStruct)); \ + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \ + MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \ + if (LoadCmd.C.cmdsize > sizeof(MachO::LCStruct)) \ + LC.Payload = ArrayRef<uint8_t>( \ + reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \ + sizeof(MachO::LCStruct), \ + LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \ + break; + + switch (LoadCmd.C.cmd) { + default: + memcpy((void *)&(LC.MachOLoadCommand.load_command_data), LoadCmd.Ptr, + sizeof(MachO::load_command)); + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(LC.MachOLoadCommand.load_command_data); + if (LoadCmd.C.cmdsize > sizeof(MachO::load_command)) + LC.Payload = ArrayRef<uint8_t>( + reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + + sizeof(MachO::load_command), + LoadCmd.C.cmdsize - sizeof(MachO::load_command)); + break; +#include "llvm/BinaryFormat/MachO.def" + } + O.LoadCommands.push_back(std::move(LC)); + } return Error::success(); -} - -template <typename nlist_t> -SymbolEntry constructSymbolEntry(StringRef StrTable, const nlist_t &nlist) { - assert(nlist.n_strx < StrTable.size() && - "n_strx exceeds the size of the string table"); - SymbolEntry SE; - SE.Name = StringRef(StrTable.data() + nlist.n_strx).str(); - SE.n_type = nlist.n_type; - SE.n_sect = nlist.n_sect; - SE.n_desc = nlist.n_desc; - SE.n_value = nlist.n_value; - return SE; -} - -void MachOReader::readSymbolTable(Object &O) const { - StringRef StrTable = MachOObj.getStringTableData(); - for (auto Symbol : MachOObj.symbols()) { - SymbolEntry SE = - (MachOObj.is64Bit() - ? constructSymbolEntry(StrTable, MachOObj.getSymbol64TableEntry( - Symbol.getRawDataRefImpl())) - : constructSymbolEntry(StrTable, MachOObj.getSymbolTableEntry( - Symbol.getRawDataRefImpl()))); - - O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE)); - } -} - -void MachOReader::setSymbolInRelocationInfo(Object &O) const { - std::vector<const Section *> Sections; - for (auto &LC : O.LoadCommands) - for (std::unique_ptr<Section> &Sec : LC.Sections) - Sections.push_back(Sec.get()); - - for (LoadCommand &LC : O.LoadCommands) - for (std::unique_ptr<Section> &Sec : LC.Sections) - for (auto &Reloc : Sec->Relocations) - if (!Reloc.Scattered) { - const uint32_t SymbolNum = - Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian()); - if (Reloc.Extern) { - Reloc.Symbol = O.SymTable.getSymbolByIndex(SymbolNum); - } else { - // FIXME: Refactor error handling in MachOReader and report an error - // if we encounter an invalid relocation. - assert(SymbolNum >= 1 && SymbolNum <= Sections.size() && - "Invalid section index."); - Reloc.Sec = Sections[SymbolNum - 1]; - } - } -} - -void MachOReader::readRebaseInfo(Object &O) const { - O.Rebases.Opcodes = MachOObj.getDyldInfoRebaseOpcodes(); -} - -void MachOReader::readBindInfo(Object &O) const { - O.Binds.Opcodes = MachOObj.getDyldInfoBindOpcodes(); -} - -void MachOReader::readWeakBindInfo(Object &O) const { - O.WeakBinds.Opcodes = MachOObj.getDyldInfoWeakBindOpcodes(); -} - -void MachOReader::readLazyBindInfo(Object &O) const { - O.LazyBinds.Opcodes = MachOObj.getDyldInfoLazyBindOpcodes(); -} - -void MachOReader::readExportInfo(Object &O) const { - O.Exports.Trie = MachOObj.getDyldInfoExportsTrie(); -} - -void MachOReader::readLinkData(Object &O, Optional<size_t> LCIndex, - LinkData &LD) const { - if (!LCIndex) - return; - const MachO::linkedit_data_command &LC = - O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; - LD.Data = - arrayRefFromStringRef(MachOObj.getData().substr(LC.dataoff, LC.datasize)); -} - -void MachOReader::readCodeSignature(Object &O) const { - return readLinkData(O, O.CodeSignatureCommandIndex, O.CodeSignature); -} - -void MachOReader::readDataInCodeData(Object &O) const { - return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode); -} - -void MachOReader::readFunctionStartsData(Object &O) const { - return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts); -} - -void MachOReader::readIndirectSymbolTable(Object &O) const { - MachO::dysymtab_command DySymTab = MachOObj.getDysymtabLoadCommand(); - constexpr uint32_t AbsOrLocalMask = - MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS; - for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i) { - uint32_t Index = MachOObj.getIndirectSymbolTableEntry(DySymTab, i); - if ((Index & AbsOrLocalMask) != 0) - O.IndirectSymTable.Symbols.emplace_back(Index, None); - else - O.IndirectSymTable.Symbols.emplace_back( - Index, O.SymTable.getSymbolByIndex(Index)); - } -} - -void MachOReader::readSwiftVersion(Object &O) const { - struct ObjCImageInfo { - uint32_t Version; - uint32_t Flags; - } ImageInfo; - - for (const LoadCommand &LC : O.LoadCommands) - for (const std::unique_ptr<Section> &Sec : LC.Sections) - if (Sec->Sectname == "__objc_imageinfo" && - (Sec->Segname == "__DATA" || Sec->Segname == "__DATA_CONST" || - Sec->Segname == "__DATA_DIRTY") && - Sec->Content.size() >= sizeof(ObjCImageInfo)) { - memcpy(&ImageInfo, Sec->Content.data(), sizeof(ObjCImageInfo)); - if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { - sys::swapByteOrder(ImageInfo.Version); - sys::swapByteOrder(ImageInfo.Flags); - } - O.SwiftVersion = (ImageInfo.Flags >> 8) & 0xff; - return; - } -} - +} + +template <typename nlist_t> +SymbolEntry constructSymbolEntry(StringRef StrTable, const nlist_t &nlist) { + assert(nlist.n_strx < StrTable.size() && + "n_strx exceeds the size of the string table"); + SymbolEntry SE; + SE.Name = StringRef(StrTable.data() + nlist.n_strx).str(); + SE.n_type = nlist.n_type; + SE.n_sect = nlist.n_sect; + SE.n_desc = nlist.n_desc; + SE.n_value = nlist.n_value; + return SE; +} + +void MachOReader::readSymbolTable(Object &O) const { + StringRef StrTable = MachOObj.getStringTableData(); + for (auto Symbol : MachOObj.symbols()) { + SymbolEntry SE = + (MachOObj.is64Bit() + ? constructSymbolEntry(StrTable, MachOObj.getSymbol64TableEntry( + Symbol.getRawDataRefImpl())) + : constructSymbolEntry(StrTable, MachOObj.getSymbolTableEntry( + Symbol.getRawDataRefImpl()))); + + O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE)); + } +} + +void MachOReader::setSymbolInRelocationInfo(Object &O) const { + std::vector<const Section *> Sections; + for (auto &LC : O.LoadCommands) + for (std::unique_ptr<Section> &Sec : LC.Sections) + Sections.push_back(Sec.get()); + + for (LoadCommand &LC : O.LoadCommands) + for (std::unique_ptr<Section> &Sec : LC.Sections) + for (auto &Reloc : Sec->Relocations) + if (!Reloc.Scattered) { + const uint32_t SymbolNum = + Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian()); + if (Reloc.Extern) { + Reloc.Symbol = O.SymTable.getSymbolByIndex(SymbolNum); + } else { + // FIXME: Refactor error handling in MachOReader and report an error + // if we encounter an invalid relocation. + assert(SymbolNum >= 1 && SymbolNum <= Sections.size() && + "Invalid section index."); + Reloc.Sec = Sections[SymbolNum - 1]; + } + } +} + +void MachOReader::readRebaseInfo(Object &O) const { + O.Rebases.Opcodes = MachOObj.getDyldInfoRebaseOpcodes(); +} + +void MachOReader::readBindInfo(Object &O) const { + O.Binds.Opcodes = MachOObj.getDyldInfoBindOpcodes(); +} + +void MachOReader::readWeakBindInfo(Object &O) const { + O.WeakBinds.Opcodes = MachOObj.getDyldInfoWeakBindOpcodes(); +} + +void MachOReader::readLazyBindInfo(Object &O) const { + O.LazyBinds.Opcodes = MachOObj.getDyldInfoLazyBindOpcodes(); +} + +void MachOReader::readExportInfo(Object &O) const { + O.Exports.Trie = MachOObj.getDyldInfoExportsTrie(); +} + +void MachOReader::readLinkData(Object &O, Optional<size_t> LCIndex, + LinkData &LD) const { + if (!LCIndex) + return; + const MachO::linkedit_data_command &LC = + O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; + LD.Data = + arrayRefFromStringRef(MachOObj.getData().substr(LC.dataoff, LC.datasize)); +} + +void MachOReader::readCodeSignature(Object &O) const { + return readLinkData(O, O.CodeSignatureCommandIndex, O.CodeSignature); +} + +void MachOReader::readDataInCodeData(Object &O) const { + return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode); +} + +void MachOReader::readFunctionStartsData(Object &O) const { + return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts); +} + +void MachOReader::readIndirectSymbolTable(Object &O) const { + MachO::dysymtab_command DySymTab = MachOObj.getDysymtabLoadCommand(); + constexpr uint32_t AbsOrLocalMask = + MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS; + for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i) { + uint32_t Index = MachOObj.getIndirectSymbolTableEntry(DySymTab, i); + if ((Index & AbsOrLocalMask) != 0) + O.IndirectSymTable.Symbols.emplace_back(Index, None); + else + O.IndirectSymTable.Symbols.emplace_back( + Index, O.SymTable.getSymbolByIndex(Index)); + } +} + +void MachOReader::readSwiftVersion(Object &O) const { + struct ObjCImageInfo { + uint32_t Version; + uint32_t Flags; + } ImageInfo; + + for (const LoadCommand &LC : O.LoadCommands) + for (const std::unique_ptr<Section> &Sec : LC.Sections) + if (Sec->Sectname == "__objc_imageinfo" && + (Sec->Segname == "__DATA" || Sec->Segname == "__DATA_CONST" || + Sec->Segname == "__DATA_DIRTY") && + Sec->Content.size() >= sizeof(ObjCImageInfo)) { + memcpy(&ImageInfo, Sec->Content.data(), sizeof(ObjCImageInfo)); + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(ImageInfo.Version); + sys::swapByteOrder(ImageInfo.Flags); + } + O.SwiftVersion = (ImageInfo.Flags >> 8) & 0xff; + return; + } +} + Expected<std::unique_ptr<Object>> MachOReader::create() const { - auto Obj = std::make_unique<Object>(); - readHeader(*Obj); + auto Obj = std::make_unique<Object>(); + readHeader(*Obj); if (Error E = readLoadCommands(*Obj)) return std::move(E); - readSymbolTable(*Obj); - setSymbolInRelocationInfo(*Obj); - readRebaseInfo(*Obj); - readBindInfo(*Obj); - readWeakBindInfo(*Obj); - readLazyBindInfo(*Obj); - readExportInfo(*Obj); - readCodeSignature(*Obj); - readDataInCodeData(*Obj); - readFunctionStartsData(*Obj); - readIndirectSymbolTable(*Obj); - readSwiftVersion(*Obj); + readSymbolTable(*Obj); + setSymbolInRelocationInfo(*Obj); + readRebaseInfo(*Obj); + readBindInfo(*Obj); + readWeakBindInfo(*Obj); + readLazyBindInfo(*Obj); + readExportInfo(*Obj); + readCodeSignature(*Obj); + readDataInCodeData(*Obj); + readFunctionStartsData(*Obj); + readIndirectSymbolTable(*Obj); + readSwiftVersion(*Obj); return std::move(Obj); -} - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.h b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.h index b446e02865..77bad3889d 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOReader.h @@ -1,54 +1,54 @@ -//===- MachOReader.h --------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "MachOObjcopy.h" -#include "Object.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Object/MachO.h" -#include <memory> - -namespace llvm { -namespace objcopy { -namespace macho { - -// The hierarchy of readers is responsible for parsing different inputs: -// raw binaries and regular MachO object files. -class Reader { -public: - virtual ~Reader(){}; +//===- MachOReader.h --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "MachOObjcopy.h" +#include "Object.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" +#include <memory> + +namespace llvm { +namespace objcopy { +namespace macho { + +// The hierarchy of readers is responsible for parsing different inputs: +// raw binaries and regular MachO object files. +class Reader { +public: + virtual ~Reader(){}; virtual Expected<std::unique_ptr<Object>> create() const = 0; -}; - -class MachOReader : public Reader { - const object::MachOObjectFile &MachOObj; - - void readHeader(Object &O) const; +}; + +class MachOReader : public Reader { + const object::MachOObjectFile &MachOObj; + + void readHeader(Object &O) const; Error readLoadCommands(Object &O) const; - void readSymbolTable(Object &O) const; - void setSymbolInRelocationInfo(Object &O) const; - void readRebaseInfo(Object &O) const; - void readBindInfo(Object &O) const; - void readWeakBindInfo(Object &O) const; - void readLazyBindInfo(Object &O) const; - void readExportInfo(Object &O) const; - void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const; - void readCodeSignature(Object &O) const; - void readDataInCodeData(Object &O) const; - void readFunctionStartsData(Object &O) const; - void readIndirectSymbolTable(Object &O) const; - void readSwiftVersion(Object &O) const; - -public: - explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {} - + void readSymbolTable(Object &O) const; + void setSymbolInRelocationInfo(Object &O) const; + void readRebaseInfo(Object &O) const; + void readBindInfo(Object &O) const; + void readWeakBindInfo(Object &O) const; + void readLazyBindInfo(Object &O) const; + void readExportInfo(Object &O) const; + void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const; + void readCodeSignature(Object &O) const; + void readDataInCodeData(Object &O) const; + void readFunctionStartsData(Object &O) const; + void readIndirectSymbolTable(Object &O) const; + void readSwiftVersion(Object &O) const; + +public: + explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {} + Expected<std::unique_ptr<Object>> create() const override; -}; - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.cpp index 56dd08df3b..166c62437d 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -1,126 +1,126 @@ -//===- MachOWriter.cpp ------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "MachOWriter.h" -#include "MachOLayoutBuilder.h" -#include "Object.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Object/MachO.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/ErrorHandling.h" -#include <memory> - -namespace llvm { -namespace objcopy { -namespace macho { - -size_t MachOWriter::headerSize() const { - return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); -} - -size_t MachOWriter::loadCommandsSize() const { return O.Header.SizeOfCmds; } - -size_t MachOWriter::symTableSize() const { - return O.SymTable.Symbols.size() * - (Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist)); -} - -size_t MachOWriter::totalSize() const { - // Going from tail to head and looking for an appropriate "anchor" to - // calculate the total size assuming that all the offsets are either valid - // ("true") or 0 (0 indicates that the corresponding part is missing). - - SmallVector<size_t, 7> Ends; - if (O.SymTabCommandIndex) { - const MachO::symtab_command &SymTabCommand = - O.LoadCommands[*O.SymTabCommandIndex] - .MachOLoadCommand.symtab_command_data; - if (SymTabCommand.symoff) - Ends.push_back(SymTabCommand.symoff + symTableSize()); - if (SymTabCommand.stroff) - Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize); - } - if (O.DyLdInfoCommandIndex) { - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - if (DyLdInfoCommand.rebase_off) { - assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && - "Incorrect rebase opcodes size"); - Ends.push_back(DyLdInfoCommand.rebase_off + DyLdInfoCommand.rebase_size); - } - if (DyLdInfoCommand.bind_off) { - assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && - "Incorrect bind opcodes size"); - Ends.push_back(DyLdInfoCommand.bind_off + DyLdInfoCommand.bind_size); - } - if (DyLdInfoCommand.weak_bind_off) { - assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && - "Incorrect weak bind opcodes size"); - Ends.push_back(DyLdInfoCommand.weak_bind_off + - DyLdInfoCommand.weak_bind_size); - } - if (DyLdInfoCommand.lazy_bind_off) { - assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && - "Incorrect lazy bind opcodes size"); - Ends.push_back(DyLdInfoCommand.lazy_bind_off + - DyLdInfoCommand.lazy_bind_size); - } - if (DyLdInfoCommand.export_off) { - assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && - "Incorrect trie size"); - Ends.push_back(DyLdInfoCommand.export_off + DyLdInfoCommand.export_size); - } - } - - if (O.DySymTabCommandIndex) { - const MachO::dysymtab_command &DySymTabCommand = - O.LoadCommands[*O.DySymTabCommandIndex] - .MachOLoadCommand.dysymtab_command_data; - - if (DySymTabCommand.indirectsymoff) - Ends.push_back(DySymTabCommand.indirectsymoff + - sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); - } - - if (O.CodeSignatureCommandIndex) { - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.CodeSignatureCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - if (LinkEditDataCommand.dataoff) - Ends.push_back(LinkEditDataCommand.dataoff + - LinkEditDataCommand.datasize); - } - - if (O.DataInCodeCommandIndex) { - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.DataInCodeCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - - if (LinkEditDataCommand.dataoff) - Ends.push_back(LinkEditDataCommand.dataoff + - LinkEditDataCommand.datasize); - } - - if (O.FunctionStartsCommandIndex) { - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.FunctionStartsCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - - if (LinkEditDataCommand.dataoff) - Ends.push_back(LinkEditDataCommand.dataoff + - LinkEditDataCommand.datasize); - } - - // Otherwise, use the last section / reloction. - for (const LoadCommand &LC : O.LoadCommands) - for (const std::unique_ptr<Section> &S : LC.Sections) { +//===- MachOWriter.cpp ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "MachOWriter.h" +#include "MachOLayoutBuilder.h" +#include "Object.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include <memory> + +namespace llvm { +namespace objcopy { +namespace macho { + +size_t MachOWriter::headerSize() const { + return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); +} + +size_t MachOWriter::loadCommandsSize() const { return O.Header.SizeOfCmds; } + +size_t MachOWriter::symTableSize() const { + return O.SymTable.Symbols.size() * + (Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist)); +} + +size_t MachOWriter::totalSize() const { + // Going from tail to head and looking for an appropriate "anchor" to + // calculate the total size assuming that all the offsets are either valid + // ("true") or 0 (0 indicates that the corresponding part is missing). + + SmallVector<size_t, 7> Ends; + if (O.SymTabCommandIndex) { + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + if (SymTabCommand.symoff) + Ends.push_back(SymTabCommand.symoff + symTableSize()); + if (SymTabCommand.stroff) + Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize); + } + if (O.DyLdInfoCommandIndex) { + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + if (DyLdInfoCommand.rebase_off) { + assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && + "Incorrect rebase opcodes size"); + Ends.push_back(DyLdInfoCommand.rebase_off + DyLdInfoCommand.rebase_size); + } + if (DyLdInfoCommand.bind_off) { + assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && + "Incorrect bind opcodes size"); + Ends.push_back(DyLdInfoCommand.bind_off + DyLdInfoCommand.bind_size); + } + if (DyLdInfoCommand.weak_bind_off) { + assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && + "Incorrect weak bind opcodes size"); + Ends.push_back(DyLdInfoCommand.weak_bind_off + + DyLdInfoCommand.weak_bind_size); + } + if (DyLdInfoCommand.lazy_bind_off) { + assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && + "Incorrect lazy bind opcodes size"); + Ends.push_back(DyLdInfoCommand.lazy_bind_off + + DyLdInfoCommand.lazy_bind_size); + } + if (DyLdInfoCommand.export_off) { + assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && + "Incorrect trie size"); + Ends.push_back(DyLdInfoCommand.export_off + DyLdInfoCommand.export_size); + } + } + + if (O.DySymTabCommandIndex) { + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + + if (DySymTabCommand.indirectsymoff) + Ends.push_back(DySymTabCommand.indirectsymoff + + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); + } + + if (O.CodeSignatureCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.CodeSignatureCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + + if (O.DataInCodeCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.DataInCodeCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + + if (O.FunctionStartsCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.FunctionStartsCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + + // Otherwise, use the last section / reloction. + for (const LoadCommand &LC : O.LoadCommands) + for (const std::unique_ptr<Section> &S : LC.Sections) { if (!S->hasValidOffset()) { assert((S->Offset == 0) && "Skipped section's offset must be zero"); assert((S->isVirtualSection() || S->Size == 0) && @@ -129,398 +129,398 @@ size_t MachOWriter::totalSize() const { } assert((S->Offset != 0) && "Non-zero-fill section's offset cannot be zero"); - Ends.push_back(S->Offset + S->Size); - if (S->RelOff) - Ends.push_back(S->RelOff + - S->NReloc * sizeof(MachO::any_relocation_info)); - } - - if (!Ends.empty()) - return *std::max_element(Ends.begin(), Ends.end()); - - // Otherwise, we have only Mach header and load commands. - return headerSize() + loadCommandsSize(); -} - -void MachOWriter::writeHeader() { - MachO::mach_header_64 Header; - - Header.magic = O.Header.Magic; - Header.cputype = O.Header.CPUType; - Header.cpusubtype = O.Header.CPUSubType; - Header.filetype = O.Header.FileType; - Header.ncmds = O.Header.NCmds; - Header.sizeofcmds = O.Header.SizeOfCmds; - Header.flags = O.Header.Flags; - Header.reserved = O.Header.Reserved; - - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(Header); - - auto HeaderSize = - Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - memcpy(B.getBufferStart(), &Header, HeaderSize); -} - -void MachOWriter::writeLoadCommands() { - uint8_t *Begin = B.getBufferStart() + headerSize(); - for (const LoadCommand &LC : O.LoadCommands) { - // Construct a load command. - MachO::macho_load_command MLC = LC.MachOLoadCommand; - switch (MLC.load_command_data.cmd) { - case MachO::LC_SEGMENT: - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(MLC.segment_command_data); - memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command)); - Begin += sizeof(MachO::segment_command); - - for (const std::unique_ptr<Section> &Sec : LC.Sections) - writeSectionInLoadCommand<MachO::section>(*Sec, Begin); - continue; - case MachO::LC_SEGMENT_64: - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(MLC.segment_command_64_data); - memcpy(Begin, &MLC.segment_command_64_data, - sizeof(MachO::segment_command_64)); - Begin += sizeof(MachO::segment_command_64); - - for (const std::unique_ptr<Section> &Sec : LC.Sections) - writeSectionInLoadCommand<MachO::section_64>(*Sec, Begin); - continue; - } - -#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ - case MachO::LCName: \ - assert(sizeof(MachO::LCStruct) + LC.Payload.size() == \ - MLC.load_command_data.cmdsize); \ - if (IsLittleEndian != sys::IsLittleEndianHost) \ - MachO::swapStruct(MLC.LCStruct##_data); \ - memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \ - Begin += sizeof(MachO::LCStruct); \ - if (!LC.Payload.empty()) \ - memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \ - Begin += LC.Payload.size(); \ - break; - - // Copy the load command as it is. - switch (MLC.load_command_data.cmd) { - default: - assert(sizeof(MachO::load_command) + LC.Payload.size() == - MLC.load_command_data.cmdsize); - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(MLC.load_command_data); - memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command)); - Begin += sizeof(MachO::load_command); - if (!LC.Payload.empty()) - memcpy(Begin, LC.Payload.data(), LC.Payload.size()); - Begin += LC.Payload.size(); - break; -#include "llvm/BinaryFormat/MachO.def" - } - } -} - -template <typename StructType> -void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) { - StructType Temp; - assert(Sec.Segname.size() <= sizeof(Temp.segname) && "too long segment name"); - assert(Sec.Sectname.size() <= sizeof(Temp.sectname) && - "too long section name"); - memset(&Temp, 0, sizeof(StructType)); - memcpy(Temp.segname, Sec.Segname.data(), Sec.Segname.size()); - memcpy(Temp.sectname, Sec.Sectname.data(), Sec.Sectname.size()); - Temp.addr = Sec.Addr; - Temp.size = Sec.Size; - Temp.offset = Sec.Offset; - Temp.align = Sec.Align; - Temp.reloff = Sec.RelOff; - Temp.nreloc = Sec.NReloc; - Temp.flags = Sec.Flags; - Temp.reserved1 = Sec.Reserved1; - Temp.reserved2 = Sec.Reserved2; - - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(Temp); - memcpy(Out, &Temp, sizeof(StructType)); - Out += sizeof(StructType); -} - -void MachOWriter::writeSections() { - for (const LoadCommand &LC : O.LoadCommands) - for (const std::unique_ptr<Section> &Sec : LC.Sections) { + Ends.push_back(S->Offset + S->Size); + if (S->RelOff) + Ends.push_back(S->RelOff + + S->NReloc * sizeof(MachO::any_relocation_info)); + } + + if (!Ends.empty()) + return *std::max_element(Ends.begin(), Ends.end()); + + // Otherwise, we have only Mach header and load commands. + return headerSize() + loadCommandsSize(); +} + +void MachOWriter::writeHeader() { + MachO::mach_header_64 Header; + + Header.magic = O.Header.Magic; + Header.cputype = O.Header.CPUType; + Header.cpusubtype = O.Header.CPUSubType; + Header.filetype = O.Header.FileType; + Header.ncmds = O.Header.NCmds; + Header.sizeofcmds = O.Header.SizeOfCmds; + Header.flags = O.Header.Flags; + Header.reserved = O.Header.Reserved; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Header); + + auto HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + memcpy(B.getBufferStart(), &Header, HeaderSize); +} + +void MachOWriter::writeLoadCommands() { + uint8_t *Begin = B.getBufferStart() + headerSize(); + for (const LoadCommand &LC : O.LoadCommands) { + // Construct a load command. + MachO::macho_load_command MLC = LC.MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(MLC.segment_command_data); + memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command)); + Begin += sizeof(MachO::segment_command); + + for (const std::unique_ptr<Section> &Sec : LC.Sections) + writeSectionInLoadCommand<MachO::section>(*Sec, Begin); + continue; + case MachO::LC_SEGMENT_64: + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(MLC.segment_command_64_data); + memcpy(Begin, &MLC.segment_command_64_data, + sizeof(MachO::segment_command_64)); + Begin += sizeof(MachO::segment_command_64); + + for (const std::unique_ptr<Section> &Sec : LC.Sections) + writeSectionInLoadCommand<MachO::section_64>(*Sec, Begin); + continue; + } + +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + assert(sizeof(MachO::LCStruct) + LC.Payload.size() == \ + MLC.load_command_data.cmdsize); \ + if (IsLittleEndian != sys::IsLittleEndianHost) \ + MachO::swapStruct(MLC.LCStruct##_data); \ + memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \ + Begin += sizeof(MachO::LCStruct); \ + if (!LC.Payload.empty()) \ + memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \ + Begin += LC.Payload.size(); \ + break; + + // Copy the load command as it is. + switch (MLC.load_command_data.cmd) { + default: + assert(sizeof(MachO::load_command) + LC.Payload.size() == + MLC.load_command_data.cmdsize); + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(MLC.load_command_data); + memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command)); + Begin += sizeof(MachO::load_command); + if (!LC.Payload.empty()) + memcpy(Begin, LC.Payload.data(), LC.Payload.size()); + Begin += LC.Payload.size(); + break; +#include "llvm/BinaryFormat/MachO.def" + } + } +} + +template <typename StructType> +void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) { + StructType Temp; + assert(Sec.Segname.size() <= sizeof(Temp.segname) && "too long segment name"); + assert(Sec.Sectname.size() <= sizeof(Temp.sectname) && + "too long section name"); + memset(&Temp, 0, sizeof(StructType)); + memcpy(Temp.segname, Sec.Segname.data(), Sec.Segname.size()); + memcpy(Temp.sectname, Sec.Sectname.data(), Sec.Sectname.size()); + Temp.addr = Sec.Addr; + Temp.size = Sec.Size; + Temp.offset = Sec.Offset; + Temp.align = Sec.Align; + Temp.reloff = Sec.RelOff; + Temp.nreloc = Sec.NReloc; + Temp.flags = Sec.Flags; + Temp.reserved1 = Sec.Reserved1; + Temp.reserved2 = Sec.Reserved2; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Temp); + memcpy(Out, &Temp, sizeof(StructType)); + Out += sizeof(StructType); +} + +void MachOWriter::writeSections() { + for (const LoadCommand &LC : O.LoadCommands) + for (const std::unique_ptr<Section> &Sec : LC.Sections) { if (!Sec->hasValidOffset()) { assert((Sec->Offset == 0) && "Skipped section's offset must be zero"); assert((Sec->isVirtualSection() || Sec->Size == 0) && "Non-zero-fill sections with zero offset must have zero size"); - continue; - } - - assert(Sec->Offset && "Section offset can not be zero"); - assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); - memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(), - Sec->Content.size()); - for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { - RelocationInfo RelocInfo = Sec->Relocations[Index]; - if (!RelocInfo.Scattered) { - const uint32_t SymbolNum = RelocInfo.Extern - ? (*RelocInfo.Symbol)->Index - : (*RelocInfo.Sec)->Index; - RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian); - } - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct( - reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); - memcpy(B.getBufferStart() + Sec->RelOff + - Index * sizeof(MachO::any_relocation_info), - &RelocInfo.Info, sizeof(RelocInfo.Info)); + continue; } - } -} - -template <typename NListType> -void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out, - uint32_t Nstrx) { - NListType ListEntry; - ListEntry.n_strx = Nstrx; - ListEntry.n_type = SE.n_type; - ListEntry.n_sect = SE.n_sect; - ListEntry.n_desc = SE.n_desc; - ListEntry.n_value = SE.n_value; - - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(ListEntry); - memcpy(Out, reinterpret_cast<const char *>(&ListEntry), sizeof(NListType)); - Out += sizeof(NListType); -} - -void MachOWriter::writeStringTable() { - if (!O.SymTabCommandIndex) - return; - const MachO::symtab_command &SymTabCommand = - O.LoadCommands[*O.SymTabCommandIndex] - .MachOLoadCommand.symtab_command_data; - - uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; - LayoutBuilder.getStringTableBuilder().write(StrTable); -} - -void MachOWriter::writeSymbolTable() { - if (!O.SymTabCommandIndex) - return; - const MachO::symtab_command &SymTabCommand = - O.LoadCommands[*O.SymTabCommandIndex] - .MachOLoadCommand.symtab_command_data; - - char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff; - for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); - Iter != End; Iter++) { - SymbolEntry *Sym = Iter->get(); - uint32_t Nstrx = LayoutBuilder.getStringTableBuilder().getOffset(Sym->Name); - - if (Is64Bit) - writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx); - else - writeNListEntry<MachO::nlist>(*Sym, IsLittleEndian, SymTable, Nstrx); - } -} - -void MachOWriter::writeRebaseInfo() { - if (!O.DyLdInfoCommandIndex) - return; - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.rebase_off; - assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && - "Incorrect rebase opcodes size"); - memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size()); -} - -void MachOWriter::writeBindInfo() { - if (!O.DyLdInfoCommandIndex) - return; - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.bind_off; - assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && - "Incorrect bind opcodes size"); - memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size()); -} - -void MachOWriter::writeWeakBindInfo() { - if (!O.DyLdInfoCommandIndex) - return; - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.weak_bind_off; - assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && - "Incorrect weak bind opcodes size"); - memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size()); -} - -void MachOWriter::writeLazyBindInfo() { - if (!O.DyLdInfoCommandIndex) - return; - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.lazy_bind_off; - assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && - "Incorrect lazy bind opcodes size"); - memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size()); -} - -void MachOWriter::writeExportInfo() { - if (!O.DyLdInfoCommandIndex) - return; - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.export_off; - assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && - "Incorrect export trie size"); - memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); -} - -void MachOWriter::writeIndirectSymbolTable() { - if (!O.DySymTabCommandIndex) - return; - - const MachO::dysymtab_command &DySymTabCommand = - O.LoadCommands[*O.DySymTabCommandIndex] - .MachOLoadCommand.dysymtab_command_data; - - uint32_t *Out = - (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff); - for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) { - uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex; - if (IsLittleEndian != sys::IsLittleEndianHost) - sys::swapByteOrder(Entry); - *Out++ = Entry; - } -} - -void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) { - if (!LCIndex) - return; - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; - char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; - assert((LinkEditDataCommand.datasize == LD.Data.size()) && - "Incorrect data size"); - memcpy(Out, LD.Data.data(), LD.Data.size()); -} - -void MachOWriter::writeCodeSignatureData() { - return writeLinkData(O.CodeSignatureCommandIndex, O.CodeSignature); -} - -void MachOWriter::writeDataInCodeData() { - return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode); -} - -void MachOWriter::writeFunctionStartsData() { - return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts); -} - -void MachOWriter::writeTail() { - typedef void (MachOWriter::*WriteHandlerType)(void); - typedef std::pair<uint64_t, WriteHandlerType> WriteOperation; - SmallVector<WriteOperation, 7> Queue; - - if (O.SymTabCommandIndex) { - const MachO::symtab_command &SymTabCommand = - O.LoadCommands[*O.SymTabCommandIndex] - .MachOLoadCommand.symtab_command_data; - if (SymTabCommand.symoff) - Queue.push_back({SymTabCommand.symoff, &MachOWriter::writeSymbolTable}); - if (SymTabCommand.stroff) - Queue.push_back({SymTabCommand.stroff, &MachOWriter::writeStringTable}); - } - - if (O.DyLdInfoCommandIndex) { - const MachO::dyld_info_command &DyLdInfoCommand = - O.LoadCommands[*O.DyLdInfoCommandIndex] - .MachOLoadCommand.dyld_info_command_data; - if (DyLdInfoCommand.rebase_off) - Queue.push_back( - {DyLdInfoCommand.rebase_off, &MachOWriter::writeRebaseInfo}); - if (DyLdInfoCommand.bind_off) - Queue.push_back({DyLdInfoCommand.bind_off, &MachOWriter::writeBindInfo}); - if (DyLdInfoCommand.weak_bind_off) - Queue.push_back( - {DyLdInfoCommand.weak_bind_off, &MachOWriter::writeWeakBindInfo}); - if (DyLdInfoCommand.lazy_bind_off) - Queue.push_back( - {DyLdInfoCommand.lazy_bind_off, &MachOWriter::writeLazyBindInfo}); - if (DyLdInfoCommand.export_off) - Queue.push_back( - {DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo}); - } - - if (O.DySymTabCommandIndex) { - const MachO::dysymtab_command &DySymTabCommand = - O.LoadCommands[*O.DySymTabCommandIndex] - .MachOLoadCommand.dysymtab_command_data; - - if (DySymTabCommand.indirectsymoff) - Queue.emplace_back(DySymTabCommand.indirectsymoff, - &MachOWriter::writeIndirectSymbolTable); - } - - if (O.CodeSignatureCommandIndex) { - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.CodeSignatureCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - - if (LinkEditDataCommand.dataoff) - Queue.emplace_back(LinkEditDataCommand.dataoff, - &MachOWriter::writeCodeSignatureData); - } - - if (O.DataInCodeCommandIndex) { - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.DataInCodeCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - - if (LinkEditDataCommand.dataoff) - Queue.emplace_back(LinkEditDataCommand.dataoff, - &MachOWriter::writeDataInCodeData); - } - - if (O.FunctionStartsCommandIndex) { - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.FunctionStartsCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - - if (LinkEditDataCommand.dataoff) - Queue.emplace_back(LinkEditDataCommand.dataoff, - &MachOWriter::writeFunctionStartsData); - } - - llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) { - return LHS.first < RHS.first; - }); - - for (auto WriteOp : Queue) - (this->*WriteOp.second)(); -} - -Error MachOWriter::finalize() { return LayoutBuilder.layout(); } - -Error MachOWriter::write() { - if (Error E = B.allocate(totalSize())) - return E; - memset(B.getBufferStart(), 0, totalSize()); - writeHeader(); - writeLoadCommands(); - writeSections(); - writeTail(); - return B.commit(); -} - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm + + assert(Sec->Offset && "Section offset can not be zero"); + assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); + memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(), + Sec->Content.size()); + for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { + RelocationInfo RelocInfo = Sec->Relocations[Index]; + if (!RelocInfo.Scattered) { + const uint32_t SymbolNum = RelocInfo.Extern + ? (*RelocInfo.Symbol)->Index + : (*RelocInfo.Sec)->Index; + RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian); + } + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct( + reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); + memcpy(B.getBufferStart() + Sec->RelOff + + Index * sizeof(MachO::any_relocation_info), + &RelocInfo.Info, sizeof(RelocInfo.Info)); + } + } +} + +template <typename NListType> +void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out, + uint32_t Nstrx) { + NListType ListEntry; + ListEntry.n_strx = Nstrx; + ListEntry.n_type = SE.n_type; + ListEntry.n_sect = SE.n_sect; + ListEntry.n_desc = SE.n_desc; + ListEntry.n_value = SE.n_value; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(ListEntry); + memcpy(Out, reinterpret_cast<const char *>(&ListEntry), sizeof(NListType)); + Out += sizeof(NListType); +} + +void MachOWriter::writeStringTable() { + if (!O.SymTabCommandIndex) + return; + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + + uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; + LayoutBuilder.getStringTableBuilder().write(StrTable); +} + +void MachOWriter::writeSymbolTable() { + if (!O.SymTabCommandIndex) + return; + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + + char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff; + for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); + Iter != End; Iter++) { + SymbolEntry *Sym = Iter->get(); + uint32_t Nstrx = LayoutBuilder.getStringTableBuilder().getOffset(Sym->Name); + + if (Is64Bit) + writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx); + else + writeNListEntry<MachO::nlist>(*Sym, IsLittleEndian, SymTable, Nstrx); + } +} + +void MachOWriter::writeRebaseInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.rebase_off; + assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && + "Incorrect rebase opcodes size"); + memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size()); +} + +void MachOWriter::writeBindInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.bind_off; + assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && + "Incorrect bind opcodes size"); + memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size()); +} + +void MachOWriter::writeWeakBindInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.weak_bind_off; + assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && + "Incorrect weak bind opcodes size"); + memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size()); +} + +void MachOWriter::writeLazyBindInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.lazy_bind_off; + assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && + "Incorrect lazy bind opcodes size"); + memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size()); +} + +void MachOWriter::writeExportInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.export_off; + assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && + "Incorrect export trie size"); + memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); +} + +void MachOWriter::writeIndirectSymbolTable() { + if (!O.DySymTabCommandIndex) + return; + + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + + uint32_t *Out = + (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff); + for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) { + uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex; + if (IsLittleEndian != sys::IsLittleEndianHost) + sys::swapByteOrder(Entry); + *Out++ = Entry; + } +} + +void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) { + if (!LCIndex) + return; + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; + char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; + assert((LinkEditDataCommand.datasize == LD.Data.size()) && + "Incorrect data size"); + memcpy(Out, LD.Data.data(), LD.Data.size()); +} + +void MachOWriter::writeCodeSignatureData() { + return writeLinkData(O.CodeSignatureCommandIndex, O.CodeSignature); +} + +void MachOWriter::writeDataInCodeData() { + return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode); +} + +void MachOWriter::writeFunctionStartsData() { + return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts); +} + +void MachOWriter::writeTail() { + typedef void (MachOWriter::*WriteHandlerType)(void); + typedef std::pair<uint64_t, WriteHandlerType> WriteOperation; + SmallVector<WriteOperation, 7> Queue; + + if (O.SymTabCommandIndex) { + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + if (SymTabCommand.symoff) + Queue.push_back({SymTabCommand.symoff, &MachOWriter::writeSymbolTable}); + if (SymTabCommand.stroff) + Queue.push_back({SymTabCommand.stroff, &MachOWriter::writeStringTable}); + } + + if (O.DyLdInfoCommandIndex) { + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + if (DyLdInfoCommand.rebase_off) + Queue.push_back( + {DyLdInfoCommand.rebase_off, &MachOWriter::writeRebaseInfo}); + if (DyLdInfoCommand.bind_off) + Queue.push_back({DyLdInfoCommand.bind_off, &MachOWriter::writeBindInfo}); + if (DyLdInfoCommand.weak_bind_off) + Queue.push_back( + {DyLdInfoCommand.weak_bind_off, &MachOWriter::writeWeakBindInfo}); + if (DyLdInfoCommand.lazy_bind_off) + Queue.push_back( + {DyLdInfoCommand.lazy_bind_off, &MachOWriter::writeLazyBindInfo}); + if (DyLdInfoCommand.export_off) + Queue.push_back( + {DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo}); + } + + if (O.DySymTabCommandIndex) { + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + + if (DySymTabCommand.indirectsymoff) + Queue.emplace_back(DySymTabCommand.indirectsymoff, + &MachOWriter::writeIndirectSymbolTable); + } + + if (O.CodeSignatureCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.CodeSignatureCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeCodeSignatureData); + } + + if (O.DataInCodeCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.DataInCodeCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeDataInCodeData); + } + + if (O.FunctionStartsCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.FunctionStartsCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeFunctionStartsData); + } + + llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) { + return LHS.first < RHS.first; + }); + + for (auto WriteOp : Queue) + (this->*WriteOp.second)(); +} + +Error MachOWriter::finalize() { return LayoutBuilder.layout(); } + +Error MachOWriter::write() { + if (Error E = B.allocate(totalSize())) + return E; + memset(B.getBufferStart(), 0, totalSize()); + writeHeader(); + writeLoadCommands(); + writeSections(); + writeTail(); + return B.commit(); +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.h b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.h index c2c6f5a55e..a6681b8a4c 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/MachOWriter.h @@ -1,67 +1,67 @@ -//===- MachOWriter.h --------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "../Buffer.h" -#include "MachOLayoutBuilder.h" -#include "MachOObjcopy.h" -#include "Object.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/Object/MachO.h" - -namespace llvm { -class Error; - -namespace objcopy { -namespace macho { - -class MachOWriter { - Object &O; - bool Is64Bit; - bool IsLittleEndian; - uint64_t PageSize; - Buffer &B; - MachOLayoutBuilder LayoutBuilder; - - size_t headerSize() const; - size_t loadCommandsSize() const; - size_t symTableSize() const; - size_t strTableSize() const; - - void writeHeader(); - void writeLoadCommands(); - template <typename StructType> - void writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out); - void writeSections(); - void writeSymbolTable(); - void writeStringTable(); - void writeRebaseInfo(); - void writeBindInfo(); - void writeWeakBindInfo(); - void writeLazyBindInfo(); - void writeExportInfo(); - void writeIndirectSymbolTable(); - void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD); - void writeCodeSignatureData(); - void writeDataInCodeData(); - void writeFunctionStartsData(); - void writeTail(); - -public: - MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, - Buffer &B) - : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), - PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {} - - size_t totalSize() const; - Error finalize(); - Error write(); -}; - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm +//===- MachOWriter.h --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "../Buffer.h" +#include "MachOLayoutBuilder.h" +#include "MachOObjcopy.h" +#include "Object.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" + +namespace llvm { +class Error; + +namespace objcopy { +namespace macho { + +class MachOWriter { + Object &O; + bool Is64Bit; + bool IsLittleEndian; + uint64_t PageSize; + Buffer &B; + MachOLayoutBuilder LayoutBuilder; + + size_t headerSize() const; + size_t loadCommandsSize() const; + size_t symTableSize() const; + size_t strTableSize() const; + + void writeHeader(); + void writeLoadCommands(); + template <typename StructType> + void writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out); + void writeSections(); + void writeSymbolTable(); + void writeStringTable(); + void writeRebaseInfo(); + void writeBindInfo(); + void writeWeakBindInfo(); + void writeLazyBindInfo(); + void writeExportInfo(); + void writeIndirectSymbolTable(); + void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD); + void writeCodeSignatureData(); + void writeDataInCodeData(); + void writeFunctionStartsData(); + void writeTail(); + +public: + MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, + Buffer &B) + : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), + PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {} + + size_t totalSize() const; + Error finalize(); + Error write(); +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.cpp index cdb97531fb..184c58ad05 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.cpp @@ -1,113 +1,113 @@ -//===- Object.cpp - Mach-O object file model --------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#include "Object.h" -#include "llvm/ADT/SmallPtrSet.h" -#include <unordered_set> - -namespace llvm { -namespace objcopy { -namespace macho { - -const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const { - assert(Index < Symbols.size() && "invalid symbol index"); - return Symbols[Index].get(); -} - -SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) { - return const_cast<SymbolEntry *>( - static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index)); -} - -void SymbolTable::removeSymbols( - function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) { +//===- Object.cpp - Mach-O object file model --------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "Object.h" +#include "llvm/ADT/SmallPtrSet.h" +#include <unordered_set> + +namespace llvm { +namespace objcopy { +namespace macho { + +const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const { + assert(Index < Symbols.size() && "invalid symbol index"); + return Symbols[Index].get(); +} + +SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) { + return const_cast<SymbolEntry *>( + static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index)); +} + +void SymbolTable::removeSymbols( + function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) { llvm::erase_if(Symbols, ToRemove); -} - -void Object::updateLoadCommandIndexes() { - // Update indices of special load commands - for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) { - LoadCommand &LC = LoadCommands[Index]; - switch (LC.MachOLoadCommand.load_command_data.cmd) { - case MachO::LC_SYMTAB: - SymTabCommandIndex = Index; - break; - case MachO::LC_DYSYMTAB: - DySymTabCommandIndex = Index; - break; - case MachO::LC_DYLD_INFO: - case MachO::LC_DYLD_INFO_ONLY: - DyLdInfoCommandIndex = Index; - break; - case MachO::LC_DATA_IN_CODE: - DataInCodeCommandIndex = Index; - break; - case MachO::LC_FUNCTION_STARTS: - FunctionStartsCommandIndex = Index; - break; - } - } -} - -Error Object::removeLoadCommands( - function_ref<bool(const LoadCommand &)> ToRemove) { - auto It = std::stable_partition( - LoadCommands.begin(), LoadCommands.end(), - [&](const LoadCommand &LC) { return !ToRemove(LC); }); - LoadCommands.erase(It, LoadCommands.end()); - - updateLoadCommandIndexes(); - return Error::success(); -} - -Error Object::removeSections( - function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) { - DenseMap<uint32_t, const Section *> OldIndexToSection; - uint32_t NextSectionIndex = 1; - for (LoadCommand &LC : LoadCommands) { - auto It = std::stable_partition( - std::begin(LC.Sections), std::end(LC.Sections), - [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); }); - for (auto I = LC.Sections.begin(), End = It; I != End; ++I) { - OldIndexToSection[(*I)->Index] = I->get(); - (*I)->Index = NextSectionIndex++; - } - LC.Sections.erase(It, LC.Sections.end()); - } - - auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool { - Optional<uint32_t> Section = S->section(); - return (Section && !OldIndexToSection.count(*Section)); - }; - - SmallPtrSet<const SymbolEntry *, 2> DeadSymbols; - for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols) - if (IsDead(Sym)) - DeadSymbols.insert(Sym.get()); - - for (const LoadCommand &LC : LoadCommands) - for (const std::unique_ptr<Section> &Sec : LC.Sections) - for (const RelocationInfo &R : Sec->Relocations) - if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol)) - return createStringError(std::errc::invalid_argument, - "symbol '%s' defined in section with index " - "'%u' cannot be removed because it is " - "referenced by a relocation in section '%s'", - (*R.Symbol)->Name.c_str(), - *((*R.Symbol)->section()), - Sec->CanonicalName.c_str()); - SymTable.removeSymbols(IsDead); - for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols) - if (S->section()) - S->n_sect = OldIndexToSection[S->n_sect]->Index; - return Error::success(); -} - +} + +void Object::updateLoadCommandIndexes() { + // Update indices of special load commands + for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) { + LoadCommand &LC = LoadCommands[Index]; + switch (LC.MachOLoadCommand.load_command_data.cmd) { + case MachO::LC_SYMTAB: + SymTabCommandIndex = Index; + break; + case MachO::LC_DYSYMTAB: + DySymTabCommandIndex = Index; + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + DyLdInfoCommandIndex = Index; + break; + case MachO::LC_DATA_IN_CODE: + DataInCodeCommandIndex = Index; + break; + case MachO::LC_FUNCTION_STARTS: + FunctionStartsCommandIndex = Index; + break; + } + } +} + +Error Object::removeLoadCommands( + function_ref<bool(const LoadCommand &)> ToRemove) { + auto It = std::stable_partition( + LoadCommands.begin(), LoadCommands.end(), + [&](const LoadCommand &LC) { return !ToRemove(LC); }); + LoadCommands.erase(It, LoadCommands.end()); + + updateLoadCommandIndexes(); + return Error::success(); +} + +Error Object::removeSections( + function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) { + DenseMap<uint32_t, const Section *> OldIndexToSection; + uint32_t NextSectionIndex = 1; + for (LoadCommand &LC : LoadCommands) { + auto It = std::stable_partition( + std::begin(LC.Sections), std::end(LC.Sections), + [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); }); + for (auto I = LC.Sections.begin(), End = It; I != End; ++I) { + OldIndexToSection[(*I)->Index] = I->get(); + (*I)->Index = NextSectionIndex++; + } + LC.Sections.erase(It, LC.Sections.end()); + } + + auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool { + Optional<uint32_t> Section = S->section(); + return (Section && !OldIndexToSection.count(*Section)); + }; + + SmallPtrSet<const SymbolEntry *, 2> DeadSymbols; + for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols) + if (IsDead(Sym)) + DeadSymbols.insert(Sym.get()); + + for (const LoadCommand &LC : LoadCommands) + for (const std::unique_ptr<Section> &Sec : LC.Sections) + for (const RelocationInfo &R : Sec->Relocations) + if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol)) + return createStringError(std::errc::invalid_argument, + "symbol '%s' defined in section with index " + "'%u' cannot be removed because it is " + "referenced by a relocation in section '%s'", + (*R.Symbol)->Name.c_str(), + *((*R.Symbol)->section()), + Sec->CanonicalName.c_str()); + SymTable.removeSymbols(IsDead); + for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols) + if (S->section()) + S->n_sect = OldIndexToSection[S->n_sect]->Index; + return Error::success(); +} + uint64_t Object::nextAvailableSegmentAddress() const { uint64_t HeaderSize = is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); @@ -129,56 +129,56 @@ uint64_t Object::nextAvailableSegmentAddress() const { } } return Addr; -} - -template <typename SegmentType> +} + +template <typename SegmentType> static void constructSegment(SegmentType &Seg, llvm::MachO::LoadCommandType CmdType, StringRef SegName, uint64_t SegVMAddr, uint64_t SegVMSize) { - assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name"); - memset(&Seg, 0, sizeof(SegmentType)); - Seg.cmd = CmdType; - strncpy(Seg.segname, SegName.data(), SegName.size()); + assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name"); + memset(&Seg, 0, sizeof(SegmentType)); + Seg.cmd = CmdType; + strncpy(Seg.segname, SegName.data(), SegName.size()); Seg.maxprot |= (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE); Seg.initprot |= (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE); Seg.vmaddr = SegVMAddr; Seg.vmsize = SegVMSize; -} - +} + LoadCommand &Object::addSegment(StringRef SegName, uint64_t SegVMSize) { - LoadCommand LC; + LoadCommand LC; const uint64_t SegVMAddr = nextAvailableSegmentAddress(); - if (is64Bit()) - constructSegment(LC.MachOLoadCommand.segment_command_64_data, + if (is64Bit()) + constructSegment(LC.MachOLoadCommand.segment_command_64_data, MachO::LC_SEGMENT_64, SegName, SegVMAddr, SegVMSize); - else - constructSegment(LC.MachOLoadCommand.segment_command_data, + else + constructSegment(LC.MachOLoadCommand.segment_command_data, MachO::LC_SEGMENT, SegName, SegVMAddr, SegVMSize); - - LoadCommands.push_back(std::move(LC)); - return LoadCommands.back(); -} - -/// Extracts a segment name from a string which is possibly non-null-terminated. -static StringRef extractSegmentName(const char *SegName) { - return StringRef(SegName, - strnlen(SegName, sizeof(MachO::segment_command::segname))); -} - -Optional<StringRef> LoadCommand::getSegmentName() const { - const MachO::macho_load_command &MLC = MachOLoadCommand; - switch (MLC.load_command_data.cmd) { - case MachO::LC_SEGMENT: - return extractSegmentName(MLC.segment_command_data.segname); - case MachO::LC_SEGMENT_64: - return extractSegmentName(MLC.segment_command_64_data.segname); - default: - return None; - } -} - + + LoadCommands.push_back(std::move(LC)); + return LoadCommands.back(); +} + +/// Extracts a segment name from a string which is possibly non-null-terminated. +static StringRef extractSegmentName(const char *SegName) { + return StringRef(SegName, + strnlen(SegName, sizeof(MachO::segment_command::segname))); +} + +Optional<StringRef> LoadCommand::getSegmentName() const { + const MachO::macho_load_command &MLC = MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + return extractSegmentName(MLC.segment_command_data.segname); + case MachO::LC_SEGMENT_64: + return extractSegmentName(MLC.segment_command_64_data.segname); + default: + return None; + } +} + Optional<uint64_t> LoadCommand::getSegmentVMAddr() const { const MachO::macho_load_command &MLC = MachOLoadCommand; switch (MLC.load_command_data.cmd) { @@ -191,6 +191,6 @@ Optional<uint64_t> LoadCommand::getSegmentVMAddr() const { } } -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.h b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.h index 0bb4b344b2..095da8c534 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/MachO/Object.h @@ -1,360 +1,360 @@ -//===- Object.h - Mach-O object file model ----------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_OBJCOPY_MACHO_OBJECT_H -#define LLVM_OBJCOPY_MACHO_OBJECT_H - -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/MC/StringTableBuilder.h" -#include "llvm/ObjectYAML/DWARFYAML.h" -#include "llvm/Support/StringSaver.h" -#include "llvm/Support/YAMLTraits.h" -#include <cstdint> -#include <string> -#include <vector> - -namespace llvm { -namespace objcopy { -namespace macho { - -struct MachHeader { - uint32_t Magic; - uint32_t CPUType; - uint32_t CPUSubType; - uint32_t FileType; - uint32_t NCmds; - uint32_t SizeOfCmds; - uint32_t Flags; - uint32_t Reserved = 0; -}; - -struct RelocationInfo; -struct Section { - uint32_t Index; - std::string Segname; - std::string Sectname; - // CanonicalName is a string formatted as “<Segname>,<Sectname>". - std::string CanonicalName; - uint64_t Addr = 0; - uint64_t Size = 0; +//===- Object.h - Mach-O object file model ----------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJCOPY_MACHO_OBJECT_H +#define LLVM_OBJCOPY_MACHO_OBJECT_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/ObjectYAML/DWARFYAML.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/YAMLTraits.h" +#include <cstdint> +#include <string> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace macho { + +struct MachHeader { + uint32_t Magic; + uint32_t CPUType; + uint32_t CPUSubType; + uint32_t FileType; + uint32_t NCmds; + uint32_t SizeOfCmds; + uint32_t Flags; + uint32_t Reserved = 0; +}; + +struct RelocationInfo; +struct Section { + uint32_t Index; + std::string Segname; + std::string Sectname; + // CanonicalName is a string formatted as “<Segname>,<Sectname>". + std::string CanonicalName; + uint64_t Addr = 0; + uint64_t Size = 0; // Offset in the input file. Optional<uint32_t> OriginalOffset; - uint32_t Offset = 0; - uint32_t Align = 0; - uint32_t RelOff = 0; - uint32_t NReloc = 0; - uint32_t Flags = 0; - uint32_t Reserved1 = 0; - uint32_t Reserved2 = 0; - uint32_t Reserved3 = 0; - StringRef Content; - std::vector<RelocationInfo> Relocations; - - Section(StringRef SegName, StringRef SectName) - : Segname(std::string(SegName)), Sectname(std::string(SectName)), - CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {} - - Section(StringRef SegName, StringRef SectName, StringRef Content) - : Segname(std::string(SegName)), Sectname(std::string(SectName)), - CanonicalName((Twine(SegName) + Twine(',') + SectName).str()), - Content(Content) {} - - MachO::SectionType getType() const { - return static_cast<MachO::SectionType>(Flags & MachO::SECTION_TYPE); - } - - bool isVirtualSection() const { - return (getType() == MachO::S_ZEROFILL || - getType() == MachO::S_GB_ZEROFILL || - getType() == MachO::S_THREAD_LOCAL_ZEROFILL); - } + uint32_t Offset = 0; + uint32_t Align = 0; + uint32_t RelOff = 0; + uint32_t NReloc = 0; + uint32_t Flags = 0; + uint32_t Reserved1 = 0; + uint32_t Reserved2 = 0; + uint32_t Reserved3 = 0; + StringRef Content; + std::vector<RelocationInfo> Relocations; + + Section(StringRef SegName, StringRef SectName) + : Segname(std::string(SegName)), Sectname(std::string(SectName)), + CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {} + + Section(StringRef SegName, StringRef SectName, StringRef Content) + : Segname(std::string(SegName)), Sectname(std::string(SectName)), + CanonicalName((Twine(SegName) + Twine(',') + SectName).str()), + Content(Content) {} + + MachO::SectionType getType() const { + return static_cast<MachO::SectionType>(Flags & MachO::SECTION_TYPE); + } + + bool isVirtualSection() const { + return (getType() == MachO::S_ZEROFILL || + getType() == MachO::S_GB_ZEROFILL || + getType() == MachO::S_THREAD_LOCAL_ZEROFILL); + } bool hasValidOffset() const { return !(isVirtualSection() || (OriginalOffset && *OriginalOffset == 0)); } -}; - -struct LoadCommand { - // The type MachO::macho_load_command is defined in llvm/BinaryFormat/MachO.h - // and it is a union of all the structs corresponding to various load - // commands. - MachO::macho_load_command MachOLoadCommand; - - // The raw content of the payload of the load command (located right after the - // corresponding struct). In some cases it is either empty or can be - // copied-over without digging into its structure. - std::vector<uint8_t> Payload; - - // Some load commands can contain (inside the payload) an array of sections, - // though the contents of the sections are stored separately. The struct - // Section describes only sections' metadata and where to find the - // corresponding content inside the binary. - std::vector<std::unique_ptr<Section>> Sections; - - // Returns the segment name if the load command is a segment command. - Optional<StringRef> getSegmentName() const; +}; + +struct LoadCommand { + // The type MachO::macho_load_command is defined in llvm/BinaryFormat/MachO.h + // and it is a union of all the structs corresponding to various load + // commands. + MachO::macho_load_command MachOLoadCommand; + + // The raw content of the payload of the load command (located right after the + // corresponding struct). In some cases it is either empty or can be + // copied-over without digging into its structure. + std::vector<uint8_t> Payload; + + // Some load commands can contain (inside the payload) an array of sections, + // though the contents of the sections are stored separately. The struct + // Section describes only sections' metadata and where to find the + // corresponding content inside the binary. + std::vector<std::unique_ptr<Section>> Sections; + + // Returns the segment name if the load command is a segment command. + Optional<StringRef> getSegmentName() const; // Returns the segment vm address if the load command is a segment command. Optional<uint64_t> getSegmentVMAddr() const; -}; - -// A symbol information. Fields which starts with "n_" are same as them in the -// nlist. -struct SymbolEntry { - std::string Name; - bool Referenced = false; - uint32_t Index; - uint8_t n_type; - uint8_t n_sect; - uint16_t n_desc; - uint64_t n_value; - - bool isExternalSymbol() const { return n_type & MachO::N_EXT; } - - bool isLocalSymbol() const { return !isExternalSymbol(); } - - bool isUndefinedSymbol() const { - return (n_type & MachO::N_TYPE) == MachO::N_UNDF; - } - - bool isSwiftSymbol() const { - return StringRef(Name).startswith("_$s") || - StringRef(Name).startswith("_$S"); - } - - Optional<uint32_t> section() const { - return n_sect == MachO::NO_SECT ? None : Optional<uint32_t>(n_sect); - } -}; - -/// The location of the symbol table inside the binary is described by LC_SYMTAB -/// load command. -struct SymbolTable { - std::vector<std::unique_ptr<SymbolEntry>> Symbols; - - using iterator = pointee_iterator< - std::vector<std::unique_ptr<SymbolEntry>>::const_iterator>; - - iterator begin() const { return iterator(Symbols.begin()); } - iterator end() const { return iterator(Symbols.end()); } - - const SymbolEntry *getSymbolByIndex(uint32_t Index) const; - SymbolEntry *getSymbolByIndex(uint32_t Index); - void removeSymbols( - function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove); -}; - -struct IndirectSymbolEntry { - // The original value in an indirect symbol table. Higher bits encode extra - // information (INDIRECT_SYMBOL_LOCAL and INDIRECT_SYMBOL_ABS). - uint32_t OriginalIndex; - /// The Symbol referenced by this entry. It's None if the index is - /// INDIRECT_SYMBOL_LOCAL or INDIRECT_SYMBOL_ABS. - Optional<SymbolEntry *> Symbol; - - IndirectSymbolEntry(uint32_t OriginalIndex, Optional<SymbolEntry *> Symbol) - : OriginalIndex(OriginalIndex), Symbol(Symbol) {} -}; - -struct IndirectSymbolTable { - std::vector<IndirectSymbolEntry> Symbols; -}; - -/// The location of the string table inside the binary is described by LC_SYMTAB -/// load command. -struct StringTable { - std::vector<std::string> Strings; -}; - -struct RelocationInfo { - // The referenced symbol entry. Set if !Scattered && Extern. - Optional<const SymbolEntry *> Symbol; - // The referenced section. Set if !Scattered && !Extern. - Optional<const Section *> Sec; - // True if Info is a scattered_relocation_info. - bool Scattered; - // True if the r_symbolnum points to a section number (i.e. r_extern=0). - bool Extern; - MachO::any_relocation_info Info; - - unsigned getPlainRelocationSymbolNum(bool IsLittleEndian) { - if (IsLittleEndian) - return Info.r_word1 & 0xffffff; - return Info.r_word1 >> 8; - } - - void setPlainRelocationSymbolNum(unsigned SymbolNum, bool IsLittleEndian) { - assert(SymbolNum < (1 << 24) && "SymbolNum out of range"); - if (IsLittleEndian) - Info.r_word1 = (Info.r_word1 & ~0x00ffffff) | SymbolNum; - else - Info.r_word1 = (Info.r_word1 & ~0xffffff00) | (SymbolNum << 8); - } -}; - -/// The location of the rebase info inside the binary is described by -/// LC_DYLD_INFO load command. Dyld rebases an image whenever dyld loads it at -/// an address different from its preferred address. The rebase information is -/// a stream of byte sized opcodes whose symbolic names start with -/// REBASE_OPCODE_. Conceptually the rebase information is a table of tuples: -/// <seg-index, seg-offset, type> -/// The opcodes are a compressed way to encode the table by only -/// encoding when a column changes. In addition simple patterns -/// like "every n'th offset for m times" can be encoded in a few -/// bytes. -struct RebaseInfo { - // At the moment we do not parse this info (and it is simply copied over), - // but the proper support will be added later. - ArrayRef<uint8_t> Opcodes; -}; - -/// The location of the bind info inside the binary is described by -/// LC_DYLD_INFO load command. Dyld binds an image during the loading process, -/// if the image requires any pointers to be initialized to symbols in other -/// images. The bind information is a stream of byte sized opcodes whose -/// symbolic names start with BIND_OPCODE_. Conceptually the bind information is -/// a table of tuples: <seg-index, seg-offset, type, symbol-library-ordinal, -/// symbol-name, addend> The opcodes are a compressed way to encode the table by -/// only encoding when a column changes. In addition simple patterns like for -/// runs of pointers initialized to the same value can be encoded in a few -/// bytes. -struct BindInfo { - // At the moment we do not parse this info (and it is simply copied over), - // but the proper support will be added later. - ArrayRef<uint8_t> Opcodes; -}; - -/// The location of the weak bind info inside the binary is described by -/// LC_DYLD_INFO load command. Some C++ programs require dyld to unique symbols -/// so that all images in the process use the same copy of some code/data. This -/// step is done after binding. The content of the weak_bind info is an opcode -/// stream like the bind_info. But it is sorted alphabetically by symbol name. -/// This enable dyld to walk all images with weak binding information in order -/// and look for collisions. If there are no collisions, dyld does no updating. -/// That means that some fixups are also encoded in the bind_info. For -/// instance, all calls to "operator new" are first bound to libstdc++.dylib -/// using the information in bind_info. Then if some image overrides operator -/// new that is detected when the weak_bind information is processed and the -/// call to operator new is then rebound. -struct WeakBindInfo { - // At the moment we do not parse this info (and it is simply copied over), - // but the proper support will be added later. - ArrayRef<uint8_t> Opcodes; -}; - -/// The location of the lazy bind info inside the binary is described by -/// LC_DYLD_INFO load command. Some uses of external symbols do not need to be -/// bound immediately. Instead they can be lazily bound on first use. The -/// lazy_bind contains a stream of BIND opcodes to bind all lazy symbols. Normal -/// use is that dyld ignores the lazy_bind section when loading an image. -/// Instead the static linker arranged for the lazy pointer to initially point -/// to a helper function which pushes the offset into the lazy_bind area for the -/// symbol needing to be bound, then jumps to dyld which simply adds the offset -/// to lazy_bind_off to get the information on what to bind. -struct LazyBindInfo { - ArrayRef<uint8_t> Opcodes; -}; - -/// The location of the export info inside the binary is described by -/// LC_DYLD_INFO load command. The symbols exported by a dylib are encoded in a -/// trie. This is a compact representation that factors out common prefixes. It -/// also reduces LINKEDIT pages in RAM because it encodes all information (name, -/// address, flags) in one small, contiguous range. The export area is a stream -/// of nodes. The first node sequentially is the start node for the trie. Nodes -/// for a symbol start with a uleb128 that is the length of the exported symbol -/// information for the string so far. If there is no exported symbol, the node -/// starts with a zero byte. If there is exported info, it follows the length. -/// First is a uleb128 containing flags. Normally, it is followed by -/// a uleb128 encoded offset which is location of the content named -/// by the symbol from the mach_header for the image. If the flags -/// is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is -/// a uleb128 encoded library ordinal, then a zero terminated -/// UTF8 string. If the string is zero length, then the symbol -/// is re-export from the specified dylib with the same name. -/// If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following -/// the flags is two uleb128s: the stub offset and the resolver offset. -/// The stub is used by non-lazy pointers. The resolver is used -/// by lazy pointers and must be called to get the actual address to use. -/// After the optional exported symbol information is a byte of -/// how many edges (0-255) that this node has leaving it, -/// followed by each edge. -/// Each edge is a zero terminated UTF8 of the addition chars -/// in the symbol, followed by a uleb128 offset for the node that -/// edge points to. -struct ExportInfo { - ArrayRef<uint8_t> Trie; -}; - -struct LinkData { - ArrayRef<uint8_t> Data; -}; - -struct Object { - MachHeader Header; - std::vector<LoadCommand> LoadCommands; - - SymbolTable SymTable; - StringTable StrTable; - - RebaseInfo Rebases; - BindInfo Binds; - WeakBindInfo WeakBinds; - LazyBindInfo LazyBinds; - ExportInfo Exports; - IndirectSymbolTable IndirectSymTable; - LinkData DataInCode; - LinkData FunctionStarts; - LinkData CodeSignature; - - Optional<uint32_t> SwiftVersion; - - /// The index of LC_CODE_SIGNATURE load command if present. - Optional<size_t> CodeSignatureCommandIndex; - /// The index of LC_SYMTAB load command if present. - Optional<size_t> SymTabCommandIndex; - /// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present. - Optional<size_t> DyLdInfoCommandIndex; - /// The index LC_DYSYMTAB load comamnd if present. - Optional<size_t> DySymTabCommandIndex; - /// The index LC_DATA_IN_CODE load comamnd if present. - Optional<size_t> DataInCodeCommandIndex; - /// The index LC_FUNCTION_STARTS load comamnd if present. - Optional<size_t> FunctionStartsCommandIndex; - - BumpPtrAllocator Alloc; - StringSaver NewSectionsContents; - - Object() : NewSectionsContents(Alloc) {} - - Error - removeSections(function_ref<bool(const std::unique_ptr<Section> &)> ToRemove); - - Error removeLoadCommands(function_ref<bool(const LoadCommand &)> ToRemove); - - void updateLoadCommandIndexes(); - - /// Creates a new segment load command in the object and returns a reference - /// to the newly created load command. The caller should verify that SegName - /// is not too long (SegName.size() should be less than or equal to 16). +}; + +// A symbol information. Fields which starts with "n_" are same as them in the +// nlist. +struct SymbolEntry { + std::string Name; + bool Referenced = false; + uint32_t Index; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; + + bool isExternalSymbol() const { return n_type & MachO::N_EXT; } + + bool isLocalSymbol() const { return !isExternalSymbol(); } + + bool isUndefinedSymbol() const { + return (n_type & MachO::N_TYPE) == MachO::N_UNDF; + } + + bool isSwiftSymbol() const { + return StringRef(Name).startswith("_$s") || + StringRef(Name).startswith("_$S"); + } + + Optional<uint32_t> section() const { + return n_sect == MachO::NO_SECT ? None : Optional<uint32_t>(n_sect); + } +}; + +/// The location of the symbol table inside the binary is described by LC_SYMTAB +/// load command. +struct SymbolTable { + std::vector<std::unique_ptr<SymbolEntry>> Symbols; + + using iterator = pointee_iterator< + std::vector<std::unique_ptr<SymbolEntry>>::const_iterator>; + + iterator begin() const { return iterator(Symbols.begin()); } + iterator end() const { return iterator(Symbols.end()); } + + const SymbolEntry *getSymbolByIndex(uint32_t Index) const; + SymbolEntry *getSymbolByIndex(uint32_t Index); + void removeSymbols( + function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove); +}; + +struct IndirectSymbolEntry { + // The original value in an indirect symbol table. Higher bits encode extra + // information (INDIRECT_SYMBOL_LOCAL and INDIRECT_SYMBOL_ABS). + uint32_t OriginalIndex; + /// The Symbol referenced by this entry. It's None if the index is + /// INDIRECT_SYMBOL_LOCAL or INDIRECT_SYMBOL_ABS. + Optional<SymbolEntry *> Symbol; + + IndirectSymbolEntry(uint32_t OriginalIndex, Optional<SymbolEntry *> Symbol) + : OriginalIndex(OriginalIndex), Symbol(Symbol) {} +}; + +struct IndirectSymbolTable { + std::vector<IndirectSymbolEntry> Symbols; +}; + +/// The location of the string table inside the binary is described by LC_SYMTAB +/// load command. +struct StringTable { + std::vector<std::string> Strings; +}; + +struct RelocationInfo { + // The referenced symbol entry. Set if !Scattered && Extern. + Optional<const SymbolEntry *> Symbol; + // The referenced section. Set if !Scattered && !Extern. + Optional<const Section *> Sec; + // True if Info is a scattered_relocation_info. + bool Scattered; + // True if the r_symbolnum points to a section number (i.e. r_extern=0). + bool Extern; + MachO::any_relocation_info Info; + + unsigned getPlainRelocationSymbolNum(bool IsLittleEndian) { + if (IsLittleEndian) + return Info.r_word1 & 0xffffff; + return Info.r_word1 >> 8; + } + + void setPlainRelocationSymbolNum(unsigned SymbolNum, bool IsLittleEndian) { + assert(SymbolNum < (1 << 24) && "SymbolNum out of range"); + if (IsLittleEndian) + Info.r_word1 = (Info.r_word1 & ~0x00ffffff) | SymbolNum; + else + Info.r_word1 = (Info.r_word1 & ~0xffffff00) | (SymbolNum << 8); + } +}; + +/// The location of the rebase info inside the binary is described by +/// LC_DYLD_INFO load command. Dyld rebases an image whenever dyld loads it at +/// an address different from its preferred address. The rebase information is +/// a stream of byte sized opcodes whose symbolic names start with +/// REBASE_OPCODE_. Conceptually the rebase information is a table of tuples: +/// <seg-index, seg-offset, type> +/// The opcodes are a compressed way to encode the table by only +/// encoding when a column changes. In addition simple patterns +/// like "every n'th offset for m times" can be encoded in a few +/// bytes. +struct RebaseInfo { + // At the moment we do not parse this info (and it is simply copied over), + // but the proper support will be added later. + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the bind info inside the binary is described by +/// LC_DYLD_INFO load command. Dyld binds an image during the loading process, +/// if the image requires any pointers to be initialized to symbols in other +/// images. The bind information is a stream of byte sized opcodes whose +/// symbolic names start with BIND_OPCODE_. Conceptually the bind information is +/// a table of tuples: <seg-index, seg-offset, type, symbol-library-ordinal, +/// symbol-name, addend> The opcodes are a compressed way to encode the table by +/// only encoding when a column changes. In addition simple patterns like for +/// runs of pointers initialized to the same value can be encoded in a few +/// bytes. +struct BindInfo { + // At the moment we do not parse this info (and it is simply copied over), + // but the proper support will be added later. + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the weak bind info inside the binary is described by +/// LC_DYLD_INFO load command. Some C++ programs require dyld to unique symbols +/// so that all images in the process use the same copy of some code/data. This +/// step is done after binding. The content of the weak_bind info is an opcode +/// stream like the bind_info. But it is sorted alphabetically by symbol name. +/// This enable dyld to walk all images with weak binding information in order +/// and look for collisions. If there are no collisions, dyld does no updating. +/// That means that some fixups are also encoded in the bind_info. For +/// instance, all calls to "operator new" are first bound to libstdc++.dylib +/// using the information in bind_info. Then if some image overrides operator +/// new that is detected when the weak_bind information is processed and the +/// call to operator new is then rebound. +struct WeakBindInfo { + // At the moment we do not parse this info (and it is simply copied over), + // but the proper support will be added later. + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the lazy bind info inside the binary is described by +/// LC_DYLD_INFO load command. Some uses of external symbols do not need to be +/// bound immediately. Instead they can be lazily bound on first use. The +/// lazy_bind contains a stream of BIND opcodes to bind all lazy symbols. Normal +/// use is that dyld ignores the lazy_bind section when loading an image. +/// Instead the static linker arranged for the lazy pointer to initially point +/// to a helper function which pushes the offset into the lazy_bind area for the +/// symbol needing to be bound, then jumps to dyld which simply adds the offset +/// to lazy_bind_off to get the information on what to bind. +struct LazyBindInfo { + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the export info inside the binary is described by +/// LC_DYLD_INFO load command. The symbols exported by a dylib are encoded in a +/// trie. This is a compact representation that factors out common prefixes. It +/// also reduces LINKEDIT pages in RAM because it encodes all information (name, +/// address, flags) in one small, contiguous range. The export area is a stream +/// of nodes. The first node sequentially is the start node for the trie. Nodes +/// for a symbol start with a uleb128 that is the length of the exported symbol +/// information for the string so far. If there is no exported symbol, the node +/// starts with a zero byte. If there is exported info, it follows the length. +/// First is a uleb128 containing flags. Normally, it is followed by +/// a uleb128 encoded offset which is location of the content named +/// by the symbol from the mach_header for the image. If the flags +/// is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is +/// a uleb128 encoded library ordinal, then a zero terminated +/// UTF8 string. If the string is zero length, then the symbol +/// is re-export from the specified dylib with the same name. +/// If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following +/// the flags is two uleb128s: the stub offset and the resolver offset. +/// The stub is used by non-lazy pointers. The resolver is used +/// by lazy pointers and must be called to get the actual address to use. +/// After the optional exported symbol information is a byte of +/// how many edges (0-255) that this node has leaving it, +/// followed by each edge. +/// Each edge is a zero terminated UTF8 of the addition chars +/// in the symbol, followed by a uleb128 offset for the node that +/// edge points to. +struct ExportInfo { + ArrayRef<uint8_t> Trie; +}; + +struct LinkData { + ArrayRef<uint8_t> Data; +}; + +struct Object { + MachHeader Header; + std::vector<LoadCommand> LoadCommands; + + SymbolTable SymTable; + StringTable StrTable; + + RebaseInfo Rebases; + BindInfo Binds; + WeakBindInfo WeakBinds; + LazyBindInfo LazyBinds; + ExportInfo Exports; + IndirectSymbolTable IndirectSymTable; + LinkData DataInCode; + LinkData FunctionStarts; + LinkData CodeSignature; + + Optional<uint32_t> SwiftVersion; + + /// The index of LC_CODE_SIGNATURE load command if present. + Optional<size_t> CodeSignatureCommandIndex; + /// The index of LC_SYMTAB load command if present. + Optional<size_t> SymTabCommandIndex; + /// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present. + Optional<size_t> DyLdInfoCommandIndex; + /// The index LC_DYSYMTAB load comamnd if present. + Optional<size_t> DySymTabCommandIndex; + /// The index LC_DATA_IN_CODE load comamnd if present. + Optional<size_t> DataInCodeCommandIndex; + /// The index LC_FUNCTION_STARTS load comamnd if present. + Optional<size_t> FunctionStartsCommandIndex; + + BumpPtrAllocator Alloc; + StringSaver NewSectionsContents; + + Object() : NewSectionsContents(Alloc) {} + + Error + removeSections(function_ref<bool(const std::unique_ptr<Section> &)> ToRemove); + + Error removeLoadCommands(function_ref<bool(const LoadCommand &)> ToRemove); + + void updateLoadCommandIndexes(); + + /// Creates a new segment load command in the object and returns a reference + /// to the newly created load command. The caller should verify that SegName + /// is not too long (SegName.size() should be less than or equal to 16). LoadCommand &addSegment(StringRef SegName, uint64_t SegVMSize); - - bool is64Bit() const { - return Header.Magic == MachO::MH_MAGIC_64 || - Header.Magic == MachO::MH_CIGAM_64; - } + + bool is64Bit() const { + return Header.Magic == MachO::MH_MAGIC_64 || + Header.Magic == MachO::MH_CIGAM_64; + } uint64_t nextAvailableSegmentAddress() const; -}; - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_OBJCOPY_MACHO_OBJECT_H +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_OBJCOPY_MACHO_OBJECT_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ObjcopyOpts.td b/contrib/libs/llvm12/tools/llvm-objcopy/ObjcopyOpts.td index 9e6b6f0005..9cd69596b3 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ObjcopyOpts.td +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ObjcopyOpts.td @@ -1,229 +1,229 @@ -include "CommonOpts.td" - -defm binary_architecture - : Eq<"binary-architecture", "Ignored for compatibility">; -def B : JoinedOrSeparate<["-"], "B">, - Alias<binary_architecture>, - HelpText<"Alias for --binary-architecture">; - -defm target : Eq<"target", "Format of the input and output file">, - Values<"binary">; -def F : JoinedOrSeparate<["-"], "F">, - Alias<target>, - HelpText<"Alias for --target">; - -defm input_target : Eq<"input-target", "Format of the input file">, - Values<"binary">; -def I : JoinedOrSeparate<["-"], "I">, - Alias<input_target>, - HelpText<"Alias for --input-target">; - -defm output_target : Eq<"output-target", "Format of the output file">, - Values<"binary">; -def O : JoinedOrSeparate<["-"], "O">, - Alias<output_target>, - HelpText<"Alias for --output-target">; - -defm new_symbol_visibility : Eq<"new-symbol-visibility", "Visibility of " - "symbols generated for binary input or added" - " with --add-symbol unless otherwise" - " specified. The default value is 'default'.">; - -def compress_debug_sections : Flag<["--"], "compress-debug-sections">; -def compress_debug_sections_eq - : Joined<["--"], "compress-debug-sections=">, - MetaVarName<"[ zlib | zlib-gnu ]">, - HelpText<"Compress DWARF debug sections using specified style. Supported " - "styles: 'zlib-gnu' and 'zlib'">; -def decompress_debug_sections : Flag<["--"], "decompress-debug-sections">, - HelpText<"Decompress DWARF debug sections.">; -defm split_dwo - : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to " - "<dwo-file>, then strip-dwo on the input file">, - MetaVarName<"dwo-file">; - -defm add_gnu_debuglink - : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">, - MetaVarName<"debug-file">; - -defm rename_section - : Eq<"rename-section", - "Renames a section from old to new, optionally with specified flags. " - "Flags supported for GNU compatibility: alloc, load, noload, " - "readonly, debug, code, data, rom, share, contents, merge, strings.">, - MetaVarName<"old=new[,flag1,...]">; -defm redefine_symbol - : Eq<"redefine-sym", "Change the name of a symbol old to new">, - MetaVarName<"old=new">; -defm redefine_symbols - : Eq<"redefine-syms", - "Reads a list of symbol pairs from <filename> and runs as if " - "--redefine-sym=<old>=<new> is set for each one. <filename> " - "contains two symbols per line separated with whitespace and may " - "contain comments beginning with '#'. Leading and trailing " - "whitespace is stripped from each line. May be repeated to read " - "symbols from many files.">, - MetaVarName<"filename">; - -defm only_section : Eq<"only-section", "Remove all but <section>">, - MetaVarName<"section">; -def j : JoinedOrSeparate<["-"], "j">, - Alias<only_section>, - HelpText<"Alias for --only-section">; -defm add_section - : Eq<"add-section", - "Make a section named <section> with the contents of <file>.">, - MetaVarName<"section=file">; - -defm set_section_alignment - : Eq<"set-section-alignment", "Set alignment for a given section.">, - MetaVarName<"section=align">; - -defm set_section_flags - : Eq<"set-section-flags", - "Set section flags for a given section. Flags supported for GNU " - "compatibility: alloc, load, noload, readonly, debug, code, data, " - "rom, share, contents, merge, strings.">, - MetaVarName<"section=flag1[,flag2,...]">; - -def S : Flag<["-"], "S">, - Alias<strip_all>, - HelpText<"Alias for --strip-all">; -def strip_dwo : Flag<["--"], "strip-dwo">, - HelpText<"Remove all DWARF .dwo sections from file">; -def strip_non_alloc - : Flag<["--"], "strip-non-alloc">, - HelpText<"Remove all non-allocated sections outside segments">; -defm strip_unneeded_symbol - : Eq<"strip-unneeded-symbol", - "Remove symbol <symbol> if it is not needed by relocations">, - MetaVarName<"symbol">; -defm strip_unneeded_symbols - : Eq<"strip-unneeded-symbols", - "Reads a list of symbols from <filename> and removes them " - "if they are not needed by relocations">, - MetaVarName<"filename">; - -def extract_dwo - : Flag<["--"], "extract-dwo">, - HelpText< - "Remove all sections that are not DWARF .dwo sections from file">; - -defm extract_partition - : Eq<"extract-partition", "Extract named partition from input file">, - MetaVarName<"name">; -def extract_main_partition - : Flag<["--"], "extract-main-partition">, - HelpText<"Extract main partition from the input file">; - -def localize_hidden - : Flag<["--"], "localize-hidden">, - HelpText< - "Mark all symbols that have hidden or internal visibility as local">; -defm localize_symbol : Eq<"localize-symbol", "Mark <symbol> as local">, - MetaVarName<"symbol">; -defm localize_symbols - : Eq<"localize-symbols", - "Reads a list of symbols from <filename> and marks them local.">, - MetaVarName<"filename">; - -def L : JoinedOrSeparate<["-"], "L">, - Alias<localize_symbol>, - HelpText<"Alias for --localize-symbol">; - -defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, - MetaVarName<"symbol">; - -defm globalize_symbols - : Eq<"globalize-symbols", - "Reads a list of symbols from <filename> and marks them global.">, - MetaVarName<"filename">; - -defm keep_global_symbol - : Eq<"keep-global-symbol", - "Convert all symbols except <symbol> to local. May be repeated to " - "convert all except a set of symbols to local.">, - MetaVarName<"symbol">; -def G : JoinedOrSeparate<["-"], "G">, - Alias<keep_global_symbol>, - HelpText<"Alias for --keep-global-symbol">; - -defm keep_global_symbols - : Eq<"keep-global-symbols", - "Reads a list of symbols from <filename> and runs as if " - "--keep-global-symbol=<symbol> is set for each one. <filename> " - "contains one symbol per line and may contain comments beginning with " - "'#'. Leading and trailing whitespace is stripped from each line. May " - "be repeated to read symbols from many files.">, - MetaVarName<"filename">; - -defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">, - MetaVarName<"symbol">; -defm weaken_symbols - : Eq<"weaken-symbols", - "Reads a list of symbols from <filename> and marks them weak.">, - MetaVarName<"filename">; - -def W : JoinedOrSeparate<["-"], "W">, - Alias<weaken_symbol>, - HelpText<"Alias for --weaken-symbol">; -def weaken : Flag<["--"], "weaken">, - HelpText<"Mark all global symbols as weak">; - -defm strip_symbols - : Eq<"strip-symbols", - "Reads a list of symbols from <filename> and removes them.">, - MetaVarName<"filename">; - -defm keep_symbols - : Eq<"keep-symbols", - "Reads a list of symbols from <filename> and runs as if " - "--keep-symbol=<symbol> is set for each one. <filename> " - "contains one symbol per line and may contain comments beginning with " - "'#'. Leading and trailing whitespace is stripped from each line. May " - "be repeated to read symbols from many files.">, - MetaVarName<"filename">; - -defm dump_section - : Eq<"dump-section", - "Dump contents of section named <section> into file <file>">, - MetaVarName<"section=file">; -defm prefix_symbols - : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">, - MetaVarName<"prefix">; - -defm prefix_alloc_sections - : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, - MetaVarName<"prefix">; - -defm build_id_link_dir - : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " - "--build-id-link-output to <dir>">, - MetaVarName<"dir">; -defm build_id_link_input - : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> " - "name derived from hex build ID">, - MetaVarName<"suffix">; -defm build_id_link_output - : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> " - "name derived from hex build ID">, - MetaVarName<"suffix">; - -defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " - "any previous --change-start or --adjust-start values.">, - MetaVarName<"addr">; -defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be " - "specified multiple times, all values will be applied " - "cumulatively.">, - MetaVarName<"incr">; -def adjust_start : JoinedOrSeparate<["--"], "adjust-start">, - Alias<change_start>, - HelpText<"Alias for --change-start">; - -defm add_symbol - : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: " - "global, local, weak, default, hidden, protected, file, section, object, " - "function, indirect-function. Accepted but ignored for " - "compatibility: debug, constructor, warning, indirect, synthetic, " - "unique-object, before.">, - MetaVarName<"name=[section:]value[,flags]">; +include "CommonOpts.td" + +defm binary_architecture + : Eq<"binary-architecture", "Ignored for compatibility">; +def B : JoinedOrSeparate<["-"], "B">, + Alias<binary_architecture>, + HelpText<"Alias for --binary-architecture">; + +defm target : Eq<"target", "Format of the input and output file">, + Values<"binary">; +def F : JoinedOrSeparate<["-"], "F">, + Alias<target>, + HelpText<"Alias for --target">; + +defm input_target : Eq<"input-target", "Format of the input file">, + Values<"binary">; +def I : JoinedOrSeparate<["-"], "I">, + Alias<input_target>, + HelpText<"Alias for --input-target">; + +defm output_target : Eq<"output-target", "Format of the output file">, + Values<"binary">; +def O : JoinedOrSeparate<["-"], "O">, + Alias<output_target>, + HelpText<"Alias for --output-target">; + +defm new_symbol_visibility : Eq<"new-symbol-visibility", "Visibility of " + "symbols generated for binary input or added" + " with --add-symbol unless otherwise" + " specified. The default value is 'default'.">; + +def compress_debug_sections : Flag<["--"], "compress-debug-sections">; +def compress_debug_sections_eq + : Joined<["--"], "compress-debug-sections=">, + MetaVarName<"[ zlib | zlib-gnu ]">, + HelpText<"Compress DWARF debug sections using specified style. Supported " + "styles: 'zlib-gnu' and 'zlib'">; +def decompress_debug_sections : Flag<["--"], "decompress-debug-sections">, + HelpText<"Decompress DWARF debug sections.">; +defm split_dwo + : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to " + "<dwo-file>, then strip-dwo on the input file">, + MetaVarName<"dwo-file">; + +defm add_gnu_debuglink + : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">, + MetaVarName<"debug-file">; + +defm rename_section + : Eq<"rename-section", + "Renames a section from old to new, optionally with specified flags. " + "Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, strings.">, + MetaVarName<"old=new[,flag1,...]">; +defm redefine_symbol + : Eq<"redefine-sym", "Change the name of a symbol old to new">, + MetaVarName<"old=new">; +defm redefine_symbols + : Eq<"redefine-syms", + "Reads a list of symbol pairs from <filename> and runs as if " + "--redefine-sym=<old>=<new> is set for each one. <filename> " + "contains two symbols per line separated with whitespace and may " + "contain comments beginning with '#'. Leading and trailing " + "whitespace is stripped from each line. May be repeated to read " + "symbols from many files.">, + MetaVarName<"filename">; + +defm only_section : Eq<"only-section", "Remove all but <section>">, + MetaVarName<"section">; +def j : JoinedOrSeparate<["-"], "j">, + Alias<only_section>, + HelpText<"Alias for --only-section">; +defm add_section + : Eq<"add-section", + "Make a section named <section> with the contents of <file>.">, + MetaVarName<"section=file">; + +defm set_section_alignment + : Eq<"set-section-alignment", "Set alignment for a given section.">, + MetaVarName<"section=align">; + +defm set_section_flags + : Eq<"set-section-flags", + "Set section flags for a given section. Flags supported for GNU " + "compatibility: alloc, load, noload, readonly, debug, code, data, " + "rom, share, contents, merge, strings.">, + MetaVarName<"section=flag1[,flag2,...]">; + +def S : Flag<["-"], "S">, + Alias<strip_all>, + HelpText<"Alias for --strip-all">; +def strip_dwo : Flag<["--"], "strip-dwo">, + HelpText<"Remove all DWARF .dwo sections from file">; +def strip_non_alloc + : Flag<["--"], "strip-non-alloc">, + HelpText<"Remove all non-allocated sections outside segments">; +defm strip_unneeded_symbol + : Eq<"strip-unneeded-symbol", + "Remove symbol <symbol> if it is not needed by relocations">, + MetaVarName<"symbol">; +defm strip_unneeded_symbols + : Eq<"strip-unneeded-symbols", + "Reads a list of symbols from <filename> and removes them " + "if they are not needed by relocations">, + MetaVarName<"filename">; + +def extract_dwo + : Flag<["--"], "extract-dwo">, + HelpText< + "Remove all sections that are not DWARF .dwo sections from file">; + +defm extract_partition + : Eq<"extract-partition", "Extract named partition from input file">, + MetaVarName<"name">; +def extract_main_partition + : Flag<["--"], "extract-main-partition">, + HelpText<"Extract main partition from the input file">; + +def localize_hidden + : Flag<["--"], "localize-hidden">, + HelpText< + "Mark all symbols that have hidden or internal visibility as local">; +defm localize_symbol : Eq<"localize-symbol", "Mark <symbol> as local">, + MetaVarName<"symbol">; +defm localize_symbols + : Eq<"localize-symbols", + "Reads a list of symbols from <filename> and marks them local.">, + MetaVarName<"filename">; + +def L : JoinedOrSeparate<["-"], "L">, + Alias<localize_symbol>, + HelpText<"Alias for --localize-symbol">; + +defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, + MetaVarName<"symbol">; + +defm globalize_symbols + : Eq<"globalize-symbols", + "Reads a list of symbols from <filename> and marks them global.">, + MetaVarName<"filename">; + +defm keep_global_symbol + : Eq<"keep-global-symbol", + "Convert all symbols except <symbol> to local. May be repeated to " + "convert all except a set of symbols to local.">, + MetaVarName<"symbol">; +def G : JoinedOrSeparate<["-"], "G">, + Alias<keep_global_symbol>, + HelpText<"Alias for --keep-global-symbol">; + +defm keep_global_symbols + : Eq<"keep-global-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-global-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + +defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">, + MetaVarName<"symbol">; +defm weaken_symbols + : Eq<"weaken-symbols", + "Reads a list of symbols from <filename> and marks them weak.">, + MetaVarName<"filename">; + +def W : JoinedOrSeparate<["-"], "W">, + Alias<weaken_symbol>, + HelpText<"Alias for --weaken-symbol">; +def weaken : Flag<["--"], "weaken">, + HelpText<"Mark all global symbols as weak">; + +defm strip_symbols + : Eq<"strip-symbols", + "Reads a list of symbols from <filename> and removes them.">, + MetaVarName<"filename">; + +defm keep_symbols + : Eq<"keep-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + +defm dump_section + : Eq<"dump-section", + "Dump contents of section named <section> into file <file>">, + MetaVarName<"section=file">; +defm prefix_symbols + : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">, + MetaVarName<"prefix">; + +defm prefix_alloc_sections + : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, + MetaVarName<"prefix">; + +defm build_id_link_dir + : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " + "--build-id-link-output to <dir>">, + MetaVarName<"dir">; +defm build_id_link_input + : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> " + "name derived from hex build ID">, + MetaVarName<"suffix">; +defm build_id_link_output + : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> " + "name derived from hex build ID">, + MetaVarName<"suffix">; + +defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " + "any previous --change-start or --adjust-start values.">, + MetaVarName<"addr">; +defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be " + "specified multiple times, all values will be applied " + "cumulatively.">, + MetaVarName<"incr">; +def adjust_start : JoinedOrSeparate<["--"], "adjust-start">, + Alias<change_start>, + HelpText<"Alias for --change-start">; + +defm add_symbol + : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: " + "global, local, weak, default, hidden, protected, file, section, object, " + "function, indirect-function. Accepted but ignored for " + "compatibility: debug, constructor, warning, indirect, synthetic, " + "unique-object, before.">, + MetaVarName<"name=[section:]value[,flags]">; diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/StripOpts.td b/contrib/libs/llvm12/tools/llvm-objcopy/StripOpts.td index 001da23528..b644d07d18 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/StripOpts.td +++ b/contrib/libs/llvm12/tools/llvm-objcopy/StripOpts.td @@ -1,20 +1,20 @@ -include "CommonOpts.td" - -def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">, - MetaVarName<"<file>">; - -def s : Flag<["-"], "s">, - Alias<strip_all>, - HelpText<"Alias for --strip-all">; -def no_strip_all : Flag<["--"], "no-strip-all">, - HelpText<"Disable --strip-all">; - -def d : Flag<["-"], "d">, - Alias<strip_debug>, - HelpText<"Alias for --strip-debug">; -def S : Flag<["-"], "S">, - Alias<strip_debug>, - HelpText<"Alias for --strip-debug">; - -def strip_swift_symbols : Flag<["-"], "T">, - HelpText<"Remove Swift symbols">; +include "CommonOpts.td" + +def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">, + MetaVarName<"<file>">; + +def s : Flag<["-"], "s">, + Alias<strip_all>, + HelpText<"Alias for --strip-all">; +def no_strip_all : Flag<["--"], "no-strip-all">, + HelpText<"Disable --strip-all">; + +def d : Flag<["-"], "d">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; +def S : Flag<["-"], "S">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; + +def strip_swift_symbols : Flag<["-"], "T">, + HelpText<"Remove Swift symbols">; diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.cpp index 7fd2acd11e..3a872ae1a3 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.cpp @@ -1,68 +1,68 @@ -//===- llvm-objcopy.cpp ---------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Buffer.h" -#include "COFF/COFFObjcopy.h" -#include "CopyConfig.h" -#include "ELF/ELFObjcopy.h" -#include "MachO/MachOObjcopy.h" -#include "wasm/WasmObjcopy.h" - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/ArchiveWriter.h" -#include "llvm/Object/Binary.h" -#include "llvm/Object/COFF.h" -#include "llvm/Object/ELFObjectFile.h" -#include "llvm/Object/ELFTypes.h" -#include "llvm/Object/Error.h" -#include "llvm/Object/MachO.h" +//===- llvm-objcopy.cpp ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Buffer.h" +#include "COFF/COFFObjcopy.h" +#include "CopyConfig.h" +#include "ELF/ELFObjcopy.h" +#include "MachO/MachOObjcopy.h" +#include "wasm/WasmObjcopy.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" -#include "llvm/Object/Wasm.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/StringSaver.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cassert> -#include <cstdlib> -#include <memory> -#include <string> -#include <system_error> -#include <utility> - -namespace llvm { -namespace objcopy { - -// The name this program was invoked as. -StringRef ToolName; - +#include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +namespace llvm { +namespace objcopy { + +// The name this program was invoked as. +StringRef ToolName; + ErrorSuccess reportWarning(Error E) { - assert(E); + assert(E); WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; return Error::success(); -} - +} + static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { StringRef Stem = sys::path::stem(ToolName); auto Is = [=](StringRef Tool) { @@ -76,7 +76,7 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { return I != StringRef::npos && (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); }; - + if (Is("bitcode-strip") || Is("bitcode_strip")) return parseBitcodeStripOptions(Args); else if (Is("strip")) @@ -85,135 +85,135 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { return parseInstallNameToolOptions(Args); else return parseObjcopyOptions(Args, reportWarning); -} - -} // end namespace objcopy -} // end namespace llvm - -using namespace llvm; -using namespace llvm::object; -using namespace llvm::objcopy; - -// For regular archives this function simply calls llvm::writeArchive, -// For thin archives it writes the archive file itself as well as its members. -static Error deepWriteArchive(StringRef ArcName, - ArrayRef<NewArchiveMember> NewMembers, - bool WriteSymtab, object::Archive::Kind Kind, - bool Deterministic, bool Thin) { - if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, - Deterministic, Thin)) - return createFileError(ArcName, std::move(E)); - - if (!Thin) - return Error::success(); - - for (const NewArchiveMember &Member : NewMembers) { - // Internally, FileBuffer will use the buffer created by - // FileOutputBuffer::create, for regular files (that is the case for - // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. - // OnDiskBuffer uses a temporary file and then renames it. So in reality - // there is no inefficiency / duplicated in-memory buffers in this case. For - // now in-memory buffers can not be completely avoided since - // NewArchiveMember still requires them even though writeArchive does not - // write them on disk. - FileBuffer FB(Member.MemberName); - if (Error E = FB.allocate(Member.Buf->getBufferSize())) - return E; - std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), - FB.getBufferStart()); - if (Error E = FB.commit()) - return E; - } - return Error::success(); -} - -/// The function executeObjcopyOnIHex does the dispatch based on the format -/// of the output specified by the command line options. -static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - // TODO: support output formats other than ELF. - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnIHex(Config, In, Out); -} - -/// The function executeObjcopyOnRawBinary does the dispatch based on the format -/// of the output specified by the command line options. -static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - switch (Config.OutputFormat) { - case FileFormat::ELF: - // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the - // output format is binary/ihex or it's not given. This behavior differs from - // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. - case FileFormat::Binary: - case FileFormat::IHex: - case FileFormat::Unspecified: - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnRawBinary(Config, In, Out); - } - - llvm_unreachable("unsupported output format"); -} - -/// The function executeObjcopyOnBinary does the dispatch based on the format -/// of the input binary (ELF, MachO or COFF). -static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, - Buffer &Out) { - if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); - } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) - return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); - else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) - return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); +} + +} // end namespace objcopy +} // end namespace llvm + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::objcopy; + +// For regular archives this function simply calls llvm::writeArchive, +// For thin archives it writes the archive file itself as well as its members. +static Error deepWriteArchive(StringRef ArcName, + ArrayRef<NewArchiveMember> NewMembers, + bool WriteSymtab, object::Archive::Kind Kind, + bool Deterministic, bool Thin) { + if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, + Deterministic, Thin)) + return createFileError(ArcName, std::move(E)); + + if (!Thin) + return Error::success(); + + for (const NewArchiveMember &Member : NewMembers) { + // Internally, FileBuffer will use the buffer created by + // FileOutputBuffer::create, for regular files (that is the case for + // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. + // OnDiskBuffer uses a temporary file and then renames it. So in reality + // there is no inefficiency / duplicated in-memory buffers in this case. For + // now in-memory buffers can not be completely avoided since + // NewArchiveMember still requires them even though writeArchive does not + // write them on disk. + FileBuffer FB(Member.MemberName); + if (Error E = FB.allocate(Member.Buf->getBufferSize())) + return E; + std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), + FB.getBufferStart()); + if (Error E = FB.commit()) + return E; + } + return Error::success(); +} + +/// The function executeObjcopyOnIHex does the dispatch based on the format +/// of the output specified by the command line options. +static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + // TODO: support output formats other than ELF. + if (Error E = Config.parseELFConfig()) + return E; + return elf::executeObjcopyOnIHex(Config, In, Out); +} + +/// The function executeObjcopyOnRawBinary does the dispatch based on the format +/// of the output specified by the command line options. +static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + switch (Config.OutputFormat) { + case FileFormat::ELF: + // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the + // output format is binary/ihex or it's not given. This behavior differs from + // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. + case FileFormat::Binary: + case FileFormat::IHex: + case FileFormat::Unspecified: + if (Error E = Config.parseELFConfig()) + return E; + return elf::executeObjcopyOnRawBinary(Config, In, Out); + } + + llvm_unreachable("unsupported output format"); +} + +/// The function executeObjcopyOnBinary does the dispatch based on the format +/// of the input binary (ELF, MachO or COFF). +static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, + Buffer &Out) { + if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { + if (Error E = Config.parseELFConfig()) + return E; + return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); + } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) + return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); + else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) + return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); else if (auto *MachOUniversalBinary = dyn_cast<object::MachOUniversalBinary>(&In)) return macho::executeObjcopyOnMachOUniversalBinary( Config, *MachOUniversalBinary, Out); - else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) - return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out); - else - return createStringError(object_error::invalid_file_type, - "unsupported object file format"); -} - + else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) + return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out); + else + return createStringError(object_error::invalid_file_type, + "unsupported object file format"); +} + namespace llvm { namespace objcopy { Expected<std::vector<NewArchiveMember>> createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) { - std::vector<NewArchiveMember> NewArchiveMembers; - Error Err = Error::success(); - for (const Archive::Child &Child : Ar.children(Err)) { - Expected<StringRef> ChildNameOrErr = Child.getName(); - if (!ChildNameOrErr) - return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); - - Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); - if (!ChildOrErr) - return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", - ChildOrErr.takeError()); - - MemBuffer MB(ChildNameOrErr.get()); - if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) + std::vector<NewArchiveMember> NewArchiveMembers; + Error Err = Error::success(); + for (const Archive::Child &Child : Ar.children(Err)) { + Expected<StringRef> ChildNameOrErr = Child.getName(); + if (!ChildNameOrErr) + return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); + + Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); + if (!ChildOrErr) + return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", + ChildOrErr.takeError()); + + MemBuffer MB(ChildNameOrErr.get()); + if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) return std::move(E); - - Expected<NewArchiveMember> Member = - NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); - if (!Member) - return createFileError(Ar.getFileName(), Member.takeError()); - Member->Buf = MB.releaseMemoryBuffer(); - Member->MemberName = Member->Buf->getBufferIdentifier(); - NewArchiveMembers.push_back(std::move(*Member)); - } - if (Err) - return createFileError(Config.InputFilename, std::move(Err)); + + Expected<NewArchiveMember> Member = + NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); + if (!Member) + return createFileError(Ar.getFileName(), Member.takeError()); + Member->Buf = MB.releaseMemoryBuffer(); + Member->MemberName = Member->Buf->getBufferIdentifier(); + NewArchiveMembers.push_back(std::move(*Member)); + } + if (Err) + return createFileError(Config.InputFilename, std::move(Err)); return std::move(NewArchiveMembers); } - + } // end namespace objcopy } // end namespace llvm @@ -224,148 +224,148 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, if (!NewArchiveMembersOrErr) return NewArchiveMembersOrErr.takeError(); return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr, - Ar.hasSymbolTable(), Ar.kind(), - Config.DeterministicArchives, Ar.isThin()); -} - -static Error restoreStatOnFile(StringRef Filename, - const sys::fs::file_status &Stat, - bool PreserveDates) { - int FD; - - // Writing to stdout should not be treated as an error here, just - // do not set access/modification times or permissions. - if (Filename == "-") - return Error::success(); - - if (auto EC = - sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) - return createFileError(Filename, EC); - - if (PreserveDates) - if (auto EC = sys::fs::setLastAccessAndModificationTime( - FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) - return createFileError(Filename, EC); - - sys::fs::file_status OStat; - if (std::error_code EC = sys::fs::status(FD, OStat)) - return createFileError(Filename, EC); - if (OStat.type() == sys::fs::file_type::regular_file) -#ifdef _WIN32 - if (auto EC = sys::fs::setPermissions( - Filename, static_cast<sys::fs::perms>(Stat.permissions() & - ~sys::fs::getUmask()))) -#else - if (auto EC = sys::fs::setPermissions( - FD, static_cast<sys::fs::perms>(Stat.permissions() & - ~sys::fs::getUmask()))) -#endif - return createFileError(Filename, EC); - - if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) - return createFileError(Filename, EC); - - return Error::success(); -} - -/// The function executeObjcopy does the higher level dispatch based on the type -/// of input (raw binary, archive or single object file) and takes care of the -/// format-agnostic modifications, i.e. preserving dates. -static Error executeObjcopy(CopyConfig &Config) { - sys::fs::file_status Stat; - if (Config.InputFilename != "-") { - if (auto EC = sys::fs::status(Config.InputFilename, Stat)) - return createFileError(Config.InputFilename, EC); - } else { - Stat.permissions(static_cast<sys::fs::perms>(0777)); - } - - using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); - ProcessRawFn ProcessRaw; - switch (Config.InputFormat) { - case FileFormat::Binary: - ProcessRaw = executeObjcopyOnRawBinary; - break; - case FileFormat::IHex: - ProcessRaw = executeObjcopyOnIHex; - break; - default: - ProcessRaw = nullptr; - } - - if (ProcessRaw) { - auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); - if (!BufOrErr) - return createFileError(Config.InputFilename, BufOrErr.getError()); - FileBuffer FB(Config.OutputFilename); - if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) - return E; - } else { - Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = - createBinary(Config.InputFilename); - if (!BinaryOrErr) - return createFileError(Config.InputFilename, BinaryOrErr.takeError()); - - if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { - if (Error E = executeObjcopyOnArchive(Config, *Ar)) - return E; - } else { - FileBuffer FB(Config.OutputFilename); - if (Error E = executeObjcopyOnBinary(Config, - *BinaryOrErr.get().getBinary(), FB)) - return E; - } - } - - if (Error E = - restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) - return E; - - if (!Config.SplitDWO.empty()) { - Stat.permissions(static_cast<sys::fs::perms>(0666)); - if (Error E = - restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) - return E; - } - - return Error::success(); -} - -namespace { - -} // anonymous namespace - -int main(int argc, char **argv) { - InitLLVM X(argc, argv); - ToolName = argv[0]; - - // Expand response files. - // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, - // into a separate function in the CommandLine library and call that function - // here. This is duplicated code. - SmallVector<const char *, 20> NewArgv(argv, argv + argc); - BumpPtrAllocator A; - StringSaver Saver(A); - cl::ExpandResponseFiles(Saver, - Triple(sys::getProcessTriple()).isOSWindows() - ? cl::TokenizeWindowsCommandLine - : cl::TokenizeGNUCommandLine, - NewArgv); - - auto Args = makeArrayRef(NewArgv).drop_front(); + Ar.hasSymbolTable(), Ar.kind(), + Config.DeterministicArchives, Ar.isThin()); +} + +static Error restoreStatOnFile(StringRef Filename, + const sys::fs::file_status &Stat, + bool PreserveDates) { + int FD; + + // Writing to stdout should not be treated as an error here, just + // do not set access/modification times or permissions. + if (Filename == "-") + return Error::success(); + + if (auto EC = + sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) + return createFileError(Filename, EC); + + if (PreserveDates) + if (auto EC = sys::fs::setLastAccessAndModificationTime( + FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + return createFileError(Filename, EC); + + sys::fs::file_status OStat; + if (std::error_code EC = sys::fs::status(FD, OStat)) + return createFileError(Filename, EC); + if (OStat.type() == sys::fs::file_type::regular_file) +#ifdef _WIN32 + if (auto EC = sys::fs::setPermissions( + Filename, static_cast<sys::fs::perms>(Stat.permissions() & + ~sys::fs::getUmask()))) +#else + if (auto EC = sys::fs::setPermissions( + FD, static_cast<sys::fs::perms>(Stat.permissions() & + ~sys::fs::getUmask()))) +#endif + return createFileError(Filename, EC); + + if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) + return createFileError(Filename, EC); + + return Error::success(); +} + +/// The function executeObjcopy does the higher level dispatch based on the type +/// of input (raw binary, archive or single object file) and takes care of the +/// format-agnostic modifications, i.e. preserving dates. +static Error executeObjcopy(CopyConfig &Config) { + sys::fs::file_status Stat; + if (Config.InputFilename != "-") { + if (auto EC = sys::fs::status(Config.InputFilename, Stat)) + return createFileError(Config.InputFilename, EC); + } else { + Stat.permissions(static_cast<sys::fs::perms>(0777)); + } + + using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); + ProcessRawFn ProcessRaw; + switch (Config.InputFormat) { + case FileFormat::Binary: + ProcessRaw = executeObjcopyOnRawBinary; + break; + case FileFormat::IHex: + ProcessRaw = executeObjcopyOnIHex; + break; + default: + ProcessRaw = nullptr; + } + + if (ProcessRaw) { + auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); + if (!BufOrErr) + return createFileError(Config.InputFilename, BufOrErr.getError()); + FileBuffer FB(Config.OutputFilename); + if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) + return E; + } else { + Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = + createBinary(Config.InputFilename); + if (!BinaryOrErr) + return createFileError(Config.InputFilename, BinaryOrErr.takeError()); + + if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { + if (Error E = executeObjcopyOnArchive(Config, *Ar)) + return E; + } else { + FileBuffer FB(Config.OutputFilename); + if (Error E = executeObjcopyOnBinary(Config, + *BinaryOrErr.get().getBinary(), FB)) + return E; + } + } + + if (Error E = + restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) + return E; + + if (!Config.SplitDWO.empty()) { + Stat.permissions(static_cast<sys::fs::perms>(0666)); + if (Error E = + restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) + return E; + } + + return Error::success(); +} + +namespace { + +} // anonymous namespace + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + ToolName = argv[0]; + + // Expand response files. + // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, + // into a separate function in the CommandLine library and call that function + // here. This is duplicated code. + SmallVector<const char *, 20> NewArgv(argv, argv + argc); + BumpPtrAllocator A; + StringSaver Saver(A); + cl::ExpandResponseFiles(Saver, + Triple(sys::getProcessTriple()).isOSWindows() + ? cl::TokenizeWindowsCommandLine + : cl::TokenizeGNUCommandLine, + NewArgv); + + auto Args = makeArrayRef(NewArgv).drop_front(); Expected<DriverConfig> DriverConfig = getDriverConfig(Args); - if (!DriverConfig) { - logAllUnhandledErrors(DriverConfig.takeError(), - WithColor::error(errs(), ToolName)); - return 1; - } - for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { - if (Error E = executeObjcopy(CopyConfig)) { - logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); - return 1; - } - } - - return 0; -} + if (!DriverConfig) { + logAllUnhandledErrors(DriverConfig.takeError(), + WithColor::error(errs(), ToolName)); + return 1; + } + for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { + if (Error E = executeObjcopy(CopyConfig)) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); + return 1; + } + } + + return 0; +} diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.h b/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.h index 97a166769f..9086bcff2e 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/llvm-objcopy.h @@ -1,22 +1,22 @@ -//===- llvm-objcopy.h -------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_OBJCOPY_H -#define LLVM_TOOLS_OBJCOPY_OBJCOPY_H - -#include "llvm/Support/Error.h" - -namespace llvm { - +//===- llvm-objcopy.h -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_OBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_OBJCOPY_H + +#include "llvm/Support/Error.h" + +namespace llvm { + struct NewArchiveMember; - + namespace object { - + class Archive; } // end namespace object @@ -26,7 +26,7 @@ struct CopyConfig; Expected<std::vector<NewArchiveMember>> createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar); -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.cpp index e7a2956fed..4fa82a8c36 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.cpp @@ -1,34 +1,34 @@ -//===- Object.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Object.h" - -#include "llvm/Support/LEB128.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { -namespace objcopy { -namespace wasm { - -using namespace object; -using namespace llvm::wasm; - -void Object::addSectionWithOwnedContents( - Section NewSection, std::unique_ptr<MemoryBuffer> &&Content) { - Sections.push_back(NewSection); - OwnedContents.emplace_back(std::move(Content)); -} - -void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { - // TODO: remove reloc sections for the removed section, handle symbols, etc. +//===- Object.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Object.h" + +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +void Object::addSectionWithOwnedContents( + Section NewSection, std::unique_ptr<MemoryBuffer> &&Content) { + Sections.push_back(NewSection); + OwnedContents.emplace_back(std::move(Content)); +} + +void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { + // TODO: remove reloc sections for the removed section, handle symbols, etc. llvm::erase_if(Sections, ToRemove); -} - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.h b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.h index 9db91c41e2..da664edd1b 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Object.h @@ -1,47 +1,47 @@ -//===- Object.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H -#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Object/Wasm.h" -#include "llvm/Support/MemoryBuffer.h" -#include <vector> - -namespace llvm { -namespace objcopy { -namespace wasm { - -struct Section { - // For now, each section is only an opaque binary blob with no distinction - // between custom and known sections. - uint8_t SectionType; - StringRef Name; - ArrayRef<uint8_t> Contents; -}; - -struct Object { - llvm::wasm::WasmObjectHeader Header; - // For now don't discriminate between kinds of sections. - std::vector<Section> Sections; - - void addSectionWithOwnedContents(Section NewSection, - std::unique_ptr<MemoryBuffer> &&Content); - void removeSections(function_ref<bool(const Section &)> ToRemove); - -private: - std::vector<std::unique_ptr<MemoryBuffer>> OwnedContents; -}; - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H +//===- Object.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Support/MemoryBuffer.h" +#include <vector> + +namespace llvm { +namespace objcopy { +namespace wasm { + +struct Section { + // For now, each section is only an opaque binary blob with no distinction + // between custom and known sections. + uint8_t SectionType; + StringRef Name; + ArrayRef<uint8_t> Contents; +}; + +struct Object { + llvm::wasm::WasmObjectHeader Header; + // For now don't discriminate between kinds of sections. + std::vector<Section> Sections; + + void addSectionWithOwnedContents(Section NewSection, + std::unique_ptr<MemoryBuffer> &&Content); + void removeSections(function_ref<bool(const Section &)> ToRemove); + +private: + std::vector<std::unique_ptr<MemoryBuffer>> OwnedContents; +}; + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.cpp index 13fa84ad80..d4dcc3c682 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.cpp @@ -1,33 +1,33 @@ -//===- Reader.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Reader.h" - -namespace llvm { -namespace objcopy { -namespace wasm { - -using namespace object; -using namespace llvm::wasm; - -Expected<std::unique_ptr<Object>> Reader::create() const { - auto Obj = std::make_unique<Object>(); - Obj->Header = WasmObj.getHeader(); - std::vector<Section> Sections; - Obj->Sections.reserve(WasmObj.getNumSections()); - for (const SectionRef &Sec : WasmObj.sections()) { - const WasmSection &WS = WasmObj.getWasmSection(Sec); - Obj->Sections.push_back( - {static_cast<uint8_t>(WS.Type), WS.Name, WS.Content}); - } - return std::move(Obj); -} - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm +//===- Reader.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Reader.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +Expected<std::unique_ptr<Object>> Reader::create() const { + auto Obj = std::make_unique<Object>(); + Obj->Header = WasmObj.getHeader(); + std::vector<Section> Sections; + Obj->Sections.reserve(WasmObj.getNumSections()); + for (const SectionRef &Sec : WasmObj.sections()) { + const WasmSection &WS = WasmObj.getWasmSection(Sec); + Obj->Sections.push_back( + {static_cast<uint8_t>(WS.Type), WS.Name, WS.Content}); + } + return std::move(Obj); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.h b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.h index 2dcf7dde02..494dd5e563 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Reader.h @@ -1,31 +1,31 @@ -//===- Reader.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H -#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H - -#include "Object.h" - -namespace llvm { -namespace objcopy { -namespace wasm { - -class Reader { -public: - explicit Reader(const object::WasmObjectFile &O) : WasmObj(O) {} - Expected<std::unique_ptr<Object>> create() const; - -private: - const object::WasmObjectFile &WasmObj; -}; - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H +//===- Reader.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H + +#include "Object.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +class Reader { +public: + explicit Reader(const object::WasmObjectFile &O) : WasmObj(O) {} + Expected<std::unique_ptr<Object>> create() const; + +private: + const object::WasmObjectFile &WasmObj; +}; + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.cpp index eb0e5635ce..fd83334ed9 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.cpp @@ -1,113 +1,113 @@ -//===- WasmObjcopy.cpp ----------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "WasmObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" -#include "Object.h" -#include "Reader.h" -#include "Writer.h" -#include "llvm/Support/Errc.h" - -namespace llvm { -namespace objcopy { -namespace wasm { - -using namespace object; - -static Error dumpSectionToFile(StringRef SecName, StringRef Filename, - Object &Obj) { - for (const Section &Sec : Obj.Sections) { - if (Sec.Name == SecName) { - ArrayRef<uint8_t> Contents = Sec.Contents; - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(Filename, Contents.size()); - if (!BufferOrErr) - return BufferOrErr.takeError(); - std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); - std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart()); - if (Error E = Buf->commit()) - return E; - return Error::success(); - } - } - return createStringError(errc::invalid_argument, "section '%s' not found", - SecName.str().c_str()); -} -static Error handleArgs(const CopyConfig &Config, Object &Obj) { - // Only support AddSection, DumpSection, RemoveSection for now. - for (StringRef Flag : Config.DumpSection) { - StringRef SecName; - StringRef FileName; - std::tie(SecName, FileName) = Flag.split("="); - if (Error E = dumpSectionToFile(SecName, FileName, Obj)) - return createFileError(FileName, std::move(E)); - } - - Obj.removeSections([&Config](const Section &Sec) { - if (Config.ToRemove.matches(Sec.Name)) - return true; - return false; - }); - - for (StringRef Flag : Config.AddSection) { - StringRef SecName, FileName; - std::tie(SecName, FileName) = Flag.split("="); - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFile(FileName); - if (!BufOrErr) - return createFileError(FileName, errorCodeToError(BufOrErr.getError())); - Section Sec; - Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM; - Sec.Name = SecName; - std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); - Sec.Contents = makeArrayRef<uint8_t>( - reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), - Buf->getBufferSize()); - Obj.addSectionWithOwnedContents(Sec, std::move(Buf)); - } - - if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - Config.ExtractPartition || !Config.SplitDWO.empty() || - !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || - Config.DiscardMode != DiscardType::None || Config.NewSymbolVisibility || - !Config.SymbolsToAdd.empty() || !Config.RPathToAdd.empty() || - !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToRemove.empty() || - !Config.UnneededSymbolsToRemove.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() || - !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) { - return createStringError( - llvm::errc::invalid_argument, - "only add-section, dump-section, and remove-section are supported"); - } - return Error::success(); -} - -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out) { - Reader TheReader(In); - Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create(); - if (!ObjOrErr) - return createFileError(Config.InputFilename, ObjOrErr.takeError()); - Object *Obj = ObjOrErr->get(); - assert(Obj && "Unable to deserialize Wasm object"); - if (Error E = handleArgs(Config, *Obj)) - return E; - Writer TheWriter(*Obj, Out); - if (Error E = TheWriter.write()) - return createFileError(Config.OutputFilename, std::move(E)); - return Error::success(); -} - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm +//===- WasmObjcopy.cpp ----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "WasmObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "Reader.h" +#include "Writer.h" +#include "llvm/Support/Errc.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (const Section &Sec : Obj.Sections) { + if (Sec.Name == SecName) { + ArrayRef<uint8_t> Contents = Sec.Contents; + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Contents.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return createStringError(errc::invalid_argument, "section '%s' not found", + SecName.str().c_str()); +} +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // Only support AddSection, DumpSection, RemoveSection for now. + for (StringRef Flag : Config.DumpSection) { + StringRef SecName; + StringRef FileName; + std::tie(SecName, FileName) = Flag.split("="); + if (Error E = dumpSectionToFile(SecName, FileName, Obj)) + return createFileError(FileName, std::move(E)); + } + + Obj.removeSections([&Config](const Section &Sec) { + if (Config.ToRemove.matches(Sec.Name)) + return true; + return false; + }); + + for (StringRef Flag : Config.AddSection) { + StringRef SecName, FileName; + std::tie(SecName, FileName) = Flag.split("="); + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return createFileError(FileName, errorCodeToError(BufOrErr.getError())); + Section Sec; + Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM; + Sec.Name = SecName; + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + Sec.Contents = makeArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()); + Obj.addSectionWithOwnedContents(Sec, std::move(Buf)); + } + + if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || + Config.BuildIdLinkInput || Config.BuildIdLinkOutput || + Config.ExtractPartition || !Config.SplitDWO.empty() || + !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || + Config.DiscardMode != DiscardType::None || Config.NewSymbolVisibility || + !Config.SymbolsToAdd.empty() || !Config.RPathToAdd.empty() || + !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToRemove.empty() || + !Config.UnneededSymbolsToRemove.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() || + !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) { + return createStringError( + llvm::errc::invalid_argument, + "only add-section, dump-section, and remove-section are supported"); + } + return Error::success(); +} + +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::WasmObjectFile &In, Buffer &Out) { + Reader TheReader(In); + Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create(); + if (!ObjOrErr) + return createFileError(Config.InputFilename, ObjOrErr.takeError()); + Object *Obj = ObjOrErr->get(); + assert(Obj && "Unable to deserialize Wasm object"); + if (Error E = handleArgs(Config, *Obj)) + return E; + Writer TheWriter(*Obj, Out); + if (Error E = TheWriter.write()) + return createFileError(Config.OutputFilename, std::move(E)); + return Error::success(); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.h b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.h index 3557d5c0a5..f0ded26911 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/WasmObjcopy.h @@ -1,31 +1,31 @@ -//===- WasmObjcopy.h -------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H -#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H - -namespace llvm { -class Error; - -namespace object { -class WasmObjectFile; -} // end namespace object - -namespace objcopy { -struct CopyConfig; -class Buffer; - -namespace wasm { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out); - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H +//===- WasmObjcopy.h -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H + +namespace llvm { +class Error; + +namespace object { +class WasmObjectFile; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace wasm { +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::WasmObjectFile &In, Buffer &Out); + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.cpp b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.cpp index 50d26507b4..17a4e04835 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.cpp +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.cpp @@ -1,78 +1,78 @@ -//===- Writer.cpp ---------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "Writer.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { -namespace objcopy { -namespace wasm { - -using namespace object; -using namespace llvm::wasm; - -Writer::SectionHeader Writer::createSectionHeader(const Section &S, - size_t &SectionSize) { - SectionHeader Header; - raw_svector_ostream OS(Header); - OS << S.SectionType; - bool HasName = S.SectionType == WASM_SEC_CUSTOM; - SectionSize = S.Contents.size(); - if (HasName) - SectionSize += getULEB128Size(S.Name.size()) + S.Name.size(); - // Pad the LEB value out to 5 bytes to make it a predictable size, and - // match the behavior of clang. - encodeULEB128(SectionSize, OS, 5); - if (HasName) { - encodeULEB128(S.Name.size(), OS); - OS << S.Name; - } - // Total section size is the content size plus 1 for the section type and - // 5 for the LEB-encoded size. - SectionSize = SectionSize + 1 + 5; - return Header; -} - -size_t Writer::finalize() { - size_t ObjectSize = sizeof(WasmMagic) + sizeof(WasmVersion); - SectionHeaders.reserve(Obj.Sections.size()); - // Finalize the headers of each section so we know the total size. - for (const Section &S : Obj.Sections) { - size_t SectionSize; - SectionHeaders.push_back(createSectionHeader(S, SectionSize)); - ObjectSize += SectionSize; - } - return ObjectSize; -} - -Error Writer::write() { - size_t FileSize = finalize(); - if (Error E = Buf.allocate(FileSize)) - return E; - - // Write the header. - uint8_t *Ptr = Buf.getBufferStart(); - Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr); - support::endian::write32le(Ptr, Obj.Header.Version); - Ptr += sizeof(Obj.Header.Version); - - // Write each section. - for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) { - Ptr = std::copy(SectionHeaders[I].begin(), SectionHeaders[I].end(), Ptr); - ArrayRef<uint8_t> Contents = Obj.Sections[I].Contents; - Ptr = std::copy(Contents.begin(), Contents.end(), Ptr); - } - return Buf.commit(); -} - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm +//===- Writer.cpp ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "llvm/BinaryFormat/Wasm.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +Writer::SectionHeader Writer::createSectionHeader(const Section &S, + size_t &SectionSize) { + SectionHeader Header; + raw_svector_ostream OS(Header); + OS << S.SectionType; + bool HasName = S.SectionType == WASM_SEC_CUSTOM; + SectionSize = S.Contents.size(); + if (HasName) + SectionSize += getULEB128Size(S.Name.size()) + S.Name.size(); + // Pad the LEB value out to 5 bytes to make it a predictable size, and + // match the behavior of clang. + encodeULEB128(SectionSize, OS, 5); + if (HasName) { + encodeULEB128(S.Name.size(), OS); + OS << S.Name; + } + // Total section size is the content size plus 1 for the section type and + // 5 for the LEB-encoded size. + SectionSize = SectionSize + 1 + 5; + return Header; +} + +size_t Writer::finalize() { + size_t ObjectSize = sizeof(WasmMagic) + sizeof(WasmVersion); + SectionHeaders.reserve(Obj.Sections.size()); + // Finalize the headers of each section so we know the total size. + for (const Section &S : Obj.Sections) { + size_t SectionSize; + SectionHeaders.push_back(createSectionHeader(S, SectionSize)); + ObjectSize += SectionSize; + } + return ObjectSize; +} + +Error Writer::write() { + size_t FileSize = finalize(); + if (Error E = Buf.allocate(FileSize)) + return E; + + // Write the header. + uint8_t *Ptr = Buf.getBufferStart(); + Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr); + support::endian::write32le(Ptr, Obj.Header.Version); + Ptr += sizeof(Obj.Header.Version); + + // Write each section. + for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) { + Ptr = std::copy(SectionHeaders[I].begin(), SectionHeaders[I].end(), Ptr); + ArrayRef<uint8_t> Contents = Obj.Sections[I].Contents; + Ptr = std::copy(Contents.begin(), Contents.end(), Ptr); + } + return Buf.commit(); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.h b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.h index da48ee730c..5cfb04772e 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.h +++ b/contrib/libs/llvm12/tools/llvm-objcopy/wasm/Writer.h @@ -1,50 +1,50 @@ -//===- Writer.h -------------------------------------------------*- 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H -#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H - -#include "Buffer.h" -#include "Object.h" -#include <cstdint> -#include <vector> - -namespace llvm { -namespace objcopy { -namespace wasm { - -class Writer { -public: - Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {} - Error write(); - -private: - using SectionHeader = SmallVector<char, 8>; - Object &Obj; - Buffer &Buf; - std::vector<SectionHeader> SectionHeaders; - - /// Generate a wasm section section header for S. - /// The header consists of - /// * A one-byte section ID (aka the section type). - /// * The size of the section contents, encoded as ULEB128. - /// * If the section is a custom section (type 0) it also has a name, which is - /// encoded as a length-prefixed string. The encoded section size *includes* - /// this string. - /// See https://webassembly.github.io/spec/core/binary/modules.html#sections - /// Return the header and store the total size in SectionSize. - static SectionHeader createSectionHeader(const Section &S, - size_t &SectionSize); - size_t finalize(); -}; - -} // end namespace wasm -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H +//===- Writer.h -------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H + +#include "Buffer.h" +#include "Object.h" +#include <cstdint> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace wasm { + +class Writer { +public: + Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {} + Error write(); + +private: + using SectionHeader = SmallVector<char, 8>; + Object &Obj; + Buffer &Buf; + std::vector<SectionHeader> SectionHeaders; + + /// Generate a wasm section section header for S. + /// The header consists of + /// * A one-byte section ID (aka the section type). + /// * The size of the section contents, encoded as ULEB128. + /// * If the section is a custom section (type 0) it also has a name, which is + /// encoded as a length-prefixed string. The encoded section size *includes* + /// this string. + /// See https://webassembly.github.io/spec/core/binary/modules.html#sections + /// Return the header and store the total size in SectionSize. + static SectionHeader createSectionHeader(const Section &S, + size_t &SectionSize); + size_t finalize(); +}; + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H diff --git a/contrib/libs/llvm12/tools/llvm-objcopy/ya.make b/contrib/libs/llvm12/tools/llvm-objcopy/ya.make index 13845f4bfa..f4f40719c9 100644 --- a/contrib/libs/llvm12/tools/llvm-objcopy/ya.make +++ b/contrib/libs/llvm12/tools/llvm-objcopy/ya.make @@ -1,17 +1,17 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/BinaryFormat @@ -26,37 +26,37 @@ PEERDIR( contrib/libs/llvm12/lib/Remarks contrib/libs/llvm12/lib/Support contrib/libs/llvm12/lib/TextAPI/MachO -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-objcopy contrib/libs/llvm12/tools/llvm-objcopy -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - Buffer.cpp - COFF/COFFObjcopy.cpp - COFF/Object.cpp - COFF/Reader.cpp - COFF/Writer.cpp - CopyConfig.cpp - ELF/ELFConfig.cpp - ELF/ELFObjcopy.cpp - ELF/Object.cpp - MachO/MachOLayoutBuilder.cpp - MachO/MachOObjcopy.cpp - MachO/MachOReader.cpp - MachO/MachOWriter.cpp - MachO/Object.cpp - llvm-objcopy.cpp - wasm/Object.cpp - wasm/Reader.cpp - wasm/WasmObjcopy.cpp - wasm/Writer.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + Buffer.cpp + COFF/COFFObjcopy.cpp + COFF/Object.cpp + COFF/Reader.cpp + COFF/Writer.cpp + CopyConfig.cpp + ELF/ELFConfig.cpp + ELF/ELFObjcopy.cpp + ELF/Object.cpp + MachO/MachOLayoutBuilder.cpp + MachO/MachOObjcopy.cpp + MachO/MachOReader.cpp + MachO/MachOWriter.cpp + MachO/Object.cpp + llvm-objcopy.cpp + wasm/Object.cpp + wasm/Reader.cpp + wasm/WasmObjcopy.cpp + wasm/Writer.cpp +) + +END() diff --git a/contrib/libs/llvm12/tools/llvm-rc/Opts.td b/contrib/libs/llvm12/tools/llvm-rc/Opts.td index 613f0a0db3..27b4f182a2 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/Opts.td +++ b/contrib/libs/llvm12/tools/llvm-rc/Opts.td @@ -1,55 +1,55 @@ -include "llvm/Option/OptParser.td" - -// All the switches can be preceded by either '/' or '-'. -// These options seem to be important for the tool -// and should be implemented. - +include "llvm/Option/OptParser.td" + +// All the switches can be preceded by either '/' or '-'. +// These options seem to be important for the tool +// and should be implemented. + def fileout : JoinedOrSeparate<[ "/", "-" ], "FO">, - HelpText<"Change the output file location.">; - + HelpText<"Change the output file location.">; + def define : Separate<[ "/", "-" ], "D">, - HelpText<"Define a symbol for the C preprocessor.">; + HelpText<"Define a symbol for the C preprocessor.">; def undef : Separate<[ "/", "-" ], "U">, - HelpText<"Undefine a symbol for the C preprocessor.">; - + HelpText<"Undefine a symbol for the C preprocessor.">; + def lang_id : JoinedOrSeparate<[ "/", "-" ], "L">, - HelpText<"Set the default language identifier.">; + HelpText<"Set the default language identifier.">; def lang_name : Separate<[ "/", "-" ], "LN">, - HelpText<"Set the default language name.">; - + HelpText<"Set the default language name.">; + def includepath : Separate<[ "/", "-" ], "I">, HelpText<"Add an include path.">; def noinclude : Flag<[ "/", "-" ], "X">, HelpText<"Ignore 'include' variable.">; - + def add_null : Flag<[ "/", "-" ], "N">, - HelpText<"Null-terminate all strings in the string table.">; - + HelpText<"Null-terminate all strings in the string table.">; + def dupid_nowarn : Flag<[ "/", "-" ], "Y">, - HelpText<"Suppress warnings on duplicate resource IDs.">; - + HelpText<"Suppress warnings on duplicate resource IDs.">; + def verbose : Flag<[ "/", "-" ], "V">, HelpText<"Be verbose.">; def help : Flag<[ "/", "-" ], "?">, HelpText<"Display this help and exit.">; def h : Flag<[ "/", "-" ], "H">, Alias<help>, - HelpText<"Display this help and exit.">; - + HelpText<"Display this help and exit.">; + def dry_run : Flag<[ "/", "-" ], "dry-run">, - HelpText<"Don't compile the input; only try to parse it.">; - + HelpText<"Don't compile the input; only try to parse it.">; + def codepage : JoinedOrSeparate<[ "/", "-" ], "C">, - HelpText<"Set the codepage used for input strings.">; - -// Unused switches (at least for now). These will stay unimplemented -// in an early stage of development and can be ignored. However, we need to -// parse them in order to preserve the compatibility with the original tool. - + HelpText<"Set the codepage used for input strings.">; + +// Unused switches (at least for now). These will stay unimplemented +// in an early stage of development and can be ignored. However, we need to +// parse them in order to preserve the compatibility with the original tool. + def nologo : Flag<[ "/", "-" ], "NOLOGO">; def r : Flag<[ "/", "-" ], "R">; def sl : Flag<[ "/", "-" ], "SL">; - -// (Codepages support.) + +// (Codepages support.) def w : Flag<[ "/", "-" ], "W">; - -// (Support of MUI and similar.) + +// (Support of MUI and similar.) def fm : Separate<[ "/", "-" ], "FM">; def q : Separate<[ "/", "-" ], "Q">; def g : Flag<[ "/", "-" ], "G">; diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.cpp b/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.cpp index 553bb754ae..a7418ba2f1 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.cpp +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.cpp @@ -1,1519 +1,1519 @@ -//===-- ResourceFileWriter.cpp --------------------------------*- 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 implements the visitor serializing resources to a .res stream. -// -//===---------------------------------------------------------------------===// - -#include "ResourceFileWriter.h" -#include "llvm/Object/WindowsResource.h" -#include "llvm/Support/ConvertUTF.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/EndianStream.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Process.h" - -using namespace llvm::support; - -// Take an expression returning llvm::Error and forward the error if it exists. -#define RETURN_IF_ERROR(Expr) \ - if (auto Err = (Expr)) \ - return Err; - -namespace llvm { -namespace rc { - -// Class that employs RAII to save the current FileWriter object state -// and revert to it as soon as we leave the scope. This is useful if resources -// declare their own resource-local statements. -class ContextKeeper { - ResourceFileWriter *FileWriter; - ResourceFileWriter::ObjectInfo SavedInfo; - -public: - ContextKeeper(ResourceFileWriter *V) - : FileWriter(V), SavedInfo(V->ObjectData) {} - ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; } -}; - -static Error createError(const Twine &Message, - std::errc Type = std::errc::invalid_argument) { - return make_error<StringError>(Message, std::make_error_code(Type)); -} - -static Error checkNumberFits(uint32_t Number, size_t MaxBits, - const Twine &FieldName) { - assert(1 <= MaxBits && MaxBits <= 32); - if (!(Number >> MaxBits)) - return Error::success(); - return createError(FieldName + " (" + Twine(Number) + ") does not fit in " + - Twine(MaxBits) + " bits.", - std::errc::value_too_large); -} - -template <typename FitType> -static Error checkNumberFits(uint32_t Number, const Twine &FieldName) { - return checkNumberFits(Number, sizeof(FitType) * 8, FieldName); -} - -// A similar function for signed integers. -template <typename FitType> -static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName, - bool CanBeNegative) { - int32_t SignedNum = Number; - if (SignedNum < std::numeric_limits<FitType>::min() || - SignedNum > std::numeric_limits<FitType>::max()) - return createError(FieldName + " (" + Twine(SignedNum) + - ") does not fit in " + Twine(sizeof(FitType) * 8) + - "-bit signed integer type.", - std::errc::value_too_large); - - if (!CanBeNegative && SignedNum < 0) - return createError(FieldName + " (" + Twine(SignedNum) + - ") cannot be negative."); - - return Error::success(); -} - -static Error checkRCInt(RCInt Number, const Twine &FieldName) { - if (Number.isLong()) - return Error::success(); - return checkNumberFits<uint16_t>(Number, FieldName); -} - -static Error checkIntOrString(IntOrString Value, const Twine &FieldName) { - if (!Value.isInt()) - return Error::success(); - return checkNumberFits<uint16_t>(Value.getInt(), FieldName); -} - -static bool stripQuotes(StringRef &Str, bool &IsLongString) { - if (!Str.contains('"')) - return false; - - // Just take the contents of the string, checking if it's been marked long. - IsLongString = Str.startswith_lower("L"); - if (IsLongString) - Str = Str.drop_front(); - - bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\""); - (void)StripSuccess; - assert(StripSuccess && "Strings should be enclosed in quotes."); - return true; -} - -static UTF16 cp1252ToUnicode(unsigned char C) { - static const UTF16 Map80[] = { - 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, - 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, - 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, - 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, - }; - if (C >= 0x80 && C <= 0x9F) - return Map80[C - 0x80]; - return C; -} - -// Describes a way to handle '\0' characters when processing the string. -// rc.exe tool sometimes behaves in a weird way in postprocessing. -// If the string to be output is equivalent to a C-string (e.g. in MENU -// titles), string is (predictably) truncated after first 0-byte. -// When outputting a string table, the behavior is equivalent to appending -// '\0\0' at the end of the string, and then stripping the string -// before the first '\0\0' occurrence. -// Finally, when handling strings in user-defined resources, 0-bytes -// aren't stripped, nor do they terminate the string. - -enum class NullHandlingMethod { - UserResource, // Don't terminate string on '\0'. - CutAtNull, // Terminate string on '\0'. - CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'. -}; - -// Parses an identifier or string and returns a processed version of it: +//===-- ResourceFileWriter.cpp --------------------------------*- 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 implements the visitor serializing resources to a .res stream. +// +//===---------------------------------------------------------------------===// + +#include "ResourceFileWriter.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" + +using namespace llvm::support; + +// Take an expression returning llvm::Error and forward the error if it exists. +#define RETURN_IF_ERROR(Expr) \ + if (auto Err = (Expr)) \ + return Err; + +namespace llvm { +namespace rc { + +// Class that employs RAII to save the current FileWriter object state +// and revert to it as soon as we leave the scope. This is useful if resources +// declare their own resource-local statements. +class ContextKeeper { + ResourceFileWriter *FileWriter; + ResourceFileWriter::ObjectInfo SavedInfo; + +public: + ContextKeeper(ResourceFileWriter *V) + : FileWriter(V), SavedInfo(V->ObjectData) {} + ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; } +}; + +static Error createError(const Twine &Message, + std::errc Type = std::errc::invalid_argument) { + return make_error<StringError>(Message, std::make_error_code(Type)); +} + +static Error checkNumberFits(uint32_t Number, size_t MaxBits, + const Twine &FieldName) { + assert(1 <= MaxBits && MaxBits <= 32); + if (!(Number >> MaxBits)) + return Error::success(); + return createError(FieldName + " (" + Twine(Number) + ") does not fit in " + + Twine(MaxBits) + " bits.", + std::errc::value_too_large); +} + +template <typename FitType> +static Error checkNumberFits(uint32_t Number, const Twine &FieldName) { + return checkNumberFits(Number, sizeof(FitType) * 8, FieldName); +} + +// A similar function for signed integers. +template <typename FitType> +static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName, + bool CanBeNegative) { + int32_t SignedNum = Number; + if (SignedNum < std::numeric_limits<FitType>::min() || + SignedNum > std::numeric_limits<FitType>::max()) + return createError(FieldName + " (" + Twine(SignedNum) + + ") does not fit in " + Twine(sizeof(FitType) * 8) + + "-bit signed integer type.", + std::errc::value_too_large); + + if (!CanBeNegative && SignedNum < 0) + return createError(FieldName + " (" + Twine(SignedNum) + + ") cannot be negative."); + + return Error::success(); +} + +static Error checkRCInt(RCInt Number, const Twine &FieldName) { + if (Number.isLong()) + return Error::success(); + return checkNumberFits<uint16_t>(Number, FieldName); +} + +static Error checkIntOrString(IntOrString Value, const Twine &FieldName) { + if (!Value.isInt()) + return Error::success(); + return checkNumberFits<uint16_t>(Value.getInt(), FieldName); +} + +static bool stripQuotes(StringRef &Str, bool &IsLongString) { + if (!Str.contains('"')) + return false; + + // Just take the contents of the string, checking if it's been marked long. + IsLongString = Str.startswith_lower("L"); + if (IsLongString) + Str = Str.drop_front(); + + bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\""); + (void)StripSuccess; + assert(StripSuccess && "Strings should be enclosed in quotes."); + return true; +} + +static UTF16 cp1252ToUnicode(unsigned char C) { + static const UTF16 Map80[] = { + 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, + }; + if (C >= 0x80 && C <= 0x9F) + return Map80[C - 0x80]; + return C; +} + +// Describes a way to handle '\0' characters when processing the string. +// rc.exe tool sometimes behaves in a weird way in postprocessing. +// If the string to be output is equivalent to a C-string (e.g. in MENU +// titles), string is (predictably) truncated after first 0-byte. +// When outputting a string table, the behavior is equivalent to appending +// '\0\0' at the end of the string, and then stripping the string +// before the first '\0\0' occurrence. +// Finally, when handling strings in user-defined resources, 0-bytes +// aren't stripped, nor do they terminate the string. + +enum class NullHandlingMethod { + UserResource, // Don't terminate string on '\0'. + CutAtNull, // Terminate string on '\0'. + CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'. +}; + +// Parses an identifier or string and returns a processed version of it: // * Strip the string boundary quotes. // * Convert the input code page characters to UTF16. -// * Squash "" to a single ". -// * Replace the escape sequences with their processed version. -// For identifiers, this is no-op. -static Error processString(StringRef Str, NullHandlingMethod NullHandler, - bool &IsLongString, SmallVectorImpl<UTF16> &Result, - int CodePage) { - bool IsString = stripQuotes(Str, IsLongString); - SmallVector<UTF16, 128> Chars; - - // Convert the input bytes according to the chosen codepage. - if (CodePage == CpUtf8) { - convertUTF8ToUTF16String(Str, Chars); - } else if (CodePage == CpWin1252) { - for (char C : Str) - Chars.push_back(cp1252ToUnicode((unsigned char)C)); - } else { - // For other, unknown codepages, only allow plain ASCII input. - for (char C : Str) { - if ((unsigned char)C > 0x7F) - return createError("Non-ASCII 8-bit codepoint (" + Twine(C) + - ") can't be interpreted in the current codepage"); - Chars.push_back((unsigned char)C); - } - } - - if (!IsString) { - // It's an identifier if it's not a string. Make all characters uppercase. - for (UTF16 &Ch : Chars) { - assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII"); - Ch = toupper(Ch); - } - Result.swap(Chars); - return Error::success(); - } - Result.reserve(Chars.size()); - size_t Pos = 0; - - auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error { - if (!IsLongString) { - if (NullHandler == NullHandlingMethod::UserResource) { - // Narrow strings in user-defined resources are *not* output in - // UTF-16 format. - if (Char > 0xFF) - return createError("Non-8-bit codepoint (" + Twine(Char) + - ") can't occur in a user-defined narrow string"); - } - } - - Result.push_back(Char); - return Error::success(); - }; - auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error { - if (!IsLongString) { - // Escaped chars in narrow strings have to be interpreted according to - // the chosen code page. - if (Char > 0xFF) - return createError("Non-8-bit escaped char (" + Twine(Char) + - ") can't occur in narrow string"); - if (CodePage == CpUtf8) { - if (Char >= 0x80) - return createError("Unable to interpret single byte (" + Twine(Char) + - ") as UTF-8"); - } else if (CodePage == CpWin1252) { - Char = cp1252ToUnicode(Char); - } else { - // Unknown/unsupported codepage, only allow ASCII input. - if (Char > 0x7F) - return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) + - ") can't " - "occur in a non-Unicode string"); - } - } - - return AddRes(Char); - }; - - while (Pos < Chars.size()) { - UTF16 CurChar = Chars[Pos]; - ++Pos; - - // Strip double "". - if (CurChar == '"') { - if (Pos == Chars.size() || Chars[Pos] != '"') - return createError("Expected \"\""); - ++Pos; - RETURN_IF_ERROR(AddRes('"')); - continue; - } - - if (CurChar == '\\') { - UTF16 TypeChar = Chars[Pos]; - ++Pos; - - if (TypeChar == 'x' || TypeChar == 'X') { - // Read a hex number. Max number of characters to read differs between - // narrow and wide strings. - UTF16 ReadInt = 0; - size_t RemainingChars = IsLongString ? 4 : 2; - // We don't want to read non-ASCII hex digits. std:: functions past - // 0xFF invoke UB. - // - // FIXME: actually, Microsoft version probably doesn't check this - // condition and uses their Unicode version of 'isxdigit'. However, - // there are some hex-digit Unicode character outside of ASCII, and - // some of these are actually accepted by rc.exe, the notable example - // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written - // instead of ASCII digits in \x... escape sequence and get accepted. - // However, the resulting hexcodes seem totally unpredictable. - // We think it's infeasible to try to reproduce this behavior, nor to - // put effort in order to detect it. - while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) { - if (!isxdigit(Chars[Pos])) - break; - char Digit = tolower(Chars[Pos]); - ++Pos; - - ReadInt <<= 4; - if (isdigit(Digit)) - ReadInt |= Digit - '0'; - else - ReadInt |= Digit - 'a' + 10; - - --RemainingChars; - } - - RETURN_IF_ERROR(AddEscapedChar(ReadInt)); - continue; - } - - if (TypeChar >= '0' && TypeChar < '8') { - // Read an octal number. Note that we've already read the first digit. - UTF16 ReadInt = TypeChar - '0'; - size_t RemainingChars = IsLongString ? 6 : 2; - - while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' && - Chars[Pos] < '8') { - ReadInt <<= 3; - ReadInt |= Chars[Pos] - '0'; - --RemainingChars; - ++Pos; - } - - RETURN_IF_ERROR(AddEscapedChar(ReadInt)); - - continue; - } - - switch (TypeChar) { - case 'A': - case 'a': - // Windows '\a' translates into '\b' (Backspace). - RETURN_IF_ERROR(AddRes('\b')); - break; - - case 'n': // Somehow, RC doesn't recognize '\N' and '\R'. - RETURN_IF_ERROR(AddRes('\n')); - break; - - case 'r': - RETURN_IF_ERROR(AddRes('\r')); - break; - - case 'T': - case 't': - RETURN_IF_ERROR(AddRes('\t')); - break; - - case '\\': - RETURN_IF_ERROR(AddRes('\\')); - break; - - case '"': - // RC accepts \" only if another " comes afterwards; then, \"" means - // a single ". - if (Pos == Chars.size() || Chars[Pos] != '"') - return createError("Expected \\\"\""); - ++Pos; - RETURN_IF_ERROR(AddRes('"')); - break; - - default: - // If TypeChar means nothing, \ is should be output to stdout with - // following char. However, rc.exe consumes these characters when - // dealing with wide strings. - if (!IsLongString) { - RETURN_IF_ERROR(AddRes('\\')); - RETURN_IF_ERROR(AddRes(TypeChar)); - } - break; - } - - continue; - } - - // If nothing interesting happens, just output the character. - RETURN_IF_ERROR(AddRes(CurChar)); - } - - switch (NullHandler) { - case NullHandlingMethod::CutAtNull: - for (size_t Pos = 0; Pos < Result.size(); ++Pos) - if (Result[Pos] == '\0') - Result.resize(Pos); - break; - - case NullHandlingMethod::CutAtDoubleNull: - for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos) - if (Result[Pos] == '\0' && Result[Pos + 1] == '\0') - Result.resize(Pos); - if (Result.size() > 0 && Result.back() == '\0') - Result.pop_back(); - break; - - case NullHandlingMethod::UserResource: - break; - } - - return Error::success(); -} - -uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) { - uint64_t Result = tell(); - FS->write((const char *)Data.begin(), Data.size()); - return Result; -} - -Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) { - SmallVector<UTF16, 128> ProcessedString; - bool IsLongString; - RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull, - IsLongString, ProcessedString, - Params.CodePage)); - for (auto Ch : ProcessedString) - writeInt<uint16_t>(Ch); - if (WriteTerminator) - writeInt<uint16_t>(0); - return Error::success(); -} - -Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) { - return writeIntOrString(Ident); -} - -Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) { - if (!Value.isInt()) - return writeCString(Value.getString()); - - writeInt<uint16_t>(0xFFFF); - writeInt<uint16_t>(Value.getInt()); - return Error::success(); -} - -void ResourceFileWriter::writeRCInt(RCInt Value) { - if (Value.isLong()) - writeInt<uint32_t>(Value); - else - writeInt<uint16_t>(Value); -} - -Error ResourceFileWriter::appendFile(StringRef Filename) { - bool IsLong; - stripQuotes(Filename, IsLong); - - auto File = loadFile(Filename); - if (!File) - return File.takeError(); - - *FS << (*File)->getBuffer(); - return Error::success(); -} - -void ResourceFileWriter::padStream(uint64_t Length) { - assert(Length > 0); - uint64_t Location = tell(); - Location %= Length; - uint64_t Pad = (Length - Location) % Length; - for (uint64_t i = 0; i < Pad; ++i) - writeInt<uint8_t>(0); -} - -Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) { - if (Err) - return joinErrors(createError("Error in " + Res->getResourceTypeName() + - " statement (ID " + Twine(Res->ResName) + - "): "), - std::move(Err)); - return Error::success(); -} - -Error ResourceFileWriter::visitNullResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeNullBody); -} - -Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); -} - -Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeBitmapBody); -} - -Error ResourceFileWriter::visitCursorResource(const RCResource *Res) { - return handleError(visitIconOrCursorResource(Res), Res); -} - -Error ResourceFileWriter::visitDialogResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeDialogBody); -} - -Error ResourceFileWriter::visitIconResource(const RCResource *Res) { - return handleError(visitIconOrCursorResource(Res), Res); -} - -Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) { - ObjectData.Caption = Stmt->Value; - return Error::success(); -} - -Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) { - ObjectData.Class = Stmt->Value; - return Error::success(); -} - -Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeHTMLBody); -} - -Error ResourceFileWriter::visitMenuResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeMenuBody); -} - -Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) { - const auto *Res = cast<StringTableResource>(Base); - - ContextKeeper RAII(this); - RETURN_IF_ERROR(Res->applyStmts(this)); - - for (auto &String : Res->Table) { - RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID")); - uint16_t BundleID = String.first >> 4; - StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo); - auto &BundleData = StringTableData.BundleData; - auto Iter = BundleData.find(Key); - - if (Iter == BundleData.end()) { - // Need to create a bundle. - StringTableData.BundleList.push_back(Key); - auto EmplaceResult = BundleData.emplace( - Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags)); - assert(EmplaceResult.second && "Could not create a bundle"); - Iter = EmplaceResult.first; - } - - RETURN_IF_ERROR( - insertStringIntoBundle(Iter->second, String.first, String.second)); - } - - return Error::success(); -} - -Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody); -} - -Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody); -} - -Error ResourceFileWriter::visitCharacteristicsStmt( - const CharacteristicsStmt *Stmt) { - ObjectData.Characteristics = Stmt->Value; - return Error::success(); -} - -Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) { - ObjectData.ExStyle = Stmt->Value; - return Error::success(); -} - -Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) { - RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size")); - RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight")); - RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset")); - ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic, - Stmt->Charset}; - ObjectData.Font.emplace(Font); - return Error::success(); -} - -Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) { - RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); - RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); - ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10); - return Error::success(); -} - -Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) { - ObjectData.Style = Stmt->Value; - return Error::success(); -} - -Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) { - ObjectData.VersionInfo = Stmt->Value; - return Error::success(); -} - -Error ResourceFileWriter::writeResource( - const RCResource *Res, - Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) { - // We don't know the sizes yet. - object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)}; - uint64_t HeaderLoc = writeObject(HeaderPrefix); - - auto ResType = Res->getResourceType(); - RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type")); - RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID")); - RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res)); - RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res)); - - // Apply the resource-local optional statements. - ContextKeeper RAII(this); - RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res)); - - padStream(sizeof(uint32_t)); - object::WinResHeaderSuffix HeaderSuffix{ - ulittle32_t(0), // DataVersion; seems to always be 0 - ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo), - ulittle32_t(ObjectData.VersionInfo), - ulittle32_t(ObjectData.Characteristics)}; - writeObject(HeaderSuffix); - - uint64_t DataLoc = tell(); - RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res)); - // RETURN_IF_ERROR(handleError(dumpResource(Ctx))); - - // Update the sizes. - HeaderPrefix.DataSize = tell() - DataLoc; - HeaderPrefix.HeaderSize = DataLoc - HeaderLoc; - writeObjectAt(HeaderPrefix, HeaderLoc); - padStream(sizeof(uint32_t)); - - return Error::success(); -} - -// --- NullResource helpers. --- // - -Error ResourceFileWriter::writeNullBody(const RCResource *) { - return Error::success(); -} - -// --- AcceleratorsResource helpers. --- // - -Error ResourceFileWriter::writeSingleAccelerator( - const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) { - using Accelerator = AcceleratorsResource::Accelerator; - using Opt = Accelerator::Options; - - struct AccelTableEntry { - ulittle16_t Flags; - ulittle16_t ANSICode; - ulittle16_t Id; - uint16_t Padding; - } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0}; - - bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY; - - // Remove ASCII flags (which doesn't occur in .res files). - Entry.Flags = Obj.Flags & ~Opt::ASCII; - - if (IsLastItem) - Entry.Flags |= 0x80; - - RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID")); - Entry.Id = ulittle16_t(Obj.Id); - - auto createAccError = [&Obj](const char *Msg) { - return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg); - }; - - if (IsASCII && IsVirtKey) - return createAccError("Accelerator can't be both ASCII and VIRTKEY"); - - if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL))) - return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY" - " accelerators"); - - if (Obj.Event.isInt()) { - if (!IsASCII && !IsVirtKey) - return createAccError( - "Accelerator with a numeric event must be either ASCII" - " or VIRTKEY"); - - uint32_t EventVal = Obj.Event.getInt(); - RETURN_IF_ERROR( - checkNumberFits<uint16_t>(EventVal, "Numeric event key ID")); - Entry.ANSICode = ulittle16_t(EventVal); - writeObject(Entry); - return Error::success(); - } - - StringRef Str = Obj.Event.getString(); - bool IsWide; - stripQuotes(Str, IsWide); - - if (Str.size() == 0 || Str.size() > 2) - return createAccError( - "Accelerator string events should have length 1 or 2"); - - if (Str[0] == '^') { - if (Str.size() == 1) - return createAccError("No character following '^' in accelerator event"); - if (IsVirtKey) - return createAccError( - "VIRTKEY accelerator events can't be preceded by '^'"); - - char Ch = Str[1]; - if (Ch >= 'a' && Ch <= 'z') - Entry.ANSICode = ulittle16_t(Ch - 'a' + 1); - else if (Ch >= 'A' && Ch <= 'Z') - Entry.ANSICode = ulittle16_t(Ch - 'A' + 1); - else - return createAccError("Control character accelerator event should be" - " alphabetic"); - - writeObject(Entry); - return Error::success(); - } - - if (Str.size() == 2) - return createAccError("Event string should be one-character, possibly" - " preceded by '^'"); - - uint8_t EventCh = Str[0]; - // The original tool just warns in this situation. We chose to fail. - if (IsVirtKey && !isalnum(EventCh)) - return createAccError("Non-alphanumeric characters cannot describe virtual" - " keys"); - if (EventCh > 0x7F) - return createAccError("Non-ASCII description of accelerator"); - - if (IsVirtKey) - EventCh = toupper(EventCh); - Entry.ANSICode = ulittle16_t(EventCh); - writeObject(Entry); - return Error::success(); -} - -Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { - auto *Res = cast<AcceleratorsResource>(Base); - size_t AcceleratorId = 0; - for (auto &Acc : Res->Accelerators) { - ++AcceleratorId; - RETURN_IF_ERROR( - writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); - } - return Error::success(); -} - -// --- BitmapResource helpers. --- // - -Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) { - StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc; - bool IsLong; - stripQuotes(Filename, IsLong); - - auto File = loadFile(Filename); - if (!File) - return File.takeError(); - - StringRef Buffer = (*File)->getBuffer(); - - // Skip the 14 byte BITMAPFILEHEADER. - constexpr size_t BITMAPFILEHEADER_size = 14; - if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' || - Buffer[1] != 'M') - return createError("Incorrect bitmap file."); - - *FS << Buffer.substr(BITMAPFILEHEADER_size); - return Error::success(); -} - -// --- CursorResource and IconResource helpers. --- // - -// ICONRESDIR structure. Describes a single icon in resource group. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx -struct IconResDir { - uint8_t Width; - uint8_t Height; - uint8_t ColorCount; - uint8_t Reserved; -}; - -// CURSORDIR structure. Describes a single cursor in resource group. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx -struct CursorDir { - ulittle16_t Width; - ulittle16_t Height; -}; - -// RESDIRENTRY structure, stripped from the last item. Stripping made -// for compatibility with RESDIR. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx -struct ResourceDirEntryStart { - union { - CursorDir Cursor; // Used in CURSOR resources. - IconResDir Icon; // Used in .ico and .cur files, and ICON resources. - }; - ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource). - ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource). - ulittle32_t Size; - // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only). - // ulittle16_t IconID; // Resource icon ID (RESDIR only). -}; - -// BITMAPINFOHEADER structure. Describes basic information about the bitmap -// being read. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx -struct BitmapInfoHeader { - ulittle32_t Size; - ulittle32_t Width; - ulittle32_t Height; - ulittle16_t Planes; - ulittle16_t BitCount; - ulittle32_t Compression; - ulittle32_t SizeImage; - ulittle32_t XPelsPerMeter; - ulittle32_t YPelsPerMeter; - ulittle32_t ClrUsed; - ulittle32_t ClrImportant; -}; - -// Group icon directory header. Called ICONDIR in .ico/.cur files and -// NEWHEADER in .res files. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx -struct GroupIconDir { - ulittle16_t Reserved; // Always 0. - ulittle16_t ResType; // 1 for icons, 2 for cursors. - ulittle16_t ResCount; // Number of items. -}; - -enum class IconCursorGroupType { Icon, Cursor }; - -class SingleIconCursorResource : public RCResource { -public: - IconCursorGroupType Type; - const ResourceDirEntryStart &Header; - ArrayRef<uint8_t> Image; - - SingleIconCursorResource(IconCursorGroupType ResourceType, - const ResourceDirEntryStart &HeaderEntry, - ArrayRef<uint8_t> ImageData, uint16_t Flags) - : RCResource(Flags), Type(ResourceType), Header(HeaderEntry), - Image(ImageData) {} - - Twine getResourceTypeName() const override { return "Icon/cursor image"; } - IntOrString getResourceType() const override { - return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor; - } - ResourceKind getKind() const override { return RkSingleCursorOrIconRes; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkSingleCursorOrIconRes; - } -}; - -class IconCursorGroupResource : public RCResource { -public: - IconCursorGroupType Type; - GroupIconDir Header; - std::vector<ResourceDirEntryStart> ItemEntries; - - IconCursorGroupResource(IconCursorGroupType ResourceType, - const GroupIconDir &HeaderData, - std::vector<ResourceDirEntryStart> &&Entries) - : Type(ResourceType), Header(HeaderData), - ItemEntries(std::move(Entries)) {} - - Twine getResourceTypeName() const override { return "Icon/cursor group"; } - IntOrString getResourceType() const override { - return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup; - } - ResourceKind getKind() const override { return RkCursorOrIconGroupRes; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkCursorOrIconGroupRes; - } -}; - -Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) { - auto *Res = cast<SingleIconCursorResource>(Base); - if (Res->Type == IconCursorGroupType::Cursor) { - // In case of cursors, two WORDS are appended to the beginning - // of the resource: HotspotX (Planes in RESDIRENTRY), - // and HotspotY (BitCount). - // - // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx - // (Remarks section). - writeObject(Res->Header.Planes); - writeObject(Res->Header.BitCount); - } - - writeObject(Res->Image); - return Error::success(); -} - -Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) { - auto *Res = cast<IconCursorGroupResource>(Base); - writeObject(Res->Header); - for (auto Item : Res->ItemEntries) { - writeObject(Item); - writeInt(IconCursorID++); - } - return Error::success(); -} - -Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody); -} - -Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody); -} - -Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) { - IconCursorGroupType Type; - StringRef FileStr; - IntOrString ResName = Base->ResName; - - if (auto *IconRes = dyn_cast<IconResource>(Base)) { - FileStr = IconRes->IconLoc; - Type = IconCursorGroupType::Icon; - } else { - auto *CursorRes = dyn_cast<CursorResource>(Base); - FileStr = CursorRes->CursorLoc; - Type = IconCursorGroupType::Cursor; - } - - bool IsLong; - stripQuotes(FileStr, IsLong); - auto File = loadFile(FileStr); - - if (!File) - return File.takeError(); - - BinaryStreamReader Reader((*File)->getBuffer(), support::little); - - // Read the file headers. - // - At the beginning, ICONDIR/NEWHEADER header. - // - Then, a number of RESDIR headers follow. These contain offsets - // to data. - const GroupIconDir *Header; - - RETURN_IF_ERROR(Reader.readObject(Header)); - if (Header->Reserved != 0) - return createError("Incorrect icon/cursor Reserved field; should be 0."); - uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2; - if (Header->ResType != NeededType) - return createError("Incorrect icon/cursor ResType field; should be " + - Twine(NeededType) + "."); - - uint16_t NumItems = Header->ResCount; - - // Read single ico/cur headers. - std::vector<ResourceDirEntryStart> ItemEntries; - ItemEntries.reserve(NumItems); - std::vector<uint32_t> ItemOffsets(NumItems); - for (size_t ID = 0; ID < NumItems; ++ID) { - const ResourceDirEntryStart *Object; - RETURN_IF_ERROR(Reader.readObject(Object)); - ItemEntries.push_back(*Object); - RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID])); - } - - // Now write each icon/cursors one by one. At first, all the contents - // without ICO/CUR header. This is described by SingleIconCursorResource. - for (size_t ID = 0; ID < NumItems; ++ID) { - // Load the fragment of file. - Reader.setOffset(ItemOffsets[ID]); - ArrayRef<uint8_t> Image; - RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size)); - SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image, - Base->MemoryFlags); - SingleRes.setName(IconCursorID + ID); - RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes)); - } - - // Now, write all the headers concatenated into a separate resource. - for (size_t ID = 0; ID < NumItems; ++ID) { - // We need to rewrite the cursor headers, and fetch actual values - // for Planes/BitCount. - const auto &OldHeader = ItemEntries[ID]; - ResourceDirEntryStart NewHeader = OldHeader; - - if (Type == IconCursorGroupType::Cursor) { - NewHeader.Cursor.Width = OldHeader.Icon.Width; - // Each cursor in fact stores two bitmaps, one under another. - // Height provided in cursor definition describes the height of the - // cursor, whereas the value existing in resource definition describes - // the height of the bitmap. Therefore, we need to double this height. - NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; - - // Two WORDs were written at the beginning of the resource (hotspot - // location). This is reflected in Size field. - NewHeader.Size += 2 * sizeof(uint16_t); - } - - // Now, we actually need to read the bitmap header to find - // the number of planes and the number of bits per pixel. - Reader.setOffset(ItemOffsets[ID]); - const BitmapInfoHeader *BMPHeader; - RETURN_IF_ERROR(Reader.readObject(BMPHeader)); - if (BMPHeader->Size == sizeof(BitmapInfoHeader)) { - NewHeader.Planes = BMPHeader->Planes; - NewHeader.BitCount = BMPHeader->BitCount; - } else { - // A PNG .ico file. - // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473 - // "The image must be in 32bpp" - NewHeader.Planes = 1; - NewHeader.BitCount = 32; - } - - ItemEntries[ID] = NewHeader; - } - - IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries)); - HeaderRes.setName(ResName); - if (Base->MemoryFlags & MfPreload) { - HeaderRes.MemoryFlags |= MfPreload; - HeaderRes.MemoryFlags &= ~MfPure; - } - RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes)); - - return Error::success(); -} - -// --- DialogResource helpers. --- // - -Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, - bool IsExtended) { - // Each control should be aligned to DWORD. - padStream(sizeof(uint32_t)); - - auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type); - IntWithNotMask CtlStyle(TypeInfo.Style); - CtlStyle |= Ctl.Style.getValueOr(RCInt(0)); - uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0); - - // DIALOG(EX) item header prefix. - if (!IsExtended) { - struct { - ulittle32_t Style; - ulittle32_t ExtStyle; - } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)}; - writeObject(Prefix); - } else { - struct { - ulittle32_t HelpID; - ulittle32_t ExtStyle; - ulittle32_t Style; - } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle), - ulittle32_t(CtlStyle.getValue())}; - writeObject(Prefix); - } - - // Common fixed-length part. - RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( - Ctl.X, "Dialog control x-coordinate", true)); - RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( - Ctl.Y, "Dialog control y-coordinate", true)); - RETURN_IF_ERROR( - checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false)); - RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( - Ctl.Height, "Dialog control height", false)); - struct { - ulittle16_t X; - ulittle16_t Y; - ulittle16_t Width; - ulittle16_t Height; - } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width), - ulittle16_t(Ctl.Height)}; - writeObject(Middle); - - // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX. - if (!IsExtended) { - // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't - // want to refer to later. - if (Ctl.ID != static_cast<uint32_t>(-1)) - RETURN_IF_ERROR(checkNumberFits<uint16_t>( - Ctl.ID, "Control ID in simple DIALOG resource")); - writeInt<uint16_t>(Ctl.ID); - } else { - writeInt<uint32_t>(Ctl.ID); - } - - // Window class - either 0xFFFF + 16-bit integer or a string. - RETURN_IF_ERROR(writeIntOrString(Ctl.Class)); - - // Element caption/reference ID. ID is preceded by 0xFFFF. - RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID")); - RETURN_IF_ERROR(writeIntOrString(Ctl.Title)); - - // # bytes of extra creation data count. Don't pass any. - writeInt<uint16_t>(0); - - return Error::success(); -} - -Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { - auto *Res = cast<DialogResource>(Base); - - // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU. - const uint32_t DefaultStyle = 0x80880000; - const uint32_t StyleFontFlag = 0x40; - const uint32_t StyleCaptionFlag = 0x00C00000; - - uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle); - if (ObjectData.Font) - UsedStyle |= StyleFontFlag; - else - UsedStyle &= ~StyleFontFlag; - - // Actually, in case of empty (but existent) caption, the examined field - // is equal to "\"\"". That's why empty captions are still noticed. - if (ObjectData.Caption != "") - UsedStyle |= StyleCaptionFlag; - - const uint16_t DialogExMagic = 0xFFFF; - uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0); - - // Write DIALOG(EX) header prefix. These are pretty different. - if (!Res->IsExtended) { - // We cannot let the higher word of DefaultStyle be equal to 0xFFFF. - // In such a case, whole object (in .res file) is equivalent to a - // DIALOGEX. It might lead to access violation/segmentation fault in - // resource readers. For example, - // 1 DIALOG 0, 0, 0, 65432 - // STYLE 0xFFFF0001 {} - // would be compiled to a DIALOGEX with 65432 controls. - if ((UsedStyle >> 16) == DialogExMagic) - return createError("16 higher bits of DIALOG resource style cannot be" - " equal to 0xFFFF"); - - struct { - ulittle32_t Style; - ulittle32_t ExtStyle; - } Prefix{ulittle32_t(UsedStyle), - ulittle32_t(ExStyle)}; - - writeObject(Prefix); - } else { - struct { - ulittle16_t Version; - ulittle16_t Magic; - ulittle32_t HelpID; - ulittle32_t ExtStyle; - ulittle32_t Style; - } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic), - ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)}; - - writeObject(Prefix); - } - - // Now, a common part. First, fixed-length fields. - RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(), - "Number of dialog controls")); - RETURN_IF_ERROR( - checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true)); - RETURN_IF_ERROR( - checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true)); - RETURN_IF_ERROR( - checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false)); - RETURN_IF_ERROR( - checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false)); - struct { - ulittle16_t Count; - ulittle16_t PosX; - ulittle16_t PosY; - ulittle16_t DialogWidth; - ulittle16_t DialogHeight; - } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X), - ulittle16_t(Res->Y), ulittle16_t(Res->Width), - ulittle16_t(Res->Height)}; - writeObject(Middle); - - // MENU field. As of now, we don't keep them in the state and can peacefully - // think there is no menu attached to the dialog. - writeInt<uint16_t>(0); - - // Window CLASS field. - RETURN_IF_ERROR(writeIntOrString(ObjectData.Class)); - - // Window title or a single word equal to 0. - RETURN_IF_ERROR(writeCString(ObjectData.Caption)); - - // If there *is* a window font declared, output its data. - auto &Font = ObjectData.Font; - if (Font) { - writeInt<uint16_t>(Font->Size); - // Additional description occurs only in DIALOGEX. - if (Res->IsExtended) { - writeInt<uint16_t>(Font->Weight); - writeInt<uint8_t>(Font->IsItalic); - writeInt<uint8_t>(Font->Charset); - } - RETURN_IF_ERROR(writeCString(Font->Typeface)); - } - - auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error { - if (!Err) - return Error::success(); - return joinErrors(createError("Error in " + Twine(Ctl.Type) + - " control (ID " + Twine(Ctl.ID) + "):"), - std::move(Err)); - }; - - for (auto &Ctl : Res->Controls) - RETURN_IF_ERROR( - handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl)); - - return Error::success(); -} - -// --- HTMLResource helpers. --- // - -Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) { - return appendFile(cast<HTMLResource>(Base)->HTMLLoc); -} - -// --- MenuResource helpers. --- // - -Error ResourceFileWriter::writeMenuDefinition( - const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) { - assert(Def); - const MenuDefinition *DefPtr = Def.get(); - - if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) { - writeInt<uint16_t>(Flags); - // Some resource files use -1, i.e. UINT32_MAX, for empty menu items. - if (MenuItemPtr->Id != static_cast<uint32_t>(-1)) - RETURN_IF_ERROR( - checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID")); - writeInt<uint16_t>(MenuItemPtr->Id); - RETURN_IF_ERROR(writeCString(MenuItemPtr->Name)); - return Error::success(); - } - - if (isa<MenuSeparator>(DefPtr)) { - writeInt<uint16_t>(Flags); - writeInt<uint32_t>(0); - return Error::success(); - } - - auto *PopupPtr = cast<PopupItem>(DefPtr); - writeInt<uint16_t>(Flags); - RETURN_IF_ERROR(writeCString(PopupPtr->Name)); - return writeMenuDefinitionList(PopupPtr->SubItems); -} - -Error ResourceFileWriter::writeMenuDefinitionList( - const MenuDefinitionList &List) { - for (auto &Def : List.Definitions) { - uint16_t Flags = Def->getResFlags(); - // Last element receives an additional 0x80 flag. - const uint16_t LastElementFlag = 0x0080; - if (&Def == &List.Definitions.back()) - Flags |= LastElementFlag; - - RETURN_IF_ERROR(writeMenuDefinition(Def, Flags)); - } - return Error::success(); -} - -Error ResourceFileWriter::writeMenuBody(const RCResource *Base) { - // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0. - // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx - writeInt<uint32_t>(0); - - return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements); -} - -// --- StringTableResource helpers. --- // - -class BundleResource : public RCResource { -public: - using BundleType = ResourceFileWriter::StringTableInfo::Bundle; - BundleType Bundle; - - BundleResource(const BundleType &StrBundle) - : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {} - IntOrString getResourceType() const override { return 6; } - - ResourceKind getKind() const override { return RkStringTableBundle; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkStringTableBundle; - } - Twine getResourceTypeName() const override { return "STRINGTABLE"; } -}; - -Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) { - return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody); -} - -Error ResourceFileWriter::insertStringIntoBundle( - StringTableInfo::Bundle &Bundle, uint16_t StringID, - const std::vector<StringRef> &String) { - uint16_t StringLoc = StringID & 15; - if (Bundle.Data[StringLoc]) - return createError("Multiple STRINGTABLE strings located under ID " + - Twine(StringID)); - Bundle.Data[StringLoc] = String; - return Error::success(); -} - -Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) { - auto *Res = cast<BundleResource>(Base); - for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) { - // The string format is a tiny bit different here. We - // first output the size of the string, and then the string itself - // (which is not null-terminated). - SmallVector<UTF16, 128> Data; - if (Res->Bundle.Data[ID]) { - bool IsLongString; - for (StringRef S : *Res->Bundle.Data[ID]) - RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull, - IsLongString, Data, Params.CodePage)); - if (AppendNull) - Data.push_back('\0'); - } - RETURN_IF_ERROR( - checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size")); - writeInt<uint16_t>(Data.size()); - for (auto Char : Data) - writeInt(Char); - } - return Error::success(); -} - -Error ResourceFileWriter::dumpAllStringTables() { - for (auto Key : StringTableData.BundleList) { - auto Iter = StringTableData.BundleData.find(Key); - assert(Iter != StringTableData.BundleData.end()); - - // For a moment, revert the context info to moment of bundle declaration. - ContextKeeper RAII(this); - ObjectData = Iter->second.DeclTimeInfo; - - BundleResource Res(Iter->second); - // Bundle #(k+1) contains keys [16k, 16k + 15]. - Res.setName(Key.first + 1); - RETURN_IF_ERROR(visitStringTableBundle(&Res)); - } - return Error::success(); -} - -// --- UserDefinedResource helpers. --- // - -Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) { - auto *Res = cast<UserDefinedResource>(Base); - - if (Res->IsFileResource) - return appendFile(Res->FileLoc); - - for (auto &Elem : Res->Contents) { - if (Elem.isInt()) { - RETURN_IF_ERROR( - checkRCInt(Elem.getInt(), "Number in user-defined resource")); - writeRCInt(Elem.getInt()); - continue; - } - - SmallVector<UTF16, 128> ProcessedString; - bool IsLongString; - RETURN_IF_ERROR( - processString(Elem.getString(), NullHandlingMethod::UserResource, - IsLongString, ProcessedString, Params.CodePage)); - - for (auto Ch : ProcessedString) { - if (IsLongString) { - writeInt(Ch); - continue; - } - - RETURN_IF_ERROR(checkNumberFits<uint8_t>( - Ch, "Character in narrow string in user-defined resource")); - writeInt<uint8_t>(Ch); - } - } - - return Error::success(); -} - -// --- VersionInfoResourceResource helpers. --- // - -Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { - // Output the header if the block has name. - bool OutputHeader = Blk.Name != ""; - uint64_t LengthLoc; - - padStream(sizeof(uint32_t)); - if (OutputHeader) { - LengthLoc = writeInt<uint16_t>(0); - writeInt<uint16_t>(0); - writeInt<uint16_t>(1); // true - RETURN_IF_ERROR(writeCString(Blk.Name)); - padStream(sizeof(uint32_t)); - } - - for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) { - VersionInfoStmt *ItemPtr = Item.get(); - - if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) { - RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr)); - continue; - } - - auto *ValuePtr = cast<VersionInfoValue>(ItemPtr); - RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr)); - } - - if (OutputHeader) { - uint64_t CurLoc = tell(); - writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); - } - - return Error::success(); -} - -Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) { - // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE - // is a mapping from the key (string) to the value (a sequence of ints or - // a sequence of strings). - // - // If integers are to be written: width of each integer written depends on - // whether it's been declared 'long' (it's DWORD then) or not (it's WORD). - // ValueLength defined in structure referenced below is then the total - // number of bytes taken by these integers. - // - // If strings are to be written: characters are always WORDs. - // Moreover, '\0' character is written after the last string, and between - // every two strings separated by comma (if strings are not comma-separated, - // they're simply concatenated). ValueLength is equal to the number of WORDs - // written (that is, half of the bytes written). - // - // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx - bool HasStrings = false, HasInts = false; - for (auto &Item : Val.Values) - (Item.isInt() ? HasInts : HasStrings) = true; - - assert((HasStrings || HasInts) && "VALUE must have at least one argument"); - if (HasStrings && HasInts) - return createError(Twine("VALUE ") + Val.Key + - " cannot contain both strings and integers"); - - padStream(sizeof(uint32_t)); - auto LengthLoc = writeInt<uint16_t>(0); - auto ValLengthLoc = writeInt<uint16_t>(0); - writeInt<uint16_t>(HasStrings); - RETURN_IF_ERROR(writeCString(Val.Key)); - padStream(sizeof(uint32_t)); - - auto DataLoc = tell(); - for (size_t Id = 0; Id < Val.Values.size(); ++Id) { - auto &Item = Val.Values[Id]; - if (Item.isInt()) { - auto Value = Item.getInt(); - RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value")); - writeRCInt(Value); - continue; - } - - bool WriteTerminator = - Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1]; - RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator)); - } - - auto CurLoc = tell(); - auto ValueLength = CurLoc - DataLoc; - if (HasStrings) { - assert(ValueLength % 2 == 0); - ValueLength /= 2; - } - writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); - writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc); - return Error::success(); -} - -template <typename Ty> -static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key, - const Ty &Default) { - auto Iter = Map.find(Key); - if (Iter != Map.end()) - return Iter->getValue(); - return Default; -} - -Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) { - auto *Res = cast<VersionInfoResource>(Base); - - const auto &FixedData = Res->FixedData; - - struct /* VS_FIXEDFILEINFO */ { - ulittle32_t Signature = ulittle32_t(0xFEEF04BD); - ulittle32_t StructVersion = ulittle32_t(0x10000); - // It's weird to have most-significant DWORD first on the little-endian - // machines, but let it be this way. - ulittle32_t FileVersionMS; - ulittle32_t FileVersionLS; - ulittle32_t ProductVersionMS; - ulittle32_t ProductVersionLS; - ulittle32_t FileFlagsMask; - ulittle32_t FileFlags; - ulittle32_t FileOS; - ulittle32_t FileType; - ulittle32_t FileSubtype; - // MS implementation seems to always set these fields to 0. - ulittle32_t FileDateMS = ulittle32_t(0); - ulittle32_t FileDateLS = ulittle32_t(0); - } FixedInfo; - - // First, VS_VERSIONINFO. - auto LengthLoc = writeInt<uint16_t>(0); - writeInt<uint16_t>(sizeof(FixedInfo)); - writeInt<uint16_t>(0); - cantFail(writeCString("VS_VERSION_INFO")); - padStream(sizeof(uint32_t)); - - using VersionInfoFixed = VersionInfoResource::VersionInfoFixed; - auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) { - static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0}; - if (!FixedData.IsTypePresent[(int)Type]) - return DefaultOut; - return FixedData.FixedInfo[(int)Type]; - }; - - auto FileVer = GetField(VersionInfoFixed::FtFileVersion); - RETURN_IF_ERROR(checkNumberFits<uint16_t>( - *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields")); - FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1]; - FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3]; - - auto ProdVer = GetField(VersionInfoFixed::FtProductVersion); - RETURN_IF_ERROR(checkNumberFits<uint16_t>( - *std::max_element(ProdVer.begin(), ProdVer.end()), - "PRODUCTVERSION fields")); - FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1]; - FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3]; - - FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0]; - FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0]; - FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0]; - FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0]; - FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0]; - - writeObject(FixedInfo); - padStream(sizeof(uint32_t)); - - RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock)); - - // FIXME: check overflow? - writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc); - - return Error::success(); -} - -Expected<std::unique_ptr<MemoryBuffer>> -ResourceFileWriter::loadFile(StringRef File) const { - SmallString<128> Path; - SmallString<128> Cwd; - std::unique_ptr<MemoryBuffer> Result; - +// * Squash "" to a single ". +// * Replace the escape sequences with their processed version. +// For identifiers, this is no-op. +static Error processString(StringRef Str, NullHandlingMethod NullHandler, + bool &IsLongString, SmallVectorImpl<UTF16> &Result, + int CodePage) { + bool IsString = stripQuotes(Str, IsLongString); + SmallVector<UTF16, 128> Chars; + + // Convert the input bytes according to the chosen codepage. + if (CodePage == CpUtf8) { + convertUTF8ToUTF16String(Str, Chars); + } else if (CodePage == CpWin1252) { + for (char C : Str) + Chars.push_back(cp1252ToUnicode((unsigned char)C)); + } else { + // For other, unknown codepages, only allow plain ASCII input. + for (char C : Str) { + if ((unsigned char)C > 0x7F) + return createError("Non-ASCII 8-bit codepoint (" + Twine(C) + + ") can't be interpreted in the current codepage"); + Chars.push_back((unsigned char)C); + } + } + + if (!IsString) { + // It's an identifier if it's not a string. Make all characters uppercase. + for (UTF16 &Ch : Chars) { + assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII"); + Ch = toupper(Ch); + } + Result.swap(Chars); + return Error::success(); + } + Result.reserve(Chars.size()); + size_t Pos = 0; + + auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error { + if (!IsLongString) { + if (NullHandler == NullHandlingMethod::UserResource) { + // Narrow strings in user-defined resources are *not* output in + // UTF-16 format. + if (Char > 0xFF) + return createError("Non-8-bit codepoint (" + Twine(Char) + + ") can't occur in a user-defined narrow string"); + } + } + + Result.push_back(Char); + return Error::success(); + }; + auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error { + if (!IsLongString) { + // Escaped chars in narrow strings have to be interpreted according to + // the chosen code page. + if (Char > 0xFF) + return createError("Non-8-bit escaped char (" + Twine(Char) + + ") can't occur in narrow string"); + if (CodePage == CpUtf8) { + if (Char >= 0x80) + return createError("Unable to interpret single byte (" + Twine(Char) + + ") as UTF-8"); + } else if (CodePage == CpWin1252) { + Char = cp1252ToUnicode(Char); + } else { + // Unknown/unsupported codepage, only allow ASCII input. + if (Char > 0x7F) + return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) + + ") can't " + "occur in a non-Unicode string"); + } + } + + return AddRes(Char); + }; + + while (Pos < Chars.size()) { + UTF16 CurChar = Chars[Pos]; + ++Pos; + + // Strip double "". + if (CurChar == '"') { + if (Pos == Chars.size() || Chars[Pos] != '"') + return createError("Expected \"\""); + ++Pos; + RETURN_IF_ERROR(AddRes('"')); + continue; + } + + if (CurChar == '\\') { + UTF16 TypeChar = Chars[Pos]; + ++Pos; + + if (TypeChar == 'x' || TypeChar == 'X') { + // Read a hex number. Max number of characters to read differs between + // narrow and wide strings. + UTF16 ReadInt = 0; + size_t RemainingChars = IsLongString ? 4 : 2; + // We don't want to read non-ASCII hex digits. std:: functions past + // 0xFF invoke UB. + // + // FIXME: actually, Microsoft version probably doesn't check this + // condition and uses their Unicode version of 'isxdigit'. However, + // there are some hex-digit Unicode character outside of ASCII, and + // some of these are actually accepted by rc.exe, the notable example + // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written + // instead of ASCII digits in \x... escape sequence and get accepted. + // However, the resulting hexcodes seem totally unpredictable. + // We think it's infeasible to try to reproduce this behavior, nor to + // put effort in order to detect it. + while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) { + if (!isxdigit(Chars[Pos])) + break; + char Digit = tolower(Chars[Pos]); + ++Pos; + + ReadInt <<= 4; + if (isdigit(Digit)) + ReadInt |= Digit - '0'; + else + ReadInt |= Digit - 'a' + 10; + + --RemainingChars; + } + + RETURN_IF_ERROR(AddEscapedChar(ReadInt)); + continue; + } + + if (TypeChar >= '0' && TypeChar < '8') { + // Read an octal number. Note that we've already read the first digit. + UTF16 ReadInt = TypeChar - '0'; + size_t RemainingChars = IsLongString ? 6 : 2; + + while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' && + Chars[Pos] < '8') { + ReadInt <<= 3; + ReadInt |= Chars[Pos] - '0'; + --RemainingChars; + ++Pos; + } + + RETURN_IF_ERROR(AddEscapedChar(ReadInt)); + + continue; + } + + switch (TypeChar) { + case 'A': + case 'a': + // Windows '\a' translates into '\b' (Backspace). + RETURN_IF_ERROR(AddRes('\b')); + break; + + case 'n': // Somehow, RC doesn't recognize '\N' and '\R'. + RETURN_IF_ERROR(AddRes('\n')); + break; + + case 'r': + RETURN_IF_ERROR(AddRes('\r')); + break; + + case 'T': + case 't': + RETURN_IF_ERROR(AddRes('\t')); + break; + + case '\\': + RETURN_IF_ERROR(AddRes('\\')); + break; + + case '"': + // RC accepts \" only if another " comes afterwards; then, \"" means + // a single ". + if (Pos == Chars.size() || Chars[Pos] != '"') + return createError("Expected \\\"\""); + ++Pos; + RETURN_IF_ERROR(AddRes('"')); + break; + + default: + // If TypeChar means nothing, \ is should be output to stdout with + // following char. However, rc.exe consumes these characters when + // dealing with wide strings. + if (!IsLongString) { + RETURN_IF_ERROR(AddRes('\\')); + RETURN_IF_ERROR(AddRes(TypeChar)); + } + break; + } + + continue; + } + + // If nothing interesting happens, just output the character. + RETURN_IF_ERROR(AddRes(CurChar)); + } + + switch (NullHandler) { + case NullHandlingMethod::CutAtNull: + for (size_t Pos = 0; Pos < Result.size(); ++Pos) + if (Result[Pos] == '\0') + Result.resize(Pos); + break; + + case NullHandlingMethod::CutAtDoubleNull: + for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos) + if (Result[Pos] == '\0' && Result[Pos + 1] == '\0') + Result.resize(Pos); + if (Result.size() > 0 && Result.back() == '\0') + Result.pop_back(); + break; + + case NullHandlingMethod::UserResource: + break; + } + + return Error::success(); +} + +uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) { + uint64_t Result = tell(); + FS->write((const char *)Data.begin(), Data.size()); + return Result; +} + +Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) { + SmallVector<UTF16, 128> ProcessedString; + bool IsLongString; + RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull, + IsLongString, ProcessedString, + Params.CodePage)); + for (auto Ch : ProcessedString) + writeInt<uint16_t>(Ch); + if (WriteTerminator) + writeInt<uint16_t>(0); + return Error::success(); +} + +Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) { + return writeIntOrString(Ident); +} + +Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) { + if (!Value.isInt()) + return writeCString(Value.getString()); + + writeInt<uint16_t>(0xFFFF); + writeInt<uint16_t>(Value.getInt()); + return Error::success(); +} + +void ResourceFileWriter::writeRCInt(RCInt Value) { + if (Value.isLong()) + writeInt<uint32_t>(Value); + else + writeInt<uint16_t>(Value); +} + +Error ResourceFileWriter::appendFile(StringRef Filename) { + bool IsLong; + stripQuotes(Filename, IsLong); + + auto File = loadFile(Filename); + if (!File) + return File.takeError(); + + *FS << (*File)->getBuffer(); + return Error::success(); +} + +void ResourceFileWriter::padStream(uint64_t Length) { + assert(Length > 0); + uint64_t Location = tell(); + Location %= Length; + uint64_t Pad = (Length - Location) % Length; + for (uint64_t i = 0; i < Pad; ++i) + writeInt<uint8_t>(0); +} + +Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) { + if (Err) + return joinErrors(createError("Error in " + Res->getResourceTypeName() + + " statement (ID " + Twine(Res->ResName) + + "): "), + std::move(Err)); + return Error::success(); +} + +Error ResourceFileWriter::visitNullResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeNullBody); +} + +Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); +} + +Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeBitmapBody); +} + +Error ResourceFileWriter::visitCursorResource(const RCResource *Res) { + return handleError(visitIconOrCursorResource(Res), Res); +} + +Error ResourceFileWriter::visitDialogResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeDialogBody); +} + +Error ResourceFileWriter::visitIconResource(const RCResource *Res) { + return handleError(visitIconOrCursorResource(Res), Res); +} + +Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) { + ObjectData.Caption = Stmt->Value; + return Error::success(); +} + +Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) { + ObjectData.Class = Stmt->Value; + return Error::success(); +} + +Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeHTMLBody); +} + +Error ResourceFileWriter::visitMenuResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeMenuBody); +} + +Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) { + const auto *Res = cast<StringTableResource>(Base); + + ContextKeeper RAII(this); + RETURN_IF_ERROR(Res->applyStmts(this)); + + for (auto &String : Res->Table) { + RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID")); + uint16_t BundleID = String.first >> 4; + StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo); + auto &BundleData = StringTableData.BundleData; + auto Iter = BundleData.find(Key); + + if (Iter == BundleData.end()) { + // Need to create a bundle. + StringTableData.BundleList.push_back(Key); + auto EmplaceResult = BundleData.emplace( + Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags)); + assert(EmplaceResult.second && "Could not create a bundle"); + Iter = EmplaceResult.first; + } + + RETURN_IF_ERROR( + insertStringIntoBundle(Iter->second, String.first, String.second)); + } + + return Error::success(); +} + +Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody); +} + +Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody); +} + +Error ResourceFileWriter::visitCharacteristicsStmt( + const CharacteristicsStmt *Stmt) { + ObjectData.Characteristics = Stmt->Value; + return Error::success(); +} + +Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) { + ObjectData.ExStyle = Stmt->Value; + return Error::success(); +} + +Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) { + RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size")); + RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight")); + RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset")); + ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic, + Stmt->Charset}; + ObjectData.Font.emplace(Font); + return Error::success(); +} + +Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) { + RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); + RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); + ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10); + return Error::success(); +} + +Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) { + ObjectData.Style = Stmt->Value; + return Error::success(); +} + +Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) { + ObjectData.VersionInfo = Stmt->Value; + return Error::success(); +} + +Error ResourceFileWriter::writeResource( + const RCResource *Res, + Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) { + // We don't know the sizes yet. + object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)}; + uint64_t HeaderLoc = writeObject(HeaderPrefix); + + auto ResType = Res->getResourceType(); + RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type")); + RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID")); + RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res)); + RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res)); + + // Apply the resource-local optional statements. + ContextKeeper RAII(this); + RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res)); + + padStream(sizeof(uint32_t)); + object::WinResHeaderSuffix HeaderSuffix{ + ulittle32_t(0), // DataVersion; seems to always be 0 + ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo), + ulittle32_t(ObjectData.VersionInfo), + ulittle32_t(ObjectData.Characteristics)}; + writeObject(HeaderSuffix); + + uint64_t DataLoc = tell(); + RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res)); + // RETURN_IF_ERROR(handleError(dumpResource(Ctx))); + + // Update the sizes. + HeaderPrefix.DataSize = tell() - DataLoc; + HeaderPrefix.HeaderSize = DataLoc - HeaderLoc; + writeObjectAt(HeaderPrefix, HeaderLoc); + padStream(sizeof(uint32_t)); + + return Error::success(); +} + +// --- NullResource helpers. --- // + +Error ResourceFileWriter::writeNullBody(const RCResource *) { + return Error::success(); +} + +// --- AcceleratorsResource helpers. --- // + +Error ResourceFileWriter::writeSingleAccelerator( + const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) { + using Accelerator = AcceleratorsResource::Accelerator; + using Opt = Accelerator::Options; + + struct AccelTableEntry { + ulittle16_t Flags; + ulittle16_t ANSICode; + ulittle16_t Id; + uint16_t Padding; + } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0}; + + bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY; + + // Remove ASCII flags (which doesn't occur in .res files). + Entry.Flags = Obj.Flags & ~Opt::ASCII; + + if (IsLastItem) + Entry.Flags |= 0x80; + + RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID")); + Entry.Id = ulittle16_t(Obj.Id); + + auto createAccError = [&Obj](const char *Msg) { + return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg); + }; + + if (IsASCII && IsVirtKey) + return createAccError("Accelerator can't be both ASCII and VIRTKEY"); + + if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL))) + return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY" + " accelerators"); + + if (Obj.Event.isInt()) { + if (!IsASCII && !IsVirtKey) + return createAccError( + "Accelerator with a numeric event must be either ASCII" + " or VIRTKEY"); + + uint32_t EventVal = Obj.Event.getInt(); + RETURN_IF_ERROR( + checkNumberFits<uint16_t>(EventVal, "Numeric event key ID")); + Entry.ANSICode = ulittle16_t(EventVal); + writeObject(Entry); + return Error::success(); + } + + StringRef Str = Obj.Event.getString(); + bool IsWide; + stripQuotes(Str, IsWide); + + if (Str.size() == 0 || Str.size() > 2) + return createAccError( + "Accelerator string events should have length 1 or 2"); + + if (Str[0] == '^') { + if (Str.size() == 1) + return createAccError("No character following '^' in accelerator event"); + if (IsVirtKey) + return createAccError( + "VIRTKEY accelerator events can't be preceded by '^'"); + + char Ch = Str[1]; + if (Ch >= 'a' && Ch <= 'z') + Entry.ANSICode = ulittle16_t(Ch - 'a' + 1); + else if (Ch >= 'A' && Ch <= 'Z') + Entry.ANSICode = ulittle16_t(Ch - 'A' + 1); + else + return createAccError("Control character accelerator event should be" + " alphabetic"); + + writeObject(Entry); + return Error::success(); + } + + if (Str.size() == 2) + return createAccError("Event string should be one-character, possibly" + " preceded by '^'"); + + uint8_t EventCh = Str[0]; + // The original tool just warns in this situation. We chose to fail. + if (IsVirtKey && !isalnum(EventCh)) + return createAccError("Non-alphanumeric characters cannot describe virtual" + " keys"); + if (EventCh > 0x7F) + return createAccError("Non-ASCII description of accelerator"); + + if (IsVirtKey) + EventCh = toupper(EventCh); + Entry.ANSICode = ulittle16_t(EventCh); + writeObject(Entry); + return Error::success(); +} + +Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { + auto *Res = cast<AcceleratorsResource>(Base); + size_t AcceleratorId = 0; + for (auto &Acc : Res->Accelerators) { + ++AcceleratorId; + RETURN_IF_ERROR( + writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); + } + return Error::success(); +} + +// --- BitmapResource helpers. --- // + +Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) { + StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc; + bool IsLong; + stripQuotes(Filename, IsLong); + + auto File = loadFile(Filename); + if (!File) + return File.takeError(); + + StringRef Buffer = (*File)->getBuffer(); + + // Skip the 14 byte BITMAPFILEHEADER. + constexpr size_t BITMAPFILEHEADER_size = 14; + if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' || + Buffer[1] != 'M') + return createError("Incorrect bitmap file."); + + *FS << Buffer.substr(BITMAPFILEHEADER_size); + return Error::success(); +} + +// --- CursorResource and IconResource helpers. --- // + +// ICONRESDIR structure. Describes a single icon in resource group. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx +struct IconResDir { + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; +}; + +// CURSORDIR structure. Describes a single cursor in resource group. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx +struct CursorDir { + ulittle16_t Width; + ulittle16_t Height; +}; + +// RESDIRENTRY structure, stripped from the last item. Stripping made +// for compatibility with RESDIR. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx +struct ResourceDirEntryStart { + union { + CursorDir Cursor; // Used in CURSOR resources. + IconResDir Icon; // Used in .ico and .cur files, and ICON resources. + }; + ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource). + ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource). + ulittle32_t Size; + // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only). + // ulittle16_t IconID; // Resource icon ID (RESDIR only). +}; + +// BITMAPINFOHEADER structure. Describes basic information about the bitmap +// being read. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx +struct BitmapInfoHeader { + ulittle32_t Size; + ulittle32_t Width; + ulittle32_t Height; + ulittle16_t Planes; + ulittle16_t BitCount; + ulittle32_t Compression; + ulittle32_t SizeImage; + ulittle32_t XPelsPerMeter; + ulittle32_t YPelsPerMeter; + ulittle32_t ClrUsed; + ulittle32_t ClrImportant; +}; + +// Group icon directory header. Called ICONDIR in .ico/.cur files and +// NEWHEADER in .res files. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx +struct GroupIconDir { + ulittle16_t Reserved; // Always 0. + ulittle16_t ResType; // 1 for icons, 2 for cursors. + ulittle16_t ResCount; // Number of items. +}; + +enum class IconCursorGroupType { Icon, Cursor }; + +class SingleIconCursorResource : public RCResource { +public: + IconCursorGroupType Type; + const ResourceDirEntryStart &Header; + ArrayRef<uint8_t> Image; + + SingleIconCursorResource(IconCursorGroupType ResourceType, + const ResourceDirEntryStart &HeaderEntry, + ArrayRef<uint8_t> ImageData, uint16_t Flags) + : RCResource(Flags), Type(ResourceType), Header(HeaderEntry), + Image(ImageData) {} + + Twine getResourceTypeName() const override { return "Icon/cursor image"; } + IntOrString getResourceType() const override { + return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor; + } + ResourceKind getKind() const override { return RkSingleCursorOrIconRes; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkSingleCursorOrIconRes; + } +}; + +class IconCursorGroupResource : public RCResource { +public: + IconCursorGroupType Type; + GroupIconDir Header; + std::vector<ResourceDirEntryStart> ItemEntries; + + IconCursorGroupResource(IconCursorGroupType ResourceType, + const GroupIconDir &HeaderData, + std::vector<ResourceDirEntryStart> &&Entries) + : Type(ResourceType), Header(HeaderData), + ItemEntries(std::move(Entries)) {} + + Twine getResourceTypeName() const override { return "Icon/cursor group"; } + IntOrString getResourceType() const override { + return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup; + } + ResourceKind getKind() const override { return RkCursorOrIconGroupRes; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkCursorOrIconGroupRes; + } +}; + +Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) { + auto *Res = cast<SingleIconCursorResource>(Base); + if (Res->Type == IconCursorGroupType::Cursor) { + // In case of cursors, two WORDS are appended to the beginning + // of the resource: HotspotX (Planes in RESDIRENTRY), + // and HotspotY (BitCount). + // + // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx + // (Remarks section). + writeObject(Res->Header.Planes); + writeObject(Res->Header.BitCount); + } + + writeObject(Res->Image); + return Error::success(); +} + +Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) { + auto *Res = cast<IconCursorGroupResource>(Base); + writeObject(Res->Header); + for (auto Item : Res->ItemEntries) { + writeObject(Item); + writeInt(IconCursorID++); + } + return Error::success(); +} + +Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody); +} + +Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody); +} + +Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) { + IconCursorGroupType Type; + StringRef FileStr; + IntOrString ResName = Base->ResName; + + if (auto *IconRes = dyn_cast<IconResource>(Base)) { + FileStr = IconRes->IconLoc; + Type = IconCursorGroupType::Icon; + } else { + auto *CursorRes = dyn_cast<CursorResource>(Base); + FileStr = CursorRes->CursorLoc; + Type = IconCursorGroupType::Cursor; + } + + bool IsLong; + stripQuotes(FileStr, IsLong); + auto File = loadFile(FileStr); + + if (!File) + return File.takeError(); + + BinaryStreamReader Reader((*File)->getBuffer(), support::little); + + // Read the file headers. + // - At the beginning, ICONDIR/NEWHEADER header. + // - Then, a number of RESDIR headers follow. These contain offsets + // to data. + const GroupIconDir *Header; + + RETURN_IF_ERROR(Reader.readObject(Header)); + if (Header->Reserved != 0) + return createError("Incorrect icon/cursor Reserved field; should be 0."); + uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2; + if (Header->ResType != NeededType) + return createError("Incorrect icon/cursor ResType field; should be " + + Twine(NeededType) + "."); + + uint16_t NumItems = Header->ResCount; + + // Read single ico/cur headers. + std::vector<ResourceDirEntryStart> ItemEntries; + ItemEntries.reserve(NumItems); + std::vector<uint32_t> ItemOffsets(NumItems); + for (size_t ID = 0; ID < NumItems; ++ID) { + const ResourceDirEntryStart *Object; + RETURN_IF_ERROR(Reader.readObject(Object)); + ItemEntries.push_back(*Object); + RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID])); + } + + // Now write each icon/cursors one by one. At first, all the contents + // without ICO/CUR header. This is described by SingleIconCursorResource. + for (size_t ID = 0; ID < NumItems; ++ID) { + // Load the fragment of file. + Reader.setOffset(ItemOffsets[ID]); + ArrayRef<uint8_t> Image; + RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size)); + SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image, + Base->MemoryFlags); + SingleRes.setName(IconCursorID + ID); + RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes)); + } + + // Now, write all the headers concatenated into a separate resource. + for (size_t ID = 0; ID < NumItems; ++ID) { + // We need to rewrite the cursor headers, and fetch actual values + // for Planes/BitCount. + const auto &OldHeader = ItemEntries[ID]; + ResourceDirEntryStart NewHeader = OldHeader; + + if (Type == IconCursorGroupType::Cursor) { + NewHeader.Cursor.Width = OldHeader.Icon.Width; + // Each cursor in fact stores two bitmaps, one under another. + // Height provided in cursor definition describes the height of the + // cursor, whereas the value existing in resource definition describes + // the height of the bitmap. Therefore, we need to double this height. + NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; + + // Two WORDs were written at the beginning of the resource (hotspot + // location). This is reflected in Size field. + NewHeader.Size += 2 * sizeof(uint16_t); + } + + // Now, we actually need to read the bitmap header to find + // the number of planes and the number of bits per pixel. + Reader.setOffset(ItemOffsets[ID]); + const BitmapInfoHeader *BMPHeader; + RETURN_IF_ERROR(Reader.readObject(BMPHeader)); + if (BMPHeader->Size == sizeof(BitmapInfoHeader)) { + NewHeader.Planes = BMPHeader->Planes; + NewHeader.BitCount = BMPHeader->BitCount; + } else { + // A PNG .ico file. + // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473 + // "The image must be in 32bpp" + NewHeader.Planes = 1; + NewHeader.BitCount = 32; + } + + ItemEntries[ID] = NewHeader; + } + + IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries)); + HeaderRes.setName(ResName); + if (Base->MemoryFlags & MfPreload) { + HeaderRes.MemoryFlags |= MfPreload; + HeaderRes.MemoryFlags &= ~MfPure; + } + RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes)); + + return Error::success(); +} + +// --- DialogResource helpers. --- // + +Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, + bool IsExtended) { + // Each control should be aligned to DWORD. + padStream(sizeof(uint32_t)); + + auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type); + IntWithNotMask CtlStyle(TypeInfo.Style); + CtlStyle |= Ctl.Style.getValueOr(RCInt(0)); + uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0); + + // DIALOG(EX) item header prefix. + if (!IsExtended) { + struct { + ulittle32_t Style; + ulittle32_t ExtStyle; + } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)}; + writeObject(Prefix); + } else { + struct { + ulittle32_t HelpID; + ulittle32_t ExtStyle; + ulittle32_t Style; + } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle), + ulittle32_t(CtlStyle.getValue())}; + writeObject(Prefix); + } + + // Common fixed-length part. + RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( + Ctl.X, "Dialog control x-coordinate", true)); + RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( + Ctl.Y, "Dialog control y-coordinate", true)); + RETURN_IF_ERROR( + checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false)); + RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( + Ctl.Height, "Dialog control height", false)); + struct { + ulittle16_t X; + ulittle16_t Y; + ulittle16_t Width; + ulittle16_t Height; + } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width), + ulittle16_t(Ctl.Height)}; + writeObject(Middle); + + // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX. + if (!IsExtended) { + // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't + // want to refer to later. + if (Ctl.ID != static_cast<uint32_t>(-1)) + RETURN_IF_ERROR(checkNumberFits<uint16_t>( + Ctl.ID, "Control ID in simple DIALOG resource")); + writeInt<uint16_t>(Ctl.ID); + } else { + writeInt<uint32_t>(Ctl.ID); + } + + // Window class - either 0xFFFF + 16-bit integer or a string. + RETURN_IF_ERROR(writeIntOrString(Ctl.Class)); + + // Element caption/reference ID. ID is preceded by 0xFFFF. + RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID")); + RETURN_IF_ERROR(writeIntOrString(Ctl.Title)); + + // # bytes of extra creation data count. Don't pass any. + writeInt<uint16_t>(0); + + return Error::success(); +} + +Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { + auto *Res = cast<DialogResource>(Base); + + // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU. + const uint32_t DefaultStyle = 0x80880000; + const uint32_t StyleFontFlag = 0x40; + const uint32_t StyleCaptionFlag = 0x00C00000; + + uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle); + if (ObjectData.Font) + UsedStyle |= StyleFontFlag; + else + UsedStyle &= ~StyleFontFlag; + + // Actually, in case of empty (but existent) caption, the examined field + // is equal to "\"\"". That's why empty captions are still noticed. + if (ObjectData.Caption != "") + UsedStyle |= StyleCaptionFlag; + + const uint16_t DialogExMagic = 0xFFFF; + uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0); + + // Write DIALOG(EX) header prefix. These are pretty different. + if (!Res->IsExtended) { + // We cannot let the higher word of DefaultStyle be equal to 0xFFFF. + // In such a case, whole object (in .res file) is equivalent to a + // DIALOGEX. It might lead to access violation/segmentation fault in + // resource readers. For example, + // 1 DIALOG 0, 0, 0, 65432 + // STYLE 0xFFFF0001 {} + // would be compiled to a DIALOGEX with 65432 controls. + if ((UsedStyle >> 16) == DialogExMagic) + return createError("16 higher bits of DIALOG resource style cannot be" + " equal to 0xFFFF"); + + struct { + ulittle32_t Style; + ulittle32_t ExtStyle; + } Prefix{ulittle32_t(UsedStyle), + ulittle32_t(ExStyle)}; + + writeObject(Prefix); + } else { + struct { + ulittle16_t Version; + ulittle16_t Magic; + ulittle32_t HelpID; + ulittle32_t ExtStyle; + ulittle32_t Style; + } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic), + ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)}; + + writeObject(Prefix); + } + + // Now, a common part. First, fixed-length fields. + RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(), + "Number of dialog controls")); + RETURN_IF_ERROR( + checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true)); + RETURN_IF_ERROR( + checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true)); + RETURN_IF_ERROR( + checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false)); + RETURN_IF_ERROR( + checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false)); + struct { + ulittle16_t Count; + ulittle16_t PosX; + ulittle16_t PosY; + ulittle16_t DialogWidth; + ulittle16_t DialogHeight; + } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X), + ulittle16_t(Res->Y), ulittle16_t(Res->Width), + ulittle16_t(Res->Height)}; + writeObject(Middle); + + // MENU field. As of now, we don't keep them in the state and can peacefully + // think there is no menu attached to the dialog. + writeInt<uint16_t>(0); + + // Window CLASS field. + RETURN_IF_ERROR(writeIntOrString(ObjectData.Class)); + + // Window title or a single word equal to 0. + RETURN_IF_ERROR(writeCString(ObjectData.Caption)); + + // If there *is* a window font declared, output its data. + auto &Font = ObjectData.Font; + if (Font) { + writeInt<uint16_t>(Font->Size); + // Additional description occurs only in DIALOGEX. + if (Res->IsExtended) { + writeInt<uint16_t>(Font->Weight); + writeInt<uint8_t>(Font->IsItalic); + writeInt<uint8_t>(Font->Charset); + } + RETURN_IF_ERROR(writeCString(Font->Typeface)); + } + + auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error { + if (!Err) + return Error::success(); + return joinErrors(createError("Error in " + Twine(Ctl.Type) + + " control (ID " + Twine(Ctl.ID) + "):"), + std::move(Err)); + }; + + for (auto &Ctl : Res->Controls) + RETURN_IF_ERROR( + handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl)); + + return Error::success(); +} + +// --- HTMLResource helpers. --- // + +Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) { + return appendFile(cast<HTMLResource>(Base)->HTMLLoc); +} + +// --- MenuResource helpers. --- // + +Error ResourceFileWriter::writeMenuDefinition( + const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) { + assert(Def); + const MenuDefinition *DefPtr = Def.get(); + + if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) { + writeInt<uint16_t>(Flags); + // Some resource files use -1, i.e. UINT32_MAX, for empty menu items. + if (MenuItemPtr->Id != static_cast<uint32_t>(-1)) + RETURN_IF_ERROR( + checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID")); + writeInt<uint16_t>(MenuItemPtr->Id); + RETURN_IF_ERROR(writeCString(MenuItemPtr->Name)); + return Error::success(); + } + + if (isa<MenuSeparator>(DefPtr)) { + writeInt<uint16_t>(Flags); + writeInt<uint32_t>(0); + return Error::success(); + } + + auto *PopupPtr = cast<PopupItem>(DefPtr); + writeInt<uint16_t>(Flags); + RETURN_IF_ERROR(writeCString(PopupPtr->Name)); + return writeMenuDefinitionList(PopupPtr->SubItems); +} + +Error ResourceFileWriter::writeMenuDefinitionList( + const MenuDefinitionList &List) { + for (auto &Def : List.Definitions) { + uint16_t Flags = Def->getResFlags(); + // Last element receives an additional 0x80 flag. + const uint16_t LastElementFlag = 0x0080; + if (&Def == &List.Definitions.back()) + Flags |= LastElementFlag; + + RETURN_IF_ERROR(writeMenuDefinition(Def, Flags)); + } + return Error::success(); +} + +Error ResourceFileWriter::writeMenuBody(const RCResource *Base) { + // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0. + // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx + writeInt<uint32_t>(0); + + return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements); +} + +// --- StringTableResource helpers. --- // + +class BundleResource : public RCResource { +public: + using BundleType = ResourceFileWriter::StringTableInfo::Bundle; + BundleType Bundle; + + BundleResource(const BundleType &StrBundle) + : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {} + IntOrString getResourceType() const override { return 6; } + + ResourceKind getKind() const override { return RkStringTableBundle; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkStringTableBundle; + } + Twine getResourceTypeName() const override { return "STRINGTABLE"; } +}; + +Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody); +} + +Error ResourceFileWriter::insertStringIntoBundle( + StringTableInfo::Bundle &Bundle, uint16_t StringID, + const std::vector<StringRef> &String) { + uint16_t StringLoc = StringID & 15; + if (Bundle.Data[StringLoc]) + return createError("Multiple STRINGTABLE strings located under ID " + + Twine(StringID)); + Bundle.Data[StringLoc] = String; + return Error::success(); +} + +Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) { + auto *Res = cast<BundleResource>(Base); + for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) { + // The string format is a tiny bit different here. We + // first output the size of the string, and then the string itself + // (which is not null-terminated). + SmallVector<UTF16, 128> Data; + if (Res->Bundle.Data[ID]) { + bool IsLongString; + for (StringRef S : *Res->Bundle.Data[ID]) + RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull, + IsLongString, Data, Params.CodePage)); + if (AppendNull) + Data.push_back('\0'); + } + RETURN_IF_ERROR( + checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size")); + writeInt<uint16_t>(Data.size()); + for (auto Char : Data) + writeInt(Char); + } + return Error::success(); +} + +Error ResourceFileWriter::dumpAllStringTables() { + for (auto Key : StringTableData.BundleList) { + auto Iter = StringTableData.BundleData.find(Key); + assert(Iter != StringTableData.BundleData.end()); + + // For a moment, revert the context info to moment of bundle declaration. + ContextKeeper RAII(this); + ObjectData = Iter->second.DeclTimeInfo; + + BundleResource Res(Iter->second); + // Bundle #(k+1) contains keys [16k, 16k + 15]. + Res.setName(Key.first + 1); + RETURN_IF_ERROR(visitStringTableBundle(&Res)); + } + return Error::success(); +} + +// --- UserDefinedResource helpers. --- // + +Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) { + auto *Res = cast<UserDefinedResource>(Base); + + if (Res->IsFileResource) + return appendFile(Res->FileLoc); + + for (auto &Elem : Res->Contents) { + if (Elem.isInt()) { + RETURN_IF_ERROR( + checkRCInt(Elem.getInt(), "Number in user-defined resource")); + writeRCInt(Elem.getInt()); + continue; + } + + SmallVector<UTF16, 128> ProcessedString; + bool IsLongString; + RETURN_IF_ERROR( + processString(Elem.getString(), NullHandlingMethod::UserResource, + IsLongString, ProcessedString, Params.CodePage)); + + for (auto Ch : ProcessedString) { + if (IsLongString) { + writeInt(Ch); + continue; + } + + RETURN_IF_ERROR(checkNumberFits<uint8_t>( + Ch, "Character in narrow string in user-defined resource")); + writeInt<uint8_t>(Ch); + } + } + + return Error::success(); +} + +// --- VersionInfoResourceResource helpers. --- // + +Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { + // Output the header if the block has name. + bool OutputHeader = Blk.Name != ""; + uint64_t LengthLoc; + + padStream(sizeof(uint32_t)); + if (OutputHeader) { + LengthLoc = writeInt<uint16_t>(0); + writeInt<uint16_t>(0); + writeInt<uint16_t>(1); // true + RETURN_IF_ERROR(writeCString(Blk.Name)); + padStream(sizeof(uint32_t)); + } + + for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) { + VersionInfoStmt *ItemPtr = Item.get(); + + if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) { + RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr)); + continue; + } + + auto *ValuePtr = cast<VersionInfoValue>(ItemPtr); + RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr)); + } + + if (OutputHeader) { + uint64_t CurLoc = tell(); + writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); + } + + return Error::success(); +} + +Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) { + // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE + // is a mapping from the key (string) to the value (a sequence of ints or + // a sequence of strings). + // + // If integers are to be written: width of each integer written depends on + // whether it's been declared 'long' (it's DWORD then) or not (it's WORD). + // ValueLength defined in structure referenced below is then the total + // number of bytes taken by these integers. + // + // If strings are to be written: characters are always WORDs. + // Moreover, '\0' character is written after the last string, and between + // every two strings separated by comma (if strings are not comma-separated, + // they're simply concatenated). ValueLength is equal to the number of WORDs + // written (that is, half of the bytes written). + // + // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx + bool HasStrings = false, HasInts = false; + for (auto &Item : Val.Values) + (Item.isInt() ? HasInts : HasStrings) = true; + + assert((HasStrings || HasInts) && "VALUE must have at least one argument"); + if (HasStrings && HasInts) + return createError(Twine("VALUE ") + Val.Key + + " cannot contain both strings and integers"); + + padStream(sizeof(uint32_t)); + auto LengthLoc = writeInt<uint16_t>(0); + auto ValLengthLoc = writeInt<uint16_t>(0); + writeInt<uint16_t>(HasStrings); + RETURN_IF_ERROR(writeCString(Val.Key)); + padStream(sizeof(uint32_t)); + + auto DataLoc = tell(); + for (size_t Id = 0; Id < Val.Values.size(); ++Id) { + auto &Item = Val.Values[Id]; + if (Item.isInt()) { + auto Value = Item.getInt(); + RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value")); + writeRCInt(Value); + continue; + } + + bool WriteTerminator = + Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1]; + RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator)); + } + + auto CurLoc = tell(); + auto ValueLength = CurLoc - DataLoc; + if (HasStrings) { + assert(ValueLength % 2 == 0); + ValueLength /= 2; + } + writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); + writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc); + return Error::success(); +} + +template <typename Ty> +static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key, + const Ty &Default) { + auto Iter = Map.find(Key); + if (Iter != Map.end()) + return Iter->getValue(); + return Default; +} + +Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) { + auto *Res = cast<VersionInfoResource>(Base); + + const auto &FixedData = Res->FixedData; + + struct /* VS_FIXEDFILEINFO */ { + ulittle32_t Signature = ulittle32_t(0xFEEF04BD); + ulittle32_t StructVersion = ulittle32_t(0x10000); + // It's weird to have most-significant DWORD first on the little-endian + // machines, but let it be this way. + ulittle32_t FileVersionMS; + ulittle32_t FileVersionLS; + ulittle32_t ProductVersionMS; + ulittle32_t ProductVersionLS; + ulittle32_t FileFlagsMask; + ulittle32_t FileFlags; + ulittle32_t FileOS; + ulittle32_t FileType; + ulittle32_t FileSubtype; + // MS implementation seems to always set these fields to 0. + ulittle32_t FileDateMS = ulittle32_t(0); + ulittle32_t FileDateLS = ulittle32_t(0); + } FixedInfo; + + // First, VS_VERSIONINFO. + auto LengthLoc = writeInt<uint16_t>(0); + writeInt<uint16_t>(sizeof(FixedInfo)); + writeInt<uint16_t>(0); + cantFail(writeCString("VS_VERSION_INFO")); + padStream(sizeof(uint32_t)); + + using VersionInfoFixed = VersionInfoResource::VersionInfoFixed; + auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) { + static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0}; + if (!FixedData.IsTypePresent[(int)Type]) + return DefaultOut; + return FixedData.FixedInfo[(int)Type]; + }; + + auto FileVer = GetField(VersionInfoFixed::FtFileVersion); + RETURN_IF_ERROR(checkNumberFits<uint16_t>( + *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields")); + FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1]; + FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3]; + + auto ProdVer = GetField(VersionInfoFixed::FtProductVersion); + RETURN_IF_ERROR(checkNumberFits<uint16_t>( + *std::max_element(ProdVer.begin(), ProdVer.end()), + "PRODUCTVERSION fields")); + FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1]; + FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3]; + + FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0]; + FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0]; + FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0]; + FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0]; + FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0]; + + writeObject(FixedInfo); + padStream(sizeof(uint32_t)); + + RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock)); + + // FIXME: check overflow? + writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc); + + return Error::success(); +} + +Expected<std::unique_ptr<MemoryBuffer>> +ResourceFileWriter::loadFile(StringRef File) const { + SmallString<128> Path; + SmallString<128> Cwd; + std::unique_ptr<MemoryBuffer> Result; + // 0. The file path is absolute or has a root directory, so we shouldn't // try to append it on top of other base directories. (An absolute path // must have a root directory, but e.g. the path "\dir\file" on windows @@ -1524,38 +1524,38 @@ ResourceFileWriter::loadFile(StringRef File) const { // properly though, so if using that to append paths below, this early // exception case could be removed.) if (sys::path::has_root_directory(File)) - return errorOrToExpected(MemoryBuffer::getFile(File, -1, false)); - - // 1. The current working directory. - sys::fs::current_path(Cwd); - Path.assign(Cwd.begin(), Cwd.end()); - sys::path::append(Path, File); - if (sys::fs::exists(Path)) - return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); - - // 2. The directory of the input resource file, if it is different from the - // current working directory. - StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath); - Path.assign(InputFileDir.begin(), InputFileDir.end()); - sys::path::append(Path, File); - if (sys::fs::exists(Path)) - return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); - - // 3. All of the include directories specified on the command line. - for (StringRef ForceInclude : Params.Include) { - Path.assign(ForceInclude.begin(), ForceInclude.end()); - sys::path::append(Path, File); - if (sys::fs::exists(Path)) - return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); - } - - if (auto Result = - llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude)) - return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false)); - - return make_error<StringError>("error : file not found : " + Twine(File), - inconvertibleErrorCode()); -} - -} // namespace rc -} // namespace llvm + return errorOrToExpected(MemoryBuffer::getFile(File, -1, false)); + + // 1. The current working directory. + sys::fs::current_path(Cwd); + Path.assign(Cwd.begin(), Cwd.end()); + sys::path::append(Path, File); + if (sys::fs::exists(Path)) + return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); + + // 2. The directory of the input resource file, if it is different from the + // current working directory. + StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath); + Path.assign(InputFileDir.begin(), InputFileDir.end()); + sys::path::append(Path, File); + if (sys::fs::exists(Path)) + return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); + + // 3. All of the include directories specified on the command line. + for (StringRef ForceInclude : Params.Include) { + Path.assign(ForceInclude.begin(), ForceInclude.end()); + sys::path::append(Path, File); + if (sys::fs::exists(Path)) + return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); + } + + if (auto Result = + llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude)) + return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false)); + + return make_error<StringError>("error : file not found : " + Twine(File), + inconvertibleErrorCode()); +} + +} // namespace rc +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.h b/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.h index d545a7a9ca..a8802d2220 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.h +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceFileWriter.h @@ -1,217 +1,217 @@ -//===-- ResourceSerializator.h ----------------------------------*- 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 defines a visitor serializing resources to a .res stream. -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H -#define LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H - -#include "ResourceScriptStmt.h" -#include "ResourceVisitor.h" - -#include "llvm/Support/Endian.h" - -namespace llvm { - -class MemoryBuffer; - -namespace rc { - -enum CodePage { - CpAcp = 0, // The current used codepage. Since there's no such - // notion in LLVM what codepage it actually means, - // this only allows ASCII. - CpWin1252 = 1252, // A codepage where most 8 bit values correspond to - // unicode code points with the same value. - CpUtf8 = 65001, // UTF-8. -}; - -struct WriterParams { - std::vector<std::string> Include; // Additional folders to search for files. - std::vector<std::string> NoInclude; // Folders to exclude from file search. - StringRef InputFilePath; // The full path of the input file. - int CodePage = CpAcp; // The codepage for interpreting characters. -}; - -class ResourceFileWriter : public Visitor { -public: - ResourceFileWriter(const WriterParams &Params, - std::unique_ptr<raw_fd_ostream> Stream) - : Params(Params), FS(std::move(Stream)), IconCursorID(1) { - assert(FS && "Output stream needs to be provided to the serializator"); - } - - Error visitNullResource(const RCResource *) override; - Error visitAcceleratorsResource(const RCResource *) override; - Error visitCursorResource(const RCResource *) override; - Error visitDialogResource(const RCResource *) override; - Error visitHTMLResource(const RCResource *) override; - Error visitIconResource(const RCResource *) override; - Error visitMenuResource(const RCResource *) override; - Error visitVersionInfoResource(const RCResource *) override; - Error visitStringTableResource(const RCResource *) override; - Error visitUserDefinedResource(const RCResource *) override; - - Error visitCaptionStmt(const CaptionStmt *) override; - Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; - Error visitClassStmt(const ClassStmt *) override; - Error visitExStyleStmt(const ExStyleStmt *) override; - Error visitFontStmt(const FontStmt *) override; - Error visitLanguageStmt(const LanguageResource *) override; - Error visitStyleStmt(const StyleStmt *) override; - Error visitVersionStmt(const VersionStmt *) override; - - // Stringtables are output at the end of .res file. We need a separate - // function to do it. - Error dumpAllStringTables(); - - bool AppendNull = false; // Append '\0' to each existing STRINGTABLE element? - - struct ObjectInfo { - uint16_t LanguageInfo; - uint32_t Characteristics; - uint32_t VersionInfo; - - Optional<uint32_t> Style; - Optional<uint32_t> ExStyle; - StringRef Caption; - struct FontInfo { - uint32_t Size; - StringRef Typeface; - uint32_t Weight; - bool IsItalic; - uint32_t Charset; - }; - Optional<FontInfo> Font; - IntOrString Class; - - ObjectInfo() - : LanguageInfo(0), Characteristics(0), VersionInfo(0), - Class(StringRef()) {} - } ObjectData; - - struct StringTableInfo { - // Each STRINGTABLE bundle depends on ID of the bundle and language - // description. - using BundleKey = std::pair<uint16_t, uint16_t>; - // Each bundle is in fact an array of 16 strings. - struct Bundle { - std::array<Optional<std::vector<StringRef>>, 16> Data; - ObjectInfo DeclTimeInfo; - uint16_t MemoryFlags; - Bundle(const ObjectInfo &Info, uint16_t Flags) - : DeclTimeInfo(Info), MemoryFlags(Flags) {} - }; - std::map<BundleKey, Bundle> BundleData; - // Bundles are listed in the order of their first occurrence. - std::vector<BundleKey> BundleList; - } StringTableData; - -private: - Error handleError(Error Err, const RCResource *Res); - - Error - writeResource(const RCResource *Res, - Error (ResourceFileWriter::*BodyWriter)(const RCResource *)); - - // NullResource - Error writeNullBody(const RCResource *); - - // AcceleratorsResource - Error writeSingleAccelerator(const AcceleratorsResource::Accelerator &, - bool IsLastItem); - Error writeAcceleratorsBody(const RCResource *); - - // BitmapResource - Error visitBitmapResource(const RCResource *) override; - Error writeBitmapBody(const RCResource *); - - // CursorResource and IconResource - Error visitIconOrCursorResource(const RCResource *); - Error visitIconOrCursorGroup(const RCResource *); - Error visitSingleIconOrCursor(const RCResource *); - Error writeSingleIconOrCursorBody(const RCResource *); - Error writeIconOrCursorGroupBody(const RCResource *); - - // DialogResource - Error writeSingleDialogControl(const Control &, bool IsExtended); - Error writeDialogBody(const RCResource *); - - // HTMLResource - Error writeHTMLBody(const RCResource *); - - // MenuResource - Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &, - uint16_t Flags); - Error writeMenuDefinitionList(const MenuDefinitionList &List); - Error writeMenuBody(const RCResource *); - - // StringTableResource - Error visitStringTableBundle(const RCResource *); - Error writeStringTableBundleBody(const RCResource *); - Error insertStringIntoBundle(StringTableInfo::Bundle &Bundle, - uint16_t StringID, - const std::vector<StringRef> &String); - - // User defined resource - Error writeUserDefinedBody(const RCResource *); - - // VersionInfoResource - Error writeVersionInfoBody(const RCResource *); - Error writeVersionInfoBlock(const VersionInfoBlock &); - Error writeVersionInfoValue(const VersionInfoValue &); - - const WriterParams &Params; - - // Output stream handling. - std::unique_ptr<raw_fd_ostream> FS; - - uint64_t tell() const { return FS->tell(); } - - uint64_t writeObject(const ArrayRef<uint8_t> Data); - - template <typename T> uint64_t writeInt(const T &Value) { - support::detail::packed_endian_specific_integral<T, support::little, - support::unaligned> - Object(Value); - return writeObject(Object); - } - - template <typename T> uint64_t writeObject(const T &Value) { - return writeObject(ArrayRef<uint8_t>( - reinterpret_cast<const uint8_t *>(&Value), sizeof(T))); - } - - template <typename T> void writeObjectAt(const T &Value, uint64_t Position) { - FS->pwrite((const char *)&Value, sizeof(T), Position); - } - - Error writeCString(StringRef Str, bool WriteTerminator = true); - - Error writeIdentifier(const IntOrString &Ident); - Error writeIntOrString(const IntOrString &Data); - - void writeRCInt(RCInt); - - Error appendFile(StringRef Filename); - - void padStream(uint64_t Length); - - Expected<std::unique_ptr<MemoryBuffer>> loadFile(StringRef File) const; - - // Icon and cursor IDs are allocated starting from 1 and increasing for - // each icon/cursor dumped. This maintains the current ID to be allocated. - uint16_t IconCursorID; -}; - -} // namespace rc -} // namespace llvm - -#endif +//===-- ResourceSerializator.h ----------------------------------*- 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 defines a visitor serializing resources to a .res stream. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H +#define LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H + +#include "ResourceScriptStmt.h" +#include "ResourceVisitor.h" + +#include "llvm/Support/Endian.h" + +namespace llvm { + +class MemoryBuffer; + +namespace rc { + +enum CodePage { + CpAcp = 0, // The current used codepage. Since there's no such + // notion in LLVM what codepage it actually means, + // this only allows ASCII. + CpWin1252 = 1252, // A codepage where most 8 bit values correspond to + // unicode code points with the same value. + CpUtf8 = 65001, // UTF-8. +}; + +struct WriterParams { + std::vector<std::string> Include; // Additional folders to search for files. + std::vector<std::string> NoInclude; // Folders to exclude from file search. + StringRef InputFilePath; // The full path of the input file. + int CodePage = CpAcp; // The codepage for interpreting characters. +}; + +class ResourceFileWriter : public Visitor { +public: + ResourceFileWriter(const WriterParams &Params, + std::unique_ptr<raw_fd_ostream> Stream) + : Params(Params), FS(std::move(Stream)), IconCursorID(1) { + assert(FS && "Output stream needs to be provided to the serializator"); + } + + Error visitNullResource(const RCResource *) override; + Error visitAcceleratorsResource(const RCResource *) override; + Error visitCursorResource(const RCResource *) override; + Error visitDialogResource(const RCResource *) override; + Error visitHTMLResource(const RCResource *) override; + Error visitIconResource(const RCResource *) override; + Error visitMenuResource(const RCResource *) override; + Error visitVersionInfoResource(const RCResource *) override; + Error visitStringTableResource(const RCResource *) override; + Error visitUserDefinedResource(const RCResource *) override; + + Error visitCaptionStmt(const CaptionStmt *) override; + Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; + Error visitClassStmt(const ClassStmt *) override; + Error visitExStyleStmt(const ExStyleStmt *) override; + Error visitFontStmt(const FontStmt *) override; + Error visitLanguageStmt(const LanguageResource *) override; + Error visitStyleStmt(const StyleStmt *) override; + Error visitVersionStmt(const VersionStmt *) override; + + // Stringtables are output at the end of .res file. We need a separate + // function to do it. + Error dumpAllStringTables(); + + bool AppendNull = false; // Append '\0' to each existing STRINGTABLE element? + + struct ObjectInfo { + uint16_t LanguageInfo; + uint32_t Characteristics; + uint32_t VersionInfo; + + Optional<uint32_t> Style; + Optional<uint32_t> ExStyle; + StringRef Caption; + struct FontInfo { + uint32_t Size; + StringRef Typeface; + uint32_t Weight; + bool IsItalic; + uint32_t Charset; + }; + Optional<FontInfo> Font; + IntOrString Class; + + ObjectInfo() + : LanguageInfo(0), Characteristics(0), VersionInfo(0), + Class(StringRef()) {} + } ObjectData; + + struct StringTableInfo { + // Each STRINGTABLE bundle depends on ID of the bundle and language + // description. + using BundleKey = std::pair<uint16_t, uint16_t>; + // Each bundle is in fact an array of 16 strings. + struct Bundle { + std::array<Optional<std::vector<StringRef>>, 16> Data; + ObjectInfo DeclTimeInfo; + uint16_t MemoryFlags; + Bundle(const ObjectInfo &Info, uint16_t Flags) + : DeclTimeInfo(Info), MemoryFlags(Flags) {} + }; + std::map<BundleKey, Bundle> BundleData; + // Bundles are listed in the order of their first occurrence. + std::vector<BundleKey> BundleList; + } StringTableData; + +private: + Error handleError(Error Err, const RCResource *Res); + + Error + writeResource(const RCResource *Res, + Error (ResourceFileWriter::*BodyWriter)(const RCResource *)); + + // NullResource + Error writeNullBody(const RCResource *); + + // AcceleratorsResource + Error writeSingleAccelerator(const AcceleratorsResource::Accelerator &, + bool IsLastItem); + Error writeAcceleratorsBody(const RCResource *); + + // BitmapResource + Error visitBitmapResource(const RCResource *) override; + Error writeBitmapBody(const RCResource *); + + // CursorResource and IconResource + Error visitIconOrCursorResource(const RCResource *); + Error visitIconOrCursorGroup(const RCResource *); + Error visitSingleIconOrCursor(const RCResource *); + Error writeSingleIconOrCursorBody(const RCResource *); + Error writeIconOrCursorGroupBody(const RCResource *); + + // DialogResource + Error writeSingleDialogControl(const Control &, bool IsExtended); + Error writeDialogBody(const RCResource *); + + // HTMLResource + Error writeHTMLBody(const RCResource *); + + // MenuResource + Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &, + uint16_t Flags); + Error writeMenuDefinitionList(const MenuDefinitionList &List); + Error writeMenuBody(const RCResource *); + + // StringTableResource + Error visitStringTableBundle(const RCResource *); + Error writeStringTableBundleBody(const RCResource *); + Error insertStringIntoBundle(StringTableInfo::Bundle &Bundle, + uint16_t StringID, + const std::vector<StringRef> &String); + + // User defined resource + Error writeUserDefinedBody(const RCResource *); + + // VersionInfoResource + Error writeVersionInfoBody(const RCResource *); + Error writeVersionInfoBlock(const VersionInfoBlock &); + Error writeVersionInfoValue(const VersionInfoValue &); + + const WriterParams &Params; + + // Output stream handling. + std::unique_ptr<raw_fd_ostream> FS; + + uint64_t tell() const { return FS->tell(); } + + uint64_t writeObject(const ArrayRef<uint8_t> Data); + + template <typename T> uint64_t writeInt(const T &Value) { + support::detail::packed_endian_specific_integral<T, support::little, + support::unaligned> + Object(Value); + return writeObject(Object); + } + + template <typename T> uint64_t writeObject(const T &Value) { + return writeObject(ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(&Value), sizeof(T))); + } + + template <typename T> void writeObjectAt(const T &Value, uint64_t Position) { + FS->pwrite((const char *)&Value, sizeof(T), Position); + } + + Error writeCString(StringRef Str, bool WriteTerminator = true); + + Error writeIdentifier(const IntOrString &Ident); + Error writeIntOrString(const IntOrString &Data); + + void writeRCInt(RCInt); + + Error appendFile(StringRef Filename); + + void padStream(uint64_t Length); + + Expected<std::unique_ptr<MemoryBuffer>> loadFile(StringRef File) const; + + // Icon and cursor IDs are allocated starting from 1 and increasing for + // each icon/cursor dumped. This maintains the current ID to be allocated. + uint16_t IconCursorID; +}; + +} // namespace rc +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.cpp b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.cpp index e610be99df..826b212db9 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.cpp +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.cpp @@ -1,111 +1,111 @@ -//===-- ResourceScriptCppFilter.cpp ----------------------------*- 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 implements an interface defined in ResourceScriptCppFilter.h. -// -//===---------------------------------------------------------------------===// - -#include "ResourceScriptCppFilter.h" -#include "llvm/ADT/StringExtras.h" - -#include <vector> - -using namespace llvm; - -namespace { - -class Filter { -public: - explicit Filter(StringRef Input) : Data(Input), DataLength(Input.size()) {} - - std::string run(); - -private: - // Parse the line, returning whether the line should be included in - // the output. - bool parseLine(StringRef Line); - - bool streamEof() const; - - StringRef Data; - size_t DataLength; - - size_t Pos = 0; - bool Outputting = true; -}; - -std::string Filter::run() { - std::vector<StringRef> Output; - - while (!streamEof() && Pos != StringRef::npos) { - size_t LineStart = Pos; - Pos = Data.find_first_of("\r\n", Pos); - Pos = Data.find_first_not_of("\r\n", Pos); - StringRef Line = Data.take_front(Pos).drop_front(LineStart); - - if (parseLine(Line)) - Output.push_back(Line); - } - - return llvm::join(Output, ""); -} - -bool Filter::parseLine(StringRef Line) { - Line = Line.ltrim(); - - if (!Line.consume_front("#")) { - // A normal content line, filtered according to the current mode. - return Outputting; - } - - // Found a preprocessing directive line. From here on, we always return - // false since the preprocessing directives should be filtered out. - - Line.consume_front("line"); - if (!Line.startswith(" ")) - return false; // Not a line directive (pragma etc). - - // #line 123 "path/file.h" - // # 123 "path/file.h" 1 - - Line = - Line.ltrim(); // There could be multiple spaces after the #line directive - - size_t N; - if (Line.consumeInteger(10, N)) // Returns true to signify an error - return false; - - Line = Line.ltrim(); - - if (!Line.consume_front("\"")) - return false; // Malformed line, no quote found. - - // Split the string at the last quote (in case the path name had - // escaped quotes as well). - Line = Line.rsplit('"').first; - - StringRef Ext = Line.rsplit('.').second; - - if (Ext.equals_lower("h") || Ext.equals_lower("c")) { - Outputting = false; - } else { - Outputting = true; - } - - return false; -} - -bool Filter::streamEof() const { return Pos == DataLength; } - -} // anonymous namespace - -namespace llvm { - -std::string filterCppOutput(StringRef Input) { return Filter(Input).run(); } - -} // namespace llvm +//===-- ResourceScriptCppFilter.cpp ----------------------------*- 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 implements an interface defined in ResourceScriptCppFilter.h. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptCppFilter.h" +#include "llvm/ADT/StringExtras.h" + +#include <vector> + +using namespace llvm; + +namespace { + +class Filter { +public: + explicit Filter(StringRef Input) : Data(Input), DataLength(Input.size()) {} + + std::string run(); + +private: + // Parse the line, returning whether the line should be included in + // the output. + bool parseLine(StringRef Line); + + bool streamEof() const; + + StringRef Data; + size_t DataLength; + + size_t Pos = 0; + bool Outputting = true; +}; + +std::string Filter::run() { + std::vector<StringRef> Output; + + while (!streamEof() && Pos != StringRef::npos) { + size_t LineStart = Pos; + Pos = Data.find_first_of("\r\n", Pos); + Pos = Data.find_first_not_of("\r\n", Pos); + StringRef Line = Data.take_front(Pos).drop_front(LineStart); + + if (parseLine(Line)) + Output.push_back(Line); + } + + return llvm::join(Output, ""); +} + +bool Filter::parseLine(StringRef Line) { + Line = Line.ltrim(); + + if (!Line.consume_front("#")) { + // A normal content line, filtered according to the current mode. + return Outputting; + } + + // Found a preprocessing directive line. From here on, we always return + // false since the preprocessing directives should be filtered out. + + Line.consume_front("line"); + if (!Line.startswith(" ")) + return false; // Not a line directive (pragma etc). + + // #line 123 "path/file.h" + // # 123 "path/file.h" 1 + + Line = + Line.ltrim(); // There could be multiple spaces after the #line directive + + size_t N; + if (Line.consumeInteger(10, N)) // Returns true to signify an error + return false; + + Line = Line.ltrim(); + + if (!Line.consume_front("\"")) + return false; // Malformed line, no quote found. + + // Split the string at the last quote (in case the path name had + // escaped quotes as well). + Line = Line.rsplit('"').first; + + StringRef Ext = Line.rsplit('.').second; + + if (Ext.equals_lower("h") || Ext.equals_lower("c")) { + Outputting = false; + } else { + Outputting = true; + } + + return false; +} + +bool Filter::streamEof() const { return Pos == DataLength; } + +} // anonymous namespace + +namespace llvm { + +std::string filterCppOutput(StringRef Input) { return Filter(Input).run(); } + +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.h b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.h index 780db317c9..754dace070 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.h +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptCppFilter.h @@ -1,34 +1,34 @@ -//===-- ResourceScriptCppFilter.h ------------------------------*- 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 filters the input to llvm-rc for preprocessor markers, removing -// preprocessing directives that a preprocessor can output or leave behind. -// -// It also filters out any contribution from files named *.h or *.c, based -// on preprocessor line markers. When preprocessing RC files, the included -// headers can leave behind C declarations, that RC doesn't understand. -// Rc.exe simply discards anything that comes from files named *.h or *.h. -// -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa381033(v=vs.85).aspx -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H -#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H - -#include "llvm/ADT/StringRef.h" - -#include <string> - -namespace llvm { - -std::string filterCppOutput(StringRef Input); - -} // namespace llvm - -#endif +//===-- ResourceScriptCppFilter.h ------------------------------*- 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 filters the input to llvm-rc for preprocessor markers, removing +// preprocessing directives that a preprocessor can output or leave behind. +// +// It also filters out any contribution from files named *.h or *.c, based +// on preprocessor line markers. When preprocessing RC files, the included +// headers can leave behind C declarations, that RC doesn't understand. +// Rc.exe simply discards anything that comes from files named *.h or *.h. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa381033(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H + +#include "llvm/ADT/StringRef.h" + +#include <string> + +namespace llvm { + +std::string filterCppOutput(StringRef Input); + +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.cpp b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.cpp index 5141ac0c38..1dbfc5e8bf 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.cpp +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.cpp @@ -1,857 +1,857 @@ -//===-- ResourceScriptParser.cpp --------------------------------*- 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 implements the parser defined in ResourceScriptParser.h. -// -//===---------------------------------------------------------------------===// - -#include "ResourceScriptParser.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Process.h" - -// Take an expression returning llvm::Error and forward the error if it exists. -#define RETURN_IF_ERROR(Expr) \ - if (auto Err = (Expr)) \ - return std::move(Err); - -// Take an expression returning llvm::Expected<T> and assign it to Var or -// forward the error out of the function. -#define ASSIGN_OR_RETURN(Var, Expr) \ - auto Var = (Expr); \ - if (!Var) \ - return Var.takeError(); - -namespace llvm { -namespace rc { - -RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc, - const LocIter End) - : ErrorLoc(CurLoc), FileEnd(End) { - CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + - (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); -} - -char RCParser::ParserError::ID = 0; - -RCParser::RCParser(std::vector<RCToken> TokenList) - : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {} - -bool RCParser::isEof() const { return CurLoc == End; } - -RCParser::ParseType RCParser::parseSingleResource() { - // The first thing we read is usually a resource's name. However, in some - // cases (LANGUAGE and STRINGTABLE) the resources don't have their names - // and the first token to be read is the type. - ASSIGN_OR_RETURN(NameToken, readTypeOrName()); - - if (NameToken->equalsLower("LANGUAGE")) - return parseLanguageResource(); - else if (NameToken->equalsLower("STRINGTABLE")) - return parseStringTableResource(); - - // If it's not an unnamed resource, what we've just read is a name. Now, - // read resource type; - ASSIGN_OR_RETURN(TypeToken, readTypeOrName()); - - ParseType Result = std::unique_ptr<RCResource>(); - (void)!Result; - - if (TypeToken->equalsLower("ACCELERATORS")) - Result = parseAcceleratorsResource(); - else if (TypeToken->equalsLower("BITMAP")) - Result = parseBitmapResource(); - else if (TypeToken->equalsLower("CURSOR")) - Result = parseCursorResource(); - else if (TypeToken->equalsLower("DIALOG")) - Result = parseDialogResource(false); - else if (TypeToken->equalsLower("DIALOGEX")) - Result = parseDialogResource(true); - else if (TypeToken->equalsLower("HTML")) - Result = parseHTMLResource(); - else if (TypeToken->equalsLower("ICON")) - Result = parseIconResource(); - else if (TypeToken->equalsLower("MENU")) - Result = parseMenuResource(); - else if (TypeToken->equalsLower("RCDATA")) - Result = parseUserDefinedResource(RkRcData); - else if (TypeToken->equalsLower("VERSIONINFO")) - Result = parseVersionInfoResource(); - else - Result = parseUserDefinedResource(*TypeToken); - - if (Result) - (*Result)->setName(*NameToken); - - return Result; -} - -bool RCParser::isNextTokenKind(Kind TokenKind) const { - return !isEof() && look().kind() == TokenKind; -} - -const RCToken &RCParser::look() const { - assert(!isEof()); - return *CurLoc; -} - -const RCToken &RCParser::read() { - assert(!isEof()); - return *CurLoc++; -} - -void RCParser::consume() { - assert(!isEof()); - CurLoc++; -} - -// An integer description might consist of a single integer or -// an arithmetic expression evaluating to the integer. The expressions -// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning -// is the same as in C++ except for 'not' expression. -// The operators in the original RC implementation have the following -// precedence: -// 1) Unary operators (- ~ not), -// 2) Binary operators (+ - & |), with no precedence. -// -// 'not' expression is mostly useful for style values. It evaluates to 0, -// but value given to the operator is stored separately from integer value. -// It's mostly useful for control style expressions and causes bits from -// default control style to be excluded from generated style. For binary -// operators the mask from the right operand is applied to the left operand -// and masks from both operands are combined in operator result. -// -// The following grammar is used to parse the expressions Exp1: -// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 -// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). -// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, -// separated by binary operators.) -// -// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 -// is read by parseIntExpr2(). -// -// The original Microsoft tool handles multiple unary operators incorrectly. -// For example, in 16-bit little-endian integers: -// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; -// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. -// Our implementation differs from the original one and handles these -// operators correctly: -// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; -// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. - -Expected<RCInt> RCParser::readInt() { - ASSIGN_OR_RETURN(Value, parseIntExpr1()); - return (*Value).getValue(); -} - -Expected<IntWithNotMask> RCParser::parseIntExpr1() { - // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. - ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); - IntWithNotMask Result = *FirstResult; - - while (!isEof() && look().isBinaryOp()) { - auto OpToken = read(); - ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); - - switch (OpToken.kind()) { - case Kind::Plus: - Result += *NextResult; - break; - - case Kind::Minus: - Result -= *NextResult; - break; - - case Kind::Pipe: - Result |= *NextResult; - break; - - case Kind::Amp: - Result &= *NextResult; - break; - - default: - llvm_unreachable("Already processed all binary ops."); - } - } - - return Result; -} - -Expected<IntWithNotMask> RCParser::parseIntExpr2() { - // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). - static const char ErrorMsg[] = "'-', '~', integer or '('"; - - if (isEof()) - return getExpectedError(ErrorMsg); - - switch (look().kind()) { - case Kind::Minus: { - consume(); - ASSIGN_OR_RETURN(Result, parseIntExpr2()); - return -(*Result); - } - - case Kind::Tilde: { - consume(); - ASSIGN_OR_RETURN(Result, parseIntExpr2()); - return ~(*Result); - } - - case Kind::Int: - return RCInt(read()); - - case Kind::LeftParen: { - consume(); - ASSIGN_OR_RETURN(Result, parseIntExpr1()); - RETURN_IF_ERROR(consumeType(Kind::RightParen)); - return *Result; - } - - case Kind::Identifier: { - if (!read().value().equals_lower("not")) - return getExpectedError(ErrorMsg, true); - ASSIGN_OR_RETURN(Result, parseIntExpr2()); - return IntWithNotMask(0, (*Result).getValue()); - } - - default: - return getExpectedError(ErrorMsg); - } -} - -Expected<StringRef> RCParser::readString() { - if (!isNextTokenKind(Kind::String)) - return getExpectedError("string"); - return read().value(); -} - -Expected<StringRef> RCParser::readFilename() { - if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier)) - return getExpectedError("string"); - return read().value(); -} - -Expected<StringRef> RCParser::readIdentifier() { - if (!isNextTokenKind(Kind::Identifier)) - return getExpectedError("identifier"); - return read().value(); -} - -Expected<IntOrString> RCParser::readIntOrString() { - if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) - return getExpectedError("int or string"); - return IntOrString(read()); -} - -Expected<IntOrString> RCParser::readTypeOrName() { - // We suggest that the correct resource name or type should be either an - // identifier or an integer. The original RC tool is much more liberal. - if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) - return getExpectedError("int or identifier"); - return IntOrString(read()); -} - -Error RCParser::consumeType(Kind TokenKind) { - if (isNextTokenKind(TokenKind)) { - consume(); - return Error::success(); - } - - switch (TokenKind) { -#define TOKEN(TokenName) \ - case Kind::TokenName: \ - return getExpectedError(#TokenName); -#define SHORT_TOKEN(TokenName, TokenCh) \ - case Kind::TokenName: \ - return getExpectedError(#TokenCh); -#include "ResourceScriptTokenList.def" - } - - llvm_unreachable("All case options exhausted."); -} - -bool RCParser::consumeOptionalType(Kind TokenKind) { - if (isNextTokenKind(TokenKind)) { - consume(); - return true; - } - - return false; -} - -Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, - size_t MaxCount) { - assert(MinCount <= MaxCount); - - SmallVector<RCInt, 8> Result; - - auto FailureHandler = - [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> { - if (Result.size() < MinCount) - return std::move(Err); - consumeError(std::move(Err)); - return Result; - }; - - for (size_t i = 0; i < MaxCount; ++i) { - // Try to read a comma unless we read the first token. - // Sometimes RC tool requires them and sometimes not. We decide to - // always require them. - if (i >= 1) { - if (auto CommaError = consumeType(Kind::Comma)) - return FailureHandler(std::move(CommaError)); - } - - if (auto IntResult = readInt()) - Result.push_back(*IntResult); - else - return FailureHandler(IntResult.takeError()); - } - - return std::move(Result); -} - -Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, - ArrayRef<uint32_t> FlagValues) { - assert(!FlagDesc.empty()); - assert(FlagDesc.size() == FlagValues.size()); - - uint32_t Result = 0; - while (isNextTokenKind(Kind::Comma)) { - consume(); - ASSIGN_OR_RETURN(FlagResult, readIdentifier()); - bool FoundFlag = false; - - for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { - if (!FlagResult->equals_lower(FlagDesc[FlagId])) - continue; - - Result |= FlagValues[FlagId]; - FoundFlag = true; - break; - } - - if (!FoundFlag) - return getExpectedError(join(FlagDesc, "/"), true); - } - - return Result; -} - -uint16_t RCParser::parseMemoryFlags(uint16_t Flags) { - while (!isEof()) { - const RCToken &Token = look(); - if (Token.kind() != Kind::Identifier) - return Flags; - const StringRef Ident = Token.value(); - if (Ident.equals_lower("PRELOAD")) - Flags |= MfPreload; - else if (Ident.equals_lower("LOADONCALL")) - Flags &= ~MfPreload; - else if (Ident.equals_lower("FIXED")) - Flags &= ~(MfMoveable | MfDiscardable); - else if (Ident.equals_lower("MOVEABLE")) - Flags |= MfMoveable; - else if (Ident.equals_lower("DISCARDABLE")) - Flags |= MfDiscardable | MfMoveable | MfPure; - else if (Ident.equals_lower("PURE")) - Flags |= MfPure; - else if (Ident.equals_lower("IMPURE")) - Flags &= ~(MfPure | MfDiscardable); - else if (Ident.equals_lower("SHARED")) - Flags |= MfPure; - else if (Ident.equals_lower("NONSHARED")) - Flags &= ~(MfPure | MfDiscardable); - else - return Flags; - consume(); - } - return Flags; -} - -Expected<OptionalStmtList> -RCParser::parseOptionalStatements(OptStmtType StmtsType) { - OptionalStmtList Result; - - // The last statement is always followed by the start of the block. - while (!isNextTokenKind(Kind::BlockBegin)) { - ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType)); - Result.addStmt(std::move(*SingleParse)); - } - - return std::move(Result); -} - -Expected<std::unique_ptr<OptionalStmt>> -RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { - ASSIGN_OR_RETURN(TypeToken, readIdentifier()); - if (TypeToken->equals_lower("CHARACTERISTICS")) - return parseCharacteristicsStmt(); - if (TypeToken->equals_lower("LANGUAGE")) - return parseLanguageStmt(); - if (TypeToken->equals_lower("VERSION")) - return parseVersionStmt(); - - if (StmtsType != OptStmtType::BasicStmt) { - if (TypeToken->equals_lower("CAPTION")) - return parseCaptionStmt(); - if (TypeToken->equals_lower("CLASS")) - return parseClassStmt(); - if (TypeToken->equals_lower("EXSTYLE")) - return parseExStyleStmt(); - if (TypeToken->equals_lower("FONT")) - return parseFontStmt(StmtsType); - if (TypeToken->equals_lower("STYLE")) - return parseStyleStmt(); - } - - return getExpectedError("optional statement type, BEGIN or '{'", - /* IsAlreadyRead = */ true); -} - -RCParser::ParseType RCParser::parseLanguageResource() { - // Read LANGUAGE as an optional statement. If it's read correctly, we can - // upcast it to RCResource. - return parseLanguageStmt(); -} - -RCParser::ParseType RCParser::parseAcceleratorsResource() { - uint16_t MemoryFlags = - parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); - RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - - auto Accels = std::make_unique<AcceleratorsResource>( - std::move(*OptStatements), MemoryFlags); - - while (!consumeOptionalType(Kind::BlockEnd)) { - ASSIGN_OR_RETURN(EventResult, readIntOrString()); - RETURN_IF_ERROR(consumeType(Kind::Comma)); - ASSIGN_OR_RETURN(IDResult, readInt()); - ASSIGN_OR_RETURN( - FlagsResult, - parseFlags(AcceleratorsResource::Accelerator::OptionsStr, - AcceleratorsResource::Accelerator::OptionsFlags)); - Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); - } - - return std::move(Accels); -} - -RCParser::ParseType RCParser::parseCursorResource() { - uint16_t MemoryFlags = - parseMemoryFlags(CursorResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(Arg, readFilename()); - return std::make_unique<CursorResource>(*Arg, MemoryFlags); -} - -RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { - uint16_t MemoryFlags = - parseMemoryFlags(DialogResource::getDefaultMemoryFlags()); - // Dialog resources have the following format of the arguments: - // DIALOG: x, y, width, height [opt stmts...] {controls...} - // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} - // These are very similar, so we parse them together. - ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); - - uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. - if (IsExtended && consumeOptionalType(Kind::Comma)) { - ASSIGN_OR_RETURN(HelpIDResult, readInt()); - HelpID = *HelpIDResult; - } - - ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( - IsExtended ? OptStmtType::DialogExStmt - : OptStmtType::DialogStmt)); - - assert(isNextTokenKind(Kind::BlockBegin) && - "parseOptionalStatements, when successful, halts on BlockBegin."); - consume(); - - auto Dialog = std::make_unique<DialogResource>( - (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], - HelpID, std::move(*OptStatements), IsExtended, MemoryFlags); - - while (!consumeOptionalType(Kind::BlockEnd)) { - ASSIGN_OR_RETURN(ControlDefResult, parseControl()); - Dialog->addControl(std::move(*ControlDefResult)); - } - - return std::move(Dialog); -} - -RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { - uint16_t MemoryFlags = - parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags()); - if (isEof()) - return getExpectedError("filename, '{' or BEGIN"); - - // Check if this is a file resource. - switch (look().kind()) { - case Kind::String: - case Kind::Identifier: - return std::make_unique<UserDefinedResource>(Type, read().value(), - MemoryFlags); - default: - break; - } - - RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - std::vector<IntOrString> Data; - - // Consume comma before each consecutive token except the first one. - bool ConsumeComma = false; - while (!consumeOptionalType(Kind::BlockEnd)) { - if (ConsumeComma) - RETURN_IF_ERROR(consumeType(Kind::Comma)); - ConsumeComma = true; - - ASSIGN_OR_RETURN(Item, readIntOrString()); - Data.push_back(*Item); - } - - return std::make_unique<UserDefinedResource>(Type, std::move(Data), - MemoryFlags); -} - -RCParser::ParseType RCParser::parseVersionInfoResource() { - uint16_t MemoryFlags = - parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); - ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); - return std::make_unique<VersionInfoResource>( - std::move(**BlockResult), std::move(*FixedResult), MemoryFlags); -} - -Expected<Control> RCParser::parseControl() { - // Each control definition (except CONTROL) follows one of the schemes below - // depending on the control class: - // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] - // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] - // Note that control ids must be integers. - // Text might be either a string or an integer pointing to resource ID. - ASSIGN_OR_RETURN(ClassResult, readIdentifier()); - std::string ClassUpper = ClassResult->upper(); - auto CtlInfo = Control::SupportedCtls.find(ClassUpper); - if (CtlInfo == Control::SupportedCtls.end()) - return getExpectedError("control type, END or '}'", true); - - // Read caption if necessary. - IntOrString Caption{StringRef()}; - if (CtlInfo->getValue().HasTitle) { - ASSIGN_OR_RETURN(CaptionResult, readIntOrString()); - RETURN_IF_ERROR(consumeType(Kind::Comma)); - Caption = *CaptionResult; - } - - ASSIGN_OR_RETURN(ID, readInt()); - RETURN_IF_ERROR(consumeType(Kind::Comma)); - - IntOrString Class; - Optional<IntWithNotMask> Style; - if (ClassUpper == "CONTROL") { - // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID] - ASSIGN_OR_RETURN(ClassStr, readString()); - RETURN_IF_ERROR(consumeType(Kind::Comma)); - Class = *ClassStr; - ASSIGN_OR_RETURN(StyleVal, parseIntExpr1()); - RETURN_IF_ERROR(consumeType(Kind::Comma)); - Style = *StyleVal; - } else { - Class = CtlInfo->getValue().CtlClass; - } - - // x, y, width, height - ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4)); - - if (ClassUpper != "CONTROL") { - if (consumeOptionalType(Kind::Comma)) { - ASSIGN_OR_RETURN(Val, parseIntExpr1()); - Style = *Val; - } - } - - Optional<uint32_t> ExStyle; - if (consumeOptionalType(Kind::Comma)) { - ASSIGN_OR_RETURN(Val, readInt()); - ExStyle = *Val; - } - Optional<uint32_t> HelpID; - if (consumeOptionalType(Kind::Comma)) { - ASSIGN_OR_RETURN(Val, readInt()); - HelpID = *Val; - } - - return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], - (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class); -} - -RCParser::ParseType RCParser::parseBitmapResource() { - uint16_t MemoryFlags = - parseMemoryFlags(BitmapResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(Arg, readFilename()); - return std::make_unique<BitmapResource>(*Arg, MemoryFlags); -} - -RCParser::ParseType RCParser::parseIconResource() { - uint16_t MemoryFlags = - parseMemoryFlags(IconResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(Arg, readFilename()); - return std::make_unique<IconResource>(*Arg, MemoryFlags); -} - -RCParser::ParseType RCParser::parseHTMLResource() { - uint16_t MemoryFlags = - parseMemoryFlags(HTMLResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(Arg, readFilename()); - return std::make_unique<HTMLResource>(*Arg, MemoryFlags); -} - -RCParser::ParseType RCParser::parseMenuResource() { - uint16_t MemoryFlags = - parseMemoryFlags(MenuResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); - ASSIGN_OR_RETURN(Items, parseMenuItemsList()); - return std::make_unique<MenuResource>(std::move(*OptStatements), - std::move(*Items), MemoryFlags); -} - -Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { - RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - - MenuDefinitionList List; - - // Read a set of items. Each item is of one of three kinds: - // MENUITEM SEPARATOR - // MENUITEM caption:String, result:Int [, menu flags]... - // POPUP caption:String [, menu flags]... { items... } - while (!consumeOptionalType(Kind::BlockEnd)) { - ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); - - bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); - bool IsPopup = ItemTypeResult->equals_lower("POPUP"); - if (!IsMenuItem && !IsPopup) - return getExpectedError("MENUITEM, POPUP, END or '}'", true); - - if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { - // Now, expecting SEPARATOR. - ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); - if (SeparatorResult->equals_lower("SEPARATOR")) { - List.addDefinition(std::make_unique<MenuSeparator>()); - continue; - } - - return getExpectedError("SEPARATOR or string", true); - } - - // Not a separator. Read the caption. - ASSIGN_OR_RETURN(CaptionResult, readString()); - - // If MENUITEM, expect also a comma and an integer. - uint32_t MenuResult = -1; - - if (IsMenuItem) { - RETURN_IF_ERROR(consumeType(Kind::Comma)); - ASSIGN_OR_RETURN(IntResult, readInt()); - MenuResult = *IntResult; - } - - ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, - MenuDefinition::OptionsFlags)); - - if (IsPopup) { - // If POPUP, read submenu items recursively. - ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); - List.addDefinition(std::make_unique<PopupItem>( - *CaptionResult, *FlagsResult, std::move(*SubMenuResult))); - continue; - } - - assert(IsMenuItem); - List.addDefinition( - std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); - } - - return std::move(List); -} - -RCParser::ParseType RCParser::parseStringTableResource() { - uint16_t MemoryFlags = - parseMemoryFlags(StringTableResource::getDefaultMemoryFlags()); - ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); - RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - - auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements), - MemoryFlags); - - // Read strings until we reach the end of the block. - while (!consumeOptionalType(Kind::BlockEnd)) { - // Each definition consists of string's ID (an integer) and a string. - // Some examples in documentation suggest that there might be a comma in - // between, however we strictly adhere to the single statement definition. - ASSIGN_OR_RETURN(IDResult, readInt()); - consumeOptionalType(Kind::Comma); - - std::vector<StringRef> Strings; - ASSIGN_OR_RETURN(StrResult, readString()); - Strings.push_back(*StrResult); - while (isNextTokenKind(Kind::String)) - Strings.push_back(read().value()); - - Table->addStrings(*IDResult, std::move(Strings)); - } - - return std::move(Table); -} - -Expected<std::unique_ptr<VersionInfoBlock>> -RCParser::parseVersionInfoBlockContents(StringRef BlockName) { - RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - - auto Contents = std::make_unique<VersionInfoBlock>(BlockName); - - while (!isNextTokenKind(Kind::BlockEnd)) { - ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); - Contents->addStmt(std::move(*Stmt)); - } - - consume(); // Consume BlockEnd. - - return std::move(Contents); -} - -Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { - // Expect either BLOCK or VALUE, then a name or a key (a string). - ASSIGN_OR_RETURN(TypeResult, readIdentifier()); - - if (TypeResult->equals_lower("BLOCK")) { - ASSIGN_OR_RETURN(NameResult, readString()); - return parseVersionInfoBlockContents(*NameResult); - } - - if (TypeResult->equals_lower("VALUE")) { - ASSIGN_OR_RETURN(KeyResult, readString()); - // Read a non-empty list of strings and/or ints, each - // possibly preceded by a comma. Unfortunately, the tool behavior depends - // on them existing or not, so we need to memorize where we found them. - std::vector<IntOrString> Values; - std::vector<bool> PrecedingCommas; - RETURN_IF_ERROR(consumeType(Kind::Comma)); - while (!isNextTokenKind(Kind::Identifier) && - !isNextTokenKind(Kind::BlockEnd)) { - // Try to eat a comma if it's not the first statement. - bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma); - ASSIGN_OR_RETURN(ValueResult, readIntOrString()); - Values.push_back(*ValueResult); - PrecedingCommas.push_back(HadComma); - } - return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), - std::move(PrecedingCommas)); - } - - return getExpectedError("BLOCK or VALUE", true); -} - -Expected<VersionInfoResource::VersionInfoFixed> -RCParser::parseVersionInfoFixed() { - using RetType = VersionInfoResource::VersionInfoFixed; - RetType Result; - - // Read until the beginning of the block. - while (!isNextTokenKind(Kind::BlockBegin)) { - ASSIGN_OR_RETURN(TypeResult, readIdentifier()); - auto FixedType = RetType::getFixedType(*TypeResult); - - if (!RetType::isTypeSupported(FixedType)) - return getExpectedError("fixed VERSIONINFO statement type", true); - if (Result.IsTypePresent[FixedType]) - return getExpectedError("yet unread fixed VERSIONINFO statement type", - true); - - // VERSION variations take multiple integers. - size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; +//===-- ResourceScriptParser.cpp --------------------------------*- 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 implements the parser defined in ResourceScriptParser.h. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptParser.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" + +// Take an expression returning llvm::Error and forward the error if it exists. +#define RETURN_IF_ERROR(Expr) \ + if (auto Err = (Expr)) \ + return std::move(Err); + +// Take an expression returning llvm::Expected<T> and assign it to Var or +// forward the error out of the function. +#define ASSIGN_OR_RETURN(Var, Expr) \ + auto Var = (Expr); \ + if (!Var) \ + return Var.takeError(); + +namespace llvm { +namespace rc { + +RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc, + const LocIter End) + : ErrorLoc(CurLoc), FileEnd(End) { + CurMessage = "Error parsing file: expected " + Expected.str() + ", got " + + (CurLoc == End ? "<EOF>" : CurLoc->value()).str(); +} + +char RCParser::ParserError::ID = 0; + +RCParser::RCParser(std::vector<RCToken> TokenList) + : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {} + +bool RCParser::isEof() const { return CurLoc == End; } + +RCParser::ParseType RCParser::parseSingleResource() { + // The first thing we read is usually a resource's name. However, in some + // cases (LANGUAGE and STRINGTABLE) the resources don't have their names + // and the first token to be read is the type. + ASSIGN_OR_RETURN(NameToken, readTypeOrName()); + + if (NameToken->equalsLower("LANGUAGE")) + return parseLanguageResource(); + else if (NameToken->equalsLower("STRINGTABLE")) + return parseStringTableResource(); + + // If it's not an unnamed resource, what we've just read is a name. Now, + // read resource type; + ASSIGN_OR_RETURN(TypeToken, readTypeOrName()); + + ParseType Result = std::unique_ptr<RCResource>(); + (void)!Result; + + if (TypeToken->equalsLower("ACCELERATORS")) + Result = parseAcceleratorsResource(); + else if (TypeToken->equalsLower("BITMAP")) + Result = parseBitmapResource(); + else if (TypeToken->equalsLower("CURSOR")) + Result = parseCursorResource(); + else if (TypeToken->equalsLower("DIALOG")) + Result = parseDialogResource(false); + else if (TypeToken->equalsLower("DIALOGEX")) + Result = parseDialogResource(true); + else if (TypeToken->equalsLower("HTML")) + Result = parseHTMLResource(); + else if (TypeToken->equalsLower("ICON")) + Result = parseIconResource(); + else if (TypeToken->equalsLower("MENU")) + Result = parseMenuResource(); + else if (TypeToken->equalsLower("RCDATA")) + Result = parseUserDefinedResource(RkRcData); + else if (TypeToken->equalsLower("VERSIONINFO")) + Result = parseVersionInfoResource(); + else + Result = parseUserDefinedResource(*TypeToken); + + if (Result) + (*Result)->setName(*NameToken); + + return Result; +} + +bool RCParser::isNextTokenKind(Kind TokenKind) const { + return !isEof() && look().kind() == TokenKind; +} + +const RCToken &RCParser::look() const { + assert(!isEof()); + return *CurLoc; +} + +const RCToken &RCParser::read() { + assert(!isEof()); + return *CurLoc++; +} + +void RCParser::consume() { + assert(!isEof()); + CurLoc++; +} + +// An integer description might consist of a single integer or +// an arithmetic expression evaluating to the integer. The expressions +// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning +// is the same as in C++ except for 'not' expression. +// The operators in the original RC implementation have the following +// precedence: +// 1) Unary operators (- ~ not), +// 2) Binary operators (+ - & |), with no precedence. +// +// 'not' expression is mostly useful for style values. It evaluates to 0, +// but value given to the operator is stored separately from integer value. +// It's mostly useful for control style expressions and causes bits from +// default control style to be excluded from generated style. For binary +// operators the mask from the right operand is applied to the left operand +// and masks from both operands are combined in operator result. +// +// The following grammar is used to parse the expressions Exp1: +// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 +// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). +// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, +// separated by binary operators.) +// +// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 +// is read by parseIntExpr2(). +// +// The original Microsoft tool handles multiple unary operators incorrectly. +// For example, in 16-bit little-endian integers: +// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; +// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. +// Our implementation differs from the original one and handles these +// operators correctly: +// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; +// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. + +Expected<RCInt> RCParser::readInt() { + ASSIGN_OR_RETURN(Value, parseIntExpr1()); + return (*Value).getValue(); +} + +Expected<IntWithNotMask> RCParser::parseIntExpr1() { + // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. + ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); + IntWithNotMask Result = *FirstResult; + + while (!isEof() && look().isBinaryOp()) { + auto OpToken = read(); + ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); + + switch (OpToken.kind()) { + case Kind::Plus: + Result += *NextResult; + break; + + case Kind::Minus: + Result -= *NextResult; + break; + + case Kind::Pipe: + Result |= *NextResult; + break; + + case Kind::Amp: + Result &= *NextResult; + break; + + default: + llvm_unreachable("Already processed all binary ops."); + } + } + + return Result; +} + +Expected<IntWithNotMask> RCParser::parseIntExpr2() { + // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). + static const char ErrorMsg[] = "'-', '~', integer or '('"; + + if (isEof()) + return getExpectedError(ErrorMsg); + + switch (look().kind()) { + case Kind::Minus: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return -(*Result); + } + + case Kind::Tilde: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return ~(*Result); + } + + case Kind::Int: + return RCInt(read()); + + case Kind::LeftParen: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr1()); + RETURN_IF_ERROR(consumeType(Kind::RightParen)); + return *Result; + } + + case Kind::Identifier: { + if (!read().value().equals_lower("not")) + return getExpectedError(ErrorMsg, true); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return IntWithNotMask(0, (*Result).getValue()); + } + + default: + return getExpectedError(ErrorMsg); + } +} + +Expected<StringRef> RCParser::readString() { + if (!isNextTokenKind(Kind::String)) + return getExpectedError("string"); + return read().value(); +} + +Expected<StringRef> RCParser::readFilename() { + if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier)) + return getExpectedError("string"); + return read().value(); +} + +Expected<StringRef> RCParser::readIdentifier() { + if (!isNextTokenKind(Kind::Identifier)) + return getExpectedError("identifier"); + return read().value(); +} + +Expected<IntOrString> RCParser::readIntOrString() { + if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String)) + return getExpectedError("int or string"); + return IntOrString(read()); +} + +Expected<IntOrString> RCParser::readTypeOrName() { + // We suggest that the correct resource name or type should be either an + // identifier or an integer. The original RC tool is much more liberal. + if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int)) + return getExpectedError("int or identifier"); + return IntOrString(read()); +} + +Error RCParser::consumeType(Kind TokenKind) { + if (isNextTokenKind(TokenKind)) { + consume(); + return Error::success(); + } + + switch (TokenKind) { +#define TOKEN(TokenName) \ + case Kind::TokenName: \ + return getExpectedError(#TokenName); +#define SHORT_TOKEN(TokenName, TokenCh) \ + case Kind::TokenName: \ + return getExpectedError(#TokenCh); +#include "ResourceScriptTokenList.def" + } + + llvm_unreachable("All case options exhausted."); +} + +bool RCParser::consumeOptionalType(Kind TokenKind) { + if (isNextTokenKind(TokenKind)) { + consume(); + return true; + } + + return false; +} + +Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount, + size_t MaxCount) { + assert(MinCount <= MaxCount); + + SmallVector<RCInt, 8> Result; + + auto FailureHandler = + [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> { + if (Result.size() < MinCount) + return std::move(Err); + consumeError(std::move(Err)); + return Result; + }; + + for (size_t i = 0; i < MaxCount; ++i) { + // Try to read a comma unless we read the first token. + // Sometimes RC tool requires them and sometimes not. We decide to + // always require them. + if (i >= 1) { + if (auto CommaError = consumeType(Kind::Comma)) + return FailureHandler(std::move(CommaError)); + } + + if (auto IntResult = readInt()) + Result.push_back(*IntResult); + else + return FailureHandler(IntResult.takeError()); + } + + return std::move(Result); +} + +Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, + ArrayRef<uint32_t> FlagValues) { + assert(!FlagDesc.empty()); + assert(FlagDesc.size() == FlagValues.size()); + + uint32_t Result = 0; + while (isNextTokenKind(Kind::Comma)) { + consume(); + ASSIGN_OR_RETURN(FlagResult, readIdentifier()); + bool FoundFlag = false; + + for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) { + if (!FlagResult->equals_lower(FlagDesc[FlagId])) + continue; + + Result |= FlagValues[FlagId]; + FoundFlag = true; + break; + } + + if (!FoundFlag) + return getExpectedError(join(FlagDesc, "/"), true); + } + + return Result; +} + +uint16_t RCParser::parseMemoryFlags(uint16_t Flags) { + while (!isEof()) { + const RCToken &Token = look(); + if (Token.kind() != Kind::Identifier) + return Flags; + const StringRef Ident = Token.value(); + if (Ident.equals_lower("PRELOAD")) + Flags |= MfPreload; + else if (Ident.equals_lower("LOADONCALL")) + Flags &= ~MfPreload; + else if (Ident.equals_lower("FIXED")) + Flags &= ~(MfMoveable | MfDiscardable); + else if (Ident.equals_lower("MOVEABLE")) + Flags |= MfMoveable; + else if (Ident.equals_lower("DISCARDABLE")) + Flags |= MfDiscardable | MfMoveable | MfPure; + else if (Ident.equals_lower("PURE")) + Flags |= MfPure; + else if (Ident.equals_lower("IMPURE")) + Flags &= ~(MfPure | MfDiscardable); + else if (Ident.equals_lower("SHARED")) + Flags |= MfPure; + else if (Ident.equals_lower("NONSHARED")) + Flags &= ~(MfPure | MfDiscardable); + else + return Flags; + consume(); + } + return Flags; +} + +Expected<OptionalStmtList> +RCParser::parseOptionalStatements(OptStmtType StmtsType) { + OptionalStmtList Result; + + // The last statement is always followed by the start of the block. + while (!isNextTokenKind(Kind::BlockBegin)) { + ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType)); + Result.addStmt(std::move(*SingleParse)); + } + + return std::move(Result); +} + +Expected<std::unique_ptr<OptionalStmt>> +RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { + ASSIGN_OR_RETURN(TypeToken, readIdentifier()); + if (TypeToken->equals_lower("CHARACTERISTICS")) + return parseCharacteristicsStmt(); + if (TypeToken->equals_lower("LANGUAGE")) + return parseLanguageStmt(); + if (TypeToken->equals_lower("VERSION")) + return parseVersionStmt(); + + if (StmtsType != OptStmtType::BasicStmt) { + if (TypeToken->equals_lower("CAPTION")) + return parseCaptionStmt(); + if (TypeToken->equals_lower("CLASS")) + return parseClassStmt(); + if (TypeToken->equals_lower("EXSTYLE")) + return parseExStyleStmt(); + if (TypeToken->equals_lower("FONT")) + return parseFontStmt(StmtsType); + if (TypeToken->equals_lower("STYLE")) + return parseStyleStmt(); + } + + return getExpectedError("optional statement type, BEGIN or '{'", + /* IsAlreadyRead = */ true); +} + +RCParser::ParseType RCParser::parseLanguageResource() { + // Read LANGUAGE as an optional statement. If it's read correctly, we can + // upcast it to RCResource. + return parseLanguageStmt(); +} + +RCParser::ParseType RCParser::parseAcceleratorsResource() { + uint16_t MemoryFlags = + parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Accels = std::make_unique<AcceleratorsResource>( + std::move(*OptStatements), MemoryFlags); + + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(EventResult, readIntOrString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(IDResult, readInt()); + ASSIGN_OR_RETURN( + FlagsResult, + parseFlags(AcceleratorsResource::Accelerator::OptionsStr, + AcceleratorsResource::Accelerator::OptionsFlags)); + Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult); + } + + return std::move(Accels); +} + +RCParser::ParseType RCParser::parseCursorResource() { + uint16_t MemoryFlags = + parseMemoryFlags(CursorResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return std::make_unique<CursorResource>(*Arg, MemoryFlags); +} + +RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { + uint16_t MemoryFlags = + parseMemoryFlags(DialogResource::getDefaultMemoryFlags()); + // Dialog resources have the following format of the arguments: + // DIALOG: x, y, width, height [opt stmts...] {controls...} + // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} + // These are very similar, so we parse them together. + ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); + + uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. + if (IsExtended && consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(HelpIDResult, readInt()); + HelpID = *HelpIDResult; + } + + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements( + IsExtended ? OptStmtType::DialogExStmt + : OptStmtType::DialogStmt)); + + assert(isNextTokenKind(Kind::BlockBegin) && + "parseOptionalStatements, when successful, halts on BlockBegin."); + consume(); + + auto Dialog = std::make_unique<DialogResource>( + (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], + HelpID, std::move(*OptStatements), IsExtended, MemoryFlags); + + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(ControlDefResult, parseControl()); + Dialog->addControl(std::move(*ControlDefResult)); + } + + return std::move(Dialog); +} + +RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { + uint16_t MemoryFlags = + parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags()); + if (isEof()) + return getExpectedError("filename, '{' or BEGIN"); + + // Check if this is a file resource. + switch (look().kind()) { + case Kind::String: + case Kind::Identifier: + return std::make_unique<UserDefinedResource>(Type, read().value(), + MemoryFlags); + default: + break; + } + + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + std::vector<IntOrString> Data; + + // Consume comma before each consecutive token except the first one. + bool ConsumeComma = false; + while (!consumeOptionalType(Kind::BlockEnd)) { + if (ConsumeComma) + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ConsumeComma = true; + + ASSIGN_OR_RETURN(Item, readIntOrString()); + Data.push_back(*Item); + } + + return std::make_unique<UserDefinedResource>(Type, std::move(Data), + MemoryFlags); +} + +RCParser::ParseType RCParser::parseVersionInfoResource() { + uint16_t MemoryFlags = + parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); + ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); + return std::make_unique<VersionInfoResource>( + std::move(**BlockResult), std::move(*FixedResult), MemoryFlags); +} + +Expected<Control> RCParser::parseControl() { + // Each control definition (except CONTROL) follows one of the schemes below + // depending on the control class: + // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] + // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] + // Note that control ids must be integers. + // Text might be either a string or an integer pointing to resource ID. + ASSIGN_OR_RETURN(ClassResult, readIdentifier()); + std::string ClassUpper = ClassResult->upper(); + auto CtlInfo = Control::SupportedCtls.find(ClassUpper); + if (CtlInfo == Control::SupportedCtls.end()) + return getExpectedError("control type, END or '}'", true); + + // Read caption if necessary. + IntOrString Caption{StringRef()}; + if (CtlInfo->getValue().HasTitle) { + ASSIGN_OR_RETURN(CaptionResult, readIntOrString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Caption = *CaptionResult; + } + + ASSIGN_OR_RETURN(ID, readInt()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + + IntOrString Class; + Optional<IntWithNotMask> Style; + if (ClassUpper == "CONTROL") { + // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID] + ASSIGN_OR_RETURN(ClassStr, readString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Class = *ClassStr; + ASSIGN_OR_RETURN(StyleVal, parseIntExpr1()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Style = *StyleVal; + } else { + Class = CtlInfo->getValue().CtlClass; + } + + // x, y, width, height + ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4)); + + if (ClassUpper != "CONTROL") { + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Val, parseIntExpr1()); + Style = *Val; + } + } + + Optional<uint32_t> ExStyle; + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Val, readInt()); + ExStyle = *Val; + } + Optional<uint32_t> HelpID; + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Val, readInt()); + HelpID = *Val; + } + + return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], + (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class); +} + +RCParser::ParseType RCParser::parseBitmapResource() { + uint16_t MemoryFlags = + parseMemoryFlags(BitmapResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return std::make_unique<BitmapResource>(*Arg, MemoryFlags); +} + +RCParser::ParseType RCParser::parseIconResource() { + uint16_t MemoryFlags = + parseMemoryFlags(IconResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return std::make_unique<IconResource>(*Arg, MemoryFlags); +} + +RCParser::ParseType RCParser::parseHTMLResource() { + uint16_t MemoryFlags = + parseMemoryFlags(HTMLResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return std::make_unique<HTMLResource>(*Arg, MemoryFlags); +} + +RCParser::ParseType RCParser::parseMenuResource() { + uint16_t MemoryFlags = + parseMemoryFlags(MenuResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + ASSIGN_OR_RETURN(Items, parseMenuItemsList()); + return std::make_unique<MenuResource>(std::move(*OptStatements), + std::move(*Items), MemoryFlags); +} + +Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + MenuDefinitionList List; + + // Read a set of items. Each item is of one of three kinds: + // MENUITEM SEPARATOR + // MENUITEM caption:String, result:Int [, menu flags]... + // POPUP caption:String [, menu flags]... { items... } + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier()); + + bool IsMenuItem = ItemTypeResult->equals_lower("MENUITEM"); + bool IsPopup = ItemTypeResult->equals_lower("POPUP"); + if (!IsMenuItem && !IsPopup) + return getExpectedError("MENUITEM, POPUP, END or '}'", true); + + if (IsMenuItem && isNextTokenKind(Kind::Identifier)) { + // Now, expecting SEPARATOR. + ASSIGN_OR_RETURN(SeparatorResult, readIdentifier()); + if (SeparatorResult->equals_lower("SEPARATOR")) { + List.addDefinition(std::make_unique<MenuSeparator>()); + continue; + } + + return getExpectedError("SEPARATOR or string", true); + } + + // Not a separator. Read the caption. + ASSIGN_OR_RETURN(CaptionResult, readString()); + + // If MENUITEM, expect also a comma and an integer. + uint32_t MenuResult = -1; + + if (IsMenuItem) { + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(IntResult, readInt()); + MenuResult = *IntResult; + } + + ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr, + MenuDefinition::OptionsFlags)); + + if (IsPopup) { + // If POPUP, read submenu items recursively. + ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList()); + List.addDefinition(std::make_unique<PopupItem>( + *CaptionResult, *FlagsResult, std::move(*SubMenuResult))); + continue; + } + + assert(IsMenuItem); + List.addDefinition( + std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult)); + } + + return std::move(List); +} + +RCParser::ParseType RCParser::parseStringTableResource() { + uint16_t MemoryFlags = + parseMemoryFlags(StringTableResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements), + MemoryFlags); + + // Read strings until we reach the end of the block. + while (!consumeOptionalType(Kind::BlockEnd)) { + // Each definition consists of string's ID (an integer) and a string. + // Some examples in documentation suggest that there might be a comma in + // between, however we strictly adhere to the single statement definition. + ASSIGN_OR_RETURN(IDResult, readInt()); + consumeOptionalType(Kind::Comma); + + std::vector<StringRef> Strings; + ASSIGN_OR_RETURN(StrResult, readString()); + Strings.push_back(*StrResult); + while (isNextTokenKind(Kind::String)) + Strings.push_back(read().value()); + + Table->addStrings(*IDResult, std::move(Strings)); + } + + return std::move(Table); +} + +Expected<std::unique_ptr<VersionInfoBlock>> +RCParser::parseVersionInfoBlockContents(StringRef BlockName) { + RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); + + auto Contents = std::make_unique<VersionInfoBlock>(BlockName); + + while (!isNextTokenKind(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt()); + Contents->addStmt(std::move(*Stmt)); + } + + consume(); // Consume BlockEnd. + + return std::move(Contents); +} + +Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() { + // Expect either BLOCK or VALUE, then a name or a key (a string). + ASSIGN_OR_RETURN(TypeResult, readIdentifier()); + + if (TypeResult->equals_lower("BLOCK")) { + ASSIGN_OR_RETURN(NameResult, readString()); + return parseVersionInfoBlockContents(*NameResult); + } + + if (TypeResult->equals_lower("VALUE")) { + ASSIGN_OR_RETURN(KeyResult, readString()); + // Read a non-empty list of strings and/or ints, each + // possibly preceded by a comma. Unfortunately, the tool behavior depends + // on them existing or not, so we need to memorize where we found them. + std::vector<IntOrString> Values; + std::vector<bool> PrecedingCommas; + RETURN_IF_ERROR(consumeType(Kind::Comma)); + while (!isNextTokenKind(Kind::Identifier) && + !isNextTokenKind(Kind::BlockEnd)) { + // Try to eat a comma if it's not the first statement. + bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma); + ASSIGN_OR_RETURN(ValueResult, readIntOrString()); + Values.push_back(*ValueResult); + PrecedingCommas.push_back(HadComma); + } + return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values), + std::move(PrecedingCommas)); + } + + return getExpectedError("BLOCK or VALUE", true); +} + +Expected<VersionInfoResource::VersionInfoFixed> +RCParser::parseVersionInfoFixed() { + using RetType = VersionInfoResource::VersionInfoFixed; + RetType Result; + + // Read until the beginning of the block. + while (!isNextTokenKind(Kind::BlockBegin)) { + ASSIGN_OR_RETURN(TypeResult, readIdentifier()); + auto FixedType = RetType::getFixedType(*TypeResult); + + if (!RetType::isTypeSupported(FixedType)) + return getExpectedError("fixed VERSIONINFO statement type", true); + if (Result.IsTypePresent[FixedType]) + return getExpectedError("yet unread fixed VERSIONINFO statement type", + true); + + // VERSION variations take multiple integers. + size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1; ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts)); - SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); + SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end()); while (ArgInts.size() < NumInts) ArgInts.push_back(0); - Result.setValue(FixedType, ArgInts); - } - - return Result; -} - -RCParser::ParseOptionType RCParser::parseLanguageStmt() { - ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); - return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); -} - -RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { - ASSIGN_OR_RETURN(Arg, readInt()); - return std::make_unique<CharacteristicsStmt>(*Arg); -} - -RCParser::ParseOptionType RCParser::parseVersionStmt() { - ASSIGN_OR_RETURN(Arg, readInt()); - return std::make_unique<VersionStmt>(*Arg); -} - -RCParser::ParseOptionType RCParser::parseCaptionStmt() { - ASSIGN_OR_RETURN(Arg, readString()); - return std::make_unique<CaptionStmt>(*Arg); -} - -RCParser::ParseOptionType RCParser::parseClassStmt() { - ASSIGN_OR_RETURN(Arg, readIntOrString()); - return std::make_unique<ClassStmt>(*Arg); -} - -RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { - assert(DialogType != OptStmtType::BasicStmt); - - ASSIGN_OR_RETURN(SizeResult, readInt()); - RETURN_IF_ERROR(consumeType(Kind::Comma)); - ASSIGN_OR_RETURN(NameResult, readString()); - - // Default values for the optional arguments. - uint32_t FontWeight = 0; - bool FontItalic = false; - uint32_t FontCharset = 1; - if (DialogType == OptStmtType::DialogExStmt) { - if (consumeOptionalType(Kind::Comma)) { - ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); - if (Args->size() >= 1) - FontWeight = (*Args)[0]; - if (Args->size() >= 2) - FontItalic = (*Args)[1] != 0; - if (Args->size() >= 3) - FontCharset = (*Args)[2]; - } - } - return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, - FontItalic, FontCharset); -} - -RCParser::ParseOptionType RCParser::parseStyleStmt() { - ASSIGN_OR_RETURN(Arg, readInt()); - return std::make_unique<StyleStmt>(*Arg); -} - -RCParser::ParseOptionType RCParser::parseExStyleStmt() { - ASSIGN_OR_RETURN(Arg, readInt()); - return std::make_unique<ExStyleStmt>(*Arg); -} - -Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { - return make_error<ParserError>( - Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); -} - -} // namespace rc -} // namespace llvm + Result.setValue(FixedType, ArgInts); + } + + return Result; +} + +RCParser::ParseOptionType RCParser::parseLanguageStmt() { + ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2)); + return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]); +} + +RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return std::make_unique<CharacteristicsStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseVersionStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return std::make_unique<VersionStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseCaptionStmt() { + ASSIGN_OR_RETURN(Arg, readString()); + return std::make_unique<CaptionStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseClassStmt() { + ASSIGN_OR_RETURN(Arg, readIntOrString()); + return std::make_unique<ClassStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { + assert(DialogType != OptStmtType::BasicStmt); + + ASSIGN_OR_RETURN(SizeResult, readInt()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(NameResult, readString()); + + // Default values for the optional arguments. + uint32_t FontWeight = 0; + bool FontItalic = false; + uint32_t FontCharset = 1; + if (DialogType == OptStmtType::DialogExStmt) { + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3)); + if (Args->size() >= 1) + FontWeight = (*Args)[0]; + if (Args->size() >= 2) + FontItalic = (*Args)[1] != 0; + if (Args->size() >= 3) + FontCharset = (*Args)[2]; + } + } + return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight, + FontItalic, FontCharset); +} + +RCParser::ParseOptionType RCParser::parseStyleStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return std::make_unique<StyleStmt>(*Arg); +} + +RCParser::ParseOptionType RCParser::parseExStyleStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return std::make_unique<ExStyleStmt>(*Arg); +} + +Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { + return make_error<ParserError>( + Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); +} + +} // namespace rc +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.h b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.h index fe202073b6..4887b0d5db 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.h +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptParser.h @@ -1,192 +1,192 @@ -//===-- ResourceScriptParser.h ----------------------------------*- 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 defines the RC scripts parser. It takes a sequence of RC tokens -// and then provides the method to parse the resources one by one. -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H -#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H - -#include "ResourceScriptStmt.h" -#include "ResourceScriptToken.h" - -#include "llvm/Support/Compiler.h" -#include "llvm/Support/raw_ostream.h" - -#include <system_error> -#include <vector> - -namespace llvm { -namespace opt { -class InputArgList; -} -namespace rc { - -class RCParser { -public: - using LocIter = std::vector<RCToken>::iterator; - using ParseType = Expected<std::unique_ptr<RCResource>>; - using ParseOptionType = Expected<std::unique_ptr<OptionalStmt>>; - - // Class describing a single failure of parser. - class ParserError : public ErrorInfo<ParserError> { - public: - ParserError(const Twine &Expected, const LocIter CurLoc, const LocIter End); - - void log(raw_ostream &OS) const override { OS << CurMessage; } - std::error_code convertToErrorCode() const override { - return std::make_error_code(std::errc::invalid_argument); - } - const std::string &getMessage() const { return CurMessage; } - - static char ID; // Keep llvm::Error happy. - - private: - std::string CurMessage; - LocIter ErrorLoc, FileEnd; - }; - - explicit RCParser(std::vector<RCToken> TokenList); - - // Reads and returns a single resource definition, or error message if any - // occurred. - ParseType parseSingleResource(); - - bool isEof() const; - -private: - using Kind = RCToken::Kind; - - // Checks if the current parser state points to the token of type TokenKind. - bool isNextTokenKind(Kind TokenKind) const; - - // These methods assume that the parser is not in EOF state. - - // Take a look at the current token. Do not fetch it. - const RCToken &look() const; - // Read the current token and advance the state by one token. - const RCToken &read(); - // Advance the state by one token, discarding the current token. - void consume(); - - // The following methods try to read a single token, check if it has the - // correct type and then parse it. - // Each integer can be written as an arithmetic expression producing an - // unsigned 32-bit integer. - Expected<RCInt> readInt(); // Parse an integer. - Expected<StringRef> readString(); // Parse a string. - Expected<StringRef> readIdentifier(); // Parse an identifier. - Expected<StringRef> readFilename(); // Parse a filename. - Expected<IntOrString> readIntOrString(); // Parse an integer or a string. - Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier. - - // Helper integer expression parsing methods. - Expected<IntWithNotMask> parseIntExpr1(); - Expected<IntWithNotMask> parseIntExpr2(); - - // Advance the state by one, discarding the current token. - // If the discarded token had an incorrect type, fail. - Error consumeType(Kind TokenKind); - - // Check the current token type. If it's TokenKind, discard it. - // Return true if the parser consumed this token successfully. - bool consumeOptionalType(Kind TokenKind); - - // Read at least MinCount, and at most MaxCount integers separated by - // commas. The parser stops reading after fetching MaxCount integers - // or after an error occurs. Whenever the parser reads a comma, it - // expects an integer to follow. - Expected<SmallVector<RCInt, 8>> readIntsWithCommas(size_t MinCount, - size_t MaxCount); - - // Read an unknown number of flags preceded by commas. Each correct flag - // has an entry in FlagDesc array of length NumFlags. In case i-th - // flag (0-based) has been read, the result is OR-ed with FlagValues[i]. - // As long as parser has a comma to read, it expects to be fed with - // a correct flag afterwards. - Expected<uint32_t> parseFlags(ArrayRef<StringRef> FlagDesc, - ArrayRef<uint32_t> FlagValues); - - // Reads a set of optional statements. These can change the behavior of - // a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided - // before the main block with the contents of the resource. - // Usually, resources use a basic set of optional statements: - // CHARACTERISTICS, LANGUAGE, VERSION - // However, DIALOG and DIALOGEX extend this list by the following items: - // CAPTION, CLASS, EXSTYLE, FONT, MENU, STYLE - // UseExtendedStatements flag (off by default) allows the parser to read - // the additional types of statements. - // - // Ref (to the list of all optional statements): - // msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx - enum class OptStmtType { BasicStmt, DialogStmt, DialogExStmt }; - - uint16_t parseMemoryFlags(uint16_t DefaultFlags); - - Expected<OptionalStmtList> - parseOptionalStatements(OptStmtType StmtsType = OptStmtType::BasicStmt); - - // Read a single optional statement. - Expected<std::unique_ptr<OptionalStmt>> - parseSingleOptionalStatement(OptStmtType StmtsType = OptStmtType::BasicStmt); - - // Top-level resource parsers. - ParseType parseLanguageResource(); - ParseType parseAcceleratorsResource(); - ParseType parseBitmapResource(); - ParseType parseCursorResource(); - ParseType parseDialogResource(bool IsExtended); - ParseType parseIconResource(); - ParseType parseHTMLResource(); - ParseType parseMenuResource(); - ParseType parseStringTableResource(); - ParseType parseUserDefinedResource(IntOrString Type); - ParseType parseVersionInfoResource(); - - // Helper DIALOG parser - a single control. - Expected<Control> parseControl(); - - // Helper MENU parser. - Expected<MenuDefinitionList> parseMenuItemsList(); - - // Helper VERSIONINFO parser - read the contents of a single BLOCK statement, - // from BEGIN to END. - Expected<std::unique_ptr<VersionInfoBlock>> - parseVersionInfoBlockContents(StringRef BlockName); - // Helper VERSIONINFO parser - read either VALUE or BLOCK statement. - Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt(); - // Helper VERSIONINFO parser - read fixed VERSIONINFO statements. - Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed(); - - // Optional statement parsers. - ParseOptionType parseLanguageStmt(); - ParseOptionType parseCharacteristicsStmt(); - ParseOptionType parseVersionStmt(); - ParseOptionType parseCaptionStmt(); - ParseOptionType parseClassStmt(); - ParseOptionType parseExStyleStmt(); - ParseOptionType parseFontStmt(OptStmtType DialogType); - ParseOptionType parseStyleStmt(); - - // Raises an error. If IsAlreadyRead = false (default), this complains about - // the token that couldn't be parsed. If the flag is on, this complains about - // the correctly read token that makes no sense (that is, the current parser - // state is beyond the erroneous token.) - Error getExpectedError(const Twine &Message, bool IsAlreadyRead = false); - - std::vector<RCToken> Tokens; - LocIter CurLoc; - const LocIter End; -}; - -} // namespace rc -} // namespace llvm - -#endif +//===-- ResourceScriptParser.h ----------------------------------*- 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 defines the RC scripts parser. It takes a sequence of RC tokens +// and then provides the method to parse the resources one by one. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H + +#include "ResourceScriptStmt.h" +#include "ResourceScriptToken.h" + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" + +#include <system_error> +#include <vector> + +namespace llvm { +namespace opt { +class InputArgList; +} +namespace rc { + +class RCParser { +public: + using LocIter = std::vector<RCToken>::iterator; + using ParseType = Expected<std::unique_ptr<RCResource>>; + using ParseOptionType = Expected<std::unique_ptr<OptionalStmt>>; + + // Class describing a single failure of parser. + class ParserError : public ErrorInfo<ParserError> { + public: + ParserError(const Twine &Expected, const LocIter CurLoc, const LocIter End); + + void log(raw_ostream &OS) const override { OS << CurMessage; } + std::error_code convertToErrorCode() const override { + return std::make_error_code(std::errc::invalid_argument); + } + const std::string &getMessage() const { return CurMessage; } + + static char ID; // Keep llvm::Error happy. + + private: + std::string CurMessage; + LocIter ErrorLoc, FileEnd; + }; + + explicit RCParser(std::vector<RCToken> TokenList); + + // Reads and returns a single resource definition, or error message if any + // occurred. + ParseType parseSingleResource(); + + bool isEof() const; + +private: + using Kind = RCToken::Kind; + + // Checks if the current parser state points to the token of type TokenKind. + bool isNextTokenKind(Kind TokenKind) const; + + // These methods assume that the parser is not in EOF state. + + // Take a look at the current token. Do not fetch it. + const RCToken &look() const; + // Read the current token and advance the state by one token. + const RCToken &read(); + // Advance the state by one token, discarding the current token. + void consume(); + + // The following methods try to read a single token, check if it has the + // correct type and then parse it. + // Each integer can be written as an arithmetic expression producing an + // unsigned 32-bit integer. + Expected<RCInt> readInt(); // Parse an integer. + Expected<StringRef> readString(); // Parse a string. + Expected<StringRef> readIdentifier(); // Parse an identifier. + Expected<StringRef> readFilename(); // Parse a filename. + Expected<IntOrString> readIntOrString(); // Parse an integer or a string. + Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier. + + // Helper integer expression parsing methods. + Expected<IntWithNotMask> parseIntExpr1(); + Expected<IntWithNotMask> parseIntExpr2(); + + // Advance the state by one, discarding the current token. + // If the discarded token had an incorrect type, fail. + Error consumeType(Kind TokenKind); + + // Check the current token type. If it's TokenKind, discard it. + // Return true if the parser consumed this token successfully. + bool consumeOptionalType(Kind TokenKind); + + // Read at least MinCount, and at most MaxCount integers separated by + // commas. The parser stops reading after fetching MaxCount integers + // or after an error occurs. Whenever the parser reads a comma, it + // expects an integer to follow. + Expected<SmallVector<RCInt, 8>> readIntsWithCommas(size_t MinCount, + size_t MaxCount); + + // Read an unknown number of flags preceded by commas. Each correct flag + // has an entry in FlagDesc array of length NumFlags. In case i-th + // flag (0-based) has been read, the result is OR-ed with FlagValues[i]. + // As long as parser has a comma to read, it expects to be fed with + // a correct flag afterwards. + Expected<uint32_t> parseFlags(ArrayRef<StringRef> FlagDesc, + ArrayRef<uint32_t> FlagValues); + + // Reads a set of optional statements. These can change the behavior of + // a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided + // before the main block with the contents of the resource. + // Usually, resources use a basic set of optional statements: + // CHARACTERISTICS, LANGUAGE, VERSION + // However, DIALOG and DIALOGEX extend this list by the following items: + // CAPTION, CLASS, EXSTYLE, FONT, MENU, STYLE + // UseExtendedStatements flag (off by default) allows the parser to read + // the additional types of statements. + // + // Ref (to the list of all optional statements): + // msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx + enum class OptStmtType { BasicStmt, DialogStmt, DialogExStmt }; + + uint16_t parseMemoryFlags(uint16_t DefaultFlags); + + Expected<OptionalStmtList> + parseOptionalStatements(OptStmtType StmtsType = OptStmtType::BasicStmt); + + // Read a single optional statement. + Expected<std::unique_ptr<OptionalStmt>> + parseSingleOptionalStatement(OptStmtType StmtsType = OptStmtType::BasicStmt); + + // Top-level resource parsers. + ParseType parseLanguageResource(); + ParseType parseAcceleratorsResource(); + ParseType parseBitmapResource(); + ParseType parseCursorResource(); + ParseType parseDialogResource(bool IsExtended); + ParseType parseIconResource(); + ParseType parseHTMLResource(); + ParseType parseMenuResource(); + ParseType parseStringTableResource(); + ParseType parseUserDefinedResource(IntOrString Type); + ParseType parseVersionInfoResource(); + + // Helper DIALOG parser - a single control. + Expected<Control> parseControl(); + + // Helper MENU parser. + Expected<MenuDefinitionList> parseMenuItemsList(); + + // Helper VERSIONINFO parser - read the contents of a single BLOCK statement, + // from BEGIN to END. + Expected<std::unique_ptr<VersionInfoBlock>> + parseVersionInfoBlockContents(StringRef BlockName); + // Helper VERSIONINFO parser - read either VALUE or BLOCK statement. + Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt(); + // Helper VERSIONINFO parser - read fixed VERSIONINFO statements. + Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed(); + + // Optional statement parsers. + ParseOptionType parseLanguageStmt(); + ParseOptionType parseCharacteristicsStmt(); + ParseOptionType parseVersionStmt(); + ParseOptionType parseCaptionStmt(); + ParseOptionType parseClassStmt(); + ParseOptionType parseExStyleStmt(); + ParseOptionType parseFontStmt(OptStmtType DialogType); + ParseOptionType parseStyleStmt(); + + // Raises an error. If IsAlreadyRead = false (default), this complains about + // the token that couldn't be parsed. If the flag is on, this complains about + // the correctly read token that makes no sense (that is, the current parser + // state is beyond the erroneous token.) + Error getExpectedError(const Twine &Message, bool IsAlreadyRead = false); + + std::vector<RCToken> Tokens; + LocIter CurLoc; + const LocIter End; +}; + +} // namespace rc +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.cpp b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.cpp index ef8c345418..eaf9711af6 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.cpp +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.cpp @@ -1,294 +1,294 @@ -// -// 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 implements methods defined in ResourceScriptStmt.h. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx -// -//===---------------------------------------------------------------------===// - -#include "ResourceScriptStmt.h" - -namespace llvm { -namespace rc { - -raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) { - if (Item.IsInt) - return OS << Item.Data.Int; - else - return OS << Item.Data.String; -} - -raw_ostream &OptionalStmtList::log(raw_ostream &OS) const { - for (const auto &Stmt : Statements) { - OS << " Option: "; - Stmt->log(OS); - } - return OS; -} - -raw_ostream &LanguageResource::log(raw_ostream &OS) const { - return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n"; -} - -StringRef AcceleratorsResource::Accelerator::OptionsStr - [AcceleratorsResource::Accelerator::NumFlags] = { - "ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"}; - -uint32_t AcceleratorsResource::Accelerator::OptionsFlags - [AcceleratorsResource::Accelerator::NumFlags] = {ASCII, VIRTKEY, NOINVERT, - ALT, SHIFT, CONTROL}; - -raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const { - OS << "Accelerators (" << ResName << "): \n"; - OptStatements->log(OS); - for (const auto &Acc : Accelerators) { - OS << " Accelerator: " << Acc.Event << " " << Acc.Id; - for (size_t i = 0; i < Accelerator::NumFlags; ++i) - if (Acc.Flags & Accelerator::OptionsFlags[i]) - OS << " " << Accelerator::OptionsStr[i]; - OS << "\n"; - } - return OS; -} - -raw_ostream &BitmapResource::log(raw_ostream &OS) const { - return OS << "Bitmap (" << ResName << "): " << BitmapLoc << "\n"; -} - -raw_ostream &CursorResource::log(raw_ostream &OS) const { - return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n"; -} - -raw_ostream &IconResource::log(raw_ostream &OS) const { - return OS << "Icon (" << ResName << "): " << IconLoc << "\n"; -} - -raw_ostream &HTMLResource::log(raw_ostream &OS) const { - return OS << "HTML (" << ResName << "): " << HTMLLoc << "\n"; -} - -StringRef MenuDefinition::OptionsStr[MenuDefinition::NumFlags] = { - "CHECKED", "GRAYED", "HELP", "INACTIVE", "MENUBARBREAK", "MENUBREAK"}; - -uint32_t MenuDefinition::OptionsFlags[MenuDefinition::NumFlags] = { - CHECKED, GRAYED, HELP, INACTIVE, MENUBARBREAK, MENUBREAK}; - -raw_ostream &MenuDefinition::logFlags(raw_ostream &OS, uint16_t Flags) { - for (size_t i = 0; i < NumFlags; ++i) - if (Flags & OptionsFlags[i]) - OS << " " << OptionsStr[i]; - return OS; -} - -raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const { - OS << " Menu list starts\n"; - for (auto &Item : Definitions) - Item->log(OS); - return OS << " Menu list ends\n"; -} - -raw_ostream &MenuItem::log(raw_ostream &OS) const { - OS << " MenuItem (" << Name << "), ID = " << Id; - logFlags(OS, Flags); - return OS << "\n"; -} - -raw_ostream &MenuSeparator::log(raw_ostream &OS) const { - return OS << " Menu separator\n"; -} - -raw_ostream &PopupItem::log(raw_ostream &OS) const { - OS << " Popup (" << Name << ")"; - logFlags(OS, Flags); - OS << ":\n"; - return SubItems.log(OS); -} - -raw_ostream &MenuResource::log(raw_ostream &OS) const { - OS << "Menu (" << ResName << "):\n"; - OptStatements->log(OS); - return Elements.log(OS); -} - -raw_ostream &StringTableResource::log(raw_ostream &OS) const { - OS << "StringTable:\n"; - OptStatements->log(OS); - for (const auto &String : Table) { - OS << " " << String.first << " =>"; - for (const auto &S : String.second) - OS << " " << S; - OS << "\n"; - } - return OS; -} - -const StringMap<Control::CtlInfo> Control::SupportedCtls = { - {"LTEXT", CtlInfo{0x50020000, ClsStatic, true}}, - {"CTEXT", CtlInfo{0x50020001, ClsStatic, true}}, - {"RTEXT", CtlInfo{0x50020002, ClsStatic, true}}, - {"ICON", CtlInfo{0x50000003, ClsStatic, true}}, - {"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}}, - {"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}}, - {"AUTO3STATE", CtlInfo{0x50010006, ClsButton, true}}, - {"AUTOCHECKBOX", CtlInfo{0x50010003, ClsButton, true}}, - {"AUTORADIOBUTTON", CtlInfo{0x50000009, ClsButton, true}}, - {"CHECKBOX", CtlInfo{0x50010002, ClsButton, true}}, - {"GROUPBOX", CtlInfo{0x50000007, ClsButton, true}}, - {"RADIOBUTTON", CtlInfo{0x50000004, ClsButton, true}}, - {"STATE3", CtlInfo{0x50010005, ClsButton, true}}, - {"PUSHBOX", CtlInfo{0x5001000A, ClsButton, true}}, - {"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}}, - {"COMBOBOX", CtlInfo{0x50000000, ClsComboBox, false}}, - {"LISTBOX", CtlInfo{0x50800001, ClsListBox, false}}, - {"SCROLLBAR", CtlInfo{0x50000000, ClsScrollBar, false}}, - {"CONTROL", CtlInfo{0x50000000, 0, true}}, -}; - -raw_ostream &Control::log(raw_ostream &OS) const { - OS << " Control (" << ID << "): " << Type << ", title: " << Title - << ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height - << "]"; - if (Style) - OS << ", style: " << (*Style).getValue(); - if (ExtStyle) - OS << ", ext. style: " << *ExtStyle; - if (HelpID) - OS << ", help ID: " << *HelpID; - return OS << "\n"; -} - -raw_ostream &DialogResource::log(raw_ostream &OS) const { - OS << "Dialog" << (IsExtended ? "Ex" : "") << " (" << ResName << "): loc: (" - << X << ", " << Y << "), size: [" << Width << ", " << Height - << "], help ID: " << HelpID << "\n"; - OptStatements->log(OS); - for (auto &Ctl : Controls) - Ctl.log(OS); - return OS; -} - -raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const { - OS << " Start of block (name: " << Name << ")\n"; - for (auto &Stmt : Stmts) - Stmt->log(OS); - return OS << " End of block\n"; -} - -raw_ostream &VersionInfoValue::log(raw_ostream &OS) const { - OS << " " << Key << " =>"; - size_t NumValues = Values.size(); - for (size_t Id = 0; Id < NumValues; ++Id) { - if (Id > 0 && HasPrecedingComma[Id]) - OS << ","; - OS << " " << Values[Id]; - } - return OS << "\n"; -} - -using VersionInfoFixed = VersionInfoResource::VersionInfoFixed; -using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType; - -const StringRef - VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = { - "", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK", - "FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"}; - -const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = { - {FixedFieldsNames[FtFileVersion], FtFileVersion}, - {FixedFieldsNames[FtProductVersion], FtProductVersion}, - {FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask}, - {FixedFieldsNames[FtFileFlags], FtFileFlags}, - {FixedFieldsNames[FtFileOS], FtFileOS}, - {FixedFieldsNames[FtFileType], FtFileType}, - {FixedFieldsNames[FtFileSubtype], FtFileSubtype}}; - -VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) { - auto UpperType = Type.upper(); - auto Iter = FixedFieldsInfoMap.find(UpperType); - if (Iter != FixedFieldsInfoMap.end()) - return Iter->getValue(); - return FtUnknown; -} - -bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) { - return FtUnknown < Type && Type < FtNumTypes; -} - -bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) { - switch (Type) { - case FtFileVersion: - case FtProductVersion: - return true; - - default: - return false; - } -} - -raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const { - for (int Type = FtUnknown; Type < FtNumTypes; ++Type) { - if (!isTypeSupported((VersionInfoFixedType)Type)) - continue; - OS << " Fixed: " << FixedFieldsNames[Type] << ":"; - for (uint32_t Val : FixedInfo[Type]) - OS << " " << Val; - OS << "\n"; - } - return OS; -} - -raw_ostream &VersionInfoResource::log(raw_ostream &OS) const { - OS << "VersionInfo (" << ResName << "):\n"; - FixedData.log(OS); - return MainBlock.log(OS); -} - -raw_ostream &UserDefinedResource::log(raw_ostream &OS) const { - OS << "User-defined (type: " << Type << ", name: " << ResName << "): "; - if (IsFileResource) - return OS << FileLoc << "\n"; - OS << "data = "; - for (auto &Item : Contents) - OS << Item << " "; - return OS << "\n"; -} - -raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const { - return OS << "Characteristics: " << Value << "\n"; -} - -raw_ostream &VersionStmt::log(raw_ostream &OS) const { - return OS << "Version: " << Value << "\n"; -} - -raw_ostream &CaptionStmt::log(raw_ostream &OS) const { - return OS << "Caption: " << Value << "\n"; -} - -raw_ostream &ClassStmt::log(raw_ostream &OS) const { - return OS << "Class: " << Value << "\n"; -} - -raw_ostream &FontStmt::log(raw_ostream &OS) const { - OS << "Font: size = " << Size << ", face = " << Name - << ", weight = " << Weight; - if (Italic) - OS << ", italic"; - return OS << ", charset = " << Charset << "\n"; -} - -raw_ostream &StyleStmt::log(raw_ostream &OS) const { - return OS << "Style: " << Value << "\n"; -} - -raw_ostream &ExStyleStmt::log(raw_ostream &OS) const { - return OS << "ExStyle: " << Value << "\n"; -} - -} // namespace rc -} // namespace llvm +// +// 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 implements methods defined in ResourceScriptStmt.h. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptStmt.h" + +namespace llvm { +namespace rc { + +raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) { + if (Item.IsInt) + return OS << Item.Data.Int; + else + return OS << Item.Data.String; +} + +raw_ostream &OptionalStmtList::log(raw_ostream &OS) const { + for (const auto &Stmt : Statements) { + OS << " Option: "; + Stmt->log(OS); + } + return OS; +} + +raw_ostream &LanguageResource::log(raw_ostream &OS) const { + return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n"; +} + +StringRef AcceleratorsResource::Accelerator::OptionsStr + [AcceleratorsResource::Accelerator::NumFlags] = { + "ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"}; + +uint32_t AcceleratorsResource::Accelerator::OptionsFlags + [AcceleratorsResource::Accelerator::NumFlags] = {ASCII, VIRTKEY, NOINVERT, + ALT, SHIFT, CONTROL}; + +raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const { + OS << "Accelerators (" << ResName << "): \n"; + OptStatements->log(OS); + for (const auto &Acc : Accelerators) { + OS << " Accelerator: " << Acc.Event << " " << Acc.Id; + for (size_t i = 0; i < Accelerator::NumFlags; ++i) + if (Acc.Flags & Accelerator::OptionsFlags[i]) + OS << " " << Accelerator::OptionsStr[i]; + OS << "\n"; + } + return OS; +} + +raw_ostream &BitmapResource::log(raw_ostream &OS) const { + return OS << "Bitmap (" << ResName << "): " << BitmapLoc << "\n"; +} + +raw_ostream &CursorResource::log(raw_ostream &OS) const { + return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n"; +} + +raw_ostream &IconResource::log(raw_ostream &OS) const { + return OS << "Icon (" << ResName << "): " << IconLoc << "\n"; +} + +raw_ostream &HTMLResource::log(raw_ostream &OS) const { + return OS << "HTML (" << ResName << "): " << HTMLLoc << "\n"; +} + +StringRef MenuDefinition::OptionsStr[MenuDefinition::NumFlags] = { + "CHECKED", "GRAYED", "HELP", "INACTIVE", "MENUBARBREAK", "MENUBREAK"}; + +uint32_t MenuDefinition::OptionsFlags[MenuDefinition::NumFlags] = { + CHECKED, GRAYED, HELP, INACTIVE, MENUBARBREAK, MENUBREAK}; + +raw_ostream &MenuDefinition::logFlags(raw_ostream &OS, uint16_t Flags) { + for (size_t i = 0; i < NumFlags; ++i) + if (Flags & OptionsFlags[i]) + OS << " " << OptionsStr[i]; + return OS; +} + +raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const { + OS << " Menu list starts\n"; + for (auto &Item : Definitions) + Item->log(OS); + return OS << " Menu list ends\n"; +} + +raw_ostream &MenuItem::log(raw_ostream &OS) const { + OS << " MenuItem (" << Name << "), ID = " << Id; + logFlags(OS, Flags); + return OS << "\n"; +} + +raw_ostream &MenuSeparator::log(raw_ostream &OS) const { + return OS << " Menu separator\n"; +} + +raw_ostream &PopupItem::log(raw_ostream &OS) const { + OS << " Popup (" << Name << ")"; + logFlags(OS, Flags); + OS << ":\n"; + return SubItems.log(OS); +} + +raw_ostream &MenuResource::log(raw_ostream &OS) const { + OS << "Menu (" << ResName << "):\n"; + OptStatements->log(OS); + return Elements.log(OS); +} + +raw_ostream &StringTableResource::log(raw_ostream &OS) const { + OS << "StringTable:\n"; + OptStatements->log(OS); + for (const auto &String : Table) { + OS << " " << String.first << " =>"; + for (const auto &S : String.second) + OS << " " << S; + OS << "\n"; + } + return OS; +} + +const StringMap<Control::CtlInfo> Control::SupportedCtls = { + {"LTEXT", CtlInfo{0x50020000, ClsStatic, true}}, + {"CTEXT", CtlInfo{0x50020001, ClsStatic, true}}, + {"RTEXT", CtlInfo{0x50020002, ClsStatic, true}}, + {"ICON", CtlInfo{0x50000003, ClsStatic, true}}, + {"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}}, + {"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}}, + {"AUTO3STATE", CtlInfo{0x50010006, ClsButton, true}}, + {"AUTOCHECKBOX", CtlInfo{0x50010003, ClsButton, true}}, + {"AUTORADIOBUTTON", CtlInfo{0x50000009, ClsButton, true}}, + {"CHECKBOX", CtlInfo{0x50010002, ClsButton, true}}, + {"GROUPBOX", CtlInfo{0x50000007, ClsButton, true}}, + {"RADIOBUTTON", CtlInfo{0x50000004, ClsButton, true}}, + {"STATE3", CtlInfo{0x50010005, ClsButton, true}}, + {"PUSHBOX", CtlInfo{0x5001000A, ClsButton, true}}, + {"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}}, + {"COMBOBOX", CtlInfo{0x50000000, ClsComboBox, false}}, + {"LISTBOX", CtlInfo{0x50800001, ClsListBox, false}}, + {"SCROLLBAR", CtlInfo{0x50000000, ClsScrollBar, false}}, + {"CONTROL", CtlInfo{0x50000000, 0, true}}, +}; + +raw_ostream &Control::log(raw_ostream &OS) const { + OS << " Control (" << ID << "): " << Type << ", title: " << Title + << ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height + << "]"; + if (Style) + OS << ", style: " << (*Style).getValue(); + if (ExtStyle) + OS << ", ext. style: " << *ExtStyle; + if (HelpID) + OS << ", help ID: " << *HelpID; + return OS << "\n"; +} + +raw_ostream &DialogResource::log(raw_ostream &OS) const { + OS << "Dialog" << (IsExtended ? "Ex" : "") << " (" << ResName << "): loc: (" + << X << ", " << Y << "), size: [" << Width << ", " << Height + << "], help ID: " << HelpID << "\n"; + OptStatements->log(OS); + for (auto &Ctl : Controls) + Ctl.log(OS); + return OS; +} + +raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const { + OS << " Start of block (name: " << Name << ")\n"; + for (auto &Stmt : Stmts) + Stmt->log(OS); + return OS << " End of block\n"; +} + +raw_ostream &VersionInfoValue::log(raw_ostream &OS) const { + OS << " " << Key << " =>"; + size_t NumValues = Values.size(); + for (size_t Id = 0; Id < NumValues; ++Id) { + if (Id > 0 && HasPrecedingComma[Id]) + OS << ","; + OS << " " << Values[Id]; + } + return OS << "\n"; +} + +using VersionInfoFixed = VersionInfoResource::VersionInfoFixed; +using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType; + +const StringRef + VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = { + "", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK", + "FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"}; + +const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = { + {FixedFieldsNames[FtFileVersion], FtFileVersion}, + {FixedFieldsNames[FtProductVersion], FtProductVersion}, + {FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask}, + {FixedFieldsNames[FtFileFlags], FtFileFlags}, + {FixedFieldsNames[FtFileOS], FtFileOS}, + {FixedFieldsNames[FtFileType], FtFileType}, + {FixedFieldsNames[FtFileSubtype], FtFileSubtype}}; + +VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) { + auto UpperType = Type.upper(); + auto Iter = FixedFieldsInfoMap.find(UpperType); + if (Iter != FixedFieldsInfoMap.end()) + return Iter->getValue(); + return FtUnknown; +} + +bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) { + return FtUnknown < Type && Type < FtNumTypes; +} + +bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) { + switch (Type) { + case FtFileVersion: + case FtProductVersion: + return true; + + default: + return false; + } +} + +raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const { + for (int Type = FtUnknown; Type < FtNumTypes; ++Type) { + if (!isTypeSupported((VersionInfoFixedType)Type)) + continue; + OS << " Fixed: " << FixedFieldsNames[Type] << ":"; + for (uint32_t Val : FixedInfo[Type]) + OS << " " << Val; + OS << "\n"; + } + return OS; +} + +raw_ostream &VersionInfoResource::log(raw_ostream &OS) const { + OS << "VersionInfo (" << ResName << "):\n"; + FixedData.log(OS); + return MainBlock.log(OS); +} + +raw_ostream &UserDefinedResource::log(raw_ostream &OS) const { + OS << "User-defined (type: " << Type << ", name: " << ResName << "): "; + if (IsFileResource) + return OS << FileLoc << "\n"; + OS << "data = "; + for (auto &Item : Contents) + OS << Item << " "; + return OS << "\n"; +} + +raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const { + return OS << "Characteristics: " << Value << "\n"; +} + +raw_ostream &VersionStmt::log(raw_ostream &OS) const { + return OS << "Version: " << Value << "\n"; +} + +raw_ostream &CaptionStmt::log(raw_ostream &OS) const { + return OS << "Caption: " << Value << "\n"; +} + +raw_ostream &ClassStmt::log(raw_ostream &OS) const { + return OS << "Class: " << Value << "\n"; +} + +raw_ostream &FontStmt::log(raw_ostream &OS) const { + OS << "Font: size = " << Size << ", face = " << Name + << ", weight = " << Weight; + if (Italic) + OS << ", italic"; + return OS << ", charset = " << Charset << "\n"; +} + +raw_ostream &StyleStmt::log(raw_ostream &OS) const { + return OS << "Style: " << Value << "\n"; +} + +raw_ostream &ExStyleStmt::log(raw_ostream &OS) const { + return OS << "ExStyle: " << Value << "\n"; +} + +} // namespace rc +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.h b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.h index 27fbea3ae8..0b56f39f83 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.h +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptStmt.h @@ -1,953 +1,953 @@ -//===-- ResourceScriptStmt.h ------------------------------------*- 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 lists all the resource and statement types occurring in RC scripts. -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H -#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H - -#include "ResourceScriptToken.h" -#include "ResourceVisitor.h" - -#include "llvm/ADT/StringSet.h" - -namespace llvm { -namespace rc { - -// Integer wrapper that also holds information whether the user declared -// the integer to be long (by appending L to the end of the integer) or not. -// It allows to be implicitly cast from and to uint32_t in order -// to be compatible with the parts of code that don't care about the integers -// being marked long. -class RCInt { - uint32_t Val; - bool Long; - -public: - RCInt(const RCToken &Token) - : Val(Token.intValue()), Long(Token.isLongInt()) {} - RCInt(uint32_t Value) : Val(Value), Long(false) {} - RCInt(uint32_t Value, bool IsLong) : Val(Value), Long(IsLong) {} - operator uint32_t() const { return Val; } - bool isLong() const { return Long; } - - RCInt &operator+=(const RCInt &Rhs) { - std::tie(Val, Long) = std::make_pair(Val + Rhs.Val, Long | Rhs.Long); - return *this; - } - - RCInt &operator-=(const RCInt &Rhs) { - std::tie(Val, Long) = std::make_pair(Val - Rhs.Val, Long | Rhs.Long); - return *this; - } - - RCInt &operator|=(const RCInt &Rhs) { - std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long); - return *this; - } - - RCInt &operator&=(const RCInt &Rhs) { - std::tie(Val, Long) = std::make_pair(Val & Rhs.Val, Long | Rhs.Long); - return *this; - } - - RCInt operator-() const { return {-Val, Long}; } - RCInt operator~() const { return {~Val, Long}; } - - friend raw_ostream &operator<<(raw_ostream &OS, const RCInt &Int) { - return OS << Int.Val << (Int.Long ? "L" : ""); - } -}; - -class IntWithNotMask { -private: - RCInt Value; - int32_t NotMask; - -public: - IntWithNotMask() : IntWithNotMask(RCInt(0)) {} - IntWithNotMask(RCInt Value, int32_t NotMask = 0) : Value(Value), NotMask(NotMask) {} - - RCInt getValue() const { - return Value; - } - - uint32_t getNotMask() const { - return NotMask; - } - - IntWithNotMask &operator+=(const IntWithNotMask &Rhs) { - Value &= ~Rhs.NotMask; - Value += Rhs.Value; - NotMask |= Rhs.NotMask; - return *this; - } - - IntWithNotMask &operator-=(const IntWithNotMask &Rhs) { - Value &= ~Rhs.NotMask; - Value -= Rhs.Value; - NotMask |= Rhs.NotMask; - return *this; - } - - IntWithNotMask &operator|=(const IntWithNotMask &Rhs) { - Value &= ~Rhs.NotMask; - Value |= Rhs.Value; - NotMask |= Rhs.NotMask; - return *this; - } - - IntWithNotMask &operator&=(const IntWithNotMask &Rhs) { - Value &= ~Rhs.NotMask; - Value &= Rhs.Value; - NotMask |= Rhs.NotMask; - return *this; - } - - IntWithNotMask operator-() const { return {-Value, NotMask}; } - IntWithNotMask operator~() const { return {~Value, 0}; } - - friend raw_ostream &operator<<(raw_ostream &OS, const IntWithNotMask &Int) { - return OS << Int.Value; - } -}; - -// A class holding a name - either an integer or a reference to the string. -class IntOrString { -private: - union Data { - RCInt Int; - StringRef String; - Data(RCInt Value) : Int(Value) {} - Data(const StringRef Value) : String(Value) {} - Data(const RCToken &Token) { - if (Token.kind() == RCToken::Kind::Int) - Int = RCInt(Token); - else - String = Token.value(); - } - } Data; - bool IsInt; - -public: - IntOrString() : IntOrString(RCInt(0)) {} - IntOrString(uint32_t Value) : Data(Value), IsInt(1) {} - IntOrString(RCInt Value) : Data(Value), IsInt(1) {} - IntOrString(StringRef Value) : Data(Value), IsInt(0) {} - IntOrString(const RCToken &Token) - : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {} - - bool equalsLower(const char *Str) { - return !IsInt && Data.String.equals_lower(Str); - } - - bool isInt() const { return IsInt; } - - RCInt getInt() const { - assert(IsInt); - return Data.Int; - } - - const StringRef &getString() const { - assert(!IsInt); - return Data.String; - } - - operator Twine() const { - return isInt() ? Twine(getInt()) : Twine(getString()); - } - - friend raw_ostream &operator<<(raw_ostream &, const IntOrString &); -}; - -enum ResourceKind { - // These resource kinds have corresponding .res resource type IDs - // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each - // kind is equal to this type ID. - RkNull = 0, - RkSingleCursor = 1, - RkBitmap = 2, - RkSingleIcon = 3, - RkMenu = 4, - RkDialog = 5, - RkStringTableBundle = 6, - RkAccelerators = 9, - RkRcData = 10, - RkCursorGroup = 12, - RkIconGroup = 14, - RkVersionInfo = 16, - RkHTML = 23, - - // These kinds don't have assigned type IDs (they might be the resources - // of invalid kind, expand to many resource structures in .res files, - // or have variable type ID). In order to avoid ID clashes with IDs above, - // we assign the kinds the values 256 and larger. - RkInvalid = 256, - RkBase, - RkCursor, - RkIcon, - RkStringTable, - RkUser, - RkSingleCursorOrIconRes, - RkCursorOrIconGroupRes, -}; - -// Non-zero memory flags. -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648027(v=vs.85).aspx -enum MemoryFlags { - MfMoveable = 0x10, - MfPure = 0x20, - MfPreload = 0x40, - MfDiscardable = 0x1000 -}; - -// Base resource. All the resources should derive from this base. -class RCResource { -public: - IntOrString ResName; - uint16_t MemoryFlags = getDefaultMemoryFlags(); - void setName(const IntOrString &Name) { ResName = Name; } - virtual raw_ostream &log(raw_ostream &OS) const { - return OS << "Base statement\n"; - }; - RCResource() {} - RCResource(uint16_t Flags) : MemoryFlags(Flags) {} - virtual ~RCResource() {} - - virtual Error visit(Visitor *) const { - llvm_unreachable("This is unable to call methods from Visitor base"); - } - - // Apply the statements attached to this resource. Generic resources - // don't have any. - virtual Error applyStmts(Visitor *) const { return Error::success(); } - - // By default, memory flags are DISCARDABLE | PURE | MOVEABLE. - static uint16_t getDefaultMemoryFlags() { - return MfDiscardable | MfPure | MfMoveable; - } - - virtual ResourceKind getKind() const { return RkBase; } - static bool classof(const RCResource *Res) { return true; } - - virtual IntOrString getResourceType() const { - llvm_unreachable("This cannot be called on objects without types."); - } - virtual Twine getResourceTypeName() const { - llvm_unreachable("This cannot be called on objects without types."); - }; -}; - -// An empty resource. It has no content, type 0, ID 0 and all of its -// characteristics are equal to 0. -class NullResource : public RCResource { -public: - NullResource() : RCResource(0) {} - raw_ostream &log(raw_ostream &OS) const override { - return OS << "Null resource\n"; - } - Error visit(Visitor *V) const override { return V->visitNullResource(this); } - IntOrString getResourceType() const override { return 0; } - Twine getResourceTypeName() const override { return "(NULL)"; } -}; - -// Optional statement base. All such statements should derive from this base. -class OptionalStmt : public RCResource {}; - -class OptionalStmtList : public OptionalStmt { - std::vector<std::unique_ptr<OptionalStmt>> Statements; - -public: - OptionalStmtList() {} - raw_ostream &log(raw_ostream &OS) const override; - - void addStmt(std::unique_ptr<OptionalStmt> Stmt) { - Statements.push_back(std::move(Stmt)); - } - - Error visit(Visitor *V) const override { - for (auto &StmtPtr : Statements) - if (auto Err = StmtPtr->visit(V)) - return Err; - return Error::success(); - } -}; - -class OptStatementsRCResource : public RCResource { -public: - std::unique_ptr<OptionalStmtList> OptStatements; - - OptStatementsRCResource(OptionalStmtList &&Stmts, - uint16_t Flags = RCResource::getDefaultMemoryFlags()) - : RCResource(Flags), - OptStatements(std::make_unique<OptionalStmtList>(std::move(Stmts))) {} - +//===-- ResourceScriptStmt.h ------------------------------------*- 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 lists all the resource and statement types occurring in RC scripts. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H + +#include "ResourceScriptToken.h" +#include "ResourceVisitor.h" + +#include "llvm/ADT/StringSet.h" + +namespace llvm { +namespace rc { + +// Integer wrapper that also holds information whether the user declared +// the integer to be long (by appending L to the end of the integer) or not. +// It allows to be implicitly cast from and to uint32_t in order +// to be compatible with the parts of code that don't care about the integers +// being marked long. +class RCInt { + uint32_t Val; + bool Long; + +public: + RCInt(const RCToken &Token) + : Val(Token.intValue()), Long(Token.isLongInt()) {} + RCInt(uint32_t Value) : Val(Value), Long(false) {} + RCInt(uint32_t Value, bool IsLong) : Val(Value), Long(IsLong) {} + operator uint32_t() const { return Val; } + bool isLong() const { return Long; } + + RCInt &operator+=(const RCInt &Rhs) { + std::tie(Val, Long) = std::make_pair(Val + Rhs.Val, Long | Rhs.Long); + return *this; + } + + RCInt &operator-=(const RCInt &Rhs) { + std::tie(Val, Long) = std::make_pair(Val - Rhs.Val, Long | Rhs.Long); + return *this; + } + + RCInt &operator|=(const RCInt &Rhs) { + std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long); + return *this; + } + + RCInt &operator&=(const RCInt &Rhs) { + std::tie(Val, Long) = std::make_pair(Val & Rhs.Val, Long | Rhs.Long); + return *this; + } + + RCInt operator-() const { return {-Val, Long}; } + RCInt operator~() const { return {~Val, Long}; } + + friend raw_ostream &operator<<(raw_ostream &OS, const RCInt &Int) { + return OS << Int.Val << (Int.Long ? "L" : ""); + } +}; + +class IntWithNotMask { +private: + RCInt Value; + int32_t NotMask; + +public: + IntWithNotMask() : IntWithNotMask(RCInt(0)) {} + IntWithNotMask(RCInt Value, int32_t NotMask = 0) : Value(Value), NotMask(NotMask) {} + + RCInt getValue() const { + return Value; + } + + uint32_t getNotMask() const { + return NotMask; + } + + IntWithNotMask &operator+=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value += Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask &operator-=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value -= Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask &operator|=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value |= Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask &operator&=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value &= Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask operator-() const { return {-Value, NotMask}; } + IntWithNotMask operator~() const { return {~Value, 0}; } + + friend raw_ostream &operator<<(raw_ostream &OS, const IntWithNotMask &Int) { + return OS << Int.Value; + } +}; + +// A class holding a name - either an integer or a reference to the string. +class IntOrString { +private: + union Data { + RCInt Int; + StringRef String; + Data(RCInt Value) : Int(Value) {} + Data(const StringRef Value) : String(Value) {} + Data(const RCToken &Token) { + if (Token.kind() == RCToken::Kind::Int) + Int = RCInt(Token); + else + String = Token.value(); + } + } Data; + bool IsInt; + +public: + IntOrString() : IntOrString(RCInt(0)) {} + IntOrString(uint32_t Value) : Data(Value), IsInt(1) {} + IntOrString(RCInt Value) : Data(Value), IsInt(1) {} + IntOrString(StringRef Value) : Data(Value), IsInt(0) {} + IntOrString(const RCToken &Token) + : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {} + + bool equalsLower(const char *Str) { + return !IsInt && Data.String.equals_lower(Str); + } + + bool isInt() const { return IsInt; } + + RCInt getInt() const { + assert(IsInt); + return Data.Int; + } + + const StringRef &getString() const { + assert(!IsInt); + return Data.String; + } + + operator Twine() const { + return isInt() ? Twine(getInt()) : Twine(getString()); + } + + friend raw_ostream &operator<<(raw_ostream &, const IntOrString &); +}; + +enum ResourceKind { + // These resource kinds have corresponding .res resource type IDs + // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each + // kind is equal to this type ID. + RkNull = 0, + RkSingleCursor = 1, + RkBitmap = 2, + RkSingleIcon = 3, + RkMenu = 4, + RkDialog = 5, + RkStringTableBundle = 6, + RkAccelerators = 9, + RkRcData = 10, + RkCursorGroup = 12, + RkIconGroup = 14, + RkVersionInfo = 16, + RkHTML = 23, + + // These kinds don't have assigned type IDs (they might be the resources + // of invalid kind, expand to many resource structures in .res files, + // or have variable type ID). In order to avoid ID clashes with IDs above, + // we assign the kinds the values 256 and larger. + RkInvalid = 256, + RkBase, + RkCursor, + RkIcon, + RkStringTable, + RkUser, + RkSingleCursorOrIconRes, + RkCursorOrIconGroupRes, +}; + +// Non-zero memory flags. +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648027(v=vs.85).aspx +enum MemoryFlags { + MfMoveable = 0x10, + MfPure = 0x20, + MfPreload = 0x40, + MfDiscardable = 0x1000 +}; + +// Base resource. All the resources should derive from this base. +class RCResource { +public: + IntOrString ResName; + uint16_t MemoryFlags = getDefaultMemoryFlags(); + void setName(const IntOrString &Name) { ResName = Name; } + virtual raw_ostream &log(raw_ostream &OS) const { + return OS << "Base statement\n"; + }; + RCResource() {} + RCResource(uint16_t Flags) : MemoryFlags(Flags) {} + virtual ~RCResource() {} + + virtual Error visit(Visitor *) const { + llvm_unreachable("This is unable to call methods from Visitor base"); + } + + // Apply the statements attached to this resource. Generic resources + // don't have any. + virtual Error applyStmts(Visitor *) const { return Error::success(); } + + // By default, memory flags are DISCARDABLE | PURE | MOVEABLE. + static uint16_t getDefaultMemoryFlags() { + return MfDiscardable | MfPure | MfMoveable; + } + + virtual ResourceKind getKind() const { return RkBase; } + static bool classof(const RCResource *Res) { return true; } + + virtual IntOrString getResourceType() const { + llvm_unreachable("This cannot be called on objects without types."); + } + virtual Twine getResourceTypeName() const { + llvm_unreachable("This cannot be called on objects without types."); + }; +}; + +// An empty resource. It has no content, type 0, ID 0 and all of its +// characteristics are equal to 0. +class NullResource : public RCResource { +public: + NullResource() : RCResource(0) {} + raw_ostream &log(raw_ostream &OS) const override { + return OS << "Null resource\n"; + } + Error visit(Visitor *V) const override { return V->visitNullResource(this); } + IntOrString getResourceType() const override { return 0; } + Twine getResourceTypeName() const override { return "(NULL)"; } +}; + +// Optional statement base. All such statements should derive from this base. +class OptionalStmt : public RCResource {}; + +class OptionalStmtList : public OptionalStmt { + std::vector<std::unique_ptr<OptionalStmt>> Statements; + +public: + OptionalStmtList() {} + raw_ostream &log(raw_ostream &OS) const override; + + void addStmt(std::unique_ptr<OptionalStmt> Stmt) { + Statements.push_back(std::move(Stmt)); + } + + Error visit(Visitor *V) const override { + for (auto &StmtPtr : Statements) + if (auto Err = StmtPtr->visit(V)) + return Err; + return Error::success(); + } +}; + +class OptStatementsRCResource : public RCResource { +public: + std::unique_ptr<OptionalStmtList> OptStatements; + + OptStatementsRCResource(OptionalStmtList &&Stmts, + uint16_t Flags = RCResource::getDefaultMemoryFlags()) + : RCResource(Flags), + OptStatements(std::make_unique<OptionalStmtList>(std::move(Stmts))) {} + Error applyStmts(Visitor *V) const override { return OptStatements->visit(V); } -}; - -// LANGUAGE statement. It can occur both as a top-level statement (in such -// a situation, it changes the default language until the end of the file) -// and as an optional resource statement (then it changes the language -// of a single resource). -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx -class LanguageResource : public OptionalStmt { -public: - uint32_t Lang, SubLang; - - LanguageResource(uint32_t LangId, uint32_t SubLangId) - : Lang(LangId), SubLang(SubLangId) {} - raw_ostream &log(raw_ostream &) const override; - - // This is not a regular top-level statement; when it occurs, it just - // modifies the language context. - Error visit(Visitor *V) const override { return V->visitLanguageStmt(this); } - Twine getResourceTypeName() const override { return "LANGUAGE"; } -}; - -// ACCELERATORS resource. Defines a named table of accelerators for the app. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx -class AcceleratorsResource : public OptStatementsRCResource { -public: - class Accelerator { - public: - IntOrString Event; - uint32_t Id; - uint16_t Flags; - - enum Options { - // This is actually 0x0000 (accelerator is assumed to be ASCII if it's - // not VIRTKEY). However, rc.exe behavior is different in situations - // "only ASCII defined" and "neither ASCII nor VIRTKEY defined". - // Therefore, we include ASCII as another flag. This must be zeroed - // when serialized. - ASCII = 0x8000, - VIRTKEY = 0x0001, - NOINVERT = 0x0002, - ALT = 0x0010, - SHIFT = 0x0004, - CONTROL = 0x0008 - }; - - static constexpr size_t NumFlags = 6; - static StringRef OptionsStr[NumFlags]; - static uint32_t OptionsFlags[NumFlags]; - }; - - AcceleratorsResource(OptionalStmtList &&List, uint16_t Flags) - : OptStatementsRCResource(std::move(List), Flags) {} - - std::vector<Accelerator> Accelerators; - - void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) { - Accelerators.push_back(Accelerator{Event, Id, Flags}); - } - raw_ostream &log(raw_ostream &) const override; - - IntOrString getResourceType() const override { return RkAccelerators; } - static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } - Twine getResourceTypeName() const override { return "ACCELERATORS"; } - - Error visit(Visitor *V) const override { - return V->visitAcceleratorsResource(this); - } - ResourceKind getKind() const override { return RkAccelerators; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkAccelerators; - } -}; - -// BITMAP resource. Represents a bitmap (".bmp") file. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx -class BitmapResource : public RCResource { -public: - StringRef BitmapLoc; - - BitmapResource(StringRef Location, uint16_t Flags) - : RCResource(Flags), BitmapLoc(Location) {} - raw_ostream &log(raw_ostream &) const override; - - IntOrString getResourceType() const override { return RkBitmap; } - static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } - - Twine getResourceTypeName() const override { return "BITMAP"; } - Error visit(Visitor *V) const override { - return V->visitBitmapResource(this); - } - ResourceKind getKind() const override { return RkBitmap; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkBitmap; - } -}; - -// CURSOR resource. Represents a single cursor (".cur") file. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx -class CursorResource : public RCResource { -public: - StringRef CursorLoc; - - CursorResource(StringRef Location, uint16_t Flags) - : RCResource(Flags), CursorLoc(Location) {} - raw_ostream &log(raw_ostream &) const override; - - Twine getResourceTypeName() const override { return "CURSOR"; } - static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; } - Error visit(Visitor *V) const override { - return V->visitCursorResource(this); - } - ResourceKind getKind() const override { return RkCursor; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkCursor; - } -}; - -// ICON resource. Represents a single ".ico" file containing a group of icons. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx -class IconResource : public RCResource { -public: - StringRef IconLoc; - - IconResource(StringRef Location, uint16_t Flags) - : RCResource(Flags), IconLoc(Location) {} - raw_ostream &log(raw_ostream &) const override; - - Twine getResourceTypeName() const override { return "ICON"; } - static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; } - Error visit(Visitor *V) const override { return V->visitIconResource(this); } - ResourceKind getKind() const override { return RkIcon; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkIcon; - } -}; - -// HTML resource. Represents a local webpage that is to be embedded into the -// resulting resource file. It embeds a file only - no additional resources -// (images etc.) are included with this resource. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa966018(v=vs.85).aspx -class HTMLResource : public RCResource { -public: - StringRef HTMLLoc; - - HTMLResource(StringRef Location, uint16_t Flags) - : RCResource(Flags), HTMLLoc(Location) {} - raw_ostream &log(raw_ostream &) const override; - - Error visit(Visitor *V) const override { return V->visitHTMLResource(this); } - - // Curiously, file resources don't have DISCARDABLE flag set. - static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } - IntOrString getResourceType() const override { return RkHTML; } - Twine getResourceTypeName() const override { return "HTML"; } - ResourceKind getKind() const override { return RkHTML; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkHTML; - } -}; - -// -- MENU resource and its helper classes -- -// This resource describes the contents of an application menu -// (usually located in the upper part of the dialog.) -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx - -// Description of a single submenu item. -class MenuDefinition { -public: - enum Options { - CHECKED = 0x0008, - GRAYED = 0x0001, - HELP = 0x4000, - INACTIVE = 0x0002, - MENUBARBREAK = 0x0020, - MENUBREAK = 0x0040 - }; - - enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup }; - - static constexpr size_t NumFlags = 6; - static StringRef OptionsStr[NumFlags]; - static uint32_t OptionsFlags[NumFlags]; - static raw_ostream &logFlags(raw_ostream &, uint16_t Flags); - virtual raw_ostream &log(raw_ostream &OS) const { - return OS << "Base menu definition\n"; - } - virtual ~MenuDefinition() {} - - virtual uint16_t getResFlags() const { return 0; } - virtual MenuDefKind getKind() const { return MkBase; } -}; - -// Recursive description of a whole submenu. -class MenuDefinitionList : public MenuDefinition { -public: - std::vector<std::unique_ptr<MenuDefinition>> Definitions; - - void addDefinition(std::unique_ptr<MenuDefinition> Def) { - Definitions.push_back(std::move(Def)); - } - raw_ostream &log(raw_ostream &) const override; -}; - -// Separator in MENU definition (MENUITEM SEPARATOR). -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx -class MenuSeparator : public MenuDefinition { -public: - raw_ostream &log(raw_ostream &) const override; - - MenuDefKind getKind() const override { return MkSeparator; } - static bool classof(const MenuDefinition *D) { - return D->getKind() == MkSeparator; - } -}; - -// MENUITEM statement definition. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx -class MenuItem : public MenuDefinition { -public: - StringRef Name; - uint32_t Id; - uint16_t Flags; - - MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags) - : Name(Caption), Id(ItemId), Flags(ItemFlags) {} - raw_ostream &log(raw_ostream &) const override; - - uint16_t getResFlags() const override { return Flags; } - MenuDefKind getKind() const override { return MkMenuItem; } - static bool classof(const MenuDefinition *D) { - return D->getKind() == MkMenuItem; - } -}; - -// POPUP statement definition. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx -class PopupItem : public MenuDefinition { -public: - StringRef Name; - uint16_t Flags; - MenuDefinitionList SubItems; - - PopupItem(StringRef Caption, uint16_t ItemFlags, - MenuDefinitionList &&SubItemsList) - : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {} - raw_ostream &log(raw_ostream &) const override; - - // This has an additional (0x10) flag. It doesn't match with documented - // 0x01 flag, though. - uint16_t getResFlags() const override { return Flags | 0x10; } - MenuDefKind getKind() const override { return MkPopup; } - static bool classof(const MenuDefinition *D) { - return D->getKind() == MkPopup; - } -}; - -// Menu resource definition. -class MenuResource : public OptStatementsRCResource { -public: - MenuDefinitionList Elements; - - MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items, - uint16_t Flags) - : OptStatementsRCResource(std::move(OptStmts), Flags), - Elements(std::move(Items)) {} - raw_ostream &log(raw_ostream &) const override; - - IntOrString getResourceType() const override { return RkMenu; } - Twine getResourceTypeName() const override { return "MENU"; } - Error visit(Visitor *V) const override { return V->visitMenuResource(this); } - ResourceKind getKind() const override { return RkMenu; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkMenu; - } -}; - -// STRINGTABLE resource. Contains a list of strings, each having its unique ID. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx -class StringTableResource : public OptStatementsRCResource { -public: - std::vector<std::pair<uint32_t, std::vector<StringRef>>> Table; - - StringTableResource(OptionalStmtList &&List, uint16_t Flags) - : OptStatementsRCResource(std::move(List), Flags) {} - void addStrings(uint32_t ID, std::vector<StringRef> &&Strings) { - Table.emplace_back(ID, Strings); - } - raw_ostream &log(raw_ostream &) const override; - Twine getResourceTypeName() const override { return "STRINGTABLE"; } - Error visit(Visitor *V) const override { - return V->visitStringTableResource(this); - } -}; - -// -- DIALOG(EX) resource and its helper classes -- -// -// This resource describes dialog boxes and controls residing inside them. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381003(v=vs.85).aspx -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx - -// Single control definition. -class Control { -public: - StringRef Type; - IntOrString Title; - uint32_t ID, X, Y, Width, Height; - Optional<IntWithNotMask> Style; - Optional<uint32_t> ExtStyle, HelpID; - IntOrString Class; - - // Control classes as described in DLGITEMTEMPLATEEX documentation. - // - // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx - enum CtlClasses { - ClsButton = 0x80, - ClsEdit = 0x81, - ClsStatic = 0x82, - ClsListBox = 0x83, - ClsScrollBar = 0x84, - ClsComboBox = 0x85 - }; - - // Simple information about a single control type. - struct CtlInfo { - uint32_t Style; - uint16_t CtlClass; - bool HasTitle; - }; - - Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID, - uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight, - Optional<IntWithNotMask> ItemStyle, Optional<uint32_t> ExtItemStyle, - Optional<uint32_t> CtlHelpID, IntOrString CtlClass) - : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY), - Width(ItemWidth), Height(ItemHeight), Style(ItemStyle), - ExtStyle(ExtItemStyle), HelpID(CtlHelpID), Class(CtlClass) {} - - static const StringMap<CtlInfo> SupportedCtls; - - raw_ostream &log(raw_ostream &) const; -}; - -// Single dialog definition. We don't create distinct classes for DIALOG and -// DIALOGEX because of their being too similar to each other. We only have a -// flag determining the type of the dialog box. -class DialogResource : public OptStatementsRCResource { -public: - uint32_t X, Y, Width, Height, HelpID; - std::vector<Control> Controls; - bool IsExtended; - - DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth, - uint32_t DlgHeight, uint32_t DlgHelpID, - OptionalStmtList &&OptStmts, bool IsDialogEx, uint16_t Flags) - : OptStatementsRCResource(std::move(OptStmts), Flags), X(PosX), Y(PosY), - Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID), - IsExtended(IsDialogEx) {} - - void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); } - - raw_ostream &log(raw_ostream &) const override; - - // It was a weird design decision to assign the same resource type number - // both for DIALOG and DIALOGEX (and the same structure version number). - // It makes it possible for DIALOG to be mistaken for DIALOGEX. - IntOrString getResourceType() const override { return RkDialog; } - Twine getResourceTypeName() const override { - return "DIALOG" + Twine(IsExtended ? "EX" : ""); - } - Error visit(Visitor *V) const override { - return V->visitDialogResource(this); - } - ResourceKind getKind() const override { return RkDialog; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkDialog; - } -}; - -// User-defined resource. It is either: -// * a link to the file, e.g. NAME TYPE "filename", -// * or contains a list of integers and strings, e.g. NAME TYPE {1, "a", 2}. -class UserDefinedResource : public RCResource { -public: - IntOrString Type; - StringRef FileLoc; - std::vector<IntOrString> Contents; - bool IsFileResource; - - UserDefinedResource(IntOrString ResourceType, StringRef FileLocation, - uint16_t Flags) - : RCResource(Flags), Type(ResourceType), FileLoc(FileLocation), - IsFileResource(true) {} - UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data, - uint16_t Flags) - : RCResource(Flags), Type(ResourceType), Contents(std::move(Data)), - IsFileResource(false) {} - - raw_ostream &log(raw_ostream &) const override; - IntOrString getResourceType() const override { return Type; } - Twine getResourceTypeName() const override { return Type; } - static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } - - Error visit(Visitor *V) const override { - return V->visitUserDefinedResource(this); - } - ResourceKind getKind() const override { return RkUser; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkUser; - } -}; - -// -- VERSIONINFO resource and its helper classes -- -// -// This resource lists the version information on the executable/library. -// The declaration consists of the following items: -// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS) -// * BEGIN -// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines -// another block of version information, whereas VALUE defines a -// key -> value correspondence. There might be more than one value -// corresponding to the single key. -// * END -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx - -// A single VERSIONINFO statement; -class VersionInfoStmt { -public: - enum StmtKind { StBase = 0, StBlock = 1, StValue = 2 }; - - virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; } - virtual ~VersionInfoStmt() {} - - virtual StmtKind getKind() const { return StBase; } - static bool classof(const VersionInfoStmt *S) { - return S->getKind() == StBase; - } -}; - -// BLOCK definition; also the main VERSIONINFO declaration is considered a -// BLOCK, although it has no name. -// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't -// care about them at the parsing phase. -class VersionInfoBlock : public VersionInfoStmt { -public: - std::vector<std::unique_ptr<VersionInfoStmt>> Stmts; - StringRef Name; - - VersionInfoBlock(StringRef BlockName) : Name(BlockName) {} - void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) { - Stmts.push_back(std::move(Stmt)); - } - raw_ostream &log(raw_ostream &) const override; - - StmtKind getKind() const override { return StBlock; } - static bool classof(const VersionInfoStmt *S) { - return S->getKind() == StBlock; - } -}; - -class VersionInfoValue : public VersionInfoStmt { -public: - StringRef Key; - std::vector<IntOrString> Values; - std::vector<bool> HasPrecedingComma; - - VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals, - std::vector<bool> &&CommasBeforeVals) - : Key(InfoKey), Values(std::move(Vals)), - HasPrecedingComma(std::move(CommasBeforeVals)) {} - raw_ostream &log(raw_ostream &) const override; - - StmtKind getKind() const override { return StValue; } - static bool classof(const VersionInfoStmt *S) { - return S->getKind() == StValue; - } -}; - -class VersionInfoResource : public RCResource { -public: - // A class listing fixed VERSIONINFO statements (occuring before main BEGIN). - // If any of these is not specified, it is assumed by the original tool to - // be equal to 0. - class VersionInfoFixed { - public: - enum VersionInfoFixedType { - FtUnknown, - FtFileVersion, - FtProductVersion, - FtFileFlagsMask, - FtFileFlags, - FtFileOS, - FtFileType, - FtFileSubtype, - FtNumTypes - }; - - private: - static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap; - static const StringRef FixedFieldsNames[FtNumTypes]; - - public: - SmallVector<uint32_t, 4> FixedInfo[FtNumTypes]; - SmallVector<bool, FtNumTypes> IsTypePresent; - - static VersionInfoFixedType getFixedType(StringRef Type); - static bool isTypeSupported(VersionInfoFixedType Type); - static bool isVersionType(VersionInfoFixedType Type); - - VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {} - - void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) { - FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end()); - IsTypePresent[Type] = true; - } - - raw_ostream &log(raw_ostream &) const; - }; - - VersionInfoBlock MainBlock; - VersionInfoFixed FixedData; - - VersionInfoResource(VersionInfoBlock &&TopLevelBlock, - VersionInfoFixed &&FixedInfo, uint16_t Flags) - : RCResource(Flags), MainBlock(std::move(TopLevelBlock)), - FixedData(std::move(FixedInfo)) {} - - raw_ostream &log(raw_ostream &) const override; - IntOrString getResourceType() const override { return RkVersionInfo; } - static uint16_t getDefaultMemoryFlags() { return MfMoveable | MfPure; } - Twine getResourceTypeName() const override { return "VERSIONINFO"; } - Error visit(Visitor *V) const override { - return V->visitVersionInfoResource(this); - } - ResourceKind getKind() const override { return RkVersionInfo; } - static bool classof(const RCResource *Res) { - return Res->getKind() == RkVersionInfo; - } -}; - -// CHARACTERISTICS optional statement. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx -class CharacteristicsStmt : public OptionalStmt { -public: - uint32_t Value; - - CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {} - raw_ostream &log(raw_ostream &) const override; - - Twine getResourceTypeName() const override { return "CHARACTERISTICS"; } - Error visit(Visitor *V) const override { - return V->visitCharacteristicsStmt(this); - } -}; - -// VERSION optional statement. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx -class VersionStmt : public OptionalStmt { -public: - uint32_t Value; - - VersionStmt(uint32_t Version) : Value(Version) {} - raw_ostream &log(raw_ostream &) const override; - - Twine getResourceTypeName() const override { return "VERSION"; } - Error visit(Visitor *V) const override { return V->visitVersionStmt(this); } -}; - -// CAPTION optional statement. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380778(v=vs.85).aspx -class CaptionStmt : public OptionalStmt { -public: - StringRef Value; - - CaptionStmt(StringRef Caption) : Value(Caption) {} - raw_ostream &log(raw_ostream &) const override; - Twine getResourceTypeName() const override { return "CAPTION"; } - Error visit(Visitor *V) const override { return V->visitCaptionStmt(this); } -}; - -// FONT optional statement. -// Note that the documentation is inaccurate: it expects five arguments to be -// given, however the example provides only two. In fact, the original tool -// expects two arguments - point size and name of the typeface. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx -class FontStmt : public OptionalStmt { -public: - uint32_t Size, Weight, Charset; - StringRef Name; - bool Italic; - - FontStmt(uint32_t FontSize, StringRef FontName, uint32_t FontWeight, - bool FontItalic, uint32_t FontCharset) - : Size(FontSize), Weight(FontWeight), Charset(FontCharset), - Name(FontName), Italic(FontItalic) {} - raw_ostream &log(raw_ostream &) const override; - Twine getResourceTypeName() const override { return "FONT"; } - Error visit(Visitor *V) const override { return V->visitFontStmt(this); } -}; - -// STYLE optional statement. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381051(v=vs.85).aspx -class StyleStmt : public OptionalStmt { -public: - uint32_t Value; - - StyleStmt(uint32_t Style) : Value(Style) {} - raw_ostream &log(raw_ostream &) const override; - Twine getResourceTypeName() const override { return "STYLE"; } - Error visit(Visitor *V) const override { return V->visitStyleStmt(this); } -}; - -// EXSTYLE optional statement. -// -// Ref: docs.microsoft.com/en-us/windows/desktop/menurc/exstyle-statement -class ExStyleStmt : public OptionalStmt { -public: - uint32_t Value; - - ExStyleStmt(uint32_t ExStyle) : Value(ExStyle) {} - raw_ostream &log(raw_ostream &) const override; - Twine getResourceTypeName() const override { return "EXSTYLE"; } - Error visit(Visitor *V) const override { return V->visitExStyleStmt(this); } -}; - -// CLASS optional statement. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx -class ClassStmt : public OptionalStmt { -public: - IntOrString Value; - - ClassStmt(IntOrString Class) : Value(Class) {} - raw_ostream &log(raw_ostream &) const override; - Twine getResourceTypeName() const override { return "CLASS"; } - Error visit(Visitor *V) const override { return V->visitClassStmt(this); } -}; - -} // namespace rc -} // namespace llvm - -#endif +}; + +// LANGUAGE statement. It can occur both as a top-level statement (in such +// a situation, it changes the default language until the end of the file) +// and as an optional resource statement (then it changes the language +// of a single resource). +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx +class LanguageResource : public OptionalStmt { +public: + uint32_t Lang, SubLang; + + LanguageResource(uint32_t LangId, uint32_t SubLangId) + : Lang(LangId), SubLang(SubLangId) {} + raw_ostream &log(raw_ostream &) const override; + + // This is not a regular top-level statement; when it occurs, it just + // modifies the language context. + Error visit(Visitor *V) const override { return V->visitLanguageStmt(this); } + Twine getResourceTypeName() const override { return "LANGUAGE"; } +}; + +// ACCELERATORS resource. Defines a named table of accelerators for the app. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx +class AcceleratorsResource : public OptStatementsRCResource { +public: + class Accelerator { + public: + IntOrString Event; + uint32_t Id; + uint16_t Flags; + + enum Options { + // This is actually 0x0000 (accelerator is assumed to be ASCII if it's + // not VIRTKEY). However, rc.exe behavior is different in situations + // "only ASCII defined" and "neither ASCII nor VIRTKEY defined". + // Therefore, we include ASCII as another flag. This must be zeroed + // when serialized. + ASCII = 0x8000, + VIRTKEY = 0x0001, + NOINVERT = 0x0002, + ALT = 0x0010, + SHIFT = 0x0004, + CONTROL = 0x0008 + }; + + static constexpr size_t NumFlags = 6; + static StringRef OptionsStr[NumFlags]; + static uint32_t OptionsFlags[NumFlags]; + }; + + AcceleratorsResource(OptionalStmtList &&List, uint16_t Flags) + : OptStatementsRCResource(std::move(List), Flags) {} + + std::vector<Accelerator> Accelerators; + + void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) { + Accelerators.push_back(Accelerator{Event, Id, Flags}); + } + raw_ostream &log(raw_ostream &) const override; + + IntOrString getResourceType() const override { return RkAccelerators; } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } + Twine getResourceTypeName() const override { return "ACCELERATORS"; } + + Error visit(Visitor *V) const override { + return V->visitAcceleratorsResource(this); + } + ResourceKind getKind() const override { return RkAccelerators; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkAccelerators; + } +}; + +// BITMAP resource. Represents a bitmap (".bmp") file. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx +class BitmapResource : public RCResource { +public: + StringRef BitmapLoc; + + BitmapResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), BitmapLoc(Location) {} + raw_ostream &log(raw_ostream &) const override; + + IntOrString getResourceType() const override { return RkBitmap; } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } + + Twine getResourceTypeName() const override { return "BITMAP"; } + Error visit(Visitor *V) const override { + return V->visitBitmapResource(this); + } + ResourceKind getKind() const override { return RkBitmap; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkBitmap; + } +}; + +// CURSOR resource. Represents a single cursor (".cur") file. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx +class CursorResource : public RCResource { +public: + StringRef CursorLoc; + + CursorResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), CursorLoc(Location) {} + raw_ostream &log(raw_ostream &) const override; + + Twine getResourceTypeName() const override { return "CURSOR"; } + static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; } + Error visit(Visitor *V) const override { + return V->visitCursorResource(this); + } + ResourceKind getKind() const override { return RkCursor; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkCursor; + } +}; + +// ICON resource. Represents a single ".ico" file containing a group of icons. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx +class IconResource : public RCResource { +public: + StringRef IconLoc; + + IconResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), IconLoc(Location) {} + raw_ostream &log(raw_ostream &) const override; + + Twine getResourceTypeName() const override { return "ICON"; } + static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; } + Error visit(Visitor *V) const override { return V->visitIconResource(this); } + ResourceKind getKind() const override { return RkIcon; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkIcon; + } +}; + +// HTML resource. Represents a local webpage that is to be embedded into the +// resulting resource file. It embeds a file only - no additional resources +// (images etc.) are included with this resource. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa966018(v=vs.85).aspx +class HTMLResource : public RCResource { +public: + StringRef HTMLLoc; + + HTMLResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), HTMLLoc(Location) {} + raw_ostream &log(raw_ostream &) const override; + + Error visit(Visitor *V) const override { return V->visitHTMLResource(this); } + + // Curiously, file resources don't have DISCARDABLE flag set. + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } + IntOrString getResourceType() const override { return RkHTML; } + Twine getResourceTypeName() const override { return "HTML"; } + ResourceKind getKind() const override { return RkHTML; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkHTML; + } +}; + +// -- MENU resource and its helper classes -- +// This resource describes the contents of an application menu +// (usually located in the upper part of the dialog.) +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx + +// Description of a single submenu item. +class MenuDefinition { +public: + enum Options { + CHECKED = 0x0008, + GRAYED = 0x0001, + HELP = 0x4000, + INACTIVE = 0x0002, + MENUBARBREAK = 0x0020, + MENUBREAK = 0x0040 + }; + + enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup }; + + static constexpr size_t NumFlags = 6; + static StringRef OptionsStr[NumFlags]; + static uint32_t OptionsFlags[NumFlags]; + static raw_ostream &logFlags(raw_ostream &, uint16_t Flags); + virtual raw_ostream &log(raw_ostream &OS) const { + return OS << "Base menu definition\n"; + } + virtual ~MenuDefinition() {} + + virtual uint16_t getResFlags() const { return 0; } + virtual MenuDefKind getKind() const { return MkBase; } +}; + +// Recursive description of a whole submenu. +class MenuDefinitionList : public MenuDefinition { +public: + std::vector<std::unique_ptr<MenuDefinition>> Definitions; + + void addDefinition(std::unique_ptr<MenuDefinition> Def) { + Definitions.push_back(std::move(Def)); + } + raw_ostream &log(raw_ostream &) const override; +}; + +// Separator in MENU definition (MENUITEM SEPARATOR). +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx +class MenuSeparator : public MenuDefinition { +public: + raw_ostream &log(raw_ostream &) const override; + + MenuDefKind getKind() const override { return MkSeparator; } + static bool classof(const MenuDefinition *D) { + return D->getKind() == MkSeparator; + } +}; + +// MENUITEM statement definition. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx +class MenuItem : public MenuDefinition { +public: + StringRef Name; + uint32_t Id; + uint16_t Flags; + + MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags) + : Name(Caption), Id(ItemId), Flags(ItemFlags) {} + raw_ostream &log(raw_ostream &) const override; + + uint16_t getResFlags() const override { return Flags; } + MenuDefKind getKind() const override { return MkMenuItem; } + static bool classof(const MenuDefinition *D) { + return D->getKind() == MkMenuItem; + } +}; + +// POPUP statement definition. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx +class PopupItem : public MenuDefinition { +public: + StringRef Name; + uint16_t Flags; + MenuDefinitionList SubItems; + + PopupItem(StringRef Caption, uint16_t ItemFlags, + MenuDefinitionList &&SubItemsList) + : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {} + raw_ostream &log(raw_ostream &) const override; + + // This has an additional (0x10) flag. It doesn't match with documented + // 0x01 flag, though. + uint16_t getResFlags() const override { return Flags | 0x10; } + MenuDefKind getKind() const override { return MkPopup; } + static bool classof(const MenuDefinition *D) { + return D->getKind() == MkPopup; + } +}; + +// Menu resource definition. +class MenuResource : public OptStatementsRCResource { +public: + MenuDefinitionList Elements; + + MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items, + uint16_t Flags) + : OptStatementsRCResource(std::move(OptStmts), Flags), + Elements(std::move(Items)) {} + raw_ostream &log(raw_ostream &) const override; + + IntOrString getResourceType() const override { return RkMenu; } + Twine getResourceTypeName() const override { return "MENU"; } + Error visit(Visitor *V) const override { return V->visitMenuResource(this); } + ResourceKind getKind() const override { return RkMenu; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkMenu; + } +}; + +// STRINGTABLE resource. Contains a list of strings, each having its unique ID. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx +class StringTableResource : public OptStatementsRCResource { +public: + std::vector<std::pair<uint32_t, std::vector<StringRef>>> Table; + + StringTableResource(OptionalStmtList &&List, uint16_t Flags) + : OptStatementsRCResource(std::move(List), Flags) {} + void addStrings(uint32_t ID, std::vector<StringRef> &&Strings) { + Table.emplace_back(ID, Strings); + } + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "STRINGTABLE"; } + Error visit(Visitor *V) const override { + return V->visitStringTableResource(this); + } +}; + +// -- DIALOG(EX) resource and its helper classes -- +// +// This resource describes dialog boxes and controls residing inside them. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381003(v=vs.85).aspx +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx + +// Single control definition. +class Control { +public: + StringRef Type; + IntOrString Title; + uint32_t ID, X, Y, Width, Height; + Optional<IntWithNotMask> Style; + Optional<uint32_t> ExtStyle, HelpID; + IntOrString Class; + + // Control classes as described in DLGITEMTEMPLATEEX documentation. + // + // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx + enum CtlClasses { + ClsButton = 0x80, + ClsEdit = 0x81, + ClsStatic = 0x82, + ClsListBox = 0x83, + ClsScrollBar = 0x84, + ClsComboBox = 0x85 + }; + + // Simple information about a single control type. + struct CtlInfo { + uint32_t Style; + uint16_t CtlClass; + bool HasTitle; + }; + + Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID, + uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight, + Optional<IntWithNotMask> ItemStyle, Optional<uint32_t> ExtItemStyle, + Optional<uint32_t> CtlHelpID, IntOrString CtlClass) + : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY), + Width(ItemWidth), Height(ItemHeight), Style(ItemStyle), + ExtStyle(ExtItemStyle), HelpID(CtlHelpID), Class(CtlClass) {} + + static const StringMap<CtlInfo> SupportedCtls; + + raw_ostream &log(raw_ostream &) const; +}; + +// Single dialog definition. We don't create distinct classes for DIALOG and +// DIALOGEX because of their being too similar to each other. We only have a +// flag determining the type of the dialog box. +class DialogResource : public OptStatementsRCResource { +public: + uint32_t X, Y, Width, Height, HelpID; + std::vector<Control> Controls; + bool IsExtended; + + DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth, + uint32_t DlgHeight, uint32_t DlgHelpID, + OptionalStmtList &&OptStmts, bool IsDialogEx, uint16_t Flags) + : OptStatementsRCResource(std::move(OptStmts), Flags), X(PosX), Y(PosY), + Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID), + IsExtended(IsDialogEx) {} + + void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); } + + raw_ostream &log(raw_ostream &) const override; + + // It was a weird design decision to assign the same resource type number + // both for DIALOG and DIALOGEX (and the same structure version number). + // It makes it possible for DIALOG to be mistaken for DIALOGEX. + IntOrString getResourceType() const override { return RkDialog; } + Twine getResourceTypeName() const override { + return "DIALOG" + Twine(IsExtended ? "EX" : ""); + } + Error visit(Visitor *V) const override { + return V->visitDialogResource(this); + } + ResourceKind getKind() const override { return RkDialog; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkDialog; + } +}; + +// User-defined resource. It is either: +// * a link to the file, e.g. NAME TYPE "filename", +// * or contains a list of integers and strings, e.g. NAME TYPE {1, "a", 2}. +class UserDefinedResource : public RCResource { +public: + IntOrString Type; + StringRef FileLoc; + std::vector<IntOrString> Contents; + bool IsFileResource; + + UserDefinedResource(IntOrString ResourceType, StringRef FileLocation, + uint16_t Flags) + : RCResource(Flags), Type(ResourceType), FileLoc(FileLocation), + IsFileResource(true) {} + UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data, + uint16_t Flags) + : RCResource(Flags), Type(ResourceType), Contents(std::move(Data)), + IsFileResource(false) {} + + raw_ostream &log(raw_ostream &) const override; + IntOrString getResourceType() const override { return Type; } + Twine getResourceTypeName() const override { return Type; } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } + + Error visit(Visitor *V) const override { + return V->visitUserDefinedResource(this); + } + ResourceKind getKind() const override { return RkUser; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkUser; + } +}; + +// -- VERSIONINFO resource and its helper classes -- +// +// This resource lists the version information on the executable/library. +// The declaration consists of the following items: +// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS) +// * BEGIN +// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines +// another block of version information, whereas VALUE defines a +// key -> value correspondence. There might be more than one value +// corresponding to the single key. +// * END +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx + +// A single VERSIONINFO statement; +class VersionInfoStmt { +public: + enum StmtKind { StBase = 0, StBlock = 1, StValue = 2 }; + + virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; } + virtual ~VersionInfoStmt() {} + + virtual StmtKind getKind() const { return StBase; } + static bool classof(const VersionInfoStmt *S) { + return S->getKind() == StBase; + } +}; + +// BLOCK definition; also the main VERSIONINFO declaration is considered a +// BLOCK, although it has no name. +// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't +// care about them at the parsing phase. +class VersionInfoBlock : public VersionInfoStmt { +public: + std::vector<std::unique_ptr<VersionInfoStmt>> Stmts; + StringRef Name; + + VersionInfoBlock(StringRef BlockName) : Name(BlockName) {} + void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) { + Stmts.push_back(std::move(Stmt)); + } + raw_ostream &log(raw_ostream &) const override; + + StmtKind getKind() const override { return StBlock; } + static bool classof(const VersionInfoStmt *S) { + return S->getKind() == StBlock; + } +}; + +class VersionInfoValue : public VersionInfoStmt { +public: + StringRef Key; + std::vector<IntOrString> Values; + std::vector<bool> HasPrecedingComma; + + VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals, + std::vector<bool> &&CommasBeforeVals) + : Key(InfoKey), Values(std::move(Vals)), + HasPrecedingComma(std::move(CommasBeforeVals)) {} + raw_ostream &log(raw_ostream &) const override; + + StmtKind getKind() const override { return StValue; } + static bool classof(const VersionInfoStmt *S) { + return S->getKind() == StValue; + } +}; + +class VersionInfoResource : public RCResource { +public: + // A class listing fixed VERSIONINFO statements (occuring before main BEGIN). + // If any of these is not specified, it is assumed by the original tool to + // be equal to 0. + class VersionInfoFixed { + public: + enum VersionInfoFixedType { + FtUnknown, + FtFileVersion, + FtProductVersion, + FtFileFlagsMask, + FtFileFlags, + FtFileOS, + FtFileType, + FtFileSubtype, + FtNumTypes + }; + + private: + static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap; + static const StringRef FixedFieldsNames[FtNumTypes]; + + public: + SmallVector<uint32_t, 4> FixedInfo[FtNumTypes]; + SmallVector<bool, FtNumTypes> IsTypePresent; + + static VersionInfoFixedType getFixedType(StringRef Type); + static bool isTypeSupported(VersionInfoFixedType Type); + static bool isVersionType(VersionInfoFixedType Type); + + VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {} + + void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) { + FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end()); + IsTypePresent[Type] = true; + } + + raw_ostream &log(raw_ostream &) const; + }; + + VersionInfoBlock MainBlock; + VersionInfoFixed FixedData; + + VersionInfoResource(VersionInfoBlock &&TopLevelBlock, + VersionInfoFixed &&FixedInfo, uint16_t Flags) + : RCResource(Flags), MainBlock(std::move(TopLevelBlock)), + FixedData(std::move(FixedInfo)) {} + + raw_ostream &log(raw_ostream &) const override; + IntOrString getResourceType() const override { return RkVersionInfo; } + static uint16_t getDefaultMemoryFlags() { return MfMoveable | MfPure; } + Twine getResourceTypeName() const override { return "VERSIONINFO"; } + Error visit(Visitor *V) const override { + return V->visitVersionInfoResource(this); + } + ResourceKind getKind() const override { return RkVersionInfo; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkVersionInfo; + } +}; + +// CHARACTERISTICS optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx +class CharacteristicsStmt : public OptionalStmt { +public: + uint32_t Value; + + CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {} + raw_ostream &log(raw_ostream &) const override; + + Twine getResourceTypeName() const override { return "CHARACTERISTICS"; } + Error visit(Visitor *V) const override { + return V->visitCharacteristicsStmt(this); + } +}; + +// VERSION optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx +class VersionStmt : public OptionalStmt { +public: + uint32_t Value; + + VersionStmt(uint32_t Version) : Value(Version) {} + raw_ostream &log(raw_ostream &) const override; + + Twine getResourceTypeName() const override { return "VERSION"; } + Error visit(Visitor *V) const override { return V->visitVersionStmt(this); } +}; + +// CAPTION optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380778(v=vs.85).aspx +class CaptionStmt : public OptionalStmt { +public: + StringRef Value; + + CaptionStmt(StringRef Caption) : Value(Caption) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "CAPTION"; } + Error visit(Visitor *V) const override { return V->visitCaptionStmt(this); } +}; + +// FONT optional statement. +// Note that the documentation is inaccurate: it expects five arguments to be +// given, however the example provides only two. In fact, the original tool +// expects two arguments - point size and name of the typeface. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx +class FontStmt : public OptionalStmt { +public: + uint32_t Size, Weight, Charset; + StringRef Name; + bool Italic; + + FontStmt(uint32_t FontSize, StringRef FontName, uint32_t FontWeight, + bool FontItalic, uint32_t FontCharset) + : Size(FontSize), Weight(FontWeight), Charset(FontCharset), + Name(FontName), Italic(FontItalic) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "FONT"; } + Error visit(Visitor *V) const override { return V->visitFontStmt(this); } +}; + +// STYLE optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381051(v=vs.85).aspx +class StyleStmt : public OptionalStmt { +public: + uint32_t Value; + + StyleStmt(uint32_t Style) : Value(Style) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "STYLE"; } + Error visit(Visitor *V) const override { return V->visitStyleStmt(this); } +}; + +// EXSTYLE optional statement. +// +// Ref: docs.microsoft.com/en-us/windows/desktop/menurc/exstyle-statement +class ExStyleStmt : public OptionalStmt { +public: + uint32_t Value; + + ExStyleStmt(uint32_t ExStyle) : Value(ExStyle) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "EXSTYLE"; } + Error visit(Visitor *V) const override { return V->visitExStyleStmt(this); } +}; + +// CLASS optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx +class ClassStmt : public OptionalStmt { +public: + IntOrString Value; + + ClassStmt(IntOrString Class) : Value(Class) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "CLASS"; } + Error visit(Visitor *V) const override { return V->visitClassStmt(this); } +}; + +} // namespace rc +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.cpp b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.cpp index 2e21f675b9..b9699d2b2e 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.cpp +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.cpp @@ -1,367 +1,367 @@ -//===-- ResourceScriptToken.cpp ---------------------------------*- 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 implements an interface defined in ResourceScriptToken.h. -// In particular, it defines an .rc script tokenizer. -// -//===---------------------------------------------------------------------===// - -#include "ResourceScriptToken.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/raw_ostream.h" - -#include <algorithm> -#include <cassert> -#include <cctype> -#include <cstdlib> -#include <utility> - -using namespace llvm; - -using Kind = RCToken::Kind; - -// Checks if Representation is a correct description of an RC integer. -// It should be a 32-bit unsigned integer, either decimal, octal (0[0-7]+), -// or hexadecimal (0x[0-9a-f]+). It might be followed by a single 'L' -// character (that is the difference between our representation and -// StringRef's one). If Representation is correct, 'true' is returned and -// the return value is put back in Num. -static bool rcGetAsInteger(StringRef Representation, uint32_t &Num) { - size_t Length = Representation.size(); - if (Length == 0) - return false; - // Strip the last 'L' if unnecessary. - if (std::toupper(Representation.back()) == 'L') - Representation = Representation.drop_back(1); - - return !Representation.getAsInteger<uint32_t>(0, Num); -} - -RCToken::RCToken(RCToken::Kind RCTokenKind, StringRef Value) - : TokenKind(RCTokenKind), TokenValue(Value) {} - -uint32_t RCToken::intValue() const { - assert(TokenKind == Kind::Int); - // We assume that the token already is a correct integer (checked by - // rcGetAsInteger). - uint32_t Result; - bool IsSuccess = rcGetAsInteger(TokenValue, Result); - assert(IsSuccess); - (void)IsSuccess; // Silence the compiler warning when -DNDEBUG flag is on. - return Result; -} - -bool RCToken::isLongInt() const { - return TokenKind == Kind::Int && std::toupper(TokenValue.back()) == 'L'; -} - -StringRef RCToken::value() const { return TokenValue; } - -Kind RCToken::kind() const { return TokenKind; } - -bool RCToken::isBinaryOp() const { - switch (TokenKind) { - case Kind::Plus: - case Kind::Minus: - case Kind::Pipe: - case Kind::Amp: - return true; - default: - return false; - } -} - -static Error getStringError(const Twine &message) { - return make_error<StringError>("Error parsing file: " + message, - inconvertibleErrorCode()); -} - -namespace { - -class Tokenizer { -public: - Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()), Pos(0) {} - - Expected<std::vector<RCToken>> run(); - -private: - // All 'advancing' methods return boolean values; if they're equal to false, - // the stream has ended or failed. - bool advance(size_t Amount = 1); - bool skipWhitespaces(); - - // Consumes a token. If any problem occurred, a non-empty Error is returned. - Error consumeToken(const Kind TokenKind); - - // Check if tokenizer is about to read FollowingChars. - bool willNowRead(StringRef FollowingChars) const; - - // Check if tokenizer can start reading an identifier at current position. - // The original tool did non specify the rules to determine what is a correct - // identifier. We assume they should follow the C convention: - // [a-zA-Z_][a-zA-Z0-9_]*. - bool canStartIdentifier() const; - // Check if tokenizer can continue reading an identifier. - bool canContinueIdentifier() const; - - // Check if tokenizer can start reading an integer. - // A correct integer always starts with a 0-9 digit, - // can contain characters 0-9A-Fa-f (digits), - // Ll (marking the integer is 32-bit), Xx (marking the representation - // is hexadecimal). As some kind of separator should come after the - // integer, we can consume the integer until a non-alphanumeric - // character. - bool canStartInt() const; - bool canContinueInt() const; - - bool canStartString() const; - - // Check if tokenizer can start reading a single line comment (e.g. a comment - // that begins with '//') - bool canStartLineComment() const; - - // Check if tokenizer can start or finish reading a block comment (e.g. a - // comment that begins with '/*' and ends with '*/') - bool canStartBlockComment() const; - - // Throw away all remaining characters on the current line. - void skipCurrentLine(); - - bool streamEof() const; - - // Classify the token that is about to be read from the current position. - Kind classifyCurrentToken() const; - - // Process the Kind::Identifier token - check if it is - // an identifier describing a block start or end. - void processIdentifier(RCToken &token) const; - - StringRef Data; - size_t DataLength, Pos; -}; - -void Tokenizer::skipCurrentLine() { - Pos = Data.find_first_of("\r\n", Pos); - Pos = Data.find_first_not_of("\r\n", Pos); - - if (Pos == StringRef::npos) - Pos = DataLength; -} - -Expected<std::vector<RCToken>> Tokenizer::run() { - Pos = 0; - std::vector<RCToken> Result; - - // Consume an optional UTF-8 Byte Order Mark. - if (willNowRead("\xef\xbb\xbf")) - advance(3); - - while (!streamEof()) { - if (!skipWhitespaces()) - break; - - Kind TokenKind = classifyCurrentToken(); - if (TokenKind == Kind::Invalid) - return getStringError("Invalid token found at position " + Twine(Pos)); - - const size_t TokenStart = Pos; - if (Error TokenError = consumeToken(TokenKind)) - return std::move(TokenError); - - // Comments are just deleted, don't bother saving them. - if (TokenKind == Kind::LineComment || TokenKind == Kind::StartComment) - continue; - - RCToken Token(TokenKind, Data.take_front(Pos).drop_front(TokenStart)); - if (TokenKind == Kind::Identifier) { - processIdentifier(Token); - } else if (TokenKind == Kind::Int) { - uint32_t TokenInt; - if (!rcGetAsInteger(Token.value(), TokenInt)) { - // The integer has incorrect format or cannot be represented in - // a 32-bit integer. - return getStringError("Integer invalid or too large: " + - Token.value().str()); - } - } - - Result.push_back(Token); - } - - return Result; -} - -bool Tokenizer::advance(size_t Amount) { - Pos += Amount; - return !streamEof(); -} - -bool Tokenizer::skipWhitespaces() { - while (!streamEof() && isSpace(Data[Pos])) - advance(); - return !streamEof(); -} - -Error Tokenizer::consumeToken(const Kind TokenKind) { - switch (TokenKind) { - // One-character token consumption. -#define TOKEN(Name) -#define SHORT_TOKEN(Name, Ch) case Kind::Name: -#include "ResourceScriptTokenList.def" - advance(); - return Error::success(); - - case Kind::LineComment: - advance(2); - skipCurrentLine(); - return Error::success(); - - case Kind::StartComment: { - advance(2); - auto EndPos = Data.find("*/", Pos); - if (EndPos == StringRef::npos) - return getStringError( - "Unclosed multi-line comment beginning at position " + Twine(Pos)); - advance(EndPos - Pos); - advance(2); - return Error::success(); - } - case Kind::Identifier: - while (!streamEof() && canContinueIdentifier()) - advance(); - return Error::success(); - - case Kind::Int: - while (!streamEof() && canContinueInt()) - advance(); - return Error::success(); - - case Kind::String: - // Consume the preceding 'L', if there is any. - if (std::toupper(Data[Pos]) == 'L') - advance(); - // Consume the double-quote. - advance(); - - // Consume the characters until the end of the file, line or string. - while (true) { - if (streamEof()) { - return getStringError("Unterminated string literal."); - } else if (Data[Pos] == '"') { - // Consume the ending double-quote. - advance(); - // However, if another '"' follows this double-quote, the string didn't - // end and we just included '"' into the string. - if (!willNowRead("\"")) - return Error::success(); - } else if (Data[Pos] == '\n') { - return getStringError("String literal not terminated in the line."); - } - - advance(); - } - - case Kind::Invalid: - assert(false && "Cannot consume an invalid token."); - } - - llvm_unreachable("Unknown RCToken::Kind"); -} - -bool Tokenizer::willNowRead(StringRef FollowingChars) const { - return Data.drop_front(Pos).startswith(FollowingChars); -} - -bool Tokenizer::canStartIdentifier() const { - assert(!streamEof()); - - const char CurChar = Data[Pos]; - return std::isalpha(CurChar) || CurChar == '_' || CurChar == '.'; -} - -bool Tokenizer::canContinueIdentifier() const { - assert(!streamEof()); - const char CurChar = Data[Pos]; - return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' || - CurChar == '/' || CurChar == '\\'; -} - -bool Tokenizer::canStartInt() const { - assert(!streamEof()); - return std::isdigit(Data[Pos]); -} - -bool Tokenizer::canStartBlockComment() const { - assert(!streamEof()); - return Data.drop_front(Pos).startswith("/*"); -} - -bool Tokenizer::canStartLineComment() const { - assert(!streamEof()); - return Data.drop_front(Pos).startswith("//"); -} - -bool Tokenizer::canContinueInt() const { - assert(!streamEof()); - return std::isalnum(Data[Pos]); -} - -bool Tokenizer::canStartString() const { - return willNowRead("\"") || willNowRead("L\"") || willNowRead("l\""); -} - -bool Tokenizer::streamEof() const { return Pos == DataLength; } - -Kind Tokenizer::classifyCurrentToken() const { - if (canStartBlockComment()) - return Kind::StartComment; - if (canStartLineComment()) - return Kind::LineComment; - - if (canStartInt()) - return Kind::Int; - if (canStartString()) - return Kind::String; - // BEGIN and END are at this point of lexing recognized as identifiers. - if (canStartIdentifier()) - return Kind::Identifier; - - const char CurChar = Data[Pos]; - - switch (CurChar) { - // One-character token classification. -#define TOKEN(Name) -#define SHORT_TOKEN(Name, Ch) \ - case Ch: \ - return Kind::Name; -#include "ResourceScriptTokenList.def" - - default: - return Kind::Invalid; - } -} - -void Tokenizer::processIdentifier(RCToken &Token) const { - assert(Token.kind() == Kind::Identifier); - StringRef Name = Token.value(); - - if (Name.equals_lower("begin")) - Token = RCToken(Kind::BlockBegin, Name); - else if (Name.equals_lower("end")) - Token = RCToken(Kind::BlockEnd, Name); -} - -} // anonymous namespace - -namespace llvm { - -Expected<std::vector<RCToken>> tokenizeRC(StringRef Input) { - return Tokenizer(Input).run(); -} - -} // namespace llvm +//===-- ResourceScriptToken.cpp ---------------------------------*- 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 implements an interface defined in ResourceScriptToken.h. +// In particular, it defines an .rc script tokenizer. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptToken.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" + +#include <algorithm> +#include <cassert> +#include <cctype> +#include <cstdlib> +#include <utility> + +using namespace llvm; + +using Kind = RCToken::Kind; + +// Checks if Representation is a correct description of an RC integer. +// It should be a 32-bit unsigned integer, either decimal, octal (0[0-7]+), +// or hexadecimal (0x[0-9a-f]+). It might be followed by a single 'L' +// character (that is the difference between our representation and +// StringRef's one). If Representation is correct, 'true' is returned and +// the return value is put back in Num. +static bool rcGetAsInteger(StringRef Representation, uint32_t &Num) { + size_t Length = Representation.size(); + if (Length == 0) + return false; + // Strip the last 'L' if unnecessary. + if (std::toupper(Representation.back()) == 'L') + Representation = Representation.drop_back(1); + + return !Representation.getAsInteger<uint32_t>(0, Num); +} + +RCToken::RCToken(RCToken::Kind RCTokenKind, StringRef Value) + : TokenKind(RCTokenKind), TokenValue(Value) {} + +uint32_t RCToken::intValue() const { + assert(TokenKind == Kind::Int); + // We assume that the token already is a correct integer (checked by + // rcGetAsInteger). + uint32_t Result; + bool IsSuccess = rcGetAsInteger(TokenValue, Result); + assert(IsSuccess); + (void)IsSuccess; // Silence the compiler warning when -DNDEBUG flag is on. + return Result; +} + +bool RCToken::isLongInt() const { + return TokenKind == Kind::Int && std::toupper(TokenValue.back()) == 'L'; +} + +StringRef RCToken::value() const { return TokenValue; } + +Kind RCToken::kind() const { return TokenKind; } + +bool RCToken::isBinaryOp() const { + switch (TokenKind) { + case Kind::Plus: + case Kind::Minus: + case Kind::Pipe: + case Kind::Amp: + return true; + default: + return false; + } +} + +static Error getStringError(const Twine &message) { + return make_error<StringError>("Error parsing file: " + message, + inconvertibleErrorCode()); +} + +namespace { + +class Tokenizer { +public: + Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()), Pos(0) {} + + Expected<std::vector<RCToken>> run(); + +private: + // All 'advancing' methods return boolean values; if they're equal to false, + // the stream has ended or failed. + bool advance(size_t Amount = 1); + bool skipWhitespaces(); + + // Consumes a token. If any problem occurred, a non-empty Error is returned. + Error consumeToken(const Kind TokenKind); + + // Check if tokenizer is about to read FollowingChars. + bool willNowRead(StringRef FollowingChars) const; + + // Check if tokenizer can start reading an identifier at current position. + // The original tool did non specify the rules to determine what is a correct + // identifier. We assume they should follow the C convention: + // [a-zA-Z_][a-zA-Z0-9_]*. + bool canStartIdentifier() const; + // Check if tokenizer can continue reading an identifier. + bool canContinueIdentifier() const; + + // Check if tokenizer can start reading an integer. + // A correct integer always starts with a 0-9 digit, + // can contain characters 0-9A-Fa-f (digits), + // Ll (marking the integer is 32-bit), Xx (marking the representation + // is hexadecimal). As some kind of separator should come after the + // integer, we can consume the integer until a non-alphanumeric + // character. + bool canStartInt() const; + bool canContinueInt() const; + + bool canStartString() const; + + // Check if tokenizer can start reading a single line comment (e.g. a comment + // that begins with '//') + bool canStartLineComment() const; + + // Check if tokenizer can start or finish reading a block comment (e.g. a + // comment that begins with '/*' and ends with '*/') + bool canStartBlockComment() const; + + // Throw away all remaining characters on the current line. + void skipCurrentLine(); + + bool streamEof() const; + + // Classify the token that is about to be read from the current position. + Kind classifyCurrentToken() const; + + // Process the Kind::Identifier token - check if it is + // an identifier describing a block start or end. + void processIdentifier(RCToken &token) const; + + StringRef Data; + size_t DataLength, Pos; +}; + +void Tokenizer::skipCurrentLine() { + Pos = Data.find_first_of("\r\n", Pos); + Pos = Data.find_first_not_of("\r\n", Pos); + + if (Pos == StringRef::npos) + Pos = DataLength; +} + +Expected<std::vector<RCToken>> Tokenizer::run() { + Pos = 0; + std::vector<RCToken> Result; + + // Consume an optional UTF-8 Byte Order Mark. + if (willNowRead("\xef\xbb\xbf")) + advance(3); + + while (!streamEof()) { + if (!skipWhitespaces()) + break; + + Kind TokenKind = classifyCurrentToken(); + if (TokenKind == Kind::Invalid) + return getStringError("Invalid token found at position " + Twine(Pos)); + + const size_t TokenStart = Pos; + if (Error TokenError = consumeToken(TokenKind)) + return std::move(TokenError); + + // Comments are just deleted, don't bother saving them. + if (TokenKind == Kind::LineComment || TokenKind == Kind::StartComment) + continue; + + RCToken Token(TokenKind, Data.take_front(Pos).drop_front(TokenStart)); + if (TokenKind == Kind::Identifier) { + processIdentifier(Token); + } else if (TokenKind == Kind::Int) { + uint32_t TokenInt; + if (!rcGetAsInteger(Token.value(), TokenInt)) { + // The integer has incorrect format or cannot be represented in + // a 32-bit integer. + return getStringError("Integer invalid or too large: " + + Token.value().str()); + } + } + + Result.push_back(Token); + } + + return Result; +} + +bool Tokenizer::advance(size_t Amount) { + Pos += Amount; + return !streamEof(); +} + +bool Tokenizer::skipWhitespaces() { + while (!streamEof() && isSpace(Data[Pos])) + advance(); + return !streamEof(); +} + +Error Tokenizer::consumeToken(const Kind TokenKind) { + switch (TokenKind) { + // One-character token consumption. +#define TOKEN(Name) +#define SHORT_TOKEN(Name, Ch) case Kind::Name: +#include "ResourceScriptTokenList.def" + advance(); + return Error::success(); + + case Kind::LineComment: + advance(2); + skipCurrentLine(); + return Error::success(); + + case Kind::StartComment: { + advance(2); + auto EndPos = Data.find("*/", Pos); + if (EndPos == StringRef::npos) + return getStringError( + "Unclosed multi-line comment beginning at position " + Twine(Pos)); + advance(EndPos - Pos); + advance(2); + return Error::success(); + } + case Kind::Identifier: + while (!streamEof() && canContinueIdentifier()) + advance(); + return Error::success(); + + case Kind::Int: + while (!streamEof() && canContinueInt()) + advance(); + return Error::success(); + + case Kind::String: + // Consume the preceding 'L', if there is any. + if (std::toupper(Data[Pos]) == 'L') + advance(); + // Consume the double-quote. + advance(); + + // Consume the characters until the end of the file, line or string. + while (true) { + if (streamEof()) { + return getStringError("Unterminated string literal."); + } else if (Data[Pos] == '"') { + // Consume the ending double-quote. + advance(); + // However, if another '"' follows this double-quote, the string didn't + // end and we just included '"' into the string. + if (!willNowRead("\"")) + return Error::success(); + } else if (Data[Pos] == '\n') { + return getStringError("String literal not terminated in the line."); + } + + advance(); + } + + case Kind::Invalid: + assert(false && "Cannot consume an invalid token."); + } + + llvm_unreachable("Unknown RCToken::Kind"); +} + +bool Tokenizer::willNowRead(StringRef FollowingChars) const { + return Data.drop_front(Pos).startswith(FollowingChars); +} + +bool Tokenizer::canStartIdentifier() const { + assert(!streamEof()); + + const char CurChar = Data[Pos]; + return std::isalpha(CurChar) || CurChar == '_' || CurChar == '.'; +} + +bool Tokenizer::canContinueIdentifier() const { + assert(!streamEof()); + const char CurChar = Data[Pos]; + return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' || + CurChar == '/' || CurChar == '\\'; +} + +bool Tokenizer::canStartInt() const { + assert(!streamEof()); + return std::isdigit(Data[Pos]); +} + +bool Tokenizer::canStartBlockComment() const { + assert(!streamEof()); + return Data.drop_front(Pos).startswith("/*"); +} + +bool Tokenizer::canStartLineComment() const { + assert(!streamEof()); + return Data.drop_front(Pos).startswith("//"); +} + +bool Tokenizer::canContinueInt() const { + assert(!streamEof()); + return std::isalnum(Data[Pos]); +} + +bool Tokenizer::canStartString() const { + return willNowRead("\"") || willNowRead("L\"") || willNowRead("l\""); +} + +bool Tokenizer::streamEof() const { return Pos == DataLength; } + +Kind Tokenizer::classifyCurrentToken() const { + if (canStartBlockComment()) + return Kind::StartComment; + if (canStartLineComment()) + return Kind::LineComment; + + if (canStartInt()) + return Kind::Int; + if (canStartString()) + return Kind::String; + // BEGIN and END are at this point of lexing recognized as identifiers. + if (canStartIdentifier()) + return Kind::Identifier; + + const char CurChar = Data[Pos]; + + switch (CurChar) { + // One-character token classification. +#define TOKEN(Name) +#define SHORT_TOKEN(Name, Ch) \ + case Ch: \ + return Kind::Name; +#include "ResourceScriptTokenList.def" + + default: + return Kind::Invalid; + } +} + +void Tokenizer::processIdentifier(RCToken &Token) const { + assert(Token.kind() == Kind::Identifier); + StringRef Name = Token.value(); + + if (Name.equals_lower("begin")) + Token = RCToken(Kind::BlockBegin, Name); + else if (Name.equals_lower("end")) + Token = RCToken(Kind::BlockEnd, Name); +} + +} // anonymous namespace + +namespace llvm { + +Expected<std::vector<RCToken>> tokenizeRC(StringRef Input) { + return Tokenizer(Input).run(); +} + +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.h b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.h index cc8ca48b49..d821f10138 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.h +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptToken.h @@ -1,81 +1,81 @@ -//===-- ResourceScriptToken.h -----------------------------------*- 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 declares the .rc script tokens and defines an interface for tokenizing -// the input data. The list of available tokens is located at -// ResourceScriptTokenList.def. -// -// Note that the tokenizer does not support preprocessor directives. The -// preprocessor should do its work on the .rc file before running llvm-rc. -// -// As for now, it is possible to parse ASCII files only (the behavior on -// UTF files might be undefined). However, it already consumes UTF-8 BOM, if -// there is any. Thus, ASCII-compatible UTF-8 files are tokenized correctly. -// -// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H -#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" - -#include <cstdint> -#include <map> -#include <string> -#include <vector> - -namespace llvm { - -// A definition of a single resource script token. Each token has its kind -// (declared in ResourceScriptTokenList) and holds a value - a reference -// representation of the token. -// RCToken does not claim ownership on its value. A memory buffer containing -// the token value should be stored in a safe place and cannot be freed -// nor reallocated. -class RCToken { -public: - enum class Kind { -#define TOKEN(Name) Name, -#define SHORT_TOKEN(Name, Ch) Name, -#include "ResourceScriptTokenList.def" - }; - - RCToken(RCToken::Kind RCTokenKind, StringRef Value); - - // Get an integer value of the integer token. - uint32_t intValue() const; - bool isLongInt() const; - - StringRef value() const; - Kind kind() const; - - // Check if a token describes a binary operator. - bool isBinaryOp() const; - -private: - Kind TokenKind; - StringRef TokenValue; -}; - -// Tokenize Input. -// In case no error occurred, the return value contains -// tokens in order they were in the input file. -// In case of any error, the return value contains -// a textual representation of error. -// -// Tokens returned by this function hold only references to the parts -// of the Input. Memory buffer containing Input cannot be freed, -// modified or reallocated. -Expected<std::vector<RCToken>> tokenizeRC(StringRef Input); - -} // namespace llvm - -#endif +//===-- ResourceScriptToken.h -----------------------------------*- 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 declares the .rc script tokens and defines an interface for tokenizing +// the input data. The list of available tokens is located at +// ResourceScriptTokenList.def. +// +// Note that the tokenizer does not support preprocessor directives. The +// preprocessor should do its work on the .rc file before running llvm-rc. +// +// As for now, it is possible to parse ASCII files only (the behavior on +// UTF files might be undefined). However, it already consumes UTF-8 BOM, if +// there is any. Thus, ASCII-compatible UTF-8 files are tokenized correctly. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include <cstdint> +#include <map> +#include <string> +#include <vector> + +namespace llvm { + +// A definition of a single resource script token. Each token has its kind +// (declared in ResourceScriptTokenList) and holds a value - a reference +// representation of the token. +// RCToken does not claim ownership on its value. A memory buffer containing +// the token value should be stored in a safe place and cannot be freed +// nor reallocated. +class RCToken { +public: + enum class Kind { +#define TOKEN(Name) Name, +#define SHORT_TOKEN(Name, Ch) Name, +#include "ResourceScriptTokenList.def" + }; + + RCToken(RCToken::Kind RCTokenKind, StringRef Value); + + // Get an integer value of the integer token. + uint32_t intValue() const; + bool isLongInt() const; + + StringRef value() const; + Kind kind() const; + + // Check if a token describes a binary operator. + bool isBinaryOp() const; + +private: + Kind TokenKind; + StringRef TokenValue; +}; + +// Tokenize Input. +// In case no error occurred, the return value contains +// tokens in order they were in the input file. +// In case of any error, the return value contains +// a textual representation of error. +// +// Tokens returned by this function hold only references to the parts +// of the Input. Memory buffer containing Input cannot be freed, +// modified or reallocated. +Expected<std::vector<RCToken>> tokenizeRC(StringRef Input); + +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptTokenList.def b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptTokenList.def index a61a96461f..e0ea32f940 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptTokenList.def +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceScriptTokenList.def @@ -1,39 +1,39 @@ -//===-- ResourceScriptTokenList.h -------------------------------*- 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 is a part of llvm-rc tokenizer. It lists all the possible tokens -// that might occur in a correct .rc script. -// -//===---------------------------------------------------------------------===// - - -// Long tokens. They might consist of more than one character. -TOKEN(Invalid) // Invalid token. Should not occur in a valid script. -TOKEN(Int) // Integer (decimal, octal or hexadecimal). -TOKEN(String) // String value. -TOKEN(Identifier) // Script identifier (resource name or type). -TOKEN(LineComment) // Beginning of single-line comment. -TOKEN(StartComment) // Beginning of multi-line comment. - -// Short tokens. They usually consist of exactly one character. -// The definitions are of the form SHORT_TOKEN(TokenName, TokenChar). -// TokenChar is the one-character token representation occuring in the correct -// .rc scripts. -SHORT_TOKEN(BlockBegin, '{') // Start of the script block; can also be BEGIN. -SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END. -SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator. -SHORT_TOKEN(Plus, '+') // Addition operator. -SHORT_TOKEN(Minus, '-') // Subtraction operator. -SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator. -SHORT_TOKEN(Amp, '&') // Bitwise-AND operator. -SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator. -SHORT_TOKEN(LeftParen, '(') // Left parenthesis in the script expressions. -SHORT_TOKEN(RightParen, ')') // Right parenthesis. - -#undef TOKEN -#undef SHORT_TOKEN +//===-- ResourceScriptTokenList.h -------------------------------*- 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 is a part of llvm-rc tokenizer. It lists all the possible tokens +// that might occur in a correct .rc script. +// +//===---------------------------------------------------------------------===// + + +// Long tokens. They might consist of more than one character. +TOKEN(Invalid) // Invalid token. Should not occur in a valid script. +TOKEN(Int) // Integer (decimal, octal or hexadecimal). +TOKEN(String) // String value. +TOKEN(Identifier) // Script identifier (resource name or type). +TOKEN(LineComment) // Beginning of single-line comment. +TOKEN(StartComment) // Beginning of multi-line comment. + +// Short tokens. They usually consist of exactly one character. +// The definitions are of the form SHORT_TOKEN(TokenName, TokenChar). +// TokenChar is the one-character token representation occuring in the correct +// .rc scripts. +SHORT_TOKEN(BlockBegin, '{') // Start of the script block; can also be BEGIN. +SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END. +SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator. +SHORT_TOKEN(Plus, '+') // Addition operator. +SHORT_TOKEN(Minus, '-') // Subtraction operator. +SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator. +SHORT_TOKEN(Amp, '&') // Bitwise-AND operator. +SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator. +SHORT_TOKEN(LeftParen, '(') // Left parenthesis in the script expressions. +SHORT_TOKEN(RightParen, ')') // Right parenthesis. + +#undef TOKEN +#undef SHORT_TOKEN diff --git a/contrib/libs/llvm12/tools/llvm-rc/ResourceVisitor.h b/contrib/libs/llvm12/tools/llvm-rc/ResourceVisitor.h index 843c8d898a..f45154ddd4 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ResourceVisitor.h +++ b/contrib/libs/llvm12/tools/llvm-rc/ResourceVisitor.h @@ -1,61 +1,61 @@ -//===-- ResourceVisitor.h ---------------------------------------*- 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 defines a base class visiting resource script resources. -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H -#define LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H - -#include "llvm/Support/Error.h" - -namespace llvm { -namespace rc { - -class RCResource; - -class CaptionStmt; -class ClassStmt; -class CharacteristicsStmt; -class ExStyleStmt; -class FontStmt; -class LanguageResource; -class StyleStmt; -class VersionStmt; - -class Visitor { -public: - virtual Error visitNullResource(const RCResource *) = 0; - virtual Error visitAcceleratorsResource(const RCResource *) = 0; - virtual Error visitBitmapResource(const RCResource *) = 0; - virtual Error visitCursorResource(const RCResource *) = 0; - virtual Error visitDialogResource(const RCResource *) = 0; - virtual Error visitHTMLResource(const RCResource *) = 0; - virtual Error visitIconResource(const RCResource *) = 0; - virtual Error visitMenuResource(const RCResource *) = 0; - virtual Error visitStringTableResource(const RCResource *) = 0; - virtual Error visitUserDefinedResource(const RCResource *) = 0; - virtual Error visitVersionInfoResource(const RCResource *) = 0; - - virtual Error visitCaptionStmt(const CaptionStmt *) = 0; - virtual Error visitClassStmt(const ClassStmt *) = 0; - virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0; - virtual Error visitExStyleStmt(const ExStyleStmt *) = 0; - virtual Error visitFontStmt(const FontStmt *) = 0; - virtual Error visitLanguageStmt(const LanguageResource *) = 0; - virtual Error visitStyleStmt(const StyleStmt *) = 0; - virtual Error visitVersionStmt(const VersionStmt *) = 0; - - virtual ~Visitor() {} -}; - -} // namespace rc -} // namespace llvm - -#endif +//===-- ResourceVisitor.h ---------------------------------------*- 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 defines a base class visiting resource script resources. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H +#define LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace rc { + +class RCResource; + +class CaptionStmt; +class ClassStmt; +class CharacteristicsStmt; +class ExStyleStmt; +class FontStmt; +class LanguageResource; +class StyleStmt; +class VersionStmt; + +class Visitor { +public: + virtual Error visitNullResource(const RCResource *) = 0; + virtual Error visitAcceleratorsResource(const RCResource *) = 0; + virtual Error visitBitmapResource(const RCResource *) = 0; + virtual Error visitCursorResource(const RCResource *) = 0; + virtual Error visitDialogResource(const RCResource *) = 0; + virtual Error visitHTMLResource(const RCResource *) = 0; + virtual Error visitIconResource(const RCResource *) = 0; + virtual Error visitMenuResource(const RCResource *) = 0; + virtual Error visitStringTableResource(const RCResource *) = 0; + virtual Error visitUserDefinedResource(const RCResource *) = 0; + virtual Error visitVersionInfoResource(const RCResource *) = 0; + + virtual Error visitCaptionStmt(const CaptionStmt *) = 0; + virtual Error visitClassStmt(const ClassStmt *) = 0; + virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0; + virtual Error visitExStyleStmt(const ExStyleStmt *) = 0; + virtual Error visitFontStmt(const FontStmt *) = 0; + virtual Error visitLanguageStmt(const LanguageResource *) = 0; + virtual Error visitStyleStmt(const StyleStmt *) = 0; + virtual Error visitVersionStmt(const VersionStmt *) = 0; + + virtual ~Visitor() {} +}; + +} // namespace rc +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-rc/llvm-rc.cpp b/contrib/libs/llvm12/tools/llvm-rc/llvm-rc.cpp index e9027a21d4..ced68a3727 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/llvm-rc.cpp +++ b/contrib/libs/llvm12/tools/llvm-rc/llvm-rc.cpp @@ -1,216 +1,216 @@ -//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- 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 -// -//===----------------------------------------------------------------------===// -// -// Compile .rc scripts into .res files. This is intended to be a -// platform-independent port of Microsoft's rc.exe tool. -// -//===----------------------------------------------------------------------===// - -#include "ResourceFileWriter.h" -#include "ResourceScriptCppFilter.h" -#include "ResourceScriptParser.h" -#include "ResourceScriptStmt.h" -#include "ResourceScriptToken.h" - -#include "llvm/Option/Arg.h" -#include "llvm/Option/ArgList.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/raw_ostream.h" - -#include <algorithm> -#include <system_error> - -using namespace llvm; -using namespace llvm::rc; - -namespace { - -// Input options tables. - -enum ID { - OPT_INVALID = 0, // This is not a correct option ID. -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - OPT_##ID, -#include "Opts.inc" -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "Opts.inc" -#undef PREFIX - -static const opt::OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR, VALUES) \ - { \ - PREFIX, NAME, HELPTEXT, \ - METAVAR, OPT_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, \ - OPT_##ALIAS, ALIASARGS, VALUES}, -#include "Opts.inc" -#undef OPTION -}; - -class RcOptTable : public opt::OptTable { -public: - RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {} -}; - -static ExitOnError ExitOnErr; - -LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) { - errs() << Message << "\n"; - exit(1); -} - -} // anonymous namespace - -int main(int Argc, const char **Argv) { - InitLLVM X(Argc, Argv); - ExitOnErr.setBanner("llvm-rc: "); - - RcOptTable T; - unsigned MAI, MAC; - const char **DashDash = std::find_if( - Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; }); - ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash); - - opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); - - // The tool prints nothing when invoked with no command-line arguments. +//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Compile .rc scripts into .res files. This is intended to be a +// platform-independent port of Microsoft's rc.exe tool. +// +//===----------------------------------------------------------------------===// + +#include "ResourceFileWriter.h" +#include "ResourceScriptCppFilter.h" +#include "ResourceScriptParser.h" +#include "ResourceScriptStmt.h" +#include "ResourceScriptToken.h" + +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +#include <algorithm> +#include <system_error> + +using namespace llvm; +using namespace llvm::rc; + +namespace { + +// Input options tables. + +enum ID { + OPT_INVALID = 0, // This is not a correct option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class RcOptTable : public opt::OptTable { +public: + RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {} +}; + +static ExitOnError ExitOnErr; + +LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) { + errs() << Message << "\n"; + exit(1); +} + +} // anonymous namespace + +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); + ExitOnErr.setBanner("llvm-rc: "); + + RcOptTable T; + unsigned MAI, MAC; + const char **DashDash = std::find_if( + Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash); + + opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + + // The tool prints nothing when invoked with no command-line arguments. if (InputArgs.hasArg(OPT_help)) { - T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false); - return 0; - } - + T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false); + return 0; + } + const bool BeVerbose = InputArgs.hasArg(OPT_verbose); - - std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT); - if (DashDash != Argv + Argc) - InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc); - if (InArgsInfo.size() != 1) { - fatalError("Exactly one input file should be provided."); - } - - // Read and tokenize the input file. - ErrorOr<std::unique_ptr<MemoryBuffer>> File = - MemoryBuffer::getFile(InArgsInfo[0]); - if (!File) { - fatalError("Error opening file '" + Twine(InArgsInfo[0]) + - "': " + File.getError().message()); - } - - std::unique_ptr<MemoryBuffer> FileContents = std::move(*File); - StringRef Contents = FileContents->getBuffer(); - - std::string FilteredContents = filterCppOutput(Contents); - std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents)); - - if (BeVerbose) { - const Twine TokenNames[] = { -#define TOKEN(Name) #Name, -#define SHORT_TOKEN(Name, Ch) #Name, -#include "ResourceScriptTokenList.def" - }; - - for (const RCToken &Token : Tokens) { - outs() << TokenNames[static_cast<int>(Token.kind())] << ": " - << Token.value(); - if (Token.kind() == RCToken::Kind::Int) - outs() << "; int value = " << Token.intValue(); - - outs() << "\n"; - } - } - - WriterParams Params; - SmallString<128> InputFile(InArgsInfo[0]); - llvm::sys::fs::make_absolute(InputFile); - Params.InputFilePath = InputFile; + + std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT); + if (DashDash != Argv + Argc) + InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc); + if (InArgsInfo.size() != 1) { + fatalError("Exactly one input file should be provided."); + } + + // Read and tokenize the input file. + ErrorOr<std::unique_ptr<MemoryBuffer>> File = + MemoryBuffer::getFile(InArgsInfo[0]); + if (!File) { + fatalError("Error opening file '" + Twine(InArgsInfo[0]) + + "': " + File.getError().message()); + } + + std::unique_ptr<MemoryBuffer> FileContents = std::move(*File); + StringRef Contents = FileContents->getBuffer(); + + std::string FilteredContents = filterCppOutput(Contents); + std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents)); + + if (BeVerbose) { + const Twine TokenNames[] = { +#define TOKEN(Name) #Name, +#define SHORT_TOKEN(Name, Ch) #Name, +#include "ResourceScriptTokenList.def" + }; + + for (const RCToken &Token : Tokens) { + outs() << TokenNames[static_cast<int>(Token.kind())] << ": " + << Token.value(); + if (Token.kind() == RCToken::Kind::Int) + outs() << "; int value = " << Token.intValue(); + + outs() << "\n"; + } + } + + WriterParams Params; + SmallString<128> InputFile(InArgsInfo[0]); + llvm::sys::fs::make_absolute(InputFile); + Params.InputFilePath = InputFile; Params.Include = InputArgs.getAllArgValues(OPT_includepath); Params.NoInclude = InputArgs.getAllArgValues(OPT_noinclude); - + if (InputArgs.hasArg(OPT_codepage)) { if (InputArgs.getLastArgValue(OPT_codepage) - .getAsInteger(10, Params.CodePage)) - fatalError("Invalid code page: " + + .getAsInteger(10, Params.CodePage)) + fatalError("Invalid code page: " + InputArgs.getLastArgValue(OPT_codepage)); - switch (Params.CodePage) { - case CpAcp: - case CpWin1252: - case CpUtf8: - break; - default: - fatalError( - "Unsupported code page, only 0, 1252 and 65001 are supported!"); - } - } - - std::unique_ptr<ResourceFileWriter> Visitor; + switch (Params.CodePage) { + case CpAcp: + case CpWin1252: + case CpUtf8: + break; + default: + fatalError( + "Unsupported code page, only 0, 1252 and 65001 are supported!"); + } + } + + std::unique_ptr<ResourceFileWriter> Visitor; bool IsDryRun = InputArgs.hasArg(OPT_dry_run); - - if (!IsDryRun) { + + if (!IsDryRun) { auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout); - if (OutArgsInfo.empty()) { - SmallString<128> OutputFile = InputFile; - llvm::sys::path::replace_extension(OutputFile, "res"); - OutArgsInfo.push_back(std::string(OutputFile.str())); - } - - if (OutArgsInfo.size() != 1) - fatalError( - "No more than one output file should be provided (using /FO flag)."); - - std::error_code EC; - auto FOut = std::make_unique<raw_fd_ostream>( - OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write); - if (EC) - fatalError("Error opening output file '" + OutArgsInfo[0] + - "': " + EC.message()); - Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut)); + if (OutArgsInfo.empty()) { + SmallString<128> OutputFile = InputFile; + llvm::sys::path::replace_extension(OutputFile, "res"); + OutArgsInfo.push_back(std::string(OutputFile.str())); + } + + if (OutArgsInfo.size() != 1) + fatalError( + "No more than one output file should be provided (using /FO flag)."); + + std::error_code EC; + auto FOut = std::make_unique<raw_fd_ostream>( + OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write); + if (EC) + fatalError("Error opening output file '" + OutArgsInfo[0] + + "': " + EC.message()); + Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut)); Visitor->AppendNull = InputArgs.hasArg(OPT_add_null); - - ExitOnErr(NullResource().visit(Visitor.get())); - - // Set the default language; choose en-US arbitrarily. - unsigned PrimaryLangId = 0x09, SubLangId = 0x01; + + ExitOnErr(NullResource().visit(Visitor.get())); + + // Set the default language; choose en-US arbitrarily. + unsigned PrimaryLangId = 0x09, SubLangId = 0x01; if (InputArgs.hasArg(OPT_lang_id)) { - unsigned LangId; + unsigned LangId; if (InputArgs.getLastArgValue(OPT_lang_id).getAsInteger(16, LangId)) - fatalError("Invalid language id: " + + fatalError("Invalid language id: " + InputArgs.getLastArgValue(OPT_lang_id)); - PrimaryLangId = LangId & 0x3ff; - SubLangId = LangId >> 10; - } - ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get())); - } - - rc::RCParser Parser{std::move(Tokens)}; - while (!Parser.isEof()) { - auto Resource = ExitOnErr(Parser.parseSingleResource()); - if (BeVerbose) - Resource->log(outs()); - if (!IsDryRun) - ExitOnErr(Resource->visit(Visitor.get())); - } - - // STRINGTABLE resources come at the very end. - if (!IsDryRun) - ExitOnErr(Visitor->dumpAllStringTables()); - - return 0; -} + PrimaryLangId = LangId & 0x3ff; + SubLangId = LangId >> 10; + } + ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get())); + } + + rc::RCParser Parser{std::move(Tokens)}; + while (!Parser.isEof()) { + auto Resource = ExitOnErr(Parser.parseSingleResource()); + if (BeVerbose) + Resource->log(outs()); + if (!IsDryRun) + ExitOnErr(Resource->visit(Visitor.get())); + } + + // STRINGTABLE resources come at the very end. + if (!IsDryRun) + ExitOnErr(Visitor->dumpAllStringTables()); + + return 0; +} diff --git a/contrib/libs/llvm12/tools/llvm-rc/ya.make b/contrib/libs/llvm12/tools/llvm-rc/ya.make index e69f41f542..e1f1b87aec 100644 --- a/contrib/libs/llvm12/tools/llvm-rc/ya.make +++ b/contrib/libs/llvm12/tools/llvm-rc/ya.make @@ -1,40 +1,40 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/Demangle contrib/libs/llvm12/lib/Option contrib/libs/llvm12/lib/Support -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-rc contrib/libs/llvm12/tools/llvm-rc -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - ResourceFileWriter.cpp - ResourceScriptCppFilter.cpp - ResourceScriptParser.cpp - ResourceScriptStmt.cpp - ResourceScriptToken.cpp - llvm-rc.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + ResourceFileWriter.cpp + ResourceScriptCppFilter.cpp + ResourceScriptParser.cpp + ResourceScriptStmt.cpp + ResourceScriptToken.cpp + llvm-rc.cpp +) + +END() diff --git a/contrib/libs/llvm12/tools/llvm-symbolizer/llvm-symbolizer.cpp b/contrib/libs/llvm12/tools/llvm-symbolizer/llvm-symbolizer.cpp index 8734c2d740..394fc5d1a7 100644 --- a/contrib/libs/llvm12/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/contrib/libs/llvm12/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -1,43 +1,43 @@ -//===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===// -// -// 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 utility works much like "addr2line". It is able of transforming -// tuples (module name, module offset) to code locations (function name, -// file, line number, column number). It is targeted for compiler-rt tools -// (especially AddressSanitizer and ThreadSanitizer) that can use it -// to symbolize stack traces in their error reports. -// -//===----------------------------------------------------------------------===// - +//===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===// +// +// 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 utility works much like "addr2line". It is able of transforming +// tuples (module name, module offset) to code locations (function name, +// file, line number, column number). It is targeted for compiler-rt tools +// (especially AddressSanitizer and ThreadSanitizer) that can use it +// to symbolize stack traces in their error reports. +// +//===----------------------------------------------------------------------===// + #include "Opts.inc" -#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Config/config.h" -#include "llvm/DebugInfo/Symbolize/DIPrinter.h" -#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/DebugInfo/Symbolize/DIPrinter.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" -#include "llvm/Support/COM.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Path.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" #include "llvm/Support/StringSaver.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cstdio> -#include <cstring> -#include <string> - -using namespace llvm; -using namespace symbolize; - +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cstdio> +#include <cstring> +#include <string> + +using namespace llvm; +using namespace symbolize; + namespace { enum ID { OPT_INVALID = 0, // This is not an option ID. @@ -47,11 +47,11 @@ enum ID { #include "Opts.inc" #undef OPTION }; - + #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "Opts.inc" #undef PREFIX - + static const opt::OptTable::Info InfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ @@ -63,139 +63,139 @@ static const opt::OptTable::Info InfoTable[] = { #include "Opts.inc" #undef OPTION }; - + class SymbolizerOptTable : public opt::OptTable { public: SymbolizerOptTable() : OptTable(InfoTable, true) {} }; } // namespace - -static cl::list<std::string> ClInputAddresses(cl::Positional, - cl::desc("<input addresses>..."), - cl::ZeroOrMore); - -template<typename T> -static bool error(Expected<T> &ResOrErr) { - if (ResOrErr) - return false; - logAllUnhandledErrors(ResOrErr.takeError(), errs(), - "LLVMSymbolizer: error reading file: "); - return true; -} - -enum class Command { - Code, - Data, - Frame, -}; - + +static cl::list<std::string> ClInputAddresses(cl::Positional, + cl::desc("<input addresses>..."), + cl::ZeroOrMore); + +template<typename T> +static bool error(Expected<T> &ResOrErr) { + if (ResOrErr) + return false; + logAllUnhandledErrors(ResOrErr.takeError(), errs(), + "LLVMSymbolizer: error reading file: "); + return true; +} + +enum class Command { + Code, + Data, + Frame, +}; + static bool parseCommand(StringRef BinaryName, bool IsAddr2Line, StringRef InputString, Command &Cmd, - std::string &ModuleName, uint64_t &ModuleOffset) { - const char kDelimiters[] = " \n\r"; - ModuleName = ""; - if (InputString.consume_front("CODE ")) { - Cmd = Command::Code; - } else if (InputString.consume_front("DATA ")) { - Cmd = Command::Data; - } else if (InputString.consume_front("FRAME ")) { - Cmd = Command::Frame; - } else { - // If no cmd, assume it's CODE. - Cmd = Command::Code; - } - const char *Pos = InputString.data(); - // Skip delimiters and parse input filename (if needed). + std::string &ModuleName, uint64_t &ModuleOffset) { + const char kDelimiters[] = " \n\r"; + ModuleName = ""; + if (InputString.consume_front("CODE ")) { + Cmd = Command::Code; + } else if (InputString.consume_front("DATA ")) { + Cmd = Command::Data; + } else if (InputString.consume_front("FRAME ")) { + Cmd = Command::Frame; + } else { + // If no cmd, assume it's CODE. + Cmd = Command::Code; + } + const char *Pos = InputString.data(); + // Skip delimiters and parse input filename (if needed). if (BinaryName.empty()) { - Pos += strspn(Pos, kDelimiters); - if (*Pos == '"' || *Pos == '\'') { - char Quote = *Pos; - Pos++; - const char *End = strchr(Pos, Quote); - if (!End) - return false; - ModuleName = std::string(Pos, End - Pos); - Pos = End + 1; - } else { - int NameLength = strcspn(Pos, kDelimiters); - ModuleName = std::string(Pos, NameLength); - Pos += NameLength; - } - } else { + Pos += strspn(Pos, kDelimiters); + if (*Pos == '"' || *Pos == '\'') { + char Quote = *Pos; + Pos++; + const char *End = strchr(Pos, Quote); + if (!End) + return false; + ModuleName = std::string(Pos, End - Pos); + Pos = End + 1; + } else { + int NameLength = strcspn(Pos, kDelimiters); + ModuleName = std::string(Pos, NameLength); + Pos += NameLength; + } + } else { ModuleName = BinaryName.str(); - } - // Skip delimiters and parse module offset. - Pos += strspn(Pos, kDelimiters); - int OffsetLength = strcspn(Pos, kDelimiters); - StringRef Offset(Pos, OffsetLength); - // GNU addr2line assumes the offset is hexadecimal and allows a redundant - // "0x" or "0X" prefix; do the same for compatibility. - if (IsAddr2Line) - Offset.consume_front("0x") || Offset.consume_front("0X"); - return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); -} - + } + // Skip delimiters and parse module offset. + Pos += strspn(Pos, kDelimiters); + int OffsetLength = strcspn(Pos, kDelimiters); + StringRef Offset(Pos, OffsetLength); + // GNU addr2line assumes the offset is hexadecimal and allows a redundant + // "0x" or "0X" prefix; do the same for compatibility. + if (IsAddr2Line) + Offset.consume_front("0x") || Offset.consume_front("0X"); + return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); +} + static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, bool IsAddr2Line, DIPrinter::OutputStyle OutputStyle, StringRef InputString, LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { - Command Cmd; - std::string ModuleName; - uint64_t Offset = 0; + Command Cmd; + std::string ModuleName; + uint64_t Offset = 0; if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, StringRef(InputString), Cmd, ModuleName, Offset)) { - outs() << InputString << "\n"; - return; - } - + outs() << InputString << "\n"; + return; + } + if (Args.hasArg(OPT_addresses)) { - outs() << "0x"; - outs().write_hex(Offset); + outs() << "0x"; + outs().write_hex(Offset); StringRef Delimiter = Args.hasArg(OPT_pretty_print) ? ": " : "\n"; - outs() << Delimiter; - } + outs() << Delimiter; + } Offset -= AdjustVMA; - if (Cmd == Command::Data) { - auto ResOrErr = Symbolizer.symbolizeData( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); - } else if (Cmd == Command::Frame) { - auto ResOrErr = Symbolizer.symbolizeFrame( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - if (!error(ResOrErr)) { - for (DILocal Local : *ResOrErr) - Printer << Local; - if (ResOrErr->empty()) - outs() << "??\n"; - } + if (Cmd == Command::Data) { + auto ResOrErr = Symbolizer.symbolizeData( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); + Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); + } else if (Cmd == Command::Frame) { + auto ResOrErr = Symbolizer.symbolizeFrame( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); + if (!error(ResOrErr)) { + for (DILocal Local : *ResOrErr) + Printer << Local; + if (ResOrErr->empty()) + outs() << "??\n"; + } } else if (Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line)) { - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); + auto ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); + Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); } else if (OutputStyle == DIPrinter::OutputStyle::GNU) { // With PrintFunctions == FunctionNameKind::LinkageName (default) // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() - // may override the name of an inlined function with the name of the topmost - // caller function in the inlining chain. This contradicts the existing - // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only - // the topmost function, which suits our needs better. - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); + // may override the name of an inlined function with the name of the topmost + // caller function in the inlining chain. This contradicts the existing + // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only + // the topmost function, which suits our needs better. + auto ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); if (!ResOrErr || ResOrErr->getNumberOfFrames() == 0) { error(ResOrErr); Printer << DILineInfo(); } else { Printer << ResOrErr->getFrame(0); } - } else { - auto ResOrErr = Symbolizer.symbolizeCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); - } + } else { + auto ResOrErr = Symbolizer.symbolizeCode( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); + Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); + } if (OutputStyle == DIPrinter::OutputStyle::LLVM) - outs() << "\n"; -} - + outs() << "\n"; +} + static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, raw_ostream &OS) { const char HelpText[] = " [options] addresses..."; @@ -204,7 +204,7 @@ static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, // TODO Replace this with OptTable API once it adds extrahelp support. OS << "\nPass @FILE as argument to read options from FILE.\n"; } - + static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line, StringSaver &Saver, SymbolizerOptTable &Tbl) { @@ -231,7 +231,7 @@ static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line, cl::PrintVersionMessage(); exit(0); } - + return Args; } @@ -246,9 +246,9 @@ static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { } } else { Value = 0; - } + } } - + static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args, bool IsAddr2Line) { if (Args.hasArg(OPT_functions)) @@ -260,18 +260,18 @@ static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args, .Default(FunctionNameKind::LinkageName); return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName; } - + int main(int argc, char **argv) { InitLLVM X(argc, argv); sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded); - + bool IsAddr2Line = sys::path::stem(argv[0]).contains("addr2line"); BumpPtrAllocator A; StringSaver Saver(A); SymbolizerOptTable Tbl; opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl); - LLVMSymbolizer::Options Opts; + LLVMSymbolizer::Options Opts; uint64_t AdjustVMA; unsigned SourceContextLines; parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA); @@ -302,16 +302,16 @@ int main(int argc, char **argv) { } #endif Opts.UseSymbolTable = true; - + for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) { StringRef Hint(A->getValue()); if (sys::path::extension(Hint) == ".dSYM") { Opts.DsymHints.emplace_back(Hint); - } else { + } else { errs() << "Warning: invalid dSYM hint: \"" << Hint << "\" (must have the '.dSYM' extension).\n"; - } - } + } + } auto OutputStyle = IsAddr2Line ? DIPrinter::OutputStyle::GNU : DIPrinter::OutputStyle::LLVM; @@ -321,30 +321,30 @@ int main(int argc, char **argv) { : DIPrinter::OutputStyle::LLVM; } - LLVMSymbolizer Symbolizer(Opts); + LLVMSymbolizer Symbolizer(Opts); DIPrinter Printer(outs(), Opts.PrintFunctions != FunctionNameKind::None, Args.hasArg(OPT_pretty_print), SourceContextLines, Args.hasArg(OPT_verbose), OutputStyle); - + std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT); if (InputAddresses.empty()) { - const int kMaxInputStringLength = 1024; - char InputString[kMaxInputStringLength]; - - while (fgets(InputString, sizeof(InputString), stdin)) { - // Strip newline characters. - std::string StrippedInputString(InputString); + const int kMaxInputStringLength = 1024; + char InputString[kMaxInputStringLength]; + + while (fgets(InputString, sizeof(InputString), stdin)) { + // Strip newline characters. + std::string StrippedInputString(InputString); llvm::erase_if(StrippedInputString, [](char c) { return c == '\r' || c == '\n'; }); symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, StrippedInputString, Symbolizer, Printer); - outs().flush(); - } - } else { + outs().flush(); + } + } else { for (StringRef Address : InputAddresses) symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, Address, Symbolizer, Printer); - } - - return 0; -} + } + + return 0; +} diff --git a/contrib/libs/llvm12/tools/llvm-symbolizer/ya.make b/contrib/libs/llvm12/tools/llvm-symbolizer/ya.make index 1f13f78269..35bf52aeca 100644 --- a/contrib/libs/llvm12/tools/llvm-symbolizer/ya.make +++ b/contrib/libs/llvm12/tools/llvm-symbolizer/ya.make @@ -1,17 +1,17 @@ -# Generated by devtools/yamaker. - -PROGRAM() - +# Generated by devtools/yamaker. + +PROGRAM() + OWNER( orivej g:cpp-contrib ) - + LICENSE(Apache-2.0 WITH LLVM-exception) - + LICENSE_TEXTS(.yandex_meta/licenses.list.txt) -PEERDIR( +PEERDIR( contrib/libs/llvm12 contrib/libs/llvm12/include contrib/libs/llvm12/lib/BinaryFormat @@ -31,19 +31,19 @@ PEERDIR( contrib/libs/llvm12/lib/Remarks contrib/libs/llvm12/lib/Support contrib/libs/llvm12/lib/TextAPI/MachO -) - -ADDINCL( +) + +ADDINCL( ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm12/tools/llvm-symbolizer contrib/libs/llvm12/tools/llvm-symbolizer -) - -NO_COMPILER_WARNINGS() - -NO_UTIL() - -SRCS( - llvm-symbolizer.cpp -) - -END() +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + llvm-symbolizer.cpp +) + +END() |