diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-03-28 15:33:02 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-03-28 15:33:02 +0000 |
commit | c580f3c787d8ad2dcccceb4438b7572d9713a296 (patch) | |
tree | d8f5d6b20552cfe016f9af38440fd9caacf4a188 /libyasm | |
parent | 1fe0cccaa99357259c9ab42ce88626b95856b77c (diff) | |
parent | 53137eb420065ea05c21ae0728a67ead80f17e1f (diff) | |
download | patched-yasm-c580f3c787d8ad2dcccceb4438b7572d9713a296.tar.gz |
Merge from Chromium at DEPS revision r190564android-sdk-4.4.2_r1.0.1android-sdk-4.4.2_r1android-cts-4.4_r4android-cts-4.4_r1android-4.4_r1.2.0.1android-4.4_r1.2android-4.4_r1.1.0.1android-4.4_r1.1android-4.4_r1.0.1android-4.4_r1android-4.4_r0.9android-4.4_r0.8android-4.4_r0.7android-4.4.4_r2.0.1android-4.4.4_r2android-4.4.4_r1.0.1android-4.4.4_r1android-4.4.3_r1.1.0.1android-4.4.3_r1.1android-4.4.3_r1.0.1android-4.4.3_r1android-4.4.2_r2.0.1android-4.4.2_r2android-4.4.2_r1.0.1android-4.4.2_r1android-4.4.1_r1.0.1android-4.4.1_r1kitkat-releasekitkat-mr2.2-releasekitkat-mr2.1-releasekitkat-mr2-releasekitkat-mr1.1-releasekitkat-mr1-releasekitkat-cts-releasekitkat-cts-dev
This commit was generated by merge_to_master.py.
Change-Id: I1975b1fce5e96446e4fa27feda7009fe340d4cff
Diffstat (limited to 'libyasm')
140 files changed, 30379 insertions, 0 deletions
diff --git a/libyasm/Makefile.inc b/libyasm/Makefile.inc new file mode 100644 index 0000000..042dd39 --- /dev/null +++ b/libyasm/Makefile.inc @@ -0,0 +1,80 @@ +libyasm_a_SOURCES += libyasm/assocdat.c +libyasm_a_SOURCES += libyasm/bitvect.c +libyasm_a_SOURCES += libyasm/bc-align.c +libyasm_a_SOURCES += libyasm/bc-data.c +libyasm_a_SOURCES += libyasm/bc-incbin.c +libyasm_a_SOURCES += libyasm/bc-org.c +libyasm_a_SOURCES += libyasm/bc-reserve.c +libyasm_a_SOURCES += libyasm/bytecode.c +libyasm_a_SOURCES += libyasm/errwarn.c +libyasm_a_SOURCES += libyasm/expr.c +libyasm_a_SOURCES += libyasm/file.c +libyasm_a_SOURCES += libyasm/floatnum.c +libyasm_a_SOURCES += libyasm/hamt.c +libyasm_a_SOURCES += libyasm/insn.c +libyasm_a_SOURCES += libyasm/intnum.c +libyasm_a_SOURCES += libyasm/inttree.c +libyasm_a_SOURCES += libyasm/linemap.c +libyasm_a_SOURCES += libyasm/md5.c +libyasm_a_SOURCES += libyasm/mergesort.c +libyasm_a_SOURCES += libyasm/phash.c +libyasm_a_SOURCES += libyasm/section.c +libyasm_a_SOURCES += libyasm/strcasecmp.c +libyasm_a_SOURCES += libyasm/strsep.c +libyasm_a_SOURCES += libyasm/symrec.c +libyasm_a_SOURCES += libyasm/valparam.c +libyasm_a_SOURCES += libyasm/value.c +libyasm_a_SOURCES += libyasm/xmalloc.c +libyasm_a_SOURCES += libyasm/xstrdup.c +nodist_libyasm_a_SOURCES += module.c + +module.c: $(top_srcdir)/libyasm/module.in genmodule$(EXEEXT) Makefile + $(top_builddir)/genmodule$(EXEEXT) $(top_srcdir)/libyasm/module.in Makefile + +CLEANFILES += module.c + +noinst_PROGRAMS += genmodule + +genmodule_SOURCES = +EXTRA_DIST += libyasm/genmodule.c +genmodule_LDADD = genmodule.$(OBJEXT) +genmodule_LINK = $(CCLD_FOR_BUILD) -o $@ + +genmodule.$(OBJEXT): libyasm/genmodule.c + $(CC_FOR_BUILD) $(DEFAULT_INCLUDES) $(INCLUDES) -c -o $@ `test -f libyasm/genmodule.c || echo '$(srcdir)/'`libyasm/genmodule.c + +EXTRA_DIST += libyasm/module.in + +modincludedir = $(includedir)/libyasm + +modinclude_HEADERS = libyasm/arch.h +modinclude_HEADERS += libyasm/assocdat.h +modinclude_HEADERS += libyasm/bitvect.h +modinclude_HEADERS += libyasm/bytecode.h +modinclude_HEADERS += libyasm/compat-queue.h +modinclude_HEADERS += libyasm/coretype.h +modinclude_HEADERS += libyasm/dbgfmt.h +modinclude_HEADERS += libyasm/errwarn.h +modinclude_HEADERS += libyasm/expr.h +modinclude_HEADERS += libyasm/file.h +modinclude_HEADERS += libyasm/floatnum.h +modinclude_HEADERS += libyasm/hamt.h +modinclude_HEADERS += libyasm/insn.h +modinclude_HEADERS += libyasm/intnum.h +modinclude_HEADERS += libyasm/inttree.h +modinclude_HEADERS += libyasm/linemap.h +modinclude_HEADERS += libyasm/listfmt.h +modinclude_HEADERS += libyasm/md5.h +modinclude_HEADERS += libyasm/module.h +modinclude_HEADERS += libyasm/objfmt.h +modinclude_HEADERS += libyasm/parser.h +modinclude_HEADERS += libyasm/phash.h +modinclude_HEADERS += libyasm/preproc.h +modinclude_HEADERS += libyasm/section.h +modinclude_HEADERS += libyasm/symrec.h +modinclude_HEADERS += libyasm/valparam.h +modinclude_HEADERS += libyasm/value.h + +EXTRA_DIST += libyasm/tests/Makefile.inc + +include libyasm/tests/Makefile.inc diff --git a/libyasm/arch.h b/libyasm/arch.h new file mode 100644 index 0000000..3da9f9f --- /dev/null +++ b/libyasm/arch.h @@ -0,0 +1,495 @@ +/** + * \file libyasm/arch.h + * \brief YASM architecture interface. + * + * \license + * Copyright (C) 2002-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_ARCH_H +#define YASM_ARCH_H + +/** Errors that may be returned by yasm_arch_module::create(). */ +typedef enum yasm_arch_create_error { + YASM_ARCH_CREATE_OK = 0, /**< No error. */ + YASM_ARCH_CREATE_BAD_MACHINE, /**< Unrecognized machine name. */ + YASM_ARCH_CREATE_BAD_PARSER /**< Unrecognized parser name. */ +} yasm_arch_create_error; + +/** Return values for yasm_arch_module::parse_check_insnprefix(). */ +typedef enum yasm_arch_insnprefix { + YASM_ARCH_NOTINSNPREFIX = 0, /**< Unrecognized */ + YASM_ARCH_INSN, /**< An instruction */ + YASM_ARCH_PREFIX /**< An instruction prefix */ +} yasm_arch_insnprefix; + +/** Types of registers / target modifiers that may be returned by + * yasm_arch_module::parse_check_regtmod(). + */ +typedef enum yasm_arch_regtmod { + YASM_ARCH_NOTREGTMOD = 0, /**< Unrecognized */ + YASM_ARCH_REG, /**< A "normal" register */ + YASM_ARCH_REGGROUP, /**< A group of indexable registers */ + YASM_ARCH_SEGREG, /**< A segment register */ + YASM_ARCH_TARGETMOD /**< A target modifier (for jumps) */ +} yasm_arch_regtmod; + +#ifndef YASM_DOXYGEN +/** Base #yasm_arch structure. Must be present as the first element in any + * #yasm_arch implementation. + */ +typedef struct yasm_arch_base { + /** #yasm_arch_module implementation for this architecture. */ + const struct yasm_arch_module *module; +} yasm_arch_base; +#endif + +/** YASM machine subtype. A number of different machine types may be + * associated with a single architecture. These may be specific CPU's, but + * the ABI used to interface with the architecture should be the primary + * differentiator between machines. Some object formats (ELF) use the machine + * to determine parameters within the generated output. + */ +typedef struct yasm_arch_machine { + /** One-line description of the machine. */ + const char *name; + + /** Keyword used to select machine. */ + const char *keyword; +} yasm_arch_machine; + +/** YASM architecture module interface. + * \note All "data" in parser-related functions (yasm_arch_parse_*) needs to + * start the parse initialized to 0 to make it okay for a parser-related + * function to use/check previously stored data to see if it's been + * called before on the same piece of data. + */ +typedef struct yasm_arch_module { + /** One-line description of the architecture. + * Call yasm_arch_name() to get the name of a particular #yasm_arch. + */ + const char *name; + + /** Keyword used to select architecture. + * Call yasm_arch_keyword() to get the keyword of a particular #yasm_arch. + */ + const char *keyword; + + /** NULL-terminated list of directives. NULL if none. */ + /*@null@*/ const yasm_directive *directives; + + /** Create architecture. + * Module-level implementation of yasm_arch_create(). + * Call yasm_arch_create() instead of calling this function. + */ + /*@only@*/ yasm_arch * (*create) (const char *machine, const char *parser, + /*@out@*/ yasm_arch_create_error *error); + + /** Module-level implementation of yasm_arch_destroy(). + * Call yasm_arch_destroy() instead of calling this function. + */ + void (*destroy) (/*@only@*/ yasm_arch *arch); + + /** Module-level implementation of yasm_arch_get_machine(). + * Call yasm_arch_get_machine() instead of calling this function. + */ + const char * (*get_machine) (const yasm_arch *arch); + + /** Module-level implementation of yasm_arch_get_address_size(). + * Call yasm_arch_get_address_size() instead of calling this function. + */ + unsigned int (*get_address_size) (const yasm_arch *arch); + + /** Module-level implementation of yasm_arch_set_var(). + * Call yasm_arch_set_var() instead of calling this function. + */ + int (*set_var) (yasm_arch *arch, const char *var, unsigned long val); + + /** Module-level implementation of yasm_arch_parse_check_insnprefix(). + * Call yasm_arch_parse_check_insnprefix() instead of calling this function. + */ + yasm_arch_insnprefix (*parse_check_insnprefix) + (yasm_arch *arch, const char *id, size_t id_len, unsigned long line, + /*@out@*/ /*@only@*/ yasm_bytecode **bc, /*@out@*/ uintptr_t *prefix); + + /** Module-level implementation of yasm_arch_parse_check_regtmod(). + * Call yasm_arch_parse_check_regtmod() instead of calling this function. + */ + yasm_arch_regtmod (*parse_check_regtmod) + (yasm_arch *arch, const char *id, size_t id_len, + /*@out@*/ uintptr_t *data); + + /** Module-level implementation of yasm_arch_get_fill(). + * Call yasm_arch_get_fill() instead of calling this function. + */ + const unsigned char ** (*get_fill) (const yasm_arch *arch); + + /** Module-level implementation of yasm_arch_floatnum_tobytes(). + * Call yasm_arch_floatnum_tobytes() instead of calling this function. + */ + int (*floatnum_tobytes) (yasm_arch *arch, const yasm_floatnum *flt, + unsigned char *buf, size_t destsize, + size_t valsize, size_t shift, int warn); + + /** Module-level implementation of yasm_arch_intnum_tobytes(). + * Call yasm_arch_intnum_tobytes() instead of calling this function. + */ + int (*intnum_tobytes) (yasm_arch *arch, const yasm_intnum *intn, + unsigned char *buf, size_t destsize, size_t valsize, + int shift, const yasm_bytecode *bc, + int warn); + + /** Module-level implementation of yasm_arch_get_reg_size(). + * Call yasm_arch_get_reg_size() instead of calling this function. + */ + unsigned int (*get_reg_size) (yasm_arch *arch, uintptr_t reg); + + /** Module-level implementation of yasm_arch_reggroup_get_reg(). + * Call yasm_arch_reggroup_get_reg() instead of calling this function. + */ + uintptr_t (*reggroup_get_reg) (yasm_arch *arch, uintptr_t reggroup, + unsigned long regindex); + + /** Module-level implementation of yasm_arch_reg_print(). + * Call yasm_arch_reg_print() instead of calling this function. + */ + void (*reg_print) (yasm_arch *arch, uintptr_t reg, FILE *f); + + /** Module-level implementation of yasm_arch_segreg_print(). + * Call yasm_arch_segreg_print() instead of calling this function. + */ + void (*segreg_print) (yasm_arch *arch, uintptr_t segreg, FILE *f); + + /** Module-level implementation of yasm_arch_ea_create(). + * Call yasm_arch_ea_create() instead of calling this function. + */ + yasm_effaddr * (*ea_create) (yasm_arch *arch, /*@keep@*/ yasm_expr *e); + + /** Module-level implementation of yasm_arch_ea_destroy(). + * Call yasm_arch_ea_destroy() instead of calling this function. + */ + void (*ea_destroy) (/*@only@*/ yasm_effaddr *ea); + + /** Module-level implementation of yasm_arch_ea_print(). + * Call yasm_arch_ea_print() instead of calling this function. + */ + void (*ea_print) (const yasm_effaddr *ea, FILE *f, int indent_level); + + /** Module-level implementation of yasm_arch_create_empty_insn(). + * Call yasm_arch_create_empty_insn() instead of calling this function. + */ + /*@only@*/ yasm_bytecode * (*create_empty_insn) (yasm_arch *arch, + unsigned long line); + + /** NULL-terminated list of machines for this architecture. + * Call yasm_arch_get_machine() to get the active machine of a particular + * #yasm_arch. + */ + const yasm_arch_machine *machines; + + /** Default machine keyword. + * Call yasm_arch_get_machine() to get the active machine of a particular + * #yasm_arch. + */ + const char *default_machine_keyword; + + /** Canonical "word" size in bits. + * Call yasm_arch_wordsize() to get the word size of a particular + * #yasm_arch. + */ + unsigned int wordsize; + + /** Worst case minimum instruction length in bytes. + * Call yasm_arch_min_insn_len() to get the minimum instruction length of + * a particular #yasm_arch. + */ + unsigned int min_insn_len; +} yasm_arch_module; + +/** Get the one-line description of an architecture. + * \param arch architecture + * \return One-line description of architecture. + */ +const char *yasm_arch_name(const yasm_arch *arch); + +/** Get the keyword used to select an architecture. + * \param arch architecture + * \return Architecture keyword. + */ +const char *yasm_arch_keyword(const yasm_arch *arch); + +/** Get the word size of an architecture. + * \param arch architecture + * \return Word size (in bits). + */ +unsigned int yasm_arch_wordsize(const yasm_arch *arch); + +/** Get the minimum instruction length of an architecture. + * \param arch architecture + * \return Minimum instruction length (in bytes). + */ +unsigned int yasm_arch_min_insn_len(const yasm_arch *arch); + +/** Create architecture. + * \param module architecture module + * \param machine keyword of machine in use (must be one listed in + * #yasm_arch_module.machines) + * \param parser keyword of parser in use + * \param error error return value + * \return NULL on error (error returned in error parameter), otherwise new + * architecture. + */ +/*@only@*/ yasm_arch *yasm_arch_create(const yasm_arch_module *module, + const char *machine, const char *parser, + /*@out@*/ yasm_arch_create_error *error); + +/** Clean up, free any architecture-allocated memory. + * \param arch architecture + */ +void yasm_arch_destroy(/*@only@*/ yasm_arch *arch); + +/** Get architecture's active machine name. + * \param arch architecture + * \return Active machine name. + */ +const char *yasm_arch_get_machine(const yasm_arch *arch); + +/** Get architecture's active address size, in bits. + * \param arch architecture + * \return Active address size (in bits). + */ +unsigned int yasm_arch_get_address_size(const yasm_arch *arch); + +/** Set any arch-specific variables. For example, "mode_bits" in x86. + * \param arch architecture + * \param var variable name + * \param val value to set + * \return Zero on success, non-zero on failure (variable does not exist). + */ +int yasm_arch_set_var(yasm_arch *arch, const char *var, unsigned long val); + +/** Check an generic identifier to see if it matches architecture specific + * names for instructions or instruction prefixes. Unrecognized identifiers + * should return #YASM_ARCH_NOTINSNPREFIX so they can be treated as normal + * symbols. Any additional data beyond just the type (almost always necessary) + * should be returned into the space provided by the data parameter. + * \param arch architecture + * \param id identifier as in the input file + * \param id_len length of id string + * \param line virtual line + * \param bc for instructions, yasm_insn-based bytecode is returned + * (and NULL otherwise) + * \param prefix for prefixes, yasm_arch-specific value is returned + * (and 0 otherwise) + * \return Identifier type (#YASM_ARCH_NOTINSNPREFIX if unrecognized) + */ +yasm_arch_insnprefix yasm_arch_parse_check_insnprefix + (yasm_arch *arch, const char *id, size_t id_len, unsigned long line, + /*@out@*/ /*@only@*/ yasm_bytecode **bc, /*@out@*/ uintptr_t *prefix); + +/** Check an generic identifier to see if it matches architecture specific + * names for registers or target modifiers. Unrecognized identifiers should + * return #YASM_ARCH_NOTREGTMOD. Any additional data beyond just the type + * (almost always necessary) should be returned into the space provided by the + * data parameter. + * \param arch architecture + * \param id identifier as in the input file + * \param id_len length of id string + * \param data extra identification information (yasm_arch-specific) + * [output] + * \return Identifier type (#YASM_ARCH_NOTREGTMOD if unrecognized) + */ +yasm_arch_regtmod yasm_arch_parse_check_regtmod + (yasm_arch *arch, const char *id, size_t id_len, + /*@out@*/ uintptr_t *data); + +/** Get NOP fill patterns for 1-15 bytes of fill. + * \param arch architecture + * \return 16-entry array of arrays; [0] is unused, [1] - [15] point to arrays + * of 1-15 bytes (respectively) in length. + */ +const unsigned char **yasm_arch_get_fill(const yasm_arch *arch); + +/** Output #yasm_floatnum to buffer. Puts the value into the least + * significant bits of the destination, or may be shifted into more + * significant bits by the shift parameter. The destination bits are + * cleared before being set. + * Architecture-specific because of endianness. + * \param arch architecture + * \param flt floating point value + * \param buf buffer to write into + * \param destsize destination size (in bytes) + * \param valsize size (in bits) + * \param shift left shift (in bits) + * \param warn enables standard overflow/underflow warnings + * \return Nonzero on error. + */ +int yasm_arch_floatnum_tobytes(yasm_arch *arch, const yasm_floatnum *flt, + unsigned char *buf, size_t destsize, + size_t valsize, size_t shift, int warn); + +/** Output #yasm_intnum to buffer. Puts the value into the least + * significant bits of the destination, or may be shifted into more + * significant bits by the shift parameter. The destination bits are + * cleared before being set. + * \param arch architecture + * \param intn integer value + * \param buf buffer to write into + * \param destsize destination size (in bytes) + * \param valsize size (in bits) + * \param shift left shift (in bits); may be negative to specify right + * shift (standard warnings include truncation to boundary) + * \param bc bytecode being output ("parent" of value) + * \param warn enables standard warnings (value doesn't fit into + * valsize bits) + * \return Nonzero on error. + */ +int yasm_arch_intnum_tobytes(yasm_arch *arch, const yasm_intnum *intn, + unsigned char *buf, size_t destsize, + size_t valsize, int shift, + const yasm_bytecode *bc, int warn); + +/** Get the equivalent size of a register in bits. + * \param arch architecture + * \param reg register + * \return 0 if there is no suitable equivalent size, otherwise the size. + */ +unsigned int yasm_arch_get_reg_size(yasm_arch *arch, uintptr_t reg); + +/** Get a specific register of a register group, based on the register + * group and the index within the group. + * \param arch architecture + * \param reggroup register group + * \param regindex register index + * \return 0 if regindex is not valid for that register group, otherwise the + * specific register value. + */ +uintptr_t yasm_arch_reggroup_get_reg(yasm_arch *arch, uintptr_t reggroup, + unsigned long regindex); + +/** Print a register. For debugging purposes. + * \param arch architecture + * \param reg register + * \param f file + */ +void yasm_arch_reg_print(yasm_arch *arch, uintptr_t reg, FILE *f); + +/** Print a segment register. For debugging purposes. + * \param arch architecture + * \param segreg segment register + * \param f file + */ +void yasm_arch_segreg_print(yasm_arch *arch, uintptr_t segreg, FILE *f); + +/** Create an effective address from an expression. + * \param arch architecture + * \param e expression (kept, do not delete) + * \return Newly allocated effective address. + */ +yasm_effaddr *yasm_arch_ea_create(yasm_arch *arch, /*@keep@*/ yasm_expr *e); + +/** Delete (free allocated memory for) an effective address. + * \param arch architecture + * \param ea effective address (only pointer to it). + */ +void yasm_arch_ea_destroy(yasm_arch *arch, /*@only@*/ yasm_effaddr *ea); + +/** Print an effective address. For debugging purposes. + * \param arch architecture + * \param ea effective address + * \param f file + * \param indent_level indentation level + */ +void yasm_arch_ea_print(const yasm_arch *arch, const yasm_effaddr *ea, + FILE *f, int indent_level); + +/** Create a bytecode that represents a single empty (0 length) instruction. + * This is used for handling solitary prefixes. + * \param arch architecture + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode. + */ +/*@only@*/ yasm_bytecode *yasm_arch_create_empty_insn(yasm_arch *arch, + unsigned long line); + +#ifndef YASM_DOXYGEN + +/* Inline macro implementations for arch functions */ + +#define yasm_arch_name(arch) \ + (((yasm_arch_base *)arch)->module->name) +#define yasm_arch_keyword(arch) \ + (((yasm_arch_base *)arch)->module->keyword) +#define yasm_arch_wordsize(arch) \ + (((yasm_arch_base *)arch)->module->wordsize) +#define yasm_arch_min_insn_len(arch) \ + (((yasm_arch_base *)arch)->module->min_insn_len) + +#define yasm_arch_create(module, machine, parser, error) \ + module->create(machine, parser, error) + +#define yasm_arch_destroy(arch) \ + ((yasm_arch_base *)arch)->module->destroy(arch) +#define yasm_arch_get_machine(arch) \ + ((yasm_arch_base *)arch)->module->get_machine(arch) +#define yasm_arch_get_address_size(arch) \ + ((yasm_arch_base *)arch)->module->get_address_size(arch) +#define yasm_arch_set_var(arch, var, val) \ + ((yasm_arch_base *)arch)->module->set_var(arch, var, val) +#define yasm_arch_parse_check_insnprefix(arch, id, id_len, line, bc, prefix) \ + ((yasm_arch_base *)arch)->module->parse_check_insnprefix \ + (arch, id, id_len, line, bc, prefix) +#define yasm_arch_parse_check_regtmod(arch, id, id_len, data) \ + ((yasm_arch_base *)arch)->module->parse_check_regtmod \ + (arch, id, id_len, data) +#define yasm_arch_get_fill(arch) \ + ((yasm_arch_base *)arch)->module->get_fill(arch) +#define yasm_arch_floatnum_tobytes(arch, flt, buf, destsize, valsize, shift, \ + warn) \ + ((yasm_arch_base *)arch)->module->floatnum_tobytes \ + (arch, flt, buf, destsize, valsize, shift, warn) +#define yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, shift, \ + bc, warn) \ + ((yasm_arch_base *)arch)->module->intnum_tobytes \ + (arch, intn, buf, destsize, valsize, shift, bc, warn) +#define yasm_arch_get_reg_size(arch, reg) \ + ((yasm_arch_base *)arch)->module->get_reg_size(arch, reg) +#define yasm_arch_reggroup_get_reg(arch, regg, regi) \ + ((yasm_arch_base *)arch)->module->reggroup_get_reg(arch, regg, regi) +#define yasm_arch_reg_print(arch, reg, f) \ + ((yasm_arch_base *)arch)->module->reg_print(arch, reg, f) +#define yasm_arch_segreg_print(arch, segreg, f) \ + ((yasm_arch_base *)arch)->module->segreg_print(arch, segreg, f) +#define yasm_arch_ea_create(arch, e) \ + ((yasm_arch_base *)arch)->module->ea_create(arch, e) +#define yasm_arch_ea_destroy(arch, ea) \ + ((yasm_arch_base *)arch)->module->ea_destroy(ea) +#define yasm_arch_ea_print(arch, ea, f, i) \ + ((yasm_arch_base *)arch)->module->ea_print(ea, f, i) +#define yasm_arch_create_empty_insn(arch, line) \ + ((yasm_arch_base *)arch)->module->create_empty_insn(arch, line) + +#endif + +#endif diff --git a/libyasm/assocdat.c b/libyasm/assocdat.c new file mode 100644 index 0000000..cdcda62 --- /dev/null +++ b/libyasm/assocdat.c @@ -0,0 +1,136 @@ +/* + * YASM associated data storage (libyasm internal use) + * + * Copyright (C) 2003-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "coretype.h" +#include "assocdat.h" + + +typedef struct assoc_data_item { + const yasm_assoc_data_callback *callback; + void *data; +} assoc_data_item; + +struct yasm__assoc_data { + assoc_data_item *vector; + size_t size; + size_t alloc; +}; + + +yasm__assoc_data * +yasm__assoc_data_create(void) +{ + yasm__assoc_data *assoc_data = yasm_xmalloc(sizeof(yasm__assoc_data)); + + assoc_data->size = 0; + assoc_data->alloc = 2; + assoc_data->vector = yasm_xmalloc(assoc_data->alloc * + sizeof(assoc_data_item)); + + return assoc_data; +} + +void * +yasm__assoc_data_get(yasm__assoc_data *assoc_data, + const yasm_assoc_data_callback *callback) +{ + size_t i; + + if (!assoc_data) + return NULL; + + for (i=0; i<assoc_data->size; i++) { + if (assoc_data->vector[i].callback == callback) + return assoc_data->vector[i].data; + } + return NULL; +} + +yasm__assoc_data * +yasm__assoc_data_add(yasm__assoc_data *assoc_data_arg, + const yasm_assoc_data_callback *callback, void *data) +{ + yasm__assoc_data *assoc_data; + assoc_data_item *item = NULL; + size_t i; + + /* Create a new assoc_data if necessary */ + if (assoc_data_arg) + assoc_data = assoc_data_arg; + else + assoc_data = yasm__assoc_data_create(); + + /* See if there's already assocated data for this callback */ + for (i=0; i<assoc_data->size; i++) { + if (assoc_data->vector[i].callback == callback) + item = &assoc_data->vector[i]; + } + + /* No? Then append a new one */ + if (!item) { + assoc_data->size++; + if (assoc_data->size > assoc_data->alloc) { + assoc_data->alloc *= 2; + assoc_data->vector = + yasm_xrealloc(assoc_data->vector, + assoc_data->alloc * sizeof(assoc_data_item)); + } + item = &assoc_data->vector[assoc_data->size-1]; + item->callback = callback; + item->data = NULL; + } + + /* Delete existing data (if any) */ + if (item->data && item->data != data) + item->callback->destroy(item->data); + + item->data = data; + + return assoc_data; +} + +void +yasm__assoc_data_destroy(yasm__assoc_data *assoc_data) +{ + size_t i; + + if (!assoc_data) + return; + + for (i=0; i<assoc_data->size; i++) + assoc_data->vector[i].callback->destroy(assoc_data->vector[i].data); + yasm_xfree(assoc_data->vector); + yasm_xfree(assoc_data); +} + +void +yasm__assoc_data_print(const yasm__assoc_data *assoc_data, FILE *f, + int indent_level) +{ + /*TODO*/ +} diff --git a/libyasm/assocdat.h b/libyasm/assocdat.h new file mode 100644 index 0000000..cf42386 --- /dev/null +++ b/libyasm/assocdat.h @@ -0,0 +1,76 @@ +/** + * \file assocdat.h + * \brief YASM associated data storage (libyasm internal use) + * + * \license + * Copyright (C) 2003-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_ASSOCDAT_H +#define YASM_ASSOCDAT_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Associated data container. */ +typedef struct yasm__assoc_data yasm__assoc_data; + +/** Create an associated data container. */ +YASM_LIB_DECL +/*@only@*/ yasm__assoc_data *yasm__assoc_data_create(void); + +/** Get associated data for a data callback. + * \param assoc_data container of associated data + * \param callback callback used when adding data + * \return Associated data (NULL if none). + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ void *yasm__assoc_data_get + (/*@null@*/ yasm__assoc_data *assoc_data, + const yasm_assoc_data_callback *callback); + +/** Add associated data to a associated data container. + * \attention Deletes any existing associated data for that data callback. + * \param assoc_data container of associated data + * \param callback callback + * \param data data to associate + */ +YASM_LIB_DECL +/*@only@*/ yasm__assoc_data *yasm__assoc_data_add + (/*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data, + const yasm_assoc_data_callback *callback, + /*@only@*/ /*@null@*/ void *data); + +/** Destroy all associated data in a container. */ +YASM_LIB_DECL +void yasm__assoc_data_destroy + (/*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data); + +/** Print all associated data in a container. */ +YASM_LIB_DECL +void yasm__assoc_data_print(const yasm__assoc_data *assoc_data, FILE *f, + int indent_level); + +#endif diff --git a/libyasm/bc-align.c b/libyasm/bc-align.c new file mode 100644 index 0000000..2a47882 --- /dev/null +++ b/libyasm/bc-align.c @@ -0,0 +1,245 @@ +/* + * Align bytecode + * + * Copyright (C) 2005-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" + +#include "bytecode.h" + + +typedef struct bytecode_align { + /*@only@*/ yasm_expr *boundary; /* alignment boundary */ + + /* What to fill intervening locations with, NULL if using code_fill */ + /*@only@*/ /*@null@*/ yasm_expr *fill; + + /* Maximum number of bytes to skip, NULL if no maximum. */ + /*@only@*/ /*@null@*/ yasm_expr *maxskip; + + /* Code fill, NULL if using 0 fill */ + /*@null@*/ const unsigned char **code_fill; +} bytecode_align; + +static void bc_align_destroy(void *contents); +static void bc_align_print(const void *contents, FILE *f, int indent_level); +static void bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); +static int bc_align_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); +static int bc_align_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static const yasm_bytecode_callback bc_align_callback = { + bc_align_destroy, + bc_align_print, + bc_align_finalize, + NULL, + bc_align_calc_len, + bc_align_expand, + bc_align_tobytes, + YASM_BC_SPECIAL_OFFSET +}; + + +static void +bc_align_destroy(void *contents) +{ + bytecode_align *align = (bytecode_align *)contents; + if (align->boundary) + yasm_expr_destroy(align->boundary); + if (align->fill) + yasm_expr_destroy(align->fill); + if (align->maxskip) + yasm_expr_destroy(align->maxskip); + yasm_xfree(contents); +} + +static void +bc_align_print(const void *contents, FILE *f, int indent_level) +{ + const bytecode_align *align = (const bytecode_align *)contents; + fprintf(f, "%*s_Align_\n", indent_level, ""); + fprintf(f, "%*sBoundary=", indent_level, ""); + yasm_expr_print(align->boundary, f); + fprintf(f, "\n%*sFill=", indent_level, ""); + yasm_expr_print(align->fill, f); + fprintf(f, "\n%*sMax Skip=", indent_level, ""); + yasm_expr_print(align->maxskip, f); + fprintf(f, "\n"); +} + +static void +bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + bytecode_align *align = (bytecode_align *)bc->contents; + if (!yasm_expr_get_intnum(&align->boundary, 0)) + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("align boundary must be a constant")); + if (align->fill && !yasm_expr_get_intnum(&align->fill, 0)) + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("align fill must be a constant")); + if (align->maxskip && !yasm_expr_get_intnum(&align->maxskip, 0)) + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("align maximum skip must be a constant")); +} + +static int +bc_align_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + long neg_thres = 0; + long pos_thres = 0; + + if (bc_align_expand(bc, 0, 0, (long)bc->offset, &neg_thres, + &pos_thres) < 0) + return -1; + + return 0; +} + +static int +bc_align_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + bytecode_align *align = (bytecode_align *)bc->contents; + unsigned long end; + unsigned long boundary = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->boundary, 0)); + + if (boundary == 0) { + bc->len = 0; + *pos_thres = new_val; + return 0; + } + + end = (unsigned long)new_val; + if ((unsigned long)new_val & (boundary-1)) + end = ((unsigned long)new_val & ~(boundary-1)) + boundary; + + *pos_thres = (long)end; + bc->len = end - (unsigned long)new_val; + + if (align->maxskip) { + unsigned long maxskip = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->maxskip, 0)); + if (bc->len > maxskip) { + *pos_thres = (long)end-maxskip-1; + bc->len = 0; + } + } + return 1; +} + +static int +bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@unused@*/ yasm_output_reloc_func output_reloc) +{ + bytecode_align *align = (bytecode_align *)bc->contents; + unsigned long len; + unsigned long boundary = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->boundary, 0)); + + if (boundary == 0) + return 0; + else { + unsigned long end = bc->offset; + if (bc->offset & (boundary-1)) + end = (bc->offset & ~(boundary-1)) + boundary; + len = end - bc->offset; + if (len == 0) + return 0; + if (align->maxskip) { + unsigned long maxskip = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->maxskip, 0)); + if (len > maxskip) + return 0; + } + } + + if (align->fill) { + unsigned long v; + v = yasm_intnum_get_uint(yasm_expr_get_intnum(&align->fill, 0)); + memset(*bufp, (int)v, len); + *bufp += len; + } else if (align->code_fill) { + unsigned long maxlen = 15; + while (!align->code_fill[maxlen] && maxlen>0) + maxlen--; + if (maxlen == 0) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("could not find any code alignment size")); + return 1; + } + + /* Fill with maximum code fill as much as possible */ + while (len > maxlen) { + memcpy(*bufp, align->code_fill[maxlen], maxlen); + *bufp += maxlen; + len -= maxlen; + } + + if (!align->code_fill[len]) { + yasm_error_set(YASM_ERROR_VALUE, + N_("invalid alignment size %d"), len); + return 1; + } + /* Handle rest of code fill */ + memcpy(*bufp, align->code_fill[len], len); + *bufp += len; + } else { + /* Just fill with 0 */ + memset(*bufp, 0, len); + *bufp += len; + } + return 0; +} + +yasm_bytecode * +yasm_bc_create_align(yasm_expr *boundary, yasm_expr *fill, + yasm_expr *maxskip, const unsigned char **code_fill, + unsigned long line) +{ + bytecode_align *align = yasm_xmalloc(sizeof(bytecode_align)); + + align->boundary = boundary; + align->fill = fill; + align->maxskip = maxskip; + align->code_fill = code_fill; + + return yasm_bc_create_common(&bc_align_callback, align, line); +} diff --git a/libyasm/bc-data.c b/libyasm/bc-data.c new file mode 100644 index 0000000..ebbdd6f --- /dev/null +++ b/libyasm/bc-data.c @@ -0,0 +1,600 @@ +/* + * Data (and LEB128) bytecode + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "value.h" + +#include "bytecode.h" +#include "arch.h" + + +struct yasm_dataval { + /*@reldef@*/ STAILQ_ENTRY(yasm_dataval) link; + + enum { DV_EMPTY, DV_VALUE, DV_RAW, DV_ULEB128, DV_SLEB128, DV_RESERVE } + type; + + union { + yasm_value val; + struct { + /*@only@*/ unsigned char *contents; + unsigned long len; + } raw; + } data; + + /* number of times data is repeated, NULL=1. */ + /*@only@*/ /*@null@*/ yasm_expr *multiple; +}; + +typedef struct bytecode_data { + /* converted data (linked list) */ + yasm_datavalhead datahead; + + int item_size; +} bytecode_data; + +static void bc_data_destroy(void *contents); +static void bc_data_print(const void *contents, FILE *f, int indent_level); +static void bc_data_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); +static int bc_data_item_size(yasm_bytecode *bc); +static int bc_data_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); +static int bc_data_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static const yasm_bytecode_callback bc_data_callback = { + bc_data_destroy, + bc_data_print, + bc_data_finalize, + bc_data_item_size, + bc_data_calc_len, + yasm_bc_expand_common, + bc_data_tobytes, + 0 +}; + + +static void +bc_data_destroy(void *contents) +{ + bytecode_data *bc_data = (bytecode_data *)contents; + yasm_dvs_delete(&bc_data->datahead); + yasm_xfree(contents); +} + +static void +bc_data_print(const void *contents, FILE *f, int indent_level) +{ + const bytecode_data *bc_data = (const bytecode_data *)contents; + fprintf(f, "%*s_Data_\n", indent_level, ""); + fprintf(f, "%*sElements:\n", indent_level+1, ""); + yasm_dvs_print(&bc_data->datahead, f, indent_level+2); +} + +static void +bc_data_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + bytecode_data *bc_data = (bytecode_data *)bc->contents; + yasm_dataval *dv; + yasm_intnum *intn; + + /* Convert values from simple expr to value. */ + STAILQ_FOREACH(dv, &bc_data->datahead, link) { + switch (dv->type) { + case DV_VALUE: + if (yasm_value_finalize(&dv->data.val, prev_bc)) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("data expression too complex")); + return; + } + break; + case DV_ULEB128: + case DV_SLEB128: + intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); + if (!intn) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("LEB128 requires constant values")); + return; + } + /* Warn for negative values in unsigned environment. + * This could be an error instead: the likelihood this is + * desired is very low! + */ + if (yasm_intnum_sign(intn) == -1 && dv->type == DV_ULEB128) + yasm_warn_set(YASM_WARN_GENERAL, + N_("negative value in unsigned LEB128")); + break; + default: + break; + } + if (dv->multiple) { + yasm_value val; + if (yasm_value_finalize_expr(&val, dv->multiple, prev_bc, 0)) + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("multiple expression too complex")); + else if (val.rel) + yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, + N_("multiple expression not absolute")); + dv->multiple = val.abs; + } + } +} + +static int +bc_data_item_size(yasm_bytecode *bc) +{ + bytecode_data *bc_data = (bytecode_data *)bc->contents; + return bc_data->item_size; +} + +static int +bc_data_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + bytecode_data *bc_data = (bytecode_data *)bc->contents; + yasm_dataval *dv; + yasm_intnum *intn; + unsigned long len = 0; + unsigned long multiple; + + /* Count up element sizes, rounding up string length. */ + STAILQ_FOREACH(dv, &bc_data->datahead, link) { + switch (dv->type) { + case DV_EMPTY: + len = 0; + break; + case DV_VALUE: + len = dv->data.val.size/8; + break; + case DV_RAW: + len = dv->data.raw.len; + break; + case DV_ULEB128: + case DV_SLEB128: + intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); + if (!intn) + yasm_internal_error(N_("non-constant in data_tobytes")); + len = yasm_intnum_size_leb128(intn, dv->type == DV_SLEB128); + break; + case DV_RESERVE: + len = dv->data.val.size/8; + break; + } + + if (!yasm_dv_get_multiple(dv, &multiple)) + len *= multiple; + + bc->len += len; + } + + return 0; +} + +static int +bc_data_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@unused@*/ yasm_output_reloc_func output_reloc) +{ + bytecode_data *bc_data = (bytecode_data *)bc->contents; + yasm_dataval *dv; + yasm_intnum *intn; + unsigned int val_len; + unsigned long multiple, i; + + STAILQ_FOREACH(dv, &bc_data->datahead, link) { + if (yasm_dv_get_multiple(dv, &multiple) || multiple == 0) + continue; + switch (dv->type) { + case DV_EMPTY: + break; + case DV_VALUE: + val_len = dv->data.val.size/8; + for (i=0; i<multiple; i++) { + if (output_value(&dv->data.val, *bufp, val_len, + (unsigned long)(*bufp-bufstart), bc, 1, + d)) + return 1; + *bufp += val_len; + } + break; + case DV_RAW: + for (i=0; i<multiple; i++) { + memcpy(*bufp, dv->data.raw.contents, dv->data.raw.len); + *bufp += dv->data.raw.len; + } + break; + case DV_ULEB128: + case DV_SLEB128: + intn = yasm_expr_get_intnum(&dv->data.val.abs, 234); + if (!intn) + yasm_internal_error(N_("non-constant in data_tobytes")); + for (i=0; i<multiple; i++) { + *bufp += + yasm_intnum_get_leb128(intn, *bufp, + dv->type == DV_SLEB128); + } + case DV_RESERVE: + val_len = dv->data.val.size/8; + for (i=0; i<multiple; i++) { + memset(*bufp, 0, val_len); + *bufp += val_len; + } + break; + } + } + + return 0; +} + +yasm_bytecode * +yasm_bc_create_data(yasm_datavalhead *datahead, unsigned int size, + int append_zero, yasm_arch *arch, unsigned long line) +{ + bytecode_data *data = yasm_xmalloc(sizeof(bytecode_data)); + yasm_bytecode *bc = yasm_bc_create_common(&bc_data_callback, data, line); + yasm_dataval *dv, *dv2, *dvo; + yasm_intnum *intn; + unsigned long len = 0, rlen, i; + + + yasm_dvs_initialize(&data->datahead); + data->item_size = size; + + /* Prescan input data for length, etc. Careful: this needs to be + * precisely paired with the second loop. + */ + STAILQ_FOREACH(dv, datahead, link) { + if (dv->multiple && dv->type != DV_EMPTY && len > 0) { + /* Flush previous data */ + dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); + STAILQ_INSERT_TAIL(&data->datahead, dvo, link); + len = 0; + } + switch (dv->type) { + case DV_EMPTY: + break; + case DV_VALUE: + case DV_ULEB128: + case DV_SLEB128: + intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); + if (intn && dv->type == DV_VALUE && (arch || size == 1)) + len += size; + else if (intn && dv->type == DV_ULEB128) + len += yasm_intnum_size_leb128(intn, 0); + else if (intn && dv->type == DV_SLEB128) + len += yasm_intnum_size_leb128(intn, 1); + else { + if (len > 0) { + /* Create bytecode for all previous len */ + dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); + STAILQ_INSERT_TAIL(&data->datahead, dvo, link); + len = 0; + } + + /* Create bytecode for this value */ + dvo = yasm_xmalloc(sizeof(yasm_dataval)); + STAILQ_INSERT_TAIL(&data->datahead, dvo, link); + dvo->multiple = dv->multiple; + } + break; + case DV_RAW: + rlen = dv->data.raw.len; + /* find count, rounding up to nearest multiple of size */ + rlen = (rlen + size - 1) / size; + len += rlen*size; + break; + case DV_RESERVE: + len += size; + break; + } + + if (dv->multiple && dv->type != DV_EMPTY && len > 0) { + /* Flush this data */ + dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); + STAILQ_INSERT_TAIL(&data->datahead, dvo, link); + dvo->multiple = dv->multiple; + len = 0; + } + + if (append_zero) + len++; + } + + /* Create final dataval for any trailing length */ + if (len > 0) { + dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); + STAILQ_INSERT_TAIL(&data->datahead, dvo, link); + } + + /* Second iteration: copy data and delete input datavals. */ + dv = STAILQ_FIRST(datahead); + dvo = STAILQ_FIRST(&data->datahead); + len = 0; + while (dv && dvo) { + if (dv->multiple && dv->type != DV_EMPTY && len > 0) { + dvo = STAILQ_NEXT(dvo, link); + len = 0; + } + switch (dv->type) { + case DV_EMPTY: + break; + case DV_VALUE: + case DV_ULEB128: + case DV_SLEB128: + intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); + if (intn && dv->type == DV_VALUE && (arch || size == 1)) { + if (size == 1) + yasm_intnum_get_sized(intn, + &dvo->data.raw.contents[len], + 1, 8, 0, 0, 1); + else + yasm_arch_intnum_tobytes(arch, intn, + &dvo->data.raw.contents[len], + size, size*8, 0, bc, 1); + yasm_value_delete(&dv->data.val); + len += size; + } else if (intn && dv->type == DV_ULEB128) { + len += yasm_intnum_get_leb128(intn, + &dvo->data.raw.contents[len], + 0); + yasm_value_delete(&dv->data.val); + } else if (intn && dv->type == DV_SLEB128) { + len += yasm_intnum_get_leb128(intn, + &dvo->data.raw.contents[len], + 1); + yasm_value_delete(&dv->data.val); + } else { + if (len > 0) + dvo = STAILQ_NEXT(dvo, link); + dvo->type = dv->type; + dvo->data.val = dv->data.val; /* structure copy */ + dvo->data.val.size = size*8; /* remember size */ + dvo = STAILQ_NEXT(dvo, link); + len = 0; + } + break; + case DV_RAW: + rlen = dv->data.raw.len; + memcpy(&dvo->data.raw.contents[len], dv->data.raw.contents, + rlen); + yasm_xfree(dv->data.raw.contents); + len += rlen; + /* pad with 0's to nearest multiple of size */ + rlen %= size; + if (rlen > 0) { + rlen = size-rlen; + for (i=0; i<rlen; i++) + dvo->data.raw.contents[len++] = 0; + } + break; + case DV_RESERVE: + memset(&dvo->data.raw.contents[len], 0, size); + len += size; + break; + } + + if (dv->multiple && dv->type != DV_EMPTY && len > 0) { + dvo = STAILQ_NEXT(dvo, link); + len = 0; + } + + if (append_zero) + dvo->data.raw.contents[len++] = 0; + dv2 = STAILQ_NEXT(dv, link); + yasm_xfree(dv); + dv = dv2; + } + + return bc; +} + +yasm_bytecode * +yasm_bc_create_leb128(yasm_datavalhead *datahead, int sign, unsigned long line) +{ + yasm_dataval *dv; + + /* Convert all values into LEB type, error on strings/raws */ + STAILQ_FOREACH(dv, datahead, link) { + switch (dv->type) { + case DV_VALUE: + dv->type = sign ? DV_SLEB128 : DV_ULEB128; + break; + case DV_RAW: + yasm_error_set(YASM_ERROR_VALUE, + N_("LEB128 does not allow string constants")); + break; + default: + break; + } + } + + return yasm_bc_create_data(datahead, 0, 0, 0, line); +} + +yasm_dataval * +yasm_dv_create_expr(yasm_expr *e) +{ + yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); + + retval->type = DV_VALUE; + yasm_value_initialize(&retval->data.val, e, 0); + retval->multiple = NULL; + + return retval; +} + +yasm_dataval * +yasm_dv_create_raw(unsigned char *contents, unsigned long len) +{ + yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); + + retval->type = DV_RAW; + retval->data.raw.contents = contents; + retval->data.raw.len = len; + retval->multiple = NULL; + + return retval; +} + +yasm_dataval * +yasm_dv_create_reserve(void) +{ + yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); + + retval->type = DV_RESERVE; + retval->multiple = NULL; + + return retval; +} + +yasm_value * +yasm_dv_get_value(yasm_dataval *dv) +{ + if (dv->type != DV_VALUE) + return NULL; + return &dv->data.val; +} + +void +yasm_dv_set_multiple(yasm_dataval *dv, yasm_expr *e) +{ + if (dv->multiple) + dv->multiple = yasm_expr_create_tree( dv->multiple, YASM_EXPR_MUL, e, + e->line); + else + dv->multiple = e; +} + +int +yasm_dv_get_multiple(yasm_dataval *dv, unsigned long *multiple) +{ + /*@dependent@*/ /*@null@*/ const yasm_intnum *num; + + *multiple = 1; + if (dv->multiple) { + num = yasm_expr_get_intnum(&dv->multiple, 0); + if (!num) { + yasm_error_set(YASM_ERROR_VALUE, + N_("could not determine multiple")); + return 1; + } + if (yasm_intnum_sign(num) < 0) { + yasm_error_set(YASM_ERROR_VALUE, N_("multiple is negative")); + return 1; + } + *multiple = yasm_intnum_get_uint(num); + } + return 0; +} + +void +yasm_dvs_delete(yasm_datavalhead *headp) +{ + yasm_dataval *cur, *next; + + cur = STAILQ_FIRST(headp); + while (cur) { + next = STAILQ_NEXT(cur, link); + switch (cur->type) { + case DV_VALUE: + yasm_value_delete(&cur->data.val); + break; + case DV_RAW: + yasm_xfree(cur->data.raw.contents); + break; + default: + break; + } + if (cur->multiple) + yasm_expr_destroy(cur->multiple); + yasm_xfree(cur); + cur = next; + } + STAILQ_INIT(headp); +} + +yasm_dataval * +yasm_dvs_append(yasm_datavalhead *headp, yasm_dataval *dv) +{ + if (dv) { + STAILQ_INSERT_TAIL(headp, dv, link); + return dv; + } + return (yasm_dataval *)NULL; +} + +void +yasm_dvs_print(const yasm_datavalhead *head, FILE *f, int indent_level) +{ + yasm_dataval *cur; + unsigned long i; + + STAILQ_FOREACH(cur, head, link) { + fprintf(f, "%*sMultiple=", indent_level, ""); + if (!cur->multiple) + fprintf(f, "nil (1)"); + else + yasm_expr_print(cur->multiple, f); + switch (cur->type) { + case DV_EMPTY: + fprintf(f, "%*sEmpty\n", indent_level, ""); + break; + case DV_VALUE: + fprintf(f, "%*sValue:\n", indent_level, ""); + yasm_value_print(&cur->data.val, f, indent_level+1); + break; + case DV_RAW: + fprintf(f, "%*sLength=%lu\n", indent_level, "", + cur->data.raw.len); + fprintf(f, "%*sBytes=[", indent_level, ""); + for (i=0; i<cur->data.raw.len; i++) + fprintf(f, "0x%02x, ", cur->data.raw.contents[i]); + fprintf(f, "]\n"); + break; + case DV_ULEB128: + fprintf(f, "%*sULEB128 value:\n", indent_level, ""); + yasm_value_print(&cur->data.val, f, indent_level+1); + break; + case DV_SLEB128: + fprintf(f, "%*sSLEB128 value:\n", indent_level, ""); + yasm_value_print(&cur->data.val, f, indent_level+1); + break; + case DV_RESERVE: + fprintf(f, "%*sReserved\n", indent_level, ""); + break; + } + } +} diff --git a/libyasm/bc-incbin.c b/libyasm/bc-incbin.c new file mode 100644 index 0000000..a8fec7d --- /dev/null +++ b/libyasm/bc-incbin.c @@ -0,0 +1,265 @@ +/* + * Incbin bytecode + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "linemap.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "value.h" + +#include "bytecode.h" + +#include "file.h" + + +typedef struct bytecode_incbin { + /*@only@*/ char *filename; /* file to include data from */ + const char *from; /* filename of what contained incbin */ + + /* starting offset to read from (NULL=0) */ + /*@only@*/ /*@null@*/ yasm_expr *start; + + /* maximum number of bytes to read (NULL=no limit) */ + /*@only@*/ /*@null@*/ yasm_expr *maxlen; +} bytecode_incbin; + +static void bc_incbin_destroy(void *contents); +static void bc_incbin_print(const void *contents, FILE *f, int indent_level); +static void bc_incbin_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); +static int bc_incbin_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); +static int bc_incbin_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static const yasm_bytecode_callback bc_incbin_callback = { + bc_incbin_destroy, + bc_incbin_print, + bc_incbin_finalize, + NULL, + bc_incbin_calc_len, + yasm_bc_expand_common, + bc_incbin_tobytes, + 0 +}; + + +static void +bc_incbin_destroy(void *contents) +{ + bytecode_incbin *incbin = (bytecode_incbin *)contents; + yasm_xfree(incbin->filename); + yasm_expr_destroy(incbin->start); + yasm_expr_destroy(incbin->maxlen); + yasm_xfree(contents); +} + +static void +bc_incbin_print(const void *contents, FILE *f, int indent_level) +{ + const bytecode_incbin *incbin = (const bytecode_incbin *)contents; + fprintf(f, "%*s_IncBin_\n", indent_level, ""); + fprintf(f, "%*sFilename=`%s'\n", indent_level, "", + incbin->filename); + fprintf(f, "%*sStart=", indent_level, ""); + if (!incbin->start) + fprintf(f, "nil (0)"); + else + yasm_expr_print(incbin->start, f); + fprintf(f, "%*sMax Len=", indent_level, ""); + if (!incbin->maxlen) + fprintf(f, "nil (unlimited)"); + else + yasm_expr_print(incbin->maxlen, f); + fprintf(f, "\n"); +} + +static void +bc_incbin_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + bytecode_incbin *incbin = (bytecode_incbin *)bc->contents; + yasm_value val; + + if (yasm_value_finalize_expr(&val, incbin->start, prev_bc, 0)) + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("start expression too complex")); + else if (val.rel) + yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, + N_("start expression not absolute")); + incbin->start = val.abs; + + if (yasm_value_finalize_expr(&val, incbin->maxlen, prev_bc, 0)) + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("maximum length expression too complex")); + else if (val.rel) + yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, + N_("maximum length expression not absolute")); + incbin->maxlen = val.abs; +} + +static int +bc_incbin_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + bytecode_incbin *incbin = (bytecode_incbin *)bc->contents; + FILE *f; + /*@dependent@*/ /*@null@*/ const yasm_intnum *num; + unsigned long start = 0, maxlen = 0xFFFFFFFFUL, flen; + + /* Try to convert start to integer value */ + if (incbin->start) { + num = yasm_expr_get_intnum(&incbin->start, 0); + if (num) + start = yasm_intnum_get_uint(num); + if (!num) { + /* FIXME */ + yasm_error_set(YASM_ERROR_NOT_IMPLEMENTED, + N_("incbin does not yet understand non-constant")); + return -1; + } + } + + /* Try to convert maxlen to integer value */ + if (incbin->maxlen) { + num = yasm_expr_get_intnum(&incbin->maxlen, 0); + if (num) + maxlen = yasm_intnum_get_uint(num); + if (!num) { + /* FIXME */ + yasm_error_set(YASM_ERROR_NOT_IMPLEMENTED, + N_("incbin does not yet understand non-constant")); + return -1; + } + } + + /* Open file and determine its length */ + f = yasm_fopen_include(incbin->filename, incbin->from, "rb", NULL); + if (!f) { + yasm_error_set(YASM_ERROR_IO, + N_("`incbin': unable to open file `%s'"), + incbin->filename); + return -1; + } + if (fseek(f, 0L, SEEK_END) < 0) { + yasm_error_set(YASM_ERROR_IO, + N_("`incbin': unable to seek on file `%s'"), + incbin->filename); + return -1; + } + flen = (unsigned long)ftell(f); + fclose(f); + + /* Compute length of incbin from start, maxlen, and len */ + if (start > flen) { + yasm_warn_set(YASM_WARN_GENERAL, + N_("`incbin': start past end of file `%s'"), + incbin->filename); + start = flen; + } + flen -= start; + if (incbin->maxlen) + if (maxlen < flen) + flen = maxlen; + bc->len += flen; + return 0; +} + +static int +bc_incbin_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@unused@*/ yasm_output_reloc_func output_reloc) +{ + bytecode_incbin *incbin = (bytecode_incbin *)bc->contents; + FILE *f; + /*@dependent@*/ /*@null@*/ const yasm_intnum *num; + unsigned long start = 0; + + /* Convert start to integer value */ + if (incbin->start) { + num = yasm_expr_get_intnum(&incbin->start, 0); + if (!num) + yasm_internal_error( + N_("could not determine start in bc_tobytes_incbin")); + start = yasm_intnum_get_uint(num); + } + + /* Open file */ + f = yasm_fopen_include(incbin->filename, incbin->from, "rb", NULL); + if (!f) { + yasm_error_set(YASM_ERROR_IO, N_("`incbin': unable to open file `%s'"), + incbin->filename); + return 1; + } + + /* Seek to start of data */ + if (fseek(f, (long)start, SEEK_SET) < 0) { + yasm_error_set(YASM_ERROR_IO, + N_("`incbin': unable to seek on file `%s'"), + incbin->filename); + fclose(f); + return 1; + } + + /* Read len bytes */ + if (fread(*bufp, 1, (size_t)bc->len, f) < (size_t)bc->len) { + yasm_error_set(YASM_ERROR_IO, + N_("`incbin': unable to read %lu bytes from file `%s'"), + bc->len, incbin->filename); + fclose(f); + return 1; + } + + *bufp += bc->len; + fclose(f); + return 0; +} + +yasm_bytecode * +yasm_bc_create_incbin(char *filename, yasm_expr *start, yasm_expr *maxlen, + yasm_linemap *linemap, unsigned long line) +{ + bytecode_incbin *incbin = yasm_xmalloc(sizeof(bytecode_incbin)); + unsigned long xline; + + /* Find from filename based on line number */ + yasm_linemap_lookup(linemap, line, &incbin->from, &xline); + + /*@-mustfree@*/ + incbin->filename = filename; + incbin->start = start; + incbin->maxlen = maxlen; + /*@=mustfree@*/ + + return yasm_bc_create_common(&bc_incbin_callback, incbin, line); +} diff --git a/libyasm/bc-org.c b/libyasm/bc-org.c new file mode 100644 index 0000000..7ef96c8 --- /dev/null +++ b/libyasm/bc-org.c @@ -0,0 +1,152 @@ +/* + * ORG bytecode + * + * Copyright (C) 2005-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "file.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "value.h" + +#include "bytecode.h" + + +typedef struct bytecode_org { + unsigned long start; /* target starting offset within section */ + unsigned long fill; /* fill value */ +} bytecode_org; + +static void bc_org_destroy(void *contents); +static void bc_org_print(const void *contents, FILE *f, int indent_level); +static void bc_org_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); +static int bc_org_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); +static int bc_org_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int bc_org_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static const yasm_bytecode_callback bc_org_callback = { + bc_org_destroy, + bc_org_print, + bc_org_finalize, + NULL, + bc_org_calc_len, + bc_org_expand, + bc_org_tobytes, + YASM_BC_SPECIAL_OFFSET +}; + + +static void +bc_org_destroy(void *contents) +{ + yasm_xfree(contents); +} + +static void +bc_org_print(const void *contents, FILE *f, int indent_level) +{ + const bytecode_org *org = (const bytecode_org *)contents; + fprintf(f, "%*s_Org_\n", indent_level, ""); + fprintf(f, "%*sStart=%lu\n", indent_level, "", org->start); +} + +static void +bc_org_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ +} + +static int +bc_org_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + bytecode_org *org = (bytecode_org *)bc->contents; + long neg_thres = 0; + long pos_thres = org->start; + + if (bc_org_expand(bc, 0, 0, (long)bc->offset, &neg_thres, &pos_thres) < 0) + return -1; + + return 0; +} + +static int +bc_org_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + bytecode_org *org = (bytecode_org *)bc->contents; + + /* Check for overrun */ + if ((unsigned long)new_val > org->start) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("ORG overlap with already existing data")); + return -1; + } + + /* Generate space to start offset */ + bc->len = org->start - new_val; + return 1; +} + +static int +bc_org_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@unused@*/ yasm_output_reloc_func output_reloc) +{ + bytecode_org *org = (bytecode_org *)bc->contents; + unsigned long len, i; + + /* Sanity check for overrun */ + if (bc->offset > org->start) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("ORG overlap with already existing data")); + return 1; + } + len = org->start - bc->offset; + for (i=0; i<len; i++) + YASM_WRITE_8(*bufp, org->fill); /* XXX: handle more than 8 bit? */ + return 0; +} + +yasm_bytecode * +yasm_bc_create_org(unsigned long start, unsigned long fill, unsigned long line) +{ + bytecode_org *org = yasm_xmalloc(sizeof(bytecode_org)); + + org->start = start; + org->fill = fill; + + return yasm_bc_create_common(&bc_org_callback, org, line); +} diff --git a/libyasm/bc-reserve.c b/libyasm/bc-reserve.c new file mode 100644 index 0000000..197175b --- /dev/null +++ b/libyasm/bc-reserve.c @@ -0,0 +1,152 @@ +/* + * Bytecode utility functions + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "value.h" + +#include "bytecode.h" + + +typedef struct bytecode_reserve { + /*@only@*/ /*@null@*/ yasm_expr *numitems; /* number of items to reserve */ + unsigned int itemsize; /* size of each item (in bytes) */ +} bytecode_reserve; + +static void bc_reserve_destroy(void *contents); +static void bc_reserve_print(const void *contents, FILE *f, int indent_level); +static void bc_reserve_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); +static int bc_reserve_elem_size(yasm_bytecode *bc); +static int bc_reserve_calc_len(yasm_bytecode *bc, + yasm_bc_add_span_func add_span, + void *add_span_data); +static int bc_reserve_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static const yasm_bytecode_callback bc_reserve_callback = { + bc_reserve_destroy, + bc_reserve_print, + bc_reserve_finalize, + bc_reserve_elem_size, + bc_reserve_calc_len, + yasm_bc_expand_common, + bc_reserve_tobytes, + YASM_BC_SPECIAL_RESERVE +}; + + +static void +bc_reserve_destroy(void *contents) +{ + bytecode_reserve *reserve = (bytecode_reserve *)contents; + yasm_expr_destroy(reserve->numitems); + yasm_xfree(contents); +} + +static void +bc_reserve_print(const void *contents, FILE *f, int indent_level) +{ + const bytecode_reserve *reserve = (const bytecode_reserve *)contents; + fprintf(f, "%*s_Reserve_\n", indent_level, ""); + fprintf(f, "%*sNum Items=", indent_level, ""); + yasm_expr_print(reserve->numitems, f); + fprintf(f, "\n%*sItem Size=%u\n", indent_level, "", reserve->itemsize); +} + +static void +bc_reserve_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + bytecode_reserve *reserve = (bytecode_reserve *)bc->contents; + /* multiply reserve expression into multiple */ + if (!bc->multiple) + bc->multiple = reserve->numitems; + else + bc->multiple = yasm_expr_create_tree(bc->multiple, YASM_EXPR_MUL, + reserve->numitems, bc->line); + reserve->numitems = NULL; +} + +static int +bc_reserve_elem_size(yasm_bytecode *bc) +{ + bytecode_reserve *reserve = (bytecode_reserve *)bc->contents; + return reserve->itemsize; +} + +static int +bc_reserve_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + bytecode_reserve *reserve = (bytecode_reserve *)bc->contents; + bc->len += reserve->itemsize; + return 0; +} + +static int +bc_reserve_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@unused@*/ yasm_output_reloc_func output_reloc) +{ + yasm_internal_error(N_("bc_reserve_tobytes called")); + /*@notreached@*/ + return 1; +} + +yasm_bytecode * +yasm_bc_create_reserve(yasm_expr *numitems, unsigned int itemsize, + unsigned long line) +{ + bytecode_reserve *reserve = yasm_xmalloc(sizeof(bytecode_reserve)); + + /*@-mustfree@*/ + reserve->numitems = numitems; + /*@=mustfree@*/ + reserve->itemsize = itemsize; + + return yasm_bc_create_common(&bc_reserve_callback, reserve, line); +} + +const yasm_expr * +yasm_bc_reserve_numitems(yasm_bytecode *bc, unsigned int *itemsize) +{ + bytecode_reserve *reserve; + + if (bc->callback != &bc_reserve_callback) + return NULL; + + reserve = (bytecode_reserve *)bc->contents; + *itemsize = reserve->itemsize; + return reserve->numitems; +} diff --git a/libyasm/bitvect.c b/libyasm/bitvect.c new file mode 100644 index 0000000..dfb0825 --- /dev/null +++ b/libyasm/bitvect.c @@ -0,0 +1,4045 @@ +#include "util.h" + +#include "coretype.h" + +/*****************************************************************************/ +/* MODULE NAME: BitVector.c MODULE TYPE: (adt) */ +/*****************************************************************************/ +/* MODULE IMPORTS: */ +/*****************************************************************************/ +#include <ctype.h> /* MODULE TYPE: (sys) */ +#include <limits.h> /* MODULE TYPE: (sys) */ +#include <string.h> /* MODULE TYPE: (sys) */ +/*****************************************************************************/ +/* MODULE INTERFACE: */ +/*****************************************************************************/ +#include "bitvect.h" + +/* ToolBox.h */ +#define and && /* logical (boolean) operators: lower case */ +#define or || +#define not ! + +#define AND & /* binary (bitwise) operators: UPPER CASE */ +#define OR | +#define XOR ^ +#define NOT ~ +#define SHL << +#define SHR >> + +#ifdef ENABLE_MODULO +#define mod % /* arithmetic operators */ +#endif + +#define blockdef(name,size) unsigned char name[size] +#define blocktypedef(name,size) typedef unsigned char name[size] + +/*****************************************************************************/ +/* MODULE RESOURCES: */ +/*****************************************************************************/ + +#define bits_(BitVector) *(BitVector-3) +#define size_(BitVector) *(BitVector-2) +#define mask_(BitVector) *(BitVector-1) + +#define ERRCODE_TYPE "sizeof(word) > sizeof(size_t)" +#define ERRCODE_BITS "bits(word) != sizeof(word)*8" +#define ERRCODE_WORD "bits(word) < 16" +#define ERRCODE_LONG "bits(word) > bits(long)" +#define ERRCODE_POWR "bits(word) != 2^x" +#define ERRCODE_LOGA "bits(word) != 2^ld(bits(word))" +#define ERRCODE_NULL "unable to allocate memory" +#define ERRCODE_INDX "index out of range" +#define ERRCODE_ORDR "minimum > maximum index" +#define ERRCODE_SIZE "bit vector size mismatch" +#define ERRCODE_PARS "input string syntax error" +#define ERRCODE_OVFL "numeric overflow error" +#define ERRCODE_SAME "result vector(s) must be distinct" +#define ERRCODE_EXPO "exponent must be positive" +#define ERRCODE_ZERO "division by zero error" +#define ERRCODE_OOPS "unexpected internal error - please contact author" + +const N_int BitVector_BYTENORM[256] = +{ + 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, /* 0x00 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x10 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x20 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x30 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x40 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x50 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x60 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0x70 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x80 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x90 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0xA0 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0xB0 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0xC0 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0xD0 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0xE0 */ + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, + 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08 /* 0xF0 */ +}; + +/*****************************************************************************/ +/* MODULE IMPLEMENTATION: */ +/*****************************************************************************/ + + /**********************************************/ + /* global implementation-intrinsic constants: */ + /**********************************************/ + +#define BIT_VECTOR_HIDDEN_WORDS 3 + + /*****************************************************************/ + /* global machine-dependent constants (set by "BitVector_Boot"): */ + /*****************************************************************/ + +static N_word BITS; /* = # of bits in machine word (must be power of 2) */ +static N_word MODMASK; /* = BITS - 1 (mask for calculating modulo BITS) */ +static N_word LOGBITS; /* = ld(BITS) (logarithmus dualis) */ +static N_word FACTOR; /* = ld(BITS / 8) (ld of # of bytes) */ + +static N_word LSB = 1; /* = mask for least significant bit */ +static N_word MSB; /* = mask for most significant bit */ + +static N_word LONGBITS; /* = # of bits in unsigned long */ + +static N_word LOG10; /* = logarithm to base 10 of BITS - 1 */ +static N_word EXP10; /* = largest possible power of 10 in signed int */ + + /********************************************************************/ + /* global bit mask table for fast access (set by "BitVector_Boot"): */ + /********************************************************************/ + +static wordptr BITMASKTAB; + + /*****************************/ + /* global macro definitions: */ + /*****************************/ + +#define BIT_VECTOR_ZERO_WORDS(target,count) \ + while (count-- > 0) *target++ = 0; + +#define BIT_VECTOR_FILL_WORDS(target,fill,count) \ + while (count-- > 0) *target++ = fill; + +#define BIT_VECTOR_FLIP_WORDS(target,flip,count) \ + while (count-- > 0) *target++ ^= flip; + +#define BIT_VECTOR_COPY_WORDS(target,source,count) \ + while (count-- > 0) *target++ = *source++; + +#define BIT_VECTOR_BACK_WORDS(target,source,count) \ + { target += count; source += count; while (count-- > 0) *--target = *--source; } + +#define BIT_VECTOR_CLR_BIT(address,index) \ + *(address+(index>>LOGBITS)) &= NOT BITMASKTAB[index AND MODMASK]; + +#define BIT_VECTOR_SET_BIT(address,index) \ + *(address+(index>>LOGBITS)) |= BITMASKTAB[index AND MODMASK]; + +#define BIT_VECTOR_TST_BIT(address,index) \ + ((*(address+(index>>LOGBITS)) AND BITMASKTAB[index AND MODMASK]) != 0) + +#define BIT_VECTOR_FLP_BIT(address,index,mask) \ + (mask = BITMASKTAB[index AND MODMASK]), \ + (((*(addr+(index>>LOGBITS)) ^= mask) AND mask) != 0) + +#define BIT_VECTOR_DIGITIZE(type,value,digit) \ + value = (type) ((digit = value) / 10); \ + digit -= value * 10; \ + digit += (type) '0'; + + /*********************************************************/ + /* private low-level functions (potentially dangerous!): */ + /*********************************************************/ + +static N_word power10(N_word x) +{ + N_word y = 1; + + while (x-- > 0) y *= 10; + return(y); +} + +static void BIT_VECTOR_zro_words(wordptr addr, N_word count) +{ + BIT_VECTOR_ZERO_WORDS(addr,count) +} + +static void BIT_VECTOR_cpy_words(wordptr target, wordptr source, N_word count) +{ + BIT_VECTOR_COPY_WORDS(target,source,count) +} + +static void BIT_VECTOR_mov_words(wordptr target, wordptr source, N_word count) +{ + if (target != source) + { + if (target < source) BIT_VECTOR_COPY_WORDS(target,source,count) + else BIT_VECTOR_BACK_WORDS(target,source,count) + } +} + +static void BIT_VECTOR_ins_words(wordptr addr, N_word total, N_word count, + boolean clear) +{ + N_word length; + + if ((total > 0) and (count > 0)) + { + if (count > total) count = total; + length = total - count; + if (length > 0) BIT_VECTOR_mov_words(addr+count,addr,length); + if (clear) BIT_VECTOR_zro_words(addr,count); + } +} + +static void BIT_VECTOR_del_words(wordptr addr, N_word total, N_word count, + boolean clear) +{ + N_word length; + + if ((total > 0) and (count > 0)) + { + if (count > total) count = total; + length = total - count; + if (length > 0) BIT_VECTOR_mov_words(addr,addr+count,length); + if (clear) BIT_VECTOR_zro_words(addr+length,count); + } +} + +static void BIT_VECTOR_reverse(charptr string, N_word length) +{ + charptr last; + N_char temp; + + if (length > 1) + { + last = string + length - 1; + while (string < last) + { + temp = *string; + *string = *last; + *last = temp; + string++; + last--; + } + } +} + +static N_word BIT_VECTOR_int2str(charptr string, N_word value) +{ + N_word length; + N_word digit; + charptr work; + + work = string; + if (value > 0) + { + length = 0; + while (value > 0) + { + BIT_VECTOR_DIGITIZE(N_word,value,digit) + *work++ = (N_char) digit; + length++; + } + BIT_VECTOR_reverse(string,length); + } + else + { + length = 1; + *work++ = (N_char) '0'; + } + return(length); +} + +static N_word BIT_VECTOR_str2int(charptr string, N_word *value) +{ + N_word length; + N_word digit; + + *value = 0; + length = 0; + digit = (N_word) *string++; + /* separate because isdigit() is likely a macro! */ + while (isdigit((int)digit) != 0) + { + length++; + digit -= (N_word) '0'; + if (*value) *value *= 10; + *value += digit; + digit = (N_word) *string++; + } + return(length); +} + + /********************************************/ + /* routine to convert error code to string: */ + /********************************************/ + +const char * BitVector_Error(ErrCode error) +{ + switch (error) + { + case ErrCode_Ok: return( NULL ); break; + case ErrCode_Type: return( ERRCODE_TYPE ); break; + case ErrCode_Bits: return( ERRCODE_BITS ); break; + case ErrCode_Word: return( ERRCODE_WORD ); break; + case ErrCode_Long: return( ERRCODE_LONG ); break; + case ErrCode_Powr: return( ERRCODE_POWR ); break; + case ErrCode_Loga: return( ERRCODE_LOGA ); break; + case ErrCode_Null: return( ERRCODE_NULL ); break; + case ErrCode_Indx: return( ERRCODE_INDX ); break; + case ErrCode_Ordr: return( ERRCODE_ORDR ); break; + case ErrCode_Size: return( ERRCODE_SIZE ); break; + case ErrCode_Pars: return( ERRCODE_PARS ); break; + case ErrCode_Ovfl: return( ERRCODE_OVFL ); break; + case ErrCode_Same: return( ERRCODE_SAME ); break; + case ErrCode_Expo: return( ERRCODE_EXPO ); break; + case ErrCode_Zero: return( ERRCODE_ZERO ); break; + default: return( ERRCODE_OOPS ); break; + } +} + + /*****************************************/ + /* automatic self-configuration routine: */ + /*****************************************/ + + /*******************************************************/ + /* */ + /* MUST be called once prior to any other function */ + /* to initialize the machine dependent constants */ + /* of this package! (But call only ONCE, or you */ + /* will suffer memory leaks!) */ + /* */ + /*******************************************************/ + +ErrCode BitVector_Boot(void) +{ + N_long longsample = 1L; + N_word sample = LSB; + N_word lsb; + + if (sizeof(N_word) > sizeof(size_t)) return(ErrCode_Type); + + BITS = 1; + while (sample <<= 1) BITS++; /* determine # of bits in a machine word */ + + if (BITS != (sizeof(N_word) << 3)) return(ErrCode_Bits); + + if (BITS < 16) return(ErrCode_Word); + + LONGBITS = 1; + while (longsample <<= 1) LONGBITS++; /* = # of bits in an unsigned long */ + + if (BITS > LONGBITS) return(ErrCode_Long); + + LOGBITS = 0; + sample = BITS; + lsb = (sample AND LSB); + while ((sample >>= 1) and (not lsb)) + { + LOGBITS++; + lsb = (sample AND LSB); + } + + if (sample) return(ErrCode_Powr); /* # of bits is not a power of 2! */ + + if (BITS != (LSB << LOGBITS)) return(ErrCode_Loga); + + MODMASK = BITS - 1; + FACTOR = LOGBITS - 3; /* ld(BITS / 8) = ld(BITS) - ld(8) = ld(BITS) - 3 */ + MSB = (LSB << MODMASK); + + BITMASKTAB = (wordptr) yasm_xmalloc((size_t) (BITS << FACTOR)); + + if (BITMASKTAB == NULL) return(ErrCode_Null); + + for ( sample = 0; sample < BITS; sample++ ) + { + BITMASKTAB[sample] = (LSB << sample); + } + + LOG10 = (N_word) (MODMASK * 0.30103); /* = (BITS - 1) * ( ln 2 / ln 10 ) */ + EXP10 = power10(LOG10); + + return(ErrCode_Ok); +} + +void BitVector_Shutdown(void) +{ + if (BITMASKTAB) yasm_xfree(BITMASKTAB); +} + +N_word BitVector_Size(N_int bits) /* bit vector size (# of words) */ +{ + N_word size; + + size = bits >> LOGBITS; + if (bits AND MODMASK) size++; + return(size); +} + +N_word BitVector_Mask(N_int bits) /* bit vector mask (unused bits) */ +{ + N_word mask; + + mask = bits AND MODMASK; + if (mask) mask = (N_word) ~(~0L << mask); else mask = (N_word) ~0L; + return(mask); +} + +const char * BitVector_Version(void) +{ + return("6.4"); +} + +N_int BitVector_Word_Bits(void) +{ + return(BITS); +} + +N_int BitVector_Long_Bits(void) +{ + return(LONGBITS); +} + +/********************************************************************/ +/* */ +/* WARNING: Do not "free()" constant character strings, i.e., */ +/* don't call "BitVector_Dispose()" for strings returned */ +/* by "BitVector_Error()" or "BitVector_Version()"! */ +/* */ +/* ONLY call this function for strings allocated with "malloc()", */ +/* i.e., the strings returned by the functions "BitVector_to_*()" */ +/* and "BitVector_Block_Read()"! */ +/* */ +/********************************************************************/ + +void BitVector_Dispose(charptr string) /* free string */ +{ + if (string != NULL) yasm_xfree((voidptr) string); +} + +void BitVector_Destroy(wordptr addr) /* free bitvec */ +{ + if (addr != NULL) + { + addr -= BIT_VECTOR_HIDDEN_WORDS; + yasm_xfree((voidptr) addr); + } +} + +void BitVector_Destroy_List(listptr list, N_int count) /* free list */ +{ + listptr slot; + + if (list != NULL) + { + slot = list; + while (count-- > 0) + { + BitVector_Destroy(*slot++); + } + free((voidptr) list); + } +} + +wordptr BitVector_Create(N_int bits, boolean clear) /* malloc */ +{ + N_word size; + N_word mask; + N_word bytes; + wordptr addr; + wordptr zero; + + size = BitVector_Size(bits); + mask = BitVector_Mask(bits); + bytes = (size + BIT_VECTOR_HIDDEN_WORDS) << FACTOR; + addr = (wordptr) yasm_xmalloc((size_t) bytes); + if (addr != NULL) + { + *addr++ = bits; + *addr++ = size; + *addr++ = mask; + if (clear) + { + zero = addr; + BIT_VECTOR_ZERO_WORDS(zero,size) + } + } + return(addr); +} + +listptr BitVector_Create_List(N_int bits, boolean clear, N_int count) +{ + listptr list = NULL; + listptr slot; + wordptr addr; + N_int i; + + if (count > 0) + { + list = (listptr) malloc(sizeof(wordptr) * count); + if (list != NULL) + { + slot = list; + for ( i = 0; i < count; i++ ) + { + addr = BitVector_Create(bits,clear); + if (addr == NULL) + { + BitVector_Destroy_List(list,i); + return(NULL); + } + *slot++ = addr; + } + } + } + return(list); +} + +wordptr BitVector_Resize(wordptr oldaddr, N_int bits) /* realloc */ +{ + N_word bytes; + N_word oldsize; + N_word oldmask; + N_word newsize; + N_word newmask; + wordptr newaddr; + wordptr source; + wordptr target; + + oldsize = size_(oldaddr); + oldmask = mask_(oldaddr); + newsize = BitVector_Size(bits); + newmask = BitVector_Mask(bits); + if (oldsize > 0) *(oldaddr+oldsize-1) &= oldmask; + if (newsize <= oldsize) + { + newaddr = oldaddr; + bits_(newaddr) = bits; + size_(newaddr) = newsize; + mask_(newaddr) = newmask; + if (newsize > 0) *(newaddr+newsize-1) &= newmask; + } + else + { + bytes = (newsize + BIT_VECTOR_HIDDEN_WORDS) << FACTOR; + newaddr = (wordptr) yasm_xmalloc((size_t) bytes); + if (newaddr != NULL) + { + *newaddr++ = bits; + *newaddr++ = newsize; + *newaddr++ = newmask; + target = newaddr; + source = oldaddr; + newsize -= oldsize; + BIT_VECTOR_COPY_WORDS(target,source,oldsize) + BIT_VECTOR_ZERO_WORDS(target,newsize) + } + BitVector_Destroy(oldaddr); + } + return(newaddr); +} + +wordptr BitVector_Shadow(wordptr addr) /* makes new, same size but empty */ +{ + return( BitVector_Create(bits_(addr),true) ); +} + +wordptr BitVector_Clone(wordptr addr) /* makes exact duplicate */ +{ + N_word bits; + wordptr twin; + + bits = bits_(addr); + twin = BitVector_Create(bits,false); + if ((twin != NULL) and (bits > 0)) + BIT_VECTOR_cpy_words(twin,addr,size_(addr)); + return(twin); +} + +wordptr BitVector_Concat(wordptr X, wordptr Y) /* returns concatenation */ +{ + /* BEWARE that X = most significant part, Y = least significant part! */ + + N_word bitsX; + N_word bitsY; + N_word bitsZ; + wordptr Z; + + bitsX = bits_(X); + bitsY = bits_(Y); + bitsZ = bitsX + bitsY; + Z = BitVector_Create(bitsZ,false); + if ((Z != NULL) and (bitsZ > 0)) + { + BIT_VECTOR_cpy_words(Z,Y,size_(Y)); + BitVector_Interval_Copy(Z,X,bitsY,0,bitsX); + *(Z+size_(Z)-1) &= mask_(Z); + } + return(Z); +} + +void BitVector_Copy(wordptr X, wordptr Y) /* X = Y */ +{ + N_word sizeX = size_(X); + N_word sizeY = size_(Y); + N_word maskX = mask_(X); + N_word maskY = mask_(Y); + N_word fill = 0; + wordptr lastX; + wordptr lastY; + + if ((X != Y) and (sizeX > 0)) + { + lastX = X + sizeX - 1; + if (sizeY > 0) + { + lastY = Y + sizeY - 1; + if ( (*lastY AND (maskY AND NOT (maskY >> 1))) == 0 ) *lastY &= maskY; + else + { + fill = (N_word) ~0L; + *lastY |= NOT maskY; + } + while ((sizeX > 0) and (sizeY > 0)) + { + *X++ = *Y++; + sizeX--; + sizeY--; + } + *lastY &= maskY; + } + while (sizeX-- > 0) *X++ = fill; + *lastX &= maskX; + } +} + +void BitVector_Empty(wordptr addr) /* X = {} clr all */ +{ + N_word size = size_(addr); + + BIT_VECTOR_ZERO_WORDS(addr,size) +} + +void BitVector_Fill(wordptr addr) /* X = ~{} set all */ +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word fill = (N_word) ~0L; + + if (size > 0) + { + BIT_VECTOR_FILL_WORDS(addr,fill,size) + *(--addr) &= mask; + } +} + +void BitVector_Flip(wordptr addr) /* X = ~X flip all */ +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word flip = (N_word) ~0L; + + if (size > 0) + { + BIT_VECTOR_FLIP_WORDS(addr,flip,size) + *(--addr) &= mask; + } +} + +void BitVector_Primes(wordptr addr) +{ + N_word bits = bits_(addr); + N_word size = size_(addr); + wordptr work; + N_word temp; + N_word i,j; + + if (size > 0) + { + temp = 0xAAAA; + i = BITS >> 4; + while (--i > 0) + { + temp <<= 16; + temp |= 0xAAAA; + } + i = size; + work = addr; + *work++ = temp XOR 0x0006; + while (--i > 0) *work++ = temp; + for ( i = 3; (j = i * i) < bits; i += 2 ) + { + for ( ; j < bits; j += i ) BIT_VECTOR_CLR_BIT(addr,j) + } + *(addr+size-1) &= mask_(addr); + } +} + +void BitVector_Reverse(wordptr X, wordptr Y) +{ + N_word bits = bits_(X); + N_word mask; + N_word bit; + N_word value; + + if (bits > 0) + { + if (X == Y) BitVector_Interval_Reverse(X,0,bits-1); + else if (bits == bits_(Y)) + { +/* mask = mask_(Y); */ +/* mask &= NOT (mask >> 1); */ + mask = BITMASKTAB[(bits-1) AND MODMASK]; + Y += size_(Y) - 1; + value = 0; + bit = LSB; + while (bits-- > 0) + { + if ((*Y AND mask) != 0) + { + value |= bit; + } + if (not (mask >>= 1)) + { + Y--; + mask = MSB; + } + if (not (bit <<= 1)) + { + *X++ = value; + value = 0; + bit = LSB; + } + } + if (bit > LSB) *X = value; + } + } +} + +void BitVector_Interval_Empty(wordptr addr, N_int lower, N_int upper) +{ /* X = X \ [lower..upper] */ + N_word bits = bits_(addr); + N_word size = size_(addr); + wordptr loaddr; + wordptr hiaddr; + N_word lobase; + N_word hibase; + N_word lomask; + N_word himask; + N_word diff; + + if ((size > 0) and (lower < bits) and (upper < bits) and (lower <= upper)) + { + lobase = lower >> LOGBITS; + hibase = upper >> LOGBITS; + diff = hibase - lobase; + loaddr = addr + lobase; + hiaddr = addr + hibase; + + lomask = (N_word) (~0L << (lower AND MODMASK)); + himask = (N_word) ~((~0L << (upper AND MODMASK)) << 1); + + if (diff == 0) + { + *loaddr &= NOT (lomask AND himask); + } + else + { + *loaddr++ &= NOT lomask; + while (--diff > 0) + { + *loaddr++ = 0; + } + *hiaddr &= NOT himask; + } + } +} + +void BitVector_Interval_Fill(wordptr addr, N_int lower, N_int upper) +{ /* X = X + [lower..upper] */ + N_word bits = bits_(addr); + N_word size = size_(addr); + N_word fill = (N_word) ~0L; + wordptr loaddr; + wordptr hiaddr; + N_word lobase; + N_word hibase; + N_word lomask; + N_word himask; + N_word diff; + + if ((size > 0) and (lower < bits) and (upper < bits) and (lower <= upper)) + { + lobase = lower >> LOGBITS; + hibase = upper >> LOGBITS; + diff = hibase - lobase; + loaddr = addr + lobase; + hiaddr = addr + hibase; + + lomask = (N_word) (~0L << (lower AND MODMASK)); + himask = (N_word) ~((~0L << (upper AND MODMASK)) << 1); + + if (diff == 0) + { + *loaddr |= (lomask AND himask); + } + else + { + *loaddr++ |= lomask; + while (--diff > 0) + { + *loaddr++ = fill; + } + *hiaddr |= himask; + } + *(addr+size-1) &= mask_(addr); + } +} + +void BitVector_Interval_Flip(wordptr addr, N_int lower, N_int upper) +{ /* X = X ^ [lower..upper] */ + N_word bits = bits_(addr); + N_word size = size_(addr); + N_word flip = (N_word) ~0L; + wordptr loaddr; + wordptr hiaddr; + N_word lobase; + N_word hibase; + N_word lomask; + N_word himask; + N_word diff; + + if ((size > 0) and (lower < bits) and (upper < bits) and (lower <= upper)) + { + lobase = lower >> LOGBITS; + hibase = upper >> LOGBITS; + diff = hibase - lobase; + loaddr = addr + lobase; + hiaddr = addr + hibase; + + lomask = (N_word) (~0L << (lower AND MODMASK)); + himask = (N_word) ~((~0L << (upper AND MODMASK)) << 1); + + if (diff == 0) + { + *loaddr ^= (lomask AND himask); + } + else + { + *loaddr++ ^= lomask; + while (--diff > 0) + { + *loaddr++ ^= flip; + } + *hiaddr ^= himask; + } + *(addr+size-1) &= mask_(addr); + } +} + +void BitVector_Interval_Reverse(wordptr addr, N_int lower, N_int upper) +{ + N_word bits = bits_(addr); + wordptr loaddr; + wordptr hiaddr; + N_word lomask; + N_word himask; + + if ((bits > 0) and (lower < bits) and (upper < bits) and (lower < upper)) + { + loaddr = addr + (lower >> LOGBITS); + hiaddr = addr + (upper >> LOGBITS); + lomask = BITMASKTAB[lower AND MODMASK]; + himask = BITMASKTAB[upper AND MODMASK]; + for ( bits = upper - lower + 1; bits > 1; bits -= 2 ) + { + if (((*loaddr AND lomask) != 0) XOR ((*hiaddr AND himask) != 0)) + { + *loaddr ^= lomask; /* swap bits only if they differ! */ + *hiaddr ^= himask; + } + if (not (lomask <<= 1)) + { + lomask = LSB; + loaddr++; + } + if (not (himask >>= 1)) + { + himask = MSB; + hiaddr--; + } + } + } +} + +boolean BitVector_interval_scan_inc(wordptr addr, N_int start, + N_intptr min, N_intptr max) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word offset; + N_word bitmask; + N_word value; + boolean empty; + + if ((size == 0) or (start >= bits_(addr))) return(FALSE); + + *min = start; + *max = start; + + offset = start >> LOGBITS; + + *(addr+size-1) &= mask; + + addr += offset; + size -= offset; + + bitmask = BITMASKTAB[start AND MODMASK]; + mask = NOT (bitmask OR (bitmask - 1)); + + value = *addr++; + if ((value AND bitmask) == 0) + { + value &= mask; + if (value == 0) + { + offset++; + empty = TRUE; + while (empty and (--size > 0)) + { + if ((value = *addr++)) empty = false; else offset++; + } + if (empty) return(FALSE); + } + start = offset << LOGBITS; + bitmask = LSB; + mask = value; + while (not (mask AND LSB)) + { + bitmask <<= 1; + mask >>= 1; + start++; + } + mask = NOT (bitmask OR (bitmask - 1)); + *min = start; + *max = start; + } + value = NOT value; + value &= mask; + if (value == 0) + { + offset++; + empty = TRUE; + while (empty and (--size > 0)) + { + if ((value = NOT *addr++)) empty = false; else offset++; + } + if (empty) value = LSB; + } + start = offset << LOGBITS; + while (not (value AND LSB)) + { + value >>= 1; + start++; + } + *max = --start; + return(TRUE); +} + +boolean BitVector_interval_scan_dec(wordptr addr, N_int start, + N_intptr min, N_intptr max) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word offset; + N_word bitmask; + N_word value; + boolean empty; + + if ((size == 0) or (start >= bits_(addr))) return(FALSE); + + *min = start; + *max = start; + + offset = start >> LOGBITS; + + if (offset >= size) return(FALSE); + + *(addr+size-1) &= mask; + + addr += offset; + size = ++offset; + + bitmask = BITMASKTAB[start AND MODMASK]; + mask = (bitmask - 1); + + value = *addr--; + if ((value AND bitmask) == 0) + { + value &= mask; + if (value == 0) + { + offset--; + empty = TRUE; + while (empty and (--size > 0)) + { + if ((value = *addr--)) empty = false; else offset--; + } + if (empty) return(FALSE); + } + start = offset << LOGBITS; + bitmask = MSB; + mask = value; + while (not (mask AND MSB)) + { + bitmask >>= 1; + mask <<= 1; + start--; + } + mask = (bitmask - 1); + *max = --start; + *min = start; + } + value = NOT value; + value &= mask; + if (value == 0) + { + offset--; + empty = TRUE; + while (empty and (--size > 0)) + { + if ((value = NOT *addr--)) empty = false; else offset--; + } + if (empty) value = MSB; + } + start = offset << LOGBITS; + while (not (value AND MSB)) + { + value <<= 1; + start--; + } + *min = start; + return(TRUE); +} + +void BitVector_Interval_Copy(wordptr X, wordptr Y, N_int Xoffset, + N_int Yoffset, N_int length) +{ + N_word bitsX = bits_(X); + N_word bitsY = bits_(Y); + N_word source = 0; /* silence compiler warning */ + N_word target = 0; /* silence compiler warning */ + N_word s_lo_base; + N_word s_hi_base; + N_word s_lo_bit; + N_word s_hi_bit; + N_word s_base; + N_word s_lower = 0; /* silence compiler warning */ + N_word s_upper = 0; /* silence compiler warning */ + N_word s_bits; + N_word s_min; + N_word s_max; + N_word t_lo_base; + N_word t_hi_base; + N_word t_lo_bit; + N_word t_hi_bit; + N_word t_base; + N_word t_lower = 0; /* silence compiler warning */ + N_word t_upper = 0; /* silence compiler warning */ + N_word t_bits; + N_word t_min; + N_word mask; + N_word bits; + N_word sel; + boolean ascending; + boolean notfirst; + wordptr Z = X; + + if ((length > 0) and (Xoffset < bitsX) and (Yoffset < bitsY)) + { + if ((Xoffset + length) > bitsX) length = bitsX - Xoffset; + if ((Yoffset + length) > bitsY) length = bitsY - Yoffset; + + ascending = (Xoffset <= Yoffset); + + s_lo_base = Yoffset >> LOGBITS; + s_lo_bit = Yoffset AND MODMASK; + Yoffset += --length; + s_hi_base = Yoffset >> LOGBITS; + s_hi_bit = Yoffset AND MODMASK; + + t_lo_base = Xoffset >> LOGBITS; + t_lo_bit = Xoffset AND MODMASK; + Xoffset += length; + t_hi_base = Xoffset >> LOGBITS; + t_hi_bit = Xoffset AND MODMASK; + + if (ascending) + { + s_base = s_lo_base; + t_base = t_lo_base; + } + else + { + s_base = s_hi_base; + t_base = t_hi_base; + } + s_bits = 0; + t_bits = 0; + Y += s_base; + X += t_base; + notfirst = FALSE; + while (TRUE) + { + if (t_bits == 0) + { + if (notfirst) + { + *X = target; + if (ascending) + { + if (t_base == t_hi_base) break; + t_base++; + X++; + } + else + { + if (t_base == t_lo_base) break; + t_base--; + X--; + } + } + sel = ((t_base == t_hi_base) << 1) OR (t_base == t_lo_base); + switch (sel) + { + case 0: + t_lower = 0; + t_upper = BITS - 1; + t_bits = BITS; + target = 0; + break; + case 1: + t_lower = t_lo_bit; + t_upper = BITS - 1; + t_bits = BITS - t_lo_bit; + mask = (N_word) (~0L << t_lower); + target = *X AND NOT mask; + break; + case 2: + t_lower = 0; + t_upper = t_hi_bit; + t_bits = t_hi_bit + 1; + mask = (N_word) ((~0L << t_upper) << 1); + target = *X AND mask; + break; + case 3: + t_lower = t_lo_bit; + t_upper = t_hi_bit; + t_bits = t_hi_bit - t_lo_bit + 1; + mask = (N_word) (~0L << t_lower); + mask &= (N_word) ~((~0L << t_upper) << 1); + target = *X AND NOT mask; + break; + } + } + if (s_bits == 0) + { + if (notfirst) + { + if (ascending) + { + if (s_base == s_hi_base) break; + s_base++; + Y++; + } + else + { + if (s_base == s_lo_base) break; + s_base--; + Y--; + } + } + source = *Y; + sel = ((s_base == s_hi_base) << 1) OR (s_base == s_lo_base); + switch (sel) + { + case 0: + s_lower = 0; + s_upper = BITS - 1; + s_bits = BITS; + break; + case 1: + s_lower = s_lo_bit; + s_upper = BITS - 1; + s_bits = BITS - s_lo_bit; + break; + case 2: + s_lower = 0; + s_upper = s_hi_bit; + s_bits = s_hi_bit + 1; + break; + case 3: + s_lower = s_lo_bit; + s_upper = s_hi_bit; + s_bits = s_hi_bit - s_lo_bit + 1; + break; + } + } + notfirst = TRUE; + if (s_bits > t_bits) + { + bits = t_bits - 1; + if (ascending) + { + s_min = s_lower; + s_max = s_lower + bits; + } + else + { + s_max = s_upper; + s_min = s_upper - bits; + } + t_min = t_lower; + } + else + { + bits = s_bits - 1; + if (ascending) t_min = t_lower; + else t_min = t_upper - bits; + s_min = s_lower; + s_max = s_upper; + } + bits++; + mask = (N_word) (~0L << s_min); + mask &= (N_word) ~((~0L << s_max) << 1); + if (s_min == t_min) target |= (source AND mask); + else + { + if (s_min < t_min) target |= (source AND mask) << (t_min-s_min); + else target |= (source AND mask) >> (s_min-t_min); + } + if (ascending) + { + s_lower += bits; + t_lower += bits; + } + else + { + s_upper -= bits; + t_upper -= bits; + } + s_bits -= bits; + t_bits -= bits; + } + *(Z+size_(Z)-1) &= mask_(Z); + } +} + + +wordptr BitVector_Interval_Substitute(wordptr X, wordptr Y, + N_int Xoffset, N_int Xlength, + N_int Yoffset, N_int Ylength) +{ + N_word Xbits = bits_(X); + N_word Ybits = bits_(Y); + N_word limit; + N_word diff; + + if ((Xoffset <= Xbits) and (Yoffset <= Ybits)) + { + limit = Xoffset + Xlength; + if (limit > Xbits) + { + limit = Xbits; + Xlength = Xbits - Xoffset; + } + if ((Yoffset + Ylength) > Ybits) + { + Ylength = Ybits - Yoffset; + } + if (Xlength == Ylength) + { + if ((Ylength > 0) and ((X != Y) or (Xoffset != Yoffset))) + { + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + } + else /* Xlength != Ylength */ + { + if (Xlength > Ylength) + { + diff = Xlength - Ylength; + if (Ylength > 0) BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + if (limit < Xbits) BitVector_Delete(X,Xoffset+Ylength,diff,FALSE); + if ((X = BitVector_Resize(X,Xbits-diff)) == NULL) return(NULL); + } + else /* Ylength > Xlength ==> Ylength > 0 */ + { + diff = Ylength - Xlength; + if (X != Y) + { + if ((X = BitVector_Resize(X,Xbits+diff)) == NULL) return(NULL); + if (limit < Xbits) BitVector_Insert(X,limit,diff,FALSE); + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* in-place */ + { + if ((Y = X = BitVector_Resize(X,Xbits+diff)) == NULL) return(NULL); + if (limit >= Xbits) + { + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* limit < Xbits */ + { + BitVector_Insert(X,limit,diff,FALSE); + if ((Yoffset+Ylength) <= limit) + { + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* overlaps or lies above critical area */ + { + if (limit <= Yoffset) + { + Yoffset += diff; + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* Yoffset < limit */ + { + Xlength = limit - Yoffset; + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Xlength); + Yoffset = Xoffset + Ylength; /* = limit + diff */ + Xoffset += Xlength; + Ylength -= Xlength; + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + } + } + } + } + } + } + return(X); +} + +boolean BitVector_is_empty(wordptr addr) /* X == {} ? */ +{ + N_word size = size_(addr); + boolean r = TRUE; + + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while (r and (size-- > 0)) r = ( *addr++ == 0 ); + } + return(r); +} + +boolean BitVector_is_full(wordptr addr) /* X == ~{} ? */ +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + boolean r = FALSE; + wordptr last; + + if (size > 0) + { + r = TRUE; + last = addr + size - 1; + *last |= NOT mask; + while (r and (size-- > 0)) r = ( NOT *addr++ == 0 ); + *last &= mask; + } + return(r); +} + +boolean BitVector_equal(wordptr X, wordptr Y) /* X == Y ? */ +{ + N_word size = size_(X); + N_word mask = mask_(X); + boolean r = FALSE; + + if (bits_(X) == bits_(Y)) + { + r = TRUE; + if (size > 0) + { + *(X+size-1) &= mask; + *(Y+size-1) &= mask; + while (r and (size-- > 0)) r = (*X++ == *Y++); + } + } + return(r); +} + +Z_int BitVector_Lexicompare(wordptr X, wordptr Y) /* X <,=,> Y ? */ +{ /* unsigned */ + N_word bitsX = bits_(X); + N_word bitsY = bits_(Y); + N_word size = size_(X); + boolean r = TRUE; + + if (bitsX == bitsY) + { + if (size > 0) + { + X += size; + Y += size; + while (r and (size-- > 0)) r = (*(--X) == *(--Y)); + } + if (r) return((Z_int) 0); + else + { + if (*X < *Y) return((Z_int) -1); else return((Z_int) 1); + } + } + else + { + if (bitsX < bitsY) return((Z_int) -1); else return((Z_int) 1); + } +} + +Z_int BitVector_Compare(wordptr X, wordptr Y) /* X <,=,> Y ? */ +{ /* signed */ + N_word bitsX = bits_(X); + N_word bitsY = bits_(Y); + N_word size = size_(X); + N_word mask = mask_(X); + N_word sign; + boolean r = TRUE; + + if (bitsX == bitsY) + { + if (size > 0) + { + X += size; + Y += size; + mask &= NOT (mask >> 1); + if ((sign = (*(X-1) AND mask)) != (*(Y-1) AND mask)) + { + if (sign) return((Z_int) -1); else return((Z_int) 1); + } + while (r and (size-- > 0)) r = (*(--X) == *(--Y)); + } + if (r) return((Z_int) 0); + else + { + if (*X < *Y) return((Z_int) -1); else return((Z_int) 1); + } + } + else + { + if (bitsX < bitsY) return((Z_int) -1); else return((Z_int) 1); + } +} + +charptr BitVector_to_Hex(wordptr addr) +{ + N_word bits = bits_(addr); + N_word size = size_(addr); + N_word value; + N_word count; + N_word digit; + N_word length; + charptr string; + + length = bits >> 2; + if (bits AND 0x0003) length++; + string = (charptr) yasm_xmalloc((size_t) (length+1)); + if (string == NULL) return(NULL); + string += length; + *string = (N_char) '\0'; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while ((size-- > 0) and (length > 0)) + { + value = *addr++; + count = BITS >> 2; + while ((count-- > 0) and (length > 0)) + { + digit = value AND 0x000F; + if (digit > 9) digit += (N_word) 'A' - 10; + else digit += (N_word) '0'; + *(--string) = (N_char) digit; length--; + if ((count > 0) and (length > 0)) value >>= 4; + } + } + } + return(string); +} + +ErrCode BitVector_from_Hex(wordptr addr, charptr string) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + boolean ok = TRUE; + size_t length; + N_word value; + N_word count; + int digit; + + if (size > 0) + { + length = strlen((char *) string); + string += length; + while (size-- > 0) + { + value = 0; + for ( count = 0; (ok and (length > 0) and (count < BITS)); count += 4 ) + { + digit = (int) *(--string); length--; + /* separate because toupper() is likely a macro! */ + digit = toupper(digit); + if (digit == '_') + count -= 4; + else if ((ok = (isxdigit(digit) != 0))) + { + if (digit >= (int) 'A') digit -= (int) 'A' - 10; + else digit -= (int) '0'; + value |= (((N_word) digit) << count); + } + } + *addr++ = value; + } + *(--addr) &= mask; + } + if (ok) return(ErrCode_Ok); + else return(ErrCode_Pars); +} + +ErrCode BitVector_from_Oct(wordptr addr, charptr string) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + boolean ok = TRUE; + size_t length; + N_word value; + N_word value_fill = 0; + N_word count; + Z_word count_fill = 0; + int digit = 0; + + if (size > 0) + { + length = strlen((char *) string); + string += length; + while (size-- > 0) + { + value = value_fill; + for ( count = count_fill; (ok and (length > 0) and (count < BITS)); count += 3 ) + { + digit = (int) *(--string); length--; + if (digit == '_') + count -= 3; + else if ((ok = (isdigit(digit) && digit != '8' && digit != '9')) != 0) + { + digit -= (int) '0'; + value |= (((N_word) digit) << count); + } + } + count_fill = (Z_word)count-(Z_word)BITS; + if (count_fill > 0) + value_fill = (((N_word) digit) >> (3-count_fill)); + else + value_fill = 0; + *addr++ = value; + } + *(--addr) &= mask; + } + if (ok) return(ErrCode_Ok); + else return(ErrCode_Pars); +} + +charptr BitVector_to_Bin(wordptr addr) +{ + N_word size = size_(addr); + N_word value; + N_word count; + N_word digit; + N_word length; + charptr string; + + length = bits_(addr); + string = (charptr) yasm_xmalloc((size_t) (length+1)); + if (string == NULL) return(NULL); + string += length; + *string = (N_char) '\0'; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while (size-- > 0) + { + value = *addr++; + count = BITS; + if (count > length) count = length; + while (count-- > 0) + { + digit = value AND 0x0001; + digit += (N_word) '0'; + *(--string) = (N_char) digit; length--; + if (count > 0) value >>= 1; + } + } + } + return(string); +} + +ErrCode BitVector_from_Bin(wordptr addr, charptr string) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + boolean ok = TRUE; + size_t length; + N_word value; + N_word count; + int digit; + + if (size > 0) + { + length = strlen((char *) string); + string += length; + while (size-- > 0) + { + value = 0; + for ( count = 0; (ok and (length > 0) and (count < BITS)); count++ ) + { + digit = (int) *(--string); length--; + switch (digit) + { + case (int) '0': + break; + case (int) '1': + value |= BITMASKTAB[count]; + break; + case (int) '_': + count--; + break; + default: + ok = FALSE; + break; + } + } + *addr++ = value; + } + *(--addr) &= mask; + } + if (ok) return(ErrCode_Ok); + else return(ErrCode_Pars); +} + +charptr BitVector_to_Dec(wordptr addr) +{ + N_word bits = bits_(addr); + N_word length; + N_word digits; + N_word count; + N_word q; + N_word r; + boolean loop; + charptr result; + charptr string; + wordptr quot; + wordptr rest; + wordptr temp; + wordptr base; + Z_int sign; + + length = (N_word) (bits / 3.3); /* digits = bits * ln(2) / ln(10) */ + length += 2; /* compensate for truncating & provide space for minus sign */ + result = (charptr) yasm_xmalloc((size_t) (length+1)); /* remember the '\0'! */ + if (result == NULL) return(NULL); + string = result; + sign = BitVector_Sign(addr); + if ((bits < 4) or (sign == 0)) + { + if (bits > 0) digits = *addr; else digits = (N_word) 0; + if (sign < 0) digits = ((N_word)(-((Z_word)digits))) AND mask_(addr); + *string++ = (N_char) digits + (N_char) '0'; + digits = 1; + } + else + { + quot = BitVector_Create(bits,FALSE); + if (quot == NULL) + { + BitVector_Dispose(result); + return(NULL); + } + rest = BitVector_Create(bits,FALSE); + if (rest == NULL) + { + BitVector_Dispose(result); + BitVector_Destroy(quot); + return(NULL); + } + temp = BitVector_Create(bits,FALSE); + if (temp == NULL) + { + BitVector_Dispose(result); + BitVector_Destroy(quot); + BitVector_Destroy(rest); + return(NULL); + } + base = BitVector_Create(bits,TRUE); + if (base == NULL) + { + BitVector_Dispose(result); + BitVector_Destroy(quot); + BitVector_Destroy(rest); + BitVector_Destroy(temp); + return(NULL); + } + if (sign < 0) BitVector_Negate(quot,addr); + else BitVector_Copy(quot,addr); + digits = 0; + *base = EXP10; + loop = (bits >= BITS); + do + { + if (loop) + { + BitVector_Copy(temp,quot); + if (BitVector_Div_Pos(quot,temp,base,rest)) + { + BitVector_Dispose(result); /* emergency exit */ + BitVector_Destroy(quot); + BitVector_Destroy(rest); /* should never occur */ + BitVector_Destroy(temp); /* under normal operation */ + BitVector_Destroy(base); + return(NULL); + } + loop = not BitVector_is_empty(quot); + q = *rest; + } + else q = *quot; + count = LOG10; + while (((loop and (count-- > 0)) or ((not loop) and (q != 0))) and + (digits < length)) + { + if (q != 0) + { + BIT_VECTOR_DIGITIZE(N_word,q,r) + } + else r = (N_word) '0'; + *string++ = (N_char) r; + digits++; + } + } + while (loop and (digits < length)); + BitVector_Destroy(quot); + BitVector_Destroy(rest); + BitVector_Destroy(temp); + BitVector_Destroy(base); + } + if ((sign < 0) and (digits < length)) + { + *string++ = (N_char) '-'; + digits++; + } + *string = (N_char) '\0'; + BIT_VECTOR_reverse(result,digits); + return(result); +} + +struct BitVector_from_Dec_static_data { + wordptr term; + wordptr base; + wordptr prod; + wordptr rank; + wordptr temp; +}; + +BitVector_from_Dec_static_data *BitVector_from_Dec_static_Boot(N_word bits) +{ + BitVector_from_Dec_static_data *data; + + data = yasm_xmalloc(sizeof(BitVector_from_Dec_static_data)); + + if (bits > 0) + { + data->term = BitVector_Create(BITS,FALSE); + data->base = BitVector_Create(BITS,FALSE); + data->prod = BitVector_Create(bits,FALSE); + data->rank = BitVector_Create(bits,FALSE); + data->temp = BitVector_Create(bits,FALSE); + } else { + data->term = NULL; + data->base = NULL; + data->prod = NULL; + data->rank = NULL; + data->temp = NULL; + } + return data; +} + +void BitVector_from_Dec_static_Shutdown(BitVector_from_Dec_static_data *data) +{ + if (data) { + BitVector_Destroy(data->term); + BitVector_Destroy(data->base); + BitVector_Destroy(data->prod); + BitVector_Destroy(data->rank); + BitVector_Destroy(data->temp); + } + yasm_xfree(data); +} + +ErrCode BitVector_from_Dec_static(BitVector_from_Dec_static_data *data, + wordptr addr, charptr string) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(addr); + N_word mask = mask_(addr); + boolean init = (bits > BITS); + boolean minus; + boolean shift; + boolean carry; + wordptr term; + wordptr base; + wordptr prod; + wordptr rank; + wordptr temp; + N_word accu; + N_word powr; + N_word count; + size_t length; + int digit; + + if (bits > 0) + { + term = data->term; + base = data->base; + prod = data->prod; + rank = data->rank; + temp = data->temp; + + length = strlen((char *) string); + if (length == 0) return(ErrCode_Pars); + digit = (int) *string; + if ((minus = (digit == (int) '-')) or + (digit == (int) '+')) + { + string++; + if (--length == 0) return(ErrCode_Pars); + } + string += length; + if (init) + { + BitVector_Empty(prod); + BitVector_Empty(rank); + } + BitVector_Empty(addr); + *base = EXP10; + shift = FALSE; + while ((not error) and (length > 0)) + { + accu = 0; + powr = 1; + count = LOG10; + while ((not error) and (length > 0) and (count-- > 0)) + { + digit = (int) *(--string); length--; + /* separate because isdigit() is likely a macro! */ + if (isdigit(digit) != 0) + { + accu += ((N_word) digit - (N_word) '0') * powr; + powr *= 10; + } + else error = ErrCode_Pars; + } + if (not error) + { + if (shift) + { + *term = accu; + BitVector_Copy(temp,rank); + error = BitVector_Mul_Pos(prod,temp,term,FALSE); + } + else + { + *prod = accu; + if ((not init) and ((accu AND NOT mask) != 0)) error = ErrCode_Ovfl; + } + if (not error) + { + carry = FALSE; + BitVector_compute(addr,addr,prod,FALSE,&carry); + /* ignores sign change (= overflow) but not */ + /* numbers too large (= carry) for resulting bit vector */ + if (carry) error = ErrCode_Ovfl; + else + { + if (length > 0) + { + if (shift) + { + BitVector_Copy(temp,rank); + error = BitVector_Mul_Pos(rank,temp,base,FALSE); + } + else + { + *rank = *base; + shift = TRUE; + } + } + } + } + } + } + if (not error and minus) + { + BitVector_Negate(addr,addr); + if ((*(addr + size_(addr) - 1) AND mask AND NOT (mask >> 1)) == 0) + error = ErrCode_Ovfl; + } + } + return(error); +} + +ErrCode BitVector_from_Dec(wordptr addr, charptr string) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(addr); + N_word mask = mask_(addr); + boolean init = (bits > BITS); + boolean minus; + boolean shift; + boolean carry; + wordptr term; + wordptr base; + wordptr prod; + wordptr rank; + wordptr temp; + N_word accu; + N_word powr; + N_word count; + size_t length; + int digit; + + if (bits > 0) + { + length = strlen((char *) string); + if (length == 0) return(ErrCode_Pars); + digit = (int) *string; + if ((minus = (digit == (int) '-')) or + (digit == (int) '+')) + { + string++; + if (--length == 0) return(ErrCode_Pars); + } + string += length; + term = BitVector_Create(BITS,FALSE); + if (term == NULL) + { + return(ErrCode_Null); + } + base = BitVector_Create(BITS,FALSE); + if (base == NULL) + { + BitVector_Destroy(term); + return(ErrCode_Null); + } + prod = BitVector_Create(bits,init); + if (prod == NULL) + { + BitVector_Destroy(term); + BitVector_Destroy(base); + return(ErrCode_Null); + } + rank = BitVector_Create(bits,init); + if (rank == NULL) + { + BitVector_Destroy(term); + BitVector_Destroy(base); + BitVector_Destroy(prod); + return(ErrCode_Null); + } + temp = BitVector_Create(bits,FALSE); + if (temp == NULL) + { + BitVector_Destroy(term); + BitVector_Destroy(base); + BitVector_Destroy(prod); + BitVector_Destroy(rank); + return(ErrCode_Null); + } + BitVector_Empty(addr); + *base = EXP10; + shift = FALSE; + while ((not error) and (length > 0)) + { + accu = 0; + powr = 1; + count = LOG10; + while ((not error) and (length > 0) and (count-- > 0)) + { + digit = (int) *(--string); length--; + /* separate because isdigit() is likely a macro! */ + if (isdigit(digit) != 0) + { + accu += ((N_word) digit - (N_word) '0') * powr; + powr *= 10; + } + else error = ErrCode_Pars; + } + if (not error) + { + if (shift) + { + *term = accu; + BitVector_Copy(temp,rank); + error = BitVector_Mul_Pos(prod,temp,term,FALSE); + } + else + { + *prod = accu; + if ((not init) and ((accu AND NOT mask) != 0)) error = ErrCode_Ovfl; + } + if (not error) + { + carry = FALSE; + BitVector_compute(addr,addr,prod,FALSE,&carry); + /* ignores sign change (= overflow) but not */ + /* numbers too large (= carry) for resulting bit vector */ + if (carry) error = ErrCode_Ovfl; + else + { + if (length > 0) + { + if (shift) + { + BitVector_Copy(temp,rank); + error = BitVector_Mul_Pos(rank,temp,base,FALSE); + } + else + { + *rank = *base; + shift = TRUE; + } + } + } + } + } + } + BitVector_Destroy(term); + BitVector_Destroy(base); + BitVector_Destroy(prod); + BitVector_Destroy(rank); + BitVector_Destroy(temp); + if (not error and minus) + { + BitVector_Negate(addr,addr); + if ((*(addr + size_(addr) - 1) AND mask AND NOT (mask >> 1)) == 0) + error = ErrCode_Ovfl; + } + } + return(error); +} + +charptr BitVector_to_Enum(wordptr addr) +{ + N_word bits = bits_(addr); + N_word sample; + N_word length; + N_word digits; + N_word factor; + N_word power; + N_word start; + N_word min; + N_word max; + charptr string; + charptr target; + boolean comma; + + if (bits > 0) + { + sample = bits - 1; /* greatest possible index */ + length = 2; /* account for index 0 and terminating '\0' */ + digits = 1; /* account for intervening dashes and commas */ + factor = 1; + power = 10; + while (sample >= (power-1)) + { + length += ++digits * factor * 6; /* 9,90,900,9000,... (9*2/3 = 6) */ + factor = power; + power *= 10; + } + if (sample > --factor) + { + sample -= factor; + factor = (N_word) ( sample / 3 ); + factor = (factor << 1) + (sample - (factor * 3)); + length += ++digits * factor; + } + } + else length = 1; + string = (charptr) yasm_xmalloc((size_t) length); + if (string == NULL) return(NULL); + start = 0; + comma = FALSE; + target = string; + while ((start < bits) and BitVector_interval_scan_inc(addr,start,&min,&max)) + { + start = max + 2; + if (comma) *target++ = (N_char) ','; + if (min == max) + { + target += BIT_VECTOR_int2str(target,min); + } + else + { + if (min+1 == max) + { + target += BIT_VECTOR_int2str(target,min); + *target++ = (N_char) ','; + target += BIT_VECTOR_int2str(target,max); + } + else + { + target += BIT_VECTOR_int2str(target,min); + *target++ = (N_char) '-'; + target += BIT_VECTOR_int2str(target,max); + } + } + comma = TRUE; + } + *target = (N_char) '\0'; + return(string); +} + +ErrCode BitVector_from_Enum(wordptr addr, charptr string) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(addr); + N_word state = 1; + N_word token; + N_word indx = 0; /* silence compiler warning */ + N_word start = 0; /* silence compiler warning */ + + if (bits > 0) + { + BitVector_Empty(addr); + while ((not error) and (state != 0)) + { + token = (N_word) *string; + /* separate because isdigit() is likely a macro! */ + if (isdigit((int)token) != 0) + { + string += BIT_VECTOR_str2int(string,&indx); + if (indx < bits) token = (N_word) '0'; + else error = ErrCode_Indx; + } + else string++; + if (not error) + switch (state) + { + case 1: + switch (token) + { + case (N_word) '0': + state = 2; + break; + case (N_word) '\0': + state = 0; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 2: + switch (token) + { + case (N_word) '-': + start = indx; + state = 3; + break; + case (N_word) ',': + BIT_VECTOR_SET_BIT(addr,indx) + state = 5; + break; + case (N_word) '\0': + BIT_VECTOR_SET_BIT(addr,indx) + state = 0; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 3: + switch (token) + { + case (N_word) '0': + if (start < indx) + BitVector_Interval_Fill(addr,start,indx); + else if (start == indx) + BIT_VECTOR_SET_BIT(addr,indx) + else error = ErrCode_Ordr; + state = 4; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 4: + switch (token) + { + case (N_word) ',': + state = 5; + break; + case (N_word) '\0': + state = 0; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 5: + switch (token) + { + case (N_word) '0': + state = 2; + break; + default: + error = ErrCode_Pars; + break; + } + break; + } + } + } + return(error); +} + +void BitVector_Bit_Off(wordptr addr, N_int indx) /* X = X \ {x} */ +{ + if (indx < bits_(addr)) BIT_VECTOR_CLR_BIT(addr,indx) +} + +void BitVector_Bit_On(wordptr addr, N_int indx) /* X = X + {x} */ +{ + if (indx < bits_(addr)) BIT_VECTOR_SET_BIT(addr,indx) +} + +boolean BitVector_bit_flip(wordptr addr, N_int indx) /* X=(X+{x})\(X*{x}) */ +{ + N_word mask; + + if (indx < bits_(addr)) return( BIT_VECTOR_FLP_BIT(addr,indx,mask) ); + else return( FALSE ); +} + +boolean BitVector_bit_test(wordptr addr, N_int indx) /* {x} in X ? */ +{ + if (indx < bits_(addr)) return( BIT_VECTOR_TST_BIT(addr,indx) ); + else return( FALSE ); +} + +void BitVector_Bit_Copy(wordptr addr, N_int indx, boolean bit) +{ + if (indx < bits_(addr)) + { + if (bit) BIT_VECTOR_SET_BIT(addr,indx) + else BIT_VECTOR_CLR_BIT(addr,indx) + } +} + +void BitVector_LSB(wordptr addr, boolean bit) +{ + if (bits_(addr) > 0) + { + if (bit) *addr |= LSB; + else *addr &= NOT LSB; + } +} + +void BitVector_MSB(wordptr addr, boolean bit) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + + if (size-- > 0) + { + if (bit) *(addr+size) |= mask AND NOT (mask >> 1); + else *(addr+size) &= NOT mask OR (mask >> 1); + } +} + +boolean BitVector_lsb_(wordptr addr) +{ + if (size_(addr) > 0) return( (*addr AND LSB) != 0 ); + else return( FALSE ); +} + +boolean BitVector_msb_(wordptr addr) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + + if (size-- > 0) + return( (*(addr+size) AND (mask AND NOT (mask >> 1))) != 0 ); + else + return( FALSE ); +} + +boolean BitVector_rotate_left(wordptr addr) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word msb; + boolean carry_in; + boolean carry_out = FALSE; + + if (size > 0) + { + msb = mask AND NOT (mask >> 1); + carry_in = ((*(addr+size-1) AND msb) != 0); + while (size-- > 1) + { + carry_out = ((*addr AND MSB) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + carry_in = carry_out; + addr++; + } + carry_out = ((*addr AND msb) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + *addr &= mask; + } + return(carry_out); +} + +boolean BitVector_rotate_right(wordptr addr) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word msb; + boolean carry_in; + boolean carry_out = FALSE; + + if (size > 0) + { + msb = mask AND NOT (mask >> 1); + carry_in = ((*addr AND LSB) != 0); + addr += size-1; + *addr &= mask; + carry_out = ((*addr AND LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= msb; + carry_in = carry_out; + addr--; + size--; + while (size-- > 0) + { + carry_out = ((*addr AND LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= MSB; + carry_in = carry_out; + addr--; + } + } + return(carry_out); +} + +boolean BitVector_shift_left(wordptr addr, boolean carry_in) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word msb; + boolean carry_out = carry_in; + + if (size > 0) + { + msb = mask AND NOT (mask >> 1); + while (size-- > 1) + { + carry_out = ((*addr AND MSB) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + carry_in = carry_out; + addr++; + } + carry_out = ((*addr AND msb) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + *addr &= mask; + } + return(carry_out); +} + +boolean BitVector_shift_right(wordptr addr, boolean carry_in) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word msb; + boolean carry_out = carry_in; + + if (size > 0) + { + msb = mask AND NOT (mask >> 1); + addr += size-1; + *addr &= mask; + carry_out = ((*addr AND LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= msb; + carry_in = carry_out; + addr--; + size--; + while (size-- > 0) + { + carry_out = ((*addr AND LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= MSB; + carry_in = carry_out; + addr--; + } + } + return(carry_out); +} + +void BitVector_Move_Left(wordptr addr, N_int bits) +{ + N_word count; + N_word words; + + if (bits > 0) + { + count = bits AND MODMASK; + words = bits >> LOGBITS; + if (bits >= bits_(addr)) BitVector_Empty(addr); + else + { + while (count-- > 0) BitVector_shift_left(addr,0); + BitVector_Word_Insert(addr,0,words,TRUE); + } + } +} + +void BitVector_Move_Right(wordptr addr, N_int bits) +{ + N_word count; + N_word words; + + if (bits > 0) + { + count = bits AND MODMASK; + words = bits >> LOGBITS; + if (bits >= bits_(addr)) BitVector_Empty(addr); + else + { + while (count-- > 0) BitVector_shift_right(addr,0); + BitVector_Word_Delete(addr,0,words,TRUE); + } + } +} + +void BitVector_Insert(wordptr addr, N_int offset, N_int count, boolean clear) +{ + N_word bits = bits_(addr); + N_word last; + + if ((count > 0) and (offset < bits)) + { + last = offset + count; + if (last < bits) + { + BitVector_Interval_Copy(addr,addr,last,offset,(bits-last)); + } + else last = bits; + if (clear) BitVector_Interval_Empty(addr,offset,(last-1)); + } +} + +void BitVector_Delete(wordptr addr, N_int offset, N_int count, boolean clear) +{ + N_word bits = bits_(addr); + N_word last; + + if ((count > 0) and (offset < bits)) + { + last = offset + count; + if (last < bits) + { + BitVector_Interval_Copy(addr,addr,offset,last,(bits-last)); + } + else count = bits - offset; + if (clear) BitVector_Interval_Empty(addr,(bits-count),(bits-1)); + } +} + +boolean BitVector_increment(wordptr addr) /* X++ */ +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + wordptr last = addr + size - 1; + boolean carry = TRUE; + + if (size > 0) + { + *last |= NOT mask; + while (carry and (size-- > 0)) + { + carry = (++(*addr++) == 0); + } + *last &= mask; + } + return(carry); +} + +boolean BitVector_decrement(wordptr addr) /* X-- */ +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + wordptr last = addr + size - 1; + boolean carry = TRUE; + + if (size > 0) + { + *last &= mask; + while (carry and (size-- > 0)) + { + carry = (*addr == 0); + --(*addr++); + } + *last &= mask; + } + return(carry); +} + +boolean BitVector_compute(wordptr X, wordptr Y, wordptr Z, boolean minus, boolean *carry) +{ + N_word size = size_(X); + N_word mask = mask_(X); + N_word vv = 0; + N_word cc; + N_word mm; + N_word yy; + N_word zz; + N_word lo; + N_word hi; + + if (size > 0) + { + if (minus) cc = (*carry == 0); + else cc = (*carry != 0); + /* deal with (size-1) least significant full words first: */ + while (--size > 0) + { + yy = *Y++; + if (minus) zz = (N_word) NOT ( Z ? *Z++ : 0 ); + else zz = (N_word) ( Z ? *Z++ : 0 ); + lo = (yy AND LSB) + (zz AND LSB) + cc; + hi = (yy >> 1) + (zz >> 1) + (lo >> 1); + cc = ((hi AND MSB) != 0); + *X++ = (hi << 1) OR (lo AND LSB); + } + /* deal with most significant word (may be used only partially): */ + yy = *Y AND mask; + if (minus) zz = (N_word) NOT ( Z ? *Z : 0 ); + else zz = (N_word) ( Z ? *Z : 0 ); + zz &= mask; + if (mask == LSB) /* special case, only one bit used */ + { + vv = cc; + lo = yy + zz + cc; + cc = (lo >> 1); + vv ^= cc; + *X = lo AND LSB; + } + else + { + if (NOT mask) /* not all bits are used, but more than one */ + { + mm = (mask >> 1); + vv = (yy AND mm) + (zz AND mm) + cc; + mm = mask AND NOT mm; + lo = yy + zz + cc; + cc = (lo >> 1); + vv ^= cc; + vv &= mm; + cc &= mm; + *X = lo AND mask; + } + else /* other special case, all bits are used */ + { + mm = NOT MSB; + lo = (yy AND mm) + (zz AND mm) + cc; + vv = lo AND MSB; + hi = ((yy AND MSB) >> 1) + ((zz AND MSB) >> 1) + (vv >> 1); + cc = hi AND MSB; + vv ^= cc; + *X = (hi << 1) OR (lo AND mm); + } + } + if (minus) *carry = (cc == 0); + else *carry = (cc != 0); + } + return(vv != 0); +} + +boolean BitVector_add(wordptr X, wordptr Y, wordptr Z, boolean *carry) +{ + return(BitVector_compute(X,Y,Z,FALSE,carry)); +} + +boolean BitVector_sub(wordptr X, wordptr Y, wordptr Z, boolean *carry) +{ + return(BitVector_compute(X,Y,Z,TRUE,carry)); +} + +boolean BitVector_inc(wordptr X, wordptr Y) +{ + boolean carry = TRUE; + + return(BitVector_compute(X,Y,NULL,FALSE,&carry)); +} + +boolean BitVector_dec(wordptr X, wordptr Y) +{ + boolean carry = TRUE; + + return(BitVector_compute(X,Y,NULL,TRUE,&carry)); +} + +void BitVector_Negate(wordptr X, wordptr Y) +{ + N_word size = size_(X); + N_word mask = mask_(X); + boolean carry = TRUE; + + if (size > 0) + { + while (size-- > 0) + { + *X = NOT *Y++; + if (carry) + { + carry = (++(*X) == 0); + } + X++; + } + *(--X) &= mask; + } +} + +void BitVector_Absolute(wordptr X, wordptr Y) +{ + N_word size = size_(Y); + N_word mask = mask_(Y); + + if (size > 0) + { + if (*(Y+size-1) AND (mask AND NOT (mask >> 1))) BitVector_Negate(X,Y); + else BitVector_Copy(X,Y); + } +} + +Z_int BitVector_Sign(wordptr addr) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + wordptr last = addr + size - 1; + boolean r = TRUE; + + if (size > 0) + { + *last &= mask; + while (r and (size-- > 0)) r = ( *addr++ == 0 ); + } + if (r) return((Z_int) 0); + else + { + if (*last AND (mask AND NOT (mask >> 1))) return((Z_int) -1); + else return((Z_int) 1); + } +} + +ErrCode BitVector_Mul_Pos(wordptr X, wordptr Y, wordptr Z, boolean strict) +{ + N_word mask; + N_word limit; + N_word count; + Z_long last; + wordptr sign; + boolean carry; + boolean overflow; + boolean ok = TRUE; + + /* + Requirements: + - X, Y and Z must be distinct + - X and Y must have equal sizes (whereas Z may be any size!) + - Z should always contain the SMALLER of the two factors Y and Z + Constraints: + - The contents of Y (and of X, of course) are destroyed + (only Z is preserved!) + */ + + if ((X == Y) or (X == Z) or (Y == Z)) return(ErrCode_Same); + if (bits_(X) != bits_(Y)) return(ErrCode_Size); + BitVector_Empty(X); + if (BitVector_is_empty(Y)) return(ErrCode_Ok); /* exit also taken if bits_(Y)==0 */ + if ((last = Set_Max(Z)) < 0L) return(ErrCode_Ok); + limit = (N_word) last; + sign = Y + size_(Y) - 1; + mask = mask_(Y); + *sign &= mask; + mask &= NOT (mask >> 1); + for ( count = 0; (ok and (count <= limit)); count++ ) + { + if ( BIT_VECTOR_TST_BIT(Z,count) ) + { + carry = false; + overflow = BitVector_compute(X,X,Y,false,&carry); + if (strict) ok = not (carry or overflow); + else ok = not carry; + } + if (ok and (count < limit)) + { + carry = BitVector_shift_left(Y,0); + if (strict) + { + overflow = ((*sign AND mask) != 0); + ok = not (carry or overflow); + } + else ok = not carry; + } + } + if (ok) return(ErrCode_Ok); else return(ErrCode_Ovfl); +} + +ErrCode BitVector_Multiply(wordptr X, wordptr Y, wordptr Z) +{ + ErrCode error = ErrCode_Ok; + N_word bit_x = bits_(X); + N_word bit_y = bits_(Y); + N_word bit_z = bits_(Z); + N_word size; + N_word mask; + N_word msb; + wordptr ptr_y; + wordptr ptr_z; + boolean sgn_x; + boolean sgn_y; + boolean sgn_z; + boolean zero; + wordptr A; + wordptr B; + + /* + Requirements: + - Y and Z must have equal sizes + - X must have at least the same size as Y and Z but may be larger (!) + Features: + - The contents of Y and Z are preserved + - X may be identical with Y or Z (or both!) + (in-place multiplication is possible!) + */ + + if ((bit_y != bit_z) or (bit_x < bit_y)) return(ErrCode_Size); + if (BitVector_is_empty(Y) or BitVector_is_empty(Z)) + { + BitVector_Empty(X); + } + else + { + A = BitVector_Create(bit_y,FALSE); + if (A == NULL) return(ErrCode_Null); + B = BitVector_Create(bit_z,FALSE); + if (B == NULL) { BitVector_Destroy(A); return(ErrCode_Null); } + size = size_(Y); + mask = mask_(Y); + msb = (mask AND NOT (mask >> 1)); + sgn_y = (((*(Y+size-1) &= mask) AND msb) != 0); + sgn_z = (((*(Z+size-1) &= mask) AND msb) != 0); + sgn_x = sgn_y XOR sgn_z; + if (sgn_y) BitVector_Negate(A,Y); else BitVector_Copy(A,Y); + if (sgn_z) BitVector_Negate(B,Z); else BitVector_Copy(B,Z); + ptr_y = A + size; + ptr_z = B + size; + zero = TRUE; + while (zero and (size-- > 0)) + { + zero &= (*(--ptr_y) == 0); + zero &= (*(--ptr_z) == 0); + } + if (*ptr_y > *ptr_z) + { + if (bit_x > bit_y) + { + A = BitVector_Resize(A,bit_x); + if (A == NULL) { BitVector_Destroy(B); return(ErrCode_Null); } + } + error = BitVector_Mul_Pos(X,A,B,TRUE); + } + else + { + if (bit_x > bit_z) + { + B = BitVector_Resize(B,bit_x); + if (B == NULL) { BitVector_Destroy(A); return(ErrCode_Null); } + } + error = BitVector_Mul_Pos(X,B,A,TRUE); + } + if ((not error) and sgn_x) BitVector_Negate(X,X); + BitVector_Destroy(A); + BitVector_Destroy(B); + } + return(error); +} + +ErrCode BitVector_Div_Pos(wordptr Q, wordptr X, wordptr Y, wordptr R) +{ + N_word bits = bits_(Q); + N_word mask; + wordptr addr; + Z_long last; + boolean flag; + boolean copy = FALSE; /* flags whether valid rest is in R (0) or X (1) */ + + /* + Requirements: + - All bit vectors must have equal sizes + - Q, X, Y and R must all be distinct bit vectors + - Y must be non-zero (of course!) + Constraints: + - The contents of X (and Q and R, of course) are destroyed + (only Y is preserved!) + */ + + if ((bits != bits_(X)) or (bits != bits_(Y)) or (bits != bits_(R))) + return(ErrCode_Size); + if ((Q == X) or (Q == Y) or (Q == R) or (X == Y) or (X == R) or (Y == R)) + return(ErrCode_Same); + if (BitVector_is_empty(Y)) + return(ErrCode_Zero); + + BitVector_Empty(R); + BitVector_Copy(Q,X); + if ((last = Set_Max(Q)) < 0L) return(ErrCode_Ok); + bits = (N_word) ++last; + while (bits-- > 0) + { + addr = Q + (bits >> LOGBITS); + mask = BITMASKTAB[bits AND MODMASK]; + flag = ((*addr AND mask) != 0); + if (copy) + { + BitVector_shift_left(X,flag); + flag = FALSE; + BitVector_compute(R,X,Y,TRUE,&flag); + } + else + { + BitVector_shift_left(R,flag); + flag = FALSE; + BitVector_compute(X,R,Y,TRUE,&flag); + } + if (flag) *addr &= NOT mask; + else + { + *addr |= mask; + copy = not copy; + } + } + if (copy) BitVector_Copy(R,X); + return(ErrCode_Ok); +} + +ErrCode BitVector_Divide(wordptr Q, wordptr X, wordptr Y, wordptr R) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(Q); + N_word size = size_(Q); + N_word mask = mask_(Q); + N_word msb = (mask AND NOT (mask >> 1)); + boolean sgn_q; + boolean sgn_x; + boolean sgn_y; + wordptr A; + wordptr B; + + /* + Requirements: + - All bit vectors must have equal sizes + - Q and R must be two distinct bit vectors + - Y must be non-zero (of course!) + Features: + - The contents of X and Y are preserved + - Q may be identical with X or Y (or both) + (in-place division is possible!) + - R may be identical with X or Y (or both) + (but not identical with Q!) + */ + + if ((bits != bits_(X)) or (bits != bits_(Y)) or (bits != bits_(R))) + return(ErrCode_Size); + if (Q == R) + return(ErrCode_Same); + if (BitVector_is_empty(Y)) + return(ErrCode_Zero); + + if (BitVector_is_empty(X)) + { + BitVector_Empty(Q); + BitVector_Empty(R); + } + else + { + A = BitVector_Create(bits,FALSE); + if (A == NULL) return(ErrCode_Null); + B = BitVector_Create(bits,FALSE); + if (B == NULL) { BitVector_Destroy(A); return(ErrCode_Null); } + size--; + sgn_x = (((*(X+size) &= mask) AND msb) != 0); + sgn_y = (((*(Y+size) &= mask) AND msb) != 0); + sgn_q = sgn_x XOR sgn_y; + if (sgn_x) BitVector_Negate(A,X); else BitVector_Copy(A,X); + if (sgn_y) BitVector_Negate(B,Y); else BitVector_Copy(B,Y); + if (not (error = BitVector_Div_Pos(Q,A,B,R))) + { + if (sgn_q) BitVector_Negate(Q,Q); + if (sgn_x) BitVector_Negate(R,R); + } + BitVector_Destroy(A); + BitVector_Destroy(B); + } + return(error); +} + +ErrCode BitVector_GCD(wordptr X, wordptr Y, wordptr Z) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(X); + N_word size = size_(X); + N_word mask = mask_(X); + N_word msb = (mask AND NOT (mask >> 1)); + boolean sgn_a; + boolean sgn_b; + boolean sgn_r; + wordptr Q; + wordptr R; + wordptr A; + wordptr B; + wordptr T; + + /* + Requirements: + - All bit vectors must have equal sizes + Features: + - The contents of Y and Z are preserved + - X may be identical with Y or Z (or both) + (in-place is possible!) + - GCD(0,z) == GCD(z,0) == z + - negative values are handled correctly + */ + + if ((bits != bits_(Y)) or (bits != bits_(Z))) return(ErrCode_Size); + if (BitVector_is_empty(Y)) + { + if (X != Z) BitVector_Copy(X,Z); + return(ErrCode_Ok); + } + if (BitVector_is_empty(Z)) + { + if (X != Y) BitVector_Copy(X,Y); + return(ErrCode_Ok); + } + Q = BitVector_Create(bits,false); + if (Q == NULL) + { + return(ErrCode_Null); + } + R = BitVector_Create(bits,FALSE); + if (R == NULL) + { + BitVector_Destroy(Q); + return(ErrCode_Null); + } + A = BitVector_Create(bits,FALSE); + if (A == NULL) + { + BitVector_Destroy(Q); + BitVector_Destroy(R); + return(ErrCode_Null); + } + B = BitVector_Create(bits,FALSE); + if (B == NULL) + { + BitVector_Destroy(Q); + BitVector_Destroy(R); + BitVector_Destroy(A); + return(ErrCode_Null); + } + size--; + sgn_a = (((*(Y+size) &= mask) AND msb) != 0); + sgn_b = (((*(Z+size) &= mask) AND msb) != 0); + if (sgn_a) BitVector_Negate(A,Y); else BitVector_Copy(A,Y); + if (sgn_b) BitVector_Negate(B,Z); else BitVector_Copy(B,Z); + while (not error) + { + if (not (error = BitVector_Div_Pos(Q,A,B,R))) + { + if (BitVector_is_empty(R)) break; + T = A; sgn_r = sgn_a; + A = B; sgn_a = sgn_b; + B = R; sgn_b = sgn_r; + R = T; + } + } + if (not error) + { + if (sgn_b) BitVector_Negate(X,B); else BitVector_Copy(X,B); + } + BitVector_Destroy(Q); + BitVector_Destroy(R); + BitVector_Destroy(A); + BitVector_Destroy(B); + return(error); +} + +ErrCode BitVector_GCD2(wordptr U, wordptr V, wordptr W, wordptr X, wordptr Y) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(U); + N_word size = size_(U); + N_word mask = mask_(U); + N_word msb = (mask AND NOT (mask >> 1)); + boolean minus; + boolean carry; + boolean sgn_q; + boolean sgn_r; + boolean sgn_a; + boolean sgn_b; + boolean sgn_x; + boolean sgn_y; + listptr L; + wordptr Q; + wordptr R; + wordptr A; + wordptr B; + wordptr T; + wordptr X1; + wordptr X2; + wordptr X3; + wordptr Y1; + wordptr Y2; + wordptr Y3; + wordptr Z; + + /* + Requirements: + - All bit vectors must have equal sizes + - U, V, and W must all be distinct bit vectors + Features: + - The contents of X and Y are preserved + - U, V and W may be identical with X or Y (or both, + provided that U, V and W are mutually distinct) + (i.e., in-place is possible!) + - GCD(0,z) == GCD(z,0) == z + - negative values are handled correctly + */ + + if ((bits != bits_(V)) or + (bits != bits_(W)) or + (bits != bits_(X)) or + (bits != bits_(Y))) + { + return(ErrCode_Size); + } + if ((U == V) or (U == W) or (V == W)) + { + return(ErrCode_Same); + } + if (BitVector_is_empty(X)) + { + if (U != Y) BitVector_Copy(U,Y); + BitVector_Empty(V); + BitVector_Empty(W); + *W = 1; + return(ErrCode_Ok); + } + if (BitVector_is_empty(Y)) + { + if (U != X) BitVector_Copy(U,X); + BitVector_Empty(V); + BitVector_Empty(W); + *V = 1; + return(ErrCode_Ok); + } + if ((L = BitVector_Create_List(bits,false,11)) == NULL) + { + return(ErrCode_Null); + } + Q = L[0]; + R = L[1]; + A = L[2]; + B = L[3]; + X1 = L[4]; + X2 = L[5]; + X3 = L[6]; + Y1 = L[7]; + Y2 = L[8]; + Y3 = L[9]; + Z = L[10]; + size--; + sgn_a = (((*(X+size) &= mask) AND msb) != 0); + sgn_b = (((*(Y+size) &= mask) AND msb) != 0); + if (sgn_a) BitVector_Negate(A,X); else BitVector_Copy(A,X); + if (sgn_b) BitVector_Negate(B,Y); else BitVector_Copy(B,Y); + BitVector_Empty(X1); + BitVector_Empty(X2); + *X1 = 1; + BitVector_Empty(Y1); + BitVector_Empty(Y2); + *Y2 = 1; + sgn_x = false; + sgn_y = false; + while (not error) + { + if ((error = BitVector_Div_Pos(Q,A,B,R))) + { + break; + } + if (BitVector_is_empty(R)) + { + break; + } + sgn_q = sgn_a XOR sgn_b; + + if (sgn_x) BitVector_Negate(Z,X2); else BitVector_Copy(Z,X2); + if ((error = BitVector_Mul_Pos(X3,Z,Q,true))) + { + break; + } + minus = not (sgn_x XOR sgn_q); + carry = 0; + if (BitVector_compute(X3,X1,X3,minus,&carry)) + { + error = ErrCode_Ovfl; + break; + } + sgn_x = (((*(X3+size) &= mask) AND msb) != 0); + + if (sgn_y) BitVector_Negate(Z,Y2); else BitVector_Copy(Z,Y2); + if ((error = BitVector_Mul_Pos(Y3,Z,Q,true))) + { + break; + } + minus = not (sgn_y XOR sgn_q); + carry = 0; + if (BitVector_compute(Y3,Y1,Y3,minus,&carry)) + { + error = ErrCode_Ovfl; + break; + } + sgn_y = (((*(Y3+size) &= mask) AND msb) != 0); + + T = A; sgn_r = sgn_a; + A = B; sgn_a = sgn_b; + B = R; sgn_b = sgn_r; + R = T; + + T = X1; + X1 = X2; + X2 = X3; + X3 = T; + + T = Y1; + Y1 = Y2; + Y2 = Y3; + Y3 = T; + } + if (not error) + { + if (sgn_b) BitVector_Negate(U,B); else BitVector_Copy(U,B); + BitVector_Copy(V,X2); + BitVector_Copy(W,Y2); + } + BitVector_Destroy_List(L,11); + return(error); +} + +ErrCode BitVector_Power(wordptr X, wordptr Y, wordptr Z) +{ + ErrCode error = ErrCode_Ok; + N_word bits = bits_(X); + boolean first = TRUE; + Z_long last; + N_word limit; + N_word count; + wordptr T; + + /* + Requirements: + - X must have at least the same size as Y but may be larger (!) + - X may not be identical with Z + - Z must be positive + Features: + - The contents of Y and Z are preserved + */ + + if (X == Z) return(ErrCode_Same); + if (bits < bits_(Y)) return(ErrCode_Size); + if (BitVector_msb_(Z)) return(ErrCode_Expo); + if ((last = Set_Max(Z)) < 0L) + { + if (bits < 2) return(ErrCode_Ovfl); + BitVector_Empty(X); + *X |= LSB; + return(ErrCode_Ok); /* anything ^ 0 == 1 */ + } + if (BitVector_is_empty(Y)) + { + if (X != Y) BitVector_Empty(X); + return(ErrCode_Ok); /* 0 ^ anything not zero == 0 */ + } + T = BitVector_Create(bits,FALSE); + if (T == NULL) return(ErrCode_Null); + limit = (N_word) last; + for ( count = 0; ((!error) and (count <= limit)); count++ ) + { + if ( BIT_VECTOR_TST_BIT(Z,count) ) + { + if (first) + { + first = FALSE; + if (count) { BitVector_Copy(X,T); } + else { if (X != Y) BitVector_Copy(X,Y); } + } + else error = BitVector_Multiply(X,T,X); /* order important because T > X */ + } + if ((!error) and (count < limit)) + { + if (count) error = BitVector_Multiply(T,T,T); + else error = BitVector_Multiply(T,Y,Y); + } + } + BitVector_Destroy(T); + return(error); +} + +void BitVector_Block_Store(wordptr addr, charptr buffer, N_int length) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + N_word value; + N_word count; + + /* provide translation for independence of endian-ness: */ + if (size > 0) + { + while (size-- > 0) + { + value = 0; + for ( count = 0; (length > 0) and (count < BITS); count += 8 ) + { + value |= (((N_word) *buffer++) << count); length--; + } + *addr++ = value; + } + *(--addr) &= mask; + } +} + +charptr BitVector_Block_Read(wordptr addr, N_intptr length) +{ + N_word size = size_(addr); + N_word value; + N_word count; + charptr buffer; + charptr target; + + /* provide translation for independence of endian-ness: */ + *length = size << FACTOR; + buffer = (charptr) yasm_xmalloc((size_t) ((*length)+1)); + if (buffer == NULL) return(NULL); + target = buffer; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while (size-- > 0) + { + value = *addr++; + count = BITS >> 3; + while (count-- > 0) + { + *target++ = (N_char) (value AND 0x00FF); + if (count > 0) value >>= 8; + } + } + } + *target = (N_char) '\0'; + return(buffer); +} + +void BitVector_Word_Store(wordptr addr, N_int offset, N_int value) +{ + N_word size = size_(addr); + + if (size > 0) + { + if (offset < size) *(addr+offset) = value; + *(addr+size-1) &= mask_(addr); + } +} + +N_int BitVector_Word_Read(wordptr addr, N_int offset) +{ + N_word size = size_(addr); + + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + if (offset < size) return( *(addr+offset) ); + } + return( (N_int) 0 ); +} + +void BitVector_Word_Insert(wordptr addr, N_int offset, N_int count, + boolean clear) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + wordptr last = addr+size-1; + + if (size > 0) + { + *last &= mask; + if (offset > size) offset = size; + BIT_VECTOR_ins_words(addr+offset,size-offset,count,clear); + *last &= mask; + } +} + +void BitVector_Word_Delete(wordptr addr, N_int offset, N_int count, + boolean clear) +{ + N_word size = size_(addr); + N_word mask = mask_(addr); + wordptr last = addr+size-1; + + if (size > 0) + { + *last &= mask; + if (offset > size) offset = size; + BIT_VECTOR_del_words(addr+offset,size-offset,count,clear); + *last &= mask; + } +} + +void BitVector_Chunk_Store(wordptr addr, N_int chunksize, N_int offset, + N_long value) +{ + N_word bits = bits_(addr); + N_word mask; + N_word temp; + + if ((chunksize > 0) and (offset < bits)) + { + if (chunksize > LONGBITS) chunksize = LONGBITS; + if ((offset + chunksize) > bits) chunksize = bits - offset; + addr += offset >> LOGBITS; + offset &= MODMASK; + while (chunksize > 0) + { + mask = (N_word) (~0L << offset); + bits = offset + chunksize; + if (bits < BITS) + { + mask &= (N_word) ~(~0L << bits); + bits = chunksize; + } + else bits = BITS - offset; + temp = (N_word) (value << offset); + temp &= mask; + *addr &= NOT mask; + *addr++ |= temp; + value >>= bits; + chunksize -= bits; + offset = 0; + } + } +} + +N_long BitVector_Chunk_Read(wordptr addr, N_int chunksize, N_int offset) +{ + N_word bits = bits_(addr); + N_word chunkbits = 0; + N_long value = 0L; + N_long temp; + N_word mask; + + if ((chunksize > 0) and (offset < bits)) + { + if (chunksize > LONGBITS) chunksize = LONGBITS; + if ((offset + chunksize) > bits) chunksize = bits - offset; + addr += offset >> LOGBITS; + offset &= MODMASK; + while (chunksize > 0) + { + bits = offset + chunksize; + if (bits < BITS) + { + mask = (N_word) ~(~0L << bits); + bits = chunksize; + } + else + { + mask = (N_word) ~0L; + bits = BITS - offset; + } + temp = (N_long) ((*addr++ AND mask) >> offset); + value |= temp << chunkbits; + chunkbits += bits; + chunksize -= bits; + offset = 0; + } + } + return(value); +} + + /*******************/ + /* set operations: */ + /*******************/ + +void Set_Union(wordptr X, wordptr Y, wordptr Z) /* X = Y + Z */ +{ + N_word bits = bits_(X); + N_word size = size_(X); + N_word mask = mask_(X); + + if ((size > 0) and (bits == bits_(Y)) and (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ OR *Z++; + *(--X) &= mask; + } +} + +void Set_Intersection(wordptr X, wordptr Y, wordptr Z) /* X = Y * Z */ +{ + N_word bits = bits_(X); + N_word size = size_(X); + N_word mask = mask_(X); + + if ((size > 0) and (bits == bits_(Y)) and (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ AND *Z++; + *(--X) &= mask; + } +} + +void Set_Difference(wordptr X, wordptr Y, wordptr Z) /* X = Y \ Z */ +{ + N_word bits = bits_(X); + N_word size = size_(X); + N_word mask = mask_(X); + + if ((size > 0) and (bits == bits_(Y)) and (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ AND NOT *Z++; + *(--X) &= mask; + } +} + +void Set_ExclusiveOr(wordptr X, wordptr Y, wordptr Z) /* X=(Y+Z)\(Y*Z) */ +{ + N_word bits = bits_(X); + N_word size = size_(X); + N_word mask = mask_(X); + + if ((size > 0) and (bits == bits_(Y)) and (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ XOR *Z++; + *(--X) &= mask; + } +} + +void Set_Complement(wordptr X, wordptr Y) /* X = ~Y */ +{ + N_word size = size_(X); + N_word mask = mask_(X); + + if ((size > 0) and (bits_(X) == bits_(Y))) + { + while (size-- > 0) *X++ = NOT *Y++; + *(--X) &= mask; + } +} + + /******************/ + /* set functions: */ + /******************/ + +boolean Set_subset(wordptr X, wordptr Y) /* X subset Y ? */ +{ + N_word size = size_(X); + boolean r = FALSE; + + if ((size > 0) and (bits_(X) == bits_(Y))) + { + r = TRUE; + while (r and (size-- > 0)) r = ((*X++ AND NOT *Y++) == 0); + } + return(r); +} + +N_int Set_Norm(wordptr addr) /* = | X | */ +{ + byteptr byte; + N_word bytes; + N_int n; + + byte = (byteptr) addr; + bytes = size_(addr) << FACTOR; + n = 0; + while (bytes-- > 0) + { + n += BitVector_BYTENORM[*byte++]; + } + return(n); +} + +N_int Set_Norm2(wordptr addr) /* = | X | */ +{ + N_word size = size_(addr); + N_word w0,w1; + N_int n,k; + + n = 0; + while (size-- > 0) + { + k = 0; + w1 = NOT (w0 = *addr++); + while (w0 and w1) + { + w0 &= w0 - 1; + w1 &= w1 - 1; + k++; + } + if (w0 == 0) n += k; + else n += BITS - k; + } + return(n); +} + +N_int Set_Norm3(wordptr addr) /* = | X | */ +{ + N_word size = size_(addr); + N_int count = 0; + N_word c; + + while (size-- > 0) + { + c = *addr++; + while (c) + { + c &= c - 1; + count++; + } + } + return(count); +} + +Z_long Set_Min(wordptr addr) /* = min(X) */ +{ + boolean empty = TRUE; + N_word size = size_(addr); + N_word i = 0; + N_word c = 0; /* silence compiler warning */ + + while (empty and (size-- > 0)) + { + if ((c = *addr++)) empty = false; else i++; + } + if (empty) return((Z_long) LONG_MAX); /* plus infinity */ + i <<= LOGBITS; + while (not (c AND LSB)) + { + c >>= 1; + i++; + } + return((Z_long) i); +} + +Z_long Set_Max(wordptr addr) /* = max(X) */ +{ + boolean empty = TRUE; + N_word size = size_(addr); + N_word i = size; + N_word c = 0; /* silence compiler warning */ + + addr += size-1; + while (empty and (size-- > 0)) + { + if ((c = *addr--)) empty = false; else i--; + } + if (empty) return((Z_long) LONG_MIN); /* minus infinity */ + i <<= LOGBITS; + while (not (c AND MSB)) + { + c <<= 1; + i--; + } + return((Z_long) --i); +} + + /**********************************/ + /* matrix-of-booleans operations: */ + /**********************************/ + +void Matrix_Multiplication(wordptr X, N_int rowsX, N_int colsX, + wordptr Y, N_int rowsY, N_int colsY, + wordptr Z, N_int rowsZ, N_int colsZ) +{ + N_word i; + N_word j; + N_word k; + N_word indxX; + N_word indxY; + N_word indxZ; + N_word termX; + N_word termY; + N_word sum; + + if ((colsY == rowsZ) and (rowsX == rowsY) and (colsX == colsZ) and + (bits_(X) == rowsX*colsX) and + (bits_(Y) == rowsY*colsY) and + (bits_(Z) == rowsZ*colsZ)) + { + for ( i = 0; i < rowsY; i++ ) + { + termX = i * colsX; + termY = i * colsY; + for ( j = 0; j < colsZ; j++ ) + { + indxX = termX + j; + sum = 0; + for ( k = 0; k < colsY; k++ ) + { + indxY = termY + k; + indxZ = k * colsZ + j; + if ( BIT_VECTOR_TST_BIT(Y,indxY) && + BIT_VECTOR_TST_BIT(Z,indxZ) ) sum ^= 1; + } + if (sum) BIT_VECTOR_SET_BIT(X,indxX) + else BIT_VECTOR_CLR_BIT(X,indxX) + } + } + } +} + +void Matrix_Product(wordptr X, N_int rowsX, N_int colsX, + wordptr Y, N_int rowsY, N_int colsY, + wordptr Z, N_int rowsZ, N_int colsZ) +{ + N_word i; + N_word j; + N_word k; + N_word indxX; + N_word indxY; + N_word indxZ; + N_word termX; + N_word termY; + N_word sum; + + if ((colsY == rowsZ) and (rowsX == rowsY) and (colsX == colsZ) and + (bits_(X) == rowsX*colsX) and + (bits_(Y) == rowsY*colsY) and + (bits_(Z) == rowsZ*colsZ)) + { + for ( i = 0; i < rowsY; i++ ) + { + termX = i * colsX; + termY = i * colsY; + for ( j = 0; j < colsZ; j++ ) + { + indxX = termX + j; + sum = 0; + for ( k = 0; k < colsY; k++ ) + { + indxY = termY + k; + indxZ = k * colsZ + j; + if ( BIT_VECTOR_TST_BIT(Y,indxY) && + BIT_VECTOR_TST_BIT(Z,indxZ) ) sum |= 1; + } + if (sum) BIT_VECTOR_SET_BIT(X,indxX) + else BIT_VECTOR_CLR_BIT(X,indxX) + } + } + } +} + +void Matrix_Closure(wordptr addr, N_int rows, N_int cols) +{ + N_word i; + N_word j; + N_word k; + N_word ii; + N_word ij; + N_word ik; + N_word kj; + N_word termi; + N_word termk; + + if ((rows == cols) and (bits_(addr) == rows*cols)) + { + for ( i = 0; i < rows; i++ ) + { + ii = i * cols + i; + BIT_VECTOR_SET_BIT(addr,ii) + } + for ( k = 0; k < rows; k++ ) + { + termk = k * cols; + for ( i = 0; i < rows; i++ ) + { + termi = i * cols; + ik = termi + k; + for ( j = 0; j < rows; j++ ) + { + ij = termi + j; + kj = termk + j; + if ( BIT_VECTOR_TST_BIT(addr,ik) && + BIT_VECTOR_TST_BIT(addr,kj) ) + BIT_VECTOR_SET_BIT(addr,ij) + } + } + } + } +} + +void Matrix_Transpose(wordptr X, N_int rowsX, N_int colsX, + wordptr Y, N_int rowsY, N_int colsY) +{ + N_word i; + N_word j; + N_word ii; + N_word ij; + N_word ji; + N_word addii; + N_word addij; + N_word addji; + N_word bitii; + N_word bitij; + N_word bitji; + N_word termi; + N_word termj; + boolean swap; + + /* BEWARE that "in-place" is ONLY possible if the matrix is quadratic!! */ + + if ((rowsX == colsY) and (colsX == rowsY) and + (bits_(X) == rowsX*colsX) and + (bits_(Y) == rowsY*colsY)) + { + if (rowsY == colsY) /* in-place is possible! */ + { + for ( i = 0; i < rowsY; i++ ) + { + termi = i * colsY; + for ( j = 0; j < i; j++ ) + { + termj = j * colsX; + ij = termi + j; + ji = termj + i; + addij = ij >> LOGBITS; + addji = ji >> LOGBITS; + bitij = BITMASKTAB[ij AND MODMASK]; + bitji = BITMASKTAB[ji AND MODMASK]; + swap = ((*(Y+addij) AND bitij) != 0); + if ((*(Y+addji) AND bitji) != 0) + *(X+addij) |= bitij; + else + *(X+addij) &= NOT bitij; + if (swap) + *(X+addji) |= bitji; + else + *(X+addji) &= NOT bitji; + } + ii = termi + i; + addii = ii >> LOGBITS; + bitii = BITMASKTAB[ii AND MODMASK]; + if ((*(Y+addii) AND bitii) != 0) + *(X+addii) |= bitii; + else + *(X+addii) &= NOT bitii; + } + } + else /* rowsX != colsX, in-place is NOT possible! */ + { + for ( i = 0; i < rowsY; i++ ) + { + termi = i * colsY; + for ( j = 0; j < colsY; j++ ) + { + termj = j * colsX; + ij = termi + j; + ji = termj + i; + addij = ij >> LOGBITS; + addji = ji >> LOGBITS; + bitij = BITMASKTAB[ij AND MODMASK]; + bitji = BITMASKTAB[ji AND MODMASK]; + if ((*(Y+addij) AND bitij) != 0) + *(X+addji) |= bitji; + else + *(X+addji) &= NOT bitji; + } + } + } + } +} + +/*****************************************************************************/ +/* VERSION: 6.4 */ +/*****************************************************************************/ +/* VERSION HISTORY: */ +/*****************************************************************************/ +/* */ +/* Version 6.4 03.10.04 Added C++ comp. directives. Improved "Norm()". */ +/* Version 6.3 28.09.02 Added "Create_List()" and "GCD2()". */ +/* Version 6.2 15.09.02 Overhauled error handling. Fixed "GCD()". */ +/* Version 6.1 08.10.01 Make VMS linker happy: _lsb,_msb => _lsb_,_msb_ */ +/* Version 6.0 08.10.00 Corrected overflow handling. */ +/* Version 5.8 14.07.00 Added "Power()". Changed "Copy()". */ +/* Version 5.7 19.05.99 Quickened "Div_Pos()". Added "Product()". */ +/* Version 5.6 02.11.98 Leading zeros eliminated in "to_Hex()". */ +/* Version 5.5 21.09.98 Fixed bug of uninitialized "error" in Multiply. */ +/* Version 5.4 07.09.98 Fixed bug of uninitialized "error" in Divide. */ +/* Version 5.3 12.05.98 Improved Norm. Completed history. */ +/* Version 5.2 31.03.98 Improved Norm. */ +/* Version 5.1 09.03.98 No changes. */ +/* Version 5.0 01.03.98 Major additions and rewrite. */ +/* Version 4.2 16.07.97 Added is_empty, is_full. */ +/* Version 4.1 30.06.97 Added word-ins/del, move-left/right, inc/dec. */ +/* Version 4.0 23.04.97 Rewrite. Added bit shift and bool. matrix ops. */ +/* Version 3.2 04.02.97 Added interval methods. */ +/* Version 3.1 21.01.97 Fixed bug on 64 bit machines. */ +/* Version 3.0 12.01.97 Added flip. */ +/* Version 2.0 14.12.96 Efficiency and consistency improvements. */ +/* Version 1.1 08.01.96 Added Resize and ExclusiveOr. */ +/* Version 1.0 14.12.95 First version under UNIX (with Perl module). */ +/* Version 0.9 01.11.93 First version of C library under MS-DOS. */ +/* Version 0.1 ??.??.89 First version in Turbo Pascal under CP/M. */ +/* */ +/*****************************************************************************/ +/* AUTHOR: */ +/*****************************************************************************/ +/* */ +/* Steffen Beyer */ +/* mailto:sb@engelschall.com */ +/* http://www.engelschall.com/u/sb/download/ */ +/* */ +/*****************************************************************************/ +/* COPYRIGHT: */ +/*****************************************************************************/ +/* */ +/* Copyright (c) 1995 - 2004 by Steffen Beyer. */ +/* All rights reserved. */ +/* */ +/*****************************************************************************/ +/* LICENSE: */ +/*****************************************************************************/ +/* This package is free software; you can use, modify and redistribute */ +/* it under the same terms as Perl itself, i.e., under the terms of */ +/* the "Artistic License" or the "GNU General Public License". */ +/* */ +/* The C library at the core of this Perl module can additionally */ +/* be used, modified and redistributed under the terms of the */ +/* "GNU Library General Public License". */ +/* */ +/*****************************************************************************/ +/* ARTISTIC LICENSE: */ +/*****************************************************************************/ +/* + The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. You may embed this Package's interpreter within +an executable of yours (by linking); this shall be construed as a mere +form of aggregation, provided that the complete Standard Version of the +interpreter is so embedded. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. C subroutines (or comparably compiled subroutines in other +languages) supplied by you and linked into this Package in order to +emulate subroutines and variables of the language defined by this +Package shall not be considered part of this Package, but are the +equivalent of input as in Paragraph 6, provided these subroutines do +not change the language in any way that would cause it to fail the +regression tests for the language. + +8. Aggregation of this Package with a commercial distribution is always +permitted provided that the use of this Package is embedded; that is, +when no overt attempt is made to make this Package's interfaces visible +to the end user of the commercial distribution. Such use shall not be +construed as a distribution of this Package. + +9. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End +*/ +/*****************************************************************************/ +/* GNU GENERAL PUBLIC LICENSE: */ +/*****************************************************************************/ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/*****************************************************************************/ +/* GNU LIBRARY GENERAL PUBLIC LICENSE: */ +/*****************************************************************************/ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* or download a copy from ftp://ftp.gnu.org/pub/gnu/COPYING.LIB-2.0 */ +/* */ +/*****************************************************************************/ diff --git a/libyasm/bitvect.h b/libyasm/bitvect.h new file mode 100644 index 0000000..3aee3a5 --- /dev/null +++ b/libyasm/bitvect.h @@ -0,0 +1,666 @@ +#ifndef YASM_BITVECT_H +#define YASM_BITVECT_H +/*****************************************************************************/ +/* MODULE NAME: BitVector.h MODULE TYPE: (adt) */ +/*****************************************************************************/ +/* MODULE IMPORTS: */ +/*****************************************************************************/ + +/* ToolBox.h */ +/*****************************************************************************/ +/* NOTE: The type names that have been chosen here are somewhat weird on */ +/* purpose, in order to avoid name clashes with system header files */ +/* and your own application(s) which might - directly or indirectly - */ +/* include this definitions file. */ +/*****************************************************************************/ +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +typedef unsigned char N_char; +typedef unsigned char N_byte; +typedef unsigned short N_short; +typedef unsigned short N_shortword; +typedef unsigned int N_int; +typedef unsigned int N_word; +typedef unsigned long N_long; +typedef unsigned long N_longword; + +/* Mnemonic 1: The natural numbers, N = { 0, 1, 2, 3, ... } */ +/* Mnemonic 2: Nnnn = u_N_signed, _N_ot signed */ + +typedef signed char Z_char; +typedef signed char Z_byte; +typedef signed short Z_short; +typedef signed short Z_shortword; +typedef signed int Z_int; +typedef signed int Z_word; +typedef signed long Z_long; +typedef signed long Z_longword; + +/* Mnemonic 1: The whole numbers, Z = { 0, -1, 1, -2, 2, -3, 3, ... } */ +/* Mnemonic 2: Zzzz = Ssss_igned */ + +typedef void *voidptr; +typedef N_char *charptr; +typedef N_byte *byteptr; +typedef N_short *shortptr; +typedef N_shortword *shortwordptr; +typedef N_int *intptr; +typedef N_word *wordptr; +typedef N_long *longptr; +typedef N_longword *longwordptr; + +typedef N_char *N_charptr; +typedef N_byte *N_byteptr; +typedef N_short *N_shortptr; +typedef N_shortword *N_shortwordptr; +typedef N_int *N_intptr; +typedef N_word *N_wordptr; +typedef N_long *N_longptr; +typedef N_longword *N_longwordptr; + +typedef Z_char *Z_charptr; +typedef Z_byte *Z_byteptr; +typedef Z_short *Z_shortptr; +typedef Z_shortword *Z_shortwordptr; +typedef Z_int *Z_intptr; +typedef Z_word *Z_wordptr; +typedef Z_long *Z_longptr; +typedef Z_longword *Z_longwordptr; + +#ifndef FALSE +#define FALSE (0!=0) +#endif + +#ifndef TRUE +#define TRUE (0==0) +#endif + +#ifdef __cplusplus + typedef bool boolean; +#else + #ifdef MACOS_TRADITIONAL + #define boolean Boolean + #else + typedef enum boolean { false = FALSE, true = TRUE } boolean; + #endif +#endif + +/*****************************************************************************/ +/* MODULE INTERFACE: */ +/*****************************************************************************/ + +typedef enum ErrCode + { + ErrCode_Ok = 0, /* everything went allright */ + + ErrCode_Type, /* types word and size_t have incompatible sizes */ + ErrCode_Bits, /* bits of word and sizeof(word) are inconsistent */ + ErrCode_Word, /* size of word is less than 16 bits */ + ErrCode_Long, /* size of word is greater than size of long */ + ErrCode_Powr, /* number of bits of word is not a power of two */ + ErrCode_Loga, /* error in calculation of logarithm */ + + ErrCode_Null, /* unable to allocate memory */ + + ErrCode_Indx, /* index out of range */ + ErrCode_Ordr, /* minimum > maximum index */ + ErrCode_Size, /* bit vector size mismatch */ + ErrCode_Pars, /* input string syntax error */ + ErrCode_Ovfl, /* numeric overflow error */ + ErrCode_Same, /* operands must be distinct */ + ErrCode_Expo, /* exponent must be positive */ + ErrCode_Zero /* division by zero error */ + } ErrCode; + +typedef wordptr *listptr; + +/* ===> MISCELLANEOUS BASIC FUNCTIONS: <=== */ + +YASM_LIB_DECL +const char * BitVector_Error (ErrCode error); /* return string for err code */ + +YASM_LIB_DECL +ErrCode BitVector_Boot (void); /* 0 = ok, 1..7 = error */ +YASM_LIB_DECL +void BitVector_Shutdown (void); /* undo Boot */ + +YASM_LIB_DECL +N_word BitVector_Size (N_int bits); /* bit vector size (# of words) */ +YASM_LIB_DECL +N_word BitVector_Mask (N_int bits); /* bit vector mask (unused bits) */ + +/* ===> CLASS METHODS: <=== */ + +YASM_LIB_DECL +const char * BitVector_Version (void); /* returns version string */ + +YASM_LIB_DECL +N_int BitVector_Word_Bits (void); /* return # of bits in machine word */ +YASM_LIB_DECL +N_int BitVector_Long_Bits (void); /* return # of bits in unsigned long */ + +/* ===> CONSTRUCTOR METHODS: <=== */ + +YASM_LIB_DECL +/*@only@*/ wordptr BitVector_Create (N_int bits, boolean clear); /* malloc */ +YASM_LIB_DECL +listptr BitVector_Create_List(N_int bits, boolean clear, N_int count); + +YASM_LIB_DECL +wordptr BitVector_Resize (wordptr oldaddr, N_int bits); /* realloc */ + +YASM_LIB_DECL +wordptr BitVector_Shadow (wordptr addr); /* make new same size but empty */ +YASM_LIB_DECL +wordptr BitVector_Clone (wordptr addr); /* make exact duplicate */ + +YASM_LIB_DECL +wordptr BitVector_Concat (wordptr X, wordptr Y); /* return concatenation */ + +/* ===> DESTRUCTOR METHODS: <=== */ + +YASM_LIB_DECL +void BitVector_Dispose (/*@only@*/ /*@out@*/ charptr string); /* string */ +YASM_LIB_DECL +void BitVector_Destroy (/*@only@*/ wordptr addr); /* bitvec */ +YASM_LIB_DECL +void BitVector_Destroy_List (listptr list, N_int count); /* list */ + +/* ===> OBJECT METHODS: <=== */ + +/* ===> bit vector copy function: */ + +YASM_LIB_DECL +void BitVector_Copy (wordptr X, wordptr Y); /* X = Y */ + +/* ===> bit vector initialization: */ + +YASM_LIB_DECL +void BitVector_Empty (wordptr addr); /* X = {} */ +YASM_LIB_DECL +void BitVector_Fill (wordptr addr); /* X = ~{} */ +YASM_LIB_DECL +void BitVector_Flip (wordptr addr); /* X = ~X */ + +YASM_LIB_DECL +void BitVector_Primes (wordptr addr); + +/* ===> miscellaneous functions: */ + +YASM_LIB_DECL +void BitVector_Reverse (wordptr X, wordptr Y); + +/* ===> bit vector interval operations and functions: */ + +YASM_LIB_DECL +void BitVector_Interval_Empty (/*@out@*/ wordptr addr, N_int lower, N_int upper); +YASM_LIB_DECL +void BitVector_Interval_Fill (/*@out@*/ wordptr addr, N_int lower, N_int upper); +YASM_LIB_DECL +void BitVector_Interval_Flip (/*@out@*/ wordptr addr, N_int lower, N_int upper); +YASM_LIB_DECL +void BitVector_Interval_Reverse (/*@out@*/ wordptr addr, N_int lower, N_int upper); + +YASM_LIB_DECL +boolean BitVector_interval_scan_inc (wordptr addr, N_int start, + N_intptr min, N_intptr max); +YASM_LIB_DECL +boolean BitVector_interval_scan_dec (wordptr addr, N_int start, + N_intptr min, N_intptr max); + +YASM_LIB_DECL +void BitVector_Interval_Copy (/*@out@*/ wordptr X, wordptr Y, N_int Xoffset, + N_int Yoffset, N_int length); + +YASM_LIB_DECL +wordptr BitVector_Interval_Substitute(/*@out@*/ wordptr X, wordptr Y, + N_int Xoffset, N_int Xlength, + N_int Yoffset, N_int Ylength); + +/* ===> bit vector test functions: */ + +YASM_LIB_DECL +boolean BitVector_is_empty (wordptr addr); /* X == {} ? */ +YASM_LIB_DECL +boolean BitVector_is_full (wordptr addr); /* X == ~{} ? */ + +YASM_LIB_DECL +boolean BitVector_equal (wordptr X, wordptr Y); /* X == Y ? */ +YASM_LIB_DECL +Z_int BitVector_Lexicompare(wordptr X, wordptr Y); /* X <,=,> Y ? */ +YASM_LIB_DECL +Z_int BitVector_Compare (wordptr X, wordptr Y); /* X <,=,> Y ? */ + +/* ===> bit vector string conversion functions: */ + +YASM_LIB_DECL +/*@only@*/ charptr BitVector_to_Hex (wordptr addr); +YASM_LIB_DECL +ErrCode BitVector_from_Hex (/*@out@*/wordptr addr, charptr string); + +YASM_LIB_DECL +ErrCode BitVector_from_Oct(/*@out@*/ wordptr addr, charptr string); + +YASM_LIB_DECL +/*@only@*/ charptr BitVector_to_Bin (wordptr addr); +YASM_LIB_DECL +ErrCode BitVector_from_Bin (/*@out@*/ wordptr addr, charptr string); + +YASM_LIB_DECL +/*@only@*/ charptr BitVector_to_Dec (wordptr addr); +YASM_LIB_DECL +ErrCode BitVector_from_Dec (/*@out@*/ wordptr addr, charptr string); + +typedef struct BitVector_from_Dec_static_data BitVector_from_Dec_static_data; +YASM_LIB_DECL +BitVector_from_Dec_static_data *BitVector_from_Dec_static_Boot(N_word bits); +YASM_LIB_DECL +void BitVector_from_Dec_static_Shutdown(/*@null@*/ BitVector_from_Dec_static_data *data); +YASM_LIB_DECL +ErrCode BitVector_from_Dec_static(BitVector_from_Dec_static_data *data, + /*@out@*/ wordptr addr, charptr string); + +YASM_LIB_DECL +/*@only@*/ charptr BitVector_to_Enum (wordptr addr); +YASM_LIB_DECL +ErrCode BitVector_from_Enum (/*@out@*/ wordptr addr, charptr string); + +/* ===> bit vector bit operations, functions & tests: */ + +YASM_LIB_DECL +void BitVector_Bit_Off (/*@out@*/ wordptr addr, N_int indx); /* X = X \ {x} */ +YASM_LIB_DECL +void BitVector_Bit_On (/*@out@*/ wordptr addr, N_int indx); /* X = X + {x} */ +YASM_LIB_DECL +boolean BitVector_bit_flip (/*@out@*/ wordptr addr, N_int indx); /* (X+{x})\(X*{x}) */ + +YASM_LIB_DECL +boolean BitVector_bit_test (wordptr addr, N_int indx); /* {x} in X ? */ + +YASM_LIB_DECL +void BitVector_Bit_Copy (/*@out@*/ wordptr addr, N_int indx, boolean bit); + +/* ===> bit vector bit shift & rotate functions: */ + +YASM_LIB_DECL +void BitVector_LSB (/*@out@*/ wordptr addr, boolean bit); +YASM_LIB_DECL +void BitVector_MSB (/*@out@*/ wordptr addr, boolean bit); +YASM_LIB_DECL +boolean BitVector_lsb_ (wordptr addr); +YASM_LIB_DECL +boolean BitVector_msb_ (wordptr addr); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_rotate_left (wordptr addr); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_rotate_right (wordptr addr); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_shift_left (wordptr addr, boolean carry_in); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_shift_right (wordptr addr, boolean carry_in); +YASM_LIB_DECL +void BitVector_Move_Left (wordptr addr, N_int bits); +YASM_LIB_DECL +void BitVector_Move_Right (wordptr addr, N_int bits); + +/* ===> bit vector insert/delete bits: */ + +YASM_LIB_DECL +void BitVector_Insert (wordptr addr, N_int offset, N_int count, + boolean clear); +YASM_LIB_DECL +void BitVector_Delete (wordptr addr, N_int offset, N_int count, + boolean clear); + +/* ===> bit vector arithmetic: */ + +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_increment (wordptr addr); /* X++ */ +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_decrement (wordptr addr); /* X-- */ + +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_compute (wordptr X, wordptr Y, wordptr Z, boolean minus, + boolean *carry); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_add (wordptr X, wordptr Y, wordptr Z, boolean *carry); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_sub (wordptr X, wordptr Y, wordptr Z, boolean *carry); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_inc (wordptr X, wordptr Y); +YASM_LIB_DECL +boolean /*@alt void@*/ BitVector_dec (wordptr X, wordptr Y); + +YASM_LIB_DECL +void BitVector_Negate (wordptr X, wordptr Y); +YASM_LIB_DECL +void BitVector_Absolute (wordptr X, wordptr Y); +YASM_LIB_DECL +Z_int BitVector_Sign (wordptr addr); +YASM_LIB_DECL +ErrCode BitVector_Mul_Pos (wordptr X, wordptr Y, wordptr Z, boolean strict); +YASM_LIB_DECL +ErrCode BitVector_Multiply (wordptr X, wordptr Y, wordptr Z); +YASM_LIB_DECL +ErrCode BitVector_Div_Pos (wordptr Q, wordptr X, wordptr Y, wordptr R); +YASM_LIB_DECL +ErrCode BitVector_Divide (wordptr Q, wordptr X, wordptr Y, wordptr R); +YASM_LIB_DECL +ErrCode BitVector_GCD (wordptr X, wordptr Y, wordptr Z); +YASM_LIB_DECL +ErrCode BitVector_GCD2 (wordptr U, wordptr V, wordptr W, /* O */ + wordptr X, wordptr Y); /* I */ +YASM_LIB_DECL +ErrCode BitVector_Power (wordptr X, wordptr Y, wordptr Z); + +/* ===> direct memory access functions: */ + +YASM_LIB_DECL +void BitVector_Block_Store(wordptr addr, charptr buffer, N_int length); +YASM_LIB_DECL +charptr BitVector_Block_Read (wordptr addr, /*@out@*/ N_intptr length); + +/* ===> word array functions: */ + +YASM_LIB_DECL +void BitVector_Word_Store (wordptr addr, N_int offset, N_int value); +YASM_LIB_DECL +N_int BitVector_Word_Read (wordptr addr, N_int offset); + +YASM_LIB_DECL +void BitVector_Word_Insert(wordptr addr, N_int offset, N_int count, + boolean clear); +YASM_LIB_DECL +void BitVector_Word_Delete(wordptr addr, N_int offset, N_int count, + boolean clear); + +/* ===> arbitrary size chunk functions: */ + +YASM_LIB_DECL +void BitVector_Chunk_Store(wordptr addr, N_int chunksize, + N_int offset, N_long value); +YASM_LIB_DECL +N_long BitVector_Chunk_Read (wordptr addr, N_int chunksize, + N_int offset); + +/* ===> set operations: */ + +YASM_LIB_DECL +void Set_Union (wordptr X, wordptr Y, wordptr Z); /* X = Y + Z */ +YASM_LIB_DECL +void Set_Intersection (wordptr X, wordptr Y, wordptr Z); /* X = Y * Z */ +YASM_LIB_DECL +void Set_Difference (wordptr X, wordptr Y, wordptr Z); /* X = Y \ Z */ +YASM_LIB_DECL +void Set_ExclusiveOr (wordptr X, wordptr Y, wordptr Z); /*(Y+Z)\(Y*Z)*/ +YASM_LIB_DECL +void Set_Complement (wordptr X, wordptr Y); /* X = ~Y */ + +/* ===> set functions: */ + +YASM_LIB_DECL +boolean Set_subset (wordptr X, wordptr Y); /* X in Y ? */ + +YASM_LIB_DECL +N_int Set_Norm (wordptr addr); /* = | X | */ +YASM_LIB_DECL +N_int Set_Norm2 (wordptr addr); /* = | X | */ +YASM_LIB_DECL +N_int Set_Norm3 (wordptr addr); /* = | X | */ +YASM_LIB_DECL +Z_long Set_Min (wordptr addr); /* = min(X) */ +YASM_LIB_DECL +Z_long Set_Max (wordptr addr); /* = max(X) */ + +/* ===> matrix-of-booleans operations: */ + +YASM_LIB_DECL +void Matrix_Multiplication(wordptr X, N_int rowsX, N_int colsX, + wordptr Y, N_int rowsY, N_int colsY, + wordptr Z, N_int rowsZ, N_int colsZ); + +YASM_LIB_DECL +void Matrix_Product (wordptr X, N_int rowsX, N_int colsX, + wordptr Y, N_int rowsY, N_int colsY, + wordptr Z, N_int rowsZ, N_int colsZ); + +YASM_LIB_DECL +void Matrix_Closure (wordptr addr, N_int rows, N_int cols); + +YASM_LIB_DECL +void Matrix_Transpose (wordptr X, N_int rowsX, N_int colsX, + wordptr Y, N_int rowsY, N_int colsY); + +/*****************************************************************************/ +/* VERSION: 6.4 */ +/*****************************************************************************/ +/* VERSION HISTORY: */ +/*****************************************************************************/ +/* */ +/* Version 6.4 03.10.04 Added C++ comp. directives. Improved "Norm()". */ +/* Version 6.3 28.09.02 Added "Create_List()" and "GCD2()". */ +/* Version 6.2 15.09.02 Overhauled error handling. Fixed "GCD()". */ +/* Version 6.1 08.10.01 Make VMS linker happy: _lsb,_msb => _lsb_,_msb_ */ +/* Version 6.0 08.10.00 Corrected overflow handling. */ +/* Version 5.8 14.07.00 Added "Power()". Changed "Copy()". */ +/* Version 5.7 19.05.99 Quickened "Div_Pos()". Added "Product()". */ +/* Version 5.6 02.11.98 Leading zeros eliminated in "to_Hex()". */ +/* Version 5.5 21.09.98 Fixed bug of uninitialized "error" in Multiply. */ +/* Version 5.4 07.09.98 Fixed bug of uninitialized "error" in Divide. */ +/* Version 5.3 12.05.98 Improved Norm. Completed history. */ +/* Version 5.2 31.03.98 Improved Norm. */ +/* Version 5.1 09.03.98 No changes. */ +/* Version 5.0 01.03.98 Major additions and rewrite. */ +/* Version 4.2 16.07.97 Added is_empty, is_full. */ +/* Version 4.1 30.06.97 Added word-ins/del, move-left/right, inc/dec. */ +/* Version 4.0 23.04.97 Rewrite. Added bit shift and bool. matrix ops. */ +/* Version 3.2 04.02.97 Added interval methods. */ +/* Version 3.1 21.01.97 Fixed bug on 64 bit machines. */ +/* Version 3.0 12.01.97 Added flip. */ +/* Version 2.0 14.12.96 Efficiency and consistency improvements. */ +/* Version 1.1 08.01.96 Added Resize and ExclusiveOr. */ +/* Version 1.0 14.12.95 First version under UNIX (with Perl module). */ +/* Version 0.9 01.11.93 First version of C library under MS-DOS. */ +/* Version 0.1 ??.??.89 First version in Turbo Pascal under CP/M. */ +/* */ +/*****************************************************************************/ +/* AUTHOR: */ +/*****************************************************************************/ +/* */ +/* Steffen Beyer */ +/* mailto:sb@engelschall.com */ +/* http://www.engelschall.com/u/sb/download/ */ +/* */ +/*****************************************************************************/ +/* COPYRIGHT: */ +/*****************************************************************************/ +/* */ +/* Copyright (c) 1995 - 2004 by Steffen Beyer. */ +/* All rights reserved. */ +/* */ +/*****************************************************************************/ +/* LICENSE: */ +/*****************************************************************************/ +/* This package is free software; you can use, modify and redistribute */ +/* it under the same terms as Perl itself, i.e., under the terms of */ +/* the "Artistic License" or the "GNU General Public License". */ +/* */ +/* The C library at the core of this Perl module can additionally */ +/* be used, modified and redistributed under the terms of the */ +/* "GNU Library General Public License". */ +/* */ +/*****************************************************************************/ +/* ARTISTIC LICENSE: */ +/*****************************************************************************/ +/* + The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. You may embed this Package's interpreter within +an executable of yours (by linking); this shall be construed as a mere +form of aggregation, provided that the complete Standard Version of the +interpreter is so embedded. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. C subroutines (or comparably compiled subroutines in other +languages) supplied by you and linked into this Package in order to +emulate subroutines and variables of the language defined by this +Package shall not be considered part of this Package, but are the +equivalent of input as in Paragraph 6, provided these subroutines do +not change the language in any way that would cause it to fail the +regression tests for the language. + +8. Aggregation of this Package with a commercial distribution is always +permitted provided that the use of this Package is embedded; that is, +when no overt attempt is made to make this Package's interfaces visible +to the end user of the commercial distribution. Such use shall not be +construed as a distribution of this Package. + +9. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End +*/ +/*****************************************************************************/ +/* GNU GENERAL PUBLIC LICENSE: */ +/*****************************************************************************/ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/*****************************************************************************/ +/* GNU LIBRARY GENERAL PUBLIC LICENSE: */ +/*****************************************************************************/ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* or download a copy from ftp://ftp.gnu.org/pub/gnu/COPYING.LIB-2.0 */ +/* */ +/*****************************************************************************/ +#endif diff --git a/libyasm/bytecode.c b/libyasm/bytecode.c new file mode 100644 index 0000000..f864bae --- /dev/null +++ b/libyasm/bytecode.c @@ -0,0 +1,386 @@ +/* + * Bytecode utility functions + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "value.h" +#include "symrec.h" + +#include "bytecode.h" + + +void +yasm_bc_set_multiple(yasm_bytecode *bc, yasm_expr *e) +{ + if (bc->multiple) + bc->multiple = yasm_expr_create_tree(bc->multiple, YASM_EXPR_MUL, e, + e->line); + else + bc->multiple = e; +} + +void +yasm_bc_finalize_common(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ +} + +int +yasm_bc_calc_len_common(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + yasm_internal_error(N_("bytecode length cannot be calculated")); + /*@unreached@*/ + return 0; +} + +int +yasm_bc_expand_common(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + yasm_internal_error(N_("bytecode does not have any dependent spans")); + /*@unreached@*/ + return 0; +} + +int +yasm_bc_tobytes_common(yasm_bytecode *bc, unsigned char **buf, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc) +{ + yasm_internal_error(N_("bytecode cannot be converted to bytes")); + /*@unreached@*/ + return 0; +} + +void +yasm_bc_transform(yasm_bytecode *bc, const yasm_bytecode_callback *callback, + void *contents) +{ + if (bc->callback) + bc->callback->destroy(bc->contents); + bc->callback = callback; + bc->contents = contents; +} + +yasm_bytecode * +yasm_bc_create_common(const yasm_bytecode_callback *callback, void *contents, + unsigned long line) +{ + yasm_bytecode *bc = yasm_xmalloc(sizeof(yasm_bytecode)); + + bc->callback = callback; + bc->section = NULL; + bc->multiple = (yasm_expr *)NULL; + bc->len = 0; + bc->mult_int = 1; + bc->line = line; + bc->offset = ~0UL; /* obviously incorrect / uninitialized value */ + bc->symrecs = NULL; + bc->contents = contents; + + return bc; +} + +yasm_section * +yasm_bc_get_section(yasm_bytecode *bc) +{ + return bc->section; +} + +void +yasm_bc__add_symrec(yasm_bytecode *bc, yasm_symrec *sym) +{ + if (!bc->symrecs) { + bc->symrecs = yasm_xmalloc(2*sizeof(yasm_symrec *)); + bc->symrecs[0] = sym; + bc->symrecs[1] = NULL; + } else { + /* Very inefficient implementation for large numbers of symbols. But + * that would be very unusual, so use the simple algorithm instead. + */ + size_t count = 1; + while (bc->symrecs[count]) + count++; + bc->symrecs = yasm_xrealloc(bc->symrecs, + (count+2)*sizeof(yasm_symrec *)); + bc->symrecs[count] = sym; + bc->symrecs[count+1] = NULL; + } +} + +void +yasm_bc_destroy(yasm_bytecode *bc) +{ + if (!bc) + return; + + if (bc->callback) + bc->callback->destroy(bc->contents); + yasm_expr_destroy(bc->multiple); + if (bc->symrecs) + yasm_xfree(bc->symrecs); + yasm_xfree(bc); +} + +void +yasm_bc_print(const yasm_bytecode *bc, FILE *f, int indent_level) +{ + if (!bc->callback) + fprintf(f, "%*s_Empty_\n", indent_level, ""); + else + bc->callback->print(bc->contents, f, indent_level); + fprintf(f, "%*sMultiple=", indent_level, ""); + if (!bc->multiple) + fprintf(f, "nil (1)"); + else + yasm_expr_print(bc->multiple, f); + fprintf(f, "\n%*sLength=%lu\n", indent_level, "", bc->len); + fprintf(f, "%*sLine Index=%lu\n", indent_level, "", bc->line); + fprintf(f, "%*sOffset=%lx\n", indent_level, "", bc->offset); +} + +void +yasm_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + if (bc->callback) + bc->callback->finalize(bc, prev_bc); + if (bc->multiple) { + yasm_value val; + + if (yasm_value_finalize_expr(&val, bc->multiple, prev_bc, 0)) + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("multiple expression too complex")); + else if (val.rel) + yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, + N_("multiple expression not absolute")); + /* Finalize creates NULL output if value=0, but bc->multiple is NULL + * if value=1 (this difference is to make the common case small). + * However, this means we need to set bc->multiple explicitly to 0 + * here if val.abs is NULL. + */ + if (val.abs) + bc->multiple = val.abs; + else + bc->multiple = yasm_expr_create_ident( + yasm_expr_int(yasm_intnum_create_uint(0)), bc->line); + } +} + +/*@null@*/ yasm_intnum * +yasm_calc_bc_dist(yasm_bytecode *precbc1, yasm_bytecode *precbc2) +{ + unsigned long dist2, dist1; + yasm_intnum *intn; + + if (precbc1->section != precbc2->section) + return NULL; + + dist1 = yasm_bc_next_offset(precbc1); + dist2 = yasm_bc_next_offset(precbc2); + if (dist2 < dist1) { + intn = yasm_intnum_create_uint(dist1 - dist2); + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + return intn; + } + dist2 -= dist1; + return yasm_intnum_create_uint(dist2); +} + +unsigned long +yasm_bc_next_offset(yasm_bytecode *precbc) +{ + return precbc->offset + precbc->len*precbc->mult_int; +} + +int +yasm_bc_elem_size(yasm_bytecode *bc) +{ + if (!bc->callback) { + yasm_internal_error(N_("got empty bytecode in yasm_bc_elem_size")); + return 0; + } else if (!bc->callback->elem_size) + return 0; + else + return bc->callback->elem_size(bc); +} + +int +yasm_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + int retval = 0; + + bc->len = 0; + + if (!bc->callback) + yasm_internal_error(N_("got empty bytecode in yasm_bc_calc_len")); + else + retval = bc->callback->calc_len(bc, add_span, add_span_data); + + /* Check for multiples */ + bc->mult_int = 1; + if (bc->multiple) { + /*@dependent@*/ /*@null@*/ const yasm_intnum *num; + + num = yasm_expr_get_intnum(&bc->multiple, 0); + if (num) { + if (yasm_intnum_sign(num) < 0) { + yasm_error_set(YASM_ERROR_VALUE, N_("multiple is negative")); + retval = -1; + } else + bc->mult_int = yasm_intnum_get_int(num); + } else { + if (yasm_expr__contains(bc->multiple, YASM_EXPR_FLOAT)) { + yasm_error_set(YASM_ERROR_VALUE, + N_("expression must not contain floating point value")); + retval = -1; + } else { + yasm_value value; + yasm_value_initialize(&value, bc->multiple, 0); + add_span(add_span_data, bc, 0, &value, 0, 0); + bc->mult_int = 0; /* assume 0 to start */ + } + } + } + + /* If we got an error somewhere along the line, clear out any calc len */ + if (retval < 0) + bc->len = 0; + + return retval; +} + +int +yasm_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + if (span == 0) { + bc->mult_int = new_val; + return 1; + } + if (!bc->callback) { + yasm_internal_error(N_("got empty bytecode in yasm_bc_expand")); + /*@unreached@*/ + return -1; + } else + return bc->callback->expand(bc, span, old_val, new_val, neg_thres, + pos_thres); +} + +/*@null@*/ /*@only@*/ unsigned char * +yasm_bc_tobytes(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, + /*@out@*/ int *gap, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc) + /*@sets *buf@*/ +{ + /*@only@*/ /*@null@*/ unsigned char *mybuf = NULL; + unsigned char *bufstart; + unsigned char *origbuf, *destbuf; + long i; + int error = 0; + + long mult; + if (yasm_bc_get_multiple(bc, &mult, 1) || mult == 0) { + *bufsize = 0; + return NULL; + } + bc->mult_int = mult; + + /* special case for reserve bytecodes */ + if (bc->callback->special == YASM_BC_SPECIAL_RESERVE) { + *bufsize = bc->len*bc->mult_int; + *gap = 1; + return NULL; /* we didn't allocate a buffer */ + } + *gap = 0; + + if (*bufsize < bc->len*bc->mult_int) { + mybuf = yasm_xmalloc(bc->len*bc->mult_int); + destbuf = mybuf; + } else + destbuf = buf; + bufstart = destbuf; + + *bufsize = bc->len*bc->mult_int; + + if (!bc->callback) + yasm_internal_error(N_("got empty bytecode in bc_tobytes")); + else for (i=0; i<bc->mult_int; i++) { + origbuf = destbuf; + error = bc->callback->tobytes(bc, &destbuf, bufstart, d, output_value, + output_reloc); + + if (!error && ((unsigned long)(destbuf - origbuf) != bc->len)) + yasm_internal_error( + N_("written length does not match optimized length")); + } + + return mybuf; +} + +int +yasm_bc_get_multiple(yasm_bytecode *bc, long *multiple, int calc_bc_dist) +{ + /*@dependent@*/ /*@null@*/ const yasm_intnum *num; + + *multiple = 1; + if (bc->multiple) { + num = yasm_expr_get_intnum(&bc->multiple, calc_bc_dist); + if (!num) { + yasm_error_set(YASM_ERROR_VALUE, + N_("could not determine multiple")); + return 1; + } + if (yasm_intnum_sign(num) < 0) { + yasm_error_set(YASM_ERROR_VALUE, N_("multiple is negative")); + return 1; + } + *multiple = yasm_intnum_get_int(num); + } + return 0; +} + +const yasm_expr * +yasm_bc_get_multiple_expr(const yasm_bytecode *bc) +{ + return bc->multiple; +} + +yasm_insn * +yasm_bc_get_insn(yasm_bytecode *bc) +{ + if (bc->callback->special != YASM_BC_SPECIAL_INSN) + return NULL; + return (yasm_insn *)bc->contents; +} diff --git a/libyasm/bytecode.h b/libyasm/bytecode.h new file mode 100644 index 0000000..4e8a801 --- /dev/null +++ b/libyasm/bytecode.h @@ -0,0 +1,633 @@ +/** + * \file libyasm/bytecode.h + * \brief YASM bytecode interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_BYTECODE_H +#define YASM_BYTECODE_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** A data value (opaque type). */ +typedef struct yasm_dataval yasm_dataval; +/** A list of data values. */ +typedef struct yasm_datavalhead yasm_datavalhead; + +/** Linked list of data values. */ +/*@reldef@*/ STAILQ_HEAD(yasm_datavalhead, yasm_dataval); + +/** Add a dependent span for a bytecode. + * \param add_span_data add_span_data passed into bc_calc_len() + * \param bc bytecode containing span + * \param id non-zero identifier for span; may be any non-zero value + * if <0, expand is called for any change; + * if >0, expand is only called when exceeds threshold + * \param value dependent value for bytecode expansion + * \param neg_thres negative threshold for long/short decision + * \param pos_thres positive threshold for long/short decision + */ +typedef void (*yasm_bc_add_span_func) + (void *add_span_data, yasm_bytecode *bc, int id, const yasm_value *value, + long neg_thres, long pos_thres); + +/** Bytecode callback structure. Any implementation of a specific bytecode + * must implement these functions and this callback structure. The bytecode + * implementation-specific data is stored in #yasm_bytecode.contents. + */ +typedef struct yasm_bytecode_callback { + /** Destroys the implementation-specific data. + * Called from yasm_bc_destroy(). + * \param contents #yasm_bytecode.contents + */ + void (*destroy) (/*@only@*/ void *contents); + + /** Prints the implementation-specific data (for debugging purposes). + * Called from yasm_bc_print(). + * \param contents #yasm_bytecode.contents + * \param f file + * \param indent_level indentation level + */ + void (*print) (const void *contents, FILE *f, int indent_level); + + /** Finalizes the bytecode after parsing. Called from yasm_bc_finalize(). + * A generic fill-in for this is yasm_bc_finalize_common(). + * \param bc bytecode + * \param prev_bc bytecode directly preceding bc + */ + void (*finalize) (yasm_bytecode *bc, yasm_bytecode *prev_bc); + + /** Return elements size of a data bytecode. + * This function should return the size of each elements of a data + * bytecode, for proper dereference of symbols attached to it. + * \param bc bytecode + * \return 0 if element size is unknown. + */ + int (*elem_size) (yasm_bytecode *bc); + + /** Calculates the minimum size of a bytecode. + * Called from yasm_bc_calc_len(). + * A generic fill-in for this is yasm_bc_calc_len_common(), but as this + * function internal errors when called, be very careful when using it! + * This function should simply add to bc->len and not set it directly + * (it's initialized by yasm_bc_calc_len() prior to passing control to + * this function). + * + * \param bc bytecode + * \param add_span function to call to add a span + * \param add_span_data extra data to be passed to add_span function + * \return 0 if no error occurred, nonzero if there was an error + * recognized (and output) during execution. + * \note May store to bytecode updated expressions. + */ + int (*calc_len) (yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); + + /** Recalculates the bytecode's length based on an expanded span length. + * Called from yasm_bc_expand(). + * A generic fill-in for this is yasm_bc_expand_common(), but as this + * function internal errors when called, if used, ensure that calc_len() + * never adds a span. + * This function should simply add to bc->len to increase the length by + * a delta amount. + * \param bc bytecode + * \param span span ID (as given to add_span in calc_len) + * \param old_val previous span value + * \param new_val new span value + * \param neg_thres negative threshold for long/short decision + * (returned) + * \param pos_thres positive threshold for long/short decision + * (returned) + * \return 0 if bc no longer dependent on this span's length, negative if + * there was an error recognized (and output) during execution, + * and positive if bc size may increase for this span further + * based on the new negative and positive thresholds returned. + * \note May store to bytecode updated expressions. + */ + int (*expand) (yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres); + + /** Convert a bytecode into its byte representation. + * Called from yasm_bc_tobytes(). + * A generic fill-in for this is yasm_bc_tobytes_common(), but as this + * function internal errors when called, be very careful when using it! + * \param bc bytecode + * \param bufp byte representation destination buffer; + * should be incremented as it's written to, + * so that on return its delta from the + * passed-in buf matches the bytecode length + * (it's okay not to do this if an error + * indication is returned) + * \param bufstart For calculating the correct offset parameter for + * the \a output_value calls: *bufp - bufstart. + * \param d data to pass to each call to + * output_value/output_reloc + * \param output_value function to call to convert values into their byte + * representation + * \param output_reloc function to call to output relocation entries + * for a single sym + * \return Nonzero on error, 0 on success. + * \note May result in non-reversible changes to the bytecode, but it's + * preferable if calling this function twice would result in the + * same output. + */ + int (*tobytes) (yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + + /** Special bytecode classifications. Most bytecode types should use + * #YASM_BC_SPECIAL_NONE. Others cause special handling to kick in + * in various parts of yasm. + */ + enum yasm_bytecode_special_type { + YASM_BC_SPECIAL_NONE = 0, + + /** Bytecode reserves space instead of outputting data. */ + YASM_BC_SPECIAL_RESERVE, + + /** Adjusts offset instead of calculating len. */ + YASM_BC_SPECIAL_OFFSET, + + /** Instruction bytecode. */ + YASM_BC_SPECIAL_INSN + } special; +} yasm_bytecode_callback; + +/** A bytecode. */ +struct yasm_bytecode { + /** Bytecodes are stored as a singly linked list, with tail insertion. + * \see section.h (#yasm_section). + */ + /*@reldef@*/ STAILQ_ENTRY(yasm_bytecode) link; + + /** The bytecode callback structure for this bytecode. May be NULL + * during partial initialization. + */ + /*@null@*/ const yasm_bytecode_callback *callback; + + /** Pointer to section containing bytecode; NULL if not part of a + * section. + */ + /*@dependent@*/ /*@null@*/ yasm_section *section; + + /** Number of times bytecode is repeated. + * NULL=1 (to save space in the common case). + */ + /*@only@*/ /*@null@*/ yasm_expr *multiple; + + /** Total length of entire bytecode (not including multiple copies). */ + unsigned long len; + + /** Number of copies, integer version. */ + long mult_int; + + /** Line number where bytecode was defined. */ + unsigned long line; + + /** Offset of bytecode from beginning of its section. + * 0-based, ~0UL (e.g. all 1 bits) if unknown. + */ + unsigned long offset; + + /** Unique integer index of bytecode. Used during optimization. */ + unsigned long bc_index; + + /** NULL-terminated array of labels that point to this bytecode (as the + * bytecode previous to the label). NULL if no labels point here. + */ + /*@null@*/ yasm_symrec **symrecs; + + /** Implementation-specific data (type identified by callback). */ + void *contents; +}; + +/** Create a bytecode of any specified type. + * \param callback bytecode callback functions, if NULL, creates empty + * bytecode (may not be resolved or output) + * \param contents type-specific data + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode of the specified type. + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_common + (/*@null@*/ const yasm_bytecode_callback *callback, + /*@only@*/ /*@null@*/ void *contents, unsigned long line); + +/** Transform a bytecode of any type into a different type. + * \param bc bytecode to transform + * \param callback new bytecode callback function + * \param contents new type-specific data + */ +YASM_LIB_DECL +void yasm_bc_transform(yasm_bytecode *bc, + const yasm_bytecode_callback *callback, + void *contents); + +/** Common bytecode callback finalize function, for where no finalization + * is ever required for this type of bytecode. + */ +YASM_LIB_DECL +void yasm_bc_finalize_common(yasm_bytecode *bc, yasm_bytecode *prev_bc); + +/** Common bytecode callback calc_len function, for where the bytecode has + * no calculatable length. Causes an internal error if called. + */ +YASM_LIB_DECL +int yasm_bc_calc_len_common(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); + +/** Common bytecode callback expand function, for where the bytecode is + * always short (calc_len never calls add_span). Causes an internal + * error if called. + */ +YASM_LIB_DECL +int yasm_bc_expand_common + (yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres); + +/** Common bytecode callback tobytes function, for where the bytecode + * cannot be converted to bytes. Causes an internal error if called. + */ +YASM_LIB_DECL +int yasm_bc_tobytes_common + (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +/** Get the next bytecode in a linked list of bytecodes. + * \param bc bytecode + * \return Next bytecode. + */ +#define yasm_bc__next(bc) STAILQ_NEXT(bc, link) + +/** Set multiple field of a bytecode. + * A bytecode can be repeated a number of times when output. This function + * sets that multiple. + * \param bc bytecode + * \param e multiple (kept, do not free) + */ +YASM_LIB_DECL +void yasm_bc_set_multiple(yasm_bytecode *bc, /*@keep@*/ yasm_expr *e); + +/** Create a bytecode containing data value(s). + * \param datahead list of data values (kept, do not free) + * \param size storage size (in bytes) for each data value + * \param append_zero append a single zero byte after each data value + * (if non-zero) + * \param arch architecture (optional); if provided, data items + * are directly simplified to bytes if possible + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode. + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_data + (yasm_datavalhead *datahead, unsigned int size, int append_zero, + /*@null@*/ yasm_arch *arch, unsigned long line); + +/** Create a bytecode containing LEB128-encoded data value(s). + * \param datahead list of data values (kept, do not free) + * \param sign signedness (1=signed, 0=unsigned) of each data value + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode. + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_leb128 + (yasm_datavalhead *datahead, int sign, unsigned long line); + +/** Create a bytecode reserving space. + * \param numitems number of reserve "items" (kept, do not free) + * \param itemsize reserved size (in bytes) for each item + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode. + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_reserve + (/*@only@*/ yasm_expr *numitems, unsigned int itemsize, + unsigned long line); + +/** Get the number of items and itemsize for a reserve bytecode. If bc + * is not a reserve bytecode, returns NULL. + * \param bc bytecode + * \param itemsize reserved size (in bytes) for each item (returned) + * \return NULL if bc is not a reserve bytecode, otherwise an expression + * for the number of items to reserve. + */ +YASM_LIB_DECL +/*@null@*/ const yasm_expr *yasm_bc_reserve_numitems + (yasm_bytecode *bc, /*@out@*/ unsigned int *itemsize); + +/** Create a bytecode that includes a binary file verbatim. + * \param filename path to binary file (kept, do not free) + * \param start starting location in file (in bytes) to read data from + * (kept, do not free); may be NULL to indicate 0 + * \param maxlen maximum number of bytes to read from the file (kept, do + * do not free); may be NULL to indicate no maximum + * \param linemap line mapping repository + * \param line virtual line (from yasm_linemap) for the bytecode + * \return Newly allocated bytecode. + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_incbin + (/*@only@*/ char *filename, /*@only@*/ /*@null@*/ yasm_expr *start, + /*@only@*/ /*@null@*/ yasm_expr *maxlen, yasm_linemap *linemap, + unsigned long line); + +/** Create a bytecode that aligns the following bytecode to a boundary. + * \param boundary byte alignment (must be a power of two) + * \param fill fill data (if NULL, code_fill or 0 is used) + * \param maxskip maximum number of bytes to skip + * \param code_fill code fill data (if NULL, 0 is used) + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode. + * \note The precedence on generated fill is as follows: + * - from fill parameter (if not NULL) + * - from code_fill parameter (if not NULL) + * - 0 + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_align + (/*@keep@*/ yasm_expr *boundary, /*@keep@*/ /*@null@*/ yasm_expr *fill, + /*@keep@*/ /*@null@*/ yasm_expr *maxskip, + /*@null@*/ const unsigned char **code_fill, unsigned long line); + +/** Create a bytecode that puts the following bytecode at a fixed section + * offset. + * \param start section offset of following bytecode + * \param fill fill value + * \param line virtual line (from yasm_linemap) + * \return Newly allocated bytecode. + */ +YASM_LIB_DECL +/*@only@*/ yasm_bytecode *yasm_bc_create_org + (unsigned long start, unsigned long fill, unsigned long line); + +/** Get the section that contains a particular bytecode. + * \param bc bytecode + * \return Section containing bc (can be NULL if bytecode is not part of a + * section). + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ yasm_section *yasm_bc_get_section + (yasm_bytecode *bc); + +/** Add to the list of symrecs that reference a bytecode. For symrec use + * only. + * \param bc bytecode + * \param sym symbol + */ +YASM_LIB_DECL +void yasm_bc__add_symrec(yasm_bytecode *bc, /*@dependent@*/ yasm_symrec *sym); + +/** Delete (free allocated memory for) a bytecode. + * \param bc bytecode (only pointer to it); may be NULL + */ +YASM_LIB_DECL +void yasm_bc_destroy(/*@only@*/ /*@null@*/ yasm_bytecode *bc); + +/** Print a bytecode. For debugging purposes. + * \param f file + * \param indent_level indentation level + * \param bc bytecode + */ +YASM_LIB_DECL +void yasm_bc_print(const yasm_bytecode *bc, FILE *f, int indent_level); + +/** Finalize a bytecode after parsing. + * \param bc bytecode + * \param prev_bc bytecode directly preceding bc in a list of bytecodes + */ +YASM_LIB_DECL +void yasm_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); + +/** Determine the distance between the starting offsets of two bytecodes. + * \param precbc1 preceding bytecode to the first bytecode + * \param precbc2 preceding bytecode to the second bytecode + * \return Distance in bytes between the two bytecodes (bc2-bc1), or NULL if + * the distance was indeterminate. + * \warning Only valid /after/ optimization. + */ +YASM_LIB_DECL +/*@null@*/ /*@only@*/ yasm_intnum *yasm_calc_bc_dist + (yasm_bytecode *precbc1, yasm_bytecode *precbc2); + +/** Get the offset of the next bytecode (the next bytecode doesn't have to + * actually exist). + * \param precbc preceding bytecode + * \return Offset of the next bytecode in bytes. + * \warning Only valid /after/ optimization. + */ +YASM_LIB_DECL +unsigned long yasm_bc_next_offset(yasm_bytecode *precbc); + +/** Return elemens size of a data bytecode. + * Returns the size of each elements of a data bytecode, for proper dereference + * of symbols attached to it. + * \param bc bytecode + * \return 0 if element size is unknown + */ +YASM_LIB_DECL +int yasm_bc_elem_size(yasm_bytecode *bc); + +/** Resolve EQUs in a bytecode and calculate its minimum size. + * Generates dependent bytecode spans for cases where, if the length spanned + * increases, it could cause the bytecode size to increase. + * Any bytecode multiple is NOT included in the length or spans generation; + * this must be handled at a higher level. + * \param bc bytecode + * \param add_span function to call to add a span + * \param add_span_data extra data to be passed to add_span function + * \return 0 if no error occurred, nonzero if there was an error recognized + * (and output) during execution. + * \note May store to bytecode updated expressions and the short length. + */ +YASM_LIB_DECL +int yasm_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); + +/** Recalculate a bytecode's length based on an expanded span length. + * \param bc bytecode + * \param span span ID (as given to yasm_bc_add_span_func in + * yasm_bc_calc_len) + * \param old_val previous span value + * \param new_val new span value + * \param neg_thres negative threshold for long/short decision (returned) + * \param pos_thres positive threshold for long/short decision (returned) + * \return 0 if bc no longer dependent on this span's length, negative if + * there was an error recognized (and output) during execution, and + * positive if bc size may increase for this span further based on the + * new negative and positive thresholds returned. + * \note May store to bytecode updated expressions and the updated length. + */ +YASM_LIB_DECL +int yasm_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres); + +/** Convert a bytecode into its byte representation. + * \param bc bytecode + * \param buf byte representation destination buffer + * \param bufsize size of buf (in bytes) prior to call; size of the + * generated data after call + * \param gap if nonzero, indicates the data does not really need to + * exist in the object file; if nonzero, contents of buf + * are undefined [output] + * \param d data to pass to each call to output_value/output_reloc + * \param output_value function to call to convert values into their byte + * representation + * \param output_reloc function to call to output relocation entries + * for a single sym + * \return Newly allocated buffer that should be used instead of buf for + * reading the byte representation, or NULL if buf was big enough to + * hold the entire byte representation. + * \note Calling twice on the same bytecode may \em not produce the same + * results on the second call, as calling this function may result in + * non-reversible changes to the bytecode. + */ +YASM_LIB_DECL +/*@null@*/ /*@only@*/ unsigned char *yasm_bc_tobytes + (yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, + /*@out@*/ int *gap, void *d, yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc) + /*@sets *buf@*/; + +/** Get the bytecode multiple value as an integer. + * \param bc bytecode + * \param multiple multiple value (output) + * \param calc_bc_dist nonzero if distances between bytecodes should be + * calculated, 0 if error should be returned in this case + * \return 1 on error (set with yasm_error_set), 0 on success. + */ +YASM_LIB_DECL +int yasm_bc_get_multiple(yasm_bytecode *bc, /*@out@*/ long *multiple, + int calc_bc_dist); + +/** Get the bytecode multiple value as an expression. + * \param bc bytecode + * \return Bytecode multiple, NULL if =1. + */ +YASM_LIB_DECL +const yasm_expr *yasm_bc_get_multiple_expr(const yasm_bytecode *bc); + +/** Get a #yasm_insn structure from an instruction bytecode (if possible). + * \param bc bytecode + * \return Instruction details if bytecode is an instruction bytecode, + * otherwise NULL. + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ yasm_insn *yasm_bc_get_insn(yasm_bytecode *bc); + +/** Create a new data value from an expression. + * \param expn expression + * \return Newly allocated data value. + */ +YASM_LIB_DECL +yasm_dataval *yasm_dv_create_expr(/*@keep@*/ yasm_expr *expn); + +/** Create a new data value from a string. + * \param contents string (may contain NULs) + * \param len length of string + * \return Newly allocated data value. + */ +yasm_dataval *yasm_dv_create_string(/*@keep@*/ char *contents, size_t len); + +/** Create a new data value from raw bytes data. + * \param contents raw data (may contain NULs) + * \param len length + * \return Newly allocated data value. + */ +YASM_LIB_DECL +yasm_dataval *yasm_dv_create_raw(/*@keep@*/ unsigned char *contents, + unsigned long len); + +/** Create a new uninitialized data value. + * \return Newly allocated data value. + */ +yasm_dataval *yasm_dv_create_reserve(void); + +#ifndef YASM_DOXYGEN +#define yasm_dv_create_string(s, l) yasm_dv_create_raw((unsigned char *)(s), \ + (unsigned long)(l)) +#endif + +/** Get the underlying value of a data value. + * \param dv data value + * \return Value, or null if non-value (e.g. string or raw). + */ +yasm_value *yasm_dv_get_value(yasm_dataval *dv); + +/** Set multiple field of a data value. + * A data value can be repeated a number of times when output. This function + * sets that multiple. + * \param dv data value + * \param e multiple (kept, do not free) + */ +void yasm_dv_set_multiple(yasm_dataval *dv, /*@keep@*/ yasm_expr *e); + +/** Get the data value multiple value as an unsigned long integer. + * \param dv data value + * \param multiple multiple value (output) + * \return 1 on error (set with yasm_error_set), 0 on success. + */ +int yasm_dv_get_multiple(yasm_dataval *dv, /*@out@*/ unsigned long *multiple); + +/** Initialize a list of data values. + * \param headp list of data values + */ +void yasm_dvs_initialize(yasm_datavalhead *headp); +#ifndef YASM_DOXYGEN +#define yasm_dvs_initialize(headp) STAILQ_INIT(headp) +#endif + +/** Delete (free allocated memory for) a list of data values. + * \param headp list of data values + */ +YASM_LIB_DECL +void yasm_dvs_delete(yasm_datavalhead *headp); + +/** Add data value to the end of a list of data values. + * \note Does not make a copy of the data value; so don't pass this function + * static or local variables, and discard the dv pointer after calling + * this function. + * \param headp data value list + * \param dv data value (may be NULL) + * \return If data value was actually appended (it wasn't NULL), the data + * value; otherwise NULL. + */ +YASM_LIB_DECL +/*@null@*/ yasm_dataval *yasm_dvs_append + (yasm_datavalhead *headp, /*@returned@*/ /*@null@*/ yasm_dataval *dv); + +/** Print a data value list. For debugging purposes. + * \param f file + * \param indent_level indentation level + * \param headp data value list + */ +YASM_LIB_DECL +void yasm_dvs_print(const yasm_datavalhead *headp, FILE *f, int indent_level); + +#endif diff --git a/libyasm/compat-queue.h b/libyasm/compat-queue.h new file mode 100644 index 0000000..da9eff4 --- /dev/null +++ b/libyasm/compat-queue.h @@ -0,0 +1,456 @@ +/* + * <sys/queue.h> implementation for systems that don't have it. + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.32.2.4 2001/03/31 03:33:39 hsu Exp $ + */ + +#ifndef SYS_QUEUE_H +#define SYS_QUEUE_H + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * + */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = \ + SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ + } \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + if ((STAILQ_NEXT(curelm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ + } \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ +} while (0) + +#endif /* !SYS_QUEUE_H */ diff --git a/libyasm/coretype.h b/libyasm/coretype.h new file mode 100644 index 0000000..624e3c4 --- /dev/null +++ b/libyasm/coretype.h @@ -0,0 +1,393 @@ +/** + * \file libyasm/coretype.h + * \brief YASM core types and utility functions. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_CORETYPE_H +#define YASM_CORETYPE_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Architecture instance (mostly opaque type). \see arch.h for details. */ +typedef struct yasm_arch yasm_arch; +/** Preprocessor interface. \see preproc.h for details. */ +typedef struct yasm_preproc yasm_preproc; +/** Parser instance (mostly opaque type). \see parser.h for details. */ +typedef struct yasm_parser yasm_parser; +/** Object format interface. \see objfmt.h for details. */ +typedef struct yasm_objfmt yasm_objfmt; +/** Debug format interface. \see dbgfmt.h for details. */ +typedef struct yasm_dbgfmt yasm_dbgfmt; +/** List format interface. \see listfmt.h for details. */ +typedef struct yasm_listfmt yasm_listfmt; + +/** Object format module interface. \see objfmt.h for details. */ +typedef struct yasm_objfmt_module yasm_objfmt_module; +/** Debug format module interface. \see dbgfmt.h for details. */ +typedef struct yasm_dbgfmt_module yasm_dbgfmt_module; + +/** Standard macro structure for modules that allows association of a set of + * standard macros with a parser/preprocessor combination. + * A NULL-terminated array of these structures is used in a number of module + * interfaces. + */ +typedef struct yasm_stdmac { + const char *parser; /**< Parser keyword */ + const char *preproc; /**< Preprocessor keyword */ + + /** NULL-terminated array of standard macros. May be NULL if no standard + * macros should be added for this preprocessor. + */ + const char **macros; +} yasm_stdmac; + +/** YASM associated data callback structure. Many data structures can have + * arbitrary data associated with them. + */ +typedef struct yasm_assoc_data_callback { + /** Free memory allocated for associated data. + * \param data associated data + */ + void (*destroy) (/*@only@*/ void *data); + + /** Print a description of allocated data. For debugging purposes. + * \param data associated data + * \param f output file + * \param indent_level indentation level + */ + void (*print) (void *data, FILE *f, int indent_level); +} yasm_assoc_data_callback; + +/** Set of collected error/warnings (opaque type). + * \see errwarn.h for details. + */ +typedef struct yasm_errwarns yasm_errwarns; + +/** Bytecode. \see bytecode.h for details and related functions. */ +typedef struct yasm_bytecode yasm_bytecode; + +/** Object. \see section.h for details and related functions. */ +typedef struct yasm_object yasm_object; + +/** Section (opaque type). \see section.h for related functions. */ +typedef struct yasm_section yasm_section; + +/** Symbol table (opaque type). \see symrec.h for related functions. */ +typedef struct yasm_symtab yasm_symtab; + +/** Symbol record (opaque type). \see symrec.h for related functions. */ +typedef struct yasm_symrec yasm_symrec; + +/** Expression. \see expr.h for details and related functions. */ +typedef struct yasm_expr yasm_expr; +/** Integer value (opaque type). \see intnum.h for related functions. */ +typedef struct yasm_intnum yasm_intnum; +/** Floating point value (opaque type). + * \see floatnum.h for related functions. + */ +typedef struct yasm_floatnum yasm_floatnum; + +/** A value. May be absolute or relative. Outside the parser, yasm_expr + * should only be used for absolute exprs. Anything that could contain + * a relocatable value should use this structure instead. + * \see value.h for related functions. + */ +typedef struct yasm_value { + /** The absolute portion of the value. May contain *differences* between + * symrecs but not standalone symrecs. May be NULL if there is no + * absolute portion (e.g. the absolute portion is 0). + */ + /*@null@*/ /*@only@*/ yasm_expr *abs; + + /** The relative portion of the value. This is the portion that may + * need to generate a relocation. May be NULL if no relative portion. + */ + /*@null@*/ /*@dependent@*/ yasm_symrec *rel; + + /** What the relative portion is in reference to. NULL if the default. */ + /*@null@*/ /*@dependent@*/ yasm_symrec *wrt; + + /** If the segment of the relative portion should be used, not the + * relative portion itself. Boolean. + */ + unsigned int seg_of : 1; + + /** If the relative portion of the value should be shifted right + * (supported only by a few object formats). If just the absolute portion + * should be shifted, that must be in the abs expr, not here! + */ + unsigned int rshift : 7; + + /** Indicates the relative portion of the value should be relocated + * relative to the current assembly position rather than relative to the + * section start. "Current assembly position" here refers to the starting + * address of the bytecode containing this value. Boolean. + */ + unsigned int curpos_rel : 1; + + /** Indicates that curpos_rel was set due to IP-relative relocation; + * in some objfmt/arch combinations (e.g. win64/x86-amd64) this info + * is needed to generate special relocations. + */ + unsigned int ip_rel : 1; + + /** Indicates the value is a jump target address (rather than a simple + * data address). In some objfmt/arch combinations (e.g. macho/amd64) + * this info is needed to generate special relocations. + */ + unsigned int jump_target : 1; + + /** Indicates the relative portion of the value should be relocated + * relative to its own section start rather than relative to the + * section start of the bytecode containing this value. E.g. the value + * resulting from the relative portion should be the offset from its + * section start. Boolean. + */ + unsigned int section_rel : 1; + + /** Indicates overflow warnings have been disabled for this value. */ + unsigned int no_warn : 1; + + /** Sign of the value. Nonzero if the final value should be treated as + * signed, 0 if it should be treated as signed. + */ + unsigned int sign : 1; + + /** Size of the value, in bits. */ + unsigned int size : 8; +} yasm_value; + +/** Maximum value of #yasm_value.rshift */ +#define YASM_VALUE_RSHIFT_MAX 127 + +/** Line number mapping repository (opaque type). \see linemap.h for related + * functions. + */ +typedef struct yasm_linemap yasm_linemap; + +/** Value/parameter pair (opaque type). + * \see valparam.h for related functions. + */ +typedef struct yasm_valparam yasm_valparam; +/** List of value/parameters (opaque type). + * \see valparam.h for related functions. + */ +typedef struct yasm_valparamhead yasm_valparamhead; +/** Directive list entry. + * \see valparam.h for details and related functions. + */ +typedef struct yasm_directive yasm_directive; + +/** An effective address. + * \see insn.h for related functions. + */ +typedef struct yasm_effaddr yasm_effaddr; + +/** An instruction. + * \see insn.h for related functions. + */ +typedef struct yasm_insn yasm_insn; + +/** Expression operators usable in #yasm_expr expressions. */ +typedef enum yasm_expr_op { + YASM_EXPR_IDENT, /**< No operation, just a value. */ + YASM_EXPR_ADD, /**< Arithmetic addition (+). */ + YASM_EXPR_SUB, /**< Arithmetic subtraction (-). */ + YASM_EXPR_MUL, /**< Arithmetic multiplication (*). */ + YASM_EXPR_DIV, /**< Arithmetic unsigned division. */ + YASM_EXPR_SIGNDIV, /**< Arithmetic signed division. */ + YASM_EXPR_MOD, /**< Arithmetic unsigned modulus. */ + YASM_EXPR_SIGNMOD, /**< Arithmetic signed modulus. */ + YASM_EXPR_NEG, /**< Arithmetic negation (-). */ + YASM_EXPR_NOT, /**< Bitwise negation. */ + YASM_EXPR_OR, /**< Bitwise OR. */ + YASM_EXPR_AND, /**< Bitwise AND. */ + YASM_EXPR_XOR, /**< Bitwise XOR. */ + YASM_EXPR_XNOR, /**< Bitwise XNOR. */ + YASM_EXPR_NOR, /**< Bitwise NOR. */ + YASM_EXPR_SHL, /**< Shift left (logical). */ + YASM_EXPR_SHR, /**< Shift right (logical). */ + YASM_EXPR_LOR, /**< Logical OR. */ + YASM_EXPR_LAND, /**< Logical AND. */ + YASM_EXPR_LNOT, /**< Logical negation. */ + YASM_EXPR_LXOR, /**< Logical XOR. */ + YASM_EXPR_LXNOR, /**< Logical XNOR. */ + YASM_EXPR_LNOR, /**< Logical NOR. */ + YASM_EXPR_LT, /**< Less than comparison. */ + YASM_EXPR_GT, /**< Greater than comparison. */ + YASM_EXPR_EQ, /**< Equality comparison. */ + YASM_EXPR_LE, /**< Less than or equal to comparison. */ + YASM_EXPR_GE, /**< Greater than or equal to comparison. */ + YASM_EXPR_NE, /**< Not equal comparison. */ + YASM_EXPR_NONNUM, /**< Start of non-numeric operations (not an op). */ + YASM_EXPR_SEG, /**< SEG operator (gets segment portion of address). */ + YASM_EXPR_WRT, /**< WRT operator (gets offset of address relative to + * some other segment). */ + YASM_EXPR_SEGOFF /**< The ':' in segment:offset. */ +} yasm_expr_op; + +/** Convert yasm_value to its byte representation. Usually implemented by + * object formats to keep track of relocations and verify legal expressions. + * Must put the value into the least significant bits of the destination, + * unless shifted into more significant bits by the shift parameter. The + * destination bits must be cleared before being set. + * \param value value + * \param buf buffer for byte representation + * \param destsize destination size (in bytes) + * \param offset offset (in bytes) of the expr contents from the start + * of the bytecode (needed for relative) + * \param bc current bytecode (usually passed into higher-level + * calling function) + * \param warn enables standard warnings: zero for none; + * nonzero for overflow/underflow floating point warnings + * \param d objfmt-specific data (passed into higher-level calling + * function) + * \return Nonzero if an error occurred, 0 otherwise. + */ +typedef int (*yasm_output_value_func) + (yasm_value *value, /*@out@*/ unsigned char *buf, unsigned int destsize, + unsigned long offset, yasm_bytecode *bc, int warn, /*@null@*/ void *d); + +/** Convert a symbol reference to its byte representation. Usually implemented + * by object formats and debug formats to keep track of relocations generated + * by themselves. + * \param sym symbol + * \param bc current bytecode (usually passed into higher-level + * calling function) + * \param buf buffer for byte representation + * \param destsize destination size (in bytes) + * \param valsize size (in bits) + * \param warn enables standard warnings: zero for none; + * nonzero for overflow/underflow floating point warnings; + * negative for signed integer warnings, + * positive for unsigned integer warnings + * \param d objfmt-specific data (passed into higher-level calling + * function) + * \return Nonzero if an error occurred, 0 otherwise. + */ +typedef int (*yasm_output_reloc_func) + (yasm_symrec *sym, yasm_bytecode *bc, unsigned char *buf, + unsigned int destsize, unsigned int valsize, int warn, void *d); + +/** Sort an array using merge sort algorithm. + * \internal + * \param base base of array + * \param nmemb number of elements in array + * \param size size of each array element + * \param compar element comparison function + */ +YASM_LIB_DECL +int yasm__mergesort(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); + +/** Separate string by delimiters. + * \internal + * \param stringp string + * \param delim set of 1 or more delimiters + * \return First/next substring. + */ +YASM_LIB_DECL +/*@null@*/ char *yasm__strsep(char **stringp, const char *delim); + +/** Compare two strings, ignoring case differences. + * \internal + * \param s1 string 1 + * \param s2 string 2 + * \return 0 if strings are equal, -1 if s1<s2, 1 if s1>s2. + */ +YASM_LIB_DECL +int yasm__strcasecmp(const char *s1, const char *s2); + +/** Compare portion of two strings, ignoring case differences. + * \internal + * \param s1 string 1 + * \param s2 string 2 + * \param n maximum number of characters to compare + * \return 0 if strings are equal, -1 if s1<s2, 1 if s1>s2. + */ +YASM_LIB_DECL +int yasm__strncasecmp(const char *s1, const char *s2, size_t n); + +/** strdup() implementation using yasm_xmalloc(). + * \internal + * \param str string + * \return Newly allocated duplicate string. + */ +YASM_LIB_DECL +/*@only@*/ char *yasm__xstrdup(const char *str); + +/** strndup() implementation using yasm_xmalloc(). + * \internal + * \param str string + * \param max maximum number of characters to copy + * \return Newly allocated duplicate string. + */ +YASM_LIB_DECL +/*@only@*/ char *yasm__xstrndup(const char *str, size_t max); + +/** Error-checking memory allocation. A default implementation is provided + * that calls yasm_fatal() on allocation errors. + * A replacement should \em never return NULL. + * \param size number of bytes to allocate + * \return Allocated memory block. + */ +YASM_LIB_DECL +extern /*@only@*/ /*@out@*/ void * (*yasm_xmalloc) (size_t size); + +/** Error-checking memory allocation (with clear-to-0). A default + * implementation is provided that calls yasm_fatal() on allocation errors. + * A replacement should \em never return NULL. + * \param size number of elements to allocate + * \param elsize size (in bytes) of each element + * \return Allocated and cleared memory block. + */ +YASM_LIB_DECL +extern /*@only@*/ void * (*yasm_xcalloc) (size_t nelem, size_t elsize); + +/** Error-checking memory reallocation. A default implementation is provided + * that calls yasm_fatal() on allocation errors. A replacement should + * \em never return NULL. + * \param oldmem memory block to resize + * \param elsize new size, in bytes + * \return Re-allocated memory block. + */ +YASM_LIB_DECL +extern /*@only@*/ void * (*yasm_xrealloc) + (/*@only@*/ /*@out@*/ /*@returned@*/ /*@null@*/ void *oldmem, size_t size) + /*@modifies oldmem@*/; + +/** Error-checking memory deallocation. A default implementation is provided + * that calls yasm_fatal() on allocation errors. + * \param p memory block to free + */ +YASM_LIB_DECL +extern void (*yasm_xfree) (/*@only@*/ /*@out@*/ /*@null@*/ void *p) + /*@modifies p@*/; + +#endif diff --git a/libyasm/dbgfmt.h b/libyasm/dbgfmt.h new file mode 100644 index 0000000..8e038bb --- /dev/null +++ b/libyasm/dbgfmt.h @@ -0,0 +1,122 @@ +/** + * \file libyasm/dbgfmt.h + * \brief YASM debug format interface. + * + * \license + * Copyright (C) 2002-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_DBGFMT_H +#define YASM_DBGFMT_H + +#ifndef YASM_DOXYGEN +/** Base #yasm_dbgfmt structure. Must be present as the first element in any + * #yasm_dbgfmt implementation. + */ +typedef struct yasm_dbgfmt_base { + /** #yasm_dbgfmt_module implementation for this debug format. */ + const struct yasm_dbgfmt_module *module; +} yasm_dbgfmt_base; +#endif + +/** Debug format module interface. */ +struct yasm_dbgfmt_module { + /** One-line description of the debug format. */ + const char *name; + + /** Keyword used to select debug format. */ + const char *keyword; + + /** NULL-terminated list of directives. NULL if none. */ + /*@null@*/ const yasm_directive *directives; + + /** Create debug format. + * Module-level implementation of yasm_dbgfmt_create(). + * The filenames are provided solely for informational purposes. + * \param object object + * \return NULL if object format does not provide needed support. + */ + /*@null@*/ /*@only@*/ yasm_dbgfmt * (*create) (yasm_object *object); + + /** Module-level implementation of yasm_dbgfmt_destroy(). + * Call yasm_dbgfmt_destroy() instead of calling this function. + */ + void (*destroy) (/*@only@*/ yasm_dbgfmt *dbgfmt); + + /** Module-level implementation of yasm_dbgfmt_generate(). + * Call yasm_dbgfmt_generate() instead of calling this function. + */ + void (*generate) (yasm_object *object, yasm_linemap *linemap, + yasm_errwarns *errwarns); +}; + +/** Get the keyword used to select a debug format. + * \param dbgfmt debug format + * \return keyword + */ +const char *yasm_dbgfmt_keyword(const yasm_dbgfmt *dbgfmt); + +/** Initialize debug output for use. Must call before any other debug + * format functions. The filenames are provided solely for informational + * purposes. + * \param module debug format module + * \param object object to generate debugging information for + * \return NULL if object format does not provide needed support. + */ +/*@null@*/ /*@only@*/ yasm_dbgfmt *yasm_dbgfmt_create + (const yasm_dbgfmt_module *module, yasm_object *object); + +/** Cleans up any allocated debug format memory. + * \param dbgfmt debug format + */ +void yasm_dbgfmt_destroy(/*@only@*/ yasm_dbgfmt *dbgfmt); + +/** Generate debugging information bytecodes. + * \param object object + * \param linemap virtual/physical line mapping + * \param errwarns error/warning set + * \note Errors and warnings are stored into errwarns. + */ +void yasm_dbgfmt_generate(yasm_object *object, yasm_linemap *linemap, + yasm_errwarns *errwarns); + +#ifndef YASM_DOXYGEN + +/* Inline macro implementations for dbgfmt functions */ + +#define yasm_dbgfmt_keyword(dbgfmt) \ + (((yasm_dbgfmt_base *)dbgfmt)->module->keyword) + +#define yasm_dbgfmt_create(module, object) \ + module->create(object) + +#define yasm_dbgfmt_destroy(dbgfmt) \ + ((yasm_dbgfmt_base *)dbgfmt)->module->destroy(dbgfmt) +#define yasm_dbgfmt_generate(object, linemap, ews) \ + ((yasm_dbgfmt_base *)((object)->dbgfmt))->module->generate \ + (object, linemap, ews) + +#endif + +#endif diff --git a/libyasm/errwarn.c b/libyasm/errwarn.c new file mode 100644 index 0000000..d51a0de --- /dev/null +++ b/libyasm/errwarn.c @@ -0,0 +1,529 @@ +/* + * Error and warning reporting and related functions. + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include <ctype.h> +#include <stdarg.h> + +#include "coretype.h" + +#include "linemap.h" +#include "errwarn.h" + + +#define MSG_MAXSIZE 1024 + +#if !defined(HAVE_TOASCII) || defined(lint) +# define toascii(c) ((c) & 0x7F) +#endif + +/* Default handlers for replacable functions */ +static /*@exits@*/ void def_internal_error_ + (const char *file, unsigned int line, const char *message); +static /*@exits@*/ void def_fatal(const char *message, va_list va); +static const char *def_gettext_hook(const char *msgid); + +/* Storage for errwarn's "extern" functions */ +/*@exits@*/ void (*yasm_internal_error_) + (const char *file, unsigned int line, const char *message) + = def_internal_error_; +/*@exits@*/ void (*yasm_fatal) (const char *message, va_list va) = def_fatal; +const char * (*yasm_gettext_hook) (const char *msgid) = def_gettext_hook; + +/* Error indicator */ +/* yasm_eclass is not static so that yasm_error_occurred macro can access it */ +yasm_error_class yasm_eclass; +static /*@only@*/ /*@null@*/ char *yasm_estr; +static unsigned long yasm_exrefline; +static /*@only@*/ /*@null@*/ char *yasm_exrefstr; + +/* Warning indicator */ +typedef struct warn { + /*@reldef@*/ STAILQ_ENTRY(warn) link; + + yasm_warn_class wclass; + /*@owned@*/ /*@null@*/ char *wstr; +} warn; +static STAILQ_HEAD(warn_head, warn) yasm_warns; + +/* Enabled warnings. See errwarn.h for a list. */ +static unsigned long warn_class_enabled; + +typedef struct errwarn_data { + /*@reldef@*/ SLIST_ENTRY(errwarn_data) link; + + enum { WE_UNKNOWN, WE_ERROR, WE_WARNING, WE_PARSERERROR } type; + + unsigned long line; + unsigned long xrefline; + /*@owned@*/ char *msg; + /*@owned@*/ char *xrefmsg; +} errwarn_data; + +struct yasm_errwarns { + /*@reldef@*/ SLIST_HEAD(errwarn_head, errwarn_data) errwarns; + + /* Total error count */ + unsigned int ecount; + + /* Total warning count */ + unsigned int wcount; + + /* Last inserted error/warning. Used to speed up insertions. */ + /*@null@*/ errwarn_data *previous_we; +}; + +/* Static buffer for use by conv_unprint(). */ +static char unprint[5]; + + +static const char * +def_gettext_hook(const char *msgid) +{ + return msgid; +} + +void +yasm_errwarn_initialize(void) +{ + /* Default enabled warnings. See errwarn.h for a list. */ + warn_class_enabled = + (1UL<<YASM_WARN_GENERAL) | (1UL<<YASM_WARN_UNREC_CHAR) | + (1UL<<YASM_WARN_PREPROC) | (0UL<<YASM_WARN_ORPHAN_LABEL) | + (1UL<<YASM_WARN_UNINIT_CONTENTS) | (0UL<<YASM_WARN_SIZE_OVERRIDE) | + (1UL<<YASM_WARN_IMPLICIT_SIZE_OVERRIDE); + + yasm_eclass = YASM_ERROR_NONE; + yasm_estr = NULL; + yasm_exrefline = 0; + yasm_exrefstr = NULL; + + STAILQ_INIT(&yasm_warns); +} + +void +yasm_errwarn_cleanup(void) +{ + yasm_error_clear(); + yasm_warn_clear(); +} + +/* Convert a possibly unprintable character into a printable string, using + * standard cat(1) convention for unprintable characters. + */ +char * +yasm__conv_unprint(int ch) +{ + int pos = 0; + + if (((ch & ~0x7F) != 0) /*!isascii(ch)*/ && !isprint(ch)) { + unprint[pos++] = 'M'; + unprint[pos++] = '-'; + ch &= toascii(ch); + } + if (iscntrl(ch)) { + unprint[pos++] = '^'; + unprint[pos++] = (ch == '\177') ? '?' : ch | 0100; + } else + unprint[pos++] = ch; + unprint[pos] = '\0'; + + return unprint; +} + +/* Report an internal error. Essentially a fatal error with trace info. + * Exit immediately because it's essentially an assert() trap. + */ +static void +def_internal_error_(const char *file, unsigned int line, const char *message) +{ + fprintf(stderr, + yasm_gettext_hook(N_("INTERNAL ERROR at %s, line %u: %s\n")), + file, line, yasm_gettext_hook(message)); +#ifdef HAVE_ABORT + abort(); +#else + exit(EXIT_FAILURE); +#endif +} + +/* Report a fatal error. These are unrecoverable (such as running out of + * memory), so just exit immediately. + */ +static void +def_fatal(const char *fmt, va_list va) +{ + fprintf(stderr, "%s: ", yasm_gettext_hook(N_("FATAL"))); + vfprintf(stderr, yasm_gettext_hook(fmt), va); + fputc('\n', stderr); + exit(EXIT_FAILURE); +} + +/* Create an errwarn structure in the correct linked list location. + * If replace_parser_error is nonzero, overwrites the last error if its + * type is WE_PARSERERROR. + */ +static errwarn_data * +errwarn_data_new(yasm_errwarns *errwarns, unsigned long line, + int replace_parser_error) +{ + errwarn_data *first, *next, *ins_we, *we; + enum { INS_NONE, INS_HEAD, INS_AFTER } action = INS_NONE; + + /* Find the entry with either line=line or the last one with line<line. + * Start with the last entry added to speed the search. + */ + ins_we = errwarns->previous_we; + first = SLIST_FIRST(&errwarns->errwarns); + if (!ins_we || !first) + action = INS_HEAD; + while (action == INS_NONE) { + next = SLIST_NEXT(ins_we, link); + if (line < ins_we->line) { + if (ins_we == first) + action = INS_HEAD; + else + ins_we = first; + } else if (!next) + action = INS_AFTER; + else if (line >= ins_we->line && line < next->line) + action = INS_AFTER; + else + ins_we = next; + } + + if (replace_parser_error && ins_we && ins_we->type == WE_PARSERERROR) { + /* overwrite last error */ + we = ins_we; + } else { + /* add a new error */ + we = yasm_xmalloc(sizeof(errwarn_data)); + + we->type = WE_UNKNOWN; + we->line = line; + we->xrefline = 0; + we->msg = NULL; + we->xrefmsg = NULL; + + if (action == INS_HEAD) + SLIST_INSERT_HEAD(&errwarns->errwarns, we, link); + else if (action == INS_AFTER) { + assert(ins_we != NULL); + SLIST_INSERT_AFTER(ins_we, we, link); + } else + yasm_internal_error(N_("Unexpected errwarn insert action")); + } + + /* Remember previous err/warn */ + errwarns->previous_we = we; + + return we; +} + +void +yasm_error_clear(void) +{ + if (yasm_estr) + yasm_xfree(yasm_estr); + if (yasm_exrefstr) + yasm_xfree(yasm_exrefstr); + yasm_eclass = YASM_ERROR_NONE; + yasm_estr = NULL; + yasm_exrefline = 0; + yasm_exrefstr = NULL; +} + +int +yasm_error_matches(yasm_error_class eclass) +{ + if (yasm_eclass == YASM_ERROR_NONE) + return eclass == YASM_ERROR_NONE; + if (yasm_eclass == YASM_ERROR_GENERAL) + return eclass == YASM_ERROR_GENERAL; + return (yasm_eclass & eclass) == eclass; +} + +void +yasm_error_set_va(yasm_error_class eclass, const char *format, va_list va) +{ + if (yasm_eclass != YASM_ERROR_NONE) + return; + + yasm_eclass = eclass; + yasm_estr = yasm_xmalloc(MSG_MAXSIZE+1); +#ifdef HAVE_VSNPRINTF + vsnprintf(yasm_estr, MSG_MAXSIZE, yasm_gettext_hook(format), va); +#else + vsprintf(yasm_estr, yasm_gettext_hook(format), va); +#endif +} + +void +yasm_error_set(yasm_error_class eclass, const char *format, ...) +{ + va_list va; + va_start(va, format); + yasm_error_set_va(eclass, format, va); + va_end(va); +} + +void +yasm_error_set_xref_va(unsigned long xrefline, const char *format, va_list va) +{ + if (yasm_eclass != YASM_ERROR_NONE) + return; + + yasm_exrefline = xrefline; + + yasm_exrefstr = yasm_xmalloc(MSG_MAXSIZE+1); +#ifdef HAVE_VSNPRINTF + vsnprintf(yasm_exrefstr, MSG_MAXSIZE, yasm_gettext_hook(format), va); +#else + vsprintf(yasm_exrefstr, yasm_gettext_hook(format), va); +#endif +} + +void +yasm_error_set_xref(unsigned long xrefline, const char *format, ...) +{ + va_list va; + va_start(va, format); + yasm_error_set_xref_va(xrefline, format, va); + va_end(va); +} + +void +yasm_error_fetch(yasm_error_class *eclass, char **str, unsigned long *xrefline, + char **xrefstr) +{ + *eclass = yasm_eclass; + *str = yasm_estr; + *xrefline = yasm_exrefline; + *xrefstr = yasm_exrefstr; + yasm_eclass = YASM_ERROR_NONE; + yasm_estr = NULL; + yasm_exrefline = 0; + yasm_exrefstr = NULL; +} + +void yasm_warn_clear(void) +{ + /* Delete all error/warnings */ + while (!STAILQ_EMPTY(&yasm_warns)) { + warn *w = STAILQ_FIRST(&yasm_warns); + + if (w->wstr) + yasm_xfree(w->wstr); + + STAILQ_REMOVE_HEAD(&yasm_warns, link); + yasm_xfree(w); + } +} + +yasm_warn_class +yasm_warn_occurred(void) +{ + if (STAILQ_EMPTY(&yasm_warns)) + return YASM_WARN_NONE; + return STAILQ_FIRST(&yasm_warns)->wclass; +} + +void +yasm_warn_set_va(yasm_warn_class wclass, const char *format, va_list va) +{ + warn *w; + + if (!(warn_class_enabled & (1UL<<wclass))) + return; /* warning is part of disabled class */ + + w = yasm_xmalloc(sizeof(warn)); + w->wclass = wclass; + w->wstr = yasm_xmalloc(MSG_MAXSIZE+1); +#ifdef HAVE_VSNPRINTF + vsnprintf(w->wstr, MSG_MAXSIZE, yasm_gettext_hook(format), va); +#else + vsprintf(w->wstr, yasm_gettext_hook(format), va); +#endif + STAILQ_INSERT_TAIL(&yasm_warns, w, link); +} + +void +yasm_warn_set(yasm_warn_class wclass, const char *format, ...) +{ + va_list va; + va_start(va, format); + yasm_warn_set_va(wclass, format, va); + va_end(va); +} + +void +yasm_warn_fetch(yasm_warn_class *wclass, char **str) +{ + warn *w = STAILQ_FIRST(&yasm_warns); + + if (!w) { + *wclass = YASM_WARN_NONE; + *str = NULL; + return; + } + + *wclass = w->wclass; + *str = w->wstr; + + STAILQ_REMOVE_HEAD(&yasm_warns, link); + yasm_xfree(w); +} + +void +yasm_warn_enable(yasm_warn_class num) +{ + warn_class_enabled |= (1UL<<num); +} + +void +yasm_warn_disable(yasm_warn_class num) +{ + warn_class_enabled &= ~(1UL<<num); +} + +void +yasm_warn_disable_all(void) +{ + warn_class_enabled = 0; +} + +yasm_errwarns * +yasm_errwarns_create(void) +{ + yasm_errwarns *errwarns = yasm_xmalloc(sizeof(yasm_errwarns)); + SLIST_INIT(&errwarns->errwarns); + errwarns->ecount = 0; + errwarns->wcount = 0; + errwarns->previous_we = NULL; + return errwarns; +} + +void +yasm_errwarns_destroy(yasm_errwarns *errwarns) +{ + errwarn_data *we; + + /* Delete all error/warnings */ + while (!SLIST_EMPTY(&errwarns->errwarns)) { + we = SLIST_FIRST(&errwarns->errwarns); + if (we->msg) + yasm_xfree(we->msg); + if (we->xrefmsg) + yasm_xfree(we->xrefmsg); + + SLIST_REMOVE_HEAD(&errwarns->errwarns, link); + yasm_xfree(we); + } + + yasm_xfree(errwarns); +} + +void +yasm_errwarn_propagate(yasm_errwarns *errwarns, unsigned long line) +{ + if (yasm_eclass != YASM_ERROR_NONE) { + errwarn_data *we = errwarn_data_new(errwarns, line, 1); + yasm_error_class eclass; + + yasm_error_fetch(&eclass, &we->msg, &we->xrefline, &we->xrefmsg); + if (eclass != YASM_ERROR_GENERAL + && (eclass & YASM_ERROR_PARSE) == YASM_ERROR_PARSE) + we->type = WE_PARSERERROR; + else + we->type = WE_ERROR; + errwarns->ecount++; + } + + while (!STAILQ_EMPTY(&yasm_warns)) { + errwarn_data *we = errwarn_data_new(errwarns, line, 0); + yasm_warn_class wclass; + + yasm_warn_fetch(&wclass, &we->msg); + we->type = WE_WARNING; + errwarns->wcount++; + } +} + +unsigned int +yasm_errwarns_num_errors(yasm_errwarns *errwarns, int warning_as_error) +{ + if (warning_as_error) + return errwarns->ecount+errwarns->wcount; + else + return errwarns->ecount; +} + +void +yasm_errwarns_output_all(yasm_errwarns *errwarns, yasm_linemap *lm, + int warning_as_error, + yasm_print_error_func print_error, + yasm_print_warning_func print_warning) +{ + errwarn_data *we; + const char *filename, *xref_filename; + unsigned long line, xref_line; + + /* If we're treating warnings as errors, tell the user about it. */ + if (warning_as_error && warning_as_error != 2) { + print_error("", 0, + yasm_gettext_hook(N_("warnings being treated as errors")), + NULL, 0, NULL); + warning_as_error = 2; + } + + /* Output error/warnings. */ + SLIST_FOREACH(we, &errwarns->errwarns, link) { + /* Output error/warning */ + yasm_linemap_lookup(lm, we->line, &filename, &line); + if (we->xrefline) + yasm_linemap_lookup(lm, we->xrefline, &xref_filename, &xref_line); + else { + xref_filename = NULL; + xref_line = 0; + } + if (we->type == WE_ERROR || we->type == WE_PARSERERROR) + print_error(filename, line, we->msg, xref_filename, xref_line, + we->xrefmsg); + else + print_warning(filename, line, we->msg); + } +} + +void +yasm__fatal(const char *message, ...) +{ + va_list va; + va_start(va, message); + yasm_fatal(message, va); + /*@notreached@*/ + va_end(va); +} diff --git a/libyasm/errwarn.h b/libyasm/errwarn.h new file mode 100644 index 0000000..ede2f28 --- /dev/null +++ b/libyasm/errwarn.h @@ -0,0 +1,348 @@ +/** + * \file libyasm/errwarn.h + * \brief YASM error and warning reporting interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_ERRWARN_H +#define YASM_ERRWARN_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Warning classes (that may be enabled/disabled). */ +typedef enum yasm_warn_class { + YASM_WARN_NONE = 0, /**< No warning */ + YASM_WARN_GENERAL, /**< Non-specific warnings */ + YASM_WARN_UNREC_CHAR, /**< Unrecognized characters (while tokenizing) */ + YASM_WARN_PREPROC, /**< Preprocessor warnings */ + YASM_WARN_ORPHAN_LABEL, /**< Label alone on a line without a colon */ + YASM_WARN_UNINIT_CONTENTS, /**< Uninitialized space in code/data section */ + YASM_WARN_SIZE_OVERRIDE,/**< Double size override */ + YASM_WARN_IMPLICIT_SIZE_OVERRIDE /**< Implicit size override */ +} yasm_warn_class; + +/** Error classes. Bitmask-based to support limited subclassing. */ +typedef enum yasm_error_class { + YASM_ERROR_NONE = 0x0000, /**< No error */ + YASM_ERROR_GENERAL = 0xFFFF, /**< Non-specific */ + YASM_ERROR_ARITHMETIC = 0x0001, /**< Arithmetic error (general) */ + YASM_ERROR_OVERFLOW = 0x8001, /**< Arithmetic overflow */ + YASM_ERROR_FLOATING_POINT = 0x4001, /**< Floating point error */ + YASM_ERROR_ZERO_DIVISION = 0x2001, /**< Divide-by-zero */ + YASM_ERROR_ASSERTION = 0x0002, /**< Assertion error */ + YASM_ERROR_VALUE = 0x0004, /**< Value inappropriate + * (e.g. not in range) */ + YASM_ERROR_NOT_ABSOLUTE = 0x8004, /**< Absolute expression required */ + YASM_ERROR_TOO_COMPLEX = 0x4004, /**< Expression too complex */ + YASM_ERROR_NOT_CONSTANT = 0x2004, /**< Constant expression required */ + YASM_ERROR_IO = 0x0008, /**< I/O error */ + YASM_ERROR_NOT_IMPLEMENTED = 0x0010, /**< Not implemented error */ + YASM_ERROR_TYPE = 0x0020, /**< Type error */ + YASM_ERROR_SYNTAX = 0x0040, /**< Syntax error */ + YASM_ERROR_PARSE = 0x8040 /**< Parser error */ +} yasm_error_class; + +/** Initialize any internal data structures. */ +YASM_LIB_DECL +void yasm_errwarn_initialize(void); + +/** Clean up any memory allocated by yasm_errwarn_initialize() or other + * functions. + */ +YASM_LIB_DECL +void yasm_errwarn_cleanup(void); + +/** Reporting point of internal errors. These are usually due to sanity + * check failures in the code. + * \warning This function must NOT return to calling code; exit or longjmp + * instead. + * \param file source file (ala __FILE__) + * \param line source line (ala __LINE__) + * \param message internal error message + */ +YASM_LIB_DECL +extern /*@exits@*/ void (*yasm_internal_error_) + (const char *file, unsigned int line, const char *message); + +/** Easily-callable version of yasm_internal_error_(). Automatically uses + * __FILE__ and __LINE__ as the file and line. + * \param message internal error message + */ +#define yasm_internal_error(message) \ + yasm_internal_error_(__FILE__, __LINE__, message) + +/** Reporting point of fatal errors. + * \warning This function must NOT return to calling code; exit or longjmp + * instead. + * \param message fatal error message + * \param va va_list argument list for message + */ +YASM_LIB_DECL +extern /*@exits@*/ void (*yasm_fatal) (const char *message, va_list va); + +/** Reporting point of fatal errors, with variable arguments (internal only). + * \warning This function calls #yasm_fatal, and thus does not return to the + * calling code. + * \param message fatal error message + * \param ... argument list for message + */ +YASM_LIB_DECL +/*@exits@*/ void yasm__fatal(const char *message, ...); + +/** Unconditionally clear the error indicator, freeing any associated data. + * Has no effect if the error indicator is not set. + */ +YASM_LIB_DECL +void yasm_error_clear(void); + +/** Get the error indicator. YASM_ERROR_NONE is returned if no error has + * been set. Note that as YASM_ERROR_NONE is 0, the return value can also + * be treated as a boolean value. + * \return Current error indicator. + */ +yasm_error_class yasm_error_occurred(void); + +/** Check the error indicator against an error class. To check if any error + * has been set, check against the YASM_ERROR_GENERAL class. This function + * properly checks error subclasses. + * \param eclass base error class to check against + * \return Nonzero if error indicator is set and a subclass of eclass, 0 + * otherwise. + */ +YASM_LIB_DECL +int yasm_error_matches(yasm_error_class eclass); + +#ifndef YASM_DOXYGEN +YASM_LIB_DECL +extern yasm_error_class yasm_eclass; +#define yasm_error_occurred() yasm_eclass +#endif + +/** Set the error indicator (va_list version). Has no effect if the error + * indicator is already set. + * \param eclass error class + * \param format printf format string + * \param va argument list for format + */ +YASM_LIB_DECL +void yasm_error_set_va(yasm_error_class eclass, const char *format, va_list va); + +/** Set the error indicator. Has no effect if the error indicator is already + * set. + * \param eclass error class + * \param format printf format string + * \param ... argument list for format + */ +YASM_LIB_DECL +void yasm_error_set(yasm_error_class eclass, const char *format, ...) + /*@printflike@*/; + +/** Set a cross-reference for a new error (va_list version). Has no effect + * if the error indicator is already set (e.g. with yasm_error_set()). This + * function must be called prior to its corresponding yasm_error_set() call. + * \param xrefline virtual line to cross-reference to (should not be 0) + * \param format printf format string + * \param va argument list for format + */ +YASM_LIB_DECL +void yasm_error_set_xref_va(unsigned long xrefline, const char *format, + va_list va); + +/** Set a cross-reference for a new error. Has no effect if the error + * indicator is already set (e.g. with yasm_error_set()). This function + * must be called prior to its corresponding yasm_error_set() call. + * \param xrefline virtual line to cross-reference to (should not be 0) + * \param format printf format string + * \param ... argument list for format + */ +YASM_LIB_DECL +void yasm_error_set_xref(unsigned long xrefline, const char *format, ...) + /*@printflike@*/; + +/** Fetch the error indicator and all associated data. If the error + * indicator is set, the output pointers are set to the current error + * indicator values, and the error indicator is cleared. + * The code using this function is then responsible for yasm_xfree()'ing + * str and xrefstr (if non-NULL). If the error indicator is not set, + * all output values are set to 0 (including eclass, which is set to + * YASM_ERROR_NONE). + * \param eclass error class (output) + * \param str error message + * \param xrefline virtual line used for cross-referencing (0 if no xref) + * \param xrefstr cross-reference error message (NULL if no xref) + */ +YASM_LIB_DECL +void yasm_error_fetch(/*@out@*/ yasm_error_class *eclass, + /*@out@*/ /*@only@*/ /*@null@*/ char **str, + /*@out@*/ unsigned long *xrefline, + /*@out@*/ /*@only@*/ /*@null@*/ char **xrefstr); + +/** Unconditionally clear all warning indicators, freeing any associated data. + * Has no effect if no warning indicators have been set. + */ +YASM_LIB_DECL +void yasm_warn_clear(void); + +/** Get the first warning indicator. YASM_WARN_NONE is returned if no warning + * has been set. Note that as YASM_WARN_NONE is 0, the return value can also + * be treated as a boolean value. + * \return First warning indicator. + */ +YASM_LIB_DECL +yasm_warn_class yasm_warn_occurred(void); + +/** Add a warning indicator (va_list version). + * \param wclass warning class + * \param format printf format string + * \param va argument list for format + */ +YASM_LIB_DECL +void yasm_warn_set_va(yasm_warn_class wclass, const char *format, va_list va); + +/** Add a warning indicator. + * \param wclass warning class + * \param format printf format string + * \param ... argument list for format + */ +YASM_LIB_DECL +void yasm_warn_set(yasm_warn_class wclass, const char *format, ...) + /*@printflike@*/; + +/** Fetch the first warning indicator and all associated data. If there + * is at least one warning indicator, the output pointers are set to the + * first warning indicator values, and first warning indicator is removed. + * The code using this function is then responsible for yasm_xfree()'ing + * str and xrefstr (if non-NULL). If there is no warning indicator set, + * all output values are set to 0 (including wclass, which is set to + * YASM_WARN_NONE). + * \param wclass warning class (output) + * \param str warning message + */ +YASM_LIB_DECL +void yasm_warn_fetch(/*@out@*/ yasm_warn_class *wclass, + /*@out@*/ /*@only@*/ char **str); + +/** Enable a class of warnings. + * \param wclass warning class + */ +YASM_LIB_DECL +void yasm_warn_enable(yasm_warn_class wclass); + +/** Disable a class of warnings. + * \param wclass warning class + */ +YASM_LIB_DECL +void yasm_warn_disable(yasm_warn_class wclass); + +/** Disable all classes of warnings. */ +YASM_LIB_DECL +void yasm_warn_disable_all(void); + +/** Create an error/warning set for collection of multiple error/warnings. + * \return Newly allocated set. + */ +YASM_LIB_DECL +/*@only@*/ yasm_errwarns *yasm_errwarns_create(void); + +/** Destroy an error/warning set. + * \param errwarns error/warning set + */ +YASM_LIB_DECL +void yasm_errwarns_destroy(/*@only@*/ yasm_errwarns *errwarns); + +/** Propagate error indicator and warning indicator(s) to an error/warning set. + * Has no effect if the error indicator and warning indicator are not set. + * Does not print immediately; yasm_errwarn_output_all() outputs + * accumulated errors and warnings. + * Generally multiple errors on the same line will be reported, but errors + * of class YASM_ERROR_PARSE will get overwritten by any other class on the + * same line. + * \param errwarns error/warning set + * \param line virtual line + */ +YASM_LIB_DECL +void yasm_errwarn_propagate(yasm_errwarns *errwarns, unsigned long line); + +/** Get total number of errors logged. + * \param errwarns error/warning set + * \param warning_as_error if nonzero, warnings are treated as errors. + * \return Number of errors. + */ +YASM_LIB_DECL +unsigned int yasm_errwarns_num_errors(yasm_errwarns *errwarns, + int warning_as_error); + +/** Print out an error. + * \param fn filename of source file + * \param line line number + * \param msg error message + * \param xref_fn cross-referenced source filename + * \param xref_line cross-referenced line number + * \param xref_msg cross-referenced error message + */ +typedef void (*yasm_print_error_func) + (const char *fn, unsigned long line, const char *msg, + /*@null@*/ const char *xref_fn, unsigned long xref_line, + /*@null@*/ const char *xref_msg); + +/** Print out a warning. + * \param fn filename of source file + * \param line line number + * \param msg warning message + */ +typedef void (*yasm_print_warning_func) + (const char *fn, unsigned long line, const char *msg); + +/** Outputs error/warning set in sorted order (sorted by virtual line number). + * \param errwarns error/warning set + * \param lm line map (to convert virtual lines into filename/line pairs) + * \param warning_as_error if nonzero, treat warnings as errors. + * \param print_error function called to print out errors + * \param print_warning function called to print out warnings + */ +YASM_LIB_DECL +void yasm_errwarns_output_all + (yasm_errwarns *errwarns, yasm_linemap *lm, int warning_as_error, + yasm_print_error_func print_error, yasm_print_warning_func print_warning); + +/** Convert a possibly unprintable character into a printable string. + * \internal + * \param ch possibly unprintable character + * \return Printable string representation (static buffer). + */ +YASM_LIB_DECL +char *yasm__conv_unprint(int ch); + +/** Hook for library users to map to gettext() if GNU gettext is being used. + * \param msgid message catalog identifier + * \return Translated message. + */ +YASM_LIB_DECL +extern const char * (*yasm_gettext_hook) (const char *msgid); + +#endif diff --git a/libyasm/expr.c b/libyasm/expr.c new file mode 100644 index 0000000..c2c868e --- /dev/null +++ b/libyasm/expr.c @@ -0,0 +1,1516 @@ +/* + * Expression handling + * + * Copyright (C) 2001-2007 Michael Urman, Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "bitvect.h" + +#include "errwarn.h" +#include "intnum.h" +#include "floatnum.h" +#include "expr.h" +#include "symrec.h" + +#include "bytecode.h" +#include "section.h" + +#include "arch.h" + + +static /*@only@*/ yasm_expr *expr_level_op + (/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const, + int simplify_ident, int simplify_reg_mul); +static int expr_traverse_nodes_post(/*@null@*/ yasm_expr *e, + /*@null@*/ void *d, + int (*func) (/*@null@*/ yasm_expr *e, + /*@null@*/ void *d)); +static void expr_delete_term(yasm_expr__item *term, int recurse); + +/* Bitmap of used items. We should really never need more than 2 at a time, + * so 31 is pretty much overkill. + */ +static unsigned long itempool_used = 0; +static yasm_expr__item itempool[31]; + +/* allocate a new expression node, with children as defined. + * If it's a unary operator, put the element in left and set right=NULL. */ +/*@-compmempass@*/ +yasm_expr * +yasm_expr_create(yasm_expr_op op, yasm_expr__item *left, + yasm_expr__item *right, unsigned long line) +{ + yasm_expr *ptr, *sube; + unsigned long z; + ptr = yasm_xmalloc(sizeof(yasm_expr)); + + ptr->op = op; + ptr->numterms = 0; + ptr->terms[0].type = YASM_EXPR_NONE; + ptr->terms[1].type = YASM_EXPR_NONE; + if (left) { + ptr->terms[0] = *left; /* structure copy */ + z = (unsigned long)(left-itempool); + if (z>=31) + yasm_internal_error(N_("could not find expritem in pool")); + itempool_used &= ~(1<<z); + ptr->numterms++; + + /* Search downward until we find something *other* than an + * IDENT, then bring it up to the current level. + */ + while (ptr->terms[0].type == YASM_EXPR_EXPR && + ptr->terms[0].data.expn->op == YASM_EXPR_IDENT) { + sube = ptr->terms[0].data.expn; + ptr->terms[0] = sube->terms[0]; /* structure copy */ + /*@-usereleased@*/ + yasm_xfree(sube); + /*@=usereleased@*/ + } + } else { + yasm_internal_error(N_("Right side of expression must exist")); + } + + if (right) { + ptr->terms[1] = *right; /* structure copy */ + z = (unsigned long)(right-itempool); + if (z>=31) + yasm_internal_error(N_("could not find expritem in pool")); + itempool_used &= ~(1<<z); + ptr->numterms++; + + /* Search downward until we find something *other* than an + * IDENT, then bring it up to the current level. + */ + while (ptr->terms[1].type == YASM_EXPR_EXPR && + ptr->terms[1].data.expn->op == YASM_EXPR_IDENT) { + sube = ptr->terms[1].data.expn; + ptr->terms[1] = sube->terms[0]; /* structure copy */ + /*@-usereleased@*/ + yasm_xfree(sube); + /*@=usereleased@*/ + } + } + + ptr->line = line; + + return expr_level_op(ptr, 1, 1, 0); +} +/*@=compmempass@*/ + +/* helpers */ +static yasm_expr__item * +expr_get_item(void) +{ + int z = 0; + unsigned long v = itempool_used & 0x7fffffff; + + while (v & 1) { + v >>= 1; + z++; + } + if (z>=31) + yasm_internal_error(N_("too many expritems")); + itempool_used |= 1<<z; + return &itempool[z]; +} + +yasm_expr__item * +yasm_expr_precbc(yasm_bytecode *precbc) +{ + yasm_expr__item *e = expr_get_item(); + e->type = YASM_EXPR_PRECBC; + e->data.precbc = precbc; + return e; +} + +yasm_expr__item * +yasm_expr_sym(yasm_symrec *s) +{ + yasm_expr__item *e = expr_get_item(); + e->type = YASM_EXPR_SYM; + e->data.sym = s; + return e; +} + +yasm_expr__item * +yasm_expr_expr(yasm_expr *x) +{ + yasm_expr__item *e = expr_get_item(); + e->type = YASM_EXPR_EXPR; + e->data.expn = x; + return e; +} + +yasm_expr__item * +yasm_expr_int(yasm_intnum *i) +{ + yasm_expr__item *e = expr_get_item(); + e->type = YASM_EXPR_INT; + e->data.intn = i; + return e; +} + +yasm_expr__item * +yasm_expr_float(yasm_floatnum *f) +{ + yasm_expr__item *e = expr_get_item(); + e->type = YASM_EXPR_FLOAT; + e->data.flt = f; + return e; +} + +yasm_expr__item * +yasm_expr_reg(uintptr_t reg) +{ + yasm_expr__item *e = expr_get_item(); + e->type = YASM_EXPR_REG; + e->data.reg = reg; + return e; +} + +/* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into single + * expritems if possible. Uses a simple n^2 algorithm because n is usually + * quite small. Also works for precbc-precbc (or symrec-precbc, + * precbc-symrec). + */ +static /*@only@*/ yasm_expr * +expr_xform_bc_dist_base(/*@returned@*/ /*@only@*/ yasm_expr *e, + /*@null@*/ void *cbd, + int (*callback) (yasm_expr__item *ei, + yasm_bytecode *precbc, + yasm_bytecode *precbc2, + void *cbd)) +{ + int i; + /*@dependent@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + int numterms; + + /* Handle symrec-symrec in ADD exprs by looking for (-1*symrec) and + * symrec term pairs (where both symrecs are in the same segment). + */ + if (e->op != YASM_EXPR_ADD) + return e; + + for (i=0; i<e->numterms; i++) { + int j; + yasm_expr *sube; + yasm_intnum *intn; + yasm_symrec *sym = NULL; + /*@dependent@*/ yasm_section *sect2; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc2; + + /* First look for an (-1*symrec) term */ + if (e->terms[i].type != YASM_EXPR_EXPR) + continue; + sube = e->terms[i].data.expn; + if (sube->op != YASM_EXPR_MUL || sube->numterms != 2) + continue; + + if (sube->terms[0].type == YASM_EXPR_INT && + (sube->terms[1].type == YASM_EXPR_SYM || + sube->terms[1].type == YASM_EXPR_PRECBC)) { + intn = sube->terms[0].data.intn; + if (sube->terms[1].type == YASM_EXPR_PRECBC) + precbc = sube->terms[1].data.precbc; + else + sym = sube->terms[1].data.sym; + } else if ((sube->terms[0].type == YASM_EXPR_SYM || + sube->terms[0].type == YASM_EXPR_PRECBC) && + sube->terms[1].type == YASM_EXPR_INT) { + if (sube->terms[0].type == YASM_EXPR_PRECBC) + precbc = sube->terms[0].data.precbc; + else + sym = sube->terms[0].data.sym; + intn = sube->terms[1].data.intn; + } else + continue; + + if (!yasm_intnum_is_neg1(intn)) + continue; + + if (sym && !yasm_symrec_get_label(sym, &precbc)) + continue; + sect2 = yasm_bc_get_section(precbc); + + /* Now look for a symrec term in the same segment */ + for (j=0; j<e->numterms; j++) { + if (((e->terms[j].type == YASM_EXPR_SYM && + yasm_symrec_get_label(e->terms[j].data.sym, &precbc2)) || + (e->terms[j].type == YASM_EXPR_PRECBC && + (precbc2 = e->terms[j].data.precbc))) && + (sect = yasm_bc_get_section(precbc2)) && + sect == sect2 && + callback(&e->terms[j], precbc, precbc2, cbd)) { + /* Delete the matching (-1*symrec) term */ + yasm_expr_destroy(sube); + e->terms[i].type = YASM_EXPR_NONE; + break; /* stop looking for matching symrec term */ + } + } + } + + /* Clean up any deleted (EXPR_NONE) terms */ + numterms = 0; + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type != YASM_EXPR_NONE) + e->terms[numterms++] = e->terms[i]; /* structure copy */ + } + if (e->numterms != numterms) { + e->numterms = numterms; + e = yasm_xrealloc(e, sizeof(yasm_expr)+((numterms<2) ? 0 : + sizeof(yasm_expr__item)*(numterms-2))); + if (numterms == 1) + e->op = YASM_EXPR_IDENT; + } + + return e; +} + +static int +expr_xform_bc_dist_cb(yasm_expr__item *ei, yasm_bytecode *precbc, + yasm_bytecode *precbc2, /*@null@*/ void *d) +{ + yasm_intnum *dist = yasm_calc_bc_dist(precbc, precbc2); + if (!dist) + return 0; + /* Change the term to an integer */ + ei->type = YASM_EXPR_INT; + ei->data.intn = dist; + return 1; +} + +/* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into integers if + * possible. + */ +static /*@only@*/ yasm_expr * +expr_xform_bc_dist(/*@returned@*/ /*@only@*/ yasm_expr *e) +{ + return expr_xform_bc_dist_base(e, NULL, expr_xform_bc_dist_cb); +} + +typedef struct bc_dist_subst_cbd { + void (*callback) (unsigned int subst, yasm_bytecode *precbc, + yasm_bytecode *precbc2, void *cbd); + void *cbd; + unsigned int subst; +} bc_dist_subst_cbd; + +static int +expr_bc_dist_subst_cb(yasm_expr__item *ei, yasm_bytecode *precbc, + yasm_bytecode *precbc2, /*@null@*/ void *d) +{ + bc_dist_subst_cbd *my_cbd = d; + assert(my_cbd != NULL); + /* Call higher-level callback */ + my_cbd->callback(my_cbd->subst, precbc, precbc2, my_cbd->cbd); + /* Change the term to an subst */ + ei->type = YASM_EXPR_SUBST; + ei->data.subst = my_cbd->subst; + my_cbd->subst++; + return 1; +} + +static yasm_expr * +expr_xform_bc_dist_subst(yasm_expr *e, void *d) +{ + return expr_xform_bc_dist_base(e, d, expr_bc_dist_subst_cb); +} + +int +yasm_expr__bc_dist_subst(yasm_expr **ep, void *cbd, + void (*callback) (unsigned int subst, + yasm_bytecode *precbc, + yasm_bytecode *precbc2, + void *cbd)) +{ + bc_dist_subst_cbd my_cbd; /* callback info for low-level callback */ + my_cbd.callback = callback; + my_cbd.cbd = cbd; + my_cbd.subst = 0; + *ep = yasm_expr__level_tree(*ep, 1, 1, 1, 0, &expr_xform_bc_dist_subst, + &my_cbd); + return my_cbd.subst; +} + +/* Negate just a single ExprItem by building a -1*ei subexpression */ +static void +expr_xform_neg_item(yasm_expr *e, yasm_expr__item *ei) +{ + yasm_expr *sube = yasm_xmalloc(sizeof(yasm_expr)); + + /* Build -1*ei subexpression */ + sube->op = YASM_EXPR_MUL; + sube->line = e->line; + sube->numterms = 2; + sube->terms[0].type = YASM_EXPR_INT; + sube->terms[0].data.intn = yasm_intnum_create_int(-1); + sube->terms[1] = *ei; /* structure copy */ + + /* Replace original ExprItem with subexp */ + ei->type = YASM_EXPR_EXPR; + ei->data.expn = sube; +} + +/* Negates e by multiplying by -1, with distribution over lower-precedence + * operators (eg ADD) and special handling to simplify result w/ADD, NEG, and + * others. + * + * Returns a possibly reallocated e. + */ +static /*@only@*/ yasm_expr * +expr_xform_neg_helper(/*@returned@*/ /*@only@*/ yasm_expr *e) +{ + yasm_expr *ne; + int i; + + switch (e->op) { + case YASM_EXPR_ADD: + /* distribute (recursively if expr) over terms */ + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_EXPR) + e->terms[i].data.expn = + expr_xform_neg_helper(e->terms[i].data.expn); + else + expr_xform_neg_item(e, &e->terms[i]); + } + break; + case YASM_EXPR_SUB: + /* change op to ADD, and recursively negate left side (if expr) */ + e->op = YASM_EXPR_ADD; + if (e->terms[0].type == YASM_EXPR_EXPR) + e->terms[0].data.expn = + expr_xform_neg_helper(e->terms[0].data.expn); + else + expr_xform_neg_item(e, &e->terms[0]); + break; + case YASM_EXPR_NEG: + /* Negating a negated value? Make it an IDENT. */ + e->op = YASM_EXPR_IDENT; + break; + case YASM_EXPR_IDENT: + /* Negating an ident? Change it into a MUL w/ -1 if there's no + * floatnums present below; if there ARE floatnums, recurse. + */ + if (e->terms[0].type == YASM_EXPR_FLOAT) + yasm_floatnum_calc(e->terms[0].data.flt, YASM_EXPR_NEG, NULL); + else if (e->terms[0].type == YASM_EXPR_INT) + yasm_intnum_calc(e->terms[0].data.intn, YASM_EXPR_NEG, NULL); + else if (e->terms[0].type == YASM_EXPR_EXPR && + yasm_expr__contains(e->terms[0].data.expn, YASM_EXPR_FLOAT)) + expr_xform_neg_helper(e->terms[0].data.expn); + else { + e->op = YASM_EXPR_MUL; + e->numterms = 2; + e->terms[1].type = YASM_EXPR_INT; + e->terms[1].data.intn = yasm_intnum_create_int(-1); + } + break; + default: + /* Everything else. MUL will be combined when it's leveled. + * Make a new expr (to replace e) with -1*e. + */ + ne = yasm_xmalloc(sizeof(yasm_expr)); + ne->op = YASM_EXPR_MUL; + ne->line = e->line; + ne->numterms = 2; + ne->terms[0].type = YASM_EXPR_INT; + ne->terms[0].data.intn = yasm_intnum_create_int(-1); + ne->terms[1].type = YASM_EXPR_EXPR; + ne->terms[1].data.expn = e; + return ne; + } + return e; +} + +/* Transforms negatives into expressions that are easier to combine: + * -x -> -1*x + * a-b -> a+(-1*b) + * + * Call post-order on an expression tree to transform the entire tree. + * + * Returns a possibly reallocated e. + */ +static /*@only@*/ yasm_expr * +expr_xform_neg(/*@returned@*/ /*@only@*/ yasm_expr *e) +{ + switch (e->op) { + case YASM_EXPR_NEG: + /* Turn -x into -1*x */ + e->op = YASM_EXPR_IDENT; + return expr_xform_neg_helper(e); + case YASM_EXPR_SUB: + /* Turn a-b into a+(-1*b) */ + + /* change op to ADD, and recursively negate right side (if expr) */ + e->op = YASM_EXPR_ADD; + if (e->terms[1].type == YASM_EXPR_EXPR) + e->terms[1].data.expn = + expr_xform_neg_helper(e->terms[1].data.expn); + else + expr_xform_neg_item(e, &e->terms[1]); + break; + default: + break; + } + + return e; +} + +/* Look for simple identities that make the entire result constant: + * 0*&x, -1|x, etc. + */ +static int +expr_is_constant(yasm_expr_op op, yasm_intnum *intn) +{ + int iszero = yasm_intnum_is_zero(intn); + return ((iszero && op == YASM_EXPR_MUL) || + (iszero && op == YASM_EXPR_AND) || + (iszero && op == YASM_EXPR_LAND) || + (yasm_intnum_is_neg1(intn) && op == YASM_EXPR_OR)); +} + +/* Look for simple "left" identities like 0+x, 1*x, etc. */ +static int +expr_can_destroy_int_left(yasm_expr_op op, yasm_intnum *intn) +{ + int iszero = yasm_intnum_is_zero(intn); + return ((yasm_intnum_is_pos1(intn) && op == YASM_EXPR_MUL) || + (iszero && op == YASM_EXPR_ADD) || + (yasm_intnum_is_neg1(intn) && op == YASM_EXPR_AND) || + (!iszero && op == YASM_EXPR_LAND) || + (iszero && op == YASM_EXPR_OR) || + (iszero && op == YASM_EXPR_LOR)); +} + +/* Look for simple "right" identities like x+|-0, x*&/1 */ +static int +expr_can_destroy_int_right(yasm_expr_op op, yasm_intnum *intn) +{ + int iszero = yasm_intnum_is_zero(intn); + int ispos1 = yasm_intnum_is_pos1(intn); + return ((ispos1 && op == YASM_EXPR_MUL) || + (ispos1 && op == YASM_EXPR_DIV) || + (iszero && op == YASM_EXPR_ADD) || + (iszero && op == YASM_EXPR_SUB) || + (yasm_intnum_is_neg1(intn) && op == YASM_EXPR_AND) || + (!iszero && op == YASM_EXPR_LAND) || + (iszero && op == YASM_EXPR_OR) || + (iszero && op == YASM_EXPR_LOR) || + (iszero && op == YASM_EXPR_SHL) || + (iszero && op == YASM_EXPR_SHR)); +} + +/* Check for and simplify identities. Returns new number of expr terms. + * Sets e->op = EXPR_IDENT if numterms ends up being 1. + * Uses numterms parameter instead of e->numterms for basis of "new" number + * of terms. + * Assumes int_term is *only* integer term in e. + * NOTE: Really designed to only be used by expr_level_op(). + */ +static int +expr_simplify_identity(yasm_expr *e, int numterms, int *int_term, + int simplify_reg_mul) +{ + int i; + int save_numterms; + + /* Don't do this step if it's 1*REG. Save and restore numterms so + * yasm_expr__contains() works correctly. + */ + save_numterms = e->numterms; + e->numterms = numterms; + if (simplify_reg_mul || e->op != YASM_EXPR_MUL + || !yasm_intnum_is_pos1(e->terms[*int_term].data.intn) + || !yasm_expr__contains(e, YASM_EXPR_REG)) { + /* Check for simple identities that delete the intnum. + * Don't delete if the intnum is the only thing in the expn. + */ + if ((*int_term == 0 && numterms > 1 && + expr_can_destroy_int_left(e->op, e->terms[0].data.intn)) || + (*int_term > 0 && + expr_can_destroy_int_right(e->op, + e->terms[*int_term].data.intn))) { + /* Delete the intnum */ + yasm_intnum_destroy(e->terms[*int_term].data.intn); + + /* Slide everything to its right over by 1 */ + if (*int_term != numterms-1) /* if it wasn't last.. */ + memmove(&e->terms[*int_term], &e->terms[*int_term+1], + (numterms-1-*int_term)*sizeof(yasm_expr__item)); + + /* Update numterms */ + numterms--; + *int_term = -1; /* no longer an int term */ + } + } + e->numterms = save_numterms; + + /* Check for simple identites that delete everything BUT the intnum. + * Don't bother if the intnum is the only thing in the expn. + */ + if (numterms > 1 && *int_term != -1 && + expr_is_constant(e->op, e->terms[*int_term].data.intn)) { + /* Loop through, deleting everything but the integer term */ + for (i=0; i<e->numterms; i++) + if (i != *int_term) + expr_delete_term(&e->terms[i], 1); + + /* Move integer term to the first term (if not already there) */ + if (*int_term != 0) + e->terms[0] = e->terms[*int_term]; /* structure copy */ + + /* Set numterms to 1 */ + numterms = 1; + } + + /* Compute NOT, NEG, and LNOT on single intnum. */ + if (numterms == 1 && *int_term == 0 && + (e->op == YASM_EXPR_NOT || e->op == YASM_EXPR_NEG || + e->op == YASM_EXPR_LNOT)) + yasm_intnum_calc(e->terms[0].data.intn, e->op, NULL); + + /* Change expression to IDENT if possible. */ + if (numterms == 1) + e->op = YASM_EXPR_IDENT; + + /* Return the updated numterms */ + return numterms; +} + +/* Levels the expression tree starting at e. Eg: + * a+(b+c) -> a+b+c + * (a+b)+(c+d) -> a+b+c+d + * Naturally, only levels operators that allow more than two operand terms. + * NOTE: only does *one* level of leveling (no recursion). Should be called + * post-order on a tree to combine deeper levels. + * Also brings up any IDENT values into the current level (for ALL operators). + * Folds (combines by evaluation) *integer* constant values if fold_const != 0. + * + * Returns a possibly reallocated e. + */ +/*@-mustfree@*/ +static /*@only@*/ yasm_expr * +expr_level_op(/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const, + int simplify_ident, int simplify_reg_mul) +{ + int i, j, o, fold_numterms, level_numterms, level_fold_numterms; + int first_int_term = -1; + + /* Determine how many operands will need to be brought up (for leveling). + * Go ahead and bring up any IDENT'ed values. + */ + while (e->op == YASM_EXPR_IDENT && e->terms[0].type == YASM_EXPR_EXPR) { + yasm_expr *sube = e->terms[0].data.expn; + yasm_xfree(e); + e = sube; + } + + /* If non-numeric expression, don't fold constants. */ + if (e->op > YASM_EXPR_NONNUM) + fold_const = 0; + + level_numterms = e->numterms; + level_fold_numterms = 0; + for (i=0; i<e->numterms; i++) { + /* Search downward until we find something *other* than an + * IDENT, then bring it up to the current level. + */ + while (e->terms[i].type == YASM_EXPR_EXPR && + e->terms[i].data.expn->op == YASM_EXPR_IDENT) { + yasm_expr *sube = e->terms[i].data.expn; + e->terms[i] = sube->terms[0]; + yasm_xfree(sube); + } + + if (e->terms[i].type == YASM_EXPR_EXPR && + e->terms[i].data.expn->op == e->op) { + /* It's an expression w/the same operator, add in its numterms. + * But don't forget to subtract one for the expr itself! + */ + level_numterms += e->terms[i].data.expn->numterms - 1; + + /* If we're folding constants, count up the number of constants + * that will be merged in. + */ + if (fold_const) + for (j=0; j<e->terms[i].data.expn->numterms; j++) + if (e->terms[i].data.expn->terms[j].type == + YASM_EXPR_INT) + level_fold_numterms++; + } + + /* Find the first integer term (if one is present) if we're folding + * constants. + */ + if (fold_const && first_int_term == -1 && + e->terms[i].type == YASM_EXPR_INT) + first_int_term = i; + } + + /* Look for other integer terms if there's one and combine. + * Also eliminate empty spaces when combining and adjust numterms + * variables. + */ + fold_numterms = e->numterms; + if (first_int_term != -1) { + for (i=first_int_term+1, o=first_int_term+1; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_INT) { + yasm_intnum_calc(e->terms[first_int_term].data.intn, e->op, + e->terms[i].data.intn); + fold_numterms--; + level_numterms--; + /* make sure to delete folded intnum */ + yasm_intnum_destroy(e->terms[i].data.intn); + } else if (o != i) { + /* copy term if it changed places */ + e->terms[o++] = e->terms[i]; + } else + o++; + } + + if (simplify_ident) { + int new_fold_numterms; + /* Simplify identities and make IDENT if possible. */ + new_fold_numterms = + expr_simplify_identity(e, fold_numterms, &first_int_term, + simplify_reg_mul); + level_numterms -= fold_numterms-new_fold_numterms; + fold_numterms = new_fold_numterms; + } + if (fold_numterms == 1) + e->op = YASM_EXPR_IDENT; + } + + /* Only level operators that allow more than two operand terms. + * Also don't bother leveling if it's not necessary to bring up any terms. + */ + if ((e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_MUL && + e->op != YASM_EXPR_OR && e->op != YASM_EXPR_AND && + e->op != YASM_EXPR_LOR && e->op != YASM_EXPR_LAND && + e->op != YASM_EXPR_LXOR && e->op != YASM_EXPR_XOR) || + level_numterms <= fold_numterms) { + /* Downsize e if necessary */ + if (fold_numterms < e->numterms && e->numterms > 2) + e = yasm_xrealloc(e, sizeof(yasm_expr)+((fold_numterms<2) ? 0 : + sizeof(yasm_expr__item)*(fold_numterms-2))); + /* Update numterms */ + e->numterms = fold_numterms; + return e; + } + + /* Adjust numterms for constant folding from terms being "pulled up". + * Careful: if there's no integer term in e, then save space for it. + */ + if (fold_const) { + level_numterms -= level_fold_numterms; + if (first_int_term == -1 && level_fold_numterms != 0) + level_numterms++; + } + + /* Alloc more (or conceivably less, but not usually) space for e */ + e = yasm_xrealloc(e, sizeof(yasm_expr)+((level_numterms<2) ? 0 : + sizeof(yasm_expr__item)*(level_numterms-2))); + + /* Copy up ExprItem's. Iterate from right to left to keep the same + * ordering as was present originally. + * Combine integer terms as necessary. + */ + for (i=fold_numterms-1, o=level_numterms-1; i>=0; i--) { + if (e->terms[i].type == YASM_EXPR_EXPR && + e->terms[i].data.expn->op == e->op) { + /* bring up subexpression */ + yasm_expr *sube = e->terms[i].data.expn; + + /* copy terms right to left */ + for (j=sube->numterms-1; j>=0; j--) { + if (fold_const && sube->terms[j].type == YASM_EXPR_INT) { + /* Need to fold it in.. but if there's no int term already, + * just copy into a new one. + */ + if (first_int_term == -1) { + first_int_term = o--; + e->terms[first_int_term] = sube->terms[j]; /* struc */ + } else { + yasm_intnum_calc(e->terms[first_int_term].data.intn, + e->op, sube->terms[j].data.intn); + /* make sure to delete folded intnum */ + yasm_intnum_destroy(sube->terms[j].data.intn); + } + } else { + if (o == first_int_term) + o--; + e->terms[o--] = sube->terms[j]; /* structure copy */ + } + } + + /* delete subexpression, but *don't delete nodes* (as we've just + * copied them!) + */ + yasm_xfree(sube); + } else if (o != i) { + /* copy operand if it changed places */ + if (o == first_int_term) + o--; + e->terms[o] = e->terms[i]; + /* If we moved the first_int_term, change first_int_num too */ + if (i == first_int_term) + first_int_term = o; + o--; + } else + o--; + } + + /* Simplify identities, make IDENT if possible, and save to e->numterms. */ + if (simplify_ident && first_int_term != -1) { + e->numterms = expr_simplify_identity(e, level_numterms, + &first_int_term, simplify_reg_mul); + } else { + e->numterms = level_numterms; + if (level_numterms == 1) + e->op = YASM_EXPR_IDENT; + } + + return e; +} +/*@=mustfree@*/ + +typedef SLIST_HEAD(yasm__exprhead, yasm__exprentry) yasm__exprhead; +typedef struct yasm__exprentry { + /*@reldef@*/ SLIST_ENTRY(yasm__exprentry) next; + /*@null@*/ const yasm_expr *e; +} yasm__exprentry; + +static yasm_expr * +expr_expand_equ(yasm_expr *e, yasm__exprhead *eh) +{ + int i; + yasm__exprentry ee; + + /* traverse terms */ + for (i=0; i<e->numterms; i++) { + const yasm_expr *equ_expr; + + /* Expand equ's. */ + if (e->terms[i].type == YASM_EXPR_SYM && + (equ_expr = yasm_symrec_get_equ(e->terms[i].data.sym))) { + yasm__exprentry *np; + + /* Check for circular reference */ + SLIST_FOREACH(np, eh, next) { + if (np->e == equ_expr) { + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("circular reference detected")); + return e; + } + } + + e->terms[i].type = YASM_EXPR_EXPR; + e->terms[i].data.expn = yasm_expr_copy(equ_expr); + + /* Remember we saw this equ and recurse */ + ee.e = equ_expr; + SLIST_INSERT_HEAD(eh, &ee, next); + e->terms[i].data.expn = expr_expand_equ(e->terms[i].data.expn, eh); + SLIST_REMOVE_HEAD(eh, next); + } else if (e->terms[i].type == YASM_EXPR_EXPR) + /* Recurse */ + e->terms[i].data.expn = expr_expand_equ(e->terms[i].data.expn, eh); + } + + return e; +} + +static yasm_expr * +expr_level_tree(yasm_expr *e, int fold_const, int simplify_ident, + int simplify_reg_mul, int calc_bc_dist, + yasm_expr_xform_func expr_xform_extra, + void *expr_xform_extra_data) +{ + int i; + + e = expr_xform_neg(e); + + /* traverse terms */ + for (i=0; i<e->numterms; i++) { + /* Recurse */ + if (e->terms[i].type == YASM_EXPR_EXPR) + e->terms[i].data.expn = + expr_level_tree(e->terms[i].data.expn, fold_const, + simplify_ident, simplify_reg_mul, calc_bc_dist, + expr_xform_extra, expr_xform_extra_data); + } + + /* Check for SEG of SEG:OFF, if we match, simplify to just the segment */ + if (e->op == YASM_EXPR_SEG && e->terms[0].type == YASM_EXPR_EXPR && + e->terms[0].data.expn->op == YASM_EXPR_SEGOFF) { + e->op = YASM_EXPR_IDENT; + e->terms[0].data.expn->op = YASM_EXPR_IDENT; + /* Destroy the second (offset) term */ + e->terms[0].data.expn->numterms = 1; + expr_delete_term(&e->terms[0].data.expn->terms[1], 1); + } + + /* do callback */ + e = expr_level_op(e, fold_const, simplify_ident, simplify_reg_mul); + if (calc_bc_dist || expr_xform_extra) { + if (calc_bc_dist) + e = expr_xform_bc_dist(e); + if (expr_xform_extra) + e = expr_xform_extra(e, expr_xform_extra_data); + e = expr_level_tree(e, fold_const, simplify_ident, simplify_reg_mul, + 0, NULL, NULL); + } + return e; +} + +/* Level an entire expn tree, expanding equ's as we go */ +yasm_expr * +yasm_expr__level_tree(yasm_expr *e, int fold_const, int simplify_ident, + int simplify_reg_mul, int calc_bc_dist, + yasm_expr_xform_func expr_xform_extra, + void *expr_xform_extra_data) +{ + yasm__exprhead eh; + SLIST_INIT(&eh); + + if (!e) + return 0; + + e = expr_expand_equ(e, &eh); + e = expr_level_tree(e, fold_const, simplify_ident, simplify_reg_mul, + calc_bc_dist, expr_xform_extra, expr_xform_extra_data); + + return e; +} + +/* Comparison function for expr_order_terms(). + * Assumes ExprType enum is in canonical order. + */ +static int +expr_order_terms_compare(const void *va, const void *vb) +{ + const yasm_expr__item *a = va, *b = vb; + return (a->type - b->type); +} + +/* Reorder terms of e into canonical order. Only reorders if reordering + * doesn't change meaning of expression. (eg, doesn't reorder SUB). + * Canonical order: REG, INT, FLOAT, SYM, EXPR. + * Multiple terms of a single type are kept in the same order as in + * the original expression. + * NOTE: Only performs reordering on *one* level (no recursion). + */ +void +yasm_expr__order_terms(yasm_expr *e) +{ + /* don't bother reordering if only one element */ + if (e->numterms == 1) + return; + + /* only reorder some types of operations */ + switch (e->op) { + case YASM_EXPR_ADD: + case YASM_EXPR_MUL: + case YASM_EXPR_OR: + case YASM_EXPR_AND: + case YASM_EXPR_XOR: + case YASM_EXPR_LOR: + case YASM_EXPR_LAND: + case YASM_EXPR_LXOR: + /* Use mergesort to sort. It's fast on already sorted values and a + * stable sort (multiple terms of same type are kept in the same + * order). + */ + yasm__mergesort(e->terms, (size_t)e->numterms, + sizeof(yasm_expr__item), expr_order_terms_compare); + break; + default: + break; + } +} + +static void +expr_item_copy(yasm_expr__item *dest, const yasm_expr__item *src) +{ + dest->type = src->type; + switch (src->type) { + case YASM_EXPR_SYM: + /* Symbols don't need to be copied */ + dest->data.sym = src->data.sym; + break; + case YASM_EXPR_PRECBC: + /* Nor do direct bytecode references */ + dest->data.precbc = src->data.precbc; + break; + case YASM_EXPR_EXPR: + dest->data.expn = yasm_expr__copy_except(src->data.expn, -1); + break; + case YASM_EXPR_INT: + dest->data.intn = yasm_intnum_copy(src->data.intn); + break; + case YASM_EXPR_FLOAT: + dest->data.flt = yasm_floatnum_copy(src->data.flt); + break; + case YASM_EXPR_REG: + dest->data.reg = src->data.reg; + break; + case YASM_EXPR_SUBST: + dest->data.subst = src->data.subst; + break; + default: + break; + } +} + +/* Copy entire expression EXCEPT for index "except" at *top level only*. */ +yasm_expr * +yasm_expr__copy_except(const yasm_expr *e, int except) +{ + yasm_expr *n; + int i; + + n = yasm_xmalloc(sizeof(yasm_expr) + + sizeof(yasm_expr__item)*(e->numterms<2?0:e->numterms-2)); + + n->op = e->op; + n->line = e->line; + n->numterms = e->numterms; + for (i=0; i<e->numterms; i++) { + if (i != except) + expr_item_copy(&n->terms[i], &e->terms[i]); + } + + return n; +} + +static void +expr_delete_term(yasm_expr__item *term, int recurse) +{ + switch (term->type) { + case YASM_EXPR_INT: + yasm_intnum_destroy(term->data.intn); + break; + case YASM_EXPR_FLOAT: + yasm_floatnum_destroy(term->data.flt); + break; + case YASM_EXPR_EXPR: + if (recurse) + yasm_expr_destroy(term->data.expn); + break; + default: + break; + } +} + +static int +expr_destroy_each(/*@only@*/ yasm_expr *e, /*@unused@*/ void *d) +{ + int i; + for (i=0; i<e->numterms; i++) + expr_delete_term(&e->terms[i], 0); + yasm_xfree(e); /* free ourselves */ + return 0; /* don't stop recursion */ +} + +/*@-mustfree@*/ +void +yasm_expr_destroy(yasm_expr *e) +{ + expr_traverse_nodes_post(e, NULL, expr_destroy_each); +} +/*@=mustfree@*/ + +int +yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op) +{ + return (e->op == op); +} + +static int +expr_contains_callback(const yasm_expr__item *ei, void *d) +{ + yasm_expr__type *t = d; + return (ei->type & *t); +} + +int +yasm_expr__contains(const yasm_expr *e, yasm_expr__type t) +{ + return yasm_expr__traverse_leaves_in_const(e, &t, expr_contains_callback); +} + +typedef struct subst_cbd { + unsigned int num_items; + const yasm_expr__item *items; +} subst_cbd; + +static int +expr_subst_callback(yasm_expr__item *ei, void *d) +{ + subst_cbd *cbd = d; + if (ei->type != YASM_EXPR_SUBST) + return 0; + if (ei->data.subst >= cbd->num_items) + return 1; /* error */ + expr_item_copy(ei, &cbd->items[ei->data.subst]); + return 0; +} + +int +yasm_expr__subst(yasm_expr *e, unsigned int num_items, + const yasm_expr__item *items) +{ + subst_cbd cbd; + cbd.num_items = num_items; + cbd.items = items; + return yasm_expr__traverse_leaves_in(e, &cbd, expr_subst_callback); +} + +/* Traverse over expression tree, calling func for each operation AFTER the + * branches (if expressions) have been traversed (eg, postorder + * traversal). The data pointer d is passed to each func call. + * + * Stops early (and returns 1) if func returns 1. Otherwise returns 0. + */ +static int +expr_traverse_nodes_post(yasm_expr *e, void *d, + int (*func) (/*@null@*/ yasm_expr *e, + /*@null@*/ void *d)) +{ + int i; + + if (!e) + return 0; + + /* traverse terms */ + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_EXPR && + expr_traverse_nodes_post(e->terms[i].data.expn, d, func)) + return 1; + } + + /* do callback */ + return func(e, d); +} + +/* Traverse over expression tree in order, calling func for each leaf + * (non-operation). The data pointer d is passed to each func call. + * + * Stops early (and returns 1) if func returns 1. Otherwise returns 0. + */ +int +yasm_expr__traverse_leaves_in_const(const yasm_expr *e, void *d, + int (*func) (/*@null@*/ const yasm_expr__item *ei, /*@null@*/ void *d)) +{ + int i; + + if (!e) + return 0; + + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_EXPR) { + if (yasm_expr__traverse_leaves_in_const(e->terms[i].data.expn, d, + func)) + return 1; + } else { + if (func(&e->terms[i], d)) + return 1; + } + } + return 0; +} + +/* Traverse over expression tree in order, calling func for each leaf + * (non-operation). The data pointer d is passed to each func call. + * + * Stops early (and returns 1) if func returns 1. Otherwise returns 0. + */ +int +yasm_expr__traverse_leaves_in(yasm_expr *e, void *d, + int (*func) (/*@null@*/ yasm_expr__item *ei, /*@null@*/ void *d)) +{ + int i; + + if (!e) + return 0; + + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_EXPR) { + if (yasm_expr__traverse_leaves_in(e->terms[i].data.expn, d, func)) + return 1; + } else { + if (func(&e->terms[i], d)) + return 1; + } + } + return 0; +} + +yasm_expr * +yasm_expr_extract_deep_segoff(yasm_expr **ep) +{ + yasm_expr *retval; + yasm_expr *e = *ep; + int i; + + /* Try to extract at this level */ + retval = yasm_expr_extract_segoff(ep); + if (retval) + return retval; + + /* Not at this level? Search any expr children. */ + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_EXPR) { + retval = yasm_expr_extract_deep_segoff(&e->terms[i].data.expn); + if (retval) + return retval; + } + } + + /* Didn't find one */ + return NULL; +} + +yasm_expr * +yasm_expr_extract_segoff(yasm_expr **ep) +{ + yasm_expr *retval; + yasm_expr *e = *ep; + + /* If not SEG:OFF, we can't do this transformation */ + if (e->op != YASM_EXPR_SEGOFF) + return NULL; + + /* Extract the SEG portion out to its own expression */ + if (e->terms[0].type == YASM_EXPR_EXPR) + retval = e->terms[0].data.expn; + else { + /* Need to build IDENT expression to hold non-expression contents */ + retval = yasm_xmalloc(sizeof(yasm_expr)); + retval->op = YASM_EXPR_IDENT; + retval->numterms = 1; + retval->terms[0] = e->terms[0]; /* structure copy */ + } + + /* Delete the SEG: portion by changing the expression into an IDENT */ + e->op = YASM_EXPR_IDENT; + e->numterms = 1; + e->terms[0] = e->terms[1]; /* structure copy */ + + return retval; +} + +yasm_expr * +yasm_expr_extract_wrt(yasm_expr **ep) +{ + yasm_expr *retval; + yasm_expr *e = *ep; + + /* If not WRT, we can't do this transformation */ + if (e->op != YASM_EXPR_WRT) + return NULL; + + /* Extract the right side portion out to its own expression */ + if (e->terms[1].type == YASM_EXPR_EXPR) + retval = e->terms[1].data.expn; + else { + /* Need to build IDENT expression to hold non-expression contents */ + retval = yasm_xmalloc(sizeof(yasm_expr)); + retval->op = YASM_EXPR_IDENT; + retval->numterms = 1; + retval->terms[0] = e->terms[1]; /* structure copy */ + } + + /* Delete the right side portion by changing the expr into an IDENT */ + e->op = YASM_EXPR_IDENT; + e->numterms = 1; + + return retval; +} + +/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/ +yasm_intnum * +yasm_expr_get_intnum(yasm_expr **ep, int calc_bc_dist) +{ + *ep = yasm_expr_simplify(*ep, calc_bc_dist); + + if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_INT) + return (*ep)->terms[0].data.intn; + else + return (yasm_intnum *)NULL; +} +/*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/ + +/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/ +const yasm_symrec * +yasm_expr_get_symrec(yasm_expr **ep, int simplify) +{ + if (simplify) + *ep = yasm_expr_simplify(*ep, 0); + + if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_SYM) + return (*ep)->terms[0].data.sym; + else + return (yasm_symrec *)NULL; +} +/*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/ + +/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/ +const uintptr_t * +yasm_expr_get_reg(yasm_expr **ep, int simplify) +{ + if (simplify) + *ep = yasm_expr_simplify(*ep, 0); + + if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_REG) + return &((*ep)->terms[0].data.reg); + else + return NULL; +} +/*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/ + +void +yasm_expr_print(const yasm_expr *e, FILE *f) +{ + char opstr[8]; + int i; + + if (!e) { + fprintf(f, "(nil)"); + return; + } + + switch (e->op) { + case YASM_EXPR_ADD: + strcpy(opstr, "+"); + break; + case YASM_EXPR_SUB: + strcpy(opstr, "-"); + break; + case YASM_EXPR_MUL: + strcpy(opstr, "*"); + break; + case YASM_EXPR_DIV: + strcpy(opstr, "/"); + break; + case YASM_EXPR_SIGNDIV: + strcpy(opstr, "//"); + break; + case YASM_EXPR_MOD: + strcpy(opstr, "%"); + break; + case YASM_EXPR_SIGNMOD: + strcpy(opstr, "%%"); + break; + case YASM_EXPR_NEG: + fprintf(f, "-"); + opstr[0] = 0; + break; + case YASM_EXPR_NOT: + fprintf(f, "~"); + opstr[0] = 0; + break; + case YASM_EXPR_OR: + strcpy(opstr, "|"); + break; + case YASM_EXPR_AND: + strcpy(opstr, "&"); + break; + case YASM_EXPR_XOR: + strcpy(opstr, "^"); + break; + case YASM_EXPR_XNOR: + strcpy(opstr, "XNOR"); + break; + case YASM_EXPR_NOR: + strcpy(opstr, "NOR"); + break; + case YASM_EXPR_SHL: + strcpy(opstr, "<<"); + break; + case YASM_EXPR_SHR: + strcpy(opstr, ">>"); + break; + case YASM_EXPR_LOR: + strcpy(opstr, "||"); + break; + case YASM_EXPR_LAND: + strcpy(opstr, "&&"); + break; + case YASM_EXPR_LNOT: + strcpy(opstr, "!"); + break; + case YASM_EXPR_LXOR: + strcpy(opstr, "^^"); + break; + case YASM_EXPR_LXNOR: + strcpy(opstr, "LXNOR"); + break; + case YASM_EXPR_LNOR: + strcpy(opstr, "LNOR"); + break; + case YASM_EXPR_LT: + strcpy(opstr, "<"); + break; + case YASM_EXPR_GT: + strcpy(opstr, ">"); + break; + case YASM_EXPR_LE: + strcpy(opstr, "<="); + break; + case YASM_EXPR_GE: + strcpy(opstr, ">="); + break; + case YASM_EXPR_NE: + strcpy(opstr, "!="); + break; + case YASM_EXPR_EQ: + strcpy(opstr, "=="); + break; + case YASM_EXPR_SEG: + fprintf(f, "SEG "); + opstr[0] = 0; + break; + case YASM_EXPR_WRT: + strcpy(opstr, " WRT "); + break; + case YASM_EXPR_SEGOFF: + strcpy(opstr, ":"); + break; + case YASM_EXPR_IDENT: + opstr[0] = 0; + break; + default: + strcpy(opstr, " !UNK! "); + break; + } + for (i=0; i<e->numterms; i++) { + switch (e->terms[i].type) { + case YASM_EXPR_PRECBC: + fprintf(f, "{%lx}", + yasm_bc_next_offset(e->terms[i].data.precbc)); + break; + case YASM_EXPR_SYM: + fprintf(f, "%s", yasm_symrec_get_name(e->terms[i].data.sym)); + break; + case YASM_EXPR_EXPR: + fprintf(f, "("); + yasm_expr_print(e->terms[i].data.expn, f); + fprintf(f, ")"); + break; + case YASM_EXPR_INT: + yasm_intnum_print(e->terms[i].data.intn, f); + break; + case YASM_EXPR_FLOAT: + yasm_floatnum_print(e->terms[i].data.flt, f); + break; + case YASM_EXPR_REG: + /* FIXME */ + /*yasm_arch_reg_print(arch, e->terms[i].data.reg, f);*/ + break; + case YASM_EXPR_SUBST: + fprintf(f, "[%u]", e->terms[i].data.subst); + break; + case YASM_EXPR_NONE: + break; + } + if (i < e->numterms-1) + fprintf(f, "%s", opstr); + } +} + +unsigned int +yasm_expr_size(const yasm_expr *e) +{ + int i; + int seen = 0; + unsigned int size = 0, newsize; + + if (e->op == YASM_EXPR_IDENT) { + if (e->terms[0].type == YASM_EXPR_SYM) + return yasm_symrec_get_size(e->terms[0].data.sym); + return 0; + } + if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_SUB) + return 0; + + for (i=0; i<e->numterms; i++) { + newsize = 0; + switch (e->terms[i].type) { + case YASM_EXPR_EXPR: + newsize = yasm_expr_size(e->terms[i].data.expn); + break; + case YASM_EXPR_SYM: + newsize = yasm_symrec_get_size(e->terms[i].data.sym); + break; + default: + break; + } + if (newsize) { + size = newsize; + if (seen) + /* either sum of idents (?!) or substract of idents */ + return 0; + seen = 1; + } + } + /* exactly one offset */ + return size; +} + +const char * +yasm_expr_segment(const yasm_expr *e) +{ + int i; + int seen = 0; + const char *segment = NULL; + + if (e->op == YASM_EXPR_IDENT) { + if (e->terms[0].type == YASM_EXPR_SYM) + return yasm_symrec_get_segment(e->terms[0].data.sym); + return NULL; + } + if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_SUB) + return NULL; + + for (i=0; i<e->numterms; i++) { + if ((e->op == YASM_EXPR_ADD || !i) && + e->terms[i].type == YASM_EXPR_EXPR) { + if ((segment = yasm_expr_segment(e->terms[i].data.expn))) { + if (seen) { + /* either sum of idents (?!) or substract of idents */ + return NULL; + } + seen = 1; + } + } + } + /* exactly one offset */ + return segment; +} diff --git a/libyasm/expr.h b/libyasm/expr.h new file mode 100644 index 0000000..2b06cd3 --- /dev/null +++ b/libyasm/expr.h @@ -0,0 +1,386 @@ +/** + * \file libyasm/expr.h + * \brief YASM expression interface. + * + * \license + * Copyright (C) 2001-2007 Michael Urman, Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_EXPR_H +#define YASM_EXPR_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Type of an expression item. Types are listed in canonical sorting order. + * See expr_order_terms(). + * Note #YASM_EXPR_PRECBC must be used carefully (in a-b pairs), as only + * symrecs can become the relative term in a #yasm_value. + */ +typedef enum yasm_expr__type { + YASM_EXPR_NONE = 0, /**< Nothing */ + YASM_EXPR_REG = 1<<0, /**< Register */ + YASM_EXPR_INT = 1<<1, /**< Integer value */ + YASM_EXPR_SUBST = 1<<2, /**< Substitution placeholder */ + YASM_EXPR_FLOAT = 1<<3, /**< Floating point value */ + YASM_EXPR_SYM = 1<<4, /**< Symbol */ + YASM_EXPR_PRECBC = 1<<5,/**< Direct bytecode ref (rather than via sym) */ + YASM_EXPR_EXPR = 1<<6 /**< Subexpression */ +} yasm_expr__type; + +/** Expression item. */ +typedef struct yasm_expr__item { + yasm_expr__type type; /**< Type */ + + /** Expression item data. Correct value depends on type. */ + union { + yasm_bytecode *precbc; /**< Direct bytecode ref (YASM_EXPR_PRECBC) */ + yasm_symrec *sym; /**< Symbol (YASM_EXPR_SYM) */ + yasm_expr *expn; /**< Subexpression (YASM_EXPR_EXPR) */ + yasm_intnum *intn; /**< Integer value (YASM_EXPR_INT) */ + yasm_floatnum *flt; /**< Floating point value (YASM_EXPR_FLOAT) */ + uintptr_t reg; /**< Register (YASM_EXPR_REG) */ + unsigned int subst; /**< Subst placeholder (YASM_EXPR_SUBST) */ + } data; +} yasm_expr__item; + +/** Expression. */ +struct yasm_expr { + yasm_expr_op op; /**< Operation. */ + unsigned long line; /**< Line number where expression was defined. */ + int numterms; /**< Number of terms in the expression. */ + + /** Terms of the expression. Structure may be extended to include more + * terms, as some operations may allow more than two operand terms + * (ADD, MUL, OR, AND, XOR). + */ + yasm_expr__item terms[2]; +}; + +/** Create a new expression e=a op b. + * \param op operation + * \param a expression item a + * \param b expression item b (optional depending on op) + * \param line virtual line (where expression defined) + * \return Newly allocated expression. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr *yasm_expr_create + (yasm_expr_op op, /*@only@*/ yasm_expr__item *a, + /*@only@*/ /*@null@*/ yasm_expr__item *b, unsigned long line); + +/** Create a new preceding-bytecode expression item. + * \param precbc preceding bytecode + * \return Newly allocated expression item. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr__item *yasm_expr_precbc(/*@keep@*/ yasm_bytecode *precbc); + +/** Create a new symbol expression item. + * \param sym symbol + * \return Newly allocated expression item. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr__item *yasm_expr_sym(/*@keep@*/ yasm_symrec *sym); + +/** Create a new expression expression item. + * \param e expression + * \return Newly allocated expression item. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr__item *yasm_expr_expr(/*@keep@*/ yasm_expr *e); + +/** Create a new intnum expression item. + * \param intn intnum + * \return Newly allocated expression item. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr__item *yasm_expr_int(/*@keep@*/ yasm_intnum *intn); + +/** Create a new floatnum expression item. + * \param flt floatnum + * \return Newly allocated expression item. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr__item *yasm_expr_float(/*@keep@*/ yasm_floatnum *flt); + +/** Create a new register expression item. + * \param reg register + * \return Newly allocated expression item. + */ +YASM_LIB_DECL +/*@only@*/ yasm_expr__item *yasm_expr_reg(uintptr_t reg); + +/** Create a new expression tree e=l op r. + * \param l expression for left side of new expression + * \param o operation + * \param r expression for right side of new expression + * \param i line index + * \return Newly allocated expression. + */ +#define yasm_expr_create_tree(l,o,r,i) \ + yasm_expr_create ((o), yasm_expr_expr(l), yasm_expr_expr(r), i) + +/** Create a new expression branch e=op r. + * \param o operation + * \param r expression for right side of new expression + * \param i line index + * \return Newly allocated expression. + */ +#define yasm_expr_create_branch(o,r,i) \ + yasm_expr_create ((o), yasm_expr_expr(r), (yasm_expr__item *)NULL, i) + +/** Create a new expression identity e=r. + * \param r expression for identity within new expression + * \param i line index + * \return Newly allocated expression. + */ +#define yasm_expr_create_ident(r,i) \ + yasm_expr_create (YASM_EXPR_IDENT, (r), (yasm_expr__item *)NULL, i) + +/** Duplicate an expression. + * \param e expression + * \return Newly allocated expression identical to e. + */ +yasm_expr *yasm_expr_copy(const yasm_expr *e); +#ifndef YASM_DOXYGEN +#define yasm_expr_copy(e) yasm_expr__copy_except(e, -1) +#endif + +/** Destroy (free allocated memory for) an expression. + * \param e expression + */ +YASM_LIB_DECL +void yasm_expr_destroy(/*@only@*/ /*@null@*/ yasm_expr *e); + +/** Determine if an expression is a specified operation (at the top level). + * \param e expression + * \param op operator + * \return Nonzero if the expression was the specified operation at the top + * level, zero otherwise. + */ +YASM_LIB_DECL +int yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op); + +/** Extra transformation function for yasm_expr__level_tree(). + * \param e expression being simplified + * \param d data provided as expr_xform_extra_data to + * yasm_expr__level_tree() + * \return Transformed e. + */ +typedef /*@only@*/ yasm_expr * (*yasm_expr_xform_func) + (/*@returned@*/ /*@only@*/ yasm_expr *e, /*@null@*/ void *d); + +/** Level an entire expression tree. + * \internal + * \param e expression + * \param fold_const enable constant folding if nonzero + * \param simplify_ident simplify identities + * \param simplify_reg_mul simplify REG*1 identities + * \param calc_bc_dist nonzero if distances between bytecodes should be + * calculated, 0 if they should be left intact + * \param expr_xform_extra extra transformation function + * \param expr_xform_extra_data data to pass to expr_xform_extra + * \return Leveled expression. + */ +YASM_LIB_DECL +/*@only@*/ /*@null@*/ yasm_expr *yasm_expr__level_tree + (/*@returned@*/ /*@only@*/ /*@null@*/ yasm_expr *e, int fold_const, + int simplify_ident, int simplify_reg_mul, int calc_bc_dist, + /*@null@*/ yasm_expr_xform_func expr_xform_extra, + /*@null@*/ void *expr_xform_extra_data); + +/** Simplify an expression as much as possible. Eliminates extraneous + * branches and simplifies integer-only subexpressions. Simplified version + * of yasm_expr__level_tree(). + * \param e expression + * \param cbd if distance between bytecodes should be calculated + * \return Simplified expression. + */ +#define yasm_expr_simplify(e, cbd) \ + yasm_expr__level_tree(e, 1, 1, 1, cbd, NULL, NULL) + +/** Extract the segment portion of an expression containing SEG:OFF, leaving + * the offset. + * \param ep expression (pointer to) + * \return NULL if unable to extract a segment (expr does not contain a + * YASM_EXPR_SEGOFF operator), otherwise the segment expression. + * The input expression is modified such that on return, it's the + * offset expression. + */ +YASM_LIB_DECL +/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_deep_segoff(yasm_expr **ep); + +/** Extract the segment portion of a SEG:OFF expression, leaving the offset. + * \param ep expression (pointer to) + * \return NULL if unable to extract a segment (YASM_EXPR_SEGOFF not the + * top-level operator), otherwise the segment expression. The input + * expression is modified such that on return, it's the offset + * expression. + */ +YASM_LIB_DECL +/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_segoff(yasm_expr **ep); + +/** Extract the right portion (y) of a x WRT y expression, leaving the left + * portion (x). + * \param ep expression (pointer to) + * \return NULL if unable to extract (YASM_EXPR_WRT not the top-level + * operator), otherwise the right side of the WRT expression. The + * input expression is modified such that on return, it's the left side + * of the WRT expression. + */ +YASM_LIB_DECL +/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_wrt(yasm_expr **ep); + +/** Get the integer value of an expression if it's just an integer. + * \param ep expression (pointer to) + * \param calc_bc_dist nonzero if distances between bytecodes should be + * calculated, 0 if NULL should be returned in this case + * \return NULL if the expression is too complex (contains anything other than + * integers, ie floats, non-valued labels, registers); otherwise the + * intnum value of the expression. + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ yasm_intnum *yasm_expr_get_intnum + (yasm_expr **ep, int calc_bc_dist); + +/** Get the symbol value of an expression if it's just a symbol. + * \param ep expression (pointer to) + * \param simplify if nonzero, simplify the expression first + * \return NULL if the expression is too complex; otherwise the symbol value of + * the expression. + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ const yasm_symrec *yasm_expr_get_symrec + (yasm_expr **ep, int simplify); + +/** Get the register value of an expression if it's just a register. + * \param ep expression (pointer to) + * \param simplify if nonzero, simplify the expression first + * \return NULL if the expression is too complex; otherwise the register value + * of the expression. + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ const uintptr_t *yasm_expr_get_reg + (yasm_expr **ep, int simplify); + +/** Print an expression. For debugging purposes. + * \param e expression + * \param f file + */ +YASM_LIB_DECL +void yasm_expr_print(/*@null@*/ const yasm_expr *e, FILE *f); + +/** Return the size of an expression, if the user provided it + * \param e expression + */ +unsigned int yasm_expr_size(const yasm_expr *e); + +/** Return the segment of an expression, if the user provided it + * \param e expression + */ +const char *yasm_expr_segment(const yasm_expr *e); + +/** Traverse over expression tree in order (const version). + * Calls func for each leaf (non-operation). + * \param e expression + * \param d data passed to each call to func + * \param func callback function + * \return Stops early (and returns 1) if func returns 1. + * Otherwise returns 0. + */ +YASM_LIB_DECL +int yasm_expr__traverse_leaves_in_const + (const yasm_expr *e, /*@null@*/ void *d, + int (*func) (/*@null@*/ const yasm_expr__item *ei, /*@null@*/ void *d)); + +/** Traverse over expression tree in order. + * Calls func for each leaf (non-operation). + * \param e expression + * \param d data passed to each call to func + * \param func callback function + * \return Stops early (and returns 1) if func returns 1. + * Otherwise returns 0. + */ +YASM_LIB_DECL +int yasm_expr__traverse_leaves_in + (yasm_expr *e, /*@null@*/ void *d, + int (*func) (/*@null@*/ yasm_expr__item *ei, /*@null@*/ void *d)); + +/** Reorder terms of e into canonical order. Only reorders if reordering + * doesn't change meaning of expression. (eg, doesn't reorder SUB). + * Canonical order: REG, INT, FLOAT, SYM, EXPR. + * Multiple terms of a single type are kept in the same order as in + * the original expression. + * \param e expression + * \note Only performs reordering on *one* level (no recursion). + */ +YASM_LIB_DECL +void yasm_expr__order_terms(yasm_expr *e); + +/** Copy entire expression EXCEPT for index "except" at *top level only*. + * \param e expression + * \param except term index not to copy; -1 to copy all terms + * \return Newly allocated copy of expression. + */ +YASM_LIB_DECL +yasm_expr *yasm_expr__copy_except(const yasm_expr *e, int except); + +/** Test if expression contains an item. Searches recursively into + * subexpressions. + * \param e expression + * \param t type of item to look for + * \return Nonzero if expression contains an item of type t, zero if not. + */ +YASM_LIB_DECL +int yasm_expr__contains(const yasm_expr *e, yasm_expr__type t); + +/** Transform symrec-symrec terms in expression into #YASM_EXPR_SUBST items. + * Calls the callback function for each symrec-symrec term. + * \param ep expression (pointer to) + * \param cbd callback data passed to callback function + * \param callback callback function: given subst index for bytecode + * pair, bytecode pair (bc2-bc1), and cbd (callback data) + * \return Number of transformations made. + */ +YASM_LIB_DECL +int yasm_expr__bc_dist_subst(yasm_expr **ep, void *cbd, + void (*callback) (unsigned int subst, + yasm_bytecode *precbc, + yasm_bytecode *precbc2, + void *cbd)); + +/** Substitute items into expr YASM_EXPR_SUBST items (by index). Items are + * copied, so caller is responsible for freeing array of items. + * \param e expression + * \param num_items number of items in items array + * \param items items array + * \return 1 on error (index out of range). + */ +YASM_LIB_DECL +int yasm_expr__subst(yasm_expr *e, unsigned int num_items, + const yasm_expr__item *items); + +#endif diff --git a/libyasm/file.c b/libyasm/file.c new file mode 100644 index 0000000..fc7dab6 --- /dev/null +++ b/libyasm/file.c @@ -0,0 +1,672 @@ +/* + * File helper functions. + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +/* Need either unistd.h or direct.h to prototype getcwd() and mkdir() */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_DIRECT_H +#include <direct.h> +#endif + +#ifdef _WIN32 +#include <io.h> +#endif + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#include <ctype.h> +#include <errno.h> + +#include "errwarn.h" +#include "file.h" + +#define BSIZE 8192 /* Fill block size */ + + +void +yasm_scanner_initialize(yasm_scanner *s) +{ + s->bot = NULL; + s->tok = NULL; + s->ptr = NULL; + s->cur = NULL; + s->lim = NULL; + s->top = NULL; + s->eof = NULL; +} + +void +yasm_scanner_delete(yasm_scanner *s) +{ + if (s->bot) { + yasm_xfree(s->bot); + s->bot = NULL; + } +} + +int +yasm_fill_helper(yasm_scanner *s, unsigned char **cursor, + size_t (*input_func) (void *d, unsigned char *buf, + size_t max), + void *input_func_data) +{ + size_t cnt; + int first = 0; + + if (s->eof) + return 0; + + cnt = s->tok - s->bot; + if (cnt > 0) { + memmove(s->bot, s->tok, (size_t)(s->lim - s->tok)); + s->tok = s->bot; + s->ptr -= cnt; + *cursor -= cnt; + s->lim -= cnt; + } + if (!s->bot) + first = 1; + if ((s->top - s->lim) < BSIZE) { + unsigned char *buf = yasm_xmalloc((size_t)(s->lim - s->bot) + BSIZE); + memcpy(buf, s->tok, (size_t)(s->lim - s->tok)); + s->tok = buf; + s->ptr = &buf[s->ptr - s->bot]; + *cursor = &buf[*cursor - s->bot]; + s->lim = &buf[s->lim - s->bot]; + s->top = &s->lim[BSIZE]; + if (s->bot) + yasm_xfree(s->bot); + s->bot = buf; + } + if ((cnt = input_func(input_func_data, s->lim, BSIZE)) == 0) { + s->eof = &s->lim[cnt]; + *s->eof++ = '\n'; + } + s->lim += cnt; + return first; +} + +void +yasm_unescape_cstring(unsigned char *str, size_t *len) +{ + unsigned char *s = str; + unsigned char *o = str; + unsigned char t[4]; + + while ((size_t)(s-str)<*len) { + if (*s == '\\' && (size_t)(&s[1]-str)<*len) { + s++; + switch (*s) { + case 'b': *o = '\b'; s++; break; + case 'f': *o = '\f'; s++; break; + case 'n': *o = '\n'; s++; break; + case 'r': *o = '\r'; s++; break; + case 't': *o = '\t'; s++; break; + case 'x': + /* hex escape; grab last two digits */ + s++; + while ((size_t)(&s[2]-str)<*len && isxdigit(s[0]) + && isxdigit(s[1]) && isxdigit(s[2])) + s++; + if ((size_t)(s-str)<*len && isxdigit(*s)) { + t[0] = *s++; + t[1] = '\0'; + t[2] = '\0'; + if ((size_t)(s-str)<*len && isxdigit(*s)) + t[1] = *s++; + *o = (unsigned char)strtoul((char *)t, NULL, 16); + } else + *o = '\0'; + break; + default: + if (isdigit(*s)) { + int warn = 0; + /* octal escape */ + if (*s > '7') + warn = 1; + *o = *s++ - '0'; + if ((size_t)(s-str)<*len && isdigit(*s)) { + if (*s > '7') + warn = 1; + *o <<= 3; + *o += *s++ - '0'; + if ((size_t)(s-str)<*len && isdigit(*s)) { + if (*s > '7') + warn = 1; + *o <<= 3; + *o += *s++ - '0'; + } + } + if (warn) + yasm_warn_set(YASM_WARN_GENERAL, + N_("octal value out of range")); + } else + *o = *s++; + break; + } + o++; + } else + *o++ = *s++; + } + *len = o-str; +} + +size_t +yasm__splitpath_unix(const char *path, /*@out@*/ const char **tail) +{ + const char *s; + s = strrchr(path, '/'); + if (!s) { + /* No head */ + *tail = path; + return 0; + } + *tail = s+1; + /* Strip trailing ./ on path */ + while ((s-1)>=path && *(s-1) == '.' && *s == '/' + && !((s-2)>=path && *(s-2) == '.')) + s -= 2; + /* Strip trailing slashes on path (except leading) */ + while (s>path && *s == '/') + s--; + /* Return length of head */ + return s-path+1; +} + +size_t +yasm__splitpath_win(const char *path, /*@out@*/ const char **tail) +{ + const char *basepath = path; + const char *s; + + /* split off drive letter first, if any */ + if (isalpha(path[0]) && path[1] == ':') + basepath += 2; + + s = basepath; + while (*s != '\0') + s++; + while (s >= basepath && *s != '\\' && *s != '/') + s--; + if (s < basepath) { + *tail = basepath; + if (path == basepath) + return 0; /* No head */ + else + return 2; /* Drive letter is head */ + } + *tail = s+1; + /* Strip trailing .\ or ./ on path */ + while ((s-1)>=basepath && *(s-1) == '.' && (*s == '/' || *s == '\\') + && !((s-2)>=basepath && *(s-2) == '.')) + s -= 2; + /* Strip trailing slashes on path (except leading) */ + while (s>basepath && (*s == '/' || *s == '\\')) + s--; + /* Return length of head */ + return s-path+1; +} + +char * +yasm__getcwd(void) +{ + char *buf; + size_t size; + + size = 1024; + buf = yasm_xmalloc(size); + + if (getenv("YASM_TEST_SUITE")) { + strcpy(buf, "./"); + return buf; + } + + while (getcwd(buf, size-1) == NULL) { + if (errno != ERANGE) { + yasm__fatal(N_("could not determine current working directory")); + yasm_xfree(buf); + return NULL; + } + size *= 2; + buf = yasm_xrealloc(buf, size); + } + + /* append a '/' if not already present */ + size = strlen(buf); + if (buf[size-1] != '\\' && buf[size-1] != '/') { + buf[size] = '/'; + buf[size+1] = '\0'; + } + return buf; +} + +char * +yasm__abspath(const char *path) +{ + char *curdir, *abspath; + + curdir = yasm__getcwd(); + abspath = yasm__combpath(curdir, path); + yasm_xfree(curdir); + + return abspath; +} + +char * +yasm__combpath_unix(const char *from, const char *to) +{ + const char *tail; + size_t pathlen, i, j; + char *out; + + if (to[0] == '/') { + /* absolute "to" */ + out = yasm_xmalloc(strlen(to)+1); + /* Combine any double slashes when copying */ + for (j=0; *to; to++) { + if (*to == '/' && *(to+1) == '/') + continue; + out[j++] = *to; + } + out[j++] = '\0'; + return out; + } + + /* Get path component; note this strips trailing slash */ + pathlen = yasm__splitpath_unix(from, &tail); + + out = yasm_xmalloc(pathlen+strlen(to)+2); /* worst case maximum len */ + + /* Combine any double slashes when copying */ + for (i=0, j=0; i<pathlen; i++) { + if (i<pathlen-1 && from[i] == '/' && from[i+1] == '/') + continue; + out[j++] = from[i]; + } + pathlen = j; + + /* Add trailing slash back in */ + if (pathlen > 0 && out[pathlen-1] != '/') + out[pathlen++] = '/'; + + /* Now scan from left to right through "to", stripping off "." and ".."; + * if we see "..", back up one directory in out unless last directory in + * out is also "..". + * + * Note this does NOT back through ..'s in the "from" path; this is just + * as well as that could skip symlinks (e.g. "foo/bar/.." might not be + * the same as "foo"). + */ + for (;;) { + if (to[0] == '.' && to[1] == '/') { + to += 2; /* current directory */ + while (*to == '/') + to++; /* strip off any additional slashes */ + } else if (pathlen == 0) + break; /* no more "from" path left, we're done */ + else if (to[0] == '.' && to[1] == '.' && to[2] == '/') { + if (pathlen >= 3 && out[pathlen-1] == '/' && out[pathlen-2] == '.' + && out[pathlen-3] == '.') { + /* can't ".." against a "..", so we're done. */ + break; + } + + to += 3; /* throw away "../" */ + while (*to == '/') + to++; /* strip off any additional slashes */ + + /* and back out last directory in "out" if not already at root */ + if (pathlen > 1) { + pathlen--; /* strip off trailing '/' */ + while (pathlen > 0 && out[pathlen-1] != '/') + pathlen--; + } + } else + break; + } + + /* Copy "to" to tail of output, and we're done */ + /* Combine any double slashes when copying */ + for (j=pathlen; *to; to++) { + if (*to == '/' && *(to+1) == '/') + continue; + out[j++] = *to; + } + out[j++] = '\0'; + + return out; +} + +char * +yasm__combpath_win(const char *from, const char *to) +{ + const char *tail; + size_t pathlen, i, j; + char *out; + + if ((isalpha(to[0]) && to[1] == ':') || (to[0] == '/' || to[0] == '\\')) { + /* absolute or drive letter "to" */ + out = yasm_xmalloc(strlen(to)+1); + /* Combine any double slashes when copying */ + for (j=0; *to; to++) { + if ((*to == '/' || *to == '\\') + && (*(to+1) == '/' || *(to+1) == '\\')) + continue; + if (*to == '/') + out[j++] = '\\'; + else + out[j++] = *to; + } + out[j++] = '\0'; + return out; + } + + /* Get path component; note this strips trailing slash */ + pathlen = yasm__splitpath_win(from, &tail); + + out = yasm_xmalloc(pathlen+strlen(to)+2); /* worst case maximum len */ + + /* Combine any double slashes when copying */ + for (i=0, j=0; i<pathlen; i++) { + if (i<pathlen-1 && (from[i] == '/' || from[i] == '\\') + && (from[i+1] == '/' || from[i+1] == '\\')) + continue; + if (from[i] == '/') + out[j++] = '\\'; + else + out[j++] = from[i]; + } + pathlen = j; + + /* Add trailing slash back in, unless it's only a raw drive letter */ + if (pathlen > 0 && out[pathlen-1] != '\\' + && !(pathlen == 2 && isalpha(out[0]) && out[1] == ':')) + out[pathlen++] = '\\'; + + /* Now scan from left to right through "to", stripping off "." and ".."; + * if we see "..", back up one directory in out unless last directory in + * out is also "..". + * + * Note this does NOT back through ..'s in the "from" path; this is just + * as well as that could skip symlinks (e.g. "foo/bar/.." might not be + * the same as "foo"). + */ + for (;;) { + if (to[0] == '.' && (to[1] == '/' || to[1] == '\\')) { + to += 2; /* current directory */ + while (*to == '/' || *to == '\\') + to++; /* strip off any additional slashes */ + } else if (pathlen == 0 + || (pathlen == 2 && isalpha(out[0]) && out[1] == ':')) + break; /* no more "from" path left, we're done */ + else if (to[0] == '.' && to[1] == '.' + && (to[2] == '/' || to[2] == '\\')) { + if (pathlen >= 3 && out[pathlen-1] == '\\' + && out[pathlen-2] == '.' && out[pathlen-3] == '.') { + /* can't ".." against a "..", so we're done. */ + break; + } + + to += 3; /* throw away "../" (or "..\") */ + while (*to == '/' || *to == '\\') + to++; /* strip off any additional slashes */ + + /* and back out last directory in "out" if not already at root */ + if (pathlen > 1) { + pathlen--; /* strip off trailing '/' */ + while (pathlen > 0 && out[pathlen-1] != '\\') + pathlen--; + } + } else + break; + } + + /* Copy "to" to tail of output, and we're done */ + /* Combine any double slashes when copying */ + for (j=pathlen; *to; to++) { + if ((*to == '/' || *to == '\\') && (*(to+1) == '/' || *(to+1) == '\\')) + continue; + if (*to == '/') + out[j++] = '\\'; + else + out[j++] = *to; + } + out[j++] = '\0'; + + return out; +} + +size_t +yasm__createpath_common(const char *path, int win) +{ + const char *pp = path, *pe; + char *ts, *tp; + size_t len, lth; + + lth = len = strlen(path); + ts = tp = (char *) malloc(len + 1); + pe = pp + len; + while (pe > pp) { + if ((win && *pe == '\\') || *pe == '/') + break; + --pe; + --lth; + } + + while (pp <= pe) { + if (pp == pe || (win && *pp == '\\') || *pp == '/') { +#ifdef _WIN32 + struct _finddata_t fi; + intptr_t h; +#elif defined(HAVE_SYS_STAT_H) + struct stat fi; +#endif + *tp = '\0'; + +#ifdef _WIN32 + h = _findfirst(ts, &fi); + if (h != -1) { + if (fi.attrib != _A_SUBDIR) { + _findclose(h); + break; + } + } else if (errno == ENOENT) { + if (_mkdir(ts) == -1) { + _findclose(h); + lth = -1; + break; + } + } + _findclose(h); +#elif defined(HAVE_SYS_STAT_H) + if (stat(ts, &fi) != -1) { + if (!S_ISDIR(fi.st_mode)) + break; + } else if (errno == ENOENT) { + if (mkdir(ts, 0755) == -1) { + lth = 0; + break; + } + } +#else + break; +#endif + } + *tp++ = *pp++; + } + free(ts); + return lth; +} + +typedef struct incpath { + STAILQ_ENTRY(incpath) link; + /*@owned@*/ char *path; +} incpath; + +STAILQ_HEAD(incpath_head, incpath) incpaths = STAILQ_HEAD_INITIALIZER(incpaths); + +FILE * +yasm_fopen_include(const char *iname, const char *from, const char *mode, + char **oname) +{ + FILE *f; + char *combine; + incpath *np; + + /* Try directly relative to from first, then each of the include paths */ + if (from) { + combine = yasm__combpath(from, iname); + f = fopen(combine, mode); + if (f) { + if (oname) + *oname = combine; + else + yasm_xfree(combine); + return f; + } + yasm_xfree(combine); + } + + STAILQ_FOREACH(np, &incpaths, link) { + combine = yasm__combpath(np->path, iname); + f = fopen(combine, mode); + if (f) { + if (oname) + *oname = combine; + else + yasm_xfree(combine); + return f; + } + yasm_xfree(combine); + } + + if (oname) + *oname = NULL; + return NULL; +} + +void +yasm_delete_include_paths(void) +{ + incpath *n1, *n2; + + n1 = STAILQ_FIRST(&incpaths); + while (n1) { + n2 = STAILQ_NEXT(n1, link); + yasm_xfree(n1->path); + yasm_xfree(n1); + n1 = n2; + } + STAILQ_INIT(&incpaths); +} + +const char * +yasm_get_include_dir(void **iter) +{ + incpath *p = (incpath *)*iter; + + if (!p) + p = STAILQ_FIRST(&incpaths); + else + p = STAILQ_NEXT(p, link); + + *iter = p; + if (p) + return p->path; + else + return NULL; +} + +void +yasm_add_include_path(const char *path) +{ + incpath *np = yasm_xmalloc(sizeof(incpath)); + size_t len = strlen(path); + + np->path = yasm_xmalloc(len+2); + memcpy(np->path, path, len+1); + /* Add trailing slash if it is missing */ + if (path[len-1] != '\\' && path[len-1] != '/') { + np->path[len] = '/'; + np->path[len+1] = '\0'; + } + + STAILQ_INSERT_TAIL(&incpaths, np, link); +} + +size_t +yasm_fwrite_16_l(unsigned short val, FILE *f) +{ + if (fputc(val & 0xFF, f) == EOF) + return 0; + if (fputc((val >> 8) & 0xFF, f) == EOF) + return 0; + return 1; +} + +size_t +yasm_fwrite_32_l(unsigned long val, FILE *f) +{ + if (fputc((int)(val & 0xFF), f) == EOF) + return 0; + if (fputc((int)((val >> 8) & 0xFF), f) == EOF) + return 0; + if (fputc((int)((val >> 16) & 0xFF), f) == EOF) + return 0; + if (fputc((int)((val >> 24) & 0xFF), f) == EOF) + return 0; + return 1; +} + +size_t +yasm_fwrite_16_b(unsigned short val, FILE *f) +{ + if (fputc((val >> 8) & 0xFF, f) == EOF) + return 0; + if (fputc(val & 0xFF, f) == EOF) + return 0; + return 1; +} + +size_t +yasm_fwrite_32_b(unsigned long val, FILE *f) +{ + if (fputc((int)((val >> 24) & 0xFF), f) == EOF) + return 0; + if (fputc((int)((val >> 16) & 0xFF), f) == EOF) + return 0; + if (fputc((int)((val >> 8) & 0xFF), f) == EOF) + return 0; + if (fputc((int)(val & 0xFF), f) == EOF) + return 0; + return 1; +} diff --git a/libyasm/file.h b/libyasm/file.h new file mode 100644 index 0000000..1e9b87b --- /dev/null +++ b/libyasm/file.h @@ -0,0 +1,525 @@ +/** + * \file libyasm/file.h + * \brief YASM file helpers. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_FILE_H +#define YASM_FILE_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Re2c scanner state. */ +typedef struct yasm_scanner { + unsigned char *bot; /**< Bottom of scan buffer */ + unsigned char *tok; /**< Start of token */ + unsigned char *ptr; /**< Scan marker */ + unsigned char *cur; /**< Cursor (1 past end of token) */ + unsigned char *lim; /**< Limit of good data */ + unsigned char *top; /**< Top of scan buffer */ + unsigned char *eof; /**< End of file */ +} yasm_scanner; + +/** Initialize scanner state. + * \param scanner Re2c scanner state + */ +YASM_LIB_DECL +void yasm_scanner_initialize(yasm_scanner *scanner); + +/** Frees any memory used by scanner state; does not free state itself. + * \param scanner Re2c scanner state + */ +YASM_LIB_DECL +void yasm_scanner_delete(yasm_scanner *scanner); + +/** Fill a scanner state structure with data coming from an input function. + * \param scanner Re2c scanner state + * \param cursor Re2c scan cursor + * \param input_func Input function to read data; takes buffer and maximum + * number of bytes, returns number of bytes read. + * \param input_func_data Data to pass as the first parameter to input_func + * \return 1 if this was the first time this function was called on this + * scanner state, 0 otherwise. + */ +YASM_LIB_DECL +int yasm_fill_helper + (yasm_scanner *scanner, unsigned char **cursor, + size_t (*input_func) (void *d, unsigned char *buf, size_t max), + void *input_func_data); + +/** Unescape a string with C-style escapes. Handles b, f, n, r, t, and hex + * and octal escapes. String is updated in-place. + * Edge cases: + * - hex escapes: reads as many hex digits as possible, takes last 2 as value. + * - oct escapes: takes up to 3 digits 0-9 and scales appropriately, with + * warning. + * \param str C-style string (updated in place) + * \param len length of string (updated with new length) + */ +YASM_LIB_DECL +void yasm_unescape_cstring(unsigned char *str, size_t *len); + +/** Split a UNIX pathname into head (directory) and tail (base filename) + * portions. + * \internal + * \param path pathname + * \param tail (returned) base filename + * \return Length of head (directory). + */ +YASM_LIB_DECL +size_t yasm__splitpath_unix(const char *path, /*@out@*/ const char **tail); + +/** Split a Windows pathname into head (directory) and tail (base filename) + * portions. + * \internal + * \param path pathname + * \param tail (returned) base filename + * \return Length of head (directory). + */ +YASM_LIB_DECL +size_t yasm__splitpath_win(const char *path, /*@out@*/ const char **tail); + +/** Split a pathname into head (directory) and tail (base filename) portions. + * Unless otherwise defined, defaults to yasm__splitpath_unix(). + * \internal + * \param path pathname + * \param tail (returned) base filename + * \return Length of head (directory). + */ +#ifndef yasm__splitpath +# if defined (_WIN32) || defined (WIN32) || defined (__MSDOS__) || \ + defined (__DJGPP__) || defined (__OS2__) +# define yasm__splitpath(path, tail) yasm__splitpath_win(path, tail) +# else +# define yasm__splitpath(path, tail) yasm__splitpath_unix(path, tail) +# endif +#endif + +/** Get the current working directory. + * \internal + * \return Current working directory pathname (newly allocated). + */ +YASM_LIB_DECL +/*@only@*/ char *yasm__getcwd(void); + +/** Convert a relative or absolute pathname into an absolute pathname. + * \internal + * \param path pathname + * \return Absolute version of path (newly allocated). + */ +YASM_LIB_DECL +/*@only@*/ char *yasm__abspath(const char *path); + +/** Build a UNIX pathname that is equivalent to accessing the "to" pathname + * when you're in the directory containing "from". Result is relative if both + * from and to are relative. + * \internal + * \param from from pathname + * \param to to pathname + * \return Combined path (newly allocated). + */ +YASM_LIB_DECL +char *yasm__combpath_unix(const char *from, const char *to); + +/** Build a Windows pathname that is equivalent to accessing the "to" pathname + * when you're in the directory containing "from". Result is relative if both + * from and to are relative. + * \internal + * \param from from pathname + * \param to to pathname + * \return Combined path (newly allocated). + */ +YASM_LIB_DECL +char *yasm__combpath_win(const char *from, const char *to); + +/** Build a pathname that is equivalent to accessing the "to" pathname + * when you're in the directory containing "from". Result is relative if both + * from and to are relative. + * Unless otherwise defined, defaults to yasm__combpath_unix(). + * \internal + * \param from from pathname + * \param to to pathname + * \return Combined path (newly allocated). + */ +#ifndef yasm__combpath +# if defined (_WIN32) || defined (WIN32) || defined (__MSDOS__) || \ + defined (__DJGPP__) || defined (__OS2__) +# define yasm__combpath(from, to) yasm__combpath_win(from, to) +# else +# define yasm__combpath(from, to) yasm__combpath_unix(from, to) +# endif +#endif + +/** Recursively create tree of directories needed for pathname. + * \internal + * \param path pathname + * \param win handle windows paths + * \return Length of directory portion of pathname. + */ +YASM_LIB_DECL +size_t yasm__createpath_common(const char *path, int win); + +/** Recursively create tree of directories needed for pathname. + * Unless otherwise defined, defaults to yasm__createpath_unix(). + * \internal + * \param path pathname + * \return Length of directory portion of pathname. + */ +#ifndef yasm__createpath +# if defined (_WIN32) || defined (WIN32) || defined (__MSDOS__) || \ + defined (__DJGPP__) || defined (__OS2__) +# define yasm__createpath(path) yasm__createpath_common(path, 1) +# else +# define yasm__createpath(path) yasm__createpath_common(path, 0) +# endif +#endif + +/** Try to find and open an include file, searching through include paths. + * First iname is looked for relative to the directory containing "from", then + * it's looked for relative to each of the include paths. + * + * All pathnames may be either absolute or relative; from, oname, and + * include paths, if relative, are relative from the current working directory. + * + * First match wins; the full pathname (newly allocated) to the opened file + * is saved into oname, and the fopen'ed FILE * is returned. If not found, + * NULL is returned. + * + * \param iname file to include + * \param from file doing the including + * \param mode fopen mode string + * \param oname full pathname of included file (may be relative). NULL + * may be passed if this is unwanted. + * \return fopen'ed include file, or NULL if not found. + */ +YASM_LIB_DECL +/*@null@*/ FILE *yasm_fopen_include + (const char *iname, const char *from, const char *mode, + /*@null@*/ /*@out@*/ /*@only@*/ char **oname); + +/** Delete any stored include paths added by yasm_add_include_path(). + */ +YASM_LIB_DECL +void yasm_delete_include_paths(void); + +/** Iterate through include paths. +*/ +YASM_LIB_DECL +const char * yasm_get_include_dir(void **iter); + +/** Add an include path for use by yasm_fopen_include(). + * If path is relative, it is treated by yasm_fopen_include() as relative to + * the current working directory. + * + * \param path path to add + */ +YASM_LIB_DECL +void yasm_add_include_path(const char *path); + +/** Write an 8-bit value to a buffer, incrementing buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 8-bit value + */ +#define YASM_WRITE_8(ptr, val) \ + *((ptr)++) = (unsigned char)((val) & 0xFF) + +/** Write a 16-bit value to a buffer in little endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_WRITE_16_L(ptr, val) \ + do { \ + *((ptr)++) = (unsigned char)((val) & 0xFF); \ + *((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \ + } while (0) + +/** Write a 32-bit value to a buffer in little endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_WRITE_32_L(ptr, val) \ + do { \ + *((ptr)++) = (unsigned char)((val) & 0xFF); \ + *((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \ + *((ptr)++) = (unsigned char)(((val) >> 16) & 0xFF); \ + *((ptr)++) = (unsigned char)(((val) >> 24) & 0xFF); \ + } while (0) + +/** Write a 16-bit value to a buffer in big endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_WRITE_16_B(ptr, val) \ + do { \ + *((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \ + *((ptr)++) = (unsigned char)((val) & 0xFF); \ + } while (0) + +/** Write a 32-bit value to a buffer in big endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_WRITE_32_B(ptr, val) \ + do { \ + *((ptr)++) = (unsigned char)(((val) >> 24) & 0xFF); \ + *((ptr)++) = (unsigned char)(((val) >> 16) & 0xFF); \ + *((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \ + *((ptr)++) = (unsigned char)((val) & 0xFF); \ + } while (0) + + +/** Write an 8-bit value to a buffer. Does not increment buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 8-bit value + */ +#define YASM_SAVE_8(ptr, val) \ + *(ptr) = (unsigned char)((val) & 0xFF) + +/** Write a 16-bit value to a buffer in little endian. Does not increment + * buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_SAVE_16_L(ptr, val) \ + do { \ + *(ptr) = (unsigned char)((val) & 0xFF); \ + *((ptr)+1) = (unsigned char)(((val) >> 8) & 0xFF); \ + } while (0) + +/** Write a 32-bit value to a buffer in little endian. Does not increment + * buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_SAVE_32_L(ptr, val) \ + do { \ + *(ptr) = (unsigned char)((val) & 0xFF); \ + *((ptr)+1) = (unsigned char)(((val) >> 8) & 0xFF); \ + *((ptr)+2) = (unsigned char)(((val) >> 16) & 0xFF); \ + *((ptr)+3) = (unsigned char)(((val) >> 24) & 0xFF); \ + } while (0) + +/** Write a 16-bit value to a buffer in big endian. Does not increment buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_SAVE_16_B(ptr, val) \ + do { \ + *(ptr) = (unsigned char)(((val) >> 8) & 0xFF); \ + *((ptr)+1) = (unsigned char)((val) & 0xFF); \ + } while (0) + +/** Write a 32-bit value to a buffer in big endian. Does not increment buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_SAVE_32_B(ptr, val) \ + do { \ + *(ptr) = (unsigned char)(((val) >> 24) & 0xFF); \ + *((ptr)+1) = (unsigned char)(((val) >> 16) & 0xFF); \ + *((ptr)+2) = (unsigned char)(((val) >> 8) & 0xFF); \ + *((ptr)+3) = (unsigned char)((val) & 0xFF); \ + } while (0) + +/** Direct-to-file version of YASM_SAVE_16_L(). + * \note Using the macro multiple times with a single fwrite() call will + * probably be faster than calling this function many times. + * \param val 16-bit value + * \param f file + * \return 1 if the write was successful, 0 if not (just like fwrite()). + */ +YASM_LIB_DECL +size_t yasm_fwrite_16_l(unsigned short val, FILE *f); + +/** Direct-to-file version of YASM_SAVE_32_L(). + * \note Using the macro multiple times with a single fwrite() call will + * probably be faster than calling this function many times. + * \param val 32-bit value + * \param f file + * \return 1 if the write was successful, 0 if not (just like fwrite()). + */ +YASM_LIB_DECL +size_t yasm_fwrite_32_l(unsigned long val, FILE *f); + +/** Direct-to-file version of YASM_SAVE_16_B(). + * \note Using the macro multiple times with a single fwrite() call will + * probably be faster than calling this function many times. + * \param val 16-bit value + * \param f file + * \return 1 if the write was successful, 0 if not (just like fwrite()). + */ +YASM_LIB_DECL +size_t yasm_fwrite_16_b(unsigned short val, FILE *f); + +/** Direct-to-file version of YASM_SAVE_32_B(). + * \note Using the macro multiple times with a single fwrite() call will + * probably be faster than calling this function many times. + * \param val 32-bit value + * \param f file + * \return 1 if the write was successful, 0 if not (just like fwrite()). + */ +YASM_LIB_DECL +size_t yasm_fwrite_32_b(unsigned long val, FILE *f); + +/** Read an 8-bit value from a buffer, incrementing buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 8-bit value + */ +#define YASM_READ_8(val, ptr) \ + (val) = *((ptr)++) & 0xFF + +/** Read a 16-bit value from a buffer in little endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_READ_16_L(val, ptr) \ + do { \ + (val) = *((ptr)++) & 0xFF; \ + (val) |= (*((ptr)++) & 0xFF) << 8; \ + } while (0) + +/** Read a 32-bit value from a buffer in little endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_READ_32_L(val, ptr) \ + do { \ + (val) = *((ptr)++) & 0xFF; \ + (val) |= (*((ptr)++) & 0xFF) << 8; \ + (val) |= (*((ptr)++) & 0xFF) << 16; \ + (val) |= (*((ptr)++) & 0xFF) << 24; \ + } while (0) + +/** Read a 16-bit value from a buffer in big endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_READ_16_B(val, ptr) \ + do { \ + (val) = (*((ptr)++) & 0xFF) << 8; \ + (val) |= *((ptr)++) & 0xFF; \ + } while (0) + +/** Read a 32-bit value from a buffer in big endian, incrementing buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_READ_32_B(val, ptr) \ + do { \ + (val) = (*((ptr)++) & 0xFF) << 24; \ + (val) |= (*((ptr)++) & 0xFF) << 16; \ + (val) |= (*((ptr)++) & 0xFF) << 8; \ + (val) |= *((ptr)++) & 0xFF; \ + } while (0) + +/** Read an 8-bit value from a buffer. Does not increment buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 8-bit value + */ +#define YASM_LOAD_8(val, ptr) \ + (val) = *(ptr) & 0xFF + +/** Read a 16-bit value from a buffer in little endian. Does not increment + * buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_LOAD_16_L(val, ptr) \ + do { \ + (val) = *(ptr) & 0xFF; \ + (val) |= (*((ptr)+1) & 0xFF) << 8; \ + } while (0) + +/** Read a 32-bit value from a buffer in little endian. Does not increment + * buffer pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_LOAD_32_L(val, ptr) \ + do { \ + (val) = (unsigned long)(*(ptr) & 0xFF); \ + (val) |= (unsigned long)((*((ptr)+1) & 0xFF) << 8); \ + (val) |= (unsigned long)((*((ptr)+2) & 0xFF) << 16); \ + (val) |= (unsigned long)((*((ptr)+3) & 0xFF) << 24); \ + } while (0) + +/** Read a 16-bit value from a buffer in big endian. Does not increment buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 16-bit value + */ +#define YASM_LOAD_16_B(val, ptr) \ + do { \ + (val) = (*(ptr) & 0xFF) << 8; \ + (val) |= *((ptr)+1) & 0xFF; \ + } while (0) + +/** Read a 32-bit value from a buffer in big endian. Does not increment buffer + * pointer. + * \note Only works properly if ptr is an (unsigned char *). + * \param ptr buffer + * \param val 32-bit value + */ +#define YASM_LOAD_32_B(val, ptr) \ + do { \ + (val) = (unsigned long)((*(ptr) & 0xFF) << 24); \ + (val) |= (unsigned long)((*((ptr)+1) & 0xFF) << 16); \ + (val) |= (unsigned long)((*((ptr)+2) & 0xFF) << 8); \ + (val) |= (unsigned long)(*((ptr)+3) & 0xFF); \ + } while (0) + +#endif diff --git a/libyasm/floatnum.c b/libyasm/floatnum.c new file mode 100644 index 0000000..ab67c2b --- /dev/null +++ b/libyasm/floatnum.c @@ -0,0 +1,760 @@ +/* + * Floating point number functions. + * + * Copyright (C) 2001-2007 Peter Johnson + * + * Based on public-domain x86 assembly code by Randall Hyde (8/28/91). + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include <ctype.h> + +#include "coretype.h" +#include "bitvect.h" +#include "file.h" + +#include "errwarn.h" +#include "floatnum.h" + + +/* 97-bit internal floating point format: + * 0000000s eeeeeeee eeeeeeee m.....................................m + * Sign exponent mantissa (80 bits) + * 79 0 + * + * Only L.O. bit of Sign byte is significant. The rest is zero. + * Exponent is bias 32767. + * Mantissa does NOT have an implied one bit (it's explicit). + */ +struct yasm_floatnum { + /*@only@*/ wordptr mantissa; /* Allocated to MANT_BITS bits */ + unsigned short exponent; + unsigned char sign; + unsigned char flags; +}; + +/* constants describing parameters of internal floating point format */ +#define MANT_BITS 80 +#define MANT_BYTES 10 +#define MANT_SIGDIGITS 24 +#define EXP_BIAS 0x7FFF +#define EXP_INF 0xFFFF +#define EXP_MAX 0xFFFE +#define EXP_MIN 1 +#define EXP_ZERO 0 + +/* Flag settings for flags field */ +#define FLAG_ISZERO 1<<0 + +/* Note this structure integrates the floatnum structure */ +typedef struct POT_Entry_s { + yasm_floatnum f; + int dec_exponent; +} POT_Entry; + +/* "Source" for POT_Entry. */ +typedef struct POT_Entry_Source_s { + unsigned char mantissa[MANT_BYTES]; /* little endian mantissa */ + unsigned short exponent; /* Bias 32767 exponent */ +} POT_Entry_Source; + +/* Power of ten tables used by the floating point I/O routines. + * The POT_Table? arrays are built from the POT_Table?_Source arrays at + * runtime by POT_Table_Init(). + */ + +/* This table contains the powers of ten raised to negative powers of two: + * + * entry[12-n] = 10 ** (-2 ** n) for 0 <= n <= 12. + * entry[13] = 1.0 + */ +static /*@only@*/ POT_Entry *POT_TableN; +static POT_Entry_Source POT_TableN_Source[] = { + {{0xe3,0x2d,0xde,0x9f,0xce,0xd2,0xc8,0x04,0xdd,0xa6},0x4ad8}, /* 1e-4096 */ + {{0x25,0x49,0xe4,0x2d,0x36,0x34,0x4f,0x53,0xae,0xce},0x656b}, /* 1e-2048 */ + {{0xa6,0x87,0xbd,0xc0,0x57,0xda,0xa5,0x82,0xa6,0xa2},0x72b5}, /* 1e-1024 */ + {{0x33,0x71,0x1c,0xd2,0x23,0xdb,0x32,0xee,0x49,0x90},0x795a}, /* 1e-512 */ + {{0x91,0xfa,0x39,0x19,0x7a,0x63,0x25,0x43,0x31,0xc0},0x7cac}, /* 1e-256 */ + {{0x7d,0xac,0xa0,0xe4,0xbc,0x64,0x7c,0x46,0xd0,0xdd},0x7e55}, /* 1e-128 */ + {{0x24,0x3f,0xa5,0xe9,0x39,0xa5,0x27,0xea,0x7f,0xa8},0x7f2a}, /* 1e-64 */ + {{0xde,0x67,0xba,0x94,0x39,0x45,0xad,0x1e,0xb1,0xcf},0x7f94}, /* 1e-32 */ + {{0x2f,0x4c,0x5b,0xe1,0x4d,0xc4,0xbe,0x94,0x95,0xe6},0x7fc9}, /* 1e-16 */ + {{0xc2,0xfd,0xfc,0xce,0x61,0x84,0x11,0x77,0xcc,0xab},0x7fe4}, /* 1e-8 */ + {{0xc3,0xd3,0x2b,0x65,0x19,0xe2,0x58,0x17,0xb7,0xd1},0x7ff1}, /* 1e-4 */ + {{0x71,0x3d,0x0a,0xd7,0xa3,0x70,0x3d,0x0a,0xd7,0xa3},0x7ff8}, /* 1e-2 */ + {{0xcd,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc},0x7ffb}, /* 1e-1 */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80},0x7fff}, /* 1e-0 */ +}; + +/* This table contains the powers of ten raised to positive powers of two: + * + * entry[12-n] = 10 ** (2 ** n) for 0 <= n <= 12. + * entry[13] = 1.0 + * entry[-1] = entry[0]; + * + * There is a -1 entry since it is possible for the algorithm to back up + * before the table. This -1 entry is created at runtime by duplicating the + * 0 entry. + */ +static /*@only@*/ POT_Entry *POT_TableP; +static POT_Entry_Source POT_TableP_Source[] = { + {{0x4c,0xc9,0x9a,0x97,0x20,0x8a,0x02,0x52,0x60,0xc4},0xb525}, /* 1e+4096 */ + {{0x4d,0xa7,0xe4,0x5d,0x3d,0xc5,0x5d,0x3b,0x8b,0x9e},0x9a92}, /* 1e+2048 */ + {{0x0d,0x65,0x17,0x0c,0x75,0x81,0x86,0x75,0x76,0xc9},0x8d48}, /* 1e+1024 */ + {{0x65,0xcc,0xc6,0x91,0x0e,0xa6,0xae,0xa0,0x19,0xe3},0x86a3}, /* 1e+512 */ + {{0xbc,0xdd,0x8d,0xde,0xf9,0x9d,0xfb,0xeb,0x7e,0xaa},0x8351}, /* 1e+256 */ + {{0x6f,0xc6,0xdf,0x8c,0xe9,0x80,0xc9,0x47,0xba,0x93},0x81a8}, /* 1e+128 */ + {{0xbf,0x3c,0xd5,0xa6,0xcf,0xff,0x49,0x1f,0x78,0xc2},0x80d3}, /* 1e+64 */ + {{0x20,0xf0,0x9d,0xb5,0x70,0x2b,0xa8,0xad,0xc5,0x9d},0x8069}, /* 1e+32 */ + {{0x00,0x00,0x00,0x00,0x00,0x04,0xbf,0xc9,0x1b,0x8e},0x8034}, /* 1e+16 */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0xbc,0xbe},0x8019}, /* 1e+8 */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x9c},0x800c}, /* 1e+4 */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc8},0x8005}, /* 1e+2 */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0},0x8002}, /* 1e+1 */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80},0x7fff}, /* 1e+0 */ +}; + + +static void +POT_Table_Init_Entry(/*@out@*/ POT_Entry *e, POT_Entry_Source *s, int dec_exp) +{ + /* Save decimal exponent */ + e->dec_exponent = dec_exp; + + /* Initialize mantissa */ + e->f.mantissa = BitVector_Create(MANT_BITS, FALSE); + BitVector_Block_Store(e->f.mantissa, s->mantissa, MANT_BYTES); + + /* Initialize exponent */ + e->f.exponent = s->exponent; + + /* Set sign to 0 (positive) */ + e->f.sign = 0; + + /* Clear flags */ + e->f.flags = 0; +} + +/*@-compdef@*/ +void +yasm_floatnum_initialize(void) +/*@globals undef POT_TableN, undef POT_TableP, POT_TableP_Source, + POT_TableN_Source @*/ +{ + int dec_exp = 1; + int i; + + /* Allocate space for two POT tables */ + POT_TableN = yasm_xmalloc(14*sizeof(POT_Entry)); + POT_TableP = yasm_xmalloc(15*sizeof(POT_Entry)); /* note 1 extra for -1 */ + + /* Initialize entry[0..12] */ + for (i=12; i>=0; i--) { + POT_Table_Init_Entry(&POT_TableN[i], &POT_TableN_Source[i], 0-dec_exp); + POT_Table_Init_Entry(&POT_TableP[i+1], &POT_TableP_Source[i], dec_exp); + dec_exp *= 2; /* Update decimal exponent */ + } + + /* Initialize entry[13] */ + POT_Table_Init_Entry(&POT_TableN[13], &POT_TableN_Source[13], 0); + POT_Table_Init_Entry(&POT_TableP[14], &POT_TableP_Source[13], 0); + + /* Initialize entry[-1] for POT_TableP */ + POT_Table_Init_Entry(&POT_TableP[0], &POT_TableP_Source[0], 4096); + + /* Offset POT_TableP so that [0] becomes [-1] */ + POT_TableP++; +} +/*@=compdef@*/ + +/*@-globstate@*/ +void +yasm_floatnum_cleanup(void) +{ + int i; + + /* Un-offset POT_TableP */ + POT_TableP--; + + for (i=0; i<14; i++) { + BitVector_Destroy(POT_TableN[i].f.mantissa); + BitVector_Destroy(POT_TableP[i].f.mantissa); + } + BitVector_Destroy(POT_TableP[14].f.mantissa); + + yasm_xfree(POT_TableN); + yasm_xfree(POT_TableP); +} +/*@=globstate@*/ + +static void +floatnum_normalize(yasm_floatnum *flt) +{ + long norm_amt; + + if (BitVector_is_empty(flt->mantissa)) { + flt->exponent = 0; + return; + } + + /* Look for the highest set bit, shift to make it the MSB, and adjust + * exponent. Don't let exponent go negative. */ + norm_amt = (MANT_BITS-1)-Set_Max(flt->mantissa); + if (norm_amt > (long)flt->exponent) + norm_amt = (long)flt->exponent; + BitVector_Move_Left(flt->mantissa, (N_int)norm_amt); + flt->exponent -= (unsigned short)norm_amt; +} + +/* acc *= op */ +static void +floatnum_mul(yasm_floatnum *acc, const yasm_floatnum *op) +{ + long expon; + wordptr product, op1, op2; + long norm_amt; + + /* Compute the new sign */ + acc->sign ^= op->sign; + + /* Check for multiply by 0 */ + if (BitVector_is_empty(acc->mantissa) || BitVector_is_empty(op->mantissa)) { + BitVector_Empty(acc->mantissa); + acc->exponent = EXP_ZERO; + return; + } + + /* Add exponents, checking for overflow/underflow. */ + expon = (((int)acc->exponent)-EXP_BIAS) + (((int)op->exponent)-EXP_BIAS); + expon += EXP_BIAS; + if (expon > EXP_MAX) { + /* Overflow; return infinity. */ + BitVector_Empty(acc->mantissa); + acc->exponent = EXP_INF; + return; + } else if (expon < EXP_MIN) { + /* Underflow; return zero. */ + BitVector_Empty(acc->mantissa); + acc->exponent = EXP_ZERO; + return; + } + + /* Add one to the final exponent, as the multiply shifts one extra time. */ + acc->exponent = (unsigned short)(expon+1); + + /* Allocate space for the multiply result */ + product = BitVector_Create((N_int)((MANT_BITS+1)*2), FALSE); + + /* Allocate 1-bit-longer fields to force the operands to be unsigned */ + op1 = BitVector_Create((N_int)(MANT_BITS+1), FALSE); + op2 = BitVector_Create((N_int)(MANT_BITS+1), FALSE); + + /* Make the operands unsigned after copying from original operands */ + BitVector_Copy(op1, acc->mantissa); + BitVector_MSB(op1, 0); + BitVector_Copy(op2, op->mantissa); + BitVector_MSB(op2, 0); + + /* Compute the product of the mantissas */ + BitVector_Multiply(product, op1, op2); + + /* Normalize the product. Note: we know the product is non-zero because + * both of the original operands were non-zero. + * + * Look for the highest set bit, shift to make it the MSB, and adjust + * exponent. Don't let exponent go negative. + */ + norm_amt = (MANT_BITS*2-1)-Set_Max(product); + if (norm_amt > (long)acc->exponent) + norm_amt = (long)acc->exponent; + BitVector_Move_Left(product, (N_int)norm_amt); + acc->exponent -= (unsigned short)norm_amt; + + /* Store the highest bits of the result */ + BitVector_Interval_Copy(acc->mantissa, product, 0, MANT_BITS, MANT_BITS); + + /* Free allocated variables */ + BitVector_Destroy(product); + BitVector_Destroy(op1); + BitVector_Destroy(op2); +} + +yasm_floatnum * +yasm_floatnum_create(const char *str) +{ + yasm_floatnum *flt; + int dec_exponent, dec_exp_add; /* decimal (powers of 10) exponent */ + int POT_index; + wordptr operand[2]; + int sig_digits; + int decimal_pt; + boolean carry; + + flt = yasm_xmalloc(sizeof(yasm_floatnum)); + + flt->mantissa = BitVector_Create(MANT_BITS, TRUE); + + /* allocate and initialize calculation variables */ + operand[0] = BitVector_Create(MANT_BITS, TRUE); + operand[1] = BitVector_Create(MANT_BITS, TRUE); + dec_exponent = 0; + sig_digits = 0; + decimal_pt = 1; + + /* set initial flags to 0 */ + flt->flags = 0; + + /* check for + or - character and skip */ + if (*str == '-') { + flt->sign = 1; + str++; + } else if (*str == '+') { + flt->sign = 0; + str++; + } else + flt->sign = 0; + + /* eliminate any leading zeros (which do not count as significant digits) */ + while (*str == '0') + str++; + + /* When we reach the end of the leading zeros, first check for a decimal + * point. If the number is of the form "0---0.0000" we need to get rid + * of the zeros after the decimal point and not count them as significant + * digits. + */ + if (*str == '.') { + str++; + while (*str == '0') { + str++; + dec_exponent--; + } + } else { + /* The number is of the form "yyy.xxxx" (where y <> 0). */ + while (isdigit(*str)) { + /* See if we've processed more than the max significant digits: */ + if (sig_digits < MANT_SIGDIGITS) { + /* Multiply mantissa by 10 [x = (x<<1)+(x<<3)] */ + BitVector_shift_left(flt->mantissa, 0); + BitVector_Copy(operand[0], flt->mantissa); + BitVector_Move_Left(flt->mantissa, 2); + carry = 0; + BitVector_add(operand[1], operand[0], flt->mantissa, &carry); + + /* Add in current digit */ + BitVector_Empty(operand[0]); + BitVector_Chunk_Store(operand[0], 4, 0, (N_long)(*str-'0')); + carry = 0; + BitVector_add(flt->mantissa, operand[1], operand[0], &carry); + } else { + /* Can't integrate more digits with mantissa, so instead just + * raise by a power of ten. + */ + dec_exponent++; + } + sig_digits++; + str++; + } + + if (*str == '.') + str++; + else + decimal_pt = 0; + } + + if (decimal_pt) { + /* Process the digits to the right of the decimal point. */ + while (isdigit(*str)) { + /* See if we've processed more than 19 significant digits: */ + if (sig_digits < 19) { + /* Raise by a power of ten */ + dec_exponent--; + + /* Multiply mantissa by 10 [x = (x<<1)+(x<<3)] */ + BitVector_shift_left(flt->mantissa, 0); + BitVector_Copy(operand[0], flt->mantissa); + BitVector_Move_Left(flt->mantissa, 2); + carry = 0; + BitVector_add(operand[1], operand[0], flt->mantissa, &carry); + + /* Add in current digit */ + BitVector_Empty(operand[0]); + BitVector_Chunk_Store(operand[0], 4, 0, (N_long)(*str-'0')); + carry = 0; + BitVector_add(flt->mantissa, operand[1], operand[0], &carry); + } + sig_digits++; + str++; + } + } + + if (*str == 'e' || *str == 'E') { + str++; + /* We just saw the "E" character, now read in the exponent value and + * add it into dec_exponent. + */ + dec_exp_add = 0; + sscanf(str, "%d", &dec_exp_add); + dec_exponent += dec_exp_add; + } + + /* Free calculation variables. */ + BitVector_Destroy(operand[1]); + BitVector_Destroy(operand[0]); + + /* Normalize the number, checking for 0 first. */ + if (BitVector_is_empty(flt->mantissa)) { + /* Mantissa is 0, zero exponent too. */ + flt->exponent = 0; + /* Set zero flag so output functions don't see 0 value as underflow. */ + flt->flags |= FLAG_ISZERO; + /* Return 0 value. */ + return flt; + } + /* Exponent if already norm. */ + flt->exponent = (unsigned short)(0x7FFF+(MANT_BITS-1)); + floatnum_normalize(flt); + + /* The number is normalized. Now multiply by 10 the number of times + * specified in DecExponent. This uses the power of ten tables to speed + * up this operation (and make it more accurate). + */ + if (dec_exponent > 0) { + POT_index = 0; + /* Until we hit 1.0 or finish exponent or overflow */ + while ((POT_index < 14) && (dec_exponent != 0) && + (flt->exponent != EXP_INF)) { + /* Find the first power of ten in the table which is just less than + * the exponent. + */ + while (dec_exponent < POT_TableP[POT_index].dec_exponent) + POT_index++; + + if (POT_index < 14) { + /* Subtract out what we're multiplying in from exponent */ + dec_exponent -= POT_TableP[POT_index].dec_exponent; + + /* Multiply by current power of 10 */ + floatnum_mul(flt, &POT_TableP[POT_index].f); + } + } + } else if (dec_exponent < 0) { + POT_index = 0; + /* Until we hit 1.0 or finish exponent or underflow */ + while ((POT_index < 14) && (dec_exponent != 0) && + (flt->exponent != EXP_ZERO)) { + /* Find the first power of ten in the table which is just less than + * the exponent. + */ + while (dec_exponent > POT_TableN[POT_index].dec_exponent) + POT_index++; + + if (POT_index < 14) { + /* Subtract out what we're multiplying in from exponent */ + dec_exponent -= POT_TableN[POT_index].dec_exponent; + + /* Multiply by current power of 10 */ + floatnum_mul(flt, &POT_TableN[POT_index].f); + } + } + } + + /* Round the result. (Don't round underflow or overflow). Also don't + * increment if this would cause the mantissa to wrap. + */ + if ((flt->exponent != EXP_INF) && (flt->exponent != EXP_ZERO) && + !BitVector_is_full(flt->mantissa)) + BitVector_increment(flt->mantissa); + + return flt; +} + +yasm_floatnum * +yasm_floatnum_copy(const yasm_floatnum *flt) +{ + yasm_floatnum *f = yasm_xmalloc(sizeof(yasm_floatnum)); + + f->mantissa = BitVector_Clone(flt->mantissa); + f->exponent = flt->exponent; + f->sign = flt->sign; + f->flags = flt->flags; + + return f; +} + +void +yasm_floatnum_destroy(yasm_floatnum *flt) +{ + BitVector_Destroy(flt->mantissa); + yasm_xfree(flt); +} + +int +yasm_floatnum_calc(yasm_floatnum *acc, yasm_expr_op op, + /*@unused@*/ yasm_floatnum *operand) +{ + if (op != YASM_EXPR_NEG) { + yasm_error_set(YASM_ERROR_FLOATING_POINT, + N_("Unsupported floating-point arithmetic operation")); + return 1; + } + acc->sign ^= 1; + return 0; +} + +int +yasm_floatnum_get_int(const yasm_floatnum *flt, unsigned long *ret_val) +{ + unsigned char t[4]; + + if (yasm_floatnum_get_sized(flt, t, 4, 32, 0, 0, 0)) { + *ret_val = 0xDEADBEEFUL; /* Obviously incorrect return value */ + return 1; + } + + YASM_LOAD_32_L(*ret_val, &t[0]); + return 0; +} + +/* Function used by conversion routines to actually perform the conversion. + * + * ptr -> the array to return the little-endian floating point value into. + * flt -> the floating point value to convert. + * byte_size -> the size in bytes of the output format. + * mant_bits -> the size in bits of the output mantissa. + * implicit1 -> does the output format have an implicit 1? 1=yes, 0=no. + * exp_bits -> the size in bits of the output exponent. + * + * Returns 0 on success, 1 if overflow, -1 if underflow. + */ +static int +floatnum_get_common(const yasm_floatnum *flt, /*@out@*/ unsigned char *ptr, + N_int byte_size, N_int mant_bits, int implicit1, + N_int exp_bits) +{ + long exponent = (long)flt->exponent; + wordptr output; + charptr buf; + unsigned int len; + unsigned int overflow = 0, underflow = 0; + int retval = 0; + long exp_bias = (1<<(exp_bits-1))-1; + long exp_inf = (1<<exp_bits)-1; + + output = BitVector_Create(byte_size*8, TRUE); + + /* copy mantissa */ + BitVector_Interval_Copy(output, flt->mantissa, 0, + (N_int)((MANT_BITS-implicit1)-mant_bits), + mant_bits); + + /* round mantissa */ + if (BitVector_bit_test(flt->mantissa, (MANT_BITS-implicit1)-(mant_bits+1))) + BitVector_increment(output); + + if (BitVector_bit_test(output, mant_bits)) { + /* overflowed, so zero mantissa (and set explicit bit if necessary) */ + BitVector_Empty(output); + BitVector_Bit_Copy(output, mant_bits-1, !implicit1); + /* and up the exponent (checking for overflow) */ + if (exponent+1 >= EXP_INF) + overflow = 1; + else + exponent++; + } + + /* adjust the exponent to the output bias, checking for overflow */ + exponent -= EXP_BIAS-exp_bias; + if (exponent >= exp_inf) + overflow = 1; + else if (exponent <= 0) + underflow = 1; + + /* underflow and overflow both set!? */ + if (underflow && overflow) + yasm_internal_error(N_("Both underflow and overflow set")); + + /* check for underflow or overflow and set up appropriate output */ + if (underflow) { + BitVector_Empty(output); + exponent = 0; + if (!(flt->flags & FLAG_ISZERO)) + retval = -1; + } else if (overflow) { + BitVector_Empty(output); + exponent = exp_inf; + retval = 1; + } + + /* move exponent into place */ + BitVector_Chunk_Store(output, exp_bits, mant_bits, (N_long)exponent); + + /* merge in sign bit */ + BitVector_Bit_Copy(output, byte_size*8-1, flt->sign); + + /* get little-endian bytes */ + buf = BitVector_Block_Read(output, &len); + if (len < byte_size) + yasm_internal_error( + N_("Byte length of BitVector does not match bit length")); + + /* copy to output */ + memcpy(ptr, buf, byte_size*sizeof(unsigned char)); + + /* free allocated resources */ + yasm_xfree(buf); + + BitVector_Destroy(output); + + return retval; +} + +/* IEEE-754r "half precision" format: + * 16 bits: + * 15 9 Bit 0 + * | | | + * seee eemm mmmm mmmm + * + * e = bias 15 exponent + * s = sign bit + * m = mantissa bits, bit 10 is an implied one bit. + * + * IEEE-754 (Intel) "single precision" format: + * 32 bits: + * Bit 31 Bit 22 Bit 0 + * | | | + * seeeeeee emmmmmmm mmmmmmmm mmmmmmmm + * + * e = bias 127 exponent + * s = sign bit + * m = mantissa bits, bit 23 is an implied one bit. + * + * IEEE-754 (Intel) "double precision" format: + * 64 bits: + * bit 63 bit 51 bit 0 + * | | | + * seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm + * + * e = bias 1023 exponent. + * s = sign bit. + * m = mantissa bits. Bit 52 is an implied one bit. + * + * IEEE-754 (Intel) "extended precision" format: + * 80 bits: + * bit 79 bit 63 bit 0 + * | | | + * seeeeeee eeeeeeee mmmmmmmm m...m m...m m...m m...m m...m + * + * e = bias 16383 exponent + * m = 64 bit mantissa with NO implied bit! + * s = sign (for mantissa) + */ +int +yasm_floatnum_get_sized(const yasm_floatnum *flt, unsigned char *ptr, + size_t destsize, size_t valsize, size_t shift, + int bigendian, int warn) +{ + int retval; + if (destsize*8 != valsize || shift>0 || bigendian) { + /* TODO */ + yasm_internal_error(N_("unsupported floatnum functionality")); + } + switch (destsize) { + case 2: + retval = floatnum_get_common(flt, ptr, 2, 10, 1, 5); + break; + case 4: + retval = floatnum_get_common(flt, ptr, 4, 23, 1, 8); + break; + case 8: + retval = floatnum_get_common(flt, ptr, 8, 52, 1, 11); + break; + case 10: + retval = floatnum_get_common(flt, ptr, 10, 64, 0, 15); + break; + default: + yasm_internal_error(N_("Invalid float conversion size")); + /*@notreached@*/ + return 1; + } + if (warn) { + if (retval < 0) + yasm_warn_set(YASM_WARN_GENERAL, + N_("underflow in floating point expression")); + else if (retval > 0) + yasm_warn_set(YASM_WARN_GENERAL, + N_("overflow in floating point expression")); + } + return retval; +} + +/* 1 if the size is valid, 0 if it isn't */ +int +yasm_floatnum_check_size(/*@unused@*/ const yasm_floatnum *flt, size_t size) +{ + switch (size) { + case 16: + case 32: + case 64: + case 80: + return 1; + default: + return 0; + } +} + +void +yasm_floatnum_print(const yasm_floatnum *flt, FILE *f) +{ + unsigned char out[10]; + unsigned char *str; + int i; + + /* Internal format */ + str = BitVector_to_Hex(flt->mantissa); + fprintf(f, "%c %s *2^%04x\n", flt->sign?'-':'+', (char *)str, + flt->exponent); + yasm_xfree(str); + + /* 32-bit (single precision) format */ + fprintf(f, "32-bit: %d: ", + yasm_floatnum_get_sized(flt, out, 4, 32, 0, 0, 0)); + for (i=0; i<4; i++) + fprintf(f, "%02x ", out[i]); + fprintf(f, "\n"); + + /* 64-bit (double precision) format */ + fprintf(f, "64-bit: %d: ", + yasm_floatnum_get_sized(flt, out, 8, 64, 0, 0, 0)); + for (i=0; i<8; i++) + fprintf(f, "%02x ", out[i]); + fprintf(f, "\n"); + + /* 80-bit (extended precision) format */ + fprintf(f, "80-bit: %d: ", + yasm_floatnum_get_sized(flt, out, 10, 80, 0, 0, 0)); + for (i=0; i<10; i++) + fprintf(f, "%02x ", out[i]); + fprintf(f, "\n"); +} diff --git a/libyasm/floatnum.h b/libyasm/floatnum.h new file mode 100644 index 0000000..d2c7042 --- /dev/null +++ b/libyasm/floatnum.h @@ -0,0 +1,131 @@ +/** + * \file libyasm/floatnum.h + * \brief YASM floating point (IEEE) interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Based on public-domain x86 assembly code by Randall Hyde (8/28/91). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_FLOATNUM_H +#define YASM_FLOATNUM_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Initialize floatnum internal data structures. */ +YASM_LIB_DECL +void yasm_floatnum_initialize(void); + +/** Clean up internal floatnum allocations. */ +YASM_LIB_DECL +void yasm_floatnum_cleanup(void); + +/** Create a new floatnum from a decimal string. The input string must be in + * standard C representation ([+-]123.456e[-+]789). + * \param str floating point decimal string + * \return Newly allocated floatnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_floatnum *yasm_floatnum_create(const char *str); + +/** Duplicate a floatnum. + * \param flt floatnum + * \return Newly allocated floatnum with the same value as flt. + */ +YASM_LIB_DECL +/*@only@*/ yasm_floatnum *yasm_floatnum_copy(const yasm_floatnum *flt); + +/** Destroy (free allocated memory for) a floatnum. + * \param flt floatnum + */ +YASM_LIB_DECL +void yasm_floatnum_destroy(/*@only@*/ yasm_floatnum *flt); + +/** Floating point calculation function: acc = acc op operand. + * \note Not all operations in yasm_expr_op may be supported; unsupported + * operations will result in an error. + * \param acc floatnum accumulator + * \param op operation + * \param operand floatnum operand + * \return Nonzero on error. + */ +YASM_LIB_DECL +int yasm_floatnum_calc(yasm_floatnum *acc, yasm_expr_op op, + yasm_floatnum *operand); + +/** Convert a floatnum to single-precision and return as 32-bit value. + * The 32-bit value is a "standard" C value (eg, of unknown endian). + * \param flt floatnum + * \param ret_val pointer to storage for 32-bit output + * \return Nonzero if flt can't fit into single precision: -1 if underflow + * occurred, 1 if overflow occurred. + */ +YASM_LIB_DECL +int yasm_floatnum_get_int(const yasm_floatnum *flt, + /*@out@*/ unsigned long *ret_val); + +/** Output a #yasm_floatnum to buffer in little-endian or big-endian. Puts the + * value into the least significant bits of the destination, or may be shifted + * into more significant bits by the shift parameter. The destination bits are + * cleared before being set. [0] should be the first byte output to the file. + * \note Not all sizes are valid. Currently, only 32 (single-precision), 64 + * (double-precision), and 80 (extended-precision) are valid sizes. + * Use yasm_floatnum_check_size() to check for supported sizes. + * \param flt floatnum + * \param ptr pointer to storage for size bytes of output + * \param destsize destination size (in bytes) + * \param valsize size (in bits) + * \param shift left shift (in bits) + * \param bigendian endianness (nonzero=big, zero=little) + * \param warn enables standard overflow/underflow warnings + * \return Nonzero if flt can't fit into the specified precision: -1 if + * underflow occurred, 1 if overflow occurred. + */ +YASM_LIB_DECL +int yasm_floatnum_get_sized(const yasm_floatnum *flt, unsigned char *ptr, + size_t destsize, size_t valsize, size_t shift, + int bigendian, int warn); + +/** Basic check to see if size is valid for flt conversion (using + * yasm_floatnum_get_sized()). Doesn't actually check for underflow/overflow + * but rather checks for size=32,64,80 + * (at present). + * \param flt floatnum + * \param size number of bits of output space + * \return 1 if valid size, 0 if invalid size. + */ +YASM_LIB_DECL +int yasm_floatnum_check_size(const yasm_floatnum *flt, size_t size); + +/** Print various representations of a floatnum. For debugging purposes only. + * \param f file + * \param flt floatnum + */ +YASM_LIB_DECL +void yasm_floatnum_print(const yasm_floatnum *flt, FILE *f); + +#endif diff --git a/libyasm/genmodule.c b/libyasm/genmodule.c new file mode 100644 index 0000000..a9be08a --- /dev/null +++ b/libyasm/genmodule.c @@ -0,0 +1,229 @@ +/* + * + * Generate module.c from module.in and Makefile.am or Makefile. + * + * Copyright (C) 2004-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "compat-queue.h" + +#define MAXNAME 128 +#define MAXLINE 1024 +#define MAXMODULES 128 +#define MAXINCLUDES 256 + +typedef struct include { + STAILQ_ENTRY(include) link; + char *filename; +} include; + +int +main(int argc, char *argv[]) +{ + FILE *in, *out; + char *str; + int i; + size_t len; + char *strp; + char *modules[MAXMODULES]; + int num_modules = 0; + STAILQ_HEAD(includehead, include) includes = + STAILQ_HEAD_INITIALIZER(includes); + include *inc; + int isam = 0; + int linecont = 0; + char *outfile; + + if (argc != 4) { + fprintf(stderr, "Usage: %s <module.in> <Makefile[.am]> <outfile>\n", argv[0]); + return EXIT_FAILURE; + } + + outfile = argv[3]; + str = malloc(MAXLINE); + + /* Starting with initial input Makefile, look for include <file> or + * YASM_MODULES += <module>. Note this currently doesn't handle + * a relative starting path. + */ + len = strlen(argv[2]); + inc = malloc(sizeof(include)); + inc->filename = malloc(len+1); + strcpy(inc->filename, argv[2]); + STAILQ_INSERT_TAIL(&includes, inc, link); + + isam = argv[2][len-2] == 'a' && argv[2][len-1] == 'm'; + + while (!STAILQ_EMPTY(&includes)) { + inc = STAILQ_FIRST(&includes); + STAILQ_REMOVE_HEAD(&includes, link); + in = fopen(inc->filename, "rt"); + if (!in) { + fprintf(stderr, "Could not open `%s'.\n", inc->filename); + return EXIT_FAILURE; + } + free(inc->filename); + free(inc); + + while (fgets(str, MAXLINE, in)) { + /* Strip off any trailing whitespace */ + len = strlen(str); + if (len > 0) { + strp = &str[len-1]; + while (len > 0 && isspace(*strp)) { + *strp-- = '\0'; + len--; + } + } + + strp = str; + + /* Skip whitespace */ + while (isspace(*strp)) + strp++; + + /* Skip comments */ + if (*strp == '#') + continue; + + /* If line continuation, skip to continue copy */ + if (linecont) + goto keepgoing; + + /* Check for include if original input is .am file */ + if (isam && strncmp(strp, "include", 7) == 0 && isspace(strp[7])) { + strp += 7; + while (isspace(*strp)) + strp++; + /* Build new include and add to end of list */ + inc = malloc(sizeof(include)); + inc->filename = malloc(strlen(strp)+1); + strcpy(inc->filename, strp); + STAILQ_INSERT_TAIL(&includes, inc, link); + continue; + } + + /* Check for YASM_MODULES = or += */ + if (strncmp(strp, "YASM_MODULES", 12) != 0) + continue; + strp += 12; + while (isspace(*strp)) + strp++; + if (strncmp(strp, "+=", 2) != 0 && *strp != '=') + continue; + if (*strp == '+') + strp++; + strp++; + while (isspace(*strp)) + strp++; + +keepgoing: + /* Check for continuation */ + if (len > 0 && str[len-1] == '\\') { + str[len-1] = '\0'; + while (isspace(*strp)) + *strp-- = '\0'; + linecont = 1; + } else + linecont = 0; + + while (*strp != '\0') { + /* Copy module name */ + modules[num_modules] = malloc(MAXNAME); + len = 0; + while (*strp != '\0' && !isspace(*strp)) + modules[num_modules][len++] = *strp++; + modules[num_modules][len] = '\0'; + num_modules++; + + while (isspace(*strp)) + strp++; + } + } + fclose(in); + } + + out = fopen(outfile, "wt"); + + if (!out) { + fprintf(stderr, "Could not open `%s'.\n", outfile); + return EXIT_FAILURE; + } + + fprintf(out, "/* This file auto-generated by genmodule.c" + " - don't edit it */\n\n"); + + in = fopen(argv[1], "rt"); + if (!in) { + fprintf(stderr, "Could not open `%s'.\n", argv[1]); + fclose(out); + remove(outfile); + return EXIT_FAILURE; + } + + len = 0; + while (fgets(str, MAXLINE, in)) { + if (strncmp(str, "MODULES_", 8) == 0) { + len = 0; + strp = str+8; + while (*strp != '\0' && *strp != '_') { + len++; + strp++; + } + *strp = '\0'; + + for (i=0; i<num_modules; i++) { + if (strncmp(modules[i], str+8, len) == 0) { + fprintf(out, " {\"%s\", &yasm_%s_LTX_%s},\n", + modules[i]+len+1, modules[i]+len+1, str+8); + } + } + } else if (strncmp(str, "EXTERN_LIST", 11) == 0) { + for (i=0; i<num_modules; i++) { + strcpy(str, modules[i]); + strp = str; + while (*strp != '\0' && *strp != '_') + strp++; + *strp++ = '\0'; + + fprintf(out, "extern yasm_%s_module yasm_%s_LTX_%s;\n", + str, strp, str); + } + } else + fputs(str, out); + } + + fclose(in); + fclose(out); + + for (i=0; i<num_modules; i++) + free(modules[i]); + free(str); + + return EXIT_SUCCESS; +} diff --git a/libyasm/hamt.c b/libyasm/hamt.c new file mode 100644 index 0000000..59b7592 --- /dev/null +++ b/libyasm/hamt.c @@ -0,0 +1,421 @@ +/* + * Hash Array Mapped Trie (HAMT) implementation + * + * Copyright (C) 2001-2007 Peter Johnson + * + * Based on the paper "Ideal Hash Tries" by Phil Bagwell [2000]. + * One algorithmic change from that described in the paper: we use the LSB's + * of the key to index the root table and move upward in the key rather than + * use the MSBs as described in the paper. The LSBs have more entropy. + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include <ctype.h> + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "hamt.h" + +struct HAMTEntry { + STAILQ_ENTRY(HAMTEntry) next; /* next hash table entry */ + /*@dependent@*/ const char *str; /* string being hashed */ + /*@owned@*/ void *data; /* data pointer being stored */ +}; + +typedef struct HAMTNode { + unsigned long BitMapKey; /* 32 bits, bitmap or hash key */ + uintptr_t BaseValue; /* Base of HAMTNode list or value */ +} HAMTNode; + +struct HAMT { + STAILQ_HEAD(HAMTEntryHead, HAMTEntry) entries; + HAMTNode *root; + /*@exits@*/ void (*error_func) (const char *file, unsigned int line, + const char *message); + unsigned long (*HashKey) (const char *key); + unsigned long (*ReHashKey) (const char *key, int Level); + int (*CmpKey) (const char *s1, const char *s2); +}; + +/* XXX make a portable version of this. This depends on the pointer being + * 4 or 2-byte aligned (as it uses the LSB of the pointer variable to store + * the subtrie flag! + */ +#define IsSubTrie(n) ((n)->BaseValue & 1) +#define SetSubTrie(h, n, v) do { \ + if ((uintptr_t)(v) & 1) \ + h->error_func(__FILE__, __LINE__, \ + N_("Subtrie is seen as subtrie before flag is set (misaligned?)")); \ + (n)->BaseValue = (uintptr_t)(v) | 1; \ + } while (0) +#define SetValue(h, n, v) do { \ + if ((uintptr_t)(v) & 1) \ + h->error_func(__FILE__, __LINE__, \ + N_("Value is seen as subtrie (misaligned?)")); \ + (n)->BaseValue = (uintptr_t)(v); \ + } while (0) +#define GetSubTrie(n) (HAMTNode *)(((n)->BaseValue | 1) ^ 1) + +static unsigned long +HashKey(const char *key) +{ + unsigned long a=31415, b=27183, vHash; + for (vHash=0; *key; key++, a*=b) + vHash = a*vHash + *key; + return vHash; +} + +static unsigned long +ReHashKey(const char *key, int Level) +{ + unsigned long a=31415, b=27183, vHash; + for (vHash=0; *key; key++, a*=b) + vHash = a*vHash*(unsigned long)Level + *key; + return vHash; +} + +static unsigned long +HashKey_nocase(const char *key) +{ + unsigned long a=31415, b=27183, vHash; + for (vHash=0; *key; key++, a*=b) + vHash = a*vHash + tolower(*key); + return vHash; +} + +static unsigned long +ReHashKey_nocase(const char *key, int Level) +{ + unsigned long a=31415, b=27183, vHash; + for (vHash=0; *key; key++, a*=b) + vHash = a*vHash*(unsigned long)Level + tolower(*key); + return vHash; +} + +HAMT * +HAMT_create(int nocase, /*@exits@*/ void (*error_func) + (const char *file, unsigned int line, const char *message)) +{ + /*@out@*/ HAMT *hamt = yasm_xmalloc(sizeof(HAMT)); + int i; + + STAILQ_INIT(&hamt->entries); + hamt->root = yasm_xmalloc(32*sizeof(HAMTNode)); + + for (i=0; i<32; i++) { + hamt->root[i].BitMapKey = 0; + hamt->root[i].BaseValue = 0; + } + + hamt->error_func = error_func; + if (nocase) { + hamt->HashKey = HashKey_nocase; + hamt->ReHashKey = ReHashKey_nocase; + hamt->CmpKey = yasm__strcasecmp; + } else { + hamt->HashKey = HashKey; + hamt->ReHashKey = ReHashKey; + hamt->CmpKey = strcmp; + } + + return hamt; +} + +static void +HAMT_delete_trie(HAMTNode *node) +{ + if (IsSubTrie(node)) { + unsigned long i, Size; + + /* Count total number of bits in bitmap to determine size */ + BitCount(Size, node->BitMapKey); + Size &= 0x1F; + if (Size == 0) + Size = 32; + + for (i=0; i<Size; i++) + HAMT_delete_trie(&(GetSubTrie(node))[i]); + yasm_xfree(GetSubTrie(node)); + } +} + +void +HAMT_destroy(HAMT *hamt, void (*deletefunc) (/*@only@*/ void *data)) +{ + int i; + + /* delete entries */ + while (!STAILQ_EMPTY(&hamt->entries)) { + HAMTEntry *entry; + entry = STAILQ_FIRST(&hamt->entries); + STAILQ_REMOVE_HEAD(&hamt->entries, next); + deletefunc(entry->data); + yasm_xfree(entry); + } + + /* delete trie */ + for (i=0; i<32; i++) + HAMT_delete_trie(&hamt->root[i]); + + yasm_xfree(hamt->root); + yasm_xfree(hamt); +} + +int +HAMT_traverse(HAMT *hamt, void *d, + int (*func) (/*@dependent@*/ /*@null@*/ void *node, + /*@null@*/ void *d)) +{ + HAMTEntry *entry; + STAILQ_FOREACH(entry, &hamt->entries, next) { + int retval = func(entry->data, d); + if (retval != 0) + return retval; + } + return 0; +} + +const HAMTEntry * +HAMT_first(const HAMT *hamt) +{ + return STAILQ_FIRST(&hamt->entries); +} + +const HAMTEntry * +HAMT_next(const HAMTEntry *prev) +{ + return STAILQ_NEXT(prev, next); +} + +void * +HAMTEntry_get_data(const HAMTEntry *entry) +{ + return entry->data; +} + +/*@-temptrans -kepttrans -mustfree@*/ +void * +HAMT_insert(HAMT *hamt, const char *str, void *data, int *replace, + void (*deletefunc) (/*@only@*/ void *data)) +{ + HAMTNode *node, *newnodes; + HAMTEntry *entry; + unsigned long key, keypart, Map; + int keypartbits = 0; + int level = 0; + + key = hamt->HashKey(str); + keypart = key & 0x1F; + node = &hamt->root[keypart]; + + if (!node->BaseValue) { + node->BitMapKey = key; + entry = yasm_xmalloc(sizeof(HAMTEntry)); + entry->str = str; + entry->data = data; + STAILQ_INSERT_TAIL(&hamt->entries, entry, next); + SetValue(hamt, node, entry); + if (IsSubTrie(node)) + hamt->error_func(__FILE__, __LINE__, + N_("Data is seen as subtrie (misaligned?)")); + *replace = 1; + return data; + } + + for (;;) { + if (!(IsSubTrie(node))) { + if (node->BitMapKey == key + && hamt->CmpKey(((HAMTEntry *)(node->BaseValue))->str, + str) == 0) { + /*@-branchstate@*/ + if (*replace) { + deletefunc(((HAMTEntry *)(node->BaseValue))->data); + ((HAMTEntry *)(node->BaseValue))->str = str; + ((HAMTEntry *)(node->BaseValue))->data = data; + } else + deletefunc(data); + /*@=branchstate@*/ + return ((HAMTEntry *)(node->BaseValue))->data; + } else { + unsigned long key2 = node->BitMapKey; + /* build tree downward until keys differ */ + for (;;) { + unsigned long keypart2; + + /* replace node with subtrie */ + keypartbits += 5; + if (keypartbits > 30) { + /* Exceeded 32 bits: rehash */ + key = hamt->ReHashKey(str, level); + key2 = hamt->ReHashKey( + ((HAMTEntry *)(node->BaseValue))->str, level); + keypartbits = 0; + } + keypart = (key >> keypartbits) & 0x1F; + keypart2 = (key2 >> keypartbits) & 0x1F; + + if (keypart == keypart2) { + /* Still equal, build one-node subtrie and continue + * downward. + */ + newnodes = yasm_xmalloc(sizeof(HAMTNode)); + newnodes[0].BitMapKey = key2; + newnodes[0].BaseValue = node->BaseValue; + node->BitMapKey = 1<<keypart; + SetSubTrie(hamt, node, newnodes); + node = &newnodes[0]; + level++; + } else { + /* partitioned: allocate two-node subtrie */ + newnodes = yasm_xmalloc(2*sizeof(HAMTNode)); + + entry = yasm_xmalloc(sizeof(HAMTEntry)); + entry->str = str; + entry->data = data; + STAILQ_INSERT_TAIL(&hamt->entries, entry, next); + + /* Copy nodes into subtrie based on order */ + if (keypart2 < keypart) { + newnodes[0].BitMapKey = key2; + newnodes[0].BaseValue = node->BaseValue; + newnodes[1].BitMapKey = key; + SetValue(hamt, &newnodes[1], entry); + } else { + newnodes[0].BitMapKey = key; + SetValue(hamt, &newnodes[0], entry); + newnodes[1].BitMapKey = key2; + newnodes[1].BaseValue = node->BaseValue; + } + + /* Set bits in bitmap corresponding to keys */ + node->BitMapKey = (1UL<<keypart) | (1UL<<keypart2); + SetSubTrie(hamt, node, newnodes); + *replace = 1; + return data; + } + } + } + } + + /* Subtrie: look up in bitmap */ + keypartbits += 5; + if (keypartbits > 30) { + /* Exceeded 32 bits of current key: rehash */ + key = hamt->ReHashKey(str, level); + keypartbits = 0; + } + keypart = (key >> keypartbits) & 0x1F; + if (!(node->BitMapKey & (1<<keypart))) { + /* bit is 0 in bitmap -> add node to table */ + unsigned long Size; + + /* set bit to 1 */ + node->BitMapKey |= 1<<keypart; + + /* Count total number of bits in bitmap to determine new size */ + BitCount(Size, node->BitMapKey); + Size &= 0x1F; + if (Size == 0) + Size = 32; + newnodes = yasm_xmalloc(Size*sizeof(HAMTNode)); + + /* Count bits below to find where to insert new node at */ + BitCount(Map, node->BitMapKey & ~((~0UL)<<keypart)); + Map &= 0x1F; /* Clamp to <32 */ + /* Copy existing nodes leaving gap for new node */ + memcpy(newnodes, GetSubTrie(node), Map*sizeof(HAMTNode)); + memcpy(&newnodes[Map+1], &(GetSubTrie(node))[Map], + (Size-Map-1)*sizeof(HAMTNode)); + /* Delete old subtrie */ + yasm_xfree(GetSubTrie(node)); + /* Set up new node */ + newnodes[Map].BitMapKey = key; + entry = yasm_xmalloc(sizeof(HAMTEntry)); + entry->str = str; + entry->data = data; + STAILQ_INSERT_TAIL(&hamt->entries, entry, next); + SetValue(hamt, &newnodes[Map], entry); + SetSubTrie(hamt, node, newnodes); + + *replace = 1; + return data; + } + + /* Count bits below */ + BitCount(Map, node->BitMapKey & ~((~0UL)<<keypart)); + Map &= 0x1F; /* Clamp to <32 */ + + /* Go down a level */ + level++; + node = &(GetSubTrie(node))[Map]; + } +} +/*@=temptrans =kepttrans =mustfree@*/ + +void * +HAMT_search(HAMT *hamt, const char *str) +{ + HAMTNode *node; + unsigned long key, keypart, Map; + int keypartbits = 0; + int level = 0; + + key = hamt->HashKey(str); + keypart = key & 0x1F; + node = &hamt->root[keypart]; + + if (!node->BaseValue) + return NULL; + + for (;;) { + if (!(IsSubTrie(node))) { + if (node->BitMapKey == key + && hamt->CmpKey(((HAMTEntry *)(node->BaseValue))->str, + str) == 0) + return ((HAMTEntry *)(node->BaseValue))->data; + else + return NULL; + } + + /* Subtree: look up in bitmap */ + keypartbits += 5; + if (keypartbits > 30) { + /* Exceeded 32 bits of current key: rehash */ + key = hamt->ReHashKey(str, level); + keypartbits = 0; + } + keypart = (key >> keypartbits) & 0x1F; + if (!(node->BitMapKey & (1<<keypart))) + return NULL; /* bit is 0 in bitmap -> no match */ + + /* Count bits below */ + BitCount(Map, node->BitMapKey & ~((~0UL)<<keypart)); + Map &= 0x1F; /* Clamp to <32 */ + + /* Go down a level */ + level++; + node = &(GetSubTrie(node))[Map]; + } +} + diff --git a/libyasm/hamt.h b/libyasm/hamt.h new file mode 100644 index 0000000..1ce9b77 --- /dev/null +++ b/libyasm/hamt.h @@ -0,0 +1,123 @@ +/** + * \file libyasm/hamt.h + * \brief Hash Array Mapped Trie (HAMT) functions. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_HAMT_H +#define YASM_HAMT_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Hash array mapped trie data structure (opaque type). */ +typedef struct HAMT HAMT; +/** Hash array mapped trie entry (opaque type). */ +typedef struct HAMTEntry HAMTEntry; + +/** Create new, empty, HAMT. error_func() is called when an internal error is + * encountered--it should NOT return to the calling function. + * \param nocase nonzero if HAMT should be case-insensitive + * \param error_func function called on internal error + * \return New, empty, hash array mapped trie. + */ +YASM_LIB_DECL +HAMT *HAMT_create(int nocase, /*@exits@*/ void (*error_func) + (const char *file, unsigned int line, const char *message)); + +/** Delete HAMT and all data associated with it. Uses deletefunc() to delete + * each data item. + * \param hamt Hash array mapped trie + * \param deletefunc Data deletion function + */ +YASM_LIB_DECL +void HAMT_destroy(/*@only@*/ HAMT *hamt, + void (*deletefunc) (/*@only@*/ void *data)); + +/** Insert key into HAMT, associating it with data. + * If the key is not present in the HAMT, inserts it, sets *replace to 1, and + * returns the data passed in. + * If the key is already present and *replace is 0, deletes the data passed + * in using deletefunc() and returns the data currently associated with the + * key. + * If the key is already present and *replace is 1, deletes the data currently + * associated with the key using deletefunc() and replaces it with the data + * passed in. + * \param hamt Hash array mapped trie + * \param str Key + * \param data Data to associate with key + * \param replace See above description + * \param deletefunc Data deletion function if data is replaced + * \return Data now associated with key. + */ +YASM_LIB_DECL +/*@dependent@*/ void *HAMT_insert(HAMT *hamt, /*@dependent@*/ const char *str, + /*@only@*/ void *data, int *replace, + void (*deletefunc) (/*@only@*/ void *data)); + +/** Search for the data associated with a key in the HAMT. + * \param hamt Hash array mapped trie + * \param str Key + * \return NULL if key/data not present in HAMT, otherwise associated data. + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ void *HAMT_search(HAMT *hamt, const char *str); + +/** Traverse over all keys in HAMT, calling function on each data item. + * \param hamt Hash array mapped trie + * \param d Data to pass to each call to func. + * \param func Function to call + * \return Stops early (and returns func's return value) if func returns a + * nonzero value; otherwise 0. + */ +YASM_LIB_DECL +int HAMT_traverse(HAMT *hamt, /*@null@*/ void *d, + int (*func) (/*@dependent@*/ /*@null@*/ void *node, + /*@null@*/ void *d)); + +/** Get the first entry in a HAMT. + * \param hamt Hash array mapped trie + * \return First entry in HAMT, or NULL if HAMT is empty. + */ +YASM_LIB_DECL +const HAMTEntry *HAMT_first(const HAMT *hamt); + +/** Get the next entry in a HAMT. + * \param prev Previous entry in HAMT + * \return Next entry in HAMT, or NULL if no more entries. + */ +YASM_LIB_DECL +/*@null@*/ const HAMTEntry *HAMT_next(const HAMTEntry *prev); + +/** Get the corresponding data for a HAMT entry. + * \param entry HAMT entry (as returned by HAMT_first() and HAMT_next()) + * \return Corresponding data item. + */ +YASM_LIB_DECL +void *HAMTEntry_get_data(const HAMTEntry *entry); + +#endif diff --git a/libyasm/insn.c b/libyasm/insn.c new file mode 100644 index 0000000..8f7a4c1 --- /dev/null +++ b/libyasm/insn.c @@ -0,0 +1,295 @@ +/* + * Mnemonic instruction bytecode + * + * Copyright (C) 2005-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "errwarn.h" +#include "expr.h" +#include "value.h" + +#include "bytecode.h" +#include "insn.h" +#include "arch.h" + + +void +yasm_ea_set_segreg(yasm_effaddr *ea, uintptr_t segreg) +{ + if (!ea) + return; + + if (segreg != 0 && ea->segreg != 0) + yasm_warn_set(YASM_WARN_GENERAL, + N_("multiple segment overrides, using leftmost")); + + ea->segreg = segreg; +} + +yasm_insn_operand * +yasm_operand_create_reg(uintptr_t reg) +{ + yasm_insn_operand *retval = yasm_xmalloc(sizeof(yasm_insn_operand)); + + retval->type = YASM_INSN__OPERAND_REG; + retval->data.reg = reg; + retval->seg = 0; + retval->targetmod = 0; + retval->size = 0; + retval->deref = 0; + retval->strict = 0; + + return retval; +} + +yasm_insn_operand * +yasm_operand_create_segreg(uintptr_t segreg) +{ + yasm_insn_operand *retval = yasm_xmalloc(sizeof(yasm_insn_operand)); + + retval->type = YASM_INSN__OPERAND_SEGREG; + retval->data.reg = segreg; + retval->seg = 0; + retval->targetmod = 0; + retval->size = 0; + retval->deref = 0; + retval->strict = 0; + + return retval; +} + +yasm_insn_operand * +yasm_operand_create_mem(/*@only@*/ yasm_effaddr *ea) +{ + yasm_insn_operand *retval = yasm_xmalloc(sizeof(yasm_insn_operand)); + + retval->type = YASM_INSN__OPERAND_MEMORY; + retval->data.ea = ea; + retval->seg = 0; + retval->targetmod = 0; + retval->size = 0; + retval->deref = 0; + retval->strict = 0; + retval->size = ea->data_len * 8; + + return retval; +} + +yasm_insn_operand * +yasm_operand_create_imm(/*@only@*/ yasm_expr *val) +{ + yasm_insn_operand *retval; + const uintptr_t *reg; + + reg = yasm_expr_get_reg(&val, 0); + if (reg) { + retval = yasm_operand_create_reg(*reg); + yasm_expr_destroy(val); + } else { + retval = yasm_xmalloc(sizeof(yasm_insn_operand)); + retval->type = YASM_INSN__OPERAND_IMM; + retval->data.val = val; + retval->seg = 0; + retval->targetmod = 0; + retval->size = 0; + retval->deref = 0; + retval->strict = 0; + } + + return retval; +} + +yasm_insn_operand * +yasm_insn_ops_append(yasm_insn *insn, yasm_insn_operand *op) +{ + if (op) { + insn->num_operands++; + STAILQ_INSERT_TAIL(&insn->operands, op, link); + return op; + } + return (yasm_insn_operand *)NULL; +} + +void +yasm_insn_add_prefix(yasm_insn *insn, uintptr_t prefix) +{ + insn->prefixes = + yasm_xrealloc(insn->prefixes, + (insn->num_prefixes+1)*sizeof(uintptr_t)); + insn->prefixes[insn->num_prefixes] = prefix; + insn->num_prefixes++; +} + +void +yasm_insn_add_seg_prefix(yasm_insn *insn, uintptr_t segreg) +{ + insn->segregs = + yasm_xrealloc(insn->segregs, (insn->num_segregs+1)*sizeof(uintptr_t)); + insn->segregs[insn->num_segregs] = segreg; + insn->num_segregs++; +} + +void +yasm_insn_initialize(yasm_insn *insn) +{ + STAILQ_INIT(&insn->operands); + + insn->prefixes = NULL; + insn->segregs = NULL; + + insn->num_operands = 0; + insn->num_prefixes = 0; + insn->num_segregs = 0; +} + +void +yasm_insn_delete(yasm_insn *insn, + void (*ea_destroy) (/*@only@*/ yasm_effaddr *)) +{ + if (insn->num_operands > 0) { + yasm_insn_operand *cur, *next; + + cur = STAILQ_FIRST(&insn->operands); + while (cur) { + next = STAILQ_NEXT(cur, link); + switch (cur->type) { + case YASM_INSN__OPERAND_MEMORY: + ea_destroy(cur->data.ea); + break; + case YASM_INSN__OPERAND_IMM: + yasm_expr_destroy(cur->data.val); + break; + default: + break; + } + yasm_xfree(cur); + cur = next; + } + } + if (insn->num_prefixes > 0) + yasm_xfree(insn->prefixes); + if (insn->num_segregs > 0) + yasm_xfree(insn->segregs); +} + +void +yasm_insn_print(const yasm_insn *insn, FILE *f, int indent_level) +{ + const yasm_insn_operand *op; + + STAILQ_FOREACH (op, &insn->operands, link) { + switch (op->type) { + case YASM_INSN__OPERAND_REG: + fprintf(f, "%*sReg=", indent_level, ""); + /*yasm_arch_reg_print(arch, op->data.reg, f);*/ + fprintf(f, "\n"); + break; + case YASM_INSN__OPERAND_SEGREG: + fprintf(f, "%*sSegReg=", indent_level, ""); + /*yasm_arch_segreg_print(arch, op->data.reg, f);*/ + fprintf(f, "\n"); + break; + case YASM_INSN__OPERAND_MEMORY: + fprintf(f, "%*sMemory=\n", indent_level, ""); + /*yasm_arch_ea_print(arch, op->data.ea, f, indent_level);*/ + break; + case YASM_INSN__OPERAND_IMM: + fprintf(f, "%*sImm=", indent_level, ""); + yasm_expr_print(op->data.val, f); + fprintf(f, "\n"); + break; + } + fprintf(f, "%*sTargetMod=%lx\n", indent_level+1, "", + (unsigned long)op->targetmod); + fprintf(f, "%*sSize=%u\n", indent_level+1, "", op->size); + fprintf(f, "%*sDeref=%d, Strict=%d\n", indent_level+1, "", + (int)op->deref, (int)op->strict); + } +} + +void +yasm_insn_finalize(yasm_insn *insn) +{ + unsigned int i; + yasm_insn_operand *op; + yasm_error_class eclass; + char *str, *xrefstr; + unsigned long xrefline; + + /* Simplify the operands' expressions first. */ + for (i = 0, op = yasm_insn_ops_first(insn); + op && i<insn->num_operands; op = yasm_insn_op_next(op), i++) { + /* Check operand type */ + switch (op->type) { + case YASM_INSN__OPERAND_MEMORY: + /* Don't get over-ambitious here; some archs' memory expr + * parser are sensitive to the presence of *1, etc, so don't + * simplify reg*1 identities. + */ + if (op->data.ea) + op->data.ea->disp.abs = + yasm_expr__level_tree(op->data.ea->disp.abs, 1, 1, 0, + 0, NULL, NULL); + if (yasm_error_occurred()) { + /* Add a pointer to where it was used to the error */ + yasm_error_fetch(&eclass, &str, &xrefline, &xrefstr); + if (xrefstr) { + yasm_error_set_xref(xrefline, "%s", xrefstr); + yasm_xfree(xrefstr); + } + if (str) { + yasm_error_set(eclass, "%s in memory expression", str); + yasm_xfree(str); + } + return; + } + break; + case YASM_INSN__OPERAND_IMM: + op->data.val = + yasm_expr__level_tree(op->data.val, 1, 1, 1, 0, NULL, + NULL); + if (yasm_error_occurred()) { + /* Add a pointer to where it was used to the error */ + yasm_error_fetch(&eclass, &str, &xrefline, &xrefstr); + if (xrefstr) { + yasm_error_set_xref(xrefline, "%s", xrefstr); + yasm_xfree(xrefstr); + } + if (str) { + yasm_error_set(eclass, "%s in immediate expression", + str); + yasm_xfree(str); + } + return; + } + break; + default: + break; + } + } +} diff --git a/libyasm/insn.h b/libyasm/insn.h new file mode 100644 index 0000000..d2d175d --- /dev/null +++ b/libyasm/insn.h @@ -0,0 +1,269 @@ +/** + * \file libyasm/insn.h + * \brief YASM mnenomic instruction. + * + * \license + * Copyright (C) 2002-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_INSN_H +#define YASM_INSN_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Base structure for an effective address. As with all base + * structures, must be present as the first element in any + * #yasm_arch implementation of an effective address. + */ +struct yasm_effaddr { + yasm_value disp; /**< address displacement */ + + /** Segment register override (0 if none). */ + uintptr_t segreg; + + /** 1 if length of disp must be >0. */ + unsigned int need_nonzero_len:1; + + /** 1 if a displacement should be present in the output. */ + unsigned int need_disp:1; + + /** 1 if reg*2 should not be split into reg+reg. (0 if not). + * This flag indicates (for architectures that support complex effective + * addresses such as x86) if various types of complex effective addresses + * can be split into different forms in order to minimize instruction + * length. + */ + unsigned int nosplit:1; + + /** 1 if effective address is /definitely/ an effective address. + * This is used in e.g. the GAS parser to differentiate + * between "expr" (which might or might not be an effective address) and + * "expr(,1)" (which is definitely an effective address). + */ + unsigned int strong:1; + + /** 1 if effective address is forced PC-relative. */ + unsigned int pc_rel:1; + + /** 1 if effective address is forced non-PC-relative. */ + unsigned int not_pc_rel:1; + + /** length of pointed data (in bytes), 0 if unknown. */ + unsigned int data_len; +}; + +/** An instruction operand (opaque type). */ +typedef struct yasm_insn_operand yasm_insn_operand; + +/** The type of an instruction operand. */ +typedef enum yasm_insn_operand_type { + YASM_INSN__OPERAND_REG = 1, /**< A register. */ + YASM_INSN__OPERAND_SEGREG, /**< A segment register. */ + YASM_INSN__OPERAND_MEMORY, /**< An effective address + * (memory reference). */ + YASM_INSN__OPERAND_IMM /**< An immediate or jump target. */ +} yasm_insn_operand_type; + +/** An instruction operand. */ +struct yasm_insn_operand { + /** Link for building linked list of operands. \internal */ + /*@reldef@*/ STAILQ_ENTRY(yasm_insn_operand) link; + + /** Operand data. */ + union { + uintptr_t reg; /**< Arch data for reg/segreg. */ + yasm_effaddr *ea; /**< Effective address for memory references. */ + yasm_expr *val; /**< Value of immediate or jump target. */ + } data; + + yasm_expr *seg; /**< Segment expression */ + + uintptr_t targetmod; /**< Arch target modifier, 0 if none. */ + + /** Specified size of the operand, in bits. 0 if not user-specified. */ + unsigned int size:16; + + /** Nonzero if dereference. Used for "*foo" in GAS. + * The reason for this is that by default in GAS, an unprefixed value + * is a memory address, except for jumps/calls, in which case it needs a + * "*" prefix to become a memory address (otherwise it's an immediate). + * This isn't knowable in the parser stage, so the parser sets this flag + * to indicate the "*" prefix has been used, and the arch needs to adjust + * the operand type appropriately depending on the instruction type. + */ + unsigned int deref:1; + + /** Nonzero if strict. Used for "strict foo" in NASM. + * This is used to inhibit optimization on otherwise "sized" values. + * For example, the user may just want to be explicit with the size on + * "push dword 4", but not actually want to force the immediate size to + * 4 bytes (rather wanting the optimizer to optimize it down to 1 byte as + * though "dword" was not specified). To indicate the immediate should + * actually be forced to 4 bytes, the user needs to write + * "push strict dword 4", which sets this flag. + */ + unsigned int strict:1; + + /** Operand type. */ + unsigned int type:4; +}; + +/** Base structure for "instruction" bytecodes. These are the mnenomic + * (rather than raw) representation of instructions. As with all base + * structures, must be present as the first element in any + * #yasm_arch implementation of mnenomic instruction bytecodes. + */ +struct yasm_insn { + /** Linked list of operands. */ + /*@reldef@*/ STAILQ_HEAD(yasm_insn_operands, yasm_insn_operand) operands; + + /** Array of prefixes. */ + /*@null@*/ uintptr_t *prefixes; + + /** Array of segment prefixes. */ + /*@null@*/ uintptr_t *segregs; + + unsigned int num_operands; /**< Number of operands. */ + unsigned int num_prefixes; /**< Number of prefixes. */ + unsigned int num_segregs; /**< Number of segment prefixes. */ +}; + +/** Set segment override for an effective address. + * Some architectures (such as x86) support segment overrides on effective + * addresses. A override of an override will result in a warning. + * \param ea effective address + * \param segreg segment register (0 if none) + */ +YASM_LIB_DECL +void yasm_ea_set_segreg(yasm_effaddr *ea, uintptr_t segreg); + +/** Create an instruction operand from a register. + * \param reg register + * \return Newly allocated operand. + */ +YASM_LIB_DECL +yasm_insn_operand *yasm_operand_create_reg(uintptr_t reg); + +/** Create an instruction operand from a segment register. + * \param segreg segment register + * \return Newly allocated operand. + */ +YASM_LIB_DECL +yasm_insn_operand *yasm_operand_create_segreg(uintptr_t segreg); + +/** Create an instruction operand from an effective address. + * \param ea effective address + * \return Newly allocated operand. + */ +YASM_LIB_DECL +yasm_insn_operand *yasm_operand_create_mem(/*@only@*/ yasm_effaddr *ea); + +/** Create an instruction operand from an immediate expression. + * Looks for cases of a single register and creates a register variant of + * #yasm_insn_operand. + * \param val immediate expression + * \return Newly allocated operand. + */ +YASM_LIB_DECL +yasm_insn_operand *yasm_operand_create_imm(/*@only@*/ yasm_expr *val); + +/** Get the first operand in an instruction. + * \param insn instruction + * \return First operand (NULL if no operands). + */ +yasm_insn_operand *yasm_insn_ops_first(yasm_insn *insn); +#define yasm_insn_ops_first(insn) STAILQ_FIRST(&((insn)->operands)) + +/** Get the next operand in an instruction. + * \param op previous operand + * \return Next operand (NULL if op was the last operand). + */ +yasm_insn_operand *yasm_insn_op_next(yasm_insn_operand *op); +#define yasm_insn_op_next(cur) STAILQ_NEXT(cur, link) + +/** Add operand to the end of an instruction. + * \note Does not make a copy of the operand; so don't pass this function + * static or local variables, and discard the op pointer after calling + * this function. + * \param insn instruction + * \param op operand (may be NULL) + * \return If operand was actually appended (it wasn't NULL), the operand; + * otherwise NULL. + */ +YASM_LIB_DECL +/*@null@*/ yasm_insn_operand *yasm_insn_ops_append + (yasm_insn *insn, + /*@returned@*/ /*@null@*/ yasm_insn_operand *op); + +/** Associate a prefix with an instruction. + * \param insn instruction + * \param prefix data that identifies the prefix + */ +YASM_LIB_DECL +void yasm_insn_add_prefix(yasm_insn *insn, uintptr_t prefix); + +/** Associate a segment prefix with an instruction. + * \param insn instruction + * \param segreg data that identifies the segment register + */ +YASM_LIB_DECL +void yasm_insn_add_seg_prefix(yasm_insn *insn, uintptr_t segreg); + +/** Initialize the common parts of an instruction. + * \internal For use by yasm_arch implementations only. + * \param insn instruction + */ +YASM_LIB_DECL +void yasm_insn_initialize(/*@out@*/ yasm_insn *insn); + +/** Delete the common parts of an instruction. + * \internal For use by yasm_arch implementations only. + * \param insn instruction + * \param content if nonzero, deletes content of each operand + * \param arch architecture + */ +YASM_LIB_DECL +void yasm_insn_delete(yasm_insn *insn, + void (*ea_destroy) (/*@only@*/ yasm_effaddr *)); + +/** Print a list of instruction operands. For debugging purposes. + * \internal For use by yasm_arch implementations only. + * \param insn instruction + * \param f file + * \param indent_level indentation level + * \param arch architecture + */ +YASM_LIB_DECL +void yasm_insn_print(const yasm_insn *insn, FILE *f, int indent_level); + +/** Finalize the common parts of an instruction. + * \internal For use by yasm_arch implementations only. + * \param insn instruction + */ +YASM_LIB_DECL +void yasm_insn_finalize(yasm_insn *insn); + +#endif diff --git a/libyasm/intnum.c b/libyasm/intnum.c new file mode 100644 index 0000000..6feba33 --- /dev/null +++ b/libyasm/intnum.c @@ -0,0 +1,1096 @@ +/* + * Integer number functions. + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include <ctype.h> +#include <limits.h> + +#include "coretype.h" +#include "bitvect.h" +#include "file.h" + +#include "errwarn.h" +#include "intnum.h" + + +/* "Native" "word" size for intnum calculations. */ +#define BITVECT_NATIVE_SIZE 256 + +struct yasm_intnum { + union val { + long l; /* integer value (for integers <32 bits) */ + wordptr bv; /* bit vector (for integers >=32 bits) */ + } val; + enum { INTNUM_L, INTNUM_BV } type; +}; + +/* static bitvect used for conversions */ +static /*@only@*/ wordptr conv_bv; + +/* static bitvects used for computation */ +static /*@only@*/ wordptr result, spare, op1static, op2static; + +static /*@only@*/ BitVector_from_Dec_static_data *from_dec_data; + + +void +yasm_intnum_initialize(void) +{ + conv_bv = BitVector_Create(BITVECT_NATIVE_SIZE, FALSE); + result = BitVector_Create(BITVECT_NATIVE_SIZE, FALSE); + spare = BitVector_Create(BITVECT_NATIVE_SIZE, FALSE); + op1static = BitVector_Create(BITVECT_NATIVE_SIZE, FALSE); + op2static = BitVector_Create(BITVECT_NATIVE_SIZE, FALSE); + from_dec_data = BitVector_from_Dec_static_Boot(BITVECT_NATIVE_SIZE); +} + +void +yasm_intnum_cleanup(void) +{ + BitVector_from_Dec_static_Shutdown(from_dec_data); + BitVector_Destroy(op2static); + BitVector_Destroy(op1static); + BitVector_Destroy(spare); + BitVector_Destroy(result); + BitVector_Destroy(conv_bv); +} + +/* Compress a bitvector into intnum storage. + * If saved as a bitvector, clones the passed bitvector. + * Can modify the passed bitvector. + */ +static void +intnum_frombv(/*@out@*/ yasm_intnum *intn, wordptr bv) +{ + if (Set_Max(bv) < 31) { + intn->type = INTNUM_L; + intn->val.l = (long)BitVector_Chunk_Read(bv, 31, 0); + } else if (BitVector_msb_(bv)) { + /* Negative, negate and see if we'll fit into a long. */ + unsigned long ul; + BitVector_Negate(bv, bv); + if (Set_Max(bv) >= 32 || + ((ul = BitVector_Chunk_Read(bv, 32, 0)) & 0x80000000)) { + /* too negative */ + BitVector_Negate(bv, bv); + intn->type = INTNUM_BV; + intn->val.bv = BitVector_Clone(bv); + } else { + intn->type = INTNUM_L; + intn->val.l = -((long)ul); + } + } else { + intn->type = INTNUM_BV; + intn->val.bv = BitVector_Clone(bv); + } +} + +/* If intnum is a BV, returns its bitvector directly. + * If not, converts into passed bv and returns that instead. + */ +static wordptr +intnum_tobv(/*@returned@*/ wordptr bv, const yasm_intnum *intn) +{ + if (intn->type == INTNUM_BV) + return intn->val.bv; + + BitVector_Empty(bv); + if (intn->val.l >= 0) + BitVector_Chunk_Store(bv, 32, 0, (unsigned long)intn->val.l); + else { + BitVector_Chunk_Store(bv, 32, 0, (unsigned long)-intn->val.l); + BitVector_Negate(bv, bv); + } + return bv; +} + +yasm_intnum * +yasm_intnum_create_dec(char *str) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + + switch (BitVector_from_Dec_static(from_dec_data, conv_bv, + (unsigned char *)str)) { + case ErrCode_Pars: + yasm_error_set(YASM_ERROR_VALUE, N_("invalid decimal literal")); + break; + case ErrCode_Ovfl: + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Numeric constant too large for internal format")); + break; + default: + break; + } + intnum_frombv(intn, conv_bv); + return intn; +} + +yasm_intnum * +yasm_intnum_create_bin(char *str) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + + switch (BitVector_from_Bin(conv_bv, (unsigned char *)str)) { + case ErrCode_Pars: + yasm_error_set(YASM_ERROR_VALUE, N_("invalid binary literal")); + break; + case ErrCode_Ovfl: + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Numeric constant too large for internal format")); + break; + default: + break; + } + intnum_frombv(intn, conv_bv); + return intn; +} + +yasm_intnum * +yasm_intnum_create_oct(char *str) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + + switch (BitVector_from_Oct(conv_bv, (unsigned char *)str)) { + case ErrCode_Pars: + yasm_error_set(YASM_ERROR_VALUE, N_("invalid octal literal")); + break; + case ErrCode_Ovfl: + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Numeric constant too large for internal format")); + break; + default: + break; + } + intnum_frombv(intn, conv_bv); + return intn; +} + +yasm_intnum * +yasm_intnum_create_hex(char *str) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + + switch (BitVector_from_Hex(conv_bv, (unsigned char *)str)) { + case ErrCode_Pars: + yasm_error_set(YASM_ERROR_VALUE, N_("invalid hex literal")); + break; + case ErrCode_Ovfl: + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Numeric constant too large for internal format")); + break; + default: + break; + } + intnum_frombv(intn, conv_bv); + return intn; +} + +/*@-usedef -compdef -uniondef@*/ +yasm_intnum * +yasm_intnum_create_charconst_nasm(const char *str) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + size_t len = strlen(str); + + if(len*8 > BITVECT_NATIVE_SIZE) + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Character constant too large for internal format")); + + /* be conservative in choosing bitvect in case MSB is set */ + if (len > 3) { + BitVector_Empty(conv_bv); + intn->type = INTNUM_BV; + } else { + intn->val.l = 0; + intn->type = INTNUM_L; + } + + switch (len) { + case 3: + intn->val.l |= ((unsigned long)str[2]) & 0xff; + intn->val.l <<= 8; + /*@fallthrough@*/ + case 2: + intn->val.l |= ((unsigned long)str[1]) & 0xff; + intn->val.l <<= 8; + /*@fallthrough@*/ + case 1: + intn->val.l |= ((unsigned long)str[0]) & 0xff; + case 0: + break; + default: + /* >=32 bit conversion */ + while (len) { + BitVector_Move_Left(conv_bv, 8); + BitVector_Chunk_Store(conv_bv, 8, 0, + ((unsigned long)str[--len]) & 0xff); + } + intn->val.bv = BitVector_Clone(conv_bv); + } + + return intn; +} + +yasm_intnum * +yasm_intnum_create_charconst_tasm(const char *str) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + size_t len = strlen(str); + size_t i; + + if(len*8 > BITVECT_NATIVE_SIZE) + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Character constant too large for internal format")); + + /* be conservative in choosing bitvect in case MSB is set */ + if (len > 3) { + BitVector_Empty(conv_bv); + intn->type = INTNUM_BV; + } else { + intn->val.l = 0; + intn->type = INTNUM_L; + } + + /* tasm uses big endian notation */ + i = 0; + switch (len) { + case 3: + intn->val.l |= ((unsigned long)str[i++]) & 0xff; + intn->val.l <<= 8; + /*@fallthrough@*/ + case 2: + intn->val.l |= ((unsigned long)str[i++]) & 0xff; + intn->val.l <<= 8; + /*@fallthrough@*/ + case 1: + intn->val.l |= ((unsigned long)str[i++]) & 0xff; + case 0: + break; + default: + /* >=32 bit conversion */ + while (i < len) { + BitVector_Chunk_Store(conv_bv, 8, (len-i-1)*8, + ((unsigned long)str[i]) & 0xff); + i++; + } + intn->val.bv = BitVector_Clone(conv_bv); + } + + return intn; +} +/*@=usedef =compdef =uniondef@*/ + +yasm_intnum * +yasm_intnum_create_uint(unsigned long i) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + + if (i > LONG_MAX) { + /* Too big, store as bitvector */ + intn->val.bv = BitVector_Create(BITVECT_NATIVE_SIZE, TRUE); + intn->type = INTNUM_BV; + BitVector_Chunk_Store(intn->val.bv, 32, 0, i); + } else { + intn->val.l = (long)i; + intn->type = INTNUM_L; + } + + return intn; +} + +yasm_intnum * +yasm_intnum_create_int(long i) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + + intn->val.l = i; + intn->type = INTNUM_L; + + return intn; +} + +yasm_intnum * +yasm_intnum_create_leb128(const unsigned char *ptr, int sign, + unsigned long *size) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + const unsigned char *ptr_orig = ptr; + unsigned long i = 0; + + BitVector_Empty(conv_bv); + for (;;) { + BitVector_Chunk_Store(conv_bv, 7, i, *ptr); + i += 7; + if ((*ptr & 0x80) != 0x80) + break; + ptr++; + } + + *size = (unsigned long)(ptr-ptr_orig)+1; + + if(i > BITVECT_NATIVE_SIZE) + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Numeric constant too large for internal format")); + else if (sign && (*ptr & 0x40) == 0x40) + BitVector_Interval_Fill(conv_bv, i, BITVECT_NATIVE_SIZE-1); + + intnum_frombv(intn, conv_bv); + return intn; +} + +yasm_intnum * +yasm_intnum_create_sized(unsigned char *ptr, int sign, size_t srcsize, + int bigendian) +{ + yasm_intnum *intn = yasm_xmalloc(sizeof(yasm_intnum)); + unsigned long i = 0; + + if (srcsize*8 > BITVECT_NATIVE_SIZE) + yasm_error_set(YASM_ERROR_OVERFLOW, + N_("Numeric constant too large for internal format")); + + /* Read the buffer into a bitvect */ + BitVector_Empty(conv_bv); + if (bigendian) { + /* TODO */ + yasm_internal_error(N_("big endian not implemented")); + } else { + for (i = 0; i < srcsize; i++) + BitVector_Chunk_Store(conv_bv, 8, i*8, ptr[i]); + } + + /* Sign extend if needed */ + if (srcsize*8 < BITVECT_NATIVE_SIZE && sign && (ptr[i-1] & 0x80) == 0x80) + BitVector_Interval_Fill(conv_bv, i*8, BITVECT_NATIVE_SIZE-1); + + intnum_frombv(intn, conv_bv); + return intn; +} + +yasm_intnum * +yasm_intnum_copy(const yasm_intnum *intn) +{ + yasm_intnum *n = yasm_xmalloc(sizeof(yasm_intnum)); + + switch (intn->type) { + case INTNUM_L: + n->val.l = intn->val.l; + break; + case INTNUM_BV: + n->val.bv = BitVector_Clone(intn->val.bv); + break; + } + n->type = intn->type; + + return n; +} + +void +yasm_intnum_destroy(yasm_intnum *intn) +{ + if (intn->type == INTNUM_BV) + BitVector_Destroy(intn->val.bv); + yasm_xfree(intn); +} + +/*@-nullderef -nullpass -branchstate@*/ +int +yasm_intnum_calc(yasm_intnum *acc, yasm_expr_op op, yasm_intnum *operand) +{ + boolean carry = 0; + wordptr op1, op2 = NULL; + N_int count; + + /* Always do computations with in full bit vector. + * Bit vector results must be calculated through intermediate storage. + */ + op1 = intnum_tobv(op1static, acc); + if (operand) + op2 = intnum_tobv(op2static, operand); + + if (!operand && op != YASM_EXPR_NEG && op != YASM_EXPR_NOT && + op != YASM_EXPR_LNOT) { + yasm_error_set(YASM_ERROR_ARITHMETIC, + N_("operation needs an operand")); + BitVector_Empty(result); + return 1; + } + + /* A operation does a bitvector computation if result is allocated. */ + switch (op) { + case YASM_EXPR_ADD: + BitVector_add(result, op1, op2, &carry); + break; + case YASM_EXPR_SUB: + BitVector_sub(result, op1, op2, &carry); + break; + case YASM_EXPR_MUL: + BitVector_Multiply(result, op1, op2); + break; + case YASM_EXPR_DIV: + /* TODO: make sure op1 and op2 are unsigned */ + if (BitVector_is_empty(op2)) { + yasm_error_set(YASM_ERROR_ZERO_DIVISION, N_("divide by zero")); + BitVector_Empty(result); + return 1; + } else + BitVector_Divide(result, op1, op2, spare); + break; + case YASM_EXPR_SIGNDIV: + if (BitVector_is_empty(op2)) { + yasm_error_set(YASM_ERROR_ZERO_DIVISION, N_("divide by zero")); + BitVector_Empty(result); + return 1; + } else + BitVector_Divide(result, op1, op2, spare); + break; + case YASM_EXPR_MOD: + /* TODO: make sure op1 and op2 are unsigned */ + if (BitVector_is_empty(op2)) { + yasm_error_set(YASM_ERROR_ZERO_DIVISION, N_("divide by zero")); + BitVector_Empty(result); + return 1; + } else + BitVector_Divide(spare, op1, op2, result); + break; + case YASM_EXPR_SIGNMOD: + if (BitVector_is_empty(op2)) { + yasm_error_set(YASM_ERROR_ZERO_DIVISION, N_("divide by zero")); + BitVector_Empty(result); + return 1; + } else + BitVector_Divide(spare, op1, op2, result); + break; + case YASM_EXPR_NEG: + BitVector_Negate(result, op1); + break; + case YASM_EXPR_NOT: + Set_Complement(result, op1); + break; + case YASM_EXPR_OR: + Set_Union(result, op1, op2); + break; + case YASM_EXPR_AND: + Set_Intersection(result, op1, op2); + break; + case YASM_EXPR_XOR: + Set_ExclusiveOr(result, op1, op2); + break; + case YASM_EXPR_XNOR: + Set_ExclusiveOr(result, op1, op2); + Set_Complement(result, result); + break; + case YASM_EXPR_NOR: + Set_Union(result, op1, op2); + Set_Complement(result, result); + break; + case YASM_EXPR_SHL: + if (operand->type == INTNUM_L && operand->val.l >= 0) { + BitVector_Copy(result, op1); + BitVector_Move_Left(result, (N_int)operand->val.l); + } else /* don't even bother, just zero result */ + BitVector_Empty(result); + break; + case YASM_EXPR_SHR: + if (operand->type == INTNUM_L && operand->val.l >= 0) { + BitVector_Copy(result, op1); + carry = BitVector_msb_(op1); + count = (N_int)operand->val.l; + while (count-- > 0) + BitVector_shift_right(result, carry); + } else /* don't even bother, just zero result */ + BitVector_Empty(result); + break; + case YASM_EXPR_LOR: + BitVector_Empty(result); + BitVector_LSB(result, !BitVector_is_empty(op1) || + !BitVector_is_empty(op2)); + break; + case YASM_EXPR_LAND: + BitVector_Empty(result); + BitVector_LSB(result, !BitVector_is_empty(op1) && + !BitVector_is_empty(op2)); + break; + case YASM_EXPR_LNOT: + BitVector_Empty(result); + BitVector_LSB(result, BitVector_is_empty(op1)); + break; + case YASM_EXPR_LXOR: + BitVector_Empty(result); + BitVector_LSB(result, !BitVector_is_empty(op1) ^ + !BitVector_is_empty(op2)); + break; + case YASM_EXPR_LXNOR: + BitVector_Empty(result); + BitVector_LSB(result, !(!BitVector_is_empty(op1) ^ + !BitVector_is_empty(op2))); + break; + case YASM_EXPR_LNOR: + BitVector_Empty(result); + BitVector_LSB(result, !(!BitVector_is_empty(op1) || + !BitVector_is_empty(op2))); + break; + case YASM_EXPR_EQ: + BitVector_Empty(result); + BitVector_LSB(result, BitVector_equal(op1, op2)); + break; + case YASM_EXPR_LT: + BitVector_Empty(result); + BitVector_LSB(result, BitVector_Compare(op1, op2) < 0); + break; + case YASM_EXPR_GT: + BitVector_Empty(result); + BitVector_LSB(result, BitVector_Compare(op1, op2) > 0); + break; + case YASM_EXPR_LE: + BitVector_Empty(result); + BitVector_LSB(result, BitVector_Compare(op1, op2) <= 0); + break; + case YASM_EXPR_GE: + BitVector_Empty(result); + BitVector_LSB(result, BitVector_Compare(op1, op2) >= 0); + break; + case YASM_EXPR_NE: + BitVector_Empty(result); + BitVector_LSB(result, !BitVector_equal(op1, op2)); + break; + case YASM_EXPR_SEG: + yasm_error_set(YASM_ERROR_ARITHMETIC, N_("invalid use of '%s'"), + "SEG"); + break; + case YASM_EXPR_WRT: + yasm_error_set(YASM_ERROR_ARITHMETIC, N_("invalid use of '%s'"), + "WRT"); + break; + case YASM_EXPR_SEGOFF: + yasm_error_set(YASM_ERROR_ARITHMETIC, N_("invalid use of '%s'"), + ":"); + break; + case YASM_EXPR_IDENT: + if (result) + BitVector_Copy(result, op1); + break; + default: + yasm_error_set(YASM_ERROR_ARITHMETIC, + N_("invalid operation in intnum calculation")); + BitVector_Empty(result); + return 1; + } + + /* Try to fit the result into 32 bits if possible */ + if (acc->type == INTNUM_BV) + BitVector_Destroy(acc->val.bv); + intnum_frombv(acc, result); + return 0; +} +/*@=nullderef =nullpass =branchstate@*/ + +int +yasm_intnum_compare(const yasm_intnum *intn1, const yasm_intnum *intn2) +{ + wordptr op1, op2; + + if (intn1->type == INTNUM_L && intn2->type == INTNUM_L) { + if (intn1->val.l < intn2->val.l) + return -1; + if (intn1->val.l > intn2->val.l) + return 1; + return 0; + } + + op1 = intnum_tobv(op1static, intn1); + op2 = intnum_tobv(op2static, intn2); + return BitVector_Compare(op1, op2); +} + +void +yasm_intnum_zero(yasm_intnum *intn) +{ + yasm_intnum_set_int(intn, 0); +} + +void +yasm_intnum_set(yasm_intnum *intn, const yasm_intnum *val) +{ + if (intn->type == val->type) { + switch (val->type) { + case INTNUM_L: + intn->val.l = val->val.l; + break; + case INTNUM_BV: + BitVector_Copy(intn->val.bv, val->val.bv); + break; + } + } else { + switch (val->type) { + case INTNUM_L: + BitVector_Destroy(intn->val.bv); + intn->val.l = val->val.l; + break; + case INTNUM_BV: + intn->val.bv = BitVector_Clone(val->val.bv); + break; + } + intn->type = val->type; + } +} + +void +yasm_intnum_set_uint(yasm_intnum *intn, unsigned long val) +{ + if (val > LONG_MAX) { + if (intn->type != INTNUM_BV) { + intn->val.bv = BitVector_Create(BITVECT_NATIVE_SIZE, TRUE); + intn->type = INTNUM_BV; + } + BitVector_Chunk_Store(intn->val.bv, 32, 0, val); + } else { + if (intn->type == INTNUM_BV) { + BitVector_Destroy(intn->val.bv); + intn->type = INTNUM_L; + } + intn->val.l = (long)val; + } +} + +void +yasm_intnum_set_int(yasm_intnum *intn, long val) +{ + if (intn->type == INTNUM_BV) + BitVector_Destroy(intn->val.bv); + intn->type = INTNUM_L; + intn->val.l = val; +} + +int +yasm_intnum_is_zero(const yasm_intnum *intn) +{ + return (intn->type == INTNUM_L && intn->val.l == 0); +} + +int +yasm_intnum_is_pos1(const yasm_intnum *intn) +{ + return (intn->type == INTNUM_L && intn->val.l == 1); +} + +int +yasm_intnum_is_neg1(const yasm_intnum *intn) +{ + return (intn->type == INTNUM_L && intn->val.l == -1); +} + +int +yasm_intnum_sign(const yasm_intnum *intn) +{ + if (intn->type == INTNUM_L) { + if (intn->val.l == 0) + return 0; + else if (intn->val.l < 0) + return -1; + else + return 1; + } else + return BitVector_Sign(intn->val.bv); +} + +unsigned long +yasm_intnum_get_uint(const yasm_intnum *intn) +{ + switch (intn->type) { + case INTNUM_L: + if (intn->val.l < 0) + return 0; + return (unsigned long)intn->val.l; + case INTNUM_BV: + if (BitVector_msb_(intn->val.bv)) + return 0; + if (Set_Max(intn->val.bv) > 32) + return ULONG_MAX; + return BitVector_Chunk_Read(intn->val.bv, 32, 0); + default: + yasm_internal_error(N_("unknown intnum type")); + /*@notreached@*/ + return 0; + } +} + +long +yasm_intnum_get_int(const yasm_intnum *intn) +{ + switch (intn->type) { + case INTNUM_L: + return intn->val.l; + case INTNUM_BV: + if (BitVector_msb_(intn->val.bv)) { + /* it's negative: negate the bitvector to get a positive + * number, then negate the positive number. + */ + unsigned long ul; + + BitVector_Negate(conv_bv, intn->val.bv); + if (Set_Max(conv_bv) >= 32) { + /* too negative */ + return LONG_MIN; + } + ul = BitVector_Chunk_Read(conv_bv, 32, 0); + /* check for too negative */ + return (ul & 0x80000000) ? LONG_MIN : -((long)ul); + } + + /* it's positive, and since it's a BV, it must be >0x7FFFFFFF */ + return LONG_MAX; + default: + yasm_internal_error(N_("unknown intnum type")); + /*@notreached@*/ + return 0; + } +} + +void +yasm_intnum_get_sized(const yasm_intnum *intn, unsigned char *ptr, + size_t destsize, size_t valsize, int shift, + int bigendian, int warn) +{ + wordptr op1 = op1static, op2; + unsigned char *buf; + unsigned int len; + size_t rshift = shift < 0 ? (size_t)(-shift) : 0; + int carry_in; + + /* Currently don't support destinations larger than our native size */ + if (destsize*8 > BITVECT_NATIVE_SIZE) + yasm_internal_error(N_("destination too large")); + + /* General size warnings */ + if (warn<0 && !yasm_intnum_check_size(intn, valsize, rshift, 1)) + yasm_warn_set(YASM_WARN_GENERAL, + N_("value does not fit in signed %d bit field"), + valsize); + if (warn>0 && !yasm_intnum_check_size(intn, valsize, rshift, 2)) + yasm_warn_set(YASM_WARN_GENERAL, + N_("value does not fit in %d bit field"), valsize); + + /* Read the original data into a bitvect */ + if (bigendian) { + /* TODO */ + yasm_internal_error(N_("big endian not implemented")); + } else + BitVector_Block_Store(op1, ptr, (N_int)destsize); + + /* If not already a bitvect, convert value to be written to a bitvect */ + op2 = intnum_tobv(op2static, intn); + + /* Check low bits if right shifting and warnings enabled */ + if (warn && rshift > 0) { + BitVector_Copy(conv_bv, op2); + BitVector_Move_Left(conv_bv, (N_int)(BITVECT_NATIVE_SIZE-rshift)); + if (!BitVector_is_empty(conv_bv)) + yasm_warn_set(YASM_WARN_GENERAL, + N_("misaligned value, truncating to boundary")); + } + + /* Shift right if needed */ + if (rshift > 0) { + carry_in = BitVector_msb_(op2); + while (rshift-- > 0) + BitVector_shift_right(op2, carry_in); + shift = 0; + } + + /* Write the new value into the destination bitvect */ + BitVector_Interval_Copy(op1, op2, (unsigned int)shift, 0, (N_int)valsize); + + /* Write out the new data */ + buf = BitVector_Block_Read(op1, &len); + if (bigendian) { + /* TODO */ + yasm_internal_error(N_("big endian not implemented")); + } else + memcpy(ptr, buf, destsize); + yasm_xfree(buf); +} + +/* Return 1 if okay size, 0 if not */ +int +yasm_intnum_check_size(const yasm_intnum *intn, size_t size, size_t rshift, + int rangetype) +{ + wordptr val; + + /* If not already a bitvect, convert value to a bitvect */ + if (intn->type == INTNUM_BV) { + if (rshift > 0) { + val = conv_bv; + BitVector_Copy(val, intn->val.bv); + } else + val = intn->val.bv; + } else + val = intnum_tobv(conv_bv, intn); + + if (size >= BITVECT_NATIVE_SIZE) + return 1; + + if (rshift > 0) { + int carry_in = BitVector_msb_(val); + while (rshift-- > 0) + BitVector_shift_right(val, carry_in); + } + + if (rangetype > 0) { + if (BitVector_msb_(val)) { + /* it's negative */ + int retval; + + BitVector_Negate(conv_bv, val); + BitVector_dec(conv_bv, conv_bv); + retval = Set_Max(conv_bv) < (long)size-1; + + return retval; + } + + if (rangetype == 1) + size--; + } + return (Set_Max(val) < (long)size); +} + +int +yasm_intnum_in_range(const yasm_intnum *intn, long low, long high) +{ + wordptr val = intnum_tobv(result, intn); + wordptr lval = op1static; + wordptr hval = op2static; + + /* Convert high and low to bitvects */ + BitVector_Empty(lval); + if (low >= 0) + BitVector_Chunk_Store(lval, 32, 0, (unsigned long)low); + else { + BitVector_Chunk_Store(lval, 32, 0, (unsigned long)(-low)); + BitVector_Negate(lval, lval); + } + + BitVector_Empty(hval); + if (high >= 0) + BitVector_Chunk_Store(hval, 32, 0, (unsigned long)high); + else { + BitVector_Chunk_Store(hval, 32, 0, (unsigned long)(-high)); + BitVector_Negate(hval, hval); + } + + /* Compare! */ + return (BitVector_Compare(val, lval) >= 0 + && BitVector_Compare(val, hval) <= 0); +} + +static unsigned long +get_leb128(wordptr val, unsigned char *ptr, int sign) +{ + unsigned long i, size; + unsigned char *ptr_orig = ptr; + + if (sign) { + /* Signed mode */ + if (BitVector_msb_(val)) { + /* Negative */ + BitVector_Negate(conv_bv, val); + size = Set_Max(conv_bv)+2; + } else { + /* Positive */ + size = Set_Max(val)+2; + } + } else { + /* Unsigned mode */ + size = Set_Max(val)+1; + } + + /* Positive/Unsigned write */ + for (i=0; i<size; i += 7) { + *ptr = (unsigned char)BitVector_Chunk_Read(val, 7, i); + *ptr |= 0x80; + ptr++; + } + *(ptr-1) &= 0x7F; /* Clear MSB of last byte */ + return (unsigned long)(ptr-ptr_orig); +} + +static unsigned long +size_leb128(wordptr val, int sign) +{ + if (sign) { + /* Signed mode */ + if (BitVector_msb_(val)) { + /* Negative */ + BitVector_Negate(conv_bv, val); + return (Set_Max(conv_bv)+8)/7; + } else { + /* Positive */ + return (Set_Max(val)+8)/7; + } + } else { + /* Unsigned mode */ + return (Set_Max(val)+7)/7; + } +} + +unsigned long +yasm_intnum_get_leb128(const yasm_intnum *intn, unsigned char *ptr, int sign) +{ + wordptr val; + + /* Shortcut 0 */ + if (intn->type == INTNUM_L && intn->val.l == 0) { + *ptr = 0; + return 1; + } + + /* If not already a bitvect, convert value to be written to a bitvect */ + val = intnum_tobv(op1static, intn); + + return get_leb128(val, ptr, sign); +} + +unsigned long +yasm_intnum_size_leb128(const yasm_intnum *intn, int sign) +{ + wordptr val; + + /* Shortcut 0 */ + if (intn->type == INTNUM_L && intn->val.l == 0) { + return 1; + } + + /* If not already a bitvect, convert value to a bitvect */ + val = intnum_tobv(op1static, intn); + + return size_leb128(val, sign); +} + +unsigned long +yasm_get_sleb128(long v, unsigned char *ptr) +{ + wordptr val = op1static; + + /* Shortcut 0 */ + if (v == 0) { + *ptr = 0; + return 1; + } + + BitVector_Empty(val); + if (v >= 0) + BitVector_Chunk_Store(val, 32, 0, (unsigned long)v); + else { + BitVector_Chunk_Store(val, 32, 0, (unsigned long)(-v)); + BitVector_Negate(val, val); + } + return get_leb128(val, ptr, 1); +} + +unsigned long +yasm_size_sleb128(long v) +{ + wordptr val = op1static; + + if (v == 0) + return 1; + + BitVector_Empty(val); + if (v >= 0) + BitVector_Chunk_Store(val, 32, 0, (unsigned long)v); + else { + BitVector_Chunk_Store(val, 32, 0, (unsigned long)(-v)); + BitVector_Negate(val, val); + } + return size_leb128(val, 1); +} + +unsigned long +yasm_get_uleb128(unsigned long v, unsigned char *ptr) +{ + wordptr val = op1static; + + /* Shortcut 0 */ + if (v == 0) { + *ptr = 0; + return 1; + } + + BitVector_Empty(val); + BitVector_Chunk_Store(val, 32, 0, v); + return get_leb128(val, ptr, 0); +} + +unsigned long +yasm_size_uleb128(unsigned long v) +{ + wordptr val = op1static; + + if (v == 0) + return 1; + + BitVector_Empty(val); + BitVector_Chunk_Store(val, 32, 0, v); + return size_leb128(val, 0); +} + +char * +yasm_intnum_get_str(const yasm_intnum *intn) +{ + unsigned char *s; + + switch (intn->type) { + case INTNUM_L: + s = yasm_xmalloc(16); + sprintf((char *)s, "%ld", intn->val.l); + return (char *)s; + break; + case INTNUM_BV: + return (char *)BitVector_to_Dec(intn->val.bv); + break; + } + /*@notreached@*/ + return NULL; +} + +void +yasm_intnum_print(const yasm_intnum *intn, FILE *f) +{ + unsigned char *s; + + switch (intn->type) { + case INTNUM_L: + fprintf(f, "0x%lx", intn->val.l); + break; + case INTNUM_BV: + s = BitVector_to_Hex(intn->val.bv); + fprintf(f, "0x%s", (char *)s); + yasm_xfree(s); + break; + } +} diff --git a/libyasm/intnum.h b/libyasm/intnum.h new file mode 100644 index 0000000..bec832c --- /dev/null +++ b/libyasm/intnum.h @@ -0,0 +1,340 @@ +/** + * \file libyasm/intnum.h + * \brief YASM integer number interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_INTNUM_H +#define YASM_INTNUM_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Initialize intnum internal data structures. */ +YASM_LIB_DECL +void yasm_intnum_initialize(void); + +/** Clean up internal intnum allocations. */ +YASM_LIB_DECL +void yasm_intnum_cleanup(void); + +/** Create a new intnum from a decimal string. + * \param str decimal string + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_dec(char *str); + +/** Create a new intnum from a binary string. + * \param str binary string + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_bin(char *str); + +/** Create a new intnum from an octal string. + * \param str octal string + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_oct(char *str); + +/** Create a new intnum from a hexidecimal string. + * \param str hexidecimal string + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_hex(char *str); + +/** Convert character constant to integer value, using NASM rules. NASM syntax + * supports automatic conversion from strings such as 'abcd' to a 32-bit + * integer value (little endian order). This function performs those conversions. + * \param str character constant string + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_charconst_nasm(const char *str); + +/** Convert character constant to integer value, using TASM rules. TASM syntax + * supports automatic conversion from strings such as 'abcd' to a 32-bit + * integer value (big endian order). This function performs those conversions. + * \param str character constant string + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_charconst_tasm(const char *str); + +/** Create a new intnum from an unsigned integer value. + * \param i unsigned integer value + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_uint(unsigned long i); + +/** Create a new intnum from an signed integer value. + * \param i signed integer value + * \return Newly allocated intnum. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_int(long i); + +/** Create a new intnum from LEB128-encoded form. + * \param ptr pointer to start of LEB128 encoded form + * \param sign signed (1) or unsigned (0) LEB128 format + * \param size number of bytes read from ptr (output) + * \return Newly allocated intnum. Number of bytes read returned into + * bytes_read parameter. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_leb128 + (const unsigned char *ptr, int sign, /*@out@*/ unsigned long *size); + +/** Create a new intnum from a little-endian or big-endian buffer. + * In little endian, the LSB is in ptr[0]. + * \param ptr pointer to start of buffer + * \param sign signed (1) or unsigned (0) source + * \param srcsize source buffer size (in bytes) + * \param bigendian endianness (nonzero=big, zero=little) + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_create_sized + (unsigned char *ptr, int sign, size_t srcsize, int bigendian); + +/** Duplicate an intnum. + * \param intn intnum + * \return Newly allocated intnum with the same value as intn. + */ +YASM_LIB_DECL +/*@only@*/ yasm_intnum *yasm_intnum_copy(const yasm_intnum *intn); + +/** Destroy (free allocated memory for) an intnum. + * \param intn intnum + */ +YASM_LIB_DECL +void yasm_intnum_destroy(/*@only@*/ yasm_intnum *intn); + +/** Floating point calculation function: acc = acc op operand. + * \note Not all operations in yasm_expr_op may be supported; unsupported + * operations will result in an error. + * \param acc intnum accumulator + * \param op operation + * \param operand intnum operand + * \return Nonzero if error occurred. + */ +YASM_LIB_DECL +int yasm_intnum_calc(yasm_intnum *acc, yasm_expr_op op, yasm_intnum *operand); + +/** Compare two intnums. + * \param intn1 first intnum + * \param intn2 second intnum + * \return -1 if intn1 < intn2, 0 if intn1 == intn2, 1 if intn1 > intn2. + */ +YASM_LIB_DECL +int yasm_intnum_compare(const yasm_intnum *intn1, const yasm_intnum *intn2); + +/** Zero an intnum. + * \param intn intnum + */ +YASM_LIB_DECL +void yasm_intnum_zero(yasm_intnum *intn); + +/** Set an intnum to the value of another intnum. + * \param intn intnum + * \param val intnum to get value from + */ +YASM_LIB_DECL +void yasm_intnum_set(yasm_intnum *intn, const yasm_intnum *val); + +/** Set an intnum to an unsigned integer. + * \param intn intnum + * \param val integer value + */ +YASM_LIB_DECL +void yasm_intnum_set_uint(yasm_intnum *intn, unsigned long val); + +/** Set an intnum to an signed integer. + * \param intn intnum + * \param val integer value + */ +YASM_LIB_DECL +void yasm_intnum_set_int(yasm_intnum *intn, long val); + +/** Simple value check for 0. + * \param acc intnum + * \return Nonzero if acc==0. + */ +YASM_LIB_DECL +int yasm_intnum_is_zero(const yasm_intnum *acc); + +/** Simple value check for 1. + * \param acc intnum + * \return Nonzero if acc==1. + */ +YASM_LIB_DECL +int yasm_intnum_is_pos1(const yasm_intnum *acc); + +/** Simple value check for -1. + * \param acc intnum + * \return Nonzero if acc==-1. + */ +YASM_LIB_DECL +int yasm_intnum_is_neg1(const yasm_intnum *acc); + +/** Simple sign check. + * \param acc intnum + * \return -1 if negative, 0 if zero, +1 if positive + */ +YASM_LIB_DECL +int yasm_intnum_sign(const yasm_intnum *acc); + +/** Convert an intnum to an unsigned 32-bit value. The value is in "standard" + * C format (eg, of unknown endian). + * \note Parameter intnum is truncated to fit into 32 bits. Use + * intnum_check_size() to check for overflow. + * \param intn intnum + * \return Unsigned 32-bit value of intn. + */ +YASM_LIB_DECL +unsigned long yasm_intnum_get_uint(const yasm_intnum *intn); + +/** Convert an intnum to a signed 32-bit value. The value is in "standard" C + * format (eg, of unknown endian). + * \note Parameter intnum is truncated to fit into 32 bits. Use + * intnum_check_size() to check for overflow. + * \param intn intnum + * \return Signed 32-bit value of intn. + */ +YASM_LIB_DECL +long yasm_intnum_get_int(const yasm_intnum *intn); + +/** Output #yasm_intnum to buffer in little-endian or big-endian. Puts the + * value into the least significant bits of the destination, or may be shifted + * into more significant bits by the shift parameter. The destination bits are + * cleared before being set. [0] should be the first byte output to the file. + * \param intn intnum + * \param ptr pointer to storage for size bytes of output + * \param destsize destination size (in bytes) + * \param valsize size (in bits) + * \param shift left shift (in bits); may be negative to specify right + * shift (standard warnings include truncation to boundary) + * \param bigendian endianness (nonzero=big, zero=little) + * \param warn enables standard warnings (value doesn't fit into valsize + * bits): <0=signed warnings, >0=unsigned warnings, 0=no warn + */ +YASM_LIB_DECL +void yasm_intnum_get_sized(const yasm_intnum *intn, unsigned char *ptr, + size_t destsize, size_t valsize, int shift, + int bigendian, int warn); + +/** Check to see if intnum will fit without overflow into size bits. + * \param intn intnum + * \param size number of bits of output space + * \param rshift right shift + * \param rangetype signed/unsigned range selection: + * 0 => (0, unsigned max); + * 1 => (signed min, signed max); + * 2 => (signed min, unsigned max) + * \return Nonzero if intnum will fit. + */ +YASM_LIB_DECL +int yasm_intnum_check_size(const yasm_intnum *intn, size_t size, + size_t rshift, int rangetype); + +/** Check to see if intnum will fit into a particular numeric range. + * \param intn intnum + * \param low low end of range (inclusive) + * \param high high end of range (inclusive) + * \return Nonzero if intnum is within range. + */ +YASM_LIB_DECL +int yasm_intnum_in_range(const yasm_intnum *intn, long low, long high); + +/** Output #yasm_intnum to buffer in LEB128-encoded form. + * \param intn intnum + * \param ptr pointer to storage for output bytes + * \param sign signedness of LEB128 encoding (0=unsigned, 1=signed) + * \return Number of bytes generated. + */ +YASM_LIB_DECL +unsigned long yasm_intnum_get_leb128(const yasm_intnum *intn, + unsigned char *ptr, int sign); + +/** Calculate number of bytes LEB128-encoded form of #yasm_intnum will take. + * \param intn intnum + * \param sign signedness of LEB128 encoding (0=unsigned, 1=signed) + * \return Number of bytes. + */ +YASM_LIB_DECL +unsigned long yasm_intnum_size_leb128(const yasm_intnum *intn, int sign); + +/** Output integer to buffer in signed LEB128-encoded form. + * \param v integer + * \param ptr pointer to storage for output bytes + * \return Number of bytes generated. + */ +YASM_LIB_DECL +unsigned long yasm_get_sleb128(long v, unsigned char *ptr); + +/** Calculate number of bytes signed LEB128-encoded form of integer will take. + * \param v integer + * \return Number of bytes. + */ +YASM_LIB_DECL +unsigned long yasm_size_sleb128(long v); + +/** Output integer to buffer in unsigned LEB128-encoded form. + * \param v integer + * \param ptr pointer to storage for output bytes + * \return Number of bytes generated. + */ +YASM_LIB_DECL +unsigned long yasm_get_uleb128(unsigned long v, unsigned char *ptr); + +/** Calculate number of bytes unsigned LEB128-encoded form of integer will take. + * \param v integer + * \return Number of bytes. + */ +YASM_LIB_DECL +unsigned long yasm_size_uleb128(unsigned long v); + +/** Get an intnum as a signed decimal string. The returned string will + * contain a leading '-' if the intnum is negative. + * \param intn intnum + * \return Newly allocated string containing the decimal representation of + * the intnum. + */ +YASM_LIB_DECL +/*@only@*/ char *yasm_intnum_get_str(const yasm_intnum *intn); + +/** Print an intnum. For debugging purposes. + * \param f file + * \param intn intnum + */ +YASM_LIB_DECL +void yasm_intnum_print(const yasm_intnum *intn, FILE *f); + +#endif diff --git a/libyasm/inttree.c b/libyasm/inttree.c new file mode 100644 index 0000000..2ae10e9 --- /dev/null +++ b/libyasm/inttree.c @@ -0,0 +1,891 @@ +#include "util.h" + +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <math.h> +#include "coretype.h" +#include "inttree.h" + +#define VERIFY(condition) \ +if (!(condition)) { \ +fprintf(stderr, "Assumption \"%s\"\nFailed in file %s: at line:%i\n", \ +#condition,__FILE__,__LINE__); \ +abort();} + +/*#define DEBUG_ASSERT 1*/ + +#ifdef DEBUG_ASSERT +static void Assert(int assertion, const char *error) +{ + if (!assertion) { + fprintf(stderr, "Assertion Failed: %s\n", error); + abort(); + } +} +#endif + +/* If the symbol CHECK_INTERVAL_TREE_ASSUMPTIONS is defined then the + * code does a lot of extra checking to make sure certain assumptions + * are satisfied. This only needs to be done if you suspect bugs are + * present or if you make significant changes and want to make sure + * your changes didn't mess anything up. + */ +/*#define CHECK_INTERVAL_TREE_ASSUMPTIONS 1*/ + +static IntervalTreeNode *ITN_create(long low, long high, void *data); + +static void LeftRotate(IntervalTree *, IntervalTreeNode *); +static void RightRotate(IntervalTree *, IntervalTreeNode *); +static void TreeInsertHelp(IntervalTree *, IntervalTreeNode *); +static void TreePrintHelper(const IntervalTree *, IntervalTreeNode *); +static void FixUpMaxHigh(IntervalTree *, IntervalTreeNode *); +static void DeleteFixUp(IntervalTree *, IntervalTreeNode *); +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS +static void CheckMaxHighFields(const IntervalTree *, IntervalTreeNode *); +static int CheckMaxHighFieldsHelper(const IntervalTree *, IntervalTreeNode *y, + const int currentHigh, int match); +static void IT_CheckAssumptions(const IntervalTree *); +#endif + +/* define a function to find the maximum of two objects. */ +#define ITMax(a, b) ( (a > b) ? a : b ) + +IntervalTreeNode * +ITN_create(long low, long high, void *data) +{ + IntervalTreeNode *itn = yasm_xmalloc(sizeof(IntervalTreeNode)); + itn->data = data; + if (low < high) { + itn->low = low; + itn->high = high; + } else { + itn->low = high; + itn->high = low; + } + itn->maxHigh = high; + return itn; +} + +IntervalTree * +IT_create(void) +{ + IntervalTree *it = yasm_xmalloc(sizeof(IntervalTree)); + + it->nil = ITN_create(LONG_MIN, LONG_MIN, NULL); + it->nil->left = it->nil; + it->nil->right = it->nil; + it->nil->parent = it->nil; + it->nil->red = 0; + + it->root = ITN_create(LONG_MAX, LONG_MAX, NULL); + it->root->left = it->nil; + it->root->right = it->nil; + it->root->parent = it->nil; + it->root->red = 0; + + /* the following are used for the Enumerate function */ + it->recursionNodeStackSize = 128; + it->recursionNodeStack = (it_recursion_node *) + yasm_xmalloc(it->recursionNodeStackSize*sizeof(it_recursion_node)); + it->recursionNodeStackTop = 1; + it->recursionNodeStack[0].start_node = NULL; + + return it; +} + +/***********************************************************************/ +/* FUNCTION: LeftRotate */ +/**/ +/* INPUTS: the node to rotate on */ +/**/ +/* OUTPUT: None */ +/**/ +/* Modifies Input: this, x */ +/**/ +/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */ +/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */ +/* makes the parent of x be to the left of x, x the parent of */ +/* its parent before the rotation and fixes other pointers */ +/* accordingly. Also updates the maxHigh fields of x and y */ +/* after rotation. */ +/***********************************************************************/ + +static void +LeftRotate(IntervalTree *it, IntervalTreeNode *x) +{ + IntervalTreeNode *y; + + /* I originally wrote this function to use the sentinel for + * nil to avoid checking for nil. However this introduces a + * very subtle bug because sometimes this function modifies + * the parent pointer of nil. This can be a problem if a + * function which calls LeftRotate also uses the nil sentinel + * and expects the nil sentinel's parent pointer to be unchanged + * after calling this function. For example, when DeleteFixUP + * calls LeftRotate it expects the parent pointer of nil to be + * unchanged. + */ + + y=x->right; + x->right=y->left; + + if (y->left != it->nil) + y->left->parent=x; /* used to use sentinel here */ + /* and do an unconditional assignment instead of testing for nil */ + + y->parent=x->parent; + + /* Instead of checking if x->parent is the root as in the book, we + * count on the root sentinel to implicitly take care of this case + */ + if (x == x->parent->left) + x->parent->left=y; + else + x->parent->right=y; + y->left=x; + x->parent=y; + + x->maxHigh=ITMax(x->left->maxHigh,ITMax(x->right->maxHigh,x->high)); + y->maxHigh=ITMax(x->maxHigh,ITMax(y->right->maxHigh,y->high)); +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#elif defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not red in ITLeftRotate"); + Assert((it->nil->maxHigh=LONG_MIN), + "nil->maxHigh != LONG_MIN in ITLeftRotate"); +#endif +} + + +/***********************************************************************/ +/* FUNCTION: RightRotate */ +/**/ +/* INPUTS: node to rotate on */ +/**/ +/* OUTPUT: None */ +/**/ +/* Modifies Input?: this, y */ +/**/ +/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */ +/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */ +/* makes the parent of x be to the left of x, x the parent of */ +/* its parent before the rotation and fixes other pointers */ +/* accordingly. Also updates the maxHigh fields of x and y */ +/* after rotation. */ +/***********************************************************************/ + + +static void +RightRotate(IntervalTree *it, IntervalTreeNode *y) +{ + IntervalTreeNode *x; + + /* I originally wrote this function to use the sentinel for + * nil to avoid checking for nil. However this introduces a + * very subtle bug because sometimes this function modifies + * the parent pointer of nil. This can be a problem if a + * function which calls LeftRotate also uses the nil sentinel + * and expects the nil sentinel's parent pointer to be unchanged + * after calling this function. For example, when DeleteFixUP + * calls LeftRotate it expects the parent pointer of nil to be + * unchanged. + */ + + x=y->left; + y->left=x->right; + + if (it->nil != x->right) + x->right->parent=y; /*used to use sentinel here */ + /* and do an unconditional assignment instead of testing for nil */ + + /* Instead of checking if x->parent is the root as in the book, we + * count on the root sentinel to implicitly take care of this case + */ + x->parent=y->parent; + if (y == y->parent->left) + y->parent->left=x; + else + y->parent->right=x; + x->right=y; + y->parent=x; + + y->maxHigh=ITMax(y->left->maxHigh,ITMax(y->right->maxHigh,y->high)); + x->maxHigh=ITMax(x->left->maxHigh,ITMax(y->maxHigh,x->high)); +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#elif defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not red in ITRightRotate"); + Assert((it->nil->maxHigh=LONG_MIN), + "nil->maxHigh != LONG_MIN in ITRightRotate"); +#endif +} + +/***********************************************************************/ +/* FUNCTION: TreeInsertHelp */ +/**/ +/* INPUTS: z is the node to insert */ +/**/ +/* OUTPUT: none */ +/**/ +/* Modifies Input: this, z */ +/**/ +/* EFFECTS: Inserts z into the tree as if it were a regular binary tree */ +/* using the algorithm described in _Introduction_To_Algorithms_ */ +/* by Cormen et al. This funciton is only intended to be called */ +/* by the InsertTree function and not by the user */ +/***********************************************************************/ + +static void +TreeInsertHelp(IntervalTree *it, IntervalTreeNode *z) +{ + /* This function should only be called by InsertITTree (see above) */ + IntervalTreeNode* x; + IntervalTreeNode* y; + + z->left=z->right=it->nil; + y=it->root; + x=it->root->left; + while( x != it->nil) { + y=x; + if (x->low > z->low) + x=x->left; + else /* x->low <= z->low */ + x=x->right; + } + z->parent=y; + if ((y == it->root) || (y->low > z->low)) + y->left=z; + else + y->right=z; + +#if defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not red in ITTreeInsertHelp"); + Assert((it->nil->maxHigh=INT_MIN), + "nil->maxHigh != INT_MIN in ITTreeInsertHelp"); +#endif +} + + +/***********************************************************************/ +/* FUNCTION: FixUpMaxHigh */ +/**/ +/* INPUTS: x is the node to start from*/ +/**/ +/* OUTPUT: none */ +/**/ +/* Modifies Input: this */ +/**/ +/* EFFECTS: Travels up to the root fixing the maxHigh fields after */ +/* an insertion or deletion */ +/***********************************************************************/ + +static void +FixUpMaxHigh(IntervalTree *it, IntervalTreeNode *x) +{ + while(x != it->root) { + x->maxHigh=ITMax(x->high,ITMax(x->left->maxHigh,x->right->maxHigh)); + x=x->parent; + } +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#endif +} + +/* Before calling InsertNode the node x should have its key set */ + +/***********************************************************************/ +/* FUNCTION: InsertNode */ +/**/ +/* INPUTS: newInterval is the interval to insert*/ +/**/ +/* OUTPUT: This function returns a pointer to the newly inserted node */ +/* which is guarunteed to be valid until this node is deleted. */ +/* What this means is if another data structure stores this */ +/* pointer then the tree does not need to be searched when this */ +/* is to be deleted. */ +/**/ +/* Modifies Input: tree */ +/**/ +/* EFFECTS: Creates a node node which contains the appropriate key and */ +/* info pointers and inserts it into the tree. */ +/***********************************************************************/ + +IntervalTreeNode * +IT_insert(IntervalTree *it, long low, long high, void *data) +{ + IntervalTreeNode *x, *y, *newNode; + + x = ITN_create(low, high, data); + TreeInsertHelp(it, x); + FixUpMaxHigh(it, x->parent); + newNode = x; + x->red=1; + while(x->parent->red) { /* use sentinel instead of checking for root */ + if (x->parent == x->parent->parent->left) { + y=x->parent->parent->right; + if (y->red) { + x->parent->red=0; + y->red=0; + x->parent->parent->red=1; + x=x->parent->parent; + } else { + if (x == x->parent->right) { + x=x->parent; + LeftRotate(it, x); + } + x->parent->red=0; + x->parent->parent->red=1; + RightRotate(it, x->parent->parent); + } + } else { /* case for x->parent == x->parent->parent->right */ + /* this part is just like the section above with */ + /* left and right interchanged */ + y=x->parent->parent->left; + if (y->red) { + x->parent->red=0; + y->red=0; + x->parent->parent->red=1; + x=x->parent->parent; + } else { + if (x == x->parent->left) { + x=x->parent; + RightRotate(it, x); + } + x->parent->red=0; + x->parent->parent->red=1; + LeftRotate(it, x->parent->parent); + } + } + } + it->root->left->red=0; + +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#elif defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not red in ITTreeInsert"); + Assert(!it->root->red,"root not red in ITTreeInsert"); + Assert((it->nil->maxHigh=LONG_MIN), + "nil->maxHigh != LONG_MIN in ITTreeInsert"); +#endif + return newNode; +} + +/***********************************************************************/ +/* FUNCTION: GetSuccessorOf */ +/**/ +/* INPUTS: x is the node we want the succesor of */ +/**/ +/* OUTPUT: This function returns the successor of x or NULL if no */ +/* successor exists. */ +/**/ +/* Modifies Input: none */ +/**/ +/* Note: uses the algorithm in _Introduction_To_Algorithms_ */ +/***********************************************************************/ + +IntervalTreeNode * +IT_get_successor(const IntervalTree *it, IntervalTreeNode *x) +{ + IntervalTreeNode *y; + + if (it->nil != (y = x->right)) { /* assignment to y is intentional */ + while(y->left != it->nil) /* returns the minium of the right subtree of x */ + y=y->left; + return y; + } else { + y=x->parent; + while(x == y->right) { /* sentinel used instead of checking for nil */ + x=y; + y=y->parent; + } + if (y == it->root) + return(it->nil); + return y; + } +} + +/***********************************************************************/ +/* FUNCTION: GetPredecessorOf */ +/**/ +/* INPUTS: x is the node to get predecessor of */ +/**/ +/* OUTPUT: This function returns the predecessor of x or NULL if no */ +/* predecessor exists. */ +/**/ +/* Modifies Input: none */ +/**/ +/* Note: uses the algorithm in _Introduction_To_Algorithms_ */ +/***********************************************************************/ + +IntervalTreeNode * +IT_get_predecessor(const IntervalTree *it, IntervalTreeNode *x) +{ + IntervalTreeNode *y; + + if (it->nil != (y = x->left)) { /* assignment to y is intentional */ + while(y->right != it->nil) /* returns the maximum of the left subtree of x */ + y=y->right; + return y; + } else { + y=x->parent; + while(x == y->left) { + if (y == it->root) + return(it->nil); + x=y; + y=y->parent; + } + return y; + } +} + +/***********************************************************************/ +/* FUNCTION: Print */ +/**/ +/* INPUTS: none */ +/**/ +/* OUTPUT: none */ +/**/ +/* EFFECTS: This function recursively prints the nodes of the tree */ +/* inorder. */ +/**/ +/* Modifies Input: none */ +/**/ +/* Note: This function should only be called from ITTreePrint */ +/***********************************************************************/ + +static void +ITN_print(const IntervalTreeNode *itn, IntervalTreeNode *nil, + IntervalTreeNode *root) +{ + printf(", l=%li, h=%li, mH=%li", itn->low, itn->high, itn->maxHigh); + printf(" l->low="); + if (itn->left == nil) + printf("NULL"); + else + printf("%li", itn->left->low); + printf(" r->low="); + if (itn->right == nil) + printf("NULL"); + else + printf("%li", itn->right->low); + printf(" p->low="); + if (itn->parent == root) + printf("NULL"); + else + printf("%li", itn->parent->low); + printf(" red=%i\n", itn->red); +} + +static void +TreePrintHelper(const IntervalTree *it, IntervalTreeNode *x) +{ + if (x != it->nil) { + TreePrintHelper(it, x->left); + ITN_print(x, it->nil, it->root); + TreePrintHelper(it, x->right); + } +} + +void +IT_destroy(IntervalTree *it) +{ + IntervalTreeNode *x = it->root->left; + SLIST_HEAD(node_head, nodeent) + stuffToFree = SLIST_HEAD_INITIALIZER(stuffToFree); + struct nodeent { + SLIST_ENTRY(nodeent) link; + struct IntervalTreeNode *node; + } *np; + + if (x != it->nil) { + if (x->left != it->nil) { + np = yasm_xmalloc(sizeof(struct nodeent)); + np->node = x->left; + SLIST_INSERT_HEAD(&stuffToFree, np, link); + } + if (x->right != it->nil) { + np = yasm_xmalloc(sizeof(struct nodeent)); + np->node = x->right; + SLIST_INSERT_HEAD(&stuffToFree, np, link); + } + yasm_xfree(x); + while (!SLIST_EMPTY(&stuffToFree)) { + np = SLIST_FIRST(&stuffToFree); + x = np->node; + SLIST_REMOVE_HEAD(&stuffToFree, link); + yasm_xfree(np); + + if (x->left != it->nil) { + np = yasm_xmalloc(sizeof(struct nodeent)); + np->node = x->left; + SLIST_INSERT_HEAD(&stuffToFree, np, link); + } + if (x->right != it->nil) { + np = yasm_xmalloc(sizeof(struct nodeent)); + np->node = x->right; + SLIST_INSERT_HEAD(&stuffToFree, np, link); + } + yasm_xfree(x); + } + } + + yasm_xfree(it->nil); + yasm_xfree(it->root); + yasm_xfree(it->recursionNodeStack); + yasm_xfree(it); +} + + +/***********************************************************************/ +/* FUNCTION: Print */ +/**/ +/* INPUTS: none */ +/**/ +/* OUTPUT: none */ +/**/ +/* EFFECT: This function recursively prints the nodes of the tree */ +/* inorder. */ +/**/ +/* Modifies Input: none */ +/**/ +/***********************************************************************/ + +void +IT_print(const IntervalTree *it) +{ + TreePrintHelper(it, it->root->left); +} + +/***********************************************************************/ +/* FUNCTION: DeleteFixUp */ +/**/ +/* INPUTS: x is the child of the spliced */ +/* out node in DeleteNode. */ +/**/ +/* OUTPUT: none */ +/**/ +/* EFFECT: Performs rotations and changes colors to restore red-black */ +/* properties after a node is deleted */ +/**/ +/* Modifies Input: this, x */ +/**/ +/* The algorithm from this function is from _Introduction_To_Algorithms_ */ +/***********************************************************************/ + +static void +DeleteFixUp(IntervalTree *it, IntervalTreeNode *x) +{ + IntervalTreeNode *w; + IntervalTreeNode *rootLeft = it->root->left; + + while ((!x->red) && (rootLeft != x)) { + if (x == x->parent->left) { + w=x->parent->right; + if (w->red) { + w->red=0; + x->parent->red=1; + LeftRotate(it, x->parent); + w=x->parent->right; + } + if ( (!w->right->red) && (!w->left->red) ) { + w->red=1; + x=x->parent; + } else { + if (!w->right->red) { + w->left->red=0; + w->red=1; + RightRotate(it, w); + w=x->parent->right; + } + w->red=x->parent->red; + x->parent->red=0; + w->right->red=0; + LeftRotate(it, x->parent); + x=rootLeft; /* this is to exit while loop */ + } + } else { /* the code below is has left and right switched from above */ + w=x->parent->left; + if (w->red) { + w->red=0; + x->parent->red=1; + RightRotate(it, x->parent); + w=x->parent->left; + } + if ((!w->right->red) && (!w->left->red)) { + w->red=1; + x=x->parent; + } else { + if (!w->left->red) { + w->right->red=0; + w->red=1; + LeftRotate(it, w); + w=x->parent->left; + } + w->red=x->parent->red; + x->parent->red=0; + w->left->red=0; + RightRotate(it, x->parent); + x=rootLeft; /* this is to exit while loop */ + } + } + } + x->red=0; + +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#elif defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not black in ITDeleteFixUp"); + Assert((it->nil->maxHigh=LONG_MIN), + "nil->maxHigh != LONG_MIN in ITDeleteFixUp"); +#endif +} + + +/***********************************************************************/ +/* FUNCTION: DeleteNode */ +/**/ +/* INPUTS: tree is the tree to delete node z from */ +/**/ +/* OUTPUT: returns the Interval stored at deleted node */ +/**/ +/* EFFECT: Deletes z from tree and but don't call destructor */ +/* Then calls FixUpMaxHigh to fix maxHigh fields then calls */ +/* ITDeleteFixUp to restore red-black properties */ +/**/ +/* Modifies Input: z */ +/**/ +/* The algorithm from this function is from _Introduction_To_Algorithms_ */ +/***********************************************************************/ + +void * +IT_delete_node(IntervalTree *it, IntervalTreeNode *z, long *low, long *high) +{ + IntervalTreeNode *x, *y; + void *returnValue = z->data; + if (low) + *low = z->low; + if (high) + *high = z->high; + + y= ((z->left == it->nil) || (z->right == it->nil)) ? + z : IT_get_successor(it, z); + x= (y->left == it->nil) ? y->right : y->left; + if (it->root == (x->parent = y->parent)) + /* assignment of y->p to x->p is intentional */ + it->root->left=x; + else { + if (y == y->parent->left) + y->parent->left=x; + else + y->parent->right=x; + } + if (y != z) { /* y should not be nil in this case */ + +#ifdef DEBUG_ASSERT + Assert( (y!=it->nil),"y is nil in DeleteNode \n"); +#endif + /* y is the node to splice out and x is its child */ + + y->maxHigh = INT_MIN; + y->left=z->left; + y->right=z->right; + y->parent=z->parent; + z->left->parent=z->right->parent=y; + if (z == z->parent->left) + z->parent->left=y; + else + z->parent->right=y; + FixUpMaxHigh(it, x->parent); + if (!(y->red)) { + y->red = z->red; + DeleteFixUp(it, x); + } else + y->red = z->red; + yasm_xfree(z); +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#elif defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not black in ITDelete"); + Assert((it->nil->maxHigh=LONG_MIN),"nil->maxHigh != LONG_MIN in ITDelete"); +#endif + } else { + FixUpMaxHigh(it, x->parent); + if (!(y->red)) + DeleteFixUp(it, x); + yasm_xfree(y); +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + IT_CheckAssumptions(it); +#elif defined(DEBUG_ASSERT) + Assert(!it->nil->red,"nil not black in ITDelete"); + Assert((it->nil->maxHigh=LONG_MIN),"nil->maxHigh != LONG_MIN in ITDelete"); +#endif + } + return returnValue; +} + + +/***********************************************************************/ +/* FUNCTION: Overlap */ +/**/ +/* INPUTS: [a1,a2] and [b1,b2] are the low and high endpoints of two */ +/* closed intervals. */ +/**/ +/* OUTPUT: stack containing pointers to the nodes between [low,high] */ +/**/ +/* Modifies Input: none */ +/**/ +/* EFFECT: returns 1 if the intervals overlap, and 0 otherwise */ +/***********************************************************************/ + +static int +Overlap(int a1, int a2, int b1, int b2) +{ + if (a1 <= b1) + return (b1 <= a2); + else + return (a1 <= b2); +} + + +/***********************************************************************/ +/* FUNCTION: Enumerate */ +/**/ +/* INPUTS: tree is the tree to look for intervals overlapping the */ +/* closed interval [low,high] */ +/**/ +/* OUTPUT: stack containing pointers to the nodes overlapping */ +/* [low,high] */ +/**/ +/* Modifies Input: none */ +/**/ +/* EFFECT: Returns a stack containing pointers to nodes containing */ +/* intervals which overlap [low,high] in O(max(N,k*log(N))) */ +/* where N is the number of intervals in the tree and k is */ +/* the number of overlapping intervals */ +/**/ +/* Note: This basic idea for this function comes from the */ +/* _Introduction_To_Algorithms_ book by Cormen et al, but */ +/* modifications were made to return all overlapping intervals */ +/* instead of just the first overlapping interval as in the */ +/* book. The natural way to do this would require recursive */ +/* calls of a basic search function. I translated the */ +/* recursive version into an interative version with a stack */ +/* as described below. */ +/***********************************************************************/ + + + +/* The basic idea for the function below is to take the IntervalSearch + * function from the book and modify to find all overlapping intervals + * instead of just one. This means that any time we take the left + * branch down the tree we must also check the right branch if and only if + * we find an overlapping interval in that left branch. Note this is a + * recursive condition because if we go left at the root then go left + * again at the first left child and find an overlap in the left subtree + * of the left child of root we must recursively check the right subtree + * of the left child of root as well as the right child of root. + */ +void +IT_enumerate(IntervalTree *it, long low, long high, void *cbd, + void (*callback) (IntervalTreeNode *node, void *cbd)) +{ + IntervalTreeNode *x=it->root->left; + int stuffToDo = (x != it->nil); + + /* Possible speed up: add min field to prune right searches */ + +#ifdef DEBUG_ASSERT + Assert((it->recursionNodeStackTop == 1), + "recursionStack not empty when entering IntervalTree::Enumerate"); +#endif + it->currentParent = 0; + + while (stuffToDo) { + if (Overlap(low,high,x->low,x->high) ) { + callback(x, cbd); + it->recursionNodeStack[it->currentParent].tryRightBranch=1; + } + if(x->left->maxHigh >= low) { /* implies x != nil */ + if (it->recursionNodeStackTop == it->recursionNodeStackSize) { + it->recursionNodeStackSize *= 2; + it->recursionNodeStack = (it_recursion_node *) + yasm_xrealloc(it->recursionNodeStack, + it->recursionNodeStackSize * sizeof(it_recursion_node)); + } + it->recursionNodeStack[it->recursionNodeStackTop].start_node = x; + it->recursionNodeStack[it->recursionNodeStackTop].tryRightBranch = 0; + it->recursionNodeStack[it->recursionNodeStackTop].parentIndex = it->currentParent; + it->currentParent = it->recursionNodeStackTop++; + x = x->left; + } else { + x = x->right; + } + stuffToDo = (x != it->nil); + while (!stuffToDo && (it->recursionNodeStackTop > 1)) { + if (it->recursionNodeStack[--it->recursionNodeStackTop].tryRightBranch) { + x=it->recursionNodeStack[it->recursionNodeStackTop].start_node->right; + it->currentParent=it->recursionNodeStack[it->recursionNodeStackTop].parentIndex; + it->recursionNodeStack[it->currentParent].tryRightBranch=1; + stuffToDo = (x != it->nil); + } + } + } +#ifdef DEBUG_ASSERT + Assert((it->recursionNodeStackTop == 1), + "recursionStack not empty when exiting IntervalTree::Enumerate"); +#endif +} + +#ifdef CHECK_INTERVAL_TREE_ASSUMPTIONS + +static int +CheckMaxHighFieldsHelper(const IntervalTree *it, IntervalTreeNode *y, + int currentHigh, int match) +{ + if (y != it->nil) { + match = CheckMaxHighFieldsHelper(it, y->left, currentHigh, match) ? + 1 : match; + VERIFY(y->high <= currentHigh); + if (y->high == currentHigh) + match = 1; + match = CheckMaxHighFieldsHelper(it, y->right, currentHigh, match) ? + 1 : match; + } + return match; +} + + + +/* Make sure the maxHigh fields for everything makes sense. * + * If something is wrong, print a warning and exit */ +static void +CheckMaxHighFields(const IntervalTree *it, IntervalTreeNode *x) +{ + if (x != it->nil) { + CheckMaxHighFields(it, x->left); + if(!(CheckMaxHighFieldsHelper(it, x, x->maxHigh, 0) > 0)) { + fprintf(stderr, "error found in CheckMaxHighFields.\n"); + abort(); + } + CheckMaxHighFields(it, x->right); + } +} + +static void +IT_CheckAssumptions(const IntervalTree *it) +{ + VERIFY(it->nil->low == INT_MIN); + VERIFY(it->nil->high == INT_MIN); + VERIFY(it->nil->maxHigh == INT_MIN); + VERIFY(it->root->low == INT_MAX); + VERIFY(it->root->high == INT_MAX); + VERIFY(it->root->maxHigh == INT_MAX); + VERIFY(it->nil->data == NULL); + VERIFY(it->root->data == NULL); + VERIFY(it->nil->red == 0); + VERIFY(it->root->red == 0); + CheckMaxHighFields(it, it->root->left); +} +#endif + diff --git a/libyasm/inttree.h b/libyasm/inttree.h new file mode 100644 index 0000000..f7a7651 --- /dev/null +++ b/libyasm/inttree.h @@ -0,0 +1,70 @@ +#ifndef YASM_INTTREE_H +#define YASM_INTTREE_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/* The interval_tree.h and interval_tree.cc files contain code for + * interval trees implemented using red-black-trees as described in + * the book _Introduction_To_Algorithms_ by Cormen, Leisserson, + * and Rivest. + */ + +typedef struct IntervalTreeNode { + struct IntervalTreeNode *left, *right, *parent; + void *data; + long low; + long high; + long maxHigh; + int red; /* if red=0 then the node is black */ +} IntervalTreeNode; + +typedef struct it_recursion_node { + /* This structure stores the information needed when we take the + * right branch in searching for intervals but possibly come back + * and check the left branch as well. + */ + IntervalTreeNode *start_node; + unsigned int parentIndex; + int tryRightBranch; +} it_recursion_node; + +typedef struct IntervalTree { + /* A sentinel is used for root and for nil. These sentinels are + * created when ITTreeCreate is called. root->left should always + * point to the node which is the root of the tree. nil points to a + * node which should always be black but has aribtrary children and + * parent and no key or info. The point of using these sentinels is so + * that the root and nil nodes do not require special cases in the code + */ + IntervalTreeNode *root; + IntervalTreeNode *nil; + +/*private:*/ + unsigned int recursionNodeStackSize; + it_recursion_node * recursionNodeStack; + unsigned int currentParent; + unsigned int recursionNodeStackTop; +} IntervalTree; + +YASM_LIB_DECL +IntervalTree *IT_create(void); +YASM_LIB_DECL +void IT_destroy(IntervalTree *); +YASM_LIB_DECL +void IT_print(const IntervalTree *); +YASM_LIB_DECL +void *IT_delete_node(IntervalTree *, IntervalTreeNode *, long *low, + long *high); +YASM_LIB_DECL +IntervalTreeNode *IT_insert(IntervalTree *, long low, long high, void *data); +YASM_LIB_DECL +IntervalTreeNode *IT_get_predecessor(const IntervalTree *, IntervalTreeNode *); +YASM_LIB_DECL +IntervalTreeNode *IT_get_successor(const IntervalTree *, IntervalTreeNode *); +YASM_LIB_DECL +void IT_enumerate(IntervalTree *, long low, long high, void *cbd, + void (*callback) (IntervalTreeNode *node, void *cbd)); + +#endif diff --git a/libyasm/linemap.c b/libyasm/linemap.c new file mode 100644 index 0000000..42201d3 --- /dev/null +++ b/libyasm/linemap.c @@ -0,0 +1,293 @@ +/* + * YASM assembler virtual line mapping handling (for parse stage) + * + * Copyright (C) 2002-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "coretype.h" +#include "hamt.h" + +#include "errwarn.h" +#include "linemap.h" + + +typedef struct line_mapping { + /* monotonically increasing virtual line */ + unsigned long line; + + /* related info */ + /* "original" source filename */ + /*@null@*/ /*@dependent@*/ const char *filename; + /* "original" source base line number */ + unsigned long file_line; + /* "original" source line number increment (for following lines) */ + unsigned long line_inc; +} line_mapping; + +typedef struct line_source_info { + /* first bytecode on line; NULL if no bytecodes on line */ + /*@null@*/ /*@dependent@*/ yasm_bytecode *bc; + + /* source code line */ + /*@owned@*/ char *source; +} line_source_info; + +struct yasm_linemap { + /* Shared storage for filenames */ + /*@only@*/ /*@null@*/ HAMT *filenames; + + /* Current virtual line number. */ + unsigned long current; + + /* Mappings from virtual to physical line numbers */ + struct line_mapping *map_vector; + unsigned long map_size; + unsigned long map_allocated; + + /* Bytecode and source line information */ + /*@only@*/ line_source_info *source_info; + size_t source_info_size; +}; + +static void +filename_delete_one(/*@only@*/ void *d) +{ + yasm_xfree(d); +} + +void +yasm_linemap_set(yasm_linemap *linemap, const char *filename, + unsigned long virtual_line, unsigned long file_line, + unsigned long line_inc) +{ + char *copy; + unsigned long i; + int replace = 0; + line_mapping *mapping = NULL; + + if (virtual_line == 0) { + virtual_line = linemap->current; + } + + /* Replace all existing mappings that have line numbers >= this one. */ + for (i = linemap->map_size; i > 0; i--) { + if (linemap->map_vector[i-1].line < virtual_line) { + if (i < linemap->map_size) { + mapping = &linemap->map_vector[i]; + linemap->map_size = i + 1; + } + break; + } + } + + if (mapping == NULL) { + /* Create a new mapping in the map */ + if (linemap->map_size >= linemap->map_allocated) { + /* allocate another size bins when full for 2x space */ + linemap->map_vector = yasm_xrealloc(linemap->map_vector, + 2*linemap->map_allocated*sizeof(line_mapping)); + linemap->map_allocated *= 2; + } + mapping = &linemap->map_vector[linemap->map_size]; + linemap->map_size++; + } + + /* Fill it */ + + if (!filename) { + if (linemap->map_size >= 2) + mapping->filename = + linemap->map_vector[linemap->map_size-2].filename; + else + filename = "unknown"; + } + if (filename) { + /* Copy the filename (via shared storage) */ + copy = yasm__xstrdup(filename); + /*@-aliasunique@*/ + mapping->filename = HAMT_insert(linemap->filenames, copy, copy, + &replace, filename_delete_one); + /*@=aliasunique@*/ + } + + mapping->line = virtual_line; + mapping->file_line = file_line; + mapping->line_inc = line_inc; +} + +unsigned long +yasm_linemap_poke(yasm_linemap *linemap, const char *filename, + unsigned long file_line) +{ + unsigned long line; + line_mapping *mapping; + + linemap->current++; + yasm_linemap_set(linemap, filename, 0, file_line, 0); + + mapping = &linemap->map_vector[linemap->map_size-1]; + + line = linemap->current; + + linemap->current++; + yasm_linemap_set(linemap, mapping->filename, 0, + mapping->file_line + + mapping->line_inc*(linemap->current-2-mapping->line), + mapping->line_inc); + + return line; +} + +yasm_linemap * +yasm_linemap_create(void) +{ + size_t i; + yasm_linemap *linemap = yasm_xmalloc(sizeof(yasm_linemap)); + + linemap->filenames = HAMT_create(0, yasm_internal_error_); + + linemap->current = 1; + + /* initialize mapping vector */ + linemap->map_vector = yasm_xmalloc(8*sizeof(line_mapping)); + linemap->map_size = 0; + linemap->map_allocated = 8; + + /* initialize source line information array */ + linemap->source_info_size = 2; + linemap->source_info = yasm_xmalloc(linemap->source_info_size * + sizeof(line_source_info)); + for (i=0; i<linemap->source_info_size; i++) { + linemap->source_info[i].bc = NULL; + linemap->source_info[i].source = NULL; + } + + return linemap; +} + +void +yasm_linemap_destroy(yasm_linemap *linemap) +{ + size_t i; + for (i=0; i<linemap->source_info_size; i++) { + if (linemap->source_info[i].source) + yasm_xfree(linemap->source_info[i].source); + } + yasm_xfree(linemap->source_info); + + yasm_xfree(linemap->map_vector); + + if (linemap->filenames) + HAMT_destroy(linemap->filenames, filename_delete_one); + + yasm_xfree(linemap); +} + +unsigned long +yasm_linemap_get_current(yasm_linemap *linemap) +{ + return linemap->current; +} + +void +yasm_linemap_add_source(yasm_linemap *linemap, yasm_bytecode *bc, + const char *source) +{ + size_t i; + + while (linemap->current > linemap->source_info_size) { + /* allocate another size bins when full for 2x space */ + linemap->source_info = yasm_xrealloc(linemap->source_info, + 2*linemap->source_info_size*sizeof(line_source_info)); + for (i=linemap->source_info_size; i<linemap->source_info_size*2; i++) { + linemap->source_info[i].bc = NULL; + linemap->source_info[i].source = NULL; + } + linemap->source_info_size *= 2; + } + + /* Delete existing info for that line (if any) */ + if (linemap->source_info[linemap->current-1].source) + yasm_xfree(linemap->source_info[linemap->current-1].source); + + linemap->source_info[linemap->current-1].bc = bc; + linemap->source_info[linemap->current-1].source = yasm__xstrdup(source); +} + +unsigned long +yasm_linemap_goto_next(yasm_linemap *linemap) +{ + return ++(linemap->current); +} + +void +yasm_linemap_lookup(yasm_linemap *linemap, unsigned long line, + const char **filename, unsigned long *file_line) +{ + line_mapping *mapping; + unsigned long vindex, step; + + assert(line <= linemap->current); + + /* Binary search through map to find highest line_index <= index */ + vindex = 0; + /* start step as the greatest power of 2 <= size */ + step = 1; + while (step*2<=linemap->map_size) + step*=2; + while (step>0) { + if (vindex+step < linemap->map_size + && linemap->map_vector[vindex+step].line <= line) + vindex += step; + step /= 2; + } + mapping = &linemap->map_vector[vindex]; + + *filename = mapping->filename; + *file_line = (line ? mapping->file_line + mapping->line_inc*(line-mapping->line) : 0); +} + +int +yasm_linemap_traverse_filenames(yasm_linemap *linemap, /*@null@*/ void *d, + int (*func) (const char *filename, void *d)) +{ + return HAMT_traverse(linemap->filenames, d, (int (*) (void *, void *))func); +} + +int +yasm_linemap_get_source(yasm_linemap *linemap, unsigned long line, + yasm_bytecode **bcp, const char **sourcep) +{ + if (line > linemap->source_info_size) { + *bcp = NULL; + *sourcep = NULL; + return 1; + } + + *bcp = linemap->source_info[line-1].bc; + *sourcep = linemap->source_info[line-1].source; + + return (!(*sourcep)); +} diff --git a/libyasm/linemap.h b/libyasm/linemap.h new file mode 100644 index 0000000..1c5aa46 --- /dev/null +++ b/libyasm/linemap.h @@ -0,0 +1,141 @@ +/** + * \file libyasm/linemap.h + * \brief YASM virtual line mapping interface. + * + * \license + * Copyright (C) 2002-2007 Peter Johnson + * + * 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_LINEMAP_H +#define YASM_LINEMAP_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Create a new line mapping repository. + * \return New repository. + */ +YASM_LIB_DECL +yasm_linemap *yasm_linemap_create(void); + +/** Clean up any memory allocated for a repository. + * \param linemap line mapping repository + */ +YASM_LIB_DECL +void yasm_linemap_destroy(yasm_linemap *linemap); + +/** Get the current line position in a repository. + * \param linemap line mapping repository + * \return Current virtual line. + */ +YASM_LIB_DECL +unsigned long yasm_linemap_get_current(yasm_linemap *linemap); + +/** Get bytecode and source line information, if any, for a virtual line. + * \param linemap line mapping repository + * \param line virtual line + * \param bcp pointer to return bytecode into + * \param sourcep pointer to return source code line pointer into + * \return Zero if source line information available for line, nonzero if not. + * \note If source line information is not available, bcp and sourcep targets + * are set to NULL. + */ +YASM_LIB_DECL +int yasm_linemap_get_source(yasm_linemap *linemap, unsigned long line, + /*@null@*/ yasm_bytecode **bcp, + const char **sourcep); + +/** Add bytecode and source line information to the current virtual line. + * \attention Deletes any existing bytecode and source line information for + * the current virtual line. + * \param linemap line mapping repository + * \param bc bytecode (if any) + * \param source source code line + * \note The source code line pointer is NOT kept, it is strdup'ed. + */ +YASM_LIB_DECL +void yasm_linemap_add_source(yasm_linemap *linemap, + /*@null@*/ yasm_bytecode *bc, + const char *source); + +/** Go to the next line (increments the current virtual line). + * \param linemap line mapping repository + * \return The current (new) virtual line. + */ +YASM_LIB_DECL +unsigned long yasm_linemap_goto_next(yasm_linemap *linemap); + +/** Set a new file/line physical association starting point at the specified + * virtual line. line_inc indicates how much the "real" line is incremented + * by for each virtual line increment (0 is perfectly legal). + * \param linemap line mapping repository + * \param filename physical file name (if NULL, not changed) + * \param virtual_line virtual line number (if 0, linemap->current is used) + * \param file_line physical line number + * \param line_inc line increment + */ +YASM_LIB_DECL +void yasm_linemap_set(yasm_linemap *linemap, /*@null@*/ const char *filename, + unsigned long virtual_line, unsigned long file_line, + unsigned long line_inc); + +/** Poke a single file/line association, restoring the original physical + * association starting point. Caution: increments the current virtual line + * twice. + * \param linemap line mapping repository + * \param filename physical file name (if NULL, not changed) + * \param file_line physical line number + * \return The virtual line number of the poked association. + */ +YASM_LIB_DECL +unsigned long yasm_linemap_poke(yasm_linemap *linemap, + /*@null@*/ const char *filename, + unsigned long file_line); + +/** Look up the associated physical file and line for a virtual line. + * \param linemap line mapping repository + * \param line virtual line + * \param filename physical file name (output) + * \param file_line physical line number (output) + */ +YASM_LIB_DECL +void yasm_linemap_lookup(yasm_linemap *linemap, unsigned long line, + /*@out@*/ const char **filename, + /*@out@*/ unsigned long *file_line); + +/** Traverses all filenames used in a linemap, calling a function on each + * filename. + * \param linemap line mapping repository + * \param d data pointer passed to func on each call + * \param func function + * \return Stops early (and returns func's return value) if func returns a + * nonzero value; otherwise 0. + */ +YASM_LIB_DECL +int yasm_linemap_traverse_filenames + (yasm_linemap *linemap, /*@null@*/ void *d, + int (*func) (const char *filename, void *d)); + +#endif diff --git a/libyasm/listfmt.h b/libyasm/listfmt.h new file mode 100644 index 0000000..945f28e --- /dev/null +++ b/libyasm/listfmt.h @@ -0,0 +1,124 @@ +/** + * \file libyasm/listfmt.h + * \brief YASM list format interface. + * + * \license + * Copyright (C) 2004-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_LISTFMT_H +#define YASM_LISTFMT_H + +#ifndef YASM_DOXYGEN +/** Base #yasm_listfmt structure. Must be present as the first element in any + * #yasm_listfmt implementation. + */ +typedef struct yasm_listfmt_base { + /** #yasm_listfmt_module implementation for this list format. */ + const struct yasm_listfmt_module *module; +} yasm_listfmt_base; +#endif + +/** YASM list format module interface. */ +typedef struct yasm_listfmt_module { + /** One-line description of the list format. */ + const char *name; + + /** Keyword used to select list format. */ + const char *keyword; + + /** Create list format. + * Module-level implementation of yasm_listfmt_create(). + * The filenames are provided solely for informational purposes. + * \param in_filename primary input filename + * \param obj_filename object filename + * \return NULL if unable to initialize. + */ + /*@null@*/ /*@only@*/ yasm_listfmt * (*create) + (const char *in_filename, const char *obj_filename); + + /** Module-level implementation of yasm_listfmt_destroy(). + * Call yasm_listfmt_destroy() instead of calling this function. + */ + void (*destroy) (/*@only@*/ yasm_listfmt *listfmt); + + /** Module-level implementation of yasm_listfmt_output(). + * Call yasm_listfmt_output() instead of calling this function. + */ + void (*output) (yasm_listfmt *listfmt, FILE *f, yasm_linemap *linemap, + yasm_arch *arch); +} yasm_listfmt_module; + +/** Get the keyword used to select a list format. + * \param listfmt list format + * \return keyword + */ +const char *yasm_listfmt_keyword(const yasm_listfmt *listfmt); + +/** Initialize list format for use. Must call before any other list + * format functions. The filenames are provided solely for informational + * purposes. + * \param module list format module + * \param in_filename primary input filename + * \param obj_filename object filename + * \return NULL if object format does not provide needed support. + */ +/*@null@*/ /*@only@*/ yasm_listfmt *yasm_listfmt_create + (const yasm_listfmt_module *module, const char *in_filename, + const char *obj_filename); + +/** Cleans up any allocated list format memory. + * \param listfmt list format + */ +void yasm_listfmt_destroy(/*@only@*/ yasm_listfmt *listfmt); + +/** Write out list to the list file. + * This function may call all read-only yasm_* functions as necessary. + * \param listfmt list format + * \param f output list file + * \param linemap line mapping repository + * \param arch architecture + */ +void yasm_listfmt_output(yasm_listfmt *listfmt, FILE *f, + yasm_linemap *linemap, yasm_arch *arch); + +#ifndef YASM_DOXYGEN + +/* Inline macro implementations for listfmt functions */ + +#define yasm_listfmt_keyword(listfmt) \ + (((yasm_listfmt_base *)listfmt)->module->keyword) + +#define yasm_listfmt_create(module, in_filename, obj_filename) \ + module->create(in_filename, obj_filename) + +#define yasm_listfmt_destroy(listfmt) \ + ((yasm_listfmt_base *)listfmt)->module->destroy(listfmt) + +#define yasm_listfmt_output(listfmt, f, linemap, a) \ + ((yasm_listfmt_base *)listfmt)->module->output(listfmt, f, linemap, a) + +#endif + +#endif diff --git a/libyasm/md5.c b/libyasm/md5.c new file mode 100644 index 0000000..ba1a307 --- /dev/null +++ b/libyasm/md5.c @@ -0,0 +1,309 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to + not require an integer type which is exactly 32 bits. This work + draws on the changes for the same purpose by Tatu Ylonen + <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use + that code, there is no copyright issue. I hereby disclaim + copyright in any changes I have made; this code remains in the + public domain. */ + +/* Note regarding cvs_* namespace: this avoids potential conflicts + with libraries such as some versions of Kerberos. No particular + need to worry about whether the system supplies an MD5 library, as + this file is only about 3k of object code. */ + +#include <util.h> + +#include "md5.h" + +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as cvs_uint32, nor do they require + us to detect the endianness of the machine we are running on. It + is possible they should be macros for speed, but I would be + surprised if they were a performance bottleneck for MD5. */ + +static unsigned long +getu32(const unsigned char *addr) +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32(unsigned long data, unsigned char *addr) +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +yasm_md5_init(yasm_md5_context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +yasm_md5_update(yasm_md5_context *ctx, unsigned char const *buf, + unsigned long len) +{ + unsigned long t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((unsigned long)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + yasm_md5_transform (ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + yasm_md5_transform (ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +yasm_md5_final(unsigned char digest[16], yasm_md5_context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + yasm_md5_transform (ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + yasm_md5_transform (ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +yasm_md5_transform(unsigned long buf[4], const unsigned char inraw[64]) +{ + register unsigned long a, b, c, d; + unsigned long in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif + +#ifdef TEST +/* Simple test program. Can use it to manually run the tests from + RFC1321 for example. */ +#include <stdio.h> + +int +main (int argc, char **argv) +{ + yasm_md5_context context; + unsigned char checksum[16]; + int i; + int j; + + if (argc < 2) + { + fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); + exit (1); + } + for (j = 1; j < argc; ++j) + { + printf ("MD5 (\"%s\") = ", argv[j]); + yasm_md5_init (&context); + yasm_md5_update (&context, argv[j], strlen (argv[j])); + yasm_md5_final (checksum, &context); + for (i = 0; i < 16; i++) + { + printf ("%02x", (unsigned int) checksum[i]); + } + printf ("\n"); + } + return 0; +} +#endif /* TEST */ diff --git a/libyasm/md5.h b/libyasm/md5.h new file mode 100644 index 0000000..7872fda --- /dev/null +++ b/libyasm/md5.h @@ -0,0 +1,32 @@ +/* See md5.c for explanation and copyright information. */ + +#ifndef YASM_MD5_H +#define YASM_MD5_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/* Unlike previous versions of this code, uint32 need not be exactly + 32 bits, merely 32 bits or more. Choosing a data type which is 32 + bits instead of 64 is not important; speed is considerably more + important. ANSI guarantees that "unsigned long" will be big enough, + and always using it seems to have few disadvantages. */ + +typedef struct yasm_md5_context { + unsigned long buf[4]; + unsigned long bits[2]; + unsigned char in[64]; +} yasm_md5_context; + +YASM_LIB_DECL +void yasm_md5_init(yasm_md5_context *context); +YASM_LIB_DECL +void yasm_md5_update(yasm_md5_context *context, unsigned char const *buf, + unsigned long len); +YASM_LIB_DECL +void yasm_md5_final(unsigned char digest[16], yasm_md5_context *context); +YASM_LIB_DECL +void yasm_md5_transform(unsigned long buf[4], const unsigned char in[64]); + +#endif /* !YASM_MD5_H */ diff --git a/libyasm/mergesort.c b/libyasm/mergesort.c new file mode 100644 index 0000000..3eeaa82 --- /dev/null +++ b/libyasm/mergesort.c @@ -0,0 +1,361 @@ +/* + * mergesort() implementation for systems that don't have it. + * + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Peter McIlroy. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "util.h" + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)merge.c 8.2 (Berkeley) 2/14/94"; +#endif /* LIBC_SCCS and not lint */ + +#ifdef HAVE_MERGESORT +#undef yasm__mergesort +#endif + +#ifndef HAVE_MERGESORT +/* + * Hybrid exponential search/linear search merge sort with hybrid + * natural/pairwise first pass. Requires about .3% more comparisons + * for random data than LSMS with pairwise first pass alone. + * It works for objects as small as two bytes. + */ + +#define NATURAL +#define THRESHOLD 16 /* Best choice for natural merge cut-off. */ + +/* #define NATURAL to get hybrid natural merge. + * (The default is pairwise merging.) + */ + +#include <errno.h> +#include <string.h> + +static void setup(unsigned char *, unsigned char *, size_t, size_t, + int (*)(const void *, const void *)); +static void insertionsort(unsigned char *, size_t, size_t, + int (*)(const void *, const void *)); + +#define ISIZE sizeof(int) +#define PSIZE sizeof(unsigned char *) +#define ICOPY_LIST(src, dst, last) \ + do \ + *(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE; \ + while(src < last) +#define ICOPY_ELT(src, dst, i) \ + do \ + *(int*) dst = *(int*) src, src += ISIZE, dst += ISIZE; \ + while (i -= ISIZE) + +#define CCOPY_LIST(src, dst, last) \ + do \ + *dst++ = *src++; \ + while (src < last) +#define CCOPY_ELT(src, dst, i) \ + do \ + *dst++ = *src++; \ + while (i -= 1) + +/* + * Find the next possible pointer head. (Trickery for forcing an array + * to do double duty as a linked list when objects do not align with word + * boundaries. + */ +/* Assumption: PSIZE is a power of 2. */ +#define EVAL(p) (unsigned char **) \ + ((unsigned char *)0 + \ + (((unsigned char *)p + PSIZE - 1 - (unsigned char *) 0) & ~(PSIZE - 1))) +#endif /*HAVE_MERGESORT*/ + +/* + * Arguments are as for qsort. + */ +int +yasm__mergesort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *)) +{ +#ifdef HAVE_MERGESORT + return mergesort(base, nmemb, size, cmp); +#else + size_t i; + int sense; + int big, iflag; + unsigned char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; + unsigned char *list2, *list1, *p2, *p, *last, **p1; + + if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ +#ifdef EINVAL + errno = EINVAL; +#endif + return (-1); + } + + if (nmemb == 0) + return (0); + + /* + * XXX + * Stupid subtraction for the Cray. + */ + iflag = 0; + if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) + iflag = 1; + + if ((list2 = yasm_xmalloc(nmemb * size + PSIZE)) == NULL) + return (-1); + + list1 = base; + setup(list1, list2, nmemb, size, cmp); + last = list2 + nmemb * size; + i = 0; + big = 0; + while (*EVAL(list2) != last) { + l2 = list1; + p1 = EVAL(list1); + for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { + p2 = *EVAL(p2); + f1 = l2; + f2 = l1 = list1 + (p2 - list2); + if (p2 != last) + p2 = *EVAL(p2); + l2 = list1 + (p2 - list2); + while (f1 < l1 && f2 < l2) { + if ((*cmp)(f1, f2) <= 0) { + q = f2; + b = f1, t = l1; + sense = -1; + } else { + q = f1; + b = f2, t = l2; + sense = 0; + } + if (!big) { /* here i = 0 */ + while ((b += size) < t && cmp(q, b) >sense) + if (++i == 6) { + big = 1; + goto EXPONENTIAL; + } + } else { +EXPONENTIAL: for (i = size; ; i <<= 1) + if ((p = (b + i)) >= t) { + if ((p = t - size) > b && + (*cmp)(q, p) <= sense) + t = p; + else + b = p; + break; + } else if ((*cmp)(q, p) <= sense) { + t = p; + if (i == size) + big = 0; + goto FASTCASE; + } else + b = p; + while (t > b+size) { + i = (((t - b) / size) >> 1) * size; + if ((*cmp)(q, p = b + i) <= sense) + t = p; + else + b = p; + } + goto COPY; +FASTCASE: while (i > size) + if ((*cmp)(q, + p = b + (i >>= 1)) <= sense) + t = p; + else + b = p; +COPY: b = t; + } + i = size; + if (q == f1) { + if (iflag) { + ICOPY_LIST(f2, tp2, b); + ICOPY_ELT(f1, tp2, i); + } else { + CCOPY_LIST(f2, tp2, b); + CCOPY_ELT(f1, tp2, i); + } + } else { + if (iflag) { + ICOPY_LIST(f1, tp2, b); + ICOPY_ELT(f2, tp2, i); + } else { + CCOPY_LIST(f1, tp2, b); + CCOPY_ELT(f2, tp2, i); + } + } + } + if (f2 < l2) { + if (iflag) + ICOPY_LIST(f2, tp2, l2); + else + CCOPY_LIST(f2, tp2, l2); + } else if (f1 < l1) { + if (iflag) + ICOPY_LIST(f1, tp2, l1); + else + CCOPY_LIST(f1, tp2, l1); + } + *p1 = l2; + } + tp2 = list1; /* swap list1, list2 */ + list1 = list2; + list2 = tp2; + last = list2 + nmemb*size; + } + if (base == list2) { + memmove(list2, list1, nmemb*size); + list2 = list1; + } + yasm_xfree(list2); + return (0); +#endif /*HAVE_MERGESORT*/ +} + +#ifndef HAVE_MERGESORT + +#define swap(a, b) { \ + s = b; \ + i = size; \ + do { \ + tmp = *a; *a++ = *s; *s++ = tmp; \ + } while (--i); \ + a -= size; \ + } +#define reverse(bot, top) { \ + s = top; \ + do { \ + i = size; \ + do { \ + tmp = *bot; *bot++ = *s; *s++ = tmp; \ + } while (--i); \ + s -= size2; \ + } while(bot < s); \ +} + +/* + * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of + * increasing order, list2 in a corresponding linked list. Checks for runs + * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL + * is defined. Otherwise simple pairwise merging is used.) + */ +void +setup(unsigned char *list1, unsigned char *list2, size_t n, size_t size, + int (*cmp)(const void *, const void *)) +{ + size_t i; + unsigned int tmp; + int length, sense; + size_t size2; + unsigned char *f1, *f2, *s, *l2, *last, *p2; + + size2 = size*2; + if (n <= 5) { + insertionsort(list1, n, size, cmp); + *EVAL(list2) = (unsigned char*) list2 + n*size; + return; + } + /* + * Avoid running pointers out of bounds; limit n to evens + * for simplicity. + */ + i = 4 + (n & 1); + insertionsort(list1 + (n - i) * size, i, size, cmp); + last = list1 + size * (n - i); + *EVAL(list2 + (last - list1)) = list2 + n * size; + +#ifdef NATURAL + p2 = list2; + f1 = list1; + sense = (cmp(f1, f1 + size) > 0); + for (; f1 < last; sense = !sense) { + length = 2; + /* Find pairs with same sense. */ + for (f2 = f1 + size2; f2 < last; f2 += size2) { + if ((cmp(f2, f2+ size) > 0) != sense) + break; + length += 2; + } + if (length < THRESHOLD) { /* Pairwise merge */ + do { + p2 = *EVAL(p2) = f1 + size2 - list1 + list2; + if (sense > 0) + swap (f1, f1 + size); + } while ((f1 += size2) < f2); + } else { /* Natural merge */ + l2 = f2; + for (f2 = f1 + size2; f2 < l2; f2 += size2) { + if ((cmp(f2-size, f2) > 0) != sense) { + p2 = *EVAL(p2) = f2 - list1 + list2; + if (sense > 0) + reverse(f1, f2-size); + f1 = f2; + } + } + if (sense > 0) + reverse (f1, f2-size); + f1 = f2; + if (f2 < last || cmp(f2 - size, f2) > 0) + p2 = *EVAL(p2) = f2 - list1 + list2; + else + p2 = *EVAL(p2) = list2 + n*size; + } + } +#else /* pairwise merge only. */ + for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { + p2 = *EVAL(p2) = p2 + size2; + if (cmp (f1, f1 + size) > 0) + swap(f1, f1 + size); + } +#endif /* NATURAL */ +} + +/* + * This is to avoid out-of-bounds addresses in sorting the + * last 4 elements. + */ +static void +insertionsort(unsigned char *a, size_t n, size_t size, + int (*cmp)(const void *, const void *)) +{ + unsigned char *ai, *s, *t, *u, tmp; + size_t i; + + for (ai = a+size; --n >= 1; ai += size) + for (t = ai; t > a; t -= size) { + u = t - size; + if (cmp(u, t) <= 0) + break; + swap(u, t); + } +} +#endif /*HAVE_MERGESORT*/ diff --git a/libyasm/module.h b/libyasm/module.h new file mode 100644 index 0000000..220017d --- /dev/null +++ b/libyasm/module.h @@ -0,0 +1,82 @@ +/* + * YASM module loader header file + * + * Copyright (C) 2002-2007 Peter Johnson + * + * 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 OTHER 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 OTHER 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. + */ +#ifndef YASM_MODULE_H +#define YASM_MODULE_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +typedef enum yasm_module_type { + YASM_MODULE_ARCH = 0, + YASM_MODULE_DBGFMT, + YASM_MODULE_OBJFMT, + YASM_MODULE_LISTFMT, + YASM_MODULE_PARSER, + YASM_MODULE_PREPROC +} yasm_module_type; + +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ void *yasm_load_module + (yasm_module_type type, const char *keyword); + +#define yasm_load_arch(keyword) \ + yasm_load_module(YASM_MODULE_ARCH, keyword) +#define yasm_load_dbgfmt(keyword) \ + yasm_load_module(YASM_MODULE_DBGFMT, keyword) +#define yasm_load_objfmt(keyword) \ + yasm_load_module(YASM_MODULE_OBJFMT, keyword) +#define yasm_load_listfmt(keyword) \ + yasm_load_module(YASM_MODULE_LISTFMT, keyword) +#define yasm_load_parser(keyword) \ + yasm_load_module(YASM_MODULE_PARSER, keyword) +#define yasm_load_preproc(keyword) \ + yasm_load_module(YASM_MODULE_PREPROC, keyword) + +YASM_LIB_DECL +void yasm_list_modules + (yasm_module_type type, + void (*printfunc) (const char *name, const char *keyword)); + +#define yasm_list_arch(func) \ + yasm_list_modules(YASM_MODULE_ARCH, func) +#define yasm_list_dbgfmt(func) \ + yasm_list_modules(YASM_MODULE_DBGFMT, func) +#define yasm_list_objfmt(func) \ + yasm_list_modules(YASM_MODULE_OBJFMT, func) +#define yasm_list_listfmt(func) \ + yasm_list_modules(YASM_MODULE_LISTFMT, func) +#define yasm_list_parser(func) \ + yasm_list_modules(YASM_MODULE_PARSER, func) +#define yasm_list_preproc(func) \ + yasm_list_modules(YASM_MODULE_PREPROC, func) + +YASM_LIB_DECL +void yasm_register_module(yasm_module_type type, const char *keyword, + void *data); + +#endif diff --git a/libyasm/module.in b/libyasm/module.in new file mode 100644 index 0000000..a18778b --- /dev/null +++ b/libyasm/module.in @@ -0,0 +1,179 @@ +/* + * YASM module loader + * + * Copyright (C) 2004-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <util.h> + +#include <libyasm.h> + + +typedef struct module { + const char *keyword; /* module keyword */ + void *data; /* associated data */ +} module; + +EXTERN_LIST + +static module arch_modules[] = { +MODULES_arch_ +}; + +static module dbgfmt_modules[] = { +MODULES_dbgfmt_ +}; + +static module objfmt_modules[] = { +MODULES_objfmt_ +}; + +static module listfmt_modules[] = { +MODULES_listfmt_ +}; + +static module parser_modules[] = { +MODULES_parser_ +}; + +static module preproc_modules[] = { +MODULES_preproc_ +}; + +static struct { + module *m; + size_t n; +} module_types[] = { + {arch_modules, sizeof(arch_modules)/sizeof(module)}, + {dbgfmt_modules, sizeof(dbgfmt_modules)/sizeof(module)}, + {objfmt_modules, sizeof(objfmt_modules)/sizeof(module)}, + {listfmt_modules, sizeof(listfmt_modules)/sizeof(module)}, + {parser_modules, sizeof(parser_modules)/sizeof(module)}, + {preproc_modules, sizeof(preproc_modules)/sizeof(module)}, +}; + +typedef struct loaded_module { + yasm_module_type type; /* module type */ + const char *keyword; /* module keyword */ + void *data; /* associated data */ +} loaded_module; + +static loaded_module *loaded_modules = NULL; +static size_t num_loaded_modules = 0; + +void * +yasm_load_module(yasm_module_type type, const char *keyword) +{ + size_t i; + module *modules; + size_t n; + + /* Look for the module/symbol, first in loaded modules */ + if (loaded_modules) { + for (i=0; i<num_loaded_modules; i++) { + if (loaded_modules[i].type == type && + yasm__strcasecmp(loaded_modules[i].keyword, keyword) == 0) + return loaded_modules[i].data; + } + } + + modules = module_types[type].m; + n = module_types[type].n; + for (i=0; i<n; i++) { + if (yasm__strcasecmp(modules[i].keyword, keyword) == 0) + return modules[i].data; + } + + return NULL; +} + +void +yasm_register_module(yasm_module_type type, const char *keyword, void *data) +{ + loaded_modules = + yasm_xrealloc(loaded_modules, + (num_loaded_modules+1)*sizeof(loaded_module)); + loaded_modules[num_loaded_modules].type = type; + loaded_modules[num_loaded_modules].keyword = keyword; + loaded_modules[num_loaded_modules].data = data; + num_loaded_modules++; +} + +static void +yasm_list_one_module(yasm_module_type type, void *data, + void (*printfunc) (const char *name, const char *keyword)) +{ + yasm_arch_module *arch; + yasm_dbgfmt_module *dbgfmt; + yasm_objfmt_module *objfmt; + yasm_listfmt_module *listfmt; + yasm_parser_module *parser; + yasm_preproc_module *preproc; + + switch (type) { + case YASM_MODULE_ARCH: + arch = data; + printfunc(arch->name, arch->keyword); + break; + case YASM_MODULE_DBGFMT: + dbgfmt = data; + printfunc(dbgfmt->name, dbgfmt->keyword); + break; + case YASM_MODULE_OBJFMT: + objfmt = data; + printfunc(objfmt->name, objfmt->keyword); + break; + case YASM_MODULE_LISTFMT: + listfmt = data; + printfunc(listfmt->name, listfmt->keyword); + break; + case YASM_MODULE_PARSER: + parser = data; + printfunc(parser->name, parser->keyword); + break; + case YASM_MODULE_PREPROC: + preproc = data; + printfunc(preproc->name, preproc->keyword); + break; + } +} + +void +yasm_list_modules(yasm_module_type type, + void (*printfunc) (const char *name, const char *keyword)) +{ + size_t i; + module *modules; + size_t n;; + + /* Go through available list, and try to load each one */ + if (loaded_modules) { + for (i=0; i<num_loaded_modules; i++) + yasm_list_one_module(type, loaded_modules[i].data, printfunc); + } + + modules = module_types[type].m; + n = module_types[type].n; + for (i=0; i<n; i++) + yasm_list_one_module(type, modules[i].data, printfunc); +} diff --git a/libyasm/objfmt.h b/libyasm/objfmt.h new file mode 100644 index 0000000..840296a --- /dev/null +++ b/libyasm/objfmt.h @@ -0,0 +1,216 @@ +/** + * \file libyasm/objfmt.h + * \brief YASM object format module interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_OBJFMT_H +#define YASM_OBJFMT_H + +#ifndef YASM_DOXYGEN +/** Base #yasm_objfmt structure. Must be present as the first element in any + * #yasm_objfmt implementation. + */ +typedef struct yasm_objfmt_base { + /** #yasm_objfmt_module implementation for this object format. */ + const struct yasm_objfmt_module *module; +} yasm_objfmt_base; +#endif + +/** Object format module interface. */ +struct yasm_objfmt_module { + /** One-line description of the object format. */ + const char *name; + + /** Keyword used to select object format. */ + const char *keyword; + + /** Default output file extension (without the '.'). + * NULL means no extension, with no '.', while "" includes the '.'. + */ + /*@null@*/ const char *extension; + + /** Default (starting) x86 BITS setting. This only appies to the x86 + * architecture; other architectures ignore this setting. + */ + const unsigned char default_x86_mode_bits; + + /** If @ signs should be legal in identifiers. */ + const unsigned char id_at_ok; + + /** NULL-terminated list of debug format (yasm_dbgfmt) keywords that are + * valid to use with this object format. The null debug format + * (null_dbgfmt, "null") should always be in this list so it's possible to + * have no debug output. + */ + const char **dbgfmt_keywords; + + /** Default debug format keyword (set even if there's only one available to + * use). + */ + const char *default_dbgfmt_keyword; + + /** NULL-terminated list of directives. NULL if none. */ + /*@null@*/ const yasm_directive *directives; + + /** NULL-terminated list of standard macro lookups. NULL if none. */ + const yasm_stdmac *stdmacs; + + /** Create object format. + * Module-level implementation of yasm_objfmt_create(). + * Call yasm_objfmt_create() instead of calling this function. + * \param object object + * \param a architecture in use + * \return NULL if architecture/machine combination not supported. + */ + /*@null@*/ /*@only@*/ yasm_objfmt * (*create) (yasm_object *object); + + /** Module-level implementation of yasm_objfmt_output(). + * Call yasm_objfmt_output() instead of calling this function. + */ + void (*output) (yasm_object *o, FILE *f, int all_syms, + yasm_errwarns *errwarns); + + /** Module-level implementation of yasm_objfmt_destroy(). + * Call yasm_objfmt_destroy() instead of calling this function. + */ + void (*destroy) (/*@only@*/ yasm_objfmt *objfmt); + + /** Module-level implementation of yasm_objfmt_add_default_section(). + * Call yasm_objfmt_add_default_section() instead of calling this function. + */ + yasm_section * (*add_default_section) (yasm_object *object); + + /** Module-level implementation of yasm_objfmt_init_new_section(). + * Call yasm_objfmt_init_new_section() instead of calling this function. + */ + void (*init_new_section) (yasm_section *section, unsigned long line); + + /** Module-level implementation of yasm_objfmt_section_switch(). + * Call yasm_objfmt_section_switch() instead of calling this function. + */ + /*@observer@*/ /*@null@*/ yasm_section * + (*section_switch)(yasm_object *object, yasm_valparamhead *valparams, + /*@null@*/ yasm_valparamhead *objext_valparams, + unsigned long line); + + /** Module-level implementation of yasm_objfmt_get_special_sym(). + * Call yasm_objfmt_get_special_sym() instead of calling this function. + */ + /*@observer@*/ /*@null@*/ yasm_symrec * + (*get_special_sym)(yasm_object *object, const char *name, + const char *parser); +}; + +/** Create object format. + * \param module object format module + * \param object object + * \return NULL if architecture/machine combination not supported. + */ +/*@null@*/ /*@only@*/ yasm_objfmt *yasm_objfmt_create + (const yasm_objfmt_module *module, yasm_object *object); + +/** Write out (post-optimized) sections to the object file. + * This function may call yasm_symrec_* functions as necessary (including + * yasm_symrec_traverse()) to retrieve symbolic information. + * \param object object + * \param f output object file + * \param all_syms if nonzero, all symbols should be included in + * the object file + * \param errwarns error/warning set + * \note Errors and warnings are stored into errwarns. + */ +void yasm_objfmt_output(yasm_object *object, FILE *f, int all_syms, + yasm_errwarns *errwarns); + +/** Cleans up any allocated object format memory. + * \param objfmt object format + */ +void yasm_objfmt_destroy(/*@only@*/ yasm_objfmt *objfmt); + +/** Add a default section to an object. + * \param object object + * \return Default section. + */ +yasm_section *yasm_objfmt_add_default_section(yasm_object *object); + +/** Initialize the object-format specific portion of a section. Called + * by yasm_object_get_general(); in general should not be directly called. + * \param section section + * \param line virtual line (from yasm_linemap) + */ +void yasm_objfmt_init_new_section(yasm_object *object, unsigned long line); + +/** Switch object file sections. The first val of the valparams should + * be the section name. Calls yasm_object_get_general() to actually get + * the section. + * \param object object + * \param valparams value/parameters + * \param objext_valparams object format-specific value/parameters + * \param line virtual line (from yasm_linemap) + * \return NULL on error, otherwise new section. + */ +/*@observer@*/ /*@null@*/ yasm_section *yasm_objfmt_section_switch + (yasm_object *object, yasm_valparamhead *valparams, + /*@null@*/ yasm_valparamhead *objext_valparams, unsigned long line); + +/** Get a special symbol. Special symbols are generally used to generate + * special relocation types via the WRT mechanism. + * \param object object + * \param name symbol name (not including any parser-specific prefix) + * \param parser parser keyword + * \return NULL if unrecognized, otherwise special symbol. + */ +/*@observer@*/ /*@null@*/ yasm_symrec *yasm_objfmt_get_special_sym + (yasm_object *object, const char *name, const char *parser); + +#ifndef YASM_DOXYGEN + +/* Inline macro implementations for objfmt functions */ + +#define yasm_objfmt_create(module, object) module->create(object) + +#define yasm_objfmt_output(object, f, all_syms, ews) \ + ((yasm_objfmt_base *)((object)->objfmt))->module->output \ + (object, f, all_syms, ews) +#define yasm_objfmt_destroy(objfmt) \ + ((yasm_objfmt_base *)objfmt)->module->destroy(objfmt) +#define yasm_objfmt_section_switch(object, vpms, oe_vpms, line) \ + ((yasm_objfmt_base *)((object)->objfmt))->module->section_switch \ + (object, vpms, oe_vpms, line) +#define yasm_objfmt_add_default_section(object) \ + ((yasm_objfmt_base *)((object)->objfmt))->module->add_default_section \ + (object) +#define yasm_objfmt_init_new_section(section, line) \ + ((yasm_objfmt_base *)((object)->objfmt))->module->init_new_section \ + (section, line) +#define yasm_objfmt_get_special_sym(object, name, parser) \ + ((yasm_objfmt_base *)((object)->objfmt))->module->get_special_sym \ + (object, name, parser) + +#endif + +#endif diff --git a/libyasm/parser.h b/libyasm/parser.h new file mode 100644 index 0000000..93d4d33 --- /dev/null +++ b/libyasm/parser.h @@ -0,0 +1,67 @@ +/** + * \file libyasm/parser.h + * \brief YASM parser module interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_PARSER_H +#define YASM_PARSER_H + +/** YASM parser module interface. The "front end" of the assembler. */ +typedef struct yasm_parser_module { + /** One-line description of the parser */ + const char *name; + + /** Keyword used to select parser on the command line */ + const char *keyword; + + /** NULL-terminated list of preprocessors that are valid to use with this + * parser. The raw preprocessor (raw_preproc) should always be in this + * list so it's always possible to have no preprocessing done. + */ + const char **preproc_keywords; + + /** Default preprocessor. */ + const char *default_preproc_keyword; + + /** NULL-terminated list of standard macro lookups. NULL if none. */ + const yasm_stdmac *stdmacs; + + /** Parse a source file into an object. + * \param object object to parse into (already created) + * \param pp preprocessor + * \param save_input nonzero if the parser should save the original + * lines of source into the object's linemap (via + * yasm_linemap_add_data()). + * \param errwarns error/warning set + * \note Parse errors and warnings are stored into errwarns. + */ + void (*do_parse) + (yasm_object *object, yasm_preproc *pp, int save_input, + yasm_linemap *linemap, yasm_errwarns *errwarns); +} yasm_parser_module; + +#endif diff --git a/libyasm/phash.c b/libyasm/phash.c new file mode 100644 index 0000000..cef8c78 --- /dev/null +++ b/libyasm/phash.c @@ -0,0 +1,268 @@ +/* Modified for use with yasm by Peter Johnson. */ +#include "util.h" + +/* +-------------------------------------------------------------------- +lookupa.c, by Bob Jenkins, December 1996. Same as lookup2.c +Use this code however you wish. Public Domain. No warranty. +Source is http://burtleburtle.net/bob/c/lookupa.c +-------------------------------------------------------------------- +*/ +#include "phash.h" + +#define ub4 unsigned long + +#define hashsize(n) ((ub4)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bit set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + a &= 0xffffffff; \ + b -= c; b -= a; b ^= (a<<8); \ + b &= 0xffffffff; \ + c -= a; c -= b; c ^= (b>>13); \ + c &= 0xffffffff; \ + a -= b; a -= c; a ^= (c>>12); \ + a &= 0xffffffff; \ + b -= c; b -= a; b ^= (a<<16); \ + b &= 0xffffffff; \ + c -= a; c -= b; c ^= (b>>5); \ + c &= 0xffffffff; \ + a -= b; a -= c; a ^= (c>>3); \ + a &= 0xffffffff; \ + b -= c; b -= a; b ^= (a<<10); \ + b &= 0xffffffff; \ + c -= a; c -= b; c ^= (b>>15); \ + c &= 0xffffffff; \ +} + +/* +-------------------------------------------------------------------- +lookup() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 6len+35 instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = lookup( k[i], len[i], h); + +By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial. + +See http://burtleburtle.net/bob/hash/evahash.html +Use for hash table lookup, or anything where one collision in 2^32 is +acceptable. Do NOT use for cryptographic purposes. +-------------------------------------------------------------------- +*/ + +unsigned long +phash_lookup( + register const char *sk, /* the key */ + register size_t length, /* the length of the key */ + register unsigned long level) /* the previous hash, or an arbitrary value */ +{ + register unsigned long a,b,c; + register size_t len; + register const unsigned char *k = (const unsigned char *)sk; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = level; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) + { + a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); + a &= 0xffffffff; + b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); + b &= 0xffffffff; + c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); + c &= 0xffffffff; + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += (ub4)length; + switch(len) /* all the case statements fall through */ + { + case 11: c+=((ub4)k[10]<<24); + case 10: c+=((ub4)k[9]<<16); + case 9 : c+=((ub4)k[8]<<8); + c &= 0xffffffff; + /* the first byte of c is reserved for the length */ + case 8 : b+=((ub4)k[7]<<24); + case 7 : b+=((ub4)k[6]<<16); + case 6 : b+=((ub4)k[5]<<8); + case 5 : b+=k[4]; + b &= 0xffffffff; + case 4 : a+=((ub4)k[3]<<24); + case 3 : a+=((ub4)k[2]<<16); + case 2 : a+=((ub4)k[1]<<8); + case 1 : a+=k[0]; + a &= 0xffffffff; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +mixc -- mixc 8 4-bit values as quickly and thoroughly as possible. +Repeating mix() three times achieves avalanche. +Repeating mix() four times eliminates all funnels and all + characteristics stronger than 2^{-11}. +-------------------------------------------------------------------- +*/ +#define mixc(a,b,c,d,e,f,g,h) \ +{ \ + a^=b<<11; d+=a; b+=c; \ + b^=c>>2; e+=b; c+=d; \ + c^=d<<8; f+=c; d+=e; \ + d^=e>>16; g+=d; e+=f; \ + e^=f<<10; h+=e; f+=g; \ + f^=g>>4; a+=f; g+=h; \ + g^=h<<8; b+=g; h+=a; \ + h^=a>>9; c+=h; a+=b; \ +} + +/* +-------------------------------------------------------------------- +checksum() -- hash a variable-length key into a 256-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + state : an array of CHECKSTATE 4-byte values (256 bits) +The state is the checksum. Every bit of the key affects every bit of +the state. There are no funnels. About 112+6.875len instructions. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0; i<8; ++i) state[i] = 0x9e3779b9; + for (i=0, h=0; i<n; ++i) checksum( k[i], len[i], state); + +(c) Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial, as long +as this whole comment accompanies it. + +See http://burtleburtle.net/bob/hash/evahash.html +Use to detect changes between revisions of documents, assuming nobody +is trying to cause collisions. Do NOT use for cryptography. +-------------------------------------------------------------------- +*/ +void +phash_checksum( + register const char *sk, + register size_t len, + register unsigned long *state) +{ + register unsigned long a,b,c,d,e,f,g,h; + register size_t length; + register const unsigned char *k = (const unsigned char *)sk; + + /* Use the length and level; add in the golden ratio. */ + length = len; + a=state[0]; b=state[1]; c=state[2]; d=state[3]; + e=state[4]; f=state[5]; g=state[6]; h=state[7]; + + /*---------------------------------------- handle most of the key */ + while (len >= 32) + { + a += (k[0] +(k[1]<<8) +(k[2]<<16) +(k[3]<<24)); + b += (k[4] +(k[5]<<8) +(k[6]<<16) +(k[7]<<24)); + c += (k[8] +(k[9]<<8) +(k[10]<<16)+(k[11]<<24)); + d += (k[12]+(k[13]<<8)+(k[14]<<16)+(k[15]<<24)); + e += (k[16]+(k[17]<<8)+(k[18]<<16)+(k[19]<<24)); + f += (k[20]+(k[21]<<8)+(k[22]<<16)+(k[23]<<24)); + g += (k[24]+(k[25]<<8)+(k[26]<<16)+(k[27]<<24)); + h += (k[28]+(k[29]<<8)+(k[30]<<16)+(k[31]<<24)); + mixc(a,b,c,d,e,f,g,h); + mixc(a,b,c,d,e,f,g,h); + mixc(a,b,c,d,e,f,g,h); + mixc(a,b,c,d,e,f,g,h); + k += 32; len -= 32; + } + + /*------------------------------------- handle the last 31 bytes */ + h += (ub4)length; + switch(len) + { + case 31: h+=(k[30]<<24); + case 30: h+=(k[29]<<16); + case 29: h+=(k[28]<<8); + case 28: g+=(k[27]<<24); + case 27: g+=(k[26]<<16); + case 26: g+=(k[25]<<8); + case 25: g+=k[24]; + case 24: f+=(k[23]<<24); + case 23: f+=(k[22]<<16); + case 22: f+=(k[21]<<8); + case 21: f+=k[20]; + case 20: e+=(k[19]<<24); + case 19: e+=(k[18]<<16); + case 18: e+=(k[17]<<8); + case 17: e+=k[16]; + case 16: d+=(k[15]<<24); + case 15: d+=(k[14]<<16); + case 14: d+=(k[13]<<8); + case 13: d+=k[12]; + case 12: c+=(k[11]<<24); + case 11: c+=(k[10]<<16); + case 10: c+=(k[9]<<8); + case 9 : c+=k[8]; + case 8 : b+=(k[7]<<24); + case 7 : b+=(k[6]<<16); + case 6 : b+=(k[5]<<8); + case 5 : b+=k[4]; + case 4 : a+=(k[3]<<24); + case 3 : a+=(k[2]<<16); + case 2 : a+=(k[1]<<8); + case 1 : a+=k[0]; + } + mixc(a,b,c,d,e,f,g,h); + mixc(a,b,c,d,e,f,g,h); + mixc(a,b,c,d,e,f,g,h); + mixc(a,b,c,d,e,f,g,h); + + /*-------------------------------------------- report the result */ + state[0]=a; state[1]=b; state[2]=c; state[3]=d; + state[4]=e; state[5]=f; state[6]=g; state[7]=h; +} diff --git a/libyasm/phash.h b/libyasm/phash.h new file mode 100644 index 0000000..2273a5d --- /dev/null +++ b/libyasm/phash.h @@ -0,0 +1,19 @@ +/* Modified for use with yasm by Peter Johnson. */ +/* +------------------------------------------------------------------------------ +By Bob Jenkins, September 1996. +lookupa.h, a hash function for table lookup, same function as lookup.c. +Use this code in any way you wish. Public Domain. It has no warranty. +Source is http://burtleburtle.net/bob/c/lookupa.h +------------------------------------------------------------------------------ +*/ + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +YASM_LIB_DECL +unsigned long phash_lookup(const char *k, size_t length, + unsigned long level); +YASM_LIB_DECL +void phash_checksum(const char *k, size_t length, unsigned long *state); diff --git a/libyasm/preproc.h b/libyasm/preproc.h new file mode 100644 index 0000000..751b19e --- /dev/null +++ b/libyasm/preproc.h @@ -0,0 +1,210 @@ +/** + * \file libyasm/preproc.h + * \brief YASM preprocessor module interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_PREPROC_H +#define YASM_PREPROC_H + +#ifndef YASM_DOXYGEN +/** Base #yasm_preproc structure. Must be present as the first element in any + * #yasm_preproc implementation. + */ +typedef struct yasm_preproc_base { + /** #yasm_preproc_module implementation for this preprocessor. */ + const struct yasm_preproc_module *module; +} yasm_preproc_base; +#endif + +/** YASM preprocesor module interface. */ +typedef struct yasm_preproc_module { + /** One-line description of the preprocessor. */ + const char *name; + + /** Keyword used to select preprocessor on the command line. */ + const char *keyword; + + /** Create preprocessor. + * Module-level implementation of yasm_preproc_create(). + * Call yasm_preproc_create() instead of calling this function. + * + * \param in_filename initial starting filename, or "-" to read from + * stdin + * \param symtab symbol table (may be NULL if none) + * \param lm line mapping repository + * \param errwarns error/warnning set. + * \return New preprocessor. + * + * \note Any preprocessor errors and warnings are stored into errwarns. + */ + /*@only@*/ yasm_preproc * (*create) (const char *in_filename, + yasm_symtab *symtab, + yasm_linemap *lm, + yasm_errwarns *errwarns); + + /** Module-level implementation of yasm_preproc_destroy(). + * Call yasm_preproc_destroy() instead of calling this function. + */ + void (*destroy) (/*@only@*/ yasm_preproc *preproc); + + /** Module-level implementation of yasm_preproc_get_line(). + * Call yasm_preproc_get_line() instead of calling this function. + */ + char * (*get_line) (yasm_preproc *preproc); + + /** Module-level implementation of yasm_preproc_get_included_file(). + * Call yasm_preproc_get_included_file() instead of calling this function. + */ + size_t (*get_included_file) (yasm_preproc *preproc, /*@out@*/ char *buf, + size_t max_size); + + /** Module-level implementation of yasm_preproc_add_include_file(). + * Call yasm_preproc_add_include_file() instead of calling this function. + */ + void (*add_include_file) (yasm_preproc *preproc, const char *filename); + + /** Module-level implementation of yasm_preproc_predefine_macro(). + * Call yasm_preproc_predefine_macro() instead of calling this function. + */ + void (*predefine_macro) (yasm_preproc *preproc, const char *macronameval); + + /** Module-level implementation of yasm_preproc_undefine_macro(). + * Call yasm_preproc_undefine_macro() instead of calling this function. + */ + void (*undefine_macro) (yasm_preproc *preproc, const char *macroname); + + /** Module-level implementation of yasm_preproc_builtin_define(). + * Call yasm_preproc_builtin_define() instead of calling this function. + */ + void (*define_builtin) (yasm_preproc *preproc, const char *macronameval); + + /** Module-level implementation of yasm_preproc_add_standard(). + * Call yasm_preproc_add_standard() instead of calling this function. + */ + void (*add_standard) (yasm_preproc *preproc, const char **macros); +} yasm_preproc_module; + +/** Initialize preprocessor. + * The preprocessor needs access to the object format module to find out + * any output format specific macros. + * \param module preprocessor module + * \param in_filename initial starting filename, or "-" to read from stdin + * \param symtab symbol table (may be NULL if none) + * \param lm line mapping repository + * \param errwarns error/warning set + * \return New preprocessor. + * \note Errors/warnings are stored into errwarns. + */ +/*@only@*/ yasm_preproc *yasm_preproc_create + (yasm_preproc_module *module, const char *in_filename, + yasm_symtab *symtab, yasm_linemap *lm, yasm_errwarns *errwarns); + +/** Cleans up any allocated preproc memory. + * \param preproc preprocessor + */ +void yasm_preproc_destroy(/*@only@*/ yasm_preproc *preproc); + +/** Gets a single line of preprocessed source code. + * \param preproc preprocessor + * \return Allocated line of code, without the trailing \n. + */ +char *yasm_preproc_get_line(yasm_preproc *preproc); + +/** Get the next filename included by the source code. + * \param preproc preprocessor + * \param buf destination buffer for filename + * \param max_size maximum number of bytes that can be returned in buf + * \return Actual number of bytes returned in buf. + */ +size_t yasm_preproc_get_included_file(yasm_preproc *preproc, + /*@out@*/ char *buf, size_t max_size); + +/** Pre-include a file. + * \param preproc preprocessor + * \param filename filename + */ +void yasm_preproc_add_include_file(yasm_preproc *preproc, + const char *filename); + +/** Pre-define a macro. + * \param preproc preprocessor + * \param macronameval "name=value" string + */ +void yasm_preproc_predefine_macro(yasm_preproc *preproc, + const char *macronameval); + +/** Un-define a macro. + * \param preproc preprocessor + * \param macroname macro name + */ +void yasm_preproc_undefine_macro(yasm_preproc *preproc, const char *macroname); + +/** Define a builtin macro, preprocessed before the "standard" macros. + * \param preproc preprocessor + * \param macronameval "name=value" string + */ +void yasm_preproc_define_builtin(yasm_preproc *preproc, + const char *macronameval); + +/** Define additional standard macros, preprocessed after the builtins but + * prior to any user-defined macros. + * \param preproc preprocessor + * \param macros NULL-terminated array of macro strings + */ +void yasm_preproc_add_standard(yasm_preproc *preproc, + const char **macros); + +#ifndef YASM_DOXYGEN + +/* Inline macro implementations for preproc functions */ + +#define yasm_preproc_create(module, in_filename, symtab, lm, ews) \ + module->create(in_filename, symtab, lm, ews) + +#define yasm_preproc_destroy(preproc) \ + ((yasm_preproc_base *)preproc)->module->destroy(preproc) +#define yasm_preproc_get_line(preproc) \ + ((yasm_preproc_base *)preproc)->module->get_line(preproc) +#define yasm_preproc_get_included_file(preproc, buf, max_size) \ + ((yasm_preproc_base *)preproc)->module->get_included_file(preproc, buf, max_size) +#define yasm_preproc_add_include_file(preproc, filename) \ + ((yasm_preproc_base *)preproc)->module->add_include_file(preproc, filename) +#define yasm_preproc_predefine_macro(preproc, macronameval) \ + ((yasm_preproc_base *)preproc)->module->predefine_macro(preproc, \ + macronameval) +#define yasm_preproc_undefine_macro(preproc, macroname) \ + ((yasm_preproc_base *)preproc)->module->undefine_macro(preproc, macroname) +#define yasm_preproc_define_builtin(preproc, macronameval) \ + ((yasm_preproc_base *)preproc)->module->define_builtin(preproc, \ + macronameval) +#define yasm_preproc_add_standard(preproc, macros) \ + ((yasm_preproc_base *)preproc)->module->add_standard(preproc, \ + macros) + +#endif + +#endif diff --git a/libyasm/section.c b/libyasm/section.c new file mode 100644 index 0000000..9242fb0 --- /dev/null +++ b/libyasm/section.c @@ -0,0 +1,1580 @@ +/* + * Section utility functions + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include <limits.h> + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "hamt.h" +#include "valparam.h" +#include "assocdat.h" + +#include "linemap.h" +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "value.h" +#include "symrec.h" + +#include "bytecode.h" +#include "arch.h" +#include "section.h" + +#include "dbgfmt.h" +#include "objfmt.h" + +#include "inttree.h" + + +struct yasm_section { + /*@reldef@*/ STAILQ_ENTRY(yasm_section) link; + + /*@dependent@*/ yasm_object *object; /* Pointer to parent object */ + + /*@owned@*/ char *name; /* strdup()'ed name (given by user) */ + + /* associated data; NULL if none */ + /*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data; + + unsigned long align; /* Section alignment */ + + unsigned long opt_flags; /* storage for optimizer flags */ + + int code; /* section contains code (instructions) */ + int res_only; /* allow only resb family of bytecodes? */ + int def; /* "default" section, e.g. not specified by + using section directive */ + + /* the bytecodes for the section's contents */ + /*@reldef@*/ STAILQ_HEAD(yasm_bytecodehead, yasm_bytecode) bcs; + + /* the relocations for the section */ + /*@reldef@*/ STAILQ_HEAD(yasm_relochead, yasm_reloc) relocs; + + void (*destroy_reloc) (/*@only@*/ void *reloc); +}; + +static void yasm_section_destroy(/*@only@*/ yasm_section *sect); + +/* Wrapper around directive for HAMT insertion */ +typedef struct yasm_directive_wrap { + const yasm_directive *directive; +} yasm_directive_wrap; + +/* + * Standard "builtin" object directives. + */ + +static void +dir_extern(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp = yasm_vps_first(valparams); + yasm_symrec *sym; + sym = yasm_symtab_declare(object->symtab, yasm_vp_id(vp), YASM_SYM_EXTERN, + line); + if (objext_valparams) { + yasm_valparamhead *vps = yasm_vps_create(); + *vps = *objext_valparams; /* structure copy */ + yasm_vps_initialize(objext_valparams); /* don't double-free */ + yasm_symrec_set_objext_valparams(sym, vps); + } +} + +static void +dir_global(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp = yasm_vps_first(valparams); + yasm_symrec *sym; + sym = yasm_symtab_declare(object->symtab, yasm_vp_id(vp), YASM_SYM_GLOBAL, + line); + if (objext_valparams) { + yasm_valparamhead *vps = yasm_vps_create(); + *vps = *objext_valparams; /* structure copy */ + yasm_vps_initialize(objext_valparams); /* don't double-free */ + yasm_symrec_set_objext_valparams(sym, vps); + } +} + +static void +dir_common(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp = yasm_vps_first(valparams); + yasm_valparam *vp2 = yasm_vps_next(vp); + yasm_expr *size = yasm_vp_expr(vp2, object->symtab, line); + yasm_symrec *sym; + + if (!size) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("no size specified in %s declaration"), "COMMON"); + return; + } + sym = yasm_symtab_declare(object->symtab, yasm_vp_id(vp), YASM_SYM_COMMON, + line); + yasm_symrec_set_common_size(sym, size); + if (objext_valparams) { + yasm_valparamhead *vps = yasm_vps_create(); + *vps = *objext_valparams; /* structure copy */ + yasm_vps_initialize(objext_valparams); /* don't double-free */ + yasm_symrec_set_objext_valparams(sym, vps); + } +} + +static void +dir_section(yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_section *new_section = + yasm_objfmt_section_switch(object, valparams, objext_valparams, line); + if (new_section) + object->cur_section = new_section; + else + yasm_error_set(YASM_ERROR_SYNTAX, + N_("invalid argument to directive `%s'"), "SECTION"); +} + +static const yasm_directive object_directives[] = { + { ".extern", "gas", dir_extern, YASM_DIR_ID_REQUIRED }, + { ".global", "gas", dir_global, YASM_DIR_ID_REQUIRED }, + { ".globl", "gas", dir_global, YASM_DIR_ID_REQUIRED }, + { "extern", "nasm", dir_extern, YASM_DIR_ID_REQUIRED }, + { "global", "nasm", dir_global, YASM_DIR_ID_REQUIRED }, + { "common", "nasm", dir_common, YASM_DIR_ID_REQUIRED }, + { "section", "nasm", dir_section, YASM_DIR_ARG_REQUIRED }, + { "segment", "nasm", dir_section, YASM_DIR_ARG_REQUIRED }, + { NULL, NULL, NULL, 0 } +}; + +static void +directive_level2_delete(/*@only@*/ void *data) +{ + yasm_xfree(data); +} + +static void +directive_level1_delete(/*@only@*/ void *data) +{ + HAMT_destroy(data, directive_level2_delete); +} + +static void +directives_add(yasm_object *object, /*@null@*/ const yasm_directive *dir) +{ + if (!dir) + return; + + while (dir->name) { + HAMT *level2 = HAMT_search(object->directives, dir->parser); + int replace; + yasm_directive_wrap *wrap = yasm_xmalloc(sizeof(yasm_directive_wrap)); + + if (!level2) { + replace = 0; + level2 = HAMT_insert(object->directives, dir->parser, + HAMT_create(1, yasm_internal_error_), + &replace, directive_level1_delete); + } + replace = 0; + wrap->directive = dir; + HAMT_insert(level2, dir->name, wrap, &replace, + directive_level2_delete); + dir++; + } +} + +/*@-compdestroy@*/ +yasm_object * +yasm_object_create(const char *src_filename, const char *obj_filename, + /*@kept@*/ yasm_arch *arch, + const yasm_objfmt_module *objfmt_module, + const yasm_dbgfmt_module *dbgfmt_module) +{ + yasm_object *object = yasm_xmalloc(sizeof(yasm_object)); + int matched, i; + + object->src_filename = yasm__xstrdup(src_filename); + object->obj_filename = yasm__xstrdup(obj_filename); + + /* No prefix/suffix */ + object->global_prefix = yasm__xstrdup(""); + object->global_suffix = yasm__xstrdup(""); + + /* Create empty symbol table */ + object->symtab = yasm_symtab_create(); + + /* Initialize sections linked list */ + STAILQ_INIT(&object->sections); + + /* Create directives HAMT */ + object->directives = HAMT_create(1, yasm_internal_error_); + + /* Initialize the target architecture */ + object->arch = arch; + + /* Initialize things to NULL in case of error */ + object->dbgfmt = NULL; + + /* Initialize the object format */ + object->objfmt = yasm_objfmt_create(objfmt_module, object); + if (!object->objfmt) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("object format `%s' does not support architecture `%s' machine `%s'"), + objfmt_module->keyword, ((yasm_arch_base *)arch)->module->keyword, + yasm_arch_get_machine(arch)); + goto error; + } + + /* Get a fresh copy of objfmt_module as it may have changed. */ + objfmt_module = ((yasm_objfmt_base *)object->objfmt)->module; + + /* Add an initial "default" section to object */ + object->cur_section = yasm_objfmt_add_default_section(object); + + /* Check to see if the requested debug format is in the allowed list + * for the active object format. + */ + matched = 0; + for (i=0; objfmt_module->dbgfmt_keywords[i]; i++) + if (yasm__strcasecmp(objfmt_module->dbgfmt_keywords[i], + dbgfmt_module->keyword) == 0) + matched = 1; + if (!matched) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("`%s' is not a valid debug format for object format `%s'"), + dbgfmt_module->keyword, objfmt_module->keyword); + goto error; + } + + /* Initialize the debug format */ + object->dbgfmt = yasm_dbgfmt_create(dbgfmt_module, object); + if (!object->dbgfmt) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("debug format `%s' does not work with object format `%s'"), + dbgfmt_module->keyword, objfmt_module->keyword); + goto error; + } + + /* Add directives to HAMT. Note ordering here determines priority. */ + directives_add(object, + ((yasm_objfmt_base *)object->objfmt)->module->directives); + directives_add(object, + ((yasm_dbgfmt_base *)object->dbgfmt)->module->directives); + directives_add(object, + ((yasm_arch_base *)object->arch)->module->directives); + directives_add(object, object_directives); + + return object; + +error: + yasm_object_destroy(object); + return NULL; +} +/*@=compdestroy@*/ + +/*@-onlytrans@*/ +yasm_section * +yasm_object_get_general(yasm_object *object, const char *name, + unsigned long align, int code, int res_only, + int *isnew, unsigned long line) +{ + yasm_section *s; + yasm_bytecode *bc; + + /* Search through current sections to see if we already have one with + * that name. + */ + STAILQ_FOREACH(s, &object->sections, link) { + if (strcmp(s->name, name) == 0) { + *isnew = 0; + return s; + } + } + + /* No: we have to allocate and create a new one. */ + + /* Okay, the name is valid; now allocate and initialize */ + s = yasm_xcalloc(1, sizeof(yasm_section)); + STAILQ_INSERT_TAIL(&object->sections, s, link); + + s->object = object; + s->name = yasm__xstrdup(name); + s->assoc_data = NULL; + s->align = align; + + /* Initialize bytecodes with one empty bytecode (acts as "prior" for first + * real bytecode in section. + */ + STAILQ_INIT(&s->bcs); + bc = yasm_bc_create_common(NULL, NULL, 0); + bc->section = s; + bc->offset = 0; + STAILQ_INSERT_TAIL(&s->bcs, bc, link); + + /* Initialize relocs */ + STAILQ_INIT(&s->relocs); + s->destroy_reloc = NULL; + + s->code = code; + s->res_only = res_only; + s->def = 0; + + /* Initialize object format specific data */ + yasm_objfmt_init_new_section(s, line); + + *isnew = 1; + return s; +} +/*@=onlytrans@*/ + +int +yasm_object_directive(yasm_object *object, const char *name, + const char *parser, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, + unsigned long line) +{ + HAMT *level2; + yasm_directive_wrap *wrap; + + level2 = HAMT_search(object->directives, parser); + if (!level2) + return 1; + + wrap = HAMT_search(level2, name); + if (!wrap) + return 1; + + yasm_call_directive(wrap->directive, object, valparams, objext_valparams, + line); + return 0; +} + +void +yasm_object_set_source_fn(yasm_object *object, const char *src_filename) +{ + yasm_xfree(object->src_filename); + object->src_filename = yasm__xstrdup(src_filename); +} + +void +yasm_object_set_global_prefix(yasm_object *object, const char *prefix) +{ + yasm_xfree(object->global_prefix); + object->global_prefix = yasm__xstrdup(prefix); +} + +void +yasm_object_set_global_suffix(yasm_object *object, const char *suffix) +{ + yasm_xfree(object->global_suffix); + object->global_suffix = yasm__xstrdup(suffix); +} + +int +yasm_section_is_code(yasm_section *sect) +{ + return sect->code; +} + +unsigned long +yasm_section_get_opt_flags(const yasm_section *sect) +{ + return sect->opt_flags; +} + +void +yasm_section_set_opt_flags(yasm_section *sect, unsigned long opt_flags) +{ + sect->opt_flags = opt_flags; +} + +int +yasm_section_is_default(const yasm_section *sect) +{ + return sect->def; +} + +void +yasm_section_set_default(yasm_section *sect, int def) +{ + sect->def = def; +} + +yasm_object * +yasm_section_get_object(const yasm_section *sect) +{ + return sect->object; +} + +void * +yasm_section_get_data(yasm_section *sect, + const yasm_assoc_data_callback *callback) +{ + return yasm__assoc_data_get(sect->assoc_data, callback); +} + +void +yasm_section_add_data(yasm_section *sect, + const yasm_assoc_data_callback *callback, void *data) +{ + sect->assoc_data = yasm__assoc_data_add(sect->assoc_data, callback, data); +} + +void +yasm_object_destroy(yasm_object *object) +{ + yasm_section *cur, *next; + + /* Delete object format, debug format, and arch. This can be called + * due to an error in yasm_object_create(), so look out for NULLs. + */ + if (object->objfmt) + yasm_objfmt_destroy(object->objfmt); + if (object->dbgfmt) + yasm_dbgfmt_destroy(object->dbgfmt); + + /* Delete sections */ + cur = STAILQ_FIRST(&object->sections); + while (cur) { + next = STAILQ_NEXT(cur, link); + yasm_section_destroy(cur); + cur = next; + } + + /* Delete directives HAMT */ + HAMT_destroy(object->directives, directive_level1_delete); + + /* Delete prefix/suffix */ + yasm_xfree(object->global_prefix); + yasm_xfree(object->global_suffix); + + /* Delete associated filenames */ + yasm_xfree(object->src_filename); + yasm_xfree(object->obj_filename); + + /* Delete symbol table */ + yasm_symtab_destroy(object->symtab); + + /* Delete architecture */ + if (object->arch) + yasm_arch_destroy(object->arch); + + yasm_xfree(object); +} + +void +yasm_object_print(const yasm_object *object, FILE *f, int indent_level) +{ + yasm_section *cur; + + /* Print symbol table */ + fprintf(f, "%*sSymbol Table:\n", indent_level, ""); + yasm_symtab_print(object->symtab, f, indent_level+1); + + /* Print sections and bytecodes */ + STAILQ_FOREACH(cur, &object->sections, link) { + fprintf(f, "%*sSection:\n", indent_level, ""); + yasm_section_print(cur, f, indent_level+1, 1); + } +} + +void +yasm_object_finalize(yasm_object *object, yasm_errwarns *errwarns) +{ + yasm_section *sect; + + /* Iterate through sections */ + STAILQ_FOREACH(sect, &object->sections, link) { + yasm_bytecode *cur = STAILQ_FIRST(§->bcs); + yasm_bytecode *prev; + + /* Skip our locally created empty bytecode first. */ + prev = cur; + cur = STAILQ_NEXT(cur, link); + + /* Iterate through the remainder, if any. */ + while (cur) { + /* Finalize */ + yasm_bc_finalize(cur, prev); + yasm_errwarn_propagate(errwarns, cur->line); + prev = cur; + cur = STAILQ_NEXT(cur, link); + } + } +} + +int +yasm_object_sections_traverse(yasm_object *object, /*@null@*/ void *d, + int (*func) (yasm_section *sect, + /*@null@*/ void *d)) +{ + yasm_section *cur; + + STAILQ_FOREACH(cur, &object->sections, link) { + int retval = func(cur, d); + if (retval != 0) + return retval; + } + return 0; +} + +/*@-onlytrans@*/ +yasm_section * +yasm_object_find_general(yasm_object *object, const char *name) +{ + yasm_section *cur; + + STAILQ_FOREACH(cur, &object->sections, link) { + if (strcmp(cur->name, name) == 0) + return cur; + } + return NULL; +} +/*@=onlytrans@*/ + +void +yasm_section_add_reloc(yasm_section *sect, yasm_reloc *reloc, + void (*destroy_func) (/*@only@*/ void *reloc)) +{ + STAILQ_INSERT_TAIL(§->relocs, reloc, link); + if (!destroy_func) + yasm_internal_error(N_("NULL destroy function given to add_reloc")); + else if (sect->destroy_reloc && destroy_func != sect->destroy_reloc) + yasm_internal_error(N_("different destroy function given to add_reloc")); + sect->destroy_reloc = destroy_func; +} + +/*@null@*/ yasm_reloc * +yasm_section_relocs_first(yasm_section *sect) +{ + return STAILQ_FIRST(§->relocs); +} + +#undef yasm_section_reloc_next +/*@null@*/ yasm_reloc * +yasm_section_reloc_next(yasm_reloc *reloc) +{ + return STAILQ_NEXT(reloc, link); +} + +void +yasm_reloc_get(yasm_reloc *reloc, yasm_intnum **addrp, yasm_symrec **symp) +{ + *addrp = reloc->addr; + *symp = reloc->sym; +} + + +yasm_bytecode * +yasm_section_bcs_first(yasm_section *sect) +{ + return STAILQ_FIRST(§->bcs); +} + +yasm_bytecode * +yasm_section_bcs_last(yasm_section *sect) +{ + return STAILQ_LAST(§->bcs, yasm_bytecode, link); +} + +yasm_bytecode * +yasm_section_bcs_append(yasm_section *sect, yasm_bytecode *bc) +{ + if (bc) { + if (bc->callback) { + bc->section = sect; /* record parent section */ + STAILQ_INSERT_TAIL(§->bcs, bc, link); + return bc; + } else + yasm_xfree(bc); + } + return (yasm_bytecode *)NULL; +} + +int +yasm_section_bcs_traverse(yasm_section *sect, + /*@null@*/ yasm_errwarns *errwarns, + /*@null@*/ void *d, + int (*func) (yasm_bytecode *bc, /*@null@*/ void *d)) +{ + yasm_bytecode *cur = STAILQ_FIRST(§->bcs); + + /* Skip our locally created empty bytecode first. */ + cur = STAILQ_NEXT(cur, link); + + /* Iterate through the remainder, if any. */ + while (cur) { + int retval = func(cur, d); + if (errwarns) + yasm_errwarn_propagate(errwarns, cur->line); + if (retval != 0) + return retval; + cur = STAILQ_NEXT(cur, link); + } + return 0; +} + +const char * +yasm_section_get_name(const yasm_section *sect) +{ + return sect->name; +} + +void +yasm_section_set_align(yasm_section *sect, unsigned long align, + unsigned long line) +{ + sect->align = align; +} + +unsigned long +yasm_section_get_align(const yasm_section *sect) +{ + return sect->align; +} + +static void +yasm_section_destroy(yasm_section *sect) +{ + yasm_bytecode *cur, *next; + yasm_reloc *r_cur, *r_next; + + if (!sect) + return; + + yasm_xfree(sect->name); + yasm__assoc_data_destroy(sect->assoc_data); + + /* Delete bytecodes */ + cur = STAILQ_FIRST(§->bcs); + while (cur) { + next = STAILQ_NEXT(cur, link); + yasm_bc_destroy(cur); + cur = next; + } + + /* Delete relocations */ + r_cur = STAILQ_FIRST(§->relocs); + while (r_cur) { + r_next = STAILQ_NEXT(r_cur, link); + yasm_intnum_destroy(r_cur->addr); + sect->destroy_reloc(r_cur); + r_cur = r_next; + } + + yasm_xfree(sect); +} + +void +yasm_section_print(const yasm_section *sect, FILE *f, int indent_level, + int print_bcs) +{ + if (!sect) { + fprintf(f, "%*s(none)\n", indent_level, ""); + return; + } + + fprintf(f, "%*sname=%s\n", indent_level, "", sect->name); + + if (sect->assoc_data) { + fprintf(f, "%*sAssociated data:\n", indent_level, ""); + yasm__assoc_data_print(sect->assoc_data, f, indent_level+1); + } + + if (print_bcs) { + yasm_bytecode *cur; + + fprintf(f, "%*sBytecodes:\n", indent_level, ""); + + STAILQ_FOREACH(cur, §->bcs, link) { + fprintf(f, "%*sNext Bytecode:\n", indent_level+1, ""); + yasm_bc_print(cur, f, indent_level+2); + } + } +} + +/* + * Robertson (1977) optimizer + * Based (somewhat loosely) on the algorithm given in: + * MRC Technical Summary Report # 1779 + * CODE GENERATION FOR SHORT/LONG ADDRESS MACHINES + * Edward L. Robertson + * Mathematics Research Center + * University of Wisconsin-Madison + * 610 Walnut Street + * Madison, Wisconsin 53706 + * August 1977 + * + * Key components of algorithm: + * - start assuming all short forms + * - build spans for short->long transition dependencies + * - if a long form is needed, walk the dependencies and update + * Major differences from Robertson's algorithm: + * - detection of cycles + * - any difference of two locations is allowed + * - handling of alignment/org gaps (offset setting) + * - handling of multiples + * + * Data structures: + * - Interval tree to store spans and associated data + * - Queues QA and QB + * + * Each span keeps track of: + * - Associated bytecode (bytecode that depends on the span length) + * - Active/inactive state (starts out active) + * - Sign (negative/positive; negative being "backwards" in address) + * - Current length in bytes + * - New length in bytes + * - Negative/Positive thresholds + * - Span ID (unique within each bytecode) + * + * How org and align and any other offset-based bytecodes are handled: + * + * Some portions are critical values that must not depend on any bytecode + * offset (either relative or absolute). + * + * All offset-setters (ORG and ALIGN) are put into a linked list in section + * order (e.g. increasing offset order). Each span keeps track of the next + * offset-setter following the span's associated bytecode. + * + * When a bytecode is expanded, the next offset-setter is examined. The + * offset-setter may be able to absorb the expansion (e.g. any offset + * following it would not change), or it may have to move forward (in the + * case of align) or error (in the case of org). If it has to move forward, + * following offset-setters must also be examined for absorption or moving + * forward. In either case, the ongoing offset is updated as well as the + * lengths of any spans dependent on the offset-setter. + * + * Alignment/ORG value is critical value. + * Cannot be combined with TIMES. + * + * How times is handled: + * + * TIMES: Handled separately from bytecode "raw" size. If not span-dependent, + * trivial (just multiplied in at any bytecode size increase). Span + * dependent times update on any change (span ID 0). If the resultant + * next bytecode offset would be less than the old next bytecode offset, + * error. Otherwise increase offset and update dependent spans. + * + * To reduce interval tree size, a first expansion pass is performed + * before the spans are added to the tree. + * + * Basic algorithm outline: + * + * 1. Initialization: + * a. Number bytecodes sequentially (via bc_index) and calculate offsets + * of all bytecodes assuming minimum length, building a list of all + * dependent spans as we go. + * "minimum" here means absolute minimum: + * - align/org (offset-based) bumps offset as normal + * - times values (with span-dependent values) assumed to be 0 + * b. Iterate over spans. Set span length based on bytecode offsets + * determined in 1a. If span is "certainly" long because the span + * is an absolute reference to another section (or external) or the + * distance calculated based on the minimum length is greater than the + * span's threshold, expand the span's bytecode, and if no further + * expansion can result, mark span as inactive. + * c. Iterate over bytecodes to update all bytecode offsets based on new + * (expanded) lengths calculated in 1b. + * d. Iterate over active spans. Add span to interval tree. Update span's + * length based on new bytecode offsets determined in 1c. If span's + * length exceeds long threshold, add that span to Q. + * 2. Main loop: + * While Q not empty: + * Expand BC dependent on span at head of Q (and remove span from Q). + * Update span: + * If BC no longer dependent on span, mark span as inactive. + * If BC has new thresholds for span, update span. + * If BC increased in size, for each active span that contains BC: + * Increase span length by difference between short and long BC length. + * If span exceeds long threshold (or is flagged to recalculate on any + * change), add it to tail of Q. + * 3. Final pass over bytecodes to generate final offsets. + */ + +typedef struct yasm_span yasm_span; + +typedef struct yasm_offset_setter { + /* Linked list in section order (e.g. offset order) */ + /*@reldef@*/ STAILQ_ENTRY(yasm_offset_setter) link; + + /*@dependent@*/ yasm_bytecode *bc; + + unsigned long cur_val, new_val; + unsigned long thres; +} yasm_offset_setter; + +typedef struct yasm_span_term { + yasm_bytecode *precbc, *precbc2; + yasm_span *span; /* span this term is a member of */ + long cur_val, new_val; + unsigned int subst; +} yasm_span_term; + +struct yasm_span { + /*@reldef@*/ TAILQ_ENTRY(yasm_span) link; /* for allocation tracking */ + /*@reldef@*/ STAILQ_ENTRY(yasm_span) linkq; /* for Q */ + + /*@dependent@*/ yasm_bytecode *bc; + + yasm_value depval; + + /* span term for relative portion of value */ + yasm_span_term *rel_term; + /* span terms in absolute portion of value */ + yasm_span_term *terms; + yasm_expr__item *items; + unsigned int num_terms; + + long cur_val; + long new_val; + + long neg_thres; + long pos_thres; + + int id; + + int active; + + /* NULL-terminated array of spans that led to this span. Used only for + * checking for circular references (cycles) with id=0 spans. + */ + yasm_span **backtrace; + int backtrace_size; + + /* First offset setter following this span's bytecode */ + yasm_offset_setter *os; +}; + +typedef struct optimize_data { + /*@reldef@*/ TAILQ_HEAD(yasm_span_head, yasm_span) spans; + /*@reldef@*/ STAILQ_HEAD(yasm_span_shead, yasm_span) QA, QB; + /*@only@*/ IntervalTree *itree; + /*@reldef@*/ STAILQ_HEAD(offset_setters_head, yasm_offset_setter) + offset_setters; + long len_diff; /* used only for optimize_term_expand */ + yasm_span *span; /* used only for check_cycle */ + yasm_offset_setter *os; +} optimize_data; + +static yasm_span * +create_span(yasm_bytecode *bc, int id, /*@null@*/ const yasm_value *value, + long neg_thres, long pos_thres, yasm_offset_setter *os) +{ + yasm_span *span = yasm_xmalloc(sizeof(yasm_span)); + + span->bc = bc; + if (value) + yasm_value_init_copy(&span->depval, value); + else + yasm_value_initialize(&span->depval, NULL, 0); + span->rel_term = NULL; + span->terms = NULL; + span->items = NULL; + span->num_terms = 0; + span->cur_val = 0; + span->new_val = 0; + span->neg_thres = neg_thres; + span->pos_thres = pos_thres; + span->id = id; + span->active = 1; + span->backtrace = NULL; + span->backtrace_size = 0; + span->os = os; + + return span; +} + +static void +optimize_add_span(void *add_span_data, yasm_bytecode *bc, int id, + const yasm_value *value, long neg_thres, long pos_thres) +{ + optimize_data *optd = (optimize_data *)add_span_data; + yasm_span *span; + span = create_span(bc, id, value, neg_thres, pos_thres, optd->os); + TAILQ_INSERT_TAIL(&optd->spans, span, link); +} + +static void +add_span_term(unsigned int subst, yasm_bytecode *precbc, + yasm_bytecode *precbc2, void *d) +{ + yasm_span *span = d; + yasm_intnum *intn; + + if (subst >= span->num_terms) { + /* Linear expansion since total number is essentially always small */ + span->num_terms = subst+1; + span->terms = yasm_xrealloc(span->terms, + span->num_terms*sizeof(yasm_span_term)); + } + span->terms[subst].precbc = precbc; + span->terms[subst].precbc2 = precbc2; + span->terms[subst].span = span; + span->terms[subst].subst = subst; + + intn = yasm_calc_bc_dist(precbc, precbc2); + if (!intn) + yasm_internal_error(N_("could not calculate bc distance")); + span->terms[subst].cur_val = 0; + span->terms[subst].new_val = yasm_intnum_get_int(intn); + yasm_intnum_destroy(intn); +} + +static void +span_create_terms(yasm_span *span) +{ + unsigned int i; + + /* Split out sym-sym terms in absolute portion of dependent value */ + if (span->depval.abs) { + span->num_terms = yasm_expr__bc_dist_subst(&span->depval.abs, span, + add_span_term); + if (span->num_terms > 0) { + span->items = yasm_xmalloc(span->num_terms*sizeof(yasm_expr__item)); + for (i=0; i<span->num_terms; i++) { + /* Create items with dummy value */ + span->items[i].type = YASM_EXPR_INT; + span->items[i].data.intn = yasm_intnum_create_int(0); + + /* Check for circular references */ + if (span->id <= 0 && + ((span->bc->bc_index > span->terms[i].precbc->bc_index && + span->bc->bc_index <= span->terms[i].precbc2->bc_index) || + (span->bc->bc_index > span->terms[i].precbc2->bc_index && + span->bc->bc_index <= span->terms[i].precbc->bc_index))) + yasm_error_set(YASM_ERROR_VALUE, + N_("circular reference detected")); + } + } + } + + /* Create term for relative portion of dependent value */ + if (span->depval.rel) { + yasm_bytecode *rel_precbc; + int sym_local; + + sym_local = yasm_symrec_get_label(span->depval.rel, &rel_precbc); + if (span->depval.wrt || span->depval.seg_of || span->depval.section_rel + || !sym_local) + return; /* we can't handle SEG, WRT, or external symbols */ + if (rel_precbc->section != span->bc->section) + return; /* not in this section */ + if (!span->depval.curpos_rel) + return; /* not PC-relative */ + + span->rel_term = yasm_xmalloc(sizeof(yasm_span_term)); + span->rel_term->precbc = NULL; + span->rel_term->precbc2 = rel_precbc; + span->rel_term->span = span; + span->rel_term->subst = ~0U; + + span->rel_term->cur_val = 0; + span->rel_term->new_val = yasm_bc_next_offset(rel_precbc) - + span->bc->offset; + } +} + +/* Recalculate span value based on current span replacement values. + * Returns 1 if span needs expansion (e.g. exceeded thresholds). + */ +static int +recalc_normal_span(yasm_span *span) +{ + span->new_val = 0; + + if (span->depval.abs) { + yasm_expr *abs_copy = yasm_expr_copy(span->depval.abs); + /*@null@*/ /*@dependent@*/ yasm_intnum *num; + + /* Update sym-sym terms and substitute back into expr */ + unsigned int i; + for (i=0; i<span->num_terms; i++) + yasm_intnum_set_int(span->items[i].data.intn, + span->terms[i].new_val); + yasm_expr__subst(abs_copy, span->num_terms, span->items); + num = yasm_expr_get_intnum(&abs_copy, 0); + if (num) + span->new_val = yasm_intnum_get_int(num); + else + span->new_val = LONG_MAX; /* too complex; force to longest form */ + yasm_expr_destroy(abs_copy); + } + + if (span->rel_term) { + if (span->new_val != LONG_MAX && span->rel_term->new_val != LONG_MAX) + span->new_val += span->rel_term->new_val >> span->depval.rshift; + else + span->new_val = LONG_MAX; /* too complex; force to longest form */ + } else if (span->depval.rel) + span->new_val = LONG_MAX; /* too complex; force to longest form */ + + if (span->new_val == LONG_MAX) + span->active = 0; + + /* If id<=0, flag update on any change */ + if (span->id <= 0) + return (span->new_val != span->cur_val); + + return (span->new_val < span->neg_thres + || span->new_val > span->pos_thres); +} + +/* Updates all bytecode offsets. For offset-based bytecodes, calls expand + * to determine new length. + */ +static int +update_all_bc_offsets(yasm_object *object, yasm_errwarns *errwarns) +{ + yasm_section *sect; + int saw_error = 0; + + STAILQ_FOREACH(sect, &object->sections, link) { + unsigned long offset = 0; + + yasm_bytecode *bc = STAILQ_FIRST(§->bcs); + yasm_bytecode *prevbc; + + /* Skip our locally created empty bytecode first. */ + prevbc = bc; + bc = STAILQ_NEXT(bc, link); + + /* Iterate through the remainder, if any. */ + while (bc) { + if (bc->callback->special == YASM_BC_SPECIAL_OFFSET) { + /* Recalculate/adjust len of offset-based bytecodes here */ + long neg_thres = 0; + long pos_thres = (long)yasm_bc_next_offset(bc); + int retval = yasm_bc_expand(bc, 1, 0, + (long)yasm_bc_next_offset(prevbc), + &neg_thres, &pos_thres); + yasm_errwarn_propagate(errwarns, bc->line); + if (retval < 0) + saw_error = 1; + } + bc->offset = offset; + offset += bc->len*bc->mult_int; + prevbc = bc; + bc = STAILQ_NEXT(bc, link); + } + } + return saw_error; +} + +static void +span_destroy(/*@only@*/ yasm_span *span) +{ + unsigned int i; + + yasm_value_delete(&span->depval); + if (span->rel_term) + yasm_xfree(span->rel_term); + if (span->terms) + yasm_xfree(span->terms); + if (span->items) { + for (i=0; i<span->num_terms; i++) + yasm_intnum_destroy(span->items[i].data.intn); + yasm_xfree(span->items); + } + if (span->backtrace) + yasm_xfree(span->backtrace); + yasm_xfree(span); +} + +static void +optimize_cleanup(optimize_data *optd) +{ + yasm_span *s1, *s2; + yasm_offset_setter *os1, *os2; + + IT_destroy(optd->itree); + + s1 = TAILQ_FIRST(&optd->spans); + while (s1) { + s2 = TAILQ_NEXT(s1, link); + span_destroy(s1); + s1 = s2; + } + + os1 = STAILQ_FIRST(&optd->offset_setters); + while (os1) { + os2 = STAILQ_NEXT(os1, link); + yasm_xfree(os1); + os1 = os2; + } +} + +static void +optimize_itree_add(IntervalTree *itree, yasm_span *span, yasm_span_term *term) +{ + long precbc_index, precbc2_index; + unsigned long low, high; + + /* Update term length */ + if (term->precbc) + precbc_index = term->precbc->bc_index; + else + precbc_index = span->bc->bc_index-1; + + if (term->precbc2) + precbc2_index = term->precbc2->bc_index; + else + precbc2_index = span->bc->bc_index-1; + + if (precbc_index < precbc2_index) { + low = precbc_index+1; + high = precbc2_index; + } else if (precbc_index > precbc2_index) { + low = precbc2_index+1; + high = precbc_index; + } else + return; /* difference is same bc - always 0! */ + + IT_insert(itree, (long)low, (long)high, term); +} + +static void +check_cycle(IntervalTreeNode *node, void *d) +{ + optimize_data *optd = d; + yasm_span_term *term = node->data; + yasm_span *depspan = term->span; + int i; + int depspan_bt_alloc; + + /* Only check for cycles in id=0 spans */ + if (depspan->id > 0) + return; + + /* Check for a circular reference by looking to see if this dependent + * span is in our backtrace. + */ + if (optd->span->backtrace) { + for (i=0; i<optd->span->backtrace_size; i++) { + if (optd->span->backtrace[i] == depspan) + yasm_error_set(YASM_ERROR_VALUE, + N_("circular reference detected")); + } + } + + /* Add our complete backtrace and ourselves to backtrace of dependent + * span. + */ + if (!depspan->backtrace) { + depspan->backtrace = yasm_xmalloc((optd->span->backtrace_size+1)* + sizeof(yasm_span *)); + if (optd->span->backtrace_size > 0) + memcpy(depspan->backtrace, optd->span->backtrace, + optd->span->backtrace_size*sizeof(yasm_span *)); + depspan->backtrace[optd->span->backtrace_size] = optd->span; + depspan->backtrace_size = optd->span->backtrace_size+1; + return; + } + + /* Add our complete backtrace, checking for duplicates */ + depspan_bt_alloc = depspan->backtrace_size; + for (i=0; i<optd->span->backtrace_size; i++) { + int present = 0; + int j; + for (j=0; j<depspan->backtrace_size; j++) { + if (optd->span->backtrace[i] == optd->span->backtrace[j]) { + present = 1; + break; + } + } + if (present) + continue; + /* Not already in array; add it. */ + if (depspan->backtrace_size >= depspan_bt_alloc) + { + depspan_bt_alloc *= 2; + depspan->backtrace = + yasm_xrealloc(depspan->backtrace, + depspan_bt_alloc*sizeof(yasm_span *)); + } + depspan->backtrace[depspan->backtrace_size] = optd->span->backtrace[i]; + depspan->backtrace_size++; + } + + /* Add ourselves. */ + if (depspan->backtrace_size >= depspan_bt_alloc) + { + depspan_bt_alloc++; + depspan->backtrace = + yasm_xrealloc(depspan->backtrace, + depspan_bt_alloc*sizeof(yasm_span *)); + } + depspan->backtrace[depspan->backtrace_size] = optd->span; + depspan->backtrace_size++; +} + +static void +optimize_term_expand(IntervalTreeNode *node, void *d) +{ + optimize_data *optd = d; + yasm_span_term *term = node->data; + yasm_span *span = term->span; + long len_diff = optd->len_diff; + long precbc_index, precbc2_index; + + /* Don't expand inactive spans */ + if (!span->active) + return; + + /* Update term length */ + if (term->precbc) + precbc_index = term->precbc->bc_index; + else + precbc_index = span->bc->bc_index-1; + + if (term->precbc2) + precbc2_index = term->precbc2->bc_index; + else + precbc2_index = span->bc->bc_index-1; + + if (precbc_index < precbc2_index) + term->new_val += len_diff; + else + term->new_val -= len_diff; + + /* If already on Q, don't re-add */ + if (span->active == 2) + return; + + /* Update term and check against thresholds */ + if (!recalc_normal_span(span)) + return; /* didn't exceed thresholds, we're done */ + + /* Exceeded thresholds, need to add to Q for expansion */ + if (span->id <= 0) + STAILQ_INSERT_TAIL(&optd->QA, span, linkq); + else + STAILQ_INSERT_TAIL(&optd->QB, span, linkq); + span->active = 2; /* Mark as being in Q */ +} + +void +yasm_object_optimize(yasm_object *object, yasm_errwarns *errwarns) +{ + yasm_section *sect; + unsigned long bc_index = 0; + int saw_error = 0; + optimize_data optd; + yasm_span *span, *span_temp; + yasm_offset_setter *os; + int retval; + unsigned int i; + + TAILQ_INIT(&optd.spans); + STAILQ_INIT(&optd.offset_setters); + optd.itree = IT_create(); + + /* Create an placeholder offset setter for spans to point to; this will + * get updated if/when we actually run into one. + */ + os = yasm_xmalloc(sizeof(yasm_offset_setter)); + os->bc = NULL; + os->cur_val = 0; + os->new_val = 0; + os->thres = 0; + STAILQ_INSERT_TAIL(&optd.offset_setters, os, link); + optd.os = os; + + /* Step 1a */ + STAILQ_FOREACH(sect, &object->sections, link) { + unsigned long offset = 0; + + yasm_bytecode *bc = STAILQ_FIRST(§->bcs); + yasm_bytecode *prevbc; + + bc->bc_index = bc_index++; + + /* Skip our locally created empty bytecode first. */ + prevbc = bc; + bc = STAILQ_NEXT(bc, link); + + /* Iterate through the remainder, if any. */ + while (bc) { + bc->bc_index = bc_index++; + bc->offset = offset; + + retval = yasm_bc_calc_len(bc, optimize_add_span, &optd); + yasm_errwarn_propagate(errwarns, bc->line); + if (retval) + saw_error = 1; + else { + if (bc->callback->special == YASM_BC_SPECIAL_OFFSET) { + /* Remember it as offset setter */ + os->bc = bc; + os->thres = yasm_bc_next_offset(bc); + + /* Create new placeholder */ + os = yasm_xmalloc(sizeof(yasm_offset_setter)); + os->bc = NULL; + os->cur_val = 0; + os->new_val = 0; + os->thres = 0; + STAILQ_INSERT_TAIL(&optd.offset_setters, os, link); + optd.os = os; + + if (bc->multiple) { + yasm_error_set(YASM_ERROR_VALUE, + N_("cannot combine multiples and setting assembly position")); + yasm_errwarn_propagate(errwarns, bc->line); + saw_error = 1; + } + } + + offset += bc->len*bc->mult_int; + } + + prevbc = bc; + bc = STAILQ_NEXT(bc, link); + } + } + + if (saw_error) { + optimize_cleanup(&optd); + return; + } + + /* Step 1b */ + TAILQ_FOREACH_SAFE(span, &optd.spans, link, span_temp) { + span_create_terms(span); + if (yasm_error_occurred()) { + yasm_errwarn_propagate(errwarns, span->bc->line); + saw_error = 1; + } else if (recalc_normal_span(span)) { + retval = yasm_bc_expand(span->bc, span->id, span->cur_val, + span->new_val, &span->neg_thres, + &span->pos_thres); + yasm_errwarn_propagate(errwarns, span->bc->line); + if (retval < 0) + saw_error = 1; + else if (retval > 0) { + if (!span->active) { + yasm_error_set(YASM_ERROR_VALUE, + N_("secondary expansion of an external/complex value")); + yasm_errwarn_propagate(errwarns, span->bc->line); + saw_error = 1; + } + } else { + TAILQ_REMOVE(&optd.spans, span, link); + span_destroy(span); + continue; + } + } + span->cur_val = span->new_val; + } + + if (saw_error) { + optimize_cleanup(&optd); + return; + } + + /* Step 1c */ + if (update_all_bc_offsets(object, errwarns)) { + optimize_cleanup(&optd); + return; + } + + /* Step 1d */ + STAILQ_INIT(&optd.QB); + TAILQ_FOREACH(span, &optd.spans, link) { + yasm_intnum *intn; + + /* Update span terms based on new bc offsets */ + for (i=0; i<span->num_terms; i++) { + intn = yasm_calc_bc_dist(span->terms[i].precbc, + span->terms[i].precbc2); + if (!intn) + yasm_internal_error(N_("could not calculate bc distance")); + span->terms[i].cur_val = span->terms[i].new_val; + span->terms[i].new_val = yasm_intnum_get_int(intn); + yasm_intnum_destroy(intn); + } + if (span->rel_term) { + span->rel_term->cur_val = span->rel_term->new_val; + if (span->rel_term->precbc2) + span->rel_term->new_val = + yasm_bc_next_offset(span->rel_term->precbc2) - + span->bc->offset; + else + span->rel_term->new_val = span->bc->offset - + yasm_bc_next_offset(span->rel_term->precbc); + } + + if (recalc_normal_span(span)) { + /* Exceeded threshold, add span to QB */ + STAILQ_INSERT_TAIL(&optd.QB, span, linkq); + span->active = 2; + } + } + + /* Do we need step 2? If not, go ahead and exit. */ + if (STAILQ_EMPTY(&optd.QB)) { + optimize_cleanup(&optd); + return; + } + + /* Update offset-setters values */ + STAILQ_FOREACH(os, &optd.offset_setters, link) { + if (!os->bc) + continue; + os->thres = yasm_bc_next_offset(os->bc); + os->new_val = os->bc->offset; + os->cur_val = os->new_val; + } + + /* Build up interval tree */ + TAILQ_FOREACH(span, &optd.spans, link) { + for (i=0; i<span->num_terms; i++) + optimize_itree_add(optd.itree, span, &span->terms[i]); + if (span->rel_term) + optimize_itree_add(optd.itree, span, span->rel_term); + } + + /* Look for cycles in times expansion (span.id==0) */ + TAILQ_FOREACH(span, &optd.spans, link) { + if (span->id > 0) + continue; + optd.span = span; + IT_enumerate(optd.itree, (long)span->bc->bc_index, + (long)span->bc->bc_index, &optd, check_cycle); + if (yasm_error_occurred()) { + yasm_errwarn_propagate(errwarns, span->bc->line); + saw_error = 1; + } + } + + if (saw_error) { + optimize_cleanup(&optd); + return; + } + + /* Step 2 */ + STAILQ_INIT(&optd.QA); + while (!STAILQ_EMPTY(&optd.QA) || !(STAILQ_EMPTY(&optd.QB))) { + unsigned long orig_len; + long offset_diff; + + /* QA is for TIMES, update those first, then update non-TIMES. + * This is so that TIMES can absorb increases before we look at + * expanding non-TIMES BCs. + */ + if (!STAILQ_EMPTY(&optd.QA)) { + span = STAILQ_FIRST(&optd.QA); + STAILQ_REMOVE_HEAD(&optd.QA, linkq); + } else { + span = STAILQ_FIRST(&optd.QB); + STAILQ_REMOVE_HEAD(&optd.QB, linkq); + } + + if (!span->active) + continue; + span->active = 1; /* no longer in Q */ + + /* Make sure we ended up ultimately exceeding thresholds; due to + * offset BCs we may have been placed on Q and then reduced in size + * again. + */ + if (!recalc_normal_span(span)) + continue; + + orig_len = span->bc->len * span->bc->mult_int; + + retval = yasm_bc_expand(span->bc, span->id, span->cur_val, + span->new_val, &span->neg_thres, + &span->pos_thres); + yasm_errwarn_propagate(errwarns, span->bc->line); + + if (retval < 0) { + /* error */ + saw_error = 1; + continue; + } else if (retval > 0) { + /* another threshold, keep active */ + for (i=0; i<span->num_terms; i++) + span->terms[i].cur_val = span->terms[i].new_val; + if (span->rel_term) + span->rel_term->cur_val = span->rel_term->new_val; + span->cur_val = span->new_val; + } else + span->active = 0; /* we're done with this span */ + + optd.len_diff = span->bc->len * span->bc->mult_int - orig_len; + if (optd.len_diff == 0) + continue; /* didn't increase in size */ + + /* Iterate over all spans dependent across the bc just expanded */ + IT_enumerate(optd.itree, (long)span->bc->bc_index, + (long)span->bc->bc_index, &optd, optimize_term_expand); + + /* Iterate over offset-setters that follow the bc just expanded. + * Stop iteration if: + * - no more offset-setters in this section + * - offset-setter didn't move its following offset + */ + os = span->os; + offset_diff = optd.len_diff; + while (os->bc && os->bc->section == span->bc->section + && offset_diff != 0) { + unsigned long old_next_offset = os->cur_val + os->bc->len; + long neg_thres_temp; + + if (offset_diff < 0 && (unsigned long)(-offset_diff) > os->new_val) + yasm_internal_error(N_("org/align went to negative offset")); + os->new_val += offset_diff; + + orig_len = os->bc->len; + retval = yasm_bc_expand(os->bc, 1, (long)os->cur_val, + (long)os->new_val, &neg_thres_temp, + (long *)&os->thres); + yasm_errwarn_propagate(errwarns, os->bc->line); + + offset_diff = os->new_val + os->bc->len - old_next_offset; + optd.len_diff = os->bc->len - orig_len; + if (optd.len_diff != 0) + IT_enumerate(optd.itree, (long)os->bc->bc_index, + (long)os->bc->bc_index, &optd, optimize_term_expand); + + os->cur_val = os->new_val; + os = STAILQ_NEXT(os, link); + } + } + + if (saw_error) { + optimize_cleanup(&optd); + return; + } + + /* Step 3 */ + update_all_bc_offsets(object, errwarns); + optimize_cleanup(&optd); +} diff --git a/libyasm/section.h b/libyasm/section.h new file mode 100644 index 0000000..2c7faa4 --- /dev/null +++ b/libyasm/section.h @@ -0,0 +1,383 @@ +/** + * \file libyasm/section.h + * \brief YASM section interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_SECTION_H +#define YASM_SECTION_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Basic YASM relocation. Object formats will need to extend this + * structure with additional fields for relocation type, etc. + */ +typedef struct yasm_reloc yasm_reloc; + +struct yasm_reloc { + /*@reldef@*/ STAILQ_ENTRY(yasm_reloc) link; /**< Link to next reloc */ + yasm_intnum *addr; /**< Offset (address) within section */ + /*@dependent@*/ yasm_symrec *sym; /**< Relocated symbol */ +}; + +/** An object. This is the internal representation of an object file. */ +struct yasm_object { + /*@owned@*/ char *src_filename; /**< Source filename */ + /*@owned@*/ char *obj_filename; /**< Object filename */ + + /*@owned@*/ yasm_symtab *symtab; /**< Symbol table */ + /*@owned@*/ yasm_arch *arch; /**< Target architecture */ + /*@owned@*/ yasm_objfmt *objfmt; /**< Object format */ + /*@owned@*/ yasm_dbgfmt *dbgfmt; /**< Debug format */ + + /** Currently active section. Used by some directives. NULL if no + * section active. + */ + /*@dependent@*/ /*@null@*/ yasm_section *cur_section; + + /** Linked list of sections. */ + /*@reldef@*/ STAILQ_HEAD(yasm_sectionhead, yasm_section) sections; + + /** Directives, organized as two level HAMT; first level is parser, + * second level is directive name. + */ + /*@owned@*/ struct HAMT *directives; + + /** Prefix prepended to externally-visible symbols (empty string if none) */ + /*@owned@*/ char *global_prefix; + + /** Suffix appended to externally-visible symbols (empty string if none) */ + /*@owned@*/ char *global_suffix; +}; + +/** Create a new object. A default section is created as the first section. + * An empty symbol table (yasm_symtab) and line mapping (yasm_linemap) are + * automatically created. + * \param src_filename source filename (e.g. "file.asm") + * \param obj_filename object filename (e.g. "file.o") + * \param arch architecture + * \param objfmt_module object format module + * \param dbgfmt_module debug format module + * \return Newly allocated object, or NULL on error. + */ +YASM_LIB_DECL +/*@null@*/ /*@only@*/ yasm_object *yasm_object_create + (const char *src_filename, const char *obj_filename, + /*@kept@*/ yasm_arch *arch, + const yasm_objfmt_module *objfmt_module, + const yasm_dbgfmt_module *dbgfmt_module); + +/** Create a new, or continue an existing, general section. The section is + * added to the object if there's not already a section by that name. + * \param object object + * \param name section name + * \param align alignment in bytes (0 if none) + * \param code if nonzero, section is intended to contain code + * (e.g. alignment should be made with NOP instructions, not 0) + * \param res_only if nonzero, only space-reserving bytecodes are allowed in + * the section (ignored if section already exists) + * \param isnew output; set to nonzero if section did not already exist + * \param line virtual line of section declaration (ignored if section + * already exists) + * \return New section. + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_section *yasm_object_get_general + (yasm_object *object, const char *name, unsigned long align, int code, + int res_only, /*@out@*/ int *isnew, unsigned long line); + +/** Handle a directive. Passed down to object format, debug format, or + * architecture as appropriate. + * \param object object + * \param name directive name + * \param parser parser keyword + * \param valparams value/parameters + * \param objext_valparams "object format-specific" value/parameters + * \param line virtual line (from yasm_linemap) + * \return 0 if directive recognized, nonzero if unrecognized. + */ +YASM_LIB_DECL +int yasm_object_directive(yasm_object *object, const char *name, + const char *parser, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, + unsigned long line); + +/** Delete (free allocated memory for) an object. All sections in the + * object and all bytecodes within those sections are also deleted. + * \param object object + */ +YASM_LIB_DECL +void yasm_object_destroy(/*@only@*/ yasm_object *object); + +/** Print an object. For debugging purposes. + * \param object object + * \param f file + * \param indent_level indentation level + */ +YASM_LIB_DECL +void yasm_object_print(const yasm_object *object, FILE *f, int indent_level); + +/** Finalize an object after parsing. + * \param object object + * \param errwarns error/warning set + * \note Errors/warnings are stored into errwarns. + */ +YASM_LIB_DECL +void yasm_object_finalize(yasm_object *object, yasm_errwarns *errwarns); + +/** Traverses all sections in an object, calling a function on each section. + * \param object object + * \param d data pointer passed to func on each call + * \param func function + * \return Stops early (and returns func's return value) if func returns a + * nonzero value; otherwise 0. + */ +YASM_LIB_DECL +int yasm_object_sections_traverse + (yasm_object *object, /*@null@*/ void *d, + int (*func) (yasm_section *sect, /*@null@*/ void *d)); + +/** Find a general section in an object, based on its name. + * \param object object + * \param name section name + * \return Section matching name, or NULL if no match found. + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ yasm_section *yasm_object_find_general + (yasm_object *object, const char *name); + +/** Change the source filename for an object. + * \param object object + * \param src_filename new source filename (e.g. "file.asm") + */ +YASM_LIB_DECL +void yasm_object_set_source_fn(yasm_object *object, const char *src_filename); + +/** Change the prefix used for externally-visible symbols. + * \param object object + * \param prefix new prefix + */ +YASM_LIB_DECL +void yasm_object_set_global_prefix(yasm_object *object, const char *prefix); + +/** Change the suffix used for externally-visible symbols. + * \param object object + * \param suffix new suffix + */ +YASM_LIB_DECL +void yasm_object_set_global_suffix(yasm_object *object, const char *suffix); + +/** Optimize an object. Takes the unoptimized object and optimizes it. + * If successful, the object is ready for output to an object file. + * \param object object + * \param errwarns error/warning set + * \note Optimization failures are stored into errwarns. + */ +YASM_LIB_DECL +void yasm_object_optimize(yasm_object *object, yasm_errwarns *errwarns); + +/** Determine if a section is flagged to contain code. + * \param sect section + * \return Nonzero if section is flagged to contain code. + */ +YASM_LIB_DECL +int yasm_section_is_code(yasm_section *sect); + +/** Get yasm_optimizer-specific flags. For yasm_optimizer use only. + * \param sect section + * \return Optimizer-specific flags. + */ +YASM_LIB_DECL +unsigned long yasm_section_get_opt_flags(const yasm_section *sect); + +/** Set yasm_optimizer-specific flags. For yasm_optimizer use only. + * \param sect section + * \param opt_flags optimizer-specific flags. + */ +YASM_LIB_DECL +void yasm_section_set_opt_flags(yasm_section *sect, unsigned long opt_flags); + +/** Determine if a section was declared as the "default" section (e.g. not + * created through a section directive). + * \param sect section + * \return Nonzero if section was declared as default. + */ +YASM_LIB_DECL +int yasm_section_is_default(const yasm_section *sect); + +/** Set section "default" flag to a new value. + * \param sect section + * \param def new value of default flag + */ +YASM_LIB_DECL +void yasm_section_set_default(yasm_section *sect, int def); + +/** Get object owner of a section. + * \param sect section + * \return Object this section is a part of. + */ +YASM_LIB_DECL +yasm_object *yasm_section_get_object(const yasm_section *sect); + +/** Get assocated data for a section and data callback. + * \param sect section + * \param callback callback used when adding data + * \return Associated data (NULL if none). + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ void *yasm_section_get_data + (yasm_section *sect, const yasm_assoc_data_callback *callback); + +/** Add associated data to a section. + * \attention Deletes any existing associated data for that data callback. + * \param sect section + * \param callback callback + * \param data data to associate + */ +YASM_LIB_DECL +void yasm_section_add_data(yasm_section *sect, + const yasm_assoc_data_callback *callback, + /*@null@*/ /*@only@*/ void *data); + +/** Add a relocation to a section. + * \param sect section + * \param reloc relocation + * \param destroy_func function that can destroy the relocation + * \note Does not make a copy of reloc. The same destroy_func must be + * used for all relocations in a section or an internal error will occur. + * The section will destroy the relocation address; it is the caller's + * responsibility to destroy any other allocated data. + */ +YASM_LIB_DECL +void yasm_section_add_reloc(yasm_section *sect, yasm_reloc *reloc, + void (*destroy_func) (/*@only@*/ void *reloc)); + +/** Get the first relocation for a section. + * \param sect section + * \return First relocation for section. NULL if no relocations. + */ +YASM_LIB_DECL +/*@null@*/ yasm_reloc *yasm_section_relocs_first(yasm_section *sect); + +/** Get the next relocation for a section. + * \param reloc previous relocation + * \return Next relocation for section. NULL if no more relocations. + */ +/*@null@*/ yasm_reloc *yasm_section_reloc_next(yasm_reloc *reloc); +#ifndef YASM_DOXYGEN +#define yasm_section_reloc_next(x) STAILQ_NEXT((x), link) +#endif + +/** Get the basic relocation information for a relocation. + * \param reloc relocation + * \param addrp address of relocation within section (returned) + * \param symp relocated symbol (returned) + */ +YASM_LIB_DECL +void yasm_reloc_get(yasm_reloc *reloc, yasm_intnum **addrp, + /*@dependent@*/ yasm_symrec **symp); + +/** Get the first bytecode in a section. + * \param sect section + * \return First bytecode in section (at least one empty bytecode is always + * present). + */ +YASM_LIB_DECL +yasm_bytecode *yasm_section_bcs_first(yasm_section *sect); + +/** Get the last bytecode in a section. + * \param sect section + * \return Last bytecode in section (at least one empty bytecode is always + * present). + */ +YASM_LIB_DECL +yasm_bytecode *yasm_section_bcs_last(yasm_section *sect); + +/** Add bytecode to the end of a section. + * \note Does not make a copy of bc; so don't pass this function static or + * local variables, and discard the bc pointer after calling this + * function. + * \param sect section + * \param bc bytecode (may be NULL) + * \return If bytecode was actually appended (it wasn't NULL or empty), the + * bytecode; otherwise NULL. + */ +YASM_LIB_DECL +/*@only@*/ /*@null@*/ yasm_bytecode *yasm_section_bcs_append + (yasm_section *sect, + /*@returned@*/ /*@only@*/ /*@null@*/ yasm_bytecode *bc); + +/** Traverses all bytecodes in a section, calling a function on each bytecode. + * \param sect section + * \param errwarns error/warning set (may be NULL) + * \param d data pointer passed to func on each call (may be NULL) + * \param func function + * \return Stops early (and returns func's return value) if func returns a + * nonzero value; otherwise 0. + * \note If errwarns is non-NULL, yasm_errwarn_propagate() is called after + * each call to func (with the bytecode's line number). + */ +YASM_LIB_DECL +int yasm_section_bcs_traverse + (yasm_section *sect, /*@null@*/ yasm_errwarns *errwarns, + /*@null@*/ void *d, int (*func) (yasm_bytecode *bc, /*@null@*/ void *d)); + +/** Get name of a section. + * \param sect section + * \return Section name. + */ +YASM_LIB_DECL +/*@observer@*/ const char *yasm_section_get_name(const yasm_section *sect); + +/** Change alignment of a section. + * \param sect section + * \param align alignment in bytes + * \param line virtual line + */ +YASM_LIB_DECL +void yasm_section_set_align(yasm_section *sect, unsigned long align, + unsigned long line); + +/** Get alignment of a section. + * \param sect section + * \return Alignment in bytes (0 if none). + */ +YASM_LIB_DECL +unsigned long yasm_section_get_align(const yasm_section *sect); + +/** Print a section. For debugging purposes. + * \param f file + * \param indent_level indentation level + * \param sect section + * \param print_bcs if nonzero, print bytecodes within section + */ +YASM_LIB_DECL +void yasm_section_print(/*@null@*/ const yasm_section *sect, FILE *f, + int indent_level, int print_bcs); + +#endif diff --git a/libyasm/strcasecmp.c b/libyasm/strcasecmp.c new file mode 100644 index 0000000..a87bd88 --- /dev/null +++ b/libyasm/strcasecmp.c @@ -0,0 +1,94 @@ +/* + * strcasecmp() implementation for systems that don't have it or stricmp() + * or strcmpi(). + * + * Copyright (c) 1987, 1993 + * The Regents of the University of California. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "util.h" + +#ifndef USE_OUR_OWN_STRCASECMP +#undef yasm__strcasecmp +#undef yasm__strncasecmp +#endif + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <ctype.h> + +int +yasm__strcasecmp(const char *s1, const char *s2) +{ +#ifdef HAVE_STRCASECMP + return strcasecmp(s1, s2); +#elif HAVE_STRICMP + return stricmp(s1, s2); +#elif HAVE__STRICMP + return _stricmp(s1, s2); +#elif HAVE_STRCMPI + return strcmpi(s1, s2); +#else + const unsigned char + *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + while (tolower(*us1) == tolower(*us2++)) + if (*us1++ == '\0') + return (0); + return (tolower(*us1) - tolower(*--us2)); +#endif +} + +int +yasm__strncasecmp(const char *s1, const char *s2, size_t n) +{ +#ifdef HAVE_STRCASECMP + return strncasecmp(s1, s2, n); +#elif HAVE_STRICMP + return strnicmp(s1, s2, n); +#elif HAVE__STRNICMP + return _strnicmp(s1, s2, n); +#elif HAVE_STRCMPI + return strncmpi(s1, s2, n); +#else + const unsigned char + *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + if (n != 0) { + do { + if (tolower(*us1) != tolower(*us2++)) + return (tolower(*us1) - tolower(*--us2)); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return (0); +#endif +} diff --git a/libyasm/strsep.c b/libyasm/strsep.c new file mode 100644 index 0000000..0fecd47 --- /dev/null +++ b/libyasm/strsep.c @@ -0,0 +1,85 @@ +/* + * strsep() implementation for systems that don't have it. + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ +#define NO_STRING_INLINES +#include "util.h" + + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#ifdef HAVE_STRSEP +#undef yasm__strsep +#endif + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +/*@-nullstate@*/ +char * +yasm__strsep(char **stringp, const char *delim) +{ +#ifdef HAVE_STRSEP + return strsep(stringp, delim); +#else + register char *s; + register const char *spanp; + register int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +#endif +} +/*@=nullstate@*/ diff --git a/libyasm/symrec.c b/libyasm/symrec.c new file mode 100644 index 0000000..694f0c6 --- /dev/null +++ b/libyasm/symrec.c @@ -0,0 +1,714 @@ +/* + * Symbol table handling + * + * Copyright (C) 2001-2007 Michael Urman, Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include <limits.h> +#include <ctype.h> + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "valparam.h" +#include "hamt.h" +#include "assocdat.h" + +#include "errwarn.h" +#include "intnum.h" +#include "floatnum.h" +#include "expr.h" +#include "symrec.h" + +#include "bytecode.h" +#include "section.h" +#include "objfmt.h" + + +typedef enum { + SYM_UNKNOWN, /* for unknown type (COMMON/EXTERN) */ + SYM_EQU, /* for EQU defined symbols (expressions) */ + SYM_LABEL, /* for labels */ + SYM_CURPOS, /* for labels representing the current + assembly position */ + SYM_SPECIAL /* for special symbols that need to be in + the symbol table but otherwise have no + purpose */ +} sym_type; + +struct yasm_symrec { + char *name; + sym_type type; + yasm_sym_status status; + yasm_sym_vis visibility; + unsigned long def_line; /* line where symbol was first defined */ + unsigned long decl_line; /* line where symbol was first declared */ + unsigned long use_line; /* line where symbol was first used */ + union { + yasm_expr *expn; /* equ value */ + + /* bytecode immediately preceding a label */ + /*@dependent@*/ yasm_bytecode *precbc; + } value; + unsigned int size; /* 0 if not user-defined */ + const char *segment; /* for segmented systems like DOS */ + + /* associated data; NULL if none */ + /*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data; +}; + +/* Linked list of symbols not in the symbol table. */ +typedef struct non_table_symrec_s { + /*@reldef@*/ SLIST_ENTRY(non_table_symrec_s) link; + /*@owned@*/ yasm_symrec *rec; +} non_table_symrec; + +struct yasm_symtab { + /* The symbol table: a hash array mapped trie (HAMT). */ + /*@only@*/ HAMT *sym_table; + /* Symbols not in the table */ + SLIST_HEAD(nontablesymhead_s, non_table_symrec_s) non_table_syms; + + int case_sensitive; +}; + +static void +objext_valparams_destroy(void *data) +{ + yasm_vps_destroy((yasm_valparamhead *)data); +} + +static void +objext_valparams_print(void *data, FILE *f, int indent_level) +{ + yasm_vps_print((yasm_valparamhead *)data, f); +} + +static yasm_assoc_data_callback objext_valparams_cb = { + objext_valparams_destroy, + objext_valparams_print +}; + +static void +common_size_destroy(void *data) +{ + yasm_expr **e = (yasm_expr **)data; + yasm_expr_destroy(*e); + yasm_xfree(data); +} + +static void +common_size_print(void *data, FILE *f, int indent_level) +{ + yasm_expr **e = (yasm_expr **)data; + yasm_expr_print(*e, f); +} + +static yasm_assoc_data_callback common_size_cb = { + common_size_destroy, + common_size_print +}; + +yasm_symtab * +yasm_symtab_create(void) +{ + yasm_symtab *symtab = yasm_xmalloc(sizeof(yasm_symtab)); + symtab->sym_table = HAMT_create(0, yasm_internal_error_); + SLIST_INIT(&symtab->non_table_syms); + symtab->case_sensitive = 1; + return symtab; +} + +void +yasm_symtab_set_case_sensitive(yasm_symtab *symtab, int sensitive) +{ + symtab->case_sensitive = sensitive; +} + +static void +symrec_destroy_one(/*@only@*/ void *d) +{ + yasm_symrec *sym = d; + yasm_xfree(sym->name); + if (sym->type == SYM_EQU && (sym->status & YASM_SYM_VALUED)) + yasm_expr_destroy(sym->value.expn); + yasm__assoc_data_destroy(sym->assoc_data); + yasm_xfree(sym); +} + +static /*@partial@*/ yasm_symrec * +symrec_new_common(/*@keep@*/ char *name, int case_sensitive) +{ + yasm_symrec *rec = yasm_xmalloc(sizeof(yasm_symrec)); + + if (!case_sensitive) { + char *c; + for (c=name; *c; c++) + *c = tolower(*c); + } + + rec->name = name; + rec->type = SYM_UNKNOWN; + rec->def_line = 0; + rec->decl_line = 0; + rec->use_line = 0; + rec->visibility = YASM_SYM_LOCAL; + rec->size = 0; + rec->segment = NULL; + rec->assoc_data = NULL; + return rec; +} + +static /*@partial@*/ /*@dependent@*/ yasm_symrec * +symtab_get_or_new_in_table(yasm_symtab *symtab, /*@only@*/ char *name) +{ + yasm_symrec *rec = symrec_new_common(name, symtab->case_sensitive); + int replace = 0; + + rec->status = YASM_SYM_NOSTATUS; + + if (!symtab->case_sensitive) { + char *c; + for (c=name; *c; c++) + *c = tolower(*c); + } + + return HAMT_insert(symtab->sym_table, name, rec, &replace, + symrec_destroy_one); +} + +static /*@partial@*/ /*@dependent@*/ yasm_symrec * +symtab_get_or_new_not_in_table(yasm_symtab *symtab, /*@only@*/ char *name) +{ + non_table_symrec *sym = yasm_xmalloc(sizeof(non_table_symrec)); + sym->rec = symrec_new_common(name, symtab->case_sensitive); + + sym->rec->status = YASM_SYM_NOTINTABLE; + + SLIST_INSERT_HEAD(&symtab->non_table_syms, sym, link); + + return sym->rec; +} + +/* create a new symrec */ +/*@-freshtrans -mustfree@*/ +static /*@partial@*/ /*@dependent@*/ yasm_symrec * +symtab_get_or_new(yasm_symtab *symtab, const char *name, int in_table) +{ + char *symname = yasm__xstrdup(name); + + if (in_table) + return symtab_get_or_new_in_table(symtab, symname); + else + return symtab_get_or_new_not_in_table(symtab, symname); +} +/*@=freshtrans =mustfree@*/ + +int +yasm_symtab_traverse(yasm_symtab *symtab, void *d, + int (*func) (yasm_symrec *sym, void *d)) +{ + return HAMT_traverse(symtab->sym_table, d, (int (*) (void *, void *))func); +} + +const yasm_symtab_iter * +yasm_symtab_first(const yasm_symtab *symtab) +{ + return (const yasm_symtab_iter *)HAMT_first(symtab->sym_table); +} + +/*@null@*/ const yasm_symtab_iter * +yasm_symtab_next(const yasm_symtab_iter *prev) +{ + return (const yasm_symtab_iter *)HAMT_next((const HAMTEntry *)prev); +} + +yasm_symrec * +yasm_symtab_iter_value(const yasm_symtab_iter *cur) +{ + return (yasm_symrec *)HAMTEntry_get_data((const HAMTEntry *)cur); +} + +yasm_symrec * +yasm_symtab_abs_sym(yasm_symtab *symtab) +{ + yasm_symrec *rec = symtab_get_or_new(symtab, "", 1); + rec->def_line = 0; + rec->decl_line = 0; + rec->use_line = 0; + rec->type = SYM_EQU; + rec->value.expn = + yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)), 0); + rec->status |= YASM_SYM_DEFINED|YASM_SYM_VALUED|YASM_SYM_USED; + return rec; +} + +yasm_symrec * +yasm_symtab_use(yasm_symtab *symtab, const char *name, unsigned long line) +{ + yasm_symrec *rec = symtab_get_or_new(symtab, name, 1); + if (rec->use_line == 0) + rec->use_line = line; /* set line number of first use */ + rec->status |= YASM_SYM_USED; + return rec; +} + +yasm_symrec * +yasm_symtab_get(yasm_symtab *symtab, const char *name) +{ + if (!symtab->case_sensitive) { + char *_name = yasm__xstrdup(name); + char *c; + yasm_symrec *ret; + for (c=_name; *c; c++) + *c = tolower(*c); + ret = HAMT_search(symtab->sym_table, _name); + yasm_xfree(_name); + return ret; + } else + return HAMT_search(symtab->sym_table, name); +} + +static /*@dependent@*/ yasm_symrec * +symtab_define(yasm_symtab *symtab, const char *name, sym_type type, + int in_table, unsigned long line) +{ + yasm_symrec *rec = symtab_get_or_new(symtab, name, in_table); + + /* Has it been defined before (either by DEFINED or COMMON/EXTERN)? */ + if (rec->status & YASM_SYM_DEFINED) { + yasm_error_set_xref(rec->def_line!=0 ? rec->def_line : rec->decl_line, + N_("`%s' previously defined here"), name); + yasm_error_set(YASM_ERROR_GENERAL, N_("redefinition of `%s'"), + name); + } else { + if (rec->visibility & YASM_SYM_EXTERN) + yasm_warn_set(YASM_WARN_GENERAL, + N_("`%s' both defined and declared extern"), name); + rec->def_line = line; /* set line number of definition */ + rec->type = type; + rec->status |= YASM_SYM_DEFINED; + rec->size = 0; + rec->segment = NULL; + } + return rec; +} + +yasm_symrec * +yasm_symtab_define_equ(yasm_symtab *symtab, const char *name, yasm_expr *e, + unsigned long line) +{ + yasm_symrec *rec = symtab_define(symtab, name, SYM_EQU, 1, line); + if (yasm_error_occurred()) + return rec; + rec->value.expn = e; + rec->status |= YASM_SYM_VALUED; + return rec; +} + +yasm_symrec * +yasm_symtab_define_label(yasm_symtab *symtab, const char *name, + yasm_bytecode *precbc, int in_table, + unsigned long line) +{ + yasm_symrec *rec = symtab_define(symtab, name, SYM_LABEL, in_table, line); + if (yasm_error_occurred()) + return rec; + rec->value.precbc = precbc; + if (in_table && precbc) + yasm_bc__add_symrec(precbc, rec); + return rec; +} + +yasm_symrec * +yasm_symtab_define_curpos(yasm_symtab *symtab, const char *name, + yasm_bytecode *precbc, unsigned long line) +{ + yasm_symrec *rec = symtab_define(symtab, name, SYM_CURPOS, 0, line); + if (yasm_error_occurred()) + return rec; + rec->value.precbc = precbc; + return rec; +} + +yasm_symrec * +yasm_symtab_define_special(yasm_symtab *symtab, const char *name, + yasm_sym_vis vis) +{ + yasm_symrec *rec = symtab_define(symtab, name, SYM_SPECIAL, 1, 0); + if (yasm_error_occurred()) + return rec; + rec->status |= YASM_SYM_VALUED; + rec->visibility = vis; + return rec; +} + +yasm_symrec * +yasm_symtab_declare(yasm_symtab *symtab, const char *name, yasm_sym_vis vis, + unsigned long line) +{ + yasm_symrec *rec = symtab_get_or_new(symtab, name, 1); + yasm_symrec_declare(rec, vis, line); + return rec; +} + +void +yasm_symrec_declare(yasm_symrec *rec, yasm_sym_vis vis, unsigned long line) +{ + /* Allowable combinations: + * Existing State-------------- vis New State------------------- + * DEFINED GLOBAL COMMON EXTERN GCE DEFINED GLOBAL COMMON EXTERN + * 0 - 0 0 GCE 0 G C E + * 0 - 0 1 GE 0 G 0 E + * 0 - 1 0 GC 0 G C 0 + * X 0 - 1 1 + * 1 - 0 0 G 1 G 0 0 + * X 1 - - 1 + * X 1 - 1 - + */ + if ((vis == YASM_SYM_GLOBAL) || + (!(rec->status & YASM_SYM_DEFINED) && + (!(rec->visibility & (YASM_SYM_COMMON | YASM_SYM_EXTERN)) || + ((rec->visibility & YASM_SYM_COMMON) && (vis == YASM_SYM_COMMON)) || + ((rec->visibility & YASM_SYM_EXTERN) && (vis == YASM_SYM_EXTERN))))) { + rec->decl_line = line; + rec->visibility |= vis; + } else + yasm_error_set(YASM_ERROR_GENERAL, + N_("duplicate definition of `%s'; first defined on line %lu"), + rec->name, rec->def_line!=0 ? rec->def_line : rec->decl_line); +} + +typedef struct symtab_finalize_info { + unsigned long firstundef_line; + int undef_extern; + yasm_errwarns *errwarns; +} symtab_finalize_info; + +static int +symtab_parser_finalize_checksym(yasm_symrec *sym, /*@null@*/ void *d) +{ + symtab_finalize_info *info = (symtab_finalize_info *)d; + + /* error if a symbol is used but never defined or extern/common declared */ + if ((sym->status & YASM_SYM_USED) && !(sym->status & YASM_SYM_DEFINED) && + !(sym->visibility & (YASM_SYM_EXTERN | YASM_SYM_COMMON))) { + if (info->undef_extern) + sym->visibility |= YASM_SYM_EXTERN; + else { + yasm_error_set(YASM_ERROR_GENERAL, + N_("undefined symbol `%s' (first use)"), sym->name); + yasm_errwarn_propagate(info->errwarns, sym->use_line); + if (sym->use_line < info->firstundef_line) + info->firstundef_line = sym->use_line; + } + } + + return 0; +} + +void +yasm_symtab_parser_finalize(yasm_symtab *symtab, int undef_extern, + yasm_errwarns *errwarns) +{ + symtab_finalize_info info; + info.firstundef_line = ULONG_MAX; + info.undef_extern = undef_extern; + info.errwarns = errwarns; + yasm_symtab_traverse(symtab, &info, symtab_parser_finalize_checksym); + if (info.firstundef_line < ULONG_MAX) { + yasm_error_set(YASM_ERROR_GENERAL, + N_(" (Each undefined symbol is reported only once.)")); + yasm_errwarn_propagate(errwarns, info.firstundef_line); + } +} + +void +yasm_symtab_destroy(yasm_symtab *symtab) +{ + HAMT_destroy(symtab->sym_table, symrec_destroy_one); + + while (!SLIST_EMPTY(&symtab->non_table_syms)) { + non_table_symrec *sym = SLIST_FIRST(&symtab->non_table_syms); + SLIST_REMOVE_HEAD(&symtab->non_table_syms, link); + symrec_destroy_one(sym->rec); + yasm_xfree(sym); + } + + yasm_xfree(symtab); +} + +typedef struct symrec_print_data { + FILE *f; + int indent_level; +} symrec_print_data; + +/*@+voidabstract@*/ +static int +symrec_print_wrapper(yasm_symrec *sym, /*@null@*/ void *d) +{ + symrec_print_data *data = (symrec_print_data *)d; + assert(data != NULL); + fprintf(data->f, "%*sSymbol `%s'\n", data->indent_level, "", sym->name); + yasm_symrec_print(sym, data->f, data->indent_level+1); + return 0; +} + +void +yasm_symtab_print(yasm_symtab *symtab, FILE *f, int indent_level) +{ + symrec_print_data data; + data.f = f; + data.indent_level = indent_level; + yasm_symtab_traverse(symtab, &data, symrec_print_wrapper); +} +/*@=voidabstract@*/ + +const char * +yasm_symrec_get_name(const yasm_symrec *sym) +{ + return sym->name; +} + +char * +yasm_symrec_get_global_name(const yasm_symrec *sym, const yasm_object *object) +{ + if (sym->visibility & (YASM_SYM_GLOBAL|YASM_SYM_COMMON|YASM_SYM_EXTERN)) { + char *name = yasm_xmalloc(strlen(object->global_prefix) + + strlen(sym->name) + + strlen(object->global_suffix) + 1); + strcpy(name, object->global_prefix); + strcat(name, sym->name); + strcat(name, object->global_suffix); + return name; + } + return yasm__xstrdup(sym->name); +} + +yasm_sym_vis +yasm_symrec_get_visibility(const yasm_symrec *sym) +{ + return sym->visibility; +} + +yasm_sym_status +yasm_symrec_get_status(const yasm_symrec *sym) +{ + return sym->status; +} + +unsigned long +yasm_symrec_get_def_line(const yasm_symrec *sym) +{ + return sym->def_line; +} + +unsigned long +yasm_symrec_get_decl_line(const yasm_symrec *sym) +{ + return sym->decl_line; +} + +unsigned long +yasm_symrec_get_use_line(const yasm_symrec *sym) +{ + return sym->use_line; +} + +const yasm_expr * +yasm_symrec_get_equ(const yasm_symrec *sym) +{ + if (sym->type == SYM_EQU && (sym->status & YASM_SYM_VALUED)) + return sym->value.expn; + return (const yasm_expr *)NULL; +} + +int +yasm_symrec_get_label(const yasm_symrec *sym, + yasm_symrec_get_label_bytecodep *precbc) +{ + if (!(sym->type == SYM_LABEL || sym->type == SYM_CURPOS) + || !sym->value.precbc) { + *precbc = (yasm_symrec_get_label_bytecodep)0xDEADBEEF; + return 0; + } + *precbc = sym->value.precbc; + return 1; +} + +void +yasm_symrec_set_size(yasm_symrec *sym, int size) +{ + sym->size = size; +} + +int +yasm_symrec_get_size(const yasm_symrec *sym) +{ + return sym->size; +} + +void +yasm_symrec_set_segment(yasm_symrec *sym, const char *segment) +{ + sym->segment = segment; +} + +const char * +yasm_symrec_get_segment(const yasm_symrec *sym) +{ + return sym->segment; +} + +int +yasm_symrec_is_abs(const yasm_symrec *sym) +{ + return (sym->def_line == 0 && sym->type == SYM_EQU && + sym->name[0] == '\0'); +} + +int +yasm_symrec_is_special(const yasm_symrec *sym) +{ + return (sym->type == SYM_SPECIAL); +} + +int +yasm_symrec_is_curpos(const yasm_symrec *sym) +{ + return (sym->type == SYM_CURPOS); +} + +void +yasm_symrec_set_objext_valparams(yasm_symrec *sym, + /*@only@*/ yasm_valparamhead *objext_valparams) +{ + yasm_symrec_add_data(sym, &objext_valparams_cb, objext_valparams); +} + +yasm_valparamhead * +yasm_symrec_get_objext_valparams(yasm_symrec *sym) +{ + return yasm_symrec_get_data(sym, &objext_valparams_cb); +} + +void +yasm_symrec_set_common_size(yasm_symrec *sym, + /*@only@*/ yasm_expr *common_size) +{ + yasm_expr **ep = yasm_xmalloc(sizeof(yasm_expr *)); + *ep = common_size; + yasm_symrec_add_data(sym, &common_size_cb, ep); +} + +yasm_expr ** +yasm_symrec_get_common_size(yasm_symrec *sym) +{ + return (yasm_expr **)yasm_symrec_get_data(sym, &common_size_cb); +} + +void * +yasm_symrec_get_data(yasm_symrec *sym, + const yasm_assoc_data_callback *callback) +{ + return yasm__assoc_data_get(sym->assoc_data, callback); +} + +void +yasm_symrec_add_data(yasm_symrec *sym, + const yasm_assoc_data_callback *callback, void *data) +{ + sym->assoc_data = yasm__assoc_data_add(sym->assoc_data, callback, data); +} + +void +yasm_symrec_print(const yasm_symrec *sym, FILE *f, int indent_level) +{ + switch (sym->type) { + case SYM_UNKNOWN: + fprintf(f, "%*s-Unknown (Common/Extern)-\n", indent_level, ""); + break; + case SYM_EQU: + fprintf(f, "%*s_EQU_\n", indent_level, ""); + fprintf(f, "%*sExpn=", indent_level, ""); + if (sym->status & YASM_SYM_VALUED) + yasm_expr_print(sym->value.expn, f); + else + fprintf(f, "***UNVALUED***"); + fprintf(f, "\n"); + break; + case SYM_LABEL: + case SYM_CURPOS: + fprintf(f, "%*s_%s_\n%*sSection:\n", indent_level, "", + sym->type == SYM_LABEL ? "Label" : "CurPos", + indent_level, ""); + yasm_section_print(yasm_bc_get_section(sym->value.precbc), f, + indent_level+1, 0); + fprintf(f, "%*sPreceding bytecode:\n", indent_level, ""); + yasm_bc_print(sym->value.precbc, f, indent_level+1); + break; + case SYM_SPECIAL: + fprintf(f, "%*s-Special-\n", indent_level, ""); + break; + } + + fprintf(f, "%*sStatus=", indent_level, ""); + if (sym->status == YASM_SYM_NOSTATUS) + fprintf(f, "None\n"); + else { + if (sym->status & YASM_SYM_USED) + fprintf(f, "Used,"); + if (sym->status & YASM_SYM_DEFINED) + fprintf(f, "Defined,"); + if (sym->status & YASM_SYM_VALUED) + fprintf(f, "Valued,"); + if (sym->status & YASM_SYM_NOTINTABLE) + fprintf(f, "Not in Table,"); + fprintf(f, "\n"); + } + + fprintf(f, "%*sVisibility=", indent_level, ""); + if (sym->visibility == YASM_SYM_LOCAL) + fprintf(f, "Local\n"); + else { + if (sym->visibility & YASM_SYM_GLOBAL) + fprintf(f, "Global,"); + if (sym->visibility & YASM_SYM_COMMON) + fprintf(f, "Common,"); + if (sym->visibility & YASM_SYM_EXTERN) + fprintf(f, "Extern,"); + fprintf(f, "\n"); + } + + if (sym->assoc_data) { + fprintf(f, "%*sAssociated data:\n", indent_level, ""); + yasm__assoc_data_print(sym->assoc_data, f, indent_level+1); + } + + fprintf(f, "%*sLine Index (Defined)=%lu\n", indent_level, "", + sym->def_line); + fprintf(f, "%*sLine Index (Declared)=%lu\n", indent_level, "", + sym->decl_line); + fprintf(f, "%*sLine Index (Used)=%lu\n", indent_level, "", sym->use_line); +} diff --git a/libyasm/symrec.h b/libyasm/symrec.h new file mode 100644 index 0000000..6207158 --- /dev/null +++ b/libyasm/symrec.h @@ -0,0 +1,433 @@ +/** + * \file libyasm/symrec.h + * \brief YASM symbol table interface. + * + * \license + * Copyright (C) 2001-2007 Michael Urman, Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_SYMREC_H +#define YASM_SYMREC_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Symbol status. YASM_SYM_DEFINED is set by yasm_symtab_define_label(), + * yasm_symtab_define_equ(), or yasm_symtab_declare()/yasm_symrec_declare() + * with a visibility of #YASM_SYM_EXTERN or #YASM_SYM_COMMON. + */ +typedef enum yasm_sym_status { + YASM_SYM_NOSTATUS = 0, /**< no status */ + YASM_SYM_USED = 1 << 0, /**< for use before definition */ + YASM_SYM_DEFINED = 1 << 1, /**< once it's been defined in the file */ + YASM_SYM_VALUED = 1 << 2, /**< once its value has been determined */ + YASM_SYM_NOTINTABLE = 1 << 3 /**< if it's not in sym_table (ex. '$') */ +} yasm_sym_status; + +/** Symbol record visibility. + * \note YASM_SYM_EXTERN and YASM_SYM_COMMON are mutually exclusive. + */ +typedef enum yasm_sym_vis { + YASM_SYM_LOCAL = 0, /**< Default, local only */ + YASM_SYM_GLOBAL = 1 << 0, /**< If symbol is declared GLOBAL */ + YASM_SYM_COMMON = 1 << 1, /**< If symbol is declared COMMON */ + YASM_SYM_EXTERN = 1 << 2, /**< If symbol is declared EXTERN */ + YASM_SYM_DLOCAL = 1 << 3 /**< If symbol is explicitly declared LOCAL */ +} yasm_sym_vis; + +/** Create a new symbol table. */ +YASM_LIB_DECL +yasm_symtab *yasm_symtab_create(void); + +/** Destroy a symbol table and all internal symbols. + * \param symtab symbol table + * \warning All yasm_symrec *'s into this symbol table become invalid after + * this is called! + */ +YASM_LIB_DECL +void yasm_symtab_destroy(/*@only@*/ yasm_symtab *symtab); + +/** Set the symbol table to be case sensitive or not. + * Should be called before adding any symbol. + * \param symtab symbol table + * \param sensitive whether the symbol table should be case sensitive. + */ +YASM_LIB_DECL +void yasm_symtab_set_case_sensitive(yasm_symtab *symtab, int sensitive); + +/** Get a reference to the symbol table's "absolute" symbol. This is + * essentially an EQU with no name and value 0, and is used for relocating + * absolute current-position-relative values. + * \see yasm_value_set_curpos_rel(). + * \param symtab symbol table + * \return Absolute symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_abs_sym(yasm_symtab *symtab); + +/** Get a reference to (use) a symbol. The symbol does not necessarily need to + * be defined before it is used. + * \param symtab symbol table + * \param name symbol name + * \param line virtual line where referenced + * \return Symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_use + (yasm_symtab *symtab, const char *name, unsigned long line); + +/** Get a reference to a symbol, without "using" it. Should be used for cases + * when an internal assembler usage of a symbol shouldn't be treated like a + * normal user symbol usage. + * \param symtab symbol table + * \param name symbol name + * \return Symbol (dependent pointer, do not free). May be NULL if symbol + * doesn't exist. + */ +YASM_LIB_DECL +/*@null@*/ /*@dependent@*/ yasm_symrec *yasm_symtab_get + (yasm_symtab *symtab, const char *name); + +/** Define a symbol as an EQU value. + * \param symtab symbol table + * \param name symbol (EQU) name + * \param e EQU value (expression) + * \param line virtual line of EQU + * \return Symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_define_equ + (yasm_symtab *symtab, const char *name, /*@keep@*/ yasm_expr *e, + unsigned long line); + +/** Define a symbol as a label. + * \param symtab symbol table + * \param name symbol (label) name + * \param precbc bytecode preceding label + * \param in_table nonzero if the label should be inserted into the symbol + * table (some specially-generated ones should not be) + * \param line virtual line of label + * \return Symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_define_label + (yasm_symtab *symtab, const char *name, + /*@dependent@*/ yasm_bytecode *precbc, int in_table, unsigned long line); + +/** Define a symbol as a label representing the current assembly position. + * This should be used for this purpose instead of yasm_symtab_define_label() + * as value_finalize_scan() looks for usage of this symbol type for special + * handling. The symbol created is not inserted into the symbol table. + * \param symtab symbol table + * \param name symbol (label) name + * \param precbc bytecode preceding label + * \param line virtual line of label + * \return Symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_define_curpos + (yasm_symtab *symtab, const char *name, + /*@dependent@*/ yasm_bytecode *precbc, unsigned long line); + +/** Define a special symbol that will appear in the symbol table and have a + * defined name, but have no other data associated with it within the + * standard symrec. + * \param symtab symbol table + * \param name symbol name + * \param vis symbol visibility + * \return Symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_define_special + (yasm_symtab *symtab, const char *name, yasm_sym_vis vis); + +/** Declare external visibility of a symbol. + * \note Not all visibility combinations are allowed. + * \param symtab symbol table + * \param name symbol name + * \param vis visibility + * \param line virtual line of visibility-setting + * \return Symbol (dependent pointer, do not free). + */ +YASM_LIB_DECL +/*@dependent@*/ yasm_symrec *yasm_symtab_declare + (yasm_symtab *symtab, const char *name, yasm_sym_vis vis, + unsigned long line); + +/** Declare external visibility of a symbol. + * \note Not all visibility combinations are allowed. + * \param symrec symbol + * \param vis visibility + * \param line virtual line of visibility-setting + */ +YASM_LIB_DECL +void yasm_symrec_declare(yasm_symrec *symrec, yasm_sym_vis vis, + unsigned long line); + +/** Callback function for yasm_symrec_traverse(). + * \param sym symbol + * \param d data passed into yasm_symrec_traverse() + * \return Nonzero to stop symbol traversal. + */ +typedef int (*yasm_symtab_traverse_callback) + (yasm_symrec *sym, /*@null@*/ void *d); + +/** Traverse all symbols in the symbol table. + * \param symtab symbol table + * \param d data to pass to each call of callback function + * \param func callback function called on each symbol + * \return Nonzero value returned by callback function if it ever returned + * nonzero. + */ +YASM_LIB_DECL +int /*@alt void@*/ yasm_symtab_traverse + (yasm_symtab *symtab, /*@null@*/ void *d, + yasm_symtab_traverse_callback func); + +/** Symbol table iterator (opaque type). */ +typedef struct yasm_symtab_iter yasm_symtab_iter; + +/** Get an iterator pointing to the first symbol in the symbol table. + * \param symtab symbol table + * \return Iterator for the symbol table. + */ +YASM_LIB_DECL +const yasm_symtab_iter *yasm_symtab_first(const yasm_symtab *symtab); + +/** Move a symbol table iterator to the next symbol in the symbol table. + * \param prev Previous iterator value + * \return Next iterator value, or NULL if no more symbols in the table. + */ +YASM_LIB_DECL +/*@null@*/ const yasm_symtab_iter *yasm_symtab_next + (const yasm_symtab_iter *prev); + +/** Get the symbol corresponding to the current symbol table iterator value. + * \param cur iterator value + * \return Corresponding symbol. + */ +YASM_LIB_DECL +yasm_symrec *yasm_symtab_iter_value(const yasm_symtab_iter *cur); + +/** Finalize symbol table after parsing stage. Checks for symbols that are + * used but never defined or declared #YASM_SYM_EXTERN or #YASM_SYM_COMMON. + * \param symtab symbol table + * \param undef_extern if nonzero, all undef syms should be declared extern + * \param errwarns error/warning set + * \note Errors/warnings are stored into errwarns. + */ +YASM_LIB_DECL +void yasm_symtab_parser_finalize(yasm_symtab *symtab, int undef_extern, + yasm_errwarns *errwarns); + +/** Print the symbol table. For debugging purposes. + * \param symtab symbol table + * \param f file + * \param indent_level indentation level + */ +YASM_LIB_DECL +void yasm_symtab_print(yasm_symtab *symtab, FILE *f, int indent_level); + +/** Get the name of a symbol. + * \param sym symbol + * \return Symbol name. + */ +YASM_LIB_DECL +/*@observer@*/ const char *yasm_symrec_get_name(const yasm_symrec *sym); + +/** Get the externally-visible (global) name of a symbol. + * \param sym symbol + * \param object object + * \return Externally-visible symbol name (allocated, caller must free). + */ +YASM_LIB_DECL +/*@only@*/ char *yasm_symrec_get_global_name(const yasm_symrec *sym, + const yasm_object *object); + +/** Get the visibility of a symbol. + * \param sym symbol + * \return Symbol visibility. + */ +YASM_LIB_DECL +yasm_sym_vis yasm_symrec_get_visibility(const yasm_symrec *sym); + +/** Get the status of a symbol. + * \param sym symbol + * \return Symbol status. + */ +YASM_LIB_DECL +yasm_sym_status yasm_symrec_get_status(const yasm_symrec *sym); + +/** Get the virtual line of where a symbol was first defined. + * \param sym symbol + * \return line virtual line + */ +YASM_LIB_DECL +unsigned long yasm_symrec_get_def_line(const yasm_symrec *sym); + +/** Get the virtual line of where a symbol was first declared. + * \param sym symbol + * \return line virtual line + */ +YASM_LIB_DECL +unsigned long yasm_symrec_get_decl_line(const yasm_symrec *sym); + +/** Get the virtual line of where a symbol was first used. + * \param sym symbol + * \return line virtual line + */ +YASM_LIB_DECL +unsigned long yasm_symrec_get_use_line(const yasm_symrec *sym); + +/** Get EQU value of a symbol. + * \param sym symbol + * \return EQU value, or NULL if symbol is not an EQU or is not defined. + */ +YASM_LIB_DECL +/*@observer@*/ /*@null@*/ const yasm_expr *yasm_symrec_get_equ + (const yasm_symrec *sym); + +/** Dependent pointer to a bytecode. */ +typedef /*@dependent@*/ yasm_bytecode *yasm_symrec_get_label_bytecodep; + +/** Get the label location of a symbol. + * \param sym symbol + * \param precbc bytecode preceding label (output) + * \return 0 if not symbol is not a label or if the symbol's visibility is + * #YASM_SYM_EXTERN or #YASM_SYM_COMMON (not defined in the file). + */ +YASM_LIB_DECL +int yasm_symrec_get_label(const yasm_symrec *sym, + /*@out@*/ yasm_symrec_get_label_bytecodep *precbc); + +/** Set the size of a symbol. + * \param sym symbol + * \param size size to be set + */ +void yasm_symrec_set_size(yasm_symrec *sym, int size); + +/** Get the size of a symbol. + * \param sym symbol + * \return size of the symbol, 0 if none specified by the user. + */ +int yasm_symrec_get_size(const yasm_symrec *sym); + +/** Set the segment of a symbol. + * \param sym symbol + * \param segment segment to be set + */ +void yasm_symrec_set_segment(yasm_symrec *sym, const char *segment); + +/** Get the segment of a symbol. + * \param sym symbol + * \return segment of the symbol, NULL if none specified by the user. + */ +const char *yasm_symrec_get_segment(const yasm_symrec *sym); + +/** Determine if symbol is the "absolute" symbol created by + * yasm_symtab_abs_sym(). + * \param sym symbol + * \return 0 if symbol is not the "absolute" symbol, nonzero otherwise. + */ +YASM_LIB_DECL +int yasm_symrec_is_abs(const yasm_symrec *sym); + +/** Determine if symbol is a special symbol. + * \param sym symbol + * \return 0 if symbol is not a special symbol, nonzero otherwise. + */ +YASM_LIB_DECL +int yasm_symrec_is_special(const yasm_symrec *sym); + +/** Determine if symbol is a label representing the current assembly position. + * \param sym symbol + * \return 0 if symbol is not a current position label, nonzero otherwise. + */ +YASM_LIB_DECL +int yasm_symrec_is_curpos(const yasm_symrec *sym); + +/** Set object-extended valparams. + * \param sym symbol + * \param objext_valparams object-extended valparams + */ +YASM_LIB_DECL +void yasm_symrec_set_objext_valparams + (yasm_symrec *sym, /*@only@*/ yasm_valparamhead *objext_valparams); + +/** Get object-extended valparams, if any, associated with symbol's + * declaration. + * \param sym symbol + * \return Object-extended valparams (NULL if none). + */ +YASM_LIB_DECL +/*@null@*/ /*@dependent@*/ yasm_valparamhead *yasm_symrec_get_objext_valparams + (yasm_symrec *sym); + +/** Set common size of symbol. + * \param sym symbol + * \param common_size common size expression + */ +YASM_LIB_DECL +void yasm_symrec_set_common_size + (yasm_symrec *sym, /*@only@*/ yasm_expr *common_size); + +/** Get common size of symbol, if symbol is declared COMMON and a size was set + * for it. + * \param sym symbol + * \return Common size (NULL if none). + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ yasm_expr **yasm_symrec_get_common_size + (yasm_symrec *sym); + +/** Get associated data for a symbol and data callback. + * \param sym symbol + * \param callback callback used when adding data + * \return Associated data (NULL if none). + */ +YASM_LIB_DECL +/*@dependent@*/ /*@null@*/ void *yasm_symrec_get_data + (yasm_symrec *sym, const yasm_assoc_data_callback *callback); + +/** Add associated data to a symbol. + * \attention Deletes any existing associated data for that data callback. + * \param sym symbol + * \param callback callback + * \param data data to associate + */ +YASM_LIB_DECL +void yasm_symrec_add_data(yasm_symrec *sym, + const yasm_assoc_data_callback *callback, + /*@only@*/ /*@null@*/ void *data); + +/** Print a symbol. For debugging purposes. + * \param f file + * \param indent_level indentation level + * \param sym symbol + */ +YASM_LIB_DECL +void yasm_symrec_print(const yasm_symrec *sym, FILE *f, int indent_level); + +#endif diff --git a/libyasm/tests/1shl0.asm b/libyasm/tests/1shl0.asm new file mode 100644 index 0000000..d69930a --- /dev/null +++ b/libyasm/tests/1shl0.asm @@ -0,0 +1 @@ +dd (1<<0) diff --git a/libyasm/tests/1shl0.hex b/libyasm/tests/1shl0.hex new file mode 100644 index 0000000..607fb55 --- /dev/null +++ b/libyasm/tests/1shl0.hex @@ -0,0 +1,4 @@ +01 +00 +00 +00 diff --git a/libyasm/tests/Makefile.inc b/libyasm/tests/Makefile.inc new file mode 100644 index 0000000..9c038a1 --- /dev/null +++ b/libyasm/tests/Makefile.inc @@ -0,0 +1,114 @@ +TESTS += bitvect_test +TESTS += floatnum_test +TESTS += leb128_test +TESTS += splitpath_test +TESTS += combpath_test +TESTS += uncstring_test +TESTS += libyasm/tests/libyasm_test.sh + +EXTRA_DIST += libyasm/tests/libyasm_test.sh +EXTRA_DIST += libyasm/tests/1shl0.asm +EXTRA_DIST += libyasm/tests/1shl0.hex +EXTRA_DIST += libyasm/tests/absloop-err.asm +EXTRA_DIST += libyasm/tests/absloop-err.errwarn +EXTRA_DIST += libyasm/tests/charconst64.asm +EXTRA_DIST += libyasm/tests/charconst64.hex +EXTRA_DIST += libyasm/tests/data-rawvalue.asm +EXTRA_DIST += libyasm/tests/data-rawvalue.hex +EXTRA_DIST += libyasm/tests/duplabel-err.asm +EXTRA_DIST += libyasm/tests/duplabel-err.errwarn +EXTRA_DIST += libyasm/tests/emptydata.asm +EXTRA_DIST += libyasm/tests/emptydata.hex +EXTRA_DIST += libyasm/tests/equ-expand.asm +EXTRA_DIST += libyasm/tests/equ-expand.hex +EXTRA_DIST += libyasm/tests/expr-fold-level.asm +EXTRA_DIST += libyasm/tests/expr-fold-level.hex +EXTRA_DIST += libyasm/tests/expr-simplify-identity.asm +EXTRA_DIST += libyasm/tests/expr-simplify-identity.hex +EXTRA_DIST += libyasm/tests/expr-wide-ident.asm +EXTRA_DIST += libyasm/tests/expr-wide-ident.hex +EXTRA_DIST += libyasm/tests/externdef.asm +EXTRA_DIST += libyasm/tests/externdef.errwarn +EXTRA_DIST += libyasm/tests/externdef.hex +EXTRA_DIST += libyasm/tests/incbin.asm +EXTRA_DIST += libyasm/tests/incbin.hex +EXTRA_DIST += libyasm/tests/jmpsize1.asm +EXTRA_DIST += libyasm/tests/jmpsize1.hex +EXTRA_DIST += libyasm/tests/jmpsize1-err.asm +EXTRA_DIST += libyasm/tests/jmpsize1-err.errwarn +EXTRA_DIST += libyasm/tests/opt-align1.asm +EXTRA_DIST += libyasm/tests/opt-align1.hex +EXTRA_DIST += libyasm/tests/opt-align2.asm +EXTRA_DIST += libyasm/tests/opt-align2.hex +EXTRA_DIST += libyasm/tests/opt-align3.asm +EXTRA_DIST += libyasm/tests/opt-align3.hex +EXTRA_DIST += libyasm/tests/opt-circular1-err.asm +EXTRA_DIST += libyasm/tests/opt-circular1-err.errwarn +EXTRA_DIST += libyasm/tests/opt-circular2-err.asm +EXTRA_DIST += libyasm/tests/opt-circular2-err.errwarn +EXTRA_DIST += libyasm/tests/opt-circular3-err.asm +EXTRA_DIST += libyasm/tests/opt-circular3-err.errwarn +EXTRA_DIST += libyasm/tests/opt-gvmat64.asm +EXTRA_DIST += libyasm/tests/opt-gvmat64.hex +EXTRA_DIST += libyasm/tests/opt-immexpand.asm +EXTRA_DIST += libyasm/tests/opt-immexpand.hex +EXTRA_DIST += libyasm/tests/opt-immnoexpand.asm +EXTRA_DIST += libyasm/tests/opt-immnoexpand.hex +EXTRA_DIST += libyasm/tests/opt-oldalign.asm +EXTRA_DIST += libyasm/tests/opt-oldalign.hex +EXTRA_DIST += libyasm/tests/opt-struc.asm +EXTRA_DIST += libyasm/tests/opt-struc.hex +EXTRA_DIST += libyasm/tests/reserve-err1.asm +EXTRA_DIST += libyasm/tests/reserve-err1.errwarn +EXTRA_DIST += libyasm/tests/reserve-err2.asm +EXTRA_DIST += libyasm/tests/reserve-err2.errwarn +EXTRA_DIST += libyasm/tests/strucsize.asm +EXTRA_DIST += libyasm/tests/strucsize.hex +EXTRA_DIST += libyasm/tests/times0.asm +EXTRA_DIST += libyasm/tests/times0.hex +EXTRA_DIST += libyasm/tests/timesfwd.asm +EXTRA_DIST += libyasm/tests/timesfwd.hex +EXTRA_DIST += libyasm/tests/timesover-err.asm +EXTRA_DIST += libyasm/tests/timesover-err.errwarn +EXTRA_DIST += libyasm/tests/timesunder.asm +EXTRA_DIST += libyasm/tests/timesunder.hex +EXTRA_DIST += libyasm/tests/times-res.asm +EXTRA_DIST += libyasm/tests/times-res.errwarn +EXTRA_DIST += libyasm/tests/times-res.hex +EXTRA_DIST += libyasm/tests/unary.asm +EXTRA_DIST += libyasm/tests/unary.hex +EXTRA_DIST += libyasm/tests/value-err.asm +EXTRA_DIST += libyasm/tests/value-err.errwarn +EXTRA_DIST += libyasm/tests/value-samesym.asm +EXTRA_DIST += libyasm/tests/value-samesym.errwarn +EXTRA_DIST += libyasm/tests/value-samesym.hex +EXTRA_DIST += libyasm/tests/value-mask.asm +EXTRA_DIST += libyasm/tests/value-mask.errwarn +EXTRA_DIST += libyasm/tests/value-mask.hex +EXTRA_DIST += libyasm/tests/value-shr-symexpr.asm +EXTRA_DIST += libyasm/tests/value-shr-symexpr.hex + +check_PROGRAMS += bitvect_test +check_PROGRAMS += floatnum_test +check_PROGRAMS += leb128_test +check_PROGRAMS += splitpath_test +check_PROGRAMS += combpath_test +check_PROGRAMS += uncstring_test + +bitvect_test_SOURCES = libyasm/tests/bitvect_test.c +bitvect_test_LDADD = libyasm.a $(INTLLIBS) + +floatnum_test_SOURCES = libyasm/tests/floatnum_test.c +floatnum_test_LDADD = libyasm.a $(INTLLIBS) + +leb128_test_SOURCES = libyasm/tests/leb128_test.c +leb128_test_LDADD = libyasm.a $(INTLLIBS) + +splitpath_test_SOURCES = libyasm/tests/splitpath_test.c +splitpath_test_LDADD = libyasm.a $(INTLLIBS) + +combpath_test_SOURCES = libyasm/tests/combpath_test.c +combpath_test_LDADD = libyasm.a $(INTLLIBS) + +uncstring_test_SOURCES = libyasm/tests/uncstring_test.c +uncstring_test_LDADD = libyasm.a $(INTLLIBS) diff --git a/libyasm/tests/absloop-err.asm b/libyasm/tests/absloop-err.asm new file mode 100644 index 0000000..27e88a0 --- /dev/null +++ b/libyasm/tests/absloop-err.asm @@ -0,0 +1,6 @@ +[absolute x] +label1: +[absolute label1] +x: +[section .text] +mov ax, [x] diff --git a/libyasm/tests/absloop-err.errwarn b/libyasm/tests/absloop-err.errwarn new file mode 100644 index 0000000..29d3a33 --- /dev/null +++ b/libyasm/tests/absloop-err.errwarn @@ -0,0 +1 @@ +-:6: error: circular reference detected in memory expression diff --git a/libyasm/tests/bitvect_test.c b/libyasm/tests/bitvect_test.c new file mode 100644 index 0000000..f7b3413 --- /dev/null +++ b/libyasm/tests/bitvect_test.c @@ -0,0 +1,176 @@ +/* + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libyasm/bitvect.h" + +static int +test_boot(void) +{ + if (BitVector_Boot() != ErrCode_Ok) + return 1; + return 0; +} + +typedef struct Val_s { + const char *ascii; + unsigned char result[10]; /* 80 bit result, little endian */ +} Val; + +Val oct_small_vals[] = { + { "0", + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + { "1", + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + { "77", + {0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, +}; + +Val oct_large_vals[] = { + { "7654321076543210", + {0x88, 0xC6, 0xFA, 0x88, 0xC6, 0xFA, 0x00, 0x00, 0x00, 0x00} + }, + { "12634727612534126530214", + {0x8C, 0xB0, 0x5A, 0xE1, 0xAA, 0xF8, 0x3A, 0x67, 0x05, 0x00} + }, + { "61076543210", + {0x88, 0xC6, 0xFA, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} + }, +}; + +wordptr testval; + +static void +num_family_setup(void) +{ + BitVector_Boot(); + testval = BitVector_Create(80, FALSE); +} + +static void +num_family_teardown(void) +{ + BitVector_Destroy(testval); +} + +static char result_msg[1024]; + +static int +num_check(Val *val) +{ + unsigned char ascii[64], *result; + unsigned int len; + int i; + int ret = 0; + + strcpy((char *)ascii, val->ascii); + strcpy(result_msg, "parser failure"); + if(BitVector_from_Oct(testval, ascii) != ErrCode_Ok) + return 1; + + result = BitVector_Block_Read(testval, &len); + + for (i=0; i<10; i++) + if (result[i] != val->result[i]) + ret = 1; + + if (ret) { + strcpy(result_msg, val->ascii); + for (i=0; i<10; i++) + sprintf((char *)ascii+3*i, "%02x ", result[i]); + strcat(result_msg, ": "); + strcat(result_msg, (char *)ascii); + } + free(result); + + return ret; +} + +static int +test_oct_small_num(void) +{ + Val *vals = oct_small_vals; + int i, num = sizeof(oct_small_vals)/sizeof(Val); + + for (i=0; i<num; i++) { + if (num_check(&vals[i]) != 0) + return 1; + } + return 0; +} + +static int +test_oct_large_num(void) +{ + Val *vals = oct_large_vals; + int i, num = sizeof(oct_large_vals)/sizeof(Val); + + for (i=0; i<num; i++) { + if (num_check(&vals[i]) != 0) + return 1; + } + return 0; +} + +char failed[1000]; + +static int +runtest_(const char *testname, int (*testfunc)(void), void (*setup)(void), + void (*teardown)(void)) +{ + int nf; + if (setup) + setup(); + nf = testfunc(); + if (teardown) + teardown(); + printf("%c", nf>0 ? 'F':'.'); + fflush(stdout); + if (nf > 0) + sprintf(failed, "%s ** F: %s failed!\n", failed, testname); + return nf; +} +#define runtest(x,y,z) runtest_(#x,test_##x,y,z) + +int +main(void) +{ + int nf = 0; + + failed[0] = '\0'; + printf("Test bitvect_test: "); + nf += runtest(boot, NULL, NULL); + nf += runtest(oct_small_num, num_family_setup, num_family_teardown); + nf += runtest(oct_large_num, num_family_setup, num_family_teardown); + printf(" +%d-%d/3 %d%%\n%s", + 3-nf, nf, 100*(3-nf)/3, failed); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libyasm/tests/charconst64.asm b/libyasm/tests/charconst64.asm new file mode 100644 index 0000000..57856c0 --- /dev/null +++ b/libyasm/tests/charconst64.asm @@ -0,0 +1,3 @@ +[bits 64] + +mov rax, '12345678' diff --git a/libyasm/tests/charconst64.hex b/libyasm/tests/charconst64.hex new file mode 100644 index 0000000..66b893c --- /dev/null +++ b/libyasm/tests/charconst64.hex @@ -0,0 +1,10 @@ +48 +b8 +31 +32 +33 +34 +35 +36 +37 +38 diff --git a/libyasm/tests/combpath_test.c b/libyasm/tests/combpath_test.c new file mode 100644 index 0000000..90f2e24 --- /dev/null +++ b/libyasm/tests/combpath_test.c @@ -0,0 +1,139 @@ +/* + * + * Copyright (C) 2006-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libyasm/file.h" +#include "libyasm/coretype.h" + +typedef struct Test_Entry { + /* combpath function to test */ + char * (*combpath) (const char *from, const char *to); + + /* input "from" path */ + const char *from; + + /* input "to" path */ + const char *to; + + /* correct path returned */ + const char *out; +} Test_Entry; + +static Test_Entry tests[] = { + /* UNIX */ + {yasm__combpath_unix, "file1", "file2", "file2"}, + {yasm__combpath_unix, "./file1.ext", "./file2.ext", "file2.ext"}, + {yasm__combpath_unix, "/file1", "file2", "/file2"}, + {yasm__combpath_unix, "file1", "/file2", "/file2"}, + {yasm__combpath_unix, "/foo/file1", "../../file2", "/file2"}, + {yasm__combpath_unix, "/foo//file1", "../../file2", "/file2"}, + {yasm__combpath_unix, "foo/bar/file1", "../file2", "foo/file2"}, + {yasm__combpath_unix, "foo/bar/file1", "../../../file2", "../file2"}, + {yasm__combpath_unix, "foo/bar//file1", "../..//..//file2", "../file2"}, + {yasm__combpath_unix, "foo/bar/", "file2", "foo/bar/file2"}, + {yasm__combpath_unix, "../../file1", "../../file2", "../../../../file2"}, + {yasm__combpath_unix, "../foo/bar/../file1", "../../file2", "../foo/bar/../../../file2"}, + {yasm__combpath_unix, "/", "../file2", "/file2"}, + {yasm__combpath_unix, "../foo/", "../file2", "../file2"}, + {yasm__combpath_unix, "../foo/file1", "../../bar/file2", "../../bar/file2"}, + + /* Windows */ + {yasm__combpath_win, "file1", "file2", "file2"}, + {yasm__combpath_win, "./file1.ext", "./file2.ext", "file2.ext"}, + {yasm__combpath_win, "./file1.ext", ".\\file2.ext", "file2.ext"}, + {yasm__combpath_win, ".\\file1.ext", "./file2.ext", "file2.ext"}, + {yasm__combpath_win, "/file1", "file2", "\\file2"}, + {yasm__combpath_win, "\\file1", "file2", "\\file2"}, + {yasm__combpath_win, "file1", "/file2", "\\file2"}, + {yasm__combpath_win, "file1", "\\file2", "\\file2"}, + {yasm__combpath_win, "/foo\\file1", "../../file2", "\\file2"}, + {yasm__combpath_win, "\\foo\\\\file1", "..\\../file2", "\\file2"}, + {yasm__combpath_win, "foo/bar/file1", "../file2", "foo\\file2"}, + {yasm__combpath_win, "foo/bar/file1", "../..\\../file2", "..\\file2"}, + {yasm__combpath_win, "foo/bar//file1", "../..\\\\..//file2", "..\\file2"}, + {yasm__combpath_win, "foo/bar/", "file2", "foo\\bar\\file2"}, + {yasm__combpath_win, "..\\../file1", "../..\\file2", "..\\..\\..\\..\\file2"}, + {yasm__combpath_win, "../foo/bar\\\\../file1", "../..\\file2", "..\\foo\\bar\\..\\..\\..\\file2"}, + {yasm__combpath_win, "/", "../file2", "\\file2"}, + {yasm__combpath_win, "../foo/", "../file2", "..\\file2"}, + {yasm__combpath_win, "../foo/file1", "../..\\bar\\file2", "..\\..\\bar\\file2"}, + {yasm__combpath_win, "c:/file1.ext", "./file2.ext", "c:\\file2.ext"}, + {yasm__combpath_win, "e:\\path\\to/file1.ext", ".\\file2.ext", "e:\\path\\to\\file2.ext"}, + {yasm__combpath_win, ".\\file1.ext", "g:file2.ext", "g:file2.ext"}, +}; + +static char failed[1000]; +static char failmsg[100]; + +static int +run_test(Test_Entry *test) +{ + char *out; + const char *funcname; + + if (test->combpath == &yasm__combpath_unix) + funcname = "unix"; + else + funcname = "win"; + + out = test->combpath(test->from, test->to); + + if (strcmp(out, test->out) != 0) { + sprintf(failmsg, + "combpath_%s(\"%s\", \"%s\"): expected \"%s\", got \"%s\"!", + funcname, test->from, test->to, test->out, out); + yasm_xfree(out); + return 1; + } + + yasm_xfree(out); + return 0; +} + +int +main(void) +{ + int nf = 0; + int numtests = sizeof(tests)/sizeof(Test_Entry); + int i; + + failed[0] = '\0'; + printf("Test combpath_test: "); + for (i=0; i<numtests; i++) { + int fail = run_test(&tests[i]); + printf("%c", fail>0 ? 'F':'.'); + fflush(stdout); + if (fail) + sprintf(failed, "%s ** F: %s\n", failed, failmsg); + nf += fail; + } + + printf(" +%d-%d/%d %d%%\n%s", + numtests-nf, nf, numtests, 100*(numtests-nf)/numtests, failed); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libyasm/tests/data-rawvalue.asm b/libyasm/tests/data-rawvalue.asm new file mode 100644 index 0000000..3ebc7d0 --- /dev/null +++ b/libyasm/tests/data-rawvalue.asm @@ -0,0 +1,3 @@ +x db 0 +dd 0,x,0 + diff --git a/libyasm/tests/data-rawvalue.hex b/libyasm/tests/data-rawvalue.hex new file mode 100644 index 0000000..089b49c --- /dev/null +++ b/libyasm/tests/data-rawvalue.hex @@ -0,0 +1,13 @@ +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 diff --git a/libyasm/tests/duplabel-err.asm b/libyasm/tests/duplabel-err.asm new file mode 100644 index 0000000..3d666b3 --- /dev/null +++ b/libyasm/tests/duplabel-err.asm @@ -0,0 +1,18 @@ +%macro TESTMAC 0 +label: + mov ax, 5 + mov dx, 4 + mov cx, 3 +%endmacro + +db 6 + +db 7 + +TESTMAC + +db 8 +db 9 +TESTMAC +db 10 +TESTMAC diff --git a/libyasm/tests/duplabel-err.errwarn b/libyasm/tests/duplabel-err.errwarn new file mode 100644 index 0000000..4ce13bf --- /dev/null +++ b/libyasm/tests/duplabel-err.errwarn @@ -0,0 +1,4 @@ +-:16: error: redefinition of `label' +-:12: error: `label' previously defined here +-:18: error: redefinition of `label' +-:12: error: `label' previously defined here diff --git a/libyasm/tests/emptydata.asm b/libyasm/tests/emptydata.asm new file mode 100644 index 0000000..27b5651 --- /dev/null +++ b/libyasm/tests/emptydata.asm @@ -0,0 +1 @@ +db '' diff --git a/libyasm/tests/emptydata.hex b/libyasm/tests/emptydata.hex new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libyasm/tests/emptydata.hex diff --git a/libyasm/tests/equ-expand.asm b/libyasm/tests/equ-expand.asm new file mode 100644 index 0000000..eaa9421 --- /dev/null +++ b/libyasm/tests/equ-expand.asm @@ -0,0 +1,9 @@ +nop +end1: + +line_out equ end1+258 ;length = 1 + 263 +real_end equ end1+258+264 + + cmp bx,(real_end-line_out)/4 + cmp bx,((end1+258+264)-(end1+258))/4 + cmp bx,(end1+258+264-end1-258)/4 diff --git a/libyasm/tests/equ-expand.hex b/libyasm/tests/equ-expand.hex new file mode 100644 index 0000000..54a04f3 --- /dev/null +++ b/libyasm/tests/equ-expand.hex @@ -0,0 +1,10 @@ +90 +83 +fb +42 +83 +fb +42 +83 +fb +42 diff --git a/libyasm/tests/expr-fold-level.asm b/libyasm/tests/expr-fold-level.asm new file mode 100644 index 0000000..814a62c --- /dev/null +++ b/libyasm/tests/expr-fold-level.asm @@ -0,0 +1,4 @@ +begin +A equ 09000h +a dd (A+(end-begin)+3) +end diff --git a/libyasm/tests/expr-fold-level.hex b/libyasm/tests/expr-fold-level.hex new file mode 100644 index 0000000..aba10f6 --- /dev/null +++ b/libyasm/tests/expr-fold-level.hex @@ -0,0 +1,4 @@ +07 +90 +00 +00 diff --git a/libyasm/tests/expr-wide-ident.asm b/libyasm/tests/expr-wide-ident.asm new file mode 100644 index 0000000..8a60e78 --- /dev/null +++ b/libyasm/tests/expr-wide-ident.asm @@ -0,0 +1,2 @@ +lea edx, [lentry+edx+ecx] +lentry: diff --git a/libyasm/tests/expr-wide-ident.hex b/libyasm/tests/expr-wide-ident.hex new file mode 100644 index 0000000..f8d54ff --- /dev/null +++ b/libyasm/tests/expr-wide-ident.hex @@ -0,0 +1,9 @@ +67 +66 +8d +94 +0a +09 +00 +00 +00 diff --git a/libyasm/tests/externdef.asm b/libyasm/tests/externdef.asm new file mode 100644 index 0000000..3663af3 --- /dev/null +++ b/libyasm/tests/externdef.asm @@ -0,0 +1,2 @@ +extern foo +foo: diff --git a/libyasm/tests/externdef.errwarn b/libyasm/tests/externdef.errwarn new file mode 100644 index 0000000..8ff4a7e --- /dev/null +++ b/libyasm/tests/externdef.errwarn @@ -0,0 +1,2 @@ +-:1: warning: binary object format does not support extern variables +-:2: warning: `foo' both defined and declared extern diff --git a/libyasm/tests/externdef.hex b/libyasm/tests/externdef.hex new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libyasm/tests/externdef.hex diff --git a/libyasm/tests/floatnum_test.c b/libyasm/tests/floatnum_test.c new file mode 100644 index 0000000..30b8d6c --- /dev/null +++ b/libyasm/tests/floatnum_test.c @@ -0,0 +1,453 @@ +/* + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libyasm/floatnum.c" + +/* constants describing parameters of internal floating point format. + * (these should match those in src/floatnum.c !) + */ +#define MANT_BITS 80 +#define MANT_BYTES 10 + +typedef struct Init_Entry_s { + /* input ASCII value */ + const char *ascii; + + /* correct output from ASCII conversion */ + unsigned char mantissa[MANT_BYTES]; /* little endian mantissa - first + byte is not checked for + correctness. */ + unsigned short exponent; /* bias 32767 exponent */ + unsigned char sign; + unsigned char flags; + + /* correct output conversions - these should be *exact* matches */ + int ret32; + unsigned char result32[4]; + int ret64; + unsigned char result64[8]; + int ret80; + unsigned char result80[10]; +} Init_Entry; + +/* Values used for normalized tests */ +static Init_Entry normalized_vals[] = { + { "3.141592653589793", + {0xc6,0x0d,0xe9,0xbd,0x68,0x21,0xa2,0xda,0x0f,0xc9},0x8000,0,0, + 0, {0xdb,0x0f,0x49,0x40}, + 0, {0x18,0x2d,0x44,0x54,0xfb,0x21,0x09,0x40}, + 0, {0xe9,0xbd,0x68,0x21,0xa2,0xda,0x0f,0xc9,0x00,0x40} + }, + { "-3.141592653589793", + {0xc6,0x0d,0xe9,0xbd,0x68,0x21,0xa2,0xda,0x0f,0xc9},0x8000,1,0, + 0, {0xdb,0x0f,0x49,0xc0}, + 0, {0x18,0x2d,0x44,0x54,0xfb,0x21,0x09,0xc0}, + 0, {0xe9,0xbd,0x68,0x21,0xa2,0xda,0x0f,0xc9,0x00,0xc0} + }, + { "1.e16", + {0x00,0x00,0x00,0x00,0x00,0x04,0xbf,0xc9,0x1b,0x8e},0x8034,0,0, + 0, {0xca,0x1b,0x0e,0x5a}, + 0, {0x00,0x80,0xe0,0x37,0x79,0xc3,0x41,0x43}, + 0, {0x00,0x00,0x00,0x04,0xbf,0xc9,0x1b,0x8e,0x34,0x40} + }, + { "1.6e-20", + {0xf6,0xd3,0xee,0x7b,0xda,0x74,0x50,0xa0,0x1d,0x97},0x7fbd,0,0, + 0, {0xa0,0x1d,0x97,0x1e}, + 0, {0x4f,0x9b,0x0e,0x0a,0xb4,0xe3,0xd2,0x3b}, + 0, {0xef,0x7b,0xda,0x74,0x50,0xa0,0x1d,0x97,0xbd,0x3f} + }, + { "-5876.", + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0xb7},0x800b,1,0, + 0, {0x00,0xa0,0xb7,0xc5}, + 0, {0x00,0x00,0x00,0x00,0x00,0xf4,0xb6,0xc0}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0xb7,0x0b,0xc0} + }, + /* Edge cases for rounding wrap. */ + { "1.00000", + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},0x7ffe,0,0, + 0, {0x00,0x00,0x80,0x3f}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x3f}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xff,0x3f} + }, + { "1.000000", + {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},0x7ffe,0,0, + 0, {0x00,0x00,0x80,0x3f}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x3f}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xff,0x3f} + }, +}; + +/* Still normalized values, but edge cases of various sizes, testing underflow/ + * overflow checks as well. + */ +static Init_Entry normalized_edgecase_vals[] = { + /* 32-bit edges */ + { "1.1754943508222875e-38", + {0xd5,0xf2,0x82,0xff,0xff,0xff,0xff,0xff,0xff,0xff},0x7f80,0,0, + 0, {0x00,0x00,0x80,0x00}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x38}, + 0, {0x83,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x3f} + }, + { "3.4028234663852886e+38", + {0x21,0x35,0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff},0x807e,0,0, + 0, {0xff,0xff,0x7f,0x7f}, + 0, {0x00,0x00,0x00,0xe0,0xff,0xff,0xef,0x47}, + 0, {0x0a,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x7e,0x40} + }, + /* 64-bit edges */ + { "2.2250738585072014E-308", + {0x26,0x18,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x80},0x7c01,0,0, + -1, {0x00,0x00,0x00,0x00}, + 0, {0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00}, + 0, {0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x3c} + }, + { "1.7976931348623157E+308", + {0x26,0x6b,0xac,0xf7,0xff,0xff,0xff,0xff,0xff,0xff},0x83fe,0,0, + 1, {0x00,0x00,0x80,0x7f}, + 0, {0xff,0xff,0xff,0xff,0xff,0xff,0xef,0x7f}, + 0, {0xac,0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x43} + }, + /* 80-bit edges */ +/* { "3.3621E-4932", + {},,0,0, + -1, {0x00,0x00,0x00,0x00}, + -1, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + 0, {} + }, + { "1.1897E+4932", + {},,0,0, + 1, {0x00,0x00,0x80,0x7f}, + 1, {}, + 0, {} + },*/ + /* internal format edges */ +/* { + }, + { + },*/ +}; + +static yasm_floatnum *flt; + +/* failure messages */ +static char ret_msg[1024], result_msg[1024]; + +static void +new_setup(Init_Entry *vals, int i) +{ + flt = yasm_floatnum_create(vals[i].ascii); + strcpy(result_msg, vals[i].ascii); + strcat(result_msg, ": incorrect "); +} + +static int +new_check_flt(Init_Entry *val) +{ + unsigned char *mantissa; + int i, result = 0; + unsigned int len; + + mantissa = BitVector_Block_Read(flt->mantissa, &len); + for (i=1;i<MANT_BYTES;i++) /* don't compare first byte */ + if (mantissa[i] != val->mantissa[i]) + result = 1; + free(mantissa); + if (result) { + strcat(result_msg, "mantissa"); + return 1; + } + + if (flt->exponent != val->exponent) { + strcat(result_msg, "exponent"); + return 1; + } + if (flt->sign != val->sign) { + strcat(result_msg, "sign"); + return 1; + } + if (flt->flags != val->flags) { + strcat(result_msg, "flags"); + return 1; + } + return 0; +} + +static int +test_new_normalized(void) +{ + Init_Entry *vals = normalized_vals; + int i, num = sizeof(normalized_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + new_setup(vals, i); + if (new_check_flt(&vals[i]) != 0) + return 1; + yasm_floatnum_destroy(flt); + } + return 0; +} + +static int +test_new_normalized_edgecase(void) +{ + Init_Entry *vals = normalized_edgecase_vals; + int i, num = sizeof(normalized_edgecase_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + new_setup(vals, i); + if (new_check_flt(&vals[i]) != 0) + return 1; + yasm_floatnum_destroy(flt); + } + return 0; +} + +static void +get_family_setup(void) +{ + flt = malloc(sizeof(yasm_floatnum)); + flt->mantissa = BitVector_Create(MANT_BITS, TRUE); +} + +static void +get_family_teardown(void) +{ + BitVector_Destroy(flt->mantissa); + free(flt); +} + +static void +get_common_setup(Init_Entry *vals, int i) +{ + /* set up flt */ + BitVector_Block_Store(flt->mantissa, vals[i].mantissa, MANT_BYTES); + flt->sign = vals[i].sign; + flt->exponent = vals[i].exponent; + flt->flags = vals[i].flags; + + /* set failure messages */ + strcpy(ret_msg, vals[i].ascii); + strcat(ret_msg, ": incorrect return value"); + strcpy(result_msg, vals[i].ascii); + strcat(result_msg, ": incorrect result generated"); +} +#if 0 +static void +append_get_return_value(int val) +{ + char str[64]; + sprintf(str, ": %d", val); + strcat(ret_msg, str); +} +#endif +static int +get_common_check_result(int len, const unsigned char *val, + const unsigned char *correct) +{ + char str[64]; + int i; + int result = 0; + + for (i=0;i<len;i++) + if (val[i] != correct[i]) + result = 1; + + if (result) { + for (i=0; i<len; i++) + sprintf(str+3*i, "%02x ", val[i]); + strcat(result_msg, ": "); + strcat(result_msg, str); + } + + return result; +} + +/* + * get_single tests + */ + +static int +test_get_single_normalized(void) +{ + unsigned char outval[4]; + Init_Entry *vals = normalized_vals; + int i, num = sizeof(normalized_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + get_common_setup(vals, i); + if (yasm_floatnum_get_sized(flt, outval, 4, 32, 0, 0, 0) != + vals[i].ret32) + return 1; + if (get_common_check_result(4, outval, vals[i].result32) != 0) + return 1; + } + return 0; +} + +static int +test_get_single_normalized_edgecase(void) +{ + unsigned char outval[4]; + Init_Entry *vals = normalized_edgecase_vals; + int i, num = sizeof(normalized_edgecase_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + get_common_setup(vals, i); + if (yasm_floatnum_get_sized(flt, outval, 4, 32, 0, 0, 0) != + vals[i].ret32) + return 1; + if (get_common_check_result(4, outval, vals[i].result32) != 0) + return 1; + } + return 0; +} + +/* + * get_double tests + */ + +static int +test_get_double_normalized(void) +{ + unsigned char outval[8]; + Init_Entry *vals = normalized_vals; + int i, num = sizeof(normalized_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + get_common_setup(vals, i); + if (yasm_floatnum_get_sized(flt, outval, 8, 64, 0, 0, 0) != + vals[i].ret64) + return 1; + if (get_common_check_result(8, outval, vals[i].result64) != 0) + return 1; + } + return 0; +} + +static int +test_get_double_normalized_edgecase(void) +{ + unsigned char outval[8]; + Init_Entry *vals = normalized_edgecase_vals; + int i, num = sizeof(normalized_edgecase_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + get_common_setup(vals, i); + if (yasm_floatnum_get_sized(flt, outval, 8, 64, 0, 0, 0) != + vals[i].ret64) + return 1; + if (get_common_check_result(8, outval, vals[i].result64) != 0) + return 1; + } + return 0; +} + +/* + * get_extended tests + */ + +static int +test_get_extended_normalized(void) +{ + unsigned char outval[10]; + Init_Entry *vals = normalized_vals; + int i, num = sizeof(normalized_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + get_common_setup(vals, i); + if (yasm_floatnum_get_sized(flt, outval, 10, 80, 0, 0, 0) != + vals[i].ret80) + return 1; + if (get_common_check_result(10, outval, vals[i].result80) != 0) + return 1; + } + return 0; +} + +static int +test_get_extended_normalized_edgecase(void) +{ + unsigned char outval[10]; + Init_Entry *vals = normalized_edgecase_vals; + int i, num = sizeof(normalized_edgecase_vals)/sizeof(Init_Entry); + + for (i=0; i<num; i++) { + get_common_setup(vals, i); + if (yasm_floatnum_get_sized(flt, outval, 10, 80, 0, 0, 0) != + vals[i].ret80) + return 1; + if (get_common_check_result(10, outval, vals[i].result80) != 0) + return 1; + } + return 0; +} + +char failed[1000]; + +static int +runtest_(const char *testname, int (*testfunc)(void), void (*setup)(void), + void (*teardown)(void)) +{ + int nf; + if (setup) + setup(); + nf = testfunc(); + if (teardown) + teardown(); + printf("%c", nf>0 ? 'F':'.'); + fflush(stdout); + if (nf > 0) + sprintf(failed, "%s ** F: %s failed: %s!\n", failed, testname, + result_msg); + return nf; +} +#define runtest(x,y,z) runtest_(#x,test_##x,y,z) + +int +main(void) +{ + int nf = 0; + if (BitVector_Boot() != ErrCode_Ok) + return EXIT_FAILURE; + yasm_floatnum_initialize(); + + failed[0] = '\0'; + printf("Test floatnum_test: "); + nf += runtest(new_normalized, NULL, NULL); + nf += runtest(new_normalized_edgecase, NULL, NULL); + nf += runtest(get_single_normalized, get_family_setup, get_family_teardown); + nf += runtest(get_single_normalized_edgecase, get_family_setup, get_family_teardown); + nf += runtest(get_double_normalized, get_family_setup, get_family_teardown); + nf += runtest(get_double_normalized_edgecase, get_family_setup, get_family_teardown); + nf += runtest(get_extended_normalized, get_family_setup, get_family_teardown); + nf += runtest(get_extended_normalized_edgecase, get_family_setup, get_family_teardown); + printf(" +%d-%d/8 %d%%\n%s", + 8-nf, nf, 100*(8-nf)/8, failed); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libyasm/tests/incbin.asm b/libyasm/tests/incbin.asm new file mode 100644 index 0000000..bd6d1a9 --- /dev/null +++ b/libyasm/tests/incbin.asm @@ -0,0 +1,18 @@ +incbin "stamp-h1" +; 1024 x's to bump above 1024 byte default bytecode buffer size +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/libyasm/tests/incbin.hex b/libyasm/tests/incbin.hex new file mode 100644 index 0000000..7a26ed8 --- /dev/null +++ b/libyasm/tests/incbin.hex @@ -0,0 +1,23 @@ +74 +69 +6d +65 +73 +74 +61 +6d +70 +20 +66 +6f +72 +20 +63 +6f +6e +66 +69 +67 +2e +68 +0a diff --git a/libyasm/tests/jmpsize1-err.asm b/libyasm/tests/jmpsize1-err.asm new file mode 100644 index 0000000..a0df3e2 --- /dev/null +++ b/libyasm/tests/jmpsize1-err.asm @@ -0,0 +1,264 @@ +jmp x ; short +nop +w: +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +x: +jmp w ; short +jmp short y ; must be near; forcing short should error +nop +z: +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +y: +jmp short z ; must be near; forcing short should error + diff --git a/libyasm/tests/jmpsize1-err.errwarn b/libyasm/tests/jmpsize1-err.errwarn new file mode 100644 index 0000000..276b1eb --- /dev/null +++ b/libyasm/tests/jmpsize1-err.errwarn @@ -0,0 +1,2 @@ +-:132: error: short jump out of range +-:263: error: short jump out of range diff --git a/libyasm/tests/jmpsize1.asm b/libyasm/tests/jmpsize1.asm new file mode 100644 index 0000000..821b245 --- /dev/null +++ b/libyasm/tests/jmpsize1.asm @@ -0,0 +1,264 @@ +jmp x ; short +nop +w: +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +x: +jmp w ; short +jmp y ; near +nop +z: +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +y: +jmp z ; near + diff --git a/libyasm/tests/jmpsize1.hex b/libyasm/tests/jmpsize1.hex new file mode 100644 index 0000000..d885f86 --- /dev/null +++ b/libyasm/tests/jmpsize1.hex @@ -0,0 +1,265 @@ +eb +7f +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +eb +80 +e9 +80 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +e9 +7e +ff diff --git a/libyasm/tests/leb128_test.c b/libyasm/tests/leb128_test.c new file mode 100644 index 0000000..87fc8d9 --- /dev/null +++ b/libyasm/tests/leb128_test.c @@ -0,0 +1,199 @@ +/* + * + * Copyright (C) 2005-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libyasm/intnum.c" + +typedef struct Test_Entry { + /* signedness (0=unsigned, 1=signed) */ + int sign; + + /* whether input value should be negated */ + int negate; + + /* input value (as hex string) */ + const char *input; + + /* correct size returned from both size_leb128 and get_leb128 */ + unsigned long outsize; + + /* correct return data from get_leb128 */ + const unsigned char *result; +} Test_Entry; + +static Test_Entry tests[] = { + /* Unsigned values */ + {0, 0, "0", 1, (const unsigned char *)"\x00"}, + {0, 0, "2", 1, (const unsigned char *)"\x02"}, + {0, 0, "7F", 1, (const unsigned char *)"\x7F"}, + {0, 0, "80", 2, (const unsigned char *)"\x80\x01"}, + {0, 0, "81", 2, (const unsigned char *)"\x81\x01"}, + {0, 0, "82", 2, (const unsigned char *)"\x82\x01"}, + {0, 0, "3239", 2, (const unsigned char *)"\xB9\x64"}, + /* Signed zero value */ + {1, 0, "0", 1, (const unsigned char *)"\x00"}, + /* Signed positive values */ + {1, 0, "2", 1, (const unsigned char *)"\x02"}, + {1, 0, "7F", 2, (const unsigned char *)"\xFF\x00"}, + {1, 0, "80", 2, (const unsigned char *)"\x80\x01"}, + {1, 0, "81", 2, (const unsigned char *)"\x81\x01"}, + /* Signed negative values */ + {1, 1, "2", 1, (const unsigned char *)"\x7E"}, + {1, 1, "7F", 2, (const unsigned char *)"\x81\x7F"}, + {1, 1, "80", 2, (const unsigned char *)"\x80\x7F"}, + {1, 1, "81", 2, (const unsigned char *)"\xFF\x7E"}, +}; + +static char failed[1000]; +static char failmsg[100]; + +static int +run_output_test(Test_Entry *test) +{ + char *valstr = yasm__xstrdup(test->input); + yasm_intnum *intn = yasm_intnum_create_hex(valstr); + unsigned long size, i; + unsigned char out[100]; + int bad; + + yasm_xfree(valstr); + + if (test->negate) + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + + size = yasm_intnum_size_leb128(intn, test->sign); + if (size != test->outsize) { + yasm_intnum_destroy(intn); + sprintf(failmsg, "%ssigned %s%s size() bad size: expected %lu, got %lu!", + test->sign?"":"un", test->negate?"-":"", test->input, + test->outsize, size); + return 1; + } + + for (i=0; i<sizeof(out); i++) + out[i] = 0xFF; + size = yasm_intnum_get_leb128(intn, out, test->sign); + if (size != test->outsize) { + yasm_intnum_destroy(intn); + sprintf(failmsg, "%ssigned %s%s get() bad size: expected %lu, got %lu!", + test->sign?"":"un", test->negate?"-":"", test->input, + test->outsize, size); + return 1; + } + + bad = 0; + for (i=0; i<test->outsize && !bad; i++) { + if (out[i] != test->result[i]) + bad = 1; + } + if (bad) { + yasm_intnum_destroy(intn); + sprintf(failmsg, "%ssigned %s%s get() bad output!", + test->sign?"":"un", test->negate?"-":"", test->input); + return 1; + } + + yasm_intnum_destroy(intn); + return 0; +} + +static int +run_input_test(Test_Entry *test) +{ + char *valstr = yasm__xstrdup(test->input); + yasm_intnum *intn = yasm_intnum_create_hex(valstr); + yasm_intnum *testn; + unsigned long size; + + yasm_xfree(valstr); + + if (test->negate) + yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); + + testn = yasm_intnum_create_leb128(test->result, test->sign, &size); + if (size != test->outsize) { + yasm_intnum_destroy(testn); + yasm_intnum_destroy(intn); + sprintf(failmsg, "%ssigned %s%s create() bad size: expected %lu, got %lu!", + test->sign?"":"un", test->negate?"-":"", test->input, + test->outsize, size); + return 1; + } + + yasm_intnum_calc(intn, YASM_EXPR_EQ, testn); + if (!yasm_intnum_is_pos1(intn)) { + yasm_intnum_destroy(testn); + yasm_intnum_destroy(intn); + sprintf(failmsg, "%ssigned %s%s create() bad output!", + test->sign?"":"un", test->negate?"-":"", test->input); + return 1; + } + + yasm_intnum_destroy(testn); + yasm_intnum_destroy(intn); + return 0; +} + +int +main(void) +{ + int nf = 0; + int numtests = sizeof(tests)/sizeof(Test_Entry); + int i; + + if (BitVector_Boot() != ErrCode_Ok) + return EXIT_FAILURE; + yasm_intnum_initialize(); + + failed[0] = '\0'; + printf("Test leb128_test: "); + for (i=0; i<numtests; i++) { + int fail; + + fail = run_output_test(&tests[i]); + printf("%c", fail>0 ? 'F':'.'); + fflush(stdout); + if (fail) + sprintf(failed, "%s ** F: %s\n", failed, failmsg); + nf += fail; + + fail = run_input_test(&tests[i]); + printf("%c", fail>0 ? 'F':'.'); + fflush(stdout); + if (fail) + sprintf(failed, "%s ** F: %s\n", failed, failmsg); + nf += fail; + } + + yasm_intnum_cleanup(); + + printf(" +%d-%d/%d %d%%\n%s", + numtests*2-nf, nf, numtests*2, 100*(numtests*2-nf)/(numtests*2), + failed); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libyasm/tests/libyasm_test.sh b/libyasm/tests/libyasm_test.sh new file mode 100755 index 0000000..37f8717 --- /dev/null +++ b/libyasm/tests/libyasm_test.sh @@ -0,0 +1,3 @@ +#! /bin/sh +${srcdir}/out_test.sh libyasm_test libyasm/tests "libyasm" "-f bin" "" +exit $? diff --git a/libyasm/tests/opt-align1.asm b/libyasm/tests/opt-align1.asm new file mode 100644 index 0000000..2005c06 --- /dev/null +++ b/libyasm/tests/opt-align1.asm @@ -0,0 +1,9 @@ +je label1 +times 4 nop +je label1 +align 8 +times 118 nop +je label2 +label1: +times 128 nop +label2: diff --git a/libyasm/tests/opt-align1.hex b/libyasm/tests/opt-align1.hex new file mode 100644 index 0000000..7785147 --- /dev/null +++ b/libyasm/tests/opt-align1.hex @@ -0,0 +1,266 @@ +0f +84 +86 +00 +90 +90 +90 +90 +0f +84 +7e +00 +8d +b4 +00 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +0f +84 +80 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 diff --git a/libyasm/tests/opt-align2.asm b/libyasm/tests/opt-align2.asm new file mode 100644 index 0000000..d93bd8e --- /dev/null +++ b/libyasm/tests/opt-align2.asm @@ -0,0 +1,10 @@ +je label1 +je label1 +times 4 nop +times 8 nop +align 8 +times 110 nop +je label2 +label1: +times 128 nop +label2: diff --git a/libyasm/tests/opt-align2.hex b/libyasm/tests/opt-align2.hex new file mode 100644 index 0000000..38a8cc8 --- /dev/null +++ b/libyasm/tests/opt-align2.hex @@ -0,0 +1,266 @@ +0f +84 +86 +00 +0f +84 +82 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +8d +b4 +00 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +0f +84 +80 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 diff --git a/libyasm/tests/opt-align3.asm b/libyasm/tests/opt-align3.asm new file mode 100644 index 0000000..747fa5c --- /dev/null +++ b/libyasm/tests/opt-align3.asm @@ -0,0 +1,16 @@ +je label1a +je label1b +je label1c +jmp label1d +align 4 +times 112 nop +je label2 +label1a: +times 4 nop +label1b: +times 4 nop +label1c: +times 4 nop +label1d: +times 128 nop +label2: diff --git a/libyasm/tests/opt-align3.hex b/libyasm/tests/opt-align3.hex new file mode 100644 index 0000000..c44bfe4 --- /dev/null +++ b/libyasm/tests/opt-align3.hex @@ -0,0 +1,272 @@ +0f +84 +80 +00 +0f +84 +80 +00 +0f +84 +80 +00 +e9 +81 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +0f +84 +8c +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 diff --git a/libyasm/tests/opt-circular1-err.asm b/libyasm/tests/opt-circular1-err.asm new file mode 100644 index 0000000..dc96746 --- /dev/null +++ b/libyasm/tests/opt-circular1-err.asm @@ -0,0 +1,2 @@ + times (label-$) db 0
+label: db 'Where am I?'
diff --git a/libyasm/tests/opt-circular1-err.errwarn b/libyasm/tests/opt-circular1-err.errwarn new file mode 100644 index 0000000..9b09e1c --- /dev/null +++ b/libyasm/tests/opt-circular1-err.errwarn @@ -0,0 +1 @@ +-:1: error: circular reference detected diff --git a/libyasm/tests/opt-circular2-err.asm b/libyasm/tests/opt-circular2-err.asm new file mode 100644 index 0000000..ea558ae --- /dev/null +++ b/libyasm/tests/opt-circular2-err.asm @@ -0,0 +1,2 @@ + times (label-$+1) db 0
+label: db 'NOW where am I?'
diff --git a/libyasm/tests/opt-circular2-err.errwarn b/libyasm/tests/opt-circular2-err.errwarn new file mode 100644 index 0000000..9b09e1c --- /dev/null +++ b/libyasm/tests/opt-circular2-err.errwarn @@ -0,0 +1 @@ +-:1: error: circular reference detected diff --git a/libyasm/tests/opt-circular3-err.asm b/libyasm/tests/opt-circular3-err.asm new file mode 100644 index 0000000..61f818a --- /dev/null +++ b/libyasm/tests/opt-circular3-err.asm @@ -0,0 +1,7 @@ +label1: +times label4-label3+1 db 0 +label2: +db 0 +label3: +times label2-label1+1 db 0 +label4: diff --git a/libyasm/tests/opt-circular3-err.errwarn b/libyasm/tests/opt-circular3-err.errwarn new file mode 100644 index 0000000..041cbba --- /dev/null +++ b/libyasm/tests/opt-circular3-err.errwarn @@ -0,0 +1 @@ +-:6: error: circular reference detected diff --git a/libyasm/tests/opt-gvmat64.asm b/libyasm/tests/opt-gvmat64.asm new file mode 100644 index 0000000..35ac7ac --- /dev/null +++ b/libyasm/tests/opt-gvmat64.asm @@ -0,0 +1,514 @@ +;uInt longest_match_x64( +; deflate_state *s, +; IPos cur_match); /* current match */ + +; gvmat64.asm -- Asm portion of the optimized longest_match for 32 bits x86 +; Copyright (C) 1995-2005 Jean-loup Gailly, Brian Raiter and Gilles Vollant. +; +; File written by Gilles Vollant, by converting to assembly the longest_match +; from Jean-loup Gailly in deflate.c of zLib and infoZip zip. +; +; and by taking inspiration on asm686 with masm, optimised assembly code +; from Brian Raiter, written 1998 +; +; http://www.zlib.net +; http://www.winimage.com/zLibDll +; http://www.muppetlabs.com/~breadbox/software/assembly.html +; +; to compile this file for infozip Zip, I use option: +; ml64.exe /Flgvmat64 /c /Zi /DINFOZIP gvmat64.asm +; +; to compile this file for zLib, I use option: +; ml64.exe /Flgvmat64 /c /Zi gvmat64.asm +; Be carrefull to adapt zlib1222add below to your version of zLib +; (if you use a version of zLib before 1.0.4 or after 1.2.2.2, change +; value of zlib1222add later) +; +; This file compile with Microsoft Macro Assembler (x64) for AMD64 +; +; ml64.exe is given with Visual Studio 2005 and Windows 2003 server DDK +; +; (you can get Windows 2003 server DDK with ml64 and cl for AMD64 from +; http://www.microsoft.com/whdc/devtools/ddk/default.mspx for low price) +; + + +;uInt longest_match(s, cur_match) +; deflate_state *s; +; IPos cur_match; /* current match */ +bits 64 + section .text + +longest_match: ; PROC + + +;%define LocalVarsSize 88 + %define LocalVarsSize 72 + +; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12 +; free register : r14,r15 +; register can be saved : rsp + + %define chainlenwmask rsp + 8 - LocalVarsSize ; high word: current chain len + ; low word: s->wmask +;%define window rsp + xx - LocalVarsSize ; local copy of s->window ; stored in r10 +;%define windowbestlen rsp + xx - LocalVarsSize ; s->window + bestlen , use r10+r11 +;%define scanstart rsp + xx - LocalVarsSize ; first two bytes of string ; stored in r12w +;%define scanend rsp + xx - LocalVarsSize ; last two bytes of string use ebx +;%define scanalign rsp + xx - LocalVarsSize ; dword-misalignment of string r13 +;%define bestlen rsp + xx - LocalVarsSize ; size of best match so far -> r11d +;%define scan rsp + xx - LocalVarsSize ; ptr to string wanting match -> r9 +%ifdef INFOZIP +%else + %define nicematch (rsp + 16 - LocalVarsSize) ; a good enough match size +%endif + +%define save_rdi rsp + 24 - LocalVarsSize +%define save_rsi rsp + 32 - LocalVarsSize +%define save_rbx rsp + 40 - LocalVarsSize +%define save_rbp rsp + 48 - LocalVarsSize +%define save_r12 rsp + 56 - LocalVarsSize +%define save_r13 rsp + 64 - LocalVarsSize +;%define save_r14 rsp + 72 - LocalVarsSize +;%define save_r15 rsp + 80 - LocalVarsSize + + + +; all the +4 offsets are due to the addition of pending_buf_size (in zlib +; in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, remove the +4). +; Note : these value are good with a 8 bytes boundary pack structure + + + %define MAX_MATCH 258 + %define MIN_MATCH 3 + %define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) + + +;;; Offsets for fields in the deflate_state structure. These numbers +;;; are calculated from the definition of deflate_state, with the +;;; assumption that the compiler will dword-align the fields. (Thus, +;;; changing the definition of deflate_state could easily cause this +;;; program to crash horribly, without so much as a warning at +;;; compile time. Sigh.) + +; all the +zlib1222add offsets are due to the addition of fields +; in zlib in the deflate_state structure since the asm code was first written +; (if you compile with zlib 1.0.4 or older, use "%define zlib1222add (-4)"). +; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "%define zlib1222add 0"). +; if you compile with zlib 1.2.2.2 or later , use "%define zlib1222add 8"). + +%ifdef INFOZIP + section .data + +extern window_size:DWORD +; WMask ; 7fff +extern window:BYTE:010040H +extern prev:WORD:08000H +; MatchLen : unused +; PrevMatch : unused +extern strstart:DWORD +extern match_start:DWORD +; Lookahead : ignore +extern prev_length:DWORD ; PrevLen +extern max_chain_length:DWORD +extern good_match:DWORD +extern nice_match:DWORD +%define prev_ad OFFSET prev +%define window_ad OFFSET window +%define nicematch nice_match + +%define WMask 07fffh + +%else + + %ifndef zlib1222add + %define zlib1222add 8 + %endif +%define dsWSize 56+zlib1222add+(zlib1222add/2) +%define dsWMask 64+zlib1222add+(zlib1222add/2) +%define dsWindow 72+zlib1222add +%define dsPrev 88+zlib1222add +%define dsMatchLen 128+zlib1222add +%define dsPrevMatch 132+zlib1222add +%define dsStrStart 140+zlib1222add +%define dsMatchStart 144+zlib1222add +%define dsLookahead 148+zlib1222add +%define dsPrevLen 152+zlib1222add +%define dsMaxChainLen 156+zlib1222add +%define dsGoodMatch 172+zlib1222add +%define dsNiceMatch 176+zlib1222add + +%define window_size [ rcx + dsWSize] +%define WMask [ rcx + dsWMask] +%define window_ad [ rcx + dsWindow] +%define prev_ad [ rcx + dsPrev] +%define strstart [ rcx + dsStrStart] +%define match_start [ rcx + dsMatchStart] +%define Lookahead [ rcx + dsLookahead] ; 0ffffffffh on infozip +%define prev_length [ rcx + dsPrevLen] +%define max_chain_length [ rcx + dsMaxChainLen] +%define good_match [ rcx + dsGoodMatch] +%define nice_match [ rcx + dsNiceMatch] +%endif + +; parameter 1 in r8(deflate state s), param 2 in rdx (cur match) + +; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and +; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp +; +; All registers must be preserved across the call, except for +; rax, rcx, rdx, r8, r9, r10, and r11, which are scratch. + + section .text + +;;; Save registers that the compiler may be using, and adjust esp to +;;; make room for our stack frame. + + +;;; Retrieve the function arguments. r8d will hold cur_match +;;; throughout the entire function. edx will hold the pointer to the +;;; deflate_state structure during the function's setup (before +;;; entering the main loop. + +; parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match) + +; this clear high 32 bits of r8, which can be garbage in both r8 and rdx + + mov [save_rdi],rdi + mov [save_rsi],rsi + mov [save_rbx],rbx + mov [save_rbp],rbp +%ifdef INFOZIP + mov r8d,ecx +%else + mov r8d,edx +%endif + mov [save_r12],r12 + mov [save_r13],r13 +; mov [save_r14],r14 +; mov [save_r15],r15 + + +;;; uInt wmask = s->w_mask; +;;; unsigned chain_length = s->max_chain_length; +;;; if (s->prev_length >= s->good_match) { +;;; chain_length >>= 2; +;;; } + + mov edi, prev_length + mov esi, good_match + mov eax, WMask + mov ebx, max_chain_length + cmp edi, esi + jl LastMatchGood + shr ebx, 2 +LastMatchGood: + +;;; chainlen is decremented once beforehand so that the function can +;;; use the sign flag instead of the zero flag for the exit test. +;;; It is then shifted into the high word, to make room for the wmask +;;; value, which it will always accompany. + + dec ebx + shl ebx, 16 + or ebx, eax + +;;; on zlib only +;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + +%ifdef INFOZIP + mov [chainlenwmask], ebx +; on infozip nice_match = [nice_match] +%else + mov eax, nice_match + mov [chainlenwmask], ebx + mov r10d, Lookahead + cmp r10d, eax + cmovnl r10d, eax + mov [nicematch],r10d +%endif + +;;; register Bytef *scan = s->window + s->strstart; + mov r10, window_ad + mov ebp, strstart + lea r13, [r10 + rbp] + +;;; Determine how many bytes the scan ptr is off from being +;;; dword-aligned. + + mov r9,r13 + neg r13 + and r13,3 + +;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ? +;;; s->strstart - (IPos)MAX_DIST(s) : NIL; +%ifdef INFOZIP + mov eax,07efah ; MAX_DIST = (WSIZE-MIN_LOOKAHEAD) (0x8000-(3+8+1)) +%else + mov eax, window_size + sub eax, MIN_LOOKAHEAD +%endif + xor edi,edi + sub ebp, eax + + mov r11d, prev_length + + cmovng ebp,edi + +;;; int best_len = s->prev_length; + + +;;; Store the sum of s->window + best_len in esi locally, and in esi. + + lea rsi,[r10+r11] + +;;; register ush scan_start = *(ushf*)scan; +;;; register ush scan_end = *(ushf*)(scan+best_len-1); +;;; Posf *prev = s->prev; + + movzx r12d,word [r9] + movzx ebx, word [r9 + r11 - 1] + + mov rdi, prev_ad + +;;; Jump into the main loop. + + mov edx, [chainlenwmask] + + cmp bx,word [rsi + r8 - 1] + jz LookupLoopIsZero + +LookupLoop1: + and r8d, edx + + movzx r8d, word [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry1: + cmp bx,word [rsi + r8 - 1] + jz LookupLoopIsZero + +LookupLoop2: + and r8d, edx + + movzx r8d, word [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry2: + cmp bx,word [rsi + r8 - 1] + jz LookupLoopIsZero + +LookupLoop4: + and r8d, edx + + movzx r8d, word [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry4: + + cmp bx,word [rsi + r8 - 1] + jnz LookupLoop1 + jmp LookupLoopIsZero + + +;;; do { +;;; match = s->window + cur_match; +;;; if (*(ushf*)(match+best_len-1) != scan_end || +;;; *(ushf*)match != scan_start) continue; +;;; [...] +;;; } while ((cur_match = prev[cur_match & wmask]) > limit +;;; && --chain_length != 0); +;;; +;;; Here is the inner loop of the function. The function will spend the +;;; majority of its time in this loop, and majority of that time will +;;; be spent in the first ten instructions. +;;; +;;; Within this loop: +;;; ebx = scanend +;;; r8d = curmatch +;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask) +;;; esi = windowbestlen - i.e., (window + bestlen) +;;; edi = prev +;;; ebp = limit + +LookupLoop: + and r8d, edx + + movzx r8d, word [rdi + r8*2] + cmp r8d, ebp + jbe LeaveNow + sub edx, 00010000h + js LeaveNow + +LoopEntry: + + cmp bx,word [rsi + r8 - 1] + jnz LookupLoop1 +LookupLoopIsZero: + cmp r12w, word [r10 + r8] + jnz LookupLoop1 + + +;;; Store the current value of chainlen. + mov [chainlenwmask], edx + +;;; Point edi to the string under scrutiny, and esi to the string we +;;; are hoping to match it up with. In actuality, esi and edi are +;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is +;;; initialized to -(MAX_MATCH_8 - scanalign). + + lea rsi,[r8+r10] + mov rdx, 0fffffffffffffef8h; -(MAX_MATCH_8) + lea rsi, [rsi + r13 + 0108h] ;MAX_MATCH_8] + lea rdi, [r9 + r13 + 0108h] ;MAX_MATCH_8] + + prefetcht1 [rsi+rdx] + prefetcht1 [rdi+rdx] + + +;;; Test the strings %define for ality, 8 bytes at a time. At the end, +;;; adjust rdx so that it is offset to the exact byte that mismatched. +;;; +;;; We already know at this point that the first three bytes of the +;;; strings match each other, and they can be safely passed over before +;;; starting the compare loop. So what this code does is skip over 0-3 +;;; bytes, as much as necessary in order to dword-align the edi +;;; pointer. (rsi will still be misaligned three times out of four.) +;;; +;;; It should be confessed that this loop usually does not represent +;;; much of the total running time. Replacing it with a more +;;; straightforward "rep cmpsb" would not drastically degrade +;;; performance. + +LoopCmps: + mov rax, [rsi + rdx] + xor rax, [rdi + rdx] + jnz LeaveLoopCmps + + mov rax, [rsi + rdx + 8] + xor rax, [rdi + rdx + 8] + jnz LeaveLoopCmps8 + + + mov rax, [rsi + rdx + 8+8] + xor rax, [rdi + rdx + 8+8] + jnz LeaveLoopCmps16 + + add rdx,8+8+8 + + jmp short LoopCmps +LeaveLoopCmps16: add rdx,8 +LeaveLoopCmps8: add rdx,8 +LeaveLoopCmps: + + test eax, 0000FFFFh + jnz LenLower + + test eax,0ffffffffh + + jnz LenLower32 + + add rdx,4 + shr rax,32 + or ax,ax + jnz LenLower + +LenLower32: + shr eax,16 + add rdx,2 +LenLower: sub al, 1 + adc rdx, 0 +;;; Calculate the length of the match. If it is longer than MAX_MATCH, +;;; then automatically accept it as the best possible match and leave. + + lea rax, [rdi + rdx] + sub rax, r9 + cmp eax, MAX_MATCH + jge LenMaximum + +;;; If the length of the match is not longer than the best match we +;;; have so far, then forget it and return to the lookup loop. +;/////////////////////////////////// + + cmp eax, r11d + jg LongerMatch + + lea rsi,[r10+r11] + + mov rdi, prev_ad + mov edx, [chainlenwmask] + jmp LookupLoop + +;;; s->match_start = cur_match; +;;; best_len = len; +;;; if (len >= nice_match) break; +;;; scan_end = *(ushf*)(scan+best_len-1); + +LongerMatch: + mov r11d, eax + mov match_start, r8d + cmp eax, [nicematch] + jge LeaveNow + + lea rsi,[r10+rax] + + movzx ebx, word [r9 + rax - 1] + mov rdi, prev_ad + mov edx, [chainlenwmask] + jmp LookupLoop + +;;; Accept the current string, with the maximum possible length. + +LenMaximum: + mov r11d,MAX_MATCH + mov match_start, r8d +;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len; +;;; return s->lookahead; + +LeaveNow: +%ifdef INFOZIP + mov eax,r11d +%else + mov eax, Lookahead + cmp r11d, eax + cmovng eax, r11d +%endif + +;;; Restore the stack and return from whence we came. + + mov rsi,[save_rsi] + mov rdi,[save_rdi] + mov rbx,[save_rbx] + mov rbp,[save_rbp] + mov r12,[save_r12] + mov r13,[save_r13] +; mov r14,[save_r14] +; mov r15,[save_r15] + + + ret 0 +; please don't remove this string ! +; Your can freely use gvmat64 in any free or externercial app +; but it is far better don't remove the string in the binary! + db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0 +%if 0 +longest_match ENDP +%endif + +match_init: ; PROC + ret 0 + %if 0 +match_init ENDP +%endif + +END diff --git a/libyasm/tests/opt-gvmat64.hex b/libyasm/tests/opt-gvmat64.hex new file mode 100644 index 0000000..b2432d6 --- /dev/null +++ b/libyasm/tests/opt-gvmat64.hex @@ -0,0 +1,731 @@ +48 +89 +7c +24 +d0 +48 +89 +74 +24 +d8 +48 +89 +5c +24 +e0 +48 +89 +6c +24 +e8 +41 +89 +d0 +4c +89 +64 +24 +f0 +4c +89 +6c +24 +f8 +8b +b9 +a0 +00 +00 +00 +8b +b1 +b4 +00 +00 +00 +8b +41 +4c +8b +99 +a4 +00 +00 +00 +39 +f7 +7c +03 +c1 +eb +02 +ff +cb +c1 +e3 +10 +09 +c3 +8b +81 +b8 +00 +00 +00 +89 +5c +24 +c0 +44 +8b +91 +9c +00 +00 +00 +41 +39 +c2 +44 +0f +4d +d0 +44 +89 +54 +24 +c8 +4c +8b +51 +50 +8b +a9 +94 +00 +00 +00 +4d +8d +2c +2a +4d +89 +e9 +49 +f7 +dd +49 +83 +e5 +03 +8b +41 +44 +2d +06 +01 +00 +00 +31 +ff +29 +c5 +44 +8b +99 +a0 +00 +00 +00 +0f +4e +ef +4b +8d +34 +1a +45 +0f +b7 +21 +43 +0f +b7 +5c +19 +ff +48 +8b +79 +60 +8b +54 +24 +c0 +66 +42 +3b +5c +06 +ff +0f +84 +9a +00 +00 +00 +41 +21 +d0 +46 +0f +b7 +04 +47 +41 +39 +e8 +0f +86 +6e +01 +00 +00 +81 +ea +00 +00 +01 +00 +0f +88 +62 +01 +00 +00 +66 +42 +3b +5c +06 +ff +74 +75 +41 +21 +d0 +46 +0f +b7 +04 +47 +41 +39 +e8 +0f +86 +49 +01 +00 +00 +81 +ea +00 +00 +01 +00 +0f +88 +3d +01 +00 +00 +66 +42 +3b +5c +06 +ff +74 +50 +41 +21 +d0 +46 +0f +b7 +04 +47 +41 +39 +e8 +0f +86 +24 +01 +00 +00 +81 +ea +00 +00 +01 +00 +0f +88 +18 +01 +00 +00 +66 +42 +3b +5c +06 +ff +75 +91 +eb +29 +41 +21 +d0 +46 +0f +b7 +04 +47 +41 +39 +e8 +0f +86 +fd +00 +00 +00 +81 +ea +00 +00 +01 +00 +0f +88 +f1 +00 +00 +00 +66 +42 +3b +5c +06 +ff +0f +85 +66 +ff +ff +ff +66 +47 +3b +24 +02 +0f +85 +5b +ff +ff +ff +89 +54 +24 +c0 +4b +8d +34 +10 +48 +ba +f8 +fe +ff +ff +ff +ff +ff +ff +4a +8d +b4 +2e +08 +01 +00 +00 +4b +8d +bc +29 +08 +01 +00 +00 +0f +18 +14 +16 +0f +18 +14 +17 +48 +8b +04 +16 +48 +33 +04 +17 +75 +26 +48 +8b +44 +16 +08 +48 +33 +44 +17 +08 +75 +16 +48 +8b +44 +16 +10 +48 +33 +44 +17 +10 +75 +06 +48 +83 +c2 +18 +eb +d8 +48 +83 +c2 +08 +48 +83 +c2 +08 +a9 +ff +ff +00 +00 +75 +1b +a9 +ff +ff +ff +ff +75 +0d +48 +83 +c2 +04 +48 +c1 +e8 +20 +66 +09 +c0 +75 +07 +c1 +e8 +10 +48 +83 +c2 +02 +2c +01 +48 +83 +d2 +00 +48 +8d +04 +17 +4c +29 +c8 +3d +02 +01 +00 +00 +7d +3d +44 +39 +d8 +7f +11 +4b +8d +34 +1a +48 +8b +79 +60 +8b +54 +24 +c0 +e9 +26 +ff +ff +ff +41 +89 +c3 +44 +89 +81 +98 +00 +00 +00 +3b +44 +24 +c8 +7d +24 +49 +8d +34 +02 +41 +0f +b7 +5c +01 +ff +48 +8b +79 +60 +8b +54 +24 +c0 +e9 +ff +fe +ff +ff +41 +bb +02 +01 +00 +00 +44 +89 +81 +98 +00 +00 +00 +8b +81 +9c +00 +00 +00 +41 +39 +c3 +41 +0f +4e +c3 +48 +8b +74 +24 +d8 +48 +8b +7c +24 +d0 +48 +8b +5c +24 +e0 +48 +8b +6c +24 +e8 +4c +8b +64 +24 +f0 +4c +8b +6c +24 +f8 +c2 +00 +00 +0d +0a +61 +73 +6d +36 +38 +36 +20 +77 +69 +74 +68 +20 +6d +61 +73 +6d +2c +20 +6f +70 +74 +69 +6d +69 +73 +65 +64 +20 +61 +73 +73 +65 +6d +62 +6c +79 +20 +63 +6f +64 +65 +20 +66 +72 +6f +6d +20 +42 +72 +69 +61 +6e +20 +52 +61 +69 +74 +65 +72 +2c +20 +77 +72 +69 +74 +74 +65 +6e +20 +31 +39 +39 +38 +2c +20 +63 +6f +6e +76 +65 +72 +74 +65 +64 +20 +74 +6f +20 +61 +6d +64 +20 +36 +34 +20 +62 +79 +20 +47 +69 +6c +6c +65 +73 +20 +56 +6f +6c +6c +61 +6e +74 +20 +32 +30 +30 +35 +0d +0a +00 +c2 +00 +00 diff --git a/libyasm/tests/opt-immexpand.asm b/libyasm/tests/opt-immexpand.asm new file mode 100644 index 0000000..55d8097 --- /dev/null +++ b/libyasm/tests/opt-immexpand.asm @@ -0,0 +1,9 @@ +label1: +je label3 +times 124 nop +label2: +je label4 +label3: +times 128 nop +label4: +push label2-label1 diff --git a/libyasm/tests/opt-immexpand.hex b/libyasm/tests/opt-immexpand.hex new file mode 100644 index 0000000..f57024e --- /dev/null +++ b/libyasm/tests/opt-immexpand.hex @@ -0,0 +1,263 @@ +0f +84 +80 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +0f +84 +80 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +68 +80 +00 diff --git a/libyasm/tests/opt-immnoexpand.asm b/libyasm/tests/opt-immnoexpand.asm new file mode 100644 index 0000000..e126f80 --- /dev/null +++ b/libyasm/tests/opt-immnoexpand.asm @@ -0,0 +1,10 @@ +label1: +times 2 nop +je label3 +times 123 nop +label2: +je label4 +label3: +times 128 nop +label4: +push label2-label1 diff --git a/libyasm/tests/opt-immnoexpand.hex b/libyasm/tests/opt-immnoexpand.hex new file mode 100644 index 0000000..c465806 --- /dev/null +++ b/libyasm/tests/opt-immnoexpand.hex @@ -0,0 +1,261 @@ +90 +90 +74 +7f +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +0f +84 +80 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +6a +7f diff --git a/libyasm/tests/opt-oldalign.asm b/libyasm/tests/opt-oldalign.asm new file mode 100644 index 0000000..013a5c8 --- /dev/null +++ b/libyasm/tests/opt-oldalign.asm @@ -0,0 +1,3 @@ +mov ax, 5 +times ($$-$) & 1Fh nop ; Long word alignment +mov bx, 5 diff --git a/libyasm/tests/opt-oldalign.hex b/libyasm/tests/opt-oldalign.hex new file mode 100644 index 0000000..dfbb9da --- /dev/null +++ b/libyasm/tests/opt-oldalign.hex @@ -0,0 +1,35 @@ +b8 +05 +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +bb +05 +00 diff --git a/libyasm/tests/opt-struc.asm b/libyasm/tests/opt-struc.asm new file mode 100644 index 0000000..4986f3b --- /dev/null +++ b/libyasm/tests/opt-struc.asm @@ -0,0 +1,39 @@ +[absolute 0] +HOSTENT: +.Name resd 1 +.Aliases resd 1 + +.AddrList resd 1 + +HOSTENT_size: + +[section .bss] +STRING_MAX equ 256 +HOSTENT_ALIASES_MAX equ 16 +HOSTENT_ADDRLIST_MAX equ 16 + +HostEnt_Name_static resb STRING_MAX +HostEnt_Aliases_static resd HOSTENT_ALIASES_MAX +HostEnt_AddrList_static resd HOSTENT_ADDRLIST_MAX +HostEnt_Aliases_data resb STRING_MAX*HOSTENT_ALIASES_MAX +HostEnt_AddrList_data resd HOSTENT_ADDRLIST_MAX + +[section .data] +HostEnt_static : +..@44.strucstart: +times HOSTENT.Name-($-..@44.strucstart) db 0 +dd HostEnt_Name_static +times HOSTENT.Aliases-($-..@44.strucstart) db 0 +dd HostEnt_Aliases_static +times HOSTENT.AddrList-($-..@44.strucstart) db 0 +dd HostEnt_AddrList_static +times HOSTENT_size-($-..@44.strucstart) db 0 + +HostEnt_static2 : +..@45.strucstart: +times HOSTENT.Name-($-..@45.strucstart) db 0 +dd HostEnt_Name_static +times HOSTENT.Aliases-($-..@45.strucstart) db 0 +dd HostEnt_Aliases_static +times HOSTENT_size-($-..@45.strucstart) db 0 + diff --git a/libyasm/tests/opt-struc.hex b/libyasm/tests/opt-struc.hex new file mode 100644 index 0000000..c8e792a --- /dev/null +++ b/libyasm/tests/opt-struc.hex @@ -0,0 +1,24 @@ +18 +00 +00 +00 +18 +01 +00 +00 +58 +01 +00 +00 +18 +00 +00 +00 +18 +01 +00 +00 +00 +00 +00 +00 diff --git a/libyasm/tests/reserve-err1.asm b/libyasm/tests/reserve-err1.asm new file mode 100644 index 0000000..ad6e31a --- /dev/null +++ b/libyasm/tests/reserve-err1.asm @@ -0,0 +1,15 @@ +; Test res* family errors +a: +resb -5 +resw 1.2 +resd -1.2 +resq 0xffffffff +rest a + +[section .bss] +resb -5 +resw 1.2 +resd -1.2 +resq 0xffffffff +rest a + diff --git a/libyasm/tests/reserve-err1.errwarn b/libyasm/tests/reserve-err1.errwarn new file mode 100644 index 0000000..e53fe3c --- /dev/null +++ b/libyasm/tests/reserve-err1.errwarn @@ -0,0 +1,2 @@ +-:7: error: multiple expression not absolute +-:14: error: multiple expression not absolute diff --git a/libyasm/tests/reserve-err2.asm b/libyasm/tests/reserve-err2.asm new file mode 100644 index 0000000..190f66b --- /dev/null +++ b/libyasm/tests/reserve-err2.asm @@ -0,0 +1,15 @@ +; Test res* family errors +a: +resb -5 +resw 1.2 +resd -1.2 +resq 0xffffffff +;rest a + +[section .bss] +resb -5 +resw 1.2 +resd -1.2 +resq 0xffffffff +;rest a + diff --git a/libyasm/tests/reserve-err2.errwarn b/libyasm/tests/reserve-err2.errwarn new file mode 100644 index 0000000..d3fbbab --- /dev/null +++ b/libyasm/tests/reserve-err2.errwarn @@ -0,0 +1,6 @@ +-:3: error: multiple is negative +-:4: error: expression must not contain floating point value +-:5: error: expression must not contain floating point value +-:10: error: multiple is negative +-:11: error: expression must not contain floating point value +-:12: error: expression must not contain floating point value diff --git a/libyasm/tests/splitpath_test.c b/libyasm/tests/splitpath_test.c new file mode 100644 index 0000000..7f873a4 --- /dev/null +++ b/libyasm/tests/splitpath_test.c @@ -0,0 +1,154 @@ +/* + * + * Copyright (C) 2006-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libyasm/file.h" + +typedef struct Test_Entry { + /* splitpath function to test */ + size_t (*splitpath) (const char *path, const char **tail); + + /* input path */ + const char *input; + + /* correct head length returned */ + size_t headlen; + + /* correct tail returned */ + const char *tail; +} Test_Entry; + +static Test_Entry tests[] = { + /* UNIX split */ + {yasm__splitpath_unix, "", 0, ""}, + {yasm__splitpath_unix, "./file.ext", 0, "file.ext"}, + {yasm__splitpath_unix, "../../file.ext", 5, "file.ext"}, + {yasm__splitpath_unix, "file.ext", 0, "file.ext"}, + {yasm__splitpath_unix, "/file.ext", 1, "file.ext"}, + {yasm__splitpath_unix, "/foo/file.ext", 4, "file.ext"}, + {yasm__splitpath_unix, "/foo/bar/file.ext", 8, "file.ext"}, + {yasm__splitpath_unix, "foo/file.ext", 3, "file.ext"}, + {yasm__splitpath_unix, "foo/bar/file.ext", 7, "file.ext"}, + {yasm__splitpath_unix, "foo/bar//file.ext", 7, "file.ext"}, + {yasm__splitpath_unix, "/", 1, ""}, + {yasm__splitpath_unix, "/foo/", 4, ""}, + {yasm__splitpath_unix, "/foo/bar/", 8, ""}, + {yasm__splitpath_unix, "foo/", 3, ""}, + {yasm__splitpath_unix, "foo/bar/", 7, ""}, + {yasm__splitpath_unix, "foo/bar//", 7, ""}, + /* Windows split */ + {yasm__splitpath_win, "", 0, ""}, + {yasm__splitpath_win, "file.ext", 0, "file.ext"}, + {yasm__splitpath_win, "./file.ext", 0, "file.ext"}, + {yasm__splitpath_win, "/file.ext", 1, "file.ext"}, + {yasm__splitpath_win, "/foo/file.ext", 4, "file.ext"}, + {yasm__splitpath_win, "/foo/bar/file.ext", 8, "file.ext"}, + {yasm__splitpath_win, "foo/file.ext", 3, "file.ext"}, + {yasm__splitpath_win, "foo/bar/file.ext", 7, "file.ext"}, + {yasm__splitpath_win, "foo/bar//file.ext", 7, "file.ext"}, + {yasm__splitpath_win, "..\\..\\file.ext", 5, "file.ext"}, + {yasm__splitpath_win, "c:file.ext", 2, "file.ext"}, + {yasm__splitpath_win, "c:.\\file.ext", 2, "file.ext"}, + {yasm__splitpath_win, "d:/file.ext", 3, "file.ext"}, + {yasm__splitpath_win, "e:/foo/file.ext", 6, "file.ext"}, + {yasm__splitpath_win, "f:/foo/bar/file.ext", 10, "file.ext"}, + {yasm__splitpath_win, "g:foo/file.ext", 5, "file.ext"}, + {yasm__splitpath_win, "h:foo/bar/file.ext", 9, "file.ext"}, + {yasm__splitpath_win, "i:foo/bar//file.ext", 9, "file.ext"}, + {yasm__splitpath_win, "d:\\file.ext", 3, "file.ext"}, + {yasm__splitpath_win, "e:\\foo/file.ext", 6, "file.ext"}, + {yasm__splitpath_win, "f:/foo\\bar\\file.ext", 10, "file.ext"}, + {yasm__splitpath_win, "g:foo\\file.ext", 5, "file.ext"}, + {yasm__splitpath_win, "h:foo/bar\\file.ext", 9, "file.ext"}, + {yasm__splitpath_win, "i:foo\\bar//\\file.ext", 9, "file.ext"}, + {yasm__splitpath_win, "\\", 1, ""}, + {yasm__splitpath_win, "c:", 2, ""}, + {yasm__splitpath_win, "d:\\", 3, ""}, + {yasm__splitpath_win, "e:\\foo/", 6, ""}, + {yasm__splitpath_win, "f:/foo\\bar\\", 10, ""}, + {yasm__splitpath_win, "g:foo\\", 5, ""}, + {yasm__splitpath_win, "h:foo/bar\\", 9, ""}, + {yasm__splitpath_win, "i:foo\\bar//\\", 9, ""}, +}; + +static char failed[1000]; +static char failmsg[100]; + +static int +run_test(Test_Entry *test) +{ + size_t headlen; + const char *tail; + const char *funcname; + + if (test->splitpath == &yasm__splitpath_unix) + funcname = "unix"; + else + funcname = "win"; + + headlen = test->splitpath(test->input, &tail); + if (headlen != test->headlen) { + sprintf(failmsg, + "splitpath_%s(\"%s\") bad head len: expected %lu, got %lu!", + funcname, test->input, (unsigned long)test->headlen, + (unsigned long)headlen); + return 1; + } + + if (strcmp(tail, test->tail) != 0) { + sprintf(failmsg, + "splitpath_%s(\"%s\") bad tail: expected \"%s\", got \"%s\"!", + funcname, test->input, test->tail, tail); + return 1; + } + + return 0; +} + +int +main(void) +{ + int nf = 0; + int numtests = sizeof(tests)/sizeof(Test_Entry); + int i; + + failed[0] = '\0'; + printf("Test splitpath_test: "); + for (i=0; i<numtests; i++) { + int fail = run_test(&tests[i]); + printf("%c", fail>0 ? 'F':'.'); + fflush(stdout); + if (fail) + sprintf(failed, "%s ** F: %s\n", failed, failmsg); + nf += fail; + } + + printf(" +%d-%d/%d %d%%\n%s", + numtests-nf, nf, numtests, 100*(numtests-nf)/numtests, failed); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libyasm/tests/strucsize.asm b/libyasm/tests/strucsize.asm new file mode 100644 index 0000000..5f5fc24 --- /dev/null +++ b/libyasm/tests/strucsize.asm @@ -0,0 +1,23 @@ +struc TST1 + .a resd 2 +endstruc + +struc TST2 + .b resb TST1_size +endstruc + +tst2: +istruc TST2 +at TST2.b + + istruc TST1 + + at TST1.a + dd 1, 2 + + iend + +iend + +dw TST1_size +dw TST2_size diff --git a/libyasm/tests/strucsize.hex b/libyasm/tests/strucsize.hex new file mode 100644 index 0000000..448779e --- /dev/null +++ b/libyasm/tests/strucsize.hex @@ -0,0 +1,12 @@ +01 +00 +00 +00 +02 +00 +00 +00 +08 +00 +08 +00 diff --git a/libyasm/tests/times-res.asm b/libyasm/tests/times-res.asm new file mode 100644 index 0000000..307a826 --- /dev/null +++ b/libyasm/tests/times-res.asm @@ -0,0 +1,3 @@ +times 5 resb 4 +times 0 resb 2 +times 1 resb 0 diff --git a/libyasm/tests/times-res.errwarn b/libyasm/tests/times-res.errwarn new file mode 100644 index 0000000..ceb76cf --- /dev/null +++ b/libyasm/tests/times-res.errwarn @@ -0,0 +1 @@ +-:1: warning: uninitialized space declared in code/data section: zeroing diff --git a/libyasm/tests/times-res.hex b/libyasm/tests/times-res.hex new file mode 100644 index 0000000..b1c8b27 --- /dev/null +++ b/libyasm/tests/times-res.hex @@ -0,0 +1,20 @@ +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 diff --git a/libyasm/tests/times0.asm b/libyasm/tests/times0.asm new file mode 100644 index 0000000..ea642d7 --- /dev/null +++ b/libyasm/tests/times0.asm @@ -0,0 +1 @@ +times 0 db 1 diff --git a/libyasm/tests/times0.hex b/libyasm/tests/times0.hex new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libyasm/tests/times0.hex diff --git a/libyasm/tests/timesover-err.asm b/libyasm/tests/timesover-err.asm new file mode 100644 index 0000000..6b3b7f9 --- /dev/null +++ b/libyasm/tests/timesover-err.asm @@ -0,0 +1,2 @@ +times 512 db 0 +times 01FEh-($-$$) db 0 diff --git a/libyasm/tests/timesover-err.errwarn b/libyasm/tests/timesover-err.errwarn new file mode 100644 index 0000000..81441ac --- /dev/null +++ b/libyasm/tests/timesover-err.errwarn @@ -0,0 +1 @@ +-:2: error: multiple is negative diff --git a/libyasm/tests/timesunder.asm b/libyasm/tests/timesunder.asm new file mode 100644 index 0000000..aceafa3 --- /dev/null +++ b/libyasm/tests/timesunder.asm @@ -0,0 +1,3 @@ +je label +times 82h-($-$$) nop +label: diff --git a/libyasm/tests/timesunder.hex b/libyasm/tests/timesunder.hex new file mode 100644 index 0000000..e3efd14 --- /dev/null +++ b/libyasm/tests/timesunder.hex @@ -0,0 +1,130 @@ +0f +84 +7e +00 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 +90 diff --git a/libyasm/tests/unary.asm b/libyasm/tests/unary.asm new file mode 100644 index 0000000..e0bcc17 --- /dev/null +++ b/libyasm/tests/unary.asm @@ -0,0 +1,5 @@ +[bits 32] +mov eax, ~0 +mov eax, ~5 +mov ebx, -0 +mov ebx, -5 diff --git a/libyasm/tests/unary.hex b/libyasm/tests/unary.hex new file mode 100644 index 0000000..ed29c52 --- /dev/null +++ b/libyasm/tests/unary.hex @@ -0,0 +1,20 @@ +b8 +ff +ff +ff +ff +b8 +fa +ff +ff +ff +bb +00 +00 +00 +00 +bb +fb +ff +ff +ff diff --git a/libyasm/tests/uncstring_test.c b/libyasm/tests/uncstring_test.c new file mode 100644 index 0000000..7303764 --- /dev/null +++ b/libyasm/tests/uncstring_test.c @@ -0,0 +1,156 @@ +/* + * + * Copyright (C) 2006-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "libyasm/errwarn.h" +#include "libyasm/file.h" + +typedef struct Test_Entry { + /* input string */ + const char *input; + + /* input length */ + size_t in_len; + + /* correct output string */ + const char *result; + + /* correct output length */ + size_t result_len; + + /* expected warning, if any */ + const char *warn; +} Test_Entry; + +static Test_Entry tests[] = { + {"noescape", 8, "noescape", 8, NULL}, + {"noescape2", 10, "noescape2", 10, NULL}, /* includes trailing zero */ + {"\\\\\\b\\f\\n\\r\\t\\\"", 14, "\\\b\f\n\r\t\"", 7, NULL}, + {"\\a", 2, "a", 1, NULL}, + /* hex tests */ + {"\\x", 2, "\x00", 1, NULL}, + {"\\x12", 4, "\x12", 1, NULL}, + {"\\x1234", 6, "\x34", 1, NULL}, + {"\\xg", 3, "\x00g", 2, NULL}, + {"\\xaga", 5, "\x0aga", 3, NULL}, + {"\\xaag", 5, "\xaag", 2, NULL}, + {"\\xaaa", 5, "\xaa", 1, NULL}, + {"\\x55559", 7, "\x59", 1, NULL}, + + /* oct tests */ + {"\\778", 4, "\000", 1, "octal value out of range"}, + {"\\779", 4, "\001", 1, "octal value out of range"}, + {"\\1x", 3, "\001x", 2, NULL}, + {"\\7779", 5, "\xff" "9", 2, NULL}, + {"\\7999", 5, "\x11" "9", 2, "octal value out of range"}, + {"\\77a", 4, "\077a", 2, NULL}, + {"\\5555555", 8, "\x6d" "5555", 5, NULL}, + {"\\9999", 5, "\x91" "9", 2, "octal value out of range"}, +}; + +static char failed[1000]; +static char failmsg[100]; + +static int +run_test(Test_Entry *test) +{ + char str[256]; + size_t len; + yasm_warn_class wclass; + char *wstr; + + strncpy(str, test->input, test->in_len); + len = test->in_len; + + yasm_unescape_cstring((unsigned char *)str, &len); + if (len != test->result_len) { + sprintf(failmsg, + "unescape_cstring(\"%s\", %lu) bad output len: expected %lu, got %lu!", + test->input, (unsigned long)test->in_len, + (unsigned long)test->result_len, (unsigned long)len); + return 1; + } + + if (strncmp(str, test->result, len) != 0) { + sprintf(failmsg, + "unescape_cstring(\"%s\", %lu) bad output: expected \"%s\", got \"%s\"!", + test->input, (unsigned long)test->in_len, test->result, str); + return 1; + } + + yasm_warn_fetch(&wclass, &wstr); + if (wstr != NULL && test->warn == NULL) { + sprintf(failmsg, + "unescape_cstring(\"%s\", %lu) unexpected warning: %s!", + test->input, (unsigned long)test->in_len, wstr); + return 1; + } + if (wstr == NULL && test->warn != NULL) { + sprintf(failmsg, + "unescape_cstring(\"%s\", %lu) expected warning: %s, did not get it!", + test->input, (unsigned long)test->in_len, test->warn); + return 1; + } + if (wstr && test->warn && strcmp(wstr, test->warn) != 0) { + sprintf(failmsg, + "unescape_cstring(\"%s\", %lu) expected warning: %s, got %s!", + test->input, (unsigned long)test->in_len, test->warn, wstr); + return 1; + } + yasm_xfree(wstr); + + return 0; +} + +int +main(void) +{ + int nf = 0; + int numtests = sizeof(tests)/sizeof(Test_Entry); + int i; + + yasm_errwarn_initialize(); + + failed[0] = '\0'; + printf("Test uncstring_test: "); + for (i=0; i<numtests; i++) { + int fail = run_test(&tests[i]); + printf("%c", fail>0 ? 'F':'.'); + fflush(stdout); + if (fail) + sprintf(failed, "%s ** F: %s\n", failed, failmsg); + nf += fail; + } + + printf(" +%d-%d/%d %d%%\n%s", + numtests-nf, nf, numtests, 100*(numtests-nf)/numtests, failed); + + yasm_errwarn_cleanup(); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libyasm/tests/value-err.asm b/libyasm/tests/value-err.asm new file mode 100644 index 0000000..883fa6f --- /dev/null +++ b/libyasm/tests/value-err.asm @@ -0,0 +1,9 @@ +label: +mov [label/2+1], ax +mov ax, label*2 +mov [label+5], ax +mov ax, label wrt foo +foo: +dd label +dd label<<5 +dd label>>2 diff --git a/libyasm/tests/value-err.errwarn b/libyasm/tests/value-err.errwarn new file mode 100644 index 0000000..a949cd8 --- /dev/null +++ b/libyasm/tests/value-err.errwarn @@ -0,0 +1,3 @@ +-:2: error: effective address too complex +-:3: error: immediate expression too complex +-:8: error: data expression too complex diff --git a/libyasm/tests/value-mask.asm b/libyasm/tests/value-mask.asm new file mode 100644 index 0000000..3e93952 --- /dev/null +++ b/libyasm/tests/value-mask.asm @@ -0,0 +1,5 @@ +db label +db label & 0xff +section .bss +resb 500 +label: diff --git a/libyasm/tests/value-mask.errwarn b/libyasm/tests/value-mask.errwarn new file mode 100644 index 0000000..9df5a66 --- /dev/null +++ b/libyasm/tests/value-mask.errwarn @@ -0,0 +1 @@ +-:1: warning: value does not fit in 8 bit field diff --git a/libyasm/tests/value-mask.hex b/libyasm/tests/value-mask.hex new file mode 100644 index 0000000..8cae27c --- /dev/null +++ b/libyasm/tests/value-mask.hex @@ -0,0 +1,2 @@ +f8 +f8 diff --git a/libyasm/tests/value-samesym.asm b/libyasm/tests/value-samesym.asm new file mode 100644 index 0000000..da32599 --- /dev/null +++ b/libyasm/tests/value-samesym.asm @@ -0,0 +1,2 @@ +extern RW +mov [eax + (RW + 22032) - (RW + 23056)], eax diff --git a/libyasm/tests/value-samesym.errwarn b/libyasm/tests/value-samesym.errwarn new file mode 100644 index 0000000..58c850b --- /dev/null +++ b/libyasm/tests/value-samesym.errwarn @@ -0,0 +1 @@ +-:1: warning: binary object format does not support extern variables diff --git a/libyasm/tests/value-samesym.hex b/libyasm/tests/value-samesym.hex new file mode 100644 index 0000000..4bab16b --- /dev/null +++ b/libyasm/tests/value-samesym.hex @@ -0,0 +1,8 @@ +67 +66 +89 +80 +00 +fc +ff +ff diff --git a/libyasm/valparam.c b/libyasm/valparam.c new file mode 100644 index 0000000..88e41a7 --- /dev/null +++ b/libyasm/valparam.c @@ -0,0 +1,385 @@ +/* + * Value/Parameter type functions + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "valparam.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" +#include "symrec.h" + +#include "section.h" + +void +yasm_call_directive(const yasm_directive *directive, yasm_object *object, + yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line) +{ + yasm_valparam *vp; + + if ((directive->flags & (YASM_DIR_ARG_REQUIRED|YASM_DIR_ID_REQUIRED)) && + (!valparams || !yasm_vps_first(valparams))) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("directive `%s' requires an argument"), + directive->name); + return; + } + if (valparams) { + vp = yasm_vps_first(valparams); + if ((directive->flags & YASM_DIR_ID_REQUIRED) && + vp->type != YASM_PARAM_ID) { + yasm_error_set(YASM_ERROR_SYNTAX, + N_("directive `%s' requires an identifier parameter"), + directive->name); + return; + } + } + directive->handler(object, valparams, objext_valparams, line); +} + +yasm_valparam * +yasm_vp_create_id(/*@keep@*/ char *v, /*@keep@*/ char *p, int id_prefix) +{ + yasm_valparam *r = yasm_xmalloc(sizeof(yasm_valparam)); + r->val = v; + r->type = YASM_PARAM_ID; + r->param.id = p; + r->id_prefix = (char)id_prefix; + return r; +} + +yasm_valparam * +yasm_vp_create_string(/*@keep@*/ char *v, /*@keep@*/ char *p) +{ + yasm_valparam *r = yasm_xmalloc(sizeof(yasm_valparam)); + r->val = v; + r->type = YASM_PARAM_STRING; + r->param.str = p; + r->id_prefix = '\0'; + return r; +} + +yasm_valparam * +yasm_vp_create_expr(/*@keep@*/ char *v, /*@keep@*/ yasm_expr *p) +{ + yasm_valparam *r = yasm_xmalloc(sizeof(yasm_valparam)); + r->val = v; + r->type = YASM_PARAM_EXPR; + r->param.e = p; + r->id_prefix = '\0'; + return r; +} + +/*@null@*/ /*@only@*/ yasm_expr * +yasm_vp_expr(const yasm_valparam *vp, yasm_symtab *symtab, unsigned long line) +{ + if (!vp) + return NULL; + switch (vp->type) { + case YASM_PARAM_ID: + return yasm_expr_create_ident(yasm_expr_sym( + yasm_symtab_use(symtab, yasm_vp_id(vp), line)), line); + case YASM_PARAM_EXPR: + return yasm_expr_copy(vp->param.e); + default: + return NULL; + } +} + +/*@null@*/ /*@dependent@*/ const char * +yasm_vp_string(const yasm_valparam *vp) +{ + if (!vp) + return NULL; + switch (vp->type) { + case YASM_PARAM_ID: + return vp->param.id; + case YASM_PARAM_STRING: + return vp->param.str; + default: + return NULL; + } +} + +/*@null@*/ /*@dependent@*/ const char * +yasm_vp_id(const yasm_valparam *vp) +{ + if (!vp) + return NULL; + if (vp->type == YASM_PARAM_ID) { + if (vp->param.id[0] == vp->id_prefix) + return &vp->param.id[1]; + else + return vp->param.id; + } + return NULL; +} + +void +yasm_vps_delete(yasm_valparamhead *headp) +{ + yasm_valparam *cur, *next; + + cur = STAILQ_FIRST(headp); + while (cur) { + next = STAILQ_NEXT(cur, link); + if (cur->val) + yasm_xfree(cur->val); + switch (cur->type) { + case YASM_PARAM_ID: + yasm_xfree(cur->param.id); + break; + case YASM_PARAM_STRING: + yasm_xfree(cur->param.str); + break; + case YASM_PARAM_EXPR: + yasm_expr_destroy(cur->param.e); + break; + } + yasm_xfree(cur); + cur = next; + } + STAILQ_INIT(headp); +} + +void +yasm_vps_print(const yasm_valparamhead *headp, FILE *f) +{ + const yasm_valparam *vp; + + if(!headp) { + fprintf(f, "(none)"); + return; + } + + yasm_vps_foreach(vp, headp) { + if (vp->val) + fprintf(f, "(\"%s\",", vp->val); + else + fprintf(f, "((nil),"); + switch (vp->type) { + case YASM_PARAM_ID: + fprintf(f, "%s", vp->param.id); + break; + case YASM_PARAM_STRING: + fprintf(f, "\"%s\"", vp->param.str); + break; + case YASM_PARAM_EXPR: + yasm_expr_print(vp->param.e, f); + break; + } + fprintf(f, ")"); + if (yasm_vps_next(vp)) + fprintf(f, ","); + } +} + +yasm_valparamhead * +yasm_vps_create(void) +{ + yasm_valparamhead *headp = yasm_xmalloc(sizeof(yasm_valparamhead)); + yasm_vps_initialize(headp); + return headp; +} + +void +yasm_vps_destroy(yasm_valparamhead *headp) +{ + yasm_vps_delete(headp); + yasm_xfree(headp); +} + +int +yasm_dir_helper(void *obj, yasm_valparam *vp_first, unsigned long line, + const yasm_dir_help *help, size_t nhelp, void *data, + int (*helper_valparam) (void *obj, yasm_valparam *vp, + unsigned long line, void *data)) +{ + yasm_valparam *vp = vp_first; + int anymatched = 0; + int matched; + + if (!vp) + return 0; + + do { + const char *s; + size_t i; + + matched = 0; + if (!vp->val && (s = yasm_vp_id(vp))) { + for (i=0; i<nhelp; i++) { + if (help[i].needsparam == 0 && + yasm__strcasecmp(s, help[i].name) == 0) { + if (help[i].helper(obj, vp, line, + ((char *)data)+help[i].off, + help[i].arg) != 0) + return -1; + matched = 1; + anymatched = 1; + break; + } + } + } else if (vp->val) { + for (i=0; i<nhelp; i++) { + if (help[i].needsparam == 1 && + yasm__strcasecmp(vp->val, help[i].name) == 0) { + if (help[i].helper(obj, vp, line, + ((char *)data)+help[i].off, + help[i].arg) != 0) + return -1; + matched = 1; + anymatched = 1; + break; + } + } + } + + if (!matched) { + int final = helper_valparam(obj, vp, line, data); + if (final < 0) + return -1; + if (final > 0) + anymatched = 1; + } + } while((vp = yasm_vps_next(vp))); + + return anymatched; +} + +int +yasm_dir_helper_flag_or(void *obj, yasm_valparam *vp, unsigned long line, + void *d, uintptr_t flag) +{ + unsigned long *flags = (unsigned long *)d; + *flags |= flag; + return 0; +} + +int +yasm_dir_helper_flag_and(void *obj, yasm_valparam *vp, unsigned long line, + void *d, uintptr_t flag) +{ + unsigned long *flags = (unsigned long *)d; + *flags &= ~flag; + return 0; +} + +int +yasm_dir_helper_flag_set(void *obj, yasm_valparam *vp, unsigned long line, + void *d, uintptr_t flag) +{ + unsigned long *flags = (unsigned long *)d; + *flags = flag; + return 0; +} + +int +yasm_dir_helper_expr(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg) +{ + yasm_object *object = (yasm_object *)obj; + yasm_expr **expr = (yasm_expr **)data; + + if (*expr) + yasm_expr_destroy(*expr); + if (!(*expr = yasm_vp_expr(vp, object->symtab, line))) { + yasm_error_set(YASM_ERROR_VALUE, N_("argument to `%s' is not an expression"), + vp->val); + return -1; + } + return 0; +} + +int +yasm_dir_helper_intn(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg) +{ + yasm_object *object = (yasm_object *)obj; + /*@only@*/ /*@null@*/ yasm_expr *e; + /*@dependent@*/ /*@null@*/ yasm_intnum *local; + yasm_intnum **intn = (yasm_intnum **)data; + + if (*intn) + yasm_intnum_destroy(*intn); + if (!(e = yasm_vp_expr(vp, object->symtab, line)) || + !(local = yasm_expr_get_intnum(&e, 0))) { + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("argument to `%s' is not an integer"), + vp->val); + if (e) + yasm_expr_destroy(e); + return -1; + } + *intn = yasm_intnum_copy(local); + yasm_expr_destroy(e); + return 0; +} + +int +yasm_dir_helper_string(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg) +{ + /*@dependent@*/ /*@null@*/ const char *local; + char **s = (char **)data; + + if (*s) + yasm_xfree(*s); + if (!(local = yasm_vp_string(vp))) { + yasm_error_set(YASM_ERROR_VALUE, + N_("argument to `%s' is not a string or identifier"), + vp->val); + return -1; + } + *s = yasm__xstrdup(local); + return 0; +} + +int +yasm_dir_helper_valparam_warn(void *obj, yasm_valparam *vp, + unsigned long line, void *data) +{ + const char *s; + + if (vp->val) { + yasm_warn_set(YASM_WARN_GENERAL, N_("Unrecognized qualifier `%s'"), + vp->val); + return 0; + } + + if ((s = yasm_vp_id(vp))) + yasm_warn_set(YASM_WARN_GENERAL, N_("Unrecognized qualifier `%s'"), s); + else if (vp->type == YASM_PARAM_STRING) + yasm_warn_set(YASM_WARN_GENERAL, N_("Unrecognized string qualifier")); + else + yasm_warn_set(YASM_WARN_GENERAL, N_("Unrecognized numeric qualifier")); + + return 0; +} diff --git a/libyasm/valparam.h b/libyasm/valparam.h new file mode 100644 index 0000000..d7343d4 --- /dev/null +++ b/libyasm/valparam.h @@ -0,0 +1,408 @@ +/** + * \file libyasm/valparam.h + * \brief YASM value/parameter interface. + * + * \license + * Copyright (C) 2001-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_VALPARAM_H +#define YASM_VALPARAM_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Value/parameter pair. \internal */ +struct yasm_valparam { + /*@reldef@*/ STAILQ_ENTRY(yasm_valparam) link; /**< Next pair in list */ + /*@owned@*/ /*@null@*/ char *val; /**< Value */ + + /** Parameter type. */ + enum yasm_param_type { + YASM_PARAM_ID, /**< Identifier */ + YASM_PARAM_STRING, /**< String */ + YASM_PARAM_EXPR /**< Expression */ + } type; /**< Parameter type */ + + /** Parameter value. */ + union yasm_param { + /*@owned@*/ char *id; /**< Identifier */ + /*@owned@*/ char *str; /**< String */ + /*@owned@*/ yasm_expr *e; /**< Expression */ + } param; /**< Parameter */ + + /** Prefix character that indicates a raw identifier. When + * yasm_vp_string() is called on a #YASM_PARAM_ID, all characters are + * returned. When yasm_vp_id() is called on a #YASM_PARAM_ID, if the + * identifier begins with this character, this character is stripped + * from the returned value. + */ + char id_prefix; +}; + +/** Linked list of value/parameter pairs. \internal */ +/*@reldef@*/ STAILQ_HEAD(yasm_valparamhead, yasm_valparam); + +/** Directive list entry structure. */ +struct yasm_directive { + /** Directive name. GAS directives should include the ".", NASM + * directives should just be the raw name (not including the []). + * NULL entry required to terminate list of directives. + */ + /*@null@*/ const char *name; + + const char *parser; /**< Parser keyword */ + + /** Handler callback function for the directive. + * \param object object + * \param valparams value/parameters + * \param objext_valparams object format-specific value/parameters + * \param line virtual line (from yasm_linemap) + */ + void (*handler) (yasm_object *object, yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, unsigned long line); + + /** Flags for pre-handler parameter checking. */ + enum yasm_directive_flags { + YASM_DIR_ANY = 0, /**< Any valparams accepted */ + YASM_DIR_ARG_REQUIRED = 1, /**< Require at least 1 valparam */ + YASM_DIR_ID_REQUIRED = 2 /**< First valparam must be ID */ + } flags; +}; + +/** Call a directive. Performs any valparam checks asked for by the + * directive prior to call. Note that for a variety of reasons, a directive + * can generate an error. + * \param directive directive + * \param object object + * \param valparams value/parameters + * \param objext_valparams object format-specific value/parameters + * \param line virtual line (from yasm_linemap) + */ +YASM_LIB_DECL +void yasm_call_directive(const yasm_directive *directive, yasm_object *object, + yasm_valparamhead *valparams, + yasm_valparamhead *objext_valparams, + unsigned long line); + +/** Create a new valparam with identifier parameter. + * \param v value + * \param p parameter + * \param id_prefix identifier prefix for raw identifiers + * \return Newly allocated valparam. + */ +YASM_LIB_DECL +yasm_valparam *yasm_vp_create_id(/*@keep@*/ char *v, /*@keep@*/ char *p, + int id_prefix); + +/** Create a new valparam with string parameter. + * \param v value + * \param p parameter + * \return Newly allocated valparam. + */ +YASM_LIB_DECL +yasm_valparam *yasm_vp_create_string(/*@keep@*/ char *v, /*@keep@*/ char *p); + +/** Create a new valparam with expression parameter. + * \param v value + * \param p parameter + * \return Newly allocated valparam. + */ +YASM_LIB_DECL +yasm_valparam *yasm_vp_create_expr(/*@keep@*/ char *v, + /*@keep@*/ yasm_expr *p); + +/** Get a valparam parameter as an expr. If the parameter is an identifier, + * it's treated as a symbol (yasm_symtab_use() is called to convert it). + * \param vp valparam + * \param symtab symbol table + * \param line virtual line + * \return Expression, or NULL if vp is NULL or the parameter cannot be + * converted to an expression. + */ +YASM_LIB_DECL +/*@null@*/ /*@only@*/ yasm_expr *yasm_vp_expr + (const yasm_valparam *vp, yasm_symtab *symtab, unsigned long line); + +/** Get a valparam parameter as a string. If the parameter is an identifier, + * it's treated as a string. + * \param vp valparam + * \return String, or NULL if vp is NULL or the parameter cannot be realized + * as a string. + */ +YASM_LIB_DECL +/*@null@*/ /*@dependent@*/ const char *yasm_vp_string(const yasm_valparam *vp); + +/** Get a valparam parameter as an identifier. + * \param vp valparam + * \return Identifier (string), or NULL if vp is NULL or the parameter is not + * an identifier. + */ +YASM_LIB_DECL +/*@null@*/ /*@dependent@*/ const char *yasm_vp_id(const yasm_valparam *vp); + +/** Create a new linked list of valparams. + * \return Newly allocated valparam list. + */ +YASM_LIB_DECL +yasm_valparamhead *yasm_vps_create(void); + +/** Destroy a list of valparams (created with yasm_vps_create). + * \param headp list of valparams + */ +YASM_LIB_DECL +void yasm_vps_destroy(yasm_valparamhead *headp); + +/** Initialize linked list of valparams. + * \param headp linked list + */ +void yasm_vps_initialize(/*@out@*/ yasm_valparamhead *headp); +#ifndef YASM_DOXYGEN +#define yasm_vps_initialize(headp) STAILQ_INIT(headp) +#endif + +/** Destroy (free allocated memory for) linked list of valparams (created with + * yasm_vps_initialize). + * \warning Deletes val/params. + * \param headp linked list + */ +YASM_LIB_DECL +void yasm_vps_delete(yasm_valparamhead *headp); + +/** Append valparam to tail of linked list. + * \param headp linked list + * \param vp valparam + */ +void yasm_vps_append(yasm_valparamhead *headp, /*@keep@*/ yasm_valparam *vp); +#ifndef YASM_DOXYGEN +#define yasm_vps_append(headp, vp) do { \ + if (vp) \ + STAILQ_INSERT_TAIL(headp, vp, link); \ + } while(0) +#endif + +/** Get first valparam in linked list. + * \param headp linked list + * \return First valparam in linked list. + */ +/*@null@*/ /*@dependent@*/ yasm_valparam *yasm_vps_first + (yasm_valparamhead *headp); +#ifndef YASM_DOXYGEN +#define yasm_vps_first(headp) STAILQ_FIRST(headp) +#endif + +/** Get next valparam in linked list. + * \param cur previous valparam in linked list + * \return Next valparam in linked list. + */ +/*@null@*/ /*@dependent@*/ yasm_valparam *yasm_vps_next(yasm_valparam *cur); +#ifndef YASM_DOXYGEN +#define yasm_vps_next(cur) STAILQ_NEXT(cur, link) +#endif + +/** Iterate through linked list of valparams. + * \internal + * \param iter iterator variable + * \param headp linked list + */ +#ifndef YASM_DOXYGEN +#define yasm_vps_foreach(iter, headp) STAILQ_FOREACH(iter, headp, link) +#endif + +/** Print linked list of valparams. For debugging purposes. + * \param f file + * \param headp linked list + */ +YASM_LIB_DECL +void yasm_vps_print(/*@null@*/ const yasm_valparamhead *headp, FILE *f); + +/** Directive valparam parse helper structure. */ +typedef struct yasm_dir_help { + /** Value portion of val=param (if needsparam=1), or standalone identifier + * (if needsparam=0). + */ + const char *name; + + /** 1 if value requires parameter, 0 if it must not have a parameter. */ + int needsparam; + + /** Helper callback function if name and parameter existence match. + * \param obj obj passed into yasm_dir_helper() + * \param vp value/parameter + * \param line line passed into yasm_dir_helper() + * \param data data passed into yasm_dir_helper() plus + #yasm_dir_help.off offset + * \param arg #yasm_dir_help.arg argument + * \return -1 on error, 0 otherwise. + */ + int (*helper) (void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + + /** Offset added to data pointer passed into yasm_dir_helper() before + * data pointer is given to #yasm_dir_help.helper(). This is so that + * a structure can be passed into yasm_dir_helper() and this can be an + * offsetof() to point the helper function to a specific structure + * member. + */ + size_t off; + + /** Argument to pass in as the arg parameter to #yasm_dir_help.helper(). + */ + uintptr_t arg; +} yasm_dir_help; + +/** Help parse a list of directive value/parameters. Takes an array of + * #yasm_dir_help structures and tries to match val=param (or just val) + * against the passed value/parameters. When no match is found in the + * array of help structures, calls helper_valparam. + * \param obj object to be passed to yasm_dir_help.helper() or + * helper_valparam() callback + * \param vp_first first value/parameter to examine + * \param line virtual line number; passed down to helper callback + * \param help array of #yasm_dir_help structures + * \param nhelp number of array elements + * \param data base data pointer; if a match is found, + * the respective #yasm_dir_help.off is added to this + * prior to it being passed to the helper callback + * \param helper_valparam catch-all callback; should return -1 on error, + * 0 if not matched, 1 if matched. + * \return -1 on error, 1 if any arguments matched (including via + * catch-all callback), 0 if no match. + */ +YASM_LIB_DECL +int yasm_dir_helper(void *obj, yasm_valparam *vp_first, unsigned long line, + const yasm_dir_help *help, size_t nhelp, void *data, + int (*helper_valparam) (void *object, + yasm_valparam *vp, + unsigned long line, + void *data)); + +/** Standard helper for yasm_dir_helper() that simply sets a flag when called. + * It does not look at the vp; rather, it uses the value of the arg parameter, + * and stores an unsigned long value to data. + * \param obj unused + * \param vp unused + * \param line unused + * \param data pointer to an unsigned long + * \param arg flag to set + * \return 0 + */ +YASM_LIB_DECL +int yasm_dir_helper_flag_set(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + +/** Standard helper for yasm_dir_helper() that simply ORs a flag when called. + * It does not look at the vp; rather, it uses the value of the arg parameter, + * and ORs it with the unsigned long value in data. + * \param obj unused + * \param vp unused + * \param line unused + * \param data pointer to an unsigned long + * \param arg flag to OR + * \return 0 + */ +YASM_LIB_DECL +int yasm_dir_helper_flag_or(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + +/** Standard helper for yasm_dir_helper() that simply ANDs a flag when called. + * It does not look at the vp; rather, it uses the value of the arg parameter, + * and ANDs its inverse (~) with the unsigned long value in data. + * \param obj unused + * \param vp unused + * \param line unused + * \param data pointer to an unsigned long + * \param arg flag to AND + * \return 0 + */ +YASM_LIB_DECL +int yasm_dir_helper_flag_and(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + +/** Standard helper for yasm_dir_helper() that parses an expr parameter. + * The #yasm_dir_help structure that uses this function should have + * needsparam=1. The obj parameter to yasm_dir_helper() when this helper + * is used MUST point to a #yasm_object. In addition, the data parameter + * that is ultimately passed to this function (e.g. yasm_dir_helper() data + * parameter plus #yasm_dir_help.off) must point to a #yasm_expr * + * initialized to NULL. + * \param obj object; must be #yasm_object + * \param vp valparam + * \param line virtual line number + * \param data pointer to #yasm_expr * + * \param arg unused argument + * \return -1 on error, 0 otherwise. + */ +YASM_LIB_DECL +int yasm_dir_helper_expr(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + +/** Standard helper for yasm_dir_helper() that parses an intnum parameter. + * The #yasm_dir_help structure that uses this function should have + * needsparam=1. The obj parameter to yasm_dir_helper() when this helper + * is used MUST point to a #yasm_object. In addition, the data parameter + * that is ultimately passed to this function (e.g. yasm_dir_helper() data + * parameter plus #yasm_dir_help.off) must point to a #yasm_intnum * + * initialized to NULL. + * \param obj object; must be #yasm_object + * \param vp valparam + * \param line virtual line number + * \param data pointer to #yasm_intnum * + * \param arg unused argument + * \return -1 on error, 0 otherwise. + */ +YASM_LIB_DECL +int yasm_dir_helper_intn(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + +/** Standard helper for yasm_dir_helper() that parses an string (or + * standalone identifier) parameter. + * The #yasm_dir_help structure that uses this function should have + * needsparam=1. The data parameter that is ultimately passed to this + * function (e.g. yasm_dir_helper() data parameter plus #yasm_dir_help.off) + * must point to a char * initialized to NULL. + * \param obj unused + * \param vp valparam + * \param line unused + * \param data pointer to char * + * \param arg unused + * \return -1 on error, 0 otherwise. + */ +YASM_LIB_DECL +int yasm_dir_helper_string(void *obj, yasm_valparam *vp, unsigned long line, + void *data, uintptr_t arg); + +/** Standard catch-all callback fro yasm_dir_helper(). Generates standard + * warning for all valparams. + * \param obj unused + * \param vp valparam + * \param line unused + * \param data unused + * \return 0 + */ +YASM_LIB_DECL +int yasm_dir_helper_valparam_warn(void *obj, yasm_valparam *vp, + unsigned long line, void *data); +#endif diff --git a/libyasm/value.c b/libyasm/value.c new file mode 100644 index 0000000..3ab73c1 --- /dev/null +++ b/libyasm/value.c @@ -0,0 +1,771 @@ +/* + * Value handling + * + * Copyright (C) 2006-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "libyasm-stdint.h" +#include "coretype.h" +#include "bitvect.h" + +#include "errwarn.h" +#include "intnum.h" +#include "floatnum.h" +#include "expr.h" +#include "value.h" +#include "symrec.h" + +#include "bytecode.h" +#include "section.h" + +#include "arch.h" + + +void +yasm_value_initialize(/*@out@*/ yasm_value *value, + /*@null@*/ /*@kept@*/ yasm_expr *e, unsigned int size) +{ + value->abs = e; + value->rel = NULL; + value->wrt = NULL; + value->seg_of = 0; + value->rshift = 0; + value->curpos_rel = 0; + value->ip_rel = 0; + value->jump_target = 0; + value->section_rel = 0; + value->no_warn = 0; + value->sign = 0; + value->size = size; +} + +void +yasm_value_init_sym(/*@out@*/ yasm_value *value, /*@null@*/ yasm_symrec *sym, + unsigned int size) +{ + value->abs = NULL; + value->rel = sym; + value->wrt = NULL; + value->seg_of = 0; + value->rshift = 0; + value->curpos_rel = 0; + value->ip_rel = 0; + value->jump_target = 0; + value->section_rel = 0; + value->no_warn = 0; + value->sign = 0; + value->size = size; +} + +void +yasm_value_init_copy(yasm_value *value, const yasm_value *orig) +{ + value->abs = orig->abs ? yasm_expr_copy(orig->abs) : NULL; + value->rel = orig->rel; + value->wrt = orig->wrt; + value->seg_of = orig->seg_of; + value->rshift = orig->rshift; + value->curpos_rel = orig->curpos_rel; + value->ip_rel = orig->ip_rel; + value->jump_target = orig->jump_target; + value->section_rel = orig->section_rel; + value->no_warn = orig->no_warn; + value->sign = orig->sign; + value->size = orig->size; +} + +void +yasm_value_delete(yasm_value *value) +{ + if (value->abs) + yasm_expr_destroy(value->abs); + value->abs = NULL; + value->rel = NULL; +} + +void +yasm_value_set_curpos_rel(yasm_value *value, yasm_bytecode *bc, + unsigned int ip_rel) +{ + value->curpos_rel = 1; + value->ip_rel = ip_rel; + /* In order for us to correctly output curpos-relative values, we must + * have a relative portion of the value. If one doesn't exist, point + * to a custom absolute symbol. + */ + if (!value->rel) { + yasm_object *object = yasm_section_get_object(yasm_bc_get_section(bc)); + value->rel = yasm_symtab_abs_sym(object->symtab); + } +} + +static int +value_finalize_scan(yasm_value *value, yasm_expr *e, + /*@null@*/ yasm_bytecode *expr_precbc, int ssym_not_ok) +{ + int i; + /*@dependent@*/ yasm_section *sect; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; + + unsigned long shamt; /* for SHR */ + + /* Yes, this has a maximum upper bound on 32 terms, based on an + * "insane number of terms" (and ease of implementation) WAG. + * The right way to do this would be a stack-based alloca, but that's + * not ISO C. We really don't want to malloc here as this function is + * hit a lot! + * + * This is a bitmask to keep things small, as this is a recursive + * routine and we don't want to eat up stack space. + */ + unsigned long used; /* for ADD */ + + /* Thanks to this running after a simplify, we don't need to iterate + * down through IDENTs or handle SUB. + * + * We scan for a single symrec, gathering info along the way. After + * we've found the symrec, we keep scanning but error if we find + * another one. We pull out the single symrec and any legal operations + * performed on it. + * + * Also, if we find a float anywhere, we don't allow mixing of a single + * symrec with it. + */ + switch (e->op) { + case YASM_EXPR_ADD: + /* Okay for single symrec anywhere in expr. + * Check for single symrec anywhere. + * Handle symrec-symrec by checking for (-1*symrec) + * and symrec term pairs (where both symrecs are in the same + * segment). + */ + if (e->numterms > 32) + yasm__fatal(N_("expression on line %d has too many add terms;" + " internal limit of 32"), e->line); + + used = 0; + + for (i=0; i<e->numterms; i++) { + int j; + yasm_expr *sube; + yasm_intnum *intn; + yasm_symrec *sym; + /*@dependent@*/ yasm_section *sect2; + /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc2; + + /* First look for an (-1*symrec) term */ + if (e->terms[i].type != YASM_EXPR_EXPR) + continue; + sube = e->terms[i].data.expn; + + if (sube->op != YASM_EXPR_MUL || sube->numterms != 2) { + /* recurse instead */ + if (value_finalize_scan(value, sube, expr_precbc, + ssym_not_ok)) + return 1; + continue; + } + + if (sube->terms[0].type == YASM_EXPR_INT && + sube->terms[1].type == YASM_EXPR_SYM) { + intn = sube->terms[0].data.intn; + sym = sube->terms[1].data.sym; + } else if (sube->terms[0].type == YASM_EXPR_SYM && + sube->terms[1].type == YASM_EXPR_INT) { + sym = sube->terms[0].data.sym; + intn = sube->terms[1].data.intn; + } else { + if (value_finalize_scan(value, sube, expr_precbc, + ssym_not_ok)) + return 1; + continue; + } + + if (!yasm_intnum_is_neg1(intn)) { + if (value_finalize_scan(value, sube, expr_precbc, + ssym_not_ok)) + return 1; + continue; + } + + /* Look for the same symrec term; even if both are external, + * they should cancel out. + */ + for (j=0; j<e->numterms; j++) { + if (e->terms[j].type == YASM_EXPR_SYM + && e->terms[j].data.sym == sym + && (used & (1<<j)) == 0) { + /* Mark as used */ + used |= 1<<j; + + /* Replace both symrec portions with 0 */ + yasm_expr_destroy(sube); + e->terms[i].type = YASM_EXPR_INT; + e->terms[i].data.intn = yasm_intnum_create_uint(0); + e->terms[j].type = YASM_EXPR_INT; + e->terms[j].data.intn = yasm_intnum_create_uint(0); + + break; /* stop looking */ + } + } + if (j != e->numterms) + continue; + + if (!yasm_symrec_get_label(sym, &precbc)) { + if (value_finalize_scan(value, sube, expr_precbc, + ssym_not_ok)) + return 1; + continue; + } + sect2 = yasm_bc_get_section(precbc); + + /* Now look for a unused symrec term in the same segment */ + for (j=0; j<e->numterms; j++) { + if (e->terms[j].type == YASM_EXPR_SYM + && yasm_symrec_get_label(e->terms[j].data.sym, + &precbc2) + && (sect = yasm_bc_get_section(precbc2)) + && sect == sect2 + && (used & (1<<j)) == 0) { + /* Mark as used */ + used |= 1<<j; + break; /* stop looking */ + } + } + + /* We didn't match in the same segment. If the + * -1*symrec is actually -1*curpos, we can match + * unused symrec terms in other segments and generate + * a curpos-relative reloc. + * + * Similarly, handle -1*symrec in other segment via the + * following transformation: + * other-this = (other-.)+(.-this) + * We can only do this transformation if "this" is in + * this expr's segment. + * + * Don't do this if we've already become curpos-relative. + * The unmatched symrec will be caught below. + */ + if (j == e->numterms && !value->curpos_rel + && (yasm_symrec_is_curpos(sym) + || (expr_precbc + && sect2 == yasm_bc_get_section(expr_precbc)))) { + for (j=0; j<e->numterms; j++) { + if (e->terms[j].type == YASM_EXPR_SYM + && !yasm_symrec_get_equ(e->terms[j].data.sym) + && !yasm_symrec_is_special(e->terms[j].data.sym) + && (used & (1<<j)) == 0) { + /* Mark as used */ + used |= 1<<j; + /* Mark value as curpos-relative */ + if (value->rel || ssym_not_ok) + return 1; + value->rel = e->terms[j].data.sym; + value->curpos_rel = 1; + if (yasm_symrec_is_curpos(sym)) { + /* Replace both symrec portions with 0 */ + yasm_expr_destroy(sube); + e->terms[i].type = YASM_EXPR_INT; + e->terms[i].data.intn = + yasm_intnum_create_uint(0); + e->terms[j].type = YASM_EXPR_INT; + e->terms[j].data.intn = + yasm_intnum_create_uint(0); + } else { + /* Replace positive portion with curpos */ + yasm_object *object = + yasm_section_get_object(sect2); + yasm_symtab *symtab = object->symtab; + e->terms[j].data.sym = + yasm_symtab_define_curpos + (symtab, ".", expr_precbc, e->line); + } + break; /* stop looking */ + } + } + } + + + if (j == e->numterms) + return 1; /* We didn't find a match! */ + } + + /* Look for unmatched symrecs. If we've already found one or + * we don't WANT to find one, error out. + */ + for (i=0; i<e->numterms; i++) { + if (e->terms[i].type == YASM_EXPR_SYM + && (used & (1<<i)) == 0) { + if (value->rel || ssym_not_ok) + return 1; + value->rel = e->terms[i].data.sym; + /* and replace with 0 */ + e->terms[i].type = YASM_EXPR_INT; + e->terms[i].data.intn = yasm_intnum_create_uint(0); + } + } + break; + case YASM_EXPR_SHR: + /* Okay for single symrec in LHS and constant on RHS. + * Single symrecs are not okay on RHS. + * If RHS is non-constant, don't allow single symrec on LHS. + * XXX: should rshift be an expr instead?? + */ + + /* Check for single sym on LHS */ + if (e->terms[0].type != YASM_EXPR_SYM) + break; + + /* If we already have a sym, we can't take another one */ + if (value->rel || ssym_not_ok) + return 1; + + /* RHS must be a positive integer */ + if (e->terms[1].type != YASM_EXPR_INT) + return 1; /* can't shift sym by non-constant integer */ + shamt = yasm_intnum_get_uint(e->terms[1].data.intn); + if ((shamt + value->rshift) > YASM_VALUE_RSHIFT_MAX) + return 1; /* total shift would be too large */ + + /* Update value */ + value->rshift += shamt; + value->rel = e->terms[0].data.sym; + + /* Replace symbol with 0 */ + e->terms[0].type = YASM_EXPR_INT; + e->terms[0].data.intn = yasm_intnum_create_uint(0); + + /* Just leave SHR in place */ + break; + case YASM_EXPR_SEG: + /* Okay for single symrec (can only be done once). + * Not okay for anything BUT a single symrec as an immediate + * child. + */ + if (e->terms[0].type != YASM_EXPR_SYM) + return 1; + + if (value->seg_of) + return 1; /* multiple SEG not legal */ + value->seg_of = 1; + + if (value->rel || ssym_not_ok) + return 1; /* got a relative portion somewhere else? */ + value->rel = e->terms[0].data.sym; + + /* replace with ident'ed 0 */ + e->op = YASM_EXPR_IDENT; + e->terms[0].type = YASM_EXPR_INT; + e->terms[0].data.intn = yasm_intnum_create_uint(0); + break; + case YASM_EXPR_WRT: + /* Okay for single symrec in LHS and either a register or single + * symrec (as an immediate child) on RHS. + * If a single symrec on RHS, can only be done once. + * WRT reg is left in expr for arch to look at. + */ + + /* Handle RHS */ + switch (e->terms[1].type) { + case YASM_EXPR_SYM: + if (value->wrt) + return 1; + value->wrt = e->terms[1].data.sym; + /* and drop the WRT portion */ + e->op = YASM_EXPR_IDENT; + e->numterms = 1; + break; + case YASM_EXPR_REG: + break; /* ignore */ + default: + return 1; + } + + /* Handle LHS */ + switch (e->terms[0].type) { + case YASM_EXPR_SYM: + if (value->rel || ssym_not_ok) + return 1; + value->rel = e->terms[0].data.sym; + /* and replace with 0 */ + e->terms[0].type = YASM_EXPR_INT; + e->terms[0].data.intn = yasm_intnum_create_uint(0); + break; + case YASM_EXPR_EXPR: + /* recurse */ + return value_finalize_scan(value, e->terms[0].data.expn, + expr_precbc, ssym_not_ok); + default: + break; /* ignore */ + } + + break; + default: + /* Single symrec not allowed anywhere */ + for (i=0; i<e->numterms; i++) { + switch (e->terms[i].type) { + case YASM_EXPR_SYM: + return 1; + case YASM_EXPR_EXPR: + /* recurse */ + return value_finalize_scan(value, + e->terms[i].data.expn, + expr_precbc, 1); + default: + break; + } + } + break; + } + + return 0; +} + +int +yasm_value_finalize_expr(yasm_value *value, yasm_expr *e, + yasm_bytecode *precbc, unsigned int size) +{ + if (!e) { + yasm_value_initialize(value, NULL, size); + return 0; + } + yasm_value_initialize(value, e, size); + return yasm_value_finalize(value, precbc); +} + +int +yasm_value_finalize(yasm_value *value, yasm_bytecode *precbc) +{ + if (!value->abs) + return 0; + + value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, 0, NULL, NULL); + + /* quit early if there was an issue in simplify() */ + if (yasm_error_occurred()) + return 1; + + /* Strip top-level AND masking to an all-1s mask the same size + * of the value size. This allows forced avoidance of overflow warnings. + */ + if (value->abs->op == YASM_EXPR_AND) { + int term; + + /* Calculate 1<<size - 1 value */ + yasm_intnum *mask = yasm_intnum_create_uint(1); + yasm_intnum *mask_tmp = yasm_intnum_create_uint(value->size); + yasm_intnum_calc(mask, YASM_EXPR_SHL, mask_tmp); + yasm_intnum_set_uint(mask_tmp, 1); + yasm_intnum_calc(mask, YASM_EXPR_SUB, mask_tmp); + yasm_intnum_destroy(mask_tmp); + + /* Walk terms and delete matching masks */ + for (term=value->abs->numterms-1; term>=0; term--) { + if (value->abs->terms[term].type == YASM_EXPR_INT && + yasm_intnum_compare(value->abs->terms[term].data.intn, + mask) == 0) { + /* Delete the intnum */ + yasm_intnum_destroy(value->abs->terms[term].data.intn); + + /* Slide everything to its right over by 1 */ + if (term != value->abs->numterms-1) /* if it wasn't last.. */ + memmove(&value->abs->terms[term], + &value->abs->terms[term+1], + (value->abs->numterms-1-term)* + sizeof(yasm_expr__item)); + + /* Update numterms */ + value->abs->numterms--; + + /* Indicate warnings have been disabled */ + value->no_warn = 1; + } + } + if (value->abs->numterms == 1) + value->abs->op = YASM_EXPR_IDENT; + yasm_intnum_destroy(mask); + } + + /* Handle trivial (IDENT) cases immediately */ + if (value->abs->op == YASM_EXPR_IDENT) { + switch (value->abs->terms[0].type) { + case YASM_EXPR_INT: + if (yasm_intnum_is_zero(value->abs->terms[0].data.intn)) { + yasm_expr_destroy(value->abs); + value->abs = NULL; + } + return 0; + case YASM_EXPR_REG: + case YASM_EXPR_FLOAT: + return 0; + case YASM_EXPR_SYM: + value->rel = value->abs->terms[0].data.sym; + yasm_expr_destroy(value->abs); + value->abs = NULL; + return 0; + case YASM_EXPR_EXPR: + /* Bring up lower values. */ + while (value->abs->op == YASM_EXPR_IDENT + && value->abs->terms[0].type == YASM_EXPR_EXPR) { + yasm_expr *sube = value->abs->terms[0].data.expn; + yasm_xfree(value->abs); + value->abs = sube; + } + break; + default: + yasm_internal_error(N_("unexpected expr term type")); + } + } + + if (value_finalize_scan(value, value->abs, precbc, 0)) + return 1; + + value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, 0, NULL, NULL); + + /* Simplify 0 in abs to NULL */ + if (value->abs->op == YASM_EXPR_IDENT + && value->abs->terms[0].type == YASM_EXPR_INT + && yasm_intnum_is_zero(value->abs->terms[0].data.intn)) { + yasm_expr_destroy(value->abs); + value->abs = NULL; + } + return 0; +} + +yasm_intnum * +yasm_value_get_intnum(yasm_value *value, yasm_bytecode *bc, int calc_bc_dist) +{ + /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; + /*@only@*/ yasm_intnum *outval; + int sym_local; + + if (value->abs) { + /* Handle integer expressions, if non-integer or too complex, return + * NULL. + */ + intn = yasm_expr_get_intnum(&value->abs, calc_bc_dist); + if (!intn) + return NULL; + } + + if (value->rel) { + /* If relative portion is not in bc section, return NULL. + * Otherwise get the relative portion's offset. + */ + /*@dependent@*/ yasm_bytecode *rel_prevbc; + unsigned long dist; + + if (!bc) + return NULL; /* Can't calculate relative value */ + + sym_local = yasm_symrec_get_label(value->rel, &rel_prevbc); + if (value->wrt || value->seg_of || value->section_rel || !sym_local) + return NULL; /* we can't handle SEG, WRT, or external symbols */ + if (rel_prevbc->section != bc->section) + return NULL; /* not in this section */ + if (!value->curpos_rel) + return NULL; /* not PC-relative */ + + /* Calculate value relative to current assembly position */ + dist = yasm_bc_next_offset(rel_prevbc); + if (dist < bc->offset) { + outval = yasm_intnum_create_uint(bc->offset - dist); + yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL); + } else { + dist -= bc->offset; + outval = yasm_intnum_create_uint(dist); + } + + if (value->rshift > 0) { + /*@only@*/ yasm_intnum *shamt = + yasm_intnum_create_uint((unsigned long)value->rshift); + yasm_intnum_calc(outval, YASM_EXPR_SHR, shamt); + yasm_intnum_destroy(shamt); + } + /* Add in absolute portion */ + if (intn) + yasm_intnum_calc(outval, YASM_EXPR_ADD, intn); + return outval; + } + + if (intn) + return yasm_intnum_copy(intn); + + /* No absolute or relative portions: output 0 */ + return yasm_intnum_create_uint(0); +} + +int +yasm_value_output_basic(yasm_value *value, /*@out@*/ unsigned char *buf, + size_t destsize, yasm_bytecode *bc, int warn, + yasm_arch *arch) +{ + /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; + /*@only@*/ yasm_intnum *outval; + int sym_local; + int retval = 1; + unsigned int valsize = value->size; + + if (value->no_warn) + warn = 0; + + if (value->abs) { + /* Handle floating point expressions */ + if (!value->rel && value->abs->op == YASM_EXPR_IDENT + && value->abs->terms[0].type == YASM_EXPR_FLOAT) { + if (yasm_arch_floatnum_tobytes(arch, value->abs->terms[0].data.flt, + buf, destsize, valsize, 0, warn)) + return -1; + else + return 1; + } + + /* Check for complex float expressions */ + if (yasm_expr__contains(value->abs, YASM_EXPR_FLOAT)) { + yasm_error_set(YASM_ERROR_FLOATING_POINT, + N_("floating point expression too complex")); + return -1; + } + + /* Handle normal integer expressions */ + intn = yasm_expr_get_intnum(&value->abs, 1); + + if (!intn) { + /* Second try before erroring: yasm_expr_get_intnum doesn't handle + * SEG:OFF, so try simplifying out any to just the OFF portion, + * then getting the intnum again. + */ + yasm_expr *seg = yasm_expr_extract_deep_segoff(&value->abs); + if (seg) + yasm_expr_destroy(seg); + intn = yasm_expr_get_intnum(&value->abs, 1); + } + + if (!intn) { + /* Still don't have an integer! */ + yasm_error_set(YASM_ERROR_TOO_COMPLEX, + N_("expression too complex")); + return -1; + } + } + + /* Adjust warn for signed/unsigned integer warnings */ + if (warn != 0) + warn = value->sign ? -1 : 1; + + if (value->rel) { + /* If relative portion is not in bc section, don't try to handle it + * here. Otherwise get the relative portion's offset. + */ + /*@dependent@*/ yasm_bytecode *rel_prevbc; + unsigned long dist; + + sym_local = yasm_symrec_get_label(value->rel, &rel_prevbc); + if (value->wrt || value->seg_of || value->section_rel || !sym_local) + return 0; /* we can't handle SEG, WRT, or external symbols */ + if (rel_prevbc->section != bc->section) + return 0; /* not in this section */ + if (!value->curpos_rel) + return 0; /* not PC-relative */ + + /* Calculate value relative to current assembly position */ + dist = yasm_bc_next_offset(rel_prevbc); + if (dist < bc->offset) { + outval = yasm_intnum_create_uint(bc->offset - dist); + yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL); + } else { + dist -= bc->offset; + outval = yasm_intnum_create_uint(dist); + } + + if (value->rshift > 0) { + /*@only@*/ yasm_intnum *shamt = + yasm_intnum_create_uint((unsigned long)value->rshift); + yasm_intnum_calc(outval, YASM_EXPR_SHR, shamt); + yasm_intnum_destroy(shamt); + } + /* Add in absolute portion */ + if (intn) + yasm_intnum_calc(outval, YASM_EXPR_ADD, intn); + /* Output! */ + if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, 0, + bc, warn)) + retval = -1; + yasm_intnum_destroy(outval); + return retval; + } + + if (value->seg_of || value->rshift || value->curpos_rel || value->ip_rel + || value->section_rel) + return 0; /* We can't handle this with just an absolute */ + + if (intn) { + /* Output just absolute portion */ + if (yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, 0, bc, + warn)) + retval = -1; + } else { + /* No absolute or relative portions: output 0 */ + outval = yasm_intnum_create_uint(0); + if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, 0, + bc, warn)) + retval = -1; + yasm_intnum_destroy(outval); + } + return retval; +} + +void +yasm_value_print(const yasm_value *value, FILE *f, int indent_level) +{ + fprintf(f, "%*s%u-bit, %ssigned", indent_level, "", value->size, + value->sign ? "" : "un"); + fprintf(f, "%*sAbsolute portion=", indent_level, ""); + yasm_expr_print(value->abs, f); + fprintf(f, "\n"); + if (value->rel) { + fprintf(f, "%*sRelative to=%s%s\n", indent_level, "", + value->seg_of ? "SEG " : "", + yasm_symrec_get_name(value->rel)); + if (value->wrt) + fprintf(f, "%*s(With respect to=%s)\n", indent_level, "", + yasm_symrec_get_name(value->wrt)); + if (value->rshift > 0) + fprintf(f, "%*s(Right shifted by=%u)\n", indent_level, "", + value->rshift); + if (value->curpos_rel) + fprintf(f, "%*s(Relative to current position)\n", indent_level, + ""); + if (value->ip_rel) + fprintf(f, "%*s(IP-relative)\n", indent_level, ""); + if (value->jump_target) + fprintf(f, "%*s(Jump target)\n", indent_level, ""); + if (value->section_rel) + fprintf(f, "%*s(Section-relative)\n", indent_level, ""); + if (value->no_warn) + fprintf(f, "%*s(Overflow warnings disabled)\n", indent_level, ""); + } +} diff --git a/libyasm/value.h b/libyasm/value.h new file mode 100644 index 0000000..4dc294b --- /dev/null +++ b/libyasm/value.h @@ -0,0 +1,172 @@ +/** + * \file libyasm/value.h + * \brief YASM value interface. + * + * \license + * Copyright (C) 2006-2007 Peter Johnson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 OTHER 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 OTHER 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. + * \endlicense + */ +#ifndef YASM_VALUE_H +#define YASM_VALUE_H + +#ifndef YASM_LIB_DECL +#define YASM_LIB_DECL +#endif + +/** Initialize a #yasm_value with just an expression. No processing is + * performed, the expression is simply stuck into value.abs and the other + * fields are initialized. Use yasm_expr_extract_value() to perform "smart" + * processing into a #yasm_value. This function is intended for use during + * parsing simply to ensure all fields of the value are initialized; after + * the parse is complete, yasm_value_extract() should be called to finalize + * the value. The value defaults to unsigned. + * \param value value to be initialized + * \param e expression (kept) + * \param size value size (in bits) + */ +YASM_LIB_DECL +void yasm_value_initialize(/*@out@*/ yasm_value *value, + /*@null@*/ /*@kept@*/ yasm_expr *e, + unsigned int size); + +/** Initialize a #yasm_value with just a symrec. No processing is performed, + * the symrec is simply stuck into value.rel and the other fields are + * initialized. + * \param value value to be initialized + * \param sym symrec + * \param size value size (in bits) + */ +YASM_LIB_DECL +void yasm_value_init_sym(/*@out@*/ yasm_value *value, + /*@null@*/ yasm_symrec *sym, unsigned int size); + +/** Initialize a #yasm_value as a copy of another yasm_value. Any expressions + * within orig are copied, so it's safe to delete the copy. + * \param value value (copy to create) + * \param orig original value + */ +YASM_LIB_DECL +void yasm_value_init_copy(yasm_value *value, const yasm_value *orig); + +/** Frees any memory inside value; does not free value itself. + * \param value value + */ +YASM_LIB_DECL +void yasm_value_delete(yasm_value *value); + +/** Set a value to be relative to the current assembly position rather than + * relative to the section start. + * \param value value + * \param bc bytecode containing value + * \param ip_rel if nonzero, indicates IP-relative data relocation, + * sometimes used to generate special relocations + * \note If value is just an absolute value, will get an absolute symrec to + * reference to (via bc's symbol table). + */ +YASM_LIB_DECL +void yasm_value_set_curpos_rel(yasm_value *value, yasm_bytecode *bc, + unsigned int ip_rel); + +/** Perform yasm_value_finalize_expr() on a value that already exists from + * being initialized with yasm_value_initialize(). + * \param value value + * \param precbc previous bytecode to bytecode containing value + * \return Nonzero if value could not be split. + */ +YASM_LIB_DECL +int yasm_value_finalize(yasm_value *value, /*@null@*/ yasm_bytecode *precbc); + +/** Break a #yasm_expr into a #yasm_value constituent parts. Extracts + * the relative portion of the value, SEG and WRT portions, and top-level + * right shift, if any. Places the remaining expr into the absolute + * portion of the value. Essentially a combination of yasm_value_initialize() + * and yasm_value_finalize(). First expands references to symrecs in + * absolute sections by expanding with the absolute section start plus the + * symrec offset within the absolute section. + * \param value value to store split portions into + * \param e expression input + * \param precbc previous bytecode to bytecode containing expression + * \param size value size (in bits) + * \return Nonzero if the expr could not be split into a value for some + * reason (e.g. the relative portion was not added, but multiplied, + * etc). + * \warning Do not use e after this call. Even if an error is returned, e + * is stored into value. + * \note This should only be called after the parse is complete. Calling + * before the parse is complete will usually result in an error return. + */ +YASM_LIB_DECL +int yasm_value_finalize_expr(/*@out@*/ yasm_value *value, + /*@null@*/ /*@kept@*/ yasm_expr *e, + /*@null@*/ yasm_bytecode *precbc, + unsigned int size); + +/** Get value if absolute or PC-relative section-local relative. Returns NULL + * otherwise. + * \param value value + * \param bc current bytecode (for PC-relative calculation); if + * NULL, NULL is returned for PC-relative values. + * \param calc_bc_dist if nonzero, calculates bytecode distances in absolute + * portion of value + * \note Adds in value.rel (correctly) if PC-relative and in the same section + * as bc (and there is no WRT or SEG). + * \return Intnum if can be resolved to integer value, otherwise NULL. + */ +YASM_LIB_DECL +/*@null@*/ /*@only@*/ yasm_intnum *yasm_value_get_intnum + (yasm_value *value, /*@null@*/ yasm_bytecode *bc, int calc_bc_dist); + +/** Output value if constant or PC-relative section-local. This should be + * used from objfmt yasm_output_value_func() functions. + * functions. + * \param value value + * \param buf buffer for byte representation + * \param destsize destination size (in bytes) + * \param bc current bytecode (usually passed into higher-level + * calling function) + * \param warn enables standard warnings: zero for none; + * nonzero for overflow/underflow floating point and + * integer warnings + * \param arch architecture + * \note Adds in value.rel (correctly) if PC-relative and in the same section + * as bc (and there is no WRT or SEG); if this is not the desired + * behavior, e.g. a reloc is needed in this case, don't use this + * function! + * \return 0 if no value output due to value needing relocation; + * 1 if value output; -1 if error. + */ +YASM_LIB_DECL +int yasm_value_output_basic + (yasm_value *value, /*@out@*/ unsigned char *buf, size_t destsize, + yasm_bytecode *bc, int warn, yasm_arch *arch); + +/** Print a value. For debugging purposes. + * \param value value + * \param indent_level indentation level + * \param f file + */ +YASM_LIB_DECL +void yasm_value_print(const yasm_value *value, FILE *f, int indent_level); + +#endif diff --git a/libyasm/xmalloc.c b/libyasm/xmalloc.c new file mode 100644 index 0000000..81b608c --- /dev/null +++ b/libyasm/xmalloc.c @@ -0,0 +1,114 @@ +/* + * Memory allocation routines with error checking. Idea from GNU libiberty. + * + * Copyright (C) 2001-2007 Peter Johnson + * + * 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 OTHER 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 OTHER CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "util.h" + +#include "coretype.h" +#include "errwarn.h" + + +#ifdef WITH_DMALLOC +#undef yasm_xmalloc +#undef yasm_xcalloc +#undef yasm_xrealloc +#undef yasm_xfree +#endif + +static /*@only@*/ /*@out@*/ void *def_xmalloc(size_t size); +static /*@only@*/ void *def_xcalloc(size_t nelem, size_t elsize); +static /*@only@*/ void *def_xrealloc + (/*@only@*/ /*@out@*/ /*@returned@*/ /*@null@*/ void *oldmem, size_t size) + /*@modifies oldmem@*/; +static void def_xfree(/*@only@*/ /*@out@*/ /*@null@*/ void *p) + /*@modifies p@*/; + +/* storage for global function pointers */ +YASM_LIB_DECL +/*@only@*/ /*@out@*/ void * (*yasm_xmalloc) (size_t size) = def_xmalloc; +YASM_LIB_DECL +/*@only@*/ void * (*yasm_xcalloc) (size_t nelem, size_t elsize) = def_xcalloc; +YASM_LIB_DECL +/*@only@*/ void * (*yasm_xrealloc) + (/*@only@*/ /*@out@*/ /*@returned@*/ /*@null@*/ void *oldmem, size_t size) + /*@modifies oldmem@*/ = def_xrealloc; +YASM_LIB_DECL +void (*yasm_xfree) (/*@only@*/ /*@out@*/ /*@null@*/ void *p) + /*@modifies p@*/ = def_xfree; + + +static void * +def_xmalloc(size_t size) +{ + void *newmem; + + if (size == 0) + size = 1; + newmem = malloc(size); + if (!newmem) + yasm__fatal(N_("out of memory")); + + return newmem; +} + +static void * +def_xcalloc(size_t nelem, size_t elsize) +{ + void *newmem; + + if (nelem == 0 || elsize == 0) + nelem = elsize = 1; + + newmem = calloc(nelem, elsize); + if (!newmem) + yasm__fatal(N_("out of memory")); + + return newmem; +} + +static void * +def_xrealloc(void *oldmem, size_t size) +{ + void *newmem; + + if (size == 0) + size = 1; + if (!oldmem) + newmem = malloc(size); + else + newmem = realloc(oldmem, size); + if (!newmem) + yasm__fatal(N_("out of memory")); + + return newmem; +} + +static void +def_xfree(void *p) +{ + if (!p) + return; + free(p); +} diff --git a/libyasm/xstrdup.c b/libyasm/xstrdup.c new file mode 100644 index 0000000..b187704 --- /dev/null +++ b/libyasm/xstrdup.c @@ -0,0 +1,68 @@ +/* + * strdup() implementation with error checking (using xmalloc). + * + * Copyright (c) 1988, 1993 + * The Regents of the University of California. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "util.h" + +#include "coretype.h" + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + + +#ifdef WITH_DMALLOC +#undef yasm__xstrdup +#endif + +char * +yasm__xstrdup(const char *str) +{ + size_t len; + char *copy; + + len = strlen(str) + 1; + copy = yasm_xmalloc(len); + memcpy(copy, str, len); + return (copy); +} + +char * +yasm__xstrndup(const char *str, size_t max) +{ + size_t len = 0; + char *copy; + + while (len < max && str[len] != '\0') + len++; + copy = yasm_xmalloc(len+1); + memcpy(copy, str, len); + copy[len] = '\0'; + return (copy); +} |