/* $NetBSD: libelf_convert.m4,v 1.4 2022/05/01 19:41:35 jkoshy Exp $ */
/*-
* Copyright (c) 2006-2011 Joseph Koshy
* 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.
*/
#if HAVE_NBTOOL_CONFIG_H
# include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#include <assert.h>
#include <libelf.h>
#include <string.h>
#include "_libelf.h"
__RCSID("$NetBSD: libelf_convert.m4,v 1.4 2022/05/01 19:41:35 jkoshy Exp $");
ELFTC_VCSID("Id: libelf_convert.m4 3174 2015-03-27 17:13:41Z emaste");
/* WARNING: GENERATED FROM __file__. */
divert(-1)
# Generate conversion routines for converting between in-memory and
# file representations of Elf data structures.
#
# These conversions use the type information defined in `elf_types.m4'.
include(SRCDIR`/elf_types.m4')
# For the purposes of generating conversion code, ELF types may be
# classified according to the following characteristics:
#
# 1. Whether the ELF type can be directly mapped to an integral C
# language type. For example, the ELF_T_WORD type maps directly to
# a 'uint32_t', but ELF_T_GNUHASH lacks a matching C type.
#
# 2. Whether the type has word size dependent variants. For example,
# ELT_T_EHDR is represented using C types Elf32_Ehdr and El64_Ehdr,
# and the ELF_T_ADDR and ELF_T_OFF types have integral C types that
# can be 32- or 64- bit wide.
#
# 3. Whether the ELF types has a fixed representation or not. For
# example, the ELF_T_SYM type has a fixed size file representation,
# some types like ELF_T_NOTE and ELF_T_GNUHASH use a variable size
# representation.
#
# We use m4 macros to generate conversion code for ELF types that have
# a fixed size representation. Conversion functions for the remaining
# types are coded by hand.
#
#* Handling File and Memory Representations
#
# `In-memory' representations of an Elf data structure use natural
# alignments and native byte ordering. This allows pointer arithmetic
# and casting to work as expected. On the other hand, the `file'
# representation of an ELF data structure could possibly be packed
# tighter than its `in-memory' representation, and could be of a
# differing byte order. Reading ELF objects that are members of `ar'
# archives present an additional complication: `ar' pads file data to
# even addresses, so file data structures in an archive member
# residing inside an `ar' archive could be at misaligned memory
# addresses when brought into memory.
#
# In summary, casting the `char *' pointers that point to memory
# representations (i.e., source pointers for the *_tof() functions and
# the destination pointers for the *_tom() functions), is safe, as
# these pointers should be correctly aligned for the memory type
# already. However, pointers to file representations have to be
# treated as being potentially unaligned and no casting can be done.
# NOCVT(TYPE) -- Do not generate the cvt[] structure entry for TYPE
define(`NOCVT',`define(`NOCVT_'$1,1)')
# NOFUNC(TYPE) -- Do not generate a conversion function for TYPE
define(`NOFUNC',`define(`NOFUNC_'$1,1)')
# IGNORE(TYPE) -- Completely ignore the type.
define(`IGNORE',`NOCVT($1)NOFUNC($1)')
# Mark ELF types that should not be processed by the M4 macros below.
# Types for which we use functions with non-standard names.
IGNORE(`BYTE') # Uses a wrapper around memcpy().
IGNORE(`NOTE') # Not a fixed size type.
# Types for which we supply hand-coded functions.
NOFUNC(`GNUHASH') # A type with complex internal structure.
NOFUNC(`VDEF') # See MAKE_VERSION_CONVERTERS below.
NOFUNC(`VNEED') # ..
# Unimplemented types.
IGNORE(`MOVEP')
# ELF types that don't exist in a 32-bit world.
NOFUNC(`XWORD32')
NOFUNC(`SXWORD32')
# `Primitive' ELF types are those that are an alias for an integral
# type. As they have no internal structure, they can be copied using
# a `memcpy()', and byteswapped in straightforward way.
#
# Mark all ELF types that directly map to integral C types.
define(`PRIM_ADDR', 1)
define(`PRIM_BYTE', 1)
define(`PRIM_HALF', 1)
define(`PRIM_LWORD', 1)
define(`PRIM_OFF', 1)
define(`PRIM_SWORD', 1)
define(`PRIM_SXWORD', 1)
define(`PRIM_WORD', 1)
define(`PRIM_XWORD', 1)
# Note the primitive types that are size-dependent.
define(`SIZEDEP_ADDR', 1)
define(`SIZEDEP_OFF', 1)
# Generate conversion functions for primitive types.
#
# Macro use: MAKEPRIMFUNCS(ELFTYPE,CTYPE,TYPESIZE,SYMSIZE)
# `$1': Name of the ELF type.
# `$2': C structure name suffix.
# `$3': ELF class specifier for types, one of [`32', `64'].
# `$4': Additional ELF class specifier, one of [`', `32', `64'].
#
# Generates a pair of conversion functions.
define(`MAKEPRIMFUNCS',`
static int
_libelf_cvt_$1$4_tof(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
Elf$3_$2 t, *s = (Elf$3_$2 *) (uintptr_t) src;
size_t c;
(void) &dsz;
if (!byteswap) {
(void) memcpy(dst, src, count * sizeof(*s));
return (1);
}
for (c = 0; c < count; c++) {
t = *s++;
SWAP_$1$4(t);
WRITE_$1$4(dst,t);
}
return (1);
}
static int
_libelf_cvt_$1$4_tom(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
Elf$3_$2 t, *d = (Elf$3_$2 *) (uintptr_t) dst;
size_t c;
if (dsz < count * sizeof(Elf$3_$2))
return (0);
if (!byteswap) {
(void) memcpy(dst, src, count * sizeof(*d));
return (1);
}
for (c = 0; c < count; c++) {
READ_$1$4(src,t);
SWAP_$1$4(t);
*d++ = t;
}
return (1);
}
')
#
# Handling composite ELF types
#
# SWAP_FIELD(FIELDNAME,ELFTYPE) -- Generate code to swap one field.
define(`SWAP_FIELD',
`ifdef(`SIZEDEP_'$2,
`SWAP_$2'SZ()`(t.$1);
',
`SWAP_$2(t.$1);
')')
# SWAP_MEMBERS(STRUCT) -- Iterate over a structure definition.
define(`SWAP_MEMBERS',
`ifelse($#,1,`/**/',
`SWAP_FIELD($1)SWAP_MEMBERS(shift($@))')')
# SWAP_STRUCT(CTYPE,SIZE) -- Generate code to swap an ELF structure.
define(`SWAP_STRUCT',
`pushdef(`SZ',$2)/* Swap an Elf$2_$1 */
SWAP_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')
# WRITE_FIELD(ELFTYPE,FIELDNAME) -- Generate code to write one field.
define(`WRITE_FIELD',
`ifdef(`SIZEDEP_'$2,
`WRITE_$2'SZ()`(dst,t.$1);
',
`WRITE_$2(dst,t.$1);
')')
# WRITE_MEMBERS(ELFTYPELIST) -- Iterate over a structure definition.
define(`WRITE_MEMBERS',
`ifelse($#,1,`/**/',
`WRITE_FIELD($1)WRITE_MEMBERS(shift($@))')')
# WRITE_STRUCT(CTYPE,SIZE) -- Generate code to write out an ELF structure.
define(`WRITE_STRUCT',
`pushdef(`SZ',$2)/* Write an Elf$2_$1 */
WRITE_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')
# READ_FIELD(ELFTYPE,CTYPE) -- Generate code to read one field.
define(`READ_FIELD',
`ifdef(`SIZEDEP_'$2,
`READ_$2'SZ()`(s,t.$1);
',
`READ_$2(s,t.$1);
')')
# READ_MEMBERS(ELFTYPELIST) -- Iterate over a structure definition.
define(`READ_MEMBERS',
`ifelse($#,1,`/**/',
`READ_FIELD($1)READ_MEMBERS(shift($@))')')
# READ_STRUCT(CTYPE,SIZE) -- Generate code to read an ELF structure.
define(`READ_STRUCT',
`pushdef(`SZ',$2)/* Read an Elf$2_$1 */
READ_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')
# MAKECOMPFUNCS -- Generate converters for composite ELF structures.
#
# When converting data to file representation, the source pointer will
# be naturally aligned for a data structure's in-memory
# representation. When converting data to memory, the destination
# pointer will be similarly aligned.
#
# For in-place conversions, when converting to file representations,
# the source buffer is large enough to hold `file' data. When
# converting from file to memory, we need to be careful to work
# `backwards', to avoid overwriting unconverted data.
#
# Macro use:
# `$1': Name of the ELF type.
# `$2': C structure name suffix.
# `$3': ELF class specifier, one of [`', `32', `64']
define(`MAKECOMPFUNCS', `ifdef(`NOFUNC_'$1$3,`',`
static int
_libelf_cvt_$1$3_tof(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
Elf$3_$2 t, *s;
size_t c;
(void) &dsz;
s = (Elf$3_$2 *) (uintptr_t) src;
for (c = 0; c < count; c++) {
t = *s++;
if (byteswap) {
SWAP_STRUCT($2,$3)
}
WRITE_STRUCT($2,$3)
}
return (1);
}
static int
_libelf_cvt_$1$3_tom(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
Elf$3_$2 t, *d;
unsigned char *s,*s0;
size_t fsz;
fsz = elf$3_fsize(ELF_T_$1, (size_t) 1, EV_CURRENT);
d = ((Elf$3_$2 *) (uintptr_t) dst) + (count - 1);
s0 = src + (count - 1) * fsz;
if (dsz < count * sizeof(Elf$3_$2))
return (0);
while (count--) {
s = s0;
READ_STRUCT($2,$3)
if (byteswap) {
SWAP_STRUCT($2,$3)
}
*d-- = t; s0 -= fsz;
}
return (1);
}
')')
# MAKE_TYPE_CONVERTER(ELFTYPE,CTYPE)
#
# Make type convertor functions from the type definition
# of the ELF type:
# - Skip convertors marked as `NOFUNC'.
# - Invoke `MAKEPRIMFUNCS' or `MAKECOMPFUNCS' as appropriate.
define(`MAKE_TYPE_CONVERTER',
`ifdef(`NOFUNC_'$1,`',
`ifdef(`PRIM_'$1,
`ifdef(`SIZEDEP_'$1,
`MAKEPRIMFUNCS($1,$2,32,32)dnl
MAKEPRIMFUNCS($1,$2,64,64)',
`MAKEPRIMFUNCS($1,$2,64)')',
`MAKECOMPFUNCS($1,$2,32)dnl
MAKECOMPFUNCS($1,$2,64)')')')
# MAKE_TYPE_CONVERTERS(ELFTYPELIST) -- Generate conversion functions.
define(`MAKE_TYPE_CONVERTERS',
`ifelse($#,1,`',
`MAKE_TYPE_CONVERTER($1)MAKE_TYPE_CONVERTERS(shift($@))')')
#
# Macros to generate entries for the table of convertors.
#
# CONV(ELFTYPE,SIZE,DIRECTION)
#
# Generate the name of a convertor function.
define(`CONV',
`ifdef(`NOFUNC_'$1$2,
`.$3$2 = NULL',
`ifdef(`PRIM_'$1,
`ifdef(`SIZEDEP_'$1,
`.$3$2 = _libelf_cvt_$1$2_$3',
`.$3$2 = _libelf_cvt_$1_$3')',
`.$3$2 = _libelf_cvt_$1$2_$3')')')
# CONVERTER_NAME(ELFTYPE)
#
# Generate the contents of one `struct cvt' instance.
define(`CONVERTER_NAME',
`ifdef(`NOCVT_'$1,`',
` [ELF_T_$1] = {
CONV($1,32,tof),
CONV($1,32,tom),
CONV($1,64,tof),
CONV($1,64,tom)
},
')')
# CONVERTER_NAMES(ELFTYPELIST)
#
# Generate the `struct cvt[]' array.
define(`CONVERTER_NAMES',
`ifelse($#,1,`',
`CONVERTER_NAME($1)CONVERTER_NAMES(shift($@))')')
#
# Handling ELF version sections.
#
# _FSZ(FIELD,BASETYPE) - return the file size for a field.
define(`_FSZ',
`ifelse($2,`HALF',2,
$2,`WORD',4)')
# FSZ(STRUCT) - determine the file size of a structure.
define(`FSZ',
`ifelse($#,1,0,
`eval(_FSZ($1) + FSZ(shift($@)))')')
# MAKE_VERSION_CONVERTERS(TYPE,BASE,AUX,PFX) -- Generate conversion
# functions for versioning structures.
define(`MAKE_VERSION_CONVERTERS',
`MAKE_VERSION_CONVERTER($1,$2,$3,$4,32)
MAKE_VERSION_CONVERTER($1,$2,$3,$4,64)')
# MAKE_VERSION_CONVERTOR(TYPE,CBASE,CAUX,PFX,SIZE) -- Generate a
# conversion function.
define(`MAKE_VERSION_CONVERTER',`
static int
_libelf_cvt_$1$5_tof(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
Elf$5_$2 t;
Elf$5_$3 a;
const size_t verfsz = FSZ(Elf$5_$2_DEF);
const size_t auxfsz = FSZ(Elf$5_$3_DEF);
const size_t vermsz = sizeof(Elf$5_$2);
const size_t auxmsz = sizeof(Elf$5_$3);
unsigned char * const dstend = dst + dsz;
unsigned char * const srcend = src + count;
unsigned char *dtmp, *dstaux, *srcaux;
Elf$5_Word aux, anext, cnt, vnext;
for (dtmp = dst, vnext = ~0U;
vnext != 0 && dtmp + verfsz <= dstend && src + vermsz <= srcend;
dtmp += vnext, src += vnext) {
/* Read in an Elf$5_$2 structure. */
t = *((Elf$5_$2 *) (uintptr_t) src);
aux = t.$4_aux;
cnt = t.$4_cnt;
vnext = t.$4_next;
if (byteswap) {
SWAP_STRUCT($2, $5)
}
dst = dtmp;
WRITE_STRUCT($2, $5)
if (aux < verfsz)
return (0);
/* Process AUX entries. */
for (anext = ~0U, dstaux = dtmp + aux, srcaux = src + aux;
cnt != 0 && anext != 0 && dstaux + auxfsz <= dstend &&
srcaux + auxmsz <= srcend;
dstaux += anext, srcaux += anext, cnt--) {
/* Read in an Elf$5_$3 structure. */
a = *((Elf$5_$3 *) (uintptr_t) srcaux);
anext = a.$4a_next;
if (byteswap) {
pushdef(`t',`a')SWAP_STRUCT($3, $5)popdef(`t')
}
dst = dstaux;
pushdef(`t',`a')WRITE_STRUCT($3, $5)popdef(`t')
}
if (anext || cnt)
return (0);
}
if (vnext)
return (0);
return (1);
}
static int
_libelf_cvt_$1$5_tom(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
Elf$5_$2 t, *dp;
Elf$5_$3 a, *ap;
const size_t verfsz = FSZ(Elf$5_$2_DEF);
const size_t auxfsz = FSZ(Elf$5_$3_DEF);
const size_t vermsz = sizeof(Elf$5_$2);
const size_t auxmsz = sizeof(Elf$5_$3);
unsigned char * const dstend = dst + dsz;
unsigned char * const srcend = src + count;
unsigned char *dstaux, *s, *srcaux, *stmp;
Elf$5_Word aux, anext, cnt, vnext;
for (stmp = src, vnext = ~0U;
vnext != 0 && stmp + verfsz <= srcend && dst + vermsz <= dstend;
stmp += vnext, dst += vnext) {
/* Read in a $1 structure. */
s = stmp;
READ_STRUCT($2, $5)
if (byteswap) {
SWAP_STRUCT($2, $5)
}
dp = (Elf$5_$2 *) (uintptr_t) dst;
*dp = t;
aux = t.$4_aux;
cnt = t.$4_cnt;
vnext = t.$4_next;
if (aux < vermsz)
return (0);
/* Process AUX entries. */
for (anext = ~0U, dstaux = dst + aux, srcaux = stmp + aux;
cnt != 0 && anext != 0 && dstaux + auxmsz <= dstend &&
srcaux + auxfsz <= srcend;
dstaux += anext, srcaux += anext, cnt--) {
s = srcaux;
pushdef(`t',`a')READ_STRUCT($3, $5)popdef(`t')
if (byteswap) {
pushdef(`t',`a')SWAP_STRUCT($3, $5)popdef(`t')
}
anext = a.$4a_next;
ap = ((Elf$5_$3 *) (uintptr_t) dstaux);
*ap = a;
}
if (anext || cnt)
return (0);
}
if (vnext)
return (0);
return (1);
}')
divert(0)
/*
* C macros to byte swap integral quantities.
*/
#define SWAP_BYTE(X) do { (void) &(X); } while (/*CONSTCOND*/0)
#define SWAP_IDENT(X) do { (void) &(X); } while (/*CONSTCOND*/0)
#define SWAP_HALF(X) do { \
uint16_t _x = (uint16_t) (X); \
uint32_t _t = _x & 0xFFU; \
_t <<= 8U; _x >>= 8U; _t |= _x & 0xFFU; \
(X) = (uint16_t) _t; \
} while (/*CONSTCOND*/0)
#define _SWAP_WORD(X, T) do { \
uint32_t _x = (uint32_t) (X); \
uint32_t _t = _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
(X) = (T) _t; \
} while (/*CONSTCOND*/0)
#define SWAP_ADDR32(X) _SWAP_WORD(X, Elf32_Addr)
#define SWAP_OFF32(X) _SWAP_WORD(X, Elf32_Off)
#define SWAP_SWORD(X) _SWAP_WORD(X, Elf32_Sword)
#define SWAP_WORD(X) _SWAP_WORD(X, Elf32_Word)
#define _SWAP_WORD64(X, T) do { \
uint64_t _x = (uint64_t) (X); \
uint64_t _t = _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
_t <<= 8; _x >>= 8; _t |= _x & 0xFF; \
(X) = (T) _t; \
} while (/*CONSTCOND*/0)
#define SWAP_ADDR64(X) _SWAP_WORD64(X, Elf64_Addr)
#define SWAP_LWORD(X) _SWAP_WORD64(X, Elf64_Lword)
#define SWAP_OFF64(X) _SWAP_WORD64(X, Elf64_Off)
#define SWAP_SXWORD(X) _SWAP_WORD64(X, Elf64_Sxword)
#define SWAP_XWORD(X) _SWAP_WORD64(X, Elf64_Xword)
/*
* C macros to write out various integral values.
*
* Note:
* - The destination pointer could be unaligned.
* - Values are written out in native byte order.
* - The destination pointer is incremented after the write.
*/
#define WRITE_BYTE(P,X) do { \
unsigned char *const _p = (unsigned char *) (P); \
_p[0] = (unsigned char) (X); \
(P) = _p + 1; \
} while (/*CONSTCOND*/0)
#define WRITE_HALF(P,X) do { \
union { \
uint16_t val; \
uint8_t bytes[2]; \
} _t; \
unsigned char *const _p = (void *) (P); \
_t.val = (X); \
_p[0] = _t.bytes[0]; \
_p[1] = _t.bytes[1]; \
(P) = (void *)(_p + 2); \
} while (/*CONSTCOND*/0)
#define WRITE_WORD(P,X) do { \
union { \
uint32_t val; \
uint8_t bytes[4]; \
} _t; \
unsigned char *const _p = (void *) (P); \
_t.val = (X); \
_p[0] = _t.bytes[0]; \
_p[1] = _t.bytes[1]; \
_p[2] = _t.bytes[2]; \
_p[3] = _t.bytes[3]; \
(P) = (void *)(_p + 4); \
} while (/*CONSTCOND*/0)
#define WRITE_ADDR32(P,X) WRITE_WORD(P,X)
#define WRITE_OFF32(P,X) WRITE_WORD(P,X)
#define WRITE_SWORD(P,X) WRITE_WORD(P,X)
#define WRITE_WORD64(P,X) do { \
union { \
uint64_t val; \
uint8_t bytes[8]; \
} _t; \
unsigned char *const _p = (void *) (P); \
_t.val = (X); \
_p[0] = _t.bytes[0]; \
_p[1] = _t.bytes[1]; \
_p[2] = _t.bytes[2]; \
_p[3] = _t.bytes[3]; \
_p[4] = _t.bytes[4]; \
_p[5] = _t.bytes[5]; \
_p[6] = _t.bytes[6]; \
_p[7] = _t.bytes[7]; \
(P) = (void *)(_p + 8); \
} while (/*CONSTCOND*/0)
#define WRITE_ADDR64(P,X) WRITE_WORD64(P,X)
#define WRITE_LWORD(P,X) WRITE_WORD64(P,X)
#define WRITE_OFF64(P,X) WRITE_WORD64(P,X)
#define WRITE_SXWORD(P,X) WRITE_WORD64(P,X)
#define WRITE_XWORD(P,X) WRITE_WORD64(P,X)
#define WRITE_IDENT(P,X) do { \
(void) memcpy((P), (X), sizeof((X))); \
(P) = (P) + EI_NIDENT; \
} while (/*CONSTCOND*/0)
/*
* C macros to read in various integral values.
*
* Note:
* - The source pointer could be unaligned.
* - Values are read in native byte order.
* - The source pointer is incremented appropriately.
*/
#define READ_BYTE(P,X) do { \
const unsigned char *const _p = \
(const void *) (P); \
(X) = _p[0]; \
(P) = (P) + 1; \
} while (/*CONSTCOND*/0)
#define READ_HALF(P,X) do { \
uint16_t _t; \
unsigned char *const _q = (unsigned char *) &_t; \
const unsigned char *const _p = \
(const unsigned char *) (P); \
_q[0] = _p[0]; \
_q[1] = _p[1]; \
(P) = (P) + 2; \
(X) = _t; \
} while (/*CONSTCOND*/0)
#define _READ_WORD(P,X,T) do { \
uint32_t _t; \
unsigned char *const _q = (unsigned char *) &_t; \
const unsigned char *const _p = \
(const unsigned char *) (P); \
_q[0] = _p[0]; \
_q[1] = _p[1]; \
_q[2] = _p[2]; \
_q[3] = _p[3]; \
(P) = (P) + 4; \
(X) = (T) _t; \
} while (/*CONSTCOND*/0)
#define READ_ADDR32(P,X) _READ_WORD(P, X, Elf32_Addr)
#define READ_OFF32(P,X) _READ_WORD(P, X, Elf32_Off)
#define READ_SWORD(P,X) _READ_WORD(P, X, Elf32_Sword)
#define READ_WORD(P,X) _READ_WORD(P, X, Elf32_Word)
#define _READ_WORD64(P,X,T) do { \
uint64_t _t; \
unsigned char *const _q = (unsigned char *) &_t; \
const unsigned char *const _p = \
(const unsigned char *) (P); \
_q[0] = _p[0]; \
_q[1] = _p[1]; \
_q[2] = _p[2]; \
_q[3] = _p[3]; \
_q[4] = _p[4]; \
_q[5] = _p[5]; \
_q[6] = _p[6]; \
_q[7] = _p[7]; \
(P) = (P) + 8; \
(X) = (T) _t; \
} while (/*CONSTCOND*/0)
#define READ_ADDR64(P,X) _READ_WORD64(P, X, Elf64_Addr)
#define READ_LWORD(P,X) _READ_WORD64(P, X, Elf64_Lword)
#define READ_OFF64(P,X) _READ_WORD64(P, X, Elf64_Off)
#define READ_SXWORD(P,X) _READ_WORD64(P, X, Elf64_Sxword)
#define READ_XWORD(P,X) _READ_WORD64(P, X, Elf64_Xword)
#define READ_IDENT(P,X) do { \
(void) memcpy((X), (P), sizeof((X))); \
(P) = (P) + EI_NIDENT; \
} while (/*CONSTCOND*/0)
#define ROUNDUP2(V,N) (V) = ((((V) + (N) - 1)) & ~((N) - 1))
/*[*/
MAKE_TYPE_CONVERTERS(ELF_TYPE_LIST)
MAKE_VERSION_CONVERTERS(VDEF,Verdef,Verdaux,vd)
MAKE_VERSION_CONVERTERS(VNEED,Verneed,Vernaux,vn)
/*]*/
/*
* Sections of type ELF_T_BYTE are never byteswapped, consequently a
* simple memcpy suffices for both directions of conversion.
*/
/*ARGSUSED*/
static int
_libelf_cvt_BYTE_tox(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
(void) &byteswap;
if (dsz < count)
return (0);
if (dst != src)
(void) memcpy(dst, src, count);
return (1);
}
/*
* Sections of type ELF_T_GNUHASH start with a header containing 4 32-bit
* words. Bloom filter data comes next, followed by hash buckets and the
* hash chain.
*
* Bloom filter words are 64 bit wide on ELFCLASS64 objects and are 32 bit
* wide on ELFCLASS32 objects. The other objects in this section are 32
* bits wide.
*
* Argument `srcsz' denotes the number of bytes to be converted. In the
* 32-bit case we need to translate `srcsz' to a count of 32-bit words.
*/
static int
_libelf_cvt_GNUHASH32_tom(unsigned char *dst, size_t dsz, unsigned char *src,
size_t srcsz, int byteswap)
{
return (_libelf_cvt_WORD_tom(dst, dsz, src, srcsz / sizeof(uint32_t),
byteswap));
}
static int
_libelf_cvt_GNUHASH32_tof(unsigned char *dst, size_t dsz, unsigned char *src,
size_t srcsz, int byteswap)
{
return (_libelf_cvt_WORD_tof(dst, dsz, src, srcsz / sizeof(uint32_t),
byteswap));
}
static int
_libelf_cvt_GNUHASH64_tom(unsigned char *dst, size_t dsz, unsigned char *src,
size_t srcsz, int byteswap)
{
size_t sz;
uint64_t t64, *bloom64;
Elf_GNU_Hash_Header *gh;
uint32_t n, nbuckets, nchains, maskwords, shift2, symndx, t32;
uint32_t *buckets, *chains;
sz = 4 * sizeof(uint32_t); /* File header is 4 words long. */
if (dsz < sizeof(Elf_GNU_Hash_Header) || srcsz < sz)
return (0);
/* Read in the section header and byteswap if needed. */
READ_WORD(src, nbuckets);
READ_WORD(src, symndx);
READ_WORD(src, maskwords);
READ_WORD(src, shift2);
srcsz -= sz;
if (byteswap) {
SWAP_WORD(nbuckets);
SWAP_WORD(symndx);
SWAP_WORD(maskwords);
SWAP_WORD(shift2);
}
/* Check source buffer and destination buffer sizes. */
sz = nbuckets * sizeof(uint32_t) + maskwords * sizeof(uint64_t);
if (srcsz < sz || dsz < sz + sizeof(Elf_GNU_Hash_Header))
return (0);
gh = (Elf_GNU_Hash_Header *) (uintptr_t) dst;
gh->gh_nbuckets = nbuckets;
gh->gh_symndx = symndx;
gh->gh_maskwords = maskwords;
gh->gh_shift2 = shift2;
dsz -= sizeof(Elf_GNU_Hash_Header);
dst += sizeof(Elf_GNU_Hash_Header);
bloom64 = (uint64_t *) (uintptr_t) dst;
/* Copy bloom filter data. */
for (n = 0; n < maskwords; n++) {
READ_XWORD(src, t64);
if (byteswap)
SWAP_XWORD(t64);
bloom64[n] = t64;
}
/* The hash buckets follows the bloom filter. */
dst += maskwords * sizeof(uint64_t);
buckets = (uint32_t *) (uintptr_t) dst;
for (n = 0; n < nbuckets; n++) {
READ_WORD(src, t32);
if (byteswap)
SWAP_WORD(t32);
buckets[n] = t32;
}
dst += nbuckets * sizeof(uint32_t);
/* The hash chain follows the hash buckets. */
dsz -= sz;
srcsz -= sz;
if (dsz < srcsz) /* Destination lacks space. */
return (0);
nchains = srcsz / sizeof(uint32_t);
chains = (uint32_t *) (uintptr_t) dst;
for (n = 0; n < nchains; n++) {
READ_WORD(src, t32);
if (byteswap)
SWAP_WORD(t32);
*chains++ = t32;
}
return (1);
}
static int
_libelf_cvt_GNUHASH64_tof(unsigned char *dst, size_t dsz, unsigned char *src,
size_t srcsz, int byteswap)
{
uint32_t *s32;
size_t sz, hdrsz;
uint64_t *s64, t64;
Elf_GNU_Hash_Header *gh;
uint32_t maskwords, n, nbuckets, nchains, t0, t1, t2, t3, t32;
hdrsz = 4 * sizeof(uint32_t); /* Header is 4x32 bits. */
if (dsz < hdrsz || srcsz < sizeof(Elf_GNU_Hash_Header))
return (0);
gh = (Elf_GNU_Hash_Header *) (uintptr_t) src;
t0 = nbuckets = gh->gh_nbuckets;
t1 = gh->gh_symndx;
t2 = maskwords = gh->gh_maskwords;
t3 = gh->gh_shift2;
src += sizeof(Elf_GNU_Hash_Header);
srcsz -= sizeof(Elf_GNU_Hash_Header);
dsz -= hdrsz;
sz = gh->gh_nbuckets * sizeof(uint32_t) + gh->gh_maskwords *
sizeof(uint64_t);
if (srcsz < sz || dsz < sz)
return (0);
/* Write out the header. */
if (byteswap) {
SWAP_WORD(t0);
SWAP_WORD(t1);
SWAP_WORD(t2);
SWAP_WORD(t3);
}
WRITE_WORD(dst, t0);
WRITE_WORD(dst, t1);
WRITE_WORD(dst, t2);
WRITE_WORD(dst, t3);
/* Copy the bloom filter and the hash table. */
s64 = (uint64_t *) (uintptr_t) src;
for (n = 0; n < maskwords; n++) {
t64 = *s64++;
if (byteswap)
SWAP_XWORD(t64);
WRITE_WORD64(dst, t64);
}
s32 = (uint32_t *) s64;
for (n = 0; n < nbuckets; n++) {
t32 = *s32++;
if (byteswap)
SWAP_WORD(t32);
WRITE_WORD(dst, t32);
}
srcsz -= sz;
dsz -= sz;
/* Copy out the hash chains. */
if (dsz < srcsz)
return (0);
nchains = srcsz / sizeof(uint32_t);
for (n = 0; n < nchains; n++) {
t32 = *s32++;
if (byteswap)
SWAP_WORD(t32);
WRITE_WORD(dst, t32);
}
return (1);
}
/*
* Elf note structures comprise a fixed size header followed by variable
* length strings. The fixed size header needs to be byte swapped, but
* not the strings.
*
* Argument `count' denotes the total number of bytes to be converted.
* The destination buffer needs to be at least `count' bytes in size.
*/
static int
_libelf_cvt_NOTE_tom(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
uint32_t namesz, descsz, type;
Elf_Note *en;
size_t sz, hdrsz;
if (dsz < count) /* Destination buffer is too small. */
return (0);
hdrsz = 3 * sizeof(uint32_t);
if (count < hdrsz) /* Source too small. */
return (0);
if (!byteswap) {
(void) memcpy(dst, src, count);
return (1);
}
/* Process all notes in the section. */
while (count > hdrsz) {
/* Read the note header. */
READ_WORD(src, namesz);
READ_WORD(src, descsz);
READ_WORD(src, type);
/* Translate. */
SWAP_WORD(namesz);
SWAP_WORD(descsz);
SWAP_WORD(type);
/* Copy out the translated note header. */
en = (Elf_Note *) (uintptr_t) dst;
en->n_namesz = namesz;
en->n_descsz = descsz;
en->n_type = type;
dsz -= sizeof(Elf_Note);
dst += sizeof(Elf_Note);
count -= hdrsz;
ROUNDUP2(namesz, 4U);
ROUNDUP2(descsz, 4U);
sz = namesz + descsz;
if (count < sz || dsz < sz) /* Buffers are too small. */
return (0);
(void) memcpy(dst, src, sz);
src += sz;
dst += sz;
count -= sz;
dsz -= sz;
}
return (1);
}
static int
_libelf_cvt_NOTE_tof(unsigned char *dst, size_t dsz, unsigned char *src,
size_t count, int byteswap)
{
uint32_t namesz, descsz, type;
Elf_Note *en;
size_t sz;
if (dsz < count)
return (0);
if (!byteswap) {
(void) memcpy(dst, src, count);
return (1);
}
while (count > sizeof(Elf_Note)) {
en = (Elf_Note *) (uintptr_t) src;
namesz = en->n_namesz;
descsz = en->n_descsz;
type = en->n_type;
sz = namesz;
ROUNDUP2(sz, 4U);
sz += descsz;
ROUNDUP2(sz, 4U);
SWAP_WORD(namesz);
SWAP_WORD(descsz);
SWAP_WORD(type);
WRITE_WORD(dst, namesz);
WRITE_WORD(dst, descsz);
WRITE_WORD(dst, type);
src += sizeof(Elf_Note);
if (count < sz)
sz = count;
(void) memcpy(dst, src, sz);
src += sz;
dst += sz;
count -= sz;
}
return (1);
}
struct converters {
int (*tof32)(unsigned char *dst, size_t dsz, unsigned char *src,
size_t cnt, int byteswap);
int (*tom32)(unsigned char *dst, size_t dsz, unsigned char *src,
size_t cnt, int byteswap);
int (*tof64)(unsigned char *dst, size_t dsz, unsigned char *src,
size_t cnt, int byteswap);
int (*tom64)(unsigned char *dst, size_t dsz, unsigned char *src,
size_t cnt, int byteswap);
};
static struct converters cvt[ELF_T_NUM] = {
/*[*/
CONVERTER_NAMES(ELF_TYPE_LIST)
/*]*/
/*
* Types that need hand-coded converters follow.
*/
[ELF_T_BYTE] = {
.tof32 = _libelf_cvt_BYTE_tox,
.tom32 = _libelf_cvt_BYTE_tox,
.tof64 = _libelf_cvt_BYTE_tox,
.tom64 = _libelf_cvt_BYTE_tox
},
[ELF_T_NOTE] = {
.tof32 = _libelf_cvt_NOTE_tof,
.tom32 = _libelf_cvt_NOTE_tom,
.tof64 = _libelf_cvt_NOTE_tof,
.tom64 = _libelf_cvt_NOTE_tom
}
};
int (*_libelf_get_translator(Elf_Type t, int direction, int elfclass))
(unsigned char *_dst, size_t dsz, unsigned char *_src, size_t _cnt,
int _byteswap)
{
assert(elfclass == ELFCLASS32 || elfclass == ELFCLASS64);
assert(direction == ELF_TOFILE || direction == ELF_TOMEMORY);
if (t >= ELF_T_NUM ||
(elfclass != ELFCLASS32 && elfclass != ELFCLASS64) ||
(direction != ELF_TOFILE && direction != ELF_TOMEMORY))
return (NULL);
return ((elfclass == ELFCLASS32) ?
(direction == ELF_TOFILE ? cvt[t].tof32 : cvt[t].tom32) :
(direction == ELF_TOFILE ? cvt[t].tof64 : cvt[t].tom64));
}