summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Ferris2017-08-30 15:15:19 -0500
committerChristopher Ferris2017-08-30 17:50:11 -0500
commit3f805ac3f8e61489f66a54bbdb1a8dd541c043d1 (patch)
tree5392038e32942faed35a0b94fd821e3dc3c887fe
parente1f9a58c8649136af7578b01c3f224ea4b88b555 (diff)
downloadplatform-system-core-3f805ac3f8e61489f66a54bbdb1a8dd541c043d1.tar.gz
platform-system-core-3f805ac3f8e61489f66a54bbdb1a8dd541c043d1.tar.xz
platform-system-core-3f805ac3f8e61489f66a54bbdb1a8dd541c043d1.zip
Add proper support for embedded elf files.
- Add a method to get the max size of an elf file by reading the section header offset + size. This will properly map an elf file embedded into an apk, instead of just mapping in what is done by the dynamic linker. It does assume that the section headers are at the end of the elf file. - Add new tests for the above functionality. - Update the unwind_symbols tool to take an address for finding a function instead of dumping the entire symbol table. Bug: 23762183 Test: Unit tests pass, unwind through the camera process and verify Test: the GoogleCamera.apk shows some function names. Change-Id: I00c021680fe1d43b60d652bf91bbf6667d9617be
-rw-r--r--libunwindstack/Elf.cpp22
-rw-r--r--libunwindstack/ElfInterface.cpp19
-rw-r--r--libunwindstack/MapInfo.cpp77
-rw-r--r--libunwindstack/include/unwindstack/Elf.h2
-rw-r--r--libunwindstack/include/unwindstack/ElfInterface.h11
-rw-r--r--libunwindstack/include/unwindstack/MapInfo.h1
-rw-r--r--libunwindstack/tests/MapInfoCreateMemoryTest.cpp84
-rw-r--r--libunwindstack/tools/unwind_symbols.cpp34
8 files changed, 210 insertions, 40 deletions
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4fc7c679f..4f7476d92 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -124,6 +124,28 @@ bool Elf::IsValidElf(Memory* memory) {
124 return true; 124 return true;
125} 125}
126 126
127void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
128 if (!IsValidElf(memory)) {
129 *valid = false;
130 return;
131 }
132 *size = 0;
133 *valid = true;
134
135 // Now read the section header information.
136 uint8_t class_type;
137 if (!memory->Read(EI_CLASS, &class_type, 1)) {
138 return;
139 }
140 if (class_type == ELFCLASS32) {
141 ElfInterface32::GetMaxSize(memory, size);
142 } else if (class_type == ELFCLASS64) {
143 ElfInterface64::GetMaxSize(memory, size);
144 } else {
145 *valid = false;
146 }
147}
148
127ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { 149ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
128 if (!IsValidElf(memory)) { 150 if (!IsValidElf(memory)) {
129 return nullptr; 151 return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 75abc85f7..be4f88a26 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -370,6 +370,22 @@ bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
370 return false; 370 return false;
371} 371}
372 372
373// This is an estimation of the size of the elf file using the location
374// of the section headers and size. This assumes that the section headers
375// are at the end of the elf file. If the elf has a load bias, the size
376// will be too large, but this is acceptable.
377template <typename EhdrType>
378void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
379 EhdrType ehdr;
380 if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
381 return;
382 }
383 if (ehdr.e_shnum == 0) {
384 return;
385 }
386 *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
387}
388
373// Instantiate all of the needed template functions. 389// Instantiate all of the needed template functions.
374template void ElfInterface::InitHeadersWithTemplate<uint32_t>(); 390template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
375template void ElfInterface::InitHeadersWithTemplate<uint64_t>(); 391template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
@@ -391,4 +407,7 @@ template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std
391template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*, 407template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
392 uint64_t*); 408 uint64_t*);
393 409
410template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
411template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
412
394} // namespace unwindstack 413} // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index d0e1216f3..32722154a 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -27,6 +27,55 @@
27 27
28namespace unwindstack { 28namespace unwindstack {
29 29
30Memory* MapInfo::GetFileMemory() {
31 std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
32 if (offset == 0) {
33 if (memory->Init(name, 0)) {
34 return memory.release();
35 }
36 return nullptr;
37 }
38
39 // There are two possibilities when the offset is non-zero.
40 // - There is an elf file embedded in a file.
41 // - The whole file is an elf file, and the offset needs to be saved.
42 //
43 // Map in just the part of the file for the map. If this is not
44 // a valid elf, then reinit as if the whole file is an elf file.
45 // If the offset is a valid elf, then determine the size of the map
46 // and reinit to that size. This is needed because the dynamic linker
47 // only maps in a portion of the original elf, and never the symbol
48 // file data.
49 uint64_t map_size = end - start;
50 if (!memory->Init(name, offset, map_size)) {
51 return nullptr;
52 }
53
54 bool valid;
55 uint64_t max_size;
56 Elf::GetInfo(memory.get(), &valid, &max_size);
57 if (!valid) {
58 // Init as if the whole file is an elf.
59 if (memory->Init(name, 0)) {
60 elf_offset = offset;
61 return memory.release();
62 }
63 return nullptr;
64 }
65
66 if (max_size > map_size) {
67 if (memory->Init(name, offset, max_size)) {
68 return memory.release();
69 }
70 // Try to reinit using the default map_size.
71 if (memory->Init(name, offset, map_size)) {
72 return memory.release();
73 }
74 return nullptr;
75 }
76 return memory.release();
77}
78
30Memory* MapInfo::CreateMemory(pid_t pid) { 79Memory* MapInfo::CreateMemory(pid_t pid) {
31 if (end <= start) { 80 if (end <= start) {
32 return nullptr; 81 return nullptr;
@@ -40,33 +89,13 @@ Memory* MapInfo::CreateMemory(pid_t pid) {
40 if (flags & MAPS_FLAGS_DEVICE_MAP) { 89 if (flags & MAPS_FLAGS_DEVICE_MAP) {
41 return nullptr; 90 return nullptr;
42 } 91 }
43 92 Memory* memory = GetFileMemory();
44 std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset); 93 if (memory != nullptr) {
45 uint64_t map_size; 94 return memory;
46 if (offset != 0) {
47 // Only map in a piece of the file.
48 map_size = end - start;
49 } else {
50 map_size = UINT64_MAX;
51 }
52 if (file_memory->Init(name, offset, map_size)) {
53 // It's possible that a non-zero offset might not be pointing to
54 // valid elf data. Check if this is a valid elf, and if not assume
55 // that this was meant to incorporate the entire file.
56 if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
57 // Don't bother checking the validity that will happen on the elf init.
58 if (file_memory->Init(name, 0)) {
59 elf_offset = offset;
60 return file_memory.release();
61 }
62 // Fall through if the init fails.
63 } else {
64 return file_memory.release();
65 }
66 } 95 }
67 } 96 }
68 97
69 Memory* memory = nullptr; 98 Memory* memory;
70 if (pid == getpid()) { 99 if (pid == getpid()) {
71 memory = new MemoryLocal(); 100 memory = new MemoryLocal();
72 } else { 101 } else {
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index d89a7464a..4e7eb3447 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -70,6 +70,8 @@ class Elf {
70 70
71 static bool IsValidElf(Memory* memory); 71 static bool IsValidElf(Memory* memory);
72 72
73 static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
74
73 protected: 75 protected:
74 bool valid_ = false; 76 bool valid_ = false;
75 std::unique_ptr<ElfInterface> interface_; 77 std::unique_ptr<ElfInterface> interface_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5cac0d358..142a6259e 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -102,6 +102,9 @@ class ElfInterface {
102 102
103 virtual bool HandleType(uint64_t, uint32_t) { return false; } 103 virtual bool HandleType(uint64_t, uint32_t) { return false; }
104 104
105 template <typename EhdrType>
106 static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
107
105 Memory* memory_; 108 Memory* memory_;
106 std::unordered_map<uint64_t, LoadInfo> pt_loads_; 109 std::unordered_map<uint64_t, LoadInfo> pt_loads_;
107 uint64_t load_bias_ = 0; 110 uint64_t load_bias_ = 0;
@@ -146,6 +149,10 @@ class ElfInterface32 : public ElfInterface {
146 bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override { 149 bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
147 return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset); 150 return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
148 } 151 }
152
153 static void GetMaxSize(Memory* memory, uint64_t* size) {
154 GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
155 }
149}; 156};
150 157
151class ElfInterface64 : public ElfInterface { 158class ElfInterface64 : public ElfInterface {
@@ -166,6 +173,10 @@ class ElfInterface64 : public ElfInterface {
166 bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override { 173 bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
167 return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset); 174 return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
168 } 175 }
176
177 static void GetMaxSize(Memory* memory, uint64_t* size) {
178 GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
179 }
169}; 180};
170 181
171} // namespace unwindstack 182} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 185476799..b8ba92576 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -40,6 +40,7 @@ struct MapInfo {
40 // instead of a portion of the file. 40 // instead of a portion of the file.
41 uint64_t elf_offset; 41 uint64_t elf_offset;
42 42
43 Memory* GetFileMemory();
43 Memory* CreateMemory(pid_t pid); 44 Memory* CreateMemory(pid_t pid);
44 // This function guarantees it will never return nullptr. 45 // This function guarantees it will never return nullptr.
45 Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false); 46 Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 9e45e7812..2aab9c652 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -38,30 +38,50 @@ namespace unwindstack {
38 38
39class MapInfoCreateMemoryTest : public ::testing::Test { 39class MapInfoCreateMemoryTest : public ::testing::Test {
40 protected: 40 protected:
41 template <typename Ehdr, typename Shdr>
42 static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
43 std::vector<uint8_t> buffer(20000);
44 memset(buffer.data(), 0, buffer.size());
45
46 Ehdr ehdr;
47 memset(&ehdr, 0, sizeof(ehdr));
48 memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
49 ehdr.e_ident[EI_CLASS] = class_type;
50 ehdr.e_shoff = sh_offset;
51 ehdr.e_shentsize = sizeof(Shdr) + 100;
52 ehdr.e_shnum = 4;
53 memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
54
55 ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
56 }
57
41 static void SetUpTestCase() { 58 static void SetUpTestCase() {
42 std::vector<uint8_t> buffer(1024); 59 std::vector<uint8_t> buffer(1024);
60 memset(buffer.data(), 0, buffer.size());
43 memcpy(buffer.data(), ELFMAG, SELFMAG); 61 memcpy(buffer.data(), ELFMAG, SELFMAG);
44 for (size_t i = SELFMAG; i < buffer.size(); i++) { 62 buffer[EI_CLASS] = ELFCLASS32;
45 buffer[i] = i / 256 + 1;
46 }
47 ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size())); 63 ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
48 64
49 for (size_t i = 0; i < 0x100; i++) { 65 memset(buffer.data(), 0, buffer.size());
50 buffer[i] = i / 256 + 1;
51 }
52 memcpy(&buffer[0x100], ELFMAG, SELFMAG); 66 memcpy(&buffer[0x100], ELFMAG, SELFMAG);
53 for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) { 67 buffer[0x100 + EI_CLASS] = ELFCLASS64;
54 buffer[i] = i / 256 + 1;
55 }
56 ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size())); 68 ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
69
70 InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
71 InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
57 } 72 }
58 73
59 static TemporaryFile elf_; 74 static TemporaryFile elf_;
60 75
61 static TemporaryFile elf_at_100_; 76 static TemporaryFile elf_at_100_;
77
78 static TemporaryFile elf32_at_map_;
79 static TemporaryFile elf64_at_map_;
62}; 80};
63TemporaryFile MapInfoCreateMemoryTest::elf_; 81TemporaryFile MapInfoCreateMemoryTest::elf_;
64TemporaryFile MapInfoCreateMemoryTest::elf_at_100_; 82TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
83TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
84TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
65 85
66TEST_F(MapInfoCreateMemoryTest, end_le_start) { 86TEST_F(MapInfoCreateMemoryTest, end_le_start) {
67 MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path}; 87 MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
@@ -93,8 +113,9 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
93 std::vector<uint8_t> buffer(1024); 113 std::vector<uint8_t> buffer(1024);
94 ASSERT_TRUE(memory->Read(0, buffer.data(), 1024)); 114 ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
95 ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0); 115 ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
96 for (size_t i = SELFMAG; i < buffer.size(); i++) { 116 ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
97 ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i; 117 for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
118 ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
98 } 119 }
99 120
100 ASSERT_FALSE(memory->Read(1024, buffer.data(), 1)); 121 ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
@@ -113,13 +134,50 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
113 std::vector<uint8_t> buffer(0x100); 134 std::vector<uint8_t> buffer(0x100);
114 ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100)); 135 ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
115 ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0); 136 ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
116 for (size_t i = SELFMAG; i < buffer.size(); i++) { 137 ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
117 ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i; 138 for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
139 ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
118 } 140 }
119 141
120 ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1)); 142 ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
121} 143}
122 144
145// Verify that if the offset is non-zero and there is an elf at that
146// offset, that only part of the file is used. Further verify that if the
147// embedded elf is bigger than the initial map, the new object is larger
148// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
149TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
150 MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
151
152 std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
153 ASSERT_TRUE(memory.get() != nullptr);
154 ASSERT_EQ(0U, info.elf_offset);
155
156 // Verify the memory is a valid elf.
157 uint8_t e_ident[SELFMAG + 1];
158 ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
159 ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
160
161 // Read past the end of what would normally be the size of the map.
162 ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
163}
164
165TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
166 MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
167
168 std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
169 ASSERT_TRUE(memory.get() != nullptr);
170 ASSERT_EQ(0U, info.elf_offset);
171
172 // Verify the memory is a valid elf.
173 uint8_t e_ident[SELFMAG + 1];
174 ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
175 ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
176
177 // Read past the end of what would normally be the size of the map.
178 ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
179}
180
123// Verify that device file names will never result in Memory object creation. 181// Verify that device file names will never result in Memory object creation.
124TEST_F(MapInfoCreateMemoryTest, check_device_maps) { 182TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
125 // Set up some memory so that a valid local memory object would 183 // Set up some memory so that a valid local memory object would
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index b757c1e56..dc9ae5a06 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -28,8 +28,11 @@
28#include <unwindstack/Memory.h> 28#include <unwindstack/Memory.h>
29 29
30int main(int argc, char** argv) { 30int main(int argc, char** argv) {
31 if (argc != 2) { 31 if (argc != 2 && argc != 3) {
32 printf("Need to pass the name of an elf file to the program.\n"); 32 printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
33 printf(" Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
34 printf(" specified, then get the function at that address.\n");
35 printf(" FUNC_ADDRESS must be a hex number.\n");
33 return 1; 36 return 1;
34 } 37 }
35 38
@@ -43,6 +46,16 @@ int main(int argc, char** argv) {
43 return 1; 46 return 1;
44 } 47 }
45 48
49 uint64_t func_addr;
50 if (argc == 3) {
51 char* name;
52 func_addr = strtoull(argv[2], &name, 16);
53 if (*name != '\0') {
54 printf("%s is not a hex number.\n", argv[2]);
55 return 1;
56 }
57 }
58
46 // Send all log messages to stdout. 59 // Send all log messages to stdout.
47 unwindstack::log_to_stdout(true); 60 unwindstack::log_to_stdout(true);
48 61
@@ -76,9 +89,24 @@ int main(int argc, char** argv) {
76 return 1; 89 return 1;
77 } 90 }
78 91
79 // This is a crude way to get the symbols in order.
80 std::string name; 92 std::string name;
81 uint64_t load_bias = elf.interface()->load_bias(); 93 uint64_t load_bias = elf.interface()->load_bias();
94 if (argc == 3) {
95 std::string cur_name;
96 uint64_t func_offset;
97 if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
98 printf("No known function at 0x%" PRIx64 "\n", func_addr);
99 return 1;
100 }
101 printf("<0x%" PRIx64 ">", func_addr - func_offset);
102 if (func_offset != 0) {
103 printf("+%" PRId64, func_offset);
104 }
105 printf(": %s\n", cur_name.c_str());
106 return 0;
107 }
108
109 // This is a crude way to get the symbols in order.
82 for (const auto& entry : elf.interface()->pt_loads()) { 110 for (const auto& entry : elf.interface()->pt_loads()) {
83 uint64_t start = entry.second.offset + load_bias; 111 uint64_t start = entry.second.offset + load_bias;
84 uint64_t end = entry.second.table_size + load_bias; 112 uint64_t end = entry.second.table_size + load_bias;