/*
 * verdef.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 verdef_h_rcsid[]
    = "@(#) $Id: verdef.h,v 1.13 2008/05/23 08:15:35 michael Exp $";
#endif /* lint */

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

#if TOFILE

static void
__store_verdaux(verdaux_ftype *dst, const verdaux_mtype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        __store_u32L(dst->vda_name, src->vda_name);
        __store_u32L(dst->vda_next, src->vda_next);
    } else {
        __store_u32M(dst->vda_name, src->vda_name);
        __store_u32M(dst->vda_next, src->vda_next);
    }
}

static void
__store_verdef(verdef_ftype *dst, const verdef_mtype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        __store_u16L(dst->vd_version, src->vd_version);
        __store_u16L(dst->vd_flags, src->vd_flags);
        __store_u16L(dst->vd_ndx, src->vd_ndx);
        __store_u16L(dst->vd_cnt, src->vd_cnt);
        __store_u32L(dst->vd_hash, src->vd_hash);
        __store_u32L(dst->vd_aux, src->vd_aux);
        __store_u32L(dst->vd_next, src->vd_next);
    } else {
        __store_u16M(dst->vd_version, src->vd_version);
        __store_u16M(dst->vd_flags, src->vd_flags);
        __store_u16M(dst->vd_ndx, src->vd_ndx);
        __store_u16M(dst->vd_cnt, src->vd_cnt);
        __store_u32M(dst->vd_hash, src->vd_hash);
        __store_u32M(dst->vd_aux, src->vd_aux);
        __store_u32M(dst->vd_next, src->vd_next);
    }
}

typedef verdaux_mtype verdaux_stype;
typedef verdaux_ftype verdaux_dtype;
typedef verdef_mtype verdef_stype;
typedef verdef_ftype verdef_dtype;
typedef align_mtype verdef_atype;

    #define copy_verdaux_srctotmp(d, s, e) (*(d) = *(s))
    #define copy_verdaux_tmptodst(d, s, e) __store_verdaux((d), (s), (e))
    #define copy_verdef_srctotmp(d, s, e) (*(d) = *(s))
    #define copy_verdef_tmptodst(d, s, e) __store_verdef((d), (s), (e))

    #define translator_suffix _tof

#else /* TOFILE */

static void
__load_verdaux(verdaux_mtype *dst, const verdaux_ftype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        dst->vda_name = __load_u32L(src->vda_name);
        dst->vda_next = __load_u32L(src->vda_next);
    } else {
        dst->vda_name = __load_u32M(src->vda_name);
        dst->vda_next = __load_u32M(src->vda_next);
    }
}

static void
__load_verdef(verdef_mtype *dst, const verdef_ftype *src, unsigned enc) {
    if (enc == ELFDATA2LSB) {
        dst->vd_version = __load_u16L(src->vd_version);
        dst->vd_flags = __load_u16L(src->vd_flags);
        dst->vd_ndx = __load_u16L(src->vd_ndx);
        dst->vd_cnt = __load_u16L(src->vd_cnt);
        dst->vd_hash = __load_u32L(src->vd_hash);
        dst->vd_aux = __load_u32L(src->vd_aux);
        dst->vd_next = __load_u32L(src->vd_next);
    } else {
        dst->vd_version = __load_u16M(src->vd_version);
        dst->vd_flags = __load_u16M(src->vd_flags);
        dst->vd_ndx = __load_u16M(src->vd_ndx);
        dst->vd_cnt = __load_u16M(src->vd_cnt);
        dst->vd_hash = __load_u32M(src->vd_hash);
        dst->vd_aux = __load_u32M(src->vd_aux);
        dst->vd_next = __load_u32M(src->vd_next);
    }
}

typedef verdaux_ftype verdaux_stype;
typedef verdaux_mtype verdaux_dtype;
typedef verdef_ftype verdef_stype;
typedef verdef_mtype verdef_dtype;
typedef align_ftype verdef_atype;

    #define copy_verdaux_srctotmp(d, s, e) __load_verdaux((d), (s), (e))
    #define copy_verdaux_tmptodst(d, s, e) (*(d) = *(s))
    #define copy_verdef_srctotmp(d, s, e) __load_verdef((d), (s), (e))
    #define copy_verdef_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_verdef(
    unsigned char *dst,
    const unsigned char *src,
    size_t n,
    unsigned enc) {
    size_t off;

    if (sizeof(verdef_stype) != sizeof(verdef_dtype)
        || sizeof(verdaux_stype) != sizeof(verdaux_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(verdef_stype) <= n) {
        const verdef_stype *svd;
        verdef_dtype *dvd;
        verdef_mtype vd;
        size_t acount;
        size_t aoff;

        /*
         * check for proper alignment
         */
        if (off % sizeof(verdef_atype)) {
            seterr(ERROR_VERDEF_FORMAT);
            return (size_t)-1;
        }
        /*
         * copy and check src
         */
        svd = (verdef_stype *)(src + off);
        dvd = (verdef_dtype *)(dst + off);
        copy_verdef_srctotmp(&vd, svd, enc);
        if (vd.vd_version < 1 || vd.vd_version > VER_DEF_CURRENT) {
            seterr(ERROR_VERDEF_VERSION);
            return (size_t)-1;
        }
        if (vd.vd_cnt < 1 || vd.vd_aux == 0) {
            seterr(ERROR_VERDEF_FORMAT);
            return (size_t)-1;
        }
        copy_verdef_tmptodst(dvd, &vd, enc);
        /*
         * copy aux array
         */
        aoff = off + vd.vd_aux;
        for (acount = 0; acount < vd.vd_cnt; acount++) {
            const verdaux_stype *svda;
            verdaux_dtype *dvda;
            verdaux_mtype vda;

            /*
             * are we still inside the buffer limits?
             */
            if (aoff + sizeof(verdaux_stype) > n) {
                break;
            }
            /*
             * check for proper alignment
             */
            if (aoff % sizeof(verdef_atype)) {
                seterr(ERROR_VERDEF_FORMAT);
                return (size_t)-1;
            }
            /*
             * copy and check src
             */
            svda = (verdaux_stype *)(src + aoff);
            dvda = (verdaux_dtype *)(dst + aoff);
            copy_verdaux_srctotmp(&vda, svda, enc);
            copy_verdaux_tmptodst(dvda, &vda, enc);
            /*
             * advance to next verdaux
             */
            if (vda.vda_next == 0) {
                /* end of list */
                break;
            }
            aoff += vda.vda_next;
        }
        /*
         * advance to next verdef
         */
        if (vd.vd_next == 0) {
            /* end of list */
            break;
        }
        off += vd.vd_next;
    }
    return n;
}

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

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