diff --git a/llvm/test/tools/llvm-profgen/Inputs/multi-load-segs.perfbin b/llvm/test/tools/llvm-profgen/Inputs/multi-load-segs.perfbin new file mode 100755 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@: -; 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: : -; 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: : -; 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: : -; 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" diff --git a/llvm/test/tools/llvm-profgen/Inputs/symbolize.perfbin b/llvm/test/tools/llvm-profgen/Inputs/symbolize.perfbin new file mode 100755 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@: -# 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: : -# 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: : -# 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: : -# 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 diff --git a/llvm/test/tools/llvm-profgen/disassemble.test b/llvm/test/tools/llvm-profgen/disassemble.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profgen/disassemble.test @@ -0,0 +1,49 @@ +; RUN: llvm-profgen --perfscript=%s --binary=%S/Inputs/inline-cs-pseudoprobe.perfbin --output=%t --show-disassembly-only | FileCheck %s + +; CHECK: : +; 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: : +; 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 + +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; +} diff --git a/llvm/test/tools/llvm-profgen/mmapEvent.test b/llvm/test/tools/llvm-profgen/mmapEvent.test --- a/llvm/test/tools/llvm-profgen/mmapEvent.test +++ b/llvm/test/tools/llvm-profgen/mmapEvent.test @@ -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 diff --git a/llvm/test/tools/llvm-profgen/multi-load-segs.test b/llvm/test/tools/llvm-profgen/multi-load-segs.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profgen/multi-load-segs.test @@ -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 diff --git a/llvm/test/tools/llvm-profgen/symbolize.test b/llvm/test/tools/llvm-profgen/symbolize.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profgen/symbolize.test @@ -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: : +; 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: : +; 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: : +; 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: : +; 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 \ No newline at end of file diff --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h --- a/llvm/tools/llvm-profgen/PerfReader.h +++ b/llvm/tools/llvm-profgen/PerfReader.h @@ -595,7 +595,7 @@ // 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; diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp --- a/llvm/tools/llvm-profgen/PerfReader.cpp +++ b/llvm/tools/llvm-profgen/PerfReader.cpp @@ -311,18 +311,46 @@ 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; - - // Update binary load address. - Binary.setBaseAddress(Event.BaseAddress); + 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.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 @@ } 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(); } diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h --- a/llvm/tools/llvm-profgen/ProfiledBinary.h +++ b/llvm/tools/llvm-profgen/ProfiledBinary.h @@ -99,10 +99,13 @@ 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 PreferredTextSegmentAddresses; + // The file offset of each executable segment. + std::vector TextSegmentOffsets; + // Mutiple MC component info std::unique_ptr MRI; std::unique_ptr AsmInfo; @@ -136,7 +139,10 @@ bool UsePseudoProbes = false; - void setPreferredBaseAddress(const ELFObjectFileBase *O); + void setPreferredTextSegmentAddresses(const ELFObjectFileBase *O); + + template + void setPreferredTextSegmentAddresses(const ELFFile &Obj, StringRef FileName); void decodePseudoProbe(const ELFObjectFileBase *Obj); @@ -174,8 +180,8 @@ 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 @@ 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 &getPreferredTextSegmentAddresses() const { + return PreferredTextSegmentAddresses; + } + const std::vector &getTextSegmentOffsets() const { + return TextSegmentOffsets; + } bool addressIsCode(uint64_t Address) const { uint64_t Offset = virtualAddrToOffset(Address); diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp --- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp +++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp @@ -52,38 +52,6 @@ return TheTarget; } -template -static uint64_t getELFImageLMAForSec(const ELFFile &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(Sec.getObject())) - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, - ELFObj->getFileName()); - else if (const auto *ELFObj = dyn_cast(Sec.getObject())) - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, - ELFObj->getFileName()); - else if (const auto *ELFObj = dyn_cast(Sec.getObject())) - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, - ELFObj->getFileName()); - const auto *ELFObj = cast(Sec.getObject()); - return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, ELFObj->getFileName()); -} - void ProfiledBinary::load() { // Attempt to open the binary. OwningBinary OBinary = unwrapOrError(createBinary(Path), Path); @@ -99,8 +67,8 @@ 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 @@ 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 +void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile &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(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = cast(Obj)) + setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName()); + else + llvm_unreachable("invalid ELF object format"); } void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) { @@ -212,11 +196,11 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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());