mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 04:06:46 +00:00

`addPendingRelocation` is the only way to add a pending relocation. Can no longer use `addRelocation` for this. Update the only user (`BinaryContextTester`).
223 lines
7.8 KiB
C++
223 lines
7.8 KiB
C++
//===- bolt/unittest/Core/BinaryContext.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 "bolt/Core/BinaryContext.h"
|
|
#include "llvm/BinaryFormat/ELF.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::object;
|
|
using namespace llvm::ELF;
|
|
using namespace bolt;
|
|
|
|
namespace {
|
|
struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
|
|
void SetUp() override {
|
|
initalizeLLVM();
|
|
prepareElf();
|
|
initializeBOLT();
|
|
}
|
|
|
|
protected:
|
|
void initalizeLLVM() {
|
|
llvm::InitializeAllTargetInfos();
|
|
llvm::InitializeAllTargetMCs();
|
|
llvm::InitializeAllAsmParsers();
|
|
llvm::InitializeAllDisassemblers();
|
|
llvm::InitializeAllTargets();
|
|
llvm::InitializeAllAsmPrinters();
|
|
}
|
|
|
|
void prepareElf() {
|
|
memcpy(ElfBuf, "\177ELF", 4);
|
|
ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
|
|
EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
|
|
EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
|
|
EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
|
|
MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
|
|
ObjFile = cantFail(ObjectFile::createObjectFile(Source));
|
|
}
|
|
|
|
void initializeBOLT() {
|
|
Relocation::Arch = ObjFile->makeTriple().getArch();
|
|
BC = cantFail(BinaryContext::createBinaryContext(
|
|
ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
|
|
ObjFile->getFileName(), nullptr, true,
|
|
DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
|
|
ASSERT_FALSE(!BC);
|
|
}
|
|
|
|
char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
|
|
std::unique_ptr<ObjectFile> ObjFile;
|
|
std::unique_ptr<BinaryContext> BC;
|
|
};
|
|
} // namespace
|
|
|
|
#ifdef X86_AVAILABLE
|
|
|
|
INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,
|
|
::testing::Values(Triple::x86_64));
|
|
|
|
#endif
|
|
|
|
#ifdef AARCH64_AVAILABLE
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester,
|
|
::testing::Values(Triple::aarch64));
|
|
|
|
TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
|
|
if (GetParam() != Triple::aarch64)
|
|
GTEST_SKIP();
|
|
|
|
// This test checks that encodeValueAArch64 used by flushPendingRelocations
|
|
// returns correctly encoded values for CALL26 relocation for both backward
|
|
// and forward branches.
|
|
//
|
|
// The offsets layout is:
|
|
// 4: func1
|
|
// 8: bl func1
|
|
// 12: bl func2
|
|
// 16: func2
|
|
|
|
constexpr size_t DataSize = 20;
|
|
uint8_t *Data = new uint8_t[DataSize];
|
|
BinarySection &BS = BC->registerOrUpdateSection(
|
|
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data,
|
|
DataSize, 4);
|
|
MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
|
|
ASSERT_TRUE(RelSymbol1);
|
|
BS.addPendingRelocation(
|
|
Relocation{8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0});
|
|
MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
|
|
ASSERT_TRUE(RelSymbol2);
|
|
BS.addPendingRelocation(
|
|
Relocation{12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0});
|
|
|
|
SmallVector<char> Vect(DataSize);
|
|
raw_svector_ostream OS(Vect);
|
|
|
|
BS.flushPendingRelocations(OS, [&](const MCSymbol *S) {
|
|
return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
|
|
});
|
|
|
|
const uint8_t Func1Call[4] = {255, 255, 255, 151};
|
|
const uint8_t Func2Call[4] = {1, 0, 0, 148};
|
|
|
|
EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n";
|
|
EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n";
|
|
}
|
|
|
|
TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
|
|
if (GetParam() != Triple::aarch64)
|
|
GTEST_SKIP();
|
|
|
|
// This test checks that encodeValueAArch64 used by flushPendingRelocations
|
|
// returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
|
|
// backward and forward branches.
|
|
//
|
|
// The offsets layout is:
|
|
// 4: func1
|
|
// 8: b func1
|
|
// 12: b func2
|
|
// 16: func2
|
|
|
|
const uint64_t Size = 20;
|
|
char *Data = new char[Size];
|
|
BinarySection &BS = BC->registerOrUpdateSection(
|
|
".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,
|
|
(uint8_t *)Data, Size, 4);
|
|
MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
|
|
ASSERT_TRUE(RelSymbol1);
|
|
BS.addPendingRelocation(
|
|
Relocation{8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0});
|
|
MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
|
|
ASSERT_TRUE(RelSymbol2);
|
|
BS.addPendingRelocation(
|
|
Relocation{12, RelSymbol2, ELF::R_AARCH64_JUMP26, 0, 0});
|
|
|
|
SmallVector<char> Vect(Size);
|
|
raw_svector_ostream OS(Vect);
|
|
|
|
BS.flushPendingRelocations(OS, [&](const MCSymbol *S) {
|
|
return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
|
|
});
|
|
|
|
const uint8_t Func1Call[4] = {255, 255, 255, 23};
|
|
const uint8_t Func2Call[4] = {1, 0, 0, 20};
|
|
|
|
EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4))
|
|
<< "Wrong backward branch value\n";
|
|
EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4))
|
|
<< "Wrong forward branch value\n";
|
|
}
|
|
|
|
#endif
|
|
|
|
TEST_P(BinaryContextTester, BaseAddress) {
|
|
// Check that base address calculation is correct for a binary with the
|
|
// following segment layout:
|
|
BC->SegmentMapInfo[0] =
|
|
SegmentInfo{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000, true};
|
|
BC->SegmentMapInfo[0x10e8d2b4] =
|
|
SegmentInfo{0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000, true};
|
|
BC->SegmentMapInfo[0x4a3bddc0] =
|
|
SegmentInfo{0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000, true};
|
|
BC->SegmentMapInfo[0x4b84d5e8] =
|
|
SegmentInfo{0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000, true};
|
|
|
|
std::optional<uint64_t> BaseAddress =
|
|
BC->getBaseAddressForMapping(0x7f13f5556000, 0x10e8c000);
|
|
ASSERT_TRUE(BaseAddress.has_value());
|
|
ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);
|
|
|
|
BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 0x137a000);
|
|
ASSERT_FALSE(BaseAddress.has_value());
|
|
}
|
|
|
|
TEST_P(BinaryContextTester, BaseAddress2) {
|
|
// Check that base address calculation is correct for a binary if the
|
|
// alignment in ELF file are different from pagesize.
|
|
// The segment layout is as follows:
|
|
BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000, true};
|
|
BC->SegmentMapInfo[0x31860] =
|
|
SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000, true};
|
|
BC->SegmentMapInfo[0x41c20] =
|
|
SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000, true};
|
|
BC->SegmentMapInfo[0x54e18] =
|
|
SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000, true};
|
|
|
|
std::optional<uint64_t> BaseAddress =
|
|
BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
|
|
ASSERT_TRUE(BaseAddress.has_value());
|
|
ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
|
|
|
|
BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
|
|
ASSERT_FALSE(BaseAddress.has_value());
|
|
}
|
|
|
|
TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {
|
|
// Check that the correct segment is used to compute the base address
|
|
// when multiple segments are close together in the ELF file (closer
|
|
// than the required alignment in the process space).
|
|
// See https://github.com/llvm/llvm-project/issues/109384
|
|
BC->SegmentMapInfo[0] = SegmentInfo{0, 0x1d1c, 0, 0x1d1c, 0x10000, false};
|
|
BC->SegmentMapInfo[0x11d40] =
|
|
SegmentInfo{0x11d40, 0x11e0, 0x1d40, 0x11e0, 0x10000, true};
|
|
BC->SegmentMapInfo[0x22f20] =
|
|
SegmentInfo{0x22f20, 0x10e0, 0x2f20, 0x1f0, 0x10000, false};
|
|
BC->SegmentMapInfo[0x33110] =
|
|
SegmentInfo{0x33110, 0x89, 0x3110, 0x88, 0x10000, false};
|
|
|
|
std::optional<uint64_t> BaseAddress =
|
|
BC->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);
|
|
ASSERT_TRUE(BaseAddress.has_value());
|
|
ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);
|
|
}
|