/*      $NetBSD: libdwarf_rw.c,v 1.5 2024/03/03 17:37:32 christos Exp $ */

/*-
* Copyright (c) 2007 John Birrell ([email protected])
* Copyright (c) 2010 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

#include "_libdwarf.h"

__RCSID("$NetBSD: libdwarf_rw.c,v 1.5 2024/03/03 17:37:32 christos Exp $");
ELFTC_VCSID("Id: libdwarf_rw.c 4007 2023-10-12 18:17:02Z kaiwang27");

uint64_t
_dwarf_read_lsb(uint8_t *data, uint64_t *offsetp, int bytes_to_read)
{
       uint64_t ret;
       uint8_t *src;

       src = data + *offsetp;

       ret = 0;
       switch (bytes_to_read) {
       case 8:
               ret |= ((uint64_t) src[4]) << 32 | ((uint64_t) src[5]) << 40;
               ret |= ((uint64_t) src[6]) << 48 | ((uint64_t) src[7]) << 56;
               /* FALLTHROUGH */
       case 4:
               ret |= ((uint64_t) src[3]) << 24;
               /* FALLTHROUGH */
       case 3:
               ret |= ((uint64_t) src[2]) << 16;
               /* FALLTHROUGH */
       case 2:
               ret |= ((uint64_t) src[1]) << 8;
               /* FALLTHROUGH */
       case 1:
               ret |= src[0];
               break;
       default:
               return (0);
       }

       *offsetp += bytes_to_read;

       return (ret);
}

uint64_t
_dwarf_decode_lsb(uint8_t **data, int bytes_to_read)
{
       uint64_t ret;
       uint8_t *src;

       src = *data;

       ret = 0;
       switch (bytes_to_read) {
       case 8:
               ret |= ((uint64_t) src[4]) << 32 | ((uint64_t) src[5]) << 40;
               ret |= ((uint64_t) src[6]) << 48 | ((uint64_t) src[7]) << 56;
               /* FALLTHROUGH */
       case 4:
               ret |= ((uint64_t) src[3]) << 24;
               /* FALLTHROUGH */
       case 3:
               ret |= ((uint64_t) src[2]) << 16;
               /* FALLTHROUGH */
       case 2:
               ret |= ((uint64_t) src[1]) << 8;
               /* FALLTHROUGH */
       case 1:
               ret |= src[0];
               break;
       default:
               return (0);
       }

       *data += bytes_to_read;

       return (ret);
}

uint64_t
_dwarf_read_msb(uint8_t *data, uint64_t *offsetp, int bytes_to_read)
{
       uint64_t ret;
       uint8_t *src;

       src = data + *offsetp;

       switch (bytes_to_read) {
       case 1:
               ret = src[0];
               break;
       case 2:
               ret = src[1] | ((uint64_t) src[0]) << 8;
               break;
       case 3:
               ret = src[2] | ((uint64_t) src[1]) << 8;
               ret |= ((uint64_t) src[0]) << 16;
               break;
       case 4:
               ret = src[3] | ((uint64_t) src[2]) << 8;
               ret |= ((uint64_t) src[1]) << 16 | ((uint64_t) src[0]) << 24;
               break;
       case 8:
               ret = src[7] | ((uint64_t) src[6]) << 8;
               ret |= ((uint64_t) src[5]) << 16 | ((uint64_t) src[4]) << 24;
               ret |= ((uint64_t) src[3]) << 32 | ((uint64_t) src[2]) << 40;
               ret |= ((uint64_t) src[1]) << 48 | ((uint64_t) src[0]) << 56;
               break;
       default:
               return (0);
       }

       *offsetp += bytes_to_read;

       return (ret);
}

uint64_t
_dwarf_decode_msb(uint8_t **data, int bytes_to_read)
{
       uint64_t ret;
       uint8_t *src;

       src = *data;

       ret = 0;
       switch (bytes_to_read) {
       case 1:
               ret = src[0];
               break;
       case 2:
               ret = src[1] | ((uint64_t) src[0]) << 8;
               break;
       case 3:
               ret = src[2] | ((uint64_t) src[1]) << 8;
               ret |= ((uint64_t) src[0]) << 16;
               break;
       case 4:
               ret = src[3] | ((uint64_t) src[2]) << 8;
               ret |= ((uint64_t) src[1]) << 16 | ((uint64_t) src[0]) << 24;
               break;
       case 8:
               ret = src[7] | ((uint64_t) src[6]) << 8;
               ret |= ((uint64_t) src[5]) << 16 | ((uint64_t) src[4]) << 24;
               ret |= ((uint64_t) src[3]) << 32 | ((uint64_t) src[2]) << 40;
               ret |= ((uint64_t) src[1]) << 48 | ((uint64_t) src[0]) << 56;
               break;
       default:
               return (0);
       }

       *data += bytes_to_read;

       return (ret);
}

void
_dwarf_write_lsb(uint8_t *data, uint64_t *offsetp, uint64_t value,
   int bytes_to_write)
{
       uint8_t *dst;

       dst = data + *offsetp;

       switch (bytes_to_write) {
       case 8:
               dst[7] = (value >> 56) & 0xff;
               dst[6] = (value >> 48) & 0xff;
               dst[5] = (value >> 40) & 0xff;
               dst[4] = (value >> 32) & 0xff;
               /* FALLTHROUGH */
       case 4:
               dst[3] = (value >> 24) & 0xff;
               dst[2] = (value >> 16) & 0xff;
               /* FALLTHROUGH */
       case 2:
               dst[1] = (value >> 8) & 0xff;
               /* FALLTHROUGH */
       case 1:
               dst[0] = value & 0xff;
               break;
       default:
               return;
       }

       *offsetp += bytes_to_write;
}

int
_dwarf_write_lsb_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   uint64_t value, int bytes_to_write, Dwarf_Error *error)
{

       assert(*size > 0);

       while (*offsetp + bytes_to_write > *size) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       _dwarf_write_lsb(*block, offsetp, value, bytes_to_write);

       return (DW_DLE_NONE);
}

void
_dwarf_write_msb(uint8_t *data, uint64_t *offsetp, uint64_t value,
   int bytes_to_write)
{
       uint8_t *dst;

       dst = data + *offsetp;

       switch (bytes_to_write) {
       case 8:
               dst[7] = value & 0xff;
               dst[6] = (value >> 8) & 0xff;
               dst[5] = (value >> 16) & 0xff;
               dst[4] = (value >> 24) & 0xff;
               value >>= 32;
               /* FALLTHROUGH */
       case 4:
               dst[3] = value & 0xff;
               dst[2] = (value >> 8) & 0xff;
               value >>= 16;
               /* FALLTHROUGH */
       case 2:
               dst[1] = value & 0xff;
               value >>= 8;
               /* FALLTHROUGH */
       case 1:
               dst[0] = value & 0xff;
               break;
       default:
               return;
       }

       *offsetp += bytes_to_write;
}

int
_dwarf_write_msb_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   uint64_t value, int bytes_to_write, Dwarf_Error *error)
{

       assert(*size > 0);

       while (*offsetp + bytes_to_write > *size) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       _dwarf_write_msb(*block, offsetp, value, bytes_to_write);

       return (DW_DLE_NONE);
}

int64_t
_dwarf_read_sleb128(uint8_t *data, uint64_t *offsetp)
{
       int64_t ret = 0;
       uint8_t b;
       int shift = 0;
       uint8_t *src;

       src = data + *offsetp;

       do {
               b = *src++;
               ret |= ((b & 0x7f) << shift);
               (*offsetp)++;
               shift += 7;
       } while ((b & 0x80) != 0);

       if (shift < 64 && (b & 0x40) != 0)
               ret |= (~0UL << shift);

       return (ret);
}

int
_dwarf_write_sleb128(uint8_t *data, uint8_t *end, int64_t val)
{
       uint8_t *p;

       p = data;

       for (;;) {
               if (p >= end)
                       return (-1);
               *p = val & 0x7f;
               val >>= 7;
               if ((val == 0 && (*p & 0x40) == 0) ||
                   (val == -1 && (*p & 0x40) != 0)) {
                       p++;
                       break;
               }
               *p++ |= 0x80;
       }

       return (p - data);
}

int
_dwarf_write_sleb128_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   int64_t val, Dwarf_Error *error)
{
       int len;

       assert(*size > 0);

       while ((len = _dwarf_write_sleb128(*block + *offsetp, *block + *size,
           val)) < 0) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       *offsetp += len;

       return (DW_DLE_NONE);
}

uint64_t
_dwarf_read_uleb128(uint8_t *data, uint64_t *offsetp)
{
       uint64_t ret = 0;
       uint8_t b;
       int shift = 0;
       uint8_t *src;

       src = data + *offsetp;

       do {
               b = *src++;
               ret |= ((b & 0x7f) << shift);
               (*offsetp)++;
               shift += 7;
       } while ((b & 0x80) != 0);

       return (ret);
}

int
_dwarf_write_uleb128(uint8_t *data, uint8_t *end, uint64_t val)
{
       uint8_t *p;

       p = data;

       do {
               if (p >= end)
                       return (-1);
               *p = val & 0x7f;
               val >>= 7;
               if (val > 0)
                       *p |= 0x80;
               p++;
       } while (val > 0);

       return (p - data);
}

int
_dwarf_write_uleb128_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   uint64_t val, Dwarf_Error *error)
{
       int len;

       assert(*size > 0);

       while ((len = _dwarf_write_uleb128(*block + *offsetp, *block + *size,
           val)) < 0) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       *offsetp += len;

       return (DW_DLE_NONE);
}

int64_t
_dwarf_decode_sleb128(uint8_t **dp)
{
       int64_t ret = 0;
       uint8_t b;
       int shift = 0;

       uint8_t *src = *dp;

       do {
               b = *src++;
               ret |= ((b & 0x7f) << shift);
               shift += 7;
       } while ((b & 0x80) != 0);

       if (shift < 64 && (b & 0x40) != 0)
               ret |= (~0UL << shift);

       *dp = src;

       return (ret);
}

uint64_t
_dwarf_decode_uleb128(uint8_t **dp)
{
       uint64_t ret = 0;
       uint8_t b;
       int shift = 0;

       uint8_t *src = *dp;

       do {
               b = *src++;
               ret |= ((b & 0x7f) << shift);
               shift += 7;
       } while ((b & 0x80) != 0);

       *dp = src;

       return (ret);
}

char *
_dwarf_read_string(void *data, Dwarf_Unsigned size, uint64_t *offsetp)
{
       char *ret, *src;

       ret = src = (char *) data + *offsetp;

       while (*src != '\0' && *offsetp < size) {
               src++;
               (*offsetp)++;
       }

       if (*src == '\0' && *offsetp < size)
               (*offsetp)++;

       return (ret);
}

void
_dwarf_write_string(void *data, uint64_t *offsetp, char *string)
{
       char *dst;

       dst = (char *) data + *offsetp;
       strcpy(dst, string);
       (*offsetp) += strlen(string) + 1;
}

int
_dwarf_write_string_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   char *string, Dwarf_Error *error)
{
       size_t len;

       assert(*size > 0);

       len = strlen(string) + 1;
       while (*offsetp + len > *size) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       _dwarf_write_string(*block, offsetp, string);

       return (DW_DLE_NONE);
}

uint8_t *
_dwarf_read_block(void *data, uint64_t *offsetp, uint64_t length)
{
       uint8_t *ret, *src;

       ret = src = (uint8_t *) data + *offsetp;

       (*offsetp) += length;

       return (ret);
}

void
_dwarf_write_block(void *data, uint64_t *offsetp, uint8_t *blk,
   uint64_t length)
{
       uint8_t *dst;

       dst = (uint8_t *) data + *offsetp;
       memcpy(dst, blk, length);
       (*offsetp) += length;
}

int
_dwarf_write_block_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   uint8_t *blk, uint64_t length, Dwarf_Error *error)
{

       assert(*size > 0);

       while (*offsetp + length > *size) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       _dwarf_write_block(*block, offsetp, blk, length);

       return (DW_DLE_NONE);
}

void
_dwarf_write_padding(void *data, uint64_t *offsetp, uint8_t byte,
   uint64_t length)
{
       uint8_t *dst;

       dst = (uint8_t *) data + *offsetp;
       memset(dst, byte, length);
       (*offsetp) += length;
}

int
_dwarf_write_padding_alloc(uint8_t **block, uint64_t *size, uint64_t *offsetp,
   uint8_t byte, uint64_t cnt, Dwarf_Error *error)
{
       assert(*size > 0);

       while (*offsetp + cnt > *size) {
               *size *= 2;
               *block = realloc(*block, (size_t) *size);
               if (*block == NULL) {
                       DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
                       return (DW_DLE_MEMORY);
               }
       }

       _dwarf_write_padding(*block, offsetp, byte, cnt);

       return (DW_DLE_NONE);
}