diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 000000000000..89834644fea3 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,22 @@ +freebsd_11_task: + freebsd_instance: + image: freebsd-11-2-release-amd64 + install_script: pkg install -y git py27-yaml + script: + - fetch http://tetworks.opengroup.org/downloads/38/software/Sources/3.8/tet3.8-src.tar.gz + - tar -x -C test/tet -f tet3.8-src.tar.gz + - make + +debian_stable_task: + container: + image: debian:stable + setup_script: + - apt-get update + - apt-get install -y + binutils bison bmake curl flex g++ gcc git + libarchive-dev libbsd-dev libc6-dev libexpat1-dev lsb-release + m4 perl python-yaml sharutils zlib1g-dev + script: + - curl -O http://tetworks.opengroup.org/downloads/38/software/Sources/3.8/tet3.8-src.tar.gz + - tar -x -C test/tet -z -f tet3.8-src.tar.gz + - bmake diff --git a/README.rst b/README.rst index 75e4bd671540..7610fc627164 100644 --- a/README.rst +++ b/README.rst @@ -62,12 +62,12 @@ The project uses subversion_ for its version control system. The subversion branch for the current set of sources may be accessed at the following URL:: - https://elftoolchain.svn.sourceforge.net/svnroot/elftoolchain/trunk + https://sourceforge.net/p/elftoolchain/code/HEAD/tree/trunk/ The project's source tree may be checked out from its repository by using the ``svn checkout`` command:: - % svn checkout https://elftoolchain.svn.sourceforge.net/svnroot/elftoolchain/trunk + % svn checkout https://svn.code.sf.net/p/elftoolchain/code/trunk Checked-out sources may be kept upto-date by running ``svn update`` inside the source directory:: @@ -105,10 +105,10 @@ The project's developers may be contacted using the mailing list: Reporting Bugs -------------- -Please use our `Trac instance`_ for viewing existing bug reports and +Please use our `bug tracker`_ for viewing existing bug reports and for submitting new bug reports. -.. _`Trac instance`: http://sourceforge.net/apps/trac/elftoolchain/report +.. _`bug tracker`: https://sourceforge.net/p/elftoolchain/tickets/ Additional Information @@ -119,7 +119,7 @@ website`_. .. _project website: http://elftoolchain.sourceforge.net/ -.. $Id: README.rst 3656 2018-12-26 09:46:24Z jkoshy $ +.. $Id: README.rst 3677 2019-02-11 09:37:09Z jkoshy $ .. Local Variables: .. mode: rst diff --git a/addr2line/addr2line.c b/addr2line/addr2line.c index 619214fb0fae..9c3a764fcd0d 100644 --- a/addr2line/addr2line.c +++ b/addr2line/addr2line.c @@ -40,7 +40,7 @@ #include "uthash.h" #include "_elftc.h" -ELFTC_VCSID("$Id: addr2line.c 3544 2017-06-05 14:51:44Z emaste $"); +ELFTC_VCSID("$Id: addr2line.c 3499 2016-11-25 16:06:29Z emaste $"); struct Func { char *name; diff --git a/common/elfdefinitions.h b/common/elfdefinitions.h index 810da8d4951a..2646833fb6c1 100644 --- a/common/elfdefinitions.h +++ b/common/elfdefinitions.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: elfdefinitions.h 3515 2017-01-24 22:04:22Z emaste $ + * $Id: elfdefinitions.h 3769 2019-06-29 15:15:02Z emaste $ */ /* @@ -72,7 +72,39 @@ _ELF_DEFINE_DF(DF_TEXTREL, 0x4, \ _ELF_DEFINE_DF(DF_BIND_NOW, 0x8, \ "process relocation entries at load time") \ _ELF_DEFINE_DF(DF_STATIC_TLS, 0x10, \ - "uses static thread-local storage") + "uses static thread-local storage") \ +_ELF_DEFINE_DF(DF_1_BIND_NOW, 0x1, \ + "process relocation entries at load time") \ +_ELF_DEFINE_DF(DF_1_GLOBAL, 0x2, \ + "unused") \ +_ELF_DEFINE_DF(DF_1_GROUP, 0x4, \ + "object is a member of a group") \ +_ELF_DEFINE_DF(DF_1_NODELETE, 0x8, \ + "object cannot be deleted from a process") \ +_ELF_DEFINE_DF(DF_1_LOADFLTR, 0x10, \ + "immediate load filtees") \ +_ELF_DEFINE_DF(DF_1_INITFIRST, 0x20, \ + "initialize object first") \ +_ELF_DEFINE_DF(DF_1_NOOPEN, 0x40, \ + "disallow dlopen()") \ +_ELF_DEFINE_DF(DF_1_ORIGIN, 0x80, \ + "object being loaded may refer to $ORIGIN") \ +_ELF_DEFINE_DF(DF_1_DIRECT, 0x100, \ + "direct bindings enabled") \ +_ELF_DEFINE_DF(DF_1_INTERPOSE, 0x400, \ + "object is interposer") \ +_ELF_DEFINE_DF(DF_1_NODEFLIB, 0x800, \ + "ignore default library search path") \ +_ELF_DEFINE_DF(DF_1_NODUMP, 0x1000, \ + "disallow dldump()") \ +_ELF_DEFINE_DF(DF_1_CONFALT, 0x2000, \ + "object is a configuration alternative") \ +_ELF_DEFINE_DF(DF_1_ENDFILTEE, 0x4000, \ + "filtee terminates filter search") \ +_ELF_DEFINE_DF(DF_1_DISPRELDNE, 0x8000, \ + "displacement relocation done") \ +_ELF_DEFINE_DF(DF_1_DISPRELPND, 0x10000, \ + "displacement relocation pending") #undef _ELF_DEFINE_DF #define _ELF_DEFINE_DF(N, V, DESCR) N = V , enum { @@ -2448,7 +2480,10 @@ _ELF_DEFINE_NT(NT_PSTATUS, 10, "Linux process status") \ _ELF_DEFINE_NT(NT_FPREGS, 12, "Linux floating point regset") \ _ELF_DEFINE_NT(NT_PSINFO, 13, "Linux process information") \ _ELF_DEFINE_NT(NT_LWPSTATUS, 16, "Linux lwpstatus_t type") \ -_ELF_DEFINE_NT(NT_LWPSINFO, 17, "Linux lwpinfo_t type") +_ELF_DEFINE_NT(NT_LWPSINFO, 17, "Linux lwpinfo_t type") \ +_ELF_DEFINE_NT(NT_FREEBSD_NOINIT_TAG, 2, "FreeBSD no .init tag") \ +_ELF_DEFINE_NT(NT_FREEBSD_ARCH_TAG, 3, "FreeBSD arch tag") \ +_ELF_DEFINE_NT(NT_FREEBSD_FEATURE_CTL, 4, "FreeBSD feature control") #undef _ELF_DEFINE_NT #define _ELF_DEFINE_NT(N, V, DESCR) N = V , @@ -2806,7 +2841,8 @@ typedef struct { #define ELF64_R_SYM(I) ((I) >> 32) #define ELF64_R_TYPE(I) ((I) & 0xFFFFFFFFUL) -#define ELF64_R_INFO(S,T) (((S) << 32) + ((T) & 0xFFFFFFFFUL)) +#define ELF64_R_INFO(S,T) \ + (((Elf64_Xword) (S) << 32) + ((T) & 0xFFFFFFFFUL)) /* * Symbol versioning structures. diff --git a/common/native-elf-format b/common/native-elf-format index f29bcb16716b..cef29e75111e 100755 --- a/common/native-elf-format +++ b/common/native-elf-format @@ -1,6 +1,6 @@ #!/bin/sh # -# $Id: native-elf-format 3650 2018-11-25 12:06:28Z jkoshy $ +# $Id: native-elf-format 3735 2019-04-25 19:44:47Z jkoshy $ # # Find the native ELF format for a host platform by compiling a # test object and examining the resulting object. @@ -37,6 +37,8 @@ $1 ~ "Machine:" { elfarch = "EM_MIPS"; } else if (match($0, ".*[xX]86[-_]64")) { elfarch = "EM_X86_64"; + } else if (match($0, "PowerPC64")) { + elfarch = "EM_PPC64"; } else { elfarch = "unknown"; } diff --git a/documentation/libelf-by-example/libelf-by-example.tex b/documentation/libelf-by-example/libelf-by-example.tex index 2183465bb52b..2389c6c80393 100644 --- a/documentation/libelf-by-example/libelf-by-example.tex +++ b/documentation/libelf-by-example/libelf-by-example.tex @@ -24,7 +24,7 @@ % out of the use of this software, even if advised of the possibility of % such damage. % -% $Id: libelf-by-example.tex 2457 2012-03-09 14:38:10Z jkoshy $ +% $Id: libelf-by-example.tex 3699 2019-02-28 06:34:53Z jkoshy $ % \documentclass[a4paper,pdftex]{book} @@ -2700,6 +2700,12 @@ typedef struct { \emph{parent} archive descriptor (referenced by variable \parameter{ar} in this example) to return the next archive member on the next call to function \function{elf\_begin}. + + The \function{elf\_next} function ordinarily returns the value + \constant{ELF\_C\_READ}, allowing the traversal of the archive to + continue normally. In the event of an error the function + returns the value \constant{ELF\_C\_NULL}, which causes the function + \function{elf\_begin} to stop archive traversal. \item[\coref{6}] It is good programming practice to call \function{elf\_end} on descriptors that are no longer needed. \end{description} diff --git a/documentation/libelf-by-example/prog3.txt b/documentation/libelf-by-example/prog3.txt index 8d1b350bce68..0b49555ec5e1 100644 --- a/documentation/libelf-by-example/prog3.txt +++ b/documentation/libelf-by-example/prog3.txt @@ -1,7 +1,7 @@ /* * Print the ELF Program Header Table in an ELF object. * - * $Id: prog3.txt 2133 2011-11-10 08:28:22Z jkoshy $ + * $Id: prog3.txt 3686 2019-02-22 07:54:47Z jkoshy $ */ #include @@ -11,7 +11,6 @@ #include #include #include -#include void print_ptype(size_t pt) @\co{7}@ diff --git a/documentation/libelf-by-example/prog4.txt b/documentation/libelf-by-example/prog4.txt index 465f9df595d7..919e4f5dc1aa 100644 --- a/documentation/libelf-by-example/prog4.txt +++ b/documentation/libelf-by-example/prog4.txt @@ -1,7 +1,7 @@ /* * Print the names of ELF sections. * - * $Id: prog4.txt 2133 2011-11-10 08:28:22Z jkoshy $ + * $Id: prog4.txt 3687 2019-02-22 07:55:09Z jkoshy $ */ #include @@ -18,11 +18,11 @@ main(int argc, char **argv) { int fd; Elf *e; - char *name, *p, pc[4*sizeof(char)]; Elf_Scn *scn; Elf_Data *data; GElf_Shdr shdr; size_t n, shstrndx, sz; + char *name, *p, pc[(4 * sizeof(char)) + 1]; if (argc != 2) errx(EXIT_FAILURE, "usage: %s file-name", argv[0]); diff --git a/documentation/libelf-by-example/prog6.txt b/documentation/libelf-by-example/prog6.txt index d895d3681e78..f2ad3388bc85 100644 --- a/documentation/libelf-by-example/prog6.txt +++ b/documentation/libelf-by-example/prog6.txt @@ -1,7 +1,7 @@ /* * Iterate through an ar(1) archive. * - * $Id: prog6.txt 2135 2011-11-10 08:59:47Z jkoshy $ + * $Id: prog6.txt 3699 2019-02-28 06:34:53Z jkoshy $ */ #include @@ -16,6 +16,7 @@ main(int argc, char **argv) { int fd; Elf *ar, *e; + Elf_Cmd cmd; Elf_Arhdr *arh; if (argc != 2) @@ -39,7 +40,8 @@ main(int argc, char **argv) errx(EXIT_FAILURE, "%s is not an ar(1) archive.", argv[1]); - while ((e = elf_begin(fd, ELF_C_READ, ar)) != NULL) { @\co{3}@ + cmd = ELF_C_READ; + while ((e = elf_begin(fd, cmd, ar)) != NULL) { @\co{3}@ if ((arh = elf_getarhdr(e)) == NULL) @\co{4}@ errx(EXIT_FAILURE, "elf_getarhdr() failed: %s.", elf_errmsg(-1)); @@ -47,7 +49,7 @@ main(int argc, char **argv) (void) printf("%20s %zd\n", arh->ar_name, arh->ar_size); - (void) elf_next(e); @\co{5}@ + cmd = elf_next(e); @\co{5}@ (void) elf_end(e); @\co{6}@ } diff --git a/elfcopy/ascii.c b/elfcopy/ascii.c index 4ba05c427d9e..caa2ada1dde3 100644 --- a/elfcopy/ascii.c +++ b/elfcopy/ascii.c @@ -36,7 +36,7 @@ #include "elfcopy.h" -ELFTC_VCSID("$Id: ascii.c 3487 2016-08-24 18:12:08Z emaste $"); +ELFTC_VCSID("$Id: ascii.c 3757 2019-06-28 01:15:28Z emaste $"); static void append_data(struct section *s, const void *buf, size_t sz); static char hex_digit(uint8_t n); @@ -378,9 +378,6 @@ create_elf_from_srec(struct elfcopy *ecp, int ifd) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); - /* Generate section name string table (.shstrtab). */ - set_shstrtab(ecp); - /* Update sh_name pointer for each section header entry. */ update_shdr(ecp, 0); @@ -605,9 +602,6 @@ create_elf_from_ihex(struct elfcopy *ecp, int ifd) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); - /* Generate section name string table (.shstrtab). */ - set_shstrtab(ecp); - /* Update sh_name pointer for each section header entry. */ update_shdr(ecp, 0); diff --git a/elfcopy/binary.c b/elfcopy/binary.c index d6cce9c99576..687cd4eab495 100644 --- a/elfcopy/binary.c +++ b/elfcopy/binary.c @@ -36,7 +36,7 @@ #include "elfcopy.h" -ELFTC_VCSID("$Id: binary.c 3611 2018-04-16 21:35:18Z jkoshy $"); +ELFTC_VCSID("$Id: binary.c 3757 2019-06-28 01:15:28Z emaste $"); /* * Convert ELF object to `binary'. Sections with SHF_ALLOC flag set @@ -250,11 +250,8 @@ create_elf_from_binary(struct elfcopy *ecp, int ifd, const char *ifn) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); - /* Generate section name string table (.shstrtab). */ - ecp->flags |= SYMTAB_EXIST; - set_shstrtab(ecp); - /* Update sh_name pointer for each section header entry. */ + ecp->flags |= SYMTAB_EXIST; update_shdr(ecp, 0); /* Properly set sh_link field of .symtab section. */ diff --git a/elfcopy/elfcopy.h b/elfcopy/elfcopy.h index a6578113fe55..3a6fdc76136b 100644 --- a/elfcopy/elfcopy.h +++ b/elfcopy/elfcopy.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: elfcopy.h 3615 2018-05-17 04:12:24Z kaiwang27 $ + * $Id: elfcopy.h 3757 2019-06-28 01:15:28Z emaste $ */ #include @@ -135,9 +135,13 @@ struct section { int pseudo; int nocopy; + Elftc_String_Table *strtab; + TAILQ_ENTRY(section) sec_list; /* next section */ }; +TAILQ_HEAD(sectionlist, section); + /* Internal data structure for segments. */ struct segment { uint64_t vaddr; /* virtual addr (VMA) */ @@ -311,7 +315,6 @@ struct sec_action *lookup_sec_act(struct elfcopy *_ecp, struct symop *lookup_symop_list(struct elfcopy *_ecp, const char *_name, unsigned int _op); void resync_sections(struct elfcopy *_ecp); -void set_shstrtab(struct elfcopy *_ecp); void setup_phdr(struct elfcopy *_ecp); void update_shdr(struct elfcopy *_ecp, int _update_link); diff --git a/elfcopy/main.c b/elfcopy/main.c index b58c01ac7bbd..42430ccea0a9 100644 --- a/elfcopy/main.c +++ b/elfcopy/main.c @@ -39,7 +39,7 @@ #include "elfcopy.h" -ELFTC_VCSID("$Id: main.c 3577 2017-09-14 02:19:42Z emaste $"); +ELFTC_VCSID("$Id: main.c 3757 2019-06-28 01:15:28Z emaste $"); enum options { @@ -388,9 +388,6 @@ create_elf(struct elfcopy *ecp) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); - /* Generate section name string table (.shstrtab). */ - set_shstrtab(ecp); - /* * Second processing of output sections: Update section headers. * At this stage we set name string index, update st_link and st_info @@ -486,6 +483,9 @@ free_elf(struct elfcopy *ecp) /* Free symbol table buffers. */ free_symtab(ecp); + /* Free section name string table. */ + elftc_string_table_destroy(ecp->shstrtab->strtab); + /* Free internal section list. */ if (!TAILQ_EMPTY(&ecp->v_sec)) { TAILQ_FOREACH_SAFE(sec, &ecp->v_sec, sec_list, sec_temp) { @@ -1565,7 +1565,6 @@ main(int argc, char **argv) ecp = calloc(1, sizeof(*ecp)); if (ecp == NULL) err(EXIT_FAILURE, "calloc failed"); - memset(ecp, 0, sizeof(*ecp)); ecp->itf = ecp->otf = ETF_ELF; ecp->iec = ecp->oec = ELFCLASSNONE; diff --git a/elfcopy/sections.c b/elfcopy/sections.c index d9d0edda676a..4d45d82a18e7 100644 --- a/elfcopy/sections.c +++ b/elfcopy/sections.c @@ -34,7 +34,7 @@ #include "elfcopy.h" -ELFTC_VCSID("$Id: sections.c 3646 2018-10-27 02:25:39Z emaste $"); +ELFTC_VCSID("$Id: sections.c 3758 2019-06-28 01:16:50Z emaste $"); static void add_gnu_debuglink(struct elfcopy *ecp); static uint32_t calc_crc32(const char *p, size_t len, uint32_t crc); @@ -42,19 +42,18 @@ static void check_section_rename(struct elfcopy *ecp, struct section *s); static void filter_reloc(struct elfcopy *ecp, struct section *s); static int get_section_flags(struct elfcopy *ecp, const char *name); static void insert_sections(struct elfcopy *ecp); -static void insert_to_strtab(struct section *t, const char *s); static int is_append_section(struct elfcopy *ecp, const char *name); static int is_compress_section(struct elfcopy *ecp, const char *name); static int is_debug_section(const char *name); static int is_dwo_section(const char *name); static int is_modify_section(struct elfcopy *ecp, const char *name); static int is_print_section(struct elfcopy *ecp, const char *name); -static int lookup_string(struct section *t, const char *s); static void modify_section(struct elfcopy *ecp, struct section *s); static void pad_section(struct elfcopy *ecp, struct section *s); static void print_data(const char *d, size_t sz); static void print_section(struct section *s); static void *read_section(struct section *s, size_t *size); +static void set_shstrtab(struct elfcopy *ecp); static void update_reloc(struct elfcopy *ecp, struct section *s); static void update_section_group(struct elfcopy *ecp, struct section *s); @@ -119,21 +118,19 @@ is_remove_reloc_sec(struct elfcopy *ecp, uint32_t sh_info) errx(EXIT_FAILURE, "elf_getshstrndx failed: %s", elf_errmsg(-1)); - is = NULL; - while ((is = elf_nextscn(ecp->ein, is)) != NULL) { - if (sh_info == elf_ndxscn(is)) { - if (gelf_getshdr(is, &ish) == NULL) - errx(EXIT_FAILURE, "gelf_getshdr failed: %s", - elf_errmsg(-1)); - if ((name = elf_strptr(ecp->ein, indx, ish.sh_name)) == - NULL) - errx(EXIT_FAILURE, "elf_strptr failed: %s", - elf_errmsg(-1)); - if (is_remove_section(ecp, name)) - return (1); - else - return (0); - } + is = elf_getscn(ecp->ein, sh_info); + if (is != NULL) { + if (gelf_getshdr(is, &ish) == NULL) + errx(EXIT_FAILURE, "gelf_getshdr failed: %s", + elf_errmsg(-1)); + if ((name = elf_strptr(ecp->ein, indx, ish.sh_name)) == + NULL) + errx(EXIT_FAILURE, "elf_strptr failed: %s", + elf_errmsg(-1)); + if (is_remove_section(ecp, name)) + return (1); + else + return (0); } elferr = elf_errno(); if (elferr != 0) @@ -314,18 +311,18 @@ insert_to_sec_list(struct elfcopy *ecp, struct section *sec, int tail) { struct section *s; - if (!tail) { + if (tail || TAILQ_EMPTY(&ecp->v_sec) || + TAILQ_LAST(&ecp->v_sec, sectionlist)->off <= sec->off) { + TAILQ_INSERT_TAIL(&ecp->v_sec, sec, sec_list); + } else { TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (sec->off < s->off) { TAILQ_INSERT_BEFORE(s, sec, sec_list); - goto inc_nos; + break; } } } - TAILQ_INSERT_TAIL(&ecp->v_sec, sec, sec_list); - -inc_nos: if (sec->pseudo == 0) ecp->nos++; } @@ -711,13 +708,13 @@ filter_reloc(struct elfcopy *ecp, struct section *s) #define COPYREL(REL, SZ) do { \ if (nrels == 0) { \ if ((REL##SZ = malloc(cap * \ - sizeof(Elf##SZ##_Rel))) == NULL) \ + sizeof(*REL##SZ))) == NULL) \ err(EXIT_FAILURE, "malloc failed"); \ } \ if (nrels >= cap) { \ cap *= 2; \ if ((REL##SZ = realloc(REL##SZ, cap * \ - sizeof(Elf##SZ##_Rel))) == NULL) \ + sizeof(*REL##SZ))) == NULL) \ err(EXIT_FAILURE, "realloc failed"); \ } \ REL##SZ[nrels].r_offset = REL.r_offset; \ @@ -1335,10 +1332,9 @@ insert_sections(struct elfcopy *ecp) void add_to_shstrtab(struct elfcopy *ecp, const char *name) { - struct section *s; - s = ecp->shstrtab; - insert_to_strtab(s, name); + if (elftc_string_table_insert(ecp->shstrtab->strtab, name) == 0) + errx(EXIT_FAILURE, "elftc_string_table_insert failed"); } void @@ -1348,6 +1344,9 @@ update_shdr(struct elfcopy *ecp, int update_link) GElf_Shdr osh; int elferr; + /* Finalize the section name string table (.shstrtab). */ + set_shstrtab(ecp); + TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (s->pseudo) continue; @@ -1357,7 +1356,8 @@ update_shdr(struct elfcopy *ecp, int update_link) elf_errmsg(-1)); /* Find section name in string table and set sh_name. */ - osh.sh_name = lookup_string(ecp->shstrtab, s->name); + osh.sh_name = elftc_string_table_lookup(ecp->shstrtab->strtab, + s->name); /* * sh_link needs to be updated, since the index of the @@ -1395,7 +1395,23 @@ update_shdr(struct elfcopy *ecp, int update_link) void init_shstrtab(struct elfcopy *ecp) { + Elf_Scn *shstrtab; + GElf_Shdr shdr; struct section *s; + size_t indx, sizehint; + + if (elf_getshstrndx(ecp->ein, &indx) != 0) { + shstrtab = elf_getscn(ecp->ein, indx); + if (shstrtab == NULL) + errx(EXIT_FAILURE, "elf_getscn failed: %s", + elf_errmsg(-1)); + if (gelf_getshdr(shstrtab, &shdr) != &shdr) + errx(EXIT_FAILURE, "gelf_getshdr failed: %s", + elf_errmsg(-1)); + sizehint = shdr.sh_size; + } else { + sizehint = 0; + } if ((ecp->shstrtab = calloc(1, sizeof(*ecp->shstrtab))) == NULL) err(EXIT_FAILURE, "calloc failed"); @@ -1407,19 +1423,22 @@ init_shstrtab(struct elfcopy *ecp) s->loadable = 0; s->type = SHT_STRTAB; s->vma = 0; + s->strtab = elftc_string_table_create(sizehint); - insert_to_strtab(s, ""); - insert_to_strtab(s, ".symtab"); - insert_to_strtab(s, ".strtab"); - insert_to_strtab(s, ".shstrtab"); + add_to_shstrtab(ecp, ""); + add_to_shstrtab(ecp, ".symtab"); + add_to_shstrtab(ecp, ".strtab"); + add_to_shstrtab(ecp, ".shstrtab"); } -void +static void set_shstrtab(struct elfcopy *ecp) { struct section *s; Elf_Data *data; GElf_Shdr sh; + const char *image; + size_t sz; s = ecp->shstrtab; @@ -1452,19 +1471,21 @@ set_shstrtab(struct elfcopy *ecp) * which are reserved for this in the beginning of shstrtab. */ if (!(ecp->flags & SYMTAB_EXIST)) { - s->sz -= sizeof(".symtab\0.strtab"); - memmove(s->buf, (char *)s->buf + sizeof(".symtab\0.strtab"), - s->sz); + elftc_string_table_remove(s->strtab, ".symtab"); + elftc_string_table_remove(s->strtab, ".strtab"); } - sh.sh_size = s->sz; + image = elftc_string_table_image(s->strtab, &sz); + s->sz = sz; + + sh.sh_size = sz; if (!gelf_update_shdr(s->os, &sh)) errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s", elf_errmsg(-1)); data->d_align = 1; - data->d_buf = s->buf; - data->d_size = s->sz; + data->d_buf = (void *)(uintptr_t)image; + data->d_size = sz; data->d_off = 0; data->d_type = ELF_T_BYTE; data->d_version = EV_CURRENT; @@ -1590,73 +1611,6 @@ add_gnu_debuglink(struct elfcopy *ecp) ecp->flags |= SEC_ADD; } -static void -insert_to_strtab(struct section *t, const char *s) -{ - const char *r; - char *b, *c; - size_t len, slen; - int append; - - if (t->sz == 0) { - t->cap = 512; - if ((t->buf = malloc(t->cap)) == NULL) - err(EXIT_FAILURE, "malloc failed"); - } - - slen = strlen(s); - append = 0; - b = t->buf; - for (c = b; c < b + t->sz;) { - len = strlen(c); - if (!append && len >= slen) { - r = c + (len - slen); - if (strcmp(r, s) == 0) - return; - } else if (len < slen && len != 0) { - r = s + (slen - len); - if (strcmp(c, r) == 0) { - t->sz -= len + 1; - memmove(c, c + len + 1, t->sz - (c - b)); - append = 1; - continue; - } - } - c += len + 1; - } - - while (t->sz + slen + 1 >= t->cap) { - t->cap *= 2; - if ((t->buf = realloc(t->buf, t->cap)) == NULL) - err(EXIT_FAILURE, "realloc failed"); - } - b = t->buf; - strncpy(&b[t->sz], s, slen); - b[t->sz + slen] = '\0'; - t->sz += slen + 1; -} - -static int -lookup_string(struct section *t, const char *s) -{ - const char *b, *c, *r; - size_t len, slen; - - slen = strlen(s); - b = t->buf; - for (c = b; c < b + t->sz;) { - len = strlen(c); - if (len >= slen) { - r = c + (len - slen); - if (strcmp(r, s) == 0) - return (r - b); - } - c += len + 1; - } - - return (-1); -} - static uint32_t crctable[256] = { 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, diff --git a/elfdump/elfdump.c b/elfdump/elfdump.c index dfde9786c9f4..82d0ca459f3a 100644 --- a/elfdump/elfdump.c +++ b/elfdump/elfdump.c @@ -50,7 +50,7 @@ #include "_elftc.h" -ELFTC_VCSID("$Id: elfdump.c 3584 2017-11-05 20:51:43Z jkoshy $"); +ELFTC_VCSID("$Id: elfdump.c 3762 2019-06-28 21:06:24Z emaste $"); #if defined(ELFTC_NEED_ELF_NOTE_DEFINITION) #include "native-elf-format.h" @@ -343,17 +343,20 @@ elf_phdr_type_str(unsigned int type) static char s_type[32]; switch (type) { - case PT_NULL: return "PT_NULL"; - case PT_LOAD: return "PT_LOAD"; - case PT_DYNAMIC: return "PT_DYNAMIC"; - case PT_INTERP: return "PT_INTERP"; - case PT_NOTE: return "PT_NOTE"; - case PT_SHLIB: return "PT_SHLIB"; - case PT_PHDR: return "PT_PHDR"; - case PT_TLS: return "PT_TLS"; - case PT_GNU_EH_FRAME: return "PT_GNU_EH_FRAME"; - case PT_GNU_STACK: return "PT_GNU_STACK"; - case PT_GNU_RELRO: return "PT_GNU_RELRO"; + case PT_NULL: return "PT_NULL"; + case PT_LOAD: return "PT_LOAD"; + case PT_DYNAMIC: return "PT_DYNAMIC"; + case PT_INTERP: return "PT_INTERP"; + case PT_NOTE: return "PT_NOTE"; + case PT_SHLIB: return "PT_SHLIB"; + case PT_PHDR: return "PT_PHDR"; + case PT_TLS: return "PT_TLS"; + case PT_GNU_EH_FRAME: return "PT_GNU_EH_FRAME"; + case PT_GNU_STACK: return "PT_GNU_STACK"; + case PT_GNU_RELRO: return "PT_GNU_RELRO"; + case PT_OPENBSD_RANDOMIZE: return "PT_OPENBSD_RANDOMIZE"; + case PT_OPENBSD_WXNEEDED: return "PT_OPENBSD_WXNEEDED"; + case PT_OPENBSD_BOOTDATA: return "PT_OPENBSD_BOOTDATA"; } snprintf(s_type, sizeof(s_type), "", type); return (s_type); diff --git a/libdwarf/dwarf.h b/libdwarf/dwarf.h index 632256886981..8f3733566527 100644 --- a/libdwarf/dwarf.h +++ b/libdwarf/dwarf.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: dwarf.h 3494 2016-09-20 17:16:13Z emaste $ + * $Id: dwarf.h 3749 2019-06-28 01:10:44Z emaste $ */ #ifndef _DWARF_H_ @@ -523,6 +523,24 @@ #define DW_LANG_ObjC_plus_plus 0x0011 #define DW_LANG_UPC 0x0012 #define DW_LANG_D 0x0013 +#define DW_LANG_Python 0x0014 +#define DW_LANG_OpenCL 0x0015 +#define DW_LANG_Go 0x0016 +#define DW_LANG_Modula3 0x0017 +#define DW_LANG_Haskell 0x0018 +#define DW_LANG_C_plus_plus_03 0x0019 +#define DW_LANG_C_plus_plus_11 0x001a +#define DW_LANG_OCaml 0x001b +#define DW_LANG_Rust 0x001c +#define DW_LANG_C11 0x001d +#define DW_LANG_Swift 0x001e +#define DW_LANG_Julia 0x001f +#define DW_LANG_Dylan 0x0020 +#define DW_LANG_C_plus_plus_14 0x0021 +#define DW_LANG_Fortran03 0x0022 +#define DW_LANG_Fortran08 0x0023 +#define DW_LANG_RenderScript 0x0024 +#define DW_LANG_BLISS 0x0025 #define DW_LANG_lo_user 0x8000 #define DW_LANG_Mips_Assembler 0x8001 #define DW_LANG_hi_user 0xffff diff --git a/libdwarf/dwarf_dump.c b/libdwarf/dwarf_dump.c index aa7ef1dfc82d..fe61bffc6a17 100644 --- a/libdwarf/dwarf_dump.c +++ b/libdwarf/dwarf_dump.c @@ -27,7 +27,7 @@ #include "_libdwarf.h" -ELFTC_VCSID("$Id: dwarf_dump.c 3494 2016-09-20 17:16:13Z emaste $"); +ELFTC_VCSID("$Id: dwarf_dump.c 3749 2019-06-28 01:10:44Z emaste $"); int dwarf_get_ACCESS_name(unsigned access, const char **s) @@ -605,7 +605,7 @@ dwarf_get_DS_name(unsigned ds, const char **s) case DW_DS_leading_separate: *s = "DW_DS_leading_separate"; break; case DW_DS_trailing_separate: - *s = "DW_DS_trailing_separate"; + *s = "DW_DS_trailing_separate"; break; default: return (DW_DLV_NO_ENTRY); } @@ -788,6 +788,42 @@ dwarf_get_LANG_name(unsigned lang, const char **s) *s = "DW_LANG_UPC"; break; case DW_LANG_D: *s = "DW_LANG_D"; break; + case DW_LANG_Python: + *s = "DW_LANG_Python"; break; + case DW_LANG_OpenCL: + *s = "DW_LANG_OpenCL"; break; + case DW_LANG_Go: + *s = "DW_LANG_Go"; break; + case DW_LANG_Modula3: + *s = "DW_LANG_Modula3"; break; + case DW_LANG_Haskell: + *s = "DW_LANG_Haskell"; break; + case DW_LANG_C_plus_plus_03: + *s = "DW_LANG_C_plus_plus_03"; break; + case DW_LANG_C_plus_plus_11: + *s = "DW_LANG_C_plus_plus_11"; break; + case DW_LANG_OCaml: + *s = "DW_LANG_OCaml"; break; + case DW_LANG_Rust: + *s = "DW_LANG_Rust"; break; + case DW_LANG_C11: + *s = "DW_LANG_C11"; break; + case DW_LANG_Swift: + *s = "DW_LANG_Swift"; break; + case DW_LANG_Julia: + *s = "DW_LANG_Julia"; break; + case DW_LANG_Dylan: + *s = "DW_LANG_Dylan"; break; + case DW_LANG_C_plus_plus_14: + *s = "DW_LANG_C_plus_plus_14"; break; + case DW_LANG_Fortran03: + *s = "DW_LANG_Fortran03"; break; + case DW_LANG_Fortran08: + *s = "DW_LANG_Fortran08"; break; + case DW_LANG_RenderScript: + *s = "DW_LANG_RenderScript"; break; + case DW_LANG_BLISS: + *s = "DW_LANG_BLISS"; break; case DW_LANG_lo_user: *s = "DW_LANG_lo_user"; break; case DW_LANG_Mips_Assembler: diff --git a/libdwarf/libdwarf_attr.c b/libdwarf/libdwarf_attr.c index dfbbc484c352..a25deedaa5b1 100644 --- a/libdwarf/libdwarf_attr.c +++ b/libdwarf/libdwarf_attr.c @@ -27,7 +27,7 @@ #include "_libdwarf.h" -ELFTC_VCSID("$Id: libdwarf_attr.c 3064 2014-06-06 19:35:55Z kaiwang27 $"); +ELFTC_VCSID("$Id: libdwarf_attr.c 3748 2019-06-28 01:11:13Z emaste $"); int _dwarf_attr_alloc(Dwarf_Die die, Dwarf_Attribute *atp, Dwarf_Error *error) @@ -100,7 +100,6 @@ _dwarf_attr_init(Dwarf_Debug dbg, Dwarf_Section *ds, uint64_t *offsetp, uint64_t form, int indirect, Dwarf_Error *error) { struct _Dwarf_Attribute atref; - Dwarf_Section *str; int ret; ret = DW_DLE_NONE; @@ -183,9 +182,7 @@ _dwarf_attr_init(Dwarf_Debug dbg, Dwarf_Section *ds, uint64_t *offsetp, break; case DW_FORM_strp: atref.u[0].u64 = dbg->read(ds->ds_data, offsetp, dwarf_size); - str = _dwarf_find_section(dbg, ".debug_str"); - assert(str != NULL); - atref.u[1].s = (char *) str->ds_data + atref.u[0].u64; + atref.u[1].s = _dwarf_strtab_get_table(dbg) + atref.u[0].u64; break; case DW_FORM_ref_sig8: atref.u[0].u64 = 8; diff --git a/libdwarf/libdwarf_reloc.c b/libdwarf/libdwarf_reloc.c index 2b1ad7214db6..e76675838e41 100644 --- a/libdwarf/libdwarf_reloc.c +++ b/libdwarf/libdwarf_reloc.c @@ -26,7 +26,7 @@ #include "_libdwarf.h" -ELFTC_VCSID("$Id: libdwarf_reloc.c 3578 2017-09-14 02:21:28Z emaste $"); +ELFTC_VCSID("$Id: libdwarf_reloc.c 3741 2019-06-07 06:32:01Z jkoshy $"); Dwarf_Unsigned _dwarf_get_reloc_type(Dwarf_P_Debug dbg, int is64) @@ -44,7 +44,7 @@ _dwarf_get_reloc_type(Dwarf_P_Debug dbg, int is64) case DW_ISA_SPARC: return (is64 ? R_SPARC_UA64 : R_SPARC_UA32); case DW_ISA_PPC: - return (R_PPC_ADDR32); + return (is64 ? R_PPC64_ADDR64 : R_PPC_ADDR32); case DW_ISA_ARM: return (R_ARM_ABS32); case DW_ISA_MIPS: @@ -97,6 +97,12 @@ _dwarf_get_reloc_size(Dwarf_Debug dbg, Dwarf_Unsigned rel_type) if (rel_type == R_PPC_ADDR32) return (4); break; + case EM_PPC64: + if (rel_type == R_PPC_ADDR32) + return (4); + else if (rel_type == R_PPC64_ADDR64) + return (8); + break; case EM_MIPS: if (rel_type == R_MIPS_32) return (4); diff --git a/libelf/_libelf.h b/libelf/_libelf.h index 26f17276b170..f826d1c360a5 100644 --- a/libelf/_libelf.h +++ b/libelf/_libelf.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: _libelf.h 3632 2018-10-10 21:12:43Z jkoshy $ + * $Id: _libelf.h 3738 2019-05-05 21:49:06Z jkoshy $ */ #ifndef __LIBELF_H_ @@ -90,7 +90,7 @@ struct _Elf { Elf_Kind e_kind; /* ELF_K_* */ Elf *e_parent; /* non-NULL for archive members */ unsigned char *e_rawfile; /* uninterpreted bytes */ - size_t e_rawsize; /* size of uninterpreted bytes */ + off_t e_rawsize; /* size of uninterpreted bytes */ unsigned int e_version; /* file version */ /* @@ -226,7 +226,7 @@ size_t _libelf_msize(Elf_Type _t, int _elfclass, unsigned int _version); void *_libelf_newphdr(Elf *_e, int _elfclass, size_t _count); Elf *_libelf_open_object(int _fd, Elf_Cmd _c, int _reporterror); struct _Libelf_Data *_libelf_release_data(struct _Libelf_Data *_d); -Elf *_libelf_release_elf(Elf *_e); +void _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); diff --git a/libelf/_libelf_config.h b/libelf/_libelf_config.h index d70c04f97098..f28dc8ddf24e 100644 --- a/libelf/_libelf_config.h +++ b/libelf/_libelf_config.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: _libelf_config.h 3566 2017-08-31 02:28:40Z emaste $ + * $Id: _libelf_config.h 3764 2019-06-28 21:44:46Z emaste $ */ #if defined(__APPLE__) || defined(__DragonFly__) @@ -103,6 +103,12 @@ #define LIBELF_BYTEORDER ELFDATA2LSB #define LIBELF_CLASS ELFCLASS64 +#elif defined(__riscv64) + +#define LIBELF_ARCH EM_RISCV +#define LIBELF_BYTEORDER ELFDATA2LSB +#define LIBELF_CLASS ELFCLASS64 + #elif defined(__sparc__) #define LIBELF_ARCH EM_SPARCV9 diff --git a/libelf/elf.3 b/libelf/elf.3 index 269725a09ac8..7e1d3cb9a5ed 100644 --- a/libelf/elf.3 +++ b/libelf/elf.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2006-2008,2011 Joseph Koshy. All rights reserved. +.\" Copyright (c) 2006-2008,2011,2019 Joseph Koshy. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elf.3 3643 2018-10-14 21:09:24Z jkoshy $ +.\" $Id: elf.3 3743 2019-06-12 19:36:30Z jkoshy $ .\" -.Dd October 10, 2018 +.Dd June 12, 2019 .Dt ELF 3 .Os .Sh NAME @@ -266,36 +266,43 @@ The operating version for the data in this buffer. .El .Pp .Vt Elf_Data -descriptors are usually associated with +descriptors are usually used in conjunction with .Vt Elf_Scn descriptors. -Existing data descriptors associated with an ELF section may be -structures are retrieved using the -.Fn elf_getdata -and -.Fn elf_rawdata -functions. -The -.Fn elf_newdata -function may be used to attach new data descriptors to an ELF section. .It Vt Elf_Scn .Vt Elf_Scn -descriptors represent a section in an ELF object. +descriptors represent sections in an ELF object. +These descriptors are opaque and contain no application modifiable +fields. .Pp -They are retrieved using the +The +.Vt Elf_Scn +descriptor for a specific section in an ELF object can be +retrieved using the .Fn elf_getscn function. -An application may iterate through the existing sections of an ELF -object using the +The sections contained in an ELF object can be traversed using the .Fn elf_nextscn function. -New sections may be allocated using the +New sections are allocated using the .Fn elf_newscn function. .Pp The -.Vt Elf_Scn -descriptor is opaque and contains no application modifiable fields. +.Vt Elf_Data +descriptors associated with a given section can be retrieved +using the +.Fn elf_getdata +function. +New data descriptors can be added to a section +descriptor using the +.Fn elf_newdata +function. +The untranslated +.Dq file +representation of data in a section can be retrieved using the +.Fn elf_rawdata +function. .El .Ss Supported Elf Types The following ELF datatypes are supported by the library. @@ -608,8 +615,11 @@ descriptor itself. .Xr ar 5 , .Xr elf 5 .Sh HISTORY -The original ELF(3) API was developed for Unix System V. -The current implementation of the ELF(3) API appeared in +The original +.Nm +API was developed for +.At V . +The current implementation of the API appeared in .Fx 7.0 . .Sh AUTHORS The ELF library was written by diff --git a/libelf/elf_data.c b/libelf/elf_data.c index bab70d003cee..6c6acb1441b0 100644 --- a/libelf/elf_data.c +++ b/libelf/elf_data.c @@ -32,7 +32,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: elf_data.c 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: elf_data.c 3732 2019-04-22 11:08:38Z jkoshy $"); Elf_Data * elf_getdata(Elf_Scn *s, Elf_Data *ed) @@ -42,7 +42,7 @@ elf_getdata(Elf_Scn *s, Elf_Data *ed) int elfclass, elftype; size_t count, fsz, msz; struct _Libelf_Data *d; - uint64_t sh_align, sh_offset, sh_size; + uint64_t sh_align, sh_offset, sh_size, raw_size; _libelf_translator_function *xlate; d = (struct _Libelf_Data *) ed; @@ -59,7 +59,8 @@ elf_getdata(Elf_Scn *s, Elf_Data *ed) return (&d->d_data); if (d != NULL) - return (&STAILQ_NEXT(d, d_next)->d_data); + return (STAILQ_NEXT(d, d_next) ? + &STAILQ_NEXT(d, d_next)->d_data : NULL); if (e->e_rawfile == NULL) { /* @@ -91,9 +92,10 @@ elf_getdata(Elf_Scn *s, Elf_Data *ed) return (NULL); } + raw_size = (uint64_t) e->e_rawsize; if ((elftype = _libelf_xlate_shtype(sh_type)) < ELF_T_FIRST || elftype > ELF_T_LAST || (sh_type != SHT_NOBITS && - (sh_offset > e->e_rawsize || sh_size > e->e_rawsize - sh_offset))) { + (sh_offset > raw_size || sh_size > raw_size - sh_offset))) { LIBELF_SET_ERROR(SECTION, 0); return (NULL); } @@ -116,7 +118,8 @@ elf_getdata(Elf_Scn *s, Elf_Data *ed) count = (size_t) (sh_size / fsz); - msz = _libelf_msize(elftype, elfclass, e->e_version); + if ((msz = _libelf_msize(elftype, elfclass, e->e_version)) == 0) + return (NULL); if (count > 0 && msz > SIZE_MAX / count) { LIBELF_SET_ERROR(RANGE, 0); @@ -215,7 +218,7 @@ elf_rawdata(Elf_Scn *s, Elf_Data *ed) int elf_class; uint32_t sh_type; struct _Libelf_Data *d; - uint64_t sh_align, sh_offset, sh_size; + uint64_t sh_align, sh_offset, sh_size, raw_size; if (s == NULL || (e = s->s_elf) == NULL || e->e_rawfile == NULL) { LIBELF_SET_ERROR(ARGUMENT, 0); @@ -253,8 +256,9 @@ elf_rawdata(Elf_Scn *s, Elf_Data *ed) return (NULL); } + raw_size = (uint64_t) e->e_rawsize; if (sh_type != SHT_NOBITS && - (sh_offset > e->e_rawsize || sh_size > e->e_rawsize - sh_offset)) { + (sh_offset > raw_size || sh_size > raw_size - sh_offset)) { LIBELF_SET_ERROR(SECTION, 0); return (NULL); } diff --git a/libelf/elf_end.c b/libelf/elf_end.c index 3f32ebbee8ec..ed0c3ef845a9 100644 --- a/libelf/elf_end.c +++ b/libelf/elf_end.c @@ -34,7 +34,7 @@ #include #endif -ELFTC_VCSID("$Id: elf_end.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: elf_end.c 3738 2019-05-05 21:49:06Z jkoshy $"); int elf_end(Elf *e) @@ -81,14 +81,14 @@ elf_end(Elf *e) free(e->e_rawfile); #if ELFTC_HAVE_MMAP else if (e->e_flags & LIBELF_F_RAWFILE_MMAP) - (void) munmap(e->e_rawfile, e->e_rawsize); + (void) munmap(e->e_rawfile, (size_t) e->e_rawsize); #endif } sv = e; if ((e = e->e_parent) != NULL) e->e_u.e_ar.e_nchildren--; - sv = _libelf_release_elf(sv); + _libelf_release_elf(sv); } return (0); diff --git a/libelf/elf_flagdata.3 b/libelf/elf_flagdata.3 index c210e4a56971..997da1e570be 100644 --- a/libelf/elf_flagdata.3 +++ b/libelf/elf_flagdata.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elf_flagdata.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: elf_flagdata.3 3743 2019-06-12 19:36:30Z jkoshy $ .\" -.Dd December 3, 2011 +.Dd June 12, 2019 .Dt ELF_FLAGDATA 3 .Os .Sh NAME @@ -34,7 +34,7 @@ .Nm elf_flagphdr , .Nm elf_flagscn , .Nm elf_flagshdr -.Nd manipulate flags associated with ELF(3) data structures +.Nd manipulate flags associated with ELF data structures .Sh LIBRARY .Lb libelf .Sh SYNOPSIS @@ -65,7 +65,9 @@ and .Ar scn denote the data structures whose flags need to be changed. These values should have been returned by prior calls to -functions in the ELF(3) API set: +functions in the +.Xr elf 3 +API set: .Bl -bullet -compact .It Argument @@ -175,7 +177,9 @@ function and the .Dv ELF_F_ARCHIVE and .Dv ELF_F_ARCHIVE_SYSV -flags are an extension to the ELF(3) API. +flags are an extension to the +.Xr elf 3 +API. .Sh ERRORS These functions may fail with the following errors: .Bl -tag -width "[ELF_E_RESOURCE]" diff --git a/libelf/elf_getdata.3 b/libelf/elf_getdata.3 index b287a25a00bc..66a6ef4fa9dd 100644 --- a/libelf/elf_getdata.3 +++ b/libelf/elf_getdata.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elf_getdata.3 3643 2018-10-14 21:09:24Z jkoshy $ +.\" $Id: elf_getdata.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd January 26, 2011 +.Dd April 22, 2019 .Dt ELF_GETDATA 3 .Os .Sh NAME @@ -214,7 +214,12 @@ is incorrect. .It Bq Er ELF_E_UNIMPL The section type associated with section .Ar scn -is currently unsupported by the library. +is not supported. +.It Bq Er ELF_E_VERSION +Section +.Ar scn +was associated with an ELF object with an unsupported +version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/elf_getident.c b/libelf/elf_getident.c index a69139c932cf..1196a76329f6 100644 --- a/libelf/elf_getident.c +++ b/libelf/elf_getident.c @@ -30,7 +30,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: elf_getident.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: elf_getident.c 3712 2019-03-16 22:23:34Z jkoshy $"); char * elf_getident(Elf *e, size_t *sz) @@ -54,7 +54,7 @@ elf_getident(Elf *e, size_t *sz) else if (e->e_kind == ELF_K_ELF) *sz = EI_NIDENT; else - *sz = e->e_rawsize; + *sz = (size_t) e->e_rawsize; } return ((char *) e->e_rawfile); diff --git a/libelf/elf_next.3 b/libelf/elf_next.3 index 0c2d92d67e2d..1ef02a9851b9 100644 --- a/libelf/elf_next.3 +++ b/libelf/elf_next.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elf_next.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: elf_next.3 3698 2019-02-28 06:34:42Z jkoshy $ .\" -.Dd June 17, 2006 +.Dd February 27, 2019 .Dt ELF_NEXT 3 .Os .Sh NAME @@ -88,6 +88,9 @@ Argument was not associated with a containing .Xr ar 1 archive. +.It Bq Er ELF_E_ARGUMENT +An error was encountered while parsing the archive containing argument +.Ar elf . .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/elf_next.c b/libelf/elf_next.c index 4c33714f291a..dd5a93e4ed1d 100644 --- a/libelf/elf_next.c +++ b/libelf/elf_next.c @@ -30,7 +30,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: elf_next.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: elf_next.c 3710 2019-03-12 09:42:35Z jkoshy $"); Elf_Cmd elf_next(Elf *e) @@ -60,5 +60,19 @@ elf_next(Elf *e) parent->e_u.e_ar.e_next = (next >= (off_t) parent->e_rawsize) ? (off_t) 0 : next; + /* + * Return an error if the 'e_next' field falls outside the current + * file. + * + * This check is performed after updating the parent descriptor's + * 'e_next' field so that the next call to elf_begin(3) will terminate + * traversal of a too-small archive even if client code forgets to + * check the return value from elf_next(3). + */ + if (next > (off_t) parent->e_rawsize) { + LIBELF_SET_ERROR(ARGUMENT, 0); + return (ELF_C_NULL); + } + return (ELF_C_READ); } diff --git a/libelf/elf_open.3 b/libelf/elf_open.3 index acfbe8bc1cfd..054036a24935 100644 --- a/libelf/elf_open.3 +++ b/libelf/elf_open.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elf_open.3 3643 2018-10-14 21:09:24Z jkoshy $ +.\" $Id: elf_open.3 3743 2019-06-12 19:36:30Z jkoshy $ .\" -.Dd May 31, 2012 +.Dd June 12, 2019 .Dt ELF_OPEN 3 .Os .Sh NAME @@ -43,7 +43,9 @@ The functions .Fn elf_open and .Fn elf_openmemory -are extensions to the ELF(3) API, for the internal use of the +are extensions to the +.Xr elf 3 +API, for the internal use of the Elftoolchain project. Portable applications should not use these functions. .Pp @@ -71,7 +73,9 @@ specifies the size of the memory area in bytes. The function returns a pointer to a ELF descriptor if successful, or NULL if an error occurred. .Sh COMPATIBILITY -These functions are non-standard extensions to the ELF(3) API set. +These functions are non-standard extensions to the +.Xr elf 3 +API set. .Pp The behavior of these functions differs from their counterparts .Xr elf_begin 3 diff --git a/libelf/elf_rand.c b/libelf/elf_rand.c index eb2c9eaa2c1a..ac3bd0bc60e0 100644 --- a/libelf/elf_rand.c +++ b/libelf/elf_rand.c @@ -29,16 +29,25 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: elf_rand.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: elf_rand.c 3716 2019-03-18 22:01:01Z jkoshy $"); off_t elf_rand(Elf *ar, off_t offset) { struct ar_hdr *arh; + off_t offset_of_member; if (ar == NULL || ar->e_kind != ELF_K_AR || (offset & 1) || offset < SARMAG || - (size_t) offset + sizeof(struct ar_hdr) >= ar->e_rawsize) { + offset >= ar->e_rawsize) { + LIBELF_SET_ERROR(ARGUMENT, 0); + return 0; + } + + offset_of_member = offset + (off_t) sizeof(struct ar_hdr); + + if (offset_of_member <= 0 || /* Numeric overflow. */ + offset_of_member >= ar->e_rawsize) { LIBELF_SET_ERROR(ARGUMENT, 0); return 0; } diff --git a/libelf/elf_rawfile.c b/libelf/elf_rawfile.c index f63982fb0b4b..97abf06a2267 100644 --- a/libelf/elf_rawfile.c +++ b/libelf/elf_rawfile.c @@ -28,15 +28,13 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: elf_rawfile.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: elf_rawfile.c 3712 2019-03-16 22:23:34Z jkoshy $"); char * elf_rawfile(Elf *e, size_t *sz) { - size_t size; unsigned char *ptr; - size = e ? e->e_rawsize : 0; ptr = NULL; if (e == NULL) @@ -45,7 +43,7 @@ elf_rawfile(Elf *e, size_t *sz) LIBELF_SET_ERROR(SEQUENCE, 0); if (sz) - *sz = size; + *sz = e ? (size_t) e->e_rawsize : 0; return ((char *) ptr); } diff --git a/libelf/elf_scn.c b/libelf/elf_scn.c index b72fef66c46b..f702474f35b2 100644 --- a/libelf/elf_scn.c +++ b/libelf/elf_scn.c @@ -36,7 +36,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: elf_scn.c 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: elf_scn.c 3712 2019-03-16 22:23:34Z jkoshy $"); /* * Load an ELF section table and create a list of Elf_Scn structures. @@ -58,10 +58,11 @@ _libelf_load_section_headers(Elf *e, void *ehdr) assert((e->e_flags & LIBELF_F_SHDRS_LOADED) == 0); #define CHECK_EHDR(E,EH) do { \ - if (shoff > e->e_rawsize || \ + uintmax_t rawsize = (uintmax_t) e->e_rawsize; \ + if (shoff > (uintmax_t) e->e_rawsize || \ fsz != (EH)->e_shentsize || \ shnum > SIZE_MAX / fsz || \ - fsz * shnum > e->e_rawsize - shoff) { \ + fsz * shnum > rawsize - shoff) { \ LIBELF_SET_ERROR(HEADER, 0); \ return (0); \ } \ diff --git a/libelf/elf_update.3 b/libelf/elf_update.3 index b6419cf9bf53..b87ab1640d1f 100644 --- a/libelf/elf_update.3 +++ b/libelf/elf_update.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elf_update.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: elf_update.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 14, 2011 +.Dd April 22, 2019 .Dt ELF_UPDATE 3 .Os .Sh NAME @@ -345,6 +345,10 @@ operation was requested after a prior call to disassociated the ELF descriptor .Ar elf from its underlying file. +.It Bq Er ELF_E_UNIMPL +Argument +.Ar elf +contained a section with an unsupported ELF type. .It Bq Er ELF_E_VERSION Argument .Ar elf diff --git a/libelf/elf_update.c b/libelf/elf_update.c index 25fa98d0bb12..d99a7bf937c6 100644 --- a/libelf/elf_update.c +++ b/libelf/elf_update.c @@ -41,7 +41,7 @@ #include #endif -ELFTC_VCSID("$Id: elf_update.c 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: elf_update.c 3763 2019-06-28 21:43:27Z emaste $"); /* * Layout strategy: @@ -142,7 +142,7 @@ _libelf_compute_section_extents(Elf *e, Elf_Scn *s, off_t rc) assert(sh_type != SHT_NULL && sh_type != SHT_NOBITS); elftype = _libelf_xlate_shtype(sh_type); - if (elftype > ELF_T_LAST) { + if (elftype < ELF_T_FIRST || elftype > ELF_T_LAST) { LIBELF_SET_ERROR(SECTION, 0); return (0); } @@ -221,11 +221,17 @@ _libelf_compute_section_extents(Elf *e, Elf_Scn *s, off_t rc) return (0); } + /* + * The data buffer's ELF type, ELF class and ELF version + * should be supported. + */ + if ((msz = _libelf_msize(d->d_type, ec, e->e_version)) == 0) + return (0); + /* * The buffer's size should be a multiple of the * memory size of the underlying type. */ - msz = _libelf_msize(d->d_type, ec, e->e_version); if (d->d_size % msz) { LIBELF_SET_ERROR(DATA, 0); return (0); @@ -800,7 +806,8 @@ _libelf_write_scn(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) d = &ld->d_data; - msz = _libelf_msize(d->d_type, ec, e->e_version); + if ((msz = _libelf_msize(d->d_type, ec, e->e_version)) == 0) + return ((off_t) -1); if ((uint64_t) rc < sh_off + d->d_off) (void) memset(nf + rc, @@ -812,6 +819,7 @@ _libelf_write_scn(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) assert(d->d_buf != NULL); assert(d->d_version == e->e_version); assert(d->d_size % msz == 0); + assert(msz != 0); nobjects = (size_t) (d->d_size / msz); @@ -851,7 +859,8 @@ _libelf_write_ehdr(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) assert(ehdr != NULL); fsz = _libelf_fsize(ELF_T_EHDR, ec, e->e_version, (size_t) 1); - msz = _libelf_msize(ELF_T_EHDR, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_EHDR, ec, e->e_version)) == 0) + return ((off_t) -1); em = _libelf_elfmachine(e); @@ -885,15 +894,17 @@ _libelf_write_phdr(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) Elf32_Ehdr *eh32; Elf64_Ehdr *eh64; Elf_Data dst, src; - size_t fsz, phnum; + size_t fsz, msz, phnum; uint64_t phoff; assert(ex->ex_type == ELF_EXTENT_PHDR); ec = e->e_class; - ehdr = _libelf_ehdr(e, ec, 0); - phnum = e->e_u.e_elf.e_nphdr; + ehdr = _libelf_ehdr(e, ec, 0); + assert(ehdr != NULL); + + phnum = e->e_u.e_elf.e_nphdr; assert(phnum > 0); if (ec == ELFCLASS32) { @@ -913,14 +924,15 @@ _libelf_write_phdr(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) (void) memset(&dst, 0, sizeof(dst)); (void) memset(&src, 0, sizeof(src)); + if ((msz = _libelf_msize(ELF_T_PHDR, ec, e->e_version)) == 0) + return ((off_t) -1); fsz = _libelf_fsize(ELF_T_PHDR, ec, e->e_version, phnum); assert(fsz > 0); src.d_buf = _libelf_getphdr(e, ec); src.d_version = dst.d_version = e->e_version; src.d_type = ELF_T_PHDR; - src.d_size = phnum * _libelf_msize(ELF_T_PHDR, ec, - e->e_version); + src.d_size = phnum * msz; dst.d_size = fsz; dst.d_buf = nf + ex->ex_start; @@ -945,13 +957,16 @@ _libelf_write_shdr(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) uint64_t shoff; Elf32_Ehdr *eh32; Elf64_Ehdr *eh64; - size_t fsz, nscn; + size_t fsz, msz, nscn; Elf_Data dst, src; assert(ex->ex_type == ELF_EXTENT_SHDR); ec = e->e_class; + ehdr = _libelf_ehdr(e, ec, 0); + assert(ehdr != NULL); + nscn = e->e_u.e_elf.e_nscn; if (ec == ELFCLASS32) { @@ -971,8 +986,11 @@ _libelf_write_shdr(Elf *e, unsigned char *nf, struct _Elf_Extent *ex) (void) memset(&dst, 0, sizeof(dst)); (void) memset(&src, 0, sizeof(src)); + if ((msz = _libelf_msize(ELF_T_SHDR, ec, e->e_version)) == 0) + return ((off_t) -1); + src.d_type = ELF_T_SHDR; - src.d_size = _libelf_msize(ELF_T_SHDR, ec, e->e_version); + src.d_size = msz; src.d_version = dst.d_version = e->e_version; fsz = _libelf_fsize(ELF_T_SHDR, ec, e->e_version, (size_t) 1); @@ -1084,7 +1102,7 @@ _libelf_write_elf(Elf *e, off_t newsize, struct _Elf_Extent_List *extents) if (e->e_flags & LIBELF_F_RAWFILE_MMAP) { assert(e->e_rawfile != NULL); assert(e->e_cmd == ELF_C_RDWR); - if (munmap(e->e_rawfile, e->e_rawsize) < 0) { + if (munmap(e->e_rawfile, (size_t) e->e_rawsize) < 0) { LIBELF_SET_ERROR(IO, errno); goto error; } @@ -1109,12 +1127,14 @@ _libelf_write_elf(Elf *e, off_t newsize, struct _Elf_Extent_List *extents) assert((e->e_flags & LIBELF_F_RAWFILE_MALLOC) || (e->e_flags & LIBELF_F_RAWFILE_MMAP)); if (e->e_flags & LIBELF_F_RAWFILE_MALLOC) { + assert((e->e_flags & LIBELF_F_RAWFILE_MMAP) == 0); free(e->e_rawfile); e->e_rawfile = newfile; newfile = NULL; } #if ELFTC_HAVE_MMAP else if (e->e_flags & LIBELF_F_RAWFILE_MMAP) { + assert((e->e_flags & LIBELF_F_RAWFILE_MALLOC) == 0); if ((e->e_rawfile = mmap(NULL, (size_t) newsize, PROT_READ, MAP_PRIVATE, e->e_fd, (off_t) 0)) == MAP_FAILED) { @@ -1125,7 +1145,7 @@ _libelf_write_elf(Elf *e, off_t newsize, struct _Elf_Extent_List *extents) #endif /* ELFTC_HAVE_MMAP */ /* Record the new size of the file. */ - e->e_rawsize = (size_t) newsize; + e->e_rawsize = newsize; } else { /* File opened in ELF_C_WRITE mode. */ assert(e->e_rawfile == NULL); diff --git a/libelf/gelf.3 b/libelf/gelf.3 index 704ae154700f..d703c1b3f65e 100644 --- a/libelf/gelf.3 +++ b/libelf/gelf.3 @@ -21,13 +21,13 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf.3 3643 2018-10-14 21:09:24Z jkoshy $ +.\" $Id: gelf.3 3743 2019-06-12 19:36:30Z jkoshy $ .\" -.Dd September 1, 2006 +.Dd June 12, 2019 .Dt GELF 3 .Os .Sh NAME -.Nm GElf +.Nm gelf .Nd class-independent API for ELF manipulation .Sh LIBRARY .Lb libelf @@ -192,7 +192,10 @@ Copy back an ELF symbol table entry. .Xr elf 3 , .Xr elf 5 .Sh HISTORY -The GELF(3) API first appeared in System V Release 4. +The +.Nm +API first appeared in +.At V.4 . This implementation of the API first appeared in .Fx 7.0 . .Sh AUTHORS diff --git a/libelf/gelf_cap.c b/libelf/gelf_cap.c index f509c69c2ba5..6ebdd97a0c52 100644 --- a/libelf/gelf_cap.c +++ b/libelf/gelf_cap.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_cap.c 3177 2015-03-30 18:19:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_cap.c 3732 2019-04-22 11:08:38Z jkoshy $"); GElf_Cap * gelf_getcap(Elf_Data *ed, int ndx, GElf_Cap *dst) @@ -67,9 +67,8 @@ gelf_getcap(Elf_Data *ed, int ndx, GElf_Cap *dst) return (NULL); } - msz = _libelf_msize(ELF_T_CAP, ec, e->e_version); - - assert(msz > 0); + if ((msz = _libelf_msize(ELF_T_CAP, ec, e->e_version)) == 0) + return (NULL); if (msz * (size_t) ndx >= d->d_data.d_size) { LIBELF_SET_ERROR(ARGUMENT, 0); @@ -127,8 +126,8 @@ gelf_update_cap(Elf_Data *ed, int ndx, GElf_Cap *gc) return (0); } - msz = _libelf_msize(ELF_T_CAP, ec, e->e_version); - assert(msz > 0); + if ((msz = _libelf_msize(ELF_T_CAP, ec, e->e_version)) == 0) + return (0); if (msz * (size_t) ndx >= d->d_data.d_size) { LIBELF_SET_ERROR(ARGUMENT, 0); diff --git a/libelf/gelf_dyn.c b/libelf/gelf_dyn.c index 74233094bf6c..1c8b133ceada 100644 --- a/libelf/gelf_dyn.c +++ b/libelf/gelf_dyn.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_dyn.c 3177 2015-03-30 18:19:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_dyn.c 3732 2019-04-22 11:08:38Z jkoshy $"); GElf_Dyn * gelf_getdyn(Elf_Data *ed, int ndx, GElf_Dyn *dst) @@ -67,9 +67,9 @@ gelf_getdyn(Elf_Data *ed, int ndx, GElf_Dyn *dst) return (NULL); } - msz = _libelf_msize(ELF_T_DYN, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_DYN, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -127,9 +127,9 @@ gelf_update_dyn(Elf_Data *ed, int ndx, GElf_Dyn *ds) return (0); } - msz = _libelf_msize(ELF_T_DYN, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_DYN, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { diff --git a/libelf/gelf_getcap.3 b/libelf/gelf_getcap.3 index 276933a8365a..d1c1c2c76c1d 100644 --- a/libelf/gelf_getcap.3 +++ b/libelf/gelf_getcap.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getcap.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getcap.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETCAP 3 .Os .Sh NAME @@ -113,6 +113,12 @@ was not associated with a section of type .Dv SHT_SUNW_cap . .It Bq Er ELF_E_RANGE A value was not representable in the target type. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getdyn.3 b/libelf/gelf_getdyn.3 index a4e51ecd0ee2..1db1a5a5e918 100644 --- a/libelf/gelf_getdyn.3 +++ b/libelf/gelf_getdyn.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getdyn.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getdyn.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETDYN 3 .Os .Sh NAME @@ -115,6 +115,13 @@ was not associated with a section of type .Dv SHT_DYNAMIC . .It Bq Er ELF_E_RANGE A value was not representable in the target type. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. +.El .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getmove.3 b/libelf/gelf_getmove.3 index 64c52d9682ed..7f10d2c0d1ca 100644 --- a/libelf/gelf_getmove.3 +++ b/libelf/gelf_getmove.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getmove.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getmove.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETMOVE 3 .Os .Sh NAME @@ -112,6 +112,12 @@ Data descriptor was not associated with a section containing move information. .It Bq Er ELF_E_RANGE A value was not representable in the target type. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getrel.3 b/libelf/gelf_getrel.3 index c21be0da8b46..056bf1a67fcc 100644 --- a/libelf/gelf_getrel.3 +++ b/libelf/gelf_getrel.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getrel.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getrel.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETREL 3 .Os .Sh NAME @@ -113,6 +113,12 @@ was not associated with a section of type .Dv SHT_REL . .It Bq Er ELF_E_RANGE A value was not representable in the target type. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getrela.3 b/libelf/gelf_getrela.3 index 946482e605ef..0eb0ab31ce10 100644 --- a/libelf/gelf_getrela.3 +++ b/libelf/gelf_getrela.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getrela.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getrela.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETRELA 3 .Os .Sh NAME @@ -113,6 +113,12 @@ was not associated with a section of type .Dv SHT_RELA . .It Bq Er ELF_E_RANGE A value was not representable in the target type. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getsym.3 b/libelf/gelf_getsym.3 index a139c152d15d..e2bbc9eb2f49 100644 --- a/libelf/gelf_getsym.3 +++ b/libelf/gelf_getsym.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getsym.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getsym.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETSYM 3 .Os .Sh NAME @@ -115,6 +115,12 @@ Data descriptor was not associated with a section containing symbol information. .It Bq Er ELF_E_RANGE A value was not representable in the target type. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getsyminfo.3 b/libelf/gelf_getsyminfo.3 index 0f602c289795..71a69b87cff1 100644 --- a/libelf/gelf_getsyminfo.3 +++ b/libelf/gelf_getsyminfo.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getsyminfo.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getsyminfo.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd August 29, 2006 +.Dd April 22, 2019 .Dt GELF_GETSYMINFO 3 .Os .Sh NAME @@ -105,6 +105,12 @@ descriptor. Data descriptor .Ar data was not associated with a section containing symbol information. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptor denoted by argument +.Ar data +is associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_getsymshndx.3 b/libelf/gelf_getsymshndx.3 index 595472a429e3..66b3efd3d89e 100644 --- a/libelf/gelf_getsymshndx.3 +++ b/libelf/gelf_getsymshndx.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_getsymshndx.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_getsymshndx.3 3734 2019-04-22 14:10:49Z jkoshy $ .\" -.Dd November 5, 2006 +.Dd April 22, 2019 .Dt GELF_GETSYMSHNDX 3 .Os .Sh NAME @@ -152,6 +152,14 @@ Data descriptor and .Ar xndxdata were associated with different ELF objects. +.It Bq Er ELF_E_VERSION +The +.Vt Elf_Data +descriptors denoted by arguments +.Ar symdata +and +.Ar xndxdata +are associated with an ELF object with an unsupported version. .El .Sh SEE ALSO .Xr elf 3 , diff --git a/libelf/gelf_move.c b/libelf/gelf_move.c index 2ec8f251f8fa..e5187bf05cf9 100644 --- a/libelf/gelf_move.c +++ b/libelf/gelf_move.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_move.c 3177 2015-03-30 18:19:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_move.c 3732 2019-04-22 11:08:38Z jkoshy $"); GElf_Move * gelf_getmove(Elf_Data *ed, int ndx, GElf_Move *dst) @@ -67,9 +67,9 @@ gelf_getmove(Elf_Data *ed, int ndx, GElf_Move *dst) return (NULL); } - msz = _libelf_msize(ELF_T_MOVE, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_MOVE, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -130,9 +130,9 @@ gelf_update_move(Elf_Data *ed, int ndx, GElf_Move *gm) return (0); } - msz = _libelf_msize(ELF_T_MOVE, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_MOVE, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { diff --git a/libelf/gelf_newehdr.3 b/libelf/gelf_newehdr.3 index 38698c32e8b0..5a15ea7d76c6 100644 --- a/libelf/gelf_newehdr.3 +++ b/libelf/gelf_newehdr.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_newehdr.3 3643 2018-10-14 21:09:24Z jkoshy $ +.\" $Id: gelf_newehdr.3 3743 2019-06-12 19:36:30Z jkoshy $ .\" -.Dd October 22, 2007 +.Dd June 12, 2019 .Dt GELF_NEWEHDR 3 .Os .Sh NAME @@ -134,7 +134,9 @@ The function uses a type of .Ft "void *" for its returned value. -This differs from some other implementations of the ELF(3) API, which use an +This differs from some other implementations of the +.Xr elf 3 +API, which use an .Ft "unsigned long" return type. .Sh ERRORS diff --git a/libelf/gelf_newphdr.3 b/libelf/gelf_newphdr.3 index a881aab4dd6e..01d6cbc38118 100644 --- a/libelf/gelf_newphdr.3 +++ b/libelf/gelf_newphdr.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: gelf_newphdr.3 3639 2018-10-14 14:07:02Z jkoshy $ +.\" $Id: gelf_newphdr.3 3743 2019-06-12 19:36:30Z jkoshy $ .\" -.Dd October 22, 2007 +.Dd June 12, 2019 .Dt GELF_NEWPHDR 3 .Os .Sh NAME @@ -103,7 +103,9 @@ The function uses a type of .Ft "void *" for its returned value. -This differs from some other implementations of the ELF(3) API, which use an +This differs from some other implementations of the +.Xr elf 3 +API, which use an .Ft "unsigned long" return type. .Sh ERRORS diff --git a/libelf/gelf_rel.c b/libelf/gelf_rel.c index 02a613383dfb..54a1002c8679 100644 --- a/libelf/gelf_rel.c +++ b/libelf/gelf_rel.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_rel.c 3177 2015-03-30 18:19:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_rel.c 3739 2019-05-06 05:18:15Z jkoshy $"); GElf_Rel * gelf_getrel(Elf_Data *ed, int ndx, GElf_Rel *dst) @@ -67,9 +67,9 @@ gelf_getrel(Elf_Data *ed, int ndx, GElf_Rel *dst) return (NULL); } - msz = _libelf_msize(ELF_T_REL, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_REL, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -129,9 +129,9 @@ gelf_update_rel(Elf_Data *ed, int ndx, GElf_Rel *dr) return (0); } - msz = _libelf_msize(ELF_T_REL, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_REL, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -144,7 +144,7 @@ gelf_update_rel(Elf_Data *ed, int ndx, GElf_Rel *dr) LIBELF_COPY_U32(rel32, dr, r_offset); - if (ELF64_R_SYM(dr->r_info) > ELF32_R_SYM(~0UL) || + if (ELF64_R_SYM(dr->r_info) > ELF32_R_SYM(~0U) || ELF64_R_TYPE(dr->r_info) > ELF32_R_TYPE(~0U)) { LIBELF_SET_ERROR(RANGE, 0); return (0); diff --git a/libelf/gelf_rela.c b/libelf/gelf_rela.c index d485ab909299..0fa20a515292 100644 --- a/libelf/gelf_rela.c +++ b/libelf/gelf_rela.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_rela.c 3177 2015-03-30 18:19:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_rela.c 3739 2019-05-06 05:18:15Z jkoshy $"); GElf_Rela * gelf_getrela(Elf_Data *ed, int ndx, GElf_Rela *dst) @@ -67,9 +67,9 @@ gelf_getrela(Elf_Data *ed, int ndx, GElf_Rela *dst) return (NULL); } - msz = _libelf_msize(ELF_T_RELA, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_RELA, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -130,9 +130,9 @@ gelf_update_rela(Elf_Data *ed, int ndx, GElf_Rela *dr) return (0); } - msz = _libelf_msize(ELF_T_RELA, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_RELA, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -145,7 +145,7 @@ gelf_update_rela(Elf_Data *ed, int ndx, GElf_Rela *dr) LIBELF_COPY_U32(rela32, dr, r_offset); - if (ELF64_R_SYM(dr->r_info) > ELF32_R_SYM(~0UL) || + if (ELF64_R_SYM(dr->r_info) > ELF32_R_SYM(~0U) || ELF64_R_TYPE(dr->r_info) > ELF32_R_TYPE(~0U)) { LIBELF_SET_ERROR(RANGE, 0); return (0); diff --git a/libelf/gelf_sym.c b/libelf/gelf_sym.c index ecd85fceefb4..c5f4484d56e6 100644 --- a/libelf/gelf_sym.c +++ b/libelf/gelf_sym.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_sym.c 3177 2015-03-30 18:19:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_sym.c 3732 2019-04-22 11:08:38Z jkoshy $"); GElf_Sym * gelf_getsym(Elf_Data *ed, int ndx, GElf_Sym *dst) @@ -67,9 +67,9 @@ gelf_getsym(Elf_Data *ed, int ndx, GElf_Sym *dst) return (NULL); } - msz = _libelf_msize(ELF_T_SYM, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_SYM, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -129,9 +129,9 @@ gelf_update_sym(Elf_Data *ed, int ndx, GElf_Sym *gs) return (0); } - msz = _libelf_msize(ELF_T_SYM, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_SYM, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { diff --git a/libelf/gelf_syminfo.c b/libelf/gelf_syminfo.c index f6d7b6c367fa..9f3cd0e6f701 100644 --- a/libelf/gelf_syminfo.c +++ b/libelf/gelf_syminfo.c @@ -29,7 +29,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_syminfo.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_syminfo.c 3732 2019-04-22 11:08:38Z jkoshy $"); GElf_Syminfo * gelf_getsyminfo(Elf_Data *ed, int ndx, GElf_Syminfo *dst) @@ -65,9 +65,9 @@ gelf_getsyminfo(Elf_Data *ed, int ndx, GElf_Syminfo *dst) return (NULL); } - msz = _libelf_msize(ELF_T_SYMINFO, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_SYMINFO, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { @@ -126,9 +126,9 @@ gelf_update_syminfo(Elf_Data *ed, int ndx, GElf_Syminfo *gs) return (0); } - msz = _libelf_msize(ELF_T_SYMINFO, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_SYMINFO, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= d->d_data.d_size) { diff --git a/libelf/gelf_symshndx.c b/libelf/gelf_symshndx.c index b490aa2bb11f..4a48a43d12b8 100644 --- a/libelf/gelf_symshndx.c +++ b/libelf/gelf_symshndx.c @@ -29,7 +29,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: gelf_symshndx.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: gelf_symshndx.c 3732 2019-04-22 11:08:38Z jkoshy $"); GElf_Sym * gelf_getsymshndx(Elf_Data *d, Elf_Data *id, int ndx, GElf_Sym *dst, @@ -69,9 +69,9 @@ gelf_getsymshndx(Elf_Data *d, Elf_Data *id, int ndx, GElf_Sym *dst, return (NULL); } - msz = _libelf_msize(ELF_T_WORD, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_WORD, ec, e->e_version)) == 0) + return (NULL); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= id->d_size) { @@ -121,9 +121,9 @@ gelf_update_symshndx(Elf_Data *d, Elf_Data *id, int ndx, GElf_Sym *gs, return (0); } - msz = _libelf_msize(ELF_T_WORD, ec, e->e_version); + if ((msz = _libelf_msize(ELF_T_WORD, ec, e->e_version)) == 0) + return (0); - assert(msz > 0); assert(ndx >= 0); if (msz * (size_t) ndx >= id->d_size) { diff --git a/libelf/libelf_allocate.c b/libelf/libelf_allocate.c index 0a74facc9fc4..2146b05141fd 100644 --- a/libelf/libelf_allocate.c +++ b/libelf/libelf_allocate.c @@ -36,33 +36,26 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_allocate.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: libelf_allocate.c 3738 2019-05-05 21:49:06Z jkoshy $"); Elf * _libelf_allocate_elf(void) { Elf *e; - if ((e = malloc(sizeof(*e))) == NULL) { + if ((e = calloc((size_t) 1, sizeof(*e))) == NULL) { LIBELF_SET_ERROR(RESOURCE, errno); return NULL; } e->e_activations = 1; - e->e_hdr.e_rawhdr = NULL; e->e_byteorder = ELFDATANONE; e->e_class = ELFCLASSNONE; e->e_cmd = ELF_C_NULL; e->e_fd = -1; - e->e_flags = 0; e->e_kind = ELF_K_NONE; - e->e_parent = NULL; - e->e_rawfile = NULL; - e->e_rawsize = 0; e->e_version = LIBELF_PRIVATE(version); - (void) memset(&e->e_u, 0, sizeof(e->e_u)); - return (e); } @@ -83,31 +76,25 @@ _libelf_init_elf(Elf *e, Elf_Kind kind) } } -#define FREE(P) do { \ - if (P) \ - free(P); \ - } while (0) - - -Elf * +void _libelf_release_elf(Elf *e) { Elf_Arhdr *arh; switch (e->e_kind) { case ELF_K_AR: - FREE(e->e_u.e_ar.e_symtab); + free(e->e_u.e_ar.e_symtab); break; case ELF_K_ELF: switch (e->e_class) { case ELFCLASS32: - FREE(e->e_u.e_elf.e_ehdr.e_ehdr32); - FREE(e->e_u.e_elf.e_phdr.e_phdr32); + free(e->e_u.e_elf.e_ehdr.e_ehdr32); + free(e->e_u.e_elf.e_phdr.e_phdr32); break; case ELFCLASS64: - FREE(e->e_u.e_elf.e_ehdr.e_ehdr64); - FREE(e->e_u.e_elf.e_phdr.e_phdr64); + free(e->e_u.e_elf.e_ehdr.e_ehdr64); + free(e->e_u.e_elf.e_phdr.e_phdr64); break; } @@ -115,8 +102,8 @@ _libelf_release_elf(Elf *e) if (e->e_flags & LIBELF_F_AR_HEADER) { arh = e->e_hdr.e_arhdr; - FREE(arh->ar_name); - FREE(arh->ar_rawname); + free(arh->ar_name); + free(arh->ar_rawname); free(arh); } @@ -127,8 +114,6 @@ _libelf_release_elf(Elf *e) } free(e); - - return (NULL); } struct _Libelf_Data * diff --git a/libelf/libelf_ar.c b/libelf/libelf_ar.c index 8fce7419726e..6634d2c73c33 100644 --- a/libelf/libelf_ar.c +++ b/libelf/libelf_ar.c @@ -33,7 +33,7 @@ #include "_libelf.h" #include "_libelf_ar.h" -ELFTC_VCSID("$Id: libelf_ar.c 3446 2016-05-03 01:31:17Z emaste $"); +ELFTC_VCSID("$Id: libelf_ar.c 3712 2019-03-16 22:23:34Z jkoshy $"); #define LIBELF_NALLOC_SIZE 16 @@ -123,8 +123,16 @@ _libelf_ar_gethdr(Elf *e) arh = (struct ar_hdr *) (uintptr_t) e->e_hdr.e_rawhdr; assert((uintptr_t) arh >= (uintptr_t) parent->e_rawfile + SARMAG); - assert((uintptr_t) arh <= (uintptr_t) parent->e_rawfile + - parent->e_rawsize - sizeof(struct ar_hdr)); + + /* + * There needs to be enough space remaining in the file for the + * archive header. + */ + if ((uintptr_t) arh > (uintptr_t) parent->e_rawfile + + (uintptr_t) parent->e_rawsize - sizeof(struct ar_hdr)) { + LIBELF_SET_ERROR(ARCHIVE, 0); + return (NULL); + } if ((eh = malloc(sizeof(Elf_Arhdr))) == NULL) { LIBELF_SET_ERROR(RESOURCE, 0); @@ -199,8 +207,8 @@ Elf * _libelf_ar_open_member(int fd, Elf_Cmd c, Elf *elf) { Elf *e; - off_t next; size_t nsz, sz; + off_t next, end; struct ar_hdr *arh; char *member, *namelen; @@ -217,6 +225,17 @@ _libelf_ar_open_member(int fd, Elf_Cmd c, Elf *elf) assert((next & 1) == 0); + /* + * There needs to be enough space in the file to contain an + * ar(1) header. + */ + end = next + (off_t) sizeof(struct ar_hdr); + if ((uintmax_t) end < (uintmax_t) next || /* Overflow. */ + end > (off_t) elf->e_rawsize) { + LIBELF_SET_ERROR(ARCHIVE, 0); + return (NULL); + } + arh = (struct ar_hdr *) (elf->e_rawfile + next); /* @@ -228,6 +247,17 @@ _libelf_ar_open_member(int fd, Elf_Cmd c, Elf *elf) return (NULL); } + /* + * Check if the archive member that follows will fit in the + * containing archive. + */ + end += (off_t) sz; + if (end < next || /* Overflow. */ + end > (off_t) elf->e_rawsize) { + LIBELF_SET_ERROR(ARCHIVE, 0); + return (NULL); + } + /* * Adjust the size field for members in BSD archives using * extended naming. @@ -286,7 +316,8 @@ Elf_Arsym * _libelf_ar_process_bsd_symtab(Elf *e, size_t *count) { Elf_Arsym *symtab, *sym; - unsigned int n, nentries; + unsigned int n; + size_t nentries; unsigned char *end, *p, *p0, *s, *s0; const size_t entrysize = 2 * sizeof(long); long arraysize, fileoffset, stroffset, strtabsize; @@ -343,7 +374,7 @@ _libelf_ar_process_bsd_symtab(Elf *e, size_t *count) GET_LONG(p, fileoffset); if (stroffset < 0 || fileoffset < 0 || - (size_t) fileoffset >= e->e_rawsize) + (off_t) fileoffset >= e->e_rawsize) goto symtaberror; s = s0 + stroffset; diff --git a/libelf/libelf_convert.m4 b/libelf/libelf_convert.m4 index 04adeb3114a7..bb601ad92841 100644 --- a/libelf/libelf_convert.m4 +++ b/libelf/libelf_convert.m4 @@ -30,7 +30,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_convert.m4 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: libelf_convert.m4 3712 2019-03-16 22:23:34Z jkoshy $"); /* WARNING: GENERATED FROM __file__. */ @@ -820,7 +820,7 @@ _libelf_cvt_GNUHASH64_tom(unsigned char *dst, size_t dsz, unsigned char *src, if (dsz < srcsz) /* Destination lacks space. */ return (0); - nchains = srcsz / sizeof(uint32_t); + nchains = (uint32_t) (srcsz / sizeof(uint32_t)); chains = (uint32_t *) (uintptr_t) dst; for (n = 0; n < nchains; n++) { @@ -901,7 +901,7 @@ _libelf_cvt_GNUHASH64_tof(unsigned char *dst, size_t dsz, unsigned char *src, if (dsz < srcsz) return (0); - nchains = srcsz / sizeof(uint32_t); + nchains = (uint32_t) (srcsz / sizeof(uint32_t)); for (n = 0; n < nchains; n++) { t32 = *s32++; if (byteswap) @@ -1078,13 +1078,8 @@ _libelf_translator_function * _libelf_get_translator(Elf_Type t, int direction, int elfclass, int elfmachine) { assert(elfclass == ELFCLASS32 || elfclass == ELFCLASS64); - assert(elfmachine >= EM_NONE && elfmachine < EM__LAST__); assert(direction == ELF_TOFILE || direction == ELF_TOMEMORY); - - if (t >= ELF_T_NUM || - (elfclass != ELFCLASS32 && elfclass != ELFCLASS64) || - (direction != ELF_TOFILE && direction != ELF_TOMEMORY)) - return (NULL); + assert(t >= ELF_T_FIRST && t <= ELF_T_LAST); /* TODO: Handle MIPS64 REL{,A} sections (ticket #559). */ (void) elfmachine; diff --git a/libelf/libelf_data.c b/libelf/libelf_data.c index fcffd8bd9d1c..968394d9bf9a 100644 --- a/libelf/libelf_data.c +++ b/libelf/libelf_data.c @@ -28,7 +28,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_data.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: libelf_data.c 3737 2019-05-05 14:49:50Z jkoshy $"); int _libelf_xlate_shtype(uint32_t sht) @@ -89,8 +89,14 @@ _libelf_xlate_shtype(uint32_t sht) * OS, processor and user-defined section types) are * legal, but since we do not know anything more about * their semantics, we return a type of ELF_T_BYTE. + * + * The ELF specification uses 32 bit unsigned values for + * denoting section types, and defines SHT_HIUSER to be + * 0xFFFFFFFFUL (i.e., UINT32_MAX). Consequently, we only + * need to check that 'sht' is greater than or equal to + * SHT_LOOS. */ - if (sht >= SHT_LOOS && sht <= SHT_HIUSER) + if (sht >= SHT_LOOS) return (ELF_T_BYTE); /* diff --git a/libelf/libelf_ehdr.c b/libelf/libelf_ehdr.c index 38e4e74e14d2..e06c03355038 100644 --- a/libelf/libelf_ehdr.c +++ b/libelf/libelf_ehdr.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_ehdr.c 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: libelf_ehdr.c 3732 2019-04-22 11:08:38Z jkoshy $"); /* * Retrieve counts for sections, phdrs and the section string table index @@ -51,7 +51,12 @@ _libelf_load_extended(Elf *e, int ec, uint64_t shoff, uint16_t phnum, fsz = _libelf_fsize(ELF_T_SHDR, ec, e->e_version, 1); assert(fsz > 0); - if (e->e_rawsize < shoff + fsz) { /* raw file too small */ + if (shoff + fsz < shoff) { /* Numeric overflow. */ + LIBELF_SET_ERROR(HEADER, 0); + return (0); + } + + if ((uint64_t) e->e_rawsize < shoff + fsz) { LIBELF_SET_ERROR(HEADER, 0); return (0); } @@ -138,14 +143,13 @@ _libelf_ehdr(Elf *e, int ec, int allocate) 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) { + if (e->e_cmd != ELF_C_WRITE && e->e_rawsize < (off_t) fsz) { LIBELF_SET_ERROR(HEADER, 0); return (NULL); } - msz = _libelf_msize(ELF_T_EHDR, ec, EV_CURRENT); - - assert(msz > 0); + if ((msz = _libelf_msize(ELF_T_EHDR, ec, EV_CURRENT)) == 0) + return (NULL); if ((ehdr = calloc((size_t) 1, msz)) == NULL) { LIBELF_SET_ERROR(RESOURCE, 0); diff --git a/libelf/libelf_extended.c b/libelf/libelf_extended.c index 96765a8293e1..59376e430861 100644 --- a/libelf/libelf_extended.c +++ b/libelf/libelf_extended.c @@ -29,7 +29,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_extended.c 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: libelf_extended.c 3712 2019-03-16 22:23:34Z jkoshy $"); /* * Retrieve section #0, allocating a new section if needed. @@ -57,7 +57,7 @@ _libelf_setshnum(Elf *e, void *eh, int ec, size_t shnum) assert(scn->s_ndx == SHN_UNDEF); if (ec == ELFCLASS32) - scn->s_shdr.s_shdr32.sh_size = shnum; + scn->s_shdr.s_shdr32.sh_size = (Elf32_Word) shnum; else scn->s_shdr.s_shdr64.sh_size = shnum; @@ -87,9 +87,9 @@ _libelf_setshstrndx(Elf *e, void *eh, int ec, size_t shstrndx) assert(scn->s_ndx == SHN_UNDEF); if (ec == ELFCLASS32) - scn->s_shdr.s_shdr32.sh_link = shstrndx; + scn->s_shdr.s_shdr32.sh_link = (Elf32_Word) shstrndx; else - scn->s_shdr.s_shdr64.sh_link = shstrndx; + scn->s_shdr.s_shdr64.sh_link = (Elf64_Word) shstrndx; (void) elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); @@ -116,9 +116,9 @@ _libelf_setphnum(Elf *e, void *eh, int ec, size_t phnum) assert(scn->s_ndx == SHN_UNDEF); if (ec == ELFCLASS32) - scn->s_shdr.s_shdr32.sh_info = phnum; + scn->s_shdr.s_shdr32.sh_info = (Elf32_Word) phnum; else - scn->s_shdr.s_shdr64.sh_info = phnum; + scn->s_shdr.s_shdr64.sh_info = (Elf64_Word) phnum; (void) elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); diff --git a/libelf/libelf_memory.c b/libelf/libelf_memory.c index cb8e8f20438a..12ab12aed70e 100644 --- a/libelf/libelf_memory.c +++ b/libelf/libelf_memory.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_memory.c 3013 2014-03-23 06:16:59Z jkoshy $"); +ELFTC_VCSID("$Id: libelf_memory.c 3738 2019-05-05 21:49:06Z jkoshy $"); /* * Create an ELF descriptor for a memory image, optionally reporting @@ -54,7 +54,7 @@ _libelf_memory(unsigned char *image, size_t sz, int reporterror) e->e_cmd = ELF_C_READ; e->e_rawfile = image; - e->e_rawsize = sz; + e->e_rawsize = (off_t) sz; #undef LIBELF_IS_ELF #define LIBELF_IS_ELF(P) ((P)[EI_MAG0] == ELFMAG0 && \ @@ -78,7 +78,7 @@ _libelf_memory(unsigned char *image, size_t sz, int reporterror) if (error != ELF_E_NONE) { if (reporterror) { LIBELF_PRIVATE(error) = LIBELF_ERROR(error, 0); - (void) _libelf_release_elf(e); + _libelf_release_elf(e); return (NULL); } } else { diff --git a/libelf/libelf_msize.m4 b/libelf/libelf_msize.m4 index 179880c43278..2751f5be8b82 100644 --- a/libelf/libelf_msize.m4 +++ b/libelf/libelf_msize.m4 @@ -30,7 +30,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_msize.m4 3174 2015-03-27 17:13:41Z emaste $"); +ELFTC_VCSID("$Id: libelf_msize.m4 3732 2019-04-22 11:08:38Z jkoshy $"); /* WARNING: GENERATED FROM __file__. */ @@ -87,6 +87,14 @@ static struct msize msize[ELF_T_NUM] = { MSIZES(ELF_TYPE_LIST) }; +/* + * Returns the memory size of the specified ELF type 't' of ELF + * class 'ec' and ELF version 'version'. + * + * If the specified combination of ELF type, class, and version is + * unsupported then a value of 0 will be returned and the appropriate + * library error code set. + */ size_t _libelf_msize(Elf_Type t, int elfclass, unsigned int version) { @@ -102,5 +110,10 @@ _libelf_msize(Elf_Type t, int elfclass, unsigned int version) sz = (elfclass == ELFCLASS32) ? msize[t].msz32 : msize[t].msz64; + if (sz == 0) { + LIBELF_SET_ERROR(UNIMPL, 0); + return (0); + } + return (sz); } diff --git a/libelf/libelf_phdr.c b/libelf/libelf_phdr.c index 336ef3877f6f..5675a2e8c54c 100644 --- a/libelf/libelf_phdr.c +++ b/libelf/libelf_phdr.c @@ -31,7 +31,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_phdr.c 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: libelf_phdr.c 3732 2019-04-22 11:08:38Z jkoshy $"); void * _libelf_getphdr(Elf *e, int ec) @@ -77,14 +77,18 @@ _libelf_getphdr(Elf *e, int ec) assert(fsz > 0); + if (phoff + fsz < phoff) { /* Numeric overflow. */ + LIBELF_SET_ERROR(HEADER, 0); + return (NULL); + } + if ((uint64_t) e->e_rawsize < (phoff + fsz)) { LIBELF_SET_ERROR(HEADER, 0); return (NULL); } - msz = _libelf_msize(ELF_T_PHDR, ec, EV_CURRENT); - - assert(msz > 0); + if ((msz = _libelf_msize(ELF_T_PHDR, ec, EV_CURRENT)) == 0) + return (NULL); if ((phdr = calloc(phnum, msz)) == NULL) { LIBELF_SET_ERROR(RESOURCE, 0); @@ -125,9 +129,8 @@ _libelf_newphdr(Elf *e, int ec, size_t count) assert(ec == ELFCLASS32 || ec == ELFCLASS64); assert(e->e_version == EV_CURRENT); - msz = _libelf_msize(ELF_T_PHDR, ec, e->e_version); - - assert(msz > 0); + if ((msz = _libelf_msize(ELF_T_PHDR, ec, e->e_version)) == 0) + return (NULL); newphdr = NULL; if (count > 0 && (newphdr = calloc(count, msz)) == NULL) { diff --git a/libelf/libelf_xlate.c b/libelf/libelf_xlate.c index cfe6a0bdfdbd..7bb0e0f9d683 100644 --- a/libelf/libelf_xlate.c +++ b/libelf/libelf_xlate.c @@ -29,7 +29,7 @@ #include "_libelf.h" -ELFTC_VCSID("$Id: libelf_xlate.c 3632 2018-10-10 21:12:43Z jkoshy $"); +ELFTC_VCSID("$Id: libelf_xlate.c 3732 2019-04-22 11:08:38Z jkoshy $"); /* * Translate to/from the file representation of ELF objects. @@ -83,9 +83,8 @@ _libelf_xlate(Elf_Data *dst, const Elf_Data *src, unsigned int encoding, (src->d_type, (size_t) 1, src->d_version)) == 0) return (NULL); - msz = _libelf_msize(src->d_type, elfclass, src->d_version); - - assert(msz > 0); + if ((msz = _libelf_msize(src->d_type, elfclass, src->d_version)) == 0) + return (NULL); if (src->d_size % (direction == ELF_TOMEMORY ? fsz : msz)) { LIBELF_SET_ERROR(DATA, 0); diff --git a/libelf/os.Linux.mk b/libelf/os.Linux.mk new file mode 100644 index 000000000000..2292b6ec5f02 --- /dev/null +++ b/libelf/os.Linux.mk @@ -0,0 +1,31 @@ +# Enable additional warnings. +CFLAGS+= -Wa,--fatal-warnings +CFLAGS+= -Wall +CFLAGS+= -Wcast-align +CFLAGS+= -Wcast-qual +CFLAGS+= -Wchar-subscripts +CFLAGS+= -Wconversion +CFLAGS+= -Werror +CFLAGS+= -Wextra +CFLAGS+= -Wformat=2 +CFLAGS+= -Winline +CFLAGS+= -Wmissing-prototypes +CFLAGS+= -Wnested-externs +CFLAGS+= -Wempty-body +CFLAGS+= -Wformat-y2k +CFLAGS+= -Wformat-zero-length +CFLAGS+= -Wpointer-sign +CFLAGS+= -Wpointer-to-int-cast +CFLAGS+= -Wsign-compare +CFLAGS+= -Wunused-const-variable +CFLAGS+= -Wunused-parameter +CFLAGS+= -Wold-style-definition +CFLAGS+= -Wpointer-arith +CFLAGS+= -Wredundant-decls +CFLAGS+= -Wreturn-type +CFLAGS+= -Wshadow +CFLAGS+= -Wstrict-prototypes +CFLAGS+= -Wstrict-overflow +CFLAGS+= -Wswitch +CFLAGS+= -Wunused-parameter +CFLAGS+= -Wwrite-strings diff --git a/libelftc/elftc_bfd_find_target.3 b/libelftc/elftc_bfd_find_target.3 index 39d72b05341f..169c4723d94c 100644 --- a/libelftc/elftc_bfd_find_target.3 +++ b/libelftc/elftc_bfd_find_target.3 @@ -21,9 +21,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elftc_bfd_find_target.3 3645 2018-10-15 20:17:14Z jkoshy $ +.\" $Id: elftc_bfd_find_target.3 3752 2019-06-28 01:12:53Z emaste $ .\" -.Dd November 30, 2011 +.Dd June 27, 2019 .Dt ELFTC_BFD_FIND_TARGET 3 .Os .Sh NAME @@ -76,6 +76,9 @@ Known descriptor names and their properties include: .It Li elf32-powerpc Ta ELF Ta MSB Ta 32 .It Li elf32-powerpc-freebsd Ta ELF Ta MSB Ta 32 .It Li elf32-powerpcle Ta ELF Ta LSB Ta 32 +.It Li elf32-riscv Ta ELF Ta LSB Ta 32 +.It Li elf64-riscv Ta ELF Ta LSB Ta 64 +.It Li elf64-riscv-freebsd Ta ELF Ta LSB Ta 64 .It Li elf32-sh Ta ELF Ta MSB Ta 32 .It Li elf32-shl Ta ELF Ta LSB Ta 32 .It Li elf32-sh-nbsd Ta ELF Ta MSB Ta 32 diff --git a/libelftc/elftc_reloc_type_str.c b/libelftc/elftc_reloc_type_str.c index fdad9a6448c6..4fa715ae0734 100644 --- a/libelftc/elftc_reloc_type_str.c +++ b/libelftc/elftc_reloc_type_str.c @@ -402,6 +402,8 @@ elftc_reloc_type_str(unsigned int mach, unsigned int type) case 22: return "R_MIPS_GOT_HI16"; case 23: return "R_MIPS_GOT_LO16"; case 24: return "R_MIPS_SUB"; + case 28: return "R_MIPS_HIGHER"; + case 29: return "R_MIPS_HIGHEST"; case 30: return "R_MIPS_CALLHI16"; case 31: return "R_MIPS_CALLLO16"; case 37: return "R_MIPS_JALR"; diff --git a/libelftc/elftc_string_table.c b/libelftc/elftc_string_table.c index bba9ac6a76cd..f9f50fa91f07 100644 --- a/libelftc/elftc_string_table.c +++ b/libelftc/elftc_string_table.c @@ -36,7 +36,7 @@ #include "libelftc.h" #include "_libelftc.h" -ELFTC_VCSID("$Id: elftc_string_table.c 2869 2013-01-06 13:29:18Z jkoshy $"); +ELFTC_VCSID("$Id: elftc_string_table.c 3750 2019-06-28 01:12:10Z emaste $"); #define ELFTC_STRING_TABLE_DEFAULT_SIZE (4*1024) #define ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE 16 @@ -44,7 +44,7 @@ ELFTC_VCSID("$Id: elftc_string_table.c 2869 2013-01-06 13:29:18Z jkoshy $"); #define ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT (4*1024) struct _Elftc_String_Table_Entry { - int ste_idx; + ssize_t ste_idx; SLIST_ENTRY(_Elftc_String_Table_Entry) ste_next; }; @@ -64,9 +64,9 @@ struct _Elftc_String_Table_Entry { } while (0) struct _Elftc_String_Table { - unsigned int st_len; /* length and flags */ + size_t st_len; /* length and flags */ int st_nbuckets; - int st_string_pool_size; + size_t st_string_pool_size; char *st_string_pool; SLIST_HEAD(_Elftc_String_Table_Bucket, _Elftc_String_Table_Entry) st_buckets[]; @@ -86,7 +86,7 @@ elftc_string_table_find_hash_entry(Elftc_String_Table *st, const char *string, *rhashindex = hashindex; SLIST_FOREACH(ste, &st->st_buckets[hashindex], ste_next) { - s = st->st_string_pool + abs(ste->ste_idx); + s = st->st_string_pool + labs(ste->ste_idx); assert(s > st->st_string_pool && s < st->st_string_pool + st->st_string_pool_size); @@ -102,7 +102,7 @@ static int elftc_string_table_add_to_pool(Elftc_String_Table *st, const char *string) { char *newpool; - int len, newsize, stlen; + size_t len, newsize, stlen; len = strlen(string) + 1; /* length, including the trailing NUL */ stlen = ELFTC_STRING_TABLE_LENGTH(st); @@ -119,17 +119,17 @@ elftc_string_table_add_to_pool(Elftc_String_Table *st, const char *string) st->st_string_pool_size = newsize; } - strcpy(st->st_string_pool + stlen, string); + memcpy(st->st_string_pool + stlen, string, len); ELFTC_STRING_TABLE_UPDATE_LENGTH(st, stlen + len); return (stlen); } Elftc_String_Table * -elftc_string_table_create(int sizehint) +elftc_string_table_create(size_t sizehint) { - int n, nbuckets, tablesize; struct _Elftc_String_Table *st; + int n, nbuckets, tablesize; if (sizehint < ELFTC_STRING_TABLE_DEFAULT_SIZE) sizehint = ELFTC_STRING_TABLE_DEFAULT_SIZE; @@ -167,21 +167,19 @@ elftc_string_table_destroy(Elftc_String_Table *st) for (n = 0; n < st->st_nbuckets; n++) SLIST_FOREACH_SAFE(s, &st->st_buckets[n], ste_next, t) - free(s); + free(s); free(st->st_string_pool); free(st); - - return; } Elftc_String_Table * -elftc_string_table_from_section(Elf_Scn *scn, int sizehint) +elftc_string_table_from_section(Elf_Scn *scn, size_t sizehint) { - int len; Elf_Data *d; GElf_Shdr sh; const char *s, *end; Elftc_String_Table *st; + size_t len; /* Verify the type of the section passed in. */ if (gelf_getshdr(scn, &sh) == NULL || @@ -237,7 +235,8 @@ elftc_string_table_image(Elftc_String_Table *st, size_t *size) char *r, *s, *end; struct _Elftc_String_Table_Entry *ste; struct _Elftc_String_Table_Bucket *head; - int copied, hashindex, offset, length, newsize; + size_t copied, offset, length, newsize; + int hashindex; /* * For the common case of a string table has not seen @@ -305,8 +304,9 @@ elftc_string_table_image(Elftc_String_Table *st, size_t *size) size_t elftc_string_table_insert(Elftc_String_Table *st, const char *string) { - int hashindex, idx; struct _Elftc_String_Table_Entry *ste; + ssize_t idx; + int hashindex; hashindex = 0; @@ -318,7 +318,7 @@ elftc_string_table_insert(Elftc_String_Table *st, const char *string) if ((ste = malloc(sizeof(*ste))) == NULL) return (0); if ((ste->ste_idx = elftc_string_table_add_to_pool(st, - string)) == 0) { + string)) == 0) { free(ste); return (0); } @@ -328,7 +328,7 @@ elftc_string_table_insert(Elftc_String_Table *st, const char *string) idx = ste->ste_idx; if (idx < 0) /* Undelete. */ - ste->ste_idx = idx = (- idx); + ste->ste_idx = idx = -idx; return (idx); } @@ -336,8 +336,9 @@ elftc_string_table_insert(Elftc_String_Table *st, const char *string) size_t elftc_string_table_lookup(Elftc_String_Table *st, const char *string) { - int hashindex, idx; struct _Elftc_String_Table_Entry *ste; + ssize_t idx; + int hashindex; ste = elftc_string_table_find_hash_entry(st, string, &hashindex); @@ -352,17 +353,17 @@ elftc_string_table_lookup(Elftc_String_Table *st, const char *string) int elftc_string_table_remove(Elftc_String_Table *st, const char *string) { - int idx; struct _Elftc_String_Table_Entry *ste; + ssize_t idx; ste = elftc_string_table_find_hash_entry(st, string, NULL); if (ste == NULL || (idx = ste->ste_idx) < 0) return (ELFTC_FAILURE); - assert(idx > 0 && idx < (int) ELFTC_STRING_TABLE_LENGTH(st)); + assert(idx > 0 && (size_t)idx < ELFTC_STRING_TABLE_LENGTH(st)); - ste->ste_idx = (- idx); + ste->ste_idx = -idx; ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st); diff --git a/libelftc/elftc_string_table_create.3 b/libelftc/elftc_string_table_create.3 index 387ad72a3727..47784639dd09 100644 --- a/libelftc/elftc_string_table_create.3 +++ b/libelftc/elftc_string_table_create.3 @@ -22,9 +22,9 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.\" $Id: elftc_string_table_create.3 3645 2018-10-15 20:17:14Z jkoshy $ +.\" $Id: elftc_string_table_create.3 3750 2019-06-28 01:12:10Z emaste $ .\" -.Dd January 5, 2013 +.Dd June 27, 2019 .Dt ELFTC_STRING_TABLE_CREATE 3 .Os .Sh NAME @@ -40,11 +40,11 @@ .Sh SYNOPSIS .In libelftc.h .Ft "Elftc_String_Table *" -.Fn elftc_string_table_create "int sizehint" -.Ft int +.Fn elftc_string_table_create "size_t sizehint" +.Ft void .Fn elftc_string_table_destroy "Elftc_String_Table *table" .Ft "Elftc_String_Table *" -.Fn elftc_string_table_from_section "Elf_Scn *scn" "int sizehint" +.Fn elftc_string_table_from_section "Elf_Scn *scn" "size_t sizehint" .Ft "const char *" .Fo elftc_string_table_image .Fa "Elftc_String_Table *table" @@ -144,7 +144,7 @@ looks up the string referenced by argument in the string table specified by argument .Ar table , and if found, returns the offset associated with the string. -The returned offset will be valid till the next call to function +The returned offset will be valid until the next call to .Fn elftc_string_table_image . .Pp Function diff --git a/libelftc/libelftc.h b/libelftc/libelftc.h index a235097e6910..244c029c9fb2 100644 --- a/libelftc/libelftc.h +++ b/libelftc/libelftc.h @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: users/kaiwang27/elftc/libelftc.h 392 2009-05-31 19:17:46Z kaiwang27 $ - * $Id: libelftc.h 3489 2016-08-31 00:12:15Z emaste $ + * $Id: libelftc.h 3744 2019-06-28 00:41:47Z emaste $ */ #ifndef _LIBELFTC_H_ @@ -77,10 +77,10 @@ int elftc_demangle(const char *_mangledname, char *_buffer, size_t _bufsize, unsigned int _flags); const char *elftc_reloc_type_str(unsigned int mach, unsigned int type); int elftc_set_timestamps(const char *_filename, struct stat *_sb); -Elftc_String_Table *elftc_string_table_create(int _hint); +Elftc_String_Table *elftc_string_table_create(size_t _sizehint); void elftc_string_table_destroy(Elftc_String_Table *_table); Elftc_String_Table *elftc_string_table_from_section(Elf_Scn *_scn, - int _hint); + size_t _sizehint); const char *elftc_string_table_image(Elftc_String_Table *_table, size_t *_sz); size_t elftc_string_table_insert(Elftc_String_Table *_table, diff --git a/libelftc/libelftc_bfdtarget.c b/libelftc/libelftc_bfdtarget.c index 48b67a00c75c..5bbf89ba78fa 100644 --- a/libelftc/libelftc_bfdtarget.c +++ b/libelftc/libelftc_bfdtarget.c @@ -30,7 +30,7 @@ #include "_libelftc.h" -ELFTC_VCSID("$Id: libelftc_bfdtarget.c 3516 2017-02-10 02:33:08Z emaste $"); +ELFTC_VCSID("$Id: libelftc_bfdtarget.c 3752 2019-06-28 01:12:53Z emaste $"); struct _Elftc_Bfd_Target _libelftc_targets[] = { @@ -315,6 +315,31 @@ struct _Elftc_Bfd_Target _libelftc_targets[] = { .bt_machine = EM_PPC64, }, + { + .bt_name = "elf32-riscv", + .bt_type = ETF_ELF, + .bt_byteorder = ELFDATA2LSB, + .bt_elfclass = ELFCLASS32, + .bt_machine = EM_RISCV, + }, + + { + .bt_name = "elf64-riscv", + .bt_type = ETF_ELF, + .bt_byteorder = ELFDATA2LSB, + .bt_elfclass = ELFCLASS64, + .bt_machine = EM_RISCV, + }, + + { + .bt_name = "elf64-riscv-freebsd", + .bt_type = ETF_ELF, + .bt_byteorder = ELFDATA2MSB, + .bt_elfclass = ELFCLASS64, + .bt_machine = EM_RISCV, + .bt_osabi = ELFOSABI_FREEBSD, + }, + { .bt_name = "elf64-sh64", .bt_type = ETF_ELF, diff --git a/libelftc/make-toolchain-version b/libelftc/make-toolchain-version index 0cdf370cc32b..cb0f437d069d 100755 --- a/libelftc/make-toolchain-version +++ b/libelftc/make-toolchain-version @@ -3,7 +3,7 @@ # This script generates a project-wide version identifier for use by # the `elftc_version()' API. # -# $Id: make-toolchain-version 3414 2016-02-16 22:55:28Z jkoshy $ +# $Id: make-toolchain-version 3731 2019-04-06 14:28:34Z jkoshy $ # # Defaults. @@ -33,6 +33,32 @@ usage() exit 1 } +# Determine the revision number for the source tree. +# +# - If CVS is detected, we use the string `unknown'. +# - If SVN is detected, we use the `svninfo' tool to determine the +# in-tree revision number. +# - Otherwise, we use `git --describe'. +get_revision_string() +{ + v="unknown:unknown" + if [ -d CVS ]; then # Look for CVS (NetBSD). + v="cvs:unknown" + elif [ -d .svn ]; then # An SVN checkout (SourceForge or FreeBSD). + svnversion="$(svnversion 2>/dev/null)" + if [ -n "${svnversion}" ]; then + v="svn:${svnversion}" + fi + else # Try git (DragonflyBSD). + gitversion="$(git describe --all --dirty --long 2> /dev/null)" + if [ -n "${gitversion}" ]; then + v="git:${gitversion}" + fi + fi + + echo "${v}" +} + # # Parse options. # @@ -51,37 +77,14 @@ done [ -n "${top}" ] || usage -# Try to determine the in-tree revision number. -# -# This script attempts to handle the case where our sources have been -# incorporated into an operating system's base sources. -# -# - If SVN is detected, we use the `svninfo' tool to determine the -# in-tree revision number. -# - If CVS is detected, we use the string `unknown'. -# - Otherwise, we use `git --describe'. - curdir=`pwd` cd ${top} || usage "ERROR: Cannot change directory to \"${top}\"." -if [ -d CVS ]; then # Look for CVS (NetBSD). - versionstring=" cvs:unknown" -else # Try git (DragonFlyBSD). - gitversion="$(git describe --all --dirty --long 2> /dev/null)" - if [ -n "${gitversion}" ]; then - versionstring=" git:${gitversion}" - else # Assume an SVN checkout (SourceForge or FreeBSD). - svnversion="$(svnversion)" - if [ -n "${svnversion}" ]; then - versionstring=" svn:$(svnversion)" - fi - fi -fi - -if [ -z "${versionstring}" ]; then - echo "ERROR: cannot determine a revision number." 1>&2 +# Determine the in-tree revision number. +versionstring="$(get_revision_string)" || { + echo "ERROR: cannot determine a revision number." 1>&2; exit 1 -fi +} cd ${curdir} || usage "Cannot change back to ${curdir}." @@ -100,7 +103,7 @@ cat > ${tmpfile} < CLEANFILES+= ${_M4_SRCS:S/.m4$/.c/g} ${TEST_DATA} -${PROG}: ${TEST_DATA} ${TEST_LIB} ${TEST_DRIVER_MAIN} +${PROG}: ${TEST_DATA} .if defined(MAKE_TEST_SCAFFOLDING) && ${MAKE_TEST_SCAFFOLDING} == "yes" _TC_SRC= ${.OBJDIR}/tc.c # Test scaffolding. @@ -45,6 +44,6 @@ ${_TC_SRC}: ${_TEST_OBJS} .endif .endif -LDADD+= ${TEST_DRIVER_MAIN} -L${TEST_LIB} -ltest +LDADD+= -L${TEST_LIB} -ltest -L${TEST_DRIVER} -ldriver .include "${TOP}/mk/elftoolchain.prog.mk" diff --git a/nm/nm.c b/nm/nm.c index 493f3a80b408..c5c80455144c 100644 --- a/nm/nm.c +++ b/nm/nm.c @@ -48,7 +48,7 @@ #include "_elftc.h" -ELFTC_VCSID("$Id: nm.c 3504 2016-12-17 15:33:16Z kaiwang27 $"); +ELFTC_VCSID("$Id: nm.c 3722 2019-03-23 17:01:58Z jkoshy $"); /* symbol information list */ STAILQ_HEAD(sym_head, sym_entry); @@ -1143,7 +1143,6 @@ read_elf(Elf *elf, const char *filename, Elf_Kind kind) Elf_Arhdr *arhdr; Elf_Scn *scn; GElf_Shdr shdr; - GElf_Half i; Dwarf_Line *lbuf; Dwarf_Unsigned lineno; Dwarf_Signed lcount, filecount; @@ -1158,7 +1157,7 @@ read_elf(Elf *elf, const char *filename, Elf_Kind kind) struct var_info_entry *var; const char *shname, *objname; char *type_table, **sec_table, *sfile, **src_files; - size_t shstrndx, shnum, dynndx, strndx; + size_t i, shstrndx, shnum, dynndx, strndx; int ret, rtn, e_err; #define OBJNAME (objname == NULL ? filename : objname) diff --git a/readelf/readelf.1 b/readelf/readelf.1 index 9df20621c278..1f0b7bc918b0 100644 --- a/readelf/readelf.1 +++ b/readelf/readelf.1 @@ -22,9 +22,9 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $Id: readelf.1 3642 2018-10-14 14:24:28Z jkoshy $ +.\" $Id: readelf.1 3753 2019-06-28 01:13:13Z emaste $ .\" -.Dd September 13, 2012 +.Dd June 27, 2019 .Dt READELF 1 .Os .Sh NAME @@ -113,6 +113,8 @@ Print symbol tables. .It Fl t | Fl -section-details Print additional information about sections, such as the flags fields in section headers. +Implies +.Fl S . .It Fl v | Fl -version Prints a version identifier for .Nm diff --git a/readelf/readelf.c b/readelf/readelf.c index b2534792e2e0..ee0f67117361 100644 --- a/readelf/readelf.c +++ b/readelf/readelf.c @@ -47,7 +47,7 @@ #include "_elftc.h" -ELFTC_VCSID("$Id: readelf.c 3649 2018-11-24 03:26:23Z emaste $"); +ELFTC_VCSID("$Id: readelf.c 3769 2019-06-29 15:15:02Z emaste $"); /* * readelf(1) options. @@ -206,6 +206,11 @@ struct eflags_desc { const char *desc; }; +struct flag_desc { + uint64_t flag; + const char *desc; +}; + struct mips_option { uint64_t flag; const char *desc; @@ -284,6 +289,7 @@ static void dump_dwarf_ranges_foreach(struct readelf *re, Dwarf_Die die, static void dump_dwarf_str(struct readelf *re); static void dump_eflags(struct readelf *re, uint64_t e_flags); static void dump_elf(struct readelf *re); +static void dump_flags(struct flag_desc *fd, uint64_t flags); static void dump_dyn_val(struct readelf *re, GElf_Dyn *dyn, uint32_t stab); static void dump_dynamic(struct readelf *re); static void dump_liblist(struct readelf *re); @@ -298,6 +304,8 @@ static void dump_mips_specific_info(struct readelf *re); static void dump_notes(struct readelf *re); static void dump_notes_content(struct readelf *re, const char *buf, size_t sz, off_t off); +static void dump_notes_data(const char *name, uint32_t type, const char *buf, + size_t sz); static void dump_svr4_hash(struct section *s); static void dump_svr4_hash64(struct readelf *re, struct section *s); static void dump_gnu_hash(struct readelf *re, struct section *s); @@ -1184,6 +1192,7 @@ note_type_gnu(unsigned int nt) case 2: return "NT_GNU_HWCAP (Hardware capabilities)"; case 3: return "NT_GNU_BUILD_ID (Build id set by ld(1))"; case 4: return "NT_GNU_GOLD_VERSION (GNU gold version)"; + case 5: return "NT_GNU_PROPERTY_TYPE_0"; default: return (note_type_unknown(nt)); } } @@ -2261,8 +2270,15 @@ dump_eflags(struct readelf *re, uint64_t e_flags) } edesc = mips_eflags_desc; break; - case EM_PPC: case EM_PPC64: + switch (e_flags) { + case 0: printf(", Unspecified or Power ELF V1 ABI"); break; + case 1: printf(", Power ELF V1 ABI"); break; + case 2: printf(", OpenPOWER ELF V2 ABI"); break; + default: break; + } + /* FALLTHROUGH */ + case EM_PPC: edesc = powerpc_eflags_desc; break; case EM_SPARC: @@ -2713,6 +2729,59 @@ dump_arch_dyn_val(struct readelf *re, GElf_Dyn *dyn) } } +static void +dump_flags(struct flag_desc *desc, uint64_t val) +{ + struct flag_desc *fd; + + for (fd = desc; fd->flag != 0; fd++) { + if (val & fd->flag) { + val &= ~fd->flag; + printf(" %s", fd->desc); + } + } + if (val != 0) + printf(" unknown (0x%jx)", (uintmax_t)val); + printf("\n"); +} + +static struct flag_desc dt_flags[] = { + { DF_ORIGIN, "ORIGIN" }, + { DF_SYMBOLIC, "SYMBOLIC" }, + { DF_TEXTREL, "TEXTREL" }, + { DF_BIND_NOW, "BIND_NOW" }, + { DF_STATIC_TLS, "STATIC_TLS" }, + { 0, NULL } +}; + +static struct flag_desc dt_flags_1[] = { + { DF_1_BIND_NOW, "NOW" }, + { DF_1_GLOBAL, "GLOBAL" }, + { 0x4, "GROUP" }, + { DF_1_NODELETE, "NODELETE" }, + { DF_1_LOADFLTR, "LOADFLTR" }, + { 0x20, "INITFIRST" }, + { DF_1_NOOPEN, "NOOPEN" }, + { DF_1_ORIGIN, "ORIGIN" }, + { 0x100, "DIRECT" }, + { DF_1_INTERPOSE, "INTERPOSE" }, + { DF_1_NODEFLIB, "NODEFLIB" }, + { 0x1000, "NODUMP" }, + { 0x2000, "CONFALT" }, + { 0x4000, "ENDFILTEE" }, + { 0x8000, "DISPRELDNE" }, + { 0x10000, "DISPRELPND" }, + { 0x20000, "NODIRECT" }, + { 0x40000, "IGNMULDEF" }, + { 0x80000, "NOKSYMS" }, + { 0x100000, "NOHDR" }, + { 0x200000, "EDITED" }, + { 0x400000, "NORELOC" }, + { 0x800000, "SYMINTPOSE" }, + { 0x1000000, "GLOBAUDIT" }, + { 0, NULL } +}; + static void dump_dyn_val(struct readelf *re, GElf_Dyn *dyn, uint32_t stab) { @@ -2760,6 +2829,7 @@ dump_dyn_val(struct readelf *re, GElf_Dyn *dyn, uint32_t stab) case DT_SYMENT: case DT_RELSZ: case DT_RELENT: + case DT_PREINIT_ARRAYSZ: case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: case DT_GNU_CONFLICTSZ: @@ -2796,6 +2866,12 @@ dump_dyn_val(struct readelf *re, GElf_Dyn *dyn, uint32_t stab) case DT_GNU_PRELINKED: printf(" %s\n", timestamp(dyn->d_un.d_val)); break; + case DT_FLAGS: + dump_flags(dt_flags, dyn->d_un.d_val); + break; + case DT_FLAGS_1: + dump_flags(dt_flags_1, dyn->d_un.d_val); + break; default: printf("\n"); } @@ -3414,6 +3490,52 @@ dump_notes(struct readelf *re) } } +static struct flag_desc note_feature_ctl_flags[] = { + { 0x1, "ASLR_DISABLE" }, + { 0, NULL } +}; + +static void +dump_notes_data(const char *name, uint32_t type, const char *buf, size_t sz) +{ + size_t i; + const uint32_t *ubuf; + + /* Note data is at least 4-byte aligned. */ + if (((uintptr_t)buf & 3) != 0) { + warnx("bad note data alignment"); + goto unknown; + } + ubuf = (const uint32_t *)(const void *)buf; + + if (strcmp(name, "FreeBSD") == 0) { + switch (type) { + case NT_FREEBSD_ABI_TAG: + if (sz != 4) + goto unknown; + printf(" ABI tag: %u\n", ubuf[0]); + return; + /* NT_FREEBSD_NOINIT_TAG carries no data, treat as unknown. */ + case NT_FREEBSD_ARCH_TAG: + if (sz != 4) + goto unknown; + printf(" Arch tag: %x\n", ubuf[0]); + return; + case NT_FREEBSD_FEATURE_CTL: + if (sz != 4) + goto unknown; + printf(" Features:"); + dump_flags(note_feature_ctl_flags, ubuf[0]); + return; + } + } +unknown: + printf(" description data:"); + for (i = 0; i < sz; i++) + printf(" %02x", (unsigned char)buf[i]); + printf("\n"); +} + static void dump_notes_content(struct readelf *re, const char *buf, size_t sz, off_t off) { @@ -3430,7 +3552,9 @@ dump_notes_content(struct readelf *re, const char *buf, size_t sz, off_t off) return; } note = (Elf_Note *)(uintptr_t) buf; - name = (char *)(uintptr_t)(note + 1); + buf += sizeof(Elf_Note); + name = buf; + buf += roundup2(note->n_namesz, 4); /* * The name field is required to be nul-terminated, and * n_namesz includes the terminating nul in observed @@ -3448,8 +3572,8 @@ dump_notes_content(struct readelf *re, const char *buf, size_t sz, off_t off) printf(" %-13s %#010jx", name, (uintmax_t) note->n_descsz); printf(" %s\n", note_type(name, re->ehdr.e_type, note->n_type)); - buf += sizeof(Elf_Note) + roundup2(note->n_namesz, 4) + - roundup2(note->n_descsz, 4); + dump_notes_data(name, note->n_type, buf, note->n_descsz); + buf += roundup2(note->n_descsz, 4); } } @@ -6906,13 +7030,13 @@ dump_object(struct readelf *re) if ((re->elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { warnx("elf_begin() failed: %s", elf_errmsg(-1)); - return; + goto done; } switch (elf_kind(re->elf)) { case ELF_K_NONE: warnx("Not an ELF file."); - return; + goto done; case ELF_K_ELF: dump_elf(re); break; @@ -6921,10 +7045,11 @@ dump_object(struct readelf *re) break; default: warnx("Internal: libelf returned unknown elf kind."); - return; } +done: elf_end(re->elf); + close(fd); } static void @@ -7345,7 +7470,7 @@ main(int argc, char **argv) re->options |= RE_S; break; case 't': - re->options |= RE_T; + re->options |= RE_SS | RE_T; break; case 'u': re->options |= RE_U; diff --git a/test/ar/tc/add-nonexistent/out/archive.a b/test/ar/tc/add-nonexistent/out/archive.a new file mode 100644 index 000000000000..8b277f0dd5dc --- /dev/null +++ b/test/ar/tc/add-nonexistent/out/archive.a @@ -0,0 +1 @@ +! diff --git a/test/libelf/tset/Makefile b/test/libelf/tset/Makefile index 63e958691306..5a5e96f6ebd1 100644 --- a/test/libelf/tset/Makefile +++ b/test/libelf/tset/Makefile @@ -1,5 +1,5 @@ # -# $Id: Makefile 3025 2014-04-18 16:20:25Z jkoshy $ +# $Id: Makefile 3715 2019-03-18 09:15:40Z jkoshy $ # TOP= ../../.. @@ -36,6 +36,7 @@ SUBDIR+= elf_ndxscn SUBDIR+= elf_next SUBDIR+= elf_newscn SUBDIR+= elf_nextscn +SUBDIR+= elf_rand SUBDIR+= elf_rawfile SUBDIR+= elf_strptr SUBDIR+= elf_update diff --git a/test/libelf/tset/bin/elfc b/test/libelf/tset/bin/elfc index 98995e820e26..4c77e4220b18 100755 --- a/test/libelf/tset/bin/elfc +++ b/test/libelf/tset/bin/elfc @@ -74,7 +74,7 @@ # sections, a section index may be manually specified using a # 'sh_index' pseudo field. # -# $Id: elfc 3614 2018-04-21 19:48:04Z jkoshy $ +# $Id: elfc 3689 2019-02-23 22:50:51Z jkoshy $ version = "%prog 1.0" usage = "usage: %prog [options] [input-file]" @@ -442,6 +442,14 @@ def check_dict(d, l, node=None): raise ElfError(node, "{%s} Unknown key(s) %s" % \ (node.tag, unknown)) +def bounded_value(v, encoding): + """Return the value of 'v' bounded to the maximum size for a type.""" + if encoding == "H": + return (v & 0xFFFF) + elif encoding == "I": + return (v & 0xFFFFFFFF) + return v + # # Helper classes. # @@ -559,8 +567,10 @@ class ElfType: else: n = 3 for t in self.fields: - if t[n] != "": - a.append(getattr(self, t[0])) + field_encoding = t[n] + if field_encoding != "": + v = getattr(self, t[0]) + a.append(bounded_value(v, field_encoding)) return tuple(a) def getfields(self, elfclass): diff --git a/test/libelf/tset/common/Makefile b/test/libelf/tset/common/Makefile index 5dd5bf9c1a5b..8f8400a39753 100644 --- a/test/libelf/tset/common/Makefile +++ b/test/libelf/tset/common/Makefile @@ -1,10 +1,11 @@ -# $Id: Makefile 1719 2011-08-12 08:24:14Z jkoshy $ +# $Id: Makefile 3690 2019-02-23 22:51:13Z jkoshy $ TOP= ../../../.. YAML_FILES= check_elf \ getclass \ ehdr \ + ehdr-malformed-1 \ fsize \ newehdr newscn newscn2 \ phdr \ diff --git a/test/libelf/tset/common/ehdr-malformed-1.yaml b/test/libelf/tset/common/ehdr-malformed-1.yaml new file mode 100644 index 000000000000..d7c000f47aed --- /dev/null +++ b/test/libelf/tset/common/ehdr-malformed-1.yaml @@ -0,0 +1,23 @@ +%YAML 1.1 +# $Id$ +--- +ehdr: !Ehdr + e_ident: !Ident # e_ident[] members + ei_class: ELFCLASSNONE + ei_data: ELFDATANONE + ei_osabi: ELFOSABI_SYSV + ei_abiversion: 0 + # other members + e_type: 0xFF03 + e_machine: 0x42 + e_version: 0xFFFFFFFF + e_entry: 0xFFFFFFFFFFFFFFFF + e_phoff: 0xFFFFFFFFFFFFFFFF + e_shoff: 0xFFFFFFFFFFFFFFFF + e_flags: [ 64, 8, 2, 1] + e_ehsize: 62 + e_phentsize: 228 + e_phnum: 0 + e_shentsize: 8192 + e_shnum: 0 + e_shstrndx: 0 diff --git a/test/libelf/tset/common/ehdr_template.m4 b/test/libelf/tset/common/ehdr_template.m4 index 872e0ff3b7c8..31cacd4de541 100644 --- a/test/libelf/tset/common/ehdr_template.m4 +++ b/test/libelf/tset/common/ehdr_template.m4 @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ehdr_template.m4 3174 2015-03-27 17:13:41Z emaste $ + * $Id: ehdr_template.m4 3703 2019-03-02 20:41:03Z jkoshy $ */ include(`elfts.m4') @@ -367,3 +367,51 @@ tcElfWrongSize$1(void) FN(`LSB') FN(`MSB') + +/* + * Verify that malformed ELF objects are rejected. + */ + +undefine(`FN') +define(`FN',` +void +tcMalformed1$1(void) +{ + int error, fd, result; + Elf *e; + char *fn; + TS_EHDR *eh; + + TP_CHECK_INITIALIZATION(); + + TP_ANNOUNCE("TS_ICNAME with a malformed ELF header " + "fails with ELF_E_HEADER."); + + e = NULL; + fd = -1; + fn = "ehdr-malformed-1.TOLOWER($1)`'TS_EHDRSZ"; + result = TET_UNRESOLVED; + + _TS_OPEN_FILE(e, fn, ELF_C_READ, fd, goto done;); + + error = 0; + if ((eh = TS_ICFUNC`'(e)) != NULL) { + TP_FAIL("\"%s\" TS_ICNAME`'() succeeded.", fn); + goto done; + } else if ((error = elf_errno()) != ELF_E_HEADER) { + TP_FAIL("\"%s\" incorrect error (%d).", fn, error); + goto done; + } + + result = TET_PASS; + +done: + if (e) + (void) elf_end(e); + if (fd != -1) + (void) close(fd); + tet_result(result); +}') + +FN(`LSB') +FN(`MSB') diff --git a/test/libelf/tset/elf32_getehdr/Makefile b/test/libelf/tset/elf32_getehdr/Makefile index 8285b1f8ed6e..1b01505e750c 100644 --- a/test/libelf/tset/elf32_getehdr/Makefile +++ b/test/libelf/tset/elf32_getehdr/Makefile @@ -1,8 +1,8 @@ -# $Id: Makefile 1368 2011-01-22 09:09:15Z jkoshy $ +# $Id: Makefile 3691 2019-02-23 23:34:04Z jkoshy $ TOP= ../../../.. TS_SRCS= ehdr.m4 -TS_YAML= ehdr +TS_YAML= ehdr ehdr-malformed-1 .include "${TOP}/mk/elftoolchain.tet.mk" diff --git a/test/libelf/tset/elf32_newehdr/Makefile b/test/libelf/tset/elf32_newehdr/Makefile index 78c4f1238af3..7c109199cf4f 100644 --- a/test/libelf/tset/elf32_newehdr/Makefile +++ b/test/libelf/tset/elf32_newehdr/Makefile @@ -1,9 +1,10 @@ -# $Id: Makefile 1358 2011-01-08 05:40:41Z jkoshy $ +# $Id: Makefile 3702 2019-03-02 20:40:55Z jkoshy $ TOP= ../../../.. TS_SRCS= ehdr.m4 TS_DATA= ehdr.msb32 ehdr.lsb32 ehdr.msb64 ehdr.lsb64 \ + ehdr-malformed-1.lsb32 ehdr-malformed-1.msb32 \ newehdr.lsb32 newehdr.msb32 .include "${TOP}/mk/elftoolchain.tet.mk" diff --git a/test/libelf/tset/elf64_getehdr/Makefile b/test/libelf/tset/elf64_getehdr/Makefile index e8bb49ad4d25..1b01505e750c 100644 --- a/test/libelf/tset/elf64_getehdr/Makefile +++ b/test/libelf/tset/elf64_getehdr/Makefile @@ -1,8 +1,8 @@ -# $Id: Makefile 1358 2011-01-08 05:40:41Z jkoshy $ +# $Id: Makefile 3691 2019-02-23 23:34:04Z jkoshy $ TOP= ../../../.. TS_SRCS= ehdr.m4 -TS_YAML= ehdr +TS_YAML= ehdr ehdr-malformed-1 .include "${TOP}/mk/elftoolchain.tet.mk" diff --git a/test/libelf/tset/elf64_newehdr/Makefile b/test/libelf/tset/elf64_newehdr/Makefile index 88ccf4d1791b..1e56f4113374 100644 --- a/test/libelf/tset/elf64_newehdr/Makefile +++ b/test/libelf/tset/elf64_newehdr/Makefile @@ -1,9 +1,10 @@ -# $Id: Makefile 1358 2011-01-08 05:40:41Z jkoshy $ +# $Id: Makefile 3702 2019-03-02 20:40:55Z jkoshy $ TOP= ../../../.. TS_SRCS= ehdr.m4 TS_DATA= ehdr.msb64 ehdr.lsb64 ehdr.msb32 ehdr.lsb32 \ + ehdr-malformed-1.lsb64 ehdr-malformed-1.msb64 \ newehdr.lsb64 newehdr.msb64 .include "${TOP}/mk/elftoolchain.tet.mk" diff --git a/test/libelf/tset/elf_begin/Makefile b/test/libelf/tset/elf_begin/Makefile index d5c675cd0449..bdc1575f80c6 100644 --- a/test/libelf/tset/elf_begin/Makefile +++ b/test/libelf/tset/elf_begin/Makefile @@ -1,8 +1,9 @@ -# $Id: Makefile 2933 2013-03-30 01:33:02Z jkoshy $ +# $Id: Makefile 3704 2019-03-02 20:41:12Z jkoshy $ TOP= ../../../.. TS_SRCS= begin.m4 +TS_FILES= entry-too-large.ar TS_DATA= check_elf.msb32 check_elf.lsb32 check_elf.msb64 \ check_elf.lsb64 a.ar a-bsd.ar a.o zero CLEANFILES+= a.c diff --git a/test/libelf/tset/elf_begin/begin.m4 b/test/libelf/tset/elf_begin/begin.m4 index 9a282eb3f746..16ac34e66826 100644 --- a/test/libelf/tset/elf_begin/begin.m4 +++ b/test/libelf/tset/elf_begin/begin.m4 @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: begin.m4 2933 2013-03-30 01:33:02Z jkoshy $ + * $Id: begin.m4 3706 2019-03-02 20:57:45Z jkoshy $ */ #include @@ -634,3 +634,44 @@ tcArMemoryFdIgnored_$1(void) ARFN(`BSD') ARFN(`SVR4') + +/* + * Verify behavior with a corrupted header containing a too-large size. + */ +void +tcArEntryTooLarge(void) +{ + Elf *ar_e, *e; + int error, fd, result; + + result = TET_UNRESOLVED; + ar_e = NULL; + e = NULL; + + TP_ANNOUNCE("elf_begin() returns ELF_E_ARCHIVE for too-large archive " + "entries."); + + TP_SET_VERSION(); + + _TS_OPEN_FILE(ar_e, "entry-too-large.ar", ELF_C_READ, fd, goto done;); + + if ((e = elf_begin(fd, ELF_C_READ, ar_e)) != NULL) { + TP_FAIL("elf_begin() succeeded."); + goto done; + } + + error = elf_errno(); + if (error != ELF_E_ARCHIVE) { + TP_FAIL("unexpected error %d", error); + goto done; + } + + result = TET_PASS; + +done: + if (e) + (void) elf_end(e); + if (ar_e) + (void) elf_end(ar_e); + tet_result(result); +} diff --git a/test/libelf/tset/elf_begin/entry-too-large.ar b/test/libelf/tset/elf_begin/entry-too-large.ar new file mode 100644 index 000000000000..5cab1486306c --- /dev/null +++ b/test/libelf/tset/elf_begin/entry-too-large.ar @@ -0,0 +1,3 @@ +! +a1.c/ 1551379738 1000 1000 100644 9 ` +1234567 diff --git a/test/libelf/tset/elf_getdata/getdata.m4 b/test/libelf/tset/elf_getdata/getdata.m4 index 40afc8a4ec30..332f81bcebe3 100644 --- a/test/libelf/tset/elf_getdata/getdata.m4 +++ b/test/libelf/tset/elf_getdata/getdata.m4 @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: getdata.m4 2090 2011-10-27 08:07:39Z jkoshy $ + * $Id: getdata.m4 3695 2019-02-25 18:55:07Z jkoshy $ */ #include @@ -68,6 +68,36 @@ findscn(Elf *e, const char *name) return (NULL); } +/* + * Check the contents of an Elf_Data descriptor. + * + * The return value from this helper is as follows: + * + * 0 - the descriptor matched the specified content. + * -1 - the descriptor size had a mismatch. + * >0 - the content of the descriptor did not match. The returned value + * is the index of the first byte that differs. + */ +static int +match_content(Elf_Data *ed, size_t nbytes, const char *content) +{ + int n; + const char *buf; + + if (ed->d_size != nbytes) + return (-1); + + buf = (const char *) ed->d_buf; + for (n = 0; n < nbytes; n++) { + if (*buf != *content) + return (n); + buf++; + content++; + } + + return (0); +} + define(`ZEROSECTION',".zerosection") undefine(`FN') define(`FN',` @@ -106,6 +136,11 @@ tcZeroSection$1$2(void) goto done; } + if ((ed = elf_getdata(scn, ed)) != NULL) { + TP_FAIL("Extra data descriptor in section."); + goto done; + } + result = TET_PASS; done: @@ -139,16 +174,17 @@ define(`_FN',` void tcNonZeroSection$1$2(void) { - Elf *e; int error, fd, result; - const size_t strsectionsize = sizeof stringsection; - size_t n, shstrndx; + int match_error; + size_t shstrndx; const char *buf; Elf_Scn *scn; Elf_Data *ed; + Elf *e; - e = NULL; fd = -1; + e = NULL; + scn = NULL; result = TET_UNRESOLVED; TP_ANNOUNCE("a data descriptor for a non-zero sized section " @@ -170,19 +206,22 @@ tcNonZeroSection$1$2(void) goto done; } - if (ed->d_size != strsectionsize) { + match_error = match_content(ed, sizeof(stringsection), + stringsection); + if (match_error == -1) { TP_FAIL("Illegal values returned: d_size %d != expected %d", - (int) ed->d_size, strsectionsize); + (int) ed->d_size, sizeof(stringsection)); + goto done; + } else if (match_error > 0) { + buf = (const char *) ed->d_buf; + TP_FAIL("String mismatch: buf[%d] \"%c\" != \"%c\"", + match_error, buf[match_error], + stringsection[match_error]); goto done; } - if (memcmp(stringsection, ed->d_buf, strsectionsize) != 0) { - buf = (const char *) ed->d_buf; - for (n = 0; n < strsectionsize; n++) - if (buf[n] != stringsection[n]) - break; - TP_FAIL("String mismatch: buf[%d] \"%c\" != \"%c\"", - n, buf[n], stringsection[n]); + if ((ed = elf_getdata(scn, ed)) != NULL) { + TP_FAIL("Extra data descriptor in section."); goto done; } @@ -201,3 +240,112 @@ _FN(lsb,32) _FN(lsb,64) _FN(msb,32) _FN(msb,64) + +static const char new_content[] = { +changequote({,}) + 'n', 'e', 'w', ' ', 'c', 'o', 'n', 't', 'e', 'n', 't', '\0' +changequote +}; + +/* + * Verify that a section with multiple Elf_Data segments is handled correctly. + */ +undefine(`_FN') +define(`_FN',` +void +tcDataTraversal$1$2(void) +{ + Elf *e; + Elf_Scn *scn; + Elf_Data *ed; + size_t shstrndx; + int error, fd, match_error, result; + + e = NULL; + fd = -1; + result = TET_UNRESOLVED; + + TP_ANNOUNCE("multiple Elf_Data segments can be traversed."); + _TS_OPEN_FILE(e, "zerosection.$1$2", ELF_C_READ, fd, goto done;); + + if (elf_getshdrstrndx(e, &shstrndx) != 0 || + (scn = elf_getscn(e, shstrndx)) == NULL) { + TP_UNRESOLVED("Cannot find the string table"); + goto done; + } + + /* + * Add new data to the string section. + */ + if ((ed = elf_newdata(scn)) == NULL) { + TP_UNRESOLVED("Cannot allocate new data."); + goto done; + } + + ed->d_buf = (char *) new_content; + ed->d_size = sizeof(new_content); + + /* + * Rescan the descriptor list for the section. + */ + ed = NULL; + if ((ed = elf_getdata(scn, ed)) == NULL) { + error = elf_errno(); + TP_FAIL("elf_getdata failed %d \"%s\"", error, + elf_errmsg(error)); + goto done; + } + + match_error = match_content(ed, sizeof(stringsection), + stringsection); + if (match_error == -1) { + TP_FAIL("Unexpected size of first descriptor: " + "d_size %d != expected %d", (int) ed->d_size, + sizeof(stringsection)); + goto done; + } else if (match_error > 0) { + TP_FAIL("String content mismatch for data descriptor 1."); + goto done; + } + + if ((ed = elf_getdata(scn, ed)) == NULL) { + error = elf_errno(); + TP_FAIL("Missing second data section: %d \"%s\"", error, + elf_errmsg(error)); + goto done; + } + + match_error = match_content(ed, sizeof(new_content), + new_content); + if (match_error == -1) { + TP_FAIL("Unexpected size of second descriptor: " + "d_size %d != expected %d", (int) ed->d_size, + sizeof(new_content)); + goto done; + } else if (match_error > 0) { + TP_FAIL("String content mismatch for data descriptor 2."); + goto done; + } + + /* + * There should be no other Elf_Data descriptors. + */ + if ((ed = elf_getdata(scn, ed)) != NULL) { + TP_FAIL("Too many Elf_Data descriptors for section."); + goto done; + } + + result = TET_PASS; + +done: + if (e) + elf_end(e); + if (fd != -1) + (void) close(fd); + tet_result(result); +}') + +_FN(lsb,32) +_FN(lsb,64) +_FN(msb,32) +_FN(msb,64) diff --git a/test/libelf/tset/elf_rand/Makefile b/test/libelf/tset/elf_rand/Makefile new file mode 100644 index 000000000000..1b17ff29796c --- /dev/null +++ b/test/libelf/tset/elf_rand/Makefile @@ -0,0 +1,18 @@ +# $Id$ + +TOP= ../../../.. + +TS_SRCS= rand.m4 +TS_DATA= a.ar s1 s2 +TS_FILES= empty-file.ar missing-file.ar + +s1: .SILENT + echo 'This is s1.' > ${.TARGET} +s2: .SILENT + echo 's2.' > ${.TARGET} + +a.ar: s1 s2 .SILENT + rm -f ${.TARGET} + ${AR} crv ${.TARGET} s1 s2 > /dev/null + +.include "${TOP}/mk/elftoolchain.tet.mk" diff --git a/test/libelf/tset/elf_rand/empty-file.ar b/test/libelf/tset/elf_rand/empty-file.ar new file mode 100644 index 000000000000..f7039eda3a06 --- /dev/null +++ b/test/libelf/tset/elf_rand/empty-file.ar @@ -0,0 +1,2 @@ +! +e1/ 0 0 0 644 0 ` diff --git a/test/libelf/tset/elf_rand/missing-file.ar b/test/libelf/tset/elf_rand/missing-file.ar new file mode 100644 index 000000000000..d62c803db28b --- /dev/null +++ b/test/libelf/tset/elf_rand/missing-file.ar @@ -0,0 +1,2 @@ +! +e1/ 0 0 0 644 42 ` diff --git a/test/libelf/tset/elf_rand/rand.m4 b/test/libelf/tset/elf_rand/rand.m4 new file mode 100644 index 000000000000..bdcf436456b5 --- /dev/null +++ b/test/libelf/tset/elf_rand/rand.m4 @@ -0,0 +1,415 @@ +/*- + * Copyright (c) 2019 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. + * + * $Id$ + */ + +#include +#include + +#include +#include +#include +#include + +#include "elfts.h" +#include "tet_api.h" + +IC_REQUIRES_VERSION_INIT(); + +include(`elfts.m4') + +/* + * The following definitions should match those in `./Makefile'. + */ +define(`TP_ARFILE',`"a.ar"') +define(`TP_NONARCHIVE', `"s1"') + +/* + * The use of an offset less than SARMAG should fail. + */ +void +tcSeekBelowSarmag(void) +{ + Elf *ar; + off_t offset; + int error, fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand() fails for an offset less than SARMAG"); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + result = TET_PASS; + + if ((offset = elf_rand(ar, 1)) != 0) { + TP_FAIL("elf_rand() succeeded with offset=%lld", + (unsigned long long) offset); + } else if ((error = elf_errno()) != ELF_E_ARGUMENT) { + TP_FAIL("unexpected error=%d \"%s\"", error, + elf_errmsg(error)); + } + + (void) elf_end(ar); + (void) close(fd); + + tet_result(result); +} + +/* + * The use of an offset greater than the largest valid file offset + * should fail. + */ +void +tcSeekMoreThanFileSize(void) +{ + Elf *ar; + off_t offset; + struct stat sb; + int error, fd, result; + + result = TET_UNRESOLVED; + ar = NULL; + fd = -1; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand() fails with a too-large offset"); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + /* Get the file size of the archive. */ + if (fstat(fd, &sb) < 0) { + TP_UNRESOLVED("cannot determine the size of \"%s\"", + TP_ARFILE); + goto done; + } + + result = TET_PASS; + + if ((offset = elf_rand(ar, sb.st_size)) != 0) { + TP_FAIL("elf_rand() succeeded with offset=%lld", + (unsigned long long) offset); + } else if ((error = elf_errno()) != ELF_E_ARGUMENT) { + TP_FAIL("unexpected error=%d \"%s\"", error, + elf_errmsg(error)); + } + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + +/* + * An offset with value SARMAG is accepted. + */ +void +tcOffsetEqualsSARMAG(void) +{ + Elf *ar; + off_t offset; + int fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand(SARMAG) succeeds."); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + if ((offset = elf_rand(ar, SARMAG)) != SARMAG) { + TP_FAIL("unexpected offset: %lld", + (long long) offset); + goto done; + } + + result = TET_PASS; + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + +/* + * Invoking elf_rand() on a non-archive should fail. + */ +void +tcOnNonArchive(void) +{ + Elf *e; + off_t offset; + int error, fd, result; + + fd = -1; + e = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand(non-archive) fails."); + + TS_OPEN_FILE(e, TP_NONARCHIVE, ELF_C_READ, fd); + + if ((offset = elf_rand(e, SARMAG)) != 0 || + (error = elf_errno()) != ELF_E_ARGUMENT) { + TP_FAIL("unexpected offset=%lld", + (long long) offset); + goto done; + } + + result = TET_PASS; + +done: + if (e) + (void) elf_end(e); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + +/* + * Use an offset value that could cause an overflow. + */ +void +tcOffsetOverflow(void) +{ + Elf *ar; + off_t offset; + uint64_t max_offset; + int error, fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + /* A even offset that is close to overflowing. */ + max_offset = (1ULL << (sizeof(off_t) * CHAR_BIT - 1)) - 2; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("offset close to overflowing an off_t"); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + if ((offset = elf_rand(ar, (off_t) max_offset)) != 0) { + TP_FAIL("unexpected success, offset=%lld", + (long long) offset); + goto done; + } + + result = TET_PASS; + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + +/* + * Setting the offset to a value that does not correspond to an ar header + * should fail. + */ +void +tcOffsetNotCorrespondingToAnArchiveHeader(void) +{ + Elf *ar; + off_t offset; + int error, fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand(non-header-offset) should fail."); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + if ((offset = elf_rand(ar, SARMAG+2)) != 0) { + TP_FAIL("unexpected success, offset=%lld", + (long long) offset); + goto done; + } else if ((error = elf_errno()) != ELF_E_ARCHIVE) { + TP_FAIL("unexpected error=%d \"%s\"", error, + elf_errmsg(error)); + goto done; + } + + result = TET_PASS; + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + +/* + * Odd values of offsets are not legal. + */ +void +tcOddOffset(void) +{ + Elf *ar; + off_t offset; + int error, fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand(odd-offset-value) should fail."); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + if ((offset = elf_rand(ar, SARMAG+1)) != 0) { + TP_FAIL("unexpected success, offset=%lld", + (long long) offset); + goto done; + } else if ((error = elf_errno()) != ELF_E_ARGUMENT) { + TP_FAIL("unexpected error=%d \"%s\"", error, + elf_errmsg(error)); + goto done; + } + + result = TET_PASS; + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + +/* + * Negative offset values are not legal. + */ +void +tcNegativeOffset(void) +{ + Elf *ar; + off_t offset; + int error, fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand(odd-offset-value) should fail."); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + if ((offset = elf_rand(ar, -SARMAG)) != 0) { + TP_FAIL("unexpected success, offset=%lld", + (long long) offset); + goto done; + } else if ((error = elf_errno()) != ELF_E_ARGUMENT) { + TP_FAIL("unexpected error=%d \"%s\"", error, + elf_errmsg(error)); + goto done; + } + + result = TET_PASS; + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} + + +/* These offsets correspond to archive TP_ARFILE. */ +static off_t valid_offsets[] = { + SARMAG, /* File 's1'. */ + 80 /* File 's2'. */ +}; + +static const int number_of_offsets = + sizeof(valid_offsets) / sizeof(valid_offsets[0]); + +/* + * Valid offsets should be usable. + */ +void +tcValidOffsets(void) +{ + Elf *ar; + off_t offset; + int i, error, fd, result; + + fd = -1; + ar = NULL; + result = TET_UNRESOLVED; + + TP_CHECK_INITIALIZATION(); + TP_ANNOUNCE("elf_rand(valid-offsets) succeeds."); + + TS_OPEN_FILE(ar, TP_ARFILE, ELF_C_READ, fd); + + for (i = 0; i < number_of_offsets; i++) { + if ((offset = elf_rand(ar, valid_offsets[i])) != + valid_offsets[i]) { + error = elf_errno(); + TP_FAIL("failed to seek to offset %lld, error=%d " + "\"%s\"", (long long) offset, error, + elf_errmsg(error)); + goto done; + } + } + + result = TET_PASS; + +done: + if (ar) + (void) elf_end(ar); + if (fd != -1) + (void) close(fd); + + tet_result(result); +} diff --git a/test/libtest/Makefile b/test/libtest/Makefile index 1dc489b9db8a..3cf6de08f316 100644 --- a/test/libtest/Makefile +++ b/test/libtest/Makefile @@ -9,9 +9,8 @@ SUBDIR+= lib SUBDIR+= driver SUBDIR+= examples -.if !make(install) +.if !make(install) && !make(test) .include "$(TOP)/mk/elftoolchain.subdir.mk" .else -install: .SILENT .PHONY - echo Nothing to install. +install test: .SILENT .PHONY .endif diff --git a/test/libtest/README.rst b/test/libtest/README.rst index 3f29c85f8a93..b93dca7a69ee 100644 --- a/test/libtest/README.rst +++ b/test/libtest/README.rst @@ -43,7 +43,7 @@ functions contained in a test case named "``helloworld``": /* File: test.c */ #include "test.h" - TESTCASE_DESCRIPTION(helloworld) = + TEST_CASE_DESCRIPTION(helloworld) = "A description of the helloworld test case."; enum test_result @@ -69,14 +69,14 @@ Test cases can define their own set up and tear down functions: tc_setup_helloworld(testcase_state *tcs) { *tcs = ..allocate a struct helloworld_test.. ; - return (TESTCASE_OK); + return (TEST_CASE_OK); } enum testcase_status tc_teardown_helloworld(testcase_state tcs) { .. deallocate test case state.. - return (TESTCASE_OK); + return (TEST_CASE_OK); } The set up function for a test case will be invoked prior to any of diff --git a/test/libtest/bin/make-test-scaffolding b/test/libtest/bin/make-test-scaffolding index c0966d34527b..6ec78e8224e1 100755 --- a/test/libtest/bin/make-test-scaffolding +++ b/test/libtest/bin/make-test-scaffolding @@ -79,7 +79,7 @@ cat < #include "test.h" -#include "test_runner.h" +#include "test_case.h" EOF if ! nm ${*} | sort -k 3 | \ @@ -108,9 +108,13 @@ if ! nm ${*} | sort -k 3 | \ function print_test_case_record(tc_name) { printf("\t{\n") printf("\t\t.tc_name = \"%s\",\n", tc_name) - printf("\t\t.tc_description = %s,\n", test_case_descriptions[tc_name]) + printf("\t\t.tc_description = %s,\n", + test_case_descriptions[tc_name]) printf("\t\t.tc_tags = %s,\n", test_case_tags[tc_name]) - printf("\t\t.tc_tests = test_functions_%s\n", tc_name) + tf_name = "test_functions_" tc_name + printf("\t\t.tc_tests = %s,\n", tf_name) + printf("\t\t.tc_count = sizeof (%s) / sizeof (%s[0]),\n", + tf_name, tf_name) printf("\t},\n") } function delete_test_functions(tc_name) { @@ -120,16 +124,19 @@ if ! nm ${*} | sort -k 3 | \ } } function print_test_functions_record(tc_name) { - printf("struct test_descriptor test_functions_%s[] = {\n", tc_name) + printf("struct test_function_descriptor test_functions_%s[]", + tc_name) + printf(" = {\n") for (tf_name in test_functions) { if (tc_name != matched_test_case(tf_name)) continue printf("\t{\n") - printf("\t\t.t_name = \"%s\",\n", tf_name) - printf("\t\t.t_description = %s,\n", + printf("\t\t.tf_name = \"%s\",\n", tf_name) + printf("\t\t.tf_description = %s,\n", test_function_descriptions[tf_name]) - printf("\t\t.t_func = %s,\n", prefix_tf tf_name) - printf("\t\t.t_tags = %s\n", test_function_tags[tf_name]) + printf("\t\t.tf_func = %s,\n", prefix_tf tf_name) + printf("\t\t.tf_tags = %s\n", + test_function_tags[tf_name]) printf("\t},\n") } printf("};\n") @@ -144,7 +151,7 @@ if ! nm ${*} | sort -k 3 | \ test_case_tags[DEFAULT] = "NULL" } ($2 == "R" || $2 == "D") && $3 ~ "^" prefix_tc_descr { - printf("extern testcase_description %s;\n", $3) + printf("extern test_case_description %s;\n", $3) tc_name = suffix($3, prefix_tc_descr) test_cases[tc_name] = 1 test_case_descriptions[tc_name] = $3 @@ -155,7 +162,7 @@ if ! nm ${*} | sort -k 3 | \ test_case_setup[tc_name] = $3 } ($2 == "R" || $2 == "D") && $3 ~ "^" prefix_tc_tags { - printf("extern testcase_tags %s;\n", $3) + printf("extern test_case_tags %s;\n", $3) tc_name = suffix($3, prefix_tc_tags) test_cases[tc_name] = 1 test_case_tags[tc_name] = $3 @@ -206,6 +213,8 @@ if ! nm ${*} | sort -k 3 | \ if (needs_default) print_test_case_record(DEFAULT) printf("};\n") + printf("const int test_case_count = sizeof(test_cases) / ") + printf("sizeof(test_cases[0]);\n") }'; then # Cleanup in case of an error. rm ${output_file} diff --git a/test/libtest/driver/Makefile b/test/libtest/driver/Makefile index d149fee159d4..87788d3fb13c 100644 --- a/test/libtest/driver/Makefile +++ b/test/libtest/driver/Makefile @@ -6,9 +6,12 @@ TOP= ../../.. CFLAGS+= -I${TOP}/test/libtest/lib -LIB= test_main -SRCS= test_main.c +LIB= driver +SRCS= driver.c \ + driver_main.c WARNS?= 6 +MAN= test_driver.1 + .include "$(TOP)/mk/elftoolchain.lib.mk" diff --git a/test/libtest/driver/driver.c b/test/libtest/driver/driver.c new file mode 100644 index 000000000000..aa85c7cb4ddf --- /dev/null +++ b/test/libtest/driver/driver.c @@ -0,0 +1,216 @@ +/*- + * Copyright (c) 2018, 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 + * in this position and unchanged. + * 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(S) ``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(S) 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. + */ + +/* + * The implementation of the test driver. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "driver.h" + +#if defined(ELFTC_VCSID) +ELFTC_VCSID("$Id$"); +#endif + +#define SYSTEM_TMPDIR_ENV_VAR "TMPDIR" + +bool +test_driver_add_search_path(struct test_run *tr, const char *directory_name) +{ + char *canonical_path; + struct test_search_path_entry *entry; + + if (!test_driver_is_directory(directory_name)) + return (false); + + if ((canonical_path = realpath(directory_name, NULL)) == NULL) + err(1, "Cannot determine the canonical path for \"%s\"", + directory_name); + + /* Look for, and ignore duplicates. */ + STAILQ_FOREACH(entry, &tr->tr_search_path, tsp_next) { + if (strcmp(canonical_path, entry->tsp_directory) == 0) + return (true); + } + + entry = calloc(1, sizeof(*entry)); + entry->tsp_directory = canonical_path; + + STAILQ_INSERT_TAIL(&tr->tr_search_path, entry, tsp_next); + + return (true); +} + +/* + * Return an initialized test run descriptor. + * + * The caller should use test_driver_free_run() to release the returned + * descriptor. + */ +struct test_run * +test_driver_allocate_run(void) +{ + struct test_run *tr; + + tr = calloc(sizeof(struct test_run), 1); + tr->tr_action = TEST_RUN_EXECUTE; + tr->tr_style = TR_STYLE_LIBTEST; + STAILQ_INIT(&tr->tr_test_cases); + STAILQ_INIT(&tr->tr_search_path); + + return (tr); +} + +/* + * Destroy an allocated test run descriptor. + * + * The passed in pointer should not be used after this function returns. + */ +void +test_driver_free_run(struct test_run *tr) +{ + struct test_search_path_entry *path_entry; + struct test_case_selector *test_case_entry; + struct test_function_selector *function_entry; + + free(tr->tr_runtime_base_directory); + free(tr->tr_name); + if (tr->tr_artefact_archive) + free(tr->tr_artefact_archive); + + /* Free the search path list. */ + while (!STAILQ_EMPTY(&tr->tr_search_path)) { + path_entry = STAILQ_FIRST(&tr->tr_search_path); + STAILQ_REMOVE_HEAD(&tr->tr_search_path, tsp_next); + free(path_entry); + } + + /* Free the test selector list. */ + while (!STAILQ_EMPTY(&tr->tr_test_cases)) { + test_case_entry = STAILQ_FIRST(&tr->tr_test_cases); + STAILQ_REMOVE_HEAD(&tr->tr_test_cases, tcs_next); + + /* Free the linked test functions. */ + while (!STAILQ_EMPTY(&test_case_entry->tcs_functions)) { + function_entry = + STAILQ_FIRST(&test_case_entry->tcs_functions); + STAILQ_REMOVE_HEAD(&test_case_entry->tcs_functions, + tfs_next); + + free(function_entry); + } + + free(test_case_entry); + } + + free(tr); +} + +/* + * Populate unset fields of a struct test_run with defaults. + */ +bool +test_driver_finish_run_initialization(struct test_run *tr, const char *argv0) +{ + struct timeval tv; + const char *basedir; + const char *search_path; + const char *last_component; + char *argv0_copy, *path_copy, *path_element; + char test_name[NAME_MAX]; + + if (tr->tr_name == NULL) { + /* Per POSIX, basename(3) can modify its argument. */ + argv0_copy = strdup(argv0); + last_component = basename(argv0_copy); + + if (gettimeofday(&tv, NULL)) + return (false); + + (void) snprintf(test_name, sizeof(test_name), "%s+%ld%ld", + last_component, (long) tv.tv_sec, (long) tv.tv_usec); + + tr->tr_name = strdup(test_name); + + free(argv0_copy); + } + + /* + * Select a base directory, if one was not specified. + */ + if (tr->tr_runtime_base_directory == NULL) { + basedir = getenv(TEST_TMPDIR_ENV_VAR); + if (basedir == NULL) + basedir = getenv(SYSTEM_TMPDIR_ENV_VAR); + if (basedir == NULL) + basedir = "/tmp"; + tr->tr_runtime_base_directory = realpath(basedir, NULL); + if (tr->tr_runtime_base_directory == NULL) + err(1, "realpath(%s) failed", basedir); + } + + /* + * Add the search paths specified by the environment variable + * 'TEST_PATH' to the end of the search list. + */ + if ((search_path = getenv(TEST_SEARCH_PATH_ENV_VAR)) != NULL && + *search_path != '\0') { + path_copy = strdup(search_path); + path_element = strtok(path_copy, ":"); + do { + if (!test_driver_add_search_path(tr, path_element)) + warnx("in environment variable \"%s\": path " + "\"%s\" does not name a directory.", + TEST_SEARCH_PATH_ENV_VAR, path_element); + } while ((path_element = strtok(NULL, ":")) != NULL); + } + + return (true); +} + +/* + * Helper: return true if the passed in path names a directory, or false + * otherwise. + */ +bool +test_driver_is_directory(const char *path) +{ + struct stat sb; + if (stat(path, &sb) != 0) + return false; + return S_ISDIR(sb.st_mode); +} diff --git a/test/libtest/driver/driver.h b/test/libtest/driver/driver.h new file mode 100644 index 000000000000..5ae5cfd81246 --- /dev/null +++ b/test/libtest/driver/driver.h @@ -0,0 +1,206 @@ +/*- + * Copyright (c) 2018,2019 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 + * in this position and unchanged. + * 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(S) ``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(S) 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. + */ + +#ifndef _LIBTEST_DRIVER_H_ +#define _LIBTEST_DRIVER_H_ + +#include + +#include +#include + +#include "_elftc.h" + +#include "test.h" + +#define TEST_SEARCH_PATH_ENV_VAR "TEST_PATH" +#define TEST_TMPDIR_ENV_VAR "TEST_TMPDIR" + +/* + * Run time data strucrures. + */ + +/* The completion status for a test run */ +enum test_run_status { + /* + * All test cases were successfully invoked, and all their contained + * test purposes passed. + */ + TR_STATUS_PASS = 0, + + /* + * All test cases were successfully invoked but at least one test + * function reported a failure. + */ + TR_STATUS_FAIL = 1, + + /* + * At least one test case reported an error during its set up or tear + * down phase. + */ + TR_STATUS_ERROR = 2 +}; + +/* + * The 'style' of the run determines the manner in which the test + * executable reports test status. + */ +enum test_run_style { + /* Libtest semantics. */ + TR_STYLE_LIBTEST, + + /* + * Be compatible with the Test Anything Protocol + * (http://testanything.org/). + */ + TR_STYLE_TAP, + + /* Be compatible with NetBSD ATF(9). */ + TR_STYLE_ATF +}; + +/* + * Structures used for selecting tests. + */ +struct test_function_selector { + const struct test_function_descriptor *tfs_descriptor; + + STAILQ_ENTRY(test_function_selector) tfs_next; + int tfs_is_selected; +}; + +STAILQ_HEAD(test_function_selector_list, test_function_selector); + +struct test_case_selector { + const struct test_case_descriptor *tcs_descriptor; + STAILQ_ENTRY(test_case_selector) tcs_next; + struct test_function_selector_list tcs_functions; + int tcs_selected_count; +}; + +/* + * The action being requested of the test driver. + */ +enum test_run_action { + TEST_RUN_EXECUTE, /* Execute the selected tests. */ + TEST_RUN_LIST, /* Only list tests. */ +}; + +STAILQ_HEAD(test_case_selector_list, test_case_selector); + +/* + * Runtime directories to look up data files. + */ +struct test_search_path_entry { + char *tsp_directory; + STAILQ_ENTRY(test_search_path_entry) tsp_next; +}; + +STAILQ_HEAD(test_search_path_list, test_search_path_entry); + +/* + * Used to track flags that were explicity set on the command line. + */ +enum test_run_flags { + TRF_BASE_DIRECTORY = 1U << 0, + TRF_EXECUTION_TIME = 1U << 1, + TRF_ARTEFACT_ARCHIVE = 1U << 2, + TRF_NAME = 1U << 3, + TRF_SEARCH_PATH = 1U << 4, + TRF_EXECUTION_STYLE = 1U << 5, +}; + +/* + * Parameters for the run. + */ +struct test_run { + /* + * Flags tracking the options which were explicitly set. + * + * This field is a bitmask formed of 'enum test_run_flags' values. + */ + unsigned int tr_commandline_flags; + + /* What the test run should do. */ + enum test_run_action tr_action; + + /* The desired behavior of the test harness. */ + enum test_run_style tr_style; + + /* The desired verbosity level. */ + int tr_verbosity; + + /* An optional name assigned by the user for this test run. */ + char *tr_name; + + /* + * The absolute path to the directory under which the test is + * to be run. + * + * Each test case will be invoked in some subdirectory of this + * directory. + */ + char *tr_runtime_base_directory; + + /* + * The test timeout in seconds. + * + * A value of zero indicates that the test driver should wait + * indefinitely for tests. + */ + long tr_max_seconds_per_test; + + /* + * If not NULL, An absolute pathname to an archive that will hold + * the artefacts created by a test run. + */ + char *tr_artefact_archive; + + /* + * Directories to use when resolving non-absolute data file + * names. + */ + struct test_search_path_list tr_search_path; + + /* All tests selected for this run. */ + struct test_case_selector_list tr_test_cases; +}; + +#ifdef __cplusplus +extern "C" { +#endif +struct test_run *test_driver_allocate_run(void); +bool test_driver_add_search_path(struct test_run *, + const char *search_path); +void test_driver_free_run(struct test_run *); +bool test_driver_is_directory(const char *); +bool test_driver_finish_run_initialization(struct test_run *, + const char *argv0); +#ifdef __cplusplus +} +#endif + +#endif /* _LIBTEST_DRIVER_H_ */ diff --git a/test/libtest/driver/driver_main.c b/test/libtest/driver/driver_main.c new file mode 100644 index 000000000000..c43ccf52ca15 --- /dev/null +++ b/test/libtest/driver/driver_main.c @@ -0,0 +1,726 @@ +/*- + * Copyright (c) 2018, 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 + * in this position and unchanged. + * 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(S) ``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(S) 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. + */ + +/* + * This file defines a "main()" that invokes (or lists) the tests that were + * linked into the current executable. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_elftc.h" + +#include "test.h" +#include "test_case.h" + +#include "driver.h" + +#if defined(ELFTC_VCSID) +ELFTC_VCSID("$Id$"); +#endif + +enum selection_scope { + SCOPE_TEST_CASE = 0, /* c:STRING */ + SCOPE_TEST_FUNCTION, /* f:STRING */ + SCOPE_TAG, /* t:STRING */ +}; + +/* Selection list entry. */ +struct selection_option { + STAILQ_ENTRY(selection_option) so_next; + + /* The text to use for matching. */ + const char *so_pattern; + + /* + * Whether matched test and test cases should be selected + * (if false) or deselected (if true). + */ + bool so_select_tests; + + /* The kind of information to match. */ + enum selection_scope so_selection_scope; +}; + +/* All selection options specified. */ +STAILQ_HEAD(selection_option_list, selection_option); + +static struct selection_option * +parse_selection_option(const char *option) +{ + int scope_char; + bool select_tests; + enum selection_scope scope; + struct selection_option *so; + + scope_char = '\0'; + select_tests = true; + scope = SCOPE_TEST_CASE; + + /* Deselection patterns start with a '-'. */ + if (*option == '-') { + select_tests = false; + option++; + } + + /* + * If a scope was not specified, the selection scope defaults + * to SCOPE_TEST_CASE. + */ + if (strchr(option, ':') == NULL) + scope_char = 'c'; + else { + scope_char = *option++; + if (*option != ':') + return (NULL); + option++; /* Skip over the ':'. */ + } + + if (*option == '\0') + return (NULL); + + switch (scope_char) { + case 'c': + scope = SCOPE_TEST_CASE; + break; + case 'f': + scope = SCOPE_TEST_FUNCTION; + break; + case 't': + scope = SCOPE_TAG; + break; + default: + return (NULL); + } + + so = calloc(1, sizeof(*so)); + so->so_pattern = option; + so->so_selection_scope = scope; + so->so_select_tests = select_tests; + + return (so); +} + +/* Test execution styles. */ +struct style_entry { + enum test_run_style se_style; + const char *se_name; +}; + +static const struct style_entry known_styles[] = { + { TR_STYLE_LIBTEST, "libtest" }, + { TR_STYLE_TAP, "tap" }, + { TR_STYLE_ATF, "atf" } +}; + +/* + * Parse a test run style. + * + * This function returns true if the run style was recognized, or + * false otherwise. + */ +static bool +parse_run_style(const char *option, enum test_run_style *run_style) +{ + size_t n; + + for (n = 0; n < sizeof(known_styles) / sizeof(known_styles[0]); n++) { + if (strcasecmp(option, known_styles[n].se_name) == 0) { + *run_style = known_styles[n].se_style; + return (true); + } + } + + return (false); +} + +/* + * Return the canonical spelling of a test execution style. + */ +static const char * +to_execution_style_name(enum test_run_style run_style) +{ + size_t n; + + for (n = 0; n < sizeof(known_styles) / sizeof(known_styles[0]); n++) { + if (known_styles[n].se_style == run_style) + return (known_styles[n].se_name); + } + + return (NULL); +} + +/* + * Parse a string value containing a positive integral number. + */ +static bool +parse_execution_time(const char *option, long *execution_time) { + char *end; + long value; + + if (option == NULL || *option == '\0') + return (false); + + value = strtol(option, &end, 10); + + /* Check for parse errors. */ + if (*end != '\0') + return (false); + + /* Reject negative numbers. */ + if (value < 0) + return (false); + + /* Check for overflows during parsing. */ + if (value == LONG_MAX && errno == ERANGE) + return (false); + + *execution_time = value; + + return (true); +} + +/* + * Match the names of test cases. + * + * In the event of a match, then the selection state specifed in + * 'option' is applied to all the test functions in the test case. + */ +static void +match_test_cases(struct selection_option *option, + struct test_case_selector *tcs) +{ + const struct test_case_descriptor *tcd; + struct test_function_selector *tfs; + + tcd = tcs->tcs_descriptor; + + if (fnmatch(option->so_pattern, tcd->tc_name, 0)) + return; + + STAILQ_FOREACH(tfs, &tcs->tcs_functions, tfs_next) + tfs->tfs_is_selected = option->so_select_tests; +} + +/* + * Match the names of test functions. + */ +static void +match_test_functions(struct selection_option *option, + struct test_case_selector *tcs) +{ + struct test_function_selector *tfs; + const struct test_function_descriptor *tfd; + + STAILQ_FOREACH(tfs, &tcs->tcs_functions, tfs_next) { + tfd = tfs->tfs_descriptor; + + if (fnmatch(option->so_pattern, tfd->tf_name, 0)) + continue; + + tfs->tfs_is_selected = option->so_select_tests; + } +} + +/* + * Helper: returns true if the specified text matches any of the + * entries in the array 'tags'. + */ +static bool +match_tags_helper(const char *pattern, const char *tags[]) +{ + const char **tag; + + if (!tags) + return (false); + + for (tag = tags; *tag && **tag != '\0'; tag++) { + if (!fnmatch(pattern, *tag, 0)) + return (true); + } + + return (false); +} + +/* + * Match tags. + * + * Matches against test case tags apply to all the test + * functions in the test case. + * + * Matches against test function tags apply to the matched + * test function only. + */ +static void +match_tags(struct selection_option *option, + struct test_case_selector *tcs) +{ + const struct test_case_descriptor *tcd; + const struct test_function_descriptor *tfd; + struct test_function_selector *tfs; + + tcd = tcs->tcs_descriptor; + + /* + * If the tag in the option matches a tag associated with + * a test case, then we set all of the test case's functions + * to the specified selection state. + */ + if (match_tags_helper(option->so_pattern, tcd->tc_tags)) { + STAILQ_FOREACH(tfs, &tcs->tcs_functions, tfs_next) + tfs->tfs_is_selected = option->so_select_tests; + return; + } + + /* + * Otherwise, check the tag against the tags for each function + * in the test case and set the selection state of each matched + * function. + */ + STAILQ_FOREACH(tfs, &tcs->tcs_functions, tfs_next) { + tfd = tfs->tfs_descriptor; + if (match_tags_helper(option->so_pattern, tfd->tf_tags)) + tfs->tfs_is_selected = option->so_select_tests; + } +} + +/* + * Add the selected tests to the test run. + * + * The memory used by the options list is returned to the system when this + * function completes. + */ +static void +select_tests(struct test_run *tr, + struct selection_option_list *selections) +{ + int i, j; + struct selection_option *selection; + const struct test_case_descriptor *tcd; + struct test_case_selector *tcs; + struct test_function_selector *tfs; + bool default_selection_state; + int selected_count; + + default_selection_state = STAILQ_EMPTY(selections); + + /* + * Set up runtime descriptors. + */ + for (i = 0; i < test_case_count; i++) { + if ((tcs = calloc(1, sizeof(*tcs))) == NULL) + err(EX_OSERR, "cannot allocate a test-case selector"); + STAILQ_INSERT_TAIL(&tr->tr_test_cases, tcs, tcs_next); + STAILQ_INIT(&tcs->tcs_functions); + + tcd = &test_cases[i]; + + tcs->tcs_descriptor = tcd; + + for (j = 0; j < tcd->tc_count; j++) { + if ((tfs = calloc(1, sizeof(*tfs))) == NULL) + err(EX_OSERR, "cannot allocate a test " + "function selector"); + STAILQ_INSERT_TAIL(&tcs->tcs_functions, tfs, tfs_next); + + tfs->tfs_descriptor = tcd->tc_tests + j; + tfs->tfs_is_selected = default_selection_state; + } + } + + /* + * Set or reset the selection state based on the options. + */ + STAILQ_FOREACH(selection, selections, so_next) { + STAILQ_FOREACH(tcs, &tr->tr_test_cases, tcs_next) { + switch (selection->so_selection_scope) { + case SCOPE_TEST_CASE: + match_test_cases(selection, tcs); + break; + case SCOPE_TEST_FUNCTION: + match_test_functions(selection, tcs); + break; + case SCOPE_TAG: + match_tags(selection, tcs); + break; + } + } + } + + /* + * Determine the count of tests selected, for each test case. + */ + STAILQ_FOREACH(tcs, &tr->tr_test_cases, tcs_next) { + selected_count = 0; + STAILQ_FOREACH(tfs, &tcs->tcs_functions, tfs_next) + selected_count += tfs->tfs_is_selected; + tcs->tcs_selected_count = selected_count; + } + + /* Free up the selection list. */ + while (!STAILQ_EMPTY(selections)) { + selection = STAILQ_FIRST(selections); + STAILQ_REMOVE_HEAD(selections, so_next); + free(selection); + } +} + +/* + * Translate a file name to absolute form. + * + * The caller needs to free the returned pointer. + */ +static char * +to_absolute_path(const char *filename) +{ + size_t space_needed; + char *absolute_path; + char current_directory[PATH_MAX]; + + if (filename == NULL || *filename == '\0') + return (NULL); + if (*filename == '/') + return strdup(filename); + + if (getcwd(current_directory, sizeof(current_directory)) == NULL) + err(1, "getcwd failed"); + + /* Reserve space for the slash separator and the trailing NUL. */ + space_needed = strlen(current_directory) + strlen(filename) + 2; + if ((absolute_path = malloc(space_needed)) == NULL) + err(1, "malloc failed"); + if (snprintf(absolute_path, space_needed, "%s/%s", current_directory, + filename) != (int) (space_needed - 1)) + err(1, "snprintf failed"); + return (absolute_path); +} + + +/* + * Display run parameters. + */ + +#define FIELD_NAME_WIDTH 24 +#define INFOLINE(NAME, FLAG, FORMAT, ...) do { \ + printf("I %c %-*s " FORMAT, \ + (FLAG) ? '!' : '.', \ + FIELD_NAME_WIDTH, NAME, __VA_ARGS__); \ + } while (0) + +static void +show_run_header(const struct test_run *tr) +{ + time_t start_time; + struct test_search_path_entry *path_entry; + + if (tr->tr_verbosity == 0) + return; + + INFOLINE("test-run-name", tr->tr_commandline_flags & TRF_NAME, + "%s\n", tr->tr_name); + + INFOLINE("test-execution-style", + tr->tr_commandline_flags & TRF_EXECUTION_STYLE, + "%s\n", to_execution_style_name(tr->tr_style)); + + if (!STAILQ_EMPTY(&tr->tr_search_path)) { + INFOLINE("test-search-path", + tr->tr_commandline_flags & TRF_SEARCH_PATH, + "%c", '['); + STAILQ_FOREACH(path_entry, &tr->tr_search_path, tsp_next) { + printf(" %s", path_entry->tsp_directory); + } + printf(" ]\n"); + } + + INFOLINE("test-run-base-directory", + tr->tr_commandline_flags & TRF_BASE_DIRECTORY, + "%s\n", tr->tr_runtime_base_directory); + + if (tr->tr_artefact_archive) { + INFOLINE("test-artefact-archive", + tr->tr_commandline_flags & TRF_ARTEFACT_ARCHIVE, + "%s\n", tr->tr_artefact_archive); + } + + printf("I %c %-*s ", + tr->tr_commandline_flags & TRF_EXECUTION_TIME ? '=' : '.', + FIELD_NAME_WIDTH, "test-execution-time"); + if (tr->tr_max_seconds_per_test == 0) + printf("unlimited\n"); + else + printf("%lu\n", tr->tr_max_seconds_per_test); + + printf("I %% %-*s %d\n", FIELD_NAME_WIDTH, "test-case-count", + test_case_count); + + if (tr->tr_action == TEST_RUN_EXECUTE) { + start_time = time(NULL); + printf("I %% %-*s %s", FIELD_NAME_WIDTH, + "test-run-start-time", ctime(&start_time)); + } +} + +static void +show_run_trailer(const struct test_run *tr) +{ + time_t end_time; + + if (tr->tr_verbosity == 0) + return; + + if (tr->tr_action == TEST_RUN_EXECUTE) { + end_time = time(NULL); + printf("I %% %-*s %s", FIELD_NAME_WIDTH, "test-run-end-time", + asctime(localtime(&end_time))); + } +} + +#undef INFOLINE +#undef FIELD_HEADER_WIDTH + +/* + * Helper: returns a character indicating the selection status for + * a test case. This character is as follows: + * + * - "*" all test functions in the test case were selected. + * - "+" some test functions in the test case were selected. + * - "-" no test functions from the test case were selected. + */ +static int +get_test_case_status(const struct test_case_selector *tcs) +{ + if (tcs->tcs_selected_count == 0) + return '-'; + if (tcs->tcs_selected_count == tcs->tcs_descriptor->tc_count) + return '*'; + return '?'; +} + +/* + * Helper: print out a comma-separated list of tags. + */ +static void +show_tags(int indent, const char *tags[]) +{ + const char **tag; + + printf("%*c: ", indent, ' '); + for (tag = tags; *tag && **tag != '\0';) { + printf("%s", *tag++); + if (*tag && **tag != '\0') + printf(","); + } + printf("\n"); +} + +/* + * Display a test case descriptor. + */ +static void +show_test_case(struct test_run *tr, const struct test_case_selector *tcs) +{ + const struct test_case_descriptor *tcd; + int prefix_char; + + prefix_char = get_test_case_status(tcs); + tcd = tcs->tcs_descriptor; + + printf("C %c %s\n", prefix_char, tcd->tc_name); + + if (tr->tr_verbosity > 0 && tcd->tc_tags != NULL) + show_tags(2, tcd->tc_tags); + + if (tr->tr_verbosity > 1 && tcd->tc_description) + printf(" & %s\n", tcd->tc_description); +} + +static void +show_test_function(struct test_run *tr, + const struct test_function_selector *tfs) +{ + const struct test_function_descriptor *tfd; + int selection_char; + + selection_char = tfs->tfs_is_selected ? '*' : '-'; + tfd = tfs->tfs_descriptor; + + printf(" F %c %s\n", selection_char, tfd->tf_name); + + if (tr->tr_verbosity > 0 && tfd->tf_tags != NULL) + show_tags(4, tfd->tf_tags); + + if (tr->tr_verbosity > 1 && tfd->tf_description) + printf(" & %s\n", tfd->tf_description); +} + +static int +show_listing(struct test_run *tr) +{ + const struct test_case_selector *tcs; + const struct test_function_selector *tfs; + + STAILQ_FOREACH(tcs, &tr->tr_test_cases, tcs_next) { + show_test_case(tr, tcs); + STAILQ_FOREACH(tfs, &tcs->tcs_functions, tfs_next) + show_test_function(tr, tfs); + } + + return (EXIT_SUCCESS); +} + +int +main(int argc, char **argv) +{ + struct test_run *tr; + int exit_code, option; + enum test_run_style run_style; + struct selection_option *selector; + struct selection_option_list selections = + STAILQ_HEAD_INITIALIZER(selections); + + tr = test_driver_allocate_run(); + + /* Parse arguments. */ + while ((option = getopt(argc, argv, ":R:T:c:ln:p:s:t:v")) != -1) { + switch (option) { + case 'R': /* Test runtime directory. */ + if (!test_driver_is_directory(optarg)) + errx(EX_USAGE, "option -%c: argument \"%s\" " + "does not name a directory.", option, + optarg); + tr->tr_runtime_base_directory = realpath(optarg, NULL); + if (tr->tr_runtime_base_directory == NULL) + err(1, "realpath failed for \"%s\"", optarg); + tr->tr_commandline_flags |= TRF_BASE_DIRECTORY; + break; + case 'T': /* Max execution time for a test function. */ + if (!parse_execution_time( + optarg, &tr->tr_max_seconds_per_test)) + errx(EX_USAGE, "option -%c: argument \"%s\" " + "is not a valid execution time value.", + option, optarg); + tr->tr_commandline_flags |= TRF_EXECUTION_TIME; + break; + case 'c': /* The archive holding artefacts. */ + tr->tr_artefact_archive = to_absolute_path(optarg); + tr->tr_commandline_flags |= TRF_ARTEFACT_ARCHIVE; + break; + case 'l': /* List matching tests. */ + tr->tr_action = TEST_RUN_LIST; + break; + case 'n': /* Test run name. */ + if (tr->tr_name) + free(tr->tr_name); + tr->tr_name = strdup(optarg); + tr->tr_commandline_flags |= TRF_NAME; + break; + case 'p': /* Add a search path entry. */ + if (!test_driver_add_search_path(tr, optarg)) + errx(EX_USAGE, "option -%c: argument \"%s\" " + "does not name a directory.", option, + optarg); + tr->tr_commandline_flags |= TRF_SEARCH_PATH; + break; + case 's': /* Test execution style. */ + if (!parse_run_style(optarg, &run_style)) + errx(EX_USAGE, "option -%c: argument \"%s\" " + "is not a supported test execution style.", + option, optarg); + tr->tr_style = run_style; + tr->tr_commandline_flags |= TRF_EXECUTION_STYLE; + break; + case 't': /* Test selection option. */ + if ((selector = parse_selection_option(optarg)) == NULL) + errx(EX_USAGE, "option -%c: argument \"%s\" " + "is not a valid selection pattern.", + option, optarg); + STAILQ_INSERT_TAIL(&selections, selector, so_next); + break; + case 'v': + tr->tr_verbosity++; + break; + case ':': + errx(EX_USAGE, + "ERROR: option -%c requires an argument.", optopt); + break; + case '?': + errx(EX_USAGE, + "ERROR: unrecognized option -%c", optopt); + break; + default: + errx(EX_USAGE, "ERROR: unspecified error."); + break; + } + } + + /* + * Set unset fields of the test run descriptor to their + * defaults. + */ + if (!test_driver_finish_run_initialization(tr, argv[0])) + err(EX_OSERR, "cannot initialize test driver"); + + /* Choose tests and test cases to act upon. */ + select_tests(tr, &selections); + + assert(STAILQ_EMPTY(&selections)); + + show_run_header(tr); + + /* Perform the requested action. */ + switch (tr->tr_action) { + case TEST_RUN_LIST: + exit_code = show_listing(tr); + break; + + case TEST_RUN_EXECUTE: + default: + /* Not yet implemented. */ + exit_code = EX_UNAVAILABLE; + } + + show_run_trailer(tr); + + test_driver_free_run(tr); + + exit(exit_code); +} diff --git a/test/libtest/driver/test_driver.1 b/test/libtest/driver/test_driver.1 new file mode 100644 index 000000000000..a987002e8c67 --- /dev/null +++ b/test/libtest/driver/test_driver.1 @@ -0,0 +1,308 @@ +.\" Copyright (c) 2019 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. +.\" +.\" $Id$ +.\" +.Dd April 02, 2019 +.Dt TEST-DRIVER 1 +.Os +.Sh NAME +.Nm test-driver +.Nd scaffolding for executing +.Xr test 3 +based tests from the command-line +.Sh SYNOPSIS +.Nm test-executable +.Op Fl c Ar artefact-archive-name +.Op Fl l +.Op Fl n Ar run-name +.Op Fl p Ar search-path-directory +.Op Fl R Ar runtime-base-directory +.Op Fl s Ar execution-style +.Op Fl t Ar test-selector +.Op Fl T Ar seconds +.Op Fl v +.Sh DESCRIPTION +The +.Nm +library provides a +.Fn main +function that will execute the +.Xr test 3 +based tests in an executable according to the options specified +on the command-line. +The +.Nm +library usually used in conjunction with code generated by the +.Xr make-test-scaffolding 1 +utility. +.Pp +Test executables built using +.Nm +recognize the following command-line options: +.Bl -tag -width indent +.It Fl c Ar archive-name +If this option is specified, then the +.Nm +provided scaffolding will copy test outputs and other artefacts from +the test run to the archive named by argument +.Ar archive-name . +The format of the archive is specified by the path name suffix of the +artefact name. +The supported output formats are those supported by +.Xr libarchive 3 . +.It Fl l +If this option is specified, then the +.Nm +utility will list the selected tests and exit. +.It Fl n Ar run-name +Use the specified test run name in status messages and to name +any files and directories created during the test run. +If this option is not specified, then the base name of the test +executable is used. +.It Fl p Ar search-path-directory +Add the argument +.Ar search-path-directory +to the list of directories searched for by +.Xr test 3 +utility functions. +.It Fl R Ar runtime-base-directory +Set the runtime base directory to the directory specified by the +argument +.Ar runtime-base-directory . +Tests execute with their current directory set to a subdirectory +within this directory. +The path specified by argument +.Ar runtime-base-directory +must exist, and must name a directory. +.Pp +If this option is not specified, then the +.Ev TEST_TMPDIR +environment variable will be examined. +If set to a non-empty value, then its value will be used. +Otherwise, the value of the +.Ev TMPDIR +environment variable will be used, if non-empty. +If neither of the environment variables +.Ev TEST_TMPDIR +and +.Ev TMPDIR +contain a non-empty value, then the path +.Dq Pa /tmp +will be used. +.It Fl s Ar execution-style +Set the desired execution style to that specified by argument +.Ar execution-style . +Legal values for +.Ar execution-style +are: +.Bl -tag -width indent -compact +.It Li atf +Be compatible with +.Nx +.Xr atf 9 . +.It Li tap +Be compatible with TAP +.Pq Test Anything Protocol . +.It Li test +Be compatible with libtest (this test framework). +.El +The default is to use libtest semantics. +.It Fl t Ar test-selector +Select (or deselect) tests to execute according to the argument +.Ar test-selector . +.Pp +Test selectors are specified using the following syntax: +.Bl -tag -compact -width indent +.It Xo +.Op Li - Ns +.Li c : Ns Ar pattern +.Xc +Select test cases whose names match +.Ar pattern . +Selecting a test case will cause all of its contained +test functions to be selected. +.It Xo +.Op Li - Ns +.Li f : Ns Ar pattern +.Xc +Select test functions whose names match +.Ar pattern . +.It Xo +.Op Li - Ns +.Li t : Ns Ar pattern +.Xc +Select the test cases and test functions associated with +tags matching +.Ar pattern . +.It Xo +.Op Li - Ns +.Ar pattern +.Xc +If the +.Li c , +.Li f +or +.Li t +qualifiers were not specified, then the pattern is matched +against the names of test cases. +.El +The +.Ar pattern +fields of test selectors use shell wildcard syntax, as implemented by +.Xr fnmatch 3 . +.Pp +If no test selectors are specified then all the tests present in +the test executable will be run. +Otherwise, the test selectors specified are processed in the +order specified on the command line. +.Pp +A test selector that does not start with a +.Dq Li - +will add the entries that it matches to the currently selected list +of tests. +A test selector that starts with a +.Dq Li - +will remove the entries that it matches from the currently selected list +of tests. +.Pp +If at least one test selector was specified, and if the result of +applying the specified test selectors was an empty list +of tests, then the +.Nm +library will exit with an error message. +.It Fl T Ar seconds +Set the timeout for individual tests to +.Ar seconds . +If a test function fails to return with the specified number of seconds +then it is treated as having failed. +The default is to wait indefinitely for the test function to complete. +.It Fl v +Increase verbosity level by 1. +The default verbosity level is 0. +.El +.Ss Link-time Pre-requisites +The +.Nm +library expects the following symbols to be present in the +test executable it is linked with: +.Pp +.Bl -tag -width indent -compact +.It Xo +.Vt struct test_case_descriptor +.Va test_cases Ns [] +.Xc +An array of test cases descriptors. +Test case descriptors described by +.Xr test_case 5 . +.It Xo +.Vt int +.Va test_case_count +.Xc +The number of entries in the +.Va test_cases +array. +.El +.Ss Test Execution +At start up, the +.Fn main +function provided by +.Nm +will select tests (and test cases) to execute, based on the test +selection options specified. +.Pp +For each selected test case, test execution proceeds as follows: +.Bl -enum -compact +.It +The runtime directory for the test case is created. +.It +The test process forks, with test execution continuing in the +child. +.It +.Pq Child +The current directory of the process is changed to the runtime +directory. +.It +.Pq Child +The test case set up function is then executed. +If this function returns an error then test case execution is +aborted. +.It +.Pq Child +Each selected test function in the test case is then executed and +its status is output to stdout (or stderr) according to the test +execution style selected. +.It +.Pq Child +The test case tear down function is then executed. +.It +If test artefacts need to be preserved, then these are +copied to the specified archive. +.It +The test's runtime directory is then deleted. +.El +.Pp +After all test cases have been attempted, the +.Fn main +function exits with the exit code appropriate for the +test execution style selected. +.Sh EXAMPLES +To run all tests in the binary named +.Pa tc_example , +copying test artefacts to a +.Xr cpio 1 +archive named +.Pa /tmp/tc_example.cpio , +use: +.Bd -literal -offset indent +tc_example -c /tmp/tc_example.cpio +.Ed +.Pp +To execute tests in the test case +.Dq tc1 +alone, use: +.Bd -literal -offset indent +tc_example -t 'c:tc1' +.Ed +.Pp +To execute tests in the test case +.Dq tc1 +but not the test functions associated with tag +.Li tag1 , +use: +.Bd -literal -offset indent +tc_example -t 'c:tc1' -t '-t:tag1' +.Ed +.Sh DIAGNOSTICS +Test programs built with the +.Nm +library will exit with an exit code of 0 if all of the selected tests +passed when run, and with a non-zero exit code if an error +occurred during test execution. +.Sh SEE ALSO +.Xr make-test-scaffolding 1 , +.Xr fnmatch 3 , +.Xr libarchive 3 , +.Xr test 3 , +.Xr test_case 5 diff --git a/test/libtest/examples/minimal_example.c b/test/libtest/examples/minimal_example.c index 4ad08b4bce06..3bc76fe66ea9 100644 --- a/test/libtest/examples/minimal_example.c +++ b/test/libtest/examples/minimal_example.c @@ -40,11 +40,17 @@ #include "test.h" +/* + * Function prototypes. + */ +enum test_result tf_helloworld(test_case_state); + /* * Function names prefixed with 'tf_' name test functions. */ enum test_result -tf_helloworld(testcase_state state) +tf_helloworld(test_case_state state) { + (void) state; return (TEST_PASS); } diff --git a/test/libtest/examples/simple_example.c b/test/libtest/examples/simple_example.c index 6a4f6697eb51..6d72f65dd451 100644 --- a/test/libtest/examples/simple_example.c +++ b/test/libtest/examples/simple_example.c @@ -30,6 +30,14 @@ #include "test.h" +/* + * Function prototypes. + */ +enum test_case_status tc_setup_helloworld(test_case_state *); +enum test_case_status tc_teardown_helloworld(test_case_state); +enum test_result tf_helloworld_sayhello(test_case_state); +enum test_result tf_helloworld_saygoodbye(test_case_state); + /* * This source defines a single test case named 'helloworld' containing a * single test function named 'sayhello' contained in that test case. At @@ -64,35 +72,37 @@ /* * A symbol name prefixed with 'tc_description_' contains a - * test case description. The TESTCASE_DESCRIPTION macro offers + * test case description. The TEST_CASE_DESCRIPTION macro offers * a convenient way to define such symbols. In the case of the * symbol below, the test case named is 'helloworld'. */ -TESTCASE_DESCRIPTION(helloworld) = "A description for a test case."; +TEST_CASE_DESCRIPTION(helloworld) = "A description for a test case."; /* * Function names prefixed with 'tc_setup_' are assumed to be test * case set up functions. */ -enum testcase_status -tc_setup_helloworld(testcase_state *state) +enum test_case_status +tc_setup_helloworld(test_case_state *state) { - return (TESTCASE_OK); + (void) state; + return (TEST_CASE_OK); } /* * Function names prefixed with 'tc_teardown_' are assumed to be test * case tear down functions. */ -enum testcase_status -tc_teardown_helloworld(testcase_state state) +enum test_case_status +tc_teardown_helloworld(test_case_state state) { - return (TESTCASE_OK); + (void) state; + return (TEST_CASE_OK); } /* * Names prefixed with 'tc_tags_' denote the tags associated with test - * cases. The TESTCASE_TAGS macro offers a convenient way to define such + * cases. The TESTC_ASE_TAGS macro offers a convenient way to define such * symbols. * * In the example below, all test functions belonging to the test case @@ -100,7 +110,7 @@ tc_teardown_helloworld(testcase_state state) * * Tags lists are terminated by a NULL entry. */ -TESTCASE_TAGS(helloworld) = { +TEST_CASE_TAGS(helloworld) = { "tag1", "tag2", NULL @@ -110,8 +120,16 @@ TESTCASE_TAGS(helloworld) = { * Function names prefixed with 'tf_' name test functions. */ enum test_result -tf_helloworld_sayhello(testcase_state state) +tf_helloworld_sayhello(test_case_state state) { + (void) state; + return (TEST_PASS); +} + +enum test_result +tf_helloworld_saygoodbye(test_case_state state) +{ + (void) state; return (TEST_PASS); } @@ -126,6 +144,9 @@ tf_helloworld_sayhello(testcase_state state) TEST_DESCRIPTION(helloworld_sayhello) = "A description for the test function 'tf_helloworld_sayhello'."; +TEST_DESCRIPTION(helloworld_saygoodbye) = + "A description for the test function 'tf_helloworld_saygoodbye'."; + /* * Names prefixed by 'tf_tags_' contain the tags associated with * test functions. @@ -143,3 +164,8 @@ test_tags tf_tags_helloworld_sayhello = { "tag4", NULL }; + +test_tags tf_tags_helloworld_saygoodbye = { + "tag5", + NULL +}; diff --git a/test/libtest/lib/Makefile b/test/libtest/lib/Makefile index 7359ddd76a6c..e36d8fa072ba 100644 --- a/test/libtest/lib/Makefile +++ b/test/libtest/lib/Makefile @@ -4,10 +4,9 @@ TOP= ../../.. LIB= test -SRCS= test.c \ - test_runner.c +SRCS= test.c -INCS= test.h +INCS= test.h test_case.h WARNS?= 6 diff --git a/test/libtest/lib/test.3 b/test/libtest/lib/test.3 index c7045b4039c4..e170c181595a 100644 --- a/test/libtest/lib/test.3 +++ b/test/libtest/lib/test.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2018, Joseph Koshy. +.\" Copyright (c) 2018,2019 Joseph Koshy. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -24,7 +24,7 @@ .\" .\" $Id$ .\" -.Dd December 25, 2018 +.Dd January 21, 2019 .Dt TEST 3 .Os .Sh NAME @@ -34,20 +34,20 @@ .Lb libtest .Sh SYNOPSIS .In test.h -.Ft enum testcase_status -.Fn testcase_setup "testcase_state *state" -.Ft enum testcase_status -.Fn testcase_teardown "testcase_state state" +.Ft enum test_case_status +.Fn test_case_setup "test_case_state *state" +.Ft enum test_case_status +.Fn test_case_teardown "test_case_state state" .Ft enum test_result -.Fn test_function "testcase_state state" +.Fn test_function "test_case_state state" .Vt "const char" .Va test_description [] ; .Vt "const char *" .Va test_tags [] ; .Vt "const char" -.Va testcase_description [] ; +.Va test_case_description [] ; .Vt "const char *" -.Va testcase_tags [] ; +.Va test_case_tags [] ; .Sh DESCRIPTION The .Lb libtest @@ -76,7 +76,7 @@ If specified, this set up function would be invoked prior to any test function contained in the test case. The set up function can allocate and initialize test-specific state, to be passed to test functions. -If no set up function is specified for the test case, a default no-op +If no set up function is specified for the test case, a default (no-op) function will be supplied. .It An optional test case tear down function. diff --git a/test/libtest/lib/test.h b/test/libtest/lib/test.h index 86f91463993b..6928f867a6f4 100644 --- a/test/libtest/lib/test.h +++ b/test/libtest/lib/test.h @@ -44,60 +44,59 @@ enum test_result { /* * The return values from test case set up and tear down functions. * - * - TESTCASE_OK : The set up or tear down function was successful. - * - TESTCASE_ERROR : Set up or tear down actions could not be completed. + * - TEST_CASE_OK : The set up or tear down function was successful. + * - TEST_CASE_ERROR : Set up or tear down actions could not be completed. * - * If a test case set up function returns TESTCASE_ERROR then: + * If a test case set up function returns TEST_CASE_ERROR then: * - The test functions in the test case will not be run. * - The test case's tear down function will not be invoked. * - The test run as a whole will be treated as being in error. * - * If a test case tear down function returns a TESTCASE_ERROR, then + * If a test case tear down function returns a TEST_CASE_ERROR, then * the test run as a whole be treated as being in error. */ -enum testcase_status { - TESTCASE_OK = 0, - TESTCASE_ERROR = 1 +enum test_case_status { + TEST_CASE_OK = 0, + TEST_CASE_ERROR = 1 }; /* - * A testcase_state denotes resources that are shared by the test - * functions that are part of a test case. A testcase_state is allocated - * by the set up function for a test case. Conversely the test case's - * tear down function is responsible for deallocating the resources - * allocated by the set up function. + * A 'test_case_state' is a handle to resources shared by the test functions + * that make up a test case. A test_case_state is allocated by the test case + * set up function and is deallocated by the test case tear down function. * - * The test(3) framework treats a testcase_state as an opaque value. + * The test(3) framework treats a 'test_case_state' as an opaque value. */ -typedef void *testcase_state; +typedef void *test_case_state; /* * Test case and test function descriptions, and convenience macros * to define these. */ -typedef const char testcase_description[]; +typedef const char test_case_description[]; -#if !defined(TEST_DESCRIPTION) -#define TEST_DESCRIPTION(NAME) test_description tf_description_##NAME +#if !defined(TEST_CASE_DESCRIPTION) +#define TEST_CASE_DESCRIPTION(NAME) test_case_description tc_description_##NAME #endif typedef const char test_description[]; -#if !defined(TESTCASE_DESCRIPTION) -#define TESTCASE_DESCRIPTION(NAME) testcase_description tc_description_##NAME +#if !defined(TEST_DESCRIPTION) +#define TEST_DESCRIPTION(NAME) test_description tf_description_##NAME #endif /* * Test case and test function tags, and convenience macros to define * these. */ -typedef const char *testcase_tags[]; +typedef const char *test_case_tags[]; -#if !defined(TESTCASE_TAGS) -#define TESTCASE_TAGS(NAME) testcase_tags tc_tags_##NAME +#if !defined(TEST_CASE_TAGS) +#define TEST_CASE_TAGS(NAME) test_case_tags tc_tags_##NAME #endif typedef const char *test_tags[]; + #if !defined(TEST_TAGS) #define TEST_TAGS(NAME) test_tags tf_tags_##NAME #endif @@ -108,7 +107,7 @@ typedef const char *test_tags[]; * If defined for a test case, this function will be called prior to * the execution of an of the test functions within the test cae. Test * case execution will be aborted if the function returns any value other - * than TESTCASE_OK. + * than TEST_CASE_OK. * * The function can set '*state' to a memory area holding test state to be * passed to test functions. @@ -116,8 +115,8 @@ typedef const char *test_tags[]; * If the test case does not define a set up function, then a default * no-op set up function will be used. */ -typedef enum testcase_status (test_case_setup_function) - (testcase_state *state); +typedef enum test_case_status test_case_setup_function( + test_case_state *state); /* * A test function. @@ -127,7 +126,7 @@ typedef enum testcase_status (test_case_setup_function) * its test succeeded or TEST_FAIL otherwise. In the event the test could * not be executed, it can return TEST_UNRESOLVED. */ -typedef enum test_result (test_function)(testcase_state state); +typedef enum test_result test_function(test_case_state state); /* * A test case tear down function. @@ -138,7 +137,8 @@ typedef enum test_result (test_function)(testcase_state state); * responsible for deallocating the resources that the set up function * had allocated. */ -typedef enum testcase_status (test_case_teardown_function)(testcase_state state); +typedef enum test_case_status test_case_teardown_function( + test_case_state state); #ifdef __cplusplus extern "C" { diff --git a/test/libtest/driver/test_main.c b/test/libtest/lib/test_case.h similarity index 58% rename from test/libtest/driver/test_main.c rename to test/libtest/lib/test_case.h index 90e46d9a5fe2..c40b15fb69a1 100644 --- a/test/libtest/driver/test_main.c +++ b/test/libtest/lib/test_case.h @@ -24,26 +24,35 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * This file defines a "main" that parses command-line arguments and invokes - * the selected test cases. - */ -#include +#ifndef _LIBTEST_TEST_CASE_H_ +#define _LIBTEST_TEST_CASE_H_ -#include -#include - -#include "_elftc.h" #include "test.h" -#include "test_runner.h" -ELFTC_VCSID("$Id$"); +/* + * These structures describe the test cases that are linked into a + * test executable. + */ -int -main(int argc, char **argv) -{ - (void) test_cases; - (void) argc; - (void) argv; - exit(0); -} +/* A single test function, with its associated tags and description. */ +struct test_function_descriptor { + const char *tf_name; /* Test name. */ + const char *tf_description; /* Test description. */ + const char **tf_tags; /* The tags for the test. */ + test_function *tf_func; /* The function to invoke. */ +}; + +/* A test case, with its associated tests. */ +struct test_case_descriptor { + const char *tc_name; /* Test case name. */ + const char *tc_description; /* Test case description. */ + const char **tc_tags; /* Any associated tags. */ + const struct test_function_descriptor *tc_tests; /* Contained tests. */ + const int tc_count; /* The number of tests. */ +}; + +/* All test cases linked into the test binary. */ +extern struct test_case_descriptor test_cases[]; +extern const int test_case_count; + +#endif /* _LIBTEST_TEST_CASE_H_ */ diff --git a/test/libtest/lib/test_runner.c b/test/libtest/lib/test_runner.c deleted file mode 100644 index 1366ff4a538e..000000000000 --- a/test/libtest/lib/test_runner.c +++ /dev/null @@ -1,31 +0,0 @@ -/*- - * Copyright (c) 2018, 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 - * in this position and unchanged. - * 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(S) ``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(S) 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. - */ - -/* - * An implementation of a test driver for test(3) tests. - */ - -/* To be implemented. */ diff --git a/test/libtest/lib/test_runner.h b/test/libtest/lib/test_runner.h deleted file mode 100644 index cbf00f29b44d..000000000000 --- a/test/libtest/lib/test_runner.h +++ /dev/null @@ -1,118 +0,0 @@ -/*- - * Copyright (c) 2018, 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 - * in this position and unchanged. - * 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(S) ``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(S) 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. - */ - -#ifndef _LIBTEST_TEST_RUNNER_H_ -#define _LIBTEST_TEST_RUNNER_H_ - -#include "test.h" - -/* - * These data structures and functions are used by test driver that - * execute tests. - */ - -/* - * The completion status for a test run: - * - * - TESTRUN_PASS : All test cases were successfully invoked and all test - * purposes in the test cases passed. - * - TESTRUN_FAIL : All test cases were successfully invoked but at least - * one test purpose reported a test failure. - * - TESTRUN_ERROR : At least one test case reported an error during its - * set up or tear down phase. - */ -enum testrun_status { - TESTRUN_PASS = 0, - TESTRUN_FAIL = 1, - TESTRUN_ERROR = 2 -}; - -/* - * A single test function, with its associated tags and description. - */ -struct test_descriptor { - const char *t_name; /* Test name. */ - const char *t_description; /* Test description. */ - const char **t_tags; /* Tags associated with the test. */ - test_function *t_func; /* The function to invoke. */ -}; - -/* - * A test case. - */ -struct test_case_descriptor { - const char *tc_name; /* Test case name. */ - const char *tc_description; /* Test case description. */ - const char **tc_tags; /* Any associated tags. */ - struct test_descriptor *tc_tests; /* The tests in this test case. */ -}; - -/* - * All test cases. - */ -extern struct test_case_descriptor test_cases[]; - -enum testrun_style { - /* Libtest semantics. */ - TESTRUN_STYLE_LIBTEST, - - /* - * Be compatible with the Test Anything Protocol - * (http://testanything.org/). - */ - TESTRUN_STYLE_TAP, - - /* Be compatible with NetBSD ATF(9). */ - TESTRUN_STYLE_ATF -}; - -/* - * Parameters for the run. - */ -struct test_run { - /* - * An optional name assigned by the user for this test run. - * - * This name is reported in test logs and is not interpreted - * by the test harness. - */ - char *testrun_name; - - /* The source directory for the run. */ - char *testrun_source_directory; - - /* The directory in which the test is executing. */ - char *testrun_test_directory; -}; - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef __cplusplus -} -#endif - -#endif /* _LIBTEST_TEST_RUNNER_H_ */ diff --git a/test/nm/ts/Makefile.tset b/test/nm/ts/Makefile.tset index ac5fd0ad4540..4b9ca8adb073 100644 --- a/test/nm/ts/Makefile.tset +++ b/test/nm/ts/Makefile.tset @@ -1,4 +1,4 @@ -# $Id: Makefile.tset 2085 2011-10-27 05:06:47Z jkoshy $ +# $Id: Makefile.tset 3719 2019-03-23 08:30:55Z jkoshy $ NM_EXEC?= ${.CURDIR}/../../../../nm/nm CSTD?= iso9899:1999 @@ -16,7 +16,7 @@ CLEANFILES+= test_nm.c .endif .endfor -.if !exists(${TS_DATA:R}) +.if !empty(${TS_DATA:R}) && !exists(${TS_DATA:R}) ${TS_DATA}: uudecode ${TS_DATA}.uu .endif diff --git a/test/tet/patches/configure.patch b/test/tet/patches/configure.patch index 9fb02049c545..c2307504cacf 100644 --- a/test/tet/patches/configure.patch +++ b/test/tet/patches/configure.patch @@ -1,13 +1,13 @@ -: $Id: configure.patch 2204 2011-11-24 05:23:42Z jkoshy $ +: $Id: configure.patch 3721 2019-03-23 09:04:45Z jkoshy $ ---- tet3.8/configure-- 2005-12-09 16:29:17 +0530 -+++ tet3.8/configure 2011-11-24 01:42:02 +0530 -@@ -317,7 +317,7 @@ +--- tet3.8/configure-- Sat Mar 23 10:36:51 2019 ++++ tet3.8/configure Sat Mar 23 10:38:03 2019 +@@ -317,7 +317,7 @@ CRAY*) *-sgi-irix*) fname=irix.mk ;; -*-freebsd) -+*-freebsd | *-netbsd | *-dragonfly) # Use FreeBSD's configuration. ++*-freebsd | *-netbsd | *-dragonfly | *-openbsd) #Use FreeBSD's configuration. fname=freebsd.mk ;; *-bsdi)