/*--------------------------------------------------------------------*/
/*--- An abstraction that provides a file-reading mechanism.       ---*/
/*---                                                 priv_image.h ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2013-2015 Mozilla Foundation

   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.
*/

/* Contributed by Julian Seward <jseward@acm.org> */

#ifndef __PRIV_IMAGE_H
#define __PRIV_IMAGE_H

#include "pub_core_basics.h"    // ULong
#include "priv_misc.h"          // ML_(dinfo_zalloc)

/*------------------------------------------------------------*/
/*--- DiImage -- abstract images                           ---*/
/*------------------------------------------------------------*/

/* The basic type, containing an abstractified memory area that can be
   read from.  This is an abstract type since there can be more than
   one implementation of it. */

/* abstract */
typedef  struct _DiImage  DiImage;

/* an offset in the image */
typedef  ULong  DiOffT;

/* This denotes an invalid DiOffT value.  Except where otherwise
   noted, you must never pass this to any of the ML_(image_*)
   functions -- they will assert.  That does mean though that they can
   be used for signalling other conditions, for example that some
   expected part of the image is missing. */
#define DiOffT_INVALID ((DiOffT)(0xFFFFFFFFFFFFFFFFULL))

/* Create an image from a file in the local filesysem.  Returns NULL
   if it fails, for whatever reason. */
DiImage* ML_(img_from_local_file)(const HChar* fullpath);

/* Create an image by connecting to a Valgrind debuginfo server
   (auxprogs/valgrind-di-server.c).  |filename| contains the object
   name to ask for; it must be a plain filename, not absolute, not a
   path.  |serverAddr| must be of the form either "d.d.d.d" or
   "d.d.d.d:d" where d is one or more digits.  These specify the IPv4
   address and (in the second case) port number for the server.  In
   the first case, port 1500 is assumed. */
DiImage* ML_(img_from_di_server)(const HChar* filename,
                                 const HChar* serverAddr);

/* Destroy an existing image. */
void ML_(img_done)(DiImage*);

/* How big is the image? */
DiOffT ML_(img_size)(const DiImage* img);

/* Does the section [offset, +size) exist in the image? */
Bool ML_(img_valid)(const DiImage* img, DiOffT offset, SizeT size);

/* Get info out of an image.  If any part of the section denoted by
   [offset, +size) is invalid, does not return. */
void ML_(img_get)(/*OUT*/void* dst,
                  DiImage* img, DiOffT offset, SizeT size);

/* A version of ML_(img_get) that is significantly cheaper when
   fetching a lot of data, at the cost of being more difficult to use.
   Fetches between 1 and |size| bytes from |img| at |offset| and
   places them in |dst|.  |size| must be at least 1.  The number of
   bytes read is returned, and the caller must be able to deal with
   any number between 1 and |size|.  |offset| must be a valid offset
   in the image; if not the function will not return.  This function
   will not read off the end of the image. */
SizeT ML_(img_get_some)(/*OUT*/void* dst,
                        DiImage* img, DiOffT offset, SizeT size);

/* Copy a C string out of the image, into ML_(dinfo_zalloc)'d space.
   The caller owns the string and must free it with ML_(dinfo_free).
   |offset| may be DiOffT_INVALID, in which case this returns NULL. */
HChar* ML_(img_strdup)(DiImage* img, const HChar* cc, DiOffT offset);

/* Do strcmp on two C strings in the image.  Chars are cast to HChar
   before comparison. */
Int ML_(img_strcmp)(DiImage* img, DiOffT off1, DiOffT off2);

/* Do strcmp of a C string in the image vs a normal one.  Chars are
   cast to HChar before comparison. */
Int ML_(img_strcmp_c)(DiImage* img, DiOffT off1, const HChar* str2);

/* Do strlen of a C string in the image. */
SizeT ML_(img_strlen)(DiImage* img, DiOffT off);

/* Fetch various sized primitive types from the image.  These operate
   at the endianness and word size of the host. */
UChar  ML_(img_get_UChar) (DiImage* img, DiOffT offset);
UShort ML_(img_get_UShort)(DiImage* img, DiOffT offset);
UInt   ML_(img_get_UInt)  (DiImage* img, DiOffT offset);
ULong  ML_(img_get_ULong) (DiImage* img, DiOffT offset);

/* Calculate the "GNU Debuglink CRC" for the image.  This
   unfortunately has to be done as part of the DiImage implementation
   because it involves reading the entire image, and is therefore
   something that needs to be handed off to the remote server -- since
   to do it otherwise would imply pulling the entire image across the
   connection, making the client/server split pointless. */
UInt ML_(img_calc_gnu_debuglink_crc32)(DiImage* img);


/*------------------------------------------------------------*/
/*--- DiCursor -- cursors for reading images               ---*/
/*------------------------------------------------------------*/

/* A type built on DiImage.  It contains a DiImage and a 'current
   offset' in the image, and is useful for building low level readers
   of images.  In the functions section below, "read" means "read data
   at the cursor without moving it along", and "step" means "read data
   at the cursor and move it along by the size of the item read". */
typedef
   struct { DiImage* img; DiOffT ioff; }
   DiCursor;

/* An invalid cursor.  You can't use it for anything. */
#define DiCursor_INVALID ((DiCursor){NULL,DiOffT_INVALID})

static inline DiCursor mk_DiCursor ( DiImage* img, DiOffT ioff ) {
   return (DiCursor){img, ioff};
}

static inline Bool ML_(cur_is_valid)(DiCursor c) {
   return c.img != NULL;
}


/*------------------------------------------------------------*/
/*--- DiSlice -- subranges within DiImages                 ---*/
/*------------------------------------------------------------*/

/* Another type built on top of DiImage.  It denotes a subrange of an
   image and is useful for representing (eg) exactly the part of an
   image that is a specific ELF section. */
typedef
   struct { DiImage* img; DiOffT ioff; DiOffT szB; }
   DiSlice;

/* A DiSlice can also be INVALID, meaning it does not refer to any
   part of any image. */
#define DiSlice_INVALID ((DiSlice){NULL,DiOffT_INVALID,0})

static inline DiSlice mk_DiSlice ( DiImage* img, DiOffT ioff, DiOffT szB ) {
   return (DiSlice){img, ioff, szB};
}

static inline Bool ML_(sli_is_valid)( DiSlice sli ) {
   return sli.img != NULL;
}

/* Create a slice from a combination of a cursor and a length.  The
   maximum implied offset must not exceed the size of the underlying
   image; this is asserted for. */
static inline DiSlice ML_(sli_from_cur)( DiCursor cur, DiOffT size ) {
   if (ML_(cur_is_valid)(cur)) {
      vg_assert(size != DiOffT_INVALID);
      vg_assert(cur.ioff + size <= ML_(img_size)(cur.img));
      return mk_DiSlice(cur.img, cur.ioff, size);
   } else {
      return DiSlice_INVALID;
   }
}

/* Create a slice which exactly covers the given image. */
static inline DiSlice ML_(sli_from_img)( DiImage* img ) {
   if (img) {
      return mk_DiSlice(img, 0, ML_(img_size)(img));
   } else {
      return DiSlice_INVALID;
   }
}


/*------------------------------------------------------------*/
/*--- Functions that operate on DiCursors                  ---*/
/*------------------------------------------------------------*/

/* Create a cursor from a slice, referring to the first byte of the
   slice. */
static inline DiCursor ML_(cur_from_sli)( DiSlice sl ) {
   if (ML_(sli_is_valid)(sl)) {
      DiCursor c;
      c.img  = sl.img;
      c.ioff = sl.ioff;
      return c;
   } else {
      return DiCursor_INVALID;
   }
}

static inline Bool ML_(cur_cmpLT)( DiCursor c1, DiCursor c2 ) {
   vg_assert(c1.img == c2.img);
   return c1.ioff < c2.ioff;
}
static inline Bool ML_(cur_cmpEQ)( DiCursor c1, DiCursor c2 ) {
   vg_assert(c1.img == c2.img);
   return c1.ioff == c2.ioff;
}
static inline Bool ML_(cur_cmpGT)( DiCursor c1, DiCursor c2 ) {
   vg_assert(c1.img == c2.img);
   return c1.ioff > c2.ioff;
}

static inline DiCursor ML_(cur_plus)( DiCursor c, Long n ) {
   c.ioff += (DiOffT)n;
   return c;
}

/* Asserts that c1 and c2 refer to the same image.  Returns the difference
   in offsets (c1.ioff - c2.ioff). */
static inline Long ML_(cur_minus)( DiCursor c1, DiCursor c2 ) {
   vg_assert(c1.img == c2.img);
   return (Long)(c1.ioff) - (Long)(c2.ioff);
}

static inline SizeT ML_(cur_strlen)( DiCursor c ) {
   return ML_(img_strlen)( c.img, c.ioff );
}

// strdup from the given cursor.  Caller must ML_(dinfo_free) the
// resulting string.
static inline HChar* ML_(cur_read_strdup)( DiCursor c, const HChar* cc ) {
   vg_assert(c.ioff != DiOffT_INVALID);
   HChar* res = ML_(img_strdup)(c.img, cc, c.ioff);
   return res;
}
// strdup from the given cursor and advance it.  Caller must
// ML_(dinfo_free) the resulting string.
static inline HChar* ML_(cur_step_strdup)( DiCursor* c, const HChar* cc ) {
   vg_assert(c->ioff != DiOffT_INVALID);
   HChar* res = ML_(img_strdup)(c->img, cc, c->ioff);
   c->ioff += VG_(strlen)(res) + 1;
   return res;
}

// Fetch an arbitrary number of bytes from the cursor.
static inline void ML_(cur_read_get) ( /*OUT*/void* dst,
                                       DiCursor c, SizeT size) {
   ML_(img_get)(dst, c.img, c.ioff, size);
}

// Fetch an arbitrary number of bytes from the cursor, and advance it.
static inline void ML_(cur_step_get) ( /*OUT*/void* dst,
                                       DiCursor* c, SizeT size) {
   ML_(img_get)(dst, c->img, c->ioff, size);
   c->ioff += size;
}

// memdup from the given cursor.  Caller must ML_(dinfo_free) the
// resulting block.
static inline UChar* ML_(cur_read_memdup)( DiCursor c, SizeT size,
                                           const HChar* cc )
{
   UChar* dst = ML_(dinfo_zalloc)(cc, size);
   if (size > 0)
      ML_(cur_read_get)(dst, c, size);
   return dst;
}

static inline UChar ML_(cur_read_UChar) ( DiCursor c ) {
   UChar r = ML_(img_get_UChar)( c.img, c.ioff );
   return r;
}
static inline UChar ML_(cur_step_UChar)( DiCursor* c ) {
   UChar r = ML_(img_get_UChar)( c->img, c->ioff );
   c->ioff += sizeof(UChar);
   return r;
}

static inline UShort ML_(cur_read_UShort) ( DiCursor c ) {
   UShort r = ML_(img_get_UShort)( c.img, c.ioff );
   return r;
}
static inline UShort ML_(cur_step_UShort) ( DiCursor* c ) {
   UShort r = ML_(img_get_UShort)( c->img, c->ioff );
   c->ioff += sizeof(UShort);
   return r;
}
static inline Short ML_(cur_step_Short) ( DiCursor* c ) {
   return (Short)ML_(cur_step_UShort)( c );
}

static inline UInt ML_(cur_read_UInt) ( DiCursor c ) {
   UInt r = ML_(img_get_UInt)( c.img, c.ioff );
   return r;
}
static inline UInt ML_(cur_step_UInt) ( DiCursor* c ) {
   UInt r = ML_(img_get_UInt)( c->img, c->ioff );
   c->ioff += sizeof(UInt);
   return r;
}
static inline Int ML_(cur_step_Int) ( DiCursor* c ) {
   return (Int)ML_(cur_step_UInt)( c );
}

static inline ULong ML_(cur_read_ULong) ( DiCursor c ) {
   ULong r = ML_(img_get_ULong)( c.img, c.ioff );
   return r;
}
static inline ULong ML_(cur_step_ULong) ( DiCursor* c ) {
   ULong r = ML_(img_get_ULong)( c->img, c->ioff );
   c->ioff += sizeof(ULong);
   return r;
}
static inline Long ML_(cur_step_Long) ( DiCursor* c ) {
   return (Long)ML_(cur_step_ULong)( c );
}

static inline Addr ML_(cur_step_Addr) ( DiCursor* c ) {
   if (sizeof(Addr) == sizeof(UInt)) {
      return ML_(cur_step_UInt)(c);
   } else if  (sizeof(Addr) == sizeof(ULong)) {
      return ML_(cur_step_ULong)(c);
   } else {
      vg_assert(0);
   }
}

#endif /* ndef __PRIV_IMAGE_H */

/*--------------------------------------------------------------------*/
/*--- end                                             priv_image.h ---*/
/*--------------------------------------------------------------------*/