Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * * Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * * Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in |
| 12 | * the documentation and/or other materials provided with the |
| 13 | * distribution. |
| 14 | * |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 18 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 19 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| 22 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| 25 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 26 | * SUCH DAMAGE. |
| 27 | */ |
| 28 | |
| 29 | #include <gtest/gtest.h> |
| 30 | |
| 31 | #include <link.h> |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 32 | #if __has_include(<sys/auxv.h>) |
| 33 | #include <sys/auxv.h> |
| 34 | #endif |
| 35 | |
| 36 | #include <string> |
| 37 | #include <unordered_map> |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 38 | |
| 39 | TEST(link, dl_iterate_phdr_early_exit) { |
| 40 | static size_t call_count = 0; |
| 41 | ASSERT_EQ(123, dl_iterate_phdr([](dl_phdr_info*, size_t, void*) { ++call_count; return 123; }, |
| 42 | nullptr)); |
| 43 | ASSERT_EQ(1u, call_count); |
| 44 | } |
| 45 | |
| 46 | TEST(link, dl_iterate_phdr) { |
| 47 | struct Functor { |
| 48 | static int Callback(dl_phdr_info* i, size_t s, void* data) { |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 49 | static_cast<Functor*>(data)->DoChecks(i, s); |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 50 | return 0; |
| 51 | } |
| 52 | void DoChecks(dl_phdr_info* info, size_t s) { |
| 53 | ASSERT_EQ(sizeof(dl_phdr_info), s); |
| 54 | |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 55 | ASSERT_TRUE(info->dlpi_name != nullptr); |
| 56 | |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 57 | // An ELF file must have at least a PT_LOAD program header. |
| 58 | ASSERT_NE(nullptr, info->dlpi_phdr); |
| 59 | ASSERT_NE(0, info->dlpi_phnum); |
| 60 | |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 61 | // Find the first PT_LOAD program header so we can find the ELF header. |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 62 | bool found_load = false; |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 63 | for (ElfW(Half) i = 0; i < info->dlpi_phnum; ++i) { |
| 64 | const ElfW(Phdr)* phdr = reinterpret_cast<const ElfW(Phdr)*>(&info->dlpi_phdr[i]); |
| 65 | if (phdr->p_type == PT_LOAD) { |
| 66 | const ElfW(Ehdr)* ehdr = reinterpret_cast<const ElfW(Ehdr)*>(info->dlpi_addr + |
| 67 | phdr->p_vaddr); |
| 68 | // Does it look like an ELF file? |
| 69 | ASSERT_EQ(0, memcmp(ehdr, ELFMAG, SELFMAG)); |
| 70 | // Does the e_phnum match what dl_iterate_phdr told us? |
| 71 | ASSERT_EQ(info->dlpi_phnum, ehdr->e_phnum); |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 72 | found_load = true; |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 73 | break; |
| 74 | } |
| 75 | } |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 76 | ASSERT_EQ(true, found_load); |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 77 | } |
| 78 | size_t count; |
| 79 | } f = {}; |
| 80 | ASSERT_EQ(0, dl_iterate_phdr(Functor::Callback, &f)); |
| 81 | } |
| 82 | |
Ryan Prichard | c20f9a5 | 2018-08-21 18:34:21 -0700 | [diff] [blame] | 83 | struct ProgHdr { |
| 84 | const ElfW(Phdr)* table; |
| 85 | size_t size; |
| 86 | }; |
| 87 | |
| 88 | __attribute__((__unused__)) |
| 89 | static ElfW(Addr) find_exe_load_bias(const ProgHdr& phdr) { |
| 90 | for (size_t i = 0; i < phdr.size; ++i) { |
| 91 | if (phdr.table[i].p_type == PT_PHDR) { |
| 92 | return reinterpret_cast<ElfW(Addr)>(phdr.table) - phdr.table[i].p_vaddr; |
| 93 | } |
| 94 | } |
| 95 | return 0; |
| 96 | } |
| 97 | |
| 98 | __attribute__((__unused__)) |
| 99 | static ElfW(Dyn)* find_dynamic(const ProgHdr& phdr, ElfW(Addr) load_bias) { |
| 100 | for (size_t i = 0; i < phdr.size; ++i) { |
| 101 | if (phdr.table[i].p_type == PT_DYNAMIC) { |
| 102 | return reinterpret_cast<ElfW(Dyn)*>(phdr.table[i].p_vaddr + load_bias); |
| 103 | } |
| 104 | } |
| 105 | return nullptr; |
| 106 | } |
| 107 | |
| 108 | __attribute__((__unused__)) |
| 109 | static r_debug* find_exe_r_debug(ElfW(Dyn)* dynamic) { |
| 110 | for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { |
| 111 | if (d->d_tag == DT_DEBUG) { |
| 112 | return reinterpret_cast<r_debug*>(d->d_un.d_val); |
| 113 | } |
| 114 | } |
| 115 | return nullptr; |
| 116 | } |
| 117 | |
| 118 | // Walk the DT_DEBUG/_r_debug global module list and compare it with the same |
| 119 | // information from dl_iterate_phdr. Verify that the executable appears first |
| 120 | // in _r_debug. |
| 121 | TEST(link, r_debug) { |
| 122 | #if __has_include(<sys/auxv.h>) |
| 123 | // Find the executable's PT_DYNAMIC segment and DT_DEBUG value. The linker |
| 124 | // will write the address of its _r_debug global into the .dynamic section. |
| 125 | ProgHdr exe_phdr = { |
| 126 | .table = reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), |
| 127 | .size = getauxval(AT_PHNUM) |
| 128 | }; |
| 129 | ASSERT_NE(nullptr, exe_phdr.table); |
| 130 | ElfW(Addr) exe_load_bias = find_exe_load_bias(exe_phdr); |
| 131 | ASSERT_NE(0u, exe_load_bias); |
| 132 | ElfW(Dyn)* exe_dynamic = find_dynamic(exe_phdr, exe_load_bias); |
| 133 | ASSERT_NE(nullptr, exe_dynamic); |
| 134 | r_debug* dbg = find_exe_r_debug(exe_dynamic); |
| 135 | ASSERT_NE(nullptr, dbg); |
| 136 | |
| 137 | // Use dl_iterate_phdr to build a table mapping from load bias values to |
| 138 | // solib names and PT_DYNAMIC segments. |
| 139 | struct DlIterateInfo { |
| 140 | std::string name; |
| 141 | ElfW(Dyn)* dynamic; |
| 142 | }; |
| 143 | struct Functor { |
| 144 | std::unordered_map<ElfW(Addr), DlIterateInfo> dl_iter_mods; |
| 145 | static int Callback(dl_phdr_info* i, size_t s, void* data) { |
| 146 | static_cast<Functor*>(data)->AddModule(i, s); |
| 147 | return 0; |
| 148 | } |
| 149 | void AddModule(dl_phdr_info* info, size_t s) { |
| 150 | ASSERT_EQ(sizeof(dl_phdr_info), s); |
| 151 | ASSERT_TRUE(dl_iter_mods.find(info->dlpi_addr) == dl_iter_mods.end()); |
| 152 | ASSERT_TRUE(info->dlpi_name != nullptr); |
| 153 | dl_iter_mods[info->dlpi_addr] = { |
| 154 | .name = info->dlpi_name, |
| 155 | .dynamic = find_dynamic({ info->dlpi_phdr, info->dlpi_phnum }, info->dlpi_addr) |
| 156 | }; |
| 157 | } |
| 158 | } f = {}; |
| 159 | ASSERT_EQ(0, dl_iterate_phdr(Functor::Callback, &f)); |
| 160 | |
| 161 | size_t map_size = 0; |
| 162 | |
| 163 | for (link_map* map = dbg->r_map; map != nullptr; map = map->l_next) { |
| 164 | ASSERT_NE(0u, map->l_addr); |
| 165 | ASSERT_NE(nullptr, map->l_ld); |
| 166 | ASSERT_NE(nullptr, map->l_name); |
| 167 | |
| 168 | auto it = f.dl_iter_mods.find(map->l_addr); |
| 169 | ASSERT_TRUE(it != f.dl_iter_mods.end()); |
| 170 | const DlIterateInfo& info = it->second; |
| 171 | ASSERT_EQ(info.name, map->l_name); |
| 172 | ASSERT_EQ(info.dynamic, map->l_ld); |
| 173 | |
| 174 | ++map_size; |
| 175 | } |
| 176 | |
| 177 | // _r_debug and dl_iterate_phdr should report the same set of modules. We |
| 178 | // verified above that every _r_debug module was reported by dl_iterate_phdr, |
| 179 | // so checking the sizes verifies the converse. |
| 180 | ASSERT_EQ(f.dl_iter_mods.size(), map_size); |
| 181 | |
| 182 | // Make sure the first entry is the executable. gdbserver assumes this and |
| 183 | // removes the first entry from its list of shared objects that it sends back |
| 184 | // to gdb. |
| 185 | ASSERT_EQ(exe_load_bias, dbg->r_map->l_addr); |
| 186 | ASSERT_EQ(exe_dynamic, dbg->r_map->l_ld); |
| 187 | #endif |
| 188 | } |
| 189 | |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 190 | #if __arm__ |
| 191 | static uintptr_t read_exidx_func(uintptr_t* entry) { |
| 192 | int32_t offset = *entry; |
| 193 | // Sign-extend from int31 to int32. |
| 194 | if ((offset & 0x40000000) != 0) { |
| 195 | offset += -0x7fffffff - 1; |
| 196 | } |
| 197 | return reinterpret_cast<uintptr_t>(entry) + offset; |
| 198 | } |
| 199 | __attribute__((__unused__)) static void another_function_in_same_ELF_file() {} |
| 200 | #endif |
| 201 | |
| 202 | TEST(link, dl_unwind_find_exidx) { |
| 203 | #if __arm__ |
| 204 | int count = 0; |
| 205 | struct eit_entry_t { |
| 206 | uintptr_t one; |
| 207 | uintptr_t two; |
| 208 | }; |
| 209 | eit_entry_t* entries = reinterpret_cast<eit_entry_t*>(dl_unwind_find_exidx( |
| 210 | reinterpret_cast<_Unwind_Ptr>(read_exidx_func), &count)); |
| 211 | ASSERT_TRUE(entries != nullptr); |
| 212 | ASSERT_GT(count, 0); |
| 213 | |
| 214 | // Sanity checks |
| 215 | uintptr_t func = reinterpret_cast<uintptr_t>(read_exidx_func); |
| 216 | bool found = false; |
| 217 | for (int i = 0; i < count; ++i) { |
| 218 | // Entries must have bit 31 clear. |
| 219 | ASSERT_TRUE((entries[i].one & (1<<31)) == 0); |
| 220 | |
| 221 | uintptr_t exidx_func = read_exidx_func(&entries[i].one); |
| 222 | |
| 223 | // If our function is compiled for thumb, exception table contains our address - 1. |
| 224 | if (func == exidx_func || func == exidx_func + 1) found = true; |
| 225 | |
| 226 | // Entries must be sorted. Some addresses may appear twice if function |
| 227 | // is compiled for arm. |
| 228 | if (i > 0) { |
| 229 | EXPECT_GE(exidx_func, read_exidx_func(&entries[i - 1].one)) << i; |
| 230 | } |
| 231 | } |
| 232 | ASSERT_TRUE(found); |
| 233 | #else |
Elliott Hughes | bcaa454 | 2019-03-08 15:20:23 -0800 | [diff] [blame] | 234 | GTEST_SKIP() << "dl_unwind_find_exidx is an ARM-only API"; |
Elliott Hughes | 7c10abb | 2017-04-21 17:15:41 -0700 | [diff] [blame] | 235 | #endif |
| 236 | } |