mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-07 16:06:09 +00:00
21827 lines
690 KiB
C
21827 lines
690 KiB
C
/*
|
||
* TCC - Tiny C Compiler
|
||
*
|
||
* Copyright (c) 2001-2004 Fabrice Bellard
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser 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
|
||
*/
|
||
#define _GNU_SOURCE
|
||
|
||
// njn: inlined config.h
|
||
//#include "config.h"
|
||
//---------------------------------------------------------------------------
|
||
/* Automatically generated by configure - do not modify */
|
||
#define CONFIG_TCCDIR "tinycc-extras"
|
||
#define GCC_MAJOR 3
|
||
#define HOST_I386 1
|
||
#define TCC_VERSION "0.9.23"
|
||
//---------------------------------------------------------------------------
|
||
|
||
// njn: comment out CONFIG_TCCBOOT branch
|
||
//#ifdef CONFIG_TCCBOOT
|
||
//
|
||
//#include "tccboot.h"
|
||
//#define CONFIG_TCC_STATIC
|
||
//
|
||
//#else
|
||
|
||
#include <assert.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <stdarg.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <math.h>
|
||
#include <unistd.h>
|
||
#include <signal.h>
|
||
#include <fcntl.h>
|
||
#include <setjmp.h>
|
||
#include <time.h>
|
||
#ifdef WIN32
|
||
#include <sys/timeb.h>
|
||
#endif
|
||
#ifndef WIN32
|
||
#include <sys/time.h>
|
||
#include <sys/ucontext.h>
|
||
#endif
|
||
|
||
//#endif /* !CONFIG_TCCBOOT */
|
||
|
||
// Dummy variables used to avoid warnings like these:
|
||
// warning: ignoring return value of ‘fwrite’, declared with attribute
|
||
// warn_unused_result
|
||
char* dummy_char_star;
|
||
size_t dummy_size_t;
|
||
|
||
// njn: inlined elf.h
|
||
//#include "elf.h"
|
||
//---------------------------------------------------------------------------
|
||
/* This file defines standard ELF types, structures, and macros.
|
||
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
|
||
This file is part of the GNU C Library.
|
||
Contributed by Ian Lance Taylor <ian@cygnus.com>.
|
||
|
||
The GNU C 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.
|
||
|
||
The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not,
|
||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
Boston, MA 02111-1307, USA. */
|
||
|
||
#ifndef _ELF_H
|
||
#define _ELF_H 1
|
||
|
||
#ifndef WIN32
|
||
#include <inttypes.h>
|
||
#else
|
||
#ifndef __int8_t_defined
|
||
#define __int8_t_defined
|
||
typedef signed char int8_t;
|
||
typedef short int int16_t;
|
||
typedef int int32_t;
|
||
typedef long long int int64_t;
|
||
#endif
|
||
|
||
typedef unsigned char uint8_t;
|
||
typedef unsigned short int uint16_t;
|
||
typedef unsigned int uint32_t;
|
||
typedef unsigned long long int uint64_t;
|
||
#endif
|
||
|
||
/* Standard ELF types. */
|
||
|
||
/* Type for a 16-bit quantity. */
|
||
typedef uint16_t Elf32_Half;
|
||
typedef uint16_t Elf64_Half;
|
||
|
||
/* Types for signed and unsigned 32-bit quantities. */
|
||
typedef uint32_t Elf32_Word;
|
||
typedef int32_t Elf32_Sword;
|
||
typedef uint32_t Elf64_Word;
|
||
typedef int32_t Elf64_Sword;
|
||
|
||
/* Types for signed and unsigned 64-bit quantities. */
|
||
typedef uint64_t Elf32_Xword;
|
||
typedef int64_t Elf32_Sxword;
|
||
typedef uint64_t Elf64_Xword;
|
||
typedef int64_t Elf64_Sxword;
|
||
|
||
/* Type of addresses. */
|
||
typedef uint32_t Elf32_Addr;
|
||
typedef uint64_t Elf64_Addr;
|
||
|
||
/* Type of file offsets. */
|
||
typedef uint32_t Elf32_Off;
|
||
typedef uint64_t Elf64_Off;
|
||
|
||
/* Type for section indices, which are 16-bit quantities. */
|
||
typedef uint16_t Elf32_Section;
|
||
typedef uint16_t Elf64_Section;
|
||
|
||
/* Type of symbol indices. */
|
||
typedef uint32_t Elf32_Symndx;
|
||
typedef uint64_t Elf64_Symndx;
|
||
|
||
|
||
/* The ELF file header. This appears at the start of every ELF file. */
|
||
|
||
#define EI_NIDENT (16)
|
||
|
||
typedef struct
|
||
{
|
||
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
|
||
Elf32_Half e_type; /* Object file type */
|
||
Elf32_Half e_machine; /* Architecture */
|
||
Elf32_Word e_version; /* Object file version */
|
||
Elf32_Addr e_entry; /* Entry point virtual address */
|
||
Elf32_Off e_phoff; /* Program header table file offset */
|
||
Elf32_Off e_shoff; /* Section header table file offset */
|
||
Elf32_Word e_flags; /* Processor-specific flags */
|
||
Elf32_Half e_ehsize; /* ELF header size in bytes */
|
||
Elf32_Half e_phentsize; /* Program header table entry size */
|
||
Elf32_Half e_phnum; /* Program header table entry count */
|
||
Elf32_Half e_shentsize; /* Section header table entry size */
|
||
Elf32_Half e_shnum; /* Section header table entry count */
|
||
Elf32_Half e_shstrndx; /* Section header string table index */
|
||
} Elf32_Ehdr;
|
||
|
||
typedef struct
|
||
{
|
||
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
|
||
Elf64_Half e_type; /* Object file type */
|
||
Elf64_Half e_machine; /* Architecture */
|
||
Elf64_Word e_version; /* Object file version */
|
||
Elf64_Addr e_entry; /* Entry point virtual address */
|
||
Elf64_Off e_phoff; /* Program header table file offset */
|
||
Elf64_Off e_shoff; /* Section header table file offset */
|
||
Elf64_Word e_flags; /* Processor-specific flags */
|
||
Elf64_Half e_ehsize; /* ELF header size in bytes */
|
||
Elf64_Half e_phentsize; /* Program header table entry size */
|
||
Elf64_Half e_phnum; /* Program header table entry count */
|
||
Elf64_Half e_shentsize; /* Section header table entry size */
|
||
Elf64_Half e_shnum; /* Section header table entry count */
|
||
Elf64_Half e_shstrndx; /* Section header string table index */
|
||
} Elf64_Ehdr;
|
||
|
||
/* Fields in the e_ident array. The EI_* macros are indices into the
|
||
array. The macros under each EI_* macro are the values the byte
|
||
may have. */
|
||
|
||
#define EI_MAG0 0 /* File identification byte 0 index */
|
||
#define ELFMAG0 0x7f /* Magic number byte 0 */
|
||
|
||
#define EI_MAG1 1 /* File identification byte 1 index */
|
||
#define ELFMAG1 'E' /* Magic number byte 1 */
|
||
|
||
#define EI_MAG2 2 /* File identification byte 2 index */
|
||
#define ELFMAG2 'L' /* Magic number byte 2 */
|
||
|
||
#define EI_MAG3 3 /* File identification byte 3 index */
|
||
#define ELFMAG3 'F' /* Magic number byte 3 */
|
||
|
||
/* Conglomeration of the identification bytes, for easy testing as a word. */
|
||
#define ELFMAG "\177ELF"
|
||
#define SELFMAG 4
|
||
|
||
#define EI_CLASS 4 /* File class byte index */
|
||
#define ELFCLASSNONE 0 /* Invalid class */
|
||
#define ELFCLASS32 1 /* 32-bit objects */
|
||
#define ELFCLASS64 2 /* 64-bit objects */
|
||
#define ELFCLASSNUM 3
|
||
|
||
#define EI_DATA 5 /* Data encoding byte index */
|
||
#define ELFDATANONE 0 /* Invalid data encoding */
|
||
#define ELFDATA2LSB 1 /* 2's complement, little endian */
|
||
#define ELFDATA2MSB 2 /* 2's complement, big endian */
|
||
#define ELFDATANUM 3
|
||
|
||
#define EI_VERSION 6 /* File version byte index */
|
||
/* Value must be EV_CURRENT */
|
||
|
||
#define EI_OSABI 7 /* OS ABI identification */
|
||
#define ELFOSABI_SYSV 0 /* UNIX System V ABI */
|
||
#define ELFOSABI_HPUX 1 /* HP-UX */
|
||
#define ELFOSABI_FREEBSD 9 /* Free BSD */
|
||
#define ELFOSABI_ARM 97 /* ARM */
|
||
#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
|
||
|
||
#define EI_ABIVERSION 8 /* ABI version */
|
||
|
||
#define EI_PAD 9 /* Byte index of padding bytes */
|
||
|
||
/* Legal values for e_type (object file type). */
|
||
|
||
#define ET_NONE 0 /* No file type */
|
||
#define ET_REL 1 /* Relocatable file */
|
||
#define ET_EXEC 2 /* Executable file */
|
||
#define ET_DYN 3 /* Shared object file */
|
||
#define ET_CORE 4 /* Core file */
|
||
#define ET_NUM 5 /* Number of defined types */
|
||
#define ET_LOPROC 0xff00 /* Processor-specific */
|
||
#define ET_HIPROC 0xffff /* Processor-specific */
|
||
|
||
/* Legal values for e_machine (architecture). */
|
||
|
||
#define EM_NONE 0 /* No machine */
|
||
#define EM_M32 1 /* AT&T WE 32100 */
|
||
#define EM_SPARC 2 /* SUN SPARC */
|
||
#define EM_386 3 /* Intel 80386 */
|
||
#define EM_68K 4 /* Motorola m68k family */
|
||
#define EM_88K 5 /* Motorola m88k family */
|
||
#define EM_486 6 /* Intel 80486 */
|
||
#define EM_860 7 /* Intel 80860 */
|
||
#define EM_MIPS 8 /* MIPS R3000 big-endian */
|
||
#define EM_S370 9 /* Amdahl */
|
||
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
|
||
#define EM_RS6000 11 /* RS6000 */
|
||
|
||
#define EM_PARISC 15 /* HPPA */
|
||
#define EM_nCUBE 16 /* nCUBE */
|
||
#define EM_VPP500 17 /* Fujitsu VPP500 */
|
||
#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
|
||
#define EM_960 19 /* Intel 80960 */
|
||
#define EM_PPC 20 /* PowerPC */
|
||
|
||
#define EM_V800 36 /* NEC V800 series */
|
||
#define EM_FR20 37 /* Fujitsu FR20 */
|
||
#define EM_RH32 38 /* TRW RH32 */
|
||
#define EM_MMA 39 /* Fujitsu MMA */
|
||
#define EM_ARM 40 /* ARM */
|
||
#define EM_FAKE_ALPHA 41 /* Digital Alpha */
|
||
#define EM_SH 42 /* Hitachi SH */
|
||
#define EM_SPARCV9 43 /* SPARC v9 64-bit */
|
||
#define EM_TRICORE 44 /* Siemens Tricore */
|
||
#define EM_ARC 45 /* Argonaut RISC Core */
|
||
#define EM_H8_300 46 /* Hitachi H8/300 */
|
||
#define EM_H8_300H 47 /* Hitachi H8/300H */
|
||
#define EM_H8S 48 /* Hitachi H8S */
|
||
#define EM_H8_500 49 /* Hitachi H8/500 */
|
||
#define EM_IA_64 50 /* Intel Merced */
|
||
#define EM_MIPS_X 51 /* Stanford MIPS-X */
|
||
#define EM_COLDFIRE 52 /* Motorola Coldfire */
|
||
#define EM_68HC12 53 /* Motorola M68HC12 */
|
||
#define EM_NUM 54
|
||
|
||
/* If it is necessary to assign new unofficial EM_* values, please
|
||
pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
|
||
chances of collision with official or non-GNU unofficial values. */
|
||
|
||
#define EM_ALPHA 0x9026
|
||
#define EM_C60 0x9c60
|
||
|
||
/* Legal values for e_version (version). */
|
||
|
||
#define EV_NONE 0 /* Invalid ELF version */
|
||
#define EV_CURRENT 1 /* Current version */
|
||
#define EV_NUM 2
|
||
|
||
/* Section header. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word sh_name; /* Section name (string tbl index) */
|
||
Elf32_Word sh_type; /* Section type */
|
||
Elf32_Word sh_flags; /* Section flags */
|
||
Elf32_Addr sh_addr; /* Section virtual addr at execution */
|
||
Elf32_Off sh_offset; /* Section file offset */
|
||
Elf32_Word sh_size; /* Section size in bytes */
|
||
Elf32_Word sh_link; /* Link to another section */
|
||
Elf32_Word sh_info; /* Additional section information */
|
||
Elf32_Word sh_addralign; /* Section alignment */
|
||
Elf32_Word sh_entsize; /* Entry size if section holds table */
|
||
} Elf32_Shdr;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word sh_name; /* Section name (string tbl index) */
|
||
Elf64_Word sh_type; /* Section type */
|
||
Elf64_Xword sh_flags; /* Section flags */
|
||
Elf64_Addr sh_addr; /* Section virtual addr at execution */
|
||
Elf64_Off sh_offset; /* Section file offset */
|
||
Elf64_Xword sh_size; /* Section size in bytes */
|
||
Elf64_Word sh_link; /* Link to another section */
|
||
Elf64_Word sh_info; /* Additional section information */
|
||
Elf64_Xword sh_addralign; /* Section alignment */
|
||
Elf64_Xword sh_entsize; /* Entry size if section holds table */
|
||
} Elf64_Shdr;
|
||
|
||
/* Special section indices. */
|
||
|
||
#define SHN_UNDEF 0 /* Undefined section */
|
||
#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
|
||
#define SHN_LOPROC 0xff00 /* Start of processor-specific */
|
||
#define SHN_HIPROC 0xff1f /* End of processor-specific */
|
||
#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
|
||
#define SHN_COMMON 0xfff2 /* Associated symbol is common */
|
||
#define SHN_HIRESERVE 0xffff /* End of reserved indices */
|
||
|
||
/* Legal values for sh_type (section type). */
|
||
|
||
#define SHT_NULL 0 /* Section header table entry unused */
|
||
#define SHT_PROGBITS 1 /* Program data */
|
||
#define SHT_SYMTAB 2 /* Symbol table */
|
||
#define SHT_STRTAB 3 /* String table */
|
||
#define SHT_RELA 4 /* Relocation entries with addends */
|
||
#define SHT_HASH 5 /* Symbol hash table */
|
||
#define SHT_DYNAMIC 6 /* Dynamic linking information */
|
||
#define SHT_NOTE 7 /* Notes */
|
||
#define SHT_NOBITS 8 /* Program space with no data (bss) */
|
||
#define SHT_REL 9 /* Relocation entries, no addends */
|
||
#define SHT_SHLIB 10 /* Reserved */
|
||
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
|
||
#define SHT_NUM 12 /* Number of defined types. */
|
||
#define SHT_LOOS 0x60000000 /* Start OS-specific */
|
||
#define SHT_LOSUNW 0x6ffffffb /* Sun-specific low bound. */
|
||
#define SHT_SUNW_COMDAT 0x6ffffffb
|
||
#define SHT_SUNW_syminfo 0x6ffffffc
|
||
#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
|
||
#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
|
||
#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
|
||
#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
|
||
#define SHT_HIOS 0x6fffffff /* End OS-specific type */
|
||
#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
|
||
#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
|
||
#define SHT_LOUSER 0x80000000 /* Start of application-specific */
|
||
#define SHT_HIUSER 0x8fffffff /* End of application-specific */
|
||
|
||
/* Legal values for sh_flags (section flags). */
|
||
|
||
#define SHF_WRITE (1 << 0) /* Writable */
|
||
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
|
||
#define SHF_EXECINSTR (1 << 2) /* Executable */
|
||
#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
|
||
|
||
/* Symbol table entry. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word st_name; /* Symbol name (string tbl index) */
|
||
Elf32_Addr st_value; /* Symbol value */
|
||
Elf32_Word st_size; /* Symbol size */
|
||
unsigned char st_info; /* Symbol type and binding */
|
||
unsigned char st_other; /* No defined meaning, 0 */
|
||
Elf32_Section st_shndx; /* Section index */
|
||
} Elf32_Sym;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word st_name; /* Symbol name (string tbl index) */
|
||
unsigned char st_info; /* Symbol type and binding */
|
||
unsigned char st_other; /* No defined meaning, 0 */
|
||
Elf64_Section st_shndx; /* Section index */
|
||
Elf64_Addr st_value; /* Symbol value */
|
||
Elf64_Xword st_size; /* Symbol size */
|
||
} Elf64_Sym;
|
||
|
||
/* The syminfo section if available contains additional information about
|
||
every dynamic symbol. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
|
||
Elf32_Half si_flags; /* Per symbol flags */
|
||
} Elf32_Syminfo;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
|
||
Elf64_Half si_flags; /* Per symbol flags */
|
||
} Elf64_Syminfo;
|
||
|
||
/* Possible values for si_boundto. */
|
||
#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
|
||
#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
|
||
#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */
|
||
|
||
/* Possible bitmasks for si_flags. */
|
||
#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
|
||
#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
|
||
#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
|
||
#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
|
||
loaded */
|
||
/* Syminfo version values. */
|
||
#define SYMINFO_NONE 0
|
||
#define SYMINFO_CURRENT 1
|
||
#define SYMINFO_NUM 2
|
||
|
||
|
||
/* Special section index. */
|
||
|
||
#define SHN_UNDEF 0 /* No section, undefined symbol. */
|
||
|
||
/* How to extract and insert information held in the st_info field. */
|
||
|
||
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
|
||
#define ELF32_ST_TYPE(val) ((val) & 0xf)
|
||
#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
|
||
|
||
/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
|
||
#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
|
||
#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
|
||
#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
|
||
|
||
/* Legal values for ST_BIND subfield of st_info (symbol binding). */
|
||
|
||
#define STB_LOCAL 0 /* Local symbol */
|
||
#define STB_GLOBAL 1 /* Global symbol */
|
||
#define STB_WEAK 2 /* Weak symbol */
|
||
#define STB_NUM 3 /* Number of defined types. */
|
||
#define STB_LOOS 10 /* Start of OS-specific */
|
||
#define STB_HIOS 12 /* End of OS-specific */
|
||
#define STB_LOPROC 13 /* Start of processor-specific */
|
||
#define STB_HIPROC 15 /* End of processor-specific */
|
||
|
||
/* Legal values for ST_TYPE subfield of st_info (symbol type). */
|
||
|
||
#define STT_NOTYPE 0 /* Symbol type is unspecified */
|
||
#define STT_OBJECT 1 /* Symbol is a data object */
|
||
#define STT_FUNC 2 /* Symbol is a code object */
|
||
#define STT_SECTION 3 /* Symbol associated with a section */
|
||
#define STT_FILE 4 /* Symbol's name is file name */
|
||
#define STT_NUM 5 /* Number of defined types. */
|
||
#define STT_LOOS 11 /* Start of OS-specific */
|
||
#define STT_HIOS 12 /* End of OS-specific */
|
||
#define STT_LOPROC 13 /* Start of processor-specific */
|
||
#define STT_HIPROC 15 /* End of processor-specific */
|
||
|
||
|
||
/* Symbol table indices are found in the hash buckets and chain table
|
||
of a symbol hash table section. This special index value indicates
|
||
the end of a chain, meaning no further symbols are found in that bucket. */
|
||
|
||
#define STN_UNDEF 0 /* End of a chain. */
|
||
|
||
|
||
/* How to extract and insert information held in the st_other field. */
|
||
|
||
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
|
||
|
||
/* For ELF64 the definitions are the same. */
|
||
#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
|
||
|
||
/* Symbol visibility specification encoded in the st_other field. */
|
||
#define STV_DEFAULT 0 /* Default symbol visibility rules */
|
||
#define STV_INTERNAL 1 /* Processor specific hidden class */
|
||
#define STV_HIDDEN 2 /* Sym unavailable in other modules */
|
||
#define STV_PROTECTED 3 /* Not preemptible, not exported */
|
||
|
||
|
||
/* Relocation table entry without addend (in section of type SHT_REL). */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Addr r_offset; /* Address */
|
||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||
} Elf32_Rel;
|
||
|
||
/* I have seen two different definitions of the Elf64_Rel and
|
||
Elf64_Rela structures, so we'll leave them out until Novell (or
|
||
whoever) gets their act together. */
|
||
/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Addr r_offset; /* Address */
|
||
Elf64_Xword r_info; /* Relocation type and symbol index */
|
||
} Elf64_Rel;
|
||
|
||
/* Relocation table entry with addend (in section of type SHT_RELA). */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Addr r_offset; /* Address */
|
||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||
Elf32_Sword r_addend; /* Addend */
|
||
} Elf32_Rela;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Addr r_offset; /* Address */
|
||
Elf64_Xword r_info; /* Relocation type and symbol index */
|
||
Elf64_Sxword r_addend; /* Addend */
|
||
} Elf64_Rela;
|
||
|
||
/* How to extract and insert information held in the r_info field. */
|
||
|
||
#define ELF32_R_SYM(val) ((val) >> 8)
|
||
#define ELF32_R_TYPE(val) ((val) & 0xff)
|
||
#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
|
||
|
||
#define ELF64_R_SYM(i) ((i) >> 32)
|
||
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
|
||
#define ELF64_R_INFO(sym,type) (((sym) << 32) + (type))
|
||
|
||
/* Program segment header. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word p_type; /* Segment type */
|
||
Elf32_Off p_offset; /* Segment file offset */
|
||
Elf32_Addr p_vaddr; /* Segment virtual address */
|
||
Elf32_Addr p_paddr; /* Segment physical address */
|
||
Elf32_Word p_filesz; /* Segment size in file */
|
||
Elf32_Word p_memsz; /* Segment size in memory */
|
||
Elf32_Word p_flags; /* Segment flags */
|
||
Elf32_Word p_align; /* Segment alignment */
|
||
} Elf32_Phdr;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word p_type; /* Segment type */
|
||
Elf64_Word p_flags; /* Segment flags */
|
||
Elf64_Off p_offset; /* Segment file offset */
|
||
Elf64_Addr p_vaddr; /* Segment virtual address */
|
||
Elf64_Addr p_paddr; /* Segment physical address */
|
||
Elf64_Xword p_filesz; /* Segment size in file */
|
||
Elf64_Xword p_memsz; /* Segment size in memory */
|
||
Elf64_Xword p_align; /* Segment alignment */
|
||
} Elf64_Phdr;
|
||
|
||
/* Legal values for p_type (segment type). */
|
||
|
||
#define PT_NULL 0 /* Program header table entry unused */
|
||
#define PT_LOAD 1 /* Loadable program segment */
|
||
#define PT_DYNAMIC 2 /* Dynamic linking information */
|
||
#define PT_INTERP 3 /* Program interpreter */
|
||
#define PT_NOTE 4 /* Auxiliary information */
|
||
#define PT_SHLIB 5 /* Reserved */
|
||
#define PT_PHDR 6 /* Entry for header table itself */
|
||
#define PT_NUM 7 /* Number of defined types. */
|
||
#define PT_LOOS 0x60000000 /* Start of OS-specific */
|
||
#define PT_HIOS 0x6fffffff /* End of OS-specific */
|
||
#define PT_LOPROC 0x70000000 /* Start of processor-specific */
|
||
#define PT_HIPROC 0x7fffffff /* End of processor-specific */
|
||
|
||
/* Legal values for p_flags (segment flags). */
|
||
|
||
#define PF_X (1 << 0) /* Segment is executable */
|
||
#define PF_W (1 << 1) /* Segment is writable */
|
||
#define PF_R (1 << 2) /* Segment is readable */
|
||
#define PF_MASKPROC 0xf0000000 /* Processor-specific */
|
||
|
||
/* Legal values for note segment descriptor types for core files. */
|
||
|
||
#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
|
||
#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
|
||
#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
|
||
#define NT_PRXREG 4 /* Contains copy of prxregset struct */
|
||
#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */
|
||
#define NT_AUXV 6 /* Contains copy of auxv array */
|
||
#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */
|
||
#define NT_PSTATUS 10 /* Contains copy of pstatus struct */
|
||
#define NT_PSINFO 13 /* Contains copy of psinfo struct */
|
||
#define NT_PRCRED 14 /* Contains copy of prcred struct */
|
||
#define NT_UTSNAME 15 /* Contains copy of utsname struct */
|
||
#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */
|
||
#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */
|
||
|
||
/* Legal values for the note segment descriptor types for object files. */
|
||
|
||
#define NT_VERSION 1 /* Contains a version string. */
|
||
|
||
|
||
/* Dynamic section entry. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Sword d_tag; /* Dynamic entry type */
|
||
union
|
||
{
|
||
Elf32_Word d_val; /* Integer value */
|
||
Elf32_Addr d_ptr; /* Address value */
|
||
} d_un;
|
||
} Elf32_Dyn;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Sxword d_tag; /* Dynamic entry type */
|
||
union
|
||
{
|
||
Elf64_Xword d_val; /* Integer value */
|
||
Elf64_Addr d_ptr; /* Address value */
|
||
} d_un;
|
||
} Elf64_Dyn;
|
||
|
||
/* Legal values for d_tag (dynamic entry type). */
|
||
|
||
#define DT_NULL 0 /* Marks end of dynamic section */
|
||
#define DT_NEEDED 1 /* Name of needed library */
|
||
#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
|
||
#define DT_PLTGOT 3 /* Processor defined value */
|
||
#define DT_HASH 4 /* Address of symbol hash table */
|
||
#define DT_STRTAB 5 /* Address of string table */
|
||
#define DT_SYMTAB 6 /* Address of symbol table */
|
||
#define DT_RELA 7 /* Address of Rela relocs */
|
||
#define DT_RELASZ 8 /* Total size of Rela relocs */
|
||
#define DT_RELAENT 9 /* Size of one Rela reloc */
|
||
#define DT_STRSZ 10 /* Size of string table */
|
||
#define DT_SYMENT 11 /* Size of one symbol table entry */
|
||
#define DT_INIT 12 /* Address of init function */
|
||
#define DT_FINI 13 /* Address of termination function */
|
||
#define DT_SONAME 14 /* Name of shared object */
|
||
#define DT_RPATH 15 /* Library search path */
|
||
#define DT_SYMBOLIC 16 /* Start symbol search here */
|
||
#define DT_REL 17 /* Address of Rel relocs */
|
||
#define DT_RELSZ 18 /* Total size of Rel relocs */
|
||
#define DT_RELENT 19 /* Size of one Rel reloc */
|
||
#define DT_PLTREL 20 /* Type of reloc in PLT */
|
||
#define DT_DEBUG 21 /* For debugging; unspecified */
|
||
#define DT_TEXTREL 22 /* Reloc might modify .text */
|
||
#define DT_JMPREL 23 /* Address of PLT relocs */
|
||
#define DT_BIND_NOW 24 /* Process relocations of object */
|
||
#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
|
||
#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
|
||
#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
|
||
#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
|
||
#define DT_NUM 29 /* Number used */
|
||
#define DT_LOOS 0x60000000 /* Start of OS-specific */
|
||
#define DT_HIOS 0x6fffffff /* End of OS-specific */
|
||
#define DT_LOPROC 0x70000000 /* Start of processor-specific */
|
||
#define DT_HIPROC 0x7fffffff /* End of processor-specific */
|
||
#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
|
||
|
||
/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
|
||
Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's
|
||
approach. */
|
||
#define DT_VALRNGLO 0x6ffffd00
|
||
#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
|
||
the following DT_* entry. */
|
||
#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
|
||
#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
|
||
#define DT_VALRNGHI 0x6ffffdff
|
||
|
||
/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
|
||
Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
|
||
|
||
If any adjustment is made to the ELF object after it has been
|
||
built these entries will need to be adjusted. */
|
||
#define DT_ADDRRNGLO 0x6ffffe00
|
||
#define DT_SYMINFO 0x6ffffeff /* syminfo table */
|
||
#define DT_ADDRRNGHI 0x6ffffeff
|
||
|
||
/* The versioning entry types. The next are defined as part of the
|
||
GNU extension. */
|
||
#define DT_VERSYM 0x6ffffff0
|
||
|
||
/* These were chosen by Sun. */
|
||
#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
|
||
#define DT_VERDEF 0x6ffffffc /* Address of version definition
|
||
table */
|
||
#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
|
||
#define DT_VERNEED 0x6ffffffe /* Address of table with needed
|
||
versions */
|
||
#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
|
||
#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
|
||
#define DT_VERSIONTAGNUM 16
|
||
|
||
/* Sun added these machine-independent extensions in the "processor-specific"
|
||
range. Be compatible. */
|
||
#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
|
||
#define DT_FILTER 0x7fffffff /* Shared object to get values from */
|
||
#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
|
||
#define DT_EXTRANUM 3
|
||
|
||
/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
|
||
entry in the dynamic section. */
|
||
#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
|
||
#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
|
||
#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */
|
||
#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
|
||
#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/
|
||
#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/
|
||
#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */
|
||
|
||
/* Version definition sections. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Half vd_version; /* Version revision */
|
||
Elf32_Half vd_flags; /* Version information */
|
||
Elf32_Half vd_ndx; /* Version Index */
|
||
Elf32_Half vd_cnt; /* Number of associated aux entries */
|
||
Elf32_Word vd_hash; /* Version name hash value */
|
||
Elf32_Word vd_aux; /* Offset in bytes to verdaux array */
|
||
Elf32_Word vd_next; /* Offset in bytes to next verdef
|
||
entry */
|
||
} Elf32_Verdef;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Half vd_version; /* Version revision */
|
||
Elf64_Half vd_flags; /* Version information */
|
||
Elf64_Half vd_ndx; /* Version Index */
|
||
Elf64_Half vd_cnt; /* Number of associated aux entries */
|
||
Elf64_Word vd_hash; /* Version name hash value */
|
||
Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
|
||
Elf64_Word vd_next; /* Offset in bytes to next verdef
|
||
entry */
|
||
} Elf64_Verdef;
|
||
|
||
|
||
/* Legal values for vd_version (version revision). */
|
||
#define VER_DEF_NONE 0 /* No version */
|
||
#define VER_DEF_CURRENT 1 /* Current version */
|
||
#define VER_DEF_NUM 2 /* Given version number */
|
||
|
||
/* Legal values for vd_flags (version information flags). */
|
||
#define VER_FLG_BASE 0x1 /* Version definition of file itself */
|
||
#define VER_FLG_WEAK 0x2 /* Weak version identifier */
|
||
|
||
/* Auxialiary version information. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word vda_name; /* Version or dependency names */
|
||
Elf32_Word vda_next; /* Offset in bytes to next verdaux
|
||
entry */
|
||
} Elf32_Verdaux;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word vda_name; /* Version or dependency names */
|
||
Elf64_Word vda_next; /* Offset in bytes to next verdaux
|
||
entry */
|
||
} Elf64_Verdaux;
|
||
|
||
|
||
/* Version dependency section. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Half vn_version; /* Version of structure */
|
||
Elf32_Half vn_cnt; /* Number of associated aux entries */
|
||
Elf32_Word vn_file; /* Offset of filename for this
|
||
dependency */
|
||
Elf32_Word vn_aux; /* Offset in bytes to vernaux array */
|
||
Elf32_Word vn_next; /* Offset in bytes to next verneed
|
||
entry */
|
||
} Elf32_Verneed;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Half vn_version; /* Version of structure */
|
||
Elf64_Half vn_cnt; /* Number of associated aux entries */
|
||
Elf64_Word vn_file; /* Offset of filename for this
|
||
dependency */
|
||
Elf64_Word vn_aux; /* Offset in bytes to vernaux array */
|
||
Elf64_Word vn_next; /* Offset in bytes to next verneed
|
||
entry */
|
||
} Elf64_Verneed;
|
||
|
||
|
||
/* Legal values for vn_version (version revision). */
|
||
#define VER_NEED_NONE 0 /* No version */
|
||
#define VER_NEED_CURRENT 1 /* Current version */
|
||
#define VER_NEED_NUM 2 /* Given version number */
|
||
|
||
/* Auxiliary needed version information. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word vna_hash; /* Hash value of dependency name */
|
||
Elf32_Half vna_flags; /* Dependency specific information */
|
||
Elf32_Half vna_other; /* Unused */
|
||
Elf32_Word vna_name; /* Dependency name string offset */
|
||
Elf32_Word vna_next; /* Offset in bytes to next vernaux
|
||
entry */
|
||
} Elf32_Vernaux;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word vna_hash; /* Hash value of dependency name */
|
||
Elf64_Half vna_flags; /* Dependency specific information */
|
||
Elf64_Half vna_other; /* Unused */
|
||
Elf64_Word vna_name; /* Dependency name string offset */
|
||
Elf64_Word vna_next; /* Offset in bytes to next vernaux
|
||
entry */
|
||
} Elf64_Vernaux;
|
||
|
||
|
||
/* Legal values for vna_flags. */
|
||
#define VER_FLG_WEAK 0x2 /* Weak version identifier */
|
||
|
||
|
||
/* Auxiliary vector. */
|
||
|
||
/* This vector is normally only used by the program interpreter. The
|
||
usual definition in an ABI supplement uses the name auxv_t. The
|
||
vector is not usually defined in a standard <elf.h> file, but it
|
||
can't hurt. We rename it to avoid conflicts. The sizes of these
|
||
types are an arrangement between the exec server and the program
|
||
interpreter, so we don't fully specify them here. */
|
||
|
||
typedef struct
|
||
{
|
||
int a_type; /* Entry type */
|
||
union
|
||
{
|
||
long int a_val; /* Integer value */
|
||
void *a_ptr; /* Pointer value */
|
||
void (*a_fcn) (void); /* Function pointer value */
|
||
} a_un;
|
||
} Elf32_auxv_t;
|
||
|
||
typedef struct
|
||
{
|
||
long int a_type; /* Entry type */
|
||
union
|
||
{
|
||
long int a_val; /* Integer value */
|
||
void *a_ptr; /* Pointer value */
|
||
void (*a_fcn) (void); /* Function pointer value */
|
||
} a_un;
|
||
} Elf64_auxv_t;
|
||
|
||
/* Legal values for a_type (entry type). */
|
||
|
||
#define AT_NULL 0 /* End of vector */
|
||
#define AT_IGNORE 1 /* Entry should be ignored */
|
||
#define AT_EXECFD 2 /* File descriptor of program */
|
||
#define AT_PHDR 3 /* Program headers for program */
|
||
#define AT_PHENT 4 /* Size of program header entry */
|
||
#define AT_PHNUM 5 /* Number of program headers */
|
||
#define AT_PAGESZ 6 /* System page size */
|
||
#define AT_BASE 7 /* Base address of interpreter */
|
||
#define AT_FLAGS 8 /* Flags */
|
||
#define AT_ENTRY 9 /* Entry point of program */
|
||
#define AT_NOTELF 10 /* Program is not ELF */
|
||
#define AT_UID 11 /* Real uid */
|
||
#define AT_EUID 12 /* Effective uid */
|
||
#define AT_GID 13 /* Real gid */
|
||
#define AT_EGID 14 /* Effective gid */
|
||
|
||
/* Some more special a_type values describing the hardware. */
|
||
#define AT_PLATFORM 15 /* String identifying platform. */
|
||
#define AT_HWCAP 16 /* Machine dependent hints about
|
||
processor capabilities. */
|
||
|
||
/* This entry gives some information about the FPU initialization
|
||
performed by the kernel. */
|
||
#define AT_FPUCW 17 /* Used FPU control word. */
|
||
|
||
|
||
/* Note section contents. Each entry in the note section begins with
|
||
a header of a fixed form. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word n_namesz; /* Length of the note's name. */
|
||
Elf32_Word n_descsz; /* Length of the note's descriptor. */
|
||
Elf32_Word n_type; /* Type of the note. */
|
||
} Elf32_Nhdr;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word n_namesz; /* Length of the note's name. */
|
||
Elf64_Word n_descsz; /* Length of the note's descriptor. */
|
||
Elf64_Word n_type; /* Type of the note. */
|
||
} Elf64_Nhdr;
|
||
|
||
/* Known names of notes. */
|
||
|
||
/* Solaris entries in the note section have this name. */
|
||
#define ELF_NOTE_SOLARIS "SUNW Solaris"
|
||
|
||
/* Note entries for GNU systems have this name. */
|
||
#define ELF_NOTE_GNU "GNU"
|
||
|
||
|
||
/* Defined types of notes for Solaris. */
|
||
|
||
/* Value of descriptor (one word) is desired pagesize for the binary. */
|
||
#define ELF_NOTE_PAGESIZE_HINT 1
|
||
|
||
|
||
/* Defined note types for GNU systems. */
|
||
|
||
/* ABI information. The descriptor consists of words:
|
||
word 0: OS descriptor
|
||
word 1: major version of the ABI
|
||
word 2: minor version of the ABI
|
||
word 3: subminor version of the ABI
|
||
*/
|
||
#define ELF_NOTE_ABI 1
|
||
|
||
/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI
|
||
note section entry. */
|
||
#define ELF_NOTE_OS_LINUX 0
|
||
#define ELF_NOTE_OS_GNU 1
|
||
#define ELF_NOTE_OS_SOLARIS2 2
|
||
|
||
|
||
/* Motorola 68k specific definitions. */
|
||
|
||
/* m68k relocs. */
|
||
|
||
#define R_68K_NONE 0 /* No reloc */
|
||
#define R_68K_32 1 /* Direct 32 bit */
|
||
#define R_68K_16 2 /* Direct 16 bit */
|
||
#define R_68K_8 3 /* Direct 8 bit */
|
||
#define R_68K_PC32 4 /* PC relative 32 bit */
|
||
#define R_68K_PC16 5 /* PC relative 16 bit */
|
||
#define R_68K_PC8 6 /* PC relative 8 bit */
|
||
#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */
|
||
#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */
|
||
#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */
|
||
#define R_68K_GOT32O 10 /* 32 bit GOT offset */
|
||
#define R_68K_GOT16O 11 /* 16 bit GOT offset */
|
||
#define R_68K_GOT8O 12 /* 8 bit GOT offset */
|
||
#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */
|
||
#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */
|
||
#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */
|
||
#define R_68K_PLT32O 16 /* 32 bit PLT offset */
|
||
#define R_68K_PLT16O 17 /* 16 bit PLT offset */
|
||
#define R_68K_PLT8O 18 /* 8 bit PLT offset */
|
||
#define R_68K_COPY 19 /* Copy symbol at runtime */
|
||
#define R_68K_GLOB_DAT 20 /* Create GOT entry */
|
||
#define R_68K_JMP_SLOT 21 /* Create PLT entry */
|
||
#define R_68K_RELATIVE 22 /* Adjust by program base */
|
||
/* Keep this the last entry. */
|
||
#define R_68K_NUM 23
|
||
|
||
/* Intel 80386 specific definitions. */
|
||
|
||
/* i386 relocs. */
|
||
|
||
#define R_386_NONE 0 /* No reloc */
|
||
#define R_386_32 1 /* Direct 32 bit */
|
||
#define R_386_PC32 2 /* PC relative 32 bit */
|
||
#define R_386_GOT32 3 /* 32 bit GOT entry */
|
||
#define R_386_PLT32 4 /* 32 bit PLT address */
|
||
#define R_386_COPY 5 /* Copy symbol at runtime */
|
||
#define R_386_GLOB_DAT 6 /* Create GOT entry */
|
||
#define R_386_JMP_SLOT 7 /* Create PLT entry */
|
||
#define R_386_RELATIVE 8 /* Adjust by program base */
|
||
#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
|
||
#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
|
||
/* Keep this the last entry. */
|
||
#define R_386_NUM 11
|
||
|
||
/* SUN SPARC specific definitions. */
|
||
|
||
/* Values for Elf64_Ehdr.e_flags. */
|
||
|
||
#define EF_SPARCV9_MM 3
|
||
#define EF_SPARCV9_TSO 0
|
||
#define EF_SPARCV9_PSO 1
|
||
#define EF_SPARCV9_RMO 2
|
||
#define EF_SPARC_EXT_MASK 0xFFFF00
|
||
#define EF_SPARC_SUN_US1 0x000200
|
||
#define EF_SPARC_HAL_R1 0x000400
|
||
|
||
/* SPARC relocs. */
|
||
|
||
#define R_SPARC_NONE 0 /* No reloc */
|
||
#define R_SPARC_8 1 /* Direct 8 bit */
|
||
#define R_SPARC_16 2 /* Direct 16 bit */
|
||
#define R_SPARC_32 3 /* Direct 32 bit */
|
||
#define R_SPARC_DISP8 4 /* PC relative 8 bit */
|
||
#define R_SPARC_DISP16 5 /* PC relative 16 bit */
|
||
#define R_SPARC_DISP32 6 /* PC relative 32 bit */
|
||
#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */
|
||
#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */
|
||
#define R_SPARC_HI22 9 /* High 22 bit */
|
||
#define R_SPARC_22 10 /* Direct 22 bit */
|
||
#define R_SPARC_13 11 /* Direct 13 bit */
|
||
#define R_SPARC_LO10 12 /* Truncated 10 bit */
|
||
#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */
|
||
#define R_SPARC_GOT13 14 /* 13 bit GOT entry */
|
||
#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */
|
||
#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */
|
||
#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */
|
||
#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */
|
||
#define R_SPARC_COPY 19 /* Copy symbol at runtime */
|
||
#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */
|
||
#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */
|
||
#define R_SPARC_RELATIVE 22 /* Adjust by program base */
|
||
#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */
|
||
|
||
/* Additional Sparc64 relocs. */
|
||
|
||
#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */
|
||
#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */
|
||
#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */
|
||
#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */
|
||
#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */
|
||
#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */
|
||
#define R_SPARC_10 30 /* Direct 10 bit */
|
||
#define R_SPARC_11 31 /* Direct 11 bit */
|
||
#define R_SPARC_64 32 /* Direct 64 bit */
|
||
#define R_SPARC_OLO10 33 /* ?? */
|
||
#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */
|
||
#define R_SPARC_HM10 35 /* High middle 10 bits of ... */
|
||
#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */
|
||
#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */
|
||
#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */
|
||
#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */
|
||
#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */
|
||
#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */
|
||
#define R_SPARC_7 43 /* Direct 7 bit */
|
||
#define R_SPARC_5 44 /* Direct 5 bit */
|
||
#define R_SPARC_6 45 /* Direct 6 bit */
|
||
#define R_SPARC_DISP64 46 /* PC relative 64 bit */
|
||
#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */
|
||
#define R_SPARC_HIX22 48 /* High 22 bit complemented */
|
||
#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */
|
||
#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */
|
||
#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */
|
||
#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */
|
||
#define R_SPARC_REGISTER 53 /* Global register usage */
|
||
#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */
|
||
#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */
|
||
/* Keep this the last entry. */
|
||
#define R_SPARC_NUM 56
|
||
|
||
/* For Sparc64, legal values for d_tag of Elf64_Dyn. */
|
||
|
||
#define DT_SPARC_REGISTER 0x70000001
|
||
#define DT_SPARC_NUM 2
|
||
|
||
/* Bits present in AT_HWCAP, primarily for Sparc32. */
|
||
|
||
#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */
|
||
#define HWCAP_SPARC_STBAR 2
|
||
#define HWCAP_SPARC_SWAP 4
|
||
#define HWCAP_SPARC_MULDIV 8
|
||
#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */
|
||
|
||
/* MIPS R3000 specific definitions. */
|
||
|
||
/* Legal values for e_flags field of Elf32_Ehdr. */
|
||
|
||
#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */
|
||
#define EF_MIPS_PIC 2 /* Contains PIC code */
|
||
#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */
|
||
#define EF_MIPS_XGOT 8
|
||
#define EF_MIPS_64BIT_WHIRL 16
|
||
#define EF_MIPS_ABI2 32
|
||
#define EF_MIPS_ABI_ON32 64
|
||
#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */
|
||
|
||
/* Legal values for MIPS architecture level. */
|
||
|
||
#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
|
||
#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
|
||
#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
|
||
#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
|
||
#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
|
||
|
||
/* The following are non-official names and should not be used. */
|
||
|
||
#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
|
||
#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
|
||
#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
|
||
#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
|
||
#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
|
||
|
||
/* Special section indices. */
|
||
|
||
#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */
|
||
#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */
|
||
#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */
|
||
#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */
|
||
#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */
|
||
|
||
/* Legal values for sh_type field of Elf32_Shdr. */
|
||
|
||
#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */
|
||
#define SHT_MIPS_MSYM 0x70000001
|
||
#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */
|
||
#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */
|
||
#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */
|
||
#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/
|
||
#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */
|
||
#define SHT_MIPS_PACKAGE 0x70000007
|
||
#define SHT_MIPS_PACKSYM 0x70000008
|
||
#define SHT_MIPS_RELD 0x70000009
|
||
#define SHT_MIPS_IFACE 0x7000000b
|
||
#define SHT_MIPS_CONTENT 0x7000000c
|
||
#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */
|
||
#define SHT_MIPS_SHDR 0x70000010
|
||
#define SHT_MIPS_FDESC 0x70000011
|
||
#define SHT_MIPS_EXTSYM 0x70000012
|
||
#define SHT_MIPS_DENSE 0x70000013
|
||
#define SHT_MIPS_PDESC 0x70000014
|
||
#define SHT_MIPS_LOCSYM 0x70000015
|
||
#define SHT_MIPS_AUXSYM 0x70000016
|
||
#define SHT_MIPS_OPTSYM 0x70000017
|
||
#define SHT_MIPS_LOCSTR 0x70000018
|
||
#define SHT_MIPS_LINE 0x70000019
|
||
#define SHT_MIPS_RFDESC 0x7000001a
|
||
#define SHT_MIPS_DELTASYM 0x7000001b
|
||
#define SHT_MIPS_DELTAINST 0x7000001c
|
||
#define SHT_MIPS_DELTACLASS 0x7000001d
|
||
#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */
|
||
#define SHT_MIPS_DELTADECL 0x7000001f
|
||
#define SHT_MIPS_SYMBOL_LIB 0x70000020
|
||
#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */
|
||
#define SHT_MIPS_TRANSLATE 0x70000022
|
||
#define SHT_MIPS_PIXIE 0x70000023
|
||
#define SHT_MIPS_XLATE 0x70000024
|
||
#define SHT_MIPS_XLATE_DEBUG 0x70000025
|
||
#define SHT_MIPS_WHIRL 0x70000026
|
||
#define SHT_MIPS_EH_REGION 0x70000027
|
||
#define SHT_MIPS_XLATE_OLD 0x70000028
|
||
#define SHT_MIPS_PDR_EXCEPTION 0x70000029
|
||
|
||
/* Legal values for sh_flags field of Elf32_Shdr. */
|
||
|
||
#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */
|
||
#define SHF_MIPS_MERGE 0x20000000
|
||
#define SHF_MIPS_ADDR 0x40000000
|
||
#define SHF_MIPS_STRINGS 0x80000000
|
||
#define SHF_MIPS_NOSTRIP 0x08000000
|
||
#define SHF_MIPS_LOCAL 0x04000000
|
||
#define SHF_MIPS_NAMES 0x02000000
|
||
#define SHF_MIPS_NODUPE 0x01000000
|
||
|
||
|
||
/* Symbol tables. */
|
||
|
||
/* MIPS specific values for `st_other'. */
|
||
#define STO_MIPS_DEFAULT 0x0
|
||
#define STO_MIPS_INTERNAL 0x1
|
||
#define STO_MIPS_HIDDEN 0x2
|
||
#define STO_MIPS_PROTECTED 0x3
|
||
#define STO_MIPS_SC_ALIGN_UNUSED 0xff
|
||
|
||
/* MIPS specific values for `st_info'. */
|
||
#define STB_MIPS_SPLIT_COMMON 13
|
||
|
||
/* Entries found in sections of type SHT_MIPS_GPTAB. */
|
||
|
||
typedef union
|
||
{
|
||
struct
|
||
{
|
||
Elf32_Word gt_current_g_value; /* -G value used for compilation */
|
||
Elf32_Word gt_unused; /* Not used */
|
||
} gt_header; /* First entry in section */
|
||
struct
|
||
{
|
||
Elf32_Word gt_g_value; /* If this value were used for -G */
|
||
Elf32_Word gt_bytes; /* This many bytes would be used */
|
||
} gt_entry; /* Subsequent entries in section */
|
||
} Elf32_gptab;
|
||
|
||
/* Entry found in sections of type SHT_MIPS_REGINFO. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word ri_gprmask; /* General registers used */
|
||
Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */
|
||
Elf32_Sword ri_gp_value; /* $gp register value */
|
||
} Elf32_RegInfo;
|
||
|
||
/* Entries found in sections of type SHT_MIPS_OPTIONS. */
|
||
|
||
typedef struct
|
||
{
|
||
unsigned char kind; /* Determines interpretation of the
|
||
variable part of descriptor. */
|
||
unsigned char size; /* Size of descriptor, including header. */
|
||
Elf32_Section section; /* Section header index of section affected,
|
||
0 for global options. */
|
||
Elf32_Word info; /* Kind-specific information. */
|
||
} Elf_Options;
|
||
|
||
/* Values for `kind' field in Elf_Options. */
|
||
|
||
#define ODK_NULL 0 /* Undefined. */
|
||
#define ODK_REGINFO 1 /* Register usage information. */
|
||
#define ODK_EXCEPTIONS 2 /* Exception processing options. */
|
||
#define ODK_PAD 3 /* Section padding options. */
|
||
#define ODK_HWPATCH 4 /* Hardware workarounds performed */
|
||
#define ODK_FILL 5 /* record the fill value used by the linker. */
|
||
#define ODK_TAGS 6 /* reserve space for desktop tools to write. */
|
||
#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */
|
||
#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */
|
||
|
||
/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */
|
||
|
||
#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */
|
||
#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */
|
||
#define OEX_PAGE0 0x10000 /* page zero must be mapped. */
|
||
#define OEX_SMM 0x20000 /* Force sequential memory mode? */
|
||
#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */
|
||
#define OEX_PRECISEFP OEX_FPDBUG
|
||
#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */
|
||
|
||
#define OEX_FPU_INVAL 0x10
|
||
#define OEX_FPU_DIV0 0x08
|
||
#define OEX_FPU_OFLO 0x04
|
||
#define OEX_FPU_UFLO 0x02
|
||
#define OEX_FPU_INEX 0x01
|
||
|
||
/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */
|
||
|
||
#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */
|
||
#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */
|
||
#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */
|
||
#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */
|
||
|
||
#define OPAD_PREFIX 0x1
|
||
#define OPAD_POSTFIX 0x2
|
||
#define OPAD_SYMBOL 0x4
|
||
|
||
/* Entry found in `.options' section. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word hwp_flags1; /* Extra flags. */
|
||
Elf32_Word hwp_flags2; /* Extra flags. */
|
||
} Elf_Options_Hw;
|
||
|
||
/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */
|
||
|
||
#define OHWA0_R4KEOP_CHECKED 0x00000001
|
||
#define OHWA1_R4KEOP_CLEAN 0x00000002
|
||
|
||
/* MIPS relocs. */
|
||
|
||
#define R_MIPS_NONE 0 /* No reloc */
|
||
#define R_MIPS_16 1 /* Direct 16 bit */
|
||
#define R_MIPS_32 2 /* Direct 32 bit */
|
||
#define R_MIPS_REL32 3 /* PC relative 32 bit */
|
||
#define R_MIPS_26 4 /* Direct 26 bit shifted */
|
||
#define R_MIPS_HI16 5 /* High 16 bit */
|
||
#define R_MIPS_LO16 6 /* Low 16 bit */
|
||
#define R_MIPS_GPREL16 7 /* GP relative 16 bit */
|
||
#define R_MIPS_LITERAL 8 /* 16 bit literal entry */
|
||
#define R_MIPS_GOT16 9 /* 16 bit GOT entry */
|
||
#define R_MIPS_PC16 10 /* PC relative 16 bit */
|
||
#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */
|
||
#define R_MIPS_GPREL32 12 /* GP relative 32 bit */
|
||
|
||
#define R_MIPS_SHIFT5 16
|
||
#define R_MIPS_SHIFT6 17
|
||
#define R_MIPS_64 18
|
||
#define R_MIPS_GOT_DISP 19
|
||
#define R_MIPS_GOT_PAGE 20
|
||
#define R_MIPS_GOT_OFST 21
|
||
#define R_MIPS_GOT_HI16 22
|
||
#define R_MIPS_GOT_LO16 23
|
||
#define R_MIPS_SUB 24
|
||
#define R_MIPS_INSERT_A 25
|
||
#define R_MIPS_INSERT_B 26
|
||
#define R_MIPS_DELETE 27
|
||
#define R_MIPS_HIGHER 28
|
||
#define R_MIPS_HIGHEST 29
|
||
#define R_MIPS_CALL_HI16 30
|
||
#define R_MIPS_CALL_LO16 31
|
||
#define R_MIPS_SCN_DISP 32
|
||
#define R_MIPS_REL16 33
|
||
#define R_MIPS_ADD_IMMEDIATE 34
|
||
#define R_MIPS_PJUMP 35
|
||
#define R_MIPS_RELGOT 36
|
||
#define R_MIPS_JALR 37
|
||
/* Keep this the last entry. */
|
||
#define R_MIPS_NUM 38
|
||
|
||
/* Legal values for p_type field of Elf32_Phdr. */
|
||
|
||
#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
|
||
#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
|
||
#define PT_MIPS_OPTIONS 0x70000002
|
||
|
||
/* Special program header types. */
|
||
|
||
#define PF_MIPS_LOCAL 0x10000000
|
||
|
||
/* Legal values for d_tag field of Elf32_Dyn. */
|
||
|
||
#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */
|
||
#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */
|
||
#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */
|
||
#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */
|
||
#define DT_MIPS_FLAGS 0x70000005 /* Flags */
|
||
#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */
|
||
#define DT_MIPS_MSYM 0x70000007
|
||
#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */
|
||
#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */
|
||
#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */
|
||
#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */
|
||
#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */
|
||
#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */
|
||
#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */
|
||
#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */
|
||
#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
|
||
#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */
|
||
#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */
|
||
#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in
|
||
DT_MIPS_DELTA_CLASS. */
|
||
#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */
|
||
#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
|
||
DT_MIPS_DELTA_INSTANCE. */
|
||
#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */
|
||
#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
|
||
DT_MIPS_DELTA_RELOC. */
|
||
#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta
|
||
relocations refer to. */
|
||
#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
|
||
DT_MIPS_DELTA_SYM. */
|
||
#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
|
||
class declaration. */
|
||
#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
|
||
DT_MIPS_DELTA_CLASSSYM. */
|
||
#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */
|
||
#define DT_MIPS_PIXIE_INIT 0x70000023
|
||
#define DT_MIPS_SYMBOL_LIB 0x70000024
|
||
#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
|
||
#define DT_MIPS_LOCAL_GOTIDX 0x70000026
|
||
#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
|
||
#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
|
||
#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */
|
||
#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */
|
||
#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
|
||
#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
|
||
#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
|
||
function stored in GOT. */
|
||
#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added
|
||
by rld on dlopen() calls. */
|
||
#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
|
||
#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */
|
||
#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */
|
||
#define DT_MIPS_NUM 0x32
|
||
|
||
/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
|
||
|
||
#define RHF_NONE 0 /* No flags */
|
||
#define RHF_QUICKSTART (1 << 0) /* Use quickstart */
|
||
#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */
|
||
#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */
|
||
#define RHF_NO_MOVE (1 << 3)
|
||
#define RHF_SGI_ONLY (1 << 4)
|
||
#define RHF_GUARANTEE_INIT (1 << 5)
|
||
#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
|
||
#define RHF_GUARANTEE_START_INIT (1 << 7)
|
||
#define RHF_PIXIE (1 << 8)
|
||
#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
|
||
#define RHF_REQUICKSTART (1 << 10)
|
||
#define RHF_REQUICKSTARTED (1 << 11)
|
||
#define RHF_CORD (1 << 12)
|
||
#define RHF_NO_UNRES_UNDEF (1 << 13)
|
||
#define RHF_RLD_ORDER_SAFE (1 << 14)
|
||
|
||
/* Entries found in sections of type SHT_MIPS_LIBLIST. */
|
||
|
||
typedef struct
|
||
{
|
||
Elf32_Word l_name; /* Name (string table index) */
|
||
Elf32_Word l_time_stamp; /* Timestamp */
|
||
Elf32_Word l_checksum; /* Checksum */
|
||
Elf32_Word l_version; /* Interface version */
|
||
Elf32_Word l_flags; /* Flags */
|
||
} Elf32_Lib;
|
||
|
||
typedef struct
|
||
{
|
||
Elf64_Word l_name; /* Name (string table index) */
|
||
Elf64_Word l_time_stamp; /* Timestamp */
|
||
Elf64_Word l_checksum; /* Checksum */
|
||
Elf64_Word l_version; /* Interface version */
|
||
Elf64_Word l_flags; /* Flags */
|
||
} Elf64_Lib;
|
||
|
||
|
||
/* Legal values for l_flags. */
|
||
|
||
#define LL_NONE 0
|
||
#define LL_EXACT_MATCH (1 << 0) /* Require exact match */
|
||
#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */
|
||
#define LL_REQUIRE_MINOR (1 << 2)
|
||
#define LL_EXPORTS (1 << 3)
|
||
#define LL_DELAY_LOAD (1 << 4)
|
||
#define LL_DELTA (1 << 5)
|
||
|
||
/* Entries found in sections of type SHT_MIPS_CONFLICT. */
|
||
|
||
typedef Elf32_Addr Elf32_Conflict;
|
||
|
||
|
||
/* HPPA specific definitions. */
|
||
|
||
/* Legal values for e_flags field of Elf32_Ehdr. */
|
||
|
||
#define EF_PARISC_TRAPNL 1 /* Trap nil pointer dereference. */
|
||
#define EF_PARISC_EXT 2 /* Program uses arch. extensions. */
|
||
#define EF_PARISC_ARCH 0xffff0000 /* Architecture version. */
|
||
/* Defined values are:
|
||
0x020b PA-RISC 1.0 big-endian
|
||
0x0210 PA-RISC 1.1 big-endian
|
||
0x028b PA-RISC 1.0 little-endian
|
||
0x0290 PA-RISC 1.1 little-endian
|
||
*/
|
||
|
||
/* Legal values for sh_type field of Elf32_Shdr. */
|
||
|
||
#define SHT_PARISC_GOT 0x70000000 /* GOT for external data. */
|
||
#define SHT_PARISC_ARCH 0x70000001 /* Architecture extensions. */
|
||
#define SHT_PARISC_GLOBAL 0x70000002 /* Definition of $global$. */
|
||
#define SHT_PARISC_MILLI 0x70000003 /* Millicode routines. */
|
||
#define SHT_PARISC_UNWIND 0x70000004 /* Unwind information. */
|
||
#define SHT_PARISC_PLT 0x70000005 /* Procedure linkage table. */
|
||
#define SHT_PARISC_SDATA 0x70000006 /* Short initialized data. */
|
||
#define SHT_PARISC_SBSS 0x70000007 /* Short uninitialized data. */
|
||
#define SHT_PARISC_SYMEXTN 0x70000008 /* Argument/relocation info. */
|
||
#define SHT_PARISC_STUBS 0x70000009 /* Linker stubs. */
|
||
|
||
/* Legal values for sh_flags field of Elf32_Shdr. */
|
||
|
||
#define SHF_PARISC_GLOBAL 0x10000000 /* Section defines dp. */
|
||
#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
|
||
|
||
/* Legal values for ST_TYPE subfield of st_info (symbol type). */
|
||
|
||
#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
|
||
|
||
/* HPPA relocs. */
|
||
|
||
#define R_PARISC_NONE 0 /* No reloc. */
|
||
#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
|
||
#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
|
||
#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
|
||
#define R_PARISC_DIR14R 4 /* Right 14 bits of eff. address. */
|
||
#define R_PARISC_PCREL21L 5 /* PC-relative, left 21 bits. */
|
||
#define R_PARISC_PCREL14R 6 /* PC-relative, right 14 bits. */
|
||
#define R_PARISC_PCREL17C 7 /* Conditional PC-relative, ignore
|
||
if displacement > 17bits. */
|
||
#define R_PARISC_PCREL17F 8 /* Conditional PC-relative, must
|
||
fit in 17bits. */
|
||
#define R_PARISC_DPREL21L 9 /* DP-relative, left 21 bits. */
|
||
#define R_PARISC_DPREL14R 10 /* DP-relative, right 14 bits. */
|
||
#define R_PARISC_DPREL14F 11 /* DP-relative, must bit in 14 bits. */
|
||
#define R_PARISC_DLTREL21L 12 /* DLT-relative, left 21 bits. */
|
||
#define R_PARISC_DLTREL14R 13 /* DLT-relative, right 14 bits. */
|
||
#define R_PARISC_DLTREL14F 14 /* DLT-relative, must fit in 14 bits.*/
|
||
#define R_PARISC_DLTIND21L 15 /* DLT-relative indirect, left
|
||
21 bits. */
|
||
#define R_PARISC_DLTIND14R 16 /* DLT-relative indirect, right
|
||
14 bits. */
|
||
#define R_PARISC_DLTIND14F 17 /* DLT-relative indirect, must fit
|
||
int 14 bits. */
|
||
#define R_PARISC_PLABEL32 18 /* Direct 32-bit reference to proc. */
|
||
|
||
/* Alpha specific definitions. */
|
||
|
||
/* Legal values for e_flags field of Elf64_Ehdr. */
|
||
|
||
#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */
|
||
#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */
|
||
|
||
/* Legal values for sh_type field of Elf64_Shdr. */
|
||
|
||
/* These two are primerily concerned with ECOFF debugging info. */
|
||
#define SHT_ALPHA_DEBUG 0x70000001
|
||
#define SHT_ALPHA_REGINFO 0x70000002
|
||
|
||
/* Legal values for sh_flags field of Elf64_Shdr. */
|
||
|
||
#define SHF_ALPHA_GPREL 0x10000000
|
||
|
||
/* Legal values for st_other field of Elf64_Sym. */
|
||
#define STO_ALPHA_NOPV 0x80 /* No PV required. */
|
||
#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */
|
||
|
||
/* Alpha relocs. */
|
||
|
||
#define R_ALPHA_NONE 0 /* No reloc */
|
||
#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
|
||
#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
|
||
#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
|
||
#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
|
||
#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
|
||
#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
|
||
#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
|
||
#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
|
||
#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
|
||
#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
|
||
#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
|
||
#define R_ALPHA_OP_PUSH 12 /* OP stack push */
|
||
#define R_ALPHA_OP_STORE 13 /* OP stack pop and store */
|
||
#define R_ALPHA_OP_PSUB 14 /* OP stack subtract */
|
||
#define R_ALPHA_OP_PRSHIFT 15 /* OP stack right shift */
|
||
#define R_ALPHA_GPVALUE 16
|
||
#define R_ALPHA_GPRELHIGH 17
|
||
#define R_ALPHA_GPRELLOW 18
|
||
#define R_ALPHA_IMMED_GP_16 19
|
||
#define R_ALPHA_IMMED_GP_HI32 20
|
||
#define R_ALPHA_IMMED_SCN_HI32 21
|
||
#define R_ALPHA_IMMED_BR_HI32 22
|
||
#define R_ALPHA_IMMED_LO32 23
|
||
#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
|
||
#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
|
||
#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
|
||
#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
|
||
/* Keep this the last entry. */
|
||
#define R_ALPHA_NUM 28
|
||
|
||
|
||
/* PowerPC specific declarations */
|
||
|
||
/* PowerPC relocations defined by the ABIs */
|
||
#define R_PPC_NONE 0
|
||
#define R_PPC_ADDR32 1 /* 32bit absolute address */
|
||
#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
|
||
#define R_PPC_ADDR16 3 /* 16bit absolute address */
|
||
#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
|
||
#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
|
||
#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
|
||
#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
|
||
#define R_PPC_ADDR14_BRTAKEN 8
|
||
#define R_PPC_ADDR14_BRNTAKEN 9
|
||
#define R_PPC_REL24 10 /* PC relative 26 bit */
|
||
#define R_PPC_REL14 11 /* PC relative 16 bit */
|
||
#define R_PPC_REL14_BRTAKEN 12
|
||
#define R_PPC_REL14_BRNTAKEN 13
|
||
#define R_PPC_GOT16 14
|
||
#define R_PPC_GOT16_LO 15
|
||
#define R_PPC_GOT16_HI 16
|
||
#define R_PPC_GOT16_HA 17
|
||
#define R_PPC_PLTREL24 18
|
||
#define R_PPC_COPY 19
|
||
#define R_PPC_GLOB_DAT 20
|
||
#define R_PPC_JMP_SLOT 21
|
||
#define R_PPC_RELATIVE 22
|
||
#define R_PPC_LOCAL24PC 23
|
||
#define R_PPC_UADDR32 24
|
||
#define R_PPC_UADDR16 25
|
||
#define R_PPC_REL32 26
|
||
#define R_PPC_PLT32 27
|
||
#define R_PPC_PLTREL32 28
|
||
#define R_PPC_PLT16_LO 29
|
||
#define R_PPC_PLT16_HI 30
|
||
#define R_PPC_PLT16_HA 31
|
||
#define R_PPC_SDAREL16 32
|
||
#define R_PPC_SECTOFF 33
|
||
#define R_PPC_SECTOFF_LO 34
|
||
#define R_PPC_SECTOFF_HI 35
|
||
#define R_PPC_SECTOFF_HA 36
|
||
/* Keep this the last entry. */
|
||
#define R_PPC_NUMm 37
|
||
|
||
/* The remaining relocs are from the Embedded ELF ABI, and are not
|
||
in the SVR4 ELF ABI. */
|
||
#define R_PPC_EMB_NADDR32 101
|
||
#define R_PPC_EMB_NADDR16 102
|
||
#define R_PPC_EMB_NADDR16_LO 103
|
||
#define R_PPC_EMB_NADDR16_HI 104
|
||
#define R_PPC_EMB_NADDR16_HA 105
|
||
#define R_PPC_EMB_SDAI16 106
|
||
#define R_PPC_EMB_SDA2I16 107
|
||
#define R_PPC_EMB_SDA2REL 108
|
||
#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
|
||
#define R_PPC_EMB_MRKREF 110
|
||
#define R_PPC_EMB_RELSEC16 111
|
||
#define R_PPC_EMB_RELST_LO 112
|
||
#define R_PPC_EMB_RELST_HI 113
|
||
#define R_PPC_EMB_RELST_HA 114
|
||
#define R_PPC_EMB_BIT_FLD 115
|
||
#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
|
||
|
||
/* Diab tool relocations. */
|
||
#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
|
||
#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
|
||
#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
|
||
#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
|
||
#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
|
||
#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
|
||
|
||
/* This is a phony reloc to handle any old fashioned TOC16 references
|
||
that may still be in object files. */
|
||
#define R_PPC_TOC16 255
|
||
|
||
|
||
/* ARM specific declarations */
|
||
|
||
/* Processor specific flags for the ELF header e_flags field. */
|
||
#define EF_ARM_RELEXEC 0x01
|
||
#define EF_ARM_HASENTRY 0x02
|
||
#define EF_ARM_INTERWORK 0x04
|
||
#define EF_ARM_APCS_26 0x08
|
||
#define EF_ARM_APCS_FLOAT 0x10
|
||
#define EF_ARM_PIC 0x20
|
||
#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */
|
||
#define EF_NEW_ABI 0x80
|
||
#define EF_OLD_ABI 0x100
|
||
|
||
/* Additional symbol types for Thumb */
|
||
#define STT_ARM_TFUNC 0xd
|
||
|
||
/* ARM-specific values for sh_flags */
|
||
#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
|
||
#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
|
||
in the input to a link step */
|
||
|
||
/* ARM-specific program header flags */
|
||
#define PF_ARM_SB 0x10000000 /* Segment contains the location
|
||
addressed by the static base */
|
||
|
||
/* ARM relocs. */
|
||
#define R_ARM_NONE 0 /* No reloc */
|
||
#define R_ARM_PC24 1 /* PC relative 26 bit branch */
|
||
#define R_ARM_ABS32 2 /* Direct 32 bit */
|
||
#define R_ARM_REL32 3 /* PC relative 32 bit */
|
||
#define R_ARM_PC13 4
|
||
#define R_ARM_ABS16 5 /* Direct 16 bit */
|
||
#define R_ARM_ABS12 6 /* Direct 12 bit */
|
||
#define R_ARM_THM_ABS5 7
|
||
#define R_ARM_ABS8 8 /* Direct 8 bit */
|
||
#define R_ARM_SBREL32 9
|
||
#define R_ARM_THM_PC22 10
|
||
#define R_ARM_THM_PC8 11
|
||
#define R_ARM_AMP_VCALL9 12
|
||
#define R_ARM_SWI24 13
|
||
#define R_ARM_THM_SWI8 14
|
||
#define R_ARM_XPC25 15
|
||
#define R_ARM_THM_XPC22 16
|
||
#define R_ARM_COPY 20 /* Copy symbol at runtime */
|
||
#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
|
||
#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
|
||
#define R_ARM_RELATIVE 23 /* Adjust by program base */
|
||
#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
|
||
#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
|
||
#define R_ARM_GOT32 26 /* 32 bit GOT entry */
|
||
#define R_ARM_PLT32 27 /* 32 bit PLT address */
|
||
#define R_ARM_GNU_VTENTRY 100
|
||
#define R_ARM_GNU_VTINHERIT 101
|
||
#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
|
||
#define R_ARM_THM_PC9 103 /* thumb conditional branch */
|
||
#define R_ARM_RXPC25 249
|
||
#define R_ARM_RSBREL32 250
|
||
#define R_ARM_THM_RPC22 251
|
||
#define R_ARM_RREL32 252
|
||
#define R_ARM_RABS22 253
|
||
#define R_ARM_RPC24 254
|
||
#define R_ARM_RBASE 255
|
||
/* Keep this the last entry. */
|
||
#define R_ARM_NUM 256
|
||
|
||
/* TMS320C67xx specific declarations */
|
||
/* XXX: no ELF standard yet */
|
||
|
||
/* TMS320C67xx relocs. */
|
||
#define R_C60_32 1
|
||
#define R_C60_GOT32 3 /* 32 bit GOT entry */
|
||
#define R_C60_PLT32 4 /* 32 bit PLT address */
|
||
#define R_C60_COPY 5 /* Copy symbol at runtime */
|
||
#define R_C60_GLOB_DAT 6 /* Create GOT entry */
|
||
#define R_C60_JMP_SLOT 7 /* Create PLT entry */
|
||
#define R_C60_RELATIVE 8 /* Adjust by program base */
|
||
#define R_C60_GOTOFF 9 /* 32 bit offset to GOT */
|
||
#define R_C60_GOTPC 10 /* 32 bit PC relative offset to GOT */
|
||
|
||
#define R_C60HI16 0x55 // high 16 bit MVKH embedded
|
||
#define R_C60LO16 0x54 // low 16 bit MVKL embedded
|
||
|
||
#endif /* elf.h */
|
||
//---------------------------------------------------------------------------
|
||
|
||
|
||
// njn: inlined stab.h
|
||
//#include "stab.h"
|
||
//---------------------------------------------------------------------------
|
||
#ifndef __GNU_STAB__
|
||
|
||
/* Indicate the GNU stab.h is in use. */
|
||
|
||
#define __GNU_STAB__
|
||
|
||
#define __define_stab(NAME, CODE, STRING) NAME=CODE,
|
||
|
||
enum __stab_debug_code
|
||
{
|
||
// njn: inlined stab.def
|
||
//#include "stab.def"
|
||
//---------------------------------------------------------------------------
|
||
/* Table of DBX symbol codes for the GNU system.
|
||
Copyright (C) 1988, 1997 Free Software Foundation, Inc.
|
||
This file is part of the GNU C Library.
|
||
|
||
The GNU C 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.
|
||
|
||
The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not,
|
||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
Boston, MA 02111-1307, USA. */
|
||
|
||
/* This contains contribution from Cygnus Support. */
|
||
|
||
/* Global variable. Only the name is significant.
|
||
To find the address, look in the corresponding external symbol. */
|
||
__define_stab (N_GSYM, 0x20, "GSYM")
|
||
|
||
/* Function name for BSD Fortran. Only the name is significant.
|
||
To find the address, look in the corresponding external symbol. */
|
||
__define_stab (N_FNAME, 0x22, "FNAME")
|
||
|
||
/* Function name or text-segment variable for C. Value is its address.
|
||
Desc is supposedly starting line number, but GCC doesn't set it
|
||
and DBX seems not to miss it. */
|
||
__define_stab (N_FUN, 0x24, "FUN")
|
||
|
||
/* Data-segment variable with internal linkage. Value is its address.
|
||
"Static Sym". */
|
||
__define_stab (N_STSYM, 0x26, "STSYM")
|
||
|
||
/* BSS-segment variable with internal linkage. Value is its address. */
|
||
__define_stab (N_LCSYM, 0x28, "LCSYM")
|
||
|
||
/* Name of main routine. Only the name is significant.
|
||
This is not used in C. */
|
||
__define_stab (N_MAIN, 0x2a, "MAIN")
|
||
|
||
/* Global symbol in Pascal.
|
||
Supposedly the value is its line number; I'm skeptical. */
|
||
__define_stab (N_PC, 0x30, "PC")
|
||
|
||
/* Number of symbols: 0, files,,funcs,lines according to Ultrix V4.0. */
|
||
__define_stab (N_NSYMS, 0x32, "NSYMS")
|
||
|
||
/* "No DST map for sym: name, ,0,type,ignored" according to Ultrix V4.0. */
|
||
__define_stab (N_NOMAP, 0x34, "NOMAP")
|
||
|
||
/* New stab from Solaris. I don't know what it means, but it
|
||
don't seem to contain useful information. */
|
||
__define_stab (N_OBJ, 0x38, "OBJ")
|
||
|
||
/* New stab from Solaris. I don't know what it means, but it
|
||
don't seem to contain useful information. Possibly related to the
|
||
optimization flags used in this module. */
|
||
__define_stab (N_OPT, 0x3c, "OPT")
|
||
|
||
/* Register variable. Value is number of register. */
|
||
__define_stab (N_RSYM, 0x40, "RSYM")
|
||
|
||
/* Modula-2 compilation unit. Can someone say what info it contains? */
|
||
__define_stab (N_M2C, 0x42, "M2C")
|
||
|
||
/* Line number in text segment. Desc is the line number;
|
||
value is corresponding address. */
|
||
__define_stab (N_SLINE, 0x44, "SLINE")
|
||
|
||
/* Similar, for data segment. */
|
||
__define_stab (N_DSLINE, 0x46, "DSLINE")
|
||
|
||
/* Similar, for bss segment. */
|
||
__define_stab (N_BSLINE, 0x48, "BSLINE")
|
||
|
||
/* Sun's source-code browser stabs. ?? Don't know what the fields are.
|
||
Supposedly the field is "path to associated .cb file". THIS VALUE
|
||
OVERLAPS WITH N_BSLINE! */
|
||
__define_stab (N_BROWS, 0x48, "BROWS")
|
||
|
||
/* GNU Modula-2 definition module dependency. Value is the modification time
|
||
of the definition file. Other is non-zero if it is imported with the
|
||
GNU M2 keyword %INITIALIZE. Perhaps N_M2C can be used if there
|
||
are enough empty fields? */
|
||
__define_stab(N_DEFD, 0x4a, "DEFD")
|
||
|
||
/* THE FOLLOWING TWO STAB VALUES CONFLICT. Happily, one is for Modula-2
|
||
and one is for C++. Still,... */
|
||
/* GNU C++ exception variable. Name is variable name. */
|
||
__define_stab (N_EHDECL, 0x50, "EHDECL")
|
||
/* Modula2 info "for imc": name,,0,0,0 according to Ultrix V4.0. */
|
||
__define_stab (N_MOD2, 0x50, "MOD2")
|
||
|
||
/* GNU C++ `catch' clause. Value is its address. Desc is nonzero if
|
||
this entry is immediately followed by a CAUGHT stab saying what exception
|
||
was caught. Multiple CAUGHT stabs means that multiple exceptions
|
||
can be caught here. If Desc is 0, it means all exceptions are caught
|
||
here. */
|
||
__define_stab (N_CATCH, 0x54, "CATCH")
|
||
|
||
/* Structure or union element. Value is offset in the structure. */
|
||
__define_stab (N_SSYM, 0x60, "SSYM")
|
||
|
||
/* Name of main source file.
|
||
Value is starting text address of the compilation. */
|
||
__define_stab (N_SO, 0x64, "SO")
|
||
|
||
/* Automatic variable in the stack. Value is offset from frame pointer.
|
||
Also used for type descriptions. */
|
||
__define_stab (N_LSYM, 0x80, "LSYM")
|
||
|
||
/* Beginning of an include file. Only Sun uses this.
|
||
In an object file, only the name is significant.
|
||
The Sun linker puts data into some of the other fields. */
|
||
__define_stab (N_BINCL, 0x82, "BINCL")
|
||
|
||
/* Name of sub-source file (#include file).
|
||
Value is starting text address of the compilation. */
|
||
__define_stab (N_SOL, 0x84, "SOL")
|
||
|
||
/* Parameter variable. Value is offset from argument pointer.
|
||
(On most machines the argument pointer is the same as the frame pointer. */
|
||
__define_stab (N_PSYM, 0xa0, "PSYM")
|
||
|
||
/* End of an include file. No name.
|
||
This and N_BINCL act as brackets around the file's output.
|
||
In an object file, there is no significant data in this entry.
|
||
The Sun linker puts data into some of the fields. */
|
||
__define_stab (N_EINCL, 0xa2, "EINCL")
|
||
|
||
/* Alternate entry point. Value is its address. */
|
||
__define_stab (N_ENTRY, 0xa4, "ENTRY")
|
||
|
||
/* Beginning of lexical block.
|
||
The desc is the nesting level in lexical blocks.
|
||
The value is the address of the start of the text for the block.
|
||
The variables declared inside the block *precede* the N_LBRAC symbol. */
|
||
__define_stab (N_LBRAC, 0xc0, "LBRAC")
|
||
|
||
/* Place holder for deleted include file. Replaces a N_BINCL and everything
|
||
up to the corresponding N_EINCL. The Sun linker generates these when
|
||
it finds multiple identical copies of the symbols from an include file.
|
||
This appears only in output from the Sun linker. */
|
||
__define_stab (N_EXCL, 0xc2, "EXCL")
|
||
|
||
/* Modula-2 scope information. Can someone say what info it contains? */
|
||
__define_stab (N_SCOPE, 0xc4, "SCOPE")
|
||
|
||
/* End of a lexical block. Desc matches the N_LBRAC's desc.
|
||
The value is the address of the end of the text for the block. */
|
||
__define_stab (N_RBRAC, 0xe0, "RBRAC")
|
||
|
||
/* Begin named common block. Only the name is significant. */
|
||
__define_stab (N_BCOMM, 0xe2, "BCOMM")
|
||
|
||
/* End named common block. Only the name is significant
|
||
(and it should match the N_BCOMM). */
|
||
__define_stab (N_ECOMM, 0xe4, "ECOMM")
|
||
|
||
/* End common (local name): value is address.
|
||
I'm not sure how this is used. */
|
||
__define_stab (N_ECOML, 0xe8, "ECOML")
|
||
|
||
/* These STAB's are used on Gould systems for Non-Base register symbols
|
||
or something like that. FIXME. I have assigned the values at random
|
||
since I don't have a Gould here. Fixups from Gould folk welcome... */
|
||
__define_stab (N_NBTEXT, 0xF0, "NBTEXT")
|
||
__define_stab (N_NBDATA, 0xF2, "NBDATA")
|
||
__define_stab (N_NBBSS, 0xF4, "NBBSS")
|
||
__define_stab (N_NBSTS, 0xF6, "NBSTS")
|
||
__define_stab (N_NBLCS, 0xF8, "NBLCS")
|
||
|
||
/* Second symbol entry containing a length-value for the preceding entry.
|
||
The value is the length. */
|
||
__define_stab (N_LENG, 0xfe, "LENG")
|
||
|
||
/* The above information, in matrix format.
|
||
|
||
STAB MATRIX
|
||
_________________________________________________
|
||
| 00 - 1F are not dbx stab symbols |
|
||
| In most cases, the low bit is the EXTernal bit|
|
||
|
||
| 00 UNDEF | 02 ABS | 04 TEXT | 06 DATA |
|
||
| 01 |EXT | 03 |EXT | 05 |EXT | 07 |EXT |
|
||
|
||
| 08 BSS | 0A INDR | 0C FN_SEQ | 0E |
|
||
| 09 |EXT | 0B | 0D | 0F |
|
||
|
||
| 10 | 12 COMM | 14 SETA | 16 SETT |
|
||
| 11 | 13 | 15 | 17 |
|
||
|
||
| 18 SETD | 1A SETB | 1C SETV | 1E WARNING|
|
||
| 19 | 1B | 1D | 1F FN |
|
||
|
||
|_______________________________________________|
|
||
| Debug entries with bit 01 set are unused. |
|
||
| 20 GSYM | 22 FNAME | 24 FUN | 26 STSYM |
|
||
| 28 LCSYM | 2A MAIN | 2C | 2E |
|
||
| 30 PC | 32 NSYMS | 34 NOMAP | 36 |
|
||
| 38 OBJ | 3A | 3C OPT | 3E |
|
||
| 40 RSYM | 42 M2C | 44 SLINE | 46 DSLINE |
|
||
| 48 BSLINE*| 4A DEFD | 4C | 4E |
|
||
| 50 EHDECL*| 52 | 54 CATCH | 56 |
|
||
| 58 | 5A | 5C | 5E |
|
||
| 60 SSYM | 62 | 64 SO | 66 |
|
||
| 68 | 6A | 6C | 6E |
|
||
| 70 | 72 | 74 | 76 |
|
||
| 78 | 7A | 7C | 7E |
|
||
| 80 LSYM | 82 BINCL | 84 SOL | 86 |
|
||
| 88 | 8A | 8C | 8E |
|
||
| 90 | 92 | 94 | 96 |
|
||
| 98 | 9A | 9C | 9E |
|
||
| A0 PSYM | A2 EINCL | A4 ENTRY | A6 |
|
||
| A8 | AA | AC | AE |
|
||
| B0 | B2 | B4 | B6 |
|
||
| B8 | BA | BC | BE |
|
||
| C0 LBRAC | C2 EXCL | C4 SCOPE | C6 |
|
||
| C8 | CA | CC | CE |
|
||
| D0 | D2 | D4 | D6 |
|
||
| D8 | DA | DC | DE |
|
||
| E0 RBRAC | E2 BCOMM | E4 ECOMM | E6 |
|
||
| E8 ECOML | EA | EC | EE |
|
||
| F0 | F2 | F4 | F6 |
|
||
| F8 | FA | FC | FE LENG |
|
||
+-----------------------------------------------+
|
||
* 50 EHDECL is also MOD2.
|
||
* 48 BSLINE is also BROWS.
|
||
*/
|
||
//---------------------------------------------------------------------------
|
||
LAST_UNUSED_STAB_CODE
|
||
};
|
||
|
||
#undef __define_stab
|
||
|
||
#endif /* __GNU_STAB_ */
|
||
//---------------------------------------------------------------------------
|
||
|
||
#ifndef O_BINARY
|
||
#define O_BINARY 0
|
||
#endif
|
||
|
||
// njn: inlined libtcc.h
|
||
//#include "libtcc.h"
|
||
//---------------------------------------------------------------------------
|
||
#ifndef LIBTCC_H
|
||
#define LIBTCC_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
struct TCCState;
|
||
|
||
typedef struct TCCState TCCState;
|
||
|
||
/* create a new TCC compilation context */
|
||
TCCState *tcc_new(void);
|
||
|
||
/* free a TCC compilation context */
|
||
void tcc_delete(TCCState *s);
|
||
|
||
/* add debug information in the generated code */
|
||
void tcc_enable_debug(TCCState *s);
|
||
|
||
/* set error/warning display callback */
|
||
void tcc_set_error_func(TCCState *s, void *error_opaque,
|
||
void (*error_func)(void *opaque, const char *msg));
|
||
|
||
/* set/reset a warning */
|
||
int tcc_set_warning(TCCState *s, const char *warning_name, int value);
|
||
|
||
/*****************************/
|
||
/* preprocessor */
|
||
|
||
/* add include path */
|
||
int tcc_add_include_path(TCCState *s, const char *pathname);
|
||
|
||
/* add in system include path */
|
||
int tcc_add_sysinclude_path(TCCState *s, const char *pathname);
|
||
|
||
/* define preprocessor symbol 'sym'. Can put optional value */
|
||
void tcc_define_symbol(TCCState *s, const char *sym, const char *value);
|
||
|
||
/* undefine preprocess symbol 'sym' */
|
||
void tcc_undefine_symbol(TCCState *s, const char *sym);
|
||
|
||
/*****************************/
|
||
/* compiling */
|
||
|
||
/* add a file (either a C file, dll, an object, a library or an ld
|
||
script). Return -1 if error. */
|
||
int tcc_add_file(TCCState *s, const char *filename);
|
||
|
||
/* compile a string containing a C source. Return non zero if
|
||
error. */
|
||
int tcc_compile_string(TCCState *s, const char *buf);
|
||
|
||
/*****************************/
|
||
/* linking commands */
|
||
|
||
/* set output type. MUST BE CALLED before any compilation */
|
||
#define TCC_OUTPUT_MEMORY 0 /* output will be ran in memory (no
|
||
output file) (default) */
|
||
#define TCC_OUTPUT_EXE 1 /* executable file */
|
||
#define TCC_OUTPUT_DLL 2 /* dynamic library */
|
||
#define TCC_OUTPUT_OBJ 3 /* object file */
|
||
int tcc_set_output_type(TCCState *s, int output_type);
|
||
|
||
#define TCC_OUTPUT_FORMAT_ELF 0 /* default output format: ELF */
|
||
#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
|
||
#define TCC_OUTPUT_FORMAT_COFF 2 /* COFF */
|
||
|
||
/* equivalent to -Lpath option */
|
||
int tcc_add_library_path(TCCState *s, const char *pathname);
|
||
|
||
/* the library name is the same as the argument of the '-l' option */
|
||
int tcc_add_library(TCCState *s, const char *libraryname);
|
||
|
||
/* add a symbol to the compiled program */
|
||
int tcc_add_symbol(TCCState *s, const char *name, unsigned long val);
|
||
|
||
/* output an executable, library or object file. DO NOT call
|
||
tcc_relocate() before. */
|
||
int tcc_output_file(TCCState *s, const char *filename);
|
||
|
||
/* link and run main() function and return its value. DO NOT call
|
||
tcc_relocate() before. */
|
||
int tcc_run(TCCState *s, int argc, char **argv);
|
||
|
||
/* do all relocations (needed before using tcc_get_symbol()). Return
|
||
non zero if link error. */
|
||
int tcc_relocate(TCCState *s);
|
||
|
||
/* return symbol value. return 0 if OK, -1 if symbol not found */
|
||
int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif
|
||
//---------------------------------------------------------------------------
|
||
|
||
/* parser debug */
|
||
//#define PARSE_DEBUG
|
||
/* preprocessor debug */
|
||
//#define PP_DEBUG
|
||
/* include file debug */
|
||
//#define INC_DEBUG
|
||
|
||
//#define MEM_DEBUG
|
||
|
||
/* assembler debug */
|
||
//#define ASM_DEBUG
|
||
|
||
/* target selection */
|
||
//#define TCC_TARGET_I386 /* i386 code generator */
|
||
//#define TCC_TARGET_ARM /* ARMv4 code generator */
|
||
//#define TCC_TARGET_C67 /* TMS320C67xx code generator */
|
||
|
||
/* default target is I386 */
|
||
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
|
||
!defined(TCC_TARGET_C67)
|
||
#define TCC_TARGET_I386
|
||
#endif
|
||
|
||
#if !defined(WIN32) && !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \
|
||
!defined(TCC_TARGET_C67)
|
||
#define CONFIG_TCC_BCHECK /* enable bound checking code */
|
||
#endif
|
||
|
||
#if defined(WIN32) && !defined(TCC_TARGET_PE)
|
||
#define CONFIG_TCC_STATIC
|
||
#endif
|
||
|
||
/* define it to include assembler support */
|
||
#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67)
|
||
#define CONFIG_TCC_ASM
|
||
#endif
|
||
|
||
/* object format selection */
|
||
#if defined(TCC_TARGET_C67)
|
||
#define TCC_TARGET_COFF
|
||
#endif
|
||
|
||
#define FALSE 0
|
||
#define false 0
|
||
#define TRUE 1
|
||
#define true 1
|
||
typedef int BOOL;
|
||
|
||
/* path to find crt1.o, crti.o and crtn.o. Only needed when generating
|
||
executables or dlls */
|
||
#define CONFIG_TCC_CRT_PREFIX "/usr/lib"
|
||
|
||
#define INCLUDE_STACK_SIZE 32
|
||
#define IFDEF_STACK_SIZE 64
|
||
#define VSTACK_SIZE 256
|
||
#define STRING_MAX_SIZE 1024
|
||
#define PACK_STACK_SIZE 8
|
||
|
||
#define TOK_HASH_SIZE 8192 /* must be a power of two */
|
||
#define TOK_ALLOC_INCR 512 /* must be a power of two */
|
||
#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */
|
||
|
||
/* token symbol management */
|
||
typedef struct TokenSym {
|
||
struct TokenSym *hash_next;
|
||
struct Sym *sym_define; /* direct pointer to define */
|
||
struct Sym *sym_label; /* direct pointer to label */
|
||
struct Sym *sym_struct; /* direct pointer to structure */
|
||
struct Sym *sym_identifier; /* direct pointer to identifier */
|
||
int tok; /* token number */
|
||
int len;
|
||
char str[1];
|
||
} TokenSym;
|
||
|
||
typedef struct CString {
|
||
int size; /* size in bytes */
|
||
void *data; /* either 'char *' or 'int *' */
|
||
int size_allocated;
|
||
void *data_allocated; /* if non NULL, data has been malloced */
|
||
} CString;
|
||
|
||
/* type definition */
|
||
typedef struct CType {
|
||
int t;
|
||
struct Sym *ref;
|
||
} CType;
|
||
|
||
/* constant value */
|
||
typedef union CValue {
|
||
long double ld;
|
||
double d;
|
||
float f;
|
||
int i;
|
||
unsigned int ui;
|
||
unsigned int ul; /* address (should be unsigned long on 64 bit cpu) */
|
||
long long ll;
|
||
unsigned long long ull;
|
||
struct CString *cstr;
|
||
void *ptr;
|
||
int tab[sizeof(long double) / sizeof(int)];
|
||
} CValue;
|
||
|
||
/* value on stack */
|
||
typedef struct SValue {
|
||
CType type; /* type */
|
||
unsigned short r; /* register + flags */
|
||
unsigned short r2; /* second register, used for 'long long'
|
||
type. If not used, set to VT_CONST */
|
||
CValue c; /* constant, if VT_CONST */
|
||
struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */
|
||
} SValue;
|
||
|
||
/* symbol management */
|
||
typedef struct Sym {
|
||
long v; /* symbol token */
|
||
long r; /* associated register */
|
||
long c; /* associated number */
|
||
CType type; /* associated type */
|
||
struct Sym *next; /* next related symbol */
|
||
struct Sym *prev; /* prev symbol in stack */
|
||
struct Sym *prev_tok; /* previous symbol for this token */
|
||
} Sym;
|
||
|
||
/* section definition */
|
||
/* XXX: use directly ELF structure for parameters ? */
|
||
/* special flag to indicate that the section should not be linked to
|
||
the other ones */
|
||
#define SHF_PRIVATE 0x80000000
|
||
|
||
typedef struct Section {
|
||
unsigned long data_offset; /* current data offset */
|
||
unsigned char *data; /* section data */
|
||
unsigned long data_allocated; /* used for realloc() handling */
|
||
int sh_name; /* elf section name (only used during output) */
|
||
int sh_num; /* elf section number */
|
||
int sh_type; /* elf section type */
|
||
int sh_flags; /* elf section flags */
|
||
int sh_info; /* elf section info */
|
||
int sh_addralign; /* elf section alignment */
|
||
int sh_entsize; /* elf entry size */
|
||
unsigned long sh_size; /* section size (only used during output) */
|
||
unsigned long sh_addr; /* address at which the section is relocated */
|
||
unsigned long sh_offset; /* address at which the section is relocated */
|
||
int nb_hashed_syms; /* used to resize the hash table */
|
||
struct Section *link; /* link to another section */
|
||
struct Section *reloc; /* corresponding section for relocation, if any */
|
||
struct Section *hash; /* hash table for symbols */
|
||
struct Section *next;
|
||
char name[1]; /* section name */
|
||
} Section;
|
||
|
||
typedef struct DLLReference {
|
||
int level;
|
||
char name[1];
|
||
} DLLReference;
|
||
|
||
/* GNUC attribute definition */
|
||
typedef struct AttributeDef {
|
||
int aligned;
|
||
int packed;
|
||
Section *section;
|
||
unsigned char func_call; /* FUNC_CDECL, FUNC_STDCALL, FUNC_FASTCALLx */
|
||
unsigned char dllexport;
|
||
} AttributeDef;
|
||
|
||
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
|
||
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
|
||
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
|
||
|
||
/* stored in 'Sym.c' field */
|
||
#define FUNC_NEW 1 /* ansi function prototype */
|
||
#define FUNC_OLD 2 /* old function prototype */
|
||
#define FUNC_ELLIPSIS 3 /* ansi function prototype with ... */
|
||
|
||
/* stored in 'Sym.r' field */
|
||
#define FUNC_CDECL 0 /* standard c call */
|
||
#define FUNC_STDCALL 1 /* pascal c call */
|
||
#define FUNC_FASTCALL1 2 /* first param in %eax */
|
||
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
|
||
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
|
||
|
||
/* field 'Sym.t' for macros */
|
||
#define MACRO_OBJ 0 /* object like macro */
|
||
#define MACRO_FUNC 1 /* function like macro */
|
||
|
||
/* field 'Sym.r' for C labels */
|
||
#define LABEL_DEFINED 0 /* label is defined */
|
||
#define LABEL_FORWARD 1 /* label is forward defined */
|
||
#define LABEL_DECLARED 2 /* label is declared but never used */
|
||
|
||
/* type_decl() types */
|
||
#define TYPE_ABSTRACT 1 /* type without variable */
|
||
#define TYPE_DIRECT 2 /* type with variable */
|
||
|
||
#define IO_BUF_SIZE 8192
|
||
|
||
typedef struct BufferedFile {
|
||
uint8_t *buf_ptr;
|
||
uint8_t *buf_end;
|
||
int fd;
|
||
int line_num; /* current line number - here to simplify code */
|
||
int ifndef_macro; /* #ifndef macro / #endif search */
|
||
int ifndef_macro_saved; /* saved ifndef_macro */
|
||
int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
|
||
char inc_type; /* type of include */
|
||
char inc_filename[512]; /* filename specified by the user */
|
||
char filename[1024]; /* current filename - here to simplify code */
|
||
unsigned char buffer[IO_BUF_SIZE + 1]; /* extra size for CH_EOB char */
|
||
} BufferedFile;
|
||
|
||
#define CH_EOB '\\' /* end of buffer or '\0' char in file */
|
||
#define CH_EOF (-1) /* end of file */
|
||
|
||
/* parsing state (used to save parser state to reparse part of the
|
||
source several times) */
|
||
typedef struct ParseState {
|
||
int *macro_ptr;
|
||
int line_num;
|
||
int tok;
|
||
CValue tokc;
|
||
} ParseState;
|
||
|
||
/* used to record tokens */
|
||
typedef struct TokenString {
|
||
int *str;
|
||
int len;
|
||
int allocated_len;
|
||
int last_line_num;
|
||
} TokenString;
|
||
|
||
/* include file cache, used to find files faster and also to eliminate
|
||
inclusion if the include file is protected by #ifndef ... #endif */
|
||
typedef struct CachedInclude {
|
||
int ifndef_macro;
|
||
int hash_next; /* -1 if none */
|
||
char type; /* '"' or '>' to give include type */
|
||
char filename[1]; /* path specified in #include */
|
||
} CachedInclude;
|
||
|
||
#define CACHED_INCLUDES_HASH_SIZE 512
|
||
|
||
/* parser */
|
||
static struct BufferedFile *file;
|
||
static int ch, tok;
|
||
static CValue tokc;
|
||
static CString tokcstr; /* current parsed string, if any */
|
||
/* additional informations about token */
|
||
static int tok_flags;
|
||
#define TOK_FLAG_BOL 0x0001 /* beginning of line before */
|
||
#define TOK_FLAG_BOF 0x0002 /* beginning of file before */
|
||
#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
|
||
|
||
static int *macro_ptr, *macro_ptr_allocated;
|
||
static int *unget_saved_macro_ptr;
|
||
static int unget_saved_buffer[TOK_MAX_SIZE + 1];
|
||
static int unget_buffer_enabled;
|
||
static int parse_flags;
|
||
#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
|
||
#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */
|
||
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
|
||
token. line feed is also
|
||
returned at eof */
|
||
#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */
|
||
|
||
static Section *text_section, *data_section, *bss_section; /* predefined sections */
|
||
static Section *cur_text_section; /* current section where function code is
|
||
generated */
|
||
#ifdef CONFIG_TCC_ASM
|
||
static Section *last_text_section; /* to handle .previous asm directive */
|
||
#endif
|
||
/* bound check related sections */
|
||
static Section *bounds_section; /* contains global data bound description */
|
||
static Section *lbounds_section; /* contains local data bound description */
|
||
/* symbol sections */
|
||
static Section *symtab_section, *strtab_section;
|
||
|
||
/* debug sections */
|
||
static Section *stab_section, *stabstr_section;
|
||
|
||
/* loc : local variable index
|
||
ind : output code index
|
||
rsym: return symbol
|
||
anon_sym: anonymous symbol index
|
||
*/
|
||
static long rsym, anon_sym, ind, loc;
|
||
/* expression generation modifiers */
|
||
static int const_wanted; /* true if constant wanted */
|
||
static int nocode_wanted; /* true if no code generation wanted for an expression */
|
||
static int global_expr; /* true if compound literals must be allocated
|
||
globally (used during initializers parsing */
|
||
static CType func_vt; /* current function return type (used by return
|
||
instruction) */
|
||
static int func_vc;
|
||
static long last_line_num, last_ind, func_ind; /* debug last line number and pc */
|
||
static int tok_ident;
|
||
static TokenSym **table_ident;
|
||
static TokenSym *hash_ident[TOK_HASH_SIZE];
|
||
static char token_buf[STRING_MAX_SIZE + 1];
|
||
static char *funcname;
|
||
static Sym *global_stack, *local_stack;
|
||
static Sym *define_stack;
|
||
static Sym *global_label_stack, *local_label_stack;
|
||
/* symbol allocator */
|
||
#define SYM_POOL_NB (8192 / sizeof(Sym))
|
||
static Sym *sym_free_first;
|
||
|
||
static SValue vstack[VSTACK_SIZE], *vtop;
|
||
/* some predefined types */
|
||
static CType char_pointer_type, func_old_type, int_type;
|
||
/* true if isid(c) || isnum(c) */
|
||
static unsigned char isidnum_table[256];
|
||
|
||
/* compile with debug symbol (and use them if error during execution) */
|
||
static int do_debug = 0;
|
||
|
||
/* compile with built-in memory and bounds checker */
|
||
static int do_bounds_check = 0;
|
||
|
||
/* display benchmark infos */
|
||
#if !defined(LIBTCC)
|
||
static int do_bench = 0;
|
||
#endif
|
||
static int total_lines;
|
||
static int total_bytes;
|
||
|
||
/* use GNU C extensions */
|
||
static int gnu_ext = 1;
|
||
|
||
/* use Tiny C extensions */
|
||
static int tcc_ext = 1;
|
||
|
||
/* max number of callers shown if error */
|
||
static int num_callers = 6;
|
||
static const char **rt_bound_error_msg;
|
||
|
||
/* XXX: get rid of this ASAP */
|
||
static struct TCCState *tcc_state;
|
||
|
||
/* give the path of the tcc libraries */
|
||
static const char *tcc_lib_path = CONFIG_TCCDIR;
|
||
|
||
struct TCCState {
|
||
int output_type;
|
||
|
||
BufferedFile **include_stack_ptr;
|
||
int *ifdef_stack_ptr;
|
||
|
||
/* include file handling */
|
||
char **include_paths;
|
||
int nb_include_paths;
|
||
char **sysinclude_paths;
|
||
int nb_sysinclude_paths;
|
||
CachedInclude **cached_includes;
|
||
int nb_cached_includes;
|
||
|
||
char **library_paths;
|
||
int nb_library_paths;
|
||
|
||
/* array of all loaded dlls (including those referenced by loaded
|
||
dlls) */
|
||
DLLReference **loaded_dlls;
|
||
int nb_loaded_dlls;
|
||
|
||
/* sections */
|
||
Section **sections;
|
||
int nb_sections; /* number of sections, including first dummy section */
|
||
|
||
/* got handling */
|
||
Section *got;
|
||
Section *plt;
|
||
unsigned long *got_offsets;
|
||
int nb_got_offsets;
|
||
/* give the correspondance from symtab indexes to dynsym indexes */
|
||
int *symtab_to_dynsym;
|
||
|
||
/* temporary dynamic symbol sections (for dll loading) */
|
||
Section *dynsymtab_section;
|
||
/* exported dynamic symbol section */
|
||
Section *dynsym;
|
||
|
||
int nostdinc; /* if true, no standard headers are added */
|
||
int nostdlib; /* if true, no standard libraries are added */
|
||
|
||
int nocommon; /* if true, do not use common symbols for .bss data */
|
||
|
||
/* if true, static linking is performed */
|
||
int static_link;
|
||
|
||
/* if true, all symbols are exported */
|
||
int rdynamic;
|
||
|
||
/* if true, only link in referenced objects from archive */
|
||
int alacarte_link;
|
||
|
||
/* address of text section */
|
||
unsigned long text_addr;
|
||
int has_text_addr;
|
||
|
||
/* output format, see TCC_OUTPUT_FORMAT_xxx */
|
||
int output_format;
|
||
|
||
/* C language options */
|
||
int char_is_unsigned;
|
||
int leading_underscore;
|
||
|
||
/* warning switches */
|
||
int warn_write_strings;
|
||
int warn_unsupported;
|
||
int warn_error;
|
||
int warn_none;
|
||
int warn_implicit_function_declaration;
|
||
|
||
/* error handling */
|
||
void *error_opaque;
|
||
void (*error_func)(void *opaque, const char *msg);
|
||
int error_set_jmp_enabled;
|
||
jmp_buf error_jmp_buf;
|
||
int nb_errors;
|
||
|
||
/* tiny assembler state */
|
||
Sym *asm_labels;
|
||
|
||
/* see include_stack_ptr */
|
||
BufferedFile *include_stack[INCLUDE_STACK_SIZE];
|
||
|
||
/* see ifdef_stack_ptr */
|
||
int ifdef_stack[IFDEF_STACK_SIZE];
|
||
|
||
/* see cached_includes */
|
||
int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
|
||
|
||
/* pack stack */
|
||
int pack_stack[PACK_STACK_SIZE];
|
||
int *pack_stack_ptr;
|
||
};
|
||
|
||
/* The current value can be: */
|
||
#define VT_VALMASK 0x00ff
|
||
#define VT_CONST 0x00f0 /* constant in vc
|
||
(must be first non register value) */
|
||
#define VT_LLOCAL 0x00f1 /* lvalue, offset on stack */
|
||
#define VT_LOCAL 0x00f2 /* offset on stack */
|
||
#define VT_CMP 0x00f3 /* the value is stored in processor flags (in vc) */
|
||
#define VT_JMP 0x00f4 /* value is the consequence of jmp true (even) */
|
||
#define VT_JMPI 0x00f5 /* value is the consequence of jmp false (odd) */
|
||
#define VT_LVAL 0x0100 /* var is an lvalue */
|
||
#define VT_SYM 0x0200 /* a symbol value is added */
|
||
#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for
|
||
char/short stored in integer registers) */
|
||
#define VT_MUSTBOUND 0x0800 /* bound checking must be done before
|
||
dereferencing value */
|
||
#define VT_BOUNDED 0x8000 /* value is bounded. The address of the
|
||
bounding function call point is in vc */
|
||
#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */
|
||
#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */
|
||
#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */
|
||
#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
|
||
|
||
/* types */
|
||
#define VT_INT 0 /* integer type */
|
||
#define VT_BYTE 1 /* signed byte type */
|
||
#define VT_SHORT 2 /* short type */
|
||
#define VT_VOID 3 /* void type */
|
||
#define VT_PTR 4 /* pointer */
|
||
#define VT_ENUM 5 /* enum definition */
|
||
#define VT_FUNC 6 /* function type */
|
||
#define VT_STRUCT 7 /* struct/union definition */
|
||
#define VT_FLOAT 8 /* IEEE float */
|
||
#define VT_DOUBLE 9 /* IEEE double */
|
||
#define VT_LDOUBLE 10 /* IEEE long double */
|
||
#define VT_BOOL 11 /* ISOC99 boolean type */
|
||
#define VT_LLONG 12 /* 64 bit integer */
|
||
#define VT_LONG 13 /* long integer (NEVER USED as type, only
|
||
during parsing) */
|
||
#define VT_BTYPE 0x000f /* mask for basic type */
|
||
#define VT_UNSIGNED 0x0010 /* unsigned type */
|
||
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
|
||
#define VT_BITFIELD 0x0040 /* bitfield modifier */
|
||
#define VT_CONSTANT 0x0800 /* const modifier */
|
||
#define VT_VOLATILE 0x1000 /* volatile modifier */
|
||
#define VT_SIGNED 0x2000 /* signed type */
|
||
|
||
/* storage */
|
||
#define VT_EXTERN 0x00000080 /* extern definition */
|
||
#define VT_STATIC 0x00000100 /* static variable */
|
||
#define VT_TYPEDEF 0x00000200 /* typedef definition */
|
||
#define VT_INLINE 0x00000400 /* inline definition */
|
||
|
||
#define VT_STRUCT_SHIFT 16 /* shift for bitfield shift values */
|
||
|
||
/* type mask (except storage) */
|
||
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
|
||
#define VT_TYPE (~(VT_STORAGE))
|
||
|
||
/* token values */
|
||
|
||
/* warning: the following compare tokens depend on i386 asm code */
|
||
#define TOK_ULT 0x92
|
||
#define TOK_UGE 0x93
|
||
#define TOK_EQ 0x94
|
||
#define TOK_NE 0x95
|
||
#define TOK_ULE 0x96
|
||
#define TOK_UGT 0x97
|
||
#define TOK_LT 0x9c
|
||
#define TOK_GE 0x9d
|
||
#define TOK_LE 0x9e
|
||
#define TOK_GT 0x9f
|
||
|
||
#define TOK_LAND 0xa0
|
||
#define TOK_LOR 0xa1
|
||
|
||
#define TOK_DEC 0xa2
|
||
#define TOK_MID 0xa3 /* inc/dec, to void constant */
|
||
#define TOK_INC 0xa4
|
||
#define TOK_UDIV 0xb0 /* unsigned division */
|
||
#define TOK_UMOD 0xb1 /* unsigned modulo */
|
||
#define TOK_PDIV 0xb2 /* fast division with undefined rounding for pointers */
|
||
#define TOK_CINT 0xb3 /* number in tokc */
|
||
#define TOK_CCHAR 0xb4 /* char constant in tokc */
|
||
#define TOK_STR 0xb5 /* pointer to string in tokc */
|
||
#define TOK_TWOSHARPS 0xb6 /* ## preprocessing token */
|
||
#define TOK_LCHAR 0xb7
|
||
#define TOK_LSTR 0xb8
|
||
#define TOK_CFLOAT 0xb9 /* float constant */
|
||
#define TOK_LINENUM 0xba /* line number info */
|
||
#define TOK_CDOUBLE 0xc0 /* double constant */
|
||
#define TOK_CLDOUBLE 0xc1 /* long double constant */
|
||
#define TOK_UMULL 0xc2 /* unsigned 32x32 -> 64 mul */
|
||
#define TOK_ADDC1 0xc3 /* add with carry generation */
|
||
#define TOK_ADDC2 0xc4 /* add with carry use */
|
||
#define TOK_SUBC1 0xc5 /* add with carry generation */
|
||
#define TOK_SUBC2 0xc6 /* add with carry use */
|
||
#define TOK_CUINT 0xc8 /* unsigned int constant */
|
||
#define TOK_CLLONG 0xc9 /* long long constant */
|
||
#define TOK_CULLONG 0xca /* unsigned long long constant */
|
||
#define TOK_ARROW 0xcb
|
||
#define TOK_DOTS 0xcc /* three dots */
|
||
#define TOK_SHR 0xcd /* unsigned shift right */
|
||
#define TOK_PPNUM 0xce /* preprocessor number */
|
||
|
||
#define TOK_SHL 0x01 /* shift left */
|
||
#define TOK_SAR 0x02 /* signed shift right */
|
||
|
||
/* assignement operators : normal operator or 0x80 */
|
||
#define TOK_A_MOD 0xa5
|
||
#define TOK_A_AND 0xa6
|
||
#define TOK_A_MUL 0xaa
|
||
#define TOK_A_ADD 0xab
|
||
#define TOK_A_SUB 0xad
|
||
#define TOK_A_DIV 0xaf
|
||
#define TOK_A_XOR 0xde
|
||
#define TOK_A_OR 0xfc
|
||
#define TOK_A_SHL 0x81
|
||
#define TOK_A_SAR 0x82
|
||
|
||
#ifndef offsetof
|
||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||
#endif
|
||
|
||
#ifndef countof
|
||
#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
|
||
#endif
|
||
|
||
/* WARNING: the content of this string encodes token numbers */
|
||
static char tok_two_chars[] = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
|
||
|
||
#define TOK_EOF (-1) /* end of file */
|
||
#define TOK_LINEFEED 10 /* line feed */
|
||
|
||
/* all identificators and strings have token above that */
|
||
#define TOK_IDENT 256
|
||
|
||
/* only used for i386 asm opcodes definitions */
|
||
#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
|
||
|
||
#define DEF_BWL(x) \
|
||
DEF(TOK_ASM_ ## x ## b, #x "b") \
|
||
DEF(TOK_ASM_ ## x ## w, #x "w") \
|
||
DEF(TOK_ASM_ ## x ## l, #x "l") \
|
||
DEF(TOK_ASM_ ## x, #x)
|
||
|
||
#define DEF_WL(x) \
|
||
DEF(TOK_ASM_ ## x ## w, #x "w") \
|
||
DEF(TOK_ASM_ ## x ## l, #x "l") \
|
||
DEF(TOK_ASM_ ## x, #x)
|
||
|
||
#define DEF_FP1(x) \
|
||
DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
|
||
DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
|
||
DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
|
||
DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")
|
||
|
||
#define DEF_FP(x) \
|
||
DEF(TOK_ASM_ ## f ## x, "f" #x ) \
|
||
DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
|
||
DEF_FP1(x)
|
||
|
||
#define DEF_ASMTEST(x) \
|
||
DEF_ASM(x ## o) \
|
||
DEF_ASM(x ## no) \
|
||
DEF_ASM(x ## b) \
|
||
DEF_ASM(x ## c) \
|
||
DEF_ASM(x ## nae) \
|
||
DEF_ASM(x ## nb) \
|
||
DEF_ASM(x ## nc) \
|
||
DEF_ASM(x ## ae) \
|
||
DEF_ASM(x ## e) \
|
||
DEF_ASM(x ## z) \
|
||
DEF_ASM(x ## ne) \
|
||
DEF_ASM(x ## nz) \
|
||
DEF_ASM(x ## be) \
|
||
DEF_ASM(x ## na) \
|
||
DEF_ASM(x ## nbe) \
|
||
DEF_ASM(x ## a) \
|
||
DEF_ASM(x ## s) \
|
||
DEF_ASM(x ## ns) \
|
||
DEF_ASM(x ## p) \
|
||
DEF_ASM(x ## pe) \
|
||
DEF_ASM(x ## np) \
|
||
DEF_ASM(x ## po) \
|
||
DEF_ASM(x ## l) \
|
||
DEF_ASM(x ## nge) \
|
||
DEF_ASM(x ## nl) \
|
||
DEF_ASM(x ## ge) \
|
||
DEF_ASM(x ## le) \
|
||
DEF_ASM(x ## ng) \
|
||
DEF_ASM(x ## nle) \
|
||
DEF_ASM(x ## g)
|
||
|
||
#define TOK_ASM_int TOK_INT
|
||
|
||
enum tcc_token {
|
||
TOK_LAST = TOK_IDENT - 1,
|
||
#define DEF(id, str) id,
|
||
// njn: inlined tcctok.h
|
||
//#include "tcctok.h"
|
||
//---------------------------------------------------------------------------
|
||
/* keywords */
|
||
DEF(TOK_INT, "int")
|
||
DEF(TOK_VOID, "void")
|
||
DEF(TOK_CHAR, "char")
|
||
DEF(TOK_IF, "if")
|
||
DEF(TOK_ELSE, "else")
|
||
DEF(TOK_WHILE, "while")
|
||
DEF(TOK_BREAK, "break")
|
||
DEF(TOK_RETURN, "return")
|
||
DEF(TOK_FOR, "for")
|
||
DEF(TOK_EXTERN, "extern")
|
||
DEF(TOK_STATIC, "static")
|
||
DEF(TOK_UNSIGNED, "unsigned")
|
||
DEF(TOK_GOTO, "goto")
|
||
DEF(TOK_DO, "do")
|
||
DEF(TOK_CONTINUE, "continue")
|
||
DEF(TOK_SWITCH, "switch")
|
||
DEF(TOK_CASE, "case")
|
||
|
||
DEF(TOK_CONST1, "const")
|
||
DEF(TOK_CONST2, "__const") /* gcc keyword */
|
||
DEF(TOK_CONST3, "__const__") /* gcc keyword */
|
||
DEF(TOK_VOLATILE1, "volatile")
|
||
DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
|
||
DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
|
||
DEF(TOK_LONG, "long")
|
||
DEF(TOK_REGISTER, "register")
|
||
DEF(TOK_SIGNED1, "signed")
|
||
DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
|
||
DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
|
||
DEF(TOK_AUTO, "auto")
|
||
DEF(TOK_INLINE1, "inline")
|
||
DEF(TOK_INLINE2, "__inline") /* gcc keyword */
|
||
DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
|
||
DEF(TOK_RESTRICT1, "restrict")
|
||
DEF(TOK_RESTRICT2, "__restrict")
|
||
DEF(TOK_RESTRICT3, "__restrict__")
|
||
DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
|
||
|
||
DEF(TOK_FLOAT, "float")
|
||
DEF(TOK_DOUBLE, "double")
|
||
DEF(TOK_BOOL, "_Bool")
|
||
DEF(TOK_SHORT, "short")
|
||
DEF(TOK_STRUCT, "struct")
|
||
DEF(TOK_UNION, "union")
|
||
DEF(TOK_TYPEDEF, "typedef")
|
||
DEF(TOK_DEFAULT, "default")
|
||
DEF(TOK_ENUM, "enum")
|
||
DEF(TOK_SIZEOF, "sizeof")
|
||
DEF(TOK_ATTRIBUTE1, "__attribute")
|
||
DEF(TOK_ATTRIBUTE2, "__attribute__")
|
||
DEF(TOK_ALIGNOF1, "__alignof")
|
||
DEF(TOK_ALIGNOF2, "__alignof__")
|
||
DEF(TOK_TYPEOF1, "typeof")
|
||
DEF(TOK_TYPEOF2, "__typeof")
|
||
DEF(TOK_TYPEOF3, "__typeof__")
|
||
DEF(TOK_LABEL, "__label__")
|
||
DEF(TOK_ASM1, "asm")
|
||
DEF(TOK_ASM2, "__asm")
|
||
DEF(TOK_ASM3, "__asm__")
|
||
|
||
/*********************************************************************/
|
||
/* the following are not keywords. They are included to ease parsing */
|
||
/* preprocessor only */
|
||
DEF(TOK_DEFINE, "define")
|
||
DEF(TOK_INCLUDE, "include")
|
||
DEF(TOK_INCLUDE_NEXT, "include_next")
|
||
DEF(TOK_IFDEF, "ifdef")
|
||
DEF(TOK_IFNDEF, "ifndef")
|
||
DEF(TOK_ELIF, "elif")
|
||
DEF(TOK_ENDIF, "endif")
|
||
DEF(TOK_DEFINED, "defined")
|
||
DEF(TOK_UNDEF, "undef")
|
||
DEF(TOK_ERROR, "error")
|
||
DEF(TOK_WARNING, "warning")
|
||
DEF(TOK_LINE, "line")
|
||
DEF(TOK_PRAGMA, "pragma")
|
||
DEF(TOK___LINE__, "__LINE__")
|
||
DEF(TOK___FILE__, "__FILE__")
|
||
DEF(TOK___DATE__, "__DATE__")
|
||
DEF(TOK___TIME__, "__TIME__")
|
||
DEF(TOK___FUNCTION__, "__FUNCTION__")
|
||
DEF(TOK___VA_ARGS__, "__VA_ARGS__")
|
||
|
||
/* special identifiers */
|
||
DEF(TOK___FUNC__, "__func__")
|
||
|
||
/* attribute identifiers */
|
||
/* XXX: handle all tokens generically since speed is not critical */
|
||
DEF(TOK_SECTION1, "section")
|
||
DEF(TOK_SECTION2, "__section__")
|
||
DEF(TOK_ALIGNED1, "aligned")
|
||
DEF(TOK_ALIGNED2, "__aligned__")
|
||
DEF(TOK_PACKED1, "packed")
|
||
DEF(TOK_PACKED2, "__packed__")
|
||
DEF(TOK_UNUSED1, "unused")
|
||
DEF(TOK_UNUSED2, "__unused__")
|
||
DEF(TOK_CDECL1, "cdecl")
|
||
DEF(TOK_CDECL2, "__cdecl")
|
||
DEF(TOK_CDECL3, "__cdecl__")
|
||
DEF(TOK_STDCALL1, "stdcall")
|
||
DEF(TOK_STDCALL2, "__stdcall")
|
||
DEF(TOK_STDCALL3, "__stdcall__")
|
||
DEF(TOK_DLLEXPORT, "dllexport")
|
||
DEF(TOK_NORETURN1, "noreturn")
|
||
DEF(TOK_NORETURN2, "__noreturn__")
|
||
DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
|
||
DEF(TOK_builtin_constant_p, "__builtin_constant_p")
|
||
DEF(TOK_REGPARM1, "regparm")
|
||
DEF(TOK_REGPARM2, "__regparm__")
|
||
|
||
/* pragma */
|
||
DEF(TOK_pack, "pack")
|
||
#if !defined(TCC_TARGET_I386)
|
||
/* already defined for assembler */
|
||
DEF(TOK_ASM_push, "push")
|
||
DEF(TOK_ASM_pop, "pop")
|
||
#endif
|
||
|
||
/* builtin functions or variables */
|
||
DEF(TOK_memcpy, "memcpy")
|
||
DEF(TOK_memset, "memset")
|
||
DEF(TOK_alloca, "alloca")
|
||
DEF(TOK___divdi3, "__divdi3")
|
||
DEF(TOK___moddi3, "__moddi3")
|
||
DEF(TOK___udivdi3, "__udivdi3")
|
||
DEF(TOK___umoddi3, "__umoddi3")
|
||
#if defined(TCC_TARGET_ARM)
|
||
DEF(TOK___divsi3, "__divsi3")
|
||
DEF(TOK___modsi3, "__modsi3")
|
||
DEF(TOK___udivsi3, "__udivsi3")
|
||
DEF(TOK___umodsi3, "__umodsi3")
|
||
DEF(TOK___sardi3, "__ashrdi3")
|
||
DEF(TOK___shrdi3, "__lshrdi3")
|
||
DEF(TOK___shldi3, "__ashldi3")
|
||
DEF(TOK___slltold, "__slltold")
|
||
DEF(TOK___fixunssfsi, "__fixunssfsi")
|
||
DEF(TOK___fixunsdfsi, "__fixunsdfsi")
|
||
DEF(TOK___fixunsxfsi, "__fixunsxfsi")
|
||
DEF(TOK___fixsfdi, "__fixsfdi")
|
||
DEF(TOK___fixdfdi, "__fixdfdi")
|
||
DEF(TOK___fixxfdi, "__fixxfdi")
|
||
#elif defined(TCC_TARGET_C67)
|
||
DEF(TOK__divi, "_divi")
|
||
DEF(TOK__divu, "_divu")
|
||
DEF(TOK__divf, "_divf")
|
||
DEF(TOK__divd, "_divd")
|
||
DEF(TOK__remi, "_remi")
|
||
DEF(TOK__remu, "_remu")
|
||
DEF(TOK___sardi3, "__sardi3")
|
||
DEF(TOK___shrdi3, "__shrdi3")
|
||
DEF(TOK___shldi3, "__shldi3")
|
||
#else
|
||
/* XXX: same names on i386 ? */
|
||
DEF(TOK___sardi3, "__sardi3")
|
||
DEF(TOK___shrdi3, "__shrdi3")
|
||
DEF(TOK___shldi3, "__shldi3")
|
||
#endif
|
||
DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control")
|
||
DEF(TOK___tcc_fpu_control, "__tcc_fpu_control")
|
||
DEF(TOK___ulltof, "__ulltof")
|
||
DEF(TOK___ulltod, "__ulltod")
|
||
DEF(TOK___ulltold, "__ulltold")
|
||
DEF(TOK___fixunssfdi, "__fixunssfdi")
|
||
DEF(TOK___fixunsdfdi, "__fixunsdfdi")
|
||
DEF(TOK___fixunsxfdi, "__fixunsxfdi")
|
||
DEF(TOK___chkstk, "__chkstk")
|
||
|
||
/* bound checking symbols */
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
DEF(TOK___bound_ptr_add, "__bound_ptr_add")
|
||
DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
|
||
DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
|
||
DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
|
||
DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
|
||
DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
|
||
DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
|
||
DEF(TOK___bound_local_new, "__bound_local_new")
|
||
DEF(TOK___bound_local_delete, "__bound_local_delete")
|
||
DEF(TOK_malloc, "malloc")
|
||
DEF(TOK_free, "free")
|
||
DEF(TOK_realloc, "realloc")
|
||
DEF(TOK_memalign, "memalign")
|
||
DEF(TOK_calloc, "calloc")
|
||
DEF(TOK_memmove, "memmove")
|
||
DEF(TOK_strlen, "strlen")
|
||
DEF(TOK_strcpy, "strcpy")
|
||
#endif
|
||
|
||
/* Tiny Assembler */
|
||
|
||
DEF_ASM(byte)
|
||
DEF_ASM(align)
|
||
DEF_ASM(skip)
|
||
DEF_ASM(space)
|
||
DEF_ASM(string)
|
||
DEF_ASM(asciz)
|
||
DEF_ASM(ascii)
|
||
DEF_ASM(globl)
|
||
DEF_ASM(global)
|
||
DEF_ASM(text)
|
||
DEF_ASM(data)
|
||
DEF_ASM(bss)
|
||
DEF_ASM(previous)
|
||
DEF_ASM(fill)
|
||
DEF_ASM(org)
|
||
DEF_ASM(quad)
|
||
|
||
#ifdef TCC_TARGET_I386
|
||
|
||
/* WARNING: relative order of tokens is important. */
|
||
DEF_ASM(al)
|
||
DEF_ASM(cl)
|
||
DEF_ASM(dl)
|
||
DEF_ASM(bl)
|
||
DEF_ASM(ah)
|
||
DEF_ASM(ch)
|
||
DEF_ASM(dh)
|
||
DEF_ASM(bh)
|
||
DEF_ASM(ax)
|
||
DEF_ASM(cx)
|
||
DEF_ASM(dx)
|
||
DEF_ASM(bx)
|
||
DEF_ASM(sp)
|
||
DEF_ASM(bp)
|
||
DEF_ASM(si)
|
||
DEF_ASM(di)
|
||
DEF_ASM(eax)
|
||
DEF_ASM(ecx)
|
||
DEF_ASM(edx)
|
||
DEF_ASM(ebx)
|
||
DEF_ASM(esp)
|
||
DEF_ASM(ebp)
|
||
DEF_ASM(esi)
|
||
DEF_ASM(edi)
|
||
DEF_ASM(mm0)
|
||
DEF_ASM(mm1)
|
||
DEF_ASM(mm2)
|
||
DEF_ASM(mm3)
|
||
DEF_ASM(mm4)
|
||
DEF_ASM(mm5)
|
||
DEF_ASM(mm6)
|
||
DEF_ASM(mm7)
|
||
DEF_ASM(xmm0)
|
||
DEF_ASM(xmm1)
|
||
DEF_ASM(xmm2)
|
||
DEF_ASM(xmm3)
|
||
DEF_ASM(xmm4)
|
||
DEF_ASM(xmm5)
|
||
DEF_ASM(xmm6)
|
||
DEF_ASM(xmm7)
|
||
DEF_ASM(cr0)
|
||
DEF_ASM(cr1)
|
||
DEF_ASM(cr2)
|
||
DEF_ASM(cr3)
|
||
DEF_ASM(cr4)
|
||
DEF_ASM(cr5)
|
||
DEF_ASM(cr6)
|
||
DEF_ASM(cr7)
|
||
DEF_ASM(tr0)
|
||
DEF_ASM(tr1)
|
||
DEF_ASM(tr2)
|
||
DEF_ASM(tr3)
|
||
DEF_ASM(tr4)
|
||
DEF_ASM(tr5)
|
||
DEF_ASM(tr6)
|
||
DEF_ASM(tr7)
|
||
DEF_ASM(db0)
|
||
DEF_ASM(db1)
|
||
DEF_ASM(db2)
|
||
DEF_ASM(db3)
|
||
DEF_ASM(db4)
|
||
DEF_ASM(db5)
|
||
DEF_ASM(db6)
|
||
DEF_ASM(db7)
|
||
DEF_ASM(dr0)
|
||
DEF_ASM(dr1)
|
||
DEF_ASM(dr2)
|
||
DEF_ASM(dr3)
|
||
DEF_ASM(dr4)
|
||
DEF_ASM(dr5)
|
||
DEF_ASM(dr6)
|
||
DEF_ASM(dr7)
|
||
DEF_ASM(es)
|
||
DEF_ASM(cs)
|
||
DEF_ASM(ss)
|
||
DEF_ASM(ds)
|
||
DEF_ASM(fs)
|
||
DEF_ASM(gs)
|
||
DEF_ASM(st)
|
||
|
||
DEF_BWL(mov)
|
||
|
||
/* generic two operands */
|
||
DEF_BWL(add)
|
||
DEF_BWL(or)
|
||
DEF_BWL(adc)
|
||
DEF_BWL(sbb)
|
||
DEF_BWL(and)
|
||
DEF_BWL(sub)
|
||
DEF_BWL(xor)
|
||
DEF_BWL(cmp)
|
||
|
||
/* unary ops */
|
||
DEF_BWL(inc)
|
||
DEF_BWL(dec)
|
||
DEF_BWL(not)
|
||
DEF_BWL(neg)
|
||
DEF_BWL(mul)
|
||
DEF_BWL(imul)
|
||
DEF_BWL(div)
|
||
DEF_BWL(idiv)
|
||
|
||
DEF_BWL(xchg)
|
||
DEF_BWL(test)
|
||
|
||
/* shifts */
|
||
DEF_BWL(rol)
|
||
DEF_BWL(ror)
|
||
DEF_BWL(rcl)
|
||
DEF_BWL(rcr)
|
||
DEF_BWL(shl)
|
||
DEF_BWL(shr)
|
||
DEF_BWL(sar)
|
||
|
||
DEF_ASM(shldw)
|
||
DEF_ASM(shldl)
|
||
DEF_ASM(shld)
|
||
DEF_ASM(shrdw)
|
||
DEF_ASM(shrdl)
|
||
DEF_ASM(shrd)
|
||
|
||
DEF_ASM(pushw)
|
||
DEF_ASM(pushl)
|
||
DEF_ASM(push)
|
||
DEF_ASM(popw)
|
||
DEF_ASM(popl)
|
||
DEF_ASM(pop)
|
||
DEF_BWL(in)
|
||
DEF_BWL(out)
|
||
|
||
DEF_WL(movzb)
|
||
|
||
DEF_ASM(movzwl)
|
||
DEF_ASM(movsbw)
|
||
DEF_ASM(movsbl)
|
||
DEF_ASM(movswl)
|
||
|
||
DEF_WL(lea)
|
||
|
||
DEF_ASM(les)
|
||
DEF_ASM(lds)
|
||
DEF_ASM(lss)
|
||
DEF_ASM(lfs)
|
||
DEF_ASM(lgs)
|
||
|
||
DEF_ASM(call)
|
||
DEF_ASM(jmp)
|
||
DEF_ASM(lcall)
|
||
DEF_ASM(ljmp)
|
||
|
||
DEF_ASMTEST(j)
|
||
|
||
DEF_ASMTEST(set)
|
||
DEF_ASMTEST(cmov)
|
||
|
||
DEF_WL(bsf)
|
||
DEF_WL(bsr)
|
||
DEF_WL(bt)
|
||
DEF_WL(bts)
|
||
DEF_WL(btr)
|
||
DEF_WL(btc)
|
||
|
||
DEF_WL(lsl)
|
||
|
||
/* generic FP ops */
|
||
DEF_FP(add)
|
||
DEF_FP(mul)
|
||
|
||
DEF_ASM(fcom)
|
||
DEF_ASM(fcom_1) /* non existent op, just to have a regular table */
|
||
DEF_FP1(com)
|
||
|
||
DEF_FP(comp)
|
||
DEF_FP(sub)
|
||
DEF_FP(subr)
|
||
DEF_FP(div)
|
||
DEF_FP(divr)
|
||
|
||
DEF_BWL(xadd)
|
||
DEF_BWL(cmpxchg)
|
||
|
||
/* string ops */
|
||
DEF_BWL(cmps)
|
||
DEF_BWL(scmp)
|
||
DEF_BWL(ins)
|
||
DEF_BWL(outs)
|
||
DEF_BWL(lods)
|
||
DEF_BWL(slod)
|
||
DEF_BWL(movs)
|
||
DEF_BWL(smov)
|
||
DEF_BWL(scas)
|
||
DEF_BWL(ssca)
|
||
DEF_BWL(stos)
|
||
DEF_BWL(ssto)
|
||
|
||
/* generic asm ops */
|
||
|
||
#define ALT(x)
|
||
#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
|
||
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
|
||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
|
||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
|
||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
|
||
// njn: inlined i386-asm.h
|
||
//#include "i386-asm.h"
|
||
//---------------------------------------------------------------------------
|
||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||
DEF_ASM_OP0(popa, 0x61)
|
||
DEF_ASM_OP0(clc, 0xf8)
|
||
DEF_ASM_OP0(cld, 0xfc)
|
||
DEF_ASM_OP0(cli, 0xfa)
|
||
DEF_ASM_OP0(clts, 0x0f06)
|
||
DEF_ASM_OP0(cmc, 0xf5)
|
||
DEF_ASM_OP0(lahf, 0x9f)
|
||
DEF_ASM_OP0(sahf, 0x9e)
|
||
DEF_ASM_OP0(pushfl, 0x9c)
|
||
DEF_ASM_OP0(popfl, 0x9d)
|
||
DEF_ASM_OP0(pushf, 0x9c)
|
||
DEF_ASM_OP0(popf, 0x9d)
|
||
DEF_ASM_OP0(stc, 0xf9)
|
||
DEF_ASM_OP0(std, 0xfd)
|
||
DEF_ASM_OP0(sti, 0xfb)
|
||
DEF_ASM_OP0(aaa, 0x37)
|
||
DEF_ASM_OP0(aas, 0x3f)
|
||
DEF_ASM_OP0(daa, 0x27)
|
||
DEF_ASM_OP0(das, 0x2f)
|
||
DEF_ASM_OP0(aad, 0xd50a)
|
||
DEF_ASM_OP0(aam, 0xd40a)
|
||
DEF_ASM_OP0(cbw, 0x6698)
|
||
DEF_ASM_OP0(cwd, 0x6699)
|
||
DEF_ASM_OP0(cwde, 0x98)
|
||
DEF_ASM_OP0(cdq, 0x99)
|
||
DEF_ASM_OP0(cbtw, 0x6698)
|
||
DEF_ASM_OP0(cwtl, 0x98)
|
||
DEF_ASM_OP0(cwtd, 0x6699)
|
||
DEF_ASM_OP0(cltd, 0x99)
|
||
DEF_ASM_OP0(int3, 0xcc)
|
||
DEF_ASM_OP0(into, 0xce)
|
||
DEF_ASM_OP0(iret, 0xcf)
|
||
DEF_ASM_OP0(rsm, 0x0faa)
|
||
DEF_ASM_OP0(hlt, 0xf4)
|
||
DEF_ASM_OP0(wait, 0x9b)
|
||
DEF_ASM_OP0(nop, 0x90)
|
||
DEF_ASM_OP0(xlat, 0xd7)
|
||
|
||
/* strings */
|
||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||
|
||
/* bits */
|
||
|
||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
/* prefixes */
|
||
DEF_ASM_OP0(aword, 0x67)
|
||
DEF_ASM_OP0(addr16, 0x67)
|
||
DEF_ASM_OP0(word, 0x66)
|
||
DEF_ASM_OP0(data16, 0x66)
|
||
DEF_ASM_OP0(lock, 0xf0)
|
||
DEF_ASM_OP0(rep, 0xf3)
|
||
DEF_ASM_OP0(repe, 0xf3)
|
||
DEF_ASM_OP0(repz, 0xf3)
|
||
DEF_ASM_OP0(repne, 0xf2)
|
||
DEF_ASM_OP0(repnz, 0xf2)
|
||
|
||
DEF_ASM_OP0(invd, 0x0f08)
|
||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||
|
||
/* NOTE: we took the same order as gas opcode definition order */
|
||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||
|
||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
|
||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
|
||
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
|
||
/* arith */
|
||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
|
||
/* shifts */
|
||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||
|
||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||
DEF_ASM_OP0(leave, 0xc9)
|
||
DEF_ASM_OP0(ret, 0xc3)
|
||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||
DEF_ASM_OP0(lret, 0xcb)
|
||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||
|
||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
|
||
/* float */
|
||
/* specific fcomp handling */
|
||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||
|
||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
|
||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||
|
||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||
DEF_ASM_OP0(fwait, 0x9b)
|
||
|
||
/* fp load */
|
||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||
|
||
/* fp store */
|
||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||
|
||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||
|
||
/* exchange */
|
||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||
|
||
/* misc FPU */
|
||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||
|
||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* segments */
|
||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||
|
||
/* 486 */
|
||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||
|
||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||
|
||
/* pentium */
|
||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* pentium pro */
|
||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||
|
||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
/* mmx */
|
||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
|
||
#undef ALT
|
||
#undef DEF_ASM_OP0
|
||
#undef DEF_ASM_OP0L
|
||
#undef DEF_ASM_OP1
|
||
#undef DEF_ASM_OP2
|
||
#undef DEF_ASM_OP3
|
||
//---------------------------------------------------------------------------
|
||
|
||
#define ALT(x)
|
||
#define DEF_ASM_OP0(name, opcode)
|
||
#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
|
||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
|
||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
|
||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
|
||
// njn: inlined i386-asm.h
|
||
//#include "i386-asm.h"
|
||
//---------------------------------------------------------------------------
|
||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||
DEF_ASM_OP0(popa, 0x61)
|
||
DEF_ASM_OP0(clc, 0xf8)
|
||
DEF_ASM_OP0(cld, 0xfc)
|
||
DEF_ASM_OP0(cli, 0xfa)
|
||
DEF_ASM_OP0(clts, 0x0f06)
|
||
DEF_ASM_OP0(cmc, 0xf5)
|
||
DEF_ASM_OP0(lahf, 0x9f)
|
||
DEF_ASM_OP0(sahf, 0x9e)
|
||
DEF_ASM_OP0(pushfl, 0x9c)
|
||
DEF_ASM_OP0(popfl, 0x9d)
|
||
DEF_ASM_OP0(pushf, 0x9c)
|
||
DEF_ASM_OP0(popf, 0x9d)
|
||
DEF_ASM_OP0(stc, 0xf9)
|
||
DEF_ASM_OP0(std, 0xfd)
|
||
DEF_ASM_OP0(sti, 0xfb)
|
||
DEF_ASM_OP0(aaa, 0x37)
|
||
DEF_ASM_OP0(aas, 0x3f)
|
||
DEF_ASM_OP0(daa, 0x27)
|
||
DEF_ASM_OP0(das, 0x2f)
|
||
DEF_ASM_OP0(aad, 0xd50a)
|
||
DEF_ASM_OP0(aam, 0xd40a)
|
||
DEF_ASM_OP0(cbw, 0x6698)
|
||
DEF_ASM_OP0(cwd, 0x6699)
|
||
DEF_ASM_OP0(cwde, 0x98)
|
||
DEF_ASM_OP0(cdq, 0x99)
|
||
DEF_ASM_OP0(cbtw, 0x6698)
|
||
DEF_ASM_OP0(cwtl, 0x98)
|
||
DEF_ASM_OP0(cwtd, 0x6699)
|
||
DEF_ASM_OP0(cltd, 0x99)
|
||
DEF_ASM_OP0(int3, 0xcc)
|
||
DEF_ASM_OP0(into, 0xce)
|
||
DEF_ASM_OP0(iret, 0xcf)
|
||
DEF_ASM_OP0(rsm, 0x0faa)
|
||
DEF_ASM_OP0(hlt, 0xf4)
|
||
DEF_ASM_OP0(wait, 0x9b)
|
||
DEF_ASM_OP0(nop, 0x90)
|
||
DEF_ASM_OP0(xlat, 0xd7)
|
||
|
||
/* strings */
|
||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||
|
||
/* bits */
|
||
|
||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
/* prefixes */
|
||
DEF_ASM_OP0(aword, 0x67)
|
||
DEF_ASM_OP0(addr16, 0x67)
|
||
DEF_ASM_OP0(word, 0x66)
|
||
DEF_ASM_OP0(data16, 0x66)
|
||
DEF_ASM_OP0(lock, 0xf0)
|
||
DEF_ASM_OP0(rep, 0xf3)
|
||
DEF_ASM_OP0(repe, 0xf3)
|
||
DEF_ASM_OP0(repz, 0xf3)
|
||
DEF_ASM_OP0(repne, 0xf2)
|
||
DEF_ASM_OP0(repnz, 0xf2)
|
||
|
||
DEF_ASM_OP0(invd, 0x0f08)
|
||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||
|
||
/* NOTE: we took the same order as gas opcode definition order */
|
||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||
|
||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
|
||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
|
||
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
|
||
/* arith */
|
||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
|
||
/* shifts */
|
||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||
|
||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||
DEF_ASM_OP0(leave, 0xc9)
|
||
DEF_ASM_OP0(ret, 0xc3)
|
||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||
DEF_ASM_OP0(lret, 0xcb)
|
||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||
|
||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
|
||
/* float */
|
||
/* specific fcomp handling */
|
||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||
|
||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
|
||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||
|
||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||
DEF_ASM_OP0(fwait, 0x9b)
|
||
|
||
/* fp load */
|
||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||
|
||
/* fp store */
|
||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||
|
||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||
|
||
/* exchange */
|
||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||
|
||
/* misc FPU */
|
||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||
|
||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* segments */
|
||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||
|
||
/* 486 */
|
||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||
|
||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||
|
||
/* pentium */
|
||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* pentium pro */
|
||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||
|
||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
/* mmx */
|
||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
|
||
#undef ALT
|
||
#undef DEF_ASM_OP0
|
||
#undef DEF_ASM_OP0L
|
||
#undef DEF_ASM_OP1
|
||
#undef DEF_ASM_OP2
|
||
#undef DEF_ASM_OP3
|
||
//---------------------------------------------------------------------------
|
||
|
||
#endif
|
||
//---------------------------------------------------------------------------
|
||
#undef DEF
|
||
};
|
||
|
||
static const char tcc_keywords[] =
|
||
#define DEF(id, str) str "\0"
|
||
// njn: inlined tcctok.h
|
||
//#include "tcctok.h"
|
||
//---------------------------------------------------------------------------
|
||
/* keywords */
|
||
DEF(TOK_INT, "int")
|
||
DEF(TOK_VOID, "void")
|
||
DEF(TOK_CHAR, "char")
|
||
DEF(TOK_IF, "if")
|
||
DEF(TOK_ELSE, "else")
|
||
DEF(TOK_WHILE, "while")
|
||
DEF(TOK_BREAK, "break")
|
||
DEF(TOK_RETURN, "return")
|
||
DEF(TOK_FOR, "for")
|
||
DEF(TOK_EXTERN, "extern")
|
||
DEF(TOK_STATIC, "static")
|
||
DEF(TOK_UNSIGNED, "unsigned")
|
||
DEF(TOK_GOTO, "goto")
|
||
DEF(TOK_DO, "do")
|
||
DEF(TOK_CONTINUE, "continue")
|
||
DEF(TOK_SWITCH, "switch")
|
||
DEF(TOK_CASE, "case")
|
||
|
||
DEF(TOK_CONST1, "const")
|
||
DEF(TOK_CONST2, "__const") /* gcc keyword */
|
||
DEF(TOK_CONST3, "__const__") /* gcc keyword */
|
||
DEF(TOK_VOLATILE1, "volatile")
|
||
DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
|
||
DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
|
||
DEF(TOK_LONG, "long")
|
||
DEF(TOK_REGISTER, "register")
|
||
DEF(TOK_SIGNED1, "signed")
|
||
DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
|
||
DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
|
||
DEF(TOK_AUTO, "auto")
|
||
DEF(TOK_INLINE1, "inline")
|
||
DEF(TOK_INLINE2, "__inline") /* gcc keyword */
|
||
DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
|
||
DEF(TOK_RESTRICT1, "restrict")
|
||
DEF(TOK_RESTRICT2, "__restrict")
|
||
DEF(TOK_RESTRICT3, "__restrict__")
|
||
DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
|
||
|
||
DEF(TOK_FLOAT, "float")
|
||
DEF(TOK_DOUBLE, "double")
|
||
DEF(TOK_BOOL, "_Bool")
|
||
DEF(TOK_SHORT, "short")
|
||
DEF(TOK_STRUCT, "struct")
|
||
DEF(TOK_UNION, "union")
|
||
DEF(TOK_TYPEDEF, "typedef")
|
||
DEF(TOK_DEFAULT, "default")
|
||
DEF(TOK_ENUM, "enum")
|
||
DEF(TOK_SIZEOF, "sizeof")
|
||
DEF(TOK_ATTRIBUTE1, "__attribute")
|
||
DEF(TOK_ATTRIBUTE2, "__attribute__")
|
||
DEF(TOK_ALIGNOF1, "__alignof")
|
||
DEF(TOK_ALIGNOF2, "__alignof__")
|
||
DEF(TOK_TYPEOF1, "typeof")
|
||
DEF(TOK_TYPEOF2, "__typeof")
|
||
DEF(TOK_TYPEOF3, "__typeof__")
|
||
DEF(TOK_LABEL, "__label__")
|
||
DEF(TOK_ASM1, "asm")
|
||
DEF(TOK_ASM2, "__asm")
|
||
DEF(TOK_ASM3, "__asm__")
|
||
|
||
/*********************************************************************/
|
||
/* the following are not keywords. They are included to ease parsing */
|
||
/* preprocessor only */
|
||
DEF(TOK_DEFINE, "define")
|
||
DEF(TOK_INCLUDE, "include")
|
||
DEF(TOK_INCLUDE_NEXT, "include_next")
|
||
DEF(TOK_IFDEF, "ifdef")
|
||
DEF(TOK_IFNDEF, "ifndef")
|
||
DEF(TOK_ELIF, "elif")
|
||
DEF(TOK_ENDIF, "endif")
|
||
DEF(TOK_DEFINED, "defined")
|
||
DEF(TOK_UNDEF, "undef")
|
||
DEF(TOK_ERROR, "error")
|
||
DEF(TOK_WARNING, "warning")
|
||
DEF(TOK_LINE, "line")
|
||
DEF(TOK_PRAGMA, "pragma")
|
||
DEF(TOK___LINE__, "__LINE__")
|
||
DEF(TOK___FILE__, "__FILE__")
|
||
DEF(TOK___DATE__, "__DATE__")
|
||
DEF(TOK___TIME__, "__TIME__")
|
||
DEF(TOK___FUNCTION__, "__FUNCTION__")
|
||
DEF(TOK___VA_ARGS__, "__VA_ARGS__")
|
||
|
||
/* special identifiers */
|
||
DEF(TOK___FUNC__, "__func__")
|
||
|
||
/* attribute identifiers */
|
||
/* XXX: handle all tokens generically since speed is not critical */
|
||
DEF(TOK_SECTION1, "section")
|
||
DEF(TOK_SECTION2, "__section__")
|
||
DEF(TOK_ALIGNED1, "aligned")
|
||
DEF(TOK_ALIGNED2, "__aligned__")
|
||
DEF(TOK_PACKED1, "packed")
|
||
DEF(TOK_PACKED2, "__packed__")
|
||
DEF(TOK_UNUSED1, "unused")
|
||
DEF(TOK_UNUSED2, "__unused__")
|
||
DEF(TOK_CDECL1, "cdecl")
|
||
DEF(TOK_CDECL2, "__cdecl")
|
||
DEF(TOK_CDECL3, "__cdecl__")
|
||
DEF(TOK_STDCALL1, "stdcall")
|
||
DEF(TOK_STDCALL2, "__stdcall")
|
||
DEF(TOK_STDCALL3, "__stdcall__")
|
||
DEF(TOK_DLLEXPORT, "dllexport")
|
||
DEF(TOK_NORETURN1, "noreturn")
|
||
DEF(TOK_NORETURN2, "__noreturn__")
|
||
DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
|
||
DEF(TOK_builtin_constant_p, "__builtin_constant_p")
|
||
DEF(TOK_REGPARM1, "regparm")
|
||
DEF(TOK_REGPARM2, "__regparm__")
|
||
|
||
/* pragma */
|
||
DEF(TOK_pack, "pack")
|
||
#if !defined(TCC_TARGET_I386)
|
||
/* already defined for assembler */
|
||
DEF(TOK_ASM_push, "push")
|
||
DEF(TOK_ASM_pop, "pop")
|
||
#endif
|
||
|
||
/* builtin functions or variables */
|
||
DEF(TOK_memcpy, "memcpy")
|
||
DEF(TOK_memset, "memset")
|
||
DEF(TOK_alloca, "alloca")
|
||
DEF(TOK___divdi3, "__divdi3")
|
||
DEF(TOK___moddi3, "__moddi3")
|
||
DEF(TOK___udivdi3, "__udivdi3")
|
||
DEF(TOK___umoddi3, "__umoddi3")
|
||
#if defined(TCC_TARGET_ARM)
|
||
DEF(TOK___divsi3, "__divsi3")
|
||
DEF(TOK___modsi3, "__modsi3")
|
||
DEF(TOK___udivsi3, "__udivsi3")
|
||
DEF(TOK___umodsi3, "__umodsi3")
|
||
DEF(TOK___sardi3, "__ashrdi3")
|
||
DEF(TOK___shrdi3, "__lshrdi3")
|
||
DEF(TOK___shldi3, "__ashldi3")
|
||
DEF(TOK___slltold, "__slltold")
|
||
DEF(TOK___fixunssfsi, "__fixunssfsi")
|
||
DEF(TOK___fixunsdfsi, "__fixunsdfsi")
|
||
DEF(TOK___fixunsxfsi, "__fixunsxfsi")
|
||
DEF(TOK___fixsfdi, "__fixsfdi")
|
||
DEF(TOK___fixdfdi, "__fixdfdi")
|
||
DEF(TOK___fixxfdi, "__fixxfdi")
|
||
#elif defined(TCC_TARGET_C67)
|
||
DEF(TOK__divi, "_divi")
|
||
DEF(TOK__divu, "_divu")
|
||
DEF(TOK__divf, "_divf")
|
||
DEF(TOK__divd, "_divd")
|
||
DEF(TOK__remi, "_remi")
|
||
DEF(TOK__remu, "_remu")
|
||
DEF(TOK___sardi3, "__sardi3")
|
||
DEF(TOK___shrdi3, "__shrdi3")
|
||
DEF(TOK___shldi3, "__shldi3")
|
||
#else
|
||
/* XXX: same names on i386 ? */
|
||
DEF(TOK___sardi3, "__sardi3")
|
||
DEF(TOK___shrdi3, "__shrdi3")
|
||
DEF(TOK___shldi3, "__shldi3")
|
||
#endif
|
||
DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control")
|
||
DEF(TOK___tcc_fpu_control, "__tcc_fpu_control")
|
||
DEF(TOK___ulltof, "__ulltof")
|
||
DEF(TOK___ulltod, "__ulltod")
|
||
DEF(TOK___ulltold, "__ulltold")
|
||
DEF(TOK___fixunssfdi, "__fixunssfdi")
|
||
DEF(TOK___fixunsdfdi, "__fixunsdfdi")
|
||
DEF(TOK___fixunsxfdi, "__fixunsxfdi")
|
||
DEF(TOK___chkstk, "__chkstk")
|
||
|
||
/* bound checking symbols */
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
DEF(TOK___bound_ptr_add, "__bound_ptr_add")
|
||
DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
|
||
DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
|
||
DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
|
||
DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
|
||
DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
|
||
DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
|
||
DEF(TOK___bound_local_new, "__bound_local_new")
|
||
DEF(TOK___bound_local_delete, "__bound_local_delete")
|
||
DEF(TOK_malloc, "malloc")
|
||
DEF(TOK_free, "free")
|
||
DEF(TOK_realloc, "realloc")
|
||
DEF(TOK_memalign, "memalign")
|
||
DEF(TOK_calloc, "calloc")
|
||
DEF(TOK_memmove, "memmove")
|
||
DEF(TOK_strlen, "strlen")
|
||
DEF(TOK_strcpy, "strcpy")
|
||
#endif
|
||
|
||
/* Tiny Assembler */
|
||
|
||
DEF_ASM(byte)
|
||
DEF_ASM(align)
|
||
DEF_ASM(skip)
|
||
DEF_ASM(space)
|
||
DEF_ASM(string)
|
||
DEF_ASM(asciz)
|
||
DEF_ASM(ascii)
|
||
DEF_ASM(globl)
|
||
DEF_ASM(global)
|
||
DEF_ASM(text)
|
||
DEF_ASM(data)
|
||
DEF_ASM(bss)
|
||
DEF_ASM(previous)
|
||
DEF_ASM(fill)
|
||
DEF_ASM(org)
|
||
DEF_ASM(quad)
|
||
|
||
#ifdef TCC_TARGET_I386
|
||
|
||
/* WARNING: relative order of tokens is important. */
|
||
DEF_ASM(al)
|
||
DEF_ASM(cl)
|
||
DEF_ASM(dl)
|
||
DEF_ASM(bl)
|
||
DEF_ASM(ah)
|
||
DEF_ASM(ch)
|
||
DEF_ASM(dh)
|
||
DEF_ASM(bh)
|
||
DEF_ASM(ax)
|
||
DEF_ASM(cx)
|
||
DEF_ASM(dx)
|
||
DEF_ASM(bx)
|
||
DEF_ASM(sp)
|
||
DEF_ASM(bp)
|
||
DEF_ASM(si)
|
||
DEF_ASM(di)
|
||
DEF_ASM(eax)
|
||
DEF_ASM(ecx)
|
||
DEF_ASM(edx)
|
||
DEF_ASM(ebx)
|
||
DEF_ASM(esp)
|
||
DEF_ASM(ebp)
|
||
DEF_ASM(esi)
|
||
DEF_ASM(edi)
|
||
DEF_ASM(mm0)
|
||
DEF_ASM(mm1)
|
||
DEF_ASM(mm2)
|
||
DEF_ASM(mm3)
|
||
DEF_ASM(mm4)
|
||
DEF_ASM(mm5)
|
||
DEF_ASM(mm6)
|
||
DEF_ASM(mm7)
|
||
DEF_ASM(xmm0)
|
||
DEF_ASM(xmm1)
|
||
DEF_ASM(xmm2)
|
||
DEF_ASM(xmm3)
|
||
DEF_ASM(xmm4)
|
||
DEF_ASM(xmm5)
|
||
DEF_ASM(xmm6)
|
||
DEF_ASM(xmm7)
|
||
DEF_ASM(cr0)
|
||
DEF_ASM(cr1)
|
||
DEF_ASM(cr2)
|
||
DEF_ASM(cr3)
|
||
DEF_ASM(cr4)
|
||
DEF_ASM(cr5)
|
||
DEF_ASM(cr6)
|
||
DEF_ASM(cr7)
|
||
DEF_ASM(tr0)
|
||
DEF_ASM(tr1)
|
||
DEF_ASM(tr2)
|
||
DEF_ASM(tr3)
|
||
DEF_ASM(tr4)
|
||
DEF_ASM(tr5)
|
||
DEF_ASM(tr6)
|
||
DEF_ASM(tr7)
|
||
DEF_ASM(db0)
|
||
DEF_ASM(db1)
|
||
DEF_ASM(db2)
|
||
DEF_ASM(db3)
|
||
DEF_ASM(db4)
|
||
DEF_ASM(db5)
|
||
DEF_ASM(db6)
|
||
DEF_ASM(db7)
|
||
DEF_ASM(dr0)
|
||
DEF_ASM(dr1)
|
||
DEF_ASM(dr2)
|
||
DEF_ASM(dr3)
|
||
DEF_ASM(dr4)
|
||
DEF_ASM(dr5)
|
||
DEF_ASM(dr6)
|
||
DEF_ASM(dr7)
|
||
DEF_ASM(es)
|
||
DEF_ASM(cs)
|
||
DEF_ASM(ss)
|
||
DEF_ASM(ds)
|
||
DEF_ASM(fs)
|
||
DEF_ASM(gs)
|
||
DEF_ASM(st)
|
||
|
||
DEF_BWL(mov)
|
||
|
||
/* generic two operands */
|
||
DEF_BWL(add)
|
||
DEF_BWL(or)
|
||
DEF_BWL(adc)
|
||
DEF_BWL(sbb)
|
||
DEF_BWL(and)
|
||
DEF_BWL(sub)
|
||
DEF_BWL(xor)
|
||
DEF_BWL(cmp)
|
||
|
||
/* unary ops */
|
||
DEF_BWL(inc)
|
||
DEF_BWL(dec)
|
||
DEF_BWL(not)
|
||
DEF_BWL(neg)
|
||
DEF_BWL(mul)
|
||
DEF_BWL(imul)
|
||
DEF_BWL(div)
|
||
DEF_BWL(idiv)
|
||
|
||
DEF_BWL(xchg)
|
||
DEF_BWL(test)
|
||
|
||
/* shifts */
|
||
DEF_BWL(rol)
|
||
DEF_BWL(ror)
|
||
DEF_BWL(rcl)
|
||
DEF_BWL(rcr)
|
||
DEF_BWL(shl)
|
||
DEF_BWL(shr)
|
||
DEF_BWL(sar)
|
||
|
||
DEF_ASM(shldw)
|
||
DEF_ASM(shldl)
|
||
DEF_ASM(shld)
|
||
DEF_ASM(shrdw)
|
||
DEF_ASM(shrdl)
|
||
DEF_ASM(shrd)
|
||
|
||
DEF_ASM(pushw)
|
||
DEF_ASM(pushl)
|
||
DEF_ASM(push)
|
||
DEF_ASM(popw)
|
||
DEF_ASM(popl)
|
||
DEF_ASM(pop)
|
||
DEF_BWL(in)
|
||
DEF_BWL(out)
|
||
|
||
DEF_WL(movzb)
|
||
|
||
DEF_ASM(movzwl)
|
||
DEF_ASM(movsbw)
|
||
DEF_ASM(movsbl)
|
||
DEF_ASM(movswl)
|
||
|
||
DEF_WL(lea)
|
||
|
||
DEF_ASM(les)
|
||
DEF_ASM(lds)
|
||
DEF_ASM(lss)
|
||
DEF_ASM(lfs)
|
||
DEF_ASM(lgs)
|
||
|
||
DEF_ASM(call)
|
||
DEF_ASM(jmp)
|
||
DEF_ASM(lcall)
|
||
DEF_ASM(ljmp)
|
||
|
||
DEF_ASMTEST(j)
|
||
|
||
DEF_ASMTEST(set)
|
||
DEF_ASMTEST(cmov)
|
||
|
||
DEF_WL(bsf)
|
||
DEF_WL(bsr)
|
||
DEF_WL(bt)
|
||
DEF_WL(bts)
|
||
DEF_WL(btr)
|
||
DEF_WL(btc)
|
||
|
||
DEF_WL(lsl)
|
||
|
||
/* generic FP ops */
|
||
DEF_FP(add)
|
||
DEF_FP(mul)
|
||
|
||
DEF_ASM(fcom)
|
||
DEF_ASM(fcom_1) /* non existent op, just to have a regular table */
|
||
DEF_FP1(com)
|
||
|
||
DEF_FP(comp)
|
||
DEF_FP(sub)
|
||
DEF_FP(subr)
|
||
DEF_FP(div)
|
||
DEF_FP(divr)
|
||
|
||
DEF_BWL(xadd)
|
||
DEF_BWL(cmpxchg)
|
||
|
||
/* string ops */
|
||
DEF_BWL(cmps)
|
||
DEF_BWL(scmp)
|
||
DEF_BWL(ins)
|
||
DEF_BWL(outs)
|
||
DEF_BWL(lods)
|
||
DEF_BWL(slod)
|
||
DEF_BWL(movs)
|
||
DEF_BWL(smov)
|
||
DEF_BWL(scas)
|
||
DEF_BWL(ssca)
|
||
DEF_BWL(stos)
|
||
DEF_BWL(ssto)
|
||
|
||
/* generic asm ops */
|
||
|
||
#define ALT(x)
|
||
#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
|
||
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
|
||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
|
||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
|
||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
|
||
// njn: inlined i386-asm.h
|
||
//#include "i386-asm.h"
|
||
//---------------------------------------------------------------------------
|
||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||
DEF_ASM_OP0(popa, 0x61)
|
||
DEF_ASM_OP0(clc, 0xf8)
|
||
DEF_ASM_OP0(cld, 0xfc)
|
||
DEF_ASM_OP0(cli, 0xfa)
|
||
DEF_ASM_OP0(clts, 0x0f06)
|
||
DEF_ASM_OP0(cmc, 0xf5)
|
||
DEF_ASM_OP0(lahf, 0x9f)
|
||
DEF_ASM_OP0(sahf, 0x9e)
|
||
DEF_ASM_OP0(pushfl, 0x9c)
|
||
DEF_ASM_OP0(popfl, 0x9d)
|
||
DEF_ASM_OP0(pushf, 0x9c)
|
||
DEF_ASM_OP0(popf, 0x9d)
|
||
DEF_ASM_OP0(stc, 0xf9)
|
||
DEF_ASM_OP0(std, 0xfd)
|
||
DEF_ASM_OP0(sti, 0xfb)
|
||
DEF_ASM_OP0(aaa, 0x37)
|
||
DEF_ASM_OP0(aas, 0x3f)
|
||
DEF_ASM_OP0(daa, 0x27)
|
||
DEF_ASM_OP0(das, 0x2f)
|
||
DEF_ASM_OP0(aad, 0xd50a)
|
||
DEF_ASM_OP0(aam, 0xd40a)
|
||
DEF_ASM_OP0(cbw, 0x6698)
|
||
DEF_ASM_OP0(cwd, 0x6699)
|
||
DEF_ASM_OP0(cwde, 0x98)
|
||
DEF_ASM_OP0(cdq, 0x99)
|
||
DEF_ASM_OP0(cbtw, 0x6698)
|
||
DEF_ASM_OP0(cwtl, 0x98)
|
||
DEF_ASM_OP0(cwtd, 0x6699)
|
||
DEF_ASM_OP0(cltd, 0x99)
|
||
DEF_ASM_OP0(int3, 0xcc)
|
||
DEF_ASM_OP0(into, 0xce)
|
||
DEF_ASM_OP0(iret, 0xcf)
|
||
DEF_ASM_OP0(rsm, 0x0faa)
|
||
DEF_ASM_OP0(hlt, 0xf4)
|
||
DEF_ASM_OP0(wait, 0x9b)
|
||
DEF_ASM_OP0(nop, 0x90)
|
||
DEF_ASM_OP0(xlat, 0xd7)
|
||
|
||
/* strings */
|
||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||
|
||
/* bits */
|
||
|
||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
/* prefixes */
|
||
DEF_ASM_OP0(aword, 0x67)
|
||
DEF_ASM_OP0(addr16, 0x67)
|
||
DEF_ASM_OP0(word, 0x66)
|
||
DEF_ASM_OP0(data16, 0x66)
|
||
DEF_ASM_OP0(lock, 0xf0)
|
||
DEF_ASM_OP0(rep, 0xf3)
|
||
DEF_ASM_OP0(repe, 0xf3)
|
||
DEF_ASM_OP0(repz, 0xf3)
|
||
DEF_ASM_OP0(repne, 0xf2)
|
||
DEF_ASM_OP0(repnz, 0xf2)
|
||
|
||
DEF_ASM_OP0(invd, 0x0f08)
|
||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||
|
||
/* NOTE: we took the same order as gas opcode definition order */
|
||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||
|
||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
|
||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
|
||
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
|
||
/* arith */
|
||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
|
||
/* shifts */
|
||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||
|
||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||
DEF_ASM_OP0(leave, 0xc9)
|
||
DEF_ASM_OP0(ret, 0xc3)
|
||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||
DEF_ASM_OP0(lret, 0xcb)
|
||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||
|
||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
|
||
/* float */
|
||
/* specific fcomp handling */
|
||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||
|
||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
|
||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||
|
||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||
DEF_ASM_OP0(fwait, 0x9b)
|
||
|
||
/* fp load */
|
||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||
|
||
/* fp store */
|
||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||
|
||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||
|
||
/* exchange */
|
||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||
|
||
/* misc FPU */
|
||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||
|
||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* segments */
|
||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||
|
||
/* 486 */
|
||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||
|
||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||
|
||
/* pentium */
|
||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* pentium pro */
|
||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||
|
||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
/* mmx */
|
||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
|
||
#undef ALT
|
||
#undef DEF_ASM_OP0
|
||
#undef DEF_ASM_OP0L
|
||
#undef DEF_ASM_OP1
|
||
#undef DEF_ASM_OP2
|
||
#undef DEF_ASM_OP3
|
||
//---------------------------------------------------------------------------
|
||
|
||
#define ALT(x)
|
||
#define DEF_ASM_OP0(name, opcode)
|
||
#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
|
||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
|
||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
|
||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
|
||
// njn: inlined i386-asm.h
|
||
//#include "i386-asm.h"
|
||
//---------------------------------------------------------------------------
|
||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||
DEF_ASM_OP0(popa, 0x61)
|
||
DEF_ASM_OP0(clc, 0xf8)
|
||
DEF_ASM_OP0(cld, 0xfc)
|
||
DEF_ASM_OP0(cli, 0xfa)
|
||
DEF_ASM_OP0(clts, 0x0f06)
|
||
DEF_ASM_OP0(cmc, 0xf5)
|
||
DEF_ASM_OP0(lahf, 0x9f)
|
||
DEF_ASM_OP0(sahf, 0x9e)
|
||
DEF_ASM_OP0(pushfl, 0x9c)
|
||
DEF_ASM_OP0(popfl, 0x9d)
|
||
DEF_ASM_OP0(pushf, 0x9c)
|
||
DEF_ASM_OP0(popf, 0x9d)
|
||
DEF_ASM_OP0(stc, 0xf9)
|
||
DEF_ASM_OP0(std, 0xfd)
|
||
DEF_ASM_OP0(sti, 0xfb)
|
||
DEF_ASM_OP0(aaa, 0x37)
|
||
DEF_ASM_OP0(aas, 0x3f)
|
||
DEF_ASM_OP0(daa, 0x27)
|
||
DEF_ASM_OP0(das, 0x2f)
|
||
DEF_ASM_OP0(aad, 0xd50a)
|
||
DEF_ASM_OP0(aam, 0xd40a)
|
||
DEF_ASM_OP0(cbw, 0x6698)
|
||
DEF_ASM_OP0(cwd, 0x6699)
|
||
DEF_ASM_OP0(cwde, 0x98)
|
||
DEF_ASM_OP0(cdq, 0x99)
|
||
DEF_ASM_OP0(cbtw, 0x6698)
|
||
DEF_ASM_OP0(cwtl, 0x98)
|
||
DEF_ASM_OP0(cwtd, 0x6699)
|
||
DEF_ASM_OP0(cltd, 0x99)
|
||
DEF_ASM_OP0(int3, 0xcc)
|
||
DEF_ASM_OP0(into, 0xce)
|
||
DEF_ASM_OP0(iret, 0xcf)
|
||
DEF_ASM_OP0(rsm, 0x0faa)
|
||
DEF_ASM_OP0(hlt, 0xf4)
|
||
DEF_ASM_OP0(wait, 0x9b)
|
||
DEF_ASM_OP0(nop, 0x90)
|
||
DEF_ASM_OP0(xlat, 0xd7)
|
||
|
||
/* strings */
|
||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||
|
||
/* bits */
|
||
|
||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
/* prefixes */
|
||
DEF_ASM_OP0(aword, 0x67)
|
||
DEF_ASM_OP0(addr16, 0x67)
|
||
DEF_ASM_OP0(word, 0x66)
|
||
DEF_ASM_OP0(data16, 0x66)
|
||
DEF_ASM_OP0(lock, 0xf0)
|
||
DEF_ASM_OP0(rep, 0xf3)
|
||
DEF_ASM_OP0(repe, 0xf3)
|
||
DEF_ASM_OP0(repz, 0xf3)
|
||
DEF_ASM_OP0(repne, 0xf2)
|
||
DEF_ASM_OP0(repnz, 0xf2)
|
||
|
||
DEF_ASM_OP0(invd, 0x0f08)
|
||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||
|
||
/* NOTE: we took the same order as gas opcode definition order */
|
||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||
|
||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
|
||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
|
||
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
|
||
/* arith */
|
||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
|
||
/* shifts */
|
||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||
|
||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||
DEF_ASM_OP0(leave, 0xc9)
|
||
DEF_ASM_OP0(ret, 0xc3)
|
||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||
DEF_ASM_OP0(lret, 0xcb)
|
||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||
|
||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
|
||
/* float */
|
||
/* specific fcomp handling */
|
||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||
|
||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
|
||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||
|
||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||
DEF_ASM_OP0(fwait, 0x9b)
|
||
|
||
/* fp load */
|
||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||
|
||
/* fp store */
|
||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||
|
||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||
|
||
/* exchange */
|
||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||
|
||
/* misc FPU */
|
||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||
|
||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* segments */
|
||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||
|
||
/* 486 */
|
||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||
|
||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||
|
||
/* pentium */
|
||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* pentium pro */
|
||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||
|
||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
/* mmx */
|
||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
|
||
#undef ALT
|
||
#undef DEF_ASM_OP0
|
||
#undef DEF_ASM_OP0L
|
||
#undef DEF_ASM_OP1
|
||
#undef DEF_ASM_OP2
|
||
#undef DEF_ASM_OP3
|
||
//---------------------------------------------------------------------------
|
||
|
||
#endif
|
||
//---------------------------------------------------------------------------
|
||
#undef DEF
|
||
;
|
||
|
||
#define TOK_UIDENT TOK_DEFINE
|
||
|
||
#ifdef WIN32
|
||
int __stdcall GetModuleFileNameA(void *, char *, int);
|
||
void *__stdcall GetProcAddress(void *, const char *);
|
||
void *__stdcall GetModuleHandleA(const char *);
|
||
void *__stdcall LoadLibraryA(const char *);
|
||
int __stdcall FreeConsole(void);
|
||
|
||
#define snprintf _snprintf
|
||
#define vsnprintf _vsnprintf
|
||
#ifndef __GNUC__
|
||
#define strtold (long double)strtod
|
||
#define strtof (float)strtod
|
||
#define strtoll (long long)strtol
|
||
#endif
|
||
#elif defined(TCC_UCLIBC) || defined(__FreeBSD__)
|
||
/* currently incorrect */
|
||
long double strtold(const char *nptr, char **endptr)
|
||
{
|
||
return (long double)strtod(nptr, endptr);
|
||
}
|
||
float strtof(const char *nptr, char **endptr)
|
||
{
|
||
return (float)strtod(nptr, endptr);
|
||
}
|
||
#else
|
||
/* XXX: need to define this to use them in non ISOC99 context */
|
||
extern float strtof (const char *__nptr, char **__endptr);
|
||
extern long double strtold (const char *__nptr, char **__endptr);
|
||
#endif
|
||
|
||
static char *pstrcpy(char *buf, int buf_size, const char *s);
|
||
static char *pstrcat(char *buf, int buf_size, const char *s);
|
||
static const char *tcc_basename(const char *name);
|
||
|
||
static void next(void);
|
||
static void next_nomacro(void);
|
||
static void parse_expr_type(CType *type);
|
||
static void expr_type(CType *type);
|
||
static void unary_type(CType *type);
|
||
static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
||
int case_reg, int is_expr);
|
||
static int expr_const(void);
|
||
static void expr_eq(void);
|
||
static void gexpr(void);
|
||
static void gen_inline_functions(void);
|
||
static void decl(int l);
|
||
static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
||
int first, int size_only);
|
||
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||
int has_init, int v, int scope);
|
||
int gv(int rc);
|
||
void gv2(int rc1, int rc2);
|
||
void move_reg(int r, int s);
|
||
void save_regs(int n);
|
||
void save_reg(int r);
|
||
void vpop(void);
|
||
void vswap(void);
|
||
void vdup(void);
|
||
int get_reg(int rc);
|
||
int get_reg_ex(int rc,int rc2);
|
||
|
||
struct macro_level {
|
||
struct macro_level *prev;
|
||
int *p;
|
||
};
|
||
|
||
static void macro_subst(TokenString *tok_str, Sym **nested_list,
|
||
const int *macro_str, struct macro_level **can_read_stream);
|
||
void gen_op(int op);
|
||
void force_charshort_cast(int t);
|
||
static void gen_cast(CType *type);
|
||
void vstore(void);
|
||
static Sym *sym_find(int v);
|
||
static Sym *sym_push(int v, CType *type, int r, int c);
|
||
|
||
/* type handling */
|
||
static int type_size(CType *type, int *a);
|
||
static inline CType *pointed_type(CType *type);
|
||
static int pointed_size(CType *type);
|
||
static int lvalue_type(int t);
|
||
static int parse_btype(CType *type, AttributeDef *ad);
|
||
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
|
||
static int is_compatible_types(CType *type1, CType *type2);
|
||
|
||
int ieee_finite(double d);
|
||
void error(const char *fmt, ...);
|
||
void vpushi(int v);
|
||
void vrott(int n);
|
||
void vnrott(int n);
|
||
void lexpand_nr(void);
|
||
static void vpush_global_sym(CType *type, int v);
|
||
void vset(CType *type, int r, int v);
|
||
void type_to_str(char *buf, int buf_size,
|
||
CType *type, const char *varstr);
|
||
char *get_tok_str(int v, CValue *cv);
|
||
static Sym *get_sym_ref(CType *type, Section *sec,
|
||
unsigned long offset, unsigned long size);
|
||
static Sym *external_global_sym(int v, CType *type, int r);
|
||
|
||
/* section generation */
|
||
static void section_realloc(Section *sec, unsigned long new_size);
|
||
static void *section_ptr_add(Section *sec, unsigned long size);
|
||
static void put_extern_sym(Sym *sym, Section *section,
|
||
unsigned long value, unsigned long size);
|
||
static void greloc(Section *s, Sym *sym, unsigned long addr, int type);
|
||
static int put_elf_str(Section *s, const char *sym);
|
||
static int put_elf_sym(Section *s,
|
||
unsigned long value, unsigned long size,
|
||
int info, int other, int shndx, const char *name);
|
||
static int add_elf_sym(Section *s, unsigned long value, unsigned long size,
|
||
int info, int other, int sh_num, const char *name);
|
||
static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
|
||
int type, int symbol);
|
||
static void put_stabs(const char *str, int type, int other, int desc,
|
||
unsigned long value);
|
||
static void put_stabs_r(const char *str, int type, int other, int desc,
|
||
unsigned long value, Section *sec, int sym_index);
|
||
static void put_stabn(int type, int other, int desc, int value);
|
||
static void put_stabd(int type, int other, int desc);
|
||
static int tcc_add_dll(TCCState *s, const char *filename, int flags);
|
||
|
||
#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */
|
||
#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */
|
||
static int tcc_add_file_internal(TCCState *s, const char *filename, int flags);
|
||
|
||
/* tcccoff.c */
|
||
int tcc_output_coff(TCCState *s1, FILE *f);
|
||
|
||
/* tccpe.c */
|
||
void *resolve_sym(TCCState *s1, const char *sym, int type);
|
||
int pe_load_def_file(struct TCCState *s1, FILE *fp);
|
||
void pe_setup_paths(struct TCCState *s1, int *p_output_type, const char **p_outfile, char *first_file);
|
||
unsigned long pe_add_runtime(struct TCCState *s1);
|
||
int tcc_output_pe(struct TCCState *s1, const char *filename);
|
||
|
||
/* tccasm.c */
|
||
|
||
#ifdef CONFIG_TCC_ASM
|
||
|
||
typedef struct ExprValue {
|
||
uint32_t v;
|
||
Sym *sym;
|
||
} ExprValue;
|
||
|
||
#define MAX_ASM_OPERANDS 30
|
||
|
||
typedef struct ASMOperand {
|
||
int id; /* GCC 3 optionnal identifier (0 if number only supported */
|
||
char *constraint;
|
||
char asm_str[16]; /* computed asm string for operand */
|
||
SValue *vt; /* C value of the expression */
|
||
int ref_index; /* if >= 0, gives reference to a output constraint */
|
||
int input_index; /* if >= 0, gives reference to an input constraint */
|
||
int priority; /* priority, used to assign registers */
|
||
int reg; /* if >= 0, register number used for this operand */
|
||
int is_llong; /* true if double register value */
|
||
int is_memory; /* true if memory operand */
|
||
int is_rw; /* for '+' modifier */
|
||
} ASMOperand;
|
||
|
||
static void asm_expr(TCCState *s1, ExprValue *pe);
|
||
static int asm_int_expr(TCCState *s1);
|
||
static int find_constraint(ASMOperand *operands, int nb_operands,
|
||
const char *name, const char **pp);
|
||
|
||
static int tcc_assemble(TCCState *s1, int do_preprocess);
|
||
|
||
#endif
|
||
|
||
static void asm_instr(void);
|
||
static void asm_global_instr(void);
|
||
|
||
/* true if float/double/long double type */
|
||
static inline int is_float(int t)
|
||
{
|
||
int bt;
|
||
bt = t & VT_BTYPE;
|
||
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT;
|
||
}
|
||
|
||
#ifdef TCC_TARGET_I386
|
||
// njn: inlined i386-gen.c
|
||
//#include "i386-gen.c"
|
||
//---------------------------------------------------------------------------
|
||
/*
|
||
* X86 code generator for TCC
|
||
*
|
||
* Copyright (c) 2001-2004 Fabrice Bellard
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser 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
|
||
*/
|
||
|
||
/* number of available registers */
|
||
#define NB_REGS 4
|
||
|
||
/* a register can belong to several classes. The classes must be
|
||
sorted from more general to more precise (see gv2() code which does
|
||
assumptions on it). */
|
||
#define RC_INT 0x0001 /* generic integer register */
|
||
#define RC_FLOAT 0x0002 /* generic float register */
|
||
#define RC_EAX 0x0004
|
||
#define RC_ST0 0x0008
|
||
#define RC_ECX 0x0010
|
||
#define RC_EDX 0x0020
|
||
#define RC_IRET RC_EAX /* function return: integer register */
|
||
#define RC_LRET RC_EDX /* function return: second integer register */
|
||
#define RC_FRET RC_ST0 /* function return: float register */
|
||
|
||
/* pretty names for the registers */
|
||
enum {
|
||
TREG_EAX = 0,
|
||
TREG_ECX,
|
||
TREG_EDX,
|
||
TREG_ST0,
|
||
};
|
||
|
||
int reg_classes[NB_REGS] = {
|
||
/* eax */ RC_INT | RC_EAX,
|
||
/* ecx */ RC_INT | RC_ECX,
|
||
/* edx */ RC_INT | RC_EDX,
|
||
/* st0 */ RC_FLOAT | RC_ST0,
|
||
};
|
||
|
||
/* return registers for function */
|
||
#define REG_IRET TREG_EAX /* single word int return register */
|
||
#define REG_LRET TREG_EDX /* second word return register (for long long) */
|
||
#define REG_FRET TREG_ST0 /* float return register */
|
||
|
||
/* defined if function parameters must be evaluated in reverse order */
|
||
#define INVERT_FUNC_PARAMS
|
||
|
||
/* defined if structures are passed as pointers. Otherwise structures
|
||
are directly pushed on stack. */
|
||
//#define FUNC_STRUCT_PARAM_AS_PTR
|
||
|
||
/* pointer size, in bytes */
|
||
#define PTR_SIZE 4
|
||
|
||
/* long double size and alignment, in bytes */
|
||
#define LDOUBLE_SIZE 12
|
||
#define LDOUBLE_ALIGN 4
|
||
/* maximum alignment (for aligned attribute support) */
|
||
#define MAX_ALIGN 8
|
||
|
||
/******************************************************/
|
||
/* ELF defines */
|
||
|
||
#define EM_TCC_TARGET EM_386
|
||
|
||
/* relocation type for 32 bit data relocation */
|
||
#define R_DATA_32 R_386_32
|
||
#define R_JMP_SLOT R_386_JMP_SLOT
|
||
#define R_COPY R_386_COPY
|
||
|
||
#define ELF_START_ADDR 0x08048000
|
||
#define ELF_PAGE_SIZE 0x1000
|
||
|
||
/******************************************************/
|
||
|
||
static unsigned long func_sub_sp_offset;
|
||
static unsigned long func_bound_offset;
|
||
static int func_ret_sub;
|
||
|
||
/* XXX: make it faster ? */
|
||
void g(int c)
|
||
{
|
||
int ind1;
|
||
ind1 = ind + 1;
|
||
if (ind1 > cur_text_section->data_allocated)
|
||
section_realloc(cur_text_section, ind1);
|
||
cur_text_section->data[ind] = c;
|
||
ind = ind1;
|
||
}
|
||
|
||
void o(unsigned int c)
|
||
{
|
||
while (c) {
|
||
g(c);
|
||
c = c >> 8;
|
||
}
|
||
}
|
||
|
||
void gen_le32(int c)
|
||
{
|
||
g(c);
|
||
g(c >> 8);
|
||
g(c >> 16);
|
||
g(c >> 24);
|
||
}
|
||
|
||
/* output a symbol and patch all calls to it */
|
||
void gsym_addr(int t, int a)
|
||
{
|
||
int n, *ptr;
|
||
while (t) {
|
||
ptr = (int *)(cur_text_section->data + t);
|
||
n = *ptr; /* next value */
|
||
*ptr = a - t - 4;
|
||
t = n;
|
||
}
|
||
}
|
||
|
||
void gsym(int t)
|
||
{
|
||
gsym_addr(t, ind);
|
||
}
|
||
|
||
/* psym is used to put an instruction with a data field which is a
|
||
reference to a symbol. It is in fact the same as oad ! */
|
||
#define psym oad
|
||
|
||
/* instruction + 4 bytes data. Return the address of the data */
|
||
static int oad(int c, int s)
|
||
{
|
||
int ind1;
|
||
|
||
o(c);
|
||
ind1 = ind + 4;
|
||
if (ind1 > cur_text_section->data_allocated)
|
||
section_realloc(cur_text_section, ind1);
|
||
*(int *)(cur_text_section->data + ind) = s;
|
||
s = ind;
|
||
ind = ind1;
|
||
return s;
|
||
}
|
||
|
||
/* output constant with relocation if 'r & VT_SYM' is true */
|
||
static void gen_addr32(int r, Sym *sym, int c)
|
||
{
|
||
if (r & VT_SYM)
|
||
greloc(cur_text_section, sym, ind, R_386_32);
|
||
gen_le32(c);
|
||
}
|
||
|
||
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
||
opcode bits */
|
||
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
|
||
{
|
||
op_reg = op_reg << 3;
|
||
if ((r & VT_VALMASK) == VT_CONST) {
|
||
/* constant memory reference */
|
||
o(0x05 | op_reg);
|
||
gen_addr32(r, sym, c);
|
||
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
||
/* currently, we use only ebp as base */
|
||
if (c == (char)c) {
|
||
/* short reference */
|
||
o(0x45 | op_reg);
|
||
g(c);
|
||
} else {
|
||
oad(0x85 | op_reg, c);
|
||
}
|
||
} else {
|
||
g(0x00 | op_reg | (r & VT_VALMASK));
|
||
}
|
||
}
|
||
|
||
|
||
/* load 'r' from value 'sv' */
|
||
void load(int r, SValue *sv)
|
||
{
|
||
int v, t, ft, fc, fr;
|
||
SValue v1;
|
||
|
||
fr = sv->r;
|
||
ft = sv->type.t;
|
||
fc = sv->c.ul;
|
||
|
||
v = fr & VT_VALMASK;
|
||
if (fr & VT_LVAL) {
|
||
if (v == VT_LLOCAL) {
|
||
v1.type.t = VT_INT;
|
||
v1.r = VT_LOCAL | VT_LVAL;
|
||
v1.c.ul = fc;
|
||
load(r, &v1);
|
||
fr = r;
|
||
}
|
||
if ((ft & VT_BTYPE) == VT_FLOAT) {
|
||
o(0xd9); /* flds */
|
||
r = 0;
|
||
} else if ((ft & VT_BTYPE) == VT_DOUBLE) {
|
||
o(0xdd); /* fldl */
|
||
r = 0;
|
||
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||
o(0xdb); /* fldt */
|
||
r = 5;
|
||
} else if ((ft & VT_TYPE) == VT_BYTE) {
|
||
o(0xbe0f); /* movsbl */
|
||
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
|
||
o(0xb60f); /* movzbl */
|
||
} else if ((ft & VT_TYPE) == VT_SHORT) {
|
||
o(0xbf0f); /* movswl */
|
||
} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
|
||
o(0xb70f); /* movzwl */
|
||
} else {
|
||
o(0x8b); /* movl */
|
||
}
|
||
gen_modrm(r, fr, sv->sym, fc);
|
||
} else {
|
||
if (v == VT_CONST) {
|
||
o(0xb8 + r); /* mov $xx, r */
|
||
gen_addr32(fr, sv->sym, fc);
|
||
} else if (v == VT_LOCAL) {
|
||
o(0x8d); /* lea xxx(%ebp), r */
|
||
gen_modrm(r, VT_LOCAL, sv->sym, fc);
|
||
} else if (v == VT_CMP) {
|
||
oad(0xb8 + r, 0); /* mov $0, r */
|
||
o(0x0f); /* setxx %br */
|
||
o(fc);
|
||
o(0xc0 + r);
|
||
} else if (v == VT_JMP || v == VT_JMPI) {
|
||
t = v & 1;
|
||
oad(0xb8 + r, t); /* mov $1, r */
|
||
o(0x05eb); /* jmp after */
|
||
gsym(fc);
|
||
oad(0xb8 + r, t ^ 1); /* mov $0, r */
|
||
} else if (v != r) {
|
||
o(0x89);
|
||
o(0xc0 + r + v * 8); /* mov v, r */
|
||
}
|
||
}
|
||
}
|
||
|
||
/* store register 'r' in lvalue 'v' */
|
||
void store(int r, SValue *v)
|
||
{
|
||
int fr, bt, ft, fc;
|
||
|
||
ft = v->type.t;
|
||
fc = v->c.ul;
|
||
fr = v->r & VT_VALMASK;
|
||
bt = ft & VT_BTYPE;
|
||
/* XXX: incorrect if float reg to reg */
|
||
if (bt == VT_FLOAT) {
|
||
o(0xd9); /* fsts */
|
||
r = 2;
|
||
} else if (bt == VT_DOUBLE) {
|
||
o(0xdd); /* fstpl */
|
||
r = 2;
|
||
} else if (bt == VT_LDOUBLE) {
|
||
o(0xc0d9); /* fld %st(0) */
|
||
o(0xdb); /* fstpt */
|
||
r = 7;
|
||
} else {
|
||
if (bt == VT_SHORT)
|
||
o(0x66);
|
||
if (bt == VT_BYTE || bt == VT_BOOL)
|
||
o(0x88);
|
||
else
|
||
o(0x89);
|
||
}
|
||
if (fr == VT_CONST ||
|
||
fr == VT_LOCAL ||
|
||
(v->r & VT_LVAL)) {
|
||
gen_modrm(r, v->r, v->sym, fc);
|
||
} else if (fr != r) {
|
||
o(0xc0 + fr + r * 8); /* mov r, fr */
|
||
}
|
||
}
|
||
|
||
static void gadd_sp(int val)
|
||
{
|
||
if (val == (char)val) {
|
||
o(0xc483);
|
||
g(val);
|
||
} else {
|
||
oad(0xc481, val); /* add $xxx, %esp */
|
||
}
|
||
}
|
||
|
||
/* 'is_jmp' is '1' if it is a jump */
|
||
static void gcall_or_jmp(int is_jmp)
|
||
{
|
||
int r;
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||
/* constant case */
|
||
if (vtop->r & VT_SYM) {
|
||
/* relocation case */
|
||
greloc(cur_text_section, vtop->sym,
|
||
ind + 1, R_386_PC32);
|
||
} else {
|
||
/* put an empty PC32 relocation */
|
||
put_elf_reloc(symtab_section, cur_text_section,
|
||
ind + 1, R_386_PC32, 0);
|
||
}
|
||
oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */
|
||
} else {
|
||
/* otherwise, indirect call */
|
||
r = gv(RC_INT);
|
||
o(0xff); /* call/jmp *r */
|
||
o(0xd0 + r + (is_jmp << 4));
|
||
}
|
||
}
|
||
|
||
static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
|
||
|
||
/* Generate function call. The function address is pushed first, then
|
||
all the parameters in call order. This functions pops all the
|
||
parameters and the function address. */
|
||
void gfunc_call(int nb_args)
|
||
{
|
||
int size, align, r, args_size, i, func_call;
|
||
Sym *func_sym;
|
||
|
||
args_size = 0;
|
||
for(i = 0;i < nb_args; i++) {
|
||
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
|
||
size = type_size(&vtop->type, &align);
|
||
/* align to stack align size */
|
||
size = (size + 3) & ~3;
|
||
/* allocate the necessary size on stack */
|
||
oad(0xec81, size); /* sub $xxx, %esp */
|
||
/* generate structure store */
|
||
r = get_reg(RC_INT);
|
||
o(0x89); /* mov %esp, r */
|
||
o(0xe0 + r);
|
||
vset(&vtop->type, r | VT_LVAL, 0);
|
||
vswap();
|
||
vstore();
|
||
args_size += size;
|
||
} else if (is_float(vtop->type.t)) {
|
||
gv(RC_FLOAT); /* only one float register */
|
||
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
|
||
size = 4;
|
||
else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
|
||
size = 8;
|
||
else
|
||
size = 12;
|
||
oad(0xec81, size); /* sub $xxx, %esp */
|
||
if (size == 12)
|
||
o(0x7cdb);
|
||
else
|
||
o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
|
||
g(0x24);
|
||
g(0x00);
|
||
args_size += size;
|
||
} else {
|
||
/* simple type (currently always same size) */
|
||
/* XXX: implicit cast ? */
|
||
r = gv(RC_INT);
|
||
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
|
||
size = 8;
|
||
o(0x50 + vtop->r2); /* push r */
|
||
} else {
|
||
size = 4;
|
||
}
|
||
o(0x50 + r); /* push r */
|
||
args_size += size;
|
||
}
|
||
vtop--;
|
||
}
|
||
save_regs(0); /* save used temporary registers */
|
||
func_sym = vtop->type.ref;
|
||
func_call = func_sym->r;
|
||
/* fast call case */
|
||
if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
|
||
int fastcall_nb_regs;
|
||
fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
|
||
for(i = 0;i < fastcall_nb_regs; i++) {
|
||
if (args_size <= 0)
|
||
break;
|
||
o(0x58 + fastcall_regs[i]); /* pop r */
|
||
/* XXX: incorrect for struct/floats */
|
||
args_size -= 4;
|
||
}
|
||
}
|
||
gcall_or_jmp(0);
|
||
if (args_size && func_sym->r != FUNC_STDCALL)
|
||
gadd_sp(args_size);
|
||
vtop--;
|
||
}
|
||
|
||
#ifdef TCC_TARGET_PE
|
||
#define FUNC_PROLOG_SIZE 10
|
||
#else
|
||
#define FUNC_PROLOG_SIZE 9
|
||
#endif
|
||
|
||
/* generate function prolog of type 't' */
|
||
void gfunc_prolog(CType *func_type)
|
||
{
|
||
int addr, align, size, func_call, fastcall_nb_regs;
|
||
int param_index, param_addr;
|
||
Sym *sym;
|
||
CType *type;
|
||
|
||
sym = func_type->ref;
|
||
func_call = sym->r;
|
||
addr = 8;
|
||
loc = 0;
|
||
if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
|
||
fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
|
||
} else {
|
||
fastcall_nb_regs = 0;
|
||
}
|
||
param_index = 0;
|
||
|
||
ind += FUNC_PROLOG_SIZE;
|
||
func_sub_sp_offset = ind;
|
||
/* if the function returns a structure, then add an
|
||
implicit pointer parameter */
|
||
func_vt = sym->type;
|
||
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
|
||
/* XXX: fastcall case ? */
|
||
func_vc = addr;
|
||
addr += 4;
|
||
param_index++;
|
||
}
|
||
/* define parameters */
|
||
while ((sym = sym->next) != NULL) {
|
||
type = &sym->type;
|
||
size = type_size(type, &align);
|
||
size = (size + 3) & ~3;
|
||
#ifdef FUNC_STRUCT_PARAM_AS_PTR
|
||
/* structs are passed as pointer */
|
||
if ((type->t & VT_BTYPE) == VT_STRUCT) {
|
||
size = 4;
|
||
}
|
||
#endif
|
||
if (param_index < fastcall_nb_regs) {
|
||
/* save FASTCALL register */
|
||
loc -= 4;
|
||
o(0x89); /* movl */
|
||
gen_modrm(fastcall_regs[param_index], VT_LOCAL, NULL, loc);
|
||
param_addr = loc;
|
||
} else {
|
||
param_addr = addr;
|
||
addr += size;
|
||
}
|
||
sym_push(sym->v & ~SYM_FIELD, type,
|
||
VT_LOCAL | VT_LVAL, param_addr);
|
||
param_index++;
|
||
}
|
||
func_ret_sub = 0;
|
||
/* pascal type call ? */
|
||
if (func_call == FUNC_STDCALL)
|
||
func_ret_sub = addr - 8;
|
||
|
||
/* leave some room for bound checking code */
|
||
if (do_bounds_check) {
|
||
oad(0xb8, 0); /* lbound section pointer */
|
||
oad(0xb8, 0); /* call to function */
|
||
func_bound_offset = lbounds_section->data_offset;
|
||
}
|
||
}
|
||
|
||
/* generate function epilog */
|
||
void gfunc_epilog(void)
|
||
{
|
||
int v, saved_ind;
|
||
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
if (do_bounds_check && func_bound_offset != lbounds_section->data_offset) {
|
||
int saved_ind;
|
||
int *bounds_ptr;
|
||
Sym *sym, *sym_data;
|
||
/* add end of table info */
|
||
bounds_ptr = section_ptr_add(lbounds_section, sizeof(int));
|
||
*bounds_ptr = 0;
|
||
/* generate bound local allocation */
|
||
saved_ind = ind;
|
||
ind = func_sub_sp_offset;
|
||
sym_data = get_sym_ref(&char_pointer_type, lbounds_section,
|
||
func_bound_offset, lbounds_section->data_offset);
|
||
greloc(cur_text_section, sym_data,
|
||
ind + 1, R_386_32);
|
||
oad(0xb8, 0); /* mov %eax, xxx */
|
||
sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0);
|
||
greloc(cur_text_section, sym,
|
||
ind + 1, R_386_PC32);
|
||
oad(0xe8, -4);
|
||
ind = saved_ind;
|
||
/* generate bound check local freeing */
|
||
o(0x5250); /* save returned value, if any */
|
||
greloc(cur_text_section, sym_data,
|
||
ind + 1, R_386_32);
|
||
oad(0xb8, 0); /* mov %eax, xxx */
|
||
sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0);
|
||
greloc(cur_text_section, sym,
|
||
ind + 1, R_386_PC32);
|
||
oad(0xe8, -4);
|
||
o(0x585a); /* restore returned value, if any */
|
||
}
|
||
#endif
|
||
o(0xc9); /* leave */
|
||
if (func_ret_sub == 0) {
|
||
o(0xc3); /* ret */
|
||
} else {
|
||
o(0xc2); /* ret n */
|
||
g(func_ret_sub);
|
||
g(func_ret_sub >> 8);
|
||
}
|
||
/* align local size to word & save local variables */
|
||
|
||
v = (-loc + 3) & -4;
|
||
saved_ind = ind;
|
||
ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
|
||
#ifdef TCC_TARGET_PE
|
||
if (v >= 4096) {
|
||
Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0);
|
||
oad(0xb8, v); /* mov stacksize, %eax */
|
||
oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */
|
||
greloc(cur_text_section, sym, ind-4, R_386_PC32);
|
||
} else
|
||
#endif
|
||
{
|
||
o(0xe58955); /* push %ebp, mov %esp, %ebp */
|
||
o(0xec81); /* sub esp, stacksize */
|
||
gen_le32(v);
|
||
#if FUNC_PROLOG_SIZE == 10
|
||
o(0x90); /* adjust to FUNC_PROLOG_SIZE */
|
||
#endif
|
||
}
|
||
ind = saved_ind;
|
||
}
|
||
|
||
/* generate a jump to a label */
|
||
long gjmp(int t)
|
||
{
|
||
return psym(0xe9, t);
|
||
}
|
||
|
||
/* generate a jump to a fixed address */
|
||
void gjmp_addr(int a)
|
||
{
|
||
int r;
|
||
r = a - ind - 2;
|
||
if (r == (char)r) {
|
||
g(0xeb);
|
||
g(r);
|
||
} else {
|
||
oad(0xe9, a - ind - 5);
|
||
}
|
||
}
|
||
|
||
/* generate a test. set 'inv' to invert test. Stack entry is popped */
|
||
int gtst(int inv, int t)
|
||
{
|
||
int v, *p;
|
||
|
||
v = vtop->r & VT_VALMASK;
|
||
if (v == VT_CMP) {
|
||
/* fast case : can jump directly since flags are set */
|
||
g(0x0f);
|
||
t = psym((vtop->c.i - 16) ^ inv, t);
|
||
} else if (v == VT_JMP || v == VT_JMPI) {
|
||
/* && or || optimization */
|
||
if ((v & 1) == inv) {
|
||
/* insert vtop->c jump list in t */
|
||
p = &vtop->c.i;
|
||
while (*p != 0)
|
||
p = (int *)(cur_text_section->data + *p);
|
||
*p = t;
|
||
t = vtop->c.i;
|
||
} else {
|
||
t = gjmp(t);
|
||
gsym(vtop->c.i);
|
||
}
|
||
} else {
|
||
if (is_float(vtop->type.t)) {
|
||
vpushi(0);
|
||
gen_op(TOK_NE);
|
||
}
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||
/* constant jmp optimization */
|
||
if ((vtop->c.i != 0) != inv)
|
||
t = gjmp(t);
|
||
} else {
|
||
v = gv(RC_INT);
|
||
o(0x85);
|
||
o(0xc0 + v * 9);
|
||
g(0x0f);
|
||
t = psym(0x85 ^ inv, t);
|
||
}
|
||
}
|
||
vtop--;
|
||
return t;
|
||
}
|
||
|
||
/* generate an integer binary operation */
|
||
void gen_opi(int op)
|
||
{
|
||
int r, fr, opc, c;
|
||
|
||
switch(op) {
|
||
case '+':
|
||
case TOK_ADDC1: /* add with carry generation */
|
||
opc = 0;
|
||
gen_op8:
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||
/* constant case */
|
||
vswap();
|
||
r = gv(RC_INT);
|
||
vswap();
|
||
c = vtop->c.i;
|
||
if (c == (char)c) {
|
||
/* XXX: generate inc and dec for smaller code ? */
|
||
o(0x83);
|
||
o(0xc0 | (opc << 3) | r);
|
||
g(c);
|
||
} else {
|
||
o(0x81);
|
||
oad(0xc0 | (opc << 3) | r, c);
|
||
}
|
||
} else {
|
||
gv2(RC_INT, RC_INT);
|
||
r = vtop[-1].r;
|
||
fr = vtop[0].r;
|
||
o((opc << 3) | 0x01);
|
||
o(0xc0 + r + fr * 8);
|
||
}
|
||
vtop--;
|
||
if (op >= TOK_ULT && op <= TOK_GT) {
|
||
vtop->r = VT_CMP;
|
||
vtop->c.i = op;
|
||
}
|
||
break;
|
||
case '-':
|
||
case TOK_SUBC1: /* sub with carry generation */
|
||
opc = 5;
|
||
goto gen_op8;
|
||
case TOK_ADDC2: /* add with carry use */
|
||
opc = 2;
|
||
goto gen_op8;
|
||
case TOK_SUBC2: /* sub with carry use */
|
||
opc = 3;
|
||
goto gen_op8;
|
||
case '&':
|
||
opc = 4;
|
||
goto gen_op8;
|
||
case '^':
|
||
opc = 6;
|
||
goto gen_op8;
|
||
case '|':
|
||
opc = 1;
|
||
goto gen_op8;
|
||
case '*':
|
||
gv2(RC_INT, RC_INT);
|
||
r = vtop[-1].r;
|
||
fr = vtop[0].r;
|
||
vtop--;
|
||
o(0xaf0f); /* imul fr, r */
|
||
o(0xc0 + fr + r * 8);
|
||
break;
|
||
case TOK_SHL:
|
||
opc = 4;
|
||
goto gen_shift;
|
||
case TOK_SHR:
|
||
opc = 5;
|
||
goto gen_shift;
|
||
case TOK_SAR:
|
||
opc = 7;
|
||
gen_shift:
|
||
opc = 0xc0 | (opc << 3);
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||
/* constant case */
|
||
vswap();
|
||
r = gv(RC_INT);
|
||
vswap();
|
||
c = vtop->c.i & 0x1f;
|
||
o(0xc1); /* shl/shr/sar $xxx, r */
|
||
o(opc | r);
|
||
g(c);
|
||
} else {
|
||
/* we generate the shift in ecx */
|
||
gv2(RC_INT, RC_ECX);
|
||
r = vtop[-1].r;
|
||
o(0xd3); /* shl/shr/sar %cl, r */
|
||
o(opc | r);
|
||
}
|
||
vtop--;
|
||
break;
|
||
case '/':
|
||
case TOK_UDIV:
|
||
case TOK_PDIV:
|
||
case '%':
|
||
case TOK_UMOD:
|
||
case TOK_UMULL:
|
||
/* first operand must be in eax */
|
||
/* XXX: need better constraint for second operand */
|
||
gv2(RC_EAX, RC_ECX);
|
||
r = vtop[-1].r;
|
||
fr = vtop[0].r;
|
||
vtop--;
|
||
save_reg(TREG_EDX);
|
||
if (op == TOK_UMULL) {
|
||
o(0xf7); /* mul fr */
|
||
o(0xe0 + fr);
|
||
vtop->r2 = TREG_EDX;
|
||
r = TREG_EAX;
|
||
} else {
|
||
if (op == TOK_UDIV || op == TOK_UMOD) {
|
||
o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
|
||
o(0xf0 + fr);
|
||
} else {
|
||
o(0xf799); /* cltd, idiv fr, %eax */
|
||
o(0xf8 + fr);
|
||
}
|
||
if (op == '%' || op == TOK_UMOD)
|
||
r = TREG_EDX;
|
||
else
|
||
r = TREG_EAX;
|
||
}
|
||
vtop->r = r;
|
||
break;
|
||
default:
|
||
opc = 7;
|
||
goto gen_op8;
|
||
}
|
||
}
|
||
|
||
/* generate a floating point operation 'v = t1 op t2' instruction. The
|
||
two operands are guaranted to have the same floating point type */
|
||
/* XXX: need to use ST1 too */
|
||
void gen_opf(int op)
|
||
{
|
||
int a, ft, fc, swapped, r;
|
||
|
||
/* convert constants to memory references */
|
||
if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||
vswap();
|
||
gv(RC_FLOAT);
|
||
vswap();
|
||
}
|
||
if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
|
||
gv(RC_FLOAT);
|
||
|
||
/* must put at least one value in the floating point register */
|
||
if ((vtop[-1].r & VT_LVAL) &&
|
||
(vtop[0].r & VT_LVAL)) {
|
||
vswap();
|
||
gv(RC_FLOAT);
|
||
vswap();
|
||
}
|
||
swapped = 0;
|
||
/* swap the stack if needed so that t1 is the register and t2 is
|
||
the memory reference */
|
||
if (vtop[-1].r & VT_LVAL) {
|
||
vswap();
|
||
swapped = 1;
|
||
}
|
||
if (op >= TOK_ULT && op <= TOK_GT) {
|
||
/* load on stack second operand */
|
||
load(TREG_ST0, vtop);
|
||
save_reg(TREG_EAX); /* eax is used by FP comparison code */
|
||
if (op == TOK_GE || op == TOK_GT)
|
||
swapped = !swapped;
|
||
else if (op == TOK_EQ || op == TOK_NE)
|
||
swapped = 0;
|
||
if (swapped)
|
||
o(0xc9d9); /* fxch %st(1) */
|
||
o(0xe9da); /* fucompp */
|
||
o(0xe0df); /* fnstsw %ax */
|
||
if (op == TOK_EQ) {
|
||
o(0x45e480); /* and $0x45, %ah */
|
||
o(0x40fC80); /* cmp $0x40, %ah */
|
||
} else if (op == TOK_NE) {
|
||
o(0x45e480); /* and $0x45, %ah */
|
||
o(0x40f480); /* xor $0x40, %ah */
|
||
op = TOK_NE;
|
||
} else if (op == TOK_GE || op == TOK_LE) {
|
||
o(0x05c4f6); /* test $0x05, %ah */
|
||
op = TOK_EQ;
|
||
} else {
|
||
o(0x45c4f6); /* test $0x45, %ah */
|
||
op = TOK_EQ;
|
||
}
|
||
vtop--;
|
||
vtop->r = VT_CMP;
|
||
vtop->c.i = op;
|
||
} else {
|
||
/* no memory reference possible for long double operations */
|
||
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
|
||
load(TREG_ST0, vtop);
|
||
swapped = !swapped;
|
||
}
|
||
|
||
switch(op) {
|
||
default:
|
||
case '+':
|
||
a = 0;
|
||
break;
|
||
case '-':
|
||
a = 4;
|
||
if (swapped)
|
||
a++;
|
||
break;
|
||
case '*':
|
||
a = 1;
|
||
break;
|
||
case '/':
|
||
a = 6;
|
||
if (swapped)
|
||
a++;
|
||
break;
|
||
}
|
||
ft = vtop->type.t;
|
||
fc = vtop->c.ul;
|
||
if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||
o(0xde); /* fxxxp %st, %st(1) */
|
||
o(0xc1 + (a << 3));
|
||
} else {
|
||
/* if saved lvalue, then we must reload it */
|
||
r = vtop->r;
|
||
if ((r & VT_VALMASK) == VT_LLOCAL) {
|
||
SValue v1;
|
||
r = get_reg(RC_INT);
|
||
v1.type.t = VT_INT;
|
||
v1.r = VT_LOCAL | VT_LVAL;
|
||
v1.c.ul = fc;
|
||
load(r, &v1);
|
||
fc = 0;
|
||
}
|
||
|
||
if ((ft & VT_BTYPE) == VT_DOUBLE)
|
||
o(0xdc);
|
||
else
|
||
o(0xd8);
|
||
gen_modrm(a, r, vtop->sym, fc);
|
||
}
|
||
vtop--;
|
||
}
|
||
}
|
||
|
||
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
|
||
and 'long long' cases. */
|
||
void gen_cvt_itof(int t)
|
||
{
|
||
save_reg(TREG_ST0);
|
||
gv(RC_INT);
|
||
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
|
||
/* signed long long to float/double/long double (unsigned case
|
||
is handled generically) */
|
||
o(0x50 + vtop->r2); /* push r2 */
|
||
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
|
||
o(0x242cdf); /* fildll (%esp) */
|
||
o(0x08c483); /* add $8, %esp */
|
||
} else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
|
||
(VT_INT | VT_UNSIGNED)) {
|
||
/* unsigned int to float/double/long double */
|
||
o(0x6a); /* push $0 */
|
||
g(0x00);
|
||
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
|
||
o(0x242cdf); /* fildll (%esp) */
|
||
o(0x08c483); /* add $8, %esp */
|
||
} else {
|
||
/* int to float/double/long double */
|
||
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
|
||
o(0x2404db); /* fildl (%esp) */
|
||
o(0x04c483); /* add $4, %esp */
|
||
}
|
||
vtop->r = TREG_ST0;
|
||
}
|
||
|
||
/* convert fp to int 't' type */
|
||
/* XXX: handle long long case */
|
||
void gen_cvt_ftoi(int t)
|
||
{
|
||
int r, r2, size;
|
||
Sym *sym;
|
||
CType ushort_type;
|
||
|
||
ushort_type.t = VT_SHORT | VT_UNSIGNED;
|
||
|
||
gv(RC_FLOAT);
|
||
if (t != VT_INT)
|
||
size = 8;
|
||
else
|
||
size = 4;
|
||
|
||
o(0x2dd9); /* ldcw xxx */
|
||
sym = external_global_sym(TOK___tcc_int_fpu_control,
|
||
&ushort_type, VT_LVAL);
|
||
greloc(cur_text_section, sym,
|
||
ind, R_386_32);
|
||
gen_le32(0);
|
||
|
||
oad(0xec81, size); /* sub $xxx, %esp */
|
||
if (size == 4)
|
||
o(0x1cdb); /* fistpl */
|
||
else
|
||
o(0x3cdf); /* fistpll */
|
||
o(0x24);
|
||
o(0x2dd9); /* ldcw xxx */
|
||
sym = external_global_sym(TOK___tcc_fpu_control,
|
||
&ushort_type, VT_LVAL);
|
||
greloc(cur_text_section, sym,
|
||
ind, R_386_32);
|
||
gen_le32(0);
|
||
|
||
r = get_reg(RC_INT);
|
||
o(0x58 + r); /* pop r */
|
||
if (size == 8) {
|
||
if (t == VT_LLONG) {
|
||
vtop->r = r; /* mark reg as used */
|
||
r2 = get_reg(RC_INT);
|
||
o(0x58 + r2); /* pop r2 */
|
||
vtop->r2 = r2;
|
||
} else {
|
||
o(0x04c483); /* add $4, %esp */
|
||
}
|
||
}
|
||
vtop->r = r;
|
||
}
|
||
|
||
/* convert from one floating point type to another */
|
||
void gen_cvt_ftof(int t)
|
||
{
|
||
/* all we have to do on i386 is to put the float in a register */
|
||
gv(RC_FLOAT);
|
||
}
|
||
|
||
/* computed goto support */
|
||
void ggoto(void)
|
||
{
|
||
gcall_or_jmp(1);
|
||
vtop--;
|
||
}
|
||
|
||
/* bound check support functions */
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
|
||
/* generate a bounded pointer addition */
|
||
void gen_bounded_ptr_add(void)
|
||
{
|
||
Sym *sym;
|
||
|
||
/* prepare fast i386 function call (args in eax and edx) */
|
||
gv2(RC_EAX, RC_EDX);
|
||
/* save all temporary registers */
|
||
vtop -= 2;
|
||
save_regs(0);
|
||
/* do a fast function call */
|
||
sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0);
|
||
greloc(cur_text_section, sym,
|
||
ind + 1, R_386_PC32);
|
||
oad(0xe8, -4);
|
||
/* returned pointer is in eax */
|
||
vtop++;
|
||
vtop->r = TREG_EAX | VT_BOUNDED;
|
||
/* address of bounding function call point */
|
||
vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
|
||
}
|
||
|
||
/* patch pointer addition in vtop so that pointer dereferencing is
|
||
also tested */
|
||
void gen_bounded_ptr_deref(void)
|
||
{
|
||
int func;
|
||
int size, align;
|
||
Elf32_Rel *rel;
|
||
Sym *sym;
|
||
|
||
size = 0;
|
||
/* XXX: put that code in generic part of tcc */
|
||
if (!is_float(vtop->type.t)) {
|
||
if (vtop->r & VT_LVAL_BYTE)
|
||
size = 1;
|
||
else if (vtop->r & VT_LVAL_SHORT)
|
||
size = 2;
|
||
}
|
||
if (!size)
|
||
size = type_size(&vtop->type, &align);
|
||
switch(size) {
|
||
case 1: func = TOK___bound_ptr_indir1; break;
|
||
case 2: func = TOK___bound_ptr_indir2; break;
|
||
case 4: func = TOK___bound_ptr_indir4; break;
|
||
case 8: func = TOK___bound_ptr_indir8; break;
|
||
case 12: func = TOK___bound_ptr_indir12; break;
|
||
case 16: func = TOK___bound_ptr_indir16; break;
|
||
default:
|
||
error("unhandled size when derefencing bounded pointer");
|
||
func = 0;
|
||
break;
|
||
}
|
||
|
||
/* patch relocation */
|
||
/* XXX: find a better solution ? */
|
||
rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul);
|
||
sym = external_global_sym(func, &func_old_type, 0);
|
||
if (!sym->c)
|
||
put_extern_sym(sym, NULL, 0, 0);
|
||
rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
|
||
}
|
||
#endif
|
||
|
||
/* end of X86 code generator */
|
||
/*************************************************************/
|
||
|
||
//---------------------------------------------------------------------------
|
||
#endif
|
||
|
||
// njn: commented these out
|
||
//#ifdef TCC_TARGET_ARM
|
||
//#include "arm-gen.c"
|
||
//#endif
|
||
//
|
||
//#ifdef TCC_TARGET_C67
|
||
//#include "c67-gen.c"
|
||
//#endif
|
||
|
||
#ifdef CONFIG_TCC_STATIC
|
||
|
||
#define RTLD_LAZY 0x001
|
||
#define RTLD_NOW 0x002
|
||
#define RTLD_GLOBAL 0x100
|
||
#define RTLD_DEFAULT NULL
|
||
|
||
/* dummy function for profiling */
|
||
void *dlopen(const char *filename, int flag)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
const char *dlerror(void)
|
||
{
|
||
return "error";
|
||
}
|
||
|
||
typedef struct TCCSyms {
|
||
char *str;
|
||
void *ptr;
|
||
} TCCSyms;
|
||
|
||
#define TCCSYM(a) { #a, &a, },
|
||
|
||
/* add the symbol you want here if no dynamic linking is done */
|
||
static TCCSyms tcc_syms[] = {
|
||
#if !defined(CONFIG_TCCBOOT)
|
||
TCCSYM(printf)
|
||
TCCSYM(fprintf)
|
||
TCCSYM(fopen)
|
||
TCCSYM(fclose)
|
||
#endif
|
||
{ NULL, NULL },
|
||
};
|
||
|
||
void *resolve_sym(TCCState *s1, const char *symbol, int type)
|
||
{
|
||
TCCSyms *p;
|
||
p = tcc_syms;
|
||
while (p->str != NULL) {
|
||
if (!strcmp(p->str, symbol))
|
||
return p->ptr;
|
||
p++;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
#elif !defined(WIN32)
|
||
|
||
#include <dlfcn.h>
|
||
|
||
void *resolve_sym(TCCState *s1, const char *sym, int type)
|
||
{
|
||
assert(0);
|
||
return 0; //dlsym(RTLD_DEFAULT, sym);
|
||
// jrs: remove need for dlsym
|
||
}
|
||
|
||
#endif
|
||
|
||
/********************************************************/
|
||
|
||
/* we use our own 'finite' function to avoid potential problems with
|
||
non standard math libs */
|
||
/* XXX: endianness dependent */
|
||
int ieee_finite(double d)
|
||
{
|
||
int *p = (int *)&d;
|
||
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
|
||
}
|
||
|
||
/* copy a string and truncate it. */
|
||
static char *pstrcpy(char *buf, int buf_size, const char *s)
|
||
{
|
||
char *q, *q_end;
|
||
int c;
|
||
|
||
if (buf_size > 0) {
|
||
q = buf;
|
||
q_end = buf + buf_size - 1;
|
||
while (q < q_end) {
|
||
c = *s++;
|
||
if (c == '\0')
|
||
break;
|
||
*q++ = c;
|
||
}
|
||
*q = '\0';
|
||
}
|
||
return buf;
|
||
}
|
||
|
||
/* strcat and truncate. */
|
||
static char *pstrcat(char *buf, int buf_size, const char *s)
|
||
{
|
||
int len;
|
||
len = strlen(buf);
|
||
if (len < buf_size)
|
||
pstrcpy(buf + len, buf_size - len, s);
|
||
return buf;
|
||
}
|
||
|
||
static int strstart(const char *str, const char *val, const char **ptr)
|
||
{
|
||
const char *p, *q;
|
||
p = str;
|
||
q = val;
|
||
while (*q != '\0') {
|
||
if (*p != *q)
|
||
return 0;
|
||
p++;
|
||
q++;
|
||
}
|
||
if (ptr)
|
||
*ptr = p;
|
||
return 1;
|
||
}
|
||
|
||
/* memory management */
|
||
#ifdef MEM_DEBUG
|
||
int mem_cur_size;
|
||
int mem_max_size;
|
||
#endif
|
||
|
||
static inline void tcc_free(void *ptr)
|
||
{
|
||
#ifdef MEM_DEBUG
|
||
mem_cur_size -= malloc_usable_size(ptr);
|
||
#endif
|
||
free(ptr);
|
||
}
|
||
|
||
static void *tcc_malloc(unsigned long size)
|
||
{
|
||
void *ptr;
|
||
ptr = malloc(size);
|
||
if (!ptr && size)
|
||
error("memory full");
|
||
#ifdef MEM_DEBUG
|
||
mem_cur_size += malloc_usable_size(ptr);
|
||
if (mem_cur_size > mem_max_size)
|
||
mem_max_size = mem_cur_size;
|
||
#endif
|
||
return ptr;
|
||
}
|
||
|
||
static void *tcc_mallocz(unsigned long size)
|
||
{
|
||
void *ptr;
|
||
ptr = tcc_malloc(size);
|
||
memset(ptr, 0, size);
|
||
return ptr;
|
||
}
|
||
|
||
static inline void *tcc_realloc(void *ptr, unsigned long size)
|
||
{
|
||
void *ptr1;
|
||
#ifdef MEM_DEBUG
|
||
mem_cur_size -= malloc_usable_size(ptr);
|
||
#endif
|
||
ptr1 = realloc(ptr, size);
|
||
#ifdef MEM_DEBUG
|
||
/* NOTE: count not correct if alloc error, but not critical */
|
||
mem_cur_size += malloc_usable_size(ptr1);
|
||
if (mem_cur_size > mem_max_size)
|
||
mem_max_size = mem_cur_size;
|
||
#endif
|
||
return ptr1;
|
||
}
|
||
|
||
static char *tcc_strdup(const char *str)
|
||
{
|
||
char *ptr;
|
||
ptr = tcc_malloc(strlen(str) + 1);
|
||
strcpy(ptr, str);
|
||
return ptr;
|
||
}
|
||
|
||
#define free(p) use_tcc_free(p)
|
||
#define malloc(s) use_tcc_malloc(s)
|
||
#define realloc(p, s) use_tcc_realloc(p, s)
|
||
|
||
static void dynarray_add(void ***ptab, int *nb_ptr, void *data)
|
||
{
|
||
int nb, nb_alloc;
|
||
void **pp;
|
||
|
||
nb = *nb_ptr;
|
||
pp = *ptab;
|
||
/* every power of two we double array size */
|
||
if ((nb & (nb - 1)) == 0) {
|
||
if (!nb)
|
||
nb_alloc = 1;
|
||
else
|
||
nb_alloc = nb * 2;
|
||
pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
|
||
if (!pp)
|
||
error("memory full");
|
||
*ptab = pp;
|
||
}
|
||
pp[nb++] = data;
|
||
*nb_ptr = nb;
|
||
}
|
||
|
||
/* symbol allocator */
|
||
static Sym *__sym_malloc(void)
|
||
{
|
||
Sym *sym_pool, *sym, *last_sym;
|
||
int i;
|
||
|
||
sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
|
||
|
||
last_sym = sym_free_first;
|
||
sym = sym_pool;
|
||
for(i = 0; i < SYM_POOL_NB; i++) {
|
||
sym->next = last_sym;
|
||
last_sym = sym;
|
||
sym++;
|
||
}
|
||
sym_free_first = last_sym;
|
||
return last_sym;
|
||
}
|
||
|
||
static inline Sym *sym_malloc(void)
|
||
{
|
||
Sym *sym;
|
||
sym = sym_free_first;
|
||
if (!sym)
|
||
sym = __sym_malloc();
|
||
sym_free_first = sym->next;
|
||
return sym;
|
||
}
|
||
|
||
static inline void sym_free(Sym *sym)
|
||
{
|
||
sym->next = sym_free_first;
|
||
sym_free_first = sym;
|
||
}
|
||
|
||
Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
|
||
{
|
||
Section *sec;
|
||
|
||
sec = tcc_mallocz(sizeof(Section) + strlen(name));
|
||
strcpy(sec->name, name);
|
||
sec->sh_type = sh_type;
|
||
sec->sh_flags = sh_flags;
|
||
switch(sh_type) {
|
||
case SHT_HASH:
|
||
case SHT_REL:
|
||
case SHT_DYNSYM:
|
||
case SHT_SYMTAB:
|
||
case SHT_DYNAMIC:
|
||
sec->sh_addralign = 4;
|
||
break;
|
||
case SHT_STRTAB:
|
||
sec->sh_addralign = 1;
|
||
break;
|
||
default:
|
||
sec->sh_addralign = 32; /* default conservative alignment */
|
||
break;
|
||
}
|
||
|
||
/* only add section if not private */
|
||
if (!(sh_flags & SHF_PRIVATE)) {
|
||
sec->sh_num = s1->nb_sections;
|
||
dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
|
||
}
|
||
return sec;
|
||
}
|
||
|
||
static void free_section(Section *s)
|
||
{
|
||
tcc_free(s->data);
|
||
tcc_free(s);
|
||
}
|
||
|
||
/* realloc section and set its content to zero */
|
||
static void section_realloc(Section *sec, unsigned long new_size)
|
||
{
|
||
unsigned long size;
|
||
unsigned char *data;
|
||
|
||
size = sec->data_allocated;
|
||
if (size == 0)
|
||
size = 1;
|
||
while (size < new_size)
|
||
size = size * 2;
|
||
data = tcc_realloc(sec->data, size);
|
||
if (!data)
|
||
error("memory full");
|
||
memset(data + sec->data_allocated, 0, size - sec->data_allocated);
|
||
sec->data = data;
|
||
sec->data_allocated = size;
|
||
}
|
||
|
||
/* reserve at least 'size' bytes in section 'sec' from
|
||
sec->data_offset. */
|
||
static void *section_ptr_add(Section *sec, unsigned long size)
|
||
{
|
||
unsigned long offset, offset1;
|
||
|
||
offset = sec->data_offset;
|
||
offset1 = offset + size;
|
||
if (offset1 > sec->data_allocated)
|
||
section_realloc(sec, offset1);
|
||
sec->data_offset = offset1;
|
||
return sec->data + offset;
|
||
}
|
||
|
||
/* return a reference to a section, and create it if it does not
|
||
exists */
|
||
Section *find_section(TCCState *s1, const char *name)
|
||
{
|
||
Section *sec;
|
||
int i;
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
sec = s1->sections[i];
|
||
if (!strcmp(name, sec->name))
|
||
return sec;
|
||
}
|
||
/* sections are created as PROGBITS */
|
||
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
|
||
}
|
||
|
||
#define SECTION_ABS ((void *)1)
|
||
|
||
/* update sym->c so that it points to an external symbol in section
|
||
'section' with value 'value' */
|
||
static void put_extern_sym2(Sym *sym, Section *section,
|
||
unsigned long value, unsigned long size,
|
||
int can_add_underscore)
|
||
{
|
||
int sym_type, sym_bind, sh_num, info;
|
||
Elf32_Sym *esym;
|
||
const char *name;
|
||
char buf1[256];
|
||
|
||
if (section == NULL)
|
||
sh_num = SHN_UNDEF;
|
||
else if (section == SECTION_ABS)
|
||
sh_num = SHN_ABS;
|
||
else
|
||
sh_num = section->sh_num;
|
||
if (!sym->c) {
|
||
if ((sym->type.t & VT_BTYPE) == VT_FUNC)
|
||
sym_type = STT_FUNC;
|
||
else
|
||
sym_type = STT_OBJECT;
|
||
if (sym->type.t & VT_STATIC)
|
||
sym_bind = STB_LOCAL;
|
||
else
|
||
sym_bind = STB_GLOBAL;
|
||
|
||
name = get_tok_str(sym->v, NULL);
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
if (do_bounds_check) {
|
||
char buf[32];
|
||
|
||
/* XXX: avoid doing that for statics ? */
|
||
/* if bound checking is activated, we change some function
|
||
names by adding the "__bound" prefix */
|
||
switch(sym->v) {
|
||
#if 0
|
||
/* XXX: we rely only on malloc hooks */
|
||
case TOK_malloc:
|
||
case TOK_free:
|
||
case TOK_realloc:
|
||
case TOK_memalign:
|
||
case TOK_calloc:
|
||
#endif
|
||
case TOK_memcpy:
|
||
case TOK_memmove:
|
||
case TOK_memset:
|
||
case TOK_strlen:
|
||
case TOK_strcpy:
|
||
strcpy(buf, "__bound_");
|
||
strcat(buf, name);
|
||
name = buf;
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
if (tcc_state->leading_underscore && can_add_underscore) {
|
||
buf1[0] = '_';
|
||
pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
|
||
name = buf1;
|
||
}
|
||
info = ELF32_ST_INFO(sym_bind, sym_type);
|
||
sym->c = add_elf_sym(symtab_section, value, size, info, 0, sh_num, name);
|
||
} else {
|
||
esym = &((Elf32_Sym *)symtab_section->data)[sym->c];
|
||
esym->st_value = value;
|
||
esym->st_size = size;
|
||
esym->st_shndx = sh_num;
|
||
}
|
||
}
|
||
|
||
static void put_extern_sym(Sym *sym, Section *section,
|
||
unsigned long value, unsigned long size)
|
||
{
|
||
put_extern_sym2(sym, section, value, size, 1);
|
||
}
|
||
|
||
/* add a new relocation entry to symbol 'sym' in section 's' */
|
||
static void greloc(Section *s, Sym *sym, unsigned long offset, int type)
|
||
{
|
||
if (!sym->c)
|
||
put_extern_sym(sym, NULL, 0, 0);
|
||
/* now we can add ELF relocation info */
|
||
put_elf_reloc(symtab_section, s, offset, type, sym->c);
|
||
}
|
||
|
||
static inline int isid(int c)
|
||
{
|
||
return (c >= 'a' && c <= 'z') ||
|
||
(c >= 'A' && c <= 'Z') ||
|
||
c == '_';
|
||
}
|
||
|
||
static inline int isnum(int c)
|
||
{
|
||
return c >= '0' && c <= '9';
|
||
}
|
||
|
||
static inline int isoct(int c)
|
||
{
|
||
return c >= '0' && c <= '7';
|
||
}
|
||
|
||
static inline int toup(int c)
|
||
{
|
||
if (c >= 'a' && c <= 'z')
|
||
return c - 'a' + 'A';
|
||
else
|
||
return c;
|
||
}
|
||
|
||
static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
|
||
{
|
||
int len;
|
||
len = strlen(buf);
|
||
vsnprintf(buf + len, buf_size - len, fmt, ap);
|
||
}
|
||
|
||
static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
|
||
{
|
||
va_list ap;
|
||
va_start(ap, fmt);
|
||
strcat_vprintf(buf, buf_size, fmt, ap);
|
||
va_end(ap);
|
||
}
|
||
|
||
void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
|
||
{
|
||
char buf[2048];
|
||
BufferedFile **f;
|
||
|
||
buf[0] = '\0';
|
||
if (file) {
|
||
for(f = s1->include_stack; f < s1->include_stack_ptr; f++)
|
||
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
|
||
(*f)->filename, (*f)->line_num);
|
||
if (file->line_num > 0) {
|
||
strcat_printf(buf, sizeof(buf),
|
||
"%s:%d: ", file->filename, file->line_num);
|
||
} else {
|
||
strcat_printf(buf, sizeof(buf),
|
||
"%s: ", file->filename);
|
||
}
|
||
} else {
|
||
strcat_printf(buf, sizeof(buf),
|
||
"tcc: ");
|
||
}
|
||
if (is_warning)
|
||
strcat_printf(buf, sizeof(buf), "warning: ");
|
||
strcat_vprintf(buf, sizeof(buf), fmt, ap);
|
||
|
||
if (!s1->error_func) {
|
||
/* default case: stderr */
|
||
fprintf(stderr, "%s\n", buf);
|
||
} else {
|
||
s1->error_func(s1->error_opaque, buf);
|
||
}
|
||
if (!is_warning || s1->warn_error)
|
||
s1->nb_errors++;
|
||
}
|
||
|
||
#ifdef LIBTCC
|
||
void tcc_set_error_func(TCCState *s, void *error_opaque,
|
||
void (*error_func)(void *opaque, const char *msg))
|
||
{
|
||
s->error_opaque = error_opaque;
|
||
s->error_func = error_func;
|
||
}
|
||
#endif
|
||
|
||
/* error without aborting current compilation */
|
||
void error_noabort(const char *fmt, ...)
|
||
{
|
||
TCCState *s1 = tcc_state;
|
||
va_list ap;
|
||
|
||
va_start(ap, fmt);
|
||
error1(s1, 0, fmt, ap);
|
||
va_end(ap);
|
||
}
|
||
|
||
void error(const char *fmt, ...)
|
||
{
|
||
TCCState *s1 = tcc_state;
|
||
va_list ap;
|
||
|
||
va_start(ap, fmt);
|
||
error1(s1, 0, fmt, ap);
|
||
va_end(ap);
|
||
/* better than nothing: in some cases, we accept to handle errors */
|
||
if (s1->error_set_jmp_enabled) {
|
||
longjmp(s1->error_jmp_buf, 1);
|
||
} else {
|
||
/* XXX: eliminate this someday */
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
void expect(const char *msg)
|
||
{
|
||
error("%s expected", msg);
|
||
}
|
||
|
||
void warning(const char *fmt, ...)
|
||
{
|
||
TCCState *s1 = tcc_state;
|
||
va_list ap;
|
||
|
||
if (s1->warn_none)
|
||
return;
|
||
|
||
va_start(ap, fmt);
|
||
error1(s1, 1, fmt, ap);
|
||
va_end(ap);
|
||
}
|
||
|
||
void skip(int c)
|
||
{
|
||
if (tok != c)
|
||
error("'%c' expected", c);
|
||
next();
|
||
}
|
||
|
||
static void test_lvalue(void)
|
||
{
|
||
if (!(vtop->r & VT_LVAL))
|
||
expect("lvalue");
|
||
}
|
||
|
||
/* allocate a new token */
|
||
static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
|
||
{
|
||
TokenSym *ts, **ptable;
|
||
int i;
|
||
|
||
if (tok_ident >= SYM_FIRST_ANOM)
|
||
error("memory full");
|
||
|
||
/* expand token table if needed */
|
||
i = tok_ident - TOK_IDENT;
|
||
if ((i % TOK_ALLOC_INCR) == 0) {
|
||
ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
|
||
if (!ptable)
|
||
error("memory full");
|
||
table_ident = ptable;
|
||
}
|
||
|
||
ts = tcc_malloc(sizeof(TokenSym) + len);
|
||
table_ident[i] = ts;
|
||
ts->tok = tok_ident++;
|
||
ts->sym_define = NULL;
|
||
ts->sym_label = NULL;
|
||
ts->sym_struct = NULL;
|
||
ts->sym_identifier = NULL;
|
||
ts->len = len;
|
||
ts->hash_next = NULL;
|
||
memcpy(ts->str, str, len);
|
||
ts->str[len] = '\0';
|
||
*pts = ts;
|
||
return ts;
|
||
}
|
||
|
||
#define TOK_HASH_INIT 1
|
||
#define TOK_HASH_FUNC(h, c) ((h) * 263 + (c))
|
||
|
||
/* find a token and add it if not found */
|
||
static TokenSym *tok_alloc(const char *str, int len)
|
||
{
|
||
TokenSym *ts, **pts;
|
||
int i;
|
||
unsigned int h;
|
||
|
||
h = TOK_HASH_INIT;
|
||
for(i=0;i<len;i++)
|
||
h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
|
||
h &= (TOK_HASH_SIZE - 1);
|
||
|
||
pts = &hash_ident[h];
|
||
for(;;) {
|
||
ts = *pts;
|
||
if (!ts)
|
||
break;
|
||
if (ts->len == len && !memcmp(ts->str, str, len))
|
||
return ts;
|
||
pts = &(ts->hash_next);
|
||
}
|
||
return tok_alloc_new(pts, str, len);
|
||
}
|
||
|
||
/* CString handling */
|
||
|
||
static void cstr_realloc(CString *cstr, int new_size)
|
||
{
|
||
int size;
|
||
void *data;
|
||
|
||
size = cstr->size_allocated;
|
||
if (size == 0)
|
||
size = 8; /* no need to allocate a too small first string */
|
||
while (size < new_size)
|
||
size = size * 2;
|
||
data = tcc_realloc(cstr->data_allocated, size);
|
||
if (!data)
|
||
error("memory full");
|
||
cstr->data_allocated = data;
|
||
cstr->size_allocated = size;
|
||
cstr->data = data;
|
||
}
|
||
|
||
/* add a byte */
|
||
static inline void cstr_ccat(CString *cstr, int ch)
|
||
{
|
||
int size;
|
||
size = cstr->size + 1;
|
||
if (size > cstr->size_allocated)
|
||
cstr_realloc(cstr, size);
|
||
((unsigned char *)cstr->data)[size - 1] = ch;
|
||
cstr->size = size;
|
||
}
|
||
|
||
static void cstr_cat(CString *cstr, const char *str)
|
||
{
|
||
int c;
|
||
for(;;) {
|
||
c = *str;
|
||
if (c == '\0')
|
||
break;
|
||
cstr_ccat(cstr, c);
|
||
str++;
|
||
}
|
||
}
|
||
|
||
/* add a wide char */
|
||
static void cstr_wccat(CString *cstr, int ch)
|
||
{
|
||
int size;
|
||
size = cstr->size + sizeof(int);
|
||
if (size > cstr->size_allocated)
|
||
cstr_realloc(cstr, size);
|
||
*(int *)(((unsigned char *)cstr->data) + size - sizeof(int)) = ch;
|
||
cstr->size = size;
|
||
}
|
||
|
||
static void cstr_new(CString *cstr)
|
||
{
|
||
memset(cstr, 0, sizeof(CString));
|
||
}
|
||
|
||
/* free string and reset it to NULL */
|
||
static void cstr_free(CString *cstr)
|
||
{
|
||
tcc_free(cstr->data_allocated);
|
||
cstr_new(cstr);
|
||
}
|
||
|
||
#define cstr_reset(cstr) cstr_free(cstr)
|
||
|
||
/* XXX: unicode ? */
|
||
static void add_char(CString *cstr, int c)
|
||
{
|
||
if (c == '\'' || c == '\"' || c == '\\') {
|
||
/* XXX: could be more precise if char or string */
|
||
cstr_ccat(cstr, '\\');
|
||
}
|
||
if (c >= 32 && c <= 126) {
|
||
cstr_ccat(cstr, c);
|
||
} else {
|
||
cstr_ccat(cstr, '\\');
|
||
if (c == '\n') {
|
||
cstr_ccat(cstr, 'n');
|
||
} else {
|
||
cstr_ccat(cstr, '0' + ((c >> 6) & 7));
|
||
cstr_ccat(cstr, '0' + ((c >> 3) & 7));
|
||
cstr_ccat(cstr, '0' + (c & 7));
|
||
}
|
||
}
|
||
}
|
||
|
||
/* XXX: buffer overflow */
|
||
/* XXX: float tokens */
|
||
char *get_tok_str(int v, CValue *cv)
|
||
{
|
||
static char buf[STRING_MAX_SIZE + 1];
|
||
static CString cstr_buf;
|
||
CString *cstr;
|
||
unsigned char *q;
|
||
char *p;
|
||
int i, len;
|
||
|
||
/* NOTE: to go faster, we give a fixed buffer for small strings */
|
||
cstr_reset(&cstr_buf);
|
||
cstr_buf.data = buf;
|
||
cstr_buf.size_allocated = sizeof(buf);
|
||
p = buf;
|
||
|
||
switch(v) {
|
||
case TOK_CINT:
|
||
case TOK_CUINT:
|
||
/* XXX: not quite exact, but only useful for testing */
|
||
sprintf(p, "%u", cv->ui);
|
||
break;
|
||
case TOK_CLLONG:
|
||
case TOK_CULLONG:
|
||
/* XXX: not quite exact, but only useful for testing */
|
||
sprintf(p, "%llu", cv->ull);
|
||
break;
|
||
case TOK_CCHAR:
|
||
case TOK_LCHAR:
|
||
cstr_ccat(&cstr_buf, '\'');
|
||
add_char(&cstr_buf, cv->i);
|
||
cstr_ccat(&cstr_buf, '\'');
|
||
cstr_ccat(&cstr_buf, '\0');
|
||
break;
|
||
case TOK_PPNUM:
|
||
cstr = cv->cstr;
|
||
len = cstr->size - 1;
|
||
for(i=0;i<len;i++)
|
||
add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
|
||
cstr_ccat(&cstr_buf, '\0');
|
||
break;
|
||
case TOK_STR:
|
||
case TOK_LSTR:
|
||
cstr = cv->cstr;
|
||
cstr_ccat(&cstr_buf, '\"');
|
||
if (v == TOK_STR) {
|
||
len = cstr->size - 1;
|
||
for(i=0;i<len;i++)
|
||
add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
|
||
} else {
|
||
len = (cstr->size / sizeof(int)) - 1;
|
||
for(i=0;i<len;i++)
|
||
add_char(&cstr_buf, ((int *)cstr->data)[i]);
|
||
}
|
||
cstr_ccat(&cstr_buf, '\"');
|
||
cstr_ccat(&cstr_buf, '\0');
|
||
break;
|
||
case TOK_LT:
|
||
v = '<';
|
||
goto addv;
|
||
case TOK_GT:
|
||
v = '>';
|
||
goto addv;
|
||
case TOK_A_SHL:
|
||
return strcpy(p, "<<=");
|
||
case TOK_A_SAR:
|
||
return strcpy(p, ">>=");
|
||
default:
|
||
if (v < TOK_IDENT) {
|
||
/* search in two bytes table */
|
||
q = tok_two_chars;
|
||
while (*q) {
|
||
if (q[2] == v) {
|
||
*p++ = q[0];
|
||
*p++ = q[1];
|
||
*p = '\0';
|
||
return buf;
|
||
}
|
||
q += 3;
|
||
}
|
||
addv:
|
||
*p++ = v;
|
||
*p = '\0';
|
||
} else if (v < tok_ident) {
|
||
return table_ident[v - TOK_IDENT]->str;
|
||
} else if (v >= SYM_FIRST_ANOM) {
|
||
/* special name for anonymous symbol */
|
||
sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
|
||
} else {
|
||
/* should never happen */
|
||
return NULL;
|
||
}
|
||
break;
|
||
}
|
||
return cstr_buf.data;
|
||
}
|
||
|
||
/* push, without hashing */
|
||
static Sym *sym_push2(Sym **ps, long v, long t, long c)
|
||
{
|
||
Sym *s;
|
||
s = sym_malloc();
|
||
s->v = v;
|
||
s->type.t = t;
|
||
s->c = c;
|
||
s->next = NULL;
|
||
/* add in stack */
|
||
s->prev = *ps;
|
||
*ps = s;
|
||
return s;
|
||
}
|
||
|
||
/* find a symbol and return its associated structure. 's' is the top
|
||
of the symbol stack */
|
||
static Sym *sym_find2(Sym *s, int v)
|
||
{
|
||
while (s) {
|
||
if (s->v == v)
|
||
return s;
|
||
s = s->prev;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/* structure lookup */
|
||
static inline Sym *struct_find(int v)
|
||
{
|
||
v -= TOK_IDENT;
|
||
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
|
||
return NULL;
|
||
return table_ident[v]->sym_struct;
|
||
}
|
||
|
||
/* find an identifier */
|
||
static inline Sym *sym_find(int v)
|
||
{
|
||
v -= TOK_IDENT;
|
||
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
|
||
return NULL;
|
||
return table_ident[v]->sym_identifier;
|
||
}
|
||
|
||
/* push a given symbol on the symbol stack */
|
||
static Sym *sym_push(int v, CType *type, int r, int c)
|
||
{
|
||
Sym *s, **ps;
|
||
TokenSym *ts;
|
||
|
||
if (local_stack)
|
||
ps = &local_stack;
|
||
else
|
||
ps = &global_stack;
|
||
s = sym_push2(ps, v, type->t, c);
|
||
s->type.ref = type->ref;
|
||
s->r = r;
|
||
/* don't record fields or anonymous symbols */
|
||
/* XXX: simplify */
|
||
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
|
||
/* record symbol in token array */
|
||
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
|
||
if (v & SYM_STRUCT)
|
||
ps = &ts->sym_struct;
|
||
else
|
||
ps = &ts->sym_identifier;
|
||
s->prev_tok = *ps;
|
||
*ps = s;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/* push a global identifier */
|
||
static Sym *global_identifier_push(int v, int t, int c)
|
||
{
|
||
Sym *s, **ps;
|
||
s = sym_push2(&global_stack, v, t, c);
|
||
/* don't record anonymous symbol */
|
||
if (v < SYM_FIRST_ANOM) {
|
||
ps = &table_ident[v - TOK_IDENT]->sym_identifier;
|
||
/* modify the top most local identifier, so that
|
||
sym_identifier will point to 's' when popped */
|
||
while (*ps != NULL)
|
||
ps = &(*ps)->prev_tok;
|
||
s->prev_tok = NULL;
|
||
*ps = s;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/* pop symbols until top reaches 'b' */
|
||
static void sym_pop(Sym **ptop, Sym *b)
|
||
{
|
||
Sym *s, *ss, **ps;
|
||
TokenSym *ts;
|
||
int v;
|
||
|
||
s = *ptop;
|
||
while(s != b) {
|
||
ss = s->prev;
|
||
v = s->v;
|
||
/* remove symbol in token array */
|
||
/* XXX: simplify */
|
||
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
|
||
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
|
||
if (v & SYM_STRUCT)
|
||
ps = &ts->sym_struct;
|
||
else
|
||
ps = &ts->sym_identifier;
|
||
*ps = s->prev_tok;
|
||
}
|
||
sym_free(s);
|
||
s = ss;
|
||
}
|
||
*ptop = b;
|
||
}
|
||
|
||
/* I/O layer */
|
||
|
||
BufferedFile *tcc_open(TCCState *s1, const char *filename)
|
||
{
|
||
int fd;
|
||
BufferedFile *bf;
|
||
|
||
fd = open(filename, O_RDONLY | O_BINARY);
|
||
if (fd < 0)
|
||
return NULL;
|
||
bf = tcc_malloc(sizeof(BufferedFile));
|
||
if (!bf) {
|
||
close(fd);
|
||
return NULL;
|
||
}
|
||
bf->fd = fd;
|
||
bf->buf_ptr = bf->buffer;
|
||
bf->buf_end = bf->buffer;
|
||
bf->buffer[0] = CH_EOB; /* put eob symbol */
|
||
pstrcpy(bf->filename, sizeof(bf->filename), filename);
|
||
bf->line_num = 1;
|
||
bf->ifndef_macro = 0;
|
||
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
|
||
// printf("opening '%s'\n", filename);
|
||
return bf;
|
||
}
|
||
|
||
void tcc_close(BufferedFile *bf)
|
||
{
|
||
total_lines += bf->line_num;
|
||
close(bf->fd);
|
||
tcc_free(bf);
|
||
}
|
||
|
||
/* fill input buffer and peek next char */
|
||
static int tcc_peekc_slow(BufferedFile *bf)
|
||
{
|
||
int len;
|
||
/* only tries to read if really end of buffer */
|
||
if (bf->buf_ptr >= bf->buf_end) {
|
||
if (bf->fd != -1) {
|
||
#if defined(PARSE_DEBUG)
|
||
len = 8;
|
||
#else
|
||
len = IO_BUF_SIZE;
|
||
#endif
|
||
len = read(bf->fd, bf->buffer, len);
|
||
if (len < 0)
|
||
len = 0;
|
||
} else {
|
||
len = 0;
|
||
}
|
||
total_bytes += len;
|
||
bf->buf_ptr = bf->buffer;
|
||
bf->buf_end = bf->buffer + len;
|
||
*bf->buf_end = CH_EOB;
|
||
}
|
||
if (bf->buf_ptr < bf->buf_end) {
|
||
return bf->buf_ptr[0];
|
||
} else {
|
||
bf->buf_ptr = bf->buf_end;
|
||
return CH_EOF;
|
||
}
|
||
}
|
||
|
||
/* return the current character, handling end of block if necessary
|
||
(but not stray) */
|
||
static int handle_eob(void)
|
||
{
|
||
return tcc_peekc_slow(file);
|
||
}
|
||
|
||
/* read next char from current input file and handle end of input buffer */
|
||
static inline void inp(void)
|
||
{
|
||
ch = *(++(file->buf_ptr));
|
||
/* end of buffer/file handling */
|
||
if (ch == CH_EOB)
|
||
ch = handle_eob();
|
||
}
|
||
|
||
/* handle '\[\r]\n' */
|
||
static void handle_stray(void)
|
||
{
|
||
while (ch == '\\') {
|
||
inp();
|
||
if (ch == '\n') {
|
||
file->line_num++;
|
||
inp();
|
||
} else if (ch == '\r') {
|
||
inp();
|
||
if (ch != '\n')
|
||
goto fail;
|
||
file->line_num++;
|
||
inp();
|
||
} else {
|
||
fail:
|
||
error("stray '\\' in program");
|
||
}
|
||
}
|
||
}
|
||
|
||
/* skip the stray and handle the \\n case. Output an error if
|
||
incorrect char after the stray */
|
||
static int handle_stray1(uint8_t *p)
|
||
{
|
||
int c;
|
||
|
||
if (p >= file->buf_end) {
|
||
file->buf_ptr = p;
|
||
c = handle_eob();
|
||
p = file->buf_ptr;
|
||
if (c == '\\')
|
||
goto parse_stray;
|
||
} else {
|
||
parse_stray:
|
||
file->buf_ptr = p;
|
||
ch = *p;
|
||
handle_stray();
|
||
p = file->buf_ptr;
|
||
c = *p;
|
||
}
|
||
return c;
|
||
}
|
||
|
||
/* handle just the EOB case, but not stray */
|
||
#define PEEKC_EOB(c, p)\
|
||
{\
|
||
p++;\
|
||
c = *p;\
|
||
if (c == '\\') {\
|
||
file->buf_ptr = p;\
|
||
c = handle_eob();\
|
||
p = file->buf_ptr;\
|
||
}\
|
||
}
|
||
|
||
/* handle the complicated stray case */
|
||
#define PEEKC(c, p)\
|
||
{\
|
||
p++;\
|
||
c = *p;\
|
||
if (c == '\\') {\
|
||
c = handle_stray1(p);\
|
||
p = file->buf_ptr;\
|
||
}\
|
||
}
|
||
|
||
/* input with '\[\r]\n' handling. Note that this function cannot
|
||
handle other characters after '\', so you cannot call it inside
|
||
strings or comments */
|
||
static void minp(void)
|
||
{
|
||
inp();
|
||
if (ch == '\\')
|
||
handle_stray();
|
||
}
|
||
|
||
|
||
/* single line C++ comments */
|
||
static uint8_t *parse_line_comment(uint8_t *p)
|
||
{
|
||
int c;
|
||
|
||
p++;
|
||
for(;;) {
|
||
c = *p;
|
||
redo:
|
||
if (c == '\n' || c == CH_EOF) {
|
||
break;
|
||
} else if (c == '\\') {
|
||
file->buf_ptr = p;
|
||
c = handle_eob();
|
||
p = file->buf_ptr;
|
||
if (c == '\\') {
|
||
PEEKC_EOB(c, p);
|
||
if (c == '\n') {
|
||
file->line_num++;
|
||
PEEKC_EOB(c, p);
|
||
} else if (c == '\r') {
|
||
PEEKC_EOB(c, p);
|
||
if (c == '\n') {
|
||
file->line_num++;
|
||
PEEKC_EOB(c, p);
|
||
}
|
||
}
|
||
} else {
|
||
goto redo;
|
||
}
|
||
} else {
|
||
p++;
|
||
}
|
||
}
|
||
return p;
|
||
}
|
||
|
||
/* C comments */
|
||
static uint8_t *parse_comment(uint8_t *p)
|
||
{
|
||
int c;
|
||
|
||
p++;
|
||
for(;;) {
|
||
/* fast skip loop */
|
||
for(;;) {
|
||
c = *p;
|
||
if (c == '\n' || c == '*' || c == '\\')
|
||
break;
|
||
p++;
|
||
c = *p;
|
||
if (c == '\n' || c == '*' || c == '\\')
|
||
break;
|
||
p++;
|
||
}
|
||
/* now we can handle all the cases */
|
||
if (c == '\n') {
|
||
file->line_num++;
|
||
p++;
|
||
} else if (c == '*') {
|
||
p++;
|
||
for(;;) {
|
||
c = *p;
|
||
if (c == '*') {
|
||
p++;
|
||
} else if (c == '/') {
|
||
goto end_of_comment;
|
||
} else if (c == '\\') {
|
||
file->buf_ptr = p;
|
||
c = handle_eob();
|
||
p = file->buf_ptr;
|
||
if (c == '\\') {
|
||
/* skip '\[\r]\n', otherwise just skip the stray */
|
||
while (c == '\\') {
|
||
PEEKC_EOB(c, p);
|
||
if (c == '\n') {
|
||
file->line_num++;
|
||
PEEKC_EOB(c, p);
|
||
} else if (c == '\r') {
|
||
PEEKC_EOB(c, p);
|
||
if (c == '\n') {
|
||
file->line_num++;
|
||
PEEKC_EOB(c, p);
|
||
}
|
||
} else {
|
||
goto after_star;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
after_star: ;
|
||
} else {
|
||
/* stray, eob or eof */
|
||
file->buf_ptr = p;
|
||
c = handle_eob();
|
||
p = file->buf_ptr;
|
||
if (c == CH_EOF) {
|
||
error("unexpected end of file in comment");
|
||
} else if (c == '\\') {
|
||
p++;
|
||
}
|
||
}
|
||
}
|
||
end_of_comment:
|
||
p++;
|
||
return p;
|
||
}
|
||
|
||
#define cinp minp
|
||
|
||
/* space exlcuding newline */
|
||
static inline int is_space(int ch)
|
||
{
|
||
return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
|
||
}
|
||
|
||
static inline void skip_spaces(void)
|
||
{
|
||
while (is_space(ch))
|
||
cinp();
|
||
}
|
||
|
||
/* parse a string without interpreting escapes */
|
||
static uint8_t *parse_pp_string(uint8_t *p,
|
||
int sep, CString *str)
|
||
{
|
||
int c;
|
||
p++;
|
||
for(;;) {
|
||
c = *p;
|
||
if (c == sep) {
|
||
break;
|
||
} else if (c == '\\') {
|
||
file->buf_ptr = p;
|
||
c = handle_eob();
|
||
p = file->buf_ptr;
|
||
if (c == CH_EOF) {
|
||
unterminated_string:
|
||
/* XXX: indicate line number of start of string */
|
||
error("missing terminating %c character", sep);
|
||
} else if (c == '\\') {
|
||
/* escape : just skip \[\r]\n */
|
||
PEEKC_EOB(c, p);
|
||
if (c == '\n') {
|
||
file->line_num++;
|
||
p++;
|
||
} else if (c == '\r') {
|
||
PEEKC_EOB(c, p);
|
||
if (c != '\n')
|
||
expect("'\n' after '\r'");
|
||
file->line_num++;
|
||
p++;
|
||
} else if (c == CH_EOF) {
|
||
goto unterminated_string;
|
||
} else {
|
||
if (str) {
|
||
cstr_ccat(str, '\\');
|
||
cstr_ccat(str, c);
|
||
}
|
||
p++;
|
||
}
|
||
}
|
||
} else if (c == '\n') {
|
||
file->line_num++;
|
||
goto add_char;
|
||
} else if (c == '\r') {
|
||
PEEKC_EOB(c, p);
|
||
if (c != '\n') {
|
||
if (str)
|
||
cstr_ccat(str, '\r');
|
||
} else {
|
||
file->line_num++;
|
||
goto add_char;
|
||
}
|
||
} else {
|
||
add_char:
|
||
if (str)
|
||
cstr_ccat(str, c);
|
||
p++;
|
||
}
|
||
}
|
||
p++;
|
||
return p;
|
||
}
|
||
|
||
/* skip block of text until #else, #elif or #endif. skip also pairs of
|
||
#if/#endif */
|
||
void preprocess_skip(void)
|
||
{
|
||
int a, start_of_line, c;
|
||
uint8_t *p;
|
||
|
||
p = file->buf_ptr;
|
||
start_of_line = 1;
|
||
a = 0;
|
||
for(;;) {
|
||
redo_no_start:
|
||
c = *p;
|
||
switch(c) {
|
||
case ' ':
|
||
case '\t':
|
||
case '\f':
|
||
case '\v':
|
||
case '\r':
|
||
p++;
|
||
goto redo_no_start;
|
||
case '\n':
|
||
start_of_line = 1;
|
||
file->line_num++;
|
||
p++;
|
||
goto redo_no_start;
|
||
case '\\':
|
||
file->buf_ptr = p;
|
||
c = handle_eob();
|
||
if (c == CH_EOF) {
|
||
expect("#endif");
|
||
} else if (c == '\\') {
|
||
/* XXX: incorrect: should not give an error */
|
||
ch = file->buf_ptr[0];
|
||
handle_stray();
|
||
}
|
||
p = file->buf_ptr;
|
||
goto redo_no_start;
|
||
/* skip strings */
|
||
case '\"':
|
||
case '\'':
|
||
p = parse_pp_string(p, c, NULL);
|
||
break;
|
||
/* skip comments */
|
||
case '/':
|
||
file->buf_ptr = p;
|
||
ch = *p;
|
||
minp();
|
||
p = file->buf_ptr;
|
||
if (ch == '*') {
|
||
p = parse_comment(p);
|
||
} else if (ch == '/') {
|
||
p = parse_line_comment(p);
|
||
}
|
||
break;
|
||
|
||
case '#':
|
||
p++;
|
||
if (start_of_line) {
|
||
file->buf_ptr = p;
|
||
next_nomacro();
|
||
p = file->buf_ptr;
|
||
if (a == 0 &&
|
||
(tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
|
||
goto the_end;
|
||
if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
|
||
a++;
|
||
else if (tok == TOK_ENDIF)
|
||
a--;
|
||
}
|
||
break;
|
||
default:
|
||
p++;
|
||
break;
|
||
}
|
||
start_of_line = 0;
|
||
}
|
||
the_end: ;
|
||
file->buf_ptr = p;
|
||
}
|
||
|
||
/* ParseState handling */
|
||
|
||
/* XXX: currently, no include file info is stored. Thus, we cannot display
|
||
accurate messages if the function or data definition spans multiple
|
||
files */
|
||
|
||
/* save current parse state in 's' */
|
||
void save_parse_state(ParseState *s)
|
||
{
|
||
s->line_num = file->line_num;
|
||
s->macro_ptr = macro_ptr;
|
||
s->tok = tok;
|
||
s->tokc = tokc;
|
||
}
|
||
|
||
/* restore parse state from 's' */
|
||
void restore_parse_state(ParseState *s)
|
||
{
|
||
file->line_num = s->line_num;
|
||
macro_ptr = s->macro_ptr;
|
||
tok = s->tok;
|
||
tokc = s->tokc;
|
||
}
|
||
|
||
/* return the number of additional 'ints' necessary to store the
|
||
token */
|
||
static inline int tok_ext_size(int t)
|
||
{
|
||
switch(t) {
|
||
/* 4 bytes */
|
||
case TOK_CINT:
|
||
case TOK_CUINT:
|
||
case TOK_CCHAR:
|
||
case TOK_LCHAR:
|
||
case TOK_CFLOAT:
|
||
case TOK_LINENUM:
|
||
return 1;
|
||
case TOK_STR:
|
||
case TOK_LSTR:
|
||
case TOK_PPNUM:
|
||
error("unsupported token");
|
||
return 1;
|
||
case TOK_CDOUBLE:
|
||
case TOK_CLLONG:
|
||
case TOK_CULLONG:
|
||
return 2;
|
||
case TOK_CLDOUBLE:
|
||
return LDOUBLE_SIZE / 4;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* token string handling */
|
||
|
||
static inline void tok_str_new(TokenString *s)
|
||
{
|
||
s->str = NULL;
|
||
s->len = 0;
|
||
s->allocated_len = 0;
|
||
s->last_line_num = -1;
|
||
}
|
||
|
||
static void tok_str_free(int *str)
|
||
{
|
||
tcc_free(str);
|
||
}
|
||
|
||
static int *tok_str_realloc(TokenString *s)
|
||
{
|
||
int *str, len;
|
||
|
||
if (s->allocated_len == 0) {
|
||
len = 8;
|
||
} else {
|
||
len = s->allocated_len * 2;
|
||
}
|
||
str = tcc_realloc(s->str, len * sizeof(int));
|
||
if (!str)
|
||
error("memory full");
|
||
s->allocated_len = len;
|
||
s->str = str;
|
||
return str;
|
||
}
|
||
|
||
static void tok_str_add(TokenString *s, int t)
|
||
{
|
||
int len, *str;
|
||
|
||
len = s->len;
|
||
str = s->str;
|
||
if (len >= s->allocated_len)
|
||
str = tok_str_realloc(s);
|
||
str[len++] = t;
|
||
s->len = len;
|
||
}
|
||
|
||
static void tok_str_add2(TokenString *s, int t, CValue *cv)
|
||
{
|
||
int len, *str;
|
||
|
||
len = s->len;
|
||
str = s->str;
|
||
|
||
/* allocate space for worst case */
|
||
if (len + TOK_MAX_SIZE > s->allocated_len)
|
||
str = tok_str_realloc(s);
|
||
str[len++] = t;
|
||
switch(t) {
|
||
case TOK_CINT:
|
||
case TOK_CUINT:
|
||
case TOK_CCHAR:
|
||
case TOK_LCHAR:
|
||
case TOK_CFLOAT:
|
||
case TOK_LINENUM:
|
||
str[len++] = cv->tab[0];
|
||
break;
|
||
case TOK_PPNUM:
|
||
case TOK_STR:
|
||
case TOK_LSTR:
|
||
{
|
||
int nb_words;
|
||
CString *cstr;
|
||
|
||
nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2;
|
||
while ((len + nb_words) > s->allocated_len)
|
||
str = tok_str_realloc(s);
|
||
cstr = (CString *)(str + len);
|
||
cstr->data = NULL;
|
||
cstr->size = cv->cstr->size;
|
||
cstr->data_allocated = NULL;
|
||
cstr->size_allocated = cstr->size;
|
||
memcpy((char *)cstr + sizeof(CString),
|
||
cv->cstr->data, cstr->size);
|
||
len += nb_words;
|
||
}
|
||
break;
|
||
case TOK_CDOUBLE:
|
||
case TOK_CLLONG:
|
||
case TOK_CULLONG:
|
||
#if LDOUBLE_SIZE == 8
|
||
case TOK_CLDOUBLE:
|
||
#endif
|
||
str[len++] = cv->tab[0];
|
||
str[len++] = cv->tab[1];
|
||
break;
|
||
#if LDOUBLE_SIZE == 12
|
||
case TOK_CLDOUBLE:
|
||
str[len++] = cv->tab[0];
|
||
str[len++] = cv->tab[1];
|
||
str[len++] = cv->tab[2];
|
||
#elif LDOUBLE_SIZE != 8
|
||
#error add long double size support
|
||
#endif
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
s->len = len;
|
||
}
|
||
|
||
/* add the current parse token in token string 's' */
|
||
static void tok_str_add_tok(TokenString *s)
|
||
{
|
||
CValue cval;
|
||
|
||
/* save line number info */
|
||
if (file->line_num != s->last_line_num) {
|
||
s->last_line_num = file->line_num;
|
||
cval.i = s->last_line_num;
|
||
tok_str_add2(s, TOK_LINENUM, &cval);
|
||
}
|
||
tok_str_add2(s, tok, &tokc);
|
||
}
|
||
|
||
#if LDOUBLE_SIZE == 12
|
||
#define LDOUBLE_GET(p, cv) \
|
||
cv.tab[0] = p[0]; \
|
||
cv.tab[1] = p[1]; \
|
||
cv.tab[2] = p[2];
|
||
#elif LDOUBLE_SIZE == 8
|
||
#define LDOUBLE_GET(p, cv) \
|
||
cv.tab[0] = p[0]; \
|
||
cv.tab[1] = p[1];
|
||
#else
|
||
#error add long double size support
|
||
#endif
|
||
|
||
|
||
/* get a token from an integer array and increment pointer
|
||
accordingly. we code it as a macro to avoid pointer aliasing. */
|
||
#define TOK_GET(t, p, cv) \
|
||
{ \
|
||
t = *p++; \
|
||
switch(t) { \
|
||
case TOK_CINT: \
|
||
case TOK_CUINT: \
|
||
case TOK_CCHAR: \
|
||
case TOK_LCHAR: \
|
||
case TOK_CFLOAT: \
|
||
case TOK_LINENUM: \
|
||
cv.tab[0] = *p++; \
|
||
break; \
|
||
case TOK_STR: \
|
||
case TOK_LSTR: \
|
||
case TOK_PPNUM: \
|
||
cv.cstr = (CString *)p; \
|
||
cv.cstr->data = (char *)p + sizeof(CString);\
|
||
p += (sizeof(CString) + cv.cstr->size + 3) >> 2;\
|
||
break; \
|
||
case TOK_CDOUBLE: \
|
||
case TOK_CLLONG: \
|
||
case TOK_CULLONG: \
|
||
cv.tab[0] = p[0]; \
|
||
cv.tab[1] = p[1]; \
|
||
p += 2; \
|
||
break; \
|
||
case TOK_CLDOUBLE: \
|
||
LDOUBLE_GET(p, cv); \
|
||
p += LDOUBLE_SIZE / 4; \
|
||
break; \
|
||
default: \
|
||
break; \
|
||
} \
|
||
}
|
||
|
||
/* defines handling */
|
||
static inline void define_push(int v, int macro_type, int *str, Sym *first_arg)
|
||
{
|
||
Sym *s;
|
||
|
||
s = sym_push2(&define_stack, v, macro_type, (long)str);
|
||
s->next = first_arg;
|
||
table_ident[v - TOK_IDENT]->sym_define = s;
|
||
}
|
||
|
||
/* undefined a define symbol. Its name is just set to zero */
|
||
static void define_undef(Sym *s)
|
||
{
|
||
int v;
|
||
v = s->v;
|
||
if (v >= TOK_IDENT && v < tok_ident)
|
||
table_ident[v - TOK_IDENT]->sym_define = NULL;
|
||
s->v = 0;
|
||
}
|
||
|
||
static inline Sym *define_find(int v)
|
||
{
|
||
v -= TOK_IDENT;
|
||
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
|
||
return NULL;
|
||
return table_ident[v]->sym_define;
|
||
}
|
||
|
||
/* free define stack until top reaches 'b' */
|
||
static void free_defines(Sym *b)
|
||
{
|
||
Sym *top, *top1;
|
||
int v;
|
||
|
||
top = define_stack;
|
||
while (top != b) {
|
||
top1 = top->prev;
|
||
/* do not free args or predefined defines */
|
||
if (top->c)
|
||
tok_str_free((int *)top->c);
|
||
v = top->v;
|
||
if (v >= TOK_IDENT && v < tok_ident)
|
||
table_ident[v - TOK_IDENT]->sym_define = NULL;
|
||
sym_free(top);
|
||
top = top1;
|
||
}
|
||
define_stack = b;
|
||
}
|
||
|
||
/* label lookup */
|
||
static Sym *label_find(int v)
|
||
{
|
||
v -= TOK_IDENT;
|
||
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
|
||
return NULL;
|
||
return table_ident[v]->sym_label;
|
||
}
|
||
|
||
static Sym *label_push(Sym **ptop, int v, int flags)
|
||
{
|
||
Sym *s, **ps;
|
||
s = sym_push2(ptop, v, 0, 0);
|
||
s->r = flags;
|
||
ps = &table_ident[v - TOK_IDENT]->sym_label;
|
||
if (ptop == &global_label_stack) {
|
||
/* modify the top most local identifier, so that
|
||
sym_identifier will point to 's' when popped */
|
||
while (*ps != NULL)
|
||
ps = &(*ps)->prev_tok;
|
||
}
|
||
s->prev_tok = *ps;
|
||
*ps = s;
|
||
return s;
|
||
}
|
||
|
||
/* pop labels until element last is reached. Look if any labels are
|
||
undefined. Define symbols if '&&label' was used. */
|
||
static void label_pop(Sym **ptop, Sym *slast)
|
||
{
|
||
Sym *s, *s1;
|
||
for(s = *ptop; s != slast; s = s1) {
|
||
s1 = s->prev;
|
||
if (s->r == LABEL_DECLARED) {
|
||
warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
|
||
} else if (s->r == LABEL_FORWARD) {
|
||
error("label '%s' used but not defined",
|
||
get_tok_str(s->v, NULL));
|
||
} else {
|
||
if (s->c) {
|
||
/* define corresponding symbol. A size of
|
||
1 is put. */
|
||
put_extern_sym(s, cur_text_section, (long)s->next, 1);
|
||
}
|
||
}
|
||
/* remove label */
|
||
table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
|
||
sym_free(s);
|
||
}
|
||
*ptop = slast;
|
||
}
|
||
|
||
/* eval an expression for #if/#elif */
|
||
static int expr_preprocess(void)
|
||
{
|
||
int c, t;
|
||
TokenString str;
|
||
|
||
tok_str_new(&str);
|
||
while (tok != TOK_LINEFEED && tok != TOK_EOF) {
|
||
next(); /* do macro subst */
|
||
if (tok == TOK_DEFINED) {
|
||
next_nomacro();
|
||
t = tok;
|
||
if (t == '(')
|
||
next_nomacro();
|
||
c = define_find(tok) != 0;
|
||
if (t == '(')
|
||
next_nomacro();
|
||
tok = TOK_CINT;
|
||
tokc.i = c;
|
||
} else if (tok >= TOK_IDENT) {
|
||
/* if undefined macro */
|
||
tok = TOK_CINT;
|
||
tokc.i = 0;
|
||
}
|
||
tok_str_add_tok(&str);
|
||
}
|
||
tok_str_add(&str, -1); /* simulate end of file */
|
||
tok_str_add(&str, 0);
|
||
/* now evaluate C constant expression */
|
||
macro_ptr = str.str;
|
||
next();
|
||
c = expr_const();
|
||
macro_ptr = NULL;
|
||
tok_str_free(str.str);
|
||
return c != 0;
|
||
}
|
||
|
||
#if defined(PARSE_DEBUG) || defined(PP_DEBUG)
|
||
static void tok_print(int *str)
|
||
{
|
||
int t;
|
||
CValue cval;
|
||
|
||
while (1) {
|
||
TOK_GET(t, str, cval);
|
||
if (!t)
|
||
break;
|
||
printf(" %s", get_tok_str(t, &cval));
|
||
}
|
||
printf("\n");
|
||
}
|
||
#endif
|
||
|
||
/* parse after #define */
|
||
static void parse_define(void)
|
||
{
|
||
Sym *s, *first, **ps;
|
||
int v, t, varg, is_vaargs, c;
|
||
TokenString str;
|
||
|
||
v = tok;
|
||
if (v < TOK_IDENT)
|
||
error("invalid macro name '%s'", get_tok_str(tok, &tokc));
|
||
/* XXX: should check if same macro (ANSI) */
|
||
first = NULL;
|
||
t = MACRO_OBJ;
|
||
/* '(' must be just after macro definition for MACRO_FUNC */
|
||
c = file->buf_ptr[0];
|
||
if (c == '\\')
|
||
c = handle_stray1(file->buf_ptr);
|
||
if (c == '(') {
|
||
next_nomacro();
|
||
next_nomacro();
|
||
ps = &first;
|
||
while (tok != ')') {
|
||
varg = tok;
|
||
next_nomacro();
|
||
is_vaargs = 0;
|
||
if (varg == TOK_DOTS) {
|
||
varg = TOK___VA_ARGS__;
|
||
is_vaargs = 1;
|
||
} else if (tok == TOK_DOTS && gnu_ext) {
|
||
is_vaargs = 1;
|
||
next_nomacro();
|
||
}
|
||
if (varg < TOK_IDENT)
|
||
error("badly punctuated parameter list");
|
||
s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
|
||
*ps = s;
|
||
ps = &s->next;
|
||
if (tok != ',')
|
||
break;
|
||
next_nomacro();
|
||
}
|
||
t = MACRO_FUNC;
|
||
}
|
||
tok_str_new(&str);
|
||
next_nomacro();
|
||
/* EOF testing necessary for '-D' handling */
|
||
while (tok != TOK_LINEFEED && tok != TOK_EOF) {
|
||
tok_str_add2(&str, tok, &tokc);
|
||
next_nomacro();
|
||
}
|
||
tok_str_add(&str, 0);
|
||
#ifdef PP_DEBUG
|
||
printf("define %s %d: ", get_tok_str(v, NULL), t);
|
||
tok_print(str.str);
|
||
#endif
|
||
define_push(v, t, str.str, first);
|
||
}
|
||
|
||
static inline int hash_cached_include(int type, const char *filename)
|
||
{
|
||
const unsigned char *s;
|
||
unsigned int h;
|
||
|
||
h = TOK_HASH_INIT;
|
||
h = TOK_HASH_FUNC(h, type);
|
||
s = filename;
|
||
while (*s) {
|
||
h = TOK_HASH_FUNC(h, *s);
|
||
s++;
|
||
}
|
||
h &= (CACHED_INCLUDES_HASH_SIZE - 1);
|
||
return h;
|
||
}
|
||
|
||
/* XXX: use a token or a hash table to accelerate matching ? */
|
||
static CachedInclude *search_cached_include(TCCState *s1,
|
||
int type, const char *filename)
|
||
{
|
||
CachedInclude *e;
|
||
int i, h;
|
||
h = hash_cached_include(type, filename);
|
||
i = s1->cached_includes_hash[h];
|
||
for(;;) {
|
||
if (i == 0)
|
||
break;
|
||
e = s1->cached_includes[i - 1];
|
||
if (e->type == type && !strcmp(e->filename, filename))
|
||
return e;
|
||
i = e->hash_next;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
static inline void add_cached_include(TCCState *s1, int type,
|
||
const char *filename, int ifndef_macro)
|
||
{
|
||
CachedInclude *e;
|
||
int h;
|
||
|
||
if (search_cached_include(s1, type, filename))
|
||
return;
|
||
#ifdef INC_DEBUG
|
||
printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL));
|
||
#endif
|
||
e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
|
||
if (!e)
|
||
return;
|
||
e->type = type;
|
||
strcpy(e->filename, filename);
|
||
e->ifndef_macro = ifndef_macro;
|
||
dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e);
|
||
/* add in hash table */
|
||
h = hash_cached_include(type, filename);
|
||
e->hash_next = s1->cached_includes_hash[h];
|
||
s1->cached_includes_hash[h] = s1->nb_cached_includes;
|
||
}
|
||
|
||
static void pragma_parse(TCCState *s1)
|
||
{
|
||
int val;
|
||
|
||
next();
|
||
if (tok == TOK_pack) {
|
||
/*
|
||
This may be:
|
||
#pragma pack(1) // set
|
||
#pragma pack() // reset to default
|
||
#pragma pack(push,1) // push & set
|
||
#pragma pack(pop) // restore previous
|
||
*/
|
||
next();
|
||
skip('(');
|
||
if (tok == TOK_ASM_pop) {
|
||
next();
|
||
if (s1->pack_stack_ptr <= s1->pack_stack) {
|
||
stk_error:
|
||
error("out of pack stack");
|
||
}
|
||
s1->pack_stack_ptr--;
|
||
} else {
|
||
val = 0;
|
||
if (tok != ')') {
|
||
if (tok == TOK_ASM_push) {
|
||
next();
|
||
if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
|
||
goto stk_error;
|
||
s1->pack_stack_ptr++;
|
||
skip(',');
|
||
}
|
||
if (tok != TOK_CINT) {
|
||
pack_error:
|
||
error("invalid pack pragma");
|
||
}
|
||
val = tokc.i;
|
||
if (val < 1 || val > 16 || (val & (val - 1)) != 0)
|
||
goto pack_error;
|
||
next();
|
||
}
|
||
*s1->pack_stack_ptr = val;
|
||
skip(')');
|
||
}
|
||
}
|
||
}
|
||
|
||
/* is_bof is true if first non space token at beginning of file */
|
||
static void preprocess(int is_bof)
|
||
{
|
||
TCCState *s1 = tcc_state;
|
||
int size, i, c, n, saved_parse_flags;
|
||
char buf[1024], *q, *p;
|
||
char buf1[1024];
|
||
BufferedFile *f;
|
||
Sym *s;
|
||
CachedInclude *e;
|
||
|
||
saved_parse_flags = parse_flags;
|
||
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM |
|
||
PARSE_FLAG_LINEFEED;
|
||
next_nomacro();
|
||
redo:
|
||
switch(tok) {
|
||
case TOK_DEFINE:
|
||
next_nomacro();
|
||
parse_define();
|
||
break;
|
||
case TOK_UNDEF:
|
||
next_nomacro();
|
||
s = define_find(tok);
|
||
/* undefine symbol by putting an invalid name */
|
||
if (s)
|
||
define_undef(s);
|
||
break;
|
||
case TOK_INCLUDE:
|
||
case TOK_INCLUDE_NEXT:
|
||
ch = file->buf_ptr[0];
|
||
/* XXX: incorrect if comments : use next_nomacro with a special mode */
|
||
skip_spaces();
|
||
if (ch == '<') {
|
||
c = '>';
|
||
goto read_name;
|
||
} else if (ch == '\"') {
|
||
c = ch;
|
||
read_name:
|
||
/* XXX: better stray handling */
|
||
minp();
|
||
q = buf;
|
||
while (ch != c && ch != '\n' && ch != CH_EOF) {
|
||
if ((q - buf) < sizeof(buf) - 1)
|
||
*q++ = ch;
|
||
minp();
|
||
}
|
||
*q = '\0';
|
||
minp();
|
||
#if 0
|
||
/* eat all spaces and comments after include */
|
||
/* XXX: slightly incorrect */
|
||
while (ch1 != '\n' && ch1 != CH_EOF)
|
||
inp();
|
||
#endif
|
||
} else {
|
||
/* computed #include : either we have only strings or
|
||
we have anything enclosed in '<>' */
|
||
next();
|
||
buf[0] = '\0';
|
||
if (tok == TOK_STR) {
|
||
while (tok != TOK_LINEFEED) {
|
||
if (tok != TOK_STR) {
|
||
include_syntax:
|
||
error("'#include' expects \"FILENAME\" or <FILENAME>");
|
||
}
|
||
pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data);
|
||
next();
|
||
}
|
||
c = '\"';
|
||
} else {
|
||
int len;
|
||
while (tok != TOK_LINEFEED) {
|
||
pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
|
||
next();
|
||
}
|
||
len = strlen(buf);
|
||
/* check syntax and remove '<>' */
|
||
if (len < 2 || buf[0] != '<' || buf[len - 1] != '>')
|
||
goto include_syntax;
|
||
memmove(buf, buf + 1, len - 2);
|
||
buf[len - 2] = '\0';
|
||
c = '>';
|
||
}
|
||
}
|
||
|
||
e = search_cached_include(s1, c, buf);
|
||
if (e && define_find(e->ifndef_macro)) {
|
||
/* no need to parse the include because the 'ifndef macro'
|
||
is defined */
|
||
#ifdef INC_DEBUG
|
||
printf("%s: skipping %s\n", file->filename, buf);
|
||
#endif
|
||
} else {
|
||
if (c == '\"') {
|
||
/* first search in current dir if "header.h" */
|
||
size = 0;
|
||
p = strrchr(file->filename, '/');
|
||
if (p)
|
||
size = p + 1 - file->filename;
|
||
if (size > sizeof(buf1) - 1)
|
||
size = sizeof(buf1) - 1;
|
||
memcpy(buf1, file->filename, size);
|
||
buf1[size] = '\0';
|
||
pstrcat(buf1, sizeof(buf1), buf);
|
||
f = tcc_open(s1, buf1);
|
||
if (f) {
|
||
if (tok == TOK_INCLUDE_NEXT)
|
||
tok = TOK_INCLUDE;
|
||
else
|
||
goto found;
|
||
}
|
||
}
|
||
if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
|
||
error("#include recursion too deep");
|
||
/* now search in all the include paths */
|
||
n = s1->nb_include_paths + s1->nb_sysinclude_paths;
|
||
for(i = 0; i < n; i++) {
|
||
const char *path;
|
||
if (i < s1->nb_include_paths)
|
||
path = s1->include_paths[i];
|
||
else
|
||
path = s1->sysinclude_paths[i - s1->nb_include_paths];
|
||
pstrcpy(buf1, sizeof(buf1), path);
|
||
pstrcat(buf1, sizeof(buf1), "/");
|
||
pstrcat(buf1, sizeof(buf1), buf);
|
||
f = tcc_open(s1, buf1);
|
||
if (f) {
|
||
if (tok == TOK_INCLUDE_NEXT)
|
||
tok = TOK_INCLUDE;
|
||
else
|
||
goto found;
|
||
}
|
||
}
|
||
error("include file '%s' not found", buf);
|
||
f = NULL;
|
||
found:
|
||
#ifdef INC_DEBUG
|
||
printf("%s: including %s\n", file->filename, buf1);
|
||
#endif
|
||
f->inc_type = c;
|
||
pstrcpy(f->inc_filename, sizeof(f->inc_filename), buf);
|
||
/* push current file in stack */
|
||
/* XXX: fix current line init */
|
||
*s1->include_stack_ptr++ = file;
|
||
file = f;
|
||
/* add include file debug info */
|
||
if (do_debug) {
|
||
put_stabs(file->filename, N_BINCL, 0, 0, 0);
|
||
}
|
||
tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
|
||
ch = file->buf_ptr[0];
|
||
goto the_end;
|
||
}
|
||
break;
|
||
case TOK_IFNDEF:
|
||
c = 1;
|
||
goto do_ifdef;
|
||
case TOK_IF:
|
||
c = expr_preprocess();
|
||
goto do_if;
|
||
case TOK_IFDEF:
|
||
c = 0;
|
||
do_ifdef:
|
||
next_nomacro();
|
||
if (tok < TOK_IDENT)
|
||
error("invalid argument for '#if%sdef'", c ? "n" : "");
|
||
if (is_bof) {
|
||
if (c) {
|
||
#ifdef INC_DEBUG
|
||
printf("#ifndef %s\n", get_tok_str(tok, NULL));
|
||
#endif
|
||
file->ifndef_macro = tok;
|
||
}
|
||
}
|
||
c = (define_find(tok) != 0) ^ c;
|
||
do_if:
|
||
if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
|
||
error("memory full");
|
||
*s1->ifdef_stack_ptr++ = c;
|
||
goto test_skip;
|
||
case TOK_ELSE:
|
||
if (s1->ifdef_stack_ptr == s1->ifdef_stack)
|
||
error("#else without matching #if");
|
||
if (s1->ifdef_stack_ptr[-1] & 2)
|
||
error("#else after #else");
|
||
c = (s1->ifdef_stack_ptr[-1] ^= 3);
|
||
goto test_skip;
|
||
case TOK_ELIF:
|
||
if (s1->ifdef_stack_ptr == s1->ifdef_stack)
|
||
error("#elif without matching #if");
|
||
c = s1->ifdef_stack_ptr[-1];
|
||
if (c > 1)
|
||
error("#elif after #else");
|
||
/* last #if/#elif expression was true: we skip */
|
||
if (c == 1)
|
||
goto skip;
|
||
c = expr_preprocess();
|
||
s1->ifdef_stack_ptr[-1] = c;
|
||
test_skip:
|
||
if (!(c & 1)) {
|
||
skip:
|
||
preprocess_skip();
|
||
is_bof = 0;
|
||
goto redo;
|
||
}
|
||
break;
|
||
case TOK_ENDIF:
|
||
if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
|
||
error("#endif without matching #if");
|
||
s1->ifdef_stack_ptr--;
|
||
/* '#ifndef macro' was at the start of file. Now we check if
|
||
an '#endif' is exactly at the end of file */
|
||
if (file->ifndef_macro &&
|
||
s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
|
||
file->ifndef_macro_saved = file->ifndef_macro;
|
||
/* need to set to zero to avoid false matches if another
|
||
#ifndef at middle of file */
|
||
file->ifndef_macro = 0;
|
||
while (tok != TOK_LINEFEED)
|
||
next_nomacro();
|
||
tok_flags |= TOK_FLAG_ENDIF;
|
||
goto the_end;
|
||
}
|
||
break;
|
||
case TOK_LINE:
|
||
next();
|
||
if (tok != TOK_CINT)
|
||
error("#line");
|
||
file->line_num = tokc.i - 1; /* the line number will be incremented after */
|
||
next();
|
||
if (tok != TOK_LINEFEED) {
|
||
if (tok != TOK_STR)
|
||
error("#line");
|
||
pstrcpy(file->filename, sizeof(file->filename),
|
||
(char *)tokc.cstr->data);
|
||
}
|
||
break;
|
||
case TOK_ERROR:
|
||
case TOK_WARNING:
|
||
c = tok;
|
||
ch = file->buf_ptr[0];
|
||
skip_spaces();
|
||
q = buf;
|
||
while (ch != '\n' && ch != CH_EOF) {
|
||
if ((q - buf) < sizeof(buf) - 1)
|
||
*q++ = ch;
|
||
minp();
|
||
}
|
||
*q = '\0';
|
||
if (c == TOK_ERROR)
|
||
error("#error %s", buf);
|
||
else
|
||
warning("#warning %s", buf);
|
||
break;
|
||
case TOK_PRAGMA:
|
||
pragma_parse(s1);
|
||
break;
|
||
default:
|
||
if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_CINT) {
|
||
/* '!' is ignored to allow C scripts. numbers are ignored
|
||
to emulate cpp behaviour */
|
||
} else {
|
||
if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS))
|
||
error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc));
|
||
}
|
||
break;
|
||
}
|
||
/* ignore other preprocess commands or #! for C scripts */
|
||
while (tok != TOK_LINEFEED)
|
||
next_nomacro();
|
||
the_end:
|
||
parse_flags = saved_parse_flags;
|
||
}
|
||
|
||
/* evaluate escape codes in a string. */
|
||
static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
|
||
{
|
||
int c, n;
|
||
const uint8_t *p;
|
||
|
||
p = buf;
|
||
for(;;) {
|
||
c = *p;
|
||
if (c == '\0')
|
||
break;
|
||
if (c == '\\') {
|
||
p++;
|
||
/* escape */
|
||
c = *p;
|
||
switch(c) {
|
||
case '0': case '1': case '2': case '3':
|
||
case '4': case '5': case '6': case '7':
|
||
/* at most three octal digits */
|
||
n = c - '0';
|
||
p++;
|
||
c = *p;
|
||
if (isoct(c)) {
|
||
n = n * 8 + c - '0';
|
||
p++;
|
||
c = *p;
|
||
if (isoct(c)) {
|
||
n = n * 8 + c - '0';
|
||
p++;
|
||
}
|
||
}
|
||
c = n;
|
||
goto add_char_nonext;
|
||
case 'x':
|
||
p++;
|
||
n = 0;
|
||
for(;;) {
|
||
c = *p;
|
||
if (c >= 'a' && c <= 'f')
|
||
c = c - 'a' + 10;
|
||
else if (c >= 'A' && c <= 'F')
|
||
c = c - 'A' + 10;
|
||
else if (isnum(c))
|
||
c = c - '0';
|
||
else
|
||
break;
|
||
n = n * 16 + c;
|
||
p++;
|
||
}
|
||
c = n;
|
||
goto add_char_nonext;
|
||
case 'a':
|
||
c = '\a';
|
||
break;
|
||
case 'b':
|
||
c = '\b';
|
||
break;
|
||
case 'f':
|
||
c = '\f';
|
||
break;
|
||
case 'n':
|
||
c = '\n';
|
||
break;
|
||
case 'r':
|
||
c = '\r';
|
||
break;
|
||
case 't':
|
||
c = '\t';
|
||
break;
|
||
case 'v':
|
||
c = '\v';
|
||
break;
|
||
case 'e':
|
||
if (!gnu_ext)
|
||
goto invalid_escape;
|
||
c = 27;
|
||
break;
|
||
case '\'':
|
||
case '\"':
|
||
case '\\':
|
||
case '?':
|
||
break;
|
||
default:
|
||
invalid_escape:
|
||
if (c >= '!' && c <= '~')
|
||
warning("unknown escape sequence: \'\\%c\'", c);
|
||
else
|
||
warning("unknown escape sequence: \'\\x%x\'", c);
|
||
break;
|
||
}
|
||
}
|
||
p++;
|
||
add_char_nonext:
|
||
if (!is_long)
|
||
cstr_ccat(outstr, c);
|
||
else
|
||
cstr_wccat(outstr, c);
|
||
}
|
||
/* add a trailing '\0' */
|
||
if (!is_long)
|
||
cstr_ccat(outstr, '\0');
|
||
else
|
||
cstr_wccat(outstr, '\0');
|
||
}
|
||
|
||
/* we use 64 bit numbers */
|
||
#define BN_SIZE 2
|
||
|
||
/* bn = (bn << shift) | or_val */
|
||
void bn_lshift(unsigned int *bn, int shift, int or_val)
|
||
{
|
||
int i;
|
||
unsigned int v;
|
||
for(i=0;i<BN_SIZE;i++) {
|
||
v = bn[i];
|
||
bn[i] = (v << shift) | or_val;
|
||
or_val = v >> (32 - shift);
|
||
}
|
||
}
|
||
|
||
void bn_zero(unsigned int *bn)
|
||
{
|
||
int i;
|
||
for(i=0;i<BN_SIZE;i++) {
|
||
bn[i] = 0;
|
||
}
|
||
}
|
||
|
||
/* parse number in null terminated string 'p' and return it in the
|
||
current token */
|
||
void parse_number(const char *p)
|
||
{
|
||
int b, t, shift, frac_bits, s, exp_val, ch;
|
||
char *q;
|
||
unsigned int bn[BN_SIZE];
|
||
double d;
|
||
|
||
/* number */
|
||
q = token_buf;
|
||
ch = *p++;
|
||
t = ch;
|
||
ch = *p++;
|
||
*q++ = t;
|
||
b = 10;
|
||
if (t == '.') {
|
||
goto float_frac_parse;
|
||
} else if (t == '0') {
|
||
if (ch == 'x' || ch == 'X') {
|
||
q--;
|
||
ch = *p++;
|
||
b = 16;
|
||
} else if (tcc_ext && (ch == 'b' || ch == 'B')) {
|
||
q--;
|
||
ch = *p++;
|
||
b = 2;
|
||
}
|
||
}
|
||
/* parse all digits. cannot check octal numbers at this stage
|
||
because of floating point constants */
|
||
while (1) {
|
||
if (ch >= 'a' && ch <= 'f')
|
||
t = ch - 'a' + 10;
|
||
else if (ch >= 'A' && ch <= 'F')
|
||
t = ch - 'A' + 10;
|
||
else if (isnum(ch))
|
||
t = ch - '0';
|
||
else
|
||
break;
|
||
if (t >= b)
|
||
break;
|
||
if (q >= token_buf + STRING_MAX_SIZE) {
|
||
num_too_long:
|
||
error("number too long");
|
||
}
|
||
*q++ = ch;
|
||
ch = *p++;
|
||
}
|
||
if (ch == '.' ||
|
||
((ch == 'e' || ch == 'E') && b == 10) ||
|
||
((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
|
||
if (b != 10) {
|
||
/* NOTE: strtox should support that for hexa numbers, but
|
||
non ISOC99 libcs do not support it, so we prefer to do
|
||
it by hand */
|
||
/* hexadecimal or binary floats */
|
||
/* XXX: handle overflows */
|
||
*q = '\0';
|
||
if (b == 16)
|
||
shift = 4;
|
||
else
|
||
shift = 2;
|
||
bn_zero(bn);
|
||
q = token_buf;
|
||
while (1) {
|
||
t = *q++;
|
||
if (t == '\0') {
|
||
break;
|
||
} else if (t >= 'a') {
|
||
t = t - 'a' + 10;
|
||
} else if (t >= 'A') {
|
||
t = t - 'A' + 10;
|
||
} else {
|
||
t = t - '0';
|
||
}
|
||
bn_lshift(bn, shift, t);
|
||
}
|
||
frac_bits = 0;
|
||
if (ch == '.') {
|
||
ch = *p++;
|
||
while (1) {
|
||
t = ch;
|
||
if (t >= 'a' && t <= 'f') {
|
||
t = t - 'a' + 10;
|
||
} else if (t >= 'A' && t <= 'F') {
|
||
t = t - 'A' + 10;
|
||
} else if (t >= '0' && t <= '9') {
|
||
t = t - '0';
|
||
} else {
|
||
break;
|
||
}
|
||
if (t >= b)
|
||
error("invalid digit");
|
||
bn_lshift(bn, shift, t);
|
||
frac_bits += shift;
|
||
ch = *p++;
|
||
}
|
||
}
|
||
if (ch != 'p' && ch != 'P')
|
||
expect("exponent");
|
||
ch = *p++;
|
||
s = 1;
|
||
exp_val = 0;
|
||
if (ch == '+') {
|
||
ch = *p++;
|
||
} else if (ch == '-') {
|
||
s = -1;
|
||
ch = *p++;
|
||
}
|
||
if (ch < '0' || ch > '9')
|
||
expect("exponent digits");
|
||
while (ch >= '0' && ch <= '9') {
|
||
exp_val = exp_val * 10 + ch - '0';
|
||
ch = *p++;
|
||
}
|
||
exp_val = exp_val * s;
|
||
|
||
/* now we can generate the number */
|
||
/* XXX: should patch directly float number */
|
||
d = (double)bn[1] * 4294967296.0 + (double)bn[0];
|
||
d = ldexp(d, exp_val - frac_bits);
|
||
t = toup(ch);
|
||
if (t == 'F') {
|
||
ch = *p++;
|
||
tok = TOK_CFLOAT;
|
||
/* float : should handle overflow */
|
||
tokc.f = (float)d;
|
||
} else if (t == 'L') {
|
||
ch = *p++;
|
||
tok = TOK_CLDOUBLE;
|
||
/* XXX: not large enough */
|
||
tokc.ld = (long double)d;
|
||
} else {
|
||
tok = TOK_CDOUBLE;
|
||
tokc.d = d;
|
||
}
|
||
} else {
|
||
/* decimal floats */
|
||
if (ch == '.') {
|
||
if (q >= token_buf + STRING_MAX_SIZE)
|
||
goto num_too_long;
|
||
*q++ = ch;
|
||
ch = *p++;
|
||
float_frac_parse:
|
||
while (ch >= '0' && ch <= '9') {
|
||
if (q >= token_buf + STRING_MAX_SIZE)
|
||
goto num_too_long;
|
||
*q++ = ch;
|
||
ch = *p++;
|
||
}
|
||
}
|
||
if (ch == 'e' || ch == 'E') {
|
||
if (q >= token_buf + STRING_MAX_SIZE)
|
||
goto num_too_long;
|
||
*q++ = ch;
|
||
ch = *p++;
|
||
if (ch == '-' || ch == '+') {
|
||
if (q >= token_buf + STRING_MAX_SIZE)
|
||
goto num_too_long;
|
||
*q++ = ch;
|
||
ch = *p++;
|
||
}
|
||
if (ch < '0' || ch > '9')
|
||
expect("exponent digits");
|
||
while (ch >= '0' && ch <= '9') {
|
||
if (q >= token_buf + STRING_MAX_SIZE)
|
||
goto num_too_long;
|
||
*q++ = ch;
|
||
ch = *p++;
|
||
}
|
||
}
|
||
*q = '\0';
|
||
t = toup(ch);
|
||
errno = 0;
|
||
if (t == 'F') {
|
||
ch = *p++;
|
||
tok = TOK_CFLOAT;
|
||
tokc.f = strtof(token_buf, NULL);
|
||
} else if (t == 'L') {
|
||
ch = *p++;
|
||
tok = TOK_CLDOUBLE;
|
||
tokc.ld = strtold(token_buf, NULL);
|
||
} else {
|
||
tok = TOK_CDOUBLE;
|
||
tokc.d = strtod(token_buf, NULL);
|
||
}
|
||
}
|
||
} else {
|
||
unsigned long long n, n1;
|
||
int lcount, ucount;
|
||
|
||
/* integer number */
|
||
*q = '\0';
|
||
q = token_buf;
|
||
if (b == 10 && *q == '0') {
|
||
b = 8;
|
||
q++;
|
||
}
|
||
n = 0;
|
||
while(1) {
|
||
t = *q++;
|
||
/* no need for checks except for base 10 / 8 errors */
|
||
if (t == '\0') {
|
||
break;
|
||
} else if (t >= 'a') {
|
||
t = t - 'a' + 10;
|
||
} else if (t >= 'A') {
|
||
t = t - 'A' + 10;
|
||
} else {
|
||
t = t - '0';
|
||
if (t >= b)
|
||
error("invalid digit");
|
||
}
|
||
n1 = n;
|
||
n = n * b + t;
|
||
/* detect overflow */
|
||
/* XXX: this test is not reliable */
|
||
if (n < n1)
|
||
error("integer constant overflow");
|
||
}
|
||
|
||
/* XXX: not exactly ANSI compliant */
|
||
if ((n & 0xffffffff00000000LL) != 0) {
|
||
if ((n >> 63) != 0)
|
||
tok = TOK_CULLONG;
|
||
else
|
||
tok = TOK_CLLONG;
|
||
} else if (n > 0x7fffffff) {
|
||
tok = TOK_CUINT;
|
||
} else {
|
||
tok = TOK_CINT;
|
||
}
|
||
lcount = 0;
|
||
ucount = 0;
|
||
for(;;) {
|
||
t = toup(ch);
|
||
if (t == 'L') {
|
||
if (lcount >= 2)
|
||
error("three 'l's in integer constant");
|
||
lcount++;
|
||
if (lcount == 2) {
|
||
if (tok == TOK_CINT)
|
||
tok = TOK_CLLONG;
|
||
else if (tok == TOK_CUINT)
|
||
tok = TOK_CULLONG;
|
||
}
|
||
ch = *p++;
|
||
} else if (t == 'U') {
|
||
if (ucount >= 1)
|
||
error("two 'u's in integer constant");
|
||
ucount++;
|
||
if (tok == TOK_CINT)
|
||
tok = TOK_CUINT;
|
||
else if (tok == TOK_CLLONG)
|
||
tok = TOK_CULLONG;
|
||
ch = *p++;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
if (tok == TOK_CINT || tok == TOK_CUINT)
|
||
tokc.ui = n;
|
||
else
|
||
tokc.ull = n;
|
||
}
|
||
}
|
||
|
||
|
||
#define PARSE2(c1, tok1, c2, tok2) \
|
||
case c1: \
|
||
PEEKC(c, p); \
|
||
if (c == c2) { \
|
||
p++; \
|
||
tok = tok2; \
|
||
} else { \
|
||
tok = tok1; \
|
||
} \
|
||
break;
|
||
|
||
/* return next token without macro substitution */
|
||
static /*inline*/ void next_nomacro1(void)
|
||
{
|
||
int t, c, is_long;
|
||
TokenSym *ts;
|
||
uint8_t *p, *p1;
|
||
unsigned int h;
|
||
|
||
p = file->buf_ptr;
|
||
redo_no_start:
|
||
c = *p;
|
||
switch(c) {
|
||
case ' ':
|
||
case '\t':
|
||
case '\f':
|
||
case '\v':
|
||
case '\r':
|
||
p++;
|
||
goto redo_no_start;
|
||
|
||
case '\\':
|
||
/* first look if it is in fact an end of buffer */
|
||
if (p >= file->buf_end) {
|
||
file->buf_ptr = p;
|
||
handle_eob();
|
||
p = file->buf_ptr;
|
||
if (p >= file->buf_end)
|
||
goto parse_eof;
|
||
else
|
||
goto redo_no_start;
|
||
} else {
|
||
file->buf_ptr = p;
|
||
ch = *p;
|
||
handle_stray();
|
||
p = file->buf_ptr;
|
||
goto redo_no_start;
|
||
}
|
||
parse_eof:
|
||
{
|
||
TCCState *s1 = tcc_state;
|
||
if (parse_flags & PARSE_FLAG_LINEFEED) {
|
||
tok = TOK_LINEFEED;
|
||
} else if (s1->include_stack_ptr == s1->include_stack ||
|
||
!(parse_flags & PARSE_FLAG_PREPROCESS)) {
|
||
/* no include left : end of file. */
|
||
tok = TOK_EOF;
|
||
} else {
|
||
/* pop include file */
|
||
|
||
/* test if previous '#endif' was after a #ifdef at
|
||
start of file */
|
||
if (tok_flags & TOK_FLAG_ENDIF) {
|
||
#ifdef INC_DEBUG
|
||
printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
|
||
#endif
|
||
add_cached_include(s1, file->inc_type, file->inc_filename,
|
||
file->ifndef_macro_saved);
|
||
}
|
||
|
||
/* add end of include file debug info */
|
||
if (do_debug) {
|
||
put_stabd(N_EINCL, 0, 0);
|
||
}
|
||
/* pop include stack */
|
||
tcc_close(file);
|
||
s1->include_stack_ptr--;
|
||
file = *s1->include_stack_ptr;
|
||
p = file->buf_ptr;
|
||
goto redo_no_start;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case '\n':
|
||
if (parse_flags & PARSE_FLAG_LINEFEED) {
|
||
tok = TOK_LINEFEED;
|
||
} else {
|
||
file->line_num++;
|
||
tok_flags |= TOK_FLAG_BOL;
|
||
p++;
|
||
goto redo_no_start;
|
||
}
|
||
break;
|
||
|
||
case '#':
|
||
/* XXX: simplify */
|
||
PEEKC(c, p);
|
||
if ((tok_flags & TOK_FLAG_BOL) &&
|
||
(parse_flags & PARSE_FLAG_PREPROCESS)) {
|
||
file->buf_ptr = p;
|
||
preprocess(tok_flags & TOK_FLAG_BOF);
|
||
p = file->buf_ptr;
|
||
goto redo_no_start;
|
||
} else {
|
||
if (c == '#') {
|
||
p++;
|
||
tok = TOK_TWOSHARPS;
|
||
} else {
|
||
if (parse_flags & PARSE_FLAG_ASM_COMMENTS) {
|
||
p = parse_line_comment(p - 1);
|
||
goto redo_no_start;
|
||
} else {
|
||
tok = '#';
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'a': case 'b': case 'c': case 'd':
|
||
case 'e': case 'f': case 'g': case 'h':
|
||
case 'i': case 'j': case 'k': case 'l':
|
||
case 'm': case 'n': case 'o': case 'p':
|
||
case 'q': case 'r': case 's': case 't':
|
||
case 'u': case 'v': case 'w': case 'x':
|
||
case 'y': case 'z':
|
||
case 'A': case 'B': case 'C': case 'D':
|
||
case 'E': case 'F': case 'G': case 'H':
|
||
case 'I': case 'J': case 'K':
|
||
case 'M': case 'N': case 'O': case 'P':
|
||
case 'Q': case 'R': case 'S': case 'T':
|
||
case 'U': case 'V': case 'W': case 'X':
|
||
case 'Y': case 'Z':
|
||
case '_':
|
||
parse_ident_fast:
|
||
p1 = p;
|
||
h = TOK_HASH_INIT;
|
||
h = TOK_HASH_FUNC(h, c);
|
||
p++;
|
||
for(;;) {
|
||
c = *p;
|
||
if (!isidnum_table[c])
|
||
break;
|
||
h = TOK_HASH_FUNC(h, c);
|
||
p++;
|
||
}
|
||
if (c != '\\') {
|
||
TokenSym **pts;
|
||
int len;
|
||
|
||
/* fast case : no stray found, so we have the full token
|
||
and we have already hashed it */
|
||
len = p - p1;
|
||
h &= (TOK_HASH_SIZE - 1);
|
||
pts = &hash_ident[h];
|
||
for(;;) {
|
||
ts = *pts;
|
||
if (!ts)
|
||
break;
|
||
if (ts->len == len && !memcmp(ts->str, p1, len))
|
||
goto token_found;
|
||
pts = &(ts->hash_next);
|
||
}
|
||
ts = tok_alloc_new(pts, p1, len);
|
||
token_found: ;
|
||
} else {
|
||
/* slower case */
|
||
cstr_reset(&tokcstr);
|
||
|
||
while (p1 < p) {
|
||
cstr_ccat(&tokcstr, *p1);
|
||
p1++;
|
||
}
|
||
p--;
|
||
PEEKC(c, p);
|
||
parse_ident_slow:
|
||
while (isidnum_table[c]) {
|
||
cstr_ccat(&tokcstr, c);
|
||
PEEKC(c, p);
|
||
}
|
||
ts = tok_alloc(tokcstr.data, tokcstr.size);
|
||
}
|
||
tok = ts->tok;
|
||
break;
|
||
case 'L':
|
||
t = p[1];
|
||
if (t != '\\' && t != '\'' && t != '\"') {
|
||
/* fast case */
|
||
goto parse_ident_fast;
|
||
} else {
|
||
PEEKC(c, p);
|
||
if (c == '\'' || c == '\"') {
|
||
is_long = 1;
|
||
goto str_const;
|
||
} else {
|
||
cstr_reset(&tokcstr);
|
||
cstr_ccat(&tokcstr, 'L');
|
||
goto parse_ident_slow;
|
||
}
|
||
}
|
||
break;
|
||
case '0': case '1': case '2': case '3':
|
||
case '4': case '5': case '6': case '7':
|
||
case '8': case '9':
|
||
|
||
cstr_reset(&tokcstr);
|
||
/* after the first digit, accept digits, alpha, '.' or sign if
|
||
prefixed by 'eEpP' */
|
||
parse_num:
|
||
for(;;) {
|
||
t = c;
|
||
cstr_ccat(&tokcstr, c);
|
||
PEEKC(c, p);
|
||
if (!(isnum(c) || isid(c) || c == '.' ||
|
||
((c == '+' || c == '-') &&
|
||
(t == 'e' || t == 'E' || t == 'p' || t == 'P'))))
|
||
break;
|
||
}
|
||
/* We add a trailing '\0' to ease parsing */
|
||
cstr_ccat(&tokcstr, '\0');
|
||
tokc.cstr = &tokcstr;
|
||
tok = TOK_PPNUM;
|
||
break;
|
||
case '.':
|
||
/* special dot handling because it can also start a number */
|
||
PEEKC(c, p);
|
||
if (isnum(c)) {
|
||
cstr_reset(&tokcstr);
|
||
cstr_ccat(&tokcstr, '.');
|
||
goto parse_num;
|
||
} else if (c == '.') {
|
||
PEEKC(c, p);
|
||
if (c != '.')
|
||
expect("'.'");
|
||
PEEKC(c, p);
|
||
tok = TOK_DOTS;
|
||
} else {
|
||
tok = '.';
|
||
}
|
||
break;
|
||
case '\'':
|
||
case '\"':
|
||
is_long = 0;
|
||
str_const:
|
||
{
|
||
CString str;
|
||
int sep;
|
||
|
||
sep = c;
|
||
|
||
/* parse the string */
|
||
cstr_new(&str);
|
||
p = parse_pp_string(p, sep, &str);
|
||
cstr_ccat(&str, '\0');
|
||
|
||
/* eval the escape (should be done as TOK_PPNUM) */
|
||
cstr_reset(&tokcstr);
|
||
parse_escape_string(&tokcstr, str.data, is_long);
|
||
cstr_free(&str);
|
||
|
||
if (sep == '\'') {
|
||
int char_size;
|
||
/* XXX: make it portable */
|
||
if (!is_long)
|
||
char_size = 1;
|
||
else
|
||
char_size = sizeof(int);
|
||
if (tokcstr.size <= char_size)
|
||
error("empty character constant");
|
||
if (tokcstr.size > 2 * char_size)
|
||
warning("multi-character character constant");
|
||
if (!is_long) {
|
||
tokc.i = *(int8_t *)tokcstr.data;
|
||
tok = TOK_CCHAR;
|
||
} else {
|
||
tokc.i = *(int *)tokcstr.data;
|
||
tok = TOK_LCHAR;
|
||
}
|
||
} else {
|
||
tokc.cstr = &tokcstr;
|
||
if (!is_long)
|
||
tok = TOK_STR;
|
||
else
|
||
tok = TOK_LSTR;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case '<':
|
||
PEEKC(c, p);
|
||
if (c == '=') {
|
||
p++;
|
||
tok = TOK_LE;
|
||
} else if (c == '<') {
|
||
PEEKC(c, p);
|
||
if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_SHL;
|
||
} else {
|
||
tok = TOK_SHL;
|
||
}
|
||
} else {
|
||
tok = TOK_LT;
|
||
}
|
||
break;
|
||
|
||
case '>':
|
||
PEEKC(c, p);
|
||
if (c == '=') {
|
||
p++;
|
||
tok = TOK_GE;
|
||
} else if (c == '>') {
|
||
PEEKC(c, p);
|
||
if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_SAR;
|
||
} else {
|
||
tok = TOK_SAR;
|
||
}
|
||
} else {
|
||
tok = TOK_GT;
|
||
}
|
||
break;
|
||
|
||
case '&':
|
||
PEEKC(c, p);
|
||
if (c == '&') {
|
||
p++;
|
||
tok = TOK_LAND;
|
||
} else if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_AND;
|
||
} else {
|
||
tok = '&';
|
||
}
|
||
break;
|
||
|
||
case '|':
|
||
PEEKC(c, p);
|
||
if (c == '|') {
|
||
p++;
|
||
tok = TOK_LOR;
|
||
} else if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_OR;
|
||
} else {
|
||
tok = '|';
|
||
}
|
||
break;
|
||
|
||
case '+':
|
||
PEEKC(c, p);
|
||
if (c == '+') {
|
||
p++;
|
||
tok = TOK_INC;
|
||
} else if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_ADD;
|
||
} else {
|
||
tok = '+';
|
||
}
|
||
break;
|
||
|
||
case '-':
|
||
PEEKC(c, p);
|
||
if (c == '-') {
|
||
p++;
|
||
tok = TOK_DEC;
|
||
} else if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_SUB;
|
||
} else if (c == '>') {
|
||
p++;
|
||
tok = TOK_ARROW;
|
||
} else {
|
||
tok = '-';
|
||
}
|
||
break;
|
||
|
||
PARSE2('!', '!', '=', TOK_NE)
|
||
PARSE2('=', '=', '=', TOK_EQ)
|
||
PARSE2('*', '*', '=', TOK_A_MUL)
|
||
PARSE2('%', '%', '=', TOK_A_MOD)
|
||
PARSE2('^', '^', '=', TOK_A_XOR)
|
||
|
||
/* comments or operator */
|
||
case '/':
|
||
PEEKC(c, p);
|
||
if (c == '*') {
|
||
p = parse_comment(p);
|
||
goto redo_no_start;
|
||
} else if (c == '/') {
|
||
p = parse_line_comment(p);
|
||
goto redo_no_start;
|
||
} else if (c == '=') {
|
||
p++;
|
||
tok = TOK_A_DIV;
|
||
} else {
|
||
tok = '/';
|
||
}
|
||
break;
|
||
|
||
/* simple tokens */
|
||
case '(':
|
||
case ')':
|
||
case '[':
|
||
case ']':
|
||
case '{':
|
||
case '}':
|
||
case ',':
|
||
case ';':
|
||
case ':':
|
||
case '?':
|
||
case '~':
|
||
case '$': /* only used in assembler */
|
||
case '@': /* dito */
|
||
tok = c;
|
||
p++;
|
||
break;
|
||
default:
|
||
error("unrecognized character \\x%02x", c);
|
||
break;
|
||
}
|
||
file->buf_ptr = p;
|
||
tok_flags = 0;
|
||
#if defined(PARSE_DEBUG)
|
||
printf("token = %s\n", get_tok_str(tok, &tokc));
|
||
#endif
|
||
}
|
||
|
||
/* return next token without macro substitution. Can read input from
|
||
macro_ptr buffer */
|
||
static void next_nomacro(void)
|
||
{
|
||
if (macro_ptr) {
|
||
redo:
|
||
tok = *macro_ptr;
|
||
if (tok) {
|
||
TOK_GET(tok, macro_ptr, tokc);
|
||
if (tok == TOK_LINENUM) {
|
||
file->line_num = tokc.i;
|
||
goto redo;
|
||
}
|
||
}
|
||
} else {
|
||
next_nomacro1();
|
||
}
|
||
}
|
||
|
||
/* substitute args in macro_str and return allocated string */
|
||
static int *macro_arg_subst(Sym **nested_list, int *macro_str, Sym *args)
|
||
{
|
||
int *st, last_tok, t, notfirst;
|
||
Sym *s;
|
||
CValue cval;
|
||
TokenString str;
|
||
CString cstr;
|
||
|
||
tok_str_new(&str);
|
||
last_tok = 0;
|
||
while(1) {
|
||
TOK_GET(t, macro_str, cval);
|
||
if (!t)
|
||
break;
|
||
if (t == '#') {
|
||
/* stringize */
|
||
TOK_GET(t, macro_str, cval);
|
||
if (!t)
|
||
break;
|
||
s = sym_find2(args, t);
|
||
if (s) {
|
||
cstr_new(&cstr);
|
||
st = (int *)s->c;
|
||
notfirst = 0;
|
||
while (*st) {
|
||
if (notfirst)
|
||
cstr_ccat(&cstr, ' ');
|
||
TOK_GET(t, st, cval);
|
||
cstr_cat(&cstr, get_tok_str(t, &cval));
|
||
notfirst = 1;
|
||
}
|
||
cstr_ccat(&cstr, '\0');
|
||
#ifdef PP_DEBUG
|
||
printf("stringize: %s\n", (char *)cstr.data);
|
||
#endif
|
||
/* add string */
|
||
cval.cstr = &cstr;
|
||
tok_str_add2(&str, TOK_STR, &cval);
|
||
cstr_free(&cstr);
|
||
} else {
|
||
tok_str_add2(&str, t, &cval);
|
||
}
|
||
} else if (t >= TOK_IDENT) {
|
||
s = sym_find2(args, t);
|
||
if (s) {
|
||
st = (int *)s->c;
|
||
/* if '##' is present before or after, no arg substitution */
|
||
if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) {
|
||
/* special case for var arg macros : ## eats the
|
||
',' if empty VA_ARGS variable. */
|
||
/* XXX: test of the ',' is not 100%
|
||
reliable. should fix it to avoid security
|
||
problems */
|
||
if (gnu_ext && s->type.t &&
|
||
last_tok == TOK_TWOSHARPS &&
|
||
str.len >= 2 && str.str[str.len - 2] == ',') {
|
||
if (*st == 0) {
|
||
/* suppress ',' '##' */
|
||
str.len -= 2;
|
||
} else {
|
||
/* suppress '##' and add variable */
|
||
str.len--;
|
||
goto add_var;
|
||
}
|
||
} else {
|
||
int t1;
|
||
add_var:
|
||
for(;;) {
|
||
TOK_GET(t1, st, cval);
|
||
if (!t1)
|
||
break;
|
||
tok_str_add2(&str, t1, &cval);
|
||
}
|
||
}
|
||
} else {
|
||
/* NOTE: the stream cannot be read when macro
|
||
substituing an argument */
|
||
macro_subst(&str, nested_list, st, NULL);
|
||
}
|
||
} else {
|
||
tok_str_add(&str, t);
|
||
}
|
||
} else {
|
||
tok_str_add2(&str, t, &cval);
|
||
}
|
||
last_tok = t;
|
||
}
|
||
tok_str_add(&str, 0);
|
||
return str.str;
|
||
}
|
||
|
||
static char const ab_month_name[12][4] =
|
||
{
|
||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||
};
|
||
|
||
/* do macro substitution of current token with macro 's' and add
|
||
result to (tok_str,tok_len). 'nested_list' is the list of all
|
||
macros we got inside to avoid recursing. Return non zero if no
|
||
substitution needs to be done */
|
||
static int macro_subst_tok(TokenString *tok_str,
|
||
Sym **nested_list, Sym *s, struct macro_level **can_read_stream)
|
||
{
|
||
Sym *args, *sa, *sa1;
|
||
int mstr_allocated, parlevel, *mstr, t, t1;
|
||
TokenString str;
|
||
char *cstrval;
|
||
CValue cval;
|
||
CString cstr;
|
||
char buf[32];
|
||
|
||
/* if symbol is a macro, prepare substitution */
|
||
/* special macros */
|
||
if (tok == TOK___LINE__) {
|
||
snprintf(buf, sizeof(buf), "%d", file->line_num);
|
||
cstrval = buf;
|
||
t1 = TOK_PPNUM;
|
||
goto add_cstr1;
|
||
} else if (tok == TOK___FILE__) {
|
||
cstrval = file->filename;
|
||
goto add_cstr;
|
||
} else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
|
||
time_t ti;
|
||
struct tm *tm;
|
||
|
||
time(&ti);
|
||
tm = localtime(&ti);
|
||
if (tok == TOK___DATE__) {
|
||
snprintf(buf, sizeof(buf), "%s %2d %d",
|
||
ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
|
||
} else {
|
||
snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
|
||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||
}
|
||
cstrval = buf;
|
||
add_cstr:
|
||
t1 = TOK_STR;
|
||
add_cstr1:
|
||
cstr_new(&cstr);
|
||
cstr_cat(&cstr, cstrval);
|
||
cstr_ccat(&cstr, '\0');
|
||
cval.cstr = &cstr;
|
||
tok_str_add2(tok_str, t1, &cval);
|
||
cstr_free(&cstr);
|
||
} else {
|
||
mstr = (int *)s->c;
|
||
mstr_allocated = 0;
|
||
if (s->type.t == MACRO_FUNC) {
|
||
/* NOTE: we do not use next_nomacro to avoid eating the
|
||
next token. XXX: find better solution */
|
||
redo:
|
||
if (macro_ptr) {
|
||
t = *macro_ptr;
|
||
if (t == 0 && can_read_stream) {
|
||
/* end of macro stream: we must look at the token
|
||
after in the file */
|
||
struct macro_level *ml = *can_read_stream;
|
||
macro_ptr = NULL;
|
||
if (ml)
|
||
{
|
||
macro_ptr = ml->p;
|
||
ml->p = NULL;
|
||
*can_read_stream = ml -> prev;
|
||
}
|
||
goto redo;
|
||
}
|
||
} else {
|
||
/* XXX: incorrect with comments */
|
||
ch = file->buf_ptr[0];
|
||
while (is_space(ch) || ch == '\n')
|
||
cinp();
|
||
t = ch;
|
||
}
|
||
if (t != '(') /* no macro subst */
|
||
return -1;
|
||
|
||
/* argument macro */
|
||
next_nomacro();
|
||
next_nomacro();
|
||
args = NULL;
|
||
sa = s->next;
|
||
/* NOTE: empty args are allowed, except if no args */
|
||
for(;;) {
|
||
/* handle '()' case */
|
||
if (!args && !sa && tok == ')')
|
||
break;
|
||
if (!sa)
|
||
error("macro '%s' used with too many args",
|
||
get_tok_str(s->v, 0));
|
||
tok_str_new(&str);
|
||
parlevel = 0;
|
||
/* NOTE: non zero sa->t indicates VA_ARGS */
|
||
while ((parlevel > 0 ||
|
||
(tok != ')' &&
|
||
(tok != ',' || sa->type.t))) &&
|
||
tok != -1) {
|
||
if (tok == '(')
|
||
parlevel++;
|
||
else if (tok == ')')
|
||
parlevel--;
|
||
tok_str_add2(&str, tok, &tokc);
|
||
next_nomacro();
|
||
}
|
||
tok_str_add(&str, 0);
|
||
sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, (long)str.str);
|
||
sa = sa->next;
|
||
if (tok == ')') {
|
||
/* special case for gcc var args: add an empty
|
||
var arg argument if it is omitted */
|
||
if (sa && sa->type.t && gnu_ext)
|
||
continue;
|
||
else
|
||
break;
|
||
}
|
||
if (tok != ',')
|
||
expect(",");
|
||
next_nomacro();
|
||
}
|
||
if (sa) {
|
||
error("macro '%s' used with too few args",
|
||
get_tok_str(s->v, 0));
|
||
}
|
||
|
||
/* now subst each arg */
|
||
mstr = macro_arg_subst(nested_list, mstr, args);
|
||
/* free memory */
|
||
sa = args;
|
||
while (sa) {
|
||
sa1 = sa->prev;
|
||
tok_str_free((int *)sa->c);
|
||
sym_free(sa);
|
||
sa = sa1;
|
||
}
|
||
mstr_allocated = 1;
|
||
}
|
||
sym_push2(nested_list, s->v, 0, 0);
|
||
macro_subst(tok_str, nested_list, mstr, can_read_stream);
|
||
/* pop nested defined symbol */
|
||
sa1 = *nested_list;
|
||
*nested_list = sa1->prev;
|
||
sym_free(sa1);
|
||
if (mstr_allocated)
|
||
tok_str_free(mstr);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
|
||
return the resulting string (which must be freed). */
|
||
static /*inline*/ int *macro_twosharps(const int *macro_str)
|
||
{
|
||
TokenSym *ts;
|
||
const int *macro_ptr1, *start_macro_ptr, *ptr, *saved_macro_ptr;
|
||
int t;
|
||
const char *p1, *p2;
|
||
CValue cval;
|
||
TokenString macro_str1;
|
||
CString cstr;
|
||
memset(&cval, 0, sizeof(cval));
|
||
start_macro_ptr = macro_str;
|
||
/* we search the first '##' */
|
||
for(;;) {
|
||
macro_ptr1 = macro_str;
|
||
TOK_GET(t, macro_str, cval);
|
||
/* nothing more to do if end of string */
|
||
if (t == 0)
|
||
return NULL;
|
||
if (*macro_str == TOK_TWOSHARPS)
|
||
break;
|
||
}
|
||
|
||
/* we saw '##', so we need more processing to handle it */
|
||
cstr_new(&cstr);
|
||
tok_str_new(¯o_str1);
|
||
tok = t;
|
||
tokc = cval;
|
||
|
||
/* add all tokens seen so far */
|
||
for(ptr = start_macro_ptr; ptr < macro_ptr1;) {
|
||
TOK_GET(t, ptr, cval);
|
||
tok_str_add2(¯o_str1, t, &cval);
|
||
}
|
||
saved_macro_ptr = macro_ptr;
|
||
/* XXX: get rid of the use of macro_ptr here */
|
||
macro_ptr = (int *)macro_str;
|
||
for(;;) {
|
||
while (*macro_ptr == TOK_TWOSHARPS) {
|
||
macro_ptr++;
|
||
macro_ptr1 = macro_ptr;
|
||
t = *macro_ptr;
|
||
if (t) {
|
||
TOK_GET(t, macro_ptr, cval);
|
||
/* We concatenate the two tokens if we have an
|
||
identifier or a preprocessing number */
|
||
cstr_reset(&cstr);
|
||
p1 = get_tok_str(tok, &tokc);
|
||
cstr_cat(&cstr, p1);
|
||
p2 = get_tok_str(t, &cval);
|
||
cstr_cat(&cstr, p2);
|
||
cstr_ccat(&cstr, '\0');
|
||
|
||
if ((tok >= TOK_IDENT || tok == TOK_PPNUM) &&
|
||
(t >= TOK_IDENT || t == TOK_PPNUM)) {
|
||
if (tok == TOK_PPNUM) {
|
||
/* if number, then create a number token */
|
||
/* NOTE: no need to allocate because
|
||
tok_str_add2() does it */
|
||
tokc.cstr = &cstr;
|
||
} else {
|
||
/* if identifier, we must do a test to
|
||
validate we have a correct identifier */
|
||
if (t == TOK_PPNUM) {
|
||
const char *p;
|
||
int c;
|
||
|
||
p = p2;
|
||
for(;;) {
|
||
c = *p;
|
||
if (c == '\0')
|
||
break;
|
||
p++;
|
||
if (!isnum(c) && !isid(c))
|
||
goto error_pasting;
|
||
}
|
||
}
|
||
ts = tok_alloc(cstr.data, strlen(cstr.data));
|
||
tok = ts->tok; /* modify current token */
|
||
}
|
||
} else {
|
||
const char *str = cstr.data;
|
||
const unsigned char *q;
|
||
|
||
/* we look for a valid token */
|
||
/* XXX: do more extensive checks */
|
||
if (!strcmp(str, ">>=")) {
|
||
tok = TOK_A_SAR;
|
||
} else if (!strcmp(str, "<<=")) {
|
||
tok = TOK_A_SHL;
|
||
} else if (strlen(str) == 2) {
|
||
/* search in two bytes table */
|
||
q = tok_two_chars;
|
||
for(;;) {
|
||
if (!*q)
|
||
goto error_pasting;
|
||
if (q[0] == str[0] && q[1] == str[1])
|
||
break;
|
||
q += 3;
|
||
}
|
||
tok = q[2];
|
||
} else {
|
||
error_pasting:
|
||
/* NOTE: because get_tok_str use a static buffer,
|
||
we must save it */
|
||
cstr_reset(&cstr);
|
||
p1 = get_tok_str(tok, &tokc);
|
||
cstr_cat(&cstr, p1);
|
||
cstr_ccat(&cstr, '\0');
|
||
p2 = get_tok_str(t, &cval);
|
||
warning("pasting \"%s\" and \"%s\" does not give a valid preprocessing token", cstr.data, p2);
|
||
/* cannot merge tokens: just add them separately */
|
||
tok_str_add2(¯o_str1, tok, &tokc);
|
||
/* XXX: free associated memory ? */
|
||
tok = t;
|
||
tokc = cval;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
tok_str_add2(¯o_str1, tok, &tokc);
|
||
next_nomacro();
|
||
if (tok == 0)
|
||
break;
|
||
}
|
||
macro_ptr = (int *)saved_macro_ptr;
|
||
cstr_free(&cstr);
|
||
tok_str_add(¯o_str1, 0);
|
||
return macro_str1.str;
|
||
}
|
||
|
||
|
||
/* do macro substitution of macro_str and add result to
|
||
(tok_str,tok_len). 'nested_list' is the list of all macros we got
|
||
inside to avoid recursing. */
|
||
static void macro_subst(TokenString *tok_str, Sym **nested_list,
|
||
const int *macro_str, struct macro_level ** can_read_stream)
|
||
{
|
||
Sym *s;
|
||
int *macro_str1;
|
||
const int *ptr;
|
||
int t, ret;
|
||
CValue cval;
|
||
struct macro_level ml;
|
||
|
||
/* first scan for '##' operator handling */
|
||
ptr = macro_str;
|
||
macro_str1 = macro_twosharps(ptr);
|
||
if (macro_str1)
|
||
ptr = macro_str1;
|
||
while (1) {
|
||
/* NOTE: ptr == NULL can only happen if tokens are read from
|
||
file stream due to a macro function call */
|
||
if (ptr == NULL)
|
||
break;
|
||
TOK_GET(t, ptr, cval);
|
||
if (t == 0)
|
||
break;
|
||
s = define_find(t);
|
||
if (s != NULL) {
|
||
/* if nested substitution, do nothing */
|
||
if (sym_find2(*nested_list, t))
|
||
goto no_subst;
|
||
ml.p = macro_ptr;
|
||
if (can_read_stream)
|
||
ml.prev = *can_read_stream, *can_read_stream = &ml;
|
||
macro_ptr = (int *)ptr;
|
||
tok = t;
|
||
ret = macro_subst_tok(tok_str, nested_list, s, can_read_stream);
|
||
ptr = (int *)macro_ptr;
|
||
macro_ptr = ml.p;
|
||
if (can_read_stream && *can_read_stream == &ml)
|
||
*can_read_stream = ml.prev;
|
||
if (ret != 0)
|
||
goto no_subst;
|
||
} else {
|
||
no_subst:
|
||
tok_str_add2(tok_str, t, &cval);
|
||
}
|
||
}
|
||
if (macro_str1)
|
||
tok_str_free(macro_str1);
|
||
}
|
||
|
||
/* return next token with macro substitution */
|
||
static void next(void)
|
||
{
|
||
Sym *nested_list, *s;
|
||
TokenString str;
|
||
struct macro_level *ml;
|
||
|
||
redo:
|
||
next_nomacro();
|
||
if (!macro_ptr) {
|
||
/* if not reading from macro substituted string, then try
|
||
to substitute macros */
|
||
if (tok >= TOK_IDENT &&
|
||
(parse_flags & PARSE_FLAG_PREPROCESS)) {
|
||
s = define_find(tok);
|
||
if (s) {
|
||
/* we have a macro: we try to substitute */
|
||
tok_str_new(&str);
|
||
nested_list = NULL;
|
||
ml = NULL;
|
||
if (macro_subst_tok(&str, &nested_list, s, &ml) == 0) {
|
||
/* substitution done, NOTE: maybe empty */
|
||
tok_str_add(&str, 0);
|
||
macro_ptr = str.str;
|
||
macro_ptr_allocated = str.str;
|
||
goto redo;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (tok == 0) {
|
||
/* end of macro or end of unget buffer */
|
||
if (unget_buffer_enabled) {
|
||
macro_ptr = unget_saved_macro_ptr;
|
||
unget_buffer_enabled = 0;
|
||
} else {
|
||
/* end of macro string: free it */
|
||
tok_str_free(macro_ptr_allocated);
|
||
macro_ptr = NULL;
|
||
}
|
||
goto redo;
|
||
}
|
||
}
|
||
|
||
/* convert preprocessor tokens into C tokens */
|
||
if (tok == TOK_PPNUM &&
|
||
(parse_flags & PARSE_FLAG_TOK_NUM)) {
|
||
parse_number((char *)tokc.cstr->data);
|
||
}
|
||
}
|
||
|
||
/* push back current token and set current token to 'last_tok'. Only
|
||
identifier case handled for labels. */
|
||
static inline void unget_tok(int last_tok)
|
||
{
|
||
int i, n;
|
||
int *q;
|
||
unget_saved_macro_ptr = macro_ptr;
|
||
unget_buffer_enabled = 1;
|
||
q = unget_saved_buffer;
|
||
macro_ptr = q;
|
||
*q++ = tok;
|
||
n = tok_ext_size(tok) - 1;
|
||
for(i=0;i<n;i++)
|
||
*q++ = tokc.tab[i];
|
||
*q = 0; /* end of token string */
|
||
tok = last_tok;
|
||
}
|
||
|
||
|
||
void swap(int *p, int *q)
|
||
{
|
||
int t;
|
||
t = *p;
|
||
*p = *q;
|
||
*q = t;
|
||
}
|
||
|
||
void vsetc(CType *type, int r, CValue *vc)
|
||
{
|
||
int v;
|
||
|
||
if (vtop >= vstack + (VSTACK_SIZE - 1))
|
||
error("memory full");
|
||
/* cannot let cpu flags if other instruction are generated. Also
|
||
avoid leaving VT_JMP anywhere except on the top of the stack
|
||
because it would complicate the code generator. */
|
||
if (vtop >= vstack) {
|
||
v = vtop->r & VT_VALMASK;
|
||
if (v == VT_CMP || (v & ~1) == VT_JMP)
|
||
gv(RC_INT);
|
||
}
|
||
vtop++;
|
||
vtop->type = *type;
|
||
vtop->r = r;
|
||
vtop->r2 = VT_CONST;
|
||
vtop->c = *vc;
|
||
}
|
||
|
||
/* push integer constant */
|
||
void vpushi(int v)
|
||
{
|
||
CValue cval;
|
||
cval.i = v;
|
||
vsetc(&int_type, VT_CONST, &cval);
|
||
}
|
||
|
||
/* Return a static symbol pointing to a section */
|
||
static Sym *get_sym_ref(CType *type, Section *sec,
|
||
unsigned long offset, unsigned long size)
|
||
{
|
||
int v;
|
||
Sym *sym;
|
||
|
||
v = anon_sym++;
|
||
sym = global_identifier_push(v, type->t | VT_STATIC, 0);
|
||
sym->type.ref = type->ref;
|
||
sym->r = VT_CONST | VT_SYM;
|
||
put_extern_sym(sym, sec, offset, size);
|
||
return sym;
|
||
}
|
||
|
||
/* push a reference to a section offset by adding a dummy symbol */
|
||
static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
|
||
{
|
||
CValue cval;
|
||
|
||
cval.ul = 0;
|
||
vsetc(type, VT_CONST | VT_SYM, &cval);
|
||
vtop->sym = get_sym_ref(type, sec, offset, size);
|
||
}
|
||
|
||
/* define a new external reference to a symbol 'v' of type 'u' */
|
||
static Sym *external_global_sym(int v, CType *type, int r)
|
||
{
|
||
Sym *s;
|
||
|
||
s = sym_find(v);
|
||
if (!s) {
|
||
/* push forward reference */
|
||
s = global_identifier_push(v, type->t | VT_EXTERN, 0);
|
||
s->type.ref = type->ref;
|
||
s->r = r | VT_CONST | VT_SYM;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/* define a new external reference to a symbol 'v' of type 'u' */
|
||
static Sym *external_sym(int v, CType *type, int r)
|
||
{
|
||
Sym *s;
|
||
|
||
s = sym_find(v);
|
||
if (!s) {
|
||
/* push forward reference */
|
||
s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
|
||
s->type.t |= VT_EXTERN;
|
||
} else {
|
||
if (!is_compatible_types(&s->type, type))
|
||
error("incompatible types for redefinition of '%s'",
|
||
get_tok_str(v, NULL));
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/* push a reference to global symbol v */
|
||
static void vpush_global_sym(CType *type, int v)
|
||
{
|
||
Sym *sym;
|
||
CValue cval;
|
||
|
||
sym = external_global_sym(v, type, 0);
|
||
cval.ul = 0;
|
||
vsetc(type, VT_CONST | VT_SYM, &cval);
|
||
vtop->sym = sym;
|
||
}
|
||
|
||
void vset(CType *type, int r, int v)
|
||
{
|
||
CValue cval;
|
||
|
||
cval.i = v;
|
||
vsetc(type, r, &cval);
|
||
}
|
||
|
||
void vseti(int r, int v)
|
||
{
|
||
CType type;
|
||
type.t = VT_INT;
|
||
vset(&type, r, v);
|
||
}
|
||
|
||
void vswap(void)
|
||
{
|
||
SValue tmp;
|
||
|
||
tmp = vtop[0];
|
||
vtop[0] = vtop[-1];
|
||
vtop[-1] = tmp;
|
||
}
|
||
|
||
void vpushv(SValue *v)
|
||
{
|
||
if (vtop >= vstack + (VSTACK_SIZE - 1))
|
||
error("memory full");
|
||
vtop++;
|
||
*vtop = *v;
|
||
}
|
||
|
||
void vdup(void)
|
||
{
|
||
vpushv(vtop);
|
||
}
|
||
|
||
/* save r to the memory stack, and mark it as being free */
|
||
void save_reg(int r)
|
||
{
|
||
int l, saved, size, align;
|
||
SValue *p, sv;
|
||
CType *type;
|
||
|
||
/* modify all stack values */
|
||
saved = 0;
|
||
l = 0;
|
||
for(p=vstack;p<=vtop;p++) {
|
||
if ((p->r & VT_VALMASK) == r ||
|
||
(p->r2 & VT_VALMASK) == r) {
|
||
/* must save value on stack if not already done */
|
||
if (!saved) {
|
||
/* NOTE: must reload 'r' because r might be equal to r2 */
|
||
r = p->r & VT_VALMASK;
|
||
/* store register in the stack */
|
||
type = &p->type;
|
||
if ((p->r & VT_LVAL) ||
|
||
(!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
|
||
type = &int_type;
|
||
size = type_size(type, &align);
|
||
loc = (loc - size) & -align;
|
||
sv.type.t = type->t;
|
||
sv.r = VT_LOCAL | VT_LVAL;
|
||
sv.c.ul = loc;
|
||
store(r, &sv);
|
||
#ifdef TCC_TARGET_I386
|
||
/* x86 specific: need to pop fp register ST0 if saved */
|
||
if (r == TREG_ST0) {
|
||
o(0xd9dd); /* fstp %st(1) */
|
||
}
|
||
#endif
|
||
/* special long long case */
|
||
if ((type->t & VT_BTYPE) == VT_LLONG) {
|
||
sv.c.ul += 4;
|
||
store(p->r2, &sv);
|
||
}
|
||
l = loc;
|
||
saved = 1;
|
||
}
|
||
/* mark that stack entry as being saved on the stack */
|
||
if (p->r & VT_LVAL) {
|
||
/* also clear the bounded flag because the
|
||
relocation address of the function was stored in
|
||
p->c.ul */
|
||
p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
|
||
} else {
|
||
p->r = lvalue_type(p->type.t) | VT_LOCAL;
|
||
}
|
||
p->r2 = VT_CONST;
|
||
p->c.ul = l;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* find a register of class 'rc2' with at most one reference on stack.
|
||
* If none, call get_reg(rc) */
|
||
int get_reg_ex(int rc, int rc2)
|
||
{
|
||
int r;
|
||
SValue *p;
|
||
|
||
for(r=0;r<NB_REGS;r++) {
|
||
if (reg_classes[r] & rc2) {
|
||
int n;
|
||
n=0;
|
||
for(p = vstack; p <= vtop; p++) {
|
||
if ((p->r & VT_VALMASK) == r ||
|
||
(p->r2 & VT_VALMASK) == r)
|
||
n++;
|
||
}
|
||
if (n <= 1)
|
||
return r;
|
||
}
|
||
}
|
||
return get_reg(rc);
|
||
}
|
||
|
||
/* find a free register of class 'rc'. If none, save one register */
|
||
int get_reg(int rc)
|
||
{
|
||
int r;
|
||
SValue *p;
|
||
|
||
/* find a free register */
|
||
for(r=0;r<NB_REGS;r++) {
|
||
if (reg_classes[r] & rc) {
|
||
for(p=vstack;p<=vtop;p++) {
|
||
if ((p->r & VT_VALMASK) == r ||
|
||
(p->r2 & VT_VALMASK) == r)
|
||
goto notfound;
|
||
}
|
||
return r;
|
||
}
|
||
notfound: ;
|
||
}
|
||
|
||
/* no register left : free the first one on the stack (VERY
|
||
IMPORTANT to start from the bottom to ensure that we don't
|
||
spill registers used in gen_opi()) */
|
||
for(p=vstack;p<=vtop;p++) {
|
||
r = p->r & VT_VALMASK;
|
||
if (r < VT_CONST && (reg_classes[r] & rc))
|
||
goto save_found;
|
||
/* also look at second register (if long long) */
|
||
r = p->r2 & VT_VALMASK;
|
||
if (r < VT_CONST && (reg_classes[r] & rc)) {
|
||
save_found:
|
||
save_reg(r);
|
||
return r;
|
||
}
|
||
}
|
||
/* Should never comes here */
|
||
return -1;
|
||
}
|
||
|
||
/* save registers up to (vtop - n) stack entry */
|
||
void save_regs(int n)
|
||
{
|
||
int r;
|
||
SValue *p, *p1;
|
||
p1 = vtop - n;
|
||
for(p = vstack;p <= p1; p++) {
|
||
r = p->r & VT_VALMASK;
|
||
if (r < VT_CONST) {
|
||
save_reg(r);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* move register 's' to 'r', and flush previous value of r to memory
|
||
if needed */
|
||
void move_reg(int r, int s)
|
||
{
|
||
SValue sv;
|
||
|
||
if (r != s) {
|
||
save_reg(r);
|
||
sv.type.t = VT_INT;
|
||
sv.r = s;
|
||
sv.c.ul = 0;
|
||
load(r, &sv);
|
||
}
|
||
}
|
||
|
||
/* get address of vtop (vtop MUST BE an lvalue) */
|
||
void gaddrof(void)
|
||
{
|
||
vtop->r &= ~VT_LVAL;
|
||
/* tricky: if saved lvalue, then we can go back to lvalue */
|
||
if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
|
||
vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;
|
||
}
|
||
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
/* generate lvalue bound code */
|
||
void gbound(void)
|
||
{
|
||
int lval_type;
|
||
CType type1;
|
||
|
||
vtop->r &= ~VT_MUSTBOUND;
|
||
/* if lvalue, then use checking code before dereferencing */
|
||
if (vtop->r & VT_LVAL) {
|
||
/* if not VT_BOUNDED value, then make one */
|
||
if (!(vtop->r & VT_BOUNDED)) {
|
||
lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
|
||
/* must save type because we must set it to int to get pointer */
|
||
type1 = vtop->type;
|
||
vtop->type.t = VT_INT;
|
||
gaddrof();
|
||
vpushi(0);
|
||
gen_bounded_ptr_add();
|
||
vtop->r |= lval_type;
|
||
vtop->type = type1;
|
||
}
|
||
/* then check for dereferencing */
|
||
gen_bounded_ptr_deref();
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* store vtop a register belonging to class 'rc'. lvalues are
|
||
converted to values. Cannot be used if cannot be converted to
|
||
register value (such as structures). */
|
||
int gv(int rc)
|
||
{
|
||
int r, r2, rc2, bit_pos, bit_size, size, align, i;
|
||
unsigned long long ll;
|
||
|
||
/* NOTE: get_reg can modify vstack[] */
|
||
if (vtop->type.t & VT_BITFIELD) {
|
||
bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
|
||
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
|
||
/* remove bit field info to avoid loops */
|
||
vtop->type.t &= ~(VT_BITFIELD | (~0U << VT_STRUCT_SHIFT));
|
||
/* generate shifts */
|
||
vpushi(32 - (bit_pos + bit_size));
|
||
gen_op(TOK_SHL);
|
||
vpushi(32 - bit_size);
|
||
/* NOTE: transformed to SHR if unsigned */
|
||
gen_op(TOK_SAR);
|
||
r = gv(rc);
|
||
} else {
|
||
if (is_float(vtop->type.t) &&
|
||
(vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||
Sym *sym;
|
||
int *ptr;
|
||
unsigned long offset;
|
||
|
||
/* XXX: unify with initializers handling ? */
|
||
/* CPUs usually cannot use float constants, so we store them
|
||
generically in data segment */
|
||
size = type_size(&vtop->type, &align);
|
||
offset = (data_section->data_offset + align - 1) & -align;
|
||
data_section->data_offset = offset;
|
||
/* XXX: not portable yet */
|
||
ptr = section_ptr_add(data_section, size);
|
||
size = size >> 2;
|
||
for(i=0;i<size;i++)
|
||
ptr[i] = vtop->c.tab[i];
|
||
sym = get_sym_ref(&vtop->type, data_section, offset, size << 2);
|
||
vtop->r |= VT_LVAL | VT_SYM;
|
||
vtop->sym = sym;
|
||
vtop->c.ul = 0;
|
||
}
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
if (vtop->r & VT_MUSTBOUND)
|
||
gbound();
|
||
#endif
|
||
|
||
r = vtop->r & VT_VALMASK;
|
||
/* need to reload if:
|
||
- constant
|
||
- lvalue (need to dereference pointer)
|
||
- already a register, but not in the right class */
|
||
if (r >= VT_CONST ||
|
||
(vtop->r & VT_LVAL) ||
|
||
!(reg_classes[r] & rc) ||
|
||
((vtop->type.t & VT_BTYPE) == VT_LLONG &&
|
||
!(reg_classes[vtop->r2] & rc))) {
|
||
r = get_reg(rc);
|
||
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
|
||
/* two register type load : expand to two words
|
||
temporarily */
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||
/* load constant */
|
||
ll = vtop->c.ull;
|
||
vtop->c.ui = ll; /* first word */
|
||
load(r, vtop);
|
||
vtop->r = r; /* save register value */
|
||
vpushi(ll >> 32); /* second word */
|
||
} else if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */
|
||
(vtop->r & VT_LVAL)) {
|
||
/* We do not want to modifier the long long
|
||
pointer here, so the safest (and less
|
||
efficient) is to save all the other registers
|
||
in the stack. XXX: totally inefficient. */
|
||
save_regs(1);
|
||
/* load from memory */
|
||
load(r, vtop);
|
||
vdup();
|
||
vtop[-1].r = r; /* save register value */
|
||
/* increment pointer to get second word */
|
||
vtop->type.t = VT_INT;
|
||
gaddrof();
|
||
vpushi(4);
|
||
gen_op('+');
|
||
vtop->r |= VT_LVAL;
|
||
} else {
|
||
/* move registers */
|
||
load(r, vtop);
|
||
vdup();
|
||
vtop[-1].r = r; /* save register value */
|
||
vtop->r = vtop[-1].r2;
|
||
}
|
||
/* allocate second register */
|
||
rc2 = RC_INT;
|
||
if (rc == RC_IRET)
|
||
rc2 = RC_LRET;
|
||
r2 = get_reg(rc2);
|
||
load(r2, vtop);
|
||
vpop();
|
||
/* write second register */
|
||
vtop->r2 = r2;
|
||
} else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
|
||
int t1, t;
|
||
/* lvalue of scalar type : need to use lvalue type
|
||
because of possible cast */
|
||
t = vtop->type.t;
|
||
t1 = t;
|
||
/* compute memory access type */
|
||
if (vtop->r & VT_LVAL_BYTE)
|
||
t = VT_BYTE;
|
||
else if (vtop->r & VT_LVAL_SHORT)
|
||
t = VT_SHORT;
|
||
if (vtop->r & VT_LVAL_UNSIGNED)
|
||
t |= VT_UNSIGNED;
|
||
vtop->type.t = t;
|
||
load(r, vtop);
|
||
/* restore wanted type */
|
||
vtop->type.t = t1;
|
||
} else {
|
||
/* one register type load */
|
||
load(r, vtop);
|
||
}
|
||
}
|
||
vtop->r = r;
|
||
#ifdef TCC_TARGET_C67
|
||
/* uses register pairs for doubles */
|
||
if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
|
||
vtop->r2 = r+1;
|
||
#endif
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
|
||
void gv2(int rc1, int rc2)
|
||
{
|
||
int v;
|
||
|
||
/* generate more generic register first. But VT_JMP or VT_CMP
|
||
values must be generated first in all cases to avoid possible
|
||
reload errors */
|
||
v = vtop[0].r & VT_VALMASK;
|
||
if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
|
||
vswap();
|
||
gv(rc1);
|
||
vswap();
|
||
gv(rc2);
|
||
/* test if reload is needed for first register */
|
||
if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
|
||
vswap();
|
||
gv(rc1);
|
||
vswap();
|
||
}
|
||
} else {
|
||
gv(rc2);
|
||
vswap();
|
||
gv(rc1);
|
||
vswap();
|
||
/* test if reload is needed for first register */
|
||
if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
|
||
gv(rc2);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* expand long long on stack in two int registers */
|
||
void lexpand(void)
|
||
{
|
||
int u;
|
||
|
||
u = vtop->type.t & VT_UNSIGNED;
|
||
gv(RC_INT);
|
||
vdup();
|
||
vtop[0].r = vtop[-1].r2;
|
||
vtop[0].r2 = VT_CONST;
|
||
vtop[-1].r2 = VT_CONST;
|
||
vtop[0].type.t = VT_INT | u;
|
||
vtop[-1].type.t = VT_INT | u;
|
||
}
|
||
|
||
#ifdef TCC_TARGET_ARM
|
||
/* expand long long on stack */
|
||
void lexpand_nr(void)
|
||
{
|
||
int u,v;
|
||
|
||
u = vtop->type.t & VT_UNSIGNED;
|
||
vdup();
|
||
vtop->r2 = VT_CONST;
|
||
vtop->type.t = VT_INT | u;
|
||
v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
|
||
if (v == VT_CONST) {
|
||
vtop[-1].c.ui = vtop->c.ull;
|
||
vtop->c.ui = vtop->c.ull >> 32;
|
||
vtop->r = VT_CONST;
|
||
} else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
|
||
vtop->c.ui += 4;
|
||
vtop->r = vtop[-1].r;
|
||
} else if (v > VT_CONST) {
|
||
vtop--;
|
||
lexpand();
|
||
} else
|
||
vtop->r = vtop[-1].r2;
|
||
vtop[-1].r2 = VT_CONST;
|
||
vtop[-1].type.t = VT_INT | u;
|
||
}
|
||
#endif
|
||
|
||
/* build a long long from two ints */
|
||
void lbuild(int t)
|
||
{
|
||
gv2(RC_INT, RC_INT);
|
||
vtop[-1].r2 = vtop[0].r;
|
||
vtop[-1].type.t = t;
|
||
vpop();
|
||
}
|
||
|
||
/* rotate n first stack elements to the bottom
|
||
I1 ... In -> I2 ... In I1 [top is right]
|
||
*/
|
||
void vrotb(int n)
|
||
{
|
||
int i;
|
||
SValue tmp;
|
||
|
||
tmp = vtop[-n + 1];
|
||
for(i=-n+1;i!=0;i++)
|
||
vtop[i] = vtop[i+1];
|
||
vtop[0] = tmp;
|
||
}
|
||
|
||
/* rotate n first stack elements to the top
|
||
I1 ... In -> In I1 ... I(n-1) [top is right]
|
||
*/
|
||
void vrott(int n)
|
||
{
|
||
int i;
|
||
SValue tmp;
|
||
|
||
tmp = vtop[0];
|
||
for(i = 0;i < n - 1; i++)
|
||
vtop[-i] = vtop[-i - 1];
|
||
vtop[-n + 1] = tmp;
|
||
}
|
||
|
||
#ifdef TCC_TARGET_ARM
|
||
/* like vrott but in other direction
|
||
In ... I1 -> I(n-1) ... I1 In [top is right]
|
||
*/
|
||
void vnrott(int n)
|
||
{
|
||
int i;
|
||
SValue tmp;
|
||
|
||
tmp = vtop[-n + 1];
|
||
for(i = n - 1; i > 0; i--)
|
||
vtop[-i] = vtop[-i + 1];
|
||
vtop[0] = tmp;
|
||
}
|
||
#endif
|
||
|
||
/* pop stack value */
|
||
void vpop(void)
|
||
{
|
||
int v;
|
||
v = vtop->r & VT_VALMASK;
|
||
#ifdef TCC_TARGET_I386
|
||
/* for x86, we need to pop the FP stack */
|
||
if (v == TREG_ST0 && !nocode_wanted) {
|
||
o(0xd9dd); /* fstp %st(1) */
|
||
} else
|
||
#endif
|
||
if (v == VT_JMP || v == VT_JMPI) {
|
||
/* need to put correct jump if && or || without test */
|
||
gsym(vtop->c.ul);
|
||
}
|
||
vtop--;
|
||
}
|
||
|
||
/* convert stack entry to register and duplicate its value in another
|
||
register */
|
||
void gv_dup(void)
|
||
{
|
||
int rc, t, r, r1;
|
||
SValue sv;
|
||
|
||
t = vtop->type.t;
|
||
if ((t & VT_BTYPE) == VT_LLONG) {
|
||
lexpand();
|
||
gv_dup();
|
||
vswap();
|
||
vrotb(3);
|
||
gv_dup();
|
||
vrotb(4);
|
||
/* stack: H L L1 H1 */
|
||
lbuild(t);
|
||
vrotb(3);
|
||
vrotb(3);
|
||
vswap();
|
||
lbuild(t);
|
||
vswap();
|
||
} else {
|
||
/* duplicate value */
|
||
rc = RC_INT;
|
||
sv.type.t = VT_INT;
|
||
if (is_float(t)) {
|
||
rc = RC_FLOAT;
|
||
sv.type.t = t;
|
||
}
|
||
r = gv(rc);
|
||
r1 = get_reg(rc);
|
||
sv.r = r;
|
||
sv.c.ul = 0;
|
||
load(r1, &sv); /* move r to r1 */
|
||
vdup();
|
||
/* duplicates value */
|
||
vtop->r = r1;
|
||
}
|
||
}
|
||
|
||
/* generate CPU independent (unsigned) long long operations */
|
||
void gen_opl(int op)
|
||
{
|
||
int t, a, b, op1, c, i;
|
||
int func;
|
||
SValue tmp;
|
||
|
||
switch(op) {
|
||
case '/':
|
||
case TOK_PDIV:
|
||
func = TOK___divdi3;
|
||
goto gen_func;
|
||
case TOK_UDIV:
|
||
func = TOK___udivdi3;
|
||
goto gen_func;
|
||
case '%':
|
||
func = TOK___moddi3;
|
||
goto gen_func;
|
||
case TOK_UMOD:
|
||
func = TOK___umoddi3;
|
||
gen_func:
|
||
/* call generic long long function */
|
||
vpush_global_sym(&func_old_type, func);
|
||
vrott(3);
|
||
gfunc_call(2);
|
||
vpushi(0);
|
||
vtop->r = REG_IRET;
|
||
vtop->r2 = REG_LRET;
|
||
break;
|
||
case '^':
|
||
case '&':
|
||
case '|':
|
||
case '*':
|
||
case '+':
|
||
case '-':
|
||
t = vtop->type.t;
|
||
vswap();
|
||
lexpand();
|
||
vrotb(3);
|
||
lexpand();
|
||
/* stack: L1 H1 L2 H2 */
|
||
tmp = vtop[0];
|
||
vtop[0] = vtop[-3];
|
||
vtop[-3] = tmp;
|
||
tmp = vtop[-2];
|
||
vtop[-2] = vtop[-3];
|
||
vtop[-3] = tmp;
|
||
vswap();
|
||
/* stack: H1 H2 L1 L2 */
|
||
if (op == '*') {
|
||
vpushv(vtop - 1);
|
||
vpushv(vtop - 1);
|
||
gen_op(TOK_UMULL);
|
||
lexpand();
|
||
/* stack: H1 H2 L1 L2 ML MH */
|
||
for(i=0;i<4;i++)
|
||
vrotb(6);
|
||
/* stack: ML MH H1 H2 L1 L2 */
|
||
tmp = vtop[0];
|
||
vtop[0] = vtop[-2];
|
||
vtop[-2] = tmp;
|
||
/* stack: ML MH H1 L2 H2 L1 */
|
||
gen_op('*');
|
||
vrotb(3);
|
||
vrotb(3);
|
||
gen_op('*');
|
||
/* stack: ML MH M1 M2 */
|
||
gen_op('+');
|
||
gen_op('+');
|
||
} else if (op == '+' || op == '-') {
|
||
/* XXX: add non carry method too (for MIPS or alpha) */
|
||
if (op == '+')
|
||
op1 = TOK_ADDC1;
|
||
else
|
||
op1 = TOK_SUBC1;
|
||
gen_op(op1);
|
||
/* stack: H1 H2 (L1 op L2) */
|
||
vrotb(3);
|
||
vrotb(3);
|
||
gen_op(op1 + 1); /* TOK_xxxC2 */
|
||
} else {
|
||
gen_op(op);
|
||
/* stack: H1 H2 (L1 op L2) */
|
||
vrotb(3);
|
||
vrotb(3);
|
||
/* stack: (L1 op L2) H1 H2 */
|
||
gen_op(op);
|
||
/* stack: (L1 op L2) (H1 op H2) */
|
||
}
|
||
/* stack: L H */
|
||
lbuild(t);
|
||
break;
|
||
case TOK_SAR:
|
||
case TOK_SHR:
|
||
case TOK_SHL:
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||
t = vtop[-1].type.t;
|
||
vswap();
|
||
lexpand();
|
||
vrotb(3);
|
||
/* stack: L H shift */
|
||
c = (int)vtop->c.i;
|
||
/* constant: simpler */
|
||
/* NOTE: all comments are for SHL. the other cases are
|
||
done by swaping words */
|
||
vpop();
|
||
if (op != TOK_SHL)
|
||
vswap();
|
||
if (c >= 32) {
|
||
/* stack: L H */
|
||
vpop();
|
||
if (c > 32) {
|
||
vpushi(c - 32);
|
||
gen_op(op);
|
||
}
|
||
if (op != TOK_SAR) {
|
||
vpushi(0);
|
||
} else {
|
||
gv_dup();
|
||
vpushi(31);
|
||
gen_op(TOK_SAR);
|
||
}
|
||
vswap();
|
||
} else {
|
||
vswap();
|
||
gv_dup();
|
||
/* stack: H L L */
|
||
vpushi(c);
|
||
gen_op(op);
|
||
vswap();
|
||
vpushi(32 - c);
|
||
if (op == TOK_SHL)
|
||
gen_op(TOK_SHR);
|
||
else
|
||
gen_op(TOK_SHL);
|
||
vrotb(3);
|
||
/* stack: L L H */
|
||
vpushi(c);
|
||
if (op == TOK_SHL)
|
||
gen_op(TOK_SHL);
|
||
else
|
||
gen_op(TOK_SHR);
|
||
gen_op('|');
|
||
}
|
||
if (op != TOK_SHL)
|
||
vswap();
|
||
lbuild(t);
|
||
} else {
|
||
/* XXX: should provide a faster fallback on x86 ? */
|
||
switch(op) {
|
||
case TOK_SAR:
|
||
func = TOK___sardi3;
|
||
goto gen_func;
|
||
case TOK_SHR:
|
||
func = TOK___shrdi3;
|
||
goto gen_func;
|
||
case TOK_SHL:
|
||
func = TOK___shldi3;
|
||
goto gen_func;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
/* compare operations */
|
||
t = vtop->type.t;
|
||
vswap();
|
||
lexpand();
|
||
vrotb(3);
|
||
lexpand();
|
||
/* stack: L1 H1 L2 H2 */
|
||
tmp = vtop[-1];
|
||
vtop[-1] = vtop[-2];
|
||
vtop[-2] = tmp;
|
||
/* stack: L1 L2 H1 H2 */
|
||
/* compare high */
|
||
op1 = op;
|
||
/* when values are equal, we need to compare low words. since
|
||
the jump is inverted, we invert the test too. */
|
||
if (op1 == TOK_LT)
|
||
op1 = TOK_LE;
|
||
else if (op1 == TOK_GT)
|
||
op1 = TOK_GE;
|
||
else if (op1 == TOK_ULT)
|
||
op1 = TOK_ULE;
|
||
else if (op1 == TOK_UGT)
|
||
op1 = TOK_UGE;
|
||
a = 0;
|
||
b = 0;
|
||
gen_op(op1);
|
||
if (op1 != TOK_NE) {
|
||
a = gtst(1, 0);
|
||
}
|
||
if (op != TOK_EQ) {
|
||
/* generate non equal test */
|
||
/* XXX: NOT PORTABLE yet */
|
||
if (a == 0) {
|
||
b = gtst(0, 0);
|
||
} else {
|
||
#if defined(TCC_TARGET_I386)
|
||
b = psym(0x850f, 0);
|
||
#elif defined(TCC_TARGET_ARM)
|
||
b = ind;
|
||
o(0x1A000000 | encbranch(ind, 0, 1));
|
||
#elif defined(TCC_TARGET_C67)
|
||
error("not implemented");
|
||
#else
|
||
#error not supported
|
||
#endif
|
||
}
|
||
}
|
||
/* compare low. Always unsigned */
|
||
op1 = op;
|
||
if (op1 == TOK_LT)
|
||
op1 = TOK_ULT;
|
||
else if (op1 == TOK_LE)
|
||
op1 = TOK_ULE;
|
||
else if (op1 == TOK_GT)
|
||
op1 = TOK_UGT;
|
||
else if (op1 == TOK_GE)
|
||
op1 = TOK_UGE;
|
||
gen_op(op1);
|
||
a = gtst(1, a);
|
||
gsym(b);
|
||
vseti(VT_JMPI, a);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* handle integer constant optimizations and various machine
|
||
independent opt */
|
||
void gen_opic(int op)
|
||
{
|
||
int fc, c1, c2, n;
|
||
SValue *v1, *v2;
|
||
|
||
v1 = vtop - 1;
|
||
v2 = vtop;
|
||
/* currently, we cannot do computations with forward symbols */
|
||
c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||
c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||
if (c1 && c2) {
|
||
fc = v2->c.i;
|
||
switch(op) {
|
||
case '+': v1->c.i += fc; break;
|
||
case '-': v1->c.i -= fc; break;
|
||
case '&': v1->c.i &= fc; break;
|
||
case '^': v1->c.i ^= fc; break;
|
||
case '|': v1->c.i |= fc; break;
|
||
case '*': v1->c.i *= fc; break;
|
||
|
||
case TOK_PDIV:
|
||
case '/':
|
||
case '%':
|
||
case TOK_UDIV:
|
||
case TOK_UMOD:
|
||
/* if division by zero, generate explicit division */
|
||
if (fc == 0) {
|
||
if (const_wanted)
|
||
error("division by zero in constant");
|
||
goto general_case;
|
||
}
|
||
switch(op) {
|
||
default: v1->c.i /= fc; break;
|
||
case '%': v1->c.i %= fc; break;
|
||
case TOK_UDIV: v1->c.i = (unsigned)v1->c.i / fc; break;
|
||
case TOK_UMOD: v1->c.i = (unsigned)v1->c.i % fc; break;
|
||
}
|
||
break;
|
||
case TOK_SHL: v1->c.i <<= fc; break;
|
||
case TOK_SHR: v1->c.i = (unsigned)v1->c.i >> fc; break;
|
||
case TOK_SAR: v1->c.i >>= fc; break;
|
||
/* tests */
|
||
case TOK_ULT: v1->c.i = (unsigned)v1->c.i < (unsigned)fc; break;
|
||
case TOK_UGE: v1->c.i = (unsigned)v1->c.i >= (unsigned)fc; break;
|
||
case TOK_EQ: v1->c.i = v1->c.i == fc; break;
|
||
case TOK_NE: v1->c.i = v1->c.i != fc; break;
|
||
case TOK_ULE: v1->c.i = (unsigned)v1->c.i <= (unsigned)fc; break;
|
||
case TOK_UGT: v1->c.i = (unsigned)v1->c.i > (unsigned)fc; break;
|
||
case TOK_LT: v1->c.i = v1->c.i < fc; break;
|
||
case TOK_GE: v1->c.i = v1->c.i >= fc; break;
|
||
case TOK_LE: v1->c.i = v1->c.i <= fc; break;
|
||
case TOK_GT: v1->c.i = v1->c.i > fc; break;
|
||
/* logical */
|
||
case TOK_LAND: v1->c.i = v1->c.i && fc; break;
|
||
case TOK_LOR: v1->c.i = v1->c.i || fc; break;
|
||
default:
|
||
goto general_case;
|
||
}
|
||
vtop--;
|
||
} else {
|
||
/* if commutative ops, put c2 as constant */
|
||
if (c1 && (op == '+' || op == '&' || op == '^' ||
|
||
op == '|' || op == '*')) {
|
||
vswap();
|
||
swap(&c1, &c2);
|
||
}
|
||
fc = vtop->c.i;
|
||
if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV ||
|
||
op == TOK_PDIV) &&
|
||
fc == 1) ||
|
||
((op == '+' || op == '-' || op == '|' || op == '^' ||
|
||
op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) &&
|
||
fc == 0) ||
|
||
(op == '&' &&
|
||
fc == -1))) {
|
||
/* nothing to do */
|
||
vtop--;
|
||
} else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
|
||
/* try to use shifts instead of muls or divs */
|
||
if (fc > 0 && (fc & (fc - 1)) == 0) {
|
||
n = -1;
|
||
while (fc) {
|
||
fc >>= 1;
|
||
n++;
|
||
}
|
||
vtop->c.i = n;
|
||
if (op == '*')
|
||
op = TOK_SHL;
|
||
else if (op == TOK_PDIV)
|
||
op = TOK_SAR;
|
||
else
|
||
op = TOK_SHR;
|
||
}
|
||
goto general_case;
|
||
} else if (c2 && (op == '+' || op == '-') &&
|
||
(vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) ==
|
||
(VT_CONST | VT_SYM)) {
|
||
/* symbol + constant case */
|
||
if (op == '-')
|
||
fc = -fc;
|
||
vtop--;
|
||
vtop->c.i += fc;
|
||
} else {
|
||
general_case:
|
||
if (!nocode_wanted) {
|
||
/* call low level op generator */
|
||
gen_opi(op);
|
||
} else {
|
||
vtop--;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* generate a floating point operation with constant propagation */
|
||
void gen_opif(int op)
|
||
{
|
||
int c1, c2;
|
||
SValue *v1, *v2;
|
||
long double f1, f2;
|
||
|
||
v1 = vtop - 1;
|
||
v2 = vtop;
|
||
/* currently, we cannot do computations with forward symbols */
|
||
c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||
c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||
if (c1 && c2) {
|
||
if (v1->type.t == VT_FLOAT) {
|
||
f1 = v1->c.f;
|
||
f2 = v2->c.f;
|
||
} else if (v1->type.t == VT_DOUBLE) {
|
||
f1 = v1->c.d;
|
||
f2 = v2->c.d;
|
||
} else {
|
||
f1 = v1->c.ld;
|
||
f2 = v2->c.ld;
|
||
}
|
||
|
||
/* NOTE: we only do constant propagation if finite number (not
|
||
NaN or infinity) (ANSI spec) */
|
||
if (!ieee_finite(f1) || !ieee_finite(f2))
|
||
goto general_case;
|
||
|
||
switch(op) {
|
||
case '+': f1 += f2; break;
|
||
case '-': f1 -= f2; break;
|
||
case '*': f1 *= f2; break;
|
||
case '/':
|
||
if (f2 == 0.0) {
|
||
if (const_wanted)
|
||
error("division by zero in constant");
|
||
goto general_case;
|
||
}
|
||
f1 /= f2;
|
||
break;
|
||
/* XXX: also handles tests ? */
|
||
default:
|
||
goto general_case;
|
||
}
|
||
/* XXX: overflow test ? */
|
||
if (v1->type.t == VT_FLOAT) {
|
||
v1->c.f = f1;
|
||
} else if (v1->type.t == VT_DOUBLE) {
|
||
v1->c.d = f1;
|
||
} else {
|
||
v1->c.ld = f1;
|
||
}
|
||
vtop--;
|
||
} else {
|
||
general_case:
|
||
if (!nocode_wanted) {
|
||
gen_opf(op);
|
||
} else {
|
||
vtop--;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* return the pointed type of t */
|
||
static inline CType *pointed_type(CType *type)
|
||
{
|
||
return &type->ref->type;
|
||
}
|
||
|
||
static int pointed_size(CType *type)
|
||
{
|
||
int align;
|
||
return type_size(pointed_type(type), &align);
|
||
}
|
||
|
||
static inline int is_null_pointer(SValue *p)
|
||
{
|
||
if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
|
||
return 0;
|
||
return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) ||
|
||
((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0);
|
||
}
|
||
|
||
static inline int is_integer_btype(int bt)
|
||
{
|
||
return (bt == VT_BYTE || bt == VT_SHORT ||
|
||
bt == VT_INT || bt == VT_LLONG);
|
||
}
|
||
|
||
/* check types for comparison or substraction of pointers */
|
||
static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
|
||
{
|
||
CType *type1, *type2, tmp_type1, tmp_type2;
|
||
int bt1, bt2;
|
||
|
||
/* null pointers are accepted for all comparisons as gcc */
|
||
if (is_null_pointer(p1) || is_null_pointer(p2))
|
||
return;
|
||
type1 = &p1->type;
|
||
type2 = &p2->type;
|
||
bt1 = type1->t & VT_BTYPE;
|
||
bt2 = type2->t & VT_BTYPE;
|
||
/* accept comparison between pointer and integer with a warning */
|
||
if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
|
||
warning("comparison between pointer and integer");
|
||
return;
|
||
}
|
||
|
||
/* both must be pointers or implicit function pointers */
|
||
if (bt1 == VT_PTR) {
|
||
type1 = pointed_type(type1);
|
||
} else if (bt1 != VT_FUNC)
|
||
goto invalid_operands;
|
||
|
||
if (bt2 == VT_PTR) {
|
||
type2 = pointed_type(type2);
|
||
} else if (bt2 != VT_FUNC) {
|
||
invalid_operands:
|
||
error("invalid operands to binary %s", get_tok_str(op, NULL));
|
||
}
|
||
if ((type1->t & VT_BTYPE) == VT_VOID ||
|
||
(type2->t & VT_BTYPE) == VT_VOID)
|
||
return;
|
||
tmp_type1 = *type1;
|
||
tmp_type2 = *type2;
|
||
tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
|
||
tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
|
||
if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
|
||
/* gcc-like error if '-' is used */
|
||
if (op == '-')
|
||
goto invalid_operands;
|
||
else
|
||
warning("comparison of distinct pointer types lacks a cast");
|
||
}
|
||
}
|
||
|
||
/* generic gen_op: handles types problems */
|
||
void gen_op(int op)
|
||
{
|
||
int u, t1, t2, bt1, bt2, t;
|
||
CType type1;
|
||
|
||
t1 = vtop[-1].type.t;
|
||
t2 = vtop[0].type.t;
|
||
bt1 = t1 & VT_BTYPE;
|
||
bt2 = t2 & VT_BTYPE;
|
||
|
||
if (bt1 == VT_PTR || bt2 == VT_PTR) {
|
||
/* at least one operand is a pointer */
|
||
/* relationnal op: must be both pointers */
|
||
if (op >= TOK_ULT && op <= TOK_GT) {
|
||
check_comparison_pointer_types(vtop - 1, vtop, op);
|
||
/* pointers are handled are unsigned */
|
||
t = VT_INT | VT_UNSIGNED;
|
||
goto std_op;
|
||
}
|
||
/* if both pointers, then it must be the '-' op */
|
||
if (bt1 == VT_PTR && bt2 == VT_PTR) {
|
||
if (op != '-')
|
||
error("cannot use pointers here");
|
||
check_comparison_pointer_types(vtop - 1, vtop, op);
|
||
/* XXX: check that types are compatible */
|
||
u = pointed_size(&vtop[-1].type);
|
||
gen_opic(op);
|
||
/* set to integer type */
|
||
vtop->type.t = VT_INT;
|
||
vpushi(u);
|
||
gen_op(TOK_PDIV);
|
||
} else {
|
||
/* exactly one pointer : must be '+' or '-'. */
|
||
if (op != '-' && op != '+')
|
||
error("cannot use pointers here");
|
||
/* Put pointer as first operand */
|
||
if (bt2 == VT_PTR) {
|
||
vswap();
|
||
swap(&t1, &t2);
|
||
}
|
||
type1 = vtop[-1].type;
|
||
/* XXX: cast to int ? (long long case) */
|
||
vpushi(pointed_size(&vtop[-1].type));
|
||
gen_op('*');
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
/* if evaluating constant expression, no code should be
|
||
generated, so no bound check */
|
||
if (do_bounds_check && !const_wanted) {
|
||
/* if bounded pointers, we generate a special code to
|
||
test bounds */
|
||
if (op == '-') {
|
||
vpushi(0);
|
||
vswap();
|
||
gen_op('-');
|
||
}
|
||
gen_bounded_ptr_add();
|
||
} else
|
||
#endif
|
||
{
|
||
gen_opic(op);
|
||
}
|
||
/* put again type if gen_opic() swaped operands */
|
||
vtop->type = type1;
|
||
}
|
||
} else if (is_float(bt1) || is_float(bt2)) {
|
||
/* compute bigger type and do implicit casts */
|
||
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
|
||
t = VT_LDOUBLE;
|
||
} else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
|
||
t = VT_DOUBLE;
|
||
} else {
|
||
t = VT_FLOAT;
|
||
}
|
||
/* floats can only be used for a few operations */
|
||
if (op != '+' && op != '-' && op != '*' && op != '/' &&
|
||
(op < TOK_ULT || op > TOK_GT))
|
||
error("invalid operands for binary operation");
|
||
goto std_op;
|
||
} else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
|
||
/* cast to biggest op */
|
||
t = VT_LLONG;
|
||
/* convert to unsigned if it does not fit in a long long */
|
||
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) ||
|
||
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
|
||
t |= VT_UNSIGNED;
|
||
goto std_op;
|
||
} else {
|
||
/* integer operations */
|
||
t = VT_INT;
|
||
/* convert to unsigned if it does not fit in an integer */
|
||
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) ||
|
||
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED))
|
||
t |= VT_UNSIGNED;
|
||
std_op:
|
||
/* XXX: currently, some unsigned operations are explicit, so
|
||
we modify them here */
|
||
if (t & VT_UNSIGNED) {
|
||
if (op == TOK_SAR)
|
||
op = TOK_SHR;
|
||
else if (op == '/')
|
||
op = TOK_UDIV;
|
||
else if (op == '%')
|
||
op = TOK_UMOD;
|
||
else if (op == TOK_LT)
|
||
op = TOK_ULT;
|
||
else if (op == TOK_GT)
|
||
op = TOK_UGT;
|
||
else if (op == TOK_LE)
|
||
op = TOK_ULE;
|
||
else if (op == TOK_GE)
|
||
op = TOK_UGE;
|
||
}
|
||
vswap();
|
||
type1.t = t;
|
||
gen_cast(&type1);
|
||
vswap();
|
||
/* special case for shifts and long long: we keep the shift as
|
||
an integer */
|
||
if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
|
||
type1.t = VT_INT;
|
||
gen_cast(&type1);
|
||
if (is_float(t))
|
||
gen_opif(op);
|
||
else if ((t & VT_BTYPE) == VT_LLONG)
|
||
gen_opl(op);
|
||
else
|
||
gen_opic(op);
|
||
if (op >= TOK_ULT && op <= TOK_GT) {
|
||
/* relationnal op: the result is an int */
|
||
vtop->type.t = VT_INT;
|
||
} else {
|
||
vtop->type.t = t;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* generic itof for unsigned long long case */
|
||
void gen_cvt_itof1(int t)
|
||
{
|
||
if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
|
||
(VT_LLONG | VT_UNSIGNED)) {
|
||
|
||
if (t == VT_FLOAT)
|
||
vpush_global_sym(&func_old_type, TOK___ulltof);
|
||
else if (t == VT_DOUBLE)
|
||
vpush_global_sym(&func_old_type, TOK___ulltod);
|
||
else
|
||
vpush_global_sym(&func_old_type, TOK___ulltold);
|
||
vrott(2);
|
||
gfunc_call(1);
|
||
vpushi(0);
|
||
vtop->r = REG_FRET;
|
||
} else {
|
||
gen_cvt_itof(t);
|
||
}
|
||
}
|
||
|
||
/* generic ftoi for unsigned long long case */
|
||
void gen_cvt_ftoi1(int t)
|
||
{
|
||
int st;
|
||
|
||
if (t == (VT_LLONG | VT_UNSIGNED)) {
|
||
/* not handled natively */
|
||
st = vtop->type.t & VT_BTYPE;
|
||
if (st == VT_FLOAT)
|
||
vpush_global_sym(&func_old_type, TOK___fixunssfdi);
|
||
else if (st == VT_DOUBLE)
|
||
vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
|
||
else
|
||
vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
|
||
vrott(2);
|
||
gfunc_call(1);
|
||
vpushi(0);
|
||
vtop->r = REG_IRET;
|
||
vtop->r2 = REG_LRET;
|
||
} else {
|
||
gen_cvt_ftoi(t);
|
||
}
|
||
}
|
||
|
||
/* force char or short cast */
|
||
void force_charshort_cast(int t)
|
||
{
|
||
int bits, dbt;
|
||
dbt = t & VT_BTYPE;
|
||
/* XXX: add optimization if lvalue : just change type and offset */
|
||
if (dbt == VT_BYTE)
|
||
bits = 8;
|
||
else
|
||
bits = 16;
|
||
if (t & VT_UNSIGNED) {
|
||
vpushi((1 << bits) - 1);
|
||
gen_op('&');
|
||
} else {
|
||
bits = 32 - bits;
|
||
vpushi(bits);
|
||
gen_op(TOK_SHL);
|
||
vpushi(bits);
|
||
gen_op(TOK_SAR);
|
||
}
|
||
}
|
||
|
||
/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
|
||
static void gen_cast(CType *type)
|
||
{
|
||
int sbt, dbt, sf, df, c;
|
||
|
||
/* special delayed cast for char/short */
|
||
/* XXX: in some cases (multiple cascaded casts), it may still
|
||
be incorrect */
|
||
if (vtop->r & VT_MUSTCAST) {
|
||
vtop->r &= ~VT_MUSTCAST;
|
||
force_charshort_cast(vtop->type.t);
|
||
}
|
||
|
||
/* bitfields first get cast to ints */
|
||
if (vtop->type.t & VT_BITFIELD) {
|
||
gv(RC_INT);
|
||
}
|
||
|
||
dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
|
||
sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
|
||
|
||
if (sbt != dbt && !nocode_wanted) {
|
||
sf = is_float(sbt);
|
||
df = is_float(dbt);
|
||
c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||
if (sf && df) {
|
||
/* convert from fp to fp */
|
||
if (c) {
|
||
/* constant case: we can do it now */
|
||
/* XXX: in ISOC, cannot do it if error in convert */
|
||
if (dbt == VT_FLOAT && sbt == VT_DOUBLE)
|
||
vtop->c.f = (float)vtop->c.d;
|
||
else if (dbt == VT_FLOAT && sbt == VT_LDOUBLE)
|
||
vtop->c.f = (float)vtop->c.ld;
|
||
else if (dbt == VT_DOUBLE && sbt == VT_FLOAT)
|
||
vtop->c.d = (double)vtop->c.f;
|
||
else if (dbt == VT_DOUBLE && sbt == VT_LDOUBLE)
|
||
vtop->c.d = (double)vtop->c.ld;
|
||
else if (dbt == VT_LDOUBLE && sbt == VT_FLOAT)
|
||
vtop->c.ld = (long double)vtop->c.f;
|
||
else if (dbt == VT_LDOUBLE && sbt == VT_DOUBLE)
|
||
vtop->c.ld = (long double)vtop->c.d;
|
||
} else {
|
||
/* non constant case: generate code */
|
||
gen_cvt_ftof(dbt);
|
||
}
|
||
} else if (df) {
|
||
/* convert int to fp */
|
||
if (c) {
|
||
switch(sbt) {
|
||
case VT_LLONG | VT_UNSIGNED:
|
||
case VT_LLONG:
|
||
/* XXX: add const cases for long long */
|
||
goto do_itof;
|
||
case VT_INT | VT_UNSIGNED:
|
||
switch(dbt) {
|
||
case VT_FLOAT: vtop->c.f = (float)vtop->c.ui; break;
|
||
case VT_DOUBLE: vtop->c.d = (double)vtop->c.ui; break;
|
||
case VT_LDOUBLE: vtop->c.ld = (long double)vtop->c.ui; break;
|
||
}
|
||
break;
|
||
default:
|
||
switch(dbt) {
|
||
case VT_FLOAT: vtop->c.f = (float)vtop->c.i; break;
|
||
case VT_DOUBLE: vtop->c.d = (double)vtop->c.i; break;
|
||
case VT_LDOUBLE: vtop->c.ld = (long double)vtop->c.i; break;
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
do_itof:
|
||
#if !defined(TCC_TARGET_ARM)
|
||
gen_cvt_itof1(dbt);
|
||
#else
|
||
gen_cvt_itof(dbt);
|
||
#endif
|
||
}
|
||
} else if (sf) {
|
||
/* convert fp to int */
|
||
/* we handle char/short/etc... with generic code */
|
||
if (dbt != (VT_INT | VT_UNSIGNED) &&
|
||
dbt != (VT_LLONG | VT_UNSIGNED) &&
|
||
dbt != VT_LLONG)
|
||
dbt = VT_INT;
|
||
if (c) {
|
||
switch(dbt) {
|
||
case VT_LLONG | VT_UNSIGNED:
|
||
case VT_LLONG:
|
||
/* XXX: add const cases for long long */
|
||
goto do_ftoi;
|
||
case VT_INT | VT_UNSIGNED:
|
||
switch(sbt) {
|
||
case VT_FLOAT: vtop->c.ui = (unsigned int)vtop->c.d; break;
|
||
case VT_DOUBLE: vtop->c.ui = (unsigned int)vtop->c.d; break;
|
||
case VT_LDOUBLE: vtop->c.ui = (unsigned int)vtop->c.d; break;
|
||
}
|
||
break;
|
||
default:
|
||
/* int case */
|
||
switch(sbt) {
|
||
case VT_FLOAT: vtop->c.i = (int)vtop->c.d; break;
|
||
case VT_DOUBLE: vtop->c.i = (int)vtop->c.d; break;
|
||
case VT_LDOUBLE: vtop->c.i = (int)vtop->c.d; break;
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
do_ftoi:
|
||
gen_cvt_ftoi1(dbt);
|
||
}
|
||
if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
|
||
/* additional cast for char/short/bool... */
|
||
vtop->type.t = dbt;
|
||
gen_cast(type);
|
||
}
|
||
} else if ((dbt & VT_BTYPE) == VT_LLONG) {
|
||
if ((sbt & VT_BTYPE) != VT_LLONG) {
|
||
/* scalar to long long */
|
||
if (c) {
|
||
if (sbt == (VT_INT | VT_UNSIGNED))
|
||
vtop->c.ll = vtop->c.ui;
|
||
else
|
||
vtop->c.ll = vtop->c.i;
|
||
} else {
|
||
/* machine independent conversion */
|
||
gv(RC_INT);
|
||
/* generate high word */
|
||
if (sbt == (VT_INT | VT_UNSIGNED)) {
|
||
vpushi(0);
|
||
gv(RC_INT);
|
||
} else {
|
||
gv_dup();
|
||
vpushi(31);
|
||
gen_op(TOK_SAR);
|
||
}
|
||
/* patch second register */
|
||
vtop[-1].r2 = vtop->r;
|
||
vpop();
|
||
}
|
||
}
|
||
} else if (dbt == VT_BOOL) {
|
||
/* scalar to bool */
|
||
vpushi(0);
|
||
gen_op(TOK_NE);
|
||
} else if ((dbt & VT_BTYPE) == VT_BYTE ||
|
||
(dbt & VT_BTYPE) == VT_SHORT) {
|
||
force_charshort_cast(dbt);
|
||
} else if ((dbt & VT_BTYPE) == VT_INT) {
|
||
/* scalar to int */
|
||
if (sbt == VT_LLONG) {
|
||
/* from long long: just take low order word */
|
||
lexpand();
|
||
vpop();
|
||
}
|
||
/* if lvalue and single word type, nothing to do because
|
||
the lvalue already contains the real type size (see
|
||
VT_LVAL_xxx constants) */
|
||
}
|
||
}
|
||
vtop->type = *type;
|
||
}
|
||
|
||
/* return type size. Put alignment at 'a' */
|
||
static int type_size(CType *type, int *a)
|
||
{
|
||
Sym *s;
|
||
int bt;
|
||
|
||
bt = type->t & VT_BTYPE;
|
||
if (bt == VT_STRUCT) {
|
||
/* struct/union */
|
||
s = type->ref;
|
||
*a = s->r;
|
||
return s->c;
|
||
} else if (bt == VT_PTR) {
|
||
if (type->t & VT_ARRAY) {
|
||
s = type->ref;
|
||
return type_size(&s->type, a) * s->c;
|
||
} else {
|
||
*a = PTR_SIZE;
|
||
return PTR_SIZE;
|
||
}
|
||
} else if (bt == VT_LDOUBLE) {
|
||
*a = LDOUBLE_ALIGN;
|
||
return LDOUBLE_SIZE;
|
||
} else if (bt == VT_DOUBLE || bt == VT_LLONG) {
|
||
#ifdef TCC_TARGET_I386
|
||
*a = 4;
|
||
#else
|
||
*a = 8;
|
||
#endif
|
||
return 8;
|
||
} else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) {
|
||
*a = 4;
|
||
return 4;
|
||
} else if (bt == VT_SHORT) {
|
||
*a = 2;
|
||
return 2;
|
||
} else {
|
||
/* char, void, function, _Bool */
|
||
*a = 1;
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* modify type so that its it is a pointer to type. */
|
||
static void mk_pointer(CType *type)
|
||
{
|
||
Sym *s;
|
||
s = sym_push(SYM_FIELD, type, 0, -1);
|
||
type->t = VT_PTR | (type->t & ~VT_TYPE);
|
||
type->ref = s;
|
||
}
|
||
|
||
/* compare function types. OLD functions match any new functions */
|
||
static int is_compatible_func(CType *type1, CType *type2)
|
||
{
|
||
Sym *s1, *s2;
|
||
|
||
s1 = type1->ref;
|
||
s2 = type2->ref;
|
||
if (!is_compatible_types(&s1->type, &s2->type))
|
||
return 0;
|
||
/* check func_call */
|
||
if (s1->r != s2->r)
|
||
return 0;
|
||
/* XXX: not complete */
|
||
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
|
||
return 1;
|
||
if (s1->c != s2->c)
|
||
return 0;
|
||
while (s1 != NULL) {
|
||
if (s2 == NULL)
|
||
return 0;
|
||
if (!is_compatible_types(&s1->type, &s2->type))
|
||
return 0;
|
||
s1 = s1->next;
|
||
s2 = s2->next;
|
||
}
|
||
if (s2)
|
||
return 0;
|
||
return 1;
|
||
}
|
||
|
||
/* return true if type1 and type2 are exactly the same (including
|
||
qualifiers).
|
||
|
||
- enums are not checked as gcc __builtin_types_compatible_p ()
|
||
*/
|
||
static int is_compatible_types(CType *type1, CType *type2)
|
||
{
|
||
int bt1, t1, t2;
|
||
|
||
t1 = type1->t & VT_TYPE;
|
||
t2 = type2->t & VT_TYPE;
|
||
/* XXX: bitfields ? */
|
||
if (t1 != t2)
|
||
return 0;
|
||
/* test more complicated cases */
|
||
bt1 = t1 & VT_BTYPE;
|
||
if (bt1 == VT_PTR) {
|
||
type1 = pointed_type(type1);
|
||
type2 = pointed_type(type2);
|
||
return is_compatible_types(type1, type2);
|
||
} else if (bt1 == VT_STRUCT) {
|
||
return (type1->ref == type2->ref);
|
||
} else if (bt1 == VT_FUNC) {
|
||
return is_compatible_func(type1, type2);
|
||
} else {
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* print a type. If 'varstr' is not NULL, then the variable is also
|
||
printed in the type */
|
||
/* XXX: union */
|
||
/* XXX: add array and function pointers */
|
||
void type_to_str(char *buf, int buf_size,
|
||
CType *type, const char *varstr)
|
||
{
|
||
int bt, v, t;
|
||
Sym *s, *sa;
|
||
char buf1[256];
|
||
const char *tstr;
|
||
|
||
t = type->t & VT_TYPE;
|
||
bt = t & VT_BTYPE;
|
||
buf[0] = '\0';
|
||
if (t & VT_CONSTANT)
|
||
pstrcat(buf, buf_size, "const ");
|
||
if (t & VT_VOLATILE)
|
||
pstrcat(buf, buf_size, "volatile ");
|
||
if (t & VT_UNSIGNED)
|
||
pstrcat(buf, buf_size, "unsigned ");
|
||
switch(bt) {
|
||
case VT_VOID:
|
||
tstr = "void";
|
||
goto add_tstr;
|
||
case VT_BOOL:
|
||
tstr = "_Bool";
|
||
goto add_tstr;
|
||
case VT_BYTE:
|
||
tstr = "char";
|
||
goto add_tstr;
|
||
case VT_SHORT:
|
||
tstr = "short";
|
||
goto add_tstr;
|
||
case VT_INT:
|
||
tstr = "int";
|
||
goto add_tstr;
|
||
case VT_LONG:
|
||
tstr = "long";
|
||
goto add_tstr;
|
||
case VT_LLONG:
|
||
tstr = "long long";
|
||
goto add_tstr;
|
||
case VT_FLOAT:
|
||
tstr = "float";
|
||
goto add_tstr;
|
||
case VT_DOUBLE:
|
||
tstr = "double";
|
||
goto add_tstr;
|
||
case VT_LDOUBLE:
|
||
tstr = "long double";
|
||
add_tstr:
|
||
pstrcat(buf, buf_size, tstr);
|
||
break;
|
||
case VT_ENUM:
|
||
case VT_STRUCT:
|
||
if (bt == VT_STRUCT)
|
||
tstr = "struct ";
|
||
else
|
||
tstr = "enum ";
|
||
pstrcat(buf, buf_size, tstr);
|
||
v = type->ref->v & ~SYM_STRUCT;
|
||
if (v >= SYM_FIRST_ANOM)
|
||
pstrcat(buf, buf_size, "<anonymous>");
|
||
else
|
||
pstrcat(buf, buf_size, get_tok_str(v, NULL));
|
||
break;
|
||
case VT_FUNC:
|
||
s = type->ref;
|
||
type_to_str(buf, buf_size, &s->type, varstr);
|
||
pstrcat(buf, buf_size, "(");
|
||
sa = s->next;
|
||
while (sa != NULL) {
|
||
type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
|
||
pstrcat(buf, buf_size, buf1);
|
||
sa = sa->next;
|
||
if (sa)
|
||
pstrcat(buf, buf_size, ", ");
|
||
}
|
||
pstrcat(buf, buf_size, ")");
|
||
goto no_var;
|
||
case VT_PTR:
|
||
s = type->ref;
|
||
pstrcpy(buf1, sizeof(buf1), "*");
|
||
if (varstr)
|
||
pstrcat(buf1, sizeof(buf1), varstr);
|
||
type_to_str(buf, buf_size, &s->type, buf1);
|
||
goto no_var;
|
||
}
|
||
if (varstr) {
|
||
pstrcat(buf, buf_size, " ");
|
||
pstrcat(buf, buf_size, varstr);
|
||
}
|
||
no_var: ;
|
||
}
|
||
|
||
/* verify type compatibility to store vtop in 'dt' type, and generate
|
||
casts if needed. */
|
||
static void gen_assign_cast(CType *dt)
|
||
{
|
||
CType *st, *type1, *type2, tmp_type1, tmp_type2;
|
||
char buf1[256], buf2[256];
|
||
int dbt, sbt;
|
||
|
||
st = &vtop->type; /* source type */
|
||
dbt = dt->t & VT_BTYPE;
|
||
sbt = st->t & VT_BTYPE;
|
||
if (dt->t & VT_CONSTANT)
|
||
warning("assignment of read-only location");
|
||
switch(dbt) {
|
||
case VT_PTR:
|
||
/* special cases for pointers */
|
||
/* '0' can also be a pointer */
|
||
if (is_null_pointer(vtop))
|
||
goto type_ok;
|
||
/* accept implicit pointer to integer cast with warning */
|
||
if (is_integer_btype(sbt)) {
|
||
warning("assignment makes pointer from integer without a cast");
|
||
goto type_ok;
|
||
}
|
||
type1 = pointed_type(dt);
|
||
/* a function is implicitely a function pointer */
|
||
if (sbt == VT_FUNC) {
|
||
if ((type1->t & VT_BTYPE) != VT_VOID &&
|
||
!is_compatible_types(pointed_type(dt), st))
|
||
goto error;
|
||
else
|
||
goto type_ok;
|
||
}
|
||
if (sbt != VT_PTR)
|
||
goto error;
|
||
type2 = pointed_type(st);
|
||
if ((type1->t & VT_BTYPE) == VT_VOID ||
|
||
(type2->t & VT_BTYPE) == VT_VOID) {
|
||
/* void * can match anything */
|
||
} else {
|
||
/* exact type match, except for unsigned */
|
||
tmp_type1 = *type1;
|
||
tmp_type2 = *type2;
|
||
tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
|
||
tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
|
||
if (!is_compatible_types(&tmp_type1, &tmp_type2))
|
||
goto error;
|
||
}
|
||
/* check const and volatile */
|
||
if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
|
||
(!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
|
||
warning("assignment discards qualifiers from pointer target type");
|
||
break;
|
||
case VT_BYTE:
|
||
case VT_SHORT:
|
||
case VT_INT:
|
||
case VT_LLONG:
|
||
if (sbt == VT_PTR || sbt == VT_FUNC) {
|
||
warning("assignment makes integer from pointer without a cast");
|
||
}
|
||
/* XXX: more tests */
|
||
break;
|
||
case VT_STRUCT:
|
||
tmp_type1 = *dt;
|
||
tmp_type2 = *st;
|
||
tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
|
||
tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
|
||
if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
|
||
error:
|
||
type_to_str(buf1, sizeof(buf1), st, NULL);
|
||
type_to_str(buf2, sizeof(buf2), dt, NULL);
|
||
error("cannot cast '%s' to '%s'", buf1, buf2);
|
||
}
|
||
break;
|
||
}
|
||
type_ok:
|
||
gen_cast(dt);
|
||
}
|
||
|
||
/* store vtop in lvalue pushed on stack */
|
||
void vstore(void)
|
||
{
|
||
int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;
|
||
|
||
ft = vtop[-1].type.t;
|
||
sbt = vtop->type.t & VT_BTYPE;
|
||
dbt = ft & VT_BTYPE;
|
||
if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
|
||
(sbt == VT_INT && dbt == VT_SHORT)) {
|
||
/* optimize char/short casts */
|
||
delayed_cast = VT_MUSTCAST;
|
||
vtop->type.t = ft & VT_TYPE;
|
||
/* XXX: factorize */
|
||
if (ft & VT_CONSTANT)
|
||
warning("assignment of read-only location");
|
||
} else {
|
||
delayed_cast = 0;
|
||
if (!(ft & VT_BITFIELD))
|
||
gen_assign_cast(&vtop[-1].type);
|
||
}
|
||
|
||
if (sbt == VT_STRUCT) {
|
||
/* if structure, only generate pointer */
|
||
/* structure assignment : generate memcpy */
|
||
/* XXX: optimize if small size */
|
||
if (!nocode_wanted) {
|
||
size = type_size(&vtop->type, &align);
|
||
|
||
vpush_global_sym(&func_old_type, TOK_memcpy);
|
||
|
||
/* destination */
|
||
vpushv(vtop - 2);
|
||
vtop->type.t = VT_INT;
|
||
gaddrof();
|
||
/* source */
|
||
vpushv(vtop - 2);
|
||
vtop->type.t = VT_INT;
|
||
gaddrof();
|
||
/* type size */
|
||
vpushi(size);
|
||
gfunc_call(3);
|
||
|
||
vswap();
|
||
vpop();
|
||
} else {
|
||
vswap();
|
||
vpop();
|
||
}
|
||
/* leave source on stack */
|
||
} else if (ft & VT_BITFIELD) {
|
||
/* bitfield store handling */
|
||
bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f;
|
||
bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
|
||
/* remove bit field info to avoid loops */
|
||
vtop[-1].type.t = ft & ~(VT_BITFIELD | (~0U << VT_STRUCT_SHIFT));
|
||
|
||
/* duplicate destination */
|
||
vdup();
|
||
vtop[-1] = vtop[-2];
|
||
|
||
/* mask and shift source */
|
||
vpushi((1 << bit_size) - 1);
|
||
gen_op('&');
|
||
vpushi(bit_pos);
|
||
gen_op(TOK_SHL);
|
||
/* load destination, mask and or with source */
|
||
vswap();
|
||
vpushi(~(((1 << bit_size) - 1) << bit_pos));
|
||
gen_op('&');
|
||
gen_op('|');
|
||
/* store result */
|
||
vstore();
|
||
} else {
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
/* bound check case */
|
||
if (vtop[-1].r & VT_MUSTBOUND) {
|
||
vswap();
|
||
gbound();
|
||
vswap();
|
||
}
|
||
#endif
|
||
if (!nocode_wanted) {
|
||
rc = RC_INT;
|
||
if (is_float(ft))
|
||
rc = RC_FLOAT;
|
||
r = gv(rc); /* generate value */
|
||
/* if lvalue was saved on stack, must read it */
|
||
if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
|
||
SValue sv;
|
||
t = get_reg(RC_INT);
|
||
sv.type.t = VT_INT;
|
||
sv.r = VT_LOCAL | VT_LVAL;
|
||
sv.c.ul = vtop[-1].c.ul;
|
||
load(t, &sv);
|
||
vtop[-1].r = t | VT_LVAL;
|
||
}
|
||
store(r, vtop - 1);
|
||
/* two word case handling : store second register at word + 4 */
|
||
if ((ft & VT_BTYPE) == VT_LLONG) {
|
||
vswap();
|
||
/* convert to int to increment easily */
|
||
vtop->type.t = VT_INT;
|
||
gaddrof();
|
||
vpushi(4);
|
||
gen_op('+');
|
||
vtop->r |= VT_LVAL;
|
||
vswap();
|
||
/* XXX: it works because r2 is spilled last ! */
|
||
store(vtop->r2, vtop - 1);
|
||
}
|
||
}
|
||
vswap();
|
||
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
|
||
vtop->r |= delayed_cast;
|
||
}
|
||
}
|
||
|
||
/* post defines POST/PRE add. c is the token ++ or -- */
|
||
void inc(int post, int c)
|
||
{
|
||
test_lvalue();
|
||
vdup(); /* save lvalue */
|
||
if (post) {
|
||
gv_dup(); /* duplicate value */
|
||
vrotb(3);
|
||
vrotb(3);
|
||
}
|
||
/* add constant */
|
||
vpushi(c - TOK_MID);
|
||
gen_op('+');
|
||
vstore(); /* store value */
|
||
if (post)
|
||
vpop(); /* if post op, return saved value */
|
||
}
|
||
|
||
/* Parse GNUC __attribute__ extension. Currently, the following
|
||
extensions are recognized:
|
||
- aligned(n) : set data/function alignment.
|
||
- packed : force data alignment to 1
|
||
- section(x) : generate data/code in this section.
|
||
- unused : currently ignored, but may be used someday.
|
||
- regparm(n) : pass function parameters in registers (i386 only)
|
||
*/
|
||
static void parse_attribute(AttributeDef *ad)
|
||
{
|
||
int t, n;
|
||
|
||
while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
|
||
next();
|
||
skip('(');
|
||
skip('(');
|
||
while (tok != ')') {
|
||
if (tok < TOK_IDENT)
|
||
expect("attribute name");
|
||
t = tok;
|
||
next();
|
||
switch(t) {
|
||
case TOK_SECTION1:
|
||
case TOK_SECTION2:
|
||
skip('(');
|
||
if (tok != TOK_STR)
|
||
expect("section name");
|
||
ad->section = find_section(tcc_state, (char *)tokc.cstr->data);
|
||
next();
|
||
skip(')');
|
||
break;
|
||
case TOK_ALIGNED1:
|
||
case TOK_ALIGNED2:
|
||
if (tok == '(') {
|
||
next();
|
||
n = expr_const();
|
||
if (n <= 0 || (n & (n - 1)) != 0)
|
||
error("alignment must be a positive power of two");
|
||
skip(')');
|
||
} else {
|
||
n = MAX_ALIGN;
|
||
}
|
||
ad->aligned = n;
|
||
break;
|
||
case TOK_PACKED1:
|
||
case TOK_PACKED2:
|
||
ad->packed = 1;
|
||
break;
|
||
case TOK_UNUSED1:
|
||
case TOK_UNUSED2:
|
||
/* currently, no need to handle it because tcc does not
|
||
track unused objects */
|
||
break;
|
||
case TOK_NORETURN1:
|
||
case TOK_NORETURN2:
|
||
/* currently, no need to handle it because tcc does not
|
||
track unused objects */
|
||
break;
|
||
case TOK_CDECL1:
|
||
case TOK_CDECL2:
|
||
case TOK_CDECL3:
|
||
ad->func_call = FUNC_CDECL;
|
||
break;
|
||
case TOK_STDCALL1:
|
||
case TOK_STDCALL2:
|
||
case TOK_STDCALL3:
|
||
ad->func_call = FUNC_STDCALL;
|
||
break;
|
||
#ifdef TCC_TARGET_I386
|
||
case TOK_REGPARM1:
|
||
case TOK_REGPARM2:
|
||
skip('(');
|
||
n = expr_const();
|
||
if (n > 3)
|
||
n = 3;
|
||
else if (n < 0)
|
||
n = 0;
|
||
if (n > 0)
|
||
ad->func_call = FUNC_FASTCALL1 + n - 1;
|
||
skip(')');
|
||
break;
|
||
#endif
|
||
case TOK_DLLEXPORT:
|
||
ad->dllexport = 1;
|
||
break;
|
||
default:
|
||
if (tcc_state->warn_unsupported)
|
||
warning("'%s' attribute ignored", get_tok_str(t, NULL));
|
||
/* skip parameters */
|
||
/* XXX: skip parenthesis too */
|
||
if (tok == '(') {
|
||
next();
|
||
while (tok != ')' && tok != -1)
|
||
next();
|
||
next();
|
||
}
|
||
break;
|
||
}
|
||
if (tok != ',')
|
||
break;
|
||
next();
|
||
}
|
||
skip(')');
|
||
skip(')');
|
||
}
|
||
}
|
||
|
||
/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */
|
||
static void struct_decl(CType *type, int u)
|
||
{
|
||
int a, v, size, align, maxalign, c, offset;
|
||
int bit_size, bit_pos, bsize, bt, lbit_pos;
|
||
Sym *s, *ss, **ps;
|
||
AttributeDef ad;
|
||
CType type1, btype;
|
||
|
||
a = tok; /* save decl type */
|
||
next();
|
||
if (tok != '{') {
|
||
v = tok;
|
||
next();
|
||
/* struct already defined ? return it */
|
||
if (v < TOK_IDENT)
|
||
expect("struct/union/enum name");
|
||
s = struct_find(v);
|
||
if (s) {
|
||
if (s->type.t != a)
|
||
error("invalid type");
|
||
goto do_decl;
|
||
}
|
||
} else {
|
||
v = anon_sym++;
|
||
}
|
||
type1.t = a;
|
||
/* we put an undefined size for struct/union */
|
||
s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
|
||
s->r = 0; /* default alignment is zero as gcc */
|
||
/* put struct/union/enum name in type */
|
||
do_decl:
|
||
type->t = u;
|
||
type->ref = s;
|
||
|
||
if (tok == '{') {
|
||
next();
|
||
if (s->c != -1)
|
||
error("struct/union/enum already defined");
|
||
/* cannot be empty */
|
||
c = 0;
|
||
/* non empty enums are not allowed */
|
||
if (a == TOK_ENUM) {
|
||
for(;;) {
|
||
v = tok;
|
||
if (v < TOK_UIDENT)
|
||
expect("identifier");
|
||
next();
|
||
if (tok == '=') {
|
||
next();
|
||
c = expr_const();
|
||
}
|
||
/* enum symbols have static storage */
|
||
ss = sym_push(v, &int_type, VT_CONST, c);
|
||
ss->type.t |= VT_STATIC;
|
||
if (tok != ',')
|
||
break;
|
||
next();
|
||
c++;
|
||
/* NOTE: we accept a trailing comma */
|
||
if (tok == '}')
|
||
break;
|
||
}
|
||
skip('}');
|
||
} else {
|
||
maxalign = 1;
|
||
ps = &s->next;
|
||
bit_pos = 0;
|
||
offset = 0;
|
||
while (tok != '}') {
|
||
parse_btype(&btype, &ad);
|
||
while (1) {
|
||
bit_size = -1;
|
||
v = 0;
|
||
type1 = btype;
|
||
if (tok != ':') {
|
||
type_decl(&type1, &ad, &v, TYPE_DIRECT);
|
||
if ((type1.t & VT_BTYPE) == VT_FUNC ||
|
||
(type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE)))
|
||
error("invalid type for '%s'",
|
||
get_tok_str(v, NULL));
|
||
}
|
||
if (tok == ':') {
|
||
next();
|
||
bit_size = expr_const();
|
||
/* XXX: handle v = 0 case for messages */
|
||
if (bit_size < 0)
|
||
error("negative width in bit-field '%s'",
|
||
get_tok_str(v, NULL));
|
||
if (v && bit_size == 0)
|
||
error("zero width for bit-field '%s'",
|
||
get_tok_str(v, NULL));
|
||
}
|
||
size = type_size(&type1, &align);
|
||
if (ad.aligned) {
|
||
if (align < ad.aligned)
|
||
align = ad.aligned;
|
||
} else if (ad.packed) {
|
||
align = 1;
|
||
} else if (*tcc_state->pack_stack_ptr) {
|
||
if (align > *tcc_state->pack_stack_ptr)
|
||
align = *tcc_state->pack_stack_ptr;
|
||
}
|
||
lbit_pos = 0;
|
||
if (bit_size >= 0) {
|
||
bt = type1.t & VT_BTYPE;
|
||
if (bt != VT_INT &&
|
||
bt != VT_BYTE &&
|
||
bt != VT_SHORT &&
|
||
bt != VT_BOOL &&
|
||
bt != VT_ENUM)
|
||
error("bitfields must have scalar type");
|
||
bsize = size * 8;
|
||
if (bit_size > bsize) {
|
||
error("width of '%s' exceeds its type",
|
||
get_tok_str(v, NULL));
|
||
} else if (bit_size == bsize) {
|
||
/* no need for bit fields */
|
||
bit_pos = 0;
|
||
} else if (bit_size == 0) {
|
||
/* XXX: what to do if only padding in a
|
||
structure ? */
|
||
/* zero size: means to pad */
|
||
if (bit_pos > 0)
|
||
bit_pos = bsize;
|
||
} else {
|
||
/* we do not have enough room ? */
|
||
if ((bit_pos + bit_size) > bsize)
|
||
bit_pos = 0;
|
||
lbit_pos = bit_pos;
|
||
/* XXX: handle LSB first */
|
||
type1.t |= VT_BITFIELD |
|
||
(bit_pos << VT_STRUCT_SHIFT) |
|
||
(bit_size << (VT_STRUCT_SHIFT + 6));
|
||
bit_pos += bit_size;
|
||
}
|
||
} else {
|
||
bit_pos = 0;
|
||
}
|
||
if (v) {
|
||
/* add new memory data only if starting
|
||
bit field */
|
||
if (lbit_pos == 0) {
|
||
if (a == TOK_STRUCT) {
|
||
c = (c + align - 1) & -align;
|
||
offset = c;
|
||
c += size;
|
||
} else {
|
||
offset = 0;
|
||
if (size > c)
|
||
c = size;
|
||
}
|
||
if (align > maxalign)
|
||
maxalign = align;
|
||
}
|
||
#if 0
|
||
printf("add field %s offset=%d",
|
||
get_tok_str(v, NULL), offset);
|
||
if (type1.t & VT_BITFIELD) {
|
||
printf(" pos=%d size=%d",
|
||
(type1.t >> VT_STRUCT_SHIFT) & 0x3f,
|
||
(type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
|
||
}
|
||
printf("\n");
|
||
#endif
|
||
ss = sym_push(v | SYM_FIELD, &type1, 0, offset);
|
||
*ps = ss;
|
||
ps = &ss->next;
|
||
}
|
||
if (tok == ';' || tok == TOK_EOF)
|
||
break;
|
||
skip(',');
|
||
}
|
||
skip(';');
|
||
}
|
||
skip('}');
|
||
/* store size and alignment */
|
||
s->c = (c + maxalign - 1) & -maxalign;
|
||
s->r = maxalign;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* return 0 if no type declaration. otherwise, return the basic type
|
||
and skip it.
|
||
*/
|
||
static int parse_btype(CType *type, AttributeDef *ad)
|
||
{
|
||
int t, u, type_found, typespec_found;
|
||
Sym *s;
|
||
CType type1;
|
||
|
||
memset(ad, 0, sizeof(AttributeDef));
|
||
type_found = 0;
|
||
typespec_found = 0;
|
||
t = 0;
|
||
while(1) {
|
||
switch(tok) {
|
||
case TOK_EXTENSION:
|
||
/* currently, we really ignore extension */
|
||
next();
|
||
continue;
|
||
|
||
/* basic types */
|
||
case TOK_CHAR:
|
||
u = VT_BYTE;
|
||
basic_type:
|
||
next();
|
||
basic_type1:
|
||
if ((t & VT_BTYPE) != 0)
|
||
error("too many basic types");
|
||
t |= u;
|
||
typespec_found = 1;
|
||
break;
|
||
case TOK_VOID:
|
||
u = VT_VOID;
|
||
goto basic_type;
|
||
case TOK_SHORT:
|
||
u = VT_SHORT;
|
||
goto basic_type;
|
||
case TOK_INT:
|
||
next();
|
||
typespec_found = 1;
|
||
break;
|
||
case TOK_LONG:
|
||
next();
|
||
if ((t & VT_BTYPE) == VT_DOUBLE) {
|
||
t = (t & ~VT_BTYPE) | VT_LDOUBLE;
|
||
} else if ((t & VT_BTYPE) == VT_LONG) {
|
||
t = (t & ~VT_BTYPE) | VT_LLONG;
|
||
} else {
|
||
u = VT_LONG;
|
||
goto basic_type1;
|
||
}
|
||
break;
|
||
case TOK_BOOL:
|
||
u = VT_BOOL;
|
||
goto basic_type;
|
||
case TOK_FLOAT:
|
||
u = VT_FLOAT;
|
||
goto basic_type;
|
||
case TOK_DOUBLE:
|
||
next();
|
||
if ((t & VT_BTYPE) == VT_LONG) {
|
||
t = (t & ~VT_BTYPE) | VT_LDOUBLE;
|
||
} else {
|
||
u = VT_DOUBLE;
|
||
goto basic_type1;
|
||
}
|
||
break;
|
||
case TOK_ENUM:
|
||
struct_decl(&type1, VT_ENUM);
|
||
basic_type2:
|
||
u = type1.t;
|
||
type->ref = type1.ref;
|
||
goto basic_type1;
|
||
case TOK_STRUCT:
|
||
case TOK_UNION:
|
||
struct_decl(&type1, VT_STRUCT);
|
||
goto basic_type2;
|
||
|
||
/* type modifiers */
|
||
case TOK_CONST1:
|
||
case TOK_CONST2:
|
||
case TOK_CONST3:
|
||
t |= VT_CONSTANT;
|
||
next();
|
||
break;
|
||
case TOK_VOLATILE1:
|
||
case TOK_VOLATILE2:
|
||
case TOK_VOLATILE3:
|
||
t |= VT_VOLATILE;
|
||
next();
|
||
break;
|
||
case TOK_SIGNED1:
|
||
case TOK_SIGNED2:
|
||
case TOK_SIGNED3:
|
||
typespec_found = 1;
|
||
t |= VT_SIGNED;
|
||
next();
|
||
break;
|
||
case TOK_REGISTER:
|
||
case TOK_AUTO:
|
||
case TOK_RESTRICT1:
|
||
case TOK_RESTRICT2:
|
||
case TOK_RESTRICT3:
|
||
next();
|
||
break;
|
||
case TOK_UNSIGNED:
|
||
t |= VT_UNSIGNED;
|
||
next();
|
||
typespec_found = 1;
|
||
break;
|
||
|
||
/* storage */
|
||
case TOK_EXTERN:
|
||
t |= VT_EXTERN;
|
||
next();
|
||
break;
|
||
case TOK_STATIC:
|
||
t |= VT_STATIC;
|
||
next();
|
||
break;
|
||
case TOK_TYPEDEF:
|
||
t |= VT_TYPEDEF;
|
||
next();
|
||
break;
|
||
case TOK_INLINE1:
|
||
case TOK_INLINE2:
|
||
case TOK_INLINE3:
|
||
t |= VT_INLINE;
|
||
next();
|
||
break;
|
||
|
||
/* GNUC attribute */
|
||
case TOK_ATTRIBUTE1:
|
||
case TOK_ATTRIBUTE2:
|
||
parse_attribute(ad);
|
||
break;
|
||
/* GNUC typeof */
|
||
case TOK_TYPEOF1:
|
||
case TOK_TYPEOF2:
|
||
case TOK_TYPEOF3:
|
||
next();
|
||
parse_expr_type(&type1);
|
||
goto basic_type2;
|
||
default:
|
||
if (typespec_found)
|
||
goto the_end;
|
||
s = sym_find(tok);
|
||
if (!s || !(s->type.t & VT_TYPEDEF))
|
||
goto the_end;
|
||
t |= (s->type.t & ~VT_TYPEDEF);
|
||
type->ref = s->type.ref;
|
||
next();
|
||
break;
|
||
}
|
||
type_found = 1;
|
||
}
|
||
the_end:
|
||
if ((t & (VT_SIGNED|VT_UNSIGNED)) == (VT_SIGNED|VT_UNSIGNED))
|
||
error("signed and unsigned modifier");
|
||
if (tcc_state->char_is_unsigned) {
|
||
if ((t & (VT_SIGNED|VT_UNSIGNED|VT_BTYPE)) == VT_BYTE)
|
||
t |= VT_UNSIGNED;
|
||
}
|
||
t &= ~VT_SIGNED;
|
||
|
||
/* long is never used as type */
|
||
if ((t & VT_BTYPE) == VT_LONG)
|
||
t = (t & ~VT_BTYPE) | VT_INT;
|
||
type->t = t;
|
||
return type_found;
|
||
}
|
||
|
||
/* convert a function parameter type (array to pointer and function to
|
||
function pointer) */
|
||
static inline void convert_parameter_type(CType *pt)
|
||
{
|
||
/* remove const and volatile qualifiers (XXX: const could be used
|
||
to indicate a const function parameter */
|
||
pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
|
||
/* array must be transformed to pointer according to ANSI C */
|
||
pt->t &= ~VT_ARRAY;
|
||
if ((pt->t & VT_BTYPE) == VT_FUNC) {
|
||
mk_pointer(pt);
|
||
}
|
||
}
|
||
|
||
static void post_type(CType *type, AttributeDef *ad)
|
||
{
|
||
int n, l, t1;
|
||
Sym **plast, *s, *first;
|
||
AttributeDef ad1;
|
||
CType pt;
|
||
|
||
if (tok == '(') {
|
||
/* function declaration */
|
||
next();
|
||
l = 0;
|
||
first = NULL;
|
||
plast = &first;
|
||
while (tok != ')') {
|
||
/* read param name and compute offset */
|
||
if (l != FUNC_OLD) {
|
||
if (!parse_btype(&pt, &ad1)) {
|
||
if (l) {
|
||
error("invalid type");
|
||
} else {
|
||
l = FUNC_OLD;
|
||
goto old_proto;
|
||
}
|
||
}
|
||
l = FUNC_NEW;
|
||
if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
|
||
break;
|
||
type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
|
||
if ((pt.t & VT_BTYPE) == VT_VOID)
|
||
error("parameter declared as void");
|
||
} else {
|
||
old_proto:
|
||
n = tok;
|
||
pt.t = VT_INT;
|
||
next();
|
||
}
|
||
convert_parameter_type(&pt);
|
||
s = sym_push(n | SYM_FIELD, &pt, 0, 0);
|
||
*plast = s;
|
||
plast = &s->next;
|
||
if (tok == ',') {
|
||
next();
|
||
if (l == FUNC_NEW && tok == TOK_DOTS) {
|
||
l = FUNC_ELLIPSIS;
|
||
next();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/* if no parameters, then old type prototype */
|
||
if (l == 0)
|
||
l = FUNC_OLD;
|
||
skip(')');
|
||
t1 = type->t & VT_STORAGE;
|
||
/* NOTE: const is ignored in returned type as it has a special
|
||
meaning in gcc / C++ */
|
||
type->t &= ~(VT_STORAGE | VT_CONSTANT);
|
||
post_type(type, ad);
|
||
/* we push a anonymous symbol which will contain the function prototype */
|
||
s = sym_push(SYM_FIELD, type, ad->func_call, l);
|
||
s->next = first;
|
||
type->t = t1 | VT_FUNC;
|
||
type->ref = s;
|
||
} else if (tok == '[') {
|
||
/* array definition */
|
||
next();
|
||
n = -1;
|
||
if (tok != ']') {
|
||
n = expr_const();
|
||
if (n < 0)
|
||
error("invalid array size");
|
||
}
|
||
skip(']');
|
||
/* parse next post type */
|
||
t1 = type->t & VT_STORAGE;
|
||
type->t &= ~VT_STORAGE;
|
||
post_type(type, ad);
|
||
|
||
/* we push a anonymous symbol which will contain the array
|
||
element type */
|
||
s = sym_push(SYM_FIELD, type, 0, n);
|
||
type->t = t1 | VT_ARRAY | VT_PTR;
|
||
type->ref = s;
|
||
}
|
||
}
|
||
|
||
/* Parse a type declaration (except basic type), and return the type
|
||
in 'type'. 'td' is a bitmask indicating which kind of type decl is
|
||
expected. 'type' should contain the basic type. 'ad' is the
|
||
attribute definition of the basic type. It can be modified by
|
||
type_decl().
|
||
*/
|
||
static void type_decl(CType *type, AttributeDef *ad, int *v, int td)
|
||
{
|
||
Sym *s;
|
||
CType type1, *type2;
|
||
int qualifiers;
|
||
|
||
while (tok == '*') {
|
||
qualifiers = 0;
|
||
redo:
|
||
next();
|
||
switch(tok) {
|
||
case TOK_CONST1:
|
||
case TOK_CONST2:
|
||
case TOK_CONST3:
|
||
qualifiers |= VT_CONSTANT;
|
||
goto redo;
|
||
case TOK_VOLATILE1:
|
||
case TOK_VOLATILE2:
|
||
case TOK_VOLATILE3:
|
||
qualifiers |= VT_VOLATILE;
|
||
goto redo;
|
||
case TOK_RESTRICT1:
|
||
case TOK_RESTRICT2:
|
||
case TOK_RESTRICT3:
|
||
goto redo;
|
||
}
|
||
mk_pointer(type);
|
||
type->t |= qualifiers;
|
||
}
|
||
|
||
/* XXX: clarify attribute handling */
|
||
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
|
||
parse_attribute(ad);
|
||
|
||
/* recursive type */
|
||
/* XXX: incorrect if abstract type for functions (e.g. 'int ()') */
|
||
type1.t = 0; /* XXX: same as int */
|
||
if (tok == '(') {
|
||
next();
|
||
/* XXX: this is not correct to modify 'ad' at this point, but
|
||
the syntax is not clear */
|
||
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
|
||
parse_attribute(ad);
|
||
type_decl(&type1, ad, v, td);
|
||
skip(')');
|
||
} else {
|
||
/* type identifier */
|
||
if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
|
||
*v = tok;
|
||
next();
|
||
} else {
|
||
if (!(td & TYPE_ABSTRACT))
|
||
expect("identifier");
|
||
*v = 0;
|
||
}
|
||
}
|
||
post_type(type, ad);
|
||
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
|
||
parse_attribute(ad);
|
||
if (!type1.t)
|
||
return;
|
||
/* append type at the end of type1 */
|
||
type2 = &type1;
|
||
for(;;) {
|
||
s = type2->ref;
|
||
type2 = &s->type;
|
||
if (!type2->t) {
|
||
*type2 = *type;
|
||
break;
|
||
}
|
||
}
|
||
*type = type1;
|
||
}
|
||
|
||
/* compute the lvalue VT_LVAL_xxx needed to match type t. */
|
||
static int lvalue_type(int t)
|
||
{
|
||
int bt, r;
|
||
r = VT_LVAL;
|
||
bt = t & VT_BTYPE;
|
||
if (bt == VT_BYTE || bt == VT_BOOL)
|
||
r |= VT_LVAL_BYTE;
|
||
else if (bt == VT_SHORT)
|
||
r |= VT_LVAL_SHORT;
|
||
else
|
||
return r;
|
||
if (t & VT_UNSIGNED)
|
||
r |= VT_LVAL_UNSIGNED;
|
||
return r;
|
||
}
|
||
|
||
/* indirection with full error checking and bound check */
|
||
static void indir(void)
|
||
{
|
||
if ((vtop->type.t & VT_BTYPE) != VT_PTR)
|
||
expect("pointer");
|
||
if ((vtop->r & VT_LVAL) && !nocode_wanted)
|
||
gv(RC_INT);
|
||
vtop->type = *pointed_type(&vtop->type);
|
||
/* an array is never an lvalue */
|
||
if (!(vtop->type.t & VT_ARRAY)) {
|
||
vtop->r |= lvalue_type(vtop->type.t);
|
||
/* if bound checking, the referenced pointer must be checked */
|
||
if (do_bounds_check)
|
||
vtop->r |= VT_MUSTBOUND;
|
||
}
|
||
}
|
||
|
||
/* pass a parameter to a function and do type checking and casting */
|
||
static void gfunc_param_typed(Sym *func, Sym *arg)
|
||
{
|
||
int func_type;
|
||
CType type;
|
||
|
||
func_type = func->c;
|
||
if (func_type == FUNC_OLD ||
|
||
(func_type == FUNC_ELLIPSIS && arg == NULL)) {
|
||
/* default casting : only need to convert float to double */
|
||
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
|
||
type.t = VT_DOUBLE;
|
||
gen_cast(&type);
|
||
}
|
||
} else if (arg == NULL) {
|
||
error("too many arguments to function");
|
||
} else {
|
||
type = arg->type;
|
||
type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
|
||
gen_assign_cast(&type);
|
||
}
|
||
}
|
||
|
||
/* parse an expression of the form '(type)' or '(expr)' and return its
|
||
type */
|
||
static void parse_expr_type(CType *type)
|
||
{
|
||
int n;
|
||
AttributeDef ad;
|
||
|
||
skip('(');
|
||
if (parse_btype(type, &ad)) {
|
||
type_decl(type, &ad, &n, TYPE_ABSTRACT);
|
||
} else {
|
||
expr_type(type);
|
||
}
|
||
skip(')');
|
||
}
|
||
|
||
static void parse_type(CType *type)
|
||
{
|
||
AttributeDef ad;
|
||
int n;
|
||
|
||
if (!parse_btype(type, &ad)) {
|
||
expect("type");
|
||
}
|
||
type_decl(type, &ad, &n, TYPE_ABSTRACT);
|
||
}
|
||
|
||
static void vpush_tokc(int t)
|
||
{
|
||
CType type;
|
||
type.t = t;
|
||
vsetc(&type, VT_CONST, &tokc);
|
||
}
|
||
|
||
static void unary(void)
|
||
{
|
||
int n, t, align, size, r;
|
||
CType type;
|
||
Sym *s;
|
||
AttributeDef ad;
|
||
|
||
/* XXX: GCC 2.95.3 does not generate a table although it should be
|
||
better here */
|
||
tok_next:
|
||
switch(tok) {
|
||
case TOK_EXTENSION:
|
||
next();
|
||
goto tok_next;
|
||
case TOK_CINT:
|
||
case TOK_CCHAR:
|
||
case TOK_LCHAR:
|
||
vpushi(tokc.i);
|
||
next();
|
||
break;
|
||
case TOK_CUINT:
|
||
vpush_tokc(VT_INT | VT_UNSIGNED);
|
||
next();
|
||
break;
|
||
case TOK_CLLONG:
|
||
vpush_tokc(VT_LLONG);
|
||
next();
|
||
break;
|
||
case TOK_CULLONG:
|
||
vpush_tokc(VT_LLONG | VT_UNSIGNED);
|
||
next();
|
||
break;
|
||
case TOK_CFLOAT:
|
||
vpush_tokc(VT_FLOAT);
|
||
next();
|
||
break;
|
||
case TOK_CDOUBLE:
|
||
vpush_tokc(VT_DOUBLE);
|
||
next();
|
||
break;
|
||
case TOK_CLDOUBLE:
|
||
vpush_tokc(VT_LDOUBLE);
|
||
next();
|
||
break;
|
||
case TOK___FUNCTION__:
|
||
if (!gnu_ext)
|
||
goto tok_identifier;
|
||
/* fall thru */
|
||
case TOK___FUNC__:
|
||
{
|
||
void *ptr;
|
||
int len;
|
||
/* special function name identifier */
|
||
len = strlen(funcname) + 1;
|
||
/* generate char[len] type */
|
||
type.t = VT_BYTE;
|
||
mk_pointer(&type);
|
||
type.t |= VT_ARRAY;
|
||
type.ref->c = len;
|
||
vpush_ref(&type, data_section, data_section->data_offset, len);
|
||
ptr = section_ptr_add(data_section, len);
|
||
memcpy(ptr, funcname, len);
|
||
next();
|
||
}
|
||
break;
|
||
case TOK_LSTR:
|
||
t = VT_INT;
|
||
goto str_init;
|
||
case TOK_STR:
|
||
/* string parsing */
|
||
t = VT_BYTE;
|
||
str_init:
|
||
if (tcc_state->warn_write_strings)
|
||
t |= VT_CONSTANT;
|
||
type.t = t;
|
||
mk_pointer(&type);
|
||
type.t |= VT_ARRAY;
|
||
memset(&ad, 0, sizeof(AttributeDef));
|
||
decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
|
||
break;
|
||
case '(':
|
||
next();
|
||
/* cast ? */
|
||
if (parse_btype(&type, &ad)) {
|
||
type_decl(&type, &ad, &n, TYPE_ABSTRACT);
|
||
skip(')');
|
||
/* check ISOC99 compound literal */
|
||
if (tok == '{') {
|
||
/* data is allocated locally by default */
|
||
if (global_expr)
|
||
r = VT_CONST;
|
||
else
|
||
r = VT_LOCAL;
|
||
/* all except arrays are lvalues */
|
||
if (!(type.t & VT_ARRAY))
|
||
r |= lvalue_type(type.t);
|
||
memset(&ad, 0, sizeof(AttributeDef));
|
||
decl_initializer_alloc(&type, &ad, r, 1, 0, 0);
|
||
} else {
|
||
unary();
|
||
gen_cast(&type);
|
||
}
|
||
} else if (tok == '{') {
|
||
/* save all registers */
|
||
save_regs(0);
|
||
/* statement expression : we do not accept break/continue
|
||
inside as GCC does */
|
||
block(NULL, NULL, NULL, NULL, 0, 1);
|
||
skip(')');
|
||
} else {
|
||
gexpr();
|
||
skip(')');
|
||
}
|
||
break;
|
||
case '*':
|
||
next();
|
||
unary();
|
||
indir();
|
||
break;
|
||
case '&':
|
||
next();
|
||
unary();
|
||
/* functions names must be treated as function pointers,
|
||
except for unary '&' and sizeof. Since we consider that
|
||
functions are not lvalues, we only have to handle it
|
||
there and in function calls. */
|
||
/* arrays can also be used although they are not lvalues */
|
||
if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
|
||
!(vtop->type.t & VT_ARRAY))
|
||
test_lvalue();
|
||
mk_pointer(&vtop->type);
|
||
gaddrof();
|
||
break;
|
||
case '!':
|
||
next();
|
||
unary();
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)
|
||
vtop->c.i = !vtop->c.i;
|
||
else if ((vtop->r & VT_VALMASK) == VT_CMP)
|
||
vtop->c.i = vtop->c.i ^ 1;
|
||
else
|
||
vseti(VT_JMP, gtst(1, 0));
|
||
break;
|
||
case '~':
|
||
next();
|
||
unary();
|
||
vpushi(-1);
|
||
gen_op('^');
|
||
break;
|
||
case '+':
|
||
next();
|
||
/* in order to force cast, we add zero */
|
||
unary();
|
||
if ((vtop->type.t & VT_BTYPE) == VT_PTR)
|
||
error("pointer not accepted for unary plus");
|
||
vpushi(0);
|
||
gen_op('+');
|
||
break;
|
||
case TOK_SIZEOF:
|
||
case TOK_ALIGNOF1:
|
||
case TOK_ALIGNOF2:
|
||
t = tok;
|
||
next();
|
||
if (tok == '(') {
|
||
parse_expr_type(&type);
|
||
} else {
|
||
unary_type(&type);
|
||
}
|
||
size = type_size(&type, &align);
|
||
if (t == TOK_SIZEOF) {
|
||
if (size < 0)
|
||
error("sizeof applied to an incomplete type");
|
||
vpushi(size);
|
||
} else {
|
||
vpushi(align);
|
||
}
|
||
break;
|
||
|
||
case TOK_builtin_types_compatible_p:
|
||
{
|
||
CType type1, type2;
|
||
next();
|
||
skip('(');
|
||
parse_type(&type1);
|
||
skip(',');
|
||
parse_type(&type2);
|
||
skip(')');
|
||
type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
|
||
type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
|
||
vpushi(is_compatible_types(&type1, &type2));
|
||
}
|
||
break;
|
||
case TOK_builtin_constant_p:
|
||
{
|
||
int saved_nocode_wanted, res;
|
||
next();
|
||
skip('(');
|
||
saved_nocode_wanted = nocode_wanted;
|
||
nocode_wanted = 1;
|
||
gexpr();
|
||
res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||
vpop();
|
||
nocode_wanted = saved_nocode_wanted;
|
||
skip(')');
|
||
vpushi(res);
|
||
}
|
||
break;
|
||
case TOK_INC:
|
||
case TOK_DEC:
|
||
t = tok;
|
||
next();
|
||
unary();
|
||
inc(0, t);
|
||
break;
|
||
case '-':
|
||
next();
|
||
vpushi(0);
|
||
unary();
|
||
gen_op('-');
|
||
break;
|
||
case TOK_LAND:
|
||
if (!gnu_ext)
|
||
goto tok_identifier;
|
||
next();
|
||
/* allow to take the address of a label */
|
||
if (tok < TOK_UIDENT)
|
||
expect("label identifier");
|
||
s = label_find(tok);
|
||
if (!s) {
|
||
s = label_push(&global_label_stack, tok, LABEL_FORWARD);
|
||
} else {
|
||
if (s->r == LABEL_DECLARED)
|
||
s->r = LABEL_FORWARD;
|
||
}
|
||
if (!s->type.t) {
|
||
s->type.t = VT_VOID;
|
||
mk_pointer(&s->type);
|
||
s->type.t |= VT_STATIC;
|
||
}
|
||
vset(&s->type, VT_CONST | VT_SYM, 0);
|
||
vtop->sym = s;
|
||
next();
|
||
break;
|
||
default:
|
||
tok_identifier:
|
||
t = tok;
|
||
next();
|
||
if (t < TOK_UIDENT)
|
||
expect("identifier");
|
||
s = sym_find(t);
|
||
if (!s) {
|
||
if (tok != '(')
|
||
error("'%s' undeclared", get_tok_str(t, NULL));
|
||
/* for simple function calls, we tolerate undeclared
|
||
external reference to int() function */
|
||
if (tcc_state->warn_implicit_function_declaration)
|
||
warning("implicit declaration of function '%s'",
|
||
get_tok_str(t, NULL));
|
||
s = external_global_sym(t, &func_old_type, 0);
|
||
}
|
||
if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) ==
|
||
(VT_STATIC | VT_INLINE | VT_FUNC)) {
|
||
/* if referencing an inline function, then we generate a
|
||
symbol to it if not already done. It will have the
|
||
effect to generate code for it at the end of the
|
||
compilation unit. Inline function as always
|
||
generated in the text section. */
|
||
if (!s->c)
|
||
put_extern_sym(s, text_section, 0, 0);
|
||
r = VT_SYM | VT_CONST;
|
||
} else {
|
||
r = s->r;
|
||
}
|
||
vset(&s->type, r, s->c);
|
||
/* if forward reference, we must point to s */
|
||
if (vtop->r & VT_SYM) {
|
||
vtop->sym = s;
|
||
vtop->c.ul = 0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/* post operations */
|
||
while (1) {
|
||
if (tok == TOK_INC || tok == TOK_DEC) {
|
||
inc(1, tok);
|
||
next();
|
||
} else if (tok == '.' || tok == TOK_ARROW) {
|
||
/* field */
|
||
if (tok == TOK_ARROW)
|
||
indir();
|
||
test_lvalue();
|
||
gaddrof();
|
||
next();
|
||
/* expect pointer on structure */
|
||
if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
|
||
expect("struct or union");
|
||
s = vtop->type.ref;
|
||
/* find field */
|
||
tok |= SYM_FIELD;
|
||
while ((s = s->next) != NULL) {
|
||
if (s->v == tok)
|
||
break;
|
||
}
|
||
if (!s)
|
||
error("field not found");
|
||
/* add field offset to pointer */
|
||
vtop->type = char_pointer_type; /* change type to 'char *' */
|
||
vpushi(s->c);
|
||
gen_op('+');
|
||
/* change type to field type, and set to lvalue */
|
||
vtop->type = s->type;
|
||
/* an array is never an lvalue */
|
||
if (!(vtop->type.t & VT_ARRAY)) {
|
||
vtop->r |= lvalue_type(vtop->type.t);
|
||
/* if bound checking, the referenced pointer must be checked */
|
||
if (do_bounds_check)
|
||
vtop->r |= VT_MUSTBOUND;
|
||
}
|
||
next();
|
||
} else if (tok == '[') {
|
||
next();
|
||
gexpr();
|
||
gen_op('+');
|
||
indir();
|
||
skip(']');
|
||
} else if (tok == '(') {
|
||
SValue ret;
|
||
Sym *sa;
|
||
int nb_args;
|
||
|
||
/* function call */
|
||
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
|
||
/* pointer test (no array accepted) */
|
||
if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
|
||
vtop->type = *pointed_type(&vtop->type);
|
||
if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
|
||
goto error_func;
|
||
} else {
|
||
error_func:
|
||
expect("function pointer");
|
||
}
|
||
} else {
|
||
vtop->r &= ~VT_LVAL; /* no lvalue */
|
||
}
|
||
/* get return type */
|
||
s = vtop->type.ref;
|
||
next();
|
||
sa = s->next; /* first parameter */
|
||
nb_args = 0;
|
||
/* compute first implicit argument if a structure is returned */
|
||
if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
|
||
/* get some space for the returned structure */
|
||
size = type_size(&s->type, &align);
|
||
loc = (loc - size) & -align;
|
||
ret.type = s->type;
|
||
ret.r = VT_LOCAL | VT_LVAL;
|
||
/* pass it as 'int' to avoid structure arg passing
|
||
problems */
|
||
vseti(VT_LOCAL, loc);
|
||
ret.c = vtop->c;
|
||
nb_args++;
|
||
} else {
|
||
ret.type = s->type;
|
||
ret.r2 = VT_CONST;
|
||
/* return in register */
|
||
if (is_float(ret.type.t)) {
|
||
ret.r = REG_FRET;
|
||
} else {
|
||
if ((ret.type.t & VT_BTYPE) == VT_LLONG)
|
||
ret.r2 = REG_LRET;
|
||
ret.r = REG_IRET;
|
||
}
|
||
ret.c.i = 0;
|
||
}
|
||
if (tok != ')') {
|
||
for(;;) {
|
||
expr_eq();
|
||
gfunc_param_typed(s, sa);
|
||
nb_args++;
|
||
if (sa)
|
||
sa = sa->next;
|
||
if (tok == ')')
|
||
break;
|
||
skip(',');
|
||
}
|
||
}
|
||
if (sa)
|
||
error("too few arguments to function");
|
||
skip(')');
|
||
if (!nocode_wanted) {
|
||
gfunc_call(nb_args);
|
||
} else {
|
||
vtop -= (nb_args + 1);
|
||
}
|
||
/* return value */
|
||
vsetc(&ret.type, ret.r, &ret.c);
|
||
vtop->r2 = ret.r2;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void uneq(void)
|
||
{
|
||
int t;
|
||
|
||
unary();
|
||
if (tok == '=' ||
|
||
(tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
|
||
tok == TOK_A_XOR || tok == TOK_A_OR ||
|
||
tok == TOK_A_SHL || tok == TOK_A_SAR) {
|
||
test_lvalue();
|
||
t = tok;
|
||
next();
|
||
if (t == '=') {
|
||
expr_eq();
|
||
} else {
|
||
vdup();
|
||
expr_eq();
|
||
gen_op(t & 0x7f);
|
||
}
|
||
vstore();
|
||
}
|
||
}
|
||
|
||
static void expr_prod(void)
|
||
{
|
||
int t;
|
||
|
||
uneq();
|
||
while (tok == '*' || tok == '/' || tok == '%') {
|
||
t = tok;
|
||
next();
|
||
uneq();
|
||
gen_op(t);
|
||
}
|
||
}
|
||
|
||
static void expr_sum(void)
|
||
{
|
||
int t;
|
||
|
||
expr_prod();
|
||
while (tok == '+' || tok == '-') {
|
||
t = tok;
|
||
next();
|
||
expr_prod();
|
||
gen_op(t);
|
||
}
|
||
}
|
||
|
||
static void expr_shift(void)
|
||
{
|
||
int t;
|
||
|
||
expr_sum();
|
||
while (tok == TOK_SHL || tok == TOK_SAR) {
|
||
t = tok;
|
||
next();
|
||
expr_sum();
|
||
gen_op(t);
|
||
}
|
||
}
|
||
|
||
static void expr_cmp(void)
|
||
{
|
||
int t;
|
||
|
||
expr_shift();
|
||
while ((tok >= TOK_ULE && tok <= TOK_GT) ||
|
||
tok == TOK_ULT || tok == TOK_UGE) {
|
||
t = tok;
|
||
next();
|
||
expr_shift();
|
||
gen_op(t);
|
||
}
|
||
}
|
||
|
||
static void expr_cmpeq(void)
|
||
{
|
||
int t;
|
||
|
||
expr_cmp();
|
||
while (tok == TOK_EQ || tok == TOK_NE) {
|
||
t = tok;
|
||
next();
|
||
expr_cmp();
|
||
gen_op(t);
|
||
}
|
||
}
|
||
|
||
static void expr_and(void)
|
||
{
|
||
expr_cmpeq();
|
||
while (tok == '&') {
|
||
next();
|
||
expr_cmpeq();
|
||
gen_op('&');
|
||
}
|
||
}
|
||
|
||
static void expr_xor(void)
|
||
{
|
||
expr_and();
|
||
while (tok == '^') {
|
||
next();
|
||
expr_and();
|
||
gen_op('^');
|
||
}
|
||
}
|
||
|
||
static void expr_or(void)
|
||
{
|
||
expr_xor();
|
||
while (tok == '|') {
|
||
next();
|
||
expr_xor();
|
||
gen_op('|');
|
||
}
|
||
}
|
||
|
||
/* XXX: fix this mess */
|
||
static void expr_land_const(void)
|
||
{
|
||
expr_or();
|
||
while (tok == TOK_LAND) {
|
||
next();
|
||
expr_or();
|
||
gen_op(TOK_LAND);
|
||
}
|
||
}
|
||
|
||
/* XXX: fix this mess */
|
||
static void expr_lor_const(void)
|
||
{
|
||
expr_land_const();
|
||
while (tok == TOK_LOR) {
|
||
next();
|
||
expr_land_const();
|
||
gen_op(TOK_LOR);
|
||
}
|
||
}
|
||
|
||
/* only used if non constant */
|
||
static void expr_land(void)
|
||
{
|
||
int t;
|
||
|
||
expr_or();
|
||
if (tok == TOK_LAND) {
|
||
t = 0;
|
||
for(;;) {
|
||
t = gtst(1, t);
|
||
if (tok != TOK_LAND) {
|
||
vseti(VT_JMPI, t);
|
||
break;
|
||
}
|
||
next();
|
||
expr_or();
|
||
}
|
||
}
|
||
}
|
||
|
||
static void expr_lor(void)
|
||
{
|
||
int t;
|
||
|
||
expr_land();
|
||
if (tok == TOK_LOR) {
|
||
t = 0;
|
||
for(;;) {
|
||
t = gtst(0, t);
|
||
if (tok != TOK_LOR) {
|
||
vseti(VT_JMP, t);
|
||
break;
|
||
}
|
||
next();
|
||
expr_land();
|
||
}
|
||
}
|
||
}
|
||
|
||
/* XXX: better constant handling */
|
||
static void expr_eq(void)
|
||
{
|
||
int tt, u, r1, r2, rc, t1, t2, bt1, bt2;
|
||
SValue sv;
|
||
CType type, type1, type2;
|
||
|
||
if (const_wanted) {
|
||
int c1, c;
|
||
expr_lor_const();
|
||
if (tok == '?') {
|
||
c = vtop->c.i;
|
||
vpop();
|
||
next();
|
||
if (tok == ':' && gnu_ext) {
|
||
c1 = c;
|
||
} else {
|
||
gexpr();
|
||
c1 = vtop->c.i;
|
||
vpop();
|
||
}
|
||
skip(':');
|
||
expr_eq();
|
||
if (c)
|
||
vtop->c.i = c1;
|
||
}
|
||
} else {
|
||
expr_lor();
|
||
if (tok == '?') {
|
||
next();
|
||
if (vtop != vstack) {
|
||
/* needed to avoid having different registers saved in
|
||
each branch */
|
||
if (is_float(vtop->type.t))
|
||
rc = RC_FLOAT;
|
||
else
|
||
rc = RC_INT;
|
||
gv(rc);
|
||
save_regs(1);
|
||
}
|
||
if (tok == ':' && gnu_ext) {
|
||
gv_dup();
|
||
tt = gtst(1, 0);
|
||
} else {
|
||
tt = gtst(1, 0);
|
||
gexpr();
|
||
}
|
||
type1 = vtop->type;
|
||
sv = *vtop; /* save value to handle it later */
|
||
vtop--; /* no vpop so that FP stack is not flushed */
|
||
skip(':');
|
||
u = gjmp(0);
|
||
gsym(tt);
|
||
expr_eq();
|
||
type2 = vtop->type;
|
||
|
||
t1 = type1.t;
|
||
bt1 = t1 & VT_BTYPE;
|
||
t2 = type2.t;
|
||
bt2 = t2 & VT_BTYPE;
|
||
/* cast operands to correct type according to ISOC rules */
|
||
if (is_float(bt1) || is_float(bt2)) {
|
||
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
|
||
type.t = VT_LDOUBLE;
|
||
} else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
|
||
type.t = VT_DOUBLE;
|
||
} else {
|
||
type.t = VT_FLOAT;
|
||
}
|
||
} else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
|
||
/* cast to biggest op */
|
||
type.t = VT_LLONG;
|
||
/* convert to unsigned if it does not fit in a long long */
|
||
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) ||
|
||
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
|
||
type.t |= VT_UNSIGNED;
|
||
} else if (bt1 == VT_PTR || bt2 == VT_PTR) {
|
||
/* XXX: test pointer compatibility */
|
||
type = type1;
|
||
} else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
|
||
/* XXX: test structure compatibility */
|
||
type = type1;
|
||
} else if (bt1 == VT_VOID || bt2 == VT_VOID) {
|
||
/* NOTE: as an extension, we accept void on only one side */
|
||
type.t = VT_VOID;
|
||
} else {
|
||
/* integer operations */
|
||
type.t = VT_INT;
|
||
/* convert to unsigned if it does not fit in an integer */
|
||
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) ||
|
||
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED))
|
||
type.t |= VT_UNSIGNED;
|
||
}
|
||
|
||
/* now we convert second operand */
|
||
gen_cast(&type);
|
||
rc = RC_INT;
|
||
if (is_float(type.t)) {
|
||
rc = RC_FLOAT;
|
||
} else if ((type.t & VT_BTYPE) == VT_LLONG) {
|
||
/* for long longs, we use fixed registers to avoid having
|
||
to handle a complicated move */
|
||
rc = RC_IRET;
|
||
}
|
||
|
||
r2 = gv(rc);
|
||
/* this is horrible, but we must also convert first
|
||
operand */
|
||
tt = gjmp(0);
|
||
gsym(u);
|
||
/* put again first value and cast it */
|
||
*vtop = sv;
|
||
gen_cast(&type);
|
||
r1 = gv(rc);
|
||
move_reg(r2, r1);
|
||
vtop->r = r2;
|
||
gsym(tt);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void gexpr(void)
|
||
{
|
||
while (1) {
|
||
expr_eq();
|
||
if (tok != ',')
|
||
break;
|
||
vpop();
|
||
next();
|
||
}
|
||
}
|
||
|
||
/* parse an expression and return its type without any side effect. */
|
||
static void expr_type(CType *type)
|
||
{
|
||
int saved_nocode_wanted;
|
||
|
||
saved_nocode_wanted = nocode_wanted;
|
||
nocode_wanted = 1;
|
||
gexpr();
|
||
*type = vtop->type;
|
||
vpop();
|
||
nocode_wanted = saved_nocode_wanted;
|
||
}
|
||
|
||
/* parse a unary expression and return its type without any side
|
||
effect. */
|
||
static void unary_type(CType *type)
|
||
{
|
||
int a;
|
||
|
||
a = nocode_wanted;
|
||
nocode_wanted = 1;
|
||
unary();
|
||
*type = vtop->type;
|
||
vpop();
|
||
nocode_wanted = a;
|
||
}
|
||
|
||
/* parse a constant expression and return value in vtop. */
|
||
static void expr_const1(void)
|
||
{
|
||
int a;
|
||
a = const_wanted;
|
||
const_wanted = 1;
|
||
expr_eq();
|
||
const_wanted = a;
|
||
}
|
||
|
||
/* parse an integer constant and return its value. */
|
||
static int expr_const(void)
|
||
{
|
||
int c;
|
||
expr_const1();
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
|
||
expect("constant expression");
|
||
c = vtop->c.i;
|
||
vpop();
|
||
return c;
|
||
}
|
||
|
||
/* return the label token if current token is a label, otherwise
|
||
return zero */
|
||
static int is_label(void)
|
||
{
|
||
int last_tok;
|
||
|
||
/* fast test first */
|
||
if (tok < TOK_UIDENT)
|
||
return 0;
|
||
/* no need to save tokc because tok is an identifier */
|
||
last_tok = tok;
|
||
next();
|
||
if (tok == ':') {
|
||
next();
|
||
return last_tok;
|
||
} else {
|
||
unget_tok(last_tok);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
||
int case_reg, int is_expr)
|
||
{
|
||
int a, b, c, d;
|
||
Sym *s;
|
||
|
||
/* generate line number info */
|
||
if (do_debug &&
|
||
(last_line_num != file->line_num || last_ind != ind)) {
|
||
put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
|
||
last_ind = ind;
|
||
last_line_num = file->line_num;
|
||
}
|
||
|
||
if (is_expr) {
|
||
/* default return value is (void) */
|
||
vpushi(0);
|
||
vtop->type.t = VT_VOID;
|
||
}
|
||
|
||
if (tok == TOK_IF) {
|
||
/* if test */
|
||
next();
|
||
skip('(');
|
||
gexpr();
|
||
skip(')');
|
||
a = gtst(1, 0);
|
||
block(bsym, csym, case_sym, def_sym, case_reg, 0);
|
||
c = tok;
|
||
if (c == TOK_ELSE) {
|
||
next();
|
||
d = gjmp(0);
|
||
gsym(a);
|
||
block(bsym, csym, case_sym, def_sym, case_reg, 0);
|
||
gsym(d); /* patch else jmp */
|
||
} else
|
||
gsym(a);
|
||
} else if (tok == TOK_WHILE) {
|
||
next();
|
||
d = ind;
|
||
skip('(');
|
||
gexpr();
|
||
skip(')');
|
||
a = gtst(1, 0);
|
||
b = 0;
|
||
block(&a, &b, case_sym, def_sym, case_reg, 0);
|
||
gjmp_addr(d);
|
||
gsym(a);
|
||
gsym_addr(b, d);
|
||
} else if (tok == '{') {
|
||
Sym *llabel;
|
||
|
||
next();
|
||
/* record local declaration stack position */
|
||
s = local_stack;
|
||
llabel = local_label_stack;
|
||
/* handle local labels declarations */
|
||
if (tok == TOK_LABEL) {
|
||
next();
|
||
for(;;) {
|
||
if (tok < TOK_UIDENT)
|
||
expect("label identifier");
|
||
label_push(&local_label_stack, tok, LABEL_DECLARED);
|
||
next();
|
||
if (tok == ',') {
|
||
next();
|
||
} else {
|
||
skip(';');
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
while (tok != '}') {
|
||
decl(VT_LOCAL);
|
||
if (tok != '}') {
|
||
if (is_expr)
|
||
vpop();
|
||
block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
|
||
}
|
||
}
|
||
/* pop locally defined labels */
|
||
label_pop(&local_label_stack, llabel);
|
||
/* pop locally defined symbols */
|
||
sym_pop(&local_stack, s);
|
||
next();
|
||
} else if (tok == TOK_RETURN) {
|
||
next();
|
||
if (tok != ';') {
|
||
gexpr();
|
||
gen_assign_cast(&func_vt);
|
||
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
|
||
CType type;
|
||
/* if returning structure, must copy it to implicit
|
||
first pointer arg location */
|
||
type = func_vt;
|
||
mk_pointer(&type);
|
||
vset(&type, VT_LOCAL | VT_LVAL, func_vc);
|
||
indir();
|
||
vswap();
|
||
/* copy structure value to pointer */
|
||
vstore();
|
||
} else if (is_float(func_vt.t)) {
|
||
gv(RC_FRET);
|
||
} else {
|
||
gv(RC_IRET);
|
||
}
|
||
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
|
||
}
|
||
skip(';');
|
||
rsym = gjmp(rsym); /* jmp */
|
||
} else if (tok == TOK_BREAK) {
|
||
/* compute jump */
|
||
if (!bsym)
|
||
error("cannot break");
|
||
*bsym = gjmp(*bsym);
|
||
next();
|
||
skip(';');
|
||
} else if (tok == TOK_CONTINUE) {
|
||
/* compute jump */
|
||
if (!csym)
|
||
error("cannot continue");
|
||
*csym = gjmp(*csym);
|
||
next();
|
||
skip(';');
|
||
} else if (tok == TOK_FOR) {
|
||
int e;
|
||
next();
|
||
skip('(');
|
||
if (tok != ';') {
|
||
gexpr();
|
||
vpop();
|
||
}
|
||
skip(';');
|
||
d = ind;
|
||
c = ind;
|
||
a = 0;
|
||
b = 0;
|
||
if (tok != ';') {
|
||
gexpr();
|
||
a = gtst(1, 0);
|
||
}
|
||
skip(';');
|
||
if (tok != ')') {
|
||
e = gjmp(0);
|
||
c = ind;
|
||
gexpr();
|
||
vpop();
|
||
gjmp_addr(d);
|
||
gsym(e);
|
||
}
|
||
skip(')');
|
||
block(&a, &b, case_sym, def_sym, case_reg, 0);
|
||
gjmp_addr(c);
|
||
gsym(a);
|
||
gsym_addr(b, c);
|
||
} else
|
||
if (tok == TOK_DO) {
|
||
next();
|
||
a = 0;
|
||
b = 0;
|
||
d = ind;
|
||
block(&a, &b, case_sym, def_sym, case_reg, 0);
|
||
skip(TOK_WHILE);
|
||
skip('(');
|
||
gsym(b);
|
||
gexpr();
|
||
c = gtst(0, 0);
|
||
gsym_addr(c, d);
|
||
skip(')');
|
||
gsym(a);
|
||
skip(';');
|
||
} else
|
||
if (tok == TOK_SWITCH) {
|
||
next();
|
||
skip('(');
|
||
gexpr();
|
||
/* XXX: other types than integer */
|
||
case_reg = gv(RC_INT);
|
||
vpop();
|
||
skip(')');
|
||
a = 0;
|
||
b = gjmp(0); /* jump to first case */
|
||
c = 0;
|
||
block(&a, csym, &b, &c, case_reg, 0);
|
||
/* if no default, jmp after switch */
|
||
if (c == 0)
|
||
c = ind;
|
||
/* default label */
|
||
gsym_addr(b, c);
|
||
/* break label */
|
||
gsym(a);
|
||
} else
|
||
if (tok == TOK_CASE) {
|
||
int v1, v2;
|
||
if (!case_sym)
|
||
expect("switch");
|
||
next();
|
||
v1 = expr_const();
|
||
v2 = v1;
|
||
if (gnu_ext && tok == TOK_DOTS) {
|
||
next();
|
||
v2 = expr_const();
|
||
if (v2 < v1)
|
||
warning("empty case range");
|
||
}
|
||
/* since a case is like a label, we must skip it with a jmp */
|
||
b = gjmp(0);
|
||
gsym(*case_sym);
|
||
vseti(case_reg, 0);
|
||
vpushi(v1);
|
||
if (v1 == v2) {
|
||
gen_op(TOK_EQ);
|
||
*case_sym = gtst(1, 0);
|
||
} else {
|
||
gen_op(TOK_GE);
|
||
*case_sym = gtst(1, 0);
|
||
vseti(case_reg, 0);
|
||
vpushi(v2);
|
||
gen_op(TOK_LE);
|
||
*case_sym = gtst(1, *case_sym);
|
||
}
|
||
gsym(b);
|
||
skip(':');
|
||
is_expr = 0;
|
||
goto block_after_label;
|
||
} else
|
||
if (tok == TOK_DEFAULT) {
|
||
next();
|
||
skip(':');
|
||
if (!def_sym)
|
||
expect("switch");
|
||
if (*def_sym)
|
||
error("too many 'default'");
|
||
*def_sym = ind;
|
||
is_expr = 0;
|
||
goto block_after_label;
|
||
} else
|
||
if (tok == TOK_GOTO) {
|
||
next();
|
||
if (tok == '*' && gnu_ext) {
|
||
/* computed goto */
|
||
next();
|
||
gexpr();
|
||
if ((vtop->type.t & VT_BTYPE) != VT_PTR)
|
||
expect("pointer");
|
||
ggoto();
|
||
} else if (tok >= TOK_UIDENT) {
|
||
s = label_find(tok);
|
||
/* put forward definition if needed */
|
||
if (!s) {
|
||
s = label_push(&global_label_stack, tok, LABEL_FORWARD);
|
||
} else {
|
||
if (s->r == LABEL_DECLARED)
|
||
s->r = LABEL_FORWARD;
|
||
}
|
||
/* label already defined */
|
||
if (s->r & LABEL_FORWARD)
|
||
s->next = (void *)gjmp((long)s->next);
|
||
else
|
||
gjmp_addr((long)s->next);
|
||
next();
|
||
} else {
|
||
expect("label identifier");
|
||
}
|
||
skip(';');
|
||
} else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
|
||
asm_instr();
|
||
} else {
|
||
b = is_label();
|
||
if (b) {
|
||
/* label case */
|
||
s = label_find(b);
|
||
if (s) {
|
||
if (s->r == LABEL_DEFINED)
|
||
error("duplicate label '%s'", get_tok_str(s->v, NULL));
|
||
gsym((long)s->next);
|
||
s->r = LABEL_DEFINED;
|
||
} else {
|
||
s = label_push(&global_label_stack, b, LABEL_DEFINED);
|
||
}
|
||
s->next = (void *)ind;
|
||
/* we accept this, but it is a mistake */
|
||
block_after_label:
|
||
if (tok == '}') {
|
||
warning("deprecated use of label at end of compound statement");
|
||
} else {
|
||
if (is_expr)
|
||
vpop();
|
||
block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
|
||
}
|
||
} else {
|
||
/* expression case */
|
||
if (tok != ';') {
|
||
if (is_expr) {
|
||
vpop();
|
||
gexpr();
|
||
} else {
|
||
gexpr();
|
||
vpop();
|
||
}
|
||
}
|
||
skip(';');
|
||
}
|
||
}
|
||
}
|
||
|
||
/* t is the array or struct type. c is the array or struct
|
||
address. cur_index/cur_field is the pointer to the current
|
||
value. 'size_only' is true if only size info is needed (only used
|
||
in arrays) */
|
||
static void decl_designator(CType *type, Section *sec, unsigned long c,
|
||
int *cur_index, Sym **cur_field,
|
||
int size_only)
|
||
{
|
||
Sym *s, *f;
|
||
int notfirst, index, index_last, align, l, nb_elems, elem_size;
|
||
CType type1;
|
||
|
||
notfirst = 0;
|
||
elem_size = 0;
|
||
nb_elems = 1;
|
||
if (gnu_ext && (l = is_label()) != 0)
|
||
goto struct_field;
|
||
while (tok == '[' || tok == '.') {
|
||
if (tok == '[') {
|
||
if (!(type->t & VT_ARRAY))
|
||
expect("array type");
|
||
s = type->ref;
|
||
next();
|
||
index = expr_const();
|
||
if (index < 0 || (s->c >= 0 && index >= s->c))
|
||
expect("invalid index");
|
||
if (tok == TOK_DOTS && gnu_ext) {
|
||
next();
|
||
index_last = expr_const();
|
||
if (index_last < 0 ||
|
||
(s->c >= 0 && index_last >= s->c) ||
|
||
index_last < index)
|
||
expect("invalid index");
|
||
} else {
|
||
index_last = index;
|
||
}
|
||
skip(']');
|
||
if (!notfirst)
|
||
*cur_index = index_last;
|
||
type = pointed_type(type);
|
||
elem_size = type_size(type, &align);
|
||
c += index * elem_size;
|
||
/* NOTE: we only support ranges for last designator */
|
||
nb_elems = index_last - index + 1;
|
||
if (nb_elems != 1) {
|
||
notfirst = 1;
|
||
break;
|
||
}
|
||
} else {
|
||
next();
|
||
l = tok;
|
||
next();
|
||
struct_field:
|
||
if ((type->t & VT_BTYPE) != VT_STRUCT)
|
||
expect("struct/union type");
|
||
s = type->ref;
|
||
l |= SYM_FIELD;
|
||
f = s->next;
|
||
while (f) {
|
||
if (f->v == l)
|
||
break;
|
||
f = f->next;
|
||
}
|
||
if (!f)
|
||
expect("field");
|
||
if (!notfirst)
|
||
*cur_field = f;
|
||
/* XXX: fix this mess by using explicit storage field */
|
||
type1 = f->type;
|
||
type1.t |= (type->t & ~VT_TYPE);
|
||
type = &type1;
|
||
c += f->c;
|
||
}
|
||
notfirst = 1;
|
||
}
|
||
if (notfirst) {
|
||
if (tok == '=') {
|
||
next();
|
||
} else {
|
||
if (!gnu_ext)
|
||
expect("=");
|
||
}
|
||
} else {
|
||
if (type->t & VT_ARRAY) {
|
||
index = *cur_index;
|
||
type = pointed_type(type);
|
||
c += index * type_size(type, &align);
|
||
} else {
|
||
f = *cur_field;
|
||
if (!f)
|
||
error("too many field init");
|
||
/* XXX: fix this mess by using explicit storage field */
|
||
type1 = f->type;
|
||
type1.t |= (type->t & ~VT_TYPE);
|
||
type = &type1;
|
||
c += f->c;
|
||
}
|
||
}
|
||
decl_initializer(type, sec, c, 0, size_only);
|
||
|
||
/* XXX: make it more general */
|
||
if (!size_only && nb_elems > 1) {
|
||
unsigned long c_end;
|
||
uint8_t *src, *dst;
|
||
int i;
|
||
|
||
if (!sec)
|
||
error("range init not supported yet for dynamic storage");
|
||
c_end = c + nb_elems * elem_size;
|
||
if (c_end > sec->data_allocated)
|
||
section_realloc(sec, c_end);
|
||
src = sec->data + c;
|
||
dst = src;
|
||
for(i = 1; i < nb_elems; i++) {
|
||
dst += elem_size;
|
||
memcpy(dst, src, elem_size);
|
||
}
|
||
}
|
||
}
|
||
|
||
#define EXPR_VAL 0
|
||
#define EXPR_CONST 1
|
||
#define EXPR_ANY 2
|
||
|
||
/* store a value or an expression directly in global data or in local array */
|
||
static void init_putv(CType *type, Section *sec, unsigned long c,
|
||
int v, int expr_type)
|
||
{
|
||
int saved_global_expr, bt, bit_pos, bit_size;
|
||
void *ptr;
|
||
unsigned long long bit_mask;
|
||
CType dtype;
|
||
|
||
switch(expr_type) {
|
||
case EXPR_VAL:
|
||
vpushi(v);
|
||
break;
|
||
case EXPR_CONST:
|
||
/* compound literals must be allocated globally in this case */
|
||
saved_global_expr = global_expr;
|
||
global_expr = 1;
|
||
expr_const1();
|
||
global_expr = saved_global_expr;
|
||
/* NOTE: symbols are accepted */
|
||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST)
|
||
error("initializer element is not constant");
|
||
break;
|
||
case EXPR_ANY:
|
||
expr_eq();
|
||
break;
|
||
}
|
||
|
||
dtype = *type;
|
||
dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
|
||
|
||
if (sec) {
|
||
/* XXX: not portable */
|
||
/* XXX: generate error if incorrect relocation */
|
||
gen_assign_cast(&dtype);
|
||
bt = type->t & VT_BTYPE;
|
||
ptr = sec->data + c;
|
||
/* XXX: make code faster ? */
|
||
if (!(type->t & VT_BITFIELD)) {
|
||
bit_pos = 0;
|
||
bit_size = 32;
|
||
bit_mask = -1LL;
|
||
} else {
|
||
bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
|
||
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
|
||
bit_mask = (1LL << bit_size) - 1;
|
||
}
|
||
if ((vtop->r & VT_SYM) &&
|
||
(bt == VT_BYTE ||
|
||
bt == VT_SHORT ||
|
||
bt == VT_DOUBLE ||
|
||
bt == VT_LDOUBLE ||
|
||
bt == VT_LLONG ||
|
||
(bt == VT_INT && bit_size != 32)))
|
||
error("initializer element is not computable at load time");
|
||
switch(bt) {
|
||
case VT_BYTE:
|
||
*(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
|
||
break;
|
||
case VT_SHORT:
|
||
*(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
|
||
break;
|
||
case VT_DOUBLE:
|
||
*(double *)ptr = vtop->c.d;
|
||
break;
|
||
case VT_LDOUBLE:
|
||
*(long double *)ptr = vtop->c.ld;
|
||
break;
|
||
case VT_LLONG:
|
||
*(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos;
|
||
break;
|
||
default:
|
||
if (vtop->r & VT_SYM) {
|
||
greloc(sec, vtop->sym, c, R_DATA_32);
|
||
}
|
||
*(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
|
||
break;
|
||
}
|
||
vtop--;
|
||
} else {
|
||
vset(&dtype, VT_LOCAL, c);
|
||
vswap();
|
||
vstore();
|
||
vpop();
|
||
}
|
||
}
|
||
|
||
/* put zeros for variable based init */
|
||
static void init_putz(CType *t, Section *sec, unsigned long c, int size)
|
||
{
|
||
if (sec) {
|
||
/* nothing to do because globals are already set to zero */
|
||
} else {
|
||
vpush_global_sym(&func_old_type, TOK_memset);
|
||
vseti(VT_LOCAL, c);
|
||
vpushi(0);
|
||
vpushi(size);
|
||
gfunc_call(3);
|
||
}
|
||
}
|
||
|
||
/* 't' contains the type and storage info. 'c' is the offset of the
|
||
object in section 'sec'. If 'sec' is NULL, it means stack based
|
||
allocation. 'first' is true if array '{' must be read (multi
|
||
dimension implicit array init handling). 'size_only' is true if
|
||
size only evaluation is wanted (only for arrays). */
|
||
static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
||
int first, int size_only)
|
||
{
|
||
int index, array_length, n, no_oblock, nb, parlevel, i;
|
||
int size1, align1, expr_type;
|
||
Sym *s, *f;
|
||
CType *t1;
|
||
|
||
if (type->t & VT_ARRAY) {
|
||
s = type->ref;
|
||
n = s->c;
|
||
array_length = 0;
|
||
t1 = pointed_type(type);
|
||
size1 = type_size(t1, &align1);
|
||
|
||
no_oblock = 1;
|
||
if ((first && tok != TOK_LSTR && tok != TOK_STR) ||
|
||
tok == '{') {
|
||
skip('{');
|
||
no_oblock = 0;
|
||
}
|
||
|
||
/* only parse strings here if correct type (otherwise: handle
|
||
them as ((w)char *) expressions */
|
||
if ((tok == TOK_LSTR &&
|
||
(t1->t & VT_BTYPE) == VT_INT) ||
|
||
(tok == TOK_STR &&
|
||
(t1->t & VT_BTYPE) == VT_BYTE)) {
|
||
while (tok == TOK_STR || tok == TOK_LSTR) {
|
||
int cstr_len, ch;
|
||
CString *cstr;
|
||
|
||
cstr = tokc.cstr;
|
||
/* compute maximum number of chars wanted */
|
||
if (tok == TOK_STR)
|
||
cstr_len = cstr->size;
|
||
else
|
||
cstr_len = cstr->size / sizeof(int);
|
||
cstr_len--;
|
||
nb = cstr_len;
|
||
if (n >= 0 && nb > (n - array_length))
|
||
nb = n - array_length;
|
||
if (!size_only) {
|
||
if (cstr_len > nb)
|
||
warning("initializer-string for array is too long");
|
||
/* in order to go faster for common case (char
|
||
string in global variable, we handle it
|
||
specifically */
|
||
if (sec && tok == TOK_STR && size1 == 1) {
|
||
memcpy(sec->data + c + array_length, cstr->data, nb);
|
||
} else {
|
||
for(i=0;i<nb;i++) {
|
||
if (tok == TOK_STR)
|
||
ch = ((unsigned char *)cstr->data)[i];
|
||
else
|
||
ch = ((int *)cstr->data)[i];
|
||
init_putv(t1, sec, c + (array_length + i) * size1,
|
||
ch, EXPR_VAL);
|
||
}
|
||
}
|
||
}
|
||
array_length += nb;
|
||
next();
|
||
}
|
||
/* only add trailing zero if enough storage (no
|
||
warning in this case since it is standard) */
|
||
if (n < 0 || array_length < n) {
|
||
if (!size_only) {
|
||
init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL);
|
||
}
|
||
array_length++;
|
||
}
|
||
} else {
|
||
index = 0;
|
||
while (tok != '}') {
|
||
decl_designator(type, sec, c, &index, NULL, size_only);
|
||
if (n >= 0 && index >= n)
|
||
error("index too large");
|
||
/* must put zero in holes (note that doing it that way
|
||
ensures that it even works with designators) */
|
||
if (!size_only && array_length < index) {
|
||
init_putz(t1, sec, c + array_length * size1,
|
||
(index - array_length) * size1);
|
||
}
|
||
index++;
|
||
if (index > array_length)
|
||
array_length = index;
|
||
/* special test for multi dimensional arrays (may not
|
||
be strictly correct if designators are used at the
|
||
same time) */
|
||
if (index >= n && no_oblock)
|
||
break;
|
||
if (tok == '}')
|
||
break;
|
||
skip(',');
|
||
}
|
||
}
|
||
if (!no_oblock)
|
||
skip('}');
|
||
/* put zeros at the end */
|
||
if (!size_only && n >= 0 && array_length < n) {
|
||
init_putz(t1, sec, c + array_length * size1,
|
||
(n - array_length) * size1);
|
||
}
|
||
/* patch type size if needed */
|
||
if (n < 0)
|
||
s->c = array_length;
|
||
} else if ((type->t & VT_BTYPE) == VT_STRUCT &&
|
||
(sec || !first || tok == '{')) {
|
||
int par_count;
|
||
|
||
/* NOTE: the previous test is a specific case for automatic
|
||
struct/union init */
|
||
/* XXX: union needs only one init */
|
||
|
||
/* XXX: this test is incorrect for local initializers
|
||
beginning with ( without {. It would be much more difficult
|
||
to do it correctly (ideally, the expression parser should
|
||
be used in all cases) */
|
||
par_count = 0;
|
||
if (tok == '(') {
|
||
AttributeDef ad1;
|
||
CType type1;
|
||
next();
|
||
while (tok == '(') {
|
||
par_count++;
|
||
next();
|
||
}
|
||
if (!parse_btype(&type1, &ad1))
|
||
expect("cast");
|
||
type_decl(&type1, &ad1, &n, TYPE_ABSTRACT);
|
||
#if 0
|
||
if (!is_assignable_types(type, &type1))
|
||
error("invalid type for cast");
|
||
#endif
|
||
skip(')');
|
||
}
|
||
no_oblock = 1;
|
||
if (first || tok == '{') {
|
||
skip('{');
|
||
no_oblock = 0;
|
||
}
|
||
s = type->ref;
|
||
f = s->next;
|
||
array_length = 0;
|
||
index = 0;
|
||
n = s->c;
|
||
while (tok != '}') {
|
||
decl_designator(type, sec, c, NULL, &f, size_only);
|
||
index = f->c;
|
||
if (!size_only && array_length < index) {
|
||
init_putz(type, sec, c + array_length,
|
||
index - array_length);
|
||
}
|
||
index = index + type_size(&f->type, &align1);
|
||
if (index > array_length)
|
||
array_length = index;
|
||
f = f->next;
|
||
if (no_oblock && f == NULL)
|
||
break;
|
||
if (tok == '}')
|
||
break;
|
||
skip(',');
|
||
}
|
||
/* put zeros at the end */
|
||
if (!size_only && array_length < n) {
|
||
init_putz(type, sec, c + array_length,
|
||
n - array_length);
|
||
}
|
||
if (!no_oblock)
|
||
skip('}');
|
||
while (par_count) {
|
||
skip(')');
|
||
par_count--;
|
||
}
|
||
} else if (tok == '{') {
|
||
next();
|
||
decl_initializer(type, sec, c, first, size_only);
|
||
skip('}');
|
||
} else if (size_only) {
|
||
/* just skip expression */
|
||
parlevel = 0;
|
||
while ((parlevel > 0 || (tok != '}' && tok != ',')) &&
|
||
tok != -1) {
|
||
if (tok == '(')
|
||
parlevel++;
|
||
else if (tok == ')')
|
||
parlevel--;
|
||
next();
|
||
}
|
||
} else {
|
||
/* currently, we always use constant expression for globals
|
||
(may change for scripting case) */
|
||
expr_type = EXPR_CONST;
|
||
if (!sec)
|
||
expr_type = EXPR_ANY;
|
||
init_putv(type, sec, c, 0, expr_type);
|
||
}
|
||
}
|
||
|
||
/* parse an initializer for type 't' if 'has_init' is non zero, and
|
||
allocate space in local or global data space ('r' is either
|
||
VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
|
||
variable 'v' of scope 'scope' is declared before initializers are
|
||
parsed. If 'v' is zero, then a reference to the new object is put
|
||
in the value stack. If 'has_init' is 2, a special parsing is done
|
||
to handle string constants. */
|
||
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||
int has_init, int v, int scope)
|
||
{
|
||
int size, align, addr, data_offset;
|
||
int level;
|
||
ParseState saved_parse_state;
|
||
TokenString init_str;
|
||
Section *sec;
|
||
|
||
size = type_size(type, &align);
|
||
/* If unknown size, we must evaluate it before
|
||
evaluating initializers because
|
||
initializers can generate global data too
|
||
(e.g. string pointers or ISOC99 compound
|
||
literals). It also simplifies local
|
||
initializers handling */
|
||
tok_str_new(&init_str);
|
||
if (size < 0) {
|
||
if (!has_init)
|
||
error("unknown type size");
|
||
/* get all init string */
|
||
if (has_init == 2) {
|
||
/* only get strings */
|
||
while (tok == TOK_STR || tok == TOK_LSTR) {
|
||
tok_str_add_tok(&init_str);
|
||
next();
|
||
}
|
||
} else {
|
||
level = 0;
|
||
while (level > 0 || (tok != ',' && tok != ';')) {
|
||
if (tok < 0)
|
||
error("unexpected end of file in initializer");
|
||
tok_str_add_tok(&init_str);
|
||
if (tok == '{')
|
||
level++;
|
||
else if (tok == '}') {
|
||
if (level == 0)
|
||
break;
|
||
level--;
|
||
}
|
||
next();
|
||
}
|
||
}
|
||
tok_str_add(&init_str, -1);
|
||
tok_str_add(&init_str, 0);
|
||
|
||
/* compute size */
|
||
save_parse_state(&saved_parse_state);
|
||
|
||
macro_ptr = init_str.str;
|
||
next();
|
||
decl_initializer(type, NULL, 0, 1, 1);
|
||
/* prepare second initializer parsing */
|
||
macro_ptr = init_str.str;
|
||
next();
|
||
|
||
/* if still unknown size, error */
|
||
size = type_size(type, &align);
|
||
if (size < 0)
|
||
error("unknown type size");
|
||
}
|
||
/* take into account specified alignment if bigger */
|
||
if (ad->aligned) {
|
||
if (ad->aligned > align)
|
||
align = ad->aligned;
|
||
} else if (ad->packed) {
|
||
align = 1;
|
||
}
|
||
if ((r & VT_VALMASK) == VT_LOCAL) {
|
||
sec = NULL;
|
||
if (do_bounds_check && (type->t & VT_ARRAY))
|
||
loc--;
|
||
loc = (loc - size) & -align;
|
||
addr = loc;
|
||
/* handles bounds */
|
||
/* XXX: currently, since we do only one pass, we cannot track
|
||
'&' operators, so we add only arrays */
|
||
if (do_bounds_check && (type->t & VT_ARRAY)) {
|
||
unsigned long *bounds_ptr;
|
||
/* add padding between regions */
|
||
loc--;
|
||
/* then add local bound info */
|
||
bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(unsigned long));
|
||
bounds_ptr[0] = addr;
|
||
bounds_ptr[1] = size;
|
||
}
|
||
if (v) {
|
||
/* local variable */
|
||
sym_push(v, type, r, addr);
|
||
} else {
|
||
/* push local reference */
|
||
vset(type, r, addr);
|
||
}
|
||
} else {
|
||
Sym *sym;
|
||
|
||
sym = NULL;
|
||
if (v && scope == VT_CONST) {
|
||
/* see if the symbol was already defined */
|
||
sym = sym_find(v);
|
||
if (sym) {
|
||
if (!is_compatible_types(&sym->type, type))
|
||
error("incompatible types for redefinition of '%s'",
|
||
get_tok_str(v, NULL));
|
||
if (sym->type.t & VT_EXTERN) {
|
||
/* if the variable is extern, it was not allocated */
|
||
sym->type.t &= ~VT_EXTERN;
|
||
/* set array size if it was ommited in extern
|
||
declaration */
|
||
if ((sym->type.t & VT_ARRAY) &&
|
||
sym->type.ref->c < 0 &&
|
||
type->ref->c >= 0)
|
||
sym->type.ref->c = type->ref->c;
|
||
} else {
|
||
/* we accept several definitions of the same
|
||
global variable. this is tricky, because we
|
||
must play with the SHN_COMMON type of the symbol */
|
||
/* XXX: should check if the variable was already
|
||
initialized. It is incorrect to initialized it
|
||
twice */
|
||
/* no init data, we won't add more to the symbol */
|
||
if (!has_init)
|
||
goto no_alloc;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* allocate symbol in corresponding section */
|
||
sec = ad->section;
|
||
if (!sec) {
|
||
if (has_init)
|
||
sec = data_section;
|
||
else if (tcc_state->nocommon)
|
||
sec = bss_section;
|
||
}
|
||
if (sec) {
|
||
data_offset = sec->data_offset;
|
||
data_offset = (data_offset + align - 1) & -align;
|
||
addr = data_offset;
|
||
/* very important to increment global pointer at this time
|
||
because initializers themselves can create new initializers */
|
||
data_offset += size;
|
||
/* add padding if bound check */
|
||
if (do_bounds_check)
|
||
data_offset++;
|
||
sec->data_offset = data_offset;
|
||
/* allocate section space to put the data */
|
||
if (sec->sh_type != SHT_NOBITS &&
|
||
data_offset > sec->data_allocated)
|
||
section_realloc(sec, data_offset);
|
||
/* align section if needed */
|
||
if (align > sec->sh_addralign)
|
||
sec->sh_addralign = align;
|
||
} else {
|
||
addr = 0; /* avoid warning */
|
||
}
|
||
|
||
if (v) {
|
||
if (scope == VT_CONST) {
|
||
if (!sym)
|
||
goto do_def;
|
||
} else {
|
||
do_def:
|
||
sym = sym_push(v, type, r | VT_SYM, 0);
|
||
}
|
||
/* update symbol definition */
|
||
if (sec) {
|
||
put_extern_sym(sym, sec, addr, size);
|
||
} else {
|
||
Elf32_Sym *esym;
|
||
/* put a common area */
|
||
put_extern_sym(sym, NULL, align, size);
|
||
/* XXX: find a nicer way */
|
||
esym = &((Elf32_Sym *)symtab_section->data)[sym->c];
|
||
esym->st_shndx = SHN_COMMON;
|
||
}
|
||
} else {
|
||
CValue cval;
|
||
|
||
/* push global reference */
|
||
sym = get_sym_ref(type, sec, addr, size);
|
||
cval.ul = 0;
|
||
vsetc(type, VT_CONST | VT_SYM, &cval);
|
||
vtop->sym = sym;
|
||
}
|
||
|
||
/* handles bounds now because the symbol must be defined
|
||
before for the relocation */
|
||
if (do_bounds_check) {
|
||
unsigned long *bounds_ptr;
|
||
|
||
greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_32);
|
||
/* then add global bound info */
|
||
bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(long));
|
||
bounds_ptr[0] = 0; /* relocated */
|
||
bounds_ptr[1] = size;
|
||
}
|
||
}
|
||
if (has_init) {
|
||
decl_initializer(type, sec, addr, 1, 0);
|
||
/* restore parse state if needed */
|
||
if (init_str.str) {
|
||
tok_str_free(init_str.str);
|
||
restore_parse_state(&saved_parse_state);
|
||
}
|
||
}
|
||
no_alloc: ;
|
||
}
|
||
|
||
void put_func_debug(Sym *sym)
|
||
{
|
||
char buf[512];
|
||
|
||
/* stabs info */
|
||
/* XXX: we put here a dummy type */
|
||
snprintf(buf, sizeof(buf), "%s:%c1",
|
||
funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
|
||
put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
|
||
cur_text_section, sym->c);
|
||
last_ind = 0;
|
||
last_line_num = 0;
|
||
}
|
||
|
||
/* parse an old style function declaration list */
|
||
/* XXX: check multiple parameter */
|
||
static void func_decl_list(Sym *func_sym)
|
||
{
|
||
AttributeDef ad;
|
||
int v;
|
||
Sym *s;
|
||
CType btype, type;
|
||
|
||
/* parse each declaration */
|
||
while (tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF) {
|
||
if (!parse_btype(&btype, &ad))
|
||
expect("declaration list");
|
||
if (((btype.t & VT_BTYPE) == VT_ENUM ||
|
||
(btype.t & VT_BTYPE) == VT_STRUCT) &&
|
||
tok == ';') {
|
||
/* we accept no variable after */
|
||
} else {
|
||
for(;;) {
|
||
type = btype;
|
||
type_decl(&type, &ad, &v, TYPE_DIRECT);
|
||
/* find parameter in function parameter list */
|
||
s = func_sym->next;
|
||
while (s != NULL) {
|
||
if ((s->v & ~SYM_FIELD) == v)
|
||
goto found;
|
||
s = s->next;
|
||
}
|
||
error("declaration for parameter '%s' but no such parameter",
|
||
get_tok_str(v, NULL));
|
||
found:
|
||
/* check that no storage specifier except 'register' was given */
|
||
if (type.t & VT_STORAGE)
|
||
error("storage class specified for '%s'", get_tok_str(v, NULL));
|
||
convert_parameter_type(&type);
|
||
/* we can add the type (NOTE: it could be local to the function) */
|
||
s->type = type;
|
||
/* accept other parameters */
|
||
if (tok == ',')
|
||
next();
|
||
else
|
||
break;
|
||
}
|
||
}
|
||
skip(';');
|
||
}
|
||
}
|
||
|
||
/* parse a function defined by symbol 'sym' and generate its code in
|
||
'cur_text_section' */
|
||
static void gen_function(Sym *sym)
|
||
{
|
||
ind = cur_text_section->data_offset;
|
||
/* NOTE: we patch the symbol size later */
|
||
put_extern_sym(sym, cur_text_section, ind, 0);
|
||
funcname = get_tok_str(sym->v, NULL);
|
||
func_ind = ind;
|
||
/* put debug symbol */
|
||
if (do_debug)
|
||
put_func_debug(sym);
|
||
/* push a dummy symbol to enable local sym storage */
|
||
sym_push2(&local_stack, SYM_FIELD, 0, 0);
|
||
gfunc_prolog(&sym->type);
|
||
rsym = 0;
|
||
block(NULL, NULL, NULL, NULL, 0, 0);
|
||
gsym(rsym);
|
||
gfunc_epilog();
|
||
cur_text_section->data_offset = ind;
|
||
label_pop(&global_label_stack, NULL);
|
||
sym_pop(&local_stack, NULL); /* reset local stack */
|
||
/* end of function */
|
||
/* patch symbol size */
|
||
((Elf32_Sym *)symtab_section->data)[sym->c].st_size =
|
||
ind - func_ind;
|
||
if (do_debug) {
|
||
put_stabn(N_FUN, 0, 0, ind - func_ind);
|
||
}
|
||
funcname = ""; /* for safety */
|
||
func_vt.t = VT_VOID; /* for safety */
|
||
ind = 0; /* for safety */
|
||
}
|
||
|
||
static void gen_inline_functions(void)
|
||
{
|
||
Sym *sym;
|
||
CType *type;
|
||
int *str, inline_generated;
|
||
|
||
/* iterate while inline function are referenced */
|
||
for(;;) {
|
||
inline_generated = 0;
|
||
for(sym = global_stack; sym != NULL; sym = sym->prev) {
|
||
type = &sym->type;
|
||
if (((type->t & VT_BTYPE) == VT_FUNC) &&
|
||
(type->t & (VT_STATIC | VT_INLINE)) ==
|
||
(VT_STATIC | VT_INLINE) &&
|
||
sym->c != 0) {
|
||
/* the function was used: generate its code and
|
||
convert it to a normal function */
|
||
str = (int *)sym->r;
|
||
sym->r = VT_SYM | VT_CONST;
|
||
type->t &= ~VT_INLINE;
|
||
|
||
macro_ptr = str;
|
||
next();
|
||
cur_text_section = text_section;
|
||
gen_function(sym);
|
||
macro_ptr = NULL; /* fail safe */
|
||
|
||
tok_str_free(str);
|
||
inline_generated = 1;
|
||
}
|
||
}
|
||
if (!inline_generated)
|
||
break;
|
||
}
|
||
|
||
/* free all remaining inline function tokens */
|
||
for(sym = global_stack; sym != NULL; sym = sym->prev) {
|
||
type = &sym->type;
|
||
if (((type->t & VT_BTYPE) == VT_FUNC) &&
|
||
(type->t & (VT_STATIC | VT_INLINE)) ==
|
||
(VT_STATIC | VT_INLINE)) {
|
||
str = (int *)sym->r;
|
||
tok_str_free(str);
|
||
sym->r = 0; /* fail safe */
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
|
||
static void decl(int l)
|
||
{
|
||
int v, has_init, r;
|
||
CType type, btype;
|
||
Sym *sym;
|
||
AttributeDef ad;
|
||
|
||
while (1) {
|
||
if (!parse_btype(&btype, &ad)) {
|
||
/* skip redundant ';' */
|
||
/* XXX: find more elegant solution */
|
||
if (tok == ';') {
|
||
next();
|
||
continue;
|
||
}
|
||
if (l == VT_CONST &&
|
||
(tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
|
||
/* global asm block */
|
||
asm_global_instr();
|
||
continue;
|
||
}
|
||
/* special test for old K&R protos without explicit int
|
||
type. Only accepted when defining global data */
|
||
if (l == VT_LOCAL || tok < TOK_DEFINE)
|
||
break;
|
||
btype.t = VT_INT;
|
||
}
|
||
if (((btype.t & VT_BTYPE) == VT_ENUM ||
|
||
(btype.t & VT_BTYPE) == VT_STRUCT) &&
|
||
tok == ';') {
|
||
/* we accept no variable after */
|
||
next();
|
||
continue;
|
||
}
|
||
while (1) { /* iterate thru each declaration */
|
||
type = btype;
|
||
type_decl(&type, &ad, &v, TYPE_DIRECT);
|
||
#if 0
|
||
{
|
||
char buf[500];
|
||
type_to_str(buf, sizeof(buf), t, get_tok_str(v, NULL));
|
||
printf("type = '%s'\n", buf);
|
||
}
|
||
#endif
|
||
if ((type.t & VT_BTYPE) == VT_FUNC) {
|
||
/* if old style function prototype, we accept a
|
||
declaration list */
|
||
sym = type.ref;
|
||
if (sym->c == FUNC_OLD)
|
||
func_decl_list(sym);
|
||
}
|
||
|
||
if (tok == '{') {
|
||
if (l == VT_LOCAL)
|
||
error("cannot use local functions");
|
||
if (!(type.t & VT_FUNC))
|
||
expect("function definition");
|
||
|
||
/* reject abstract declarators in function definition */
|
||
sym = type.ref;
|
||
while ((sym = sym->next) != NULL)
|
||
if (!(sym->v & ~SYM_FIELD))
|
||
expect("identifier");
|
||
|
||
/* XXX: cannot do better now: convert extern line to static inline */
|
||
if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
|
||
type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
|
||
|
||
sym = sym_find(v);
|
||
if (sym) {
|
||
if ((sym->type.t & VT_BTYPE) != VT_FUNC)
|
||
goto func_error1;
|
||
/* specific case: if not func_call defined, we put
|
||
the one of the prototype */
|
||
/* XXX: should have default value */
|
||
if (sym->type.ref->r != FUNC_CDECL &&
|
||
type.ref->r == FUNC_CDECL)
|
||
type.ref->r = sym->type.ref->r;
|
||
if (!is_compatible_types(&sym->type, &type)) {
|
||
func_error1:
|
||
error("incompatible types for redefinition of '%s'",
|
||
get_tok_str(v, NULL));
|
||
}
|
||
/* if symbol is already defined, then put complete type */
|
||
sym->type = type;
|
||
} else {
|
||
/* put function symbol */
|
||
sym = global_identifier_push(v, type.t, 0);
|
||
sym->type.ref = type.ref;
|
||
}
|
||
|
||
/* static inline functions are just recorded as a kind
|
||
of macro. Their code will be emitted at the end of
|
||
the compilation unit only if they are used */
|
||
if ((type.t & (VT_INLINE | VT_STATIC)) ==
|
||
(VT_INLINE | VT_STATIC)) {
|
||
TokenString func_str;
|
||
int block_level;
|
||
|
||
tok_str_new(&func_str);
|
||
|
||
block_level = 0;
|
||
for(;;) {
|
||
int t;
|
||
if (tok == TOK_EOF)
|
||
error("unexpected end of file");
|
||
tok_str_add_tok(&func_str);
|
||
t = tok;
|
||
next();
|
||
if (t == '{') {
|
||
block_level++;
|
||
} else if (t == '}') {
|
||
block_level--;
|
||
if (block_level == 0)
|
||
break;
|
||
}
|
||
}
|
||
tok_str_add(&func_str, -1);
|
||
tok_str_add(&func_str, 0);
|
||
sym->r = (long)func_str.str;
|
||
} else {
|
||
/* compute text section */
|
||
cur_text_section = ad.section;
|
||
if (!cur_text_section)
|
||
cur_text_section = text_section;
|
||
sym->r = VT_SYM | VT_CONST;
|
||
gen_function(sym);
|
||
#ifdef TCC_TARGET_PE
|
||
if (ad.dllexport) {
|
||
((Elf32_Sym *)symtab_section->data)[sym->c].st_other |= 1;
|
||
}
|
||
#endif
|
||
}
|
||
break;
|
||
} else {
|
||
if (btype.t & VT_TYPEDEF) {
|
||
/* save typedefed type */
|
||
/* XXX: test storage specifiers ? */
|
||
sym = sym_push(v, &type, 0, 0);
|
||
sym->type.t |= VT_TYPEDEF;
|
||
} else if ((type.t & VT_BTYPE) == VT_FUNC) {
|
||
/* external function definition */
|
||
/* specific case for func_call attribute */
|
||
if (ad.func_call)
|
||
type.ref->r = ad.func_call;
|
||
external_sym(v, &type, 0);
|
||
} else {
|
||
/* not lvalue if array */
|
||
r = 0;
|
||
if (!(type.t & VT_ARRAY))
|
||
r |= lvalue_type(type.t);
|
||
has_init = (tok == '=');
|
||
if ((btype.t & VT_EXTERN) ||
|
||
((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
|
||
!has_init && l == VT_CONST && type.ref->c < 0)) {
|
||
/* external variable */
|
||
/* NOTE: as GCC, uninitialized global static
|
||
arrays of null size are considered as
|
||
extern */
|
||
external_sym(v, &type, r);
|
||
} else {
|
||
if (type.t & VT_STATIC)
|
||
r |= VT_CONST;
|
||
else
|
||
r |= l;
|
||
if (has_init)
|
||
next();
|
||
decl_initializer_alloc(&type, &ad, r,
|
||
has_init, v, l);
|
||
}
|
||
}
|
||
if (tok != ',') {
|
||
skip(';');
|
||
break;
|
||
}
|
||
next();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* better than nothing, but needs extension to handle '-E' option
|
||
correctly too */
|
||
static void preprocess_init(TCCState *s1)
|
||
{
|
||
s1->include_stack_ptr = s1->include_stack;
|
||
/* XXX: move that before to avoid having to initialize
|
||
file->ifdef_stack_ptr ? */
|
||
s1->ifdef_stack_ptr = s1->ifdef_stack;
|
||
file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
|
||
|
||
/* XXX: not ANSI compliant: bound checking says error */
|
||
vtop = vstack - 1;
|
||
s1->pack_stack[0] = 0;
|
||
s1->pack_stack_ptr = s1->pack_stack;
|
||
}
|
||
|
||
/* compile the C file opened in 'file'. Return non zero if errors. */
|
||
static int tcc_compile(TCCState *s1)
|
||
{
|
||
Sym *define_start;
|
||
char buf[512];
|
||
volatile int section_sym;
|
||
|
||
#ifdef INC_DEBUG
|
||
printf("%s: **** new file\n", file->filename);
|
||
#endif
|
||
preprocess_init(s1);
|
||
|
||
funcname = "";
|
||
anon_sym = SYM_FIRST_ANOM;
|
||
|
||
/* file info: full path + filename */
|
||
section_sym = 0; /* avoid warning */
|
||
if (do_debug) {
|
||
section_sym = put_elf_sym(symtab_section, 0, 0,
|
||
ELF32_ST_INFO(STB_LOCAL, STT_SECTION), 0,
|
||
text_section->sh_num, NULL);
|
||
dummy_char_star = getcwd(buf, sizeof(buf));
|
||
pstrcat(buf, sizeof(buf), "/");
|
||
put_stabs_r(buf, N_SO, 0, 0,
|
||
text_section->data_offset, text_section, section_sym);
|
||
put_stabs_r(file->filename, N_SO, 0, 0,
|
||
text_section->data_offset, text_section, section_sym);
|
||
}
|
||
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL
|
||
symbols can be safely used */
|
||
put_elf_sym(symtab_section, 0, 0,
|
||
ELF32_ST_INFO(STB_LOCAL, STT_FILE), 0,
|
||
SHN_ABS, file->filename);
|
||
|
||
/* define some often used types */
|
||
int_type.t = VT_INT;
|
||
|
||
char_pointer_type.t = VT_BYTE;
|
||
mk_pointer(&char_pointer_type);
|
||
|
||
func_old_type.t = VT_FUNC;
|
||
func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
|
||
|
||
#if 0
|
||
/* define 'void *alloca(unsigned int)' builtin function */
|
||
{
|
||
Sym *s1;
|
||
|
||
p = anon_sym++;
|
||
sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW);
|
||
s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
|
||
s1->next = NULL;
|
||
sym->next = s1;
|
||
sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
|
||
}
|
||
#endif
|
||
|
||
define_start = define_stack;
|
||
|
||
if (setjmp(s1->error_jmp_buf) == 0) {
|
||
s1->nb_errors = 0;
|
||
s1->error_set_jmp_enabled = 1;
|
||
|
||
ch = file->buf_ptr[0];
|
||
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
|
||
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM;
|
||
next();
|
||
decl(VT_CONST);
|
||
if (tok != TOK_EOF)
|
||
expect("declaration");
|
||
|
||
/* end of translation unit info */
|
||
if (do_debug) {
|
||
put_stabs_r(NULL, N_SO, 0, 0,
|
||
text_section->data_offset, text_section, section_sym);
|
||
}
|
||
}
|
||
s1->error_set_jmp_enabled = 0;
|
||
|
||
/* reset define stack, but leave -Dsymbols (may be incorrect if
|
||
they are undefined) */
|
||
free_defines(define_start);
|
||
|
||
gen_inline_functions();
|
||
|
||
sym_pop(&global_stack, NULL);
|
||
|
||
return s1->nb_errors != 0 ? -1 : 0;
|
||
}
|
||
|
||
#ifdef LIBTCC
|
||
int tcc_compile_string(TCCState *s, const char *str)
|
||
{
|
||
BufferedFile bf1, *bf = &bf1;
|
||
int ret, len;
|
||
char *buf;
|
||
|
||
/* init file structure */
|
||
bf->fd = -1;
|
||
/* XXX: avoid copying */
|
||
len = strlen(str);
|
||
buf = tcc_malloc(len + 1);
|
||
if (!buf)
|
||
return -1;
|
||
memcpy(buf, str, len);
|
||
buf[len] = CH_EOB;
|
||
bf->buf_ptr = buf;
|
||
bf->buf_end = buf + len;
|
||
pstrcpy(bf->filename, sizeof(bf->filename), "<string>");
|
||
bf->line_num = 1;
|
||
file = bf;
|
||
|
||
ret = tcc_compile(s);
|
||
|
||
tcc_free(buf);
|
||
|
||
/* currently, no need to close */
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
/* define a preprocessor symbol. A value can also be provided with the '=' operator */
|
||
void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
|
||
{
|
||
BufferedFile bf1, *bf = &bf1;
|
||
|
||
pstrcpy(bf->buffer, IO_BUF_SIZE, sym);
|
||
pstrcat(bf->buffer, IO_BUF_SIZE, " ");
|
||
/* default value */
|
||
if (!value)
|
||
value = "1";
|
||
pstrcat(bf->buffer, IO_BUF_SIZE, value);
|
||
|
||
/* init file structure */
|
||
bf->fd = -1;
|
||
bf->buf_ptr = bf->buffer;
|
||
bf->buf_end = bf->buffer + strlen(bf->buffer);
|
||
*bf->buf_end = CH_EOB;
|
||
bf->filename[0] = '\0';
|
||
bf->line_num = 1;
|
||
file = bf;
|
||
|
||
s1->include_stack_ptr = s1->include_stack;
|
||
|
||
/* parse with define parser */
|
||
ch = file->buf_ptr[0];
|
||
next_nomacro();
|
||
parse_define();
|
||
file = NULL;
|
||
}
|
||
|
||
/* undefine a preprocessor symbol */
|
||
void tcc_undefine_symbol(TCCState *s1, const char *sym)
|
||
{
|
||
TokenSym *ts;
|
||
Sym *s;
|
||
ts = tok_alloc(sym, strlen(sym));
|
||
s = define_find(ts->tok);
|
||
/* undefine symbol by putting an invalid name */
|
||
if (s)
|
||
define_undef(s);
|
||
}
|
||
|
||
#ifdef CONFIG_TCC_ASM
|
||
|
||
#ifdef TCC_TARGET_I386
|
||
// njn: inlined i386-asm.c
|
||
//#include "i386-asm.c"
|
||
//---------------------------------------------------------------------------
|
||
/*
|
||
* i386 specific functions for TCC assembler
|
||
*
|
||
* Copyright (c) 2001, 2002 Fabrice Bellard
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser 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
|
||
*/
|
||
|
||
#define MAX_OPERANDS 3
|
||
|
||
typedef struct ASMInstr {
|
||
uint16_t sym;
|
||
uint16_t opcode;
|
||
uint16_t instr_type;
|
||
#define OPC_JMP 0x01 /* jmp operand */
|
||
#define OPC_B 0x02 /* only used zith OPC_WL */
|
||
#define OPC_WL 0x04 /* accepts w, l or no suffix */
|
||
#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
|
||
#define OPC_REG 0x08 /* register is added to opcode */
|
||
#define OPC_MODRM 0x10 /* modrm encoding */
|
||
#define OPC_FWAIT 0x20 /* add fwait opcode */
|
||
#define OPC_TEST 0x40 /* test opcodes */
|
||
#define OPC_SHIFT 0x80 /* shift opcodes */
|
||
#define OPC_D16 0x0100 /* generate data16 prefix */
|
||
#define OPC_ARITH 0x0200 /* arithmetic opcodes */
|
||
#define OPC_SHORTJMP 0x0400 /* short jmp operand */
|
||
#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */
|
||
#define OPC_GROUP_SHIFT 13
|
||
|
||
/* in order to compress the operand type, we use specific operands and
|
||
we or only with EA */
|
||
#define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_MMX 3 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_SSE 4 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_CR 5 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_TR 6 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_DB 7 /* warning: value is hardcoded from TOK_ASM_xxx */
|
||
#define OPT_SEG 8
|
||
#define OPT_ST 9
|
||
#define OPT_IM8 10
|
||
#define OPT_IM8S 11
|
||
#define OPT_IM16 12
|
||
#define OPT_IM32 13
|
||
#define OPT_EAX 14 /* %al, %ax or %eax register */
|
||
#define OPT_ST0 15 /* %st(0) register */
|
||
#define OPT_CL 16 /* %cl register */
|
||
#define OPT_DX 17 /* %dx register */
|
||
#define OPT_ADDR 18 /* OP_EA with only offset */
|
||
#define OPT_INDIR 19 /* *(expr) */
|
||
|
||
/* composite types */
|
||
#define OPT_COMPOSITE_FIRST 20
|
||
#define OPT_IM 20 /* IM8 | IM16 | IM32 */
|
||
#define OPT_REG 21 /* REG8 | REG16 | REG32 */
|
||
#define OPT_REGW 22 /* REG16 | REG32 */
|
||
#define OPT_IMW 23 /* IM16 | IM32 */
|
||
|
||
/* can be ored with any OPT_xxx */
|
||
#define OPT_EA 0x80
|
||
|
||
uint8_t nb_ops;
|
||
uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
|
||
} ASMInstr;
|
||
|
||
typedef struct Operand {
|
||
uint32_t type;
|
||
#define OP_REG8 (1 << OPT_REG8)
|
||
#define OP_REG16 (1 << OPT_REG16)
|
||
#define OP_REG32 (1 << OPT_REG32)
|
||
#define OP_MMX (1 << OPT_MMX)
|
||
#define OP_SSE (1 << OPT_SSE)
|
||
#define OP_CR (1 << OPT_CR)
|
||
#define OP_TR (1 << OPT_TR)
|
||
#define OP_DB (1 << OPT_DB)
|
||
#define OP_SEG (1 << OPT_SEG)
|
||
#define OP_ST (1 << OPT_ST)
|
||
#define OP_IM8 (1 << OPT_IM8)
|
||
#define OP_IM8S (1 << OPT_IM8S)
|
||
#define OP_IM16 (1 << OPT_IM16)
|
||
#define OP_IM32 (1 << OPT_IM32)
|
||
#define OP_EAX (1 << OPT_EAX)
|
||
#define OP_ST0 (1 << OPT_ST0)
|
||
#define OP_CL (1 << OPT_CL)
|
||
#define OP_DX (1 << OPT_DX)
|
||
#define OP_ADDR (1 << OPT_ADDR)
|
||
#define OP_INDIR (1 << OPT_INDIR)
|
||
|
||
#define OP_EA 0x40000000
|
||
#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32)
|
||
#define OP_IM OP_IM32
|
||
int8_t reg; /* register, -1 if none */
|
||
int8_t reg2; /* second register, -1 if none */
|
||
uint8_t shift;
|
||
ExprValue e;
|
||
} Operand;
|
||
|
||
static const uint8_t reg_to_size[5] = {
|
||
[OP_REG8] = 0,
|
||
[OP_REG16] = 1,
|
||
[OP_REG32] = 2,
|
||
};
|
||
|
||
#define WORD_PREFIX_OPCODE 0x66
|
||
|
||
#define NB_TEST_OPCODES 30
|
||
|
||
static const uint8_t test_bits[NB_TEST_OPCODES] = {
|
||
0x00, /* o */
|
||
0x01, /* no */
|
||
0x02, /* b */
|
||
0x02, /* c */
|
||
0x02, /* nae */
|
||
0x03, /* nb */
|
||
0x03, /* nc */
|
||
0x03, /* ae */
|
||
0x04, /* e */
|
||
0x04, /* z */
|
||
0x05, /* ne */
|
||
0x05, /* nz */
|
||
0x06, /* be */
|
||
0x06, /* na */
|
||
0x07, /* nbe */
|
||
0x07, /* a */
|
||
0x08, /* s */
|
||
0x09, /* ns */
|
||
0x0a, /* p */
|
||
0x0a, /* pe */
|
||
0x0b, /* np */
|
||
0x0b, /* po */
|
||
0x0c, /* l */
|
||
0x0c, /* nge */
|
||
0x0d, /* nl */
|
||
0x0d, /* ge */
|
||
0x0e, /* le */
|
||
0x0e, /* ng */
|
||
0x0f, /* nle */
|
||
0x0f, /* g */
|
||
};
|
||
|
||
static const ASMInstr asm_instrs[] = {
|
||
#define ALT(x) x
|
||
#define DEF_ASM_OP0(name, opcode)
|
||
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
|
||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
|
||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
|
||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
|
||
// njn: inlined i386-asm.h
|
||
//#include "i386-asm.h"
|
||
//---------------------------------------------------------------------------
|
||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||
DEF_ASM_OP0(popa, 0x61)
|
||
DEF_ASM_OP0(clc, 0xf8)
|
||
DEF_ASM_OP0(cld, 0xfc)
|
||
DEF_ASM_OP0(cli, 0xfa)
|
||
DEF_ASM_OP0(clts, 0x0f06)
|
||
DEF_ASM_OP0(cmc, 0xf5)
|
||
DEF_ASM_OP0(lahf, 0x9f)
|
||
DEF_ASM_OP0(sahf, 0x9e)
|
||
DEF_ASM_OP0(pushfl, 0x9c)
|
||
DEF_ASM_OP0(popfl, 0x9d)
|
||
DEF_ASM_OP0(pushf, 0x9c)
|
||
DEF_ASM_OP0(popf, 0x9d)
|
||
DEF_ASM_OP0(stc, 0xf9)
|
||
DEF_ASM_OP0(std, 0xfd)
|
||
DEF_ASM_OP0(sti, 0xfb)
|
||
DEF_ASM_OP0(aaa, 0x37)
|
||
DEF_ASM_OP0(aas, 0x3f)
|
||
DEF_ASM_OP0(daa, 0x27)
|
||
DEF_ASM_OP0(das, 0x2f)
|
||
DEF_ASM_OP0(aad, 0xd50a)
|
||
DEF_ASM_OP0(aam, 0xd40a)
|
||
DEF_ASM_OP0(cbw, 0x6698)
|
||
DEF_ASM_OP0(cwd, 0x6699)
|
||
DEF_ASM_OP0(cwde, 0x98)
|
||
DEF_ASM_OP0(cdq, 0x99)
|
||
DEF_ASM_OP0(cbtw, 0x6698)
|
||
DEF_ASM_OP0(cwtl, 0x98)
|
||
DEF_ASM_OP0(cwtd, 0x6699)
|
||
DEF_ASM_OP0(cltd, 0x99)
|
||
DEF_ASM_OP0(int3, 0xcc)
|
||
DEF_ASM_OP0(into, 0xce)
|
||
DEF_ASM_OP0(iret, 0xcf)
|
||
DEF_ASM_OP0(rsm, 0x0faa)
|
||
DEF_ASM_OP0(hlt, 0xf4)
|
||
DEF_ASM_OP0(wait, 0x9b)
|
||
DEF_ASM_OP0(nop, 0x90)
|
||
DEF_ASM_OP0(xlat, 0xd7)
|
||
|
||
/* strings */
|
||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||
|
||
/* bits */
|
||
|
||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
/* prefixes */
|
||
DEF_ASM_OP0(aword, 0x67)
|
||
DEF_ASM_OP0(addr16, 0x67)
|
||
DEF_ASM_OP0(word, 0x66)
|
||
DEF_ASM_OP0(data16, 0x66)
|
||
DEF_ASM_OP0(lock, 0xf0)
|
||
DEF_ASM_OP0(rep, 0xf3)
|
||
DEF_ASM_OP0(repe, 0xf3)
|
||
DEF_ASM_OP0(repz, 0xf3)
|
||
DEF_ASM_OP0(repne, 0xf2)
|
||
DEF_ASM_OP0(repnz, 0xf2)
|
||
|
||
DEF_ASM_OP0(invd, 0x0f08)
|
||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||
|
||
/* NOTE: we took the same order as gas opcode definition order */
|
||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||
|
||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
|
||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
|
||
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
|
||
/* arith */
|
||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
|
||
/* shifts */
|
||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||
|
||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||
DEF_ASM_OP0(leave, 0xc9)
|
||
DEF_ASM_OP0(ret, 0xc3)
|
||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||
DEF_ASM_OP0(lret, 0xcb)
|
||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||
|
||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
|
||
/* float */
|
||
/* specific fcomp handling */
|
||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||
|
||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
|
||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||
|
||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||
DEF_ASM_OP0(fwait, 0x9b)
|
||
|
||
/* fp load */
|
||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||
|
||
/* fp store */
|
||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||
|
||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||
|
||
/* exchange */
|
||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||
|
||
/* misc FPU */
|
||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||
|
||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* segments */
|
||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||
|
||
/* 486 */
|
||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||
|
||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||
|
||
/* pentium */
|
||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* pentium pro */
|
||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||
|
||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
/* mmx */
|
||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
|
||
#undef ALT
|
||
#undef DEF_ASM_OP0
|
||
#undef DEF_ASM_OP0L
|
||
#undef DEF_ASM_OP1
|
||
#undef DEF_ASM_OP2
|
||
#undef DEF_ASM_OP3
|
||
//---------------------------------------------------------------------------
|
||
|
||
/* last operation */
|
||
{ 0, },
|
||
};
|
||
|
||
static const uint16_t op0_codes[] = {
|
||
#define ALT(x)
|
||
#define DEF_ASM_OP0(x, opcode) opcode,
|
||
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
|
||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
|
||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
|
||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
|
||
// njn: inlined i386-asm.h
|
||
//#include "i386-asm.h"
|
||
//---------------------------------------------------------------------------
|
||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||
DEF_ASM_OP0(popa, 0x61)
|
||
DEF_ASM_OP0(clc, 0xf8)
|
||
DEF_ASM_OP0(cld, 0xfc)
|
||
DEF_ASM_OP0(cli, 0xfa)
|
||
DEF_ASM_OP0(clts, 0x0f06)
|
||
DEF_ASM_OP0(cmc, 0xf5)
|
||
DEF_ASM_OP0(lahf, 0x9f)
|
||
DEF_ASM_OP0(sahf, 0x9e)
|
||
DEF_ASM_OP0(pushfl, 0x9c)
|
||
DEF_ASM_OP0(popfl, 0x9d)
|
||
DEF_ASM_OP0(pushf, 0x9c)
|
||
DEF_ASM_OP0(popf, 0x9d)
|
||
DEF_ASM_OP0(stc, 0xf9)
|
||
DEF_ASM_OP0(std, 0xfd)
|
||
DEF_ASM_OP0(sti, 0xfb)
|
||
DEF_ASM_OP0(aaa, 0x37)
|
||
DEF_ASM_OP0(aas, 0x3f)
|
||
DEF_ASM_OP0(daa, 0x27)
|
||
DEF_ASM_OP0(das, 0x2f)
|
||
DEF_ASM_OP0(aad, 0xd50a)
|
||
DEF_ASM_OP0(aam, 0xd40a)
|
||
DEF_ASM_OP0(cbw, 0x6698)
|
||
DEF_ASM_OP0(cwd, 0x6699)
|
||
DEF_ASM_OP0(cwde, 0x98)
|
||
DEF_ASM_OP0(cdq, 0x99)
|
||
DEF_ASM_OP0(cbtw, 0x6698)
|
||
DEF_ASM_OP0(cwtl, 0x98)
|
||
DEF_ASM_OP0(cwtd, 0x6699)
|
||
DEF_ASM_OP0(cltd, 0x99)
|
||
DEF_ASM_OP0(int3, 0xcc)
|
||
DEF_ASM_OP0(into, 0xce)
|
||
DEF_ASM_OP0(iret, 0xcf)
|
||
DEF_ASM_OP0(rsm, 0x0faa)
|
||
DEF_ASM_OP0(hlt, 0xf4)
|
||
DEF_ASM_OP0(wait, 0x9b)
|
||
DEF_ASM_OP0(nop, 0x90)
|
||
DEF_ASM_OP0(xlat, 0xd7)
|
||
|
||
/* strings */
|
||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||
|
||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||
|
||
/* bits */
|
||
|
||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||
|
||
/* prefixes */
|
||
DEF_ASM_OP0(aword, 0x67)
|
||
DEF_ASM_OP0(addr16, 0x67)
|
||
DEF_ASM_OP0(word, 0x66)
|
||
DEF_ASM_OP0(data16, 0x66)
|
||
DEF_ASM_OP0(lock, 0xf0)
|
||
DEF_ASM_OP0(rep, 0xf3)
|
||
DEF_ASM_OP0(repe, 0xf3)
|
||
DEF_ASM_OP0(repz, 0xf3)
|
||
DEF_ASM_OP0(repne, 0xf2)
|
||
DEF_ASM_OP0(repnz, 0xf2)
|
||
|
||
DEF_ASM_OP0(invd, 0x0f08)
|
||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||
|
||
/* NOTE: we took the same order as gas opcode definition order */
|
||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||
|
||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||
|
||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
|
||
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
|
||
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||
|
||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||
|
||
/* arith */
|
||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||
|
||
/* shifts */
|
||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||
|
||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||
|
||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||
|
||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||
DEF_ASM_OP0(leave, 0xc9)
|
||
DEF_ASM_OP0(ret, 0xc3)
|
||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||
DEF_ASM_OP0(lret, 0xcb)
|
||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||
|
||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||
|
||
/* float */
|
||
/* specific fcomp handling */
|
||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||
|
||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||
|
||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||
|
||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||
DEF_ASM_OP0(fwait, 0x9b)
|
||
|
||
/* fp load */
|
||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||
|
||
/* fp store */
|
||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||
|
||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||
|
||
/* exchange */
|
||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||
|
||
/* misc FPU */
|
||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||
|
||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
|
||
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* segments */
|
||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||
|
||
/* 486 */
|
||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||
|
||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||
|
||
/* pentium */
|
||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||
|
||
/* pentium pro */
|
||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||
|
||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||
|
||
/* mmx */
|
||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||
|
||
#undef ALT
|
||
#undef DEF_ASM_OP0
|
||
#undef DEF_ASM_OP0L
|
||
#undef DEF_ASM_OP1
|
||
#undef DEF_ASM_OP2
|
||
#undef DEF_ASM_OP3
|
||
//---------------------------------------------------------------------------
|
||
};
|
||
|
||
static inline int get_reg_shift(TCCState *s1)
|
||
{
|
||
int shift, v;
|
||
|
||
v = asm_int_expr(s1);
|
||
switch(v) {
|
||
case 1:
|
||
shift = 0;
|
||
break;
|
||
case 2:
|
||
shift = 1;
|
||
break;
|
||
case 4:
|
||
shift = 2;
|
||
break;
|
||
case 8:
|
||
shift = 3;
|
||
break;
|
||
default:
|
||
expect("1, 2, 4 or 8 constant");
|
||
shift = 0;
|
||
break;
|
||
}
|
||
return shift;
|
||
}
|
||
|
||
static int asm_parse_reg(void)
|
||
{
|
||
int reg;
|
||
if (tok != '%')
|
||
goto error_32;
|
||
next();
|
||
if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
|
||
reg = tok - TOK_ASM_eax;
|
||
next();
|
||
return reg;
|
||
} else {
|
||
error_32:
|
||
expect("32 bit register");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
static void parse_operand(TCCState *s1, Operand *op)
|
||
{
|
||
ExprValue e;
|
||
int reg, indir;
|
||
const char *p;
|
||
|
||
indir = 0;
|
||
if (tok == '*') {
|
||
next();
|
||
indir = OP_INDIR;
|
||
}
|
||
|
||
if (tok == '%') {
|
||
next();
|
||
if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
|
||
reg = tok - TOK_ASM_al;
|
||
op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
|
||
op->reg = reg & 7;
|
||
if ((op->type & OP_REG) && op->reg == TREG_EAX)
|
||
op->type |= OP_EAX;
|
||
else if (op->type == OP_REG8 && op->reg == TREG_ECX)
|
||
op->type |= OP_CL;
|
||
else if (op->type == OP_REG16 && op->reg == TREG_EDX)
|
||
op->type |= OP_DX;
|
||
} else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
|
||
op->type = OP_DB;
|
||
op->reg = tok - TOK_ASM_dr0;
|
||
} else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
|
||
op->type = OP_SEG;
|
||
op->reg = tok - TOK_ASM_es;
|
||
} else if (tok == TOK_ASM_st) {
|
||
op->type = OP_ST;
|
||
op->reg = 0;
|
||
next();
|
||
if (tok == '(') {
|
||
next();
|
||
if (tok != TOK_PPNUM)
|
||
goto reg_error;
|
||
p = tokc.cstr->data;
|
||
reg = p[0] - '0';
|
||
if ((unsigned)reg >= 8 || p[1] != '\0')
|
||
goto reg_error;
|
||
op->reg = reg;
|
||
next();
|
||
skip(')');
|
||
}
|
||
if (op->reg == 0)
|
||
op->type |= OP_ST0;
|
||
goto no_skip;
|
||
} else {
|
||
reg_error:
|
||
error("unknown register");
|
||
}
|
||
next();
|
||
no_skip: ;
|
||
} else if (tok == '$') {
|
||
/* constant value */
|
||
next();
|
||
asm_expr(s1, &e);
|
||
op->type = OP_IM32;
|
||
op->e.v = e.v;
|
||
op->e.sym = e.sym;
|
||
if (!op->e.sym) {
|
||
if (op->e.v == (uint8_t)op->e.v)
|
||
op->type |= OP_IM8;
|
||
if (op->e.v == (int8_t)op->e.v)
|
||
op->type |= OP_IM8S;
|
||
if (op->e.v == (uint16_t)op->e.v)
|
||
op->type |= OP_IM16;
|
||
}
|
||
} else {
|
||
/* address(reg,reg2,shift) with all variants */
|
||
op->type = OP_EA;
|
||
op->reg = -1;
|
||
op->reg2 = -1;
|
||
op->shift = 0;
|
||
if (tok != '(') {
|
||
asm_expr(s1, &e);
|
||
op->e.v = e.v;
|
||
op->e.sym = e.sym;
|
||
} else {
|
||
op->e.v = 0;
|
||
op->e.sym = NULL;
|
||
}
|
||
if (tok == '(') {
|
||
next();
|
||
if (tok != ',') {
|
||
op->reg = asm_parse_reg();
|
||
}
|
||
if (tok == ',') {
|
||
next();
|
||
if (tok != ',') {
|
||
op->reg2 = asm_parse_reg();
|
||
}
|
||
skip(',');
|
||
op->shift = get_reg_shift(s1);
|
||
}
|
||
skip(')');
|
||
}
|
||
if (op->reg == -1 && op->reg2 == -1)
|
||
op->type |= OP_ADDR;
|
||
}
|
||
op->type |= indir;
|
||
}
|
||
|
||
/* XXX: unify with C code output ? */
|
||
static void gen_expr32(ExprValue *pe)
|
||
{
|
||
if (pe->sym)
|
||
greloc(cur_text_section, pe->sym, ind, R_386_32);
|
||
gen_le32(pe->v);
|
||
}
|
||
|
||
/* XXX: unify with C code output ? */
|
||
static void gen_disp32(ExprValue *pe)
|
||
{
|
||
Sym *sym;
|
||
sym = pe->sym;
|
||
if (sym) {
|
||
if (sym->r == cur_text_section->sh_num) {
|
||
/* same section: we can output an absolute value. Note
|
||
that the TCC compiler behaves differently here because
|
||
it always outputs a relocation to ease (future) code
|
||
elimination in the linker */
|
||
gen_le32(pe->v + (long)sym->next - ind - 4);
|
||
} else {
|
||
greloc(cur_text_section, sym, ind, R_386_PC32);
|
||
gen_le32(pe->v - 4);
|
||
}
|
||
} else {
|
||
/* put an empty PC32 relocation */
|
||
put_elf_reloc(symtab_section, cur_text_section,
|
||
ind, R_386_PC32, 0);
|
||
gen_le32(pe->v - 4);
|
||
}
|
||
}
|
||
|
||
|
||
static void gen_le16(int v)
|
||
{
|
||
g(v);
|
||
g(v >> 8);
|
||
}
|
||
|
||
/* generate the modrm operand */
|
||
static inline void asm_modrm(int reg, Operand *op)
|
||
{
|
||
int mod, reg1, reg2, sib_reg1;
|
||
|
||
if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
|
||
g(0xc0 + (reg << 3) + op->reg);
|
||
} else if (op->reg == -1 && op->reg2 == -1) {
|
||
/* displacement only */
|
||
g(0x05 + (reg << 3));
|
||
gen_expr32(&op->e);
|
||
} else {
|
||
sib_reg1 = op->reg;
|
||
/* fist compute displacement encoding */
|
||
if (sib_reg1 == -1) {
|
||
sib_reg1 = 5;
|
||
mod = 0x00;
|
||
} else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
|
||
mod = 0x00;
|
||
} else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
|
||
mod = 0x40;
|
||
} else {
|
||
mod = 0x80;
|
||
}
|
||
/* compute if sib byte needed */
|
||
reg1 = op->reg;
|
||
if (op->reg2 != -1)
|
||
reg1 = 4;
|
||
g(mod + (reg << 3) + reg1);
|
||
if (reg1 == 4) {
|
||
/* add sib byte */
|
||
reg2 = op->reg2;
|
||
if (reg2 == -1)
|
||
reg2 = 4; /* indicate no index */
|
||
g((op->shift << 6) + (reg2 << 3) + sib_reg1);
|
||
}
|
||
|
||
/* add offset */
|
||
if (mod == 0x40) {
|
||
g(op->e.v);
|
||
} else if (mod == 0x80 || op->reg == -1) {
|
||
gen_expr32(&op->e);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void asm_opcode(TCCState *s1, int opcode)
|
||
{
|
||
const ASMInstr *pa;
|
||
int i, modrm_index, reg, v, op1, is_short_jmp;
|
||
int nb_ops, s, ss;
|
||
Operand ops[MAX_OPERANDS], *pop;
|
||
int op_type[3]; /* decoded op type */
|
||
|
||
/* get operands */
|
||
pop = ops;
|
||
nb_ops = 0;
|
||
for(;;) {
|
||
if (tok == ';' || tok == TOK_LINEFEED)
|
||
break;
|
||
if (nb_ops >= MAX_OPERANDS) {
|
||
error("incorrect number of operands");
|
||
}
|
||
parse_operand(s1, pop);
|
||
pop++;
|
||
nb_ops++;
|
||
if (tok != ',')
|
||
break;
|
||
next();
|
||
}
|
||
|
||
is_short_jmp = 0;
|
||
s = 0; /* avoid warning */
|
||
|
||
/* optimize matching by using a lookup table (no hashing is needed
|
||
!) */
|
||
for(pa = asm_instrs; pa->sym != 0; pa++) {
|
||
s = 0;
|
||
if (pa->instr_type & OPC_FARITH) {
|
||
v = opcode - pa->sym;
|
||
if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
|
||
continue;
|
||
} else if (pa->instr_type & OPC_ARITH) {
|
||
if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4))
|
||
continue;
|
||
goto compute_size;
|
||
} else if (pa->instr_type & OPC_SHIFT) {
|
||
if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4))
|
||
continue;
|
||
goto compute_size;
|
||
} else if (pa->instr_type & OPC_TEST) {
|
||
if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
|
||
continue;
|
||
} else if (pa->instr_type & OPC_B) {
|
||
if (!(opcode >= pa->sym && opcode <= pa->sym + 3))
|
||
continue;
|
||
compute_size:
|
||
s = (opcode - pa->sym) & 3;
|
||
} else if (pa->instr_type & OPC_WL) {
|
||
if (!(opcode >= pa->sym && opcode <= pa->sym + 2))
|
||
continue;
|
||
s = opcode - pa->sym + 1;
|
||
} else {
|
||
if (pa->sym != opcode)
|
||
continue;
|
||
}
|
||
if (pa->nb_ops != nb_ops)
|
||
continue;
|
||
/* now decode and check each operand */
|
||
for(i = 0; i < nb_ops; i++) {
|
||
int op1, op2;
|
||
op1 = pa->op_type[i];
|
||
op2 = op1 & 0x1f;
|
||
switch(op2) {
|
||
case OPT_IM:
|
||
v = OP_IM8 | OP_IM16 | OP_IM32;
|
||
break;
|
||
case OPT_REG:
|
||
v = OP_REG8 | OP_REG16 | OP_REG32;
|
||
break;
|
||
case OPT_REGW:
|
||
v = OP_REG16 | OP_REG32;
|
||
break;
|
||
case OPT_IMW:
|
||
v = OP_IM16 | OP_IM32;
|
||
break;
|
||
default:
|
||
v = 1 << op2;
|
||
break;
|
||
}
|
||
if (op1 & OPT_EA)
|
||
v |= OP_EA;
|
||
op_type[i] = v;
|
||
if ((ops[i].type & v) == 0)
|
||
goto next;
|
||
}
|
||
/* all is matching ! */
|
||
break;
|
||
next: ;
|
||
}
|
||
if (pa->sym == 0) {
|
||
if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) {
|
||
int b;
|
||
b = op0_codes[opcode - TOK_ASM_pusha];
|
||
if (b & 0xff00)
|
||
g(b >> 8);
|
||
g(b);
|
||
return;
|
||
} else {
|
||
error("unknown opcode '%s'",
|
||
get_tok_str(opcode, NULL));
|
||
}
|
||
}
|
||
/* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
|
||
if (s == 3) {
|
||
for(i = 0; s == 3 && i < nb_ops; i++) {
|
||
if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
|
||
s = reg_to_size[ops[i].type & OP_REG];
|
||
}
|
||
if (s == 3) {
|
||
if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
|
||
(ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
|
||
s = 2;
|
||
else
|
||
error("cannot infer opcode suffix");
|
||
}
|
||
}
|
||
|
||
/* generate data16 prefix if needed */
|
||
ss = s;
|
||
if (s == 1 || (pa->instr_type & OPC_D16))
|
||
g(WORD_PREFIX_OPCODE);
|
||
else if (s == 2)
|
||
s = 1;
|
||
/* now generates the operation */
|
||
if (pa->instr_type & OPC_FWAIT)
|
||
g(0x9b);
|
||
|
||
v = pa->opcode;
|
||
if (v == 0x69 || v == 0x69) {
|
||
/* kludge for imul $im, %reg */
|
||
nb_ops = 3;
|
||
ops[2] = ops[1];
|
||
} else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
|
||
v--; /* int $3 case */
|
||
nb_ops = 0;
|
||
} else if ((v == 0x06 || v == 0x07)) {
|
||
if (ops[0].reg >= 4) {
|
||
/* push/pop %fs or %gs */
|
||
v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
|
||
} else {
|
||
v += ops[0].reg << 3;
|
||
}
|
||
nb_ops = 0;
|
||
} else if (v <= 0x05) {
|
||
/* arith case */
|
||
v += ((opcode - TOK_ASM_addb) >> 2) << 3;
|
||
} else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) {
|
||
/* fpu arith case */
|
||
v += ((opcode - pa->sym) / 6) << 3;
|
||
}
|
||
if (pa->instr_type & OPC_REG) {
|
||
for(i = 0; i < nb_ops; i++) {
|
||
if (op_type[i] & (OP_REG | OP_ST)) {
|
||
v += ops[i].reg;
|
||
break;
|
||
}
|
||
}
|
||
/* mov $im, %reg case */
|
||
if (pa->opcode == 0xb0 && s >= 1)
|
||
v += 7;
|
||
}
|
||
if (pa->instr_type & OPC_B)
|
||
v += s;
|
||
if (pa->instr_type & OPC_TEST)
|
||
v += test_bits[opcode - pa->sym];
|
||
if (pa->instr_type & OPC_SHORTJMP) {
|
||
Sym *sym;
|
||
int jmp_disp;
|
||
|
||
/* see if we can really generate the jump with a byte offset */
|
||
sym = ops[0].e.sym;
|
||
if (!sym)
|
||
goto no_short_jump;
|
||
if (sym->r != cur_text_section->sh_num)
|
||
goto no_short_jump;
|
||
jmp_disp = ops[0].e.v + (long)sym->next - ind - 2;
|
||
if (jmp_disp == (int8_t)jmp_disp) {
|
||
/* OK to generate jump */
|
||
is_short_jmp = 1;
|
||
ops[0].e.v = jmp_disp;
|
||
} else {
|
||
no_short_jump:
|
||
if (pa->instr_type & OPC_JMP) {
|
||
/* long jump will be allowed. need to modify the
|
||
opcode slightly */
|
||
if (v == 0xeb)
|
||
v = 0xe9;
|
||
else
|
||
v += 0x0f10;
|
||
} else {
|
||
error("invalid displacement");
|
||
}
|
||
}
|
||
}
|
||
op1 = v >> 8;
|
||
if (op1)
|
||
g(op1);
|
||
g(v);
|
||
|
||
/* search which operand will used for modrm */
|
||
modrm_index = 0;
|
||
if (pa->instr_type & OPC_SHIFT) {
|
||
reg = (opcode - pa->sym) >> 2;
|
||
if (reg == 6)
|
||
reg = 7;
|
||
} else if (pa->instr_type & OPC_ARITH) {
|
||
reg = (opcode - pa->sym) >> 2;
|
||
} else if (pa->instr_type & OPC_FARITH) {
|
||
reg = (opcode - pa->sym) / 6;
|
||
} else {
|
||
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
|
||
}
|
||
if (pa->instr_type & OPC_MODRM) {
|
||
/* first look for an ea operand */
|
||
for(i = 0;i < nb_ops; i++) {
|
||
if (op_type[i] & OP_EA)
|
||
goto modrm_found;
|
||
}
|
||
/* then if not found, a register or indirection (shift instructions) */
|
||
for(i = 0;i < nb_ops; i++) {
|
||
if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
|
||
goto modrm_found;
|
||
}
|
||
#ifdef ASM_DEBUG
|
||
error("bad op table");
|
||
#endif
|
||
modrm_found:
|
||
modrm_index = i;
|
||
/* if a register is used in another operand then it is
|
||
used instead of group */
|
||
for(i = 0;i < nb_ops; i++) {
|
||
v = op_type[i];
|
||
if (i != modrm_index &&
|
||
(v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
|
||
reg = ops[i].reg;
|
||
break;
|
||
}
|
||
}
|
||
|
||
asm_modrm(reg, &ops[modrm_index]);
|
||
}
|
||
|
||
/* emit constants */
|
||
if (pa->opcode == 0x9a || pa->opcode == 0xea) {
|
||
/* ljmp or lcall kludge */
|
||
gen_expr32(&ops[1].e);
|
||
if (ops[0].e.sym)
|
||
error("cannot relocate");
|
||
gen_le16(ops[0].e.v);
|
||
} else {
|
||
for(i = 0;i < nb_ops; i++) {
|
||
v = op_type[i];
|
||
if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) {
|
||
/* if multiple sizes are given it means we must look
|
||
at the op size */
|
||
if (v == (OP_IM8 | OP_IM16 | OP_IM32) ||
|
||
v == (OP_IM16 | OP_IM32)) {
|
||
if (ss == 0)
|
||
v = OP_IM8;
|
||
else if (ss == 1)
|
||
v = OP_IM16;
|
||
else
|
||
v = OP_IM32;
|
||
}
|
||
if (v & (OP_IM8 | OP_IM8S)) {
|
||
if (ops[i].e.sym)
|
||
goto error_relocate;
|
||
g(ops[i].e.v);
|
||
} else if (v & OP_IM16) {
|
||
if (ops[i].e.sym) {
|
||
error_relocate:
|
||
error("cannot relocate");
|
||
}
|
||
gen_le16(ops[i].e.v);
|
||
} else {
|
||
if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
|
||
if (is_short_jmp)
|
||
g(ops[i].e.v);
|
||
else
|
||
gen_disp32(&ops[i].e);
|
||
} else {
|
||
gen_expr32(&ops[i].e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#define NB_SAVED_REGS 3
|
||
#define NB_ASM_REGS 8
|
||
|
||
/* return the constraint priority (we allocate first the lowest
|
||
numbered constraints) */
|
||
static inline int constraint_priority(const char *str)
|
||
{
|
||
int priority, c, pr;
|
||
|
||
/* we take the lowest priority */
|
||
priority = 0;
|
||
for(;;) {
|
||
c = *str;
|
||
if (c == '\0')
|
||
break;
|
||
str++;
|
||
switch(c) {
|
||
case 'A':
|
||
pr = 0;
|
||
break;
|
||
case 'a':
|
||
case 'b':
|
||
case 'c':
|
||
case 'd':
|
||
case 'S':
|
||
case 'D':
|
||
pr = 1;
|
||
break;
|
||
case 'q':
|
||
pr = 2;
|
||
break;
|
||
case 'r':
|
||
pr = 3;
|
||
break;
|
||
case 'N':
|
||
case 'M':
|
||
case 'I':
|
||
case 'i':
|
||
case 'm':
|
||
case 'g':
|
||
pr = 4;
|
||
break;
|
||
default:
|
||
error("unknown constraint '%c'", c);
|
||
pr = 0;
|
||
}
|
||
if (pr > priority)
|
||
priority = pr;
|
||
}
|
||
return priority;
|
||
}
|
||
|
||
static const char *skip_constraint_modifiers(const char *p)
|
||
{
|
||
while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
|
||
p++;
|
||
return p;
|
||
}
|
||
|
||
#define REG_OUT_MASK 0x01
|
||
#define REG_IN_MASK 0x02
|
||
|
||
#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
|
||
|
||
static void asm_compute_constraints(ASMOperand *operands,
|
||
int nb_operands, int nb_outputs,
|
||
const uint8_t *clobber_regs,
|
||
int *pout_reg)
|
||
{
|
||
ASMOperand *op;
|
||
int sorted_op[MAX_ASM_OPERANDS];
|
||
int i, j, k, p1, p2, tmp, reg, c, reg_mask;
|
||
const char *str;
|
||
uint8_t regs_allocated[NB_ASM_REGS];
|
||
|
||
/* init fields */
|
||
for(i=0;i<nb_operands;i++) {
|
||
op = &operands[i];
|
||
op->input_index = -1;
|
||
op->ref_index = -1;
|
||
op->reg = -1;
|
||
op->is_memory = 0;
|
||
op->is_rw = 0;
|
||
}
|
||
/* compute constraint priority and evaluate references to output
|
||
constraints if input constraints */
|
||
for(i=0;i<nb_operands;i++) {
|
||
op = &operands[i];
|
||
str = op->constraint;
|
||
str = skip_constraint_modifiers(str);
|
||
if (isnum(*str) || *str == '[') {
|
||
/* this is a reference to another constraint */
|
||
k = find_constraint(operands, nb_operands, str, NULL);
|
||
if ((unsigned)k >= i || i < nb_outputs)
|
||
error("invalid reference in constraint %d ('%s')",
|
||
i, str);
|
||
op->ref_index = k;
|
||
if (operands[k].input_index >= 0)
|
||
error("cannot reference twice the same operand");
|
||
operands[k].input_index = i;
|
||
op->priority = 5;
|
||
} else {
|
||
op->priority = constraint_priority(str);
|
||
}
|
||
}
|
||
|
||
/* sort operands according to their priority */
|
||
for(i=0;i<nb_operands;i++)
|
||
sorted_op[i] = i;
|
||
for(i=0;i<nb_operands - 1;i++) {
|
||
for(j=i+1;j<nb_operands;j++) {
|
||
p1 = operands[sorted_op[i]].priority;
|
||
p2 = operands[sorted_op[j]].priority;
|
||
if (p2 < p1) {
|
||
tmp = sorted_op[i];
|
||
sorted_op[i] = sorted_op[j];
|
||
sorted_op[j] = tmp;
|
||
}
|
||
}
|
||
}
|
||
|
||
for(i = 0;i < NB_ASM_REGS; i++) {
|
||
if (clobber_regs[i])
|
||
regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
|
||
else
|
||
regs_allocated[i] = 0;
|
||
}
|
||
/* esp cannot be used */
|
||
regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
|
||
/* ebp cannot be used yet */
|
||
regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
|
||
|
||
/* allocate registers and generate corresponding asm moves */
|
||
for(i=0;i<nb_operands;i++) {
|
||
j = sorted_op[i];
|
||
op = &operands[j];
|
||
str = op->constraint;
|
||
/* no need to allocate references */
|
||
if (op->ref_index >= 0)
|
||
continue;
|
||
/* select if register is used for output, input or both */
|
||
if (op->input_index >= 0) {
|
||
reg_mask = REG_IN_MASK | REG_OUT_MASK;
|
||
} else if (j < nb_outputs) {
|
||
reg_mask = REG_OUT_MASK;
|
||
} else {
|
||
reg_mask = REG_IN_MASK;
|
||
}
|
||
try_next:
|
||
c = *str++;
|
||
switch(c) {
|
||
case '=':
|
||
goto try_next;
|
||
case '+':
|
||
op->is_rw = 1;
|
||
/* FALL THRU */
|
||
case '&':
|
||
if (j >= nb_outputs)
|
||
error("'%c' modifier can only be applied to outputs", c);
|
||
reg_mask = REG_IN_MASK | REG_OUT_MASK;
|
||
goto try_next;
|
||
case 'A':
|
||
/* allocate both eax and edx */
|
||
if (is_reg_allocated(TREG_EAX) ||
|
||
is_reg_allocated(TREG_EDX))
|
||
goto try_next;
|
||
op->is_llong = 1;
|
||
op->reg = TREG_EAX;
|
||
regs_allocated[TREG_EAX] |= reg_mask;
|
||
regs_allocated[TREG_EDX] |= reg_mask;
|
||
break;
|
||
case 'a':
|
||
reg = TREG_EAX;
|
||
goto alloc_reg;
|
||
case 'b':
|
||
reg = 3;
|
||
goto alloc_reg;
|
||
case 'c':
|
||
reg = TREG_ECX;
|
||
goto alloc_reg;
|
||
case 'd':
|
||
reg = TREG_EDX;
|
||
goto alloc_reg;
|
||
case 'S':
|
||
reg = 6;
|
||
goto alloc_reg;
|
||
case 'D':
|
||
reg = 7;
|
||
alloc_reg:
|
||
if (is_reg_allocated(reg))
|
||
goto try_next;
|
||
goto reg_found;
|
||
case 'q':
|
||
/* eax, ebx, ecx or edx */
|
||
for(reg = 0; reg < 4; reg++) {
|
||
if (!is_reg_allocated(reg))
|
||
goto reg_found;
|
||
}
|
||
goto try_next;
|
||
case 'r':
|
||
/* any general register */
|
||
for(reg = 0; reg < 8; reg++) {
|
||
if (!is_reg_allocated(reg))
|
||
goto reg_found;
|
||
}
|
||
goto try_next;
|
||
reg_found:
|
||
/* now we can reload in the register */
|
||
op->is_llong = 0;
|
||
op->reg = reg;
|
||
regs_allocated[reg] |= reg_mask;
|
||
break;
|
||
case 'i':
|
||
if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
|
||
goto try_next;
|
||
break;
|
||
case 'I':
|
||
case 'N':
|
||
case 'M':
|
||
if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
|
||
goto try_next;
|
||
break;
|
||
case 'm':
|
||
case 'g':
|
||
/* nothing special to do because the operand is already in
|
||
memory, except if the pointer itself is stored in a
|
||
memory variable (VT_LLOCAL case) */
|
||
/* XXX: fix constant case */
|
||
/* if it is a reference to a memory zone, it must lie
|
||
in a register, so we reserve the register in the
|
||
input registers and a load will be generated
|
||
later */
|
||
if (j < nb_outputs || c == 'm') {
|
||
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
|
||
/* any general register */
|
||
for(reg = 0; reg < 8; reg++) {
|
||
if (!(regs_allocated[reg] & REG_IN_MASK))
|
||
goto reg_found1;
|
||
}
|
||
goto try_next;
|
||
reg_found1:
|
||
/* now we can reload in the register */
|
||
regs_allocated[reg] |= REG_IN_MASK;
|
||
op->reg = reg;
|
||
op->is_memory = 1;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
error("asm constraint %d ('%s') could not be satisfied",
|
||
j, op->constraint);
|
||
break;
|
||
}
|
||
/* if a reference is present for that operand, we assign it too */
|
||
if (op->input_index >= 0) {
|
||
operands[op->input_index].reg = op->reg;
|
||
operands[op->input_index].is_llong = op->is_llong;
|
||
}
|
||
}
|
||
|
||
/* compute out_reg. It is used to store outputs registers to memory
|
||
locations references by pointers (VT_LLOCAL case) */
|
||
*pout_reg = -1;
|
||
for(i=0;i<nb_operands;i++) {
|
||
op = &operands[i];
|
||
if (op->reg >= 0 &&
|
||
(op->vt->r & VT_VALMASK) == VT_LLOCAL &&
|
||
!op->is_memory) {
|
||
for(reg = 0; reg < 8; reg++) {
|
||
if (!(regs_allocated[reg] & REG_OUT_MASK))
|
||
goto reg_found2;
|
||
}
|
||
error("could not find free output register for reloading");
|
||
reg_found2:
|
||
*pout_reg = reg;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* print sorted constraints */
|
||
#ifdef ASM_DEBUG
|
||
for(i=0;i<nb_operands;i++) {
|
||
j = sorted_op[i];
|
||
op = &operands[j];
|
||
printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
|
||
j,
|
||
op->id ? get_tok_str(op->id, NULL) : "",
|
||
op->constraint,
|
||
op->vt->r,
|
||
op->reg);
|
||
}
|
||
if (*pout_reg >= 0)
|
||
printf("out_reg=%d\n", *pout_reg);
|
||
#endif
|
||
}
|
||
|
||
static void subst_asm_operand(CString *add_str,
|
||
SValue *sv, int modifier)
|
||
{
|
||
int r, reg, size, val;
|
||
char buf[64];
|
||
|
||
r = sv->r;
|
||
if ((r & VT_VALMASK) == VT_CONST) {
|
||
if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n')
|
||
cstr_ccat(add_str, '$');
|
||
if (r & VT_SYM) {
|
||
cstr_cat(add_str, get_tok_str(sv->sym->v, NULL));
|
||
if (sv->c.i != 0) {
|
||
cstr_ccat(add_str, '+');
|
||
} else {
|
||
return;
|
||
}
|
||
}
|
||
val = sv->c.i;
|
||
if (modifier == 'n')
|
||
val = -val;
|
||
snprintf(buf, sizeof(buf), "%d", sv->c.i);
|
||
cstr_cat(add_str, buf);
|
||
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
||
snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i);
|
||
cstr_cat(add_str, buf);
|
||
} else if (r & VT_LVAL) {
|
||
reg = r & VT_VALMASK;
|
||
if (reg >= VT_CONST)
|
||
error("internal compiler error");
|
||
snprintf(buf, sizeof(buf), "(%%%s)",
|
||
get_tok_str(TOK_ASM_eax + reg, NULL));
|
||
cstr_cat(add_str, buf);
|
||
} else {
|
||
/* register case */
|
||
reg = r & VT_VALMASK;
|
||
if (reg >= VT_CONST)
|
||
error("internal compiler error");
|
||
|
||
/* choose register operand size */
|
||
if ((sv->type.t & VT_BTYPE) == VT_BYTE)
|
||
size = 1;
|
||
else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
|
||
size = 2;
|
||
else
|
||
size = 4;
|
||
if (size == 1 && reg >= 4)
|
||
size = 4;
|
||
|
||
if (modifier == 'b') {
|
||
if (reg >= 4)
|
||
error("cannot use byte register");
|
||
size = 1;
|
||
} else if (modifier == 'h') {
|
||
if (reg >= 4)
|
||
error("cannot use byte register");
|
||
size = -1;
|
||
} else if (modifier == 'w') {
|
||
size = 2;
|
||
}
|
||
|
||
switch(size) {
|
||
case -1:
|
||
reg = TOK_ASM_ah + reg;
|
||
break;
|
||
case 1:
|
||
reg = TOK_ASM_al + reg;
|
||
break;
|
||
case 2:
|
||
reg = TOK_ASM_ax + reg;
|
||
break;
|
||
default:
|
||
reg = TOK_ASM_eax + reg;
|
||
break;
|
||
}
|
||
snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
|
||
cstr_cat(add_str, buf);
|
||
}
|
||
}
|
||
|
||
/* generate prolog and epilog code for asm statement */
|
||
static void asm_gen_code(ASMOperand *operands, int nb_operands,
|
||
int nb_outputs, int is_output,
|
||
uint8_t *clobber_regs,
|
||
int out_reg)
|
||
{
|
||
uint8_t regs_allocated[NB_ASM_REGS];
|
||
ASMOperand *op;
|
||
int i, reg;
|
||
static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 };
|
||
|
||
/* mark all used registers */
|
||
memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
|
||
for(i = 0; i < nb_operands;i++) {
|
||
op = &operands[i];
|
||
if (op->reg >= 0)
|
||
regs_allocated[op->reg] = 1;
|
||
}
|
||
if (!is_output) {
|
||
/* generate reg save code */
|
||
for(i = 0; i < NB_SAVED_REGS; i++) {
|
||
reg = reg_saved[i];
|
||
if (regs_allocated[reg])
|
||
g(0x50 + reg);
|
||
}
|
||
|
||
/* generate load code */
|
||
for(i = 0; i < nb_operands; i++) {
|
||
op = &operands[i];
|
||
if (op->reg >= 0) {
|
||
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
|
||
op->is_memory) {
|
||
/* memory reference case (for both input and
|
||
output cases) */
|
||
SValue sv;
|
||
sv = *op->vt;
|
||
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
|
||
load(op->reg, &sv);
|
||
} else if (i >= nb_outputs || op->is_rw) {
|
||
/* load value in register */
|
||
load(op->reg, op->vt);
|
||
if (op->is_llong) {
|
||
SValue sv;
|
||
sv = *op->vt;
|
||
sv.c.ul += 4;
|
||
load(TREG_EDX, &sv);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
/* generate save code */
|
||
for(i = 0 ; i < nb_outputs; i++) {
|
||
op = &operands[i];
|
||
if (op->reg >= 0) {
|
||
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
|
||
if (!op->is_memory) {
|
||
SValue sv;
|
||
sv = *op->vt;
|
||
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
|
||
load(out_reg, &sv);
|
||
|
||
sv.r = (sv.r & ~VT_VALMASK) | out_reg;
|
||
store(op->reg, &sv);
|
||
}
|
||
} else {
|
||
store(op->reg, op->vt);
|
||
if (op->is_llong) {
|
||
SValue sv;
|
||
sv = *op->vt;
|
||
sv.c.ul += 4;
|
||
store(TREG_EDX, &sv);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/* generate reg restore code */
|
||
for(i = NB_SAVED_REGS - 1; i >= 0; i--) {
|
||
reg = reg_saved[i];
|
||
if (regs_allocated[reg])
|
||
g(0x58 + reg);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void asm_clobber(uint8_t *clobber_regs, const char *str)
|
||
{
|
||
int reg;
|
||
TokenSym *ts;
|
||
|
||
if (!strcmp(str, "memory") ||
|
||
!strcmp(str, "cc"))
|
||
return;
|
||
ts = tok_alloc(str, strlen(str));
|
||
reg = ts->tok;
|
||
if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
|
||
reg -= TOK_ASM_eax;
|
||
} else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
|
||
reg -= TOK_ASM_ax;
|
||
} else {
|
||
error("invalid clobber register '%s'", str);
|
||
}
|
||
clobber_regs[reg] = 1;
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
#endif
|
||
// njn: inlined tccasm.c
|
||
//#include "tccasm.c"
|
||
//---------------------------------------------------------------------------
|
||
/*
|
||
* GAS like assembler for TCC
|
||
*
|
||
* Copyright (c) 2001-2004 Fabrice Bellard
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser 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
|
||
*/
|
||
|
||
static int asm_get_local_label_name(TCCState *s1, unsigned int n)
|
||
{
|
||
char buf[64];
|
||
TokenSym *ts;
|
||
|
||
snprintf(buf, sizeof(buf), "L..%u", n);
|
||
ts = tok_alloc(buf, strlen(buf));
|
||
return ts->tok;
|
||
}
|
||
|
||
static void asm_expr(TCCState *s1, ExprValue *pe);
|
||
|
||
/* We do not use the C expression parser to handle symbols. Maybe the
|
||
C expression parser could be tweaked to do so. */
|
||
|
||
static void asm_expr_unary(TCCState *s1, ExprValue *pe)
|
||
{
|
||
Sym *sym;
|
||
int op, n, label;
|
||
const char *p;
|
||
|
||
switch(tok) {
|
||
case TOK_PPNUM:
|
||
p = tokc.cstr->data;
|
||
n = strtoul(p, (char **)&p, 0);
|
||
if (*p == 'b' || *p == 'f') {
|
||
/* backward or forward label */
|
||
label = asm_get_local_label_name(s1, n);
|
||
sym = label_find(label);
|
||
if (*p == 'b') {
|
||
/* backward : find the last corresponding defined label */
|
||
if (sym && sym->r == 0)
|
||
sym = sym->prev_tok;
|
||
if (!sym)
|
||
error("local label '%d' not found backward", n);
|
||
} else {
|
||
/* forward */
|
||
if (!sym || sym->r) {
|
||
/* if the last label is defined, then define a new one */
|
||
sym = label_push(&s1->asm_labels, label, 0);
|
||
sym->type.t = VT_STATIC | VT_VOID;
|
||
}
|
||
}
|
||
pe->v = 0;
|
||
pe->sym = sym;
|
||
} else if (*p == '\0') {
|
||
pe->v = n;
|
||
pe->sym = NULL;
|
||
} else {
|
||
error("invalid number syntax");
|
||
}
|
||
next();
|
||
break;
|
||
case '+':
|
||
next();
|
||
asm_expr_unary(s1, pe);
|
||
break;
|
||
case '-':
|
||
case '~':
|
||
op = tok;
|
||
next();
|
||
asm_expr_unary(s1, pe);
|
||
if (pe->sym)
|
||
error("invalid operation with label");
|
||
if (op == '-')
|
||
pe->v = -pe->v;
|
||
else
|
||
pe->v = ~pe->v;
|
||
break;
|
||
case TOK_CCHAR:
|
||
case TOK_LCHAR:
|
||
pe->v = tokc.i;
|
||
pe->sym = NULL;
|
||
next();
|
||
break;
|
||
case '(':
|
||
next();
|
||
asm_expr(s1, pe);
|
||
skip(')');
|
||
break;
|
||
default:
|
||
if (tok >= TOK_IDENT) {
|
||
/* label case : if the label was not found, add one */
|
||
sym = label_find(tok);
|
||
if (!sym) {
|
||
sym = label_push(&s1->asm_labels, tok, 0);
|
||
/* NOTE: by default, the symbol is global */
|
||
sym->type.t = VT_VOID;
|
||
}
|
||
if (sym->r == SHN_ABS) {
|
||
/* if absolute symbol, no need to put a symbol value */
|
||
pe->v = (long)sym->next;
|
||
pe->sym = NULL;
|
||
} else {
|
||
pe->v = 0;
|
||
pe->sym = sym;
|
||
}
|
||
next();
|
||
} else {
|
||
error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void asm_expr_prod(TCCState *s1, ExprValue *pe)
|
||
{
|
||
int op;
|
||
ExprValue e2;
|
||
|
||
asm_expr_unary(s1, pe);
|
||
for(;;) {
|
||
op = tok;
|
||
if (op != '*' && op != '/' && op != '%' &&
|
||
op != TOK_SHL && op != TOK_SAR)
|
||
break;
|
||
next();
|
||
asm_expr_unary(s1, &e2);
|
||
if (pe->sym || e2.sym)
|
||
error("invalid operation with label");
|
||
switch(op) {
|
||
case '*':
|
||
pe->v *= e2.v;
|
||
break;
|
||
case '/':
|
||
if (e2.v == 0) {
|
||
div_error:
|
||
error("division by zero");
|
||
}
|
||
pe->v /= e2.v;
|
||
break;
|
||
case '%':
|
||
if (e2.v == 0)
|
||
goto div_error;
|
||
pe->v %= e2.v;
|
||
break;
|
||
case TOK_SHL:
|
||
pe->v <<= e2.v;
|
||
break;
|
||
default:
|
||
case TOK_SAR:
|
||
pe->v >>= e2.v;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void asm_expr_logic(TCCState *s1, ExprValue *pe)
|
||
{
|
||
int op;
|
||
ExprValue e2;
|
||
|
||
asm_expr_prod(s1, pe);
|
||
for(;;) {
|
||
op = tok;
|
||
if (op != '&' && op != '|' && op != '^')
|
||
break;
|
||
next();
|
||
asm_expr_prod(s1, &e2);
|
||
if (pe->sym || e2.sym)
|
||
error("invalid operation with label");
|
||
switch(op) {
|
||
case '&':
|
||
pe->v &= e2.v;
|
||
break;
|
||
case '|':
|
||
pe->v |= e2.v;
|
||
break;
|
||
default:
|
||
case '^':
|
||
pe->v ^= e2.v;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
|
||
{
|
||
int op;
|
||
ExprValue e2;
|
||
|
||
asm_expr_logic(s1, pe);
|
||
for(;;) {
|
||
op = tok;
|
||
if (op != '+' && op != '-')
|
||
break;
|
||
next();
|
||
asm_expr_logic(s1, &e2);
|
||
if (op == '+') {
|
||
if (pe->sym != NULL && e2.sym != NULL)
|
||
goto cannot_relocate;
|
||
pe->v += e2.v;
|
||
if (pe->sym == NULL && e2.sym != NULL)
|
||
pe->sym = e2.sym;
|
||
} else {
|
||
pe->v -= e2.v;
|
||
/* NOTE: we are less powerful than gas in that case
|
||
because we store only one symbol in the expression */
|
||
if (!pe->sym && !e2.sym) {
|
||
/* OK */
|
||
} else if (pe->sym && !e2.sym) {
|
||
/* OK */
|
||
} else if (pe->sym && e2.sym) {
|
||
if (pe->sym == e2.sym) {
|
||
/* OK */
|
||
} else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
|
||
/* we also accept defined symbols in the same section */
|
||
pe->v += (long)pe->sym->next - (long)e2.sym->next;
|
||
} else {
|
||
goto cannot_relocate;
|
||
}
|
||
pe->sym = NULL; /* same symbols can be substracted to NULL */
|
||
} else {
|
||
cannot_relocate:
|
||
error("invalid operation with label");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void asm_expr(TCCState *s1, ExprValue *pe)
|
||
{
|
||
asm_expr_sum(s1, pe);
|
||
}
|
||
|
||
static int asm_int_expr(TCCState *s1)
|
||
{
|
||
ExprValue e;
|
||
asm_expr(s1, &e);
|
||
if (e.sym)
|
||
expect("constant");
|
||
return e.v;
|
||
}
|
||
|
||
/* NOTE: the same name space as C labels is used to avoid using too
|
||
much memory when storing labels in TokenStrings */
|
||
static void asm_new_label1(TCCState *s1, int label, int is_local,
|
||
int sh_num, long value)
|
||
{
|
||
Sym *sym;
|
||
|
||
sym = label_find(label);
|
||
if (sym) {
|
||
if (sym->r) {
|
||
/* the label is already defined */
|
||
if (!is_local) {
|
||
error("assembler label '%s' already defined",
|
||
get_tok_str(label, NULL));
|
||
} else {
|
||
/* redefinition of local labels is possible */
|
||
goto new_label;
|
||
}
|
||
}
|
||
} else {
|
||
new_label:
|
||
sym = label_push(&s1->asm_labels, label, 0);
|
||
sym->type.t = VT_STATIC | VT_VOID;
|
||
}
|
||
sym->r = sh_num;
|
||
sym->next = (void *)value;
|
||
}
|
||
|
||
static void asm_new_label(TCCState *s1, int label, int is_local)
|
||
{
|
||
asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
|
||
}
|
||
|
||
static void asm_free_labels(TCCState *st)
|
||
{
|
||
Sym *s, *s1;
|
||
Section *sec;
|
||
|
||
for(s = st->asm_labels; s != NULL; s = s1) {
|
||
s1 = s->prev;
|
||
/* define symbol value in object file */
|
||
if (s->r) {
|
||
if (s->r == SHN_ABS)
|
||
sec = SECTION_ABS;
|
||
else
|
||
sec = st->sections[s->r];
|
||
put_extern_sym2(s, sec, (long)s->next, 0, 0);
|
||
}
|
||
/* remove label */
|
||
table_ident[s->v - TOK_IDENT]->sym_label = NULL;
|
||
sym_free(s);
|
||
}
|
||
st->asm_labels = NULL;
|
||
}
|
||
|
||
static void use_section1(TCCState *s1, Section *sec)
|
||
{
|
||
cur_text_section->data_offset = ind;
|
||
cur_text_section = sec;
|
||
ind = cur_text_section->data_offset;
|
||
}
|
||
|
||
static void use_section(TCCState *s1, const char *name)
|
||
{
|
||
Section *sec;
|
||
sec = find_section(s1, name);
|
||
use_section1(s1, sec);
|
||
}
|
||
|
||
static void asm_parse_directive(TCCState *s1)
|
||
{
|
||
int n, offset, v, size, tok1;
|
||
Section *sec;
|
||
uint8_t *ptr;
|
||
|
||
/* assembler directive */
|
||
next();
|
||
sec = cur_text_section;
|
||
switch(tok) {
|
||
case TOK_ASM_align:
|
||
case TOK_ASM_skip:
|
||
case TOK_ASM_space:
|
||
tok1 = tok;
|
||
next();
|
||
n = asm_int_expr(s1);
|
||
if (tok1 == TOK_ASM_align) {
|
||
if (n < 0 || (n & (n-1)) != 0)
|
||
error("alignment must be a positive power of two");
|
||
offset = (ind + n - 1) & -n;
|
||
size = offset - ind;
|
||
/* the section must have a compatible alignment */
|
||
if (sec->sh_addralign < n)
|
||
sec->sh_addralign = n;
|
||
} else {
|
||
size = n;
|
||
}
|
||
v = 0;
|
||
if (tok == ',') {
|
||
next();
|
||
v = asm_int_expr(s1);
|
||
}
|
||
zero_pad:
|
||
if (sec->sh_type != SHT_NOBITS) {
|
||
sec->data_offset = ind;
|
||
ptr = section_ptr_add(sec, size);
|
||
memset(ptr, v, size);
|
||
}
|
||
ind += size;
|
||
break;
|
||
case TOK_ASM_quad:
|
||
next();
|
||
for(;;) {
|
||
uint64_t vl;
|
||
const char *p;
|
||
|
||
p = tokc.cstr->data;
|
||
if (tok != TOK_PPNUM) {
|
||
error_constant:
|
||
error("64 bit constant");
|
||
}
|
||
vl = strtoll(p, (char **)&p, 0);
|
||
if (*p != '\0')
|
||
goto error_constant;
|
||
next();
|
||
if (sec->sh_type != SHT_NOBITS) {
|
||
/* XXX: endianness */
|
||
gen_le32(vl);
|
||
gen_le32(vl >> 32);
|
||
} else {
|
||
ind += 8;
|
||
}
|
||
if (tok != ',')
|
||
break;
|
||
next();
|
||
}
|
||
break;
|
||
case TOK_ASM_byte:
|
||
size = 1;
|
||
goto asm_data;
|
||
case TOK_ASM_word:
|
||
case TOK_SHORT:
|
||
size = 2;
|
||
goto asm_data;
|
||
case TOK_LONG:
|
||
case TOK_INT:
|
||
size = 4;
|
||
asm_data:
|
||
next();
|
||
for(;;) {
|
||
ExprValue e;
|
||
asm_expr(s1, &e);
|
||
if (sec->sh_type != SHT_NOBITS) {
|
||
if (size == 4) {
|
||
gen_expr32(&e);
|
||
} else {
|
||
if (e.sym)
|
||
expect("constant");
|
||
if (size == 1)
|
||
g(e.v);
|
||
else
|
||
gen_le16(e.v);
|
||
}
|
||
} else {
|
||
ind += size;
|
||
}
|
||
if (tok != ',')
|
||
break;
|
||
next();
|
||
}
|
||
break;
|
||
case TOK_ASM_fill:
|
||
{
|
||
int repeat, size, val, i, j;
|
||
uint8_t repeat_buf[8];
|
||
next();
|
||
repeat = asm_int_expr(s1);
|
||
if (repeat < 0) {
|
||
error("repeat < 0; .fill ignored");
|
||
break;
|
||
}
|
||
size = 1;
|
||
val = 0;
|
||
if (tok == ',') {
|
||
next();
|
||
size = asm_int_expr(s1);
|
||
if (size < 0) {
|
||
error("size < 0; .fill ignored");
|
||
break;
|
||
}
|
||
if (size > 8)
|
||
size = 8;
|
||
if (tok == ',') {
|
||
next();
|
||
val = asm_int_expr(s1);
|
||
}
|
||
}
|
||
/* XXX: endianness */
|
||
repeat_buf[0] = val;
|
||
repeat_buf[1] = val >> 8;
|
||
repeat_buf[2] = val >> 16;
|
||
repeat_buf[3] = val >> 24;
|
||
repeat_buf[4] = 0;
|
||
repeat_buf[5] = 0;
|
||
repeat_buf[6] = 0;
|
||
repeat_buf[7] = 0;
|
||
for(i = 0; i < repeat; i++) {
|
||
for(j = 0; j < size; j++) {
|
||
g(repeat_buf[j]);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case TOK_ASM_org:
|
||
{
|
||
unsigned long n;
|
||
next();
|
||
/* XXX: handle section symbols too */
|
||
n = asm_int_expr(s1);
|
||
if (n < ind)
|
||
error("attempt to .org backwards");
|
||
v = 0;
|
||
size = n - ind;
|
||
goto zero_pad;
|
||
}
|
||
break;
|
||
case TOK_ASM_globl:
|
||
case TOK_ASM_global:
|
||
{
|
||
Sym *sym;
|
||
|
||
next();
|
||
sym = label_find(tok);
|
||
if (!sym) {
|
||
sym = label_push(&s1->asm_labels, tok, 0);
|
||
sym->type.t = VT_VOID;
|
||
}
|
||
sym->type.t &= ~VT_STATIC;
|
||
next();
|
||
}
|
||
break;
|
||
case TOK_ASM_string:
|
||
case TOK_ASM_ascii:
|
||
case TOK_ASM_asciz:
|
||
{
|
||
const uint8_t *p;
|
||
int i, size, t;
|
||
|
||
t = tok;
|
||
next();
|
||
for(;;) {
|
||
if (tok != TOK_STR)
|
||
expect("string constant");
|
||
p = tokc.cstr->data;
|
||
size = tokc.cstr->size;
|
||
if (t == TOK_ASM_ascii && size > 0)
|
||
size--;
|
||
for(i = 0; i < size; i++)
|
||
g(p[i]);
|
||
next();
|
||
if (tok == ',') {
|
||
next();
|
||
} else if (tok != TOK_STR) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case TOK_ASM_text:
|
||
case TOK_ASM_data:
|
||
case TOK_ASM_bss:
|
||
{
|
||
char sname[64];
|
||
tok1 = tok;
|
||
n = 0;
|
||
next();
|
||
if (tok != ';' && tok != TOK_LINEFEED) {
|
||
n = asm_int_expr(s1);
|
||
next();
|
||
}
|
||
sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
|
||
use_section(s1, sname);
|
||
}
|
||
break;
|
||
case TOK_SECTION1:
|
||
{
|
||
char sname[256];
|
||
|
||
/* XXX: support more options */
|
||
next();
|
||
sname[0] = '\0';
|
||
while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
|
||
if (tok == TOK_STR)
|
||
pstrcat(sname, sizeof(sname), tokc.cstr->data);
|
||
else
|
||
pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
|
||
next();
|
||
}
|
||
if (tok == ',') {
|
||
/* skip section options */
|
||
next();
|
||
if (tok != TOK_STR)
|
||
expect("string constant");
|
||
next();
|
||
}
|
||
last_text_section = cur_text_section;
|
||
use_section(s1, sname);
|
||
}
|
||
break;
|
||
case TOK_ASM_previous:
|
||
{
|
||
Section *sec;
|
||
next();
|
||
if (!last_text_section)
|
||
error("no previous section referenced");
|
||
sec = cur_text_section;
|
||
use_section1(s1, last_text_section);
|
||
last_text_section = sec;
|
||
}
|
||
break;
|
||
default:
|
||
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/* assemble a file */
|
||
static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
|
||
{
|
||
int opcode;
|
||
|
||
#if 0
|
||
/* print stats about opcodes */
|
||
{
|
||
const ASMInstr *pa;
|
||
int freq[4];
|
||
int op_vals[500];
|
||
int nb_op_vals, i, j;
|
||
|
||
nb_op_vals = 0;
|
||
memset(freq, 0, sizeof(freq));
|
||
for(pa = asm_instrs; pa->sym != 0; pa++) {
|
||
freq[pa->nb_ops]++;
|
||
for(i=0;i<pa->nb_ops;i++) {
|
||
for(j=0;j<nb_op_vals;j++) {
|
||
if (pa->op_type[i] == op_vals[j])
|
||
goto found;
|
||
}
|
||
op_vals[nb_op_vals++] = pa->op_type[i];
|
||
found: ;
|
||
}
|
||
}
|
||
for(i=0;i<nb_op_vals;i++) {
|
||
int v = op_vals[i];
|
||
if ((v & (v - 1)) != 0)
|
||
printf("%3d: %08x\n", i, v);
|
||
}
|
||
printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
|
||
sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
|
||
freq[0], freq[1], freq[2], freq[3]);
|
||
}
|
||
#endif
|
||
|
||
/* XXX: undefine C labels */
|
||
|
||
ch = file->buf_ptr[0];
|
||
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
|
||
parse_flags = PARSE_FLAG_ASM_COMMENTS;
|
||
if (do_preprocess)
|
||
parse_flags |= PARSE_FLAG_PREPROCESS;
|
||
next();
|
||
for(;;) {
|
||
if (tok == TOK_EOF)
|
||
break;
|
||
parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
|
||
redo:
|
||
if (tok == '#') {
|
||
/* horrible gas comment */
|
||
while (tok != TOK_LINEFEED)
|
||
next();
|
||
} else if (tok == '.') {
|
||
asm_parse_directive(s1);
|
||
} else if (tok == TOK_PPNUM) {
|
||
const char *p;
|
||
int n;
|
||
p = tokc.cstr->data;
|
||
n = strtoul(p, (char **)&p, 10);
|
||
if (*p != '\0')
|
||
expect("':'");
|
||
/* new local label */
|
||
asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
|
||
next();
|
||
skip(':');
|
||
goto redo;
|
||
} else if (tok >= TOK_IDENT) {
|
||
/* instruction or label */
|
||
opcode = tok;
|
||
next();
|
||
if (tok == ':') {
|
||
/* new label */
|
||
asm_new_label(s1, opcode, 0);
|
||
next();
|
||
goto redo;
|
||
} else if (tok == '=') {
|
||
int n;
|
||
next();
|
||
n = asm_int_expr(s1);
|
||
asm_new_label1(s1, opcode, 0, SHN_ABS, n);
|
||
goto redo;
|
||
} else {
|
||
asm_opcode(s1, opcode);
|
||
}
|
||
}
|
||
/* end of line */
|
||
if (tok != ';' && tok != TOK_LINEFEED){
|
||
expect("end of line");
|
||
}
|
||
parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
|
||
next();
|
||
}
|
||
|
||
asm_free_labels(s1);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Assemble the current file */
|
||
static int tcc_assemble(TCCState *s1, int do_preprocess)
|
||
{
|
||
Sym *define_start;
|
||
int ret;
|
||
|
||
preprocess_init(s1);
|
||
|
||
/* default section is text */
|
||
cur_text_section = text_section;
|
||
ind = cur_text_section->data_offset;
|
||
|
||
define_start = define_stack;
|
||
|
||
ret = tcc_assemble_internal(s1, do_preprocess);
|
||
|
||
cur_text_section->data_offset = ind;
|
||
|
||
free_defines(define_start);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/********************************************************************/
|
||
/* GCC inline asm support */
|
||
|
||
/* assemble the string 'str' in the current C compilation unit without
|
||
C preprocessing. NOTE: str is modified by modifying the '\0' at the
|
||
end */
|
||
static void tcc_assemble_inline(TCCState *s1, char *str, int len)
|
||
{
|
||
BufferedFile *bf, *saved_file;
|
||
int saved_parse_flags, *saved_macro_ptr;
|
||
|
||
bf = tcc_malloc(sizeof(BufferedFile));
|
||
memset(bf, 0, sizeof(BufferedFile));
|
||
bf->fd = -1;
|
||
bf->buf_ptr = str;
|
||
bf->buf_end = str + len;
|
||
str[len] = CH_EOB;
|
||
/* same name as current file so that errors are correctly
|
||
reported */
|
||
pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
|
||
bf->line_num = file->line_num;
|
||
saved_file = file;
|
||
file = bf;
|
||
saved_parse_flags = parse_flags;
|
||
saved_macro_ptr = macro_ptr;
|
||
macro_ptr = NULL;
|
||
|
||
tcc_assemble_internal(s1, 0);
|
||
|
||
parse_flags = saved_parse_flags;
|
||
macro_ptr = saved_macro_ptr;
|
||
file = saved_file;
|
||
tcc_free(bf);
|
||
}
|
||
|
||
/* find a constraint by its number or id (gcc 3 extended
|
||
syntax). return -1 if not found. Return in *pp in char after the
|
||
constraint */
|
||
static int find_constraint(ASMOperand *operands, int nb_operands,
|
||
const char *name, const char **pp)
|
||
{
|
||
int index;
|
||
TokenSym *ts;
|
||
const char *p;
|
||
|
||
if (isnum(*name)) {
|
||
index = 0;
|
||
while (isnum(*name)) {
|
||
index = (index * 10) + (*name) - '0';
|
||
name++;
|
||
}
|
||
if ((unsigned)index >= nb_operands)
|
||
index = -1;
|
||
} else if (*name == '[') {
|
||
name++;
|
||
p = strchr(name, ']');
|
||
if (p) {
|
||
ts = tok_alloc(name, p - name);
|
||
for(index = 0; index < nb_operands; index++) {
|
||
if (operands[index].id == ts->tok)
|
||
goto found;
|
||
}
|
||
index = -1;
|
||
found:
|
||
name = p + 1;
|
||
} else {
|
||
index = -1;
|
||
}
|
||
} else {
|
||
index = -1;
|
||
}
|
||
if (pp)
|
||
*pp = name;
|
||
return index;
|
||
}
|
||
|
||
static void subst_asm_operands(ASMOperand *operands, int nb_operands,
|
||
int nb_outputs,
|
||
CString *out_str, CString *in_str)
|
||
{
|
||
int c, index, modifier;
|
||
const char *str;
|
||
ASMOperand *op;
|
||
SValue sv;
|
||
|
||
cstr_new(out_str);
|
||
str = in_str->data;
|
||
for(;;) {
|
||
c = *str++;
|
||
if (c == '%') {
|
||
if (*str == '%') {
|
||
str++;
|
||
goto add_char;
|
||
}
|
||
modifier = 0;
|
||
if (*str == 'c' || *str == 'n' ||
|
||
*str == 'b' || *str == 'w' || *str == 'h')
|
||
modifier = *str++;
|
||
index = find_constraint(operands, nb_operands, str, &str);
|
||
if (index < 0)
|
||
error("invalid operand reference after %%");
|
||
op = &operands[index];
|
||
sv = *op->vt;
|
||
if (op->reg >= 0) {
|
||
sv.r = op->reg;
|
||
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL)
|
||
sv.r |= VT_LVAL;
|
||
}
|
||
subst_asm_operand(out_str, &sv, modifier);
|
||
} else {
|
||
add_char:
|
||
cstr_ccat(out_str, c);
|
||
if (c == '\0')
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
|
||
int is_output)
|
||
{
|
||
ASMOperand *op;
|
||
int nb_operands;
|
||
|
||
if (tok != ':') {
|
||
nb_operands = *nb_operands_ptr;
|
||
for(;;) {
|
||
if (nb_operands >= MAX_ASM_OPERANDS)
|
||
error("too many asm operands");
|
||
op = &operands[nb_operands++];
|
||
op->id = 0;
|
||
if (tok == '[') {
|
||
next();
|
||
if (tok < TOK_IDENT)
|
||
expect("identifier");
|
||
op->id = tok;
|
||
next();
|
||
skip(']');
|
||
}
|
||
if (tok != TOK_STR)
|
||
expect("string constant");
|
||
op->constraint = tcc_malloc(tokc.cstr->size);
|
||
strcpy(op->constraint, tokc.cstr->data);
|
||
next();
|
||
skip('(');
|
||
gexpr();
|
||
if (is_output) {
|
||
test_lvalue();
|
||
} else {
|
||
/* we want to avoid LLOCAL case, except when the 'm'
|
||
constraint is used. Note that it may come from
|
||
register storage, so we need to convert (reg)
|
||
case */
|
||
if ((vtop->r & VT_LVAL) &&
|
||
((vtop->r & VT_VALMASK) == VT_LLOCAL ||
|
||
(vtop->r & VT_VALMASK) < VT_CONST) &&
|
||
!strchr(op->constraint, 'm')) {
|
||
gv(RC_INT);
|
||
}
|
||
}
|
||
op->vt = vtop;
|
||
skip(')');
|
||
if (tok == ',') {
|
||
next();
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
*nb_operands_ptr = nb_operands;
|
||
}
|
||
}
|
||
|
||
static void parse_asm_str(CString *astr)
|
||
{
|
||
skip('(');
|
||
/* read the string */
|
||
if (tok != TOK_STR)
|
||
expect("string constant");
|
||
cstr_new(astr);
|
||
while (tok == TOK_STR) {
|
||
/* XXX: add \0 handling too ? */
|
||
cstr_cat(astr, tokc.cstr->data);
|
||
next();
|
||
}
|
||
cstr_ccat(astr, '\0');
|
||
}
|
||
|
||
/* parse the GCC asm() instruction */
|
||
static void asm_instr(void)
|
||
{
|
||
CString astr, astr1;
|
||
ASMOperand operands[MAX_ASM_OPERANDS];
|
||
int nb_inputs __attribute__((unused));
|
||
int nb_outputs, nb_operands, i, must_subst, out_reg;
|
||
uint8_t clobber_regs[NB_ASM_REGS];
|
||
|
||
next();
|
||
/* since we always generate the asm() instruction, we can ignore
|
||
volatile */
|
||
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
|
||
next();
|
||
}
|
||
parse_asm_str(&astr);
|
||
nb_operands = 0;
|
||
nb_outputs = 0;
|
||
must_subst = 0;
|
||
memset(clobber_regs, 0, sizeof(clobber_regs));
|
||
if (tok == ':') {
|
||
next();
|
||
must_subst = 1;
|
||
/* output args */
|
||
parse_asm_operands(operands, &nb_operands, 1);
|
||
nb_outputs = nb_operands;
|
||
if (tok == ':') {
|
||
next();
|
||
/* input args */
|
||
parse_asm_operands(operands, &nb_operands, 0);
|
||
if (tok == ':') {
|
||
/* clobber list */
|
||
/* XXX: handle registers */
|
||
next();
|
||
for(;;) {
|
||
if (tok != TOK_STR)
|
||
expect("string constant");
|
||
asm_clobber(clobber_regs, tokc.cstr->data);
|
||
next();
|
||
if (tok == ',') {
|
||
next();
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
skip(')');
|
||
/* NOTE: we do not eat the ';' so that we can restore the current
|
||
token after the assembler parsing */
|
||
if (tok != ';')
|
||
expect("';'");
|
||
nb_inputs = nb_operands - nb_outputs;
|
||
|
||
/* save all values in the memory */
|
||
save_regs(0);
|
||
|
||
/* compute constraints */
|
||
asm_compute_constraints(operands, nb_operands, nb_outputs,
|
||
clobber_regs, &out_reg);
|
||
|
||
/* substitute the operands in the asm string. No substitution is
|
||
done if no operands (GCC behaviour) */
|
||
#ifdef ASM_DEBUG
|
||
printf("asm: \"%s\"\n", (char *)astr.data);
|
||
#endif
|
||
if (must_subst) {
|
||
subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
|
||
cstr_free(&astr);
|
||
} else {
|
||
astr1 = astr;
|
||
}
|
||
#ifdef ASM_DEBUG
|
||
printf("subst_asm: \"%s\"\n", (char *)astr1.data);
|
||
#endif
|
||
|
||
/* generate loads */
|
||
asm_gen_code(operands, nb_operands, nb_outputs, 0,
|
||
clobber_regs, out_reg);
|
||
|
||
/* assemble the string with tcc internal assembler */
|
||
tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
|
||
|
||
/* restore the current C token */
|
||
next();
|
||
|
||
/* store the output values if needed */
|
||
asm_gen_code(operands, nb_operands, nb_outputs, 1,
|
||
clobber_regs, out_reg);
|
||
|
||
/* free everything */
|
||
for(i=0;i<nb_operands;i++) {
|
||
ASMOperand *op;
|
||
op = &operands[i];
|
||
tcc_free(op->constraint);
|
||
vpop();
|
||
}
|
||
cstr_free(&astr1);
|
||
}
|
||
|
||
static void asm_global_instr(void)
|
||
{
|
||
CString astr;
|
||
|
||
next();
|
||
parse_asm_str(&astr);
|
||
skip(')');
|
||
/* NOTE: we do not eat the ';' so that we can restore the current
|
||
token after the assembler parsing */
|
||
if (tok != ';')
|
||
expect("';'");
|
||
|
||
#ifdef ASM_DEBUG
|
||
printf("asm_global: \"%s\"\n", (char *)astr.data);
|
||
#endif
|
||
cur_text_section = text_section;
|
||
ind = cur_text_section->data_offset;
|
||
|
||
/* assemble the string with tcc internal assembler */
|
||
tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
|
||
|
||
cur_text_section->data_offset = ind;
|
||
|
||
/* restore the current C token */
|
||
next();
|
||
|
||
cstr_free(&astr);
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
|
||
#else
|
||
static void asm_instr(void)
|
||
{
|
||
error("inline asm() not supported");
|
||
}
|
||
static void asm_global_instr(void)
|
||
{
|
||
error("inline asm() not supported");
|
||
}
|
||
#endif
|
||
|
||
// njn: inlined tccelf.c
|
||
//#include "tccelf.c"
|
||
//---------------------------------------------------------------------------
|
||
/*
|
||
* ELF file handling for TCC
|
||
*
|
||
* Copyright (c) 2001-2004 Fabrice Bellard
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser 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
|
||
*/
|
||
|
||
static int put_elf_str(Section *s, const char *sym)
|
||
{
|
||
int offset, len;
|
||
char *ptr;
|
||
|
||
len = strlen(sym) + 1;
|
||
offset = s->data_offset;
|
||
ptr = section_ptr_add(s, len);
|
||
memcpy(ptr, sym, len);
|
||
return offset;
|
||
}
|
||
|
||
/* elf symbol hashing function */
|
||
static unsigned long elf_hash(const unsigned char *name)
|
||
{
|
||
unsigned long h = 0, g;
|
||
|
||
while (*name) {
|
||
h = (h << 4) + *name++;
|
||
g = h & 0xf0000000;
|
||
if (g)
|
||
h ^= g >> 24;
|
||
h &= ~g;
|
||
}
|
||
return h;
|
||
}
|
||
|
||
/* rebuild hash table of section s */
|
||
/* NOTE: we do factorize the hash table code to go faster */
|
||
static void rebuild_hash(Section *s, unsigned int nb_buckets)
|
||
{
|
||
Elf32_Sym *sym;
|
||
int *ptr, *hash, nb_syms, sym_index, h;
|
||
char *strtab;
|
||
|
||
strtab = s->link->data;
|
||
nb_syms = s->data_offset / sizeof(Elf32_Sym);
|
||
|
||
s->hash->data_offset = 0;
|
||
ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
|
||
ptr[0] = nb_buckets;
|
||
ptr[1] = nb_syms;
|
||
ptr += 2;
|
||
hash = ptr;
|
||
memset(hash, 0, (nb_buckets + 1) * sizeof(int));
|
||
ptr += nb_buckets + 1;
|
||
|
||
sym = (Elf32_Sym *)s->data + 1;
|
||
for(sym_index = 1; sym_index < nb_syms; sym_index++) {
|
||
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
|
||
h = elf_hash(strtab + sym->st_name) % nb_buckets;
|
||
*ptr = hash[h];
|
||
hash[h] = sym_index;
|
||
} else {
|
||
*ptr = 0;
|
||
}
|
||
ptr++;
|
||
sym++;
|
||
}
|
||
}
|
||
|
||
/* return the symbol number */
|
||
static int put_elf_sym(Section *s,
|
||
unsigned long value, unsigned long size,
|
||
int info, int other, int shndx, const char *name)
|
||
{
|
||
int name_offset, sym_index;
|
||
int nbuckets, h;
|
||
Elf32_Sym *sym;
|
||
Section *hs;
|
||
|
||
sym = section_ptr_add(s, sizeof(Elf32_Sym));
|
||
if (name)
|
||
name_offset = put_elf_str(s->link, name);
|
||
else
|
||
name_offset = 0;
|
||
/* XXX: endianness */
|
||
sym->st_name = name_offset;
|
||
sym->st_value = value;
|
||
sym->st_size = size;
|
||
sym->st_info = info;
|
||
sym->st_other = other;
|
||
sym->st_shndx = shndx;
|
||
sym_index = sym - (Elf32_Sym *)s->data;
|
||
hs = s->hash;
|
||
if (hs) {
|
||
int *ptr, *base;
|
||
ptr = section_ptr_add(hs, sizeof(int));
|
||
base = (int *)hs->data;
|
||
/* only add global or weak symbols */
|
||
if (ELF32_ST_BIND(info) != STB_LOCAL) {
|
||
/* add another hashing entry */
|
||
nbuckets = base[0];
|
||
h = elf_hash(name) % nbuckets;
|
||
*ptr = base[2 + h];
|
||
base[2 + h] = sym_index;
|
||
base[1]++;
|
||
/* we resize the hash table */
|
||
hs->nb_hashed_syms++;
|
||
if (hs->nb_hashed_syms > 2 * nbuckets) {
|
||
rebuild_hash(s, 2 * nbuckets);
|
||
}
|
||
} else {
|
||
*ptr = 0;
|
||
base[1]++;
|
||
}
|
||
}
|
||
return sym_index;
|
||
}
|
||
|
||
/* find global ELF symbol 'name' and return its index. Return 0 if not
|
||
found. */
|
||
static int find_elf_sym(Section *s, const char *name)
|
||
{
|
||
Elf32_Sym *sym;
|
||
Section *hs;
|
||
int nbuckets, sym_index, h;
|
||
const char *name1;
|
||
|
||
hs = s->hash;
|
||
if (!hs)
|
||
return 0;
|
||
nbuckets = ((int *)hs->data)[0];
|
||
h = elf_hash(name) % nbuckets;
|
||
sym_index = ((int *)hs->data)[2 + h];
|
||
while (sym_index != 0) {
|
||
sym = &((Elf32_Sym *)s->data)[sym_index];
|
||
name1 = s->link->data + sym->st_name;
|
||
if (!strcmp(name, name1))
|
||
return sym_index;
|
||
sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* return elf symbol value or error */
|
||
int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name)
|
||
{
|
||
int sym_index;
|
||
Elf32_Sym *sym;
|
||
|
||
sym_index = find_elf_sym(symtab_section, name);
|
||
if (!sym_index)
|
||
return -1;
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
*pval = sym->st_value;
|
||
return 0;
|
||
}
|
||
|
||
void *tcc_get_symbol_err(TCCState *s, const char *name)
|
||
{
|
||
unsigned long val;
|
||
if (tcc_get_symbol(s, &val, name) < 0)
|
||
error("%s not defined", name);
|
||
return (void *)val;
|
||
}
|
||
|
||
/* add an elf symbol : check if it is already defined and patch
|
||
it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
|
||
static int add_elf_sym(Section *s, unsigned long value, unsigned long size,
|
||
int info, int other, int sh_num, const char *name)
|
||
{
|
||
Elf32_Sym *esym;
|
||
int sym_bind, sym_index, sym_type, esym_bind;
|
||
|
||
sym_bind = ELF32_ST_BIND(info);
|
||
sym_type = ELF32_ST_TYPE(info);
|
||
|
||
if (sym_bind != STB_LOCAL) {
|
||
/* we search global or weak symbols */
|
||
sym_index = find_elf_sym(s, name);
|
||
if (!sym_index)
|
||
goto do_def;
|
||
esym = &((Elf32_Sym *)s->data)[sym_index];
|
||
if (esym->st_shndx != SHN_UNDEF) {
|
||
esym_bind = ELF32_ST_BIND(esym->st_info);
|
||
if (sh_num == SHN_UNDEF) {
|
||
/* ignore adding of undefined symbol if the
|
||
corresponding symbol is already defined */
|
||
} else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
|
||
/* global overrides weak, so patch */
|
||
goto do_patch;
|
||
} else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
|
||
/* weak is ignored if already global */
|
||
} else {
|
||
#if 0
|
||
printf("new_bind=%d new_shndx=%d last_bind=%d old_shndx=%d\n",
|
||
sym_bind, sh_num, esym_bind, esym->st_shndx);
|
||
#endif
|
||
/* NOTE: we accept that two DLL define the same symbol */
|
||
if (s != tcc_state->dynsymtab_section)
|
||
error_noabort("'%s' defined twice", name);
|
||
}
|
||
} else {
|
||
do_patch:
|
||
esym->st_info = ELF32_ST_INFO(sym_bind, sym_type);
|
||
esym->st_shndx = sh_num;
|
||
esym->st_value = value;
|
||
esym->st_size = size;
|
||
esym->st_other = other;
|
||
}
|
||
} else {
|
||
do_def:
|
||
sym_index = put_elf_sym(s, value, size,
|
||
ELF32_ST_INFO(sym_bind, sym_type), other,
|
||
sh_num, name);
|
||
}
|
||
return sym_index;
|
||
}
|
||
|
||
/* put relocation */
|
||
static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
|
||
int type, int symbol)
|
||
{
|
||
char buf[256];
|
||
Section *sr;
|
||
Elf32_Rel *rel;
|
||
|
||
sr = s->reloc;
|
||
if (!sr) {
|
||
/* if no relocation section, create it */
|
||
snprintf(buf, sizeof(buf), ".rel%s", s->name);
|
||
/* if the symtab is allocated, then we consider the relocation
|
||
are also */
|
||
sr = new_section(tcc_state, buf, SHT_REL, symtab->sh_flags);
|
||
sr->sh_entsize = sizeof(Elf32_Rel);
|
||
sr->link = symtab;
|
||
sr->sh_info = s->sh_num;
|
||
s->reloc = sr;
|
||
}
|
||
rel = section_ptr_add(sr, sizeof(Elf32_Rel));
|
||
rel->r_offset = offset;
|
||
rel->r_info = ELF32_R_INFO(symbol, type);
|
||
}
|
||
|
||
/* put stab debug information */
|
||
|
||
typedef struct {
|
||
unsigned long n_strx; /* index into string table of name */
|
||
unsigned char n_type; /* type of symbol */
|
||
unsigned char n_other; /* misc info (usually empty) */
|
||
unsigned short n_desc; /* description field */
|
||
unsigned long n_value; /* value of symbol */
|
||
} Stab_Sym;
|
||
|
||
static void put_stabs(const char *str, int type, int other, int desc,
|
||
unsigned long value)
|
||
{
|
||
Stab_Sym *sym;
|
||
|
||
sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
|
||
if (str) {
|
||
sym->n_strx = put_elf_str(stabstr_section, str);
|
||
} else {
|
||
sym->n_strx = 0;
|
||
}
|
||
sym->n_type = type;
|
||
sym->n_other = other;
|
||
sym->n_desc = desc;
|
||
sym->n_value = value;
|
||
}
|
||
|
||
static void put_stabs_r(const char *str, int type, int other, int desc,
|
||
unsigned long value, Section *sec, int sym_index)
|
||
{
|
||
put_stabs(str, type, other, desc, value);
|
||
put_elf_reloc(symtab_section, stab_section,
|
||
stab_section->data_offset - sizeof(unsigned long),
|
||
R_DATA_32, sym_index);
|
||
}
|
||
|
||
static void put_stabn(int type, int other, int desc, int value)
|
||
{
|
||
put_stabs(NULL, type, other, desc, value);
|
||
}
|
||
|
||
static void put_stabd(int type, int other, int desc)
|
||
{
|
||
put_stabs(NULL, type, other, desc, 0);
|
||
}
|
||
|
||
/* In an ELF file symbol table, the local symbols must appear below
|
||
the global and weak ones. Since TCC cannot sort it while generating
|
||
the code, we must do it after. All the relocation tables are also
|
||
modified to take into account the symbol table sorting */
|
||
static void sort_syms(TCCState *s1, Section *s)
|
||
{
|
||
int *old_to_new_syms;
|
||
Elf32_Sym *new_syms;
|
||
int nb_syms, i;
|
||
Elf32_Sym *p, *q;
|
||
Elf32_Rel *rel, *rel_end;
|
||
Section *sr;
|
||
int type, sym_index;
|
||
|
||
nb_syms = s->data_offset / sizeof(Elf32_Sym);
|
||
new_syms = tcc_malloc(nb_syms * sizeof(Elf32_Sym));
|
||
old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));
|
||
|
||
/* first pass for local symbols */
|
||
p = (Elf32_Sym *)s->data;
|
||
q = new_syms;
|
||
for(i = 0; i < nb_syms; i++) {
|
||
if (ELF32_ST_BIND(p->st_info) == STB_LOCAL) {
|
||
old_to_new_syms[i] = q - new_syms;
|
||
*q++ = *p;
|
||
}
|
||
p++;
|
||
}
|
||
/* save the number of local symbols in section header */
|
||
s->sh_info = q - new_syms;
|
||
|
||
/* then second pass for non local symbols */
|
||
p = (Elf32_Sym *)s->data;
|
||
for(i = 0; i < nb_syms; i++) {
|
||
if (ELF32_ST_BIND(p->st_info) != STB_LOCAL) {
|
||
old_to_new_syms[i] = q - new_syms;
|
||
*q++ = *p;
|
||
}
|
||
p++;
|
||
}
|
||
|
||
/* we copy the new symbols to the old */
|
||
memcpy(s->data, new_syms, nb_syms * sizeof(Elf32_Sym));
|
||
tcc_free(new_syms);
|
||
|
||
/* now we modify all the relocations */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
sr = s1->sections[i];
|
||
if (sr->sh_type == SHT_REL && sr->link == s) {
|
||
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
|
||
for(rel = (Elf32_Rel *)sr->data;
|
||
rel < rel_end;
|
||
rel++) {
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
type = ELF32_R_TYPE(rel->r_info);
|
||
sym_index = old_to_new_syms[sym_index];
|
||
rel->r_info = ELF32_R_INFO(sym_index, type);
|
||
}
|
||
}
|
||
}
|
||
|
||
tcc_free(old_to_new_syms);
|
||
}
|
||
|
||
/* relocate common symbols in the .bss section */
|
||
static void relocate_common_syms(void)
|
||
{
|
||
Elf32_Sym *sym, *sym_end;
|
||
unsigned long offset, align;
|
||
|
||
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
|
||
for(sym = (Elf32_Sym *)symtab_section->data + 1;
|
||
sym < sym_end;
|
||
sym++) {
|
||
if (sym->st_shndx == SHN_COMMON) {
|
||
/* align symbol */
|
||
align = sym->st_value;
|
||
offset = bss_section->data_offset;
|
||
offset = (offset + align - 1) & -align;
|
||
sym->st_value = offset;
|
||
sym->st_shndx = bss_section->sh_num;
|
||
offset += sym->st_size;
|
||
bss_section->data_offset = offset;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* relocate symbol table, resolve undefined symbols if do_resolve is
|
||
true and output error if undefined symbol. */
|
||
static void relocate_syms(TCCState *s1, int do_resolve)
|
||
{
|
||
Elf32_Sym *sym, *esym, *sym_end;
|
||
int sym_bind, sh_num, sym_index;
|
||
const char *name;
|
||
unsigned long addr;
|
||
|
||
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
|
||
for(sym = (Elf32_Sym *)symtab_section->data + 1;
|
||
sym < sym_end;
|
||
sym++) {
|
||
sh_num = sym->st_shndx;
|
||
if (sh_num == SHN_UNDEF) {
|
||
name = strtab_section->data + sym->st_name;
|
||
if (do_resolve) {
|
||
name = symtab_section->link->data + sym->st_name;
|
||
addr = (unsigned long)resolve_sym(s1, name, ELF32_ST_TYPE(sym->st_info));
|
||
if (addr) {
|
||
sym->st_value = addr;
|
||
goto found;
|
||
}
|
||
} else if (s1->dynsym) {
|
||
/* if dynamic symbol exist, then use it */
|
||
sym_index = find_elf_sym(s1->dynsym, name);
|
||
if (sym_index) {
|
||
esym = &((Elf32_Sym *)s1->dynsym->data)[sym_index];
|
||
sym->st_value = esym->st_value;
|
||
goto found;
|
||
}
|
||
}
|
||
/* XXX: _fp_hw seems to be part of the ABI, so we ignore
|
||
it */
|
||
if (!strcmp(name, "_fp_hw"))
|
||
goto found;
|
||
/* only weak symbols are accepted to be undefined. Their
|
||
value is zero */
|
||
sym_bind = ELF32_ST_BIND(sym->st_info);
|
||
if (sym_bind == STB_WEAK) {
|
||
sym->st_value = 0;
|
||
} else {
|
||
error_noabort("undefined symbol '%s'", name);
|
||
}
|
||
} else if (sh_num < SHN_LORESERVE) {
|
||
/* add section base */
|
||
sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
|
||
}
|
||
found: ;
|
||
}
|
||
}
|
||
|
||
/* relocate a given section (CPU dependent) */
|
||
static void relocate_section(TCCState *s1, Section *s)
|
||
{
|
||
Section *sr;
|
||
Elf32_Rel *rel, *rel_end, *qrel;
|
||
Elf32_Sym *sym;
|
||
int type, sym_index;
|
||
unsigned char *ptr;
|
||
unsigned long val, addr;
|
||
#if defined(TCC_TARGET_I386)
|
||
int esym_index;
|
||
#endif
|
||
|
||
sr = s->reloc;
|
||
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
|
||
qrel = (Elf32_Rel *)sr->data;
|
||
for(rel = qrel;
|
||
rel < rel_end;
|
||
rel++) {
|
||
ptr = s->data + rel->r_offset;
|
||
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
val = sym->st_value;
|
||
type = ELF32_R_TYPE(rel->r_info);
|
||
addr = s->sh_addr + rel->r_offset;
|
||
|
||
/* CPU specific */
|
||
switch(type) {
|
||
#if defined(TCC_TARGET_I386)
|
||
case R_386_32:
|
||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
||
esym_index = s1->symtab_to_dynsym[sym_index];
|
||
qrel->r_offset = rel->r_offset;
|
||
if (esym_index) {
|
||
qrel->r_info = ELF32_R_INFO(esym_index, R_386_32);
|
||
qrel++;
|
||
break;
|
||
} else {
|
||
qrel->r_info = ELF32_R_INFO(0, R_386_RELATIVE);
|
||
qrel++;
|
||
}
|
||
}
|
||
*(int *)ptr += val;
|
||
break;
|
||
case R_386_PC32:
|
||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
||
/* DLL relocation */
|
||
esym_index = s1->symtab_to_dynsym[sym_index];
|
||
if (esym_index) {
|
||
qrel->r_offset = rel->r_offset;
|
||
qrel->r_info = ELF32_R_INFO(esym_index, R_386_PC32);
|
||
qrel++;
|
||
break;
|
||
}
|
||
}
|
||
*(int *)ptr += val - addr;
|
||
break;
|
||
case R_386_PLT32:
|
||
*(int *)ptr += val - addr;
|
||
break;
|
||
case R_386_GLOB_DAT:
|
||
case R_386_JMP_SLOT:
|
||
*(int *)ptr = val;
|
||
break;
|
||
case R_386_GOTPC:
|
||
*(int *)ptr += s1->got->sh_addr - addr;
|
||
break;
|
||
case R_386_GOTOFF:
|
||
*(int *)ptr += val - s1->got->sh_addr;
|
||
break;
|
||
case R_386_GOT32:
|
||
/* we load the got offset */
|
||
*(int *)ptr += s1->got_offsets[sym_index];
|
||
break;
|
||
#elif defined(TCC_TARGET_ARM)
|
||
case R_ARM_PC24:
|
||
case R_ARM_PLT32:
|
||
{
|
||
int x;
|
||
x = (*(int *)ptr)&0xffffff;
|
||
(*(int *)ptr) &= 0xff000000;
|
||
if (x & 0x800000)
|
||
x -= 0x1000000;
|
||
x *= 4;
|
||
x += val - addr;
|
||
if((x & 3) != 0 || x >= 0x4000000 || x < -0x4000000)
|
||
error("can't relocate value at %x",addr);
|
||
x >>= 2;
|
||
x &= 0xffffff;
|
||
(*(int *)ptr) |= x;
|
||
}
|
||
break;
|
||
case R_ARM_ABS32:
|
||
*(int *)ptr += val;
|
||
break;
|
||
case R_ARM_GOTPC:
|
||
*(int *)ptr += s1->got->sh_addr - addr;
|
||
break;
|
||
case R_ARM_GOT32:
|
||
/* we load the got offset */
|
||
*(int *)ptr += s1->got_offsets[sym_index];
|
||
break;
|
||
case R_ARM_COPY:
|
||
break;
|
||
default:
|
||
fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n",
|
||
type,addr,(unsigned int )ptr,val);
|
||
break;
|
||
#elif defined(TCC_TARGET_C67)
|
||
case R_C60_32:
|
||
*(int *)ptr += val;
|
||
break;
|
||
case R_C60LO16:
|
||
{
|
||
uint32_t orig;
|
||
|
||
/* put the low 16 bits of the absolute address */
|
||
// add to what is already there
|
||
|
||
orig = ((*(int *)(ptr )) >> 7) & 0xffff;
|
||
orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16;
|
||
|
||
//patch both at once - assumes always in pairs Low - High
|
||
|
||
*(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) | (((val+orig) & 0xffff) << 7);
|
||
*(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | ((((val+orig)>>16) & 0xffff) << 7);
|
||
}
|
||
break;
|
||
case R_C60HI16:
|
||
break;
|
||
default:
|
||
fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n",
|
||
type,addr,(unsigned int )ptr,val);
|
||
break;
|
||
#else
|
||
#error unsupported processor
|
||
#endif
|
||
}
|
||
}
|
||
/* if the relocation is allocated, we change its symbol table */
|
||
if (sr->sh_flags & SHF_ALLOC)
|
||
sr->link = s1->dynsym;
|
||
}
|
||
|
||
/* relocate relocation table in 'sr' */
|
||
static void relocate_rel(TCCState *s1, Section *sr)
|
||
{
|
||
Section *s;
|
||
Elf32_Rel *rel, *rel_end;
|
||
|
||
s = s1->sections[sr->sh_info];
|
||
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
|
||
for(rel = (Elf32_Rel *)sr->data;
|
||
rel < rel_end;
|
||
rel++) {
|
||
rel->r_offset += s->sh_addr;
|
||
}
|
||
}
|
||
|
||
/* count the number of dynamic relocations so that we can reserve
|
||
their space */
|
||
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
||
{
|
||
Elf32_Rel *rel, *rel_end;
|
||
int sym_index, esym_index, type, count;
|
||
|
||
count = 0;
|
||
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset);
|
||
for(rel = (Elf32_Rel *)sr->data; rel < rel_end; rel++) {
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
type = ELF32_R_TYPE(rel->r_info);
|
||
switch(type) {
|
||
case R_386_32:
|
||
count++;
|
||
break;
|
||
case R_386_PC32:
|
||
esym_index = s1->symtab_to_dynsym[sym_index];
|
||
if (esym_index)
|
||
count++;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
if (count) {
|
||
/* allocate the section */
|
||
sr->sh_flags |= SHF_ALLOC;
|
||
sr->sh_size = count * sizeof(Elf32_Rel);
|
||
}
|
||
return count;
|
||
}
|
||
|
||
static void put_got_offset(TCCState *s1, int index, unsigned long val)
|
||
{
|
||
int n;
|
||
unsigned long *tab;
|
||
|
||
if (index >= s1->nb_got_offsets) {
|
||
/* find immediately bigger power of 2 and reallocate array */
|
||
n = 1;
|
||
while (index >= n)
|
||
n *= 2;
|
||
tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long));
|
||
if (!tab)
|
||
error("memory full");
|
||
s1->got_offsets = tab;
|
||
memset(s1->got_offsets + s1->nb_got_offsets, 0,
|
||
(n - s1->nb_got_offsets) * sizeof(unsigned long));
|
||
s1->nb_got_offsets = n;
|
||
}
|
||
s1->got_offsets[index] = val;
|
||
}
|
||
|
||
/* XXX: suppress that */
|
||
static void put32(unsigned char *p, uint32_t val)
|
||
{
|
||
p[0] = val;
|
||
p[1] = val >> 8;
|
||
p[2] = val >> 16;
|
||
p[3] = val >> 24;
|
||
}
|
||
|
||
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM)
|
||
static uint32_t get32(unsigned char *p)
|
||
{
|
||
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||
}
|
||
#endif
|
||
|
||
static void build_got(TCCState *s1)
|
||
{
|
||
unsigned char *ptr;
|
||
|
||
/* if no got, then create it */
|
||
s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||
s1->got->sh_entsize = 4;
|
||
add_elf_sym(symtab_section, 0, 4, ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT),
|
||
0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
|
||
ptr = section_ptr_add(s1->got, 3 * sizeof(int));
|
||
/* keep space for _DYNAMIC pointer, if present */
|
||
put32(ptr, 0);
|
||
/* two dummy got entries */
|
||
put32(ptr + 4, 0);
|
||
put32(ptr + 8, 0);
|
||
}
|
||
|
||
/* put a got entry corresponding to a symbol in symtab_section. 'size'
|
||
and 'info' can be modifed if more precise info comes from the DLL */
|
||
static void put_got_entry(TCCState *s1,
|
||
int reloc_type, unsigned long size, int info,
|
||
int sym_index)
|
||
{
|
||
int index;
|
||
const char *name;
|
||
Elf32_Sym *sym;
|
||
unsigned long offset;
|
||
int *ptr;
|
||
|
||
if (!s1->got)
|
||
build_got(s1);
|
||
|
||
/* if a got entry already exists for that symbol, no need to add one */
|
||
if (sym_index < s1->nb_got_offsets &&
|
||
s1->got_offsets[sym_index] != 0)
|
||
return;
|
||
|
||
put_got_offset(s1, sym_index, s1->got->data_offset);
|
||
|
||
if (s1->dynsym) {
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
name = symtab_section->link->data + sym->st_name;
|
||
offset = sym->st_value;
|
||
#ifdef TCC_TARGET_I386
|
||
if (reloc_type == R_386_JMP_SLOT) {
|
||
Section *plt;
|
||
uint8_t *p;
|
||
int modrm;
|
||
|
||
/* if we build a DLL, we add a %ebx offset */
|
||
if (s1->output_type == TCC_OUTPUT_DLL)
|
||
modrm = 0xa3;
|
||
else
|
||
modrm = 0x25;
|
||
|
||
/* add a PLT entry */
|
||
plt = s1->plt;
|
||
if (plt->data_offset == 0) {
|
||
/* first plt entry */
|
||
p = section_ptr_add(plt, 16);
|
||
p[0] = 0xff; /* pushl got + 4 */
|
||
p[1] = modrm + 0x10;
|
||
put32(p + 2, 4);
|
||
p[6] = 0xff; /* jmp *(got + 8) */
|
||
p[7] = modrm;
|
||
put32(p + 8, 8);
|
||
}
|
||
|
||
p = section_ptr_add(plt, 16);
|
||
p[0] = 0xff; /* jmp *(got + x) */
|
||
p[1] = modrm;
|
||
put32(p + 2, s1->got->data_offset);
|
||
p[6] = 0x68; /* push $xxx */
|
||
put32(p + 7, (plt->data_offset - 32) >> 1);
|
||
p[11] = 0xe9; /* jmp plt_start */
|
||
put32(p + 12, -(plt->data_offset));
|
||
|
||
/* the symbol is modified so that it will be relocated to
|
||
the PLT */
|
||
if (s1->output_type == TCC_OUTPUT_EXE)
|
||
offset = plt->data_offset - 16;
|
||
}
|
||
#elif defined(TCC_TARGET_ARM)
|
||
if (reloc_type == R_ARM_JUMP_SLOT) {
|
||
Section *plt;
|
||
uint8_t *p;
|
||
|
||
/* if we build a DLL, we add a %ebx offset */
|
||
if (s1->output_type == TCC_OUTPUT_DLL)
|
||
error("DLLs unimplemented!");
|
||
|
||
/* add a PLT entry */
|
||
plt = s1->plt;
|
||
if (plt->data_offset == 0) {
|
||
/* first plt entry */
|
||
p = section_ptr_add(plt, 16);
|
||
put32(p , 0xe52de004);
|
||
put32(p + 4, 0xe59fe010);
|
||
put32(p + 8, 0xe08fe00e);
|
||
put32(p + 12, 0xe5bef008);
|
||
}
|
||
|
||
p = section_ptr_add(plt, 16);
|
||
put32(p , 0xe59fc004);
|
||
put32(p+4, 0xe08fc00c);
|
||
put32(p+8, 0xe59cf000);
|
||
put32(p+12, s1->got->data_offset);
|
||
|
||
/* the symbol is modified so that it will be relocated to
|
||
the PLT */
|
||
if (s1->output_type == TCC_OUTPUT_EXE)
|
||
offset = plt->data_offset - 16;
|
||
}
|
||
#elif defined(TCC_TARGET_C67)
|
||
error("C67 got not implemented");
|
||
#else
|
||
#error unsupported CPU
|
||
#endif
|
||
index = put_elf_sym(s1->dynsym, offset,
|
||
size, info, 0, sym->st_shndx, name);
|
||
/* put a got entry */
|
||
put_elf_reloc(s1->dynsym, s1->got,
|
||
s1->got->data_offset,
|
||
reloc_type, index);
|
||
}
|
||
ptr = section_ptr_add(s1->got, sizeof(int));
|
||
*ptr = 0;
|
||
}
|
||
|
||
/* build GOT and PLT entries */
|
||
static void build_got_entries(TCCState *s1)
|
||
{
|
||
Section *s, *symtab __attribute__((unused));
|
||
Elf32_Rel *rel, *rel_end;
|
||
Elf32_Sym *sym;
|
||
int i, type, reloc_type, sym_index;
|
||
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if (s->sh_type != SHT_REL)
|
||
continue;
|
||
/* no need to handle got relocations */
|
||
if (s->link != symtab_section)
|
||
continue;
|
||
symtab = s->link;
|
||
rel_end = (Elf32_Rel *)(s->data + s->data_offset);
|
||
for(rel = (Elf32_Rel *)s->data;
|
||
rel < rel_end;
|
||
rel++) {
|
||
type = ELF32_R_TYPE(rel->r_info);
|
||
switch(type) {
|
||
#if defined(TCC_TARGET_I386)
|
||
case R_386_GOT32:
|
||
case R_386_GOTOFF:
|
||
case R_386_GOTPC:
|
||
case R_386_PLT32:
|
||
if (!s1->got)
|
||
build_got(s1);
|
||
if (type == R_386_GOT32 || type == R_386_PLT32) {
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
/* look at the symbol got offset. If none, then add one */
|
||
if (type == R_386_GOT32)
|
||
reloc_type = R_386_GLOB_DAT;
|
||
else
|
||
reloc_type = R_386_JMP_SLOT;
|
||
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
|
||
sym_index);
|
||
}
|
||
break;
|
||
#elif defined(TCC_TARGET_ARM)
|
||
case R_ARM_GOT32:
|
||
case R_ARM_GOTOFF:
|
||
case R_ARM_GOTPC:
|
||
case R_ARM_PLT32:
|
||
if (!s1->got)
|
||
build_got(s1);
|
||
if (type == R_ARM_GOT32 || type == R_ARM_PLT32) {
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
/* look at the symbol got offset. If none, then add one */
|
||
if (type == R_ARM_GOT32)
|
||
reloc_type = R_ARM_GLOB_DAT;
|
||
else
|
||
reloc_type = R_ARM_JUMP_SLOT;
|
||
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
|
||
sym_index);
|
||
}
|
||
break;
|
||
#elif defined(TCC_TARGET_C67)
|
||
case R_C60_GOT32:
|
||
case R_C60_GOTOFF:
|
||
case R_C60_GOTPC:
|
||
case R_C60_PLT32:
|
||
if (!s1->got)
|
||
build_got(s1);
|
||
if (type == R_C60_GOT32 || type == R_C60_PLT32) {
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
/* look at the symbol got offset. If none, then add one */
|
||
if (type == R_C60_GOT32)
|
||
reloc_type = R_C60_GLOB_DAT;
|
||
else
|
||
reloc_type = R_C60_JMP_SLOT;
|
||
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
|
||
sym_index);
|
||
}
|
||
break;
|
||
#else
|
||
#error unsupported CPU
|
||
#endif
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static Section *new_symtab(TCCState *s1,
|
||
const char *symtab_name, int sh_type, int sh_flags,
|
||
const char *strtab_name,
|
||
const char *hash_name, int hash_sh_flags)
|
||
{
|
||
Section *symtab, *strtab, *hash;
|
||
int *ptr, nb_buckets;
|
||
|
||
symtab = new_section(s1, symtab_name, sh_type, sh_flags);
|
||
symtab->sh_entsize = sizeof(Elf32_Sym);
|
||
strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
|
||
put_elf_str(strtab, "");
|
||
symtab->link = strtab;
|
||
put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
|
||
|
||
nb_buckets = 1;
|
||
|
||
hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
|
||
hash->sh_entsize = sizeof(int);
|
||
symtab->hash = hash;
|
||
hash->link = symtab;
|
||
|
||
ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
|
||
ptr[0] = nb_buckets;
|
||
ptr[1] = 1;
|
||
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
|
||
return symtab;
|
||
}
|
||
|
||
/* put dynamic tag */
|
||
static void put_dt(Section *dynamic, int dt, unsigned long val)
|
||
{
|
||
Elf32_Dyn *dyn;
|
||
dyn = section_ptr_add(dynamic, sizeof(Elf32_Dyn));
|
||
dyn->d_tag = dt;
|
||
dyn->d_un.d_val = val;
|
||
}
|
||
|
||
static void add_init_array_defines(TCCState *s1, const char *section_name)
|
||
{
|
||
Section *s;
|
||
long end_offset;
|
||
char sym_start[1024];
|
||
char sym_end[1024];
|
||
|
||
snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
|
||
snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);
|
||
|
||
s = find_section(s1, section_name);
|
||
if (!s) {
|
||
end_offset = 0;
|
||
s = data_section;
|
||
} else {
|
||
end_offset = s->data_offset;
|
||
}
|
||
|
||
add_elf_sym(symtab_section,
|
||
0, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
s->sh_num, sym_start);
|
||
add_elf_sym(symtab_section,
|
||
end_offset, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
s->sh_num, sym_end);
|
||
}
|
||
|
||
/* add tcc runtime libraries */
|
||
static void tcc_add_runtime(TCCState *s1)
|
||
{
|
||
char buf[1024];
|
||
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
if (do_bounds_check) {
|
||
unsigned long *ptr;
|
||
Section *init_section;
|
||
unsigned char *pinit;
|
||
int sym_index;
|
||
|
||
/* XXX: add an object file to do that */
|
||
ptr = section_ptr_add(bounds_section, sizeof(unsigned long));
|
||
*ptr = 0;
|
||
add_elf_sym(symtab_section, 0, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
bounds_section->sh_num, "__bounds_start");
|
||
/* add bound check code */
|
||
snprintf(buf, sizeof(buf), "%s/%s", tcc_lib_path, "bcheck.o");
|
||
tcc_add_file(s1, buf);
|
||
#ifdef TCC_TARGET_I386
|
||
if (s1->output_type != TCC_OUTPUT_MEMORY) {
|
||
/* add 'call __bound_init()' in .init section */
|
||
init_section = find_section(s1, ".init");
|
||
pinit = section_ptr_add(init_section, 5);
|
||
pinit[0] = 0xe8;
|
||
put32(pinit + 1, -4);
|
||
sym_index = find_elf_sym(symtab_section, "__bound_init");
|
||
put_elf_reloc(symtab_section, init_section,
|
||
init_section->data_offset - 4, R_386_PC32, sym_index);
|
||
}
|
||
#endif
|
||
}
|
||
#endif
|
||
/* add libc */
|
||
if (!s1->nostdlib) {
|
||
tcc_add_library(s1, "c");
|
||
|
||
snprintf(buf, sizeof(buf), "%s/%s", tcc_lib_path, "libtcc1.a");
|
||
tcc_add_file(s1, buf);
|
||
}
|
||
/* add crt end if not memory output */
|
||
if (s1->output_type != TCC_OUTPUT_MEMORY && !s1->nostdlib) {
|
||
tcc_add_file(s1, CONFIG_TCC_CRT_PREFIX "/crtn.o");
|
||
}
|
||
}
|
||
|
||
/* add various standard linker symbols (must be done after the
|
||
sections are filled (for example after allocating common
|
||
symbols)) */
|
||
static void tcc_add_linker_symbols(TCCState *s1)
|
||
{
|
||
char buf[1024];
|
||
int i;
|
||
Section *s;
|
||
|
||
add_elf_sym(symtab_section,
|
||
text_section->data_offset, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
text_section->sh_num, "_etext");
|
||
add_elf_sym(symtab_section,
|
||
data_section->data_offset, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
data_section->sh_num, "_edata");
|
||
add_elf_sym(symtab_section,
|
||
bss_section->data_offset, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
bss_section->sh_num, "_end");
|
||
/* horrible new standard ldscript defines */
|
||
add_init_array_defines(s1, ".preinit_array");
|
||
add_init_array_defines(s1, ".init_array");
|
||
add_init_array_defines(s1, ".fini_array");
|
||
|
||
/* add start and stop symbols for sections whose name can be
|
||
expressed in C */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if (s->sh_type == SHT_PROGBITS &&
|
||
(s->sh_flags & SHF_ALLOC)) {
|
||
const char *p;
|
||
int ch;
|
||
|
||
/* check if section name can be expressed in C */
|
||
p = s->name;
|
||
for(;;) {
|
||
ch = *p;
|
||
if (!ch)
|
||
break;
|
||
if (!isid(ch) && !isnum(ch))
|
||
goto next_sec;
|
||
p++;
|
||
}
|
||
snprintf(buf, sizeof(buf), "__start_%s", s->name);
|
||
add_elf_sym(symtab_section,
|
||
0, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
s->sh_num, buf);
|
||
snprintf(buf, sizeof(buf), "__stop_%s", s->name);
|
||
add_elf_sym(symtab_section,
|
||
s->data_offset, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
s->sh_num, buf);
|
||
}
|
||
next_sec: ;
|
||
}
|
||
}
|
||
|
||
/* name of ELF interpreter */
|
||
#ifdef __FreeBSD__
|
||
static char elf_interp[] = "/usr/libexec/ld-elf.so.1";
|
||
#else
|
||
static char elf_interp[] = "/lib/ld-linux.so.2";
|
||
#endif
|
||
|
||
static void tcc_output_binary(TCCState *s1, FILE *f,
|
||
const int *section_order)
|
||
{
|
||
Section *s;
|
||
int i, offset, size;
|
||
|
||
offset = 0;
|
||
for(i=1;i<s1->nb_sections;i++) {
|
||
s = s1->sections[section_order[i]];
|
||
if (s->sh_type != SHT_NOBITS &&
|
||
(s->sh_flags & SHF_ALLOC)) {
|
||
while (offset < s->sh_offset) {
|
||
fputc(0, f);
|
||
offset++;
|
||
}
|
||
size = s->sh_size;
|
||
dummy_size_t = fwrite(s->data, 1, size, f);
|
||
offset += size;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* output an ELF file */
|
||
/* XXX: suppress unneeded sections */
|
||
int tcc_output_file(TCCState *s1, const char *filename)
|
||
{
|
||
Elf32_Ehdr ehdr;
|
||
FILE *f;
|
||
int fd, mode, ret;
|
||
int *section_order;
|
||
int shnum, i, phnum, file_offset, offset, size, j, tmp, sh_order_index, k;
|
||
unsigned long addr;
|
||
Section *strsec, *s;
|
||
Elf32_Shdr shdr, *sh;
|
||
Elf32_Phdr *phdr, *ph;
|
||
Section *interp, *dynamic, *dynstr;
|
||
unsigned long saved_dynamic_data_offset;
|
||
Elf32_Sym *sym;
|
||
int type, file_type;
|
||
unsigned long rel_addr, rel_size;
|
||
|
||
file_type = s1->output_type;
|
||
s1->nb_errors = 0;
|
||
|
||
if (file_type != TCC_OUTPUT_OBJ) {
|
||
tcc_add_runtime(s1);
|
||
}
|
||
|
||
phdr = NULL;
|
||
section_order = NULL;
|
||
interp = NULL;
|
||
dynamic = NULL;
|
||
dynstr = NULL; /* avoid warning */
|
||
saved_dynamic_data_offset = 0; /* avoid warning */
|
||
|
||
if (file_type != TCC_OUTPUT_OBJ) {
|
||
relocate_common_syms();
|
||
|
||
tcc_add_linker_symbols(s1);
|
||
|
||
if (!s1->static_link) {
|
||
const char *name;
|
||
int sym_index, index;
|
||
Elf32_Sym *esym, *sym_end;
|
||
|
||
if (file_type == TCC_OUTPUT_EXE) {
|
||
char *ptr;
|
||
/* add interpreter section only if executable */
|
||
interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
|
||
interp->sh_addralign = 1;
|
||
ptr = section_ptr_add(interp, sizeof(elf_interp));
|
||
strcpy(ptr, elf_interp);
|
||
}
|
||
|
||
/* add dynamic symbol table */
|
||
s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
|
||
".dynstr",
|
||
".hash", SHF_ALLOC);
|
||
dynstr = s1->dynsym->link;
|
||
|
||
/* add dynamic section */
|
||
dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
|
||
SHF_ALLOC | SHF_WRITE);
|
||
dynamic->link = dynstr;
|
||
dynamic->sh_entsize = sizeof(Elf32_Dyn);
|
||
|
||
/* add PLT */
|
||
s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
|
||
SHF_ALLOC | SHF_EXECINSTR);
|
||
s1->plt->sh_entsize = 4;
|
||
|
||
build_got(s1);
|
||
|
||
/* scan for undefined symbols and see if they are in the
|
||
dynamic symbols. If a symbol STT_FUNC is found, then we
|
||
add it in the PLT. If a symbol STT_OBJECT is found, we
|
||
add it in the .bss section with a suitable relocation */
|
||
sym_end = (Elf32_Sym *)(symtab_section->data +
|
||
symtab_section->data_offset);
|
||
if (file_type == TCC_OUTPUT_EXE) {
|
||
for(sym = (Elf32_Sym *)symtab_section->data + 1;
|
||
sym < sym_end;
|
||
sym++) {
|
||
if (sym->st_shndx == SHN_UNDEF) {
|
||
name = symtab_section->link->data + sym->st_name;
|
||
sym_index = find_elf_sym(s1->dynsymtab_section, name);
|
||
if (sym_index) {
|
||
esym = &((Elf32_Sym *)s1->dynsymtab_section->data)[sym_index];
|
||
type = ELF32_ST_TYPE(esym->st_info);
|
||
if (type == STT_FUNC) {
|
||
put_got_entry(s1, R_JMP_SLOT, esym->st_size,
|
||
esym->st_info,
|
||
sym - (Elf32_Sym *)symtab_section->data);
|
||
} else if (type == STT_OBJECT) {
|
||
unsigned long offset;
|
||
offset = bss_section->data_offset;
|
||
/* XXX: which alignment ? */
|
||
offset = (offset + 16 - 1) & -16;
|
||
index = put_elf_sym(s1->dynsym, offset, esym->st_size,
|
||
esym->st_info, 0,
|
||
bss_section->sh_num, name);
|
||
put_elf_reloc(s1->dynsym, bss_section,
|
||
offset, R_COPY, index);
|
||
offset += esym->st_size;
|
||
bss_section->data_offset = offset;
|
||
}
|
||
} else {
|
||
/* STB_WEAK undefined symbols are accepted */
|
||
/* XXX: _fp_hw seems to be part of the ABI, so we ignore
|
||
it */
|
||
if (ELF32_ST_BIND(sym->st_info) == STB_WEAK ||
|
||
!strcmp(name, "_fp_hw")) {
|
||
} else {
|
||
error_noabort("undefined symbol '%s'", name);
|
||
}
|
||
}
|
||
} else if (s1->rdynamic &&
|
||
ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
|
||
/* if -rdynamic option, then export all non
|
||
local symbols */
|
||
name = symtab_section->link->data + sym->st_name;
|
||
put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
|
||
sym->st_info, 0,
|
||
sym->st_shndx, name);
|
||
}
|
||
}
|
||
|
||
if (s1->nb_errors)
|
||
goto fail;
|
||
|
||
/* now look at unresolved dynamic symbols and export
|
||
corresponding symbol */
|
||
sym_end = (Elf32_Sym *)(s1->dynsymtab_section->data +
|
||
s1->dynsymtab_section->data_offset);
|
||
for(esym = (Elf32_Sym *)s1->dynsymtab_section->data + 1;
|
||
esym < sym_end;
|
||
esym++) {
|
||
if (esym->st_shndx == SHN_UNDEF) {
|
||
name = s1->dynsymtab_section->link->data + esym->st_name;
|
||
sym_index = find_elf_sym(symtab_section, name);
|
||
if (sym_index) {
|
||
/* XXX: avoid adding a symbol if already
|
||
present because of -rdynamic ? */
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
|
||
sym->st_info, 0,
|
||
sym->st_shndx, name);
|
||
} else {
|
||
if (ELF32_ST_BIND(esym->st_info) == STB_WEAK) {
|
||
/* weak symbols can stay undefined */
|
||
} else {
|
||
warning("undefined dynamic symbol '%s'", name);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
int nb_syms;
|
||
/* shared library case : we simply export all the global symbols */
|
||
nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym);
|
||
s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms);
|
||
for(sym = (Elf32_Sym *)symtab_section->data + 1;
|
||
sym < sym_end;
|
||
sym++) {
|
||
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
|
||
name = symtab_section->link->data + sym->st_name;
|
||
index = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
|
||
sym->st_info, 0,
|
||
sym->st_shndx, name);
|
||
s1->symtab_to_dynsym[sym -
|
||
(Elf32_Sym *)symtab_section->data] =
|
||
index;
|
||
}
|
||
}
|
||
}
|
||
|
||
build_got_entries(s1);
|
||
|
||
/* add a list of needed dlls */
|
||
for(i = 0; i < s1->nb_loaded_dlls; i++) {
|
||
DLLReference *dllref = s1->loaded_dlls[i];
|
||
if (dllref->level == 0)
|
||
put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
|
||
}
|
||
/* XXX: currently, since we do not handle PIC code, we
|
||
must relocate the readonly segments */
|
||
if (file_type == TCC_OUTPUT_DLL)
|
||
put_dt(dynamic, DT_TEXTREL, 0);
|
||
|
||
/* add necessary space for other entries */
|
||
saved_dynamic_data_offset = dynamic->data_offset;
|
||
dynamic->data_offset += 8 * 9;
|
||
} else {
|
||
/* still need to build got entries in case of static link */
|
||
build_got_entries(s1);
|
||
}
|
||
}
|
||
|
||
memset(&ehdr, 0, sizeof(ehdr));
|
||
|
||
/* we add a section for symbols */
|
||
strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
|
||
put_elf_str(strsec, "");
|
||
|
||
/* compute number of sections */
|
||
shnum = s1->nb_sections;
|
||
|
||
/* this array is used to reorder sections in the output file */
|
||
section_order = tcc_malloc(sizeof(int) * shnum);
|
||
section_order[0] = 0;
|
||
sh_order_index = 1;
|
||
|
||
/* compute number of program headers */
|
||
switch(file_type) {
|
||
default:
|
||
case TCC_OUTPUT_OBJ:
|
||
phnum = 0;
|
||
break;
|
||
case TCC_OUTPUT_EXE:
|
||
if (!s1->static_link)
|
||
phnum = 4;
|
||
else
|
||
phnum = 2;
|
||
break;
|
||
case TCC_OUTPUT_DLL:
|
||
phnum = 3;
|
||
break;
|
||
}
|
||
|
||
/* allocate strings for section names and decide if an unallocated
|
||
section should be output */
|
||
/* NOTE: the strsec section comes last, so its size is also
|
||
correct ! */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
s->sh_name = put_elf_str(strsec, s->name);
|
||
/* when generating a DLL, we include relocations but we may
|
||
patch them */
|
||
if (file_type == TCC_OUTPUT_DLL &&
|
||
s->sh_type == SHT_REL &&
|
||
!(s->sh_flags & SHF_ALLOC)) {
|
||
prepare_dynamic_rel(s1, s);
|
||
} else if (do_debug ||
|
||
file_type == TCC_OUTPUT_OBJ ||
|
||
(s->sh_flags & SHF_ALLOC) ||
|
||
i == (s1->nb_sections - 1)) {
|
||
/* we output all sections if debug or object file */
|
||
s->sh_size = s->data_offset;
|
||
}
|
||
}
|
||
|
||
/* allocate program segment headers */
|
||
phdr = tcc_mallocz(phnum * sizeof(Elf32_Phdr));
|
||
|
||
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
|
||
file_offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
|
||
} else {
|
||
file_offset = 0;
|
||
}
|
||
if (phnum > 0) {
|
||
/* compute section to program header mapping */
|
||
if (s1->has_text_addr) {
|
||
int a_offset, p_offset;
|
||
addr = s1->text_addr;
|
||
/* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
|
||
ELF_PAGE_SIZE */
|
||
a_offset = addr & (ELF_PAGE_SIZE - 1);
|
||
p_offset = file_offset & (ELF_PAGE_SIZE - 1);
|
||
if (a_offset < p_offset)
|
||
a_offset += ELF_PAGE_SIZE;
|
||
file_offset += (a_offset - p_offset);
|
||
} else {
|
||
if (file_type == TCC_OUTPUT_DLL)
|
||
addr = 0;
|
||
else
|
||
addr = ELF_START_ADDR;
|
||
/* compute address after headers */
|
||
addr += (file_offset & (ELF_PAGE_SIZE - 1));
|
||
}
|
||
|
||
/* dynamic relocation table information, for .dynamic section */
|
||
rel_size = 0;
|
||
rel_addr = 0;
|
||
|
||
/* leave one program header for the program interpreter */
|
||
ph = &phdr[0];
|
||
if (interp)
|
||
ph++;
|
||
|
||
for(j = 0; j < 2; j++) {
|
||
ph->p_type = PT_LOAD;
|
||
if (j == 0)
|
||
ph->p_flags = PF_R | PF_X;
|
||
else
|
||
ph->p_flags = PF_R | PF_W;
|
||
ph->p_align = ELF_PAGE_SIZE;
|
||
|
||
/* we do the following ordering: interp, symbol tables,
|
||
relocations, progbits, nobits */
|
||
/* XXX: do faster and simpler sorting */
|
||
for(k = 0; k < 5; k++) {
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
/* compute if section should be included */
|
||
if (j == 0) {
|
||
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
|
||
SHF_ALLOC)
|
||
continue;
|
||
} else {
|
||
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
|
||
(SHF_ALLOC | SHF_WRITE))
|
||
continue;
|
||
}
|
||
if (s == interp) {
|
||
if (k != 0)
|
||
continue;
|
||
} else if (s->sh_type == SHT_DYNSYM ||
|
||
s->sh_type == SHT_STRTAB ||
|
||
s->sh_type == SHT_HASH) {
|
||
if (k != 1)
|
||
continue;
|
||
} else if (s->sh_type == SHT_REL) {
|
||
if (k != 2)
|
||
continue;
|
||
} else if (s->sh_type == SHT_NOBITS) {
|
||
if (k != 4)
|
||
continue;
|
||
} else {
|
||
if (k != 3)
|
||
continue;
|
||
}
|
||
section_order[sh_order_index++] = i;
|
||
|
||
/* section matches: we align it and add its size */
|
||
tmp = addr;
|
||
addr = (addr + s->sh_addralign - 1) &
|
||
~(s->sh_addralign - 1);
|
||
file_offset += addr - tmp;
|
||
s->sh_offset = file_offset;
|
||
s->sh_addr = addr;
|
||
|
||
/* update program header infos */
|
||
if (ph->p_offset == 0) {
|
||
ph->p_offset = file_offset;
|
||
ph->p_vaddr = addr;
|
||
ph->p_paddr = ph->p_vaddr;
|
||
}
|
||
/* update dynamic relocation infos */
|
||
if (s->sh_type == SHT_REL) {
|
||
if (rel_size == 0)
|
||
rel_addr = addr;
|
||
rel_size += s->sh_size;
|
||
}
|
||
addr += s->sh_size;
|
||
if (s->sh_type != SHT_NOBITS)
|
||
file_offset += s->sh_size;
|
||
}
|
||
}
|
||
ph->p_filesz = file_offset - ph->p_offset;
|
||
ph->p_memsz = addr - ph->p_vaddr;
|
||
ph++;
|
||
if (j == 0) {
|
||
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
|
||
/* if in the middle of a page, we duplicate the page in
|
||
memory so that one copy is RX and the other is RW */
|
||
if ((addr & (ELF_PAGE_SIZE - 1)) != 0)
|
||
addr += ELF_PAGE_SIZE;
|
||
} else {
|
||
addr = (addr + ELF_PAGE_SIZE - 1) & ~(ELF_PAGE_SIZE - 1);
|
||
file_offset = (file_offset + ELF_PAGE_SIZE - 1) &
|
||
~(ELF_PAGE_SIZE - 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* if interpreter, then add corresponing program header */
|
||
if (interp) {
|
||
ph = &phdr[0];
|
||
|
||
ph->p_type = PT_INTERP;
|
||
ph->p_offset = interp->sh_offset;
|
||
ph->p_vaddr = interp->sh_addr;
|
||
ph->p_paddr = ph->p_vaddr;
|
||
ph->p_filesz = interp->sh_size;
|
||
ph->p_memsz = interp->sh_size;
|
||
ph->p_flags = PF_R;
|
||
ph->p_align = interp->sh_addralign;
|
||
}
|
||
|
||
/* if dynamic section, then add corresponing program header */
|
||
if (dynamic) {
|
||
Elf32_Sym *sym_end;
|
||
|
||
ph = &phdr[phnum - 1];
|
||
|
||
ph->p_type = PT_DYNAMIC;
|
||
ph->p_offset = dynamic->sh_offset;
|
||
ph->p_vaddr = dynamic->sh_addr;
|
||
ph->p_paddr = ph->p_vaddr;
|
||
ph->p_filesz = dynamic->sh_size;
|
||
ph->p_memsz = dynamic->sh_size;
|
||
ph->p_flags = PF_R | PF_W;
|
||
ph->p_align = dynamic->sh_addralign;
|
||
|
||
/* put GOT dynamic section address */
|
||
put32(s1->got->data, dynamic->sh_addr);
|
||
|
||
/* relocate the PLT */
|
||
if (file_type == TCC_OUTPUT_EXE) {
|
||
uint8_t *p, *p_end;
|
||
|
||
p = s1->plt->data;
|
||
p_end = p + s1->plt->data_offset;
|
||
if (p < p_end) {
|
||
#if defined(TCC_TARGET_I386)
|
||
put32(p + 2, get32(p + 2) + s1->got->sh_addr);
|
||
put32(p + 8, get32(p + 8) + s1->got->sh_addr);
|
||
p += 16;
|
||
while (p < p_end) {
|
||
put32(p + 2, get32(p + 2) + s1->got->sh_addr);
|
||
p += 16;
|
||
}
|
||
#elif defined(TCC_TARGET_ARM)
|
||
int x;
|
||
x=s1->got->sh_addr - s1->plt->sh_addr - 12;
|
||
p +=16;
|
||
while (p < p_end) {
|
||
put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
|
||
p += 16;
|
||
}
|
||
#elif defined(TCC_TARGET_C67)
|
||
/* XXX: TODO */
|
||
#else
|
||
#error unsupported CPU
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/* relocate symbols in .dynsym */
|
||
sym_end = (Elf32_Sym *)(s1->dynsym->data + s1->dynsym->data_offset);
|
||
for(sym = (Elf32_Sym *)s1->dynsym->data + 1;
|
||
sym < sym_end;
|
||
sym++) {
|
||
if (sym->st_shndx == SHN_UNDEF) {
|
||
/* relocate to the PLT if the symbol corresponds
|
||
to a PLT entry */
|
||
if (sym->st_value)
|
||
sym->st_value += s1->plt->sh_addr;
|
||
} else if (sym->st_shndx < SHN_LORESERVE) {
|
||
/* do symbol relocation */
|
||
sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
|
||
}
|
||
}
|
||
|
||
/* put dynamic section entries */
|
||
dynamic->data_offset = saved_dynamic_data_offset;
|
||
put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
|
||
put_dt(dynamic, DT_STRTAB, dynstr->sh_addr);
|
||
put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
|
||
put_dt(dynamic, DT_STRSZ, dynstr->data_offset);
|
||
put_dt(dynamic, DT_SYMENT, sizeof(Elf32_Sym));
|
||
put_dt(dynamic, DT_REL, rel_addr);
|
||
put_dt(dynamic, DT_RELSZ, rel_size);
|
||
put_dt(dynamic, DT_RELENT, sizeof(Elf32_Rel));
|
||
put_dt(dynamic, DT_NULL, 0);
|
||
}
|
||
|
||
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||
ehdr.e_phnum = phnum;
|
||
ehdr.e_phoff = sizeof(Elf32_Ehdr);
|
||
}
|
||
|
||
/* all other sections come after */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
|
||
continue;
|
||
section_order[sh_order_index++] = i;
|
||
|
||
file_offset = (file_offset + s->sh_addralign - 1) &
|
||
~(s->sh_addralign - 1);
|
||
s->sh_offset = file_offset;
|
||
if (s->sh_type != SHT_NOBITS)
|
||
file_offset += s->sh_size;
|
||
}
|
||
|
||
/* if building executable or DLL, then relocate each section
|
||
except the GOT which is already relocated */
|
||
if (file_type != TCC_OUTPUT_OBJ) {
|
||
relocate_syms(s1, 0);
|
||
|
||
if (s1->nb_errors != 0) {
|
||
fail:
|
||
ret = -1;
|
||
goto the_end;
|
||
}
|
||
|
||
/* relocate sections */
|
||
/* XXX: ignore sections with allocated relocations ? */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if (s->reloc && s != s1->got)
|
||
relocate_section(s1, s);
|
||
}
|
||
|
||
/* relocate relocation entries if the relocation tables are
|
||
allocated in the executable */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if ((s->sh_flags & SHF_ALLOC) &&
|
||
s->sh_type == SHT_REL) {
|
||
relocate_rel(s1, s);
|
||
}
|
||
}
|
||
|
||
/* get entry point address */
|
||
if (file_type == TCC_OUTPUT_EXE)
|
||
ehdr.e_entry = (unsigned long)tcc_get_symbol_err(s1, "_start");
|
||
else
|
||
ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
|
||
}
|
||
|
||
/* write elf file */
|
||
if (file_type == TCC_OUTPUT_OBJ)
|
||
mode = 0666;
|
||
else
|
||
mode = 0777;
|
||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
|
||
if (fd < 0) {
|
||
error_noabort("could not write '%s'", filename);
|
||
goto fail;
|
||
}
|
||
f = fdopen(fd, "wb");
|
||
|
||
#ifdef TCC_TARGET_COFF
|
||
if (s1->output_format == TCC_OUTPUT_FORMAT_COFF) {
|
||
tcc_output_coff(s1, f);
|
||
} else
|
||
#endif
|
||
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
|
||
sort_syms(s1, symtab_section);
|
||
|
||
/* align to 4 */
|
||
file_offset = (file_offset + 3) & -4;
|
||
|
||
/* fill header */
|
||
ehdr.e_ident[0] = ELFMAG0;
|
||
ehdr.e_ident[1] = ELFMAG1;
|
||
ehdr.e_ident[2] = ELFMAG2;
|
||
ehdr.e_ident[3] = ELFMAG3;
|
||
ehdr.e_ident[4] = ELFCLASS32;
|
||
ehdr.e_ident[5] = ELFDATA2LSB;
|
||
ehdr.e_ident[6] = EV_CURRENT;
|
||
#ifdef __FreeBSD__
|
||
ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
|
||
#endif
|
||
#ifdef TCC_TARGET_ARM
|
||
ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
|
||
#endif
|
||
switch(file_type) {
|
||
default:
|
||
case TCC_OUTPUT_EXE:
|
||
ehdr.e_type = ET_EXEC;
|
||
break;
|
||
case TCC_OUTPUT_DLL:
|
||
ehdr.e_type = ET_DYN;
|
||
break;
|
||
case TCC_OUTPUT_OBJ:
|
||
ehdr.e_type = ET_REL;
|
||
break;
|
||
}
|
||
ehdr.e_machine = EM_TCC_TARGET;
|
||
ehdr.e_version = EV_CURRENT;
|
||
ehdr.e_shoff = file_offset;
|
||
ehdr.e_ehsize = sizeof(Elf32_Ehdr);
|
||
ehdr.e_shentsize = sizeof(Elf32_Shdr);
|
||
ehdr.e_shnum = shnum;
|
||
ehdr.e_shstrndx = shnum - 1;
|
||
|
||
dummy_size_t = fwrite(&ehdr, 1, sizeof(Elf32_Ehdr), f);
|
||
dummy_size_t = fwrite(phdr, 1, phnum * sizeof(Elf32_Phdr), f);
|
||
offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
|
||
|
||
for(i=1;i<s1->nb_sections;i++) {
|
||
s = s1->sections[section_order[i]];
|
||
if (s->sh_type != SHT_NOBITS) {
|
||
while (offset < s->sh_offset) {
|
||
fputc(0, f);
|
||
offset++;
|
||
}
|
||
size = s->sh_size;
|
||
dummy_size_t = fwrite(s->data, 1, size, f);
|
||
offset += size;
|
||
}
|
||
}
|
||
|
||
/* output section headers */
|
||
while (offset < ehdr.e_shoff) {
|
||
fputc(0, f);
|
||
offset++;
|
||
}
|
||
|
||
for(i=0;i<s1->nb_sections;i++) {
|
||
sh = &shdr;
|
||
memset(sh, 0, sizeof(Elf32_Shdr));
|
||
s = s1->sections[i];
|
||
if (s) {
|
||
sh->sh_name = s->sh_name;
|
||
sh->sh_type = s->sh_type;
|
||
sh->sh_flags = s->sh_flags;
|
||
sh->sh_entsize = s->sh_entsize;
|
||
sh->sh_info = s->sh_info;
|
||
if (s->link)
|
||
sh->sh_link = s->link->sh_num;
|
||
sh->sh_addralign = s->sh_addralign;
|
||
sh->sh_addr = s->sh_addr;
|
||
sh->sh_offset = s->sh_offset;
|
||
sh->sh_size = s->sh_size;
|
||
}
|
||
dummy_size_t = fwrite(sh, 1, sizeof(Elf32_Shdr), f);
|
||
}
|
||
} else {
|
||
tcc_output_binary(s1, f, section_order);
|
||
}
|
||
fclose(f);
|
||
|
||
ret = 0;
|
||
the_end:
|
||
tcc_free(s1->symtab_to_dynsym);
|
||
tcc_free(section_order);
|
||
tcc_free(phdr);
|
||
tcc_free(s1->got_offsets);
|
||
return ret;
|
||
}
|
||
|
||
static void *load_data(int fd, unsigned long file_offset, unsigned long size)
|
||
{
|
||
void *data;
|
||
|
||
data = tcc_malloc(size);
|
||
lseek(fd, file_offset, SEEK_SET);
|
||
dummy_size_t = read(fd, data, size);
|
||
return data;
|
||
}
|
||
|
||
typedef struct SectionMergeInfo {
|
||
Section *s; /* corresponding existing section */
|
||
unsigned long offset; /* offset of the new section in the existing section */
|
||
uint8_t new_section; /* true if section 's' was added */
|
||
uint8_t link_once; /* true if link once section */
|
||
} SectionMergeInfo;
|
||
|
||
/* load an object file and merge it with current files */
|
||
/* XXX: handle correctly stab (debug) info */
|
||
static int tcc_load_object_file(TCCState *s1,
|
||
int fd, unsigned long file_offset)
|
||
{
|
||
Elf32_Ehdr ehdr;
|
||
Elf32_Shdr *shdr, *sh;
|
||
int size, i, j, offset, offseti, nb_syms, sym_index, ret;
|
||
unsigned char *strsec, *strtab;
|
||
int *old_to_new_syms;
|
||
char *sh_name, *name;
|
||
SectionMergeInfo *sm_table, *sm;
|
||
Elf32_Sym *sym, *symtab;
|
||
Elf32_Rel *rel, *rel_end;
|
||
Section *s;
|
||
|
||
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
|
||
goto fail1;
|
||
if (ehdr.e_ident[0] != ELFMAG0 ||
|
||
ehdr.e_ident[1] != ELFMAG1 ||
|
||
ehdr.e_ident[2] != ELFMAG2 ||
|
||
ehdr.e_ident[3] != ELFMAG3)
|
||
goto fail1;
|
||
/* test if object file */
|
||
if (ehdr.e_type != ET_REL)
|
||
goto fail1;
|
||
/* test CPU specific stuff */
|
||
if (ehdr.e_ident[5] != ELFDATA2LSB ||
|
||
ehdr.e_machine != EM_TCC_TARGET) {
|
||
fail1:
|
||
error_noabort("invalid object file");
|
||
return -1;
|
||
}
|
||
/* read sections */
|
||
shdr = load_data(fd, file_offset + ehdr.e_shoff,
|
||
sizeof(Elf32_Shdr) * ehdr.e_shnum);
|
||
sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);
|
||
|
||
/* load section names */
|
||
sh = &shdr[ehdr.e_shstrndx];
|
||
strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
|
||
|
||
/* load symtab and strtab */
|
||
old_to_new_syms = NULL;
|
||
symtab = NULL;
|
||
strtab = NULL;
|
||
nb_syms = 0;
|
||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||
sh = &shdr[i];
|
||
if (sh->sh_type == SHT_SYMTAB) {
|
||
if (symtab) {
|
||
error_noabort("object must contain only one symtab");
|
||
fail:
|
||
ret = -1;
|
||
goto the_end;
|
||
}
|
||
nb_syms = sh->sh_size / sizeof(Elf32_Sym);
|
||
symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
|
||
sm_table[i].s = symtab_section;
|
||
|
||
/* now load strtab */
|
||
sh = &shdr[sh->sh_link];
|
||
strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
|
||
}
|
||
}
|
||
|
||
/* now examine each section and try to merge its content with the
|
||
ones in memory */
|
||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||
/* no need to examine section name strtab */
|
||
if (i == ehdr.e_shstrndx)
|
||
continue;
|
||
sh = &shdr[i];
|
||
sh_name = strsec + sh->sh_name;
|
||
/* ignore sections types we do not handle */
|
||
if (sh->sh_type != SHT_PROGBITS &&
|
||
sh->sh_type != SHT_REL &&
|
||
sh->sh_type != SHT_NOBITS)
|
||
continue;
|
||
if (sh->sh_addralign < 1)
|
||
sh->sh_addralign = 1;
|
||
/* find corresponding section, if any */
|
||
for(j = 1; j < s1->nb_sections;j++) {
|
||
s = s1->sections[j];
|
||
if (!strcmp(s->name, sh_name)) {
|
||
if (!strncmp(sh_name, ".gnu.linkonce",
|
||
sizeof(".gnu.linkonce") - 1)) {
|
||
/* if a 'linkonce' section is already present, we
|
||
do not add it again. It is a little tricky as
|
||
symbols can still be defined in
|
||
it. */
|
||
sm_table[i].link_once = 1;
|
||
goto next;
|
||
} else {
|
||
goto found;
|
||
}
|
||
}
|
||
}
|
||
/* not found: create new section */
|
||
s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags);
|
||
/* take as much info as possible from the section. sh_link and
|
||
sh_info will be updated later */
|
||
s->sh_addralign = sh->sh_addralign;
|
||
s->sh_entsize = sh->sh_entsize;
|
||
sm_table[i].new_section = 1;
|
||
found:
|
||
if (sh->sh_type != s->sh_type) {
|
||
error_noabort("invalid section type");
|
||
goto fail;
|
||
}
|
||
|
||
/* align start of section */
|
||
offset = s->data_offset;
|
||
size = sh->sh_addralign - 1;
|
||
offset = (offset + size) & ~size;
|
||
if (sh->sh_addralign > s->sh_addralign)
|
||
s->sh_addralign = sh->sh_addralign;
|
||
s->data_offset = offset;
|
||
sm_table[i].offset = offset;
|
||
sm_table[i].s = s;
|
||
/* concatenate sections */
|
||
size = sh->sh_size;
|
||
if (sh->sh_type != SHT_NOBITS) {
|
||
unsigned char *ptr;
|
||
lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
|
||
ptr = section_ptr_add(s, size);
|
||
dummy_size_t = read(fd, ptr, size);
|
||
} else {
|
||
s->data_offset += size;
|
||
}
|
||
next: ;
|
||
}
|
||
|
||
/* second short pass to update sh_link and sh_info fields of new
|
||
sections */
|
||
sm = sm_table;
|
||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||
s = sm_table[i].s;
|
||
if (!s || !sm_table[i].new_section)
|
||
continue;
|
||
sh = &shdr[i];
|
||
if (sh->sh_link > 0)
|
||
s->link = sm_table[sh->sh_link].s;
|
||
if (sh->sh_type == SHT_REL) {
|
||
s->sh_info = sm_table[sh->sh_info].s->sh_num;
|
||
/* update backward link */
|
||
s1->sections[s->sh_info]->reloc = s;
|
||
}
|
||
}
|
||
|
||
/* resolve symbols */
|
||
old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));
|
||
|
||
sym = symtab + 1;
|
||
for(i = 1; i < nb_syms; i++, sym++) {
|
||
if (sym->st_shndx != SHN_UNDEF &&
|
||
sym->st_shndx < SHN_LORESERVE) {
|
||
sm = &sm_table[sym->st_shndx];
|
||
if (sm->link_once) {
|
||
/* if a symbol is in a link once section, we use the
|
||
already defined symbol. It is very important to get
|
||
correct relocations */
|
||
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
|
||
name = strtab + sym->st_name;
|
||
sym_index = find_elf_sym(symtab_section, name);
|
||
if (sym_index)
|
||
old_to_new_syms[i] = sym_index;
|
||
}
|
||
continue;
|
||
}
|
||
/* if no corresponding section added, no need to add symbol */
|
||
if (!sm->s)
|
||
continue;
|
||
/* convert section number */
|
||
sym->st_shndx = sm->s->sh_num;
|
||
/* offset value */
|
||
sym->st_value += sm->offset;
|
||
}
|
||
/* add symbol */
|
||
name = strtab + sym->st_name;
|
||
sym_index = add_elf_sym(symtab_section, sym->st_value, sym->st_size,
|
||
sym->st_info, sym->st_other,
|
||
sym->st_shndx, name);
|
||
old_to_new_syms[i] = sym_index;
|
||
}
|
||
|
||
/* third pass to patch relocation entries */
|
||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||
s = sm_table[i].s;
|
||
if (!s)
|
||
continue;
|
||
sh = &shdr[i];
|
||
offset = sm_table[i].offset;
|
||
switch(s->sh_type) {
|
||
case SHT_REL:
|
||
/* take relocation offset information */
|
||
offseti = sm_table[sh->sh_info].offset;
|
||
rel_end = (Elf32_Rel *)(s->data + s->data_offset);
|
||
for(rel = (Elf32_Rel *)(s->data + offset);
|
||
rel < rel_end;
|
||
rel++) {
|
||
int type;
|
||
unsigned sym_index;
|
||
/* convert symbol index */
|
||
type = ELF32_R_TYPE(rel->r_info);
|
||
sym_index = ELF32_R_SYM(rel->r_info);
|
||
/* NOTE: only one symtab assumed */
|
||
if (sym_index >= nb_syms)
|
||
goto invalid_reloc;
|
||
sym_index = old_to_new_syms[sym_index];
|
||
if (!sym_index) {
|
||
invalid_reloc:
|
||
error_noabort("Invalid relocation entry");
|
||
goto fail;
|
||
}
|
||
rel->r_info = ELF32_R_INFO(sym_index, type);
|
||
/* offset the relocation offset */
|
||
rel->r_offset += offseti;
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
ret = 0;
|
||
the_end:
|
||
tcc_free(symtab);
|
||
tcc_free(strtab);
|
||
tcc_free(old_to_new_syms);
|
||
tcc_free(sm_table);
|
||
tcc_free(strsec);
|
||
tcc_free(shdr);
|
||
return ret;
|
||
}
|
||
|
||
#define ARMAG "!<arch>\012" /* For COFF and a.out archives */
|
||
|
||
typedef struct ArchiveHeader {
|
||
char ar_name[16]; /* name of this member */
|
||
char ar_date[12]; /* file mtime */
|
||
char ar_uid[6]; /* owner uid; printed as decimal */
|
||
char ar_gid[6]; /* owner gid; printed as decimal */
|
||
char ar_mode[8]; /* file mode, printed as octal */
|
||
char ar_size[10]; /* file size, printed as decimal */
|
||
char ar_fmag[2]; /* should contain ARFMAG */
|
||
} ArchiveHeader;
|
||
|
||
static int get_be32(const uint8_t *b)
|
||
{
|
||
return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
|
||
}
|
||
|
||
/* load only the objects which resolve undefined symbols */
|
||
static int tcc_load_alacarte(TCCState *s1, int fd, int size)
|
||
{
|
||
int i, bound, nsyms, sym_index, off, ret;
|
||
uint8_t *data;
|
||
const char *ar_names, *p;
|
||
const uint8_t *ar_index;
|
||
Elf32_Sym *sym;
|
||
|
||
data = tcc_malloc(size);
|
||
if (read(fd, data, size) != size)
|
||
goto fail;
|
||
nsyms = get_be32(data);
|
||
ar_index = data + 4;
|
||
ar_names = ar_index + nsyms * 4;
|
||
|
||
do {
|
||
bound = 0;
|
||
for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
|
||
sym_index = find_elf_sym(symtab_section, p);
|
||
if(sym_index) {
|
||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||
if(sym->st_shndx == SHN_UNDEF) {
|
||
off = get_be32(ar_index + i * 4) + sizeof(ArchiveHeader);
|
||
#if 0
|
||
printf("%5d\t%s\t%08x\n", i, p, sym->st_shndx);
|
||
#endif
|
||
++bound;
|
||
lseek(fd, off, SEEK_SET);
|
||
if(tcc_load_object_file(s1, fd, off) < 0) {
|
||
fail:
|
||
ret = -1;
|
||
goto the_end;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} while(bound);
|
||
ret = 0;
|
||
the_end:
|
||
tcc_free(data);
|
||
return ret;
|
||
}
|
||
|
||
/* load a '.a' file */
|
||
static int tcc_load_archive(TCCState *s1, int fd)
|
||
{
|
||
ArchiveHeader hdr;
|
||
char ar_size[11];
|
||
char ar_name[17];
|
||
char magic[8];
|
||
int size, len, i;
|
||
unsigned long file_offset;
|
||
|
||
/* skip magic which was already checked */
|
||
dummy_size_t = read(fd, magic, sizeof(magic));
|
||
|
||
for(;;) {
|
||
len = read(fd, &hdr, sizeof(hdr));
|
||
if (len == 0)
|
||
break;
|
||
if (len != sizeof(hdr)) {
|
||
error_noabort("invalid archive");
|
||
return -1;
|
||
}
|
||
memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
|
||
ar_size[sizeof(hdr.ar_size)] = '\0';
|
||
size = strtol(ar_size, NULL, 0);
|
||
memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
|
||
for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
|
||
if (ar_name[i] != ' ')
|
||
break;
|
||
}
|
||
ar_name[i + 1] = '\0';
|
||
// printf("name='%s' size=%d %s\n", ar_name, size, ar_size);
|
||
file_offset = lseek(fd, 0, SEEK_CUR);
|
||
/* align to even */
|
||
size = (size + 1) & ~1;
|
||
if (!strcmp(ar_name, "/")) {
|
||
/* coff symbol table : we handle it */
|
||
if(s1->alacarte_link)
|
||
return tcc_load_alacarte(s1, fd, size);
|
||
} else if (!strcmp(ar_name, "//") ||
|
||
!strcmp(ar_name, "__.SYMDEF") ||
|
||
!strcmp(ar_name, "__.SYMDEF/") ||
|
||
!strcmp(ar_name, "ARFILENAMES/")) {
|
||
/* skip symbol table or archive names */
|
||
} else {
|
||
if (tcc_load_object_file(s1, fd, file_offset) < 0)
|
||
return -1;
|
||
}
|
||
lseek(fd, file_offset + size, SEEK_SET);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
|
||
is referenced by the user (so it should be added as DT_NEEDED in
|
||
the generated ELF file) */
|
||
static int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
|
||
{
|
||
Elf32_Ehdr ehdr;
|
||
Elf32_Shdr *shdr, *sh, *sh1;
|
||
int i, nb_syms, nb_dts, sym_bind, ret;
|
||
Elf32_Sym *sym, *dynsym;
|
||
Elf32_Dyn *dt, *dynamic;
|
||
unsigned char *dynstr;
|
||
const char *name, *soname, *p;
|
||
DLLReference *dllref;
|
||
|
||
dummy_size_t = read(fd, &ehdr, sizeof(ehdr));
|
||
|
||
/* test CPU specific stuff */
|
||
if (ehdr.e_ident[5] != ELFDATA2LSB ||
|
||
ehdr.e_machine != EM_TCC_TARGET) {
|
||
error_noabort("bad architecture");
|
||
return -1;
|
||
}
|
||
|
||
/* read sections */
|
||
shdr = load_data(fd, ehdr.e_shoff, sizeof(Elf32_Shdr) * ehdr.e_shnum);
|
||
|
||
/* load dynamic section and dynamic symbols */
|
||
nb_syms = 0;
|
||
nb_dts = 0;
|
||
dynamic = NULL;
|
||
dynsym = NULL; /* avoid warning */
|
||
dynstr = NULL; /* avoid warning */
|
||
for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
|
||
switch(sh->sh_type) {
|
||
case SHT_DYNAMIC:
|
||
nb_dts = sh->sh_size / sizeof(Elf32_Dyn);
|
||
dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
|
||
break;
|
||
case SHT_DYNSYM:
|
||
nb_syms = sh->sh_size / sizeof(Elf32_Sym);
|
||
dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
|
||
sh1 = &shdr[sh->sh_link];
|
||
dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* compute the real library name */
|
||
soname = filename;
|
||
p = strrchr(soname, '/');
|
||
if (p)
|
||
soname = p + 1;
|
||
|
||
for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
|
||
if (dt->d_tag == DT_SONAME) {
|
||
soname = dynstr + dt->d_un.d_val;
|
||
}
|
||
}
|
||
|
||
/* if the dll is already loaded, do not load it */
|
||
for(i = 0; i < s1->nb_loaded_dlls; i++) {
|
||
dllref = s1->loaded_dlls[i];
|
||
if (!strcmp(soname, dllref->name)) {
|
||
/* but update level if needed */
|
||
if (level < dllref->level)
|
||
dllref->level = level;
|
||
ret = 0;
|
||
goto the_end;
|
||
}
|
||
}
|
||
|
||
// printf("loading dll '%s'\n", soname);
|
||
|
||
/* add the dll and its level */
|
||
dllref = tcc_malloc(sizeof(DLLReference) + strlen(soname));
|
||
dllref->level = level;
|
||
strcpy(dllref->name, soname);
|
||
dynarray_add((void ***)&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
|
||
|
||
/* add dynamic symbols in dynsym_section */
|
||
for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
|
||
sym_bind = ELF32_ST_BIND(sym->st_info);
|
||
if (sym_bind == STB_LOCAL)
|
||
continue;
|
||
name = dynstr + sym->st_name;
|
||
add_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
|
||
sym->st_info, sym->st_other, sym->st_shndx, name);
|
||
}
|
||
|
||
/* load all referenced DLLs */
|
||
for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
|
||
switch(dt->d_tag) {
|
||
case DT_NEEDED:
|
||
name = dynstr + dt->d_un.d_val;
|
||
for(i = 0; i < s1->nb_loaded_dlls; i++) {
|
||
dllref = s1->loaded_dlls[i];
|
||
if (!strcmp(name, dllref->name))
|
||
goto already_loaded;
|
||
}
|
||
if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
|
||
error_noabort("referenced dll '%s' not found", name);
|
||
ret = -1;
|
||
goto the_end;
|
||
}
|
||
already_loaded:
|
||
break;
|
||
}
|
||
}
|
||
ret = 0;
|
||
the_end:
|
||
tcc_free(dynstr);
|
||
tcc_free(dynsym);
|
||
tcc_free(dynamic);
|
||
tcc_free(shdr);
|
||
return ret;
|
||
}
|
||
|
||
#define LD_TOK_NAME 256
|
||
#define LD_TOK_EOF (-1)
|
||
|
||
/* return next ld script token */
|
||
static int ld_next(TCCState *s1, char *name, int name_size)
|
||
{
|
||
int c;
|
||
char *q;
|
||
|
||
redo:
|
||
switch(ch) {
|
||
case ' ':
|
||
case '\t':
|
||
case '\f':
|
||
case '\v':
|
||
case '\r':
|
||
case '\n':
|
||
inp();
|
||
goto redo;
|
||
case '/':
|
||
minp();
|
||
if (ch == '*') {
|
||
file->buf_ptr = parse_comment(file->buf_ptr);
|
||
ch = file->buf_ptr[0];
|
||
goto redo;
|
||
} else {
|
||
q = name;
|
||
*q++ = '/';
|
||
goto parse_name;
|
||
}
|
||
break;
|
||
case 'a' ... 'z':
|
||
case 'A' ... 'Z':
|
||
case '_':
|
||
case '\\':
|
||
case '.':
|
||
case '$':
|
||
case '~':
|
||
q = name;
|
||
parse_name:
|
||
for(;;) {
|
||
if (!((ch >= 'a' && ch <= 'z') ||
|
||
(ch >= 'A' && ch <= 'Z') ||
|
||
(ch >= '0' && ch <= '9') ||
|
||
strchr("/.-_+=$:\\,~", ch)))
|
||
break;
|
||
if ((q - name) < name_size - 1) {
|
||
*q++ = ch;
|
||
}
|
||
minp();
|
||
}
|
||
*q = '\0';
|
||
c = LD_TOK_NAME;
|
||
break;
|
||
case CH_EOF:
|
||
c = LD_TOK_EOF;
|
||
break;
|
||
default:
|
||
c = ch;
|
||
inp();
|
||
break;
|
||
}
|
||
#if 0
|
||
printf("tok=%c %d\n", c, c);
|
||
if (c == LD_TOK_NAME)
|
||
printf(" name=%s\n", name);
|
||
#endif
|
||
return c;
|
||
}
|
||
|
||
/* interpret a subset of GNU ldscripts to handle the dummy libc.so
|
||
files */
|
||
static int tcc_load_ldscript(TCCState *s1)
|
||
{
|
||
char cmd[64];
|
||
char filename[1024];
|
||
int t;
|
||
|
||
ch = file->buf_ptr[0];
|
||
ch = handle_eob();
|
||
for(;;) {
|
||
t = ld_next(s1, cmd, sizeof(cmd));
|
||
if (t == LD_TOK_EOF)
|
||
return 0;
|
||
else if (t != LD_TOK_NAME)
|
||
return -1;
|
||
if (!strcmp(cmd, "INPUT") ||
|
||
!strcmp(cmd, "GROUP")) {
|
||
t = ld_next(s1, cmd, sizeof(cmd));
|
||
if (t != '(')
|
||
expect("(");
|
||
t = ld_next(s1, filename, sizeof(filename));
|
||
for(;;) {
|
||
if (t == LD_TOK_EOF) {
|
||
error_noabort("unexpected end of file");
|
||
return -1;
|
||
} else if (t == ')') {
|
||
break;
|
||
} else if (t != LD_TOK_NAME) {
|
||
error_noabort("filename expected");
|
||
return -1;
|
||
}
|
||
tcc_add_file(s1, filename);
|
||
t = ld_next(s1, filename, sizeof(filename));
|
||
if (t == ',') {
|
||
t = ld_next(s1, filename, sizeof(filename));
|
||
}
|
||
}
|
||
} else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
|
||
!strcmp(cmd, "TARGET")) {
|
||
/* ignore some commands */
|
||
t = ld_next(s1, cmd, sizeof(cmd));
|
||
if (t != '(')
|
||
expect("(");
|
||
for(;;) {
|
||
t = ld_next(s1, filename, sizeof(filename));
|
||
if (t == LD_TOK_EOF) {
|
||
error_noabort("unexpected end of file");
|
||
return -1;
|
||
} else if (t == ')') {
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
return -1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
//---------------------------------------------------------------------------
|
||
|
||
#ifdef TCC_TARGET_COFF
|
||
#include "tcccoff.c"
|
||
#endif
|
||
|
||
#ifdef TCC_TARGET_PE
|
||
#include "tccpe.c"
|
||
#endif
|
||
|
||
/* print the position in the source file of PC value 'pc' by reading
|
||
the stabs debug information */
|
||
static void rt_printline(unsigned long wanted_pc)
|
||
{
|
||
Stab_Sym *sym, *sym_end;
|
||
char func_name[128], last_func_name[128];
|
||
unsigned long func_addr, last_pc, pc;
|
||
const char *incl_files[INCLUDE_STACK_SIZE];
|
||
int incl_index, len, last_line_num, i;
|
||
const char *str, *p;
|
||
|
||
fprintf(stderr, "0x%08lx:", wanted_pc);
|
||
|
||
func_name[0] = '\0';
|
||
func_addr = 0;
|
||
incl_index = 0;
|
||
last_func_name[0] = '\0';
|
||
last_pc = 0xffffffff;
|
||
last_line_num = 1;
|
||
sym = (Stab_Sym *)stab_section->data + 1;
|
||
sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
|
||
while (sym < sym_end) {
|
||
switch(sym->n_type) {
|
||
/* function start or end */
|
||
case N_FUN:
|
||
if (sym->n_strx == 0) {
|
||
/* we test if between last line and end of function */
|
||
pc = sym->n_value + func_addr;
|
||
if (wanted_pc >= last_pc && wanted_pc < pc)
|
||
goto found;
|
||
func_name[0] = '\0';
|
||
func_addr = 0;
|
||
} else {
|
||
str = stabstr_section->data + sym->n_strx;
|
||
p = strchr(str, ':');
|
||
if (!p) {
|
||
pstrcpy(func_name, sizeof(func_name), str);
|
||
} else {
|
||
len = p - str;
|
||
if (len > sizeof(func_name) - 1)
|
||
len = sizeof(func_name) - 1;
|
||
memcpy(func_name, str, len);
|
||
func_name[len] = '\0';
|
||
}
|
||
func_addr = sym->n_value;
|
||
}
|
||
break;
|
||
/* line number info */
|
||
case N_SLINE:
|
||
pc = sym->n_value + func_addr;
|
||
if (wanted_pc >= last_pc && wanted_pc < pc)
|
||
goto found;
|
||
last_pc = pc;
|
||
last_line_num = sym->n_desc;
|
||
/* XXX: slow! */
|
||
strcpy(last_func_name, func_name);
|
||
break;
|
||
/* include files */
|
||
case N_BINCL:
|
||
str = stabstr_section->data + sym->n_strx;
|
||
add_incl:
|
||
if (incl_index < INCLUDE_STACK_SIZE) {
|
||
incl_files[incl_index++] = str;
|
||
}
|
||
break;
|
||
case N_EINCL:
|
||
if (incl_index > 1)
|
||
incl_index--;
|
||
break;
|
||
case N_SO:
|
||
if (sym->n_strx == 0) {
|
||
incl_index = 0; /* end of translation unit */
|
||
} else {
|
||
str = stabstr_section->data + sym->n_strx;
|
||
/* do not add path */
|
||
len = strlen(str);
|
||
if (len > 0 && str[len - 1] != '/')
|
||
goto add_incl;
|
||
}
|
||
break;
|
||
}
|
||
sym++;
|
||
}
|
||
|
||
/* second pass: we try symtab symbols (no line number info) */
|
||
incl_index = 0;
|
||
{
|
||
Elf32_Sym *sym, *sym_end;
|
||
int type;
|
||
|
||
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset);
|
||
for(sym = (Elf32_Sym *)symtab_section->data + 1;
|
||
sym < sym_end;
|
||
sym++) {
|
||
type = ELF32_ST_TYPE(sym->st_info);
|
||
if (type == STT_FUNC) {
|
||
if (wanted_pc >= sym->st_value &&
|
||
wanted_pc < sym->st_value + sym->st_size) {
|
||
pstrcpy(last_func_name, sizeof(last_func_name),
|
||
strtab_section->data + sym->st_name);
|
||
goto found;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/* did not find any info: */
|
||
fprintf(stderr, " ???\n");
|
||
return;
|
||
found:
|
||
if (last_func_name[0] != '\0') {
|
||
fprintf(stderr, " %s()", last_func_name);
|
||
}
|
||
if (incl_index > 0) {
|
||
fprintf(stderr, " (%s:%d",
|
||
incl_files[incl_index - 1], last_line_num);
|
||
for(i = incl_index - 2; i >= 0; i--)
|
||
fprintf(stderr, ", included from %s", incl_files[i]);
|
||
fprintf(stderr, ")");
|
||
}
|
||
fprintf(stderr, "\n");
|
||
}
|
||
|
||
#if !defined(WIN32) && !defined(CONFIG_TCCBOOT)
|
||
|
||
/* return the PC at frame level 'level'. Return non zero if not found */
|
||
static int rt_get_caller_pc(unsigned long *paddr,
|
||
ucontext_t *uc, int level)
|
||
{
|
||
unsigned long fp __attribute__((unused));
|
||
//int i;
|
||
|
||
if (level == 0) {
|
||
*paddr = 12345; //uc->uc_mcontext.gregs[REG_EIP];
|
||
return 0;
|
||
} else {
|
||
fp = 23456; //uc->uc_mcontext.gregs[REG_EBP];
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* emit a run time error at position 'pc' */
|
||
void rt_error(ucontext_t *uc, const char *fmt, ...)
|
||
{
|
||
va_list ap;
|
||
unsigned long pc = 0; // shut gcc up
|
||
int i;
|
||
|
||
va_start(ap, fmt);
|
||
fprintf(stderr, "Runtime error: ");
|
||
vfprintf(stderr, fmt, ap);
|
||
fprintf(stderr, "\n");
|
||
for(i=0;i<num_callers;i++) {
|
||
if (rt_get_caller_pc(&pc, uc, i) < 0)
|
||
break;
|
||
if (i == 0)
|
||
fprintf(stderr, "at ");
|
||
else
|
||
fprintf(stderr, "by ");
|
||
rt_printline(pc);
|
||
}
|
||
exit(255);
|
||
va_end(ap);
|
||
}
|
||
|
||
/* signal handler for fatal errors */
|
||
static void sig_error(int signum, siginfo_t *siginf, void *puc)
|
||
{
|
||
ucontext_t *uc = puc;
|
||
|
||
switch(signum) {
|
||
case SIGFPE:
|
||
switch(siginf->si_code) {
|
||
case FPE_INTDIV:
|
||
case FPE_FLTDIV:
|
||
rt_error(uc, "division by zero");
|
||
break;
|
||
default:
|
||
rt_error(uc, "floating point exception");
|
||
break;
|
||
}
|
||
break;
|
||
case SIGBUS:
|
||
case SIGSEGV:
|
||
if (rt_bound_error_msg && *rt_bound_error_msg)
|
||
rt_error(uc, *rt_bound_error_msg);
|
||
else
|
||
rt_error(uc, "dereferencing invalid pointer");
|
||
break;
|
||
case SIGILL:
|
||
rt_error(uc, "illegal instruction");
|
||
break;
|
||
case SIGABRT:
|
||
rt_error(uc, "abort() called");
|
||
break;
|
||
default:
|
||
rt_error(uc, "caught signal %d", signum);
|
||
break;
|
||
}
|
||
exit(255);
|
||
}
|
||
#endif
|
||
|
||
/* do all relocations (needed before using tcc_get_symbol()) */
|
||
int tcc_relocate(TCCState *s1)
|
||
{
|
||
Section *s;
|
||
int i;
|
||
|
||
s1->nb_errors = 0;
|
||
|
||
#ifdef TCC_TARGET_PE
|
||
pe_add_runtime(s1);
|
||
#else
|
||
tcc_add_runtime(s1);
|
||
#endif
|
||
|
||
relocate_common_syms();
|
||
|
||
tcc_add_linker_symbols(s1);
|
||
|
||
build_got_entries(s1);
|
||
|
||
/* compute relocation address : section are relocated in place. We
|
||
also alloc the bss space */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if (s->sh_flags & SHF_ALLOC) {
|
||
if (s->sh_type == SHT_NOBITS)
|
||
s->data = tcc_mallocz(s->data_offset);
|
||
s->sh_addr = (unsigned long)s->data;
|
||
}
|
||
}
|
||
|
||
relocate_syms(s1, 1);
|
||
|
||
if (s1->nb_errors != 0)
|
||
return -1;
|
||
|
||
/* relocate each section */
|
||
for(i = 1; i < s1->nb_sections; i++) {
|
||
s = s1->sections[i];
|
||
if (s->reloc)
|
||
relocate_section(s1, s);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* launch the compiled program with the given arguments */
|
||
int tcc_run(TCCState *s1, int argc, char **argv)
|
||
{
|
||
int (*prog_main)(int, char **);
|
||
|
||
if (tcc_relocate(s1) < 0)
|
||
return -1;
|
||
|
||
prog_main = tcc_get_symbol_err(s1, "main");
|
||
|
||
if (do_debug) {
|
||
#if defined(WIN32) || defined(CONFIG_TCCBOOT)
|
||
error("debug mode currently not available for Windows");
|
||
#else
|
||
struct sigaction sigact;
|
||
/* install TCC signal handlers to print debug info on fatal
|
||
runtime errors */
|
||
sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
|
||
sigact.sa_sigaction = sig_error;
|
||
sigemptyset(&sigact.sa_mask);
|
||
sigaction(SIGFPE, &sigact, NULL);
|
||
sigaction(SIGILL, &sigact, NULL);
|
||
sigaction(SIGSEGV, &sigact, NULL);
|
||
sigaction(SIGBUS, &sigact, NULL);
|
||
sigaction(SIGABRT, &sigact, NULL);
|
||
#endif
|
||
}
|
||
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
if (do_bounds_check) {
|
||
void (*bound_init)(void);
|
||
|
||
/* set error function */
|
||
rt_bound_error_msg = (void *)tcc_get_symbol_err(s1,
|
||
"__bound_error_msg");
|
||
|
||
/* XXX: use .init section so that it also work in binary ? */
|
||
bound_init = (void *)tcc_get_symbol_err(s1, "__bound_init");
|
||
bound_init();
|
||
}
|
||
#endif
|
||
return (*prog_main)(argc, argv);
|
||
}
|
||
|
||
TCCState *tcc_new(void)
|
||
{
|
||
const char *p, *r;
|
||
TCCState *s;
|
||
TokenSym *ts __attribute__((unused));
|
||
int i, c;
|
||
|
||
s = tcc_mallocz(sizeof(TCCState));
|
||
if (!s)
|
||
return NULL;
|
||
tcc_state = s;
|
||
s->output_type = TCC_OUTPUT_MEMORY;
|
||
|
||
/* init isid table */
|
||
for(i=0;i<256;i++)
|
||
isidnum_table[i] = isid(i) || isnum(i);
|
||
|
||
/* add all tokens */
|
||
table_ident = NULL;
|
||
memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
|
||
|
||
tok_ident = TOK_IDENT;
|
||
p = tcc_keywords;
|
||
while (*p) {
|
||
r = p;
|
||
for(;;) {
|
||
c = *r++;
|
||
if (c == '\0')
|
||
break;
|
||
}
|
||
ts = tok_alloc(p, r - p - 1);
|
||
p = r;
|
||
}
|
||
|
||
/* we add dummy defines for some special macros to speed up tests
|
||
and to have working defined() */
|
||
define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
|
||
define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
|
||
define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
|
||
define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
|
||
|
||
/* standard defines */
|
||
tcc_define_symbol(s, "__STDC__", NULL);
|
||
#if defined(TCC_TARGET_I386)
|
||
tcc_define_symbol(s, "__i386__", NULL);
|
||
#endif
|
||
#if defined(TCC_TARGET_ARM)
|
||
tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
|
||
tcc_define_symbol(s, "__arm_elf__", NULL);
|
||
tcc_define_symbol(s, "__arm_elf", NULL);
|
||
tcc_define_symbol(s, "arm_elf", NULL);
|
||
tcc_define_symbol(s, "__arm__", NULL);
|
||
tcc_define_symbol(s, "__arm", NULL);
|
||
tcc_define_symbol(s, "arm", NULL);
|
||
tcc_define_symbol(s, "__APCS_32__", NULL);
|
||
#endif
|
||
#if defined(linux)
|
||
tcc_define_symbol(s, "__linux__", NULL);
|
||
tcc_define_symbol(s, "linux", NULL);
|
||
#endif
|
||
/* tiny C specific defines */
|
||
tcc_define_symbol(s, "__TINYC__", NULL);
|
||
|
||
/* tiny C & gcc defines */
|
||
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int");
|
||
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int");
|
||
tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
|
||
|
||
/* default library paths */
|
||
#ifdef TCC_TARGET_PE
|
||
{
|
||
char buf[1024];
|
||
snprintf(buf, sizeof(buf), "%s/lib", tcc_lib_path);
|
||
tcc_add_library_path(s, buf);
|
||
}
|
||
#else
|
||
tcc_add_library_path(s, "/usr/local/lib");
|
||
tcc_add_library_path(s, "/usr/lib");
|
||
tcc_add_library_path(s, "/lib");
|
||
#endif
|
||
|
||
/* no section zero */
|
||
dynarray_add((void ***)&s->sections, &s->nb_sections, NULL);
|
||
|
||
/* create standard sections */
|
||
text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
|
||
data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
|
||
|
||
/* symbols are always generated for linking stage */
|
||
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
|
||
".strtab",
|
||
".hashtab", SHF_PRIVATE);
|
||
strtab_section = symtab_section->link;
|
||
|
||
/* private symbol table for dynamic symbols */
|
||
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
|
||
".dynstrtab",
|
||
".dynhashtab", SHF_PRIVATE);
|
||
s->alacarte_link = 1;
|
||
|
||
#ifdef CHAR_IS_UNSIGNED
|
||
s->char_is_unsigned = 1;
|
||
#endif
|
||
#if defined(TCC_TARGET_PE) && 0
|
||
/* XXX: currently the PE linker is not ready to support that */
|
||
s->leading_underscore = 1;
|
||
#endif
|
||
return s;
|
||
}
|
||
|
||
void tcc_delete(TCCState *s1)
|
||
{
|
||
int i, n;
|
||
|
||
/* free -D defines */
|
||
free_defines(NULL);
|
||
|
||
/* free tokens */
|
||
n = tok_ident - TOK_IDENT;
|
||
for(i = 0; i < n; i++)
|
||
tcc_free(table_ident[i]);
|
||
tcc_free(table_ident);
|
||
|
||
/* free all sections */
|
||
|
||
free_section(symtab_section->hash);
|
||
|
||
free_section(s1->dynsymtab_section->hash);
|
||
free_section(s1->dynsymtab_section->link);
|
||
free_section(s1->dynsymtab_section);
|
||
|
||
for(i = 1; i < s1->nb_sections; i++)
|
||
free_section(s1->sections[i]);
|
||
tcc_free(s1->sections);
|
||
|
||
/* free loaded dlls array */
|
||
for(i = 0; i < s1->nb_loaded_dlls; i++)
|
||
tcc_free(s1->loaded_dlls[i]);
|
||
tcc_free(s1->loaded_dlls);
|
||
|
||
/* library paths */
|
||
for(i = 0; i < s1->nb_library_paths; i++)
|
||
tcc_free(s1->library_paths[i]);
|
||
tcc_free(s1->library_paths);
|
||
|
||
/* cached includes */
|
||
for(i = 0; i < s1->nb_cached_includes; i++)
|
||
tcc_free(s1->cached_includes[i]);
|
||
tcc_free(s1->cached_includes);
|
||
|
||
for(i = 0; i < s1->nb_include_paths; i++)
|
||
tcc_free(s1->include_paths[i]);
|
||
tcc_free(s1->include_paths);
|
||
|
||
for(i = 0; i < s1->nb_sysinclude_paths; i++)
|
||
tcc_free(s1->sysinclude_paths[i]);
|
||
tcc_free(s1->sysinclude_paths);
|
||
|
||
tcc_free(s1);
|
||
}
|
||
|
||
int tcc_add_include_path(TCCState *s1, const char *pathname)
|
||
{
|
||
char *pathname1;
|
||
|
||
pathname1 = tcc_strdup(pathname);
|
||
dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1);
|
||
return 0;
|
||
}
|
||
|
||
int tcc_add_sysinclude_path(TCCState *s1, const char *pathname)
|
||
{
|
||
char *pathname1;
|
||
|
||
pathname1 = tcc_strdup(pathname);
|
||
dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1);
|
||
return 0;
|
||
}
|
||
|
||
static int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
||
{
|
||
const char *ext, *filename1;
|
||
Elf32_Ehdr ehdr;
|
||
int fd, ret;
|
||
BufferedFile *saved_file;
|
||
|
||
/* find source file type with extension */
|
||
filename1 = strrchr(filename, '/');
|
||
if (filename1)
|
||
filename1++;
|
||
else
|
||
filename1 = filename;
|
||
ext = strrchr(filename1, '.');
|
||
if (ext)
|
||
ext++;
|
||
|
||
/* open the file */
|
||
saved_file = file;
|
||
file = tcc_open(s1, filename);
|
||
if (!file) {
|
||
if (flags & AFF_PRINT_ERROR) {
|
||
error_noabort("file '%s' not found", filename);
|
||
}
|
||
ret = -1;
|
||
goto fail1;
|
||
}
|
||
|
||
if (!ext || !strcmp(ext, "c")) {
|
||
/* C file assumed */
|
||
ret = tcc_compile(s1);
|
||
} else
|
||
#ifdef CONFIG_TCC_ASM
|
||
if (!strcmp(ext, "S")) {
|
||
/* preprocessed assembler */
|
||
ret = tcc_assemble(s1, 1);
|
||
} else if (!strcmp(ext, "s")) {
|
||
/* non preprocessed assembler */
|
||
ret = tcc_assemble(s1, 0);
|
||
} else
|
||
#endif
|
||
#ifdef TCC_TARGET_PE
|
||
if (!strcmp(ext, "def")) {
|
||
ret = pe_load_def_file(s1, fdopen(file->fd, "rb"));
|
||
} else
|
||
#endif
|
||
{
|
||
fd = file->fd;
|
||
/* assume executable format: auto guess file type */
|
||
ret = read(fd, &ehdr, sizeof(ehdr));
|
||
lseek(fd, 0, SEEK_SET);
|
||
if (ret <= 0) {
|
||
error_noabort("could not read header");
|
||
goto fail;
|
||
} else if (ret != sizeof(ehdr)) {
|
||
goto try_load_script;
|
||
}
|
||
|
||
if (ehdr.e_ident[0] == ELFMAG0 &&
|
||
ehdr.e_ident[1] == ELFMAG1 &&
|
||
ehdr.e_ident[2] == ELFMAG2 &&
|
||
ehdr.e_ident[3] == ELFMAG3) {
|
||
file->line_num = 0; /* do not display line number if error */
|
||
if (ehdr.e_type == ET_REL) {
|
||
ret = tcc_load_object_file(s1, fd, 0);
|
||
} else if (ehdr.e_type == ET_DYN) {
|
||
if (s1->output_type == TCC_OUTPUT_MEMORY) {
|
||
#ifdef TCC_TARGET_PE
|
||
ret = -1;
|
||
#else
|
||
void *h;
|
||
assert(0);
|
||
h = 0;
|
||
//h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY);
|
||
// jrs: remove need for dlopen
|
||
if (h)
|
||
ret = 0;
|
||
else
|
||
ret = -1;
|
||
#endif
|
||
} else {
|
||
ret = tcc_load_dll(s1, fd, filename,
|
||
(flags & AFF_REFERENCED_DLL) != 0);
|
||
}
|
||
} else {
|
||
error_noabort("unrecognized ELF file");
|
||
goto fail;
|
||
}
|
||
} else if (memcmp((char *)&ehdr, ARMAG, 8) == 0) {
|
||
file->line_num = 0; /* do not display line number if error */
|
||
ret = tcc_load_archive(s1, fd);
|
||
} else
|
||
#ifdef TCC_TARGET_COFF
|
||
if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) {
|
||
ret = tcc_load_coff(s1, fd);
|
||
} else
|
||
#endif
|
||
{
|
||
/* as GNU ld, consider it is an ld script if not recognized */
|
||
try_load_script:
|
||
ret = tcc_load_ldscript(s1);
|
||
if (ret < 0) {
|
||
error_noabort("unrecognized file type");
|
||
goto fail;
|
||
}
|
||
}
|
||
}
|
||
the_end:
|
||
tcc_close(file);
|
||
fail1:
|
||
file = saved_file;
|
||
return ret;
|
||
fail:
|
||
ret = -1;
|
||
goto the_end;
|
||
}
|
||
|
||
int tcc_add_file(TCCState *s, const char *filename)
|
||
{
|
||
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR);
|
||
}
|
||
|
||
int tcc_add_library_path(TCCState *s, const char *pathname)
|
||
{
|
||
char *pathname1;
|
||
|
||
pathname1 = tcc_strdup(pathname);
|
||
dynarray_add((void ***)&s->library_paths, &s->nb_library_paths, pathname1);
|
||
return 0;
|
||
}
|
||
|
||
/* find and load a dll. Return non zero if not found */
|
||
/* XXX: add '-rpath' option support ? */
|
||
static int tcc_add_dll(TCCState *s, const char *filename, int flags)
|
||
{
|
||
char buf[1024];
|
||
int i;
|
||
|
||
for(i = 0; i < s->nb_library_paths; i++) {
|
||
snprintf(buf, sizeof(buf), "%s/%s",
|
||
s->library_paths[i], filename);
|
||
if (tcc_add_file_internal(s, buf, flags) == 0)
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/* the library name is the same as the argument of the '-l' option */
|
||
int tcc_add_library(TCCState *s, const char *libraryname)
|
||
{
|
||
char buf[1024];
|
||
int i;
|
||
|
||
/* first we look for the dynamic library if not static linking */
|
||
if (!s->static_link) {
|
||
#ifdef TCC_TARGET_PE
|
||
snprintf(buf, sizeof(buf), "%s.def", libraryname);
|
||
#else
|
||
snprintf(buf, sizeof(buf), "lib%s.so", libraryname);
|
||
#endif
|
||
if (tcc_add_dll(s, buf, 0) == 0)
|
||
return 0;
|
||
}
|
||
|
||
/* then we look for the static library */
|
||
for(i = 0; i < s->nb_library_paths; i++) {
|
||
snprintf(buf, sizeof(buf), "%s/lib%s.a",
|
||
s->library_paths[i], libraryname);
|
||
if (tcc_add_file_internal(s, buf, 0) == 0)
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
int tcc_add_symbol(TCCState *s, const char *name, unsigned long val)
|
||
{
|
||
add_elf_sym(symtab_section, val, 0,
|
||
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
|
||
SHN_ABS, name);
|
||
return 0;
|
||
}
|
||
|
||
int tcc_set_output_type(TCCState *s, int output_type)
|
||
{
|
||
s->output_type = output_type;
|
||
|
||
if (!s->nostdinc) {
|
||
char buf[1024];
|
||
|
||
/* default include paths */
|
||
/* XXX: reverse order needed if -isystem support */
|
||
#ifndef TCC_TARGET_PE
|
||
tcc_add_sysinclude_path(s, "/usr/local/include");
|
||
tcc_add_sysinclude_path(s, "/usr/include");
|
||
#endif
|
||
snprintf(buf, sizeof(buf), "%s/include", tcc_lib_path);
|
||
tcc_add_sysinclude_path(s, buf);
|
||
#ifdef TCC_TARGET_PE
|
||
snprintf(buf, sizeof(buf), "%s/include/winapi", tcc_lib_path);
|
||
tcc_add_sysinclude_path(s, buf);
|
||
#endif
|
||
}
|
||
|
||
/* if bound checking, then add corresponding sections */
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
if (do_bounds_check) {
|
||
/* define symbol */
|
||
tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
|
||
/* create bounds sections */
|
||
bounds_section = new_section(s, ".bounds",
|
||
SHT_PROGBITS, SHF_ALLOC);
|
||
lbounds_section = new_section(s, ".lbounds",
|
||
SHT_PROGBITS, SHF_ALLOC);
|
||
}
|
||
#endif
|
||
|
||
if (s->char_is_unsigned) {
|
||
tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
|
||
}
|
||
|
||
/* add debug sections */
|
||
if (do_debug) {
|
||
/* stab symbols */
|
||
stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
|
||
stab_section->sh_entsize = sizeof(Stab_Sym);
|
||
stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
|
||
put_elf_str(stabstr_section, "");
|
||
stab_section->link = stabstr_section;
|
||
/* put first entry */
|
||
put_stabs("", 0, 0, 0, 0);
|
||
}
|
||
|
||
/* add libc crt1/crti objects */
|
||
#ifndef TCC_TARGET_PE
|
||
if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
|
||
!s->nostdlib) {
|
||
if (output_type != TCC_OUTPUT_DLL)
|
||
tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crt1.o");
|
||
tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crti.o");
|
||
}
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
#define WD_ALL 0x0001 /* warning is activated when using -Wall */
|
||
#define FD_INVERT 0x0002 /* invert value before storing */
|
||
|
||
typedef struct FlagDef {
|
||
uint16_t offset;
|
||
uint16_t flags;
|
||
const char *name;
|
||
} FlagDef;
|
||
|
||
static const FlagDef warning_defs[] = {
|
||
{ offsetof(TCCState, warn_unsupported), 0, "unsupported" },
|
||
{ offsetof(TCCState, warn_write_strings), 0, "write-strings" },
|
||
{ offsetof(TCCState, warn_error), 0, "error" },
|
||
{ offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
|
||
"implicit-function-declaration" },
|
||
};
|
||
|
||
static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
|
||
const char *name, int value)
|
||
{
|
||
int i;
|
||
const FlagDef *p;
|
||
const char *r;
|
||
|
||
r = name;
|
||
if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') {
|
||
r += 3;
|
||
value = !value;
|
||
}
|
||
for(i = 0, p = flags; i < nb_flags; i++, p++) {
|
||
if (!strcmp(r, p->name))
|
||
goto found;
|
||
}
|
||
return -1;
|
||
found:
|
||
if (p->flags & FD_INVERT)
|
||
value = !value;
|
||
*(int *)((uint8_t *)s + p->offset) = value;
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* set/reset a warning */
|
||
int tcc_set_warning(TCCState *s, const char *warning_name, int value)
|
||
{
|
||
int i;
|
||
const FlagDef *p;
|
||
|
||
if (!strcmp(warning_name, "all")) {
|
||
for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) {
|
||
if (p->flags & WD_ALL)
|
||
*(int *)((uint8_t *)s + p->offset) = 1;
|
||
}
|
||
return 0;
|
||
} else {
|
||
return set_flag(s, warning_defs, countof(warning_defs),
|
||
warning_name, value);
|
||
}
|
||
}
|
||
|
||
static const FlagDef flag_defs[] = {
|
||
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
|
||
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
|
||
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
|
||
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
|
||
};
|
||
|
||
/* set/reset a flag */
|
||
int tcc_set_flag(TCCState *s, const char *flag_name, int value)
|
||
{
|
||
return set_flag(s, flag_defs, countof(flag_defs),
|
||
flag_name, value);
|
||
}
|
||
|
||
#if !defined(LIBTCC)
|
||
|
||
/* extract the basename of a file */
|
||
static const char *tcc_basename(const char *name)
|
||
{
|
||
const char *p;
|
||
p = strrchr(name, '/');
|
||
#ifdef WIN32
|
||
if (!p)
|
||
p = strrchr(name, '\\');
|
||
#endif
|
||
if (!p)
|
||
p = name;
|
||
else
|
||
p++;
|
||
return p;
|
||
}
|
||
|
||
static int64_t getclock_us(void)
|
||
{
|
||
#ifdef WIN32
|
||
struct _timeb tb;
|
||
_ftime(&tb);
|
||
return (tb.time * 1000LL + tb.millitm) * 1000LL;
|
||
#else
|
||
struct timeval tv;
|
||
gettimeofday(&tv, NULL);
|
||
return tv.tv_sec * 1000000LL + tv.tv_usec;
|
||
#endif
|
||
}
|
||
|
||
void help(void)
|
||
{
|
||
printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001-2015 Fabrice Bellard\n"
|
||
"usage: tcc [-v] [-c] [-o outfile] [-Bdir] [-bench] [-Idir] [-Dsym[=val]] [-Usym]\n"
|
||
" [-Wwarn] [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-static]\n"
|
||
" [infile1 infile2...] [-run infile args...]\n"
|
||
"\n"
|
||
"General options:\n"
|
||
" -v display current version\n"
|
||
" -c compile only - generate an object file\n"
|
||
" -o outfile set output filename\n"
|
||
" -Bdir set tcc internal library path\n"
|
||
" -bench output compilation statistics\n"
|
||
" -run run compiled source\n"
|
||
" -fflag set or reset (with 'no-' prefix) 'flag' (see man page)\n"
|
||
" -Wwarning set or reset (with 'no-' prefix) 'warning' (see man page)\n"
|
||
" -w disable all warnings\n"
|
||
"Preprocessor options:\n"
|
||
" -Idir add include path 'dir'\n"
|
||
" -Dsym[=val] define 'sym' with value 'val'\n"
|
||
" -Usym undefine 'sym'\n"
|
||
"Linker options:\n"
|
||
" -Ldir add library path 'dir'\n"
|
||
" -llib link with dynamic or static library 'lib'\n"
|
||
" -shared generate a shared library\n"
|
||
" -static static linking\n"
|
||
" -rdynamic export all global symbols to dynamic linker\n"
|
||
" -r relocatable output\n"
|
||
"Debugger options:\n"
|
||
" -g generate runtime debug info\n"
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
" -b compile with built-in memory and bounds checker (implies -g)\n"
|
||
#endif
|
||
" -bt N show N callers in stack traces\n"
|
||
);
|
||
}
|
||
|
||
#define TCC_OPTION_HAS_ARG 0x0001
|
||
#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */
|
||
|
||
typedef struct TCCOption {
|
||
const char *name;
|
||
uint16_t index;
|
||
uint16_t flags;
|
||
} TCCOption;
|
||
|
||
enum {
|
||
TCC_OPTION_HELP,
|
||
TCC_OPTION_I,
|
||
TCC_OPTION_D,
|
||
TCC_OPTION_U,
|
||
TCC_OPTION_L,
|
||
TCC_OPTION_B,
|
||
TCC_OPTION_l,
|
||
TCC_OPTION_bench,
|
||
TCC_OPTION_bt,
|
||
TCC_OPTION_b,
|
||
TCC_OPTION_g,
|
||
TCC_OPTION_c,
|
||
TCC_OPTION_static,
|
||
TCC_OPTION_shared,
|
||
TCC_OPTION_o,
|
||
TCC_OPTION_r,
|
||
TCC_OPTION_Wl,
|
||
TCC_OPTION_W,
|
||
TCC_OPTION_O,
|
||
TCC_OPTION_m,
|
||
TCC_OPTION_f,
|
||
TCC_OPTION_nostdinc,
|
||
TCC_OPTION_nostdlib,
|
||
TCC_OPTION_print_search_dirs,
|
||
TCC_OPTION_rdynamic,
|
||
TCC_OPTION_run,
|
||
TCC_OPTION_v,
|
||
TCC_OPTION_w,
|
||
TCC_OPTION_pipe,
|
||
};
|
||
|
||
static const TCCOption tcc_options[] = {
|
||
{ "h", TCC_OPTION_HELP, 0 },
|
||
{ "?", TCC_OPTION_HELP, 0 },
|
||
{ "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
|
||
{ "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
|
||
{ "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
|
||
{ "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
|
||
{ "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
|
||
{ "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "bench", TCC_OPTION_bench, 0 },
|
||
{ "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
{ "b", TCC_OPTION_b, 0 },
|
||
#endif
|
||
{ "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "c", TCC_OPTION_c, 0 },
|
||
{ "static", TCC_OPTION_static, 0 },
|
||
{ "shared", TCC_OPTION_shared, 0 },
|
||
{ "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
|
||
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "rdynamic", TCC_OPTION_rdynamic, 0 },
|
||
{ "r", TCC_OPTION_r, 0 },
|
||
{ "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG },
|
||
{ "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||
{ "nostdinc", TCC_OPTION_nostdinc, 0 },
|
||
{ "nostdlib", TCC_OPTION_nostdlib, 0 },
|
||
{ "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
|
||
{ "v", TCC_OPTION_v, 0 },
|
||
{ "w", TCC_OPTION_w, 0 },
|
||
{ "pipe", TCC_OPTION_pipe, 0},
|
||
{ NULL },
|
||
};
|
||
|
||
/* convert 'str' into an array of space separated strings */
|
||
static int expand_args(char ***pargv, const char *str)
|
||
{
|
||
const char *s1;
|
||
char **argv, *arg;
|
||
int argc, len;
|
||
|
||
argc = 0;
|
||
argv = NULL;
|
||
for(;;) {
|
||
while (is_space(*str))
|
||
str++;
|
||
if (*str == '\0')
|
||
break;
|
||
s1 = str;
|
||
while (*str != '\0' && !is_space(*str))
|
||
str++;
|
||
len = str - s1;
|
||
arg = tcc_malloc(len + 1);
|
||
memcpy(arg, s1, len);
|
||
arg[len] = '\0';
|
||
dynarray_add((void ***)&argv, &argc, arg);
|
||
}
|
||
*pargv = argv;
|
||
return argc;
|
||
}
|
||
|
||
static char **files;
|
||
static int nb_files, nb_libraries;
|
||
static int multiple_files;
|
||
static int print_search_dirs;
|
||
static int output_type;
|
||
static int reloc_output;
|
||
static const char *outfile;
|
||
|
||
int parse_args(TCCState *s, int argc, char **argv)
|
||
{
|
||
int optind;
|
||
const TCCOption *popt;
|
||
const char *optarg, *p1, *r1;
|
||
char *r;
|
||
|
||
optind = 0;
|
||
while (1) {
|
||
if (optind >= argc) {
|
||
if (nb_files == 0 && !print_search_dirs)
|
||
goto show_help;
|
||
else
|
||
break;
|
||
}
|
||
r = argv[optind++];
|
||
if (r[0] != '-') {
|
||
/* add a new file */
|
||
dynarray_add((void ***)&files, &nb_files, r);
|
||
if (!multiple_files) {
|
||
optind--;
|
||
/* argv[0] will be this file */
|
||
break;
|
||
}
|
||
} else {
|
||
/* find option in table (match only the first chars */
|
||
popt = tcc_options;
|
||
for(;;) {
|
||
p1 = popt->name;
|
||
if (p1 == NULL)
|
||
error("invalid option -- '%s'", r);
|
||
r1 = r + 1;
|
||
for(;;) {
|
||
if (*p1 == '\0')
|
||
goto option_found;
|
||
if (*r1 != *p1)
|
||
break;
|
||
p1++;
|
||
r1++;
|
||
}
|
||
popt++;
|
||
}
|
||
option_found:
|
||
if (popt->flags & TCC_OPTION_HAS_ARG) {
|
||
if (*r1 != '\0' || (popt->flags & TCC_OPTION_NOSEP)) {
|
||
optarg = r1;
|
||
} else {
|
||
if (optind >= argc)
|
||
error("argument to '%s' is missing", r);
|
||
optarg = argv[optind++];
|
||
}
|
||
} else {
|
||
if (*r1 != '\0')
|
||
goto show_help;
|
||
optarg = NULL;
|
||
}
|
||
|
||
switch(popt->index) {
|
||
case TCC_OPTION_HELP:
|
||
show_help:
|
||
help();
|
||
exit(1);
|
||
case TCC_OPTION_I:
|
||
if (tcc_add_include_path(s, optarg) < 0)
|
||
error("too many include paths");
|
||
break;
|
||
case TCC_OPTION_D:
|
||
{
|
||
char *sym, *value;
|
||
sym = (char *)optarg;
|
||
value = strchr(sym, '=');
|
||
if (value) {
|
||
*value = '\0';
|
||
value++;
|
||
}
|
||
tcc_define_symbol(s, sym, value);
|
||
}
|
||
break;
|
||
case TCC_OPTION_U:
|
||
tcc_undefine_symbol(s, optarg);
|
||
break;
|
||
case TCC_OPTION_L:
|
||
tcc_add_library_path(s, optarg);
|
||
break;
|
||
case TCC_OPTION_B:
|
||
/* set tcc utilities path (mainly for tcc development) */
|
||
tcc_lib_path = optarg;
|
||
break;
|
||
case TCC_OPTION_l:
|
||
dynarray_add((void ***)&files, &nb_files, r);
|
||
nb_libraries++;
|
||
break;
|
||
case TCC_OPTION_bench:
|
||
do_bench = 1;
|
||
break;
|
||
case TCC_OPTION_bt:
|
||
num_callers = atoi(optarg);
|
||
break;
|
||
#ifdef CONFIG_TCC_BCHECK
|
||
case TCC_OPTION_b:
|
||
do_bounds_check = 1;
|
||
do_debug = 1;
|
||
break;
|
||
#endif
|
||
case TCC_OPTION_g:
|
||
do_debug = 1;
|
||
break;
|
||
case TCC_OPTION_c:
|
||
multiple_files = 1;
|
||
output_type = TCC_OUTPUT_OBJ;
|
||
break;
|
||
case TCC_OPTION_static:
|
||
s->static_link = 1;
|
||
break;
|
||
case TCC_OPTION_shared:
|
||
output_type = TCC_OUTPUT_DLL;
|
||
break;
|
||
case TCC_OPTION_o:
|
||
multiple_files = 1;
|
||
outfile = optarg;
|
||
break;
|
||
case TCC_OPTION_r:
|
||
/* generate a .o merging several output files */
|
||
reloc_output = 1;
|
||
output_type = TCC_OUTPUT_OBJ;
|
||
break;
|
||
case TCC_OPTION_nostdinc:
|
||
s->nostdinc = 1;
|
||
break;
|
||
case TCC_OPTION_nostdlib:
|
||
s->nostdlib = 1;
|
||
break;
|
||
case TCC_OPTION_print_search_dirs:
|
||
print_search_dirs = 1;
|
||
break;
|
||
case TCC_OPTION_run:
|
||
{
|
||
int argc1;
|
||
char **argv1;
|
||
argc1 = expand_args(&argv1, optarg);
|
||
if (argc1 > 0) {
|
||
parse_args(s, argc1, argv1);
|
||
}
|
||
multiple_files = 0;
|
||
output_type = TCC_OUTPUT_MEMORY;
|
||
}
|
||
break;
|
||
case TCC_OPTION_v:
|
||
printf("tcc version %s\n", TCC_VERSION);
|
||
exit(0);
|
||
case TCC_OPTION_f:
|
||
if (tcc_set_flag(s, optarg, 1) < 0 && s->warn_unsupported)
|
||
goto unsupported_option;
|
||
break;
|
||
case TCC_OPTION_W:
|
||
if (tcc_set_warning(s, optarg, 1) < 0 &&
|
||
s->warn_unsupported)
|
||
goto unsupported_option;
|
||
break;
|
||
case TCC_OPTION_w:
|
||
s->warn_none = 1;
|
||
break;
|
||
case TCC_OPTION_rdynamic:
|
||
s->rdynamic = 1;
|
||
break;
|
||
case TCC_OPTION_Wl:
|
||
{
|
||
const char *p;
|
||
if (strstart(optarg, "-Ttext,", &p)) {
|
||
s->text_addr = strtoul(p, NULL, 16);
|
||
s->has_text_addr = 1;
|
||
} else if (strstart(optarg, "--oformat,", &p)) {
|
||
if (strstart(p, "elf32-", NULL)) {
|
||
s->output_format = TCC_OUTPUT_FORMAT_ELF;
|
||
} else if (!strcmp(p, "binary")) {
|
||
s->output_format = TCC_OUTPUT_FORMAT_BINARY;
|
||
} else
|
||
#ifdef TCC_TARGET_COFF
|
||
if (!strcmp(p, "coff")) {
|
||
s->output_format = TCC_OUTPUT_FORMAT_COFF;
|
||
} else
|
||
#endif
|
||
{
|
||
error("target %s not found", p);
|
||
}
|
||
} else {
|
||
error("unsupported linker option '%s'", optarg);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
if (s->warn_unsupported) {
|
||
unsupported_option:
|
||
warning("unsupported option '%s'", r);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return optind;
|
||
}
|
||
|
||
// njn: renamed main() as main2() in order to repeat it multiple times.
|
||
int main2(int argc, char **argv)
|
||
{
|
||
int i;
|
||
TCCState *s;
|
||
int nb_objfiles, ret, optind;
|
||
char objfilename[1024];
|
||
int64_t start_time = 0;
|
||
|
||
#ifdef WIN32
|
||
/* on win32, we suppose the lib and includes are at the location
|
||
of 'tcc.exe' */
|
||
{
|
||
static char path[1024];
|
||
char *p, *d;
|
||
|
||
GetModuleFileNameA(NULL, path, sizeof path);
|
||
p = d = strlwr(path);
|
||
while (*d)
|
||
{
|
||
if (*d == '\\') *d = '/', p = d;
|
||
++d;
|
||
}
|
||
*p = '\0';
|
||
tcc_lib_path = path;
|
||
}
|
||
#endif
|
||
|
||
s = tcc_new();
|
||
output_type = TCC_OUTPUT_EXE;
|
||
outfile = NULL;
|
||
multiple_files = 1;
|
||
files = NULL;
|
||
nb_files = 0;
|
||
nb_libraries = 0;
|
||
reloc_output = 0;
|
||
print_search_dirs = 0;
|
||
|
||
optind = parse_args(s, argc - 1, argv + 1) + 1;
|
||
|
||
if (print_search_dirs) {
|
||
/* enough for Linux kernel */
|
||
printf("install: %s/\n", tcc_lib_path);
|
||
return 0;
|
||
}
|
||
|
||
nb_objfiles = nb_files - nb_libraries;
|
||
|
||
/* if outfile provided without other options, we output an
|
||
executable */
|
||
if (outfile && output_type == TCC_OUTPUT_MEMORY)
|
||
output_type = TCC_OUTPUT_EXE;
|
||
|
||
/* check -c consistency : only single file handled. XXX: checks file type */
|
||
if (output_type == TCC_OUTPUT_OBJ && !reloc_output) {
|
||
/* accepts only a single input file */
|
||
if (nb_objfiles != 1)
|
||
error("cannot specify multiple files with -c");
|
||
if (nb_libraries != 0)
|
||
error("cannot specify libraries with -c");
|
||
}
|
||
|
||
if (output_type != TCC_OUTPUT_MEMORY) {
|
||
if (!outfile) {
|
||
/* compute default outfile name */
|
||
pstrcpy(objfilename, sizeof(objfilename) - 1,
|
||
/* strip path */
|
||
tcc_basename(files[0]));
|
||
#ifdef TCC_TARGET_PE
|
||
pe_guess_outfile(objfilename, output_type);
|
||
#else
|
||
if (output_type == TCC_OUTPUT_OBJ && !reloc_output) {
|
||
char *ext = strrchr(objfilename, '.');
|
||
if (!ext)
|
||
goto default_outfile;
|
||
/* add .o extension */
|
||
strcpy(ext + 1, "o");
|
||
} else {
|
||
default_outfile:
|
||
pstrcpy(objfilename, sizeof(objfilename), "a.out");
|
||
}
|
||
#endif
|
||
outfile = objfilename;
|
||
}
|
||
}
|
||
|
||
if (do_bench) {
|
||
start_time = getclock_us();
|
||
}
|
||
|
||
tcc_set_output_type(s, output_type);
|
||
|
||
/* compile or add each files or library */
|
||
for(i = 0;i < nb_files; i++) {
|
||
const char *filename;
|
||
|
||
filename = files[i];
|
||
if (filename[0] == '-') {
|
||
if (tcc_add_library(s, filename + 2) < 0)
|
||
error("cannot find %s", filename);
|
||
} else {
|
||
if (tcc_add_file(s, filename) < 0) {
|
||
ret = 1;
|
||
goto the_end;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* free all files */
|
||
tcc_free(files);
|
||
|
||
if (do_bench) {
|
||
double total_time;
|
||
total_time = (double)(getclock_us() - start_time) / 1000000.0;
|
||
if (total_time < 0.001)
|
||
total_time = 0.001;
|
||
if (total_bytes < 1)
|
||
total_bytes = 1;
|
||
printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n",
|
||
tok_ident - TOK_IDENT, total_lines, total_bytes,
|
||
total_time, (int)(total_lines / total_time),
|
||
total_bytes / total_time / 1000000.0);
|
||
}
|
||
|
||
if (s->output_type == TCC_OUTPUT_MEMORY) {
|
||
ret = tcc_run(s, argc - optind, argv + optind);
|
||
} else
|
||
#ifdef TCC_TARGET_PE
|
||
if (s->output_type != TCC_OUTPUT_OBJ) {
|
||
ret = tcc_output_pe(s, outfile);
|
||
} else
|
||
#endif
|
||
{
|
||
tcc_output_file(s, outfile);
|
||
ret = 0;
|
||
}
|
||
the_end:
|
||
/* XXX: cannot do it with bound checking because of the malloc hooks */
|
||
if (!do_bounds_check)
|
||
tcc_delete(s);
|
||
|
||
#ifdef MEM_DEBUG
|
||
if (do_bench) {
|
||
printf("memory: %d bytes, max = %d bytes\n", mem_cur_size, mem_max_size);
|
||
}
|
||
#endif
|
||
return ret;
|
||
}
|
||
|
||
// njn: created this wrapper main() function to execute compilation multiple
|
||
// times. TinyCC is fast!
|
||
// Nb: we get a link error, and TinyCC would normally return non-zero. But
|
||
// the link error is not a problem for benchmarking purposes, so we return
|
||
// zero here (as required by vg_perf).
|
||
int main(int argc, char **argv)
|
||
{
|
||
#define REPS 30
|
||
int i;
|
||
for (i = 0; i < REPS; i++) {
|
||
main2(argc, argv);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#endif
|
||
|
||
// njn: copied these in from libtcc1.c to avoid link errors when libtcc1.a
|
||
// is not present.
|
||
unsigned short __tcc_fpu_control = 0x137f;
|
||
unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00;
|
||
|
||
#if 0
|
||
long long __shldi3(long long a, int b)
|
||
{
|
||
#ifdef __TINYC__
|
||
DWunion u;
|
||
u.ll = a;
|
||
if (b >= 32) {
|
||
u.s.high = (unsigned)u.s.low << (b - 32);
|
||
u.s.low = 0;
|
||
} else if (b != 0) {
|
||
u.s.high = ((unsigned)u.s.high << b) | (u.s.low >> (32 - b));
|
||
u.s.low = (unsigned)u.s.low << b;
|
||
}
|
||
return u.ll;
|
||
#else
|
||
return a << b;
|
||
#endif
|
||
}
|
||
#endif
|