blob: 3eefc9cb9830dcc30fa1d9acf98abc06b8839f5c [file] [log] [blame]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001From 007965d341349679607699d005c4af811b2c419a Mon Sep 17 00:00:00 2001
2From: Andrew Jeffery <andrew@aj.id.au>
3Date: Fri, 4 May 2018 11:23:53 +0930
4Subject: [PATCH] symbol: Account for prelinked shared objects
5
6Some projects, such as those derived from Yocto, tend to prelink their
7binaries and libraries to reduce runtime overhead. Currently this trips
8up pyflame in its symbol address calculations, and leads to ptrace
9failures due to spurious addresses:
10
11 $ pyflame -t python -c "print 'foo'"
12 Unexpected ptrace(2) exception: Failed to PTRACE_PEEKDATA (pid 1482, addr 0x9f9b1d70): Input/output error
13 Unexpected ptrace(2) exception: Failed to PTRACE_PEEKDATA (pid 1482, addr 0x9f9b1d70): Input/output error
14 Unexpected ptrace(2) exception: Failed to PTRACE_PEEKDATA (pid 1482, addr 0x9f9b1d70): Input/output error
15 Unexpected ptrace(2) exception: Failed to PTRACE_PEEKDATA (pid 1482, addr 0x9f9b1d70): Input/output error
16 Unexpected ptrace(2) exception: Failed to PTRACE_PEEKDATA (pid 1482, addr 0x9f9b1d70): Input/output error
17 ...
18
19Add support for reading a prelinked base p_vaddr out of the ELF and
20adjust the PyAddresses values accordingly.
21
22Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
23---
24 src/symbol.cc | 15 +++++++++++++++
25 src/symbol.h | 29 ++++++++++++++++++++++++++++-
26 2 files changed, 43 insertions(+), 1 deletion(-)
27
28diff --git a/src/symbol.cc b/src/symbol.cc
29index 125174efeeb5..39c3e8132dd1 100644
30--- a/src/symbol.cc
31+++ b/src/symbol.cc
32@@ -166,6 +166,17 @@ PyABI ELF::WalkTable(int sym, int str, PyAddresses *addrs) {
33 return abi;
34 }
35
36+addr_t ELF::GetBaseAddress() {
37+ int32_t phnum = hdr()->e_phnum;
38+ int32_t i;
39+ for (i = 0; i < phnum && phdr(i)->p_type != PT_LOAD; i++) {
40+ }
41+ if (i == phnum) {
42+ throw FatalException("Failed to find PT_LOAD entry in program headers");
43+ }
44+ return phdr(i)->p_vaddr;
45+}
46+
47 PyAddresses ELF::GetAddresses(PyABI *abi) {
48 PyAddresses addrs;
49 PyABI detected_abi = WalkTable(dynsym_, dynstr_, &addrs);
50@@ -176,6 +187,10 @@ PyAddresses ELF::GetAddresses(PyABI *abi) {
51 if (abi != nullptr) {
52 *abi = detected_abi;
53 }
54+ // Handle prelinked shared objects
55+ if (hdr()->e_type == ET_DYN) {
56+ return addrs - GetBaseAddress();
57+ }
58 return addrs;
59 }
60 } // namespace pyflame
61diff --git a/src/symbol.h b/src/symbol.h
62index 124853bcc1c1..bb92b9a2604b 100644
63--- a/src/symbol.h
64+++ b/src/symbol.h
65@@ -28,15 +28,19 @@
66
67 #if USE_ELF64
68 #define ehdr_t Elf64_Ehdr
69+#define phdr_t Elf64_Phdr
70 #define shdr_t Elf64_Shdr
71 #define dyn_t Elf64_Dyn
72 #define sym_t Elf64_Sym
73+#define addr_t Elf64_Addr
74 #define ARCH_ELFCLASS ELFCLASS64
75 #else
76 #define ehdr_t Elf32_Ehdr
77+#define phdr_t Elf32_Phdr
78 #define shdr_t Elf32_Shdr
79 #define dyn_t Elf32_Dyn
80 #define sym_t Elf32_Sym
81+#define addr_t Elf32_Addr
82 #define ARCH_ELFCLASS ELFCLASS32
83 #endif
84
85@@ -67,8 +71,18 @@ struct PyAddresses {
86 interp_head_hint(0),
87 pie(false) {}
88
89+ PyAddresses operator-(const unsigned long base) const {
90+ PyAddresses res(*this);
91+ res.tstate_addr = this->tstate_addr == 0 ? 0 : this->tstate_addr - base;
92+ res.interp_head_addr =
93+ this->interp_head_addr == 0 ? 0 : this->interp_head_addr - base;
94+ res.interp_head_fn_addr =
95+ this->interp_head_fn_addr == 0 ? 0 : this->interp_head_fn_addr - base;
96+ return res;
97+ }
98+
99 PyAddresses operator+(const unsigned long base) const {
100- PyAddresses res;
101+ PyAddresses res(*this);
102 res.tstate_addr = this->tstate_addr == 0 ? 0 : this->tstate_addr + base;
103 res.interp_head_addr =
104 this->interp_head_addr == 0 ? 0 : this->interp_head_addr + base;
105@@ -113,6 +127,9 @@ class ELF {
106 // ABI.
107 PyAddresses GetAddresses(PyABI *abi);
108
109+ // Extract the base load address from the Program Header table
110+ addr_t GetBaseAddress();
111+
112 private:
113 void *addr_;
114 size_t length_;
115@@ -122,6 +139,16 @@ class ELF {
116 return reinterpret_cast<const ehdr_t *>(addr_);
117 }
118
119+ inline const phdr_t *phdr(int idx) const {
120+ if (idx < 0) {
121+ std::ostringstream ss;
122+ ss << "Illegal phdr index: " << idx;
123+ throw FatalException(ss.str());
124+ }
125+ return reinterpret_cast<const phdr_t *>(p() + hdr()->e_phoff +
126+ idx * hdr()->e_phentsize);
127+ }
128+
129 inline const shdr_t *shdr(int idx) const {
130 if (idx < 0) {
131 std::ostringstream ss;
132--
1332.14.1
134