Skip to content

Commit

Permalink
llvm-readobj: Add support for reading relocations in the Android pack…
Browse files Browse the repository at this point in the history
…ed format.

This is in preparation for testing lld's upcoming relocation packing
feature (D39152). I have verified that this implementation correctly
unpacks the relocations from a Chromium DSO built with gold and the
Android relocation packer for ARM32 and ARM64.

Differential Revision: https://reviews.llvm.org/D39272

llvm-svn: 316543
  • Loading branch information
pcc committed Oct 25, 2017
1 parent 9f46ece commit 689e6c0
Show file tree
Hide file tree
Showing 14 changed files with 338 additions and 5 deletions.
19 changes: 19 additions & 0 deletions llvm/include/llvm/BinaryFormat/ELF.h
Expand Up @@ -730,6 +730,10 @@ enum : unsigned {
SHT_GROUP = 17, // Section group.
SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries.
SHT_LOOS = 0x60000000, // Lowest operating system-specific type.
// Android packed relocation section types.
// https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#37
SHT_ANDROID_REL = 0x60000001,
SHT_ANDROID_RELA = 0x60000002,
SHT_LLVM_ODRTAB = 0x6fff4c00, // LLVM ODR table.
SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes.
SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table.
Expand Down Expand Up @@ -1166,6 +1170,13 @@ enum {
DT_LOPROC = 0x70000000, // Start of processor specific tags.
DT_HIPROC = 0x7FFFFFFF, // End of processor specific tags.

// Android packed relocation section tags.
// https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#31
DT_ANDROID_REL = 0x6000000F,
DT_ANDROID_RELSZ = 0x60000010,
DT_ANDROID_RELA = 0x60000011,
DT_ANDROID_RELASZ = 0x60000012,

DT_GNU_HASH = 0x6FFFFEF5, // Reference to the GNU hash table.
DT_TLSDESC_PLT =
0x6FFFFEF6, // Location of PLT entry for TLS descriptor resolver calls.
Expand Down Expand Up @@ -1387,6 +1398,14 @@ enum {
GNU_ABI_TAG_NACL = 6,
};

// Android packed relocation group flags.
enum {
RELOCATION_GROUPED_BY_INFO_FLAG = 1,
RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2,
RELOCATION_GROUPED_BY_ADDEND_FLAG = 4,
RELOCATION_GROUP_HAS_ADDEND_FLAG = 8,
};

// Compressed section header for ELF32.
struct Elf32_Chdr {
Elf32_Word ch_type;
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Object/ELF.h
Expand Up @@ -142,6 +142,8 @@ class ELFFile {
return getSectionContentsAsArray<Elf_Rel>(Sec);
}

Expected<std::vector<Elf_Rela>> android_relas(const Elf_Shdr *Sec) const;

/// \brief Iterate over program header table.
Expected<Elf_Phdr_Range> program_headers() const {
if (getHeader()->e_phnum && getHeader()->e_phentsize != sizeof(Elf_Phdr))
Expand Down
90 changes: 90 additions & 0 deletions llvm/lib/Object/ELF.cpp
Expand Up @@ -9,6 +9,7 @@

#include "llvm/Object/ELF.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/LEB128.h"

using namespace llvm;
using namespace object;
Expand Down Expand Up @@ -210,3 +211,92 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
return "Unknown";
}
}

template <class ELFT>
Expected<std::vector<typename ELFT::Rela>>
ELFFile<ELFT>::android_relas(const Elf_Shdr *Sec) const {
// This function reads relocations in Android's packed relocation format,
// which is based on SLEB128 and delta encoding.
Expected<ArrayRef<uint8_t>> ContentsOrErr = getSectionContents(Sec);
if (!ContentsOrErr)
return ContentsOrErr.takeError();
const uint8_t *Cur = ContentsOrErr->begin();
const uint8_t *End = ContentsOrErr->end();
if (ContentsOrErr->size() < 4 || Cur[0] != 'A' || Cur[1] != 'P' ||
Cur[2] != 'S' || Cur[3] != '2')
return createError("invalid packed relocation header");
Cur += 4;

const char *ErrStr = nullptr;
auto ReadSLEB = [&]() -> int64_t {
if (ErrStr)
return 0;
unsigned Len;
int64_t Result = decodeSLEB128(Cur, &Len, End, &ErrStr);
Cur += Len;
return Result;
};

uint64_t NumRelocs = ReadSLEB();
uint64_t Offset = ReadSLEB();
uint64_t Addend = 0;

if (ErrStr)
return createError(ErrStr);

std::vector<Elf_Rela> Relocs;
Relocs.reserve(NumRelocs);
while (NumRelocs) {
uint64_t NumRelocsInGroup = ReadSLEB();
if (NumRelocsInGroup > NumRelocs)
return createError("relocation group unexpectedly large");
NumRelocs -= NumRelocsInGroup;

uint64_t GroupFlags = ReadSLEB();
bool GroupedByInfo = GroupFlags & ELF::RELOCATION_GROUPED_BY_INFO_FLAG;
bool GroupedByOffsetDelta = GroupFlags & ELF::RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG;
bool GroupedByAddend = GroupFlags & ELF::RELOCATION_GROUPED_BY_ADDEND_FLAG;
bool GroupHasAddend = GroupFlags & ELF::RELOCATION_GROUP_HAS_ADDEND_FLAG;

uint64_t GroupOffsetDelta;
if (GroupedByOffsetDelta)
GroupOffsetDelta = ReadSLEB();

uint64_t GroupRInfo;
if (GroupedByInfo)
GroupRInfo = ReadSLEB();

if (GroupedByAddend && GroupHasAddend)
Addend += ReadSLEB();

for (uint64_t I = 0; I != NumRelocsInGroup; ++I) {
Elf_Rela R;
Offset += GroupedByOffsetDelta ? GroupOffsetDelta : ReadSLEB();
R.r_offset = Offset;
R.r_info = GroupedByInfo ? GroupRInfo : ReadSLEB();

if (GroupHasAddend) {
if (!GroupedByAddend)
Addend += ReadSLEB();
R.r_addend = Addend;
} else {
R.r_addend = 0;
}

Relocs.push_back(R);

if (ErrStr)
return createError(ErrStr);
}

if (ErrStr)
return createError(ErrStr);
}

return Relocs;
}

template class llvm::object::ELFFile<ELF32LE>;
template class llvm::object::ELFFile<ELF32BE>;
template class llvm::object::ELFFile<ELF64LE>;
template class llvm::object::ELFFile<ELF64BE>;
2 changes: 2 additions & 0 deletions llvm/lib/ObjectYAML/ELFYAML.cpp
Expand Up @@ -405,6 +405,8 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration(
ECase(SHT_GROUP);
ECase(SHT_SYMTAB_SHNDX);
ECase(SHT_LOOS);
ECase(SHT_ANDROID_REL);
ECase(SHT_ANDROID_RELA);
ECase(SHT_LLVM_ODRTAB);
ECase(SHT_GNU_ATTRIBUTES);
ECase(SHT_GNU_HASH);
Expand Down
37 changes: 37 additions & 0 deletions llvm/test/tools/llvm-readobj/Inputs/elf-packed-relocs1.s
@@ -0,0 +1,37 @@
.ascii "APS2"
.sleb128 8 // Number of relocations
.sleb128 4096 // Initial offset

.sleb128 2 // Number of relocations in group
.sleb128 1 // RELOCATION_GROUPED_BY_INFO_FLAG
.sleb128 8 // R_X86_RELATIVE

.sleb128 256 // Reloc 1: r_offset delta
.sleb128 128 // Reloc 2: r_offset delta

.sleb128 2 // Number of relocations in group
.sleb128 2 // RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG
.sleb128 8 // offset delta

.sleb128 (1 << 32) | 1 // R_X86_64_64 (sym index 1)
.sleb128 (2 << 32) | 1 // R_X86_64_64 (sym index 2)

.sleb128 2 // Number of relocations in group
.sleb128 8 // RELOCATION_GROUP_HAS_ADDEND_FLAG

.sleb128 1 // offset delta
.sleb128 (1 << 32) | 1 // R_X86_64_64 (sym index 1)
.sleb128 8 // addend delta

.sleb128 2 // offset delta
.sleb128 (2 << 32) | 1 // R_X86_64_64 (sym index 2)
.sleb128 4 // addend delta

.sleb128 2 // Number of relocations in group
.sleb128 12 // RELOCATION_GROUP_HAS_ADDEND_FLAG | RELOCATION_GROUPED_BY_ADDEND_FLAG
.sleb128 -2 // addend delta

.sleb128 4 // offset delta
.sleb128 (1 << 32) | 1 // R_X86_64_64 (sym index 1)
.sleb128 8 // offset delta
.sleb128 (2 << 32) | 1 // R_X86_64_64 (sym index 2)
15 changes: 15 additions & 0 deletions llvm/test/tools/llvm-readobj/Inputs/elf-packed-relocs2.s
@@ -0,0 +1,15 @@
.ascii "APS2"
.sleb128 10 // Number of relocations
.sleb128 4096 // Initial offset

.sleb128 2 // Number of relocations in group
.sleb128 2 // RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG
.sleb128 8 // offset delta

.sleb128 (1 << 8) | 1 // R_386_32 (sym index 1)
.sleb128 (2 << 8) | 3 // R_386_GOT32 (sym index 2)

.sleb128 8 // Number of relocations in group
.sleb128 3 // RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG | RELOCATION_GROUPED_BY_INFO_FLAG
.sleb128 -4 // offset delta
.sleb128 8 // R_386_RELATIVE
11 changes: 11 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs-empty.s
@@ -0,0 +1,11 @@
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readobj -relocations - | FileCheck %s

// CHECK: Relocations [
// CHECK-NEXT: Section (3) .rela.dyn {
// CHECK-NEXT: }
// CHECK-NEXT: ]

.section .rela.dyn, "a", @0x60000001
.ascii "APS2"
.sleb128 0
.sleb128 0
6 changes: 6 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs-error1.s
@@ -0,0 +1,6 @@
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | not llvm-readobj -relocations - 2>&1 | FileCheck %s

// CHECK: Error reading file: invalid packed relocation header

.section .rela.dyn, "a", @0x60000001
.ascii "APS9"
6 changes: 6 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs-error2.s
@@ -0,0 +1,6 @@
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | not llvm-readobj -relocations - 2>&1 | FileCheck %s

// CHECK: Error reading file: malformed sleb128, extends past end

.section .rela.dyn, "a", @0x60000001
.ascii "APS2"
8 changes: 8 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs-error3.s
@@ -0,0 +1,8 @@
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | not llvm-readobj -relocations - 2>&1 | FileCheck %s

// CHECK: Error reading file: malformed sleb128, extends past end

.section .rela.dyn, "a", @0x60000001
.ascii "APS2"
.sleb128 4 // Number of relocations
.sleb128 0 // Initial offset
12 changes: 12 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs-error4.s
@@ -0,0 +1,12 @@
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | not llvm-readobj -relocations - 2>&1 | FileCheck %s

// CHECK: Error reading file: malformed sleb128, extends past end

.section .rela.dyn, "a", @0x60000001
.ascii "APS2"
.sleb128 4 // Number of relocations
.sleb128 0 // Initial offset

.sleb128 2 // Number of relocations in group
.sleb128 2 // RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG
.sleb128 8 // offset delta
12 changes: 12 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs-error5.s
@@ -0,0 +1,12 @@
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | not llvm-readobj -relocations - 2>&1 | FileCheck %s

// CHECK: Error reading file: relocation group unexpectedly large

.section .rela.dyn, "a", @0x60000001
.ascii "APS2"
.sleb128 4 // Number of relocations
.sleb128 0 // Initial offset

.sleb128 5 // Number of relocations in group
.sleb128 2 // RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG
.sleb128 8 // offset delta
94 changes: 94 additions & 0 deletions llvm/test/tools/llvm-readobj/elf-packed-relocs.test
@@ -0,0 +1,94 @@
# The binary blobs in this file were created like this:
# llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu Inputs/elf-packed-relocs1.s -o - | obj2yaml | grep Content:

# RUN: yaml2obj -docnum 1 %s | llvm-readobj -elf-output-style=LLVM -relocations - | FileCheck --check-prefix=LLVM1 %s
# LLVM1: Section (1) .rela.dyn {
# LLVM1-NEXT: 0x1100 R_X86_64_RELATIVE - 0x0
# LLVM1-NEXT: 0x1180 R_X86_64_RELATIVE - 0x0
# LLVM1-NEXT: 0x1188 R_X86_64_64 sym1 0x0
# LLVM1-NEXT: 0x1190 R_X86_64_64 sym2 0x0
# LLVM1-NEXT: 0x1191 R_X86_64_64 sym1 0x8
# LLVM1-NEXT: 0x1193 R_X86_64_64 sym2 0xC
# LLVM1-NEXT: 0x1197 R_X86_64_64 sym1 0xA
# LLVM1-NEXT: 0x119F R_X86_64_64 sym2 0xA
# LLVM1-NEXT: }

# RUN: yaml2obj -docnum 1 %s | llvm-readobj -elf-output-style=GNU -relocations - | FileCheck --check-prefix=GNU1 %s
# GNU1: 0000000000001100 0000000000000008 R_X86_64_RELATIVE 0
# GNU1-NEXT: 0000000000001180 0000000000000008 R_X86_64_RELATIVE 0
# GNU1-NEXT: 0000000000001188 0000000100000001 R_X86_64_64 0000000000000000 sym1 + 0
# GNU1-NEXT: 0000000000001190 0000000200000001 R_X86_64_64 0000000000000000 sym2 + 0
# GNU1-NEXT: 0000000000001191 0000000100000001 R_X86_64_64 0000000000000000 sym1 + 8
# GNU1-NEXT: 0000000000001193 0000000200000001 R_X86_64_64 0000000000000000 sym2 + c
# GNU1-NEXT: 0000000000001197 0000000100000001 R_X86_64_64 0000000000000000 sym1 + a
# GNU1-NEXT: 000000000000119f 0000000200000001 R_X86_64_64 0000000000000000 sym2 + a

# elf-packed-relocs1.s
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_X86_64
Entry: 0x0000000000001000
Sections:
- Name: .rela.dyn
Type: SHT_ANDROID_RELA
Flags: [ SHF_ALLOC ]
Address: 0x00000000000001C8
Link: .symtab
AddressAlign: 0x0000000000000001
Content: 41505332088020020108800280010202088180808010818080802002080181808080100802818080802004020C7E048180808010088180808020
Symbols:
Global:
- Name: sym1
- Name: sym2
...

# RUN: yaml2obj -docnum 2 %s | llvm-readobj -elf-output-style=LLVM -relocations - | FileCheck --check-prefix=LLVM2 %s
# LLVM2: Section (1) .rel.dyn {
# LLVM2-NEXT: 0x1008 R_386_32 sym1 0x0
# LLVM2-NEXT: 0x1010 R_386_GOT32 sym2 0x0
# LLVM2-NEXT: 0x100C R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0x1008 R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0x1004 R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0x1000 R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0xFFC R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0xFF8 R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0xFF4 R_386_RELATIVE - 0x0
# LLVM2-NEXT: 0xFF0 R_386_RELATIVE - 0x0
# LLVM2-NEXT: }

# RUN: yaml2obj -docnum 2 %s | llvm-readobj -elf-output-style=GNU -relocations - | FileCheck --check-prefix=GNU2 %s
# GNU2: 00001008 00000101 R_386_32 00000000 sym1
# GNU2-NEXT: 00001010 00000203 R_386_GOT32 00000000 sym2
# GNU2-NEXT: 0000100c 00000008 R_386_RELATIVE
# GNU2-NEXT: 00001008 00000008 R_386_RELATIVE
# GNU2-NEXT: 00001004 00000008 R_386_RELATIVE
# GNU2-NEXT: 00001000 00000008 R_386_RELATIVE
# GNU2-NEXT: 00000ffc 00000008 R_386_RELATIVE
# GNU2-NEXT: 00000ff8 00000008 R_386_RELATIVE
# GNU2-NEXT: 00000ff4 00000008 R_386_RELATIVE
# GNU2-NEXT: 00000ff0 00000008 R_386_RELATIVE

# elf-packed-relocs2.s
--- !ELF
FileHeader:
Class: ELFCLASS32
Data: ELFDATA2LSB
Type: ET_DYN
Machine: EM_386
Entry: 0x0000000000001000
Sections:
- Name: .rel.dyn
Type: SHT_ANDROID_REL
Flags: [ SHF_ALLOC ]
Address: 0x00000000000001C8
Link: .symtab
AddressAlign: 0x0000000000000001
Content: 415053320A80200202088102830408037C08
Symbols:
Global:
- Name: sym1
- Name: sym2
...

0 comments on commit 689e6c0

Please sign in to comment.