Home | History | Annotate | Download | only in m_debuginfo
      1 /* -*- mode: C; c-basic-offset: 3; -*- */
      2 
      3 /*--------------------------------------------------------------------*/
      4 /*--- An abstraction that provides a file-reading mechanism.       ---*/
      5 /*---                                                      image.c ---*/
      6 /*--------------------------------------------------------------------*/
      7 
      8 /*
      9    This file is part of Valgrind, a dynamic binary instrumentation
     10    framework.
     11 
     12    Copyright (C) 2013-2013 Mozilla Foundation
     13 
     14    This program is free software; you can redistribute it and/or
     15    modify it under the terms of the GNU General Public License as
     16    published by the Free Software Foundation; either version 2 of the
     17    License, or (at your option) any later version.
     18 
     19    This program is distributed in the hope that it will be useful, but
     20    WITHOUT ANY WARRANTY; without even the implied warranty of
     21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     22    General Public License for more details.
     23 
     24    You should have received a copy of the GNU General Public License
     25    along with this program; if not, write to the Free Software
     26    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     27    02111-1307, USA.
     28 
     29    The GNU General Public License is contained in the file COPYING.
     30 */
     31 
     32 /* Contributed by Julian Seward <jseward (at) acm.org> */
     33 
     34 /* See the corresponding auxprogs/valgrind-di-server.c for a list of
     35    cleanups for this file and itself. */
     36 
     37 #include "pub_core_basics.h"
     38 #include "pub_core_vki.h"
     39 #include "pub_core_libcbase.h"
     40 #include "pub_core_libcassert.h"
     41 #include "pub_core_libcprint.h"
     42 #include "pub_core_libcproc.h"     /* VG_(read_millisecond_timer) */
     43 #include "pub_core_libcfile.h"
     44 #include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
     45 #include "priv_image.h"            /* self */
     46 
     47 #include "minilzo.h"
     48 
     49 /* These values (1024 entries of 8192 bytes each) gives a cache
     50    size of 8MB. */
     51 #define CACHE_ENTRY_SIZE_BITS (12+1)
     52 #define CACHE_N_ENTRIES       1024
     53 
     54 #define CACHE_ENTRY_SIZE      (1 << CACHE_ENTRY_SIZE_BITS)
     55 
     56 /* An entry in the cache. */
     57 typedef
     58    struct {
     59       DiOffT off; // file offset for data[0]
     60       SizeT  used; // 1 .. sizeof(data), or 0 to denote not-in-use
     61       UChar  data[CACHE_ENTRY_SIZE];
     62    }
     63    CEnt;
     64 
     65 /* Source for files */
     66 typedef
     67    struct {
     68       // True: img is of local file.  False: img is from a server.
     69       Bool  is_local;
     70       // The fd for the local file, or sd for a remote server.
     71       Int   fd;
     72       // The name.  In ML_(dinfo_zalloc)'d space.  Used only for printing
     73       // error messages; hence it doesn't really matter what this contains.
     74       HChar* name;
     75       // The rest of these fields are only valid when using remote files
     76       // (that is, using a debuginfo server; hence when is_local==False)
     77       // Session ID allocated to us by the server.  Cannot be zero.
     78       ULong session_id;
     79    }
     80    Source;
     81 
     82 struct _DiImage {
     83    // The source -- how to get hold of the file we are reading
     84    Source source;
     85    // Total size of the image.
     86    SizeT size;
     87    // The number of entries used.  0 .. CACHE_N_ENTRIES
     88    UInt  ces_used;
     89    // Pointers to the entries.  ces[0 .. ces_used-1] are non-NULL.
     90    // ces[ces_used .. CACHE_N_ENTRIES-1] are NULL.
     91    // The non-NULL entries may be arranged arbitrarily.  We expect to use
     92    // a pseudo-LRU scheme though.
     93    CEnt* ces[CACHE_N_ENTRIES];
     94 };
     95 
     96 /* A frame.  The first 4 bytes of |data| give the kind of the frame,
     97    and the rest of it is kind-specific data. */
     98 typedef  struct { UChar* data; SizeT n_data; }  Frame;
     99 
    100 static void write_UInt_le ( /*OUT*/UChar* dst, UInt n )
    101 {
    102    Int i;
    103    for (i = 0; i <= 3; i++) {
    104       dst[i] = (UChar)(n & 0xFF);
    105       n >>= 8;
    106    }
    107 }
    108 
    109 static UInt read_UInt_le ( const UChar* src )
    110 {
    111    UInt r = 0;
    112    Int i;
    113    for (i = 3; i >= 0; i--) {
    114       r <<= 8;
    115       r += (UInt)src[i];
    116    }
    117    return r;
    118 }
    119 
    120 static void write_ULong_le ( /*OUT*/UChar* dst, ULong n )
    121 {
    122    Int i;
    123    for (i = 0; i <= 7; i++) {
    124       dst[i] = (UChar)(n & 0xFF);
    125       n >>= 8;
    126    }
    127 }
    128 
    129 static ULong read_ULong_le ( const UChar* src )
    130 {
    131    ULong r = 0;
    132    Int i;
    133    for (i = 7; i >= 0; i--) {
    134       r <<= 8;
    135       r += (ULong)src[i];
    136    }
    137    return r;
    138 }
    139 
    140 
    141 /* Set |sd| to be blocking.  Returns True on success. */
    142 static Bool set_blocking ( int sd )
    143 {
    144    Int res;
    145    res = VG_(fcntl)(sd, VKI_F_GETFL, 0/*ignored*/);
    146    if (res != -1)
    147       res = VG_(fcntl)(sd, VKI_F_SETFL, res & ~VKI_O_NONBLOCK);
    148    return (res != -1);
    149 }
    150 
    151 /* Tries to read 'len' bytes from fd, blocking if necessary.  Assumes
    152    fd has been set in blocking mode.  If it returns with the number of
    153    bytes read < len, it means that either fd was closed, or there was
    154    an error on it. */
    155 static Int my_read ( Int fd, UChar* buf, Int len )
    156 {
    157    Int nRead = 0;
    158    while (1) {
    159       if (nRead == len) return nRead;
    160       vg_assert(nRead < len);
    161       Int nNeeded = len - nRead;
    162       vg_assert(nNeeded > 0);
    163       Int n = VG_(read)(fd, &buf[nRead], nNeeded);
    164       if (n <= 0) return nRead; /* error or EOF */
    165       nRead += n;
    166    }
    167 }
    168 
    169 /* Tries to write 'len' bytes to fd, blocking if necessary.  Assumes
    170    fd has been set in blocking mode.  If it returns with the number of
    171    bytes written < len, it means that either fd was closed, or there was
    172    an error on it. */
    173 static Int my_write ( Int fd, const UChar* buf, Int len )
    174 {
    175    Int nWritten = 0;
    176    while (1) {
    177       if (nWritten == len) return nWritten;
    178       vg_assert(nWritten < len);
    179       Int nStillToDo = len - nWritten;
    180       vg_assert(nStillToDo > 0);
    181       Int n = VG_(write_socket)(fd, &buf[nWritten], nStillToDo);
    182       if (n < 0) return nWritten; /* error or EOF */
    183       nWritten += n;
    184    }
    185 }
    186 
    187 /* If we lost communication with the remote server, just give up.
    188    Recovering is too difficult. */
    189 static void give_up__comms_lost(void)
    190 {
    191    VG_(umsg)("\n");
    192    VG_(umsg)(
    193       "Valgrind: debuginfo reader: Lost communication with the remote\n");
    194    VG_(umsg)(
    195       "Valgrind: debuginfo server.  I can't recover.  Giving up.  Sorry.\n");
    196    VG_(umsg)("\n");
    197    VG_(exit)(1);
    198    /*NOTREACHED*/
    199 }
    200 
    201 static void give_up__image_overrun(void)
    202 {
    203    VG_(umsg)("\n");
    204    VG_(umsg)(
    205       "Valgrind: debuginfo reader: Possibly corrupted debuginfo file.\n");
    206    VG_(umsg)(
    207       "Valgrind: I can't recover.  Giving up.  Sorry.\n");
    208    VG_(umsg)("\n");
    209    VG_(exit)(1);
    210    /*NOTREACHED*/
    211 }
    212 
    213 /* "Do" a transaction: that is, send the given frame to the server and
    214    return the frame it sends back.  Caller owns the resulting frame
    215    and must free it.  A NULL return means the transaction failed for
    216    some reason. */
    217 static Frame* do_transaction ( Int sd, const Frame* req )
    218 {
    219    if (0) VG_(printf)("CLIENT: send %c%c%c%c\n",
    220                       req->data[0], req->data[1], req->data[2], req->data[3]);
    221 
    222    /* What goes on the wire is:
    223          adler(le32) n_data(le32) data[0 .. n_data-1]
    224       where the checksum covers n_data as well as data[].
    225    */
    226    /* The initial Adler-32 value */
    227    UInt adler = VG_(adler32)(0, NULL, 0);
    228 
    229    /* Fold in the length field, encoded as le32. */
    230    UChar wr_first8[8];
    231    write_UInt_le(&wr_first8[4], req->n_data);
    232    adler = VG_(adler32)(adler, &wr_first8[4], 4);
    233    /* Fold in the data values */
    234    adler = VG_(adler32)(adler, req->data, req->n_data);
    235    write_UInt_le(&wr_first8[0], adler);
    236 
    237    Int r = my_write(sd, &wr_first8[0], 8);
    238    if (r != 8) return NULL;
    239    vg_assert(req->n_data >= 4); // else ill formed -- no KIND field
    240    r = my_write(sd, req->data, req->n_data);
    241    if (r != req->n_data) return NULL;
    242 
    243    /* So, the request is sent.  Now get a request of the same format
    244       out of the channel. */
    245    UChar rd_first8[8];  // adler32; length32
    246    r = my_read(sd, &rd_first8[0], 8);
    247    if (r != 8) return NULL;
    248    UInt rd_adler = read_UInt_le(&rd_first8[0]);
    249    UInt rd_len   = read_UInt_le(&rd_first8[4]);
    250    /* Allocate a Frame to hold the result data, and read into it. */
    251    // Reject obviously-insane length fields.
    252    if (rd_len < 4 || rd_len > 4*1024*1024) return NULL;
    253    Frame* res = ML_(dinfo_zalloc)("di.do_transaction.1", sizeof(Frame));
    254    res->n_data = rd_len;
    255    res->data = ML_(dinfo_zalloc)("di.do_transaction.2", rd_len);
    256    r = my_read(sd, res->data, res->n_data);
    257    if (r != rd_len) return NULL;
    258 
    259    if (0) VG_(printf)("CLIENT: recv %c%c%c%c\n",
    260                       res->data[0], res->data[1], res->data[2], res->data[3]);
    261 
    262    /* Compute the checksum for the received data, and check it. */
    263    adler = VG_(adler32)(0, NULL, 0); // initial value
    264    adler = VG_(adler32)(adler, &rd_first8[4], 4);
    265    if (res->n_data > 0)
    266       adler = VG_(adler32)(adler, res->data, res->n_data);
    267 
    268    if (adler/*computed*/ != rd_adler/*expected*/) return NULL;
    269    return res;
    270 }
    271 
    272 static void free_Frame ( Frame* fr )
    273 {
    274    vg_assert(fr && fr->data);
    275    ML_(dinfo_free)(fr->data);
    276    ML_(dinfo_free)(fr);
    277 }
    278 
    279 static Frame* mk_Frame_noargs ( const HChar* tag )
    280 {
    281    vg_assert(VG_(strlen)(tag) == 4);
    282    Frame* f = ML_(dinfo_zalloc)("di.mFn.1", sizeof(Frame));
    283    f->n_data = 4;
    284    f->data = ML_(dinfo_zalloc)("di.mFn.2", f->n_data);
    285    VG_(memcpy)(&f->data[0], tag, 4);
    286    return f;
    287 }
    288 
    289 static Frame* mk_Frame_le64_le64_le64 ( const HChar* tag,
    290                                         ULong n1, ULong n2, ULong n3 )
    291 {
    292    vg_assert(VG_(strlen)(tag) == 4);
    293    Frame* f = ML_(dinfo_zalloc)("di.mFlll.1", sizeof(Frame));
    294    f->n_data = 4 + 3*8;
    295    f->data = ML_(dinfo_zalloc)("di.mFlll.2", f->n_data);
    296    VG_(memcpy)(&f->data[0], tag, 4);
    297    write_ULong_le(&f->data[4 + 0*8], n1);
    298    write_ULong_le(&f->data[4 + 1*8], n2);
    299    write_ULong_le(&f->data[4 + 2*8], n3);
    300    return f;
    301 }
    302 
    303 static Frame* mk_Frame_asciiz ( const HChar* tag, const HChar* str )
    304 {
    305    vg_assert(VG_(strlen)(tag) == 4);
    306    Frame* f = ML_(dinfo_zalloc)("di.mFa.1", sizeof(Frame));
    307    SizeT n_str = VG_(strlen)(str);
    308    f->n_data = 4 + n_str + 1;
    309    f->data = ML_(dinfo_zalloc)("di.mFa.2", f->n_data);
    310    VG_(memcpy)(&f->data[0], tag, 4);
    311    VG_(memcpy)(&f->data[4], str, n_str);
    312    vg_assert(f->data[4 + n_str] == 0);
    313    return f;
    314 }
    315 
    316 static Bool parse_Frame_le64 ( const Frame* fr, const HChar* tag,
    317                                /*OUT*/ULong* n1 )
    318 {
    319    vg_assert(VG_(strlen)(tag) == 4);
    320    if (!fr || !fr->data) return False;
    321    if (fr->n_data < 4) return False;
    322    if (VG_(memcmp)(&fr->data[0], tag, 4) != 0) return False;
    323    if (fr->n_data != 4 + 1*8) return False;
    324    *n1 = read_ULong_le(&fr->data[4 + 0*8]);
    325    return True;
    326 }
    327 
    328 static Bool parse_Frame_le64_le64 ( const Frame* fr, const HChar* tag,
    329                                     /*OUT*/ULong* n1, /*OUT*/ULong* n2 )
    330 {
    331    vg_assert(VG_(strlen)(tag) == 4);
    332    if (!fr || !fr->data) return False;
    333    if (fr->n_data < 4) return False;
    334    if (VG_(memcmp)(&fr->data[0], tag, 4) != 0) return False;
    335    if (fr->n_data != 4 + 2*8) return False;
    336    *n1 = read_ULong_le(&fr->data[4 + 0*8]);
    337    *n2 = read_ULong_le(&fr->data[4 + 1*8]);
    338    return True;
    339 }
    340 
    341 static Bool parse_Frame_asciiz ( const Frame* fr, const HChar* tag,
    342                                  /*OUT*/UChar** str )
    343 {
    344    vg_assert(VG_(strlen)(tag) == 4);
    345    if (!fr || !fr->data) return False;
    346    if (fr->n_data < 4) return False;
    347    if (VG_(memcmp)(&fr->data[0], tag, 4) != 0) return False;
    348    if (fr->n_data < 5) return False; // else there isn't even enough
    349                                      // space for the terminating zero
    350    /* Find the terminating zero and ensure it's right at the end
    351       of the data.  If not, the frame is malformed. */
    352    SizeT i = 4;
    353    while (True) {
    354       if (i >= fr->n_data) break;
    355       if (fr->data[i] == 0) break;
    356       i++;
    357    }
    358    vg_assert(i <= fr->n_data);
    359    if (i == fr->n_data-1 && fr->data[i] == 0) {
    360       *str = &fr->data[4];
    361       return True;
    362    } else {
    363       return False;
    364    }
    365 }
    366 
    367 static Bool parse_Frame_le64_le64_le64_bytes (
    368                const Frame* fr, const HChar* tag,
    369                /*OUT*/ULong* n1, /*OUT*/ULong* n2, /*OUT*/ULong* n3,
    370                /*OUT*/UChar** data, /*OUT*/ULong* n_data
    371             )
    372 {
    373    vg_assert(VG_(strlen)(tag) == 4);
    374    if (!fr || !fr->data) return False;
    375    if (fr->n_data < 4) return False;
    376    if (VG_(memcmp)(&fr->data[0], tag, 4) != 0) return False;
    377    if (fr->n_data < 4 + 3*8) return False;
    378    *n1 = read_ULong_le(&fr->data[4 + 0*8]);
    379    *n2 = read_ULong_le(&fr->data[4 + 1*8]);
    380    *n3 = read_ULong_le(&fr->data[4 + 2*8]);
    381    *data   = &fr->data[4 + 3*8];
    382    *n_data = fr->n_data - (4 + 3*8);
    383    vg_assert(fr->n_data >= 4 + 3*8);
    384    return True;
    385 }
    386 
    387 static DiOffT block_round_down ( DiOffT i )
    388 {
    389    return i & ((DiOffT)~(CACHE_ENTRY_SIZE-1));
    390 }
    391 
    392 /* Is this offset inside this CEnt? */
    393 static inline Bool is_in_CEnt ( const CEnt* cent, DiOffT off )
    394 {
    395    /* This assertion is checked by set_CEnt, so checking it here has
    396       no benefit, whereas skipping it does remove it from the hottest
    397       path. */
    398    /* vg_assert(cent->used > 0 && cent->used <= CACHE_ENTRY_SIZE); */
    399    /* What we want to return is:
    400         cent->off <= off && off < cent->off + cent->used;
    401       This is however a very hot path, so here's alternative that uses
    402       only one conditional branch, using the following transformation,
    403       where all quantities are unsigned:
    404               x >= LO && x < LO+N
    405          -->  x-LO >= 0 && x-LO < LO+N-LO
    406          -->  x-LO >= 0 && x-LO < N
    407          -->  x-LO < N
    408       This is however only valid when the original bounds, that is, LO
    409       .. LO+N-1, do not wrap around the end of the address space.  That
    410       is, we require that LO <= LO+N-1.  But that's OK .. we don't
    411       expect wraparounds in CEnts or for that matter any object
    412       allocated from C-land.  See Hacker's Delight, Chapter 4.1,
    413       "Checking Bounds of Integers", for more details.
    414    */
    415    return off - cent->off < cent->used;
    416 }
    417 
    418 /* Allocate a new CEnt, connect it to |img|, and return its index. */
    419 static UInt alloc_CEnt ( DiImage* img )
    420 {
    421    vg_assert(img);
    422    vg_assert(img->ces_used < CACHE_N_ENTRIES);
    423    UInt entNo = img->ces_used;
    424    img->ces_used++;
    425    vg_assert(img->ces[entNo] == NULL);
    426    img->ces[entNo] = ML_(dinfo_zalloc)("di.alloc_CEnt.1", sizeof(CEnt));
    427    return entNo;
    428 }
    429 
    430 /* Move the given entry to the top and slide those above it down by 1,
    431    to make space. */
    432 static void move_CEnt_to_top ( DiImage* img, UInt entNo )
    433 {
    434    vg_assert(img->ces_used <= CACHE_N_ENTRIES);
    435    vg_assert(entNo > 0 && entNo < img->ces_used);
    436    CEnt* tmp = img->ces[entNo];
    437    while (entNo > 0) {
    438       img->ces[entNo] = img->ces[entNo-1];
    439       entNo--;
    440    }
    441    img->ces[0] = tmp;
    442 }
    443 
    444 /* Set the given entry so that it has a chunk of the file containing
    445    the given offset.  It is this function that brings data into the
    446    cache, either by reading the local file or pulling it from the
    447    remote server. */
    448 static void set_CEnt ( const DiImage* img, UInt entNo, DiOffT off )
    449 {
    450    SizeT len;
    451    DiOffT off_orig = off;
    452    vg_assert(img);
    453    vg_assert(img->ces_used <= CACHE_N_ENTRIES);
    454    vg_assert(entNo >= 0 && entNo < img->ces_used);
    455    vg_assert(off < img->size);
    456    vg_assert(img->ces[entNo] != NULL);
    457    /* Compute [off, +len) as the slice we are going to read. */
    458    off = block_round_down(off);
    459    len = img->size - off;
    460    if (len > CACHE_ENTRY_SIZE) len = CACHE_ENTRY_SIZE;
    461    /* It is conceivable that the 'len > 0' bit could fail if we make
    462       an image with a zero sized file.  But then no 'get' request on
    463       that image would be valid. */
    464    vg_assert(len > 0 && len <= CACHE_ENTRY_SIZE);
    465    vg_assert(off + len <= img->size);
    466    vg_assert(off <= off_orig && off_orig < off+len);
    467    /* So, read  off .. off+len-1  into the entry. */
    468    CEnt* ce = img->ces[entNo];
    469 
    470    if (0) {
    471       static UInt t_last = 0;
    472       static ULong nread = 0;
    473       UInt now = VG_(read_millisecond_timer)();
    474       UInt delay = now - t_last;
    475       t_last = now;
    476       nread += len;
    477       VG_(printf)("XXXXXXXX (tot %'lld)  read %'ld  offset %'lld  delay %'u\n",
    478                   nread, len, off, delay);
    479    }
    480 
    481    if (img->source.is_local) {
    482       // Simple: just read it
    483       SysRes sr = VG_(pread)(img->source.fd, &ce->data[0], (Int)len, off);
    484       vg_assert(!sr_isError(sr));
    485    } else {
    486       // Not so simple: poke the server
    487       vg_assert(img->source.session_id > 0);
    488       Frame* req
    489          = mk_Frame_le64_le64_le64("READ", img->source.session_id, off, len);
    490       Frame* res = do_transaction(img->source.fd, req);
    491       free_Frame(req); req = NULL;
    492       if (!res) goto server_fail;
    493       ULong  rx_session_id = 0, rx_off = 0, rx_len = 0, rx_zdata_len = 0;
    494       UChar* rx_data = NULL;
    495       /* Pretty confusing.  rx_sessionid, rx_off and rx_len are copies
    496          of the values that we requested in the READ frame just above,
    497          so we can be sure that the server is responding to the right
    498          request.  It just copies them from the request into the
    499          response.  rx_data is the actual data, and rx_zdata_len is
    500          its compressed length.  Hence rx_len must equal len, but
    501          rx_zdata_len can be different -- smaller, hopefully.. */
    502       if (!parse_Frame_le64_le64_le64_bytes
    503           (res, "RDOK", &rx_session_id, &rx_off,
    504                         &rx_len, &rx_data, &rx_zdata_len))
    505          goto server_fail;
    506       if (rx_session_id != img->source.session_id
    507           || rx_off != off || rx_len != len || rx_data == NULL)
    508          goto server_fail;
    509 
    510       //VG_(memcpy)(&ce->data[0], rx_data, len);
    511       // Decompress into the destination buffer
    512       // Tell the lib the max number of output bytes it can write.
    513       // After the call, this holds the number of bytes actually written,
    514       // and it's an error if it is different.
    515       lzo_uint out_len = len;
    516       Int lzo_rc = lzo1x_decompress_safe(rx_data, rx_zdata_len,
    517                                          &ce->data[0], &out_len,
    518                                          NULL);
    519       Bool ok = lzo_rc == LZO_E_OK && out_len == len;
    520       if (!ok) goto server_fail;
    521 
    522       free_Frame(res); res = NULL;
    523       goto end_of_else_clause;
    524      server_fail:
    525       /* The server screwed up somehow.  Now what? */
    526       if (res) {
    527          UChar* reason = NULL;
    528          if (parse_Frame_asciiz(res, "FAIL", &reason)) {
    529             VG_(umsg)("set_CEnt (reading data from DI server): fail: "
    530                       "%s\n", reason);
    531          } else {
    532             VG_(umsg)("set_CEnt (reading data from DI server): fail: "
    533                       "unknown reason\n");
    534          }
    535          free_Frame(res); res = NULL;
    536       } else {
    537          VG_(umsg)("set_CEnt (reading data from DI server): fail: "
    538                    "server unexpectedly closed the connection\n");
    539       }
    540       give_up__comms_lost();
    541       /* NOTREACHED */
    542       vg_assert(0);
    543      end_of_else_clause:
    544       {}
    545    }
    546 
    547    ce->off  = off;
    548    ce->used = len;
    549    vg_assert(ce->used > 0 && ce->used <= CACHE_ENTRY_SIZE);
    550 }
    551 
    552 __attribute__((noinline))
    553 static UChar get_slowcase ( DiImage* img, DiOffT off )
    554 {
    555    /* Stay sane .. */
    556    vg_assert(off < img->size);
    557    vg_assert(img->ces_used <= CACHE_N_ENTRIES);
    558    UInt i;
    559    /* Start the search at entry 1, since the fast-case function
    560       checked slot zero already. */
    561    for (i = 1; i < img->ces_used; i++) {
    562       vg_assert(img->ces[i]);
    563       if (is_in_CEnt(img->ces[i], off))
    564          break;
    565    }
    566    vg_assert(i <= img->ces_used);
    567    if (i == img->ces_used) {
    568       /* It's not in any entry.  Either allocate a new entry or
    569          recycle the LRU one. */
    570       if (img->ces_used == CACHE_N_ENTRIES) {
    571          /* All entries in use.  Recycle the (ostensibly) LRU one. */
    572          set_CEnt(img, CACHE_N_ENTRIES-1, off);
    573          i = CACHE_N_ENTRIES-1;
    574       } else {
    575          /* Allocate a new one, and fill it in. */
    576          UInt entNo = alloc_CEnt(img);
    577          set_CEnt(img, entNo, off);
    578          i = entNo;
    579       }
    580    } else {
    581       /* We found it at position 'i'. */
    582       vg_assert(i > 0);
    583    }
    584    if (i > 0) {
    585       move_CEnt_to_top(img, i);
    586       i = 0;
    587    }
    588    vg_assert(is_in_CEnt(img->ces[i], off));
    589    return img->ces[i]->data[ off - img->ces[i]->off ];
    590 }
    591 
    592 // This is called a lot, so do the usual fast/slow split stuff on it. */
    593 static inline UChar get ( DiImage* img, DiOffT off )
    594 {
    595    /* Most likely case is, it's in the ces[0] position. */
    596    /* ML_(img_from_local_file) requests a read for ces[0] when
    597       creating the image.  Hence slot zero is always non-NULL, so we
    598       can skip this test. */
    599    if (LIKELY(/* img->ces[0] != NULL && */
    600               is_in_CEnt(img->ces[0], off))) {
    601       return img->ces[0]->data[ off - img->ces[0]->off ];
    602    }
    603    /* Else we'll have to fish around for it. */
    604    return get_slowcase(img, off);
    605 }
    606 
    607 /* Create an image from a file in the local filesystem.  This is
    608    relatively straightforward. */
    609 DiImage* ML_(img_from_local_file)(const HChar* fullpath)
    610 {
    611    SysRes         fd;
    612    struct vg_stat stat_buf;
    613    DiOffT         size;
    614 
    615    fd = VG_(open)(fullpath, VKI_O_RDONLY, 0);
    616    if (sr_isError(fd))
    617       return NULL;
    618 
    619    if (VG_(fstat)(sr_Res(fd), &stat_buf) != 0) {
    620       VG_(close)(sr_Res(fd));
    621       return NULL;
    622    }
    623 
    624    size = stat_buf.size;
    625    if (size == 0 || size == DiOffT_INVALID
    626        || /* size is unrepresentable as a SizeT */
    627           size != (DiOffT)(SizeT)(size)) {
    628       VG_(close)(sr_Res(fd));
    629       return NULL;
    630    }
    631 
    632    DiImage* img = ML_(dinfo_zalloc)("di.image.ML_iflf.1", sizeof(DiImage));
    633    img->source.is_local = True;
    634    img->source.fd       = sr_Res(fd);
    635    img->size            = size;
    636    img->ces_used        = 0;
    637    img->source.name     = ML_(dinfo_strdup)("di.image.ML_iflf.2", fullpath);
    638    /* img->ces is already zeroed out */
    639    vg_assert(img->source.fd >= 0);
    640 
    641    /* Force the zeroth entry to be the first chunk of the file.
    642       That's likely to be the first part that's requested anyway, and
    643       loading it at this point forcing img->cent[0] to always be
    644       non-empty, thereby saving us an is-it-empty check on the fast
    645       path in get(). */
    646    UInt entNo = alloc_CEnt(img);
    647    vg_assert(entNo == 0);
    648    set_CEnt(img, 0, 0);
    649 
    650    return img;
    651 }
    652 
    653 
    654 /* Create an image from a file on a remote debuginfo server.  This is
    655    more complex.  There are lots of ways in which it can fail. */
    656 DiImage* ML_(img_from_di_server)(const HChar* filename,
    657                                  const HChar* serverAddr)
    658 {
    659    if (filename == NULL || serverAddr == NULL)
    660       return NULL;
    661 
    662    /* The filename must be a plain filename -- no slashes at all. */
    663    if (VG_(strchr)(filename, '/') != NULL)
    664       return NULL;
    665 
    666    /* Try to connect to the server.  A side effect of this is to parse
    667       and reject, if syntactically invalid, |serverAddr|.  Reasons why
    668       this could fail:
    669       - serverAddr is not of the form d.d.d.d:d or d.d.d.d
    670       - attempt to connect to that address:port failed
    671    */
    672    Int sd = VG_(connect_via_socket)(serverAddr);
    673    if (sd < 0)
    674       return NULL;
    675    if (!set_blocking(sd))
    676       return NULL;
    677    Int one = 1;
    678    Int sr = VG_(setsockopt)(sd, VKI_IPPROTO_TCP, VKI_TCP_NODELAY,
    679                             &one, sizeof(one));
    680    vg_assert(sr == 0);
    681 
    682    /* Ok, we got a connection.  Ask it for version string, so as to be
    683       reasonably sure we're talking to an instance of
    684       auxprogs/valgrind-di-server and not to some other random program
    685       that happens to be listening on that port. */
    686    Frame* req = mk_Frame_noargs("VERS");
    687    Frame* res = do_transaction(sd, req);
    688    if (res == NULL)
    689       goto fail; // do_transaction failed?!
    690    UChar* vstr = NULL;
    691    if (!parse_Frame_asciiz(res, "VEOK", &vstr))
    692       goto fail; // unexpected response kind, or invalid ID string
    693    vg_assert(vstr);
    694    if (VG_(strcmp)("Valgrind Debuginfo Server, Version 1",
    695                    (const HChar*)vstr) != 0)
    696       goto fail; // wrong version string
    697    free_Frame(req);
    698    free_Frame(res);
    699    req = NULL;
    700    res = NULL;
    701 
    702    /* Server seems plausible.  Present it with the name of the file we
    703       want and see if it'll give us back a session ID for it. */
    704    req = mk_Frame_asciiz("OPEN", filename);
    705    res = do_transaction(sd, req);
    706    if (res == NULL)
    707       goto fail;
    708    ULong session_id = 0, size = 0;
    709    if (!parse_Frame_le64_le64(res, "OPOK", &session_id, &size))
    710       goto fail;
    711    free_Frame(req);
    712    free_Frame(res);
    713    req = NULL;
    714    res = NULL;
    715 
    716    /* We have a session ID.  We're ready to roll. */
    717    DiImage* img = ML_(dinfo_zalloc)("di.image.ML_ifds.1", sizeof(DiImage));
    718    img->source.is_local   = False;
    719    img->source.fd         = sd;
    720    img->source.session_id = session_id;
    721    img->size              = size;
    722    img->ces_used          = 0;
    723    img->source.name       = ML_(dinfo_zalloc)("di.image.ML_ifds.2",
    724                                               20 + VG_(strlen)(filename)
    725                                                  + VG_(strlen)(serverAddr));
    726    VG_(sprintf)(img->source.name, "%s at %s", filename, serverAddr);
    727 
    728    /* img->ces is already zeroed out */
    729    vg_assert(img->source.fd >= 0);
    730 
    731    /* See comment on equivalent bit in ML_(img_from_local_file) for
    732       rationale. */
    733    UInt entNo = alloc_CEnt(img);
    734    vg_assert(entNo == 0);
    735    set_CEnt(img, 0, 0);
    736 
    737    return img;
    738 
    739   fail:
    740    free_Frame(req);
    741    if (res) {
    742       UChar* reason = NULL;
    743       if (parse_Frame_asciiz(res, "FAIL", &reason)) {
    744          // HACK: if it's just telling us that the file can't
    745          // be opened, don't print it, else we'll get flooded with
    746          // such complaints, one for each main object for which there
    747          // isn't a debuginfo file on the server.
    748          if (0 != VG_(strcmp)((const HChar*)reason, "OPEN: cannot open file"))
    749             VG_(umsg)("ML_(img_from_di_server): fail: %s\n", reason);
    750       } else {
    751          VG_(umsg)("ML_(img_from_di_server): fail: unknown reason\n");
    752       }
    753       free_Frame(res);
    754    }
    755    VG_(close)(sd);
    756    return NULL;
    757 }
    758 
    759 void ML_(img_done)(DiImage* img)
    760 {
    761    vg_assert(img);
    762    if (img->source.is_local) {
    763       /* Close the file; nothing else to do. */
    764       vg_assert(img->source.session_id == 0);
    765       VG_(close)(img->source.fd);
    766    } else {
    767       /* Close the socket.  The server can detect this and will scrub
    768          the connection when it happens, so there's no need to tell it
    769          explicitly by sending it a "CLOSE" message, or any such. */
    770       vg_assert(img->source.session_id != 0);
    771       VG_(close)(img->source.fd);
    772    }
    773 
    774    /* Free up the cache entries, ultimately |img| itself. */
    775    UInt i;
    776    vg_assert(img->ces_used <= CACHE_N_ENTRIES);
    777    for (i = 0; i < img->ces_used; i++) {
    778       ML_(dinfo_free)(img->ces[i]);
    779    }
    780    /* Take the opportunity to sanity check the rest. */
    781    for (i = i; i < img->ces_used; i++) {
    782       vg_assert(img->ces[i] == NULL);
    783    }
    784    ML_(dinfo_free)(img->source.name);
    785    ML_(dinfo_free)(img);
    786 }
    787 
    788 DiOffT ML_(img_size)(const DiImage* img)
    789 {
    790    vg_assert(img);
    791    return img->size;
    792 }
    793 
    794 inline Bool ML_(img_valid)(const DiImage* img, DiOffT offset, SizeT size)
    795 {
    796    vg_assert(img);
    797    vg_assert(offset != DiOffT_INVALID);
    798    return img->size > 0 && offset + size <= (DiOffT)img->size;
    799 }
    800 
    801 /* Check the given range is valid, and if not, shut down the system.
    802    An invalid range would imply that we're trying to read outside the
    803    image, which normally means the image is corrupted somehow, or the
    804    caller is buggy.  Recovering is too complex, and we have
    805    probably-corrupt debuginfo, so just give up. */
    806 static void ensure_valid(const DiImage* img, DiOffT offset, SizeT size,
    807                          const HChar* caller)
    808 {
    809    if (LIKELY(ML_(img_valid)(img, offset, size)))
    810       return;
    811    VG_(umsg)("Valgrind: debuginfo reader: ensure_valid failed:\n");
    812    VG_(umsg)("Valgrind:   during call to %s\n", caller);
    813    VG_(umsg)("Valgrind:   request for range [%llu, +%llu) exceeds\n",
    814              (ULong)offset, (ULong)size);
    815    VG_(umsg)("Valgrind:   valid image size of %llu for image:\n",
    816              (ULong)img->size);
    817    VG_(umsg)("Valgrind:   \"%s\"\n", img->source.name);
    818    give_up__image_overrun();
    819 }
    820 
    821 
    822 void ML_(img_get)(/*OUT*/void* dst,
    823                   DiImage* img, DiOffT offset, SizeT size)
    824 {
    825    vg_assert(img);
    826    vg_assert(size > 0);
    827    ensure_valid(img, offset, size, "ML_(img_get)");
    828    SizeT i;
    829    for (i = 0; i < size; i++) {
    830       ((UChar*)dst)[i] = get(img, offset + i);
    831    }
    832 }
    833 
    834 SizeT ML_(img_get_some)(/*OUT*/void* dst,
    835                         DiImage* img, DiOffT offset, SizeT size)
    836 {
    837    vg_assert(img);
    838    vg_assert(size > 0);
    839    ensure_valid(img, offset, size, "ML_(img_get_some)");
    840    UChar* dstU = (UChar*)dst;
    841    /* Use |get| in the normal way to get the first byte of the range.
    842       This guarantees to put the cache entry containing |offset| in
    843       position zero. */
    844    dstU[0] = get(img, offset);
    845    /* Now just read as many bytes as we can (or need) directly out of
    846       entry zero, without bothering to call |get| each time. */
    847    const CEnt* ce = img->ces[0];
    848    vg_assert(ce && ce->used >= 1);
    849    vg_assert(is_in_CEnt(ce, offset));
    850    SizeT nToCopy = size - 1;
    851    SizeT nAvail  = (SizeT)(ce->used - (offset + 1 - ce->off));
    852    vg_assert(nAvail >= 0 && nAvail <= ce->used-1);
    853    if (nAvail < nToCopy) nToCopy = nAvail;
    854    VG_(memcpy)(&dstU[1], &ce->data[offset + 1 - ce->off], nToCopy);
    855    return nToCopy + 1;
    856 }
    857 
    858 
    859 SizeT ML_(img_strlen)(DiImage* img, DiOffT off)
    860 {
    861    ensure_valid(img, off, 1, "ML_(img_strlen)");
    862    SizeT i = 0;
    863    while (get(img, off + i) != 0) i++;
    864    return i;
    865 }
    866 
    867 HChar* ML_(img_strdup)(DiImage* img, const HChar* cc, DiOffT offset)
    868 {
    869    ensure_valid(img, offset, 1, "ML_(img_strdup)");
    870    SizeT  len = ML_(img_strlen)(img, offset);
    871    HChar* res = ML_(dinfo_zalloc)(cc, len+1);
    872    SizeT  i;
    873    for (i = 0; i < len; i++) {
    874       res[i] = get(img, offset+i);
    875    }
    876    vg_assert(res[len] == 0);
    877    return res;
    878 }
    879 
    880 Int ML_(img_strcmp)(DiImage* img, DiOffT off1, DiOffT off2)
    881 {
    882    ensure_valid(img, off1, 1, "ML_(img_strcmp)(first arg)");
    883    ensure_valid(img, off2, 1, "ML_(img_strcmp)(second arg)");
    884    while (True) {
    885       UChar c1 = get(img, off1);
    886       UChar c2 = get(img, off2);
    887       if (c1 < c2) return -1;
    888       if (c1 > c2) return 1;
    889       if (c1 == 0) return 0;
    890       off1++; off2++;
    891    }
    892 }
    893 
    894 Int ML_(img_strcmp_c)(DiImage* img, DiOffT off1, const HChar* str2)
    895 {
    896    ensure_valid(img, off1, 1, "ML_(img_strcmp_c)");
    897    while (True) {
    898       UChar c1 = get(img, off1);
    899       UChar c2 = *(const UChar*)str2;
    900       if (c1 < c2) return -1;
    901       if (c1 > c2) return 1;
    902       if (c1 == 0) return 0;
    903       off1++; str2++;
    904    }
    905 }
    906 
    907 UChar ML_(img_get_UChar)(DiImage* img, DiOffT offset)
    908 {
    909    ensure_valid(img, offset, 1, "ML_(img_get_UChar)");
    910    return get(img, offset);
    911 }
    912 
    913 UShort ML_(img_get_UShort)(DiImage* img, DiOffT offset)
    914 {
    915    UShort r;
    916    ML_(img_get)(&r, img, offset, sizeof(r));
    917    return r;
    918 }
    919 
    920 UInt ML_(img_get_UInt)(DiImage* img, DiOffT offset)
    921 {
    922    UInt r;
    923    ML_(img_get)(&r, img, offset, sizeof(r));
    924    return r;
    925 }
    926 
    927 ULong ML_(img_get_ULong)(DiImage* img, DiOffT offset)
    928 {
    929    ULong r;
    930    ML_(img_get)(&r, img, offset, sizeof(r));
    931    return r;
    932 }
    933 
    934 
    935 /*
    936  * This routine for calculating the CRC for a separate debug file
    937  * is GPLed code borrowed from GNU binutils.
    938  */
    939 UInt ML_(img_calc_gnu_debuglink_crc32)(DiImage* img)
    940 {
    941   static const UInt crc32_table[256] =
    942     {
    943       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
    944       0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
    945       0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
    946       0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
    947       0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
    948       0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    949       0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
    950       0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    951       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
    952       0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
    953       0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
    954       0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    955       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
    956       0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
    957       0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
    958       0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    959       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
    960       0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    961       0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
    962       0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
    963       0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
    964       0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
    965       0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
    966       0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    967       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
    968       0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
    969       0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
    970       0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
    971       0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
    972       0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    973       0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
    974       0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    975       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
    976       0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
    977       0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
    978       0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    979       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
    980       0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
    981       0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
    982       0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    983       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
    984       0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    985       0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
    986       0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
    987       0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
    988       0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
    989       0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
    990       0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    991       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
    992       0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
    993       0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
    994       0x2d02ef8d
    995     };
    996 
    997    vg_assert(img);
    998 
    999    /* If the image is local, calculate the CRC here directly.  If it's
   1000       remote, forward the request to the server. */
   1001    if (img->source.is_local) {
   1002       /* Work through the image in 1 KB chunks. */
   1003       UInt   crc      = 0xFFFFFFFF;
   1004       DiOffT img_szB  = ML_(img_size)(img);
   1005       DiOffT curr_off = 0;
   1006       while (1) {
   1007          vg_assert(curr_off >= 0 && curr_off <= img_szB);
   1008          if (curr_off == img_szB) break;
   1009          DiOffT avail = img_szB - curr_off;
   1010          vg_assert(avail > 0 && avail <= img_szB);
   1011          if (avail > 1024) avail = 1024;
   1012          UChar buf[1024];
   1013          SizeT nGot = ML_(img_get_some)(buf, img, curr_off, avail);
   1014          vg_assert(nGot >= 1 && nGot <= avail);
   1015          UInt i;
   1016          for (i = 0; i < (UInt)nGot; i++)
   1017             crc = crc32_table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
   1018          curr_off += nGot;
   1019       }
   1020       return ~crc & 0xFFFFFFFF;
   1021    } else {
   1022       Frame* req = mk_Frame_noargs("CRC3");
   1023       Frame* res = do_transaction(img->source.fd, req);
   1024       if (!res) goto remote_crc_fail;
   1025       ULong crc32 = 0;
   1026       if (!parse_Frame_le64(res, "CROK", &crc32)) goto remote_crc_fail;
   1027       if ((crc32 & ~0xFFFFFFFFULL) != 0) goto remote_crc_fail;
   1028       free_Frame(req);
   1029       free_Frame(res);
   1030       return (UInt)crc32;
   1031      remote_crc_fail:
   1032 
   1033       // XXXX common this up with the READ diagnostic cases
   1034       if (res) {
   1035          UChar* reason = NULL;
   1036          if (parse_Frame_asciiz(res, "FAIL", &reason)) {
   1037             VG_(umsg)("img_calc_gnu_debuglink_crc32: fail: "
   1038                       "%s\n", reason);
   1039          } else {
   1040             VG_(umsg)("img_calc_gnu_debuglink_crc32: fail: "
   1041                       "unknown reason\n");
   1042          }
   1043       } else {
   1044          VG_(umsg)("img_calc_gnu_debuglink_crc32: fail: "
   1045                    "server unexpectedly closed the connection\n");
   1046       }
   1047 
   1048       if (req) free_Frame(req);
   1049       if (res) free_Frame(res);
   1050       // FIXME: now what?
   1051       give_up__comms_lost();
   1052       /* NOTREACHED */
   1053       vg_assert(0);
   1054    }
   1055    /*NOTREACHED*/
   1056    vg_assert(0);
   1057 }
   1058 
   1059 ////////////////////////////////////////////////////
   1060 #include "minilzo-inl.c"
   1061 
   1062 /*--------------------------------------------------------------------*/
   1063 /*--- end                                                  image.c ---*/
   1064 /*--------------------------------------------------------------------*/
   1065