/*--------------------------------------------------------------------*/ /*--- Format-neutral storage of and querying of info acquired from ---*/ /*--- ELF/XCOFF stabs/dwarf1/dwarf2 debug info. ---*/ /*--- storage.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2006 Julian Seward jseward@acm.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ /* This file manages the data structures built by the debuginfo system. These are: the top level SegInfo list. For each SegInfo, there are tables for for address-to-symbol mappings, address-to-src-file/line mappings, and address-to-CFI-info mappings. */ #include "pub_core_basics.h" #include "pub_core_options.h" /* VG_(clo_verbosity) */ #include "pub_core_libcassert.h" #include "pub_core_libcbase.h" #include "pub_core_libcprint.h" #include "pub_core_mallocfree.h" #include "priv_storage.h" /* self */ /*------------------------------------------------------------*/ /*--- Misc (printing, errors) ---*/ /*------------------------------------------------------------*/ /* Show a non-fatal debug info reading error. Use vg_panic if terminal. */ void ML_(symerr) ( HChar* msg ) { if (VG_(clo_verbosity) > 1) VG_(message)(Vg_DebugMsg, "%s", msg); } /* Print a symbol. */ void ML_(ppSym) ( Int idx, DiSym* sym ) { VG_(printf)( "%5d: %8p .. %8p (%d) %s\n", idx, sym->addr, sym->addr + sym->size - 1, sym->size, sym->name ); } /* Print a call-frame-info summary. */ void ML_(ppDiCfSI) ( DiCfSI* si ) { # define SHOW_HOW(_how, _off) \ do { \ if (_how == CFIR_UNKNOWN) { \ VG_(printf)("Unknown"); \ } else \ if (_how == CFIR_SAME) { \ VG_(printf)("Same"); \ } else \ if (_how == CFIR_CFAREL) { \ VG_(printf)("cfa+%d", _off); \ } else \ if (_how == CFIR_MEMCFAREL) { \ VG_(printf)("*(cfa+%d)", _off); \ } else { \ VG_(printf)("???"); \ } \ } while (0) VG_(printf)("[%p .. %p]: ", si->base, si->base + (UWord)si->len - 1); VG_(printf)("let cfa=%s+%d", si->cfa_sprel ? "oldSP" : "oldFP", si->cfa_off); VG_(printf)(" in RA="); SHOW_HOW(si->ra_how, si->ra_off); VG_(printf)(" SP="); SHOW_HOW(si->sp_how, si->sp_off); VG_(printf)(" FP="); SHOW_HOW(si->fp_how, si->fp_off); VG_(printf)("\n"); # undef SHOW_HOW } /*------------------------------------------------------------*/ /*--- Adding stuff ---*/ /*------------------------------------------------------------*/ /* Add a str to the string table, including terminating zero, and return pointer to the string in vg_strtab. Unless it's been seen recently, in which case we find the old pointer and return that. This avoids the most egregious duplications. JSGF: changed from returning an index to a pointer, and changed to a chunking memory allocator rather than reallocating, so the pointers are stable. */ UChar* ML_(addStr) ( struct _SegInfo* si, UChar* str, Int len ) { struct strchunk *chunk; Int space_needed; UChar* p; if (len == -1) len = VG_(strlen)(str); space_needed = 1 + len; // Allocate a new strtab chunk if necessary if (si->strchunks == NULL || (si->strchunks->strtab_used + space_needed) > SEGINFO_STRCHUNKSIZE) { chunk = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*chunk)); chunk->strtab_used = 0; chunk->next = si->strchunks; si->strchunks = chunk; } chunk = si->strchunks; p = &chunk->strtab[chunk->strtab_used]; VG_(memcpy)(p, str, len); chunk->strtab[chunk->strtab_used+len] = '\0'; chunk->strtab_used += space_needed; return p; } /* Add a symbol to the symbol table. */ void ML_(addSym) ( struct _SegInfo* si, DiSym* sym ) { UInt new_sz, i; DiSym* new_tab; /* Ignore zero-sized syms. */ if (sym->size == 0) return; if (si->symtab_used == si->symtab_size) { new_sz = 2 * si->symtab_size; if (new_sz == 0) new_sz = 500; new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(DiSym) ); if (si->symtab != NULL) { for (i = 0; i < si->symtab_used; i++) new_tab[i] = si->symtab[i]; VG_(arena_free)(VG_AR_SYMTAB, si->symtab); } si->symtab = new_tab; si->symtab_size = new_sz; } si->symtab[si->symtab_used] = *sym; si->symtab_used++; vg_assert(si->symtab_used <= si->symtab_size); } /* Add a location to the location table. */ static void addLoc ( struct _SegInfo* si, DiLoc* loc ) { UInt new_sz, i; DiLoc* new_tab; /* Zero-sized locs should have been ignored earlier */ vg_assert(loc->size > 0); if (si->loctab_used == si->loctab_size) { new_sz = 2 * si->loctab_size; if (new_sz == 0) new_sz = 500; new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(DiLoc) ); if (si->loctab != NULL) { for (i = 0; i < si->loctab_used; i++) new_tab[i] = si->loctab[i]; VG_(arena_free)(VG_AR_SYMTAB, si->loctab); } si->loctab = new_tab; si->loctab_size = new_sz; } si->loctab[si->loctab_used] = *loc; si->loctab_used++; vg_assert(si->loctab_used <= si->loctab_size); } /* Top-level place to call to add a source-location mapping entry. */ void ML_(addLineInfo) ( struct _SegInfo* si, UChar* filename, UChar* dirname, /* NULL == directory is unknown */ Addr this, Addr next, Int lineno, Int entry /* only needed for debug printing */ ) { static const Bool debug = False; DiLoc loc; Int size = next - this; /* Ignore zero-sized locs */ if (this == next) return; if (debug) VG_(printf)( " src %s %s line %d %p-%p\n", dirname ? dirname : (UChar*)"(unknown)", filename, lineno, this, next ); /* Maximum sanity checking. Some versions of GNU as do a shabby * job with stabs entries; if anything looks suspicious, revert to * a size of 1. This should catch the instruction of interest * (since if using asm-level debug info, one instruction will * correspond to one line, unlike with C-level debug info where * multiple instructions can map to the one line), but avoid * catching any other instructions bogusly. */ if (this > next) { if (VG_(clo_verbosity) > 2) { VG_(message)(Vg_DebugMsg, "warning: line info addresses out of order " "at entry %d: 0x%x 0x%x", entry, this, next); } size = 1; } if (size > MAX_LOC_SIZE) { if (0) VG_(message)(Vg_DebugMsg, "warning: line info address range too large " "at entry %d: %d", entry, size); size = 1; } /* vg_assert(this < si->start + si->size && next-1 >= si->start); */ if (this >= si->start + si->size || next-1 < si->start) { if (0) VG_(message)(Vg_DebugMsg, "warning: ignoring line info entry falling " "outside current SegInfo: %p %p %p %p", si->start, si->start + si->size, this, next-1); return; } vg_assert(lineno >= 0); if (lineno > MAX_LINENO) { static Bool complained = False; if (!complained) { complained = True; VG_(message)(Vg_UserMsg, "warning: ignoring line info entry with " "huge line number (%d)", lineno); VG_(message)(Vg_UserMsg, " Can't handle line numbers " "greater than %d, sorry", MAX_LINENO); VG_(message)(Vg_UserMsg, "(Nb: this message is only shown once)"); } return; } loc.addr = this; loc.size = (UShort)size; loc.lineno = lineno; loc.filename = filename; loc.dirname = dirname; if (0) VG_(message)(Vg_DebugMsg, "addLoc: addr %p, size %d, line %d, file %s", this,size,lineno,filename); addLoc ( si, &loc ); } /* Top-level place to call to add a CFI summary record. The supplied DiCfSI is copied. */ void ML_(addDiCfSI) ( struct _SegInfo* si, DiCfSI* cfsi ) { static const Bool debug = False; UInt new_sz, i; DiCfSI* new_tab; if (debug) { VG_(printf)("adding DiCfSI: "); ML_(ppDiCfSI)(cfsi); } vg_assert(cfsi->len > 0 && cfsi->len < 2000000); /* Rule out ones which are completely outside the segment. These probably indicate some kind of bug, but for the meantime ignore them. */ if ( cfsi->base + cfsi->len - 1 < si->start || si->start + si->size - 1 < cfsi->base ) { static Int complaints = 3; if (VG_(clo_trace_cfi) || complaints > 0) { complaints--; if (VG_(clo_verbosity) > 1) { VG_(message)( Vg_DebugMsg, "warning: DiCfSI %p .. %p outside segment %p .. %p", cfsi->base, cfsi->base + cfsi->len - 1, si->start, si->start + si->size - 1 ); } if (VG_(clo_trace_cfi)) ML_(ppDiCfSI)(cfsi); } return; } if (si->cfsi_used == si->cfsi_size) { new_sz = 2 * si->cfsi_size; if (new_sz == 0) new_sz = 20; new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(DiCfSI) ); if (si->cfsi != NULL) { for (i = 0; i < si->cfsi_used; i++) new_tab[i] = si->cfsi[i]; VG_(arena_free)(VG_AR_SYMTAB, si->cfsi); } si->cfsi = new_tab; si->cfsi_size = new_sz; } si->cfsi[si->cfsi_used] = *cfsi; si->cfsi_used++; vg_assert(si->cfsi_used <= si->cfsi_size); } /*------------------------------------------------------------*/ /*--- Canonicalisers ---*/ /*------------------------------------------------------------*/ /* Sort the symtab by starting address, and emit warnings if any symbols have overlapping address ranges. We use that old chestnut, shellsort. Mash the table around so as to establish the property that addresses are in order and the ranges to not overlap. This facilitates using binary search to map addresses to symbols when we come to query the table. */ static Int compare_DiSym ( void* va, void* vb ) { DiSym* a = (DiSym*)va; DiSym* b = (DiSym*)vb; if (a->addr < b->addr) return -1; if (a->addr > b->addr) return 1; return 0; } /* Two symbols have the same address. Which name do we prefer? The general rule is to prefer the shorter symbol name. If the symbol contains a '@', which means its versioned, then the length up to the '@' is used for length comparison purposes (so "foo@GLIBC_2.4.2" is considered shorter than "foobar"), but if two symbols have the same length, the one with the version string is preferred. If all else fails, use alphabetical ordering. Very occasionally this goes wrong (eg. 'memcmp' and 'bcmp' are aliases in glibc, we choose the 'bcmp' symbol because it's shorter, so we can misdescribe memcmp() as bcmp()). This is hard to avoid. It's mentioned in the FAQ file. */ static DiSym* prefersym ( DiSym* a, DiSym* b ) { Int lena, lenb; /* full length */ Int vlena, vlenb; /* length without version */ const UChar *vpa, *vpb; vlena = lena = VG_(strlen)(a->name); vlenb = lenb = VG_(strlen)(b->name); vpa = VG_(strchr)(a->name, '@'); vpb = VG_(strchr)(b->name, '@'); if (vpa) vlena = vpa - a->name; if (vpb) vlenb = vpb - b->name; TRACE_SYMTAB("choosing between '%s' and '%s'\n", a->name, b->name); /* MPI hack: prefer PMPI_Foo over MPI_Foo */ if (0==VG_(strncmp)(a->name, "MPI_", 4) && 0==VG_(strncmp)(b->name, "PMPI_", 5) && 0==VG_(strcmp)(a->name, 1+b->name)) return b; else if (0==VG_(strncmp)(b->name, "MPI_", 4) && 0==VG_(strncmp)(a->name, "PMPI_", 5) && 0==VG_(strcmp)(b->name, 1+a->name)) return a; /* Select the shortest unversioned name */ if (vlena < vlenb) return a; else if (vlenb < vlena) return b; /* Equal lengths; select the versioned name */ if (vpa && !vpb) return a; if (vpb && !vpa) return b; /* Either both versioned or neither is versioned; select them alphabetically */ if (VG_(strcmp)(a->name, b->name) < 0) return a; else return b; } static void canonicaliseSymtab ( struct _SegInfo* si ) { Int i, j, n_merged, n_truncated; Addr s1, s2, e1, e2; # define SWAP(ty,aa,bb) \ do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0) if (si->symtab_used == 0) return; VG_(ssort)(si->symtab, si->symtab_used, sizeof(*si->symtab), compare_DiSym); cleanup_more: /* If two symbols have identical address ranges, we pick one using prefersym() (see it for details). */ do { n_merged = 0; j = si->symtab_used; si->symtab_used = 0; for (i = 0; i < j; i++) { if (i < j-1 && si->symtab[i].addr == si->symtab[i+1].addr && si->symtab[i].size == si->symtab[i+1].size) { n_merged++; /* merge the two into one */ si->symtab[si->symtab_used++] = *prefersym(&si->symtab[i], &si->symtab[i+1]); i++; } else { si->symtab[si->symtab_used++] = si->symtab[i]; } } TRACE_SYMTAB( "%d merged\n", n_merged); } while (n_merged > 0); /* Detect and "fix" overlapping address ranges. */ n_truncated = 0; for (i = 0; i < ((Int)si->symtab_used) -1; i++) { vg_assert(si->symtab[i].addr <= si->symtab[i+1].addr); /* Check for common (no overlap) case. */ if (si->symtab[i].addr + si->symtab[i].size <= si->symtab[i+1].addr) continue; /* There's an overlap. Truncate one or the other. */ if (VG_(clo_trace_symtab)) { VG_(printf)("overlapping address ranges in symbol table\n\t"); ML_(ppSym)( i, &si->symtab[i] ); VG_(printf)("\t"); ML_(ppSym)( i+1, &si->symtab[i+1] ); VG_(printf)("\n"); } /* Truncate one or the other. */ s1 = si->symtab[i].addr; s2 = si->symtab[i+1].addr; e1 = s1 + si->symtab[i].size - 1; e2 = s2 + si->symtab[i+1].size - 1; if (s1 < s2) { e1 = s2-1; } else { vg_assert(s1 == s2); if (e1 > e2) { s1 = e2+1; SWAP(Addr,s1,s2); SWAP(Addr,e1,e2); } else if (e1 < e2) { s2 = e1+1; } else { /* e1 == e2. Identical addr ranges. We'll eventually wind up back at cleanup_more, which will take care of it. */ } } si->symtab[i].addr = s1; si->symtab[i+1].addr = s2; si->symtab[i].size = e1 - s1 + 1; si->symtab[i+1].size = e2 - s2 + 1; vg_assert(s1 <= s2); vg_assert(si->symtab[i].size > 0); vg_assert(si->symtab[i+1].size > 0); /* It may be that the i+1 entry now needs to be moved further along to maintain the address order requirement. */ j = i+1; while (j < ((Int)si->symtab_used)-1 && si->symtab[j].addr > si->symtab[j+1].addr) { SWAP(DiSym,si->symtab[j],si->symtab[j+1]); j++; } n_truncated++; } if (n_truncated > 0) goto cleanup_more; /* Ensure relevant postconditions hold. */ for (i = 0; i < ((Int)si->symtab_used)-1; i++) { /* No zero-sized symbols. */ vg_assert(si->symtab[i].size > 0); /* In order. */ vg_assert(si->symtab[i].addr < si->symtab[i+1].addr); /* No overlaps. */ vg_assert(si->symtab[i].addr + si->symtab[i].size - 1 < si->symtab[i+1].addr); } # undef SWAP } /* Sort the location table by starting address. Mash the table around so as to establish the property that addresses are in order and the ranges do not overlap. This facilitates using binary search to map addresses to locations when we come to query the table. */ static Int compare_RiLoc ( void* va, void* vb ) { DiLoc* a = (DiLoc*)va; DiLoc* b = (DiLoc*)vb; if (a->addr < b->addr) return -1; if (a->addr > b->addr) return 1; return 0; } static void canonicaliseLoctab ( struct _SegInfo* si ) { Int i, j; # define SWAP(ty,aa,bb) \ do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0); if (si->loctab_used == 0) return; /* Sort by start address. */ VG_(ssort)(si->loctab, si->loctab_used, sizeof(*si->loctab), compare_RiLoc); /* If two adjacent entries overlap, truncate the first. */ for (i = 0; i < ((Int)si->loctab_used)-1; i++) { vg_assert(si->loctab[i].size < 10000); if (si->loctab[i].addr + si->loctab[i].size > si->loctab[i+1].addr) { /* Do this in signed int32 because the actual .size fields are only 12 bits. */ Int new_size = si->loctab[i+1].addr - si->loctab[i].addr; if (new_size < 0) { si->loctab[i].size = 0; } else if (new_size > MAX_LOC_SIZE) { si->loctab[i].size = MAX_LOC_SIZE; } else { si->loctab[i].size = (UShort)new_size; } } } /* Zap any zero-sized entries resulting from the truncation process. */ j = 0; for (i = 0; i < (Int)si->loctab_used; i++) { if (si->loctab[i].size > 0) { if (j != i) si->loctab[j] = si->loctab[i]; j++; } } si->loctab_used = j; /* Ensure relevant postconditions hold. */ for (i = 0; i < ((Int)si->loctab_used)-1; i++) { /* VG_(printf)("%d (%d) %d 0x%x\n", i, si->loctab[i+1].confident, si->loctab[i+1].size, si->loctab[i+1].addr ); */ /* No zero-sized symbols. */ vg_assert(si->loctab[i].size > 0); /* In order. */ vg_assert(si->loctab[i].addr < si->loctab[i+1].addr); /* No overlaps. */ vg_assert(si->loctab[i].addr + si->loctab[i].size - 1 < si->loctab[i+1].addr); } # undef SWAP } /* Sort the call-frame-info table by starting address. Mash the table around so as to establish the property that addresses are in order and the ranges do not overlap. This facilitates using binary search to map addresses to locations when we come to query the table. Also, set cfisi_minaddr and cfisi_maxaddr to be the min and max of any of the address ranges contained in cfisi[0 .. cfisi_used-1], so as to facilitate rapidly skipping this SegInfo when looking for an address which falls outside that range. */ static Int compare_DiCfSI ( void* va, void* vb ) { DiCfSI* a = (DiCfSI*)va; DiCfSI* b = (DiCfSI*)vb; if (a->base < b->base) return -1; if (a->base > b->base) return 1; return 0; } static void canonicaliseCFI ( struct _SegInfo* si ) { Int i, j; const Addr minAddr = 0; const Addr maxAddr = ~minAddr; /* Note: take care in here. si->cfsi can be NULL, in which case _used and _size fields will be zero. */ if (si->cfsi == NULL) { vg_assert(si->cfsi_used == 0); vg_assert(si->cfsi_size == 0); } /* Set cfsi_minaddr and cfsi_maxaddr to summarise the entire address range contained in cfsi[0 .. cfsi_used-1]. */ si->cfsi_minaddr = maxAddr; si->cfsi_maxaddr = minAddr; for (i = 0; i < (Int)si->cfsi_used; i++) { Addr here_min = si->cfsi[i].base; Addr here_max = si->cfsi[i].base + si->cfsi[i].len - 1; if (here_min < si->cfsi_minaddr) si->cfsi_minaddr = here_min; if (here_max > si->cfsi_maxaddr) si->cfsi_maxaddr = here_max; } if (VG_(clo_trace_cfi)) VG_(printf)("canonicaliseCfiSI: %d entries, %p .. %p\n", si->cfsi_used, si->cfsi_minaddr, si->cfsi_maxaddr); /* Sort the cfsi array by base address. */ VG_(ssort)(si->cfsi, si->cfsi_used, sizeof(*si->cfsi), compare_DiCfSI); /* If two adjacent entries overlap, truncate the first. */ for (i = 0; i < (Int)si->cfsi_used-1; i++) { if (si->cfsi[i].base + si->cfsi[i].len > si->cfsi[i+1].base) { Int new_len = si->cfsi[i+1].base - si->cfsi[i].base; /* how could it be otherwise? The entries are sorted by the .base field. */ vg_assert(new_len >= 0); vg_assert(new_len <= si->cfsi[i].len); si->cfsi[i].len = new_len; } } /* Zap any zero-sized entries resulting from the truncation process. */ j = 0; for (i = 0; i < (Int)si->cfsi_used; i++) { if (si->cfsi[i].len > 0) { if (j != i) si->cfsi[j] = si->cfsi[i]; j++; } } /* VG_(printf)("XXXXXXXXXXXXX %d %d\n", si->cfsi_used, j); */ si->cfsi_used = j; /* Ensure relevant postconditions hold. */ for (i = 0; i < (Int)si->cfsi_used; i++) { /* No zero-length ranges. */ vg_assert(si->cfsi[i].len > 0); /* Makes sense w.r.t. summary address range */ vg_assert(si->cfsi[i].base >= si->cfsi_minaddr); vg_assert(si->cfsi[i].base + si->cfsi[i].len - 1 <= si->cfsi_maxaddr); if (i < si->cfsi_used - 1) { /* if (!(si->cfsi[i].base < si->cfsi[i+1].base)) { VG_(printf)("\nOOO cfsis:\n"); ML_(ppCfiSI)(&si->cfsi[i]); ML_(ppCfiSI)(&si->cfsi[i+1]); } */ /* In order. */ vg_assert(si->cfsi[i].base < si->cfsi[i+1].base); /* No overlaps. */ vg_assert(si->cfsi[i].base + si->cfsi[i].len - 1 < si->cfsi[i+1].base); } } } /* Canonicalise the tables held by 'si', in preparation for use. Call this after finishing adding entries to these tables. */ void ML_(canonicaliseTables) ( struct _SegInfo* si ) { canonicaliseSymtab ( si ); canonicaliseLoctab ( si ); canonicaliseCFI ( si ); } /*------------------------------------------------------------*/ /*--- Searching the tables ---*/ /*------------------------------------------------------------*/ /* Find a symbol-table index containing the specified pointer, or -1 if not found. Binary search. */ Int ML_(search_one_symtab) ( struct _SegInfo* si, Addr ptr, Bool match_anywhere_in_fun ) { Addr a_mid_lo, a_mid_hi; Int mid, size, lo = 0, hi = si->symtab_used-1; while (True) { /* current unsearched space is from lo to hi, inclusive. */ if (lo > hi) return -1; /* not found */ mid = (lo + hi) / 2; a_mid_lo = si->symtab[mid].addr; size = ( match_anywhere_in_fun ? si->symtab[mid].size : 1); a_mid_hi = ((Addr)si->symtab[mid].addr) + size - 1; if (ptr < a_mid_lo) { hi = mid-1; continue; } if (ptr > a_mid_hi) { lo = mid+1; continue; } vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi); return mid; } } /* Find a location-table index containing the specified pointer, or -1 if not found. Binary search. */ Int ML_(search_one_loctab) ( struct _SegInfo* si, Addr ptr ) { Addr a_mid_lo, a_mid_hi; Int mid, lo = 0, hi = si->loctab_used-1; while (True) { /* current unsearched space is from lo to hi, inclusive. */ if (lo > hi) return -1; /* not found */ mid = (lo + hi) / 2; a_mid_lo = si->loctab[mid].addr; a_mid_hi = ((Addr)si->loctab[mid].addr) + si->loctab[mid].size - 1; if (ptr < a_mid_lo) { hi = mid-1; continue; } if (ptr > a_mid_hi) { lo = mid+1; continue; } vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi); return mid; } } /* Find a CFI-table index containing the specified pointer, or -1 if not found. Binary search. */ Int ML_(search_one_cfitab) ( struct _SegInfo* si, Addr ptr ) { Addr a_mid_lo, a_mid_hi; Int mid, size, lo = 0, hi = si->cfsi_used-1; while (True) { /* current unsearched space is from lo to hi, inclusive. */ if (lo > hi) return -1; /* not found */ mid = (lo + hi) / 2; a_mid_lo = si->cfsi[mid].base; size = si->cfsi[mid].len; a_mid_hi = a_mid_lo + size - 1; vg_assert(a_mid_hi >= a_mid_lo); if (ptr < a_mid_lo) { hi = mid-1; continue; } if (ptr > a_mid_hi) { lo = mid+1; continue; } vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi); return mid; } } /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/