[CSSPGO][llvm-profgen] Allow multiple executable load segments.

The linker or post-link optimizer can create an ELF image with multiple executable segments each of which will be loaded separately at run time. This breaks the assumption of llvm-profgen that currently only supports one base load address. What it ends up with is that the subsequent mmap events will be treated as an overwrite of the first mmap event which will in turn screw up address mapping. While it is non-trivial to support multiple separate load addresses and given that on x64 those segments will always be loaded at consecutive addresses (though via separate mmap
sys calls), I'm adding an error checking logic to bail out if that's violated and keep using a single load address which is the address of the first executable segment.

Also changing the disassembly output from printing section offset to printing the virtual address instead, which matches the behavior of objdump.

Differential Revision: https://reviews.llvm.org/D103178
This commit is contained in:
Hongtao Yu 2021-07-12 09:47:05 -07:00
parent 35ce66330a
commit 0712038458
13 changed files with 218 additions and 236 deletions

Binary file not shown.

View File

@ -0,0 +1,10 @@
PERF_RECORD_MMAP2 3476378/3476378: [0x201000(0x1000) @ 0 00:23 51215901 72455]: r-xp multi-load-segs.perfbin
PERF_RECORD_MMAP2 3476378/3476378: [0x202000(0x1000) @ 0x1000 00:23 51215901 72455]: r-xp multi-load-segs.perfbin
PERF_RECORD_MMAP2 3476378/3476378: [0x400000(0x1000) @ 0x200000 00:23 51215901 72455]: r-xp multi-load-segs.perfbin
201a1e
7f9f03a46d95
20178a
0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/5 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/5 0x201a56/0x201a10/P/-/-/5 0x201a56/0x201a10/P/-/-/5 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/3 0x201a56/0x201a10/P/-/-/7 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/3 0x201a56/0x201a10/P/-/-/6 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/3 0x201a56/0x201a10/P/-/-/6 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/4 0x201a56/0x201a10/P/-/-/3 0x201a56/0x201a10/P/-/-/6

View File

@ -1,45 +1,3 @@
; REQUIRES: x86-registered-target
; RUN: llc -filetype=obj %s -o %t
; RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 --show-disassembly-only -x86-asm-syntax=intel --show-source-locations | FileCheck %s --match-full-lines
; RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t2 --show-disassembly-only -x86-asm-syntax=intel --show-source-locations --show-canonical-fname | FileCheck %s --match-full-lines --check-prefix=CHECK-CANO
; CHECK: Disassembly of section .text [0x0, 0x4a]:
; CHECK: <funcA.llvm.1000>:
; CHECK: 0: mov eax, edi funcA.llvm.1000:0
; CHECK: 2: mov ecx, dword ptr [rip] funcLeaf:2 @ funcA.llvm.1000:1
; CHECK: 8: lea edx, [rcx + 3] fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK: b: cmp ecx, 3 fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK: e: cmovl edx, ecx fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK: 11: sub eax, edx funcLeaf:2 @ funcA.llvm.1000:1
; CHECK: 13: ret funcA.llvm.1000:2
; CHECK: 14: nop word ptr cs:[rax + rax]
; CHECK: 1e: nop
; CHECK-CANO: <funcA>:
; CHECK-CANO: 0: mov eax, edi funcA:0
; CHECK-CANO: 2: mov ecx, dword ptr [rip] funcLeaf:2 @ funcA:1
; CHECK-CANO: 8: lea edx, [rcx + 3] fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO: b: cmp ecx, 3 fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO: e: cmovl edx, ecx fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO: 11: sub eax, edx funcLeaf:2 @ funcA:1
; CHECK-CANO: 13: ret funcA:2
; CHECK-CANO: 14: nop word ptr cs:[rax + rax]
; CHECK-CANO: 1e: nop
; CHECK: <funcLeaf>:
; CHECK: 20: mov eax, edi funcLeaf:1
; CHECK: 22: mov ecx, dword ptr [rip] funcLeaf:2
; CHECK: 28: lea edx, [rcx + 3] fib:2 @ funcLeaf:2
; CHECK: 2b: cmp ecx, 3 fib:2 @ funcLeaf:2
; CHECK: 2e: cmovl edx, ecx fib:2 @ funcLeaf:2
; CHECK: 31: sub eax, edx funcLeaf:2
; CHECK: 33: ret funcLeaf:3
; CHECK: 34: nop word ptr cs:[rax + rax]
; CHECK: 3e: nop
; CHECK: <fib>:
; CHECK: 40: lea eax, [rdi + 3] fib:2
; CHECK: 43: cmp edi, 3 fib:2
; CHECK: 46: cmovl eax, edi fib:2
; CHECK: 49: ret fib:8
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

Binary file not shown.

View File

@ -1,121 +0,0 @@
# REQUIRES: x86-registered-target
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t
# RUN: llvm-profgen --binary=%t --perfscript=%s --output=%t1 -show-disassembly-only -x86-asm-syntax=intel | FileCheck %s --match-full-lines
# CHECK: Disassembly of section .text [0x0, 0x66]:
# CHECK: <foo1>:
# CHECK: 0: push rbp
# CHECK: 1: mov rbp, rsp
# CHECK: 4: sub rsp, 16
# CHECK: 8: mov dword ptr [rbp - 4], 0
# CHECK: f: mov edi, 1
# CHECK: 14: call 0x19
# CHECK: 19: mov edi, 2
# CHECK: 1e: mov dword ptr [rbp - 8], eax
# CHECK: 21: call 0x26
# CHECK: 26: mov ecx, dword ptr [rbp - 8]
# CHECK: 29: add ecx, eax
# CHECK: 2b: mov eax, ecx
# CHECK: 2d: add rsp, 16
# CHECK: 31: pop rbp
# CHECK: 32: ret
# CHECK: <foo2>:
# CHECK: 33: push rbp
# CHECK: 34: mov rbp, rsp
# CHECK: 37: sub rsp, 16
# CHECK: 3b: mov dword ptr [rbp - 4], 0
# CHECK: 42: mov edi, 1
# CHECK: 47: call 0x4c
# CHECK: 4c: mov edi, 2
# CHECK: 51: mov dword ptr [rbp - 8], eax
# CHECK: 54: call 0x59
# CHECK: 59: mov ecx, dword ptr [rbp - 8]
# CHECK: 5c: add ecx, eax
# CHECK: 5e: mov eax, ecx
# CHECK: 60: add rsp, 16
# CHECK: 64: pop rbp
# CHECK: 65: ret
.section .text
foo1:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $0, -4(%rbp)
movl $1, %edi
callq _Z5funcAi
movl $2, %edi
movl %eax, -8(%rbp)
callq _Z5funcBi
movl -8(%rbp), %ecx
addl %eax, %ecx
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
.section .text
foo2:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $0, -4(%rbp)
movl $1, %edi
callq _Z5funcBi
movl $2, %edi
movl %eax, -8(%rbp)
callq _Z5funcAi
movl -8(%rbp), %ecx
addl %eax, %ecx
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
# CHECK: Disassembly of section .text.hot [0x0, 0x12]:
# CHECK: <bar>:
# CHECK: 0: push rbp
# CHECK: 1: mov rbp, rsp
# CHECK: 4: mov dword ptr [rbp - 4], edi
# CHECK: 7: mov dword ptr [rbp - 8], esi
# CHECK: a: mov eax, dword ptr [rbp - 4]
# CHECK: d: add eax, dword ptr [rbp - 8]
# CHECK: 10: pop rbp
# CHECK: 11: ret
.section .text.hot
bar:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
addl -8(%rbp), %eax
popq %rbp
retq
# CHECK: Disassembly of section .text.unlikely [0x0, 0x12]:
# CHECK: <baz>:
# CHECK: 0: push rbp
# CHECK: 1: mov rbp, rsp
# CHECK: 4: mov dword ptr [rbp - 4], edi
# CHECK: 7: mov dword ptr [rbp - 8], esi
# CHECK: a: mov eax, dword ptr [rbp - 4]
# CHECK: d: sub eax, dword ptr [rbp - 8]
# CHECK: 10: pop rbp
# CHECK: 11: ret
.section .text.unlikely
baz:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
subl -8(%rbp), %eax
popq %rbp
retq

View File

@ -0,0 +1,49 @@
; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-disassembly-only | FileCheck %s
; CHECK: <bar>:
; CHECK-NEXT: 201750: pushq %rbp
; CHECK-NEXT: 201751: movq %rsp, %rbp
; CHECK-NEXT: 201754: imull $2863311531, %edi, %eax
; CHECK-NEXT: 20175a: addl $715827882, %eax
; CHECK-NEXT: 20175f: movl %esi, %ecx
; CHECK-NEXT: 201761: negl %ecx
; CHECK-NEXT: 201763: cmpl $1431655765, %eax
; CHECK-NEXT: 201768: cmovbl %esi, %ecx
; CHECK-NEXT: 20176b: leal (%rcx,%rdi), %eax
; CHECK-NEXT: 20176e: popq %rbp
; CHECK-NEXT: 20176f: retq
; CHECK: <foo>:
; CHECK-NEXT 201770: movl $1, %ecx
; CHECK-NEXT 201775: movl $2863311531, %r8d
; CHECK-NEXT 20177b: jmp 0x78e
; CHECK-NEXT 20177d: nopl (%rax)
; CHECK-NEXT 201780: addl $30, %esi
; CHECK-NEXT 201783: addl $1, %ecx
; CHECK-NEXT 201786: cmpl $16000001, %ecx
; clang -O3 -fexperimental-new-pass-manager -fuse-ld=lld -fpseudo-probe-for-profiling
; -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -Xclang -mdisable-tail-calls
; -g test.c -o a.out
#include <stdio.h>
int bar(int x, int y) {
if (x % 3) {
return x - y;
}
return x + y;
}
void foo() {
int s, i = 0;
while (i++ < 4000 * 4000)
if (i % 91) s = bar(i, s); else s += 30;
printf("sum is %d\n", s);
}
int main() {
foo();
return 0;
}

View File

@ -1,6 +1,5 @@
; REQUIRES: x86-registered-target
; RUN: llvm-mc -filetype=obj -triple=x86_64 %S/disassemble.s -o %t
; RUN: llvm-profgen --perfscript=%s --binary=%t --output=%t --show-mmap-events | FileCheck %s
; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-mmap-events | FileCheck %s
PERF_RECORD_MMAP2 2580483/2580483: [0x400000(0x1000) @ 0 103:01 539973862 1972407324]: r-xp /home/a.out
PERF_RECORD_MMAP2 2580483/2580483: [0x7f2505b40000(0x224000) @ 0 08:04 19532214 4169021329]: r-xp /usr/lib64/ld-2.17.so

View File

@ -0,0 +1,17 @@
; RUN: llvm-profgen --perfscript=%S/Inputs/multi-load-segs.perfscript --binary=%S/Inputs/multi-load-segs.perfbin --output=%t --format=text
; RUN: FileCheck %s --input-file %t
;; %S/Inputs/multi-load-segs.perfbin is an ELF image with two executable load segments.
; running llvm-readelf -l %S/Inputs/multi-load-segs.perfbin gives:
;; LOAD 0x000000 0x0000000000200000 0x0000000000200000 0x00075c 0x00075c R 0x1000
;; LOAD 0x000760 0x0000000000201760 0x0000000000201760 0x0004c0 0x0004c0 R E 0x1000
;; LOAD 0x000c20 0x0000000000202c20 0x0000000000202c20 0x0001f0 0x0001f0 RW 0x1000
;; LOAD 0x000e10 0x0000000000203e10 0x0000000000203e10 0x000040 0x000058 RW 0x1000
;; LOAD 0x200000 0x0000000000400000 0x0000000000400000 0x0005e8 0x0005e8 R E 0x200000
; CHECK: [main:2 @ _Z10sort_arrayv:6 @ _Z11bubble_sortPii]:465:0
; CHECK-NEXT: 4: 31
; CHECK-NEXT: 5: 31
; CHECK-NEXT: 7: 31
; CHECK-NEXT: 8: 31
; CHECK-NEXT: !Attributes: 1

View File

@ -0,0 +1,41 @@
; REQUIRES: x86-registered-target
; RUN: llvm-profgen --binary=%S/Inputs/symbolize.perfbin --perfscript=%s --output=%t1 --show-disassembly-only -x86-asm-syntax=intel --show-source-locations | FileCheck %s --match-full-lines
; RUN: llvm-profgen --binary=%S/Inputs/symbolize.perfbin --perfscript=%s --output=%t2 --show-disassembly-only -x86-asm-syntax=intel --show-source-locations --show-canonical-fname | FileCheck %s --match-full-lines --check-prefix=CHECK-CANO
; CHECK: Disassembly of section .text [0x520, 0x62c]:
; CHECK: <funcA.llvm.1000>:
; CHECK-NEXT: 5e0: mov eax, edi funcA.llvm.1000:0
; CHECK-NEXT: 5e2: mov edx, dword ptr [rip + 2099768] funcLeaf:2 @ funcA.llvm.1000:1
; CHECK-NEXT: 5e8: mov ecx, edx fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK-NEXT: 5ea: add ecx, 3 fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK-NEXT: 5ed: cmp edx, 3 fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK-NEXT: 5f0: cmovl ecx, edx fib:2 @ funcLeaf:2 @ funcA.llvm.1000:1
; CHECK-NEXT: 5f3: sub eax, ecx funcLeaf:2 @ funcA.llvm.1000:1
; CHECK-NEXT: 5f5: ret funcA.llvm.1000:2
; CHECK-CANO: <funcA>:
; CHECK-CANO-NEXT: 5e0: mov eax, edi funcA:0
; CHECK-CANO-NEXT: 5e2: mov edx, dword ptr [rip + 2099768] funcLeaf:2 @ funcA:1
; CHECK-CANO-NEXT: 5e8: mov ecx, edx fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO-NEXT: 5ea: add ecx, 3 fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO-NEXT: 5ed: cmp edx, 3 fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO-NEXT: 5f0: cmovl ecx, edx fib:2 @ funcLeaf:2 @ funcA:1
; CHECK-CANO-NEXT: 5f3: sub eax, ecx funcLeaf:2 @ funcA:1
; CHECK-CANO-NEXT: 5f5: ret funcA:2
; CHECK: <funcLeaf>:
; CHECK-NEXT: 600: mov eax, edi funcLeaf:1
; CHECK-NEXT: 602: mov edx, dword ptr [rip + 2099736] funcLeaf:2
; CHECK-NEXT: 608: mov ecx, edx fib:2 @ funcLeaf:2
; CHECK-NEXT: 60a: add ecx, 3 fib:2 @ funcLeaf:2
; CHECK-NEXT: 60d: cmp edx, 3 fib:2 @ funcLeaf:2
; CHECK-NEXT: 610: cmovl ecx, edx fib:2 @ funcLeaf:2
; CHECK-NEXT: 613: sub eax, ecx funcLeaf:2
; CHECK-NEXT: 615: ret funcLeaf:3
; CHECK: <fib>:
; CHECK-NEXT: 620: mov eax, edi fib:2
; CHECK-NEXT: 622: add eax, 3 fib:2
; CHECK-NEXT: 625: cmp edi, 3 fib:2
; CHECK-NEXT: 628: cmovl eax, edi fib:2
; CHECK-NEXT: 62b: ret fib:8
; symbolize.perfbin is from the following compile commands:
; clang %S/Inputs/symbolize.ll -shared -fPIC -o %S/Inputs/symbolize.perfbin

View File

@ -311,18 +311,46 @@ void PerfReader::updateBinaryAddress(const MMapEvent &Event) {
auto I = BinaryTable.find(BinaryName);
// Drop the event which doesn't belong to user-provided binaries
// or if its image is loaded at the same address
if (I == BinaryTable.end() || Event.BaseAddress == I->second.getBaseAddress())
if (I == BinaryTable.end() || Event.Address == I->second.getBaseAddress())
return;
ProfiledBinary &Binary = I->second;
// A binary image could be uploaded and then reloaded at different
// place, so update the address map here
AddrToBinaryMap.erase(Binary.getBaseAddress());
AddrToBinaryMap[Event.BaseAddress] = &Binary;
if (Event.Offset == Binary.getTextSegmentOffset()) {
// A binary image could be unloaded and then reloaded at different
// place, so update the address map here.
// Only update for the first executable segment and assume all other
// segments are loaded at consecutive memory addresses, which is the case on
// X64.
AddrToBinaryMap.erase(Binary.getBaseAddress());
AddrToBinaryMap[Event.Address] = &Binary;
// Update binary load address.
Binary.setBaseAddress(Event.BaseAddress);
// Update binary load address.
Binary.setBaseAddress(Event.Address);
} else {
// Verify segments are loaded consecutively.
const auto &Offsets = Binary.getTextSegmentOffsets();
auto It = std::lower_bound(Offsets.begin(), Offsets.end(), Event.Offset);
if (It != Offsets.end() && *It == Event.Offset) {
// The event is for loading a separate executable segment.
auto I = std::distance(Offsets.begin(), It);
const auto &PreferredAddrs = Binary.getPreferredTextSegmentAddresses();
if (PreferredAddrs[I] - Binary.getPreferredBaseAddress() !=
Event.Address - Binary.getBaseAddress())
exitWithError("Executable segments not loaded consecutively");
} else {
if (It == Offsets.begin())
exitWithError("File offset not found");
else {
// Find the segment the event falls in. A large segment could be loaded
// via multiple mmap calls with consecutive memory addresses.
--It;
assert(*It < Event.Offset);
if (Event.Offset - *It != Event.Address - Binary.getBaseAddress())
exitWithError("Segment not loaded by consecutive mmaps");
}
}
}
}
ProfiledBinary *PerfReader::getBinary(uint64_t Address) {
@ -637,14 +665,14 @@ void PerfReader::parseMMap2Event(TraceStream &TraceIt) {
}
MMapEvent Event;
Fields[PID].getAsInteger(10, Event.PID);
Fields[BASE_ADDRESS].getAsInteger(0, Event.BaseAddress);
Fields[BASE_ADDRESS].getAsInteger(0, Event.Address);
Fields[MMAPPED_SIZE].getAsInteger(0, Event.Size);
Fields[PAGE_OFFSET].getAsInteger(0, Event.Offset);
Event.BinaryPath = Fields[BINARY_PATH];
updateBinaryAddress(Event);
if (ShowMmapEvents) {
outs() << "Mmap: Binary " << Event.BinaryPath << " loaded at "
<< format("0x%" PRIx64 ":", Event.BaseAddress) << " \n";
<< format("0x%" PRIx64 ":", Event.Address) << " \n";
}
TraceIt.advance();
}

View File

@ -595,7 +595,7 @@ public:
// The parsed MMap event
struct MMapEvent {
uint64_t PID = 0;
uint64_t BaseAddress = 0;
uint64_t Address = 0;
uint64_t Size = 0;
uint64_t Offset = 0;
StringRef BinaryPath;

View File

@ -52,38 +52,6 @@ static const Target *getTarget(const ObjectFile *Obj) {
return TheTarget;
}
template <class ELFT>
static uint64_t getELFImageLMAForSec(const ELFFile<ELFT> &Obj,
const object::ELFSectionRef &Sec,
StringRef FileName) {
// Search for a PT_LOAD segment containing the requested section. Return this
// segment's p_addr as the image load address for the section.
const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
for (const typename ELFT::Phdr &Phdr : PhdrRange)
if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) &&
(Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress()))
// Segments will always be loaded at a page boundary.
return Phdr.p_paddr & ~(Phdr.p_align - 1U);
return 0;
}
// Get the image load address for a specific section. Note that an image is
// loaded by segments (a group of sections) and segments may not be consecutive
// in memory.
static uint64_t getELFImageLMAForSec(const object::ELFSectionRef &Sec) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
ELFObj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
ELFObj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject()))
return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
ELFObj->getFileName());
const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject());
return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, ELFObj->getFileName());
}
void ProfiledBinary::load() {
// Attempt to open the binary.
OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
@ -99,8 +67,8 @@ void ProfiledBinary::load() {
exitWithError("unsupported target", TheTriple.getTriple());
LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
// Find the preferred base address for text sections.
setPreferredBaseAddress(Obj);
// Find the preferred load address for text sections.
setPreferredTextSegmentAddresses(Obj);
// Decode pseudo probe related section
decodePseudoProbe(Obj);
@ -172,16 +140,32 @@ ProfiledBinary::getExpandedContextStr(const SmallVectorImpl<uint64_t> &Stack,
return OContextStr.str();
}
void ProfiledBinary::setPreferredBaseAddress(const ELFObjectFileBase *Obj) {
for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
SI != SE; ++SI) {
const SectionRef &Section = *SI;
if (Section.isText()) {
PreferredBaseAddress = getELFImageLMAForSec(Section);
return;
}
template <class ELFT>
void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName) {
const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
for (const typename ELFT::Phdr &Phdr : PhdrRange) {
if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_flags & ELF::PF_X)) {
// Segments will always be loaded at a page boundary.
PreferredTextSegmentAddresses.push_back(Phdr.p_vaddr & ~(Phdr.p_align - 1U));
TextSegmentOffsets.push_back(Phdr.p_offset & ~(Phdr.p_align - 1U));
}
}
exitWithError("no text section found", Obj->getFileName());
if (PreferredTextSegmentAddresses.empty())
exitWithError("no executable segment found", FileName);
}
void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFObjectFileBase *Obj) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = cast<ELF64BEObjectFile>(Obj))
setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
else
llvm_unreachable("invalid ELF object format");
}
void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
@ -212,11 +196,11 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
SectionSymbolsTy &Symbols,
const SectionRef &Section) {
std::size_t SE = Symbols.size();
uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress;
uint64_t SectionOffset = Section.getAddress() - getPreferredBaseAddress();
uint64_t SectSize = Section.getSize();
uint64_t StartOffset = Symbols[SI].Addr - PreferredBaseAddress;
uint64_t StartOffset = Symbols[SI].Addr - getPreferredBaseAddress();
uint64_t EndOffset = (SI + 1 < SE)
? Symbols[SI + 1].Addr - PreferredBaseAddress
? Symbols[SI + 1].Addr - getPreferredBaseAddress()
: SectionOffset + SectSize;
if (StartOffset >= EndOffset)
return true;
@ -244,16 +228,16 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
// Disassemble an instruction.
bool Disassembled =
DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
Offset + PreferredBaseAddress, nulls());
Offset + getPreferredBaseAddress(), nulls());
if (Size == 0)
Size = 1;
if (ShowDisassemblyOnly) {
if (ShowPseudoProbe) {
ProbeDecoder.printProbeForAddress(outs(),
Offset + PreferredBaseAddress);
Offset + getPreferredBaseAddress());
}
outs() << format("%8" PRIx64 ":", Offset);
outs() << format("%8" PRIx64 ":", Offset + getPreferredBaseAddress());
size_t Start = outs().tell();
if (Disassembled)
IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
@ -378,7 +362,7 @@ void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
if (!Section.isText())
continue;
uint64_t ImageLoadAddr = PreferredBaseAddress;
uint64_t ImageLoadAddr = getPreferredBaseAddress();
uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
uint64_t SectSize = Section.getSize();
if (!SectSize)
@ -390,8 +374,9 @@ void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
if (ShowDisassemblyOnly) {
StringRef SectionName = unwrapOrError(Section.getName(), FileName);
outs() << "\nDisassembly of section " << SectionName;
outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", "
<< format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n";
outs() << " [" << format("0x%" PRIx64, Section.getAddress()) << ", "
<< format("0x%" PRIx64, Section.getAddress() + SectSize)
<< "]:\n\n";
}
// Get the section data.
@ -424,7 +409,7 @@ FrameLocationStack ProfiledBinary::symbolize(const InstructionPointer &IP,
bool UseCanonicalFnName) {
assert(this == IP.Binary &&
"Binary should only symbolize its own instruction");
auto Addr = object::SectionedAddress{IP.Offset + PreferredBaseAddress,
auto Addr = object::SectionedAddress{IP.Offset + getPreferredBaseAddress(),
object::SectionedAddress::UndefSection};
DIInliningInfo InlineStack =
unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName());

View File

@ -99,10 +99,13 @@ class ProfiledBinary {
std::string Path;
// The target triple.
Triple TheTriple;
// The runtime base address that the executable sections are loaded at.
mutable uint64_t BaseAddress = 0;
// The preferred base address that the executable sections are loaded at.
uint64_t PreferredBaseAddress = 0;
// The runtime base address that the first executable segment is loaded at.
uint64_t BaseAddress;
// The preferred load address of each executable segment.
std::vector<uint64_t> PreferredTextSegmentAddresses;
// The file offset of each executable segment.
std::vector<uint64_t> TextSegmentOffsets;
// Mutiple MC component info
std::unique_ptr<const MCRegisterInfo> MRI;
std::unique_ptr<const MCAsmInfo> AsmInfo;
@ -136,7 +139,10 @@ class ProfiledBinary {
bool UsePseudoProbes = false;
void setPreferredBaseAddress(const ELFObjectFileBase *O);
void setPreferredTextSegmentAddresses(const ELFObjectFileBase *O);
template <class ELFT>
void setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName);
void decodePseudoProbe(const ELFObjectFileBase *Obj);
@ -174,8 +180,8 @@ public:
setupSymbolizer();
load();
}
uint64_t virtualAddrToOffset(uint64_t VitualAddress) const {
return VitualAddress - BaseAddress;
uint64_t virtualAddrToOffset(uint64_t VirtualAddress) const {
return VirtualAddress - BaseAddress;
}
uint64_t offsetToVirtualAddr(uint64_t Offset) const {
return Offset + BaseAddress;
@ -184,7 +190,17 @@ public:
StringRef getName() const { return llvm::sys::path::filename(Path); }
uint64_t getBaseAddress() const { return BaseAddress; }
void setBaseAddress(uint64_t Address) { BaseAddress = Address; }
uint64_t getPreferredBaseAddress() const { return PreferredBaseAddress; }
// Return the preferred load address for the first executable segment.
uint64_t getPreferredBaseAddress() const { return PreferredTextSegmentAddresses[0]; }
// Return the file offset for the first executable segment.
uint64_t getTextSegmentOffset() const { return TextSegmentOffsets[0]; }
const std::vector<uint64_t> &getPreferredTextSegmentAddresses() const {
return PreferredTextSegmentAddresses;
}
const std::vector<uint64_t> &getTextSegmentOffsets() const {
return TextSegmentOffsets;
}
bool addressIsCode(uint64_t Address) const {
uint64_t Offset = virtualAddrToOffset(Address);