/*
 * verneed.h - copy versioning information.
 * Copyright (C) 2001 - 2006 Michael Riepe
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

#ifndef lint
static const char verneed_h_rcsid[]
    = "@(#) $Id: verneed.h,v 1.13 2008/05/23 08:15:35 michael Exp $";
#endif /* lint */

#if VER_NEED_CURRENT != 1
    #error libelf currently does not support VER_NEED_CURRENT != 1
#endif /* VER_NEED_CURRENT != 1 */

#if TOFILE

static void
__store_vernaux(vernaux_ftype *dst, const vernaux_mtype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        __store_u32L(dst->vna_hash, src->vna_hash);
        __store_u16L(dst->vna_flags, src->vna_flags);
        __store_u16L(dst->vna_other, src->vna_other);
        __store_u32L(dst->vna_name, src->vna_name);
        __store_u32L(dst->vna_next, src->vna_next);
    } else {
        __store_u32M(dst->vna_hash, src->vna_hash);
        __store_u16M(dst->vna_flags, src->vna_flags);
        __store_u16M(dst->vna_other, src->vna_other);
        __store_u32M(dst->vna_name, src->vna_name);
        __store_u32M(dst->vna_next, src->vna_next);
    }
}

static void
__store_verneed(verneed_ftype *dst, const verneed_mtype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        __store_u16L(dst->vn_version, src->vn_version);
        __store_u16L(dst->vn_cnt, src->vn_cnt);
        __store_u32L(dst->vn_file, src->vn_file);
        __store_u32L(dst->vn_aux, src->vn_aux);
        __store_u32L(dst->vn_next, src->vn_next);
    } else {
        __store_u16M(dst->vn_version, src->vn_version);
        __store_u16M(dst->vn_cnt, src->vn_cnt);
        __store_u32M(dst->vn_file, src->vn_file);
        __store_u32M(dst->vn_aux, src->vn_aux);
        __store_u32M(dst->vn_next, src->vn_next);
    }
}

typedef vernaux_mtype vernaux_stype;
typedef vernaux_ftype vernaux_dtype;
typedef verneed_mtype verneed_stype;
typedef verneed_ftype verneed_dtype;
typedef align_mtype verneed_atype;

    #define copy_vernaux_srctotmp(d, s, e) (*(d) = *(s))
    #define copy_vernaux_tmptodst(d, s, e) __store_vernaux((d), (s), (e))
    #define copy_verneed_srctotmp(d, s, e) (*(d) = *(s))
    #define copy_verneed_tmptodst(d, s, e) __store_verneed((d), (s), (e))

    #define translator_suffix _tof

#else /* TOFILE */

static void
__load_vernaux(vernaux_mtype *dst, const vernaux_ftype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        dst->vna_hash = __load_u32L(src->vna_hash);
        dst->vna_flags = __load_u16L(src->vna_flags);
        dst->vna_other = __load_u16L(src->vna_other);
        dst->vna_name = __load_u32L(src->vna_name);
        dst->vna_next = __load_u32L(src->vna_next);
    } else {
        dst->vna_hash = __load_u32M(src->vna_hash);
        dst->vna_flags = __load_u16M(src->vna_flags);
        dst->vna_other = __load_u16M(src->vna_other);
        dst->vna_name = __load_u32M(src->vna_name);
        dst->vna_next = __load_u32M(src->vna_next);
    }
}

static void
__load_verneed(verneed_mtype *dst, const verneed_ftype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        dst->vn_version = __load_u16L(src->vn_version);
        dst->vn_cnt = __load_u16L(src->vn_cnt);
        dst->vn_file = __load_u32L(src->vn_file);
        dst->vn_aux = __load_u32L(src->vn_aux);
        dst->vn_next = __load_u32L(src->vn_next);
    } else {
        dst->vn_version = __load_u16M(src->vn_version);
        dst->vn_cnt = __load_u16M(src->vn_cnt);
        dst->vn_file = __load_u32M(src->vn_file);
        dst->vn_aux = __load_u32M(src->vn_aux);
        dst->vn_next = __load_u32M(src->vn_next);
    }
}

typedef vernaux_ftype vernaux_stype;
typedef vernaux_mtype vernaux_dtype;
typedef verneed_ftype verneed_stype;
typedef verneed_mtype verneed_dtype;
typedef align_ftype verneed_atype;

    #define copy_vernaux_srctotmp(d, s, e) __load_vernaux((d), (s), (e))
    #define copy_vernaux_tmptodst(d, s, e) (*(d) = *(s))
    #define copy_verneed_srctotmp(d, s, e) __load_verneed((d), (s), (e))
    #define copy_verneed_tmptodst(d, s, e) (*(d) = *(s))

    #define translator_suffix _tom

#endif /* TOFILE */

#define cat3(a, b, c) a##b##c
#define xlt3(p, e, s) cat3(p, e, s)
#define xltprefix(x) xlt3(x, _, class_suffix)
#define translator(x, e) xlt3(xltprefix(_elf_##x), e, translator_suffix)

static size_t xlt_verneed(
    unsigned char *dst,
    const unsigned char *src,
    size_t n,
    unsigned enc) {
    size_t off;

    if (sizeof(verneed_stype) != sizeof(verneed_dtype)
        || sizeof(vernaux_stype) != sizeof(vernaux_dtype)) {
        /* never happens for ELF v1 and Verneed v1 */
        seterr(ERROR_UNIMPLEMENTED);
        return (size_t)-1;
    }
    /* size translation shortcut */
    if (dst == NULL) {
        return n;
    }
    if (src == NULL) {
        seterr(ERROR_NULLBUF);
        return (size_t)-1;
    }
    off = 0;
    while (off + sizeof(verneed_stype) <= n) {
        const verneed_stype *svn;
        verneed_dtype *dvn;
        verneed_mtype vn;
        size_t acount;
        size_t aoff;

        /*
         * check for proper alignment
         */
        if (off % sizeof(verneed_atype)) {
            seterr(ERROR_VERNEED_FORMAT);
            return (size_t)-1;
        }
        /*
         * copy and check src
         */
        svn = (verneed_stype *)(src + off);
        dvn = (verneed_dtype *)(dst + off);
        copy_verneed_srctotmp(&vn, svn, enc);
        if (vn.vn_version < 1 || vn.vn_version > VER_NEED_CURRENT) {
            seterr(ERROR_VERNEED_VERSION);
            return (size_t)-1;
        }
        if (vn.vn_cnt < 1 || vn.vn_aux == 0) {
            seterr(ERROR_VERNEED_FORMAT);
            return (size_t)-1;
        }
        copy_verneed_tmptodst(dvn, &vn, enc);
        /*
         * copy aux array
         */
        aoff = off + vn.vn_aux;
        for (acount = 0; acount < vn.vn_cnt; acount++) {
            const vernaux_stype *svna;
            vernaux_dtype *dvna;
            vernaux_mtype vna;

            /*
             * are we still inside the buffer limits?
             */
            if (aoff + sizeof(vernaux_stype) > n) {
                break;
            }
            /*
             * check for proper alignment
             */
            if (aoff % sizeof(verneed_atype)) {
                seterr(ERROR_VERNEED_FORMAT);
                return (size_t)-1;
            }
            /*
             * copy and check src
             */
            svna = (vernaux_stype *)(src + aoff);
            dvna = (vernaux_dtype *)(dst + aoff);
            copy_vernaux_srctotmp(&vna, svna, enc);
            copy_vernaux_tmptodst(dvna, &vna, enc);
            /*
             * advance to next vernaux
             */
            if (vna.vna_next == 0) {
                /* end of list */
                break;
            }
            aoff += vna.vna_next;
        }
        /*
         * advance to next verneed
         */
        if (vn.vn_next == 0) {
            /* end of list */
            break;
        }
        off += vn.vn_next;
    }
    return n;
}

size_t translator(
    verneed,
    L11)(unsigned char *dst, const unsigned char *src, size_t n) {
    return xlt_verneed(dst, src, n, ELFDATA2LSB);
}

size_t translator(
    verneed,
    M11)(unsigned char *dst, const unsigned char *src, size_t n) {
    return xlt_verneed(dst, src, n, ELFDATA2MSB);
}
