Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c8ac562 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Fix static GetLoadBias function.

The load bias value set in ReadProgramHeaders is out of sync with the
algorithm used in the static GetLoadBias function.

Sync the two and add tests to verify that they stay in sync.

Test: Unit tests pass.
Change-Id: I20ac0104970a22a92a5314a41dcadad0c9c22e64
parent c63ef7fc
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -187,8 +187,13 @@ uint64_t ElfInterface::GetLoadBias(Memory* memory) {
    if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
      return 0;
    }
    if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
      return phdr.p_vaddr;

    // Find the first executable load when looking for the load bias.
    if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
      if (phdr.p_vaddr > phdr.p_offset) {
        return phdr.p_vaddr - phdr.p_offset;
      }
      break;
    }
  }
  return 0;
+124 −0
Original line number Diff line number Diff line
@@ -131,6 +131,12 @@ class ElfInterfaceTest : public ::testing::Test {
  template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
  void BuildIDSectionTooSmallForHeader();

  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
  void CheckLoadBiasInFirstPhdr(uint64_t load_bias);

  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
  void CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr, uint64_t load_bias);

  MemoryFake memory_;
};

@@ -1495,4 +1501,122 @@ TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
  BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
}

template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
void ElfInterfaceTest::CheckLoadBiasInFirstPhdr(uint64_t load_bias) {
  Ehdr ehdr = {};
  ehdr.e_phoff = 0x100;
  ehdr.e_phnum = 2;
  ehdr.e_phentsize = sizeof(Phdr);
  memory_.SetMemory(0, &ehdr, sizeof(ehdr));

  Phdr phdr = {};
  phdr.p_type = PT_LOAD;
  phdr.p_offset = 0;
  phdr.p_vaddr = load_bias;
  phdr.p_memsz = 0x10000;
  phdr.p_flags = PF_R | PF_X;
  phdr.p_align = 0x1000;
  memory_.SetMemory(0x100, &phdr, sizeof(phdr));

  memset(&phdr, 0, sizeof(phdr));
  phdr.p_type = PT_LOAD;
  phdr.p_offset = 0x1000;
  phdr.p_memsz = 0x2000;
  phdr.p_flags = PF_R | PF_X;
  phdr.p_align = 0x1000;
  memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));

  uint64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
  ASSERT_EQ(load_bias, static_load_bias);

  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
  uint64_t init_load_bias = 0;
  ASSERT_TRUE(elf->Init(&init_load_bias));
  ASSERT_EQ(init_load_bias, static_load_bias);
}

TEST_F(ElfInterfaceTest, get_load_bias_zero_32) {
  CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0);
}

TEST_F(ElfInterfaceTest, get_load_bias_zero_64) {
  CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0);
}

TEST_F(ElfInterfaceTest, get_load_bias_non_zero_32) {
  CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000);
}

TEST_F(ElfInterfaceTest, get_load_bias_non_zero_64) {
  CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000);
}

template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
void ElfInterfaceTest::CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr,
                                                    uint64_t load_bias) {
  Ehdr ehdr = {};
  ehdr.e_phoff = 0x100;
  ehdr.e_phnum = 3;
  ehdr.e_phentsize = sizeof(Phdr);
  memory_.SetMemory(0, &ehdr, sizeof(ehdr));

  Phdr phdr = {};
  phdr.p_type = PT_LOAD;
  phdr.p_memsz = 0x10000;
  phdr.p_flags = PF_R;
  phdr.p_align = 0x1000;
  memory_.SetMemory(0x100, &phdr, sizeof(phdr));

  memset(&phdr, 0, sizeof(phdr));
  phdr.p_type = PT_LOAD;
  phdr.p_offset = offset;
  phdr.p_vaddr = vaddr;
  phdr.p_memsz = 0x2000;
  phdr.p_flags = PF_R | PF_X;
  phdr.p_align = 0x1000;
  memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));

  // Second executable load should be ignored for load bias computation.
  memset(&phdr, 0, sizeof(phdr));
  phdr.p_type = PT_LOAD;
  phdr.p_offset = 0x1234;
  phdr.p_vaddr = 0x2000;
  phdr.p_memsz = 0x2000;
  phdr.p_flags = PF_R | PF_X;
  phdr.p_align = 0x1000;
  memory_.SetMemory(0x200 + sizeof(phdr), &phdr, sizeof(phdr));

  uint64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
  ASSERT_EQ(load_bias, static_load_bias);

  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
  uint64_t init_load_bias = 0;
  ASSERT_TRUE(elf->Init(&init_load_bias));
  ASSERT_EQ(init_load_bias, static_load_bias);
}

TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_32) {
  CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x1000, 0);
}

TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_64) {
  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x1000, 0);
}

TEST_F(ElfInterfaceTest, get_load_bias_exec_non_zero_32) {
  CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x4000, 0x3000);
}

TEST_F(ElfInterfaceTest, get_load_bias_exec_non_zero_64) {
  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x4000, 0x3000);
}

TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_from_error_32) {
  CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x5000, 0x1000, 0);
}

TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_from_error_64) {
  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, 0);
}

}  // namespace unwindstack
+1 −0
Original line number Diff line number Diff line
@@ -141,6 +141,7 @@ static void InitElfData(MemoryFake* memory, uint64_t offset) {
  phdr.p_type = PT_NULL;
  memory->SetMemory(offset + 0x5000, &phdr, sizeof(phdr));
  phdr.p_type = PT_LOAD;
  phdr.p_flags = PF_X;
  phdr.p_offset = 0;
  phdr.p_vaddr = 0xe000;
  memory->SetMemory(offset + 0x5000 + sizeof(phdr), &phdr, sizeof(phdr));