diff --git a/lib/libelf/Makefile b/lib/libelf/Makefile index ae1ad42e38ef..0af640c95b8a 100644 --- a/lib/libelf/Makefile +++ b/lib/libelf/Makefile @@ -18,6 +18,7 @@ SRCS= elf_begin.c \ elf_next.c \ elf_rand.c \ elf_rawfile.c \ + elf_phnum.c \ elf_shnum.c \ elf_shstrndx.c \ elf_scn.c \ @@ -46,6 +47,7 @@ SRCS= elf_begin.c \ libelf_checksum.c \ libelf_data.c \ libelf_ehdr.c \ + libelf_extended.c \ libelf_phdr.c \ libelf_shdr.c \ libelf_xlate.c \ @@ -72,6 +74,7 @@ MAN= elf.3 \ elf_getdata.3 \ elf_getident.3 \ elf_getscn.3 \ + elf_getphnum.3 \ elf_getshnum.3 \ elf_getshstrndx.3 \ elf_hash.3 \ diff --git a/lib/libelf/Version.map b/lib/libelf/Version.map index e8cc819dd616..2c8e81d50f62 100644 --- a/lib/libelf/Version.map +++ b/lib/libelf/Version.map @@ -39,6 +39,7 @@ global: elf_getdata; elf_getident; elf_getscn; + elf_getphnum; elf_getshnum; elf_getshstrndx; elf_hash; diff --git a/lib/libelf/_libelf.h b/lib/libelf/_libelf.h index 02e7cf58f3ad..08185cc76c7f 100644 --- a/lib/libelf/_libelf.h +++ b/lib/libelf/_libelf.h @@ -71,6 +71,7 @@ extern struct _libelf_globals _libelf; */ #define LIBELF_F_MALLOCED 0x010000 /* whether data was malloc'ed */ #define LIBELF_F_MMAP 0x020000 /* whether e_rawfile was mmap'ed */ +#define LIBELF_F_SHDRS_LOADED 0x040000 /* whether all shdrs were read in */ struct _Elf { int e_activations; /* activation count */ @@ -107,6 +108,9 @@ struct _Elf { Elf64_Phdr *e_phdr64; } e_phdr; STAILQ_HEAD(, _Elf_Scn) e_scn; /* section list */ + size_t e_nphdr; /* number of Phdr entries */ + size_t e_nscn; /* number of sections */ + size_t e_strndx; /* string table section index */ } e_elf; } e_u; }; @@ -171,9 +175,6 @@ void (*_libelf_get_translator(Elf_Type _t, int _direction, int _elfclass)) (char *_dst, char *_src, size_t _cnt, int _byteswap); void *_libelf_getphdr(Elf *_e, int _elfclass); void *_libelf_getshdr(Elf_Scn *_scn, int _elfclass); -int _libelf_getshnum(Elf *_e, void *_eh, int _elfclass, size_t *_shnum); -int _libelf_getshstrndx(Elf *_e, void *_eh, int _elfclass, - size_t *_shstrndx); void _libelf_init_elf(Elf *_e, Elf_Kind _kind); int _libelf_malign(Elf_Type _t, int _elfclass); size_t _libelf_msize(Elf_Type _t, int _elfclass, unsigned int _version); @@ -181,6 +182,7 @@ void *_libelf_newphdr(Elf *_e, int _elfclass, size_t _count); Elf_Data *_libelf_release_data(Elf_Data *_d); Elf *_libelf_release_elf(Elf *_e); Elf_Scn *_libelf_release_scn(Elf_Scn *_s); +int _libelf_setphnum(Elf *_e, void *_eh, int _elfclass, size_t _phnum); int _libelf_setshnum(Elf *_e, void *_eh, int _elfclass, size_t _shnum); int _libelf_setshstrndx(Elf *_e, void *_eh, int _elfclass, size_t _shstrndx); diff --git a/lib/libelf/elf_getphnum.3 b/lib/libelf/elf_getphnum.3 new file mode 100644 index 000000000000..606ceb0e755e --- /dev/null +++ b/lib/libelf/elf_getphnum.3 @@ -0,0 +1,87 @@ +.\" Copyright (c) 2006 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 Joseph Koshy ``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 Joseph Koshy 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. +.\" +.\" $FreeBSD$ +.\" +.Dd December 16, 2006 +.Os +.Dt ELF_GETPHNUM 3 +.Sh NAME +.Nm elf_getphnum +.Nd return the number of program headers in an ELF file +.Sh LIBRARY +.Lb libelf +.Sh SYNOPSIS +.In libelf.h +.Ft int +.Fn elf_getphnum "Elf *elf" "size_t *phnum" +.Sh DESCRIPTION +Function +.Fn elf_getphnum +retrieves the number of ELF program headers associated with descriptor +.Ar elf +and stores it into the location pointed to by argument +.Ar phnum . +.Pp +This routine allows applications to uniformly process both normal ELF +objects and ELF objects that use extended numbering. +.Pp +.Sh RETURN VALUES +Function +.Fn elf_getphnum +returns a non-zero value if successful, or zero in case of an +error. +.Sh ERRORS +Function +.Fn elf_getphnum +can fail with the following errors: +.Bl -tag -width "[ELF_E_RESOURCE]" +.It Bq Er ELF_E_ARGUMENT +A NULL value was passed in for argument +.Ar elf . +.It Bq Er ELF_E_ARGUMENT +Argument +.Ar elf +was not for an ELF file. +.It Bq Er ELF_E_ARGUMENT +Argument +.Ar elf +lacks an ELF Executable Header. +.It Bq Er ELF_E_HEADER +The ELF Executable Header associated with argument +.Ar elf +was corrupt. +.It Bq Er ELF_E_SECTION +The section header at index +.Dv SHN_UNDEF +was corrupt. +.El +.Sh SEE ALSO +.Xr elf 3 , +.Xr elf32_getehdr 3 , +.Xr elf64_getehdr 3 , +.Xr elf_getident 3 , +.Xr elf_getshnum 3 , +.Xr elf_getshstrndx 3 , +.Xr gelf 3 , +.Xr gelf_getehdr 3 diff --git a/lib/libelf/elf_phnum.c b/lib/libelf/elf_phnum.c new file mode 100644 index 000000000000..ff10c7e9c96e --- /dev/null +++ b/lib/libelf/elf_phnum.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2006 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "_libelf.h" + +int +elf_getphnum(Elf *e, size_t *phnum) +{ + void *eh; + int ec; + + if (e == NULL || e->e_kind != ELF_K_ELF || + ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64)) { + LIBELF_SET_ERROR(ARGUMENT, 0); + return (0); + } + + if ((eh = _libelf_ehdr(e, ec, 0)) == NULL) + return (0); + + *phnum = e->e_u.e_elf.e_nphdr; + + return (1); +} diff --git a/lib/libelf/elf_scn.c b/lib/libelf/elf_scn.c index fa1527fcdba0..139f6d9f37e1 100644 --- a/lib/libelf/elf_scn.c +++ b/lib/libelf/elf_scn.c @@ -44,7 +44,6 @@ _libelf_load_scn(Elf *e, void *ehdr) int ec, swapbytes; size_t fsz, i, shnum; uint64_t shoff; - uint32_t shtype; char *src; Elf32_Ehdr *eh32; Elf64_Ehdr *eh64; @@ -53,6 +52,7 @@ _libelf_load_scn(Elf *e, void *ehdr) assert(e != NULL); assert(ehdr != NULL); + assert((e->e_flags & LIBELF_F_SHDRS_LOADED) == 0); #define CHECK_EHDR(E,EH) do { \ if (fsz != (EH)->e_shentsize || \ @@ -62,18 +62,18 @@ _libelf_load_scn(Elf *e, void *ehdr) } \ } while (0) - fsz = gelf_fsize(e, ELF_T_SHDR, (size_t) 1, e->e_version); + ec = e->e_class; + fsz = _libelf_fsize(ELF_T_SHDR, ec, e->e_version, (size_t) 1); assert(fsz > 0); - ec = e->e_class; + shnum = e->e_u.e_elf.e_nscn; + if (ec == ELFCLASS32) { eh32 = (Elf32_Ehdr *) ehdr; - shnum = eh32->e_shnum; shoff = (uint64_t) eh32->e_shoff; CHECK_EHDR(e, eh32); } else { eh64 = (Elf64_Ehdr *) ehdr; - shnum = eh64->e_shnum; shoff = eh64->e_shoff; CHECK_EHDR(e, eh64); } @@ -82,32 +82,18 @@ _libelf_load_scn(Elf *e, void *ehdr) swapbytes = e->e_byteorder != LIBELF_PRIVATE(byteorder); src = e->e_rawfile + shoff; + + /* + * If the file is using extended numbering then section #0 + * would have already been read in. + */ + i = 0; + if (!STAILQ_EMPTY(&e->e_u.e_elf.e_scn)) { + assert(STAILQ_FIRST(&e->e_u.e_elf.e_scn) == + STAILQ_LAST(&e->e_u.e_elf.e_scn, _Elf_Scn, s_next)); - if (shnum == (size_t) 0 && shoff != 0LL) { - /* Extended section numbering */ - if ((scn = _libelf_allocate_scn(e, (size_t) 0)) == NULL) - return (0); - - (*xlator)((char *) &scn->s_shdr, src, (size_t) 1, swapbytes); - - if (ec == ELFCLASS32) { - shtype = scn->s_shdr.s_shdr32.sh_type; - shnum = scn->s_shdr.s_shdr32.sh_size; - } else { - shtype = scn->s_shdr.s_shdr64.sh_type; - shnum = scn->s_shdr.s_shdr64.sh_size; - } - - if (shtype != SHT_NULL) { - LIBELF_SET_ERROR(SECTION, 0); - return (0); - } - - scn->s_size = 0LL; - scn->s_offset = scn->s_rawoff = 0LL; - - i++; + i = 1; src += fsz; } @@ -127,6 +113,9 @@ _libelf_load_scn(Elf *e, void *ehdr) scn->s_size = scn->s_shdr.s_shdr64.sh_size; } } + + e->e_flags |= LIBELF_F_SHDRS_LOADED; + return (1); } @@ -147,7 +136,8 @@ elf_getscn(Elf *e, size_t index) if ((ehdr = _libelf_ehdr(e, ec, 0)) == NULL) return (NULL); - if (e->e_cmd != ELF_C_WRITE && STAILQ_EMPTY(&e->e_u.e_elf.e_scn) && + if (e->e_cmd != ELF_C_WRITE && + (e->e_flags & LIBELF_F_SHDRS_LOADED) == 0 && _libelf_load_scn(e, ehdr) == 0) return (NULL); @@ -174,7 +164,6 @@ elf_newscn(Elf *e) { int ec; void *ehdr; - size_t shnum; Elf_Scn *scn; if (e == NULL || e->e_kind != ELF_K_ELF) { @@ -200,30 +189,25 @@ elf_newscn(Elf *e) * file using ELF_C_READ, mess with its internal structure and * use elf_update(...,ELF_C_NULL) to compute its new layout. */ - if (e->e_cmd != ELF_C_WRITE && STAILQ_EMPTY(&e->e_u.e_elf.e_scn) && + if (e->e_cmd != ELF_C_WRITE && + (e->e_flags & LIBELF_F_SHDRS_LOADED) == 0 && _libelf_load_scn(e, ehdr) == 0) return (NULL); - if (_libelf_getshnum(e, ehdr, ec, &shnum) == 0) - return (NULL); - if (STAILQ_EMPTY(&e->e_u.e_elf.e_scn)) { - assert(shnum == 0); + assert(e->e_u.e_elf.e_nscn == 0); if ((scn = _libelf_allocate_scn(e, (size_t) SHN_UNDEF)) == NULL) return (NULL); - shnum++; + e->e_u.e_elf.e_nscn++; } - assert(shnum > 0); + assert(e->e_u.e_elf.e_nscn > 0); - if ((scn = _libelf_allocate_scn(e, shnum)) == NULL) + if ((scn = _libelf_allocate_scn(e, e->e_u.e_elf.e_nscn)) == NULL) return (NULL); - shnum++; - - if (_libelf_setshnum(e, ehdr, ec, shnum) == 0) - return (NULL); + e->e_u.e_elf.e_nscn++; (void) elf_flagscn(scn, ELF_C_SET, ELF_F_DIRTY); diff --git a/lib/libelf/elf_shnum.c b/lib/libelf/elf_shnum.c index 8a53eed07b38..d22a1fc4b6f4 100644 --- a/lib/libelf/elf_shnum.c +++ b/lib/libelf/elf_shnum.c @@ -32,80 +32,6 @@ __FBSDID("$FreeBSD$"); #include "_libelf.h" -int -_libelf_getshnum(Elf *e, void *eh, int ec, size_t *shnum) -{ - Elf64_Off off; - Elf_Scn *scn; - void *sh; - size_t n; - - if (ec == ELFCLASS32) { - n = ((Elf32_Ehdr *) eh)->e_shnum; - off = (Elf64_Off) ((Elf32_Ehdr *) eh)->e_shoff; - } else { - n = ((Elf64_Ehdr *) eh)->e_shnum; - off = ((Elf64_Ehdr *) eh)->e_shoff; - } - - if (n != 0) { - *shnum = n; - return (1); - } - - if (off == 0L) { - *shnum = (size_t) 0; - return (1); - } - - /* - * If 'e_shnum' is zero and 'e_shoff' is non-zero, the file is - * using extended section numbering, and the true section - * number is kept in the 'sh_size' field of the section header - * at offset SHN_UNDEF. - */ - if ((scn = elf_getscn(e, (size_t) SHN_UNDEF)) == NULL) - return (0); - if ((sh = _libelf_getshdr(scn, ec)) == NULL) - return (0); - - if (ec == ELFCLASS32) - *shnum = ((Elf32_Shdr *) sh)->sh_size; - else - *shnum = ((Elf64_Shdr *) sh)->sh_size; - - return (1); -} - -int -_libelf_setshnum(Elf *e, void *eh, int ec, size_t shnum) -{ - Elf_Scn *scn; - void *sh; - - if (shnum < SHN_LORESERVE) { - if (ec == ELFCLASS32) - ((Elf32_Ehdr *) eh)->e_shnum = shnum; - else - ((Elf64_Ehdr *) eh)->e_shnum = shnum; - return (1); - } - - if ((scn = elf_getscn(e, (size_t) SHN_UNDEF)) == NULL) - return (0); - if ((sh = _libelf_getshdr(scn, ec)) == NULL) - return (0); - - if (ec == ELFCLASS32) - ((Elf32_Shdr *) sh)->sh_size = shnum; - else - ((Elf64_Shdr *) sh)->sh_size = shnum; - - (void) elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); - - return (1); -} - int elf_getshnum(Elf *e, size_t *shnum) { @@ -113,11 +39,15 @@ elf_getshnum(Elf *e, size_t *shnum) int ec; if (e == NULL || e->e_kind != ELF_K_ELF || - ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64) || - ((eh = _libelf_ehdr(e, ec, 0)) == NULL)) { + ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64)) { LIBELF_SET_ERROR(ARGUMENT, 0); return (0); } - return (_libelf_getshnum(e, eh, ec, shnum)); + if ((eh = _libelf_ehdr(e, ec, 0)) == NULL) + return (0); + + *shnum = e->e_u.e_elf.e_nscn; + + return (1); } diff --git a/lib/libelf/elf_shstrndx.c b/lib/libelf/elf_shstrndx.c index cae25c395bcc..f4f502db6694 100644 --- a/lib/libelf/elf_shstrndx.c +++ b/lib/libelf/elf_shstrndx.c @@ -32,68 +32,6 @@ __FBSDID("$FreeBSD$"); #include "_libelf.h" -/* - * Helpers to get/set the e_shstrndx field of the ELF header. - */ - -int -_libelf_getshstrndx(Elf *e, void *eh, int ec, size_t *strndx) -{ - Elf_Scn *scn; - void *sh; - size_t n; - - n = (ec == ELFCLASS32) ? ((Elf32_Ehdr *) eh)->e_shstrndx : - ((Elf64_Ehdr *) eh)->e_shstrndx; - - if (n < SHN_LORESERVE) { - *strndx = n; - return (1); - } - - if ((scn = elf_getscn(e, (size_t) SHN_UNDEF)) == NULL) - return (0); - if ((sh = _libelf_getshdr(scn, ec)) == NULL) - return (0); - - if (ec == ELFCLASS32) - *strndx = ((Elf32_Shdr *) sh)->sh_link; - else - *strndx = ((Elf64_Shdr *) sh)->sh_link; - - return (1); -} - -int -_libelf_setshstrndx(Elf *e, void *eh, int ec, size_t strndx) -{ - Elf_Scn *scn; - void *sh; - - if (strndx < SHN_LORESERVE) { - if (ec == ELFCLASS32) - ((Elf32_Ehdr *) eh)->e_shstrndx = strndx; - else - ((Elf64_Ehdr *) eh)->e_shstrndx = strndx; - return (1); - } - - if ((scn = elf_getscn(e, (size_t) SHN_UNDEF)) == NULL) - return (0); - if ((sh = _libelf_getshdr(scn, ec)) == NULL) - return (0); - - if (ec == ELFCLASS32) { - ((Elf32_Ehdr *) eh)->e_shstrndx = SHN_XINDEX; - ((Elf32_Shdr *) sh)->sh_link = strndx; - } else { - ((Elf64_Ehdr *) eh)->e_shstrndx = SHN_XINDEX; - ((Elf64_Shdr *) sh)->sh_link = strndx; - } - - return (1); -} - int elf_getshstrndx(Elf *e, size_t *strndx) { @@ -101,13 +39,17 @@ elf_getshstrndx(Elf *e, size_t *strndx) int ec; if (e == NULL || e->e_kind != ELF_K_ELF || - ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64) || - ((eh = _libelf_ehdr(e, ec, 0)) == NULL)) { + ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64)) { LIBELF_SET_ERROR(ARGUMENT, 0); return (0); } - return (_libelf_getshstrndx(e, eh, ec, strndx)); + if ((eh = _libelf_ehdr(e, ec, 0)) == NULL) + return (0); + + *strndx = e->e_u.e_elf.e_strndx; + + return (1); } int diff --git a/lib/libelf/elf_update.c b/lib/libelf/elf_update.c index c0203f9f3293..5e8ee9c510cf 100644 --- a/lib/libelf/elf_update.c +++ b/lib/libelf/elf_update.c @@ -346,7 +346,6 @@ _libelf_resync_elf(Elf *e) if (ec == ELFCLASS32) { eh_byteorder = eh32->e_ident[EI_DATA]; eh_class = eh32->e_ident[EI_CLASS]; - phnum = eh32->e_phnum; phoff = (uint64_t) eh32->e_phoff; shoff = (uint64_t) eh32->e_shoff; eh_type = eh32->e_type; @@ -354,7 +353,6 @@ _libelf_resync_elf(Elf *e) } else { eh_byteorder = eh64->e_ident[EI_DATA]; eh_class = eh64->e_ident[EI_CLASS]; - phnum = eh64->e_phnum; phoff = eh64->e_phoff; shoff = eh64->e_shoff; eh_type = eh64->e_type; @@ -379,8 +377,8 @@ _libelf_resync_elf(Elf *e) return ((off_t) -1); } - if (_libelf_getshnum(e, ehdr, ec, &shnum) == 0) - return ((off_t) -1); + shnum = e->e_u.e_elf.e_nscn; + phnum = e->e_u.e_elf.e_nphdr; e->e_byteorder = eh_byteorder; @@ -471,6 +469,13 @@ _libelf_resync_elf(Elf *e) } else shoff = 0; + /* + * Set the fields of the Executable Header that could potentially use + * extended numbering. + */ + _libelf_setphnum(e, ehdr, ec, phnum); + _libelf_setshnum(e, ehdr, ec, shnum); + /* * Update the `e_phoff' and `e_shoff' fields if the library is * doing the layout. @@ -638,18 +643,17 @@ _libelf_write_elf(Elf *e, off_t newsize) ehdr = _libelf_ehdr(e, ec, 0); assert(ehdr != NULL); + phnum = e->e_u.e_elf.e_nphdr; + if (ec == ELFCLASS32) { eh32 = (Elf32_Ehdr *) ehdr; - phnum = eh32->e_phnum; phoff = (uint64_t) eh32->e_phoff; shnum = eh32->e_shnum; shoff = (uint64_t) eh32->e_shoff; - } else { eh64 = (Elf64_Ehdr *) ehdr; - phnum = eh64->e_phnum; phoff = eh64->e_phoff; shnum = eh64->e_shnum; shoff = eh64->e_shoff; diff --git a/lib/libelf/libelf_checksum.c b/lib/libelf/libelf_checksum.c index 3210412f435b..c7eb8a0e4e89 100644 --- a/lib/libelf/libelf_checksum.c +++ b/lib/libelf/libelf_checksum.c @@ -76,7 +76,7 @@ _libelf_checksum(Elf *e, int elfclass) */ checksum = 0; - for (shn = 1; shn < eh.e_shnum; shn++) { + for (shn = 1; shn < e->e_u.e_elf.e_nscn; shn++) { if ((scn = elf_getscn(e, shn)) == NULL) return (0); if (gelf_getshdr(scn, &shdr) == NULL) diff --git a/lib/libelf/libelf_ehdr.c b/lib/libelf/libelf_ehdr.c index 6c938b3a1f55..e0c488650d1a 100644 --- a/lib/libelf/libelf_ehdr.c +++ b/lib/libelf/libelf_ehdr.c @@ -36,6 +36,54 @@ __FBSDID("$FreeBSD$"); #include "_libelf.h" +/* + * Retrieve counts for sections, phdrs and the section string table index + * from section header #0 of the ELF object. + */ +static int +_libelf_load_extended(Elf *e, int ec, uint64_t shoff, uint16_t phnum, + uint16_t strndx) +{ + Elf_Scn *scn; + size_t fsz; + void (*xlator)(char *_d, char *_s, size_t _c, int _swap); + uint32_t shtype; + + assert(STAILQ_EMPTY(&e->e_u.e_elf.e_scn)); + + fsz = _libelf_fsize(ELF_T_SHDR, ec, e->e_version, 1); + assert(fsz > 0); + + if (e->e_rawsize < shoff + fsz) { /* raw file too small */ + LIBELF_SET_ERROR(HEADER, 0); + return (0); + } + + if ((scn = _libelf_allocate_scn(e, (size_t) 0)) == NULL) + return (0); + + xlator = _libelf_get_translator(ELF_T_SHDR, ELF_TOMEMORY, ec); + (*xlator)((char *) &scn->s_shdr, e->e_rawfile + shoff, (size_t) 1, + e->e_byteorder != LIBELF_PRIVATE(byteorder)); + +#define GET_SHDR_MEMBER(M) ((ec == ELFCLASS32) ? scn->s_shdr.s_shdr32.M : \ + scn->s_shdr.s_shdr64.M) + + if ((shtype = GET_SHDR_MEMBER(sh_type)) != SHT_NULL) { + LIBELF_SET_ERROR(SECTION, 0); + return (0); + } + + e->e_u.e_elf.e_nscn = GET_SHDR_MEMBER(sh_size); + e->e_u.e_elf.e_nphdr = (phnum != PN_XNUM) ? phnum : + GET_SHDR_MEMBER(sh_info); + e->e_u.e_elf.e_strndx = (strndx != SHN_XINDEX) ? strndx : + GET_SHDR_MEMBER(sh_link); +#undef GET_SHDR_MEMBER + + return (1); +} + #define EHDR_INIT(E,SZ) do { \ Elf##SZ##_Ehdr *eh = (E); \ eh->e_ident[EI_MAG0] = ELFMAG0; \ @@ -53,8 +101,10 @@ __FBSDID("$FreeBSD$"); void * _libelf_ehdr(Elf *e, int ec, int allocate) { - size_t fsz, msz; void *ehdr; + size_t fsz, msz; + uint16_t phnum, shnum, strndx; + uint64_t shoff; void (*xlator)(char *_d, char *_s, size_t _c, int _swap); assert(ec == ELFCLASS32 || ec == ELFCLASS64); @@ -85,8 +135,7 @@ _libelf_ehdr(Elf *e, int ec, int allocate) if (ehdr != NULL) /* already have a translated ehdr */ return (ehdr); - fsz = gelf_fsize(e, ELF_T_EHDR, (size_t) 1, e->e_version); - + fsz = _libelf_fsize(ELF_T_EHDR, ec, e->e_version, (size_t) 1); assert(fsz > 0); if (e->e_cmd != ELF_C_WRITE && e->e_rawsize < fsz) { @@ -121,5 +170,35 @@ _libelf_ehdr(Elf *e, int ec, int allocate) (*xlator)(ehdr, e->e_rawfile, (size_t) 1, e->e_byteorder != LIBELF_PRIVATE(byteorder)); + /* + * If extended numbering is being used, read the correct + * number of sections and program header entries. + */ + if (ec == ELFCLASS32) { + phnum = ((Elf32_Ehdr *) ehdr)->e_phnum; + shnum = ((Elf32_Ehdr *) ehdr)->e_shnum; + shoff = ((Elf32_Ehdr *) ehdr)->e_shoff; + strndx = ((Elf32_Ehdr *) ehdr)->e_shstrndx; + } else { + phnum = ((Elf64_Ehdr *) ehdr)->e_phnum; + shnum = ((Elf64_Ehdr *) ehdr)->e_shnum; + shoff = ((Elf64_Ehdr *) ehdr)->e_shoff; + strndx = ((Elf64_Ehdr *) ehdr)->e_shstrndx; + } + + if (shnum >= SHN_LORESERVE || + (shoff == 0LL && (shnum != 0 || phnum == PN_XNUM || + strndx == SHN_XINDEX))) { + LIBELF_SET_ERROR(HEADER, 0); + return (NULL); + } + + if (shnum != 0 || shoff == 0LL) { /* not using extended numbering */ + e->e_u.e_elf.e_nphdr = phnum; + e->e_u.e_elf.e_nscn = shnum; + e->e_u.e_elf.e_strndx = strndx; + } else if (_libelf_load_extended(e, ec, shoff, phnum, strndx) == 0) + return (NULL); + return (ehdr); } diff --git a/lib/libelf/libelf_extended.c b/lib/libelf/libelf_extended.c new file mode 100644 index 000000000000..6e45d818fb23 --- /dev/null +++ b/lib/libelf/libelf_extended.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2006 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "_libelf.h" + +/* + * Retrieve section #0, allocating a new section if needed. + */ +static Elf_Scn * +_libelf_getscn0(Elf *e) +{ + Elf_Scn *s; + + if ((s = STAILQ_FIRST(&e->e_u.e_elf.e_scn)) != NULL) + return (s); + + return (_libelf_allocate_scn(e, (size_t) SHN_UNDEF)); +} + +int +_libelf_setshnum(Elf *e, void *eh, int ec, size_t shnum) +{ + Elf_Scn *scn; + + if (shnum >= SHN_LORESERVE) { + if ((scn = _libelf_getscn0(e)) == NULL) + return (0); + + assert(scn->s_ndx == SHN_UNDEF); + + if (ec == ELFCLASS32) + scn->s_shdr.s_shdr32.sh_size = shnum; + else + scn->s_shdr.s_shdr64.sh_size = shnum; + + (void) elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); + + shnum = 0; + } + + if (ec == ELFCLASS32) + ((Elf32_Ehdr *) eh)->e_shnum = shnum; + else + ((Elf64_Ehdr *) eh)->e_shnum = shnum; + + + return (1); +} + +int +_libelf_setshstrndx(Elf *e, void *eh, int ec, size_t shstrndx) +{ + Elf_Scn *scn; + + if (shstrndx >= SHN_LORESERVE) { + if ((scn = _libelf_getscn0(e)) == NULL) + return (0); + + assert(scn->s_ndx == SHN_UNDEF); + + if (ec == ELFCLASS32) + scn->s_shdr.s_shdr32.sh_link = shstrndx; + else + scn->s_shdr.s_shdr64.sh_link = shstrndx; + + (void) elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); + + shstrndx = SHN_XINDEX; + } + + if (ec == ELFCLASS32) + ((Elf32_Ehdr *) eh)->e_shstrndx = shstrndx; + else + ((Elf64_Ehdr *) eh)->e_shstrndx = shstrndx; + + return (1); +} + +int +_libelf_setphnum(Elf *e, void *eh, int ec, size_t phnum) +{ + Elf_Scn *scn; + + if (phnum >= PN_XNUM) { + if ((scn = _libelf_getscn0(e)) == NULL) + return (0); + + assert(scn->s_ndx == SHN_UNDEF); + + if (ec == ELFCLASS32) + scn->s_shdr.s_shdr32.sh_info = phnum; + else + scn->s_shdr.s_shdr64.sh_info = phnum; + + (void) elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); + + phnum = PN_XNUM; + } + + if (ec == ELFCLASS32) + ((Elf32_Ehdr *) eh)->e_phnum = phnum; + else + ((Elf64_Ehdr *) eh)->e_phnum = phnum; + + return (1); +} + diff --git a/lib/libelf/libelf_phdr.c b/lib/libelf/libelf_phdr.c index 5ba58d4eb3f9..fd43851725e1 100644 --- a/lib/libelf/libelf_phdr.c +++ b/lib/libelf/libelf_phdr.c @@ -66,14 +66,14 @@ _libelf_getphdr(Elf *e, int ec) if ((ehdr = _libelf_ehdr(e, ec, 0)) == NULL) return (NULL); + phnum = e->e_u.e_elf.e_nphdr; + if (ec == ELFCLASS32) { eh32 = (Elf32_Ehdr *) ehdr; - phnum = eh32->e_phnum; phentsize = eh32->e_phentsize; phoff = (uint64_t) eh32->e_phoff; } else { eh64 = (Elf64_Ehdr *) ehdr; - phnum = eh64->e_phnum; phentsize = eh64->e_phentsize; phoff = (uint64_t) eh64->e_phoff; } @@ -112,9 +112,7 @@ _libelf_getphdr(Elf *e, int ec) void * _libelf_newphdr(Elf *e, int ec, size_t count) { - void *ehdr, *nphdr, *ophdr; - Elf32_Ehdr *eh32; - Elf64_Ehdr *eh64; + void *ehdr, *newphdr, *oldphdr; size_t msz; if (e == NULL) { @@ -135,31 +133,25 @@ _libelf_newphdr(Elf *e, int ec, size_t count) assert(msz > 0); - nphdr = NULL; - if (count > 0 && (nphdr = calloc(count, msz)) == NULL) { + newphdr = NULL; + if (count > 0 && (newphdr = calloc(count, msz)) == NULL) { LIBELF_SET_ERROR(RESOURCE, 0); return (NULL); } if (ec == ELFCLASS32) { - if ((ophdr = (void *) e->e_u.e_elf.e_phdr.e_phdr32) != NULL) - free(ophdr); - e->e_u.e_elf.e_phdr.e_phdr32 = (Elf32_Phdr *) nphdr; + if ((oldphdr = (void *) e->e_u.e_elf.e_phdr.e_phdr32) != NULL) + free(oldphdr); + e->e_u.e_elf.e_phdr.e_phdr32 = (Elf32_Phdr *) newphdr; } else { - if ((ophdr = (void *) e->e_u.e_elf.e_phdr.e_phdr64) != NULL) - free(ophdr); - e->e_u.e_elf.e_phdr.e_phdr64 = (Elf64_Phdr *) nphdr; + if ((oldphdr = (void *) e->e_u.e_elf.e_phdr.e_phdr64) != NULL) + free(oldphdr); + e->e_u.e_elf.e_phdr.e_phdr64 = (Elf64_Phdr *) newphdr; } - if (ec == ELFCLASS32) { - eh32 = (Elf32_Ehdr *) ehdr; - eh32->e_phnum = count; - } else { - eh64 = (Elf64_Ehdr *) ehdr; - eh64->e_phnum = count; - } + e->e_u.e_elf.e_nphdr = count; elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY); - return (nphdr); + return (newphdr); }