1da0c48c4Sopenharmony_ci/* Manage address space lookup table for libdwfl. 2da0c48c4Sopenharmony_ci Copyright (C) 2008, 2009, 2010, 2013, 2015 Red Hat, Inc. 3da0c48c4Sopenharmony_ci This file is part of elfutils. 4da0c48c4Sopenharmony_ci 5da0c48c4Sopenharmony_ci This file is free software; you can redistribute it and/or modify 6da0c48c4Sopenharmony_ci it under the terms of either 7da0c48c4Sopenharmony_ci 8da0c48c4Sopenharmony_ci * the GNU Lesser General Public License as published by the Free 9da0c48c4Sopenharmony_ci Software Foundation; either version 3 of the License, or (at 10da0c48c4Sopenharmony_ci your option) any later version 11da0c48c4Sopenharmony_ci 12da0c48c4Sopenharmony_ci or 13da0c48c4Sopenharmony_ci 14da0c48c4Sopenharmony_ci * the GNU General Public License as published by the Free 15da0c48c4Sopenharmony_ci Software Foundation; either version 2 of the License, or (at 16da0c48c4Sopenharmony_ci your option) any later version 17da0c48c4Sopenharmony_ci 18da0c48c4Sopenharmony_ci or both in parallel, as here. 19da0c48c4Sopenharmony_ci 20da0c48c4Sopenharmony_ci elfutils is distributed in the hope that it will be useful, but 21da0c48c4Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 22da0c48c4Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23da0c48c4Sopenharmony_ci General Public License for more details. 24da0c48c4Sopenharmony_ci 25da0c48c4Sopenharmony_ci You should have received copies of the GNU General Public License and 26da0c48c4Sopenharmony_ci the GNU Lesser General Public License along with this program. If 27da0c48c4Sopenharmony_ci not, see <http://www.gnu.org/licenses/>. */ 28da0c48c4Sopenharmony_ci 29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H 30da0c48c4Sopenharmony_ci# include <config.h> 31da0c48c4Sopenharmony_ci#endif 32da0c48c4Sopenharmony_ci 33da0c48c4Sopenharmony_ci#include "libdwflP.h" 34da0c48c4Sopenharmony_ci 35da0c48c4Sopenharmony_ciGElf_Addr 36da0c48c4Sopenharmony_ciinternal_function 37da0c48c4Sopenharmony_ci__libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start) 38da0c48c4Sopenharmony_ci{ 39da0c48c4Sopenharmony_ci if (dwfl->segment_align > 1) 40da0c48c4Sopenharmony_ci start &= -dwfl->segment_align; 41da0c48c4Sopenharmony_ci return start; 42da0c48c4Sopenharmony_ci} 43da0c48c4Sopenharmony_ci 44da0c48c4Sopenharmony_ciGElf_Addr 45da0c48c4Sopenharmony_ciinternal_function 46da0c48c4Sopenharmony_ci__libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end) 47da0c48c4Sopenharmony_ci{ 48da0c48c4Sopenharmony_ci if (dwfl->segment_align > 1) 49da0c48c4Sopenharmony_ci end = (end + dwfl->segment_align - 1) & -dwfl->segment_align; 50da0c48c4Sopenharmony_ci return end; 51da0c48c4Sopenharmony_ci} 52da0c48c4Sopenharmony_ci 53da0c48c4Sopenharmony_cistatic bool 54da0c48c4Sopenharmony_ciinsert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx) 55da0c48c4Sopenharmony_ci{ 56da0c48c4Sopenharmony_ci bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start); 57da0c48c4Sopenharmony_ci bool need_end = (i + 1 >= dwfl->lookup_elts 58da0c48c4Sopenharmony_ci || dwfl->lookup_addr[i + 1] != end); 59da0c48c4Sopenharmony_ci size_t need = need_start + need_end; 60da0c48c4Sopenharmony_ci if (need == 0) 61da0c48c4Sopenharmony_ci return false; 62da0c48c4Sopenharmony_ci 63da0c48c4Sopenharmony_ci if (dwfl->lookup_alloc - dwfl->lookup_elts < need) 64da0c48c4Sopenharmony_ci { 65da0c48c4Sopenharmony_ci size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2; 66da0c48c4Sopenharmony_ci GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n); 67da0c48c4Sopenharmony_ci if (unlikely (naddr == NULL)) 68da0c48c4Sopenharmony_ci return true; 69da0c48c4Sopenharmony_ci int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n); 70da0c48c4Sopenharmony_ci if (unlikely (nsegndx == NULL)) 71da0c48c4Sopenharmony_ci { 72da0c48c4Sopenharmony_ci if (naddr != dwfl->lookup_addr) 73da0c48c4Sopenharmony_ci free (naddr); 74da0c48c4Sopenharmony_ci return true; 75da0c48c4Sopenharmony_ci } 76da0c48c4Sopenharmony_ci dwfl->lookup_alloc = n; 77da0c48c4Sopenharmony_ci dwfl->lookup_addr = naddr; 78da0c48c4Sopenharmony_ci dwfl->lookup_segndx = nsegndx; 79da0c48c4Sopenharmony_ci 80da0c48c4Sopenharmony_ci if (dwfl->lookup_module != NULL) 81da0c48c4Sopenharmony_ci { 82da0c48c4Sopenharmony_ci /* Make sure this array is big enough too. */ 83da0c48c4Sopenharmony_ci Dwfl_Module **old = dwfl->lookup_module; 84da0c48c4Sopenharmony_ci dwfl->lookup_module = realloc (dwfl->lookup_module, 85da0c48c4Sopenharmony_ci sizeof dwfl->lookup_module[0] * n); 86da0c48c4Sopenharmony_ci if (unlikely (dwfl->lookup_module == NULL)) 87da0c48c4Sopenharmony_ci { 88da0c48c4Sopenharmony_ci free (old); 89da0c48c4Sopenharmony_ci return true; 90da0c48c4Sopenharmony_ci } 91da0c48c4Sopenharmony_ci } 92da0c48c4Sopenharmony_ci } 93da0c48c4Sopenharmony_ci 94da0c48c4Sopenharmony_ci if (unlikely (i < dwfl->lookup_elts)) 95da0c48c4Sopenharmony_ci { 96da0c48c4Sopenharmony_ci const size_t move = dwfl->lookup_elts - i; 97da0c48c4Sopenharmony_ci memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i], 98da0c48c4Sopenharmony_ci move * sizeof dwfl->lookup_addr[0]); 99da0c48c4Sopenharmony_ci memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i], 100da0c48c4Sopenharmony_ci move * sizeof dwfl->lookup_segndx[0]); 101da0c48c4Sopenharmony_ci if (dwfl->lookup_module != NULL) 102da0c48c4Sopenharmony_ci memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i], 103da0c48c4Sopenharmony_ci move * sizeof dwfl->lookup_module[0]); 104da0c48c4Sopenharmony_ci } 105da0c48c4Sopenharmony_ci 106da0c48c4Sopenharmony_ci if (need_start) 107da0c48c4Sopenharmony_ci { 108da0c48c4Sopenharmony_ci dwfl->lookup_addr[i] = start; 109da0c48c4Sopenharmony_ci dwfl->lookup_segndx[i] = segndx; 110da0c48c4Sopenharmony_ci if (dwfl->lookup_module != NULL) 111da0c48c4Sopenharmony_ci dwfl->lookup_module[i] = NULL; 112da0c48c4Sopenharmony_ci ++i; 113da0c48c4Sopenharmony_ci } 114da0c48c4Sopenharmony_ci else 115da0c48c4Sopenharmony_ci dwfl->lookup_segndx[i - 1] = segndx; 116da0c48c4Sopenharmony_ci 117da0c48c4Sopenharmony_ci if (need_end) 118da0c48c4Sopenharmony_ci { 119da0c48c4Sopenharmony_ci dwfl->lookup_addr[i] = end; 120da0c48c4Sopenharmony_ci dwfl->lookup_segndx[i] = -1; 121da0c48c4Sopenharmony_ci if (dwfl->lookup_module != NULL) 122da0c48c4Sopenharmony_ci dwfl->lookup_module[i] = NULL; 123da0c48c4Sopenharmony_ci } 124da0c48c4Sopenharmony_ci 125da0c48c4Sopenharmony_ci dwfl->lookup_elts += need; 126da0c48c4Sopenharmony_ci 127da0c48c4Sopenharmony_ci return false; 128da0c48c4Sopenharmony_ci} 129da0c48c4Sopenharmony_ci 130da0c48c4Sopenharmony_cistatic int 131da0c48c4Sopenharmony_cilookup (Dwfl *dwfl, GElf_Addr address, int hint) 132da0c48c4Sopenharmony_ci{ 133da0c48c4Sopenharmony_ci if (hint >= 0 134da0c48c4Sopenharmony_ci && address >= dwfl->lookup_addr[hint] 135da0c48c4Sopenharmony_ci && ((size_t) hint + 1 == dwfl->lookup_elts 136da0c48c4Sopenharmony_ci || address < dwfl->lookup_addr[hint + 1])) 137da0c48c4Sopenharmony_ci return hint; 138da0c48c4Sopenharmony_ci 139da0c48c4Sopenharmony_ci /* Do binary search on the array indexed by module load address. */ 140da0c48c4Sopenharmony_ci size_t l = 0, u = dwfl->lookup_elts; 141da0c48c4Sopenharmony_ci while (l < u) 142da0c48c4Sopenharmony_ci { 143da0c48c4Sopenharmony_ci size_t idx = (l + u) / 2; 144da0c48c4Sopenharmony_ci if (address < dwfl->lookup_addr[idx]) 145da0c48c4Sopenharmony_ci u = idx; 146da0c48c4Sopenharmony_ci else 147da0c48c4Sopenharmony_ci { 148da0c48c4Sopenharmony_ci l = idx + 1; 149da0c48c4Sopenharmony_ci if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l]) 150da0c48c4Sopenharmony_ci return idx; 151da0c48c4Sopenharmony_ci } 152da0c48c4Sopenharmony_ci } 153da0c48c4Sopenharmony_ci 154da0c48c4Sopenharmony_ci return -1; 155da0c48c4Sopenharmony_ci} 156da0c48c4Sopenharmony_ci 157da0c48c4Sopenharmony_cistatic bool 158da0c48c4Sopenharmony_cireify_segments (Dwfl *dwfl) 159da0c48c4Sopenharmony_ci{ 160da0c48c4Sopenharmony_ci int hint = -1; 161da0c48c4Sopenharmony_ci int highest = -1; 162da0c48c4Sopenharmony_ci bool fixup = false; 163da0c48c4Sopenharmony_ci for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) 164da0c48c4Sopenharmony_ci if (! mod->gc) 165da0c48c4Sopenharmony_ci { 166da0c48c4Sopenharmony_ci const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr); 167da0c48c4Sopenharmony_ci const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr); 168da0c48c4Sopenharmony_ci bool resized = false; 169da0c48c4Sopenharmony_ci 170da0c48c4Sopenharmony_ci int idx = lookup (dwfl, start, hint); 171da0c48c4Sopenharmony_ci if (unlikely (idx < 0)) 172da0c48c4Sopenharmony_ci { 173da0c48c4Sopenharmony_ci /* Module starts below any segment. Insert a low one. */ 174da0c48c4Sopenharmony_ci if (unlikely (insert (dwfl, 0, start, end, -1))) 175da0c48c4Sopenharmony_ci return true; 176da0c48c4Sopenharmony_ci idx = 0; 177da0c48c4Sopenharmony_ci resized = true; 178da0c48c4Sopenharmony_ci } 179da0c48c4Sopenharmony_ci else if (dwfl->lookup_addr[idx] > start) 180da0c48c4Sopenharmony_ci { 181da0c48c4Sopenharmony_ci /* The module starts in the middle of this segment. Split it. */ 182da0c48c4Sopenharmony_ci if (unlikely (insert (dwfl, idx + 1, start, end, 183da0c48c4Sopenharmony_ci dwfl->lookup_segndx[idx]))) 184da0c48c4Sopenharmony_ci return true; 185da0c48c4Sopenharmony_ci ++idx; 186da0c48c4Sopenharmony_ci resized = true; 187da0c48c4Sopenharmony_ci } 188da0c48c4Sopenharmony_ci else if (dwfl->lookup_addr[idx] < start) 189da0c48c4Sopenharmony_ci { 190da0c48c4Sopenharmony_ci /* The module starts past the end of this segment. 191da0c48c4Sopenharmony_ci Add a new one. */ 192da0c48c4Sopenharmony_ci if (unlikely (insert (dwfl, idx + 1, start, end, -1))) 193da0c48c4Sopenharmony_ci return true; 194da0c48c4Sopenharmony_ci ++idx; 195da0c48c4Sopenharmony_ci resized = true; 196da0c48c4Sopenharmony_ci } 197da0c48c4Sopenharmony_ci 198da0c48c4Sopenharmony_ci if ((size_t) idx + 1 < dwfl->lookup_elts 199da0c48c4Sopenharmony_ci && end < dwfl->lookup_addr[idx + 1]) 200da0c48c4Sopenharmony_ci { 201da0c48c4Sopenharmony_ci /* The module ends in the middle of this segment. Split it. */ 202da0c48c4Sopenharmony_ci if (unlikely (insert (dwfl, idx + 1, 203da0c48c4Sopenharmony_ci end, dwfl->lookup_addr[idx + 1], -1))) 204da0c48c4Sopenharmony_ci return true; 205da0c48c4Sopenharmony_ci resized = true; 206da0c48c4Sopenharmony_ci } 207da0c48c4Sopenharmony_ci 208da0c48c4Sopenharmony_ci if (dwfl->lookup_module == NULL) 209da0c48c4Sopenharmony_ci { 210da0c48c4Sopenharmony_ci dwfl->lookup_module = calloc (dwfl->lookup_alloc, 211da0c48c4Sopenharmony_ci sizeof dwfl->lookup_module[0]); 212da0c48c4Sopenharmony_ci if (unlikely (dwfl->lookup_module == NULL)) 213da0c48c4Sopenharmony_ci return true; 214da0c48c4Sopenharmony_ci } 215da0c48c4Sopenharmony_ci 216da0c48c4Sopenharmony_ci /* Cache a backpointer in the module. */ 217da0c48c4Sopenharmony_ci mod->segment = idx; 218da0c48c4Sopenharmony_ci 219da0c48c4Sopenharmony_ci /* Put MOD in the table for each segment that's inside it. */ 220da0c48c4Sopenharmony_ci do 221da0c48c4Sopenharmony_ci dwfl->lookup_module[idx++] = mod; 222da0c48c4Sopenharmony_ci while ((size_t) idx < dwfl->lookup_elts 223da0c48c4Sopenharmony_ci && dwfl->lookup_addr[idx] < end); 224da0c48c4Sopenharmony_ci assert (dwfl->lookup_module[mod->segment] == mod); 225da0c48c4Sopenharmony_ci 226da0c48c4Sopenharmony_ci if (resized && idx - 1 >= highest) 227da0c48c4Sopenharmony_ci /* Expanding the lookup tables invalidated backpointers 228da0c48c4Sopenharmony_ci we've already stored. Reset those ones. */ 229da0c48c4Sopenharmony_ci fixup = true; 230da0c48c4Sopenharmony_ci 231da0c48c4Sopenharmony_ci highest = idx - 1; 232da0c48c4Sopenharmony_ci hint = (size_t) idx < dwfl->lookup_elts ? idx : -1; 233da0c48c4Sopenharmony_ci } 234da0c48c4Sopenharmony_ci 235da0c48c4Sopenharmony_ci if (fixup) 236da0c48c4Sopenharmony_ci /* Reset backpointer indices invalidated by table insertions. */ 237da0c48c4Sopenharmony_ci for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx) 238da0c48c4Sopenharmony_ci if (dwfl->lookup_module[idx] != NULL) 239da0c48c4Sopenharmony_ci dwfl->lookup_module[idx]->segment = idx; 240da0c48c4Sopenharmony_ci 241da0c48c4Sopenharmony_ci return false; 242da0c48c4Sopenharmony_ci} 243da0c48c4Sopenharmony_ci 244da0c48c4Sopenharmony_ciint 245da0c48c4Sopenharmony_cidwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod) 246da0c48c4Sopenharmony_ci{ 247da0c48c4Sopenharmony_ci if (unlikely (dwfl == NULL)) 248da0c48c4Sopenharmony_ci return -1; 249da0c48c4Sopenharmony_ci 250da0c48c4Sopenharmony_ci if (unlikely (dwfl->lookup_module == NULL) 251da0c48c4Sopenharmony_ci && mod != NULL 252da0c48c4Sopenharmony_ci && unlikely (reify_segments (dwfl))) 253da0c48c4Sopenharmony_ci { 254da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_NOMEM); 255da0c48c4Sopenharmony_ci return -1; 256da0c48c4Sopenharmony_ci } 257da0c48c4Sopenharmony_ci 258da0c48c4Sopenharmony_ci int idx = lookup (dwfl, address, -1); 259da0c48c4Sopenharmony_ci if (likely (mod != NULL)) 260da0c48c4Sopenharmony_ci { 261da0c48c4Sopenharmony_ci if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL)) 262da0c48c4Sopenharmony_ci *mod = NULL; 263da0c48c4Sopenharmony_ci else 264da0c48c4Sopenharmony_ci { 265da0c48c4Sopenharmony_ci *mod = dwfl->lookup_module[idx]; 266da0c48c4Sopenharmony_ci 267da0c48c4Sopenharmony_ci /* If this segment does not have a module, but the address is 268da0c48c4Sopenharmony_ci the upper boundary of the previous segment's module, use that. */ 269da0c48c4Sopenharmony_ci if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address) 270da0c48c4Sopenharmony_ci { 271da0c48c4Sopenharmony_ci *mod = dwfl->lookup_module[idx - 1]; 272da0c48c4Sopenharmony_ci if (*mod != NULL && (*mod)->high_addr != address) 273da0c48c4Sopenharmony_ci *mod = NULL; 274da0c48c4Sopenharmony_ci } 275da0c48c4Sopenharmony_ci } 276da0c48c4Sopenharmony_ci } 277da0c48c4Sopenharmony_ci 278da0c48c4Sopenharmony_ci if (likely (idx >= 0)) 279da0c48c4Sopenharmony_ci /* Translate internal segment table index to user segment index. */ 280da0c48c4Sopenharmony_ci idx = dwfl->lookup_segndx[idx]; 281da0c48c4Sopenharmony_ci 282da0c48c4Sopenharmony_ci return idx; 283da0c48c4Sopenharmony_ci} 284da0c48c4Sopenharmony_ciINTDEF (dwfl_addrsegment) 285da0c48c4Sopenharmony_ci 286da0c48c4Sopenharmony_ciint 287da0c48c4Sopenharmony_cidwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias, 288da0c48c4Sopenharmony_ci const void *ident) 289da0c48c4Sopenharmony_ci{ 290da0c48c4Sopenharmony_ci /* This was previously used for coalescing segments, but it was buggy since 291da0c48c4Sopenharmony_ci day one. We don't use it anymore. */ 292da0c48c4Sopenharmony_ci (void)ident; 293da0c48c4Sopenharmony_ci 294da0c48c4Sopenharmony_ci if (dwfl == NULL) 295da0c48c4Sopenharmony_ci return -1; 296da0c48c4Sopenharmony_ci 297da0c48c4Sopenharmony_ci if (ndx < 0) 298da0c48c4Sopenharmony_ci ndx = dwfl->next_segndx; 299da0c48c4Sopenharmony_ci 300da0c48c4Sopenharmony_ci if (phdr->p_align > 1 && (dwfl->segment_align <= 1 || 301da0c48c4Sopenharmony_ci phdr->p_align < dwfl->segment_align)) 302da0c48c4Sopenharmony_ci dwfl->segment_align = phdr->p_align; 303da0c48c4Sopenharmony_ci 304da0c48c4Sopenharmony_ci if (unlikely (dwfl->lookup_module != NULL)) 305da0c48c4Sopenharmony_ci { 306da0c48c4Sopenharmony_ci free (dwfl->lookup_module); 307da0c48c4Sopenharmony_ci dwfl->lookup_module = NULL; 308da0c48c4Sopenharmony_ci } 309da0c48c4Sopenharmony_ci 310da0c48c4Sopenharmony_ci GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr); 311da0c48c4Sopenharmony_ci GElf_Addr end = __libdwfl_segment_end (dwfl, 312da0c48c4Sopenharmony_ci bias + phdr->p_vaddr + phdr->p_memsz); 313da0c48c4Sopenharmony_ci 314da0c48c4Sopenharmony_ci /* Normally just appending keeps us sorted. */ 315da0c48c4Sopenharmony_ci 316da0c48c4Sopenharmony_ci size_t i = dwfl->lookup_elts; 317da0c48c4Sopenharmony_ci while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1])) 318da0c48c4Sopenharmony_ci --i; 319da0c48c4Sopenharmony_ci 320da0c48c4Sopenharmony_ci if (unlikely (insert (dwfl, i, start, end, ndx))) 321da0c48c4Sopenharmony_ci { 322da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_NOMEM); 323da0c48c4Sopenharmony_ci return -1; 324da0c48c4Sopenharmony_ci } 325da0c48c4Sopenharmony_ci 326da0c48c4Sopenharmony_ci dwfl->next_segndx = ndx + 1; 327da0c48c4Sopenharmony_ci 328da0c48c4Sopenharmony_ci return ndx; 329da0c48c4Sopenharmony_ci} 330da0c48c4Sopenharmony_ciINTDEF (dwfl_report_segment) 331