Index: llvm/include/llvm/Object/MutableELFObject.h =================================================================== --- /dev/null +++ llvm/include/llvm/Object/MutableELFObject.h @@ -0,0 +1,182 @@ +//===-- MutableELFObject.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_OBJECT_MUTABLEELFOBJECT_H +#define LLVM_OBJECT_MUTABLEELFOBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MutableObject.h" + +namespace llvm { +namespace object { + +template class MutableELFObject; + +template static inline DataRefImpl toDataRef(T Ptr) { + DataRefImpl Ref; + Ref.p = (uintptr_t)Ptr; + return Ref; +} + +template class MutableELFSection { +public: + Elf_Shdr_Impl Header; + std::string Name; + OwningArrayRef Data; + + MutableELFSection(uintptr_t ToCopy, const MutableELFObject *ObjFile) + : Header(*ObjFile->getSection(toDataRef(ToCopy))), + Data(OwningArrayRef(Header.sh_size)) { + ::memcpy(Data.data(), ObjFile->base() + Header.sh_offset, Header.sh_size); + } + + void updateData(ArrayRef Ref) { + Data = OwningArrayRef(Ref); + Header.sh_size = Data.size(); + } +}; + +template class MutableELFObject : public ELFObjectFile { + friend class MutableELFSection; + + MutableRange> Sections; + +protected: + using MappingType = + typename MutableRange>::MappingType; + + // Returns DataRef with pointer to the correct section header. + DataRefImpl getSectionRef(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + return Mapping.New ? toDataRef(&Sections.getNew(Mapping.Ptr)->Header) + : toDataRef(Mapping.Ptr); + } + + void moveSectionNext(DataRefImpl &Sec) const override; + Expected getSectionName(DataRefImpl Sec) const override; + uint64_t getSectionAddress(DataRefImpl Sec) const override; + uint64_t getSectionIndex(DataRefImpl Sec) const override; + uint64_t getSectionSize(DataRefImpl Sec) const override; + Expected> + getSectionContents(DataRefImpl Sec) const override; + uint64_t getSectionAlignment(DataRefImpl Sec) const override; + bool isSectionCompressed(DataRefImpl Sec) const override; + bool isSectionText(DataRefImpl Sec) const override; + bool isSectionData(DataRefImpl Sec) const override; + bool isSectionBSS(DataRefImpl Sec) const override; + bool isSectionVirtual(DataRefImpl Sec) const override; + bool isBerkeleyText(DataRefImpl Sec) const override; + bool isBerkeleyData(DataRefImpl Sec) const override; + +public: + MutableELFObject(ELFObjectFile &B) + : ELFObjectFile(std::move(B)), + Sections(B.section_begin(), B.section_end(), + [&](SectionRef Ref) { return Ref.getRawDataRefImpl().p; }) {} + + section_iterator section_begin() const override { + return section_iterator(SectionRef(toDataRef(0), this)); + } + + section_iterator section_end() const override { + return section_iterator(SectionRef(toDataRef(Sections.size()), this)); + } + + MutableELFSection *getMutableSection(section_iterator Sec) { + uintptr_t Index = Sec->getRawDataRefImpl().p; + return Sections.makeMutable(Index, this); + } +}; + +template +void MutableELFObject::moveSectionNext(DataRefImpl &Sec) const { + ++Sec.p; +} + +template +Expected +MutableELFObject::getSectionName(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + if (Mapping.New) { + const MutableELFSection *NewSec = Sections.getNew(Mapping.Ptr); + return NewSec->Name; + } + return ELFObjectFile::getSectionName(toDataRef(Mapping.Ptr)); +} + +template +uint64_t MutableELFObject::getSectionAddress(DataRefImpl Sec) const { + return ELFObjectFile::getSectionAddress(getSectionRef(Sec)); +} + +template +uint64_t MutableELFObject::getSectionIndex(DataRefImpl Sec) const { + return Sec.p; +} + +template +uint64_t MutableELFObject::getSectionSize(DataRefImpl Sec) const { + return ELFObjectFile::getSectionSize(getSectionRef(Sec)); +} + +template +Expected> +MutableELFObject::getSectionContents(DataRefImpl Sec) const { + MappingType Mapping = Sections[Sec.p]; + if (Mapping.New) { + const MutableELFSection *NewSec = Sections.getNew(Mapping.Ptr); + return ArrayRef(NewSec->Data.data(), NewSec->Header.sh_size); + } + return ELFObjectFile::getSectionContents(toDataRef(Mapping.Ptr)); +} + +template +uint64_t MutableELFObject::getSectionAlignment(DataRefImpl Sec) const { + return ELFObjectFile::getSectionAlignment(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionCompressed(DataRefImpl Sec) const { + return ELFObjectFile::isSectionCompressed(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionText(DataRefImpl Sec) const { + return ELFObjectFile::isSectionText(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionData(DataRefImpl Sec) const { + return ELFObjectFile::isSectionData(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionBSS(DataRefImpl Sec) const { + return ELFObjectFile::isSectionBSS(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isSectionVirtual(DataRefImpl Sec) const { + return ELFObjectFile::isSectionVirtual(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isBerkeleyText(DataRefImpl Sec) const { + return ELFObjectFile::isBerkeleyText(getSectionRef(Sec)); +} + +template +bool MutableELFObject::isBerkeleyData(DataRefImpl Sec) const { + return ELFObjectFile::isBerkeleyData(getSectionRef(Sec)); +} + +} // namespace object +} // namespace llvm + +#endif // LLVM_OBJECT_MUTABLEELFOBJECT_H Index: llvm/include/llvm/Object/MutableObject.h =================================================================== --- /dev/null +++ llvm/include/llvm/Object/MutableObject.h @@ -0,0 +1,78 @@ +//===-- MutableObject.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_OBJECT_MUTABLEOBJECT_H +#define LLVM_OBJECT_MUTABLEOBJECT_H + +namespace llvm { +namespace object { + +// T is a wrapper type around Iterable::value_type and must have a constructor +// taking an Iterable::value_type. +template class MutableRange { +public: + struct MappingType { + bool New : 1; + uintptr_t Ptr : sizeof(void *) * 8 - 1; + + MappingType(bool New, uintptr_t Ptr) : New(New), Ptr(Ptr) {} + + uintptr_t getPtr() const { return Ptr; } + }; + + using iterator = typename std::vector::iterator; + using value_type = MappingType; + +private: + std::vector Mappings; + std::vector NewValues; + +public: + template + MutableRange( + Iterable Range, + function_ref Extract) + : MutableRange(Range.begin(), Range.end(), Extract) {} + + template + MutableRange(Iter Begin, Iter End, + function_ref Extract) { + std::transform(Begin, End, std::back_inserter(Mappings), + [&Extract](typename Iter::value_type Value) -> MappingType { + uintptr_t Extracted = Extract(Value); + assert(Extracted <= (UINTPTR_MAX >> 1) && + "returned type too large"); + return MappingType(false, Extracted); + }); + } + + MappingType operator[](uint64_t Index) const { return Mappings[Index]; } + + size_t size() const { return Mappings.size(); } + + iterator begin() { return Mappings.begin(); } + iterator end() { return Mappings.end(); } + + const T *getNew(uint64_t Index) const { return &NewValues[Index]; } + T *getNew(uint64_t Index) { return &NewValues[Index]; } + + template + T *makeMutable(uint64_t Index, Args &&... Arguments) { + MappingType Mapping = Mappings[Index]; + if (Mapping.New) + return &NewValues[reinterpret_cast(Mapping.Ptr)]; + NewValues.emplace_back(Mapping.getPtr(), Arguments...); + Mappings[Index] = MappingType(true, NewValues.size() - 1); + return &NewValues.back(); + } +}; + +} // namespace object +} // namespace llvm + +#endif // LVM_OBJECT_MUTABLEOBJECT_H Index: llvm/unittests/Object/CMakeLists.txt =================================================================== --- llvm/unittests/Object/CMakeLists.txt +++ llvm/unittests/Object/CMakeLists.txt @@ -1,10 +1,12 @@ set(LLVM_LINK_COMPONENTS BinaryFormat Object + ObjectYAML ) add_llvm_unittest(ObjectTests MinidumpTest.cpp + MutableObjectTest.cpp SymbolSizeTest.cpp SymbolicFileTest.cpp ) Index: llvm/unittests/Object/MutableObjectTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Object/MutableObjectTest.cpp @@ -0,0 +1,120 @@ +//===- MutableObjectTest.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 "llvm/ADT/SmallString.h" +#include "llvm/Object/MutableELFObject.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace object; +using namespace yaml; + +TEST(MutableELFObject, ChangeSectionName) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .sec0 + Type: SHT_PROGBITS + - Name: .sec1 + Type: SHT_PROGBITS + - Name: .sec2 + Type: SHT_PROGBITS)"); + + ASSERT_TRUE(!!ErrOrObj); + + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + + ASSERT_TRUE(ELFObjFile); + + MutableELFObject MutableObject(*ELFObjFile); + + auto getSectionName = [](section_iterator Iter) -> const char * { + StringRef Name; + Iter->getName(Name); + return Name.data(); + }; + + auto Iter = MutableObject.section_begin(); + EXPECT_STREQ(getSectionName(Iter), nullptr); + EXPECT_STREQ(getSectionName(++Iter), ".sec0"); + EXPECT_STREQ(getSectionName(++Iter), ".sec1"); + EXPECT_STREQ(getSectionName(++Iter), ".sec2"); + + Iter = MutableObject.section_begin(); + std::advance(Iter, 2); + auto *MutSection = MutableObject.getMutableSection(Iter); + MutSection->Name = ".new_name"; + + Iter = MutableObject.section_begin(); + EXPECT_STREQ(getSectionName(Iter), nullptr); + EXPECT_STREQ(getSectionName(++Iter), ".sec0"); + EXPECT_STREQ(getSectionName(++Iter), ".new_name"); + EXPECT_STREQ(getSectionName(++Iter), ".sec2"); +} + +TEST(MutableELFObject, ChangeSectionContents) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "DEADBEEF")"); + + ASSERT_TRUE(!!ErrOrObj); + + auto *ELFObjFile = dyn_cast>(ErrOrObj->get()); + + ASSERT_TRUE(ELFObjFile); + + MutableELFObject MutableObject(*ELFObjFile); + + auto FirstSec = ++MutableObject.section_begin(); + Expected Contents = FirstSec->getContents(); + ASSERT_TRUE(!!Contents); + + // Might not always be 0 after StringRef's data so EXPECT_STREQ isn't safe. + EXPECT_FALSE(::strncmp(Contents->data(), "\xDE\xAD\xBE\xEF", 4)); + EXPECT_EQ(FirstSec->getSize(), 4U); + + auto *MutSec = MutableObject.getMutableSection(FirstSec); + MutSec->updateData(ArrayRef((const uint8_t *)"0000", 5)); + + FirstSec = ++MutableObject.section_begin(); + Contents = FirstSec->getContents(); + ASSERT_TRUE(!!Contents); + + // streq safe to use because the ArrayRef was created from size 5 string so + // it includes the 0 byte at the end. + EXPECT_STREQ(Contents->data(), "0000"); + + MutSec->Header.sh_size = 2; + Contents = FirstSec->getContents(); + ASSERT_TRUE(!!Contents); + + // check that getSize properly uses the headers sh_size value. + EXPECT_EQ(FirstSec->getSize(), 2UL); + + // Check that the StringRef has size 2 because headers sh_size was changed. + EXPECT_EQ(Contents->size(), 2UL); +} Index: llvm/utils/benchmark/CMakeLists.txt =================================================================== --- llvm/utils/benchmark/CMakeLists.txt +++ llvm/utils/benchmark/CMakeLists.txt @@ -95,9 +95,9 @@ include(AddCXXCompilerFlag) include(CXXFeatureCheck) -if (BENCHMARK_BUILD_32_BITS) - add_required_cxx_compiler_flag(-m32) -endif() +#if (BENCHMARK_BUILD_32_BITS) + #add_required_cxx_compiler_flag(-m32) +#endif() if (MSVC) # Turn compiler warnings up to 11