diff options
| author | gus knight <waddlesplash@gmail.com> | 2015-07-27 16:03:25 -0400 |
|---|---|---|
| committer | Augustin Cavalier <waddlesplash@gmail.com> | 2015-07-27 16:03:25 -0400 |
| commit | 47e06c6d4e542e47fcbad69a78c2436a854a0779 (patch) | |
| tree | d979fb8f3372966c0ef3031c4edeaa8f017241d9 /src | |
| parent | 694d0fdade8bff3bc03466675350f596b2f4f8ed (diff) | |
| download | tinycc-47e06c6d4e542e47fcbad69a78c2436a854a0779.tar.gz tinycc-47e06c6d4e542e47fcbad69a78c2436a854a0779.tar.bz2 | |
Reorganize the source tree.
* Documentation is now in "docs".
* Source code is now in "src".
* Misc. fixes here and there so that everything still works.
I think I got everything in this commit, but I only tested this
on Linux (Make) and Windows (CMake), so I might've messed
something up on other platforms...
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 421 | ||||
| -rw-r--r-- | src/arm/arm-gen.c | 2162 | ||||
| -rw-r--r-- | src/arm/arm64-gen.c | 1846 | ||||
| -rw-r--r-- | src/cil/il-gen.c | 655 | ||||
| -rw-r--r-- | src/cil/il-opcodes.h | 251 | ||||
| -rw-r--r-- | src/coff.h | 446 | ||||
| -rw-r--r-- | src/config.h.in | 7 | ||||
| -rw-r--r-- | src/conftest.c | 77 | ||||
| -rw-r--r-- | src/elf.h | 3228 | ||||
| -rw-r--r-- | src/libtcc.c | 2322 | ||||
| -rw-r--r-- | src/libtcc.h | 104 | ||||
| -rw-r--r-- | src/stab.def | 234 | ||||
| -rw-r--r-- | src/stab.h | 17 | ||||
| -rw-r--r-- | src/tcc.c | 365 | ||||
| -rw-r--r-- | src/tcc.h | 1498 | ||||
| -rw-r--r-- | src/tccasm.c | 1130 | ||||
| -rw-r--r-- | src/tcccoff.c | 924 | ||||
| -rw-r--r-- | src/tccelf.c | 3504 | ||||
| -rw-r--r-- | src/tccgen.c | 6463 | ||||
| -rw-r--r-- | src/tcclib.h | 80 | ||||
| -rw-r--r-- | src/tccpe.c | 1890 | ||||
| -rw-r--r-- | src/tccpp.c | 3430 | ||||
| -rw-r--r-- | src/tccrun.c | 772 | ||||
| -rw-r--r-- | src/tcctok.h | 335 | ||||
| -rw-r--r-- | src/tms320c67/c67-gen.c | 2559 | ||||
| -rw-r--r-- | src/x86/i386-asm.c | 1502 | ||||
| -rw-r--r-- | src/x86/i386-asm.h | 474 | ||||
| -rw-r--r-- | src/x86/i386-gen.c | 1144 | ||||
| -rw-r--r-- | src/x86/i386-tok.h | 244 | ||||
| -rw-r--r-- | src/x86/x86_64-asm.h | 449 | ||||
| -rw-r--r-- | src/x86/x86_64-gen.c | 2360 |
31 files changed, 40893 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..a098a76 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,421 @@ +# +# Tiny C Compiler Makefile +# + +TOP ?= . +include $(TOP)/../config.mak +VPATH = $(top_srcdir) + +CPPFLAGS += -I$(TOP) # for config.h + +ifneq (-$(findstring gcc,$(CC))-,-gcc-) +ifeq (-$(findstring clang,$(CC))-,-clang-) +# make clang accept gnuisms in libtcc1.c +CFLAGS+=-fheinous-gnu-extensions +endif +endif + +CPPFLAGS_P=$(CPPFLAGS) -DCONFIG_TCC_STATIC +CFLAGS_P=$(CFLAGS) -pg -static +LIBS_P= +LDFLAGS_P=$(LDFLAGS) + +ifdef CONFIG_WIN64 +CONFIG_WIN32=yes +endif + +ifndef CONFIG_WIN32 +LIBS=-lm +ifndef CONFIG_NOLDL +LIBS+=-ldl +endif +endif + +# make libtcc as static or dynamic library? +ifdef DISABLE_STATIC +ifndef CONFIG_WIN32 +LIBTCC=libtcc.so.1.0 +else +LIBTCC=libtcc.dll +LIBTCC_DLL=yes +LIBTCC_EXTRA=libtcc.def libtcc.a +endif +LINK_LIBTCC=-Wl,-rpath,"$(libdir)" +ifdef DISABLE_RPATH +LINK_LIBTCC= +endif +else +LIBTCC=libtcc.a +LINK_LIBTCC= +endif + +CONFIG_$(ARCH) = yes +NATIVE_DEFINES_$(CONFIG_i386) += -DTCC_TARGET_I386 +NATIVE_DEFINES_$(CONFIG_x86-64) += -DTCC_TARGET_X86_64 +NATIVE_DEFINES_$(CONFIG_WIN32) += -DTCC_TARGET_PE +NATIVE_DEFINES_$(CONFIG_uClibc) += -DTCC_UCLIBC +NATIVE_DEFINES_$(CONFIG_arm) += -DTCC_TARGET_ARM +NATIVE_DEFINES_$(CONFIG_arm_eabihf) += -DTCC_ARM_EABI -DTCC_ARM_HARDFLOAT +NATIVE_DEFINES_$(CONFIG_arm_eabi) += -DTCC_ARM_EABI +NATIVE_DEFINES_$(CONFIG_arm_vfp) += -DTCC_ARM_VFP +NATIVE_DEFINES_$(CONFIG_arm64) += -DTCC_TARGET_ARM64 +NATIVE_DEFINES += $(NATIVE_DEFINES_yes) + +ifeq ($(TOP),.) + +PROGS=tcc$(EXESUF) +I386_CROSS = i386-linux-gnu-tcc$(EXESUF) +WIN32_CROSS = i386-win-mingw32-tcc$(EXESUF) +WIN64_CROSS = x86_64-win-mingw32-tcc$(EXESUF) +WINCE_CROSS = arm-win-mingw32ce-tcc$(EXESUF) +X64_CROSS = x86_64-linux-gnu-tcc$(EXESUF) +ARM_FPA_CROSS = arm-linux-fpa-tcc$(EXESUF) +ARM_FPA_LD_CROSS = arm-linux-fpa-ld-tcc$(EXESUF) +ARM_VFP_CROSS = arm-linux-gnu-tcc$(EXESUF) +ARM_EABI_CROSS = arm-linux-gnueabi-tcc$(EXESUF) +ARM_EABIHF_CROSS = arm-linux-gnueabihf-tcc$(EXESUF) +ARM_CROSS = $(ARM_FPA_CROSS) $(ARM_FPA_LD_CROSS) $(ARM_VFP_CROSS) $(ARM_EABI_CROSS) +ARM64_CROSS = arm64-tcc$(EXESUF) +C67_CROSS = c67-tcc$(EXESUF) + +# Legacy symlinks for cross compilers +$(I386_CROSS)_LINK = i386-tcc$(EXESUF) +$(WIN32_CROSS)_LINK = i386-win-tcc$(EXESUF) +$(WIN64_CROSS)_LINK = x86_64-win-tcc$(EXESUF) +$(WINCE_CROSS)_LINK = arm-win-tcc$(EXESUF) +$(X64_CROSS)_LINK = x86_64-tcc$(EXESUF) +$(ARM_FPA_CROSS)_LINK = arm-fpa-tcc$(EXESUF) +$(ARM_FPA_LD_CROSS)_LINK = arm-fpa-ld-tcc$(EXESUF) +$(ARM_VFP_CROSS)_LINK = arm-vfp-tcc$(EXESUF) +$(ARM_EABI_CROSS)_LINK = arm-eabi-tcc$(EXESUF) + +ifeq ($(TARGETOS),Windows) +ifeq ($(ARCH),i386) +PROGS:=$($(WIN32_CROSS)_LINK) +$($(WIN32_CROSS)_LINK)_TCC = yes +endif +ifeq ($(ARCH),x86-64) +PROGS:=$($(WIN64_CROSS)_LINK) +$($(WIN64_CROSS)_LINK)_TCC = yes +endif +endif + +ifeq ($(TARGETOS),Linux) +ifeq ($(ARCH),i386) +PROGS:=$($(I386_CROSS)_LINK) +$($(I386_CROSS)_LINK)_TCC = yes +endif +ifeq ($(ARCH),x86-64) +PROGS:=$($(X64_CROSS)_LINK) +$($(X64_CROSS)_LINK)_TCC = yes +endif +endif + +CORE_FILES = tcc.c libtcc.c tccpp.c tccgen.c tccelf.c tccasm.c tccrun.c +CORE_FILES += tcc.h config.h libtcc.h tcctok.h +I386_FILES = $(CORE_FILES) x86/i386-gen.c x86/i386-asm.c x86/i386-asm.h x86/i386-tok.h +WIN32_FILES = $(CORE_FILES) x86/i386-gen.c x86/i386-asm.c x86/i386-asm.h x86/i386-tok.h tccpe.c +WIN64_FILES = $(CORE_FILES) x86/x86_64-gen.c x86/i386-asm.c x86/x86_64-asm.h tccpe.c +WINCE_FILES = $(CORE_FILES) arm/arm-gen.c tccpe.c +X86_64_FILES = $(CORE_FILES) x86/x86_64-gen.c x86/i386-asm.c x86/x86_64-asm.h +ARM_FILES = $(CORE_FILES) arm/arm-gen.c +ARM64_FILES = $(CORE_FILES) arm/arm64-gen.c +C67_FILES = $(CORE_FILES) tms320c67/c67-gen.c tcccoff.c + +ifdef CONFIG_WIN64 +PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF) +NATIVE_FILES=$(WIN64_FILES) +PROGS_CROSS=$(WIN32_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1_CROSS=lib/i386-win/libtcc1.a +LIBTCC1=libtcc1.a +else ifdef CONFIG_WIN32 +PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF) +NATIVE_FILES=$(WIN32_FILES) +PROGS_CROSS=$(WIN64_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1_CROSS=lib/x86_64-win/libtcc1.a +LIBTCC1=libtcc1.a +else ifeq ($(ARCH),i386) +NATIVE_FILES=$(I386_FILES) +PROGS_CROSS=$($(X64_CROSS)_LINK) $($(WIN32_CROSS)_LINK) $($(WIN64_CROSS)_LINK) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a lib/x86_64/libtcc1.a \ + lib/arm64/libtcc1.a +LIBTCC1=libtcc1.a +else ifeq ($(ARCH),x86-64) +ifeq ($(TARGETOS),Darwin) +NATIVE_FILES=$(X86_64_FILES) +PROGS_CROSS=$($(I386_CROSS)_LINK) $($(WIN32_CROSS)_LINK) $($(WIN64_CROSS)_LINK) $(ARM_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a lib/x86_64/libtcc1.a +LIBTCC1=libtcc1.a +else +NATIVE_FILES=$(X86_64_FILES) +PROGS_CROSS=$($(I386_CROSS)_LINK) $($(WIN32_CROSS)_LINK) $($(WIN64_CROSS)_LINK) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a lib/x86_64/libtcc1.a \ + lib/arm64/libtcc1.a +LIBTCC1=libtcc1.a +endif +else ifeq ($(ARCH),arm) +NATIVE_FILES=$(ARM_FILES) +PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1=libtcc1.a +LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a +else ifeq ($(ARCH),arm64) +NATIVE_FILES=$(ARM64_FILES) +PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(ARM_CROSS) $(C67_CROSS) $(WINCE_CROSS) +LIBTCC1=libtcc1.a +LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a +endif +PROGS_CROSS_LINK=$(foreach PROG_CROSS,$(PROGS_CROSS),$($(PROG_CROSS)_LINK)) + +ifeq ($(TARGETOS),Darwin) +PROGS+=tiny_libmaker$(EXESUF) +endif + +TCCLIBS = $(LIBTCC1) $(LIBTCC) $(LIBTCC_EXTRA) +TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info + +ifdef CONFIG_CROSS +PROGS+=$(PROGS_CROSS) +TCCLIBS+=$(LIBTCC1_CROSS) +endif + +all: $(PROGS) $(TCCLIBS) $(TCCDOCS) + +# Host Tiny C Compiler +tcc$(EXESUF): tcc.o $(LIBTCC) + $(CC) -o $@ $^ $(LIBS) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LINK_LIBTCC) + +# Cross Tiny C Compilers +%-tcc$(EXESUF): tcc.c + $(CC) -o $@ $< -DONE_SOURCE $(if $($@_TCC),$(NATIVE_DEFINES),$(DEFINES)) $(CPPFLAGS) $(CFLAGS) $(LIBS) $(LDFLAGS) + $(if $($@_LINK),ln -sf $@ $($@_LINK)) + $(if $($@_TCC),ln -sf $@ tcc$(EXESUF)) + +# profiling version +tcc_p$(EXESUF): $(NATIVE_FILES) + $(CC) -o $@ $< -DONE_SOURCE $(NATIVE_DEFINES) $(CPPFLAGS_P) $(CFLAGS_P) $(LIBS_P) $(LDFLAGS_P) + +$(I386_CROSS) $($(I386_CROSS)_LINK): DEFINES = -DTCC_TARGET_I386 +$(X64_CROSS) $($(X64_CROSS)_LINK): DEFINES = -DTCC_TARGET_X86_64 +$(WIN32_CROSS) $($(WIN32_CROSS)_LINK): DEFINES = -DTCC_TARGET_I386 -DTCC_TARGET_PE \ + -DCONFIG_TCCDIR="\"$(tccdir)/win32\"" \ + -DCONFIG_TCC_LIBPATHS="\"{B}/lib/32;{B}/lib\"" +$(WIN64_CROSS) $($(WIN64_CROSS)_LINK): DEFINES = -DTCC_TARGET_X86_64 -DTCC_TARGET_PE \ + -DCONFIG_TCCDIR="\"$(tccdir)/win32\"" \ + -DCONFIG_TCC_LIBPATHS="\"{B}/lib/64;{B}/lib\"" +$(WINCE_CROSS): DEFINES = -DTCC_TARGET_PE +$(C67_CROSS): DEFINES = -DTCC_TARGET_C67 +$(ARM_FPA_CROSS): DEFINES = -DTCC_TARGET_ARM +$(ARM_FPA_LD_CROSS)$(EXESUF): DEFINES = -DTCC_TARGET_ARM -DLDOUBLE_SIZE=12 +$(ARM_VFP_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_VFP +$(ARM_EABI_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_EABI -DTCC_ARM_VFP +$(ARM64_CROSS): DEFINES = -DTCC_TARGET_ARM64 + +$(I386_CROSS) $($(I386_CROSS)_LINK): $(I386_FILES) +$(X64_CROSS) $($(X64_CROSS)_LINK): $(X86_64_FILES) +$(WIN32_CROSS) $($(WIN32_CROSS)_LINK): $(WIN32_FILES) +$(WIN64_CROSS) $($(WIN64_CROSS)_LINK): $(WIN64_FILES) +$(WINCE_CROSS) $($(WINCE_CROSS)_LINK): $(WINCE_FILES) +$(C67_CROSS) $($(C67_CROSS)_LINK): $(C67_FILES) +$(ARM_FPA_CROSS) $(ARM_FPA_LD_CROSS) $(ARM_VFP_CROSS) $(ARM_EABI_CROSS): $(ARM_FILES) +$($(ARM_FPA_CROSS)_LINK) $($(ARM_FPA_LD_CROSS)_LINK) $($(ARM_VFP_CROSS)_LINK) $($(ARM_EABI_CROSS)_LINK): $(ARM_FILES) +$(ARM64_CROSS): $(ARM64_FILES) + +# libtcc generation and test +ifndef ONE_SOURCE +LIBTCC_OBJ = $(filter-out tcc.o,$(patsubst %.c,%.o,$(filter %.c,$(NATIVE_FILES)))) +LIBTCC_INC = $(filter %.h,$(CORE_FILES)) $(filter-out $(CORE_FILES),$(NATIVE_FILES)) +else +LIBTCC_OBJ = libtcc.o +LIBTCC_INC = $(NATIVE_FILES) +libtcc.o : NATIVE_DEFINES += -DONE_SOURCE +endif + +$(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC) + $(CC) -o $@ -c $< $(NATIVE_DEFINES) $(CPPFLAGS) $(CFLAGS) + +ifndef LIBTCC_DLL +libtcc.a: $(LIBTCC_OBJ) + $(AR) rcs $@ $^ +endif + +libtcc.so.1.0: $(LIBTCC_OBJ) + $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS) + +libtcc.so.1.0: CFLAGS+=-fPIC + +ifdef LIBTCC_DLL +libtcc.dll libtcc.def libtcc.a: $(LIBTCC_OBJ) + $(CC) -shared $^ -o $@ $(LDFLAGS) -Wl,--output-def,libtcc.def,--out-implib,libtcc.a +endif + +# windows utilities +tiny_impdef$(EXESUF): ../win32/tools/tiny_impdef.c + $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +tiny_libmaker$(EXESUF): ../win32/tools/tiny_libmaker.c + $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + +# TinyCC runtime libraries +libtcc1.a : FORCE + $(MAKE) -C ../lib native + if test ! -d $(ARCH); then mkdir $(ARCH); fi + if test ! -L $(ARCH)/$@; then ln -sf ../$@ $(ARCH)/$@; fi +lib/%/libtcc1.a : FORCE $(PROGS_CROSS) + $(MAKE) -C ../lib cross TARGET=$* + +FORCE: + +# install +TCC_INCLUDES = stdarg.h stddef.h stdbool.h float.h varargs.h +INSTALL=install +ifdef STRIP_BINARIES +INSTALLBIN=$(INSTALL) -s +else +INSTALLBIN=$(INSTALL) +endif + +install-strip: install + strip $(foreach PROG,$(PROGS),"$(bindir)"/$(PROG)) + +ifndef CONFIG_WIN32 +install: $(PROGS) $(TCCLIBS) $(TCCDOCS) + mkdir -p "$(bindir)" + $(INSTALLBIN) -m755 $(PROGS) "$(bindir)" + cp -P tcc$(EXESUF) "$(bindir)" + mkdir -p "$(mandir)/man1" + -$(INSTALL) -m644 tcc.1 "$(mandir)/man1" + mkdir -p "$(infodir)" + -$(INSTALL) -m644 tcc-doc.info "$(infodir)" + mkdir -p "$(tccdir)" + mkdir -p "$(tccdir)/include" +ifneq ($(LIBTCC1),) + mkdir -p "$(tccdir)/$(ARCH)" + $(INSTALL) -m644 $(LIBTCC1) "$(tccdir)/$(ARCH)" +endif + $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) $(top_srcdir)/tcclib.h "$(tccdir)/include" + mkdir -p "$(libdir)" + $(INSTALL) -m644 $(LIBTCC) "$(libdir)" +ifdef DISABLE_STATIC + ln -sf "$(ln_libdir)/libtcc.so.1.0" "$(libdir)/libtcc.so.1" + ln -sf "$(ln_libdir)/libtcc.so.1.0" "$(libdir)/libtcc.so" +endif + mkdir -p "$(includedir)" + $(INSTALL) -m644 $(top_srcdir)/libtcc.h "$(includedir)" + mkdir -p "$(docdir)" + -$(INSTALL) -m644 tcc-doc.html "$(docdir)" +ifdef CONFIG_CROSS + mkdir -p "$(tccdir)/win32/lib/32" + mkdir -p "$(tccdir)/win32/lib/64" + mkdir -p "$(tccdir)/i386" + mkdir -p "$(tccdir)/x86-64" +ifneq ($(HOST_OS),Darwin) + mkdir -p "$(tccdir)/arm64" + $(INSTALL) -m644 lib/arm64/libtcc1.a "$(tccdir)/arm64" +endif + $(INSTALL) -m644 lib/i386/libtcc1.a "$(tccdir)/i386" + $(INSTALL) -m644 lib/x86_64/libtcc1.a "$(tccdir)/x86-64" + $(INSTALL) -m644 $(top_srcdir)/win32/lib/*.def "$(tccdir)/win32/lib" + $(INSTALL) -m644 lib/i386-win/libtcc1.a "$(tccdir)/win32/lib/32" + $(INSTALL) -m644 lib/x86_64-win/libtcc1.a "$(tccdir)/win32/lib/64" + cp -r $(top_srcdir)/win32/include/. "$(tccdir)/win32/include" + cp -r "$(tccdir)/include" "$(tccdir)/win32" +endif + +uninstall: + rm -fv $(foreach P,$(PROGS),"$(bindir)/$P") + rm -fv "$(bindir)/tcc$(EXESUF)" + rm -fv $(foreach P,$(LIBTCC1),"$(tccdir)/$P") + rm -fv $(foreach P,$(TCC_INCLUDES),"$(tccdir)/include/$P") + rm -fv "$(mandir)/man1/tcc.1" "$(infodir)/tcc-doc.info" + rm -fv "$(libdir)/$(LIBTCC)" "$(includedir)/libtcc.h" + rm -fv "$(libdir)/libtcc.so*" + rm -rv "$(tccdir)" + rm -rv "$(docdir)" +else +# on windows +install: $(PROGS) $(TCCLIBS) $(TCCDOCS) + mkdir -p "$(tccdir)" + mkdir -p "$(tccdir)/lib" + mkdir -p "$(tccdir)/include" + mkdir -p "$(tccdir)/examples" + mkdir -p "$(tccdir)/doc" + mkdir -p "$(tccdir)/libtcc" + $(INSTALLBIN) -m755 $(PROGS) "$(tccdir)" + $(INSTALLBIN) -m755 tcc.exe "$(tccdir)" + $(INSTALL) -m644 $(LIBTCC1) $(top_srcdir)/win32/lib/*.def "$(tccdir)/lib" + cp -r $(top_srcdir)/win32/include/. "$(tccdir)/include" + cp -r $(top_srcdir)/win32/examples/. "$(tccdir)/examples" + $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) $(top_srcdir)/tcclib.h "$(tccdir)/include" + $(INSTALL) -m644 tcc-doc.html $(top_srcdir)/win32/tcc-win32.txt "$(tccdir)/doc" + $(INSTALL) -m644 $(top_srcdir)/libtcc.h $(LIBTCC_EXTRA) "$(tccdir)/libtcc" + $(INSTALL) -m644 $(LIBTCC) "$(tccdir)" +ifdef CONFIG_CROSS + mkdir -p "$(tccdir)/lib/32" + mkdir -p "$(tccdir)/lib/64" + -$(INSTALL) -m644 lib/i386-win/libtcc1.a "$(tccdir)/lib/32" + -$(INSTALL) -m644 lib/x86_64-win/libtcc1.a "$(tccdir)/lib/64" +endif + +uninstall: + rm -rfv "$(tccdir)/*" +endif + +# documentation and man page +tcc-doc.html: ../docs/tcc-doc.texi + -makeinfo --no-split --html --number-sections -o $@ $< + +tcc.1: ../docs/tcc-doc.texi + -$(top_srcdir)/texi2pod.pl $< tcc.pod + -pod2man --section=1 --center="Tiny C Compiler" --release=`cat $(top_srcdir)/../VERSION` tcc.pod > $@ + +tcc-doc.info: ../docs/tcc-doc.texi + -makeinfo $< + +# in tests subdir +export LIBTCC1 + +%est: + $(MAKE) -C ../tests $@ 'PROGS_CROSS=$(PROGS_CROSS)' + +clean: + rm -vf $(PROGS) tcc_p$(EXESUF) tcc.pod *~ *.o *.a *.so* *.out *.log \ + *.exe a.out tags TAGS libtcc_test$(EXESUF) tcc$(EXESUF) + -rm -r $(ARCH) arm64 +ifeq ($(HOST_OS),Linux) + -rm -r ./C: +endif + -rm *-tcc$(EXESUF) + $(MAKE) -C ../tests $@ +ifneq ($(LIBTCC1),) + $(MAKE) -C ../lib $@ +endif + +distclean: clean + rm -vf config.h config.mak config.texi tcc.1 tcc-doc.info tcc-doc.html + +config.mak: + @echo "Please run ./configure." + @exit 1 + +tags: + ctags $(top_srcdir)/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[chS] + +TAGS: + ctags -e $(top_srcdir)/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[chS] + +# create release tarball from *current* git branch (including tcc-doc.html +# and converting two files to CRLF) +TCC-VERSION := tcc-$(shell cat $(top_srcdir)/../VERSION) +tar: tcc-doc.html + mkdir $(TCC-VERSION) + ( cd $(TCC-VERSION) && git --git-dir ../.git checkout -f ) + cp tcc-doc.html $(TCC-VERSION) + for f in tcc-win32.txt build-tcc.bat ; do \ + cat win32/$$f | sed 's,\(.*\),\1\r,g' > $(TCC-VERSION)/win32/$$f ; \ + done + tar cjf $(TCC-VERSION).tar.bz2 $(TCC-VERSION) + rm -rf $(TCC-VERSION) + git reset + +.PHONY: all clean tar tags TAGS distclean install uninstall FORCE + +endif # ifeq ($(TOP),.) diff --git a/src/arm/arm-gen.c b/src/arm/arm-gen.c new file mode 100644 index 0000000..f31fb88 --- /dev/null +++ b/src/arm/arm-gen.c @@ -0,0 +1,2162 @@ +/* + * ARMv4 code generator for TCC + * + * Copyright (c) 2003 Daniel Glöckner + * Copyright (c) 2012 Thomas Preud'homme + * + * Based on i386-gen.c by 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 + */ + +#ifdef TARGET_DEFS_ONLY + +#if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP) +#error "Currently TinyCC only supports float computation with VFP instructions" +#endif + +/* number of available registers */ +#ifdef TCC_ARM_VFP +#define NB_REGS 13 +#else +#define NB_REGS 9 +#endif + +typedef int RegArgs; + +#ifndef TCC_ARM_VERSION +#define TCC_ARM_VERSION 5 +#endif + +/* 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_R0 0x0004 +#define RC_R1 0x0008 +#define RC_R2 0x0010 +#define RC_R3 0x0020 +#define RC_R12 0x0040 +#define RC_F0 0x0080 +#define RC_F1 0x0100 +#define RC_F2 0x0200 +#define RC_F3 0x0400 +#ifdef TCC_ARM_VFP +#define RC_F4 0x0800 +#define RC_F5 0x1000 +#define RC_F6 0x2000 +#define RC_F7 0x4000 +#endif +#define RC_IRET RC_R0 /* function return: integer register */ +#define RC_LRET RC_R1 /* function return: second integer register */ +#define RC_FRET RC_F0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_R0 = 0, + TREG_R1, + TREG_R2, + TREG_R3, + TREG_R12, + TREG_F0, + TREG_F1, + TREG_F2, + TREG_F3, +#ifdef TCC_ARM_VFP + TREG_F4, + TREG_F5, + TREG_F6, + TREG_F7, +#endif +}; + +#ifdef TCC_ARM_VFP +#define T2CPR(t) (((t)&VT_BTYPE) != VT_FLOAT ? 0x100 : 0) +#endif + +/* return registers for function */ +#define REG_IRET TREG_R0 /* single word int return register */ +#define REG_LRET TREG_R1 /* second word return register (for long long) */ +#define REG_FRET TREG_F0 /* float return register */ + +#ifdef TCC_ARM_EABI +#define TOK___divdi3 TOK___aeabi_ldivmod +#define TOK___moddi3 TOK___aeabi_ldivmod +#define TOK___udivdi3 TOK___aeabi_uldivmod +#define TOK___umoddi3 TOK___aeabi_uldivmod +#endif + +/* 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 */ +#ifdef TCC_ARM_VFP +#define LDOUBLE_SIZE 8 +#endif + +#ifndef LDOUBLE_SIZE +#define LDOUBLE_SIZE 8 +#endif + +#ifdef TCC_ARM_EABI +#define LDOUBLE_ALIGN 8 +#else +#define LDOUBLE_ALIGN 4 +#endif + +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +#define CHAR_IS_UNSIGNED + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_ARM + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_ARM_ABS32 +#define R_DATA_PTR R_ARM_ABS32 +#define R_JMP_SLOT R_ARM_JUMP_SLOT +#define R_COPY R_ARM_COPY + +#define ELF_START_ADDR 0x00008000 +#define ELF_PAGE_SIZE 0x1000 + +enum float_abi { + ARM_SOFTFP_FLOAT, + ARM_HARD_FLOAT, +}; + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#include "../tcc.h" + +enum float_abi float_abi; + +ST_DATA const int reg_classes[NB_REGS] = { + /* r0 */ RC_INT | RC_R0, + /* r1 */ RC_INT | RC_R1, + /* r2 */ RC_INT | RC_R2, + /* r3 */ RC_INT | RC_R3, + /* r12 */ RC_INT | RC_R12, + /* f0 */ RC_FLOAT | RC_F0, + /* f1 */ RC_FLOAT | RC_F1, + /* f2 */ RC_FLOAT | RC_F2, + /* f3 */ RC_FLOAT | RC_F3, +#ifdef TCC_ARM_VFP + /* d4/s8 */ RC_FLOAT | RC_F4, + /* d5/s10 */ RC_FLOAT | RC_F5, + /* d6/s12 */ RC_FLOAT | RC_F6, + /* d7/s14 */ RC_FLOAT | RC_F7, +#endif +}; + +static int func_sub_sp_offset, last_itod_magic; +static int leaffunc; + +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) +static CType float_type, double_type, func_float_type, func_double_type; +ST_FUNC void arm_init(struct TCCState* s) +{ + float_type.t = VT_FLOAT; + double_type.t = VT_DOUBLE; + func_float_type.t = VT_FUNC; + func_float_type.ref = + sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD); + func_double_type.t = VT_FUNC; + func_double_type.ref = + sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD); + + float_abi = s->float_abi; +#ifndef TCC_ARM_HARDFLOAT + tcc_warning("soft float ABI currently not supported: default to softfp"); +#endif +} +#else +#define func_float_type func_old_type +#define func_double_type func_old_type +#define func_ldouble_type func_old_type +ST_FUNC void arm_init(struct TCCState* s) +{ +#if !defined(TCC_ARM_VFP) + tcc_warning("Support for FPA is deprecated and will be removed in next" + " release"); +#endif +#if !defined(TCC_ARM_EABI) + tcc_warning("Support for OABI is deprecated and will be removed in next" + " release"); +#endif +} +#endif + +static int two2mask(int a, int b) +{ + return (reg_classes[a] | reg_classes[b]) & ~(RC_INT | RC_FLOAT); +} + +static int regmask(int r) +{ + return reg_classes[r] & ~(RC_INT | RC_FLOAT); +} + +/******************************************************/ + +#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP) +char* default_elfinterp(struct TCCState* s) +{ + if (s->float_abi == ARM_HARD_FLOAT) + return "/lib/ld-linux-armhf.so.3"; + else + return "/lib/ld-linux.so.3"; +} +#endif + +void o(uint32_t i) +{ + /* this is a good place to start adding big-endian support*/ + int ind1; + + ind1 = ind + 4; + if (!cur_text_section) + tcc_error("compiler error! This happens f.ex. if the compiler\n" + "can't evaluate constant expressions outside of a function."); + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind++] = i & 255; + i >>= 8; + cur_text_section->data[ind++] = i & 255; + i >>= 8; + cur_text_section->data[ind++] = i & 255; + i >>= 8; + cur_text_section->data[ind++] = i; +} + +static uint32_t stuff_const(uint32_t op, uint32_t c) +{ + int try_neg = 0; + uint32_t nc = 0, negop = 0; + + switch (op & 0x1F00000) { + case 0x800000: // add + case 0x400000: // sub + try_neg = 1; + negop = op ^ 0xC00000; + nc = -c; + break; + case 0x1A00000: // mov + case 0x1E00000: // mvn + try_neg = 1; + negop = op ^ 0x400000; + nc = ~c; + break; + case 0x200000: // xor + if (c == ~0) + return (op & 0xF010F000) | ((op >> 16) & 0xF) | 0x1E00000; + break; + case 0x0: // and + if (c == ~0) + return (op & 0xF010F000) | ((op >> 16) & 0xF) | 0x1A00000; + case 0x1C00000: // bic + try_neg = 1; + negop = op ^ 0x1C00000; + nc = ~c; + break; + case 0x1800000: // orr + if (c == ~0) + return (op & 0xFFF0FFFF) | 0x1E00000; + break; + } + do { + uint32_t m; + int i; + if (c < 256) /* catch undefined <<32 */ + return op | c; + for (i = 2; i < 32; i += 2) { + m = (0xff >> i) | (0xff << (32 - i)); + if (!(c & ~m)) + return op | (i << 7) | (c << i) | (c >> (32 - i)); + } + op = negop; + c = nc; + } while (try_neg--); + return 0; +} + +// only add,sub +void stuff_const_harder(uint32_t op, uint32_t v) +{ + uint32_t x; + x = stuff_const(op, v); + if (x) + o(x); + else { + uint32_t a[16], nv, no, o2, n2; + int i, j, k; + a[0] = 0xff; + o2 = (op & 0xfff0ffff) | ((op & 0xf000) << 4); + ; + for (i = 1; i < 16; i++) + a[i] = (a[i - 1] >> 2) | (a[i - 1] << 30); + for (i = 0; i < 12; i++) + for (j = i < 4 ? i + 12 : 15; j >= i + 4; j--) + if ((v & (a[i] | a[j])) == v) { + o(stuff_const(op, v & a[i])); + o(stuff_const(o2, v & a[j])); + return; + } + no = op ^ 0xC00000; + n2 = o2 ^ 0xC00000; + nv = -v; + for (i = 0; i < 12; i++) + for (j = i < 4 ? i + 12 : 15; j >= i + 4; j--) + if ((nv & (a[i] | a[j])) == nv) { + o(stuff_const(no, nv & a[i])); + o(stuff_const(n2, nv & a[j])); + return; + } + for (i = 0; i < 8; i++) + for (j = i + 4; j < 12; j++) + for (k = i < 4 ? i + 12 : 15; k >= j + 4; k--) + if ((v & (a[i] | a[j] | a[k])) == v) { + o(stuff_const(op, v & a[i])); + o(stuff_const(o2, v & a[j])); + o(stuff_const(o2, v & a[k])); + return; + } + no = op ^ 0xC00000; + nv = -v; + for (i = 0; i < 8; i++) + for (j = i + 4; j < 12; j++) + for (k = i < 4 ? i + 12 : 15; k >= j + 4; k--) + if ((nv & (a[i] | a[j] | a[k])) == nv) { + o(stuff_const(no, nv & a[i])); + o(stuff_const(n2, nv & a[j])); + o(stuff_const(n2, nv & a[k])); + return; + } + o(stuff_const(op, v & a[0])); + o(stuff_const(o2, v & a[4])); + o(stuff_const(o2, v & a[8])); + o(stuff_const(o2, v & a[12])); + } +} + +ST_FUNC uint32_t encbranch(int pos, int addr, int fail) +{ + addr -= pos + 8; + addr /= 4; + if (addr >= 0x1000000 || addr < -0x1000000) { + if (fail) + tcc_error("FIXME: function bigger than 32MB"); + return 0; + } + return 0x0A000000 | (addr & 0xffffff); +} + +int decbranch(int pos) +{ + int x; + x = *(uint32_t*)(cur_text_section->data + pos); + x &= 0x00ffffff; + if (x & 0x800000) + x -= 0x1000000; + return x * 4 + pos + 8; +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + uint32_t* x; + int lt; + while (t) { + x = (uint32_t*)(cur_text_section->data + t); + t = decbranch(lt = t); + if (a == lt + 4) + *x = 0xE1A00000; // nop + else { + *x &= 0xff000000; + *x |= encbranch(lt, a, 1); + } + } +} + +void gsym(int t) +{ + gsym_addr(t, ind); +} + +#ifdef TCC_ARM_VFP +static uint32_t vfpr(int r) +{ + if (r < TREG_F0 || r > TREG_F7) + tcc_error("compiler error! register %i is no vfp register", r); + return r - 5; +} +#else +static uint32_t fpr(int r) +{ + if (r < TREG_F0 || r > TREG_F3) + tcc_error("compiler error! register %i is no fpa register", r); + return r - 5; +} +#endif + +static uint32_t intr(int r) +{ + if (r == 4) + return 12; + if ((r < 0 || r > 4) && r != 14) + tcc_error("compiler error! register %i is no int register", r); + return r; +} + +static void calcaddr(uint32_t* base, int* off, int* sgn, int maxoff, + unsigned shift) +{ + if (*off > maxoff || *off & ((1 << shift) - 1)) { + uint32_t x, y; + x = 0xE280E000; + if (*sgn) + x = 0xE240E000; + x |= (*base) << 16; + *base = 14; // lr + y = stuff_const(x, *off & ~maxoff); + if (y) { + o(y); + *off &= maxoff; + return; + } + y = stuff_const(x, (*off + maxoff) & ~maxoff); + if (y) { + o(y); + *sgn = !*sgn; + *off = ((*off + maxoff) & ~maxoff) - *off; + return; + } + stuff_const_harder(x, *off & ~maxoff); + *off &= maxoff; + } +} + +static uint32_t mapcc(int cc) +{ + switch (cc) { + case TOK_ULT: + return 0x30000000; /* CC/LO */ + case TOK_UGE: + return 0x20000000; /* CS/HS */ + case TOK_EQ: + return 0x00000000; /* EQ */ + case TOK_NE: + return 0x10000000; /* NE */ + case TOK_ULE: + return 0x90000000; /* LS */ + case TOK_UGT: + return 0x80000000; /* HI */ + case TOK_Nset: + return 0x40000000; /* MI */ + case TOK_Nclear: + return 0x50000000; /* PL */ + case TOK_LT: + return 0xB0000000; /* LT */ + case TOK_GE: + return 0xA0000000; /* GE */ + case TOK_LE: + return 0xD0000000; /* LE */ + case TOK_GT: + return 0xC0000000; /* GT */ + } + tcc_error("unexpected condition code"); + return 0xE0000000; /* AL */ +} + +static int negcc(int cc) +{ + switch (cc) { + case TOK_ULT: + return TOK_UGE; + case TOK_UGE: + return TOK_ULT; + case TOK_EQ: + return TOK_NE; + case TOK_NE: + return TOK_EQ; + case TOK_ULE: + return TOK_UGT; + case TOK_UGT: + return TOK_ULE; + case TOK_Nset: + return TOK_Nclear; + case TOK_Nclear: + return TOK_Nset; + case TOK_LT: + return TOK_GE; + case TOK_GE: + return TOK_LT; + case TOK_LE: + return TOK_GT; + case TOK_GT: + return TOK_LE; + } + tcc_error("unexpected condition code"); + return TOK_NE; +} + +/* load 'r' from value 'sv' */ +void load(int r, SValue* sv) +{ + int v, ft, fc, fr, sign; + uint32_t op; + SValue v1; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.ul; + + if (fc >= 0) + sign = 0; + else { + sign = 1; + fc = -fc; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + uint32_t base = 0xB; // fp + if (v == VT_LLOCAL) { + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = sv->c.ul; + load(base = 14 /* lr */, &v1); + fc = sign = 0; + v = VT_LOCAL; + } else if (v == VT_CONST) { + v1.type.t = VT_PTR; + v1.r = fr & ~VT_LVAL; + v1.c.ul = sv->c.ul; + v1.sym = sv->sym; + load(base = 14, &v1); + fc = sign = 0; + v = VT_LOCAL; + } else if (v < VT_CONST) { + base = intr(v); + fc = sign = 0; + v = VT_LOCAL; + } + if (v == VT_LOCAL) { + if (is_float(ft)) { + calcaddr(&base, &fc, &sign, 1020, 2); +#ifdef TCC_ARM_VFP + op = 0xED100A00; /* flds */ + if (!sign) + op |= 0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op |= 0x100; /* flds -> fldd */ + o(op | (vfpr(r) << 12) | (fc >> 2) | (base << 16)); +#else + op = 0xED100100; + if (!sign) + op |= 0x800000; +#if LDOUBLE_SIZE == 8 + if ((ft & VT_BTYPE) != VT_FLOAT) + op |= 0x8000; +#else + if ((ft & VT_BTYPE) == VT_DOUBLE) + op |= 0x8000; + else if ((ft & VT_BTYPE) == VT_LDOUBLE) + op |= 0x400000; +#endif + o(op | (fpr(r) << 12) | (fc >> 2) | (base << 16)); +#endif + } else if ((ft & (VT_BTYPE | VT_UNSIGNED)) == VT_BYTE || + (ft & VT_BTYPE) == VT_SHORT) { + calcaddr(&base, &fc, &sign, 255, 0); + op = 0xE1500090; + if ((ft & VT_BTYPE) == VT_SHORT) + op |= 0x20; + if ((ft & VT_UNSIGNED) == 0) + op |= 0x40; + if (!sign) + op |= 0x800000; + o(op | (intr(r) << 12) | (base << 16) | ((fc & 0xf0) << 4) | + (fc & 0xf)); + } else { + calcaddr(&base, &fc, &sign, 4095, 0); + op = 0xE5100000; + if (!sign) + op |= 0x800000; + if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) + op |= 0x400000; + o(op | (intr(r) << 12) | fc | (base << 16)); + } + return; + } + } else { + if (v == VT_CONST) { + op = stuff_const(0xE3A00000 | (intr(r) << 12), sv->c.ul); + if (fr & VT_SYM || !op) { + o(0xE59F0000 | (intr(r) << 12)); + o(0xEA000000); + if (fr & VT_SYM) + greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); + o(sv->c.ul); + } else + o(op); + return; + } else if (v == VT_LOCAL) { + op = stuff_const(0xE28B0000 | (intr(r) << 12), sv->c.ul); + if (fr & VT_SYM || !op) { + o(0xE59F0000 | (intr(r) << 12)); + o(0xEA000000); + if (fr & VT_SYM) // needed ? + greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); + o(sv->c.ul); + o(0xE08B0000 | (intr(r) << 12) | intr(r)); + } else + o(op); + return; + } else if (v == VT_CMP) { + o(mapcc(sv->c.ul) | 0x3A00001 | (intr(r) << 12)); + o(mapcc(negcc(sv->c.ul)) | 0x3A00000 | (intr(r) << 12)); + return; + } else if (v == VT_JMP || v == VT_JMPI) { + int t; + t = v & 1; + o(0xE3A00000 | (intr(r) << 12) | t); + o(0xEA000000); + gsym(sv->c.ul); + o(0xE3A00000 | (intr(r) << 12) | (t ^ 1)); + return; + } else if (v < VT_CONST) { + if (is_float(ft)) +#ifdef TCC_ARM_VFP + o(0xEEB00A40 | (vfpr(r) << 12) | vfpr(v) | + T2CPR(ft)); /* fcpyX */ +#else + o(0xEE008180 | (fpr(r) << 12) | fpr(v)); +#endif + else + o(0xE1A00000 | (intr(r) << 12) | intr(v)); + return; + } + } + tcc_error("load unimplemented!"); +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue* sv) +{ + SValue v1; + int v, ft, fc, fr, sign; + uint32_t op; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.ul; + + if (fc >= 0) + sign = 0; + else { + sign = 1; + fc = -fc; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL || fr == VT_LOCAL) { + uint32_t base = 0xb; + if (v < VT_CONST) { + base = intr(v); + v = VT_LOCAL; + fc = sign = 0; + } else if (v == VT_CONST) { + v1.type.t = ft; + v1.r = fr & ~VT_LVAL; + v1.c.ul = sv->c.ul; + v1.sym = sv->sym; + load(base = 14, &v1); + fc = sign = 0; + v = VT_LOCAL; + } + if (v == VT_LOCAL) { + if (is_float(ft)) { + calcaddr(&base, &fc, &sign, 1020, 2); +#ifdef TCC_ARM_VFP + op = 0xED000A00; /* fsts */ + if (!sign) + op |= 0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op |= 0x100; /* fsts -> fstd */ + o(op | (vfpr(r) << 12) | (fc >> 2) | (base << 16)); +#else + op = 0xED000100; + if (!sign) + op |= 0x800000; +#if LDOUBLE_SIZE == 8 + if ((ft & VT_BTYPE) != VT_FLOAT) + op |= 0x8000; +#else + if ((ft & VT_BTYPE) == VT_DOUBLE) + op |= 0x8000; + if ((ft & VT_BTYPE) == VT_LDOUBLE) + op |= 0x400000; +#endif + o(op | (fpr(r) << 12) | (fc >> 2) | (base << 16)); +#endif + return; + } else if ((ft & VT_BTYPE) == VT_SHORT) { + calcaddr(&base, &fc, &sign, 255, 0); + op = 0xE14000B0; + if (!sign) + op |= 0x800000; + o(op | (intr(r) << 12) | (base << 16) | ((fc & 0xf0) << 4) | + (fc & 0xf)); + } else { + calcaddr(&base, &fc, &sign, 4095, 0); + op = 0xE5000000; + if (!sign) + op |= 0x800000; + if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) + op |= 0x400000; + o(op | (intr(r) << 12) | fc | (base << 16)); + } + return; + } + } + tcc_error("store unimplemented"); +} + +static void gadd_sp(int val) +{ + stuff_const_harder(0xE28DD000, val); +} + +/* '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) { + uint32_t x; + /* constant case */ + x = encbranch(ind, ind + vtop->c.ul, 0); + if (x) { + if (vtop->r & VT_SYM) { + /* relocation case */ + greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24); + } else + put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24, + 0); + o(x | (is_jmp ? 0xE0000000 : 0xE1000000)); + } else { + if (!is_jmp) + o(0xE28FE004); // add lr,pc,#4 + o(0xE51FF004); // ldr pc,[pc,#-4] + if (vtop->r & VT_SYM) + greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32); + o(vtop->c.ul); + } + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + if (!is_jmp) + o(0xE1A0E00F); // mov lr,pc + o(0xE1A0F000 | intr(r)); // mov pc,r + } +} + +/* Return whether a structure is an homogeneous float aggregate or not. + The answer is true if all the elements of the structure are of the same + primitive float type and there is less than 4 elements. + + type: the type corresponding to the structure to be tested */ +static int is_hgen_float_aggr(CType* type) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + struct Sym* ref; + int btype, nb_fields = 0; + + ref = type->ref->next; + btype = ref->type.t & VT_BTYPE; + if (btype == VT_FLOAT || btype == VT_DOUBLE) { + for (; ref && btype == (ref->type.t & VT_BTYPE); + ref = ref->next, nb_fields++) + ; + return !ref && nb_fields <= 4; + } + } + return 0; +} + +struct avail_regs { + signed char + avail[3]; /* 3 holes max with only float and double alignments */ + int first_hole; /* first available hole */ + int last_hole; /* last available hole (none if equal to first_hole) */ + int first_free_reg; /* next free register in the sequence, hole excluded */ +}; + +#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 } + +/* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC + param) according to the rules described in the procedure call standard for + the ARM architecture (AAPCS). If found, the registers are assigned to this + VFP CPRC parameter. Registers are allocated in sequence unless a hole exists + and the parameter is a single float. + + avregs: opaque structure to keep track of available VFP co-processor regs + align: alignment contraints for the param, as returned by type_size() + size: size of the parameter, as returned by type_size() */ +int assign_vfpreg(struct avail_regs* avregs, int align, int size) +{ + int first_reg = 0; + + if (avregs->first_free_reg == -1) + return -1; + if (align >> 3) { /* double alignment */ + first_reg = avregs->first_free_reg; + /* alignment contraint not respected so use next reg and record hole */ + if (first_reg & 1) + avregs->avail[avregs->last_hole++] = first_reg++; + } else { /* no special alignment (float or array of float) */ + /* if single float and a hole is available, assign the param to it */ + if (size == 4 && avregs->first_hole != avregs->last_hole) + return avregs->avail[avregs->first_hole++]; + else + first_reg = avregs->first_free_reg; + } + if (first_reg + size / 4 <= 16) { + avregs->first_free_reg = first_reg + size / 4; + return first_reg; + } + avregs->first_free_reg = -1; + return -1; +} + +/* Returns whether all params need to be passed in core registers or not. + This is the case for function part of the runtime ABI. */ +int floats_in_core_regs(SValue* sval) +{ + if (!sval->sym) + return 0; + + switch (sval->sym->v) { + case TOK___floatundisf: + case TOK___floatundidf: + case TOK___fixunssfdi: + case TOK___fixunsdfdi: +#ifndef TCC_ARM_VFP + case TOK___fixunsxfdi: +#endif + case TOK___floatdisf: + case TOK___floatdidf: + case TOK___fixsfdi: + case TOK___fixdfdi: + return 1; + + default: + return 0; + } +} + +ST_FUNC int regargs_nregs(RegArgs* args) +{ + return *args; +} + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType* vt, int variadic, CType* ret, int* ret_align, + int* regsize, RegArgs* args) +{ +#ifdef TCC_ARM_EABI + int size, align; + size = type_size(vt, &align); + if (float_abi == ARM_HARD_FLOAT && !variadic && + (is_float(vt->t) || is_hgen_float_aggr(vt))) { + *ret_align = 8; + *regsize = 8; + ret->ref = NULL; + ret->t = VT_DOUBLE; + *args = (size + 7) >> 3; + } else if (size <= 4) { + *ret_align = 4; + *regsize = 4; + ret->ref = NULL; + ret->t = VT_INT; + *args = 1; + } else + *args = 0; +#else + *args = 0; +#endif + + return *args != 0; +} + +/* Parameters are classified according to how they are copied to their final + destination for the function call. Because the copying is performed class + after class according to the order in the union below, it is important that + some constraints about the order of the members of this union are respected: + - CORE_STRUCT_CLASS must come after STACK_CLASS; + - CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and + VFP_STRUCT_CLASS; + - VFP_STRUCT_CLASS must come after VFP_CLASS. + See the comment for the main loop in copy_params() for the reason. */ +enum reg_class { + STACK_CLASS = 0, + CORE_STRUCT_CLASS, + VFP_CLASS, + VFP_STRUCT_CLASS, + CORE_CLASS, + NB_CLASSES +}; + +struct param_plan { + int start; /* first reg or addr used depending on the class */ + int end; /* last reg used or next free addr depending on the class */ + SValue* sval; /* pointer to SValue on the value stack */ + struct param_plan* prev; /* previous element in this class */ +}; + +struct plan { + struct param_plan* pplans; /* array of all the param plans */ + struct param_plan* + clsplans[NB_CLASSES]; /* per class lists of param plans */ +}; + +#define add_param_plan(plan, pplan, class) \ + do { \ + pplan.prev = plan->clsplans[class]; \ + plan->pplans[plan##_nb] = pplan; \ + plan->clsplans[class] = &plan->pplans[plan##_nb++]; \ + } while (0) + +/* Assign parameters to registers and stack with alignment according to the + rules in the procedure call standard for the ARM architecture (AAPCS). + The overall assignment is recorded in an array of per parameter structures + called parameter plans. The parameter plans are also further organized in a + number of linked lists, one per class of parameter (see the comment for the + definition of union reg_class). + + nb_args: number of parameters of the function for which a call is generated + float_abi: float ABI in use for this function call + plan: the structure where the overall assignment is recorded + todo: a bitmap that record which core registers hold a parameter + + Returns the amount of stack space needed for parameter passing + + Note: this function allocated an array in plan->pplans with tcc_malloc. It + is the responsibility of the caller to free this array once used (ie not + before copy_params). */ +static int assign_regs(int nb_args, int float_abi, struct plan* plan, int* todo) +{ + int i, size, align; + int ncrn /* next core register number */, + nsaa /* next stacked argument address*/; + int plan_nb = 0; + struct param_plan pplan; + struct avail_regs avregs = AVAIL_REGS_INITIALIZER; + + ncrn = nsaa = 0; + *todo = 0; + plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans)); + memset(plan->clsplans, 0, sizeof(plan->clsplans)); + for (i = nb_args; i--;) { + int j, start_vfpreg = 0; + CType type = vtop[-i].type; + type.t &= ~VT_ARRAY; + size = type_size(&type, &align); + size = (size + 3) & ~3; + align = (align + 3) & ~3; + switch (vtop[-i].type.t & VT_BTYPE) { + case VT_STRUCT: + case VT_FLOAT: + case VT_DOUBLE: + case VT_LDOUBLE: + if (float_abi == ARM_HARD_FLOAT) { + int is_hfa = 0; /* Homogeneous float aggregate */ + + if (is_float(vtop[-i].type.t) || + (is_hfa = is_hgen_float_aggr(&vtop[-i].type))) { + int end_vfpreg; + + start_vfpreg = assign_vfpreg(&avregs, align, size); + end_vfpreg = start_vfpreg + ((size - 1) >> 2); + if (start_vfpreg >= 0) { + pplan = (struct param_plan){start_vfpreg, end_vfpreg, + &vtop[-i]}; + if (is_hfa) + add_param_plan(plan, pplan, VFP_STRUCT_CLASS); + else + add_param_plan(plan, pplan, VFP_CLASS); + continue; + } else + break; + } + } + ncrn = (ncrn + (align - 1) / 4) & ~((align / 4) - 1); + if (ncrn + size / 4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) { + /* The parameter is allocated both in core register and on + * stack. As + * such, it can be of either class: it would either be the last of + * CORE_STRUCT_CLASS or the first of STACK_CLASS. */ + for (j = ncrn; j < 4 && j < ncrn + size / 4; j++) + *todo |= (1 << j); + pplan = (struct param_plan){ncrn, j, &vtop[-i]}; + add_param_plan(plan, pplan, CORE_STRUCT_CLASS); + ncrn += size / 4; + if (ncrn > 4) + nsaa = (ncrn - 4) * 4; + } else { + ncrn = 4; + break; + } + continue; + default: + if (ncrn < 4) { + int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG; + + if (is_long) { + ncrn = (ncrn + 1) & -2; + if (ncrn == 4) + break; + } + pplan = (struct param_plan){ncrn, ncrn, &vtop[-i]}; + ncrn++; + if (is_long) + pplan.end = ncrn++; + add_param_plan(plan, pplan, CORE_CLASS); + continue; + } + } + nsaa = (nsaa + (align - 1)) & ~(align - 1); + pplan = (struct param_plan){nsaa, nsaa + size, &vtop[-i]}; + add_param_plan(plan, pplan, STACK_CLASS); + nsaa += size; /* size already rounded up before */ + } + return nsaa; +} + +#undef add_param_plan + +/* Copy parameters to their final destination (core reg, VFP reg or stack) for + function call. + + nb_args: number of parameters the function take + plan: the overall assignment plan for parameters + todo: a bitmap indicating what core reg will hold a parameter + + Returns the number of SValue added by this function on the value stack */ +static int copy_params(int nb_args, struct plan* plan, int todo) +{ + int size, align, r, i, nb_extra_sval = 0; + struct param_plan* pplan; + + /* Several constraints require parameters to be copied in a specific order: + - structures are copied to the stack before being loaded in a reg; + - floats loaded to an odd numbered VFP reg are first copied to the + preceding even numbered VFP reg and then moved to the next VFP reg. + + It is thus important that: + - structures assigned to core regs must be copied after parameters + assigned to the stack but before structures assigned to VFP regs + because a structure can lie partly in core registers and partly on + the stack; + - parameters assigned to the stack and all structures be copied before + parameters assigned to a core reg since copying a parameter to the + stack require using a core reg; + - parameters assigned to VFP regs be copied before structures assigned to + VFP regs as the copy might use an even numbered VFP reg that already + holds part of a structure. */ + for (i = 0; i < NB_CLASSES; i++) { + for (pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) { + vpushv(pplan->sval); + pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */ + switch (i) { + case STACK_CLASS: + case CORE_STRUCT_CLASS: + case VFP_STRUCT_CLASS: + if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) { + int padding = 0; + size = type_size(&pplan->sval->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + if (i == STACK_CLASS && pplan->prev) + padding = pplan->start - pplan->prev->end; + size += padding; /* Add padding if any */ + /* allocate the necessary size on stack */ + gadd_sp(-size); + /* generate structure store */ + r = get_reg(RC_INT); + o(0xE28D0000 | (intr(r) << 12) | + padding); /* add r, sp, padding */ + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); /* memcpy to current sp + potential padding */ + + /* Homogeneous float aggregate are loaded to VFP registers + immediately since there is no way of loading data in + multiple non consecutive VFP registers as what is done + for other structures (see the use of todo). */ + if (i == VFP_STRUCT_CLASS) { + int first = pplan->start, nb = pplan->end - first + 1; + /* vpop.32 {pplan->start, ..., pplan->end} */ + o(0xECBD0A00 | (first & 1) << 22 | (first >> 1) << 12 | + nb); + /* No need to write the register used to a SValue since + VFP regs cannot be used for gcall_or_jmp */ + } + } else { + if (is_float(pplan->sval->type.t)) { +#ifdef TCC_ARM_VFP + r = vfpr(gv(RC_FLOAT)) << 12; + if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else { + size = 8; + r |= 0x101; /* vpush.32 -> vpush.64 */ + } + o(0xED2D0A01 + r); /* vpush */ +#else + r = fpr(gv(RC_FLOAT)) << 12; + if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE) + size = 8; + else + size = LDOUBLE_SIZE; + + if (size == 12) + r |= 0x400000; + else if (size == 8) + r |= 0x8000; + + o(0xED2D0100 | r | (size >> 2)); /* some kind of vpush for FPA */ +#endif + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + size = 4; + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { + lexpand_nr(); + size = 8; + r = gv(RC_INT); + o(0xE52D0004 | (intr(r) << 12)); /* push r */ + vtop--; + } + r = gv(RC_INT); + o(0xE52D0004 | (intr(r) << 12)); /* push r */ + } + if (i == STACK_CLASS && pplan->prev) + gadd_sp(pplan->prev->end - + pplan->start); /* Add padding if any */ + } + break; + + case VFP_CLASS: + gv(regmask(TREG_F0 + (pplan->start >> 1))); + if (pplan->start & + 1) { /* Must be in upper part of double register */ + o(0xEEF00A40 | ((pplan->start >> 1) << 12) | + (pplan->start >> 1)); /* vmov.f32 s(n+1), sn */ + vtop->r = + VT_CONST; /* avoid being saved on stack by gv for next + float */ + } + break; + + case CORE_CLASS: + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { + lexpand_nr(); + gv(regmask(pplan->end)); + pplan->sval->r2 = vtop->r; + vtop--; + } + gv(regmask(pplan->start)); + /* Mark register as used so that gcall_or_jmp use another one + (regs >=4 are free as never used to pass parameters) */ + pplan->sval->r = vtop->r; + break; + } + vtop--; + } + } + + /* Manually free remaining registers since next parameters are loaded + * manually, without the help of gv(int). */ + save_regs(nb_args); + + if (todo) { + o(0xE8BD0000 | todo); /* pop {todo} */ + for (pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; + pplan = pplan->prev) { + int r; + pplan->sval->r = pplan->start; + /* An SValue can only pin 2 registers at best (r and r2) but a + structure can occupy more than 2 registers. Thus, we need to + push on the value stack some fake parameter to have on SValue + for each registers used by a structure (r2 is not used). */ + for (r = pplan->start + 1; r <= pplan->end; r++) { + if (todo & (1 << r)) { + nb_extra_sval++; + vpushi(0); + vtop->r = r; + } + } + } + } + return nb_extra_sval; +} + +/* 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 r, args_size; + int def_float_abi = float_abi; + int todo; + struct plan plan; + +#ifdef TCC_ARM_EABI + int variadic; + + if (float_abi == ARM_HARD_FLOAT) { + variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); + if (variadic || floats_in_core_regs(&vtop[-nb_args])) + float_abi = ARM_SOFTFP_FLOAT; + } +#endif + /* 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. */ + r = vtop->r & VT_VALMASK; + if (r == VT_CMP || (r & ~1) == VT_JMP) + gv(RC_INT); + + args_size = assign_regs(nb_args, float_abi, &plan, &todo); + +#ifdef TCC_ARM_EABI + if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ + args_size = (args_size + 7) & ~7; + o(0xE24DD004); /* sub sp, sp, #4 */ + } +#endif + + nb_args += copy_params(nb_args, &plan, todo); + tcc_free(plan.pplans); + + /* Move fct SValue on top as required by gcall_or_jmp */ + vrotb(nb_args + 1); + gcall_or_jmp(0); + if (args_size) + gadd_sp(args_size); /* pop all parameters passed on the stack */ +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + if (float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) { + if ((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { + o(0xEE000A10); /*vmov s0, r0 */ + } else { + o(0xEE000B10); /* vmov.32 d0[0], r0 */ + o(0xEE201B10); /* vmov.32 d0[1], r1 */ + } + } +#endif + vtop -= nb_args + 1; /* Pop all params and fct address from value stack */ + leaffunc = + 0; /* we are calling a function, so we aren't in a leaf function */ + float_abi = def_float_abi; +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType* func_type) +{ + Sym* sym, *sym2; + int n, nf, size, align, rs, struct_ret = 0; + int addr, pn, sn; /* pn=core, sn=stack */ + CType ret_type; + RegArgs dummy; + +#ifdef TCC_ARM_EABI + struct avail_regs avregs = AVAIL_REGS_INITIALIZER; +#endif + + sym = func_type->ref; + func_vt = sym->type; + func_var = (func_type->ref->c == FUNC_ELLIPSIS); + + n = nf = 0; + if ((func_vt.t & VT_BTYPE) == VT_STRUCT && + !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs, &dummy)) { + n++; + struct_ret = 1; + func_vc = 12; /* Offset from fp of the place to store the result */ + } + for (sym2 = sym->next; sym2 && (n < 4 || nf < 16); sym2 = sym2->next) { + size = type_size(&sym2->type, &align); +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT && !func_var && + (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) { + int tmpnf = assign_vfpreg(&avregs, align, size); + tmpnf += (size + 3) / 4; + nf = (tmpnf > nf) ? tmpnf : nf; + } else +#endif + if (n < 4) + n += (size + 3) / 4; + } + o(0xE1A0C00D); /* mov ip,sp */ + if (func_var) + n = 4; + if (n) { + if (n > 4) + n = 4; +#ifdef TCC_ARM_EABI + n = (n + 1) & -2; +#endif + o(0xE92D0000 | ((1 << n) - 1)); /* save r0-r4 on stack if needed */ + } + if (nf) { + if (nf > 16) + nf = 16; + nf = (nf + 1) & -2; /* nf => HARDFLOAT => EABI */ + o(0xED2D0A00 | nf); /* save s0-s15 on stack if needed */ + } + o(0xE92D5800); /* save fp, ip, lr */ + o(0xE1A0B00D); /* mov fp, sp */ + func_sub_sp_offset = ind; + o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */ + +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT) { + func_vc += nf * 4; + avregs = AVAIL_REGS_INITIALIZER; + } +#endif + pn = struct_ret, sn = 0; + while ((sym = sym->next)) { + CType* type; + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) >> 2; + align = (align + 3) & ~3; +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT && !func_var && + (is_float(sym->type.t) || is_hgen_float_aggr(&sym->type))) { + int fpn = assign_vfpreg(&avregs, align, size << 2); + if (fpn >= 0) + addr = fpn * 4; + else + goto from_stack; + } else +#endif + if (pn < 4) { +#ifdef TCC_ARM_EABI + pn = (pn + (align - 1) / 4) & -(align / 4); +#endif + addr = (nf + pn) * 4; + pn += size; + if (!sn && pn > 4) + sn = (pn - 4); + } else { +#ifdef TCC_ARM_EABI + from_stack: + sn = (sn + (align - 1) / 4) & -(align / 4); +#endif + addr = (n + nf + sn) * 4; + sn += size; + } + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), + addr + 12); + } + last_itod_magic = 0; + leaffunc = 1; + loc = 0; +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + uint32_t x; + int diff; +/* Copy float return value to core register if base standard is used and + float computation is made with VFP */ +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) { + if ((func_vt.t & VT_BTYPE) == VT_FLOAT) + o(0xEE100A10); /* fmrs r0, s0 */ + else { + o(0xEE100B10); /* fmrdl r0, d0 */ + o(0xEE301B10); /* fmrdh r1, d0 */ + } + } +#endif + o(0xE89BA800); /* restore fp, sp, pc */ + diff = (-loc + 3) & -4; +#ifdef TCC_ARM_EABI + if (!leaffunc) + diff = ((diff + 11) & -8) - 4; +#endif + if (diff > 0) { + x = stuff_const(0xE24BD000, diff); /* sub sp,fp,# */ + if (x) + *(uint32_t*)(cur_text_section->data + func_sub_sp_offset) = x; + else { + int addr; + addr = ind; + o(0xE59FC004); /* ldr ip,[pc+4] */ + o(0xE04BD00C); /* sub sp,fp,ip */ + o(0xE1A0F00E); /* mov pc,lr */ + o(diff); + *(uint32_t*)(cur_text_section->data + func_sub_sp_offset) = + 0xE1000000 | encbranch(func_sub_sp_offset, addr, 1); + } + } +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + int r; + r = ind; + o(0xE0000000 | encbranch(r, t, 1)); + return r; +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + gjmp(a); +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, r; + uint32_t op; + v = vtop->r & VT_VALMASK; + r = ind; + if (v == VT_CMP) { + op = mapcc(inv ? negcc(vtop->c.i) : vtop->c.i); + op |= encbranch(r, t, 1); + o(op); + t = r; + } else if (v == VT_JMP || v == VT_JMPI) { + if ((v & 1) == inv) { + if (!vtop->c.i) + vtop->c.i = t; + else { + uint32_t* x; + int p, lp; + if (t) { + p = vtop->c.i; + do { + p = decbranch(lp = p); + } while (p); + x = (uint32_t*)(cur_text_section->data + lp); + *x &= 0xff000000; + *x |= encbranch(lp, t, 1); + } + t = vtop->c.i; + } + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int c, func = 0; + uint32_t opc = 0, r, fr; + unsigned short retreg = REG_IRET; + + c = 0; + switch (op) { + case '+': + opc = 0x8; + c = 1; + break; + case TOK_ADDC1: /* add with carry generation */ + opc = 0x9; + c = 1; + break; + case '-': + opc = 0x4; + c = 1; + break; + case TOK_SUBC1: /* sub with carry generation */ + opc = 0x5; + c = 1; + break; + case TOK_ADDC2: /* add with carry use */ + opc = 0xA; + c = 1; + break; + case TOK_SUBC2: /* sub with carry use */ + opc = 0xC; + c = 1; + break; + case '&': + opc = 0x0; + c = 1; + break; + case '^': + opc = 0x2; + c = 1; + break; + case '|': + opc = 0x18; + c = 1; + break; + case '*': + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + o(0xE0000090 | (intr(r) << 16) | (intr(r) << 8) | intr(fr)); + return; + case TOK_SHL: + opc = 0; + c = 2; + break; + case TOK_SHR: + opc = 1; + c = 2; + break; + case TOK_SAR: + opc = 2; + c = 2; + break; + case '/': + case TOK_PDIV: + func = TOK___divsi3; + c = 3; + break; + case TOK_UDIV: + func = TOK___udivsi3; + c = 3; + break; + case '%': +#ifdef TCC_ARM_EABI + func = TOK___aeabi_idivmod; + retreg = REG_LRET; +#else + func = TOK___modsi3; +#endif + c = 3; + break; + case TOK_UMOD: +#ifdef TCC_ARM_EABI + func = TOK___aeabi_uidivmod; + retreg = REG_LRET; +#else + func = TOK___umodsi3; +#endif + c = 3; + break; + case TOK_UMULL: + gv2(RC_INT, RC_INT); + r = intr(vtop[-1].r2 = get_reg(RC_INT)); + c = vtop[-1].r; + vtop[-1].r = get_reg_ex(RC_INT, regmask(c)); + vtop--; + o(0xE0800090 | (r << 16) | (intr(vtop->r) << 12) | (intr(c) << 8) | + intr(vtop[1].r)); + return; + default: + opc = 0x15; + c = 1; + break; + } + switch (c) { + case 1: + if ((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + if (opc == 4 || opc == 5 || opc == 0xc) { + vswap(); + opc |= 2; // sub -> rsb + } + } + if ((vtop->r & VT_VALMASK) == VT_CMP || + (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) + gv(RC_INT); + vswap(); + c = intr(gv(RC_INT)); + vswap(); + opc = 0xE0000000 | (opc << 20) | (c << 16); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + uint32_t x; + x = stuff_const(opc | 0x2000000, vtop->c.i); + if (x) { + r = intr(vtop[-1].r = get_reg_ex(RC_INT, regmask(vtop[-1].r))); + o(x | (r << 12)); + goto done; + } + } + fr = intr(gv(RC_INT)); + r = intr(vtop[-1].r = + get_reg_ex(RC_INT, two2mask(vtop->r, vtop[-1].r))); + o(opc | (r << 12) | fr); + done: + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) { + vtop->r = VT_CMP; + vtop->c.i = op; + } + break; + case 2: + opc = 0xE1A00000 | (opc << 5); + if ((vtop->r & VT_VALMASK) == VT_CMP || + (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) + gv(RC_INT); + vswap(); + r = intr(gv(RC_INT)); + vswap(); + opc |= r; + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + fr = intr(vtop[-1].r = get_reg_ex(RC_INT, regmask(vtop[-1].r))); + c = vtop->c.i & 0x1f; + o(opc | (c << 7) | (fr << 12)); + } else { + fr = intr(gv(RC_INT)); + c = intr(vtop[-1].r = + get_reg_ex(RC_INT, two2mask(vtop->r, vtop[-1].r))); + o(opc | (c << 12) | (fr << 8) | 0x10); + } + vtop--; + break; + case 3: + vpush_global_sym(&func_old_type, func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = retreg; + break; + default: + tcc_error("gen_opi %i unimplemented!", op); + } +} + +#ifdef TCC_ARM_VFP +static int is_zero(int i) +{ + if ((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop[i].type.t == VT_FLOAT) + return (vtop[i].c.f == 0.f); + else if (vtop[i].type.t == VT_DOUBLE) + return (vtop[i].c.d == 0.0); + return (vtop[i].c.ld == 0.l); +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + * two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + uint32_t x; + int fneg = 0, r; + x = 0xEE000A00 | T2CPR(vtop->type.t); + switch (op) { + case '+': + if (is_zero(-1)) + vswap(); + if (is_zero(0)) { + vtop--; + return; + } + x |= 0x300000; + break; + case '-': + x |= 0x300040; + if (is_zero(0)) { + vtop--; + return; + } + if (is_zero(-1)) { + x |= 0x810000; /* fsubX -> fnegX */ + vswap(); + vtop--; + fneg = 1; + } + break; + case '*': + x |= 0x200000; + break; + case '/': + x |= 0x800000; + break; + default: + if (op < TOK_ULT || op > TOK_GT) { + tcc_error("unknown fp op %x!", op); + return; + } + if (is_zero(-1)) { + vswap(); + switch(op) { + case TOK_LT: op = TOK_GT; break; + case TOK_GE: op = TOK_ULE; break; + case TOK_LE: op = TOK_GE; break; + case TOK_GT: op = TOK_ULT; break; + } + } + x |= 0xB40040; /* fcmpX */ + if (op != TOK_EQ && op != TOK_NE) + x |= 0x80; /* fcmpX -> fcmpeX */ + if (is_zero(0)) { + vtop--; + o(x | 0x10000 | + (vfpr(gv(RC_FLOAT)) << 12)); /* fcmp(e)X -> fcmp(e)zX */ + } else { + x |= vfpr(gv(RC_FLOAT)); + vswap(); + o(x | (vfpr(gv(RC_FLOAT)) << 12)); + vtop--; + } + o(0xEEF1FA10); /* fmstat */ + + switch(op) { + case TOK_LE: op = TOK_ULE; break; + case TOK_LT: op = TOK_ULT; break; + case TOK_UGE: op = TOK_GE; break; + case TOK_UGT: op = TOK_GT; break; + } + + vtop->r = VT_CMP; + vtop->c.i = op; + return; + } + r = gv(RC_FLOAT); + x |= vfpr(r); + r = regmask(r); + if (!fneg) { + int r2; + vswap(); + r2 = gv(RC_FLOAT); + x |= vfpr(r2) << 16; + r |= regmask(r2); + } + vtop->r = get_reg_ex(RC_FLOAT, r); + if (!fneg) + vtop--; + o(x | (vfpr(vtop->r) << 12)); +} + +#else +static uint32_t is_fconst() +{ + long double f; + uint32_t r; + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop->type.t == VT_FLOAT) + f = vtop->c.f; + else if (vtop->type.t == VT_DOUBLE) + f = vtop->c.d; + else + f = vtop->c.ld; + if (!ieee_finite(f)) + return 0; + r = 0x8; + if (f < 0.0) { + r = 0x18; + f = -f; + } + if (f == 0.0) + return r; + if (f == 1.0) + return r | 1; + if (f == 2.0) + return r | 2; + if (f == 3.0) + return r | 3; + if (f == 4.0) + return r | 4; + if (f == 5.0) + return r | 5; + if (f == 0.5) + return r | 6; + if (f == 10.0) + return r | 7; + return 0; +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + uint32_t x, r, r2, c1, c2; + // fputs("gen_opf\n",stderr); + vswap(); + c1 = is_fconst(); + vswap(); + c2 = is_fconst(); + x = 0xEE000100; +#if LDOUBLE_SIZE == 8 + if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) + x |= 0x80; +#else + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + x |= 0x80; + else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) + x |= 0x80000; +#endif + switch (op) { + case '+': + if (!c2) { + vswap(); + c2 = c1; + } + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + if (c2) { + if (c2 > 0xf) + x |= 0x200000; // suf + r2 = c2 & 0xf; + } else { + r2 = fpr(gv(RC_FLOAT)); + } + break; + case '-': + if (c2) { + if (c2 <= 0xf) + x |= 0x200000; // suf + r2 = c2 & 0xf; + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + } else if (c1 && c1 <= 0xf) { + x |= 0x300000; // rsf + r2 = c1; + r = fpr(gv(RC_FLOAT)); + vswap(); + } else { + x |= 0x200000; // suf + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + r2 = fpr(gv(RC_FLOAT)); + } + break; + case '*': + if (!c2 || c2 > 0xf) { + vswap(); + c2 = c1; + } + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + if (c2 && c2 <= 0xf) + r2 = c2; + else + r2 = fpr(gv(RC_FLOAT)); + x |= 0x100000; // muf + break; + case '/': + if (c2 && c2 <= 0xf) { + x |= 0x400000; // dvf + r2 = c2; + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + } else if (c1 && c1 <= 0xf) { + x |= 0x500000; // rdf + r2 = c1; + r = fpr(gv(RC_FLOAT)); + vswap(); + } else { + x |= 0x400000; // dvf + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + r2 = fpr(gv(RC_FLOAT)); + } + break; + default: + if (op >= TOK_ULT && op <= TOK_GT) { + x |= 0xd0f110; // cmfe + /* bug (intention?) in Linux FPU emulator + doesn't set carry if equal */ + switch (op) { + case TOK_ULT: + case TOK_UGE: + case TOK_ULE: + case TOK_UGT: + tcc_error("unsigned comparison on floats?"); + break; + case TOK_LT: + op = TOK_Nset; + break; + case TOK_LE: + op = TOK_ULE; /* correct in unordered case only if AC bit in + FPSR set */ + break; + case TOK_EQ: + case TOK_NE: + x &= ~0x400000; // cmfe -> cmf + break; + } + if (c1 && !c2) { + c2 = c1; + vswap(); + switch (op) { + case TOK_Nset: + op = TOK_GT; + break; + case TOK_GE: + op = TOK_ULE; + break; + case TOK_ULE: + op = TOK_GE; + break; + case TOK_GT: + op = TOK_Nset; + break; + } + } + vswap(); + r = fpr(gv(RC_FLOAT)); + vswap(); + if (c2) { + if (c2 > 0xf) + x |= 0x200000; + r2 = c2 & 0xf; + } else { + r2 = fpr(gv(RC_FLOAT)); + } + vtop[-1].r = VT_CMP; + vtop[-1].c.i = op; + } else { + tcc_error("unknown fp op %x!", op); + return; + } + } + if (vtop[-1].r == VT_CMP) + c1 = 15; + else { + c1 = vtop->r; + if (r2 & 0x8) + c1 = vtop[-1].r; + vtop[-1].r = get_reg_ex(RC_FLOAT, two2mask(vtop[-1].r, c1)); + c1 = fpr(vtop[-1].r); + } + vtop--; + o(x | (r << 16) | (c1 << 12) | r2); +} +#endif + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +ST_FUNC void gen_cvt_itof1(int t) +{ + uint32_t r, r2; + int bt; + bt = vtop->type.t & VT_BTYPE; + if (bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { +#ifndef TCC_ARM_VFP + uint32_t dsize = 0; +#endif + r = intr(gv(RC_INT)); +#ifdef TCC_ARM_VFP + r2 = vfpr(vtop->r = get_reg(RC_FLOAT)); + o(0xEE000A10 | (r << 12) | (r2 << 16)); /* fmsr */ + r2 |= r2 << 12; + if (!(vtop->type.t & VT_UNSIGNED)) + r2 |= 0x80; /* fuitoX -> fsituX */ + o(0xEEB80A40 | r2 | T2CPR(t)); /* fYitoX*/ +#else + r2 = fpr(vtop->r = get_reg(RC_FLOAT)); + if ((t & VT_BTYPE) != VT_FLOAT) + dsize = 0x80; /* flts -> fltd */ + o(0xEE000110 | dsize | (r2 << 16) | (r << 12)); /* flts */ + if ((vtop->type.t & (VT_UNSIGNED | VT_BTYPE)) == + (VT_UNSIGNED | VT_INT)) { + uint32_t off = 0; + o(0xE3500000 | (r << 12)); /* cmp */ + r = fpr(get_reg(RC_FLOAT)); + if (last_itod_magic) { + off = ind + 8 - last_itod_magic; + off /= 4; + if (off > 255) + off = 0; + } + o(0xBD1F0100 | (r << 12) | off); /* ldflts */ + if (!off) { + o(0xEA000000); /* b */ + last_itod_magic = ind; + o(0x4F800000); /* 4294967296.0f */ + } + o(0xBE000100 | dsize | (r2 << 16) | (r2 << 12) | r); /* adflt */ + } +#endif + return; + } else if (bt == VT_LLONG) { + int func; + CType* func_type = 0; + if ((t & VT_BTYPE) == VT_FLOAT) { + func_type = &func_float_type; + if (vtop->type.t & VT_UNSIGNED) + func = TOK___floatundisf; + else + func = TOK___floatdisf; +#if LDOUBLE_SIZE != 8 + } else if ((t & VT_BTYPE) == VT_LDOUBLE) { + func_type = &func_ldouble_type; + if (vtop->type.t & VT_UNSIGNED) + func = TOK___floatundixf; + else + func = TOK___floatdixf; + } else if ((t & VT_BTYPE) == VT_DOUBLE) { +#else + } else if ((t & VT_BTYPE) == VT_DOUBLE || + (t & VT_BTYPE) == VT_LDOUBLE) { +#endif + func_type = &func_double_type; + if (vtop->type.t & VT_UNSIGNED) + func = TOK___floatundidf; + else + func = TOK___floatdidf; + } + if (func_type) { + vpush_global_sym(func_type, func); + vswap(); + gfunc_call(1); + vpushi(0); + vtop->r = TREG_F0; + return; + } + } + tcc_error("unimplemented gen_cvt_itof %x!", vtop->type.t); +} + +/* convert fp to int 't' type */ +void gen_cvt_ftoi(int t) +{ + uint32_t r, r2; + int u, func = 0; + u = t & VT_UNSIGNED; + t &= VT_BTYPE; + r2 = vtop->type.t & VT_BTYPE; + if (t == VT_INT) { +#ifdef TCC_ARM_VFP + r = vfpr(gv(RC_FLOAT)); + u = u ? 0 : 0x10000; + o(0xEEBC0AC0 | (r << 12) | r | T2CPR(r2) | u); /* ftoXizY */ + r2 = intr(vtop->r = get_reg(RC_INT)); + o(0xEE100A10 | (r << 16) | (r2 << 12)); + return; +#else + if (u) { + if (r2 == VT_FLOAT) + func = TOK___fixunssfsi; +#if LDOUBLE_SIZE != 8 + else if (r2 == VT_LDOUBLE) + func = TOK___fixunsxfsi; + else if (r2 == VT_DOUBLE) +#else + else if (r2 == VT_LDOUBLE || r2 == VT_DOUBLE) +#endif + func = TOK___fixunsdfsi; + } else { + r = fpr(gv(RC_FLOAT)); + r2 = intr(vtop->r = get_reg(RC_INT)); + o(0xEE100170 | (r2 << 12) | r); + return; + } +#endif + } else if (t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 + if (r2 == VT_FLOAT) + func = TOK___fixsfdi; +#if LDOUBLE_SIZE != 8 + else if (r2 == VT_LDOUBLE) + func = TOK___fixxfdi; + else if (r2 == VT_DOUBLE) +#else + else if (r2 == VT_LDOUBLE || r2 == VT_DOUBLE) +#endif + func = TOK___fixdfdi; + } + if (func) { + vpush_global_sym(&func_old_type, func); + vswap(); + gfunc_call(1); + vpushi(0); + if (t == VT_LLONG) + vtop->r2 = REG_LRET; + vtop->r = REG_IRET; + return; + } + tcc_error("unimplemented gen_cvt_ftoi!"); +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ +#ifdef TCC_ARM_VFP + if (((vtop->type.t & VT_BTYPE) == VT_FLOAT) != + ((t & VT_BTYPE) == VT_FLOAT)) { + uint32_t r = vfpr(gv(RC_FLOAT)); + o(0xEEB70AC0 | (r << 12) | r | T2CPR(vtop->type.t)); + } +#else + /* all we have to do on i386 and FPA ARM is to put the float in a register + */ + gv(RC_FLOAT); +#endif +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* Save the stack pointer onto the stack and return the location of its address + */ +ST_FUNC void gen_vla_sp_save(int addr) +{ + tcc_error("variable length arrays unsupported for this target"); +} + +/* Restore the SP from a location on the stack */ +ST_FUNC void gen_vla_sp_restore(int addr) +{ + tcc_error("variable length arrays unsupported for this target"); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack + */ +ST_FUNC void gen_vla_alloc(CType* type, int align) +{ + tcc_error("variable length arrays unsupported for this target"); +} + +/* end of ARM code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/src/arm/arm64-gen.c b/src/arm/arm64-gen.c new file mode 100644 index 0000000..fe3d7aa --- /dev/null +++ b/src/arm/arm64-gen.c @@ -0,0 +1,1846 @@ +/* + * A64 code generator for TCC + * + * Copyright (c) 2014-2015 Edmund Grimley Evans + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + */ + +#ifdef TARGET_DEFS_ONLY + +// Number of registers available to allocator: +#define NB_REGS 28 // x0-x18, x30, v0-v7 + +typedef int RegArgs; + +#define TREG_R(x) (x) // x = 0..18 +#define TREG_R30 19 +#define TREG_F(x) (x + 20) // x = 0..7 + +// Register classes sorted from more general to more precise: +#define RC_INT (1 << 0) +#define RC_FLOAT (1 << 1) +#define RC_R(x) (1 << (2 + (x))) // x = 0..18 +#define RC_R30 (1 << 21) +#define RC_F(x) (1 << (22 + (x))) // x = 0..7 + +#define RC_IRET (RC_R(0)) // int return register class +#define RC_FRET (RC_F(0)) // float return register class + +#define REG_IRET (TREG_R(0)) // int return register number +#define REG_FRET (TREG_F(0)) // float return register number + +#define PTR_SIZE 8 + +#define LDOUBLE_SIZE 16 +#define LDOUBLE_ALIGN 16 + +#define MAX_ALIGN 16 + +#define CHAR_IS_UNSIGNED + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_AARCH64 + +#define R_DATA_32 R_AARCH64_ABS32 +#define R_DATA_PTR R_AARCH64_ABS64 +#define R_JMP_SLOT R_AARCH64_JUMP_SLOT +#define R_COPY R_AARCH64_COPY + +#define ELF_START_ADDR 0x00400000 +#define ELF_PAGE_SIZE 0x1000 + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#include "../tcc.h" +#include <assert.h> + +ST_DATA const int reg_classes[NB_REGS] = { + RC_INT | RC_R(0), + RC_INT | RC_R(1), + RC_INT | RC_R(2), + RC_INT | RC_R(3), + RC_INT | RC_R(4), + RC_INT | RC_R(5), + RC_INT | RC_R(6), + RC_INT | RC_R(7), + RC_INT | RC_R(8), + RC_INT | RC_R(9), + RC_INT | RC_R(10), + RC_INT | RC_R(11), + RC_INT | RC_R(12), + RC_INT | RC_R(13), + RC_INT | RC_R(14), + RC_INT | RC_R(15), + RC_INT | RC_R(16), + RC_INT | RC_R(17), + RC_INT | RC_R(18), + RC_R30, // not in RC_INT as we make special use of x30 + RC_FLOAT | RC_F(0), + RC_FLOAT | RC_F(1), + RC_FLOAT | RC_F(2), + RC_FLOAT | RC_F(3), + RC_FLOAT | RC_F(4), + RC_FLOAT | RC_F(5), + RC_FLOAT | RC_F(6), + RC_FLOAT | RC_F(7) +}; + +#define IS_FREG(x) ((x) >= TREG_F(0)) + +static uint32_t intr(int r) +{ + assert(TREG_R(0) <= r && r <= TREG_R30); + return r < TREG_R30 ? r : 30; +} + +static uint32_t fltr(int r) +{ + assert(TREG_F(0) <= r && r <= TREG_F(7)); + return r - TREG_F(0); +} + +// Add an instruction to text section: +ST_FUNC void o(unsigned int c) +{ + int ind1 = ind + 4; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + *(uint32_t *)(cur_text_section->data + ind) = c; + ind = ind1; +} + +static int arm64_encode_bimm64(uint64_t x) +{ + int neg = x & 1; + int rep, pos, len; + + if (neg) + x = ~x; + if (!x) + return -1; + + if (x >> 2 == (x & (((uint64_t)1 << (64 - 2)) - 1))) + rep = 2, x &= ((uint64_t)1 << 2) - 1; + else if (x >> 4 == (x & (((uint64_t)1 << (64 - 4)) - 1))) + rep = 4, x &= ((uint64_t)1 << 4) - 1; + else if (x >> 8 == (x & (((uint64_t)1 << (64 - 8)) - 1))) + rep = 8, x &= ((uint64_t)1 << 8) - 1; + else if (x >> 16 == (x & (((uint64_t)1 << (64 - 16)) - 1))) + rep = 16, x &= ((uint64_t)1 << 16) - 1; + else if (x >> 32 == (x & (((uint64_t)1 << (64 - 32)) - 1))) + rep = 32, x &= ((uint64_t)1 << 32) - 1; + else + rep = 64; + + pos = 0; + if (!(x & (((uint64_t)1 << 32) - 1))) x >>= 32, pos += 32; + if (!(x & (((uint64_t)1 << 16) - 1))) x >>= 16, pos += 16; + if (!(x & (((uint64_t)1 << 8) - 1))) x >>= 8, pos += 8; + if (!(x & (((uint64_t)1 << 4) - 1))) x >>= 4, pos += 4; + if (!(x & (((uint64_t)1 << 2) - 1))) x >>= 2, pos += 2; + if (!(x & (((uint64_t)1 << 1) - 1))) x >>= 1, pos += 1; + + len = 0; + if (!(~x & (((uint64_t)1 << 32) - 1))) x >>= 32, len += 32; + if (!(~x & (((uint64_t)1 << 16) - 1))) x >>= 16, len += 16; + if (!(~x & (((uint64_t)1 << 8) - 1))) x >>= 8, len += 8; + if (!(~x & (((uint64_t)1 << 4) - 1))) x >>= 4, len += 4; + if (!(~x & (((uint64_t)1 << 2) - 1))) x >>= 2, len += 2; + if (!(~x & (((uint64_t)1 << 1) - 1))) x >>= 1, len += 1; + + if (x) + return -1; + if (neg) { + pos = (pos + len) & (rep - 1); + len = rep - len; + } + return ((0x1000 & rep << 6) | (((rep - 1) ^ 31) << 1 & 63) | + ((rep - pos) & (rep - 1)) << 6 | (len - 1)); +} + +static uint32_t arm64_movi(int r, uint64_t x) +{ + uint64_t m = 0xffff; + int e; + if (!(x & ~m)) + return 0x52800000 | r | x << 5; // movz w(r),#(x) + if (!(x & ~(m << 16))) + return 0x52a00000 | r | x >> 11; // movz w(r),#(x >> 16),lsl #16 + if (!(x & ~(m << 32))) + return 0xd2c00000 | r | x >> 27; // movz x(r),#(x >> 32),lsl #32 + if (!(x & ~(m << 48))) + return 0xd2e00000 | r | x >> 43; // movz x(r),#(x >> 48),lsl #48 + if ((x & ~m) == m << 16) + return (0x12800000 | r | + (~x << 5 & 0x1fffe0)); // movn w(r),#(~x) + if ((x & ~(m << 16)) == m) + return (0x12a00000 | r | + (~x >> 11 & 0x1fffe0)); // movn w(r),#(~x >> 16),lsl #16 + if (!~(x | m)) + return (0x92800000 | r | + (~x << 5 & 0x1fffe0)); // movn x(r),#(~x) + if (!~(x | m << 16)) + return (0x92a00000 | r | + (~x >> 11 & 0x1fffe0)); // movn x(r),#(~x >> 16),lsl #16 + if (!~(x | m << 32)) + return (0x92c00000 | r | + (~x >> 27 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32 + if (!~(x | m << 48)) + return (0x92e00000 | r | + (~x >> 43 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32 + if (!(x >> 32) && (e = arm64_encode_bimm64(x | x << 32)) >= 0) + return 0x320003e0 | r | (uint32_t)e << 10; // movi w(r),#(x) + if ((e = arm64_encode_bimm64(x)) >= 0) + return 0xb20003e0 | r | (uint32_t)e << 10; // movi x(r),#(x) + return 0; +} + +static void arm64_movimm(int r, uint64_t x) +{ + uint32_t i; + if ((i = arm64_movi(r, x))) + o(i); // a single MOV + else { + // MOVZ/MOVN and 1-3 MOVKs + int z = 0, m = 0; + uint32_t mov1 = 0xd2800000; // movz + uint64_t x1 = x; + for (i = 0; i < 64; i += 16) { + z += !(x >> i & 0xffff); + m += !(~x >> i & 0xffff); + } + if (m > z) { + x1 = ~x; + mov1 = 0x92800000; // movn + } + for (i = 0; i < 64; i += 16) + if (x1 >> i & 0xffff) { + o(mov1 | r | (x1 >> i & 0xffff) << 5 | i << 17); + // movz/movn x(r),#(*),lsl #(i) + break; + } + for (i += 16; i < 64; i += 16) + if (x1 >> i & 0xffff) + o(0xf2800000 | r | (x >> i & 0xffff) << 5 | i << 17); + // movk x(r),#(*),lsl #(i) + } +} + +// Patch all branches in list pointed to by t to branch to a: +ST_FUNC void gsym_addr(int t_, int a_) +{ + uint32_t t = t_; + uint32_t a = a_; + while (t) { + uint32_t *ptr = (uint32_t *)(cur_text_section->data + t); + uint32_t next = *ptr; + if (a - t + 0x8000000 >= 0x10000000) + tcc_error("branch out of range"); + *ptr = (a - t == 4 ? 0xd503201f : // nop + 0x14000000 | ((a - t) >> 2 & 0x3ffffff)); // b + t = next; + } +} + +// Patch all branches in list pointed to by t to branch to current location: +ST_FUNC void gsym(int t) +{ + gsym_addr(t, ind); +} + +static int arm64_type_size(int t) +{ + switch (t & VT_BTYPE) { + case VT_INT: return 2; + case VT_BYTE: return 0; + case VT_SHORT: return 1; + case VT_PTR: return 3; + case VT_ENUM: return 2; + case VT_FUNC: return 3; + case VT_FLOAT: return 2; + case VT_DOUBLE: return 3; + case VT_LDOUBLE: return 4; + case VT_BOOL: return 0; + case VT_LLONG: return 3; + } + assert(0); + return 0; +} + +static void arm64_spoff(int reg, uint64_t off) +{ + uint32_t sub = off >> 63; + if (sub) + off = -off; + if (off < 4096) + o(0x910003e0 | sub << 30 | reg | off << 10); + // (add|sub) x(reg),sp,#(off) + else { + arm64_movimm(30, off); // use x30 for offset + o(0x8b3e63e0 | sub << 30 | reg); // (add|sub) x(reg),sp,x30 + } +} + +static void arm64_ldrx(int sg, int sz, int dst, int bas, uint64_t off) +{ + if (sz >= 2) + sg = 0; + if (!(off & ~(0xfff << sz))) + o(0x39400000 | dst | bas << 5 | off << (10 - sz) | + !!sg << 23 | sz << 30); // ldr(*) x(dst),[x(bas),#(off)] + else if (off < 256 || -off <= 256) + o(0x38400000 | dst | bas << 5 | (off & 511) << 12 | + !!sg << 23 | sz << 30); // ldur(*) x(dst),[x(bas),#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x38206800 | dst | bas << 5 | 30 << 16 | + (!!sg + 1) << 22 | sz << 30); // ldr(*) x(dst),[x(bas),x30] + } +} + +static void arm64_ldrv(int sz, int dst, int bas, uint64_t off) +{ + if (!(off & ~(0xfff << sz))) + o(0x3d400000 | dst | bas << 5 | off << (10 - sz) | + (sz & 4) << 21 | (sz & 3) << 30); // ldr (s|d|q)(dst),[x(bas),#(off)] + else if (off < 256 || -off <= 256) + o(0x3c400000 | dst | bas << 5 | (off & 511) << 12 | + (sz & 4) << 21 | (sz & 3) << 30); // ldur (s|d|q)(dst),[x(bas),#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x3c606800 | dst | bas << 5 | 30 << 16 | sz << 30 | (sz & 4) << 21); + // ldr (s|d|q)(dst),[x(bas),x30] + } +} + +static void arm64_ldrs(int reg, int size) +{ + // Use x30 for intermediate value in some cases. + switch (size) { + default: assert(0); break; + case 1: + arm64_ldrx(0, 0, reg, reg, 0); + break; + case 2: + arm64_ldrx(0, 1, reg, reg, 0); + break; + case 3: + arm64_ldrx(0, 1, 30, reg, 0); + arm64_ldrx(0, 0, reg, reg, 2); + o(0x2a0043c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #16 + break; + case 4: + arm64_ldrx(0, 2, reg, reg, 0); + break; + case 5: + arm64_ldrx(0, 2, 30, reg, 0); + arm64_ldrx(0, 0, reg, reg, 4); + o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32 + break; + case 6: + arm64_ldrx(0, 2, 30, reg, 0); + arm64_ldrx(0, 1, reg, reg, 4); + o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32 + break; + case 7: + arm64_ldrx(0, 2, 30, reg, 0); + arm64_ldrx(0, 2, reg, reg, 3); + o(0x53087c00 | reg | reg << 5); // lsr w(reg), w(reg), #8 + o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32 + break; + case 8: + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 9: + arm64_ldrx(0, 0, reg + 1, reg, 8); + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 10: + arm64_ldrx(0, 1, reg + 1, reg, 8); + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 11: + arm64_ldrx(0, 2, reg + 1, reg, 7); + o(0x53087c00 | (reg+1) | (reg+1) << 5); // lsr w(reg+1), w(reg+1), #8 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 12: + arm64_ldrx(0, 2, reg + 1, reg, 8); + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 13: + arm64_ldrx(0, 3, reg + 1, reg, 5); + o(0xd358fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #24 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 14: + arm64_ldrx(0, 3, reg + 1, reg, 6); + o(0xd350fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #16 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 15: + arm64_ldrx(0, 3, reg + 1, reg, 7); + o(0xd348fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #8 + arm64_ldrx(0, 3, reg, reg, 0); + break; + case 16: + o(0xa9400000 | reg | (reg+1) << 10 | reg << 5); + // ldp x(reg),x(reg+1),[x(reg)] + break; + } +} + +static void arm64_strx(int sz, int dst, int bas, uint64_t off) +{ + if (!(off & ~(0xfff << sz))) + o(0x39000000 | dst | bas << 5 | off << (10 - sz) | sz << 30); + // str(*) x(dst),[x(bas],#(off)] + else if (off < 256 || -off <= 256) + o(0x38000000 | dst | bas << 5 | (off & 511) << 12 | sz << 30); + // stur(*) x(dst),[x(bas],#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x38206800 | dst | bas << 5 | 30 << 16 | sz << 30); + // str(*) x(dst),[x(bas),x30] + } +} + +static void arm64_strv(int sz, int dst, int bas, uint64_t off) +{ + if (!(off & ~(0xfff << sz))) + o(0x3d000000 | dst | bas << 5 | off << (10 - sz) | + (sz & 4) << 21 | (sz & 3) << 30); // str (s|d|q)(dst),[x(bas),#(off)] + else if (off < 256 || -off <= 256) + o(0x3c000000 | dst | bas << 5 | (off & 511) << 12 | + (sz & 4) << 21 | (sz & 3) << 30); // stur (s|d|q)(dst),[x(bas),#(off)] + else { + arm64_movimm(30, off); // use x30 for offset + o(0x3c206800 | dst | bas << 5 | 30 << 16 | sz << 30 | (sz & 4) << 21); + // str (s|d|q)(dst),[x(bas),x30] + } +} + +static void arm64_sym(int r, Sym *sym, unsigned long addend) +{ + // Currently TCC's linker does not generate COPY relocations for + // STT_OBJECTs when tcc is invoked with "-run". This typically + // results in "R_AARCH64_ADR_PREL_PG_HI21 relocation failed" when + // a program refers to stdin. A workaround is to avoid that + // relocation and use only relocations with unlimited range. + int avoid_adrp = 1; + + if (avoid_adrp || (sym->type.t & VT_WEAK)) { + // (GCC uses a R_AARCH64_ABS64 in this case.) + greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G0_NC, addend); + o(0xd2800000 | r); // mov x(rt),#0,lsl #0 + greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G1_NC, addend); + o(0xf2a00000 | r); // movk x(rt),#0,lsl #16 + greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G2_NC, addend); + o(0xf2c00000 | r); // movk x(rt),#0,lsl #32 + greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G3, addend); + o(0xf2e00000 | r); // movk x(rt),#0,lsl #48 + } + else { + greloca(cur_text_section, sym, ind, R_AARCH64_ADR_PREL_PG_HI21, addend); + o(0x90000000 | r); + greloca(cur_text_section, sym, ind, R_AARCH64_ADD_ABS_LO12_NC, addend); + o(0x91000000 | r | r << 5); + } +} + +ST_FUNC void load(int r, SValue *sv) +{ + int svtt = sv->type.t; + int svr = sv->r & ~VT_LVAL_TYPE; + int svrv = svr & VT_VALMASK; + uint64_t svcul = (int32_t)sv->c.ul; + + if (svr == (VT_LOCAL | VT_LVAL)) { + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcul); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), 29, svcul); + return; + } + + if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) { + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), intr(svrv), 0); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), intr(svrv), 0); + return; + } + + if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { + arm64_sym(30, sv->sym, svcul); // use x30 for address + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), 30, 0); + return; + } + + if (svr == (VT_CONST | VT_SYM)) { + arm64_sym(intr(r), sv->sym, svcul); + return; + } + + if (svr == VT_CONST) { + if ((svtt & VT_BTYPE) != VT_VOID) + arm64_movimm(intr(r), arm64_type_size(svtt) == 3 ? + sv->c.ull : (uint32_t)svcul); + return; + } + + if (svr < VT_CONST) { + if (IS_FREG(r) && IS_FREG(svr)) + if (svtt == VT_LDOUBLE) + o(0x4ea01c00 | fltr(r) | fltr(svr) << 5); + // mov v(r).16b,v(svr).16b + else + o(0x1e604000 | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr) + else if (!IS_FREG(r) && !IS_FREG(svr)) + o(0xaa0003e0 | intr(r) | intr(svr) << 16); // mov x(r),x(svr) + else + assert(0); + return; + } + + if (svr == VT_LOCAL) { + if (-svcul < 0x1000) + o(0xd10003a0 | intr(r) | -svcul << 10); // sub x(r),x29,#... + else { + arm64_movimm(30, -svcul); // use x30 for offset + o(0xcb0003a0 | intr(r) | 30 << 16); // sub x(r),x29,x30 + } + return; + } + + if (svr == VT_JMP || svr == VT_JMPI) { + int t = (svr == VT_JMPI); + arm64_movimm(intr(r), t); + o(0x14000002); // b .+8 + gsym(svcul); + arm64_movimm(intr(r), t ^ 1); + return; + } + + if (svr == (VT_LLOCAL | VT_LVAL)) { + arm64_ldrx(0, 3, 30, 29, svcul); // use x30 for offset + if (IS_FREG(r)) + arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0); + else + arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), + intr(r), 30, 0); + return; + } + + printf("load(%x, (%x, %x, %llx))\n", r, svtt, sv->r, (long long)svcul); + assert(0); +} + +ST_FUNC void store(int r, SValue *sv) +{ + int svtt = sv->type.t; + int svr = sv->r & ~VT_LVAL_TYPE; + int svrv = svr & VT_VALMASK; + uint64_t svcul = (int32_t)sv->c.ul; + + if (svr == (VT_LOCAL | VT_LVAL)) { + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcul); + else + arm64_strx(arm64_type_size(svtt), intr(r), 29, svcul); + return; + } + + if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) { + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), intr(svrv), 0); + else + arm64_strx(arm64_type_size(svtt), intr(r), intr(svrv), 0); + return; + } + + if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { + arm64_sym(30, sv->sym, svcul); // use x30 for address + if (IS_FREG(r)) + arm64_strv(arm64_type_size(svtt), fltr(r), 30, 0); + else + arm64_strx(arm64_type_size(svtt), intr(r), 30, 0); + return; + } + + printf("store(%x, (%x, %x, %llx))\n", r, svtt, sv->r, (long long)svcul); + assert(0); +} + +static void arm64_gen_bl_or_b(int b) +{ + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + assert(!b); + if (vtop->r & VT_SYM) + greloc(cur_text_section, vtop->sym, ind, R_AARCH64_CALL26); + else + assert(0); + o(0x94000000); // bl . + } + else + o(0xd61f0000 | !b << 21 | intr(gv(RC_R30)) << 5); // br/blr +} + +static int arm64_hfa_aux(CType *type, int *fsize, int num) +{ + if (is_float(type->t)) { + int a, n = type_size(type, &a); + if (num >= 4 || (*fsize && *fsize != n)) + return -1; + *fsize = n; + return num + 1; + } + else if ((type->t & VT_BTYPE) == VT_STRUCT) { + int is_struct = 0; // rather than union + Sym *field; + for (field = type->ref->next; field; field = field->next) + if (field->c) { + is_struct = 1; + break; + } + if (is_struct) { + int num0 = num; + for (field = type->ref->next; field; field = field->next) { + if (field->c != (num - num0) * *fsize) + return -1; + num = arm64_hfa_aux(&field->type, fsize, num); + if (num == -1) + return -1; + } + if (type->ref->c != (num - num0) * *fsize) + return -1; + return num; + } + else { // union + int num0 = num; + for (field = type->ref->next; field; field = field->next) { + int num1 = arm64_hfa_aux(&field->type, fsize, num0); + if (num1 == -1) + return -1; + num = num1 < num ? num : num1; + } + if (type->ref->c != (num - num0) * *fsize) + return -1; + return num; + } + } + else if (type->t & VT_ARRAY) { + int num1; + if (!type->ref->c) + return num; + num1 = arm64_hfa_aux(&type->ref->type, fsize, num); + if (num1 == -1 || (num1 != num && type->ref->c > 4)) + return -1; + num1 = num + type->ref->c * (num1 - num); + if (num1 > 4) + return -1; + return num1; + } + return -1; +} + +static int arm64_hfa(CType *type, int *fsize) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT || (type->t & VT_ARRAY)) { + int sz = 0; + int n = arm64_hfa_aux(type, &sz, 0); + if (0 < n && n <= 4) { + if (fsize) + *fsize = sz; + return n; + } + } + return 0; +} + +static unsigned long arm64_pcs_aux(int n, CType **type, unsigned long *a) +{ + int nx = 0; // next integer register + int nv = 0; // next vector register + unsigned long ns = 32; // next stack offset + int i; + + for (i = 0; i < n; i++) { + int hfa = arm64_hfa(type[i], 0); + int size, align; + + if ((type[i]->t & VT_ARRAY) || + (type[i]->t & VT_BTYPE) == VT_FUNC) + size = align = 8; + else + size = type_size(type[i], &align); + + if (hfa) + // B.2 + ; + else if (size > 16) { + // B.3: replace with pointer + if (nx < 8) + a[i] = nx++ << 1 | 1; + else { + ns = (ns + 7) & ~7; + a[i] = ns | 1; + ns += 8; + } + continue; + } + else if ((type[i]->t & VT_BTYPE) == VT_STRUCT) + // B.4 + size = (size + 7) & ~7; + + // C.1 + if (is_float(type[i]->t) && nv < 8) { + a[i] = 16 + (nv++ << 1); + continue; + } + + // C.2 + if (hfa && nv + hfa <= 8) { + a[i] = 16 + (nv << 1); + nv += hfa; + continue; + } + + // C.3 + if (hfa) { + nv = 8; + size = (size + 7) & ~7; + } + + // C.4 + if (hfa || (type[i]->t & VT_BTYPE) == VT_LDOUBLE) { + ns = (ns + 7) & ~7; + ns = (ns + align - 1) & -align; + } + + // C.5 + if ((type[i]->t & VT_BTYPE) == VT_FLOAT) + size = 8; + + // C.6 + if (hfa || is_float(type[i]->t)) { + a[i] = ns; + ns += size; + continue; + } + + // C.7 + if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size <= 8 && nx < 8) { + a[i] = nx++ << 1; + continue; + } + + // C.8 + if (align == 16) + nx = (nx + 1) & ~1; + + // C.9 + if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size == 16 && nx < 7) { + a[i] = nx << 1; + nx += 2; + continue; + } + + // C.10 + if ((type[i]->t & VT_BTYPE) == VT_STRUCT && size <= (8 - nx) * 8) { + a[i] = nx << 1; + nx += (size + 7) >> 3; + continue; + } + + // C.11 + nx = 8; + + // C.12 + ns = (ns + 7) & ~7; + ns = (ns + align - 1) & -align; + + // C.13 + if ((type[i]->t & VT_BTYPE) == VT_STRUCT) { + a[i] = ns; + ns += size; + continue; + } + + // C.14 + if (size < 8) + size = 8; + + // C.15 + a[i] = ns; + ns += size; + } + + return ns - 32; +} + +static unsigned long arm64_pcs(int n, CType **type, unsigned long *a) +{ + unsigned long stack; + + // Return type: + if ((type[0]->t & VT_BTYPE) == VT_VOID) + a[0] = -1; + else { + arm64_pcs_aux(1, type, a); + assert(a[0] == 0 || a[0] == 1 || a[0] == 16); + } + + // Argument types: + stack = arm64_pcs_aux(n, type + 1, a + 1); + + if (0) { + int i; + for (i = 0; i <= n; i++) { + if (!i) + printf("arm64_pcs return: "); + else + printf("arm64_pcs arg %d: ", i); + if (a[i] == (unsigned long)-1) + printf("void\n"); + else if (a[i] == 1 && !i) + printf("X8 pointer\n"); + else if (a[i] < 16) + printf("X%lu%s\n", a[i] / 2, a[i] & 1 ? " pointer" : ""); + else if (a[i] < 32) + printf("V%lu\n", a[i] / 2 - 8); + else + printf("stack %lu%s\n", + (a[i] - 32) & ~1, a[i] & 1 ? " pointer" : ""); + } + } + + return stack; +} + +ST_FUNC void gfunc_call(int nb_args) +{ + CType *return_type; + CType **t; + unsigned long *a, *a1; + unsigned long stack; + int i; + + return_type = &vtop[-nb_args].type.ref->type; + if ((return_type->t & VT_BTYPE) == VT_STRUCT) + --nb_args; + + t = tcc_malloc((nb_args + 1) * sizeof(*t)); + a = tcc_malloc((nb_args + 1) * sizeof(*a)); + a1 = tcc_malloc((nb_args + 1) * sizeof(*a1)); + + t[0] = return_type; + for (i = 0; i < nb_args; i++) + t[nb_args - i] = &vtop[-i].type; + + stack = arm64_pcs(nb_args, t, a); + + // Allocate space for structs replaced by pointer: + for (i = nb_args; i; i--) + if (a[i] & 1) { + SValue *arg = &vtop[i - nb_args]; + int align, size = type_size(&arg->type, &align); + assert((arg->type.t & VT_BTYPE) == VT_STRUCT); + stack = (stack + align - 1) & -align; + a1[i] = stack; + stack += size; + } + + stack = (stack + 15) >> 4 << 4; + + assert(stack < 0x1000); + if (stack) + o(0xd10003ff | stack << 10); // sub sp,sp,#(n) + + // First pass: set all values on stack + for (i = nb_args; i; i--) { + vpushv(vtop - nb_args + i); + + if (a[i] & 1) { + // struct replaced by pointer + int r = get_reg(RC_INT); + arm64_spoff(intr(r), a1[i]); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + if (a[i] >= 32) { + // pointer on stack + r = get_reg(RC_INT); + arm64_spoff(intr(r), a1[i]); + arm64_strx(3, intr(r), 31, (a[i] - 32) >> 1 << 1); + } + } + else if (a[i] >= 32) { + // value on stack + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + int r = get_reg(RC_INT); + arm64_spoff(intr(r), a[i] - 32); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + } + else if (is_float(vtop->type.t)) { + gv(RC_FLOAT); + arm64_strv(arm64_type_size(vtop[0].type.t), + fltr(vtop[0].r), 31, a[i] - 32); + } + else { + gv(RC_INT); + arm64_strx(arm64_type_size(vtop[0].type.t), + intr(vtop[0].r), 31, a[i] - 32); + } + } + + --vtop; + } + + // Second pass: assign values to registers + for (i = nb_args; i; i--, vtop--) { + if (a[i] < 16 && !(a[i] & 1)) { + // value in general-purpose registers + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + int align, size = type_size(&vtop->type, &align); + vtop->type.t = VT_PTR; + gaddrof(); + gv(RC_R(a[i] / 2)); + arm64_ldrs(a[i] / 2, size); + } + else + gv(RC_R(a[i] / 2)); + } + else if (a[i] < 16) + // struct replaced by pointer in register + arm64_spoff(a[i] / 2, a1[i]); + else if (a[i] < 32) { + // value in floating-point registers + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + int j, sz, n = arm64_hfa(&vtop->type, &sz); + vtop->type.t = VT_PTR; + gaddrof(); + gv(RC_R30); + for (j = 0; j < n; j++) + o(0x3d4003c0 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + (a[i] / 2 - 8 + j) | + j << 10); // ldr ([sdq])(*),[x30,#(j * sz)] + } + else + gv(RC_F(a[i] / 2 - 8)); + } + } + + if ((return_type->t & VT_BTYPE) == VT_STRUCT) { + if (a[0] == 1) { + // indirect return: set x8 and discard the stack value + gv(RC_R(8)); + --vtop; + } + else + // return in registers: keep the address for after the call + vswap(); + } + + save_regs(0); + arm64_gen_bl_or_b(0); + --vtop; + if (stack) + o(0x910003ff | stack << 10); // add sp,sp,#(n) + + { + int rt = return_type->t; + int bt = rt & VT_BTYPE; + if (bt == VT_BYTE || bt == VT_SHORT) + // Promote small integers: + o(0x13001c00 | (bt == VT_SHORT) << 13 | + !!(rt & VT_UNSIGNED) << 30); // [su]xt[bh] w0,w0 + else if (bt == VT_STRUCT && !(a[0] & 1)) { + // A struct was returned in registers, so write it out: + gv(RC_R(8)); + --vtop; + if (a[0] == 0) { + int align, size = type_size(return_type, &align); + assert(size <= 16); + if (size > 8) + o(0xa9000500); // stp x0,x1,[x8] + else if (size) + arm64_strx(size > 4 ? 3 : size > 2 ? 2 : size > 1, 0, 8, 0); + + } + else if (a[0] == 16) { + int j, sz, n = arm64_hfa(return_type, &sz); + for (j = 0; j < n; j++) + o(0x3d000100 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + (a[i] / 2 - 8 + j) | + j << 10); // str ([sdq])(*),[x8,#(j * sz)] + } + } + } + + tcc_free(a1); + tcc_free(a); + tcc_free(t); +} + +static unsigned long arm64_func_va_list_stack; +static int arm64_func_va_list_gr_offs; +static int arm64_func_va_list_vr_offs; +static int arm64_func_sub_sp_offset; + +ST_FUNC void gfunc_prolog(CType *func_type) +{ + int n = 0; + int i = 0; + Sym *sym; + CType **t; + unsigned long *a; + + // Why doesn't the caller (gen_function) set func_vt? + func_vt = func_type->ref->type; + func_vc = 144; // offset of where x8 is stored + + for (sym = func_type->ref; sym; sym = sym->next) + ++n; + t = tcc_malloc(n * sizeof(*t)); + a = tcc_malloc(n * sizeof(*a)); + + for (sym = func_type->ref; sym; sym = sym->next) + t[i++] = &sym->type; + + arm64_func_va_list_stack = arm64_pcs(n - 1, t, a); + + o(0xa9b27bfd); // stp x29,x30,[sp,#-224]! + o(0xad0087e0); // stp q0,q1,[sp,#16] + o(0xad018fe2); // stp q2,q3,[sp,#48] + o(0xad0297e4); // stp q4,q5,[sp,#80] + o(0xad039fe6); // stp q6,q7,[sp,#112] + o(0xa90923e8); // stp x8,x8,[sp,#144] + o(0xa90a07e0); // stp x0,x1,[sp,#160] + o(0xa90b0fe2); // stp x2,x3,[sp,#176] + o(0xa90c17e4); // stp x4,x5,[sp,#192] + o(0xa90d1fe6); // stp x6,x7,[sp,#208] + + arm64_func_va_list_gr_offs = -64; + arm64_func_va_list_vr_offs = -128; + + for (i = 1, sym = func_type->ref->next; sym; i++, sym = sym->next) { + int off = (a[i] < 16 ? 160 + a[i] / 2 * 8 : + a[i] < 32 ? 16 + (a[i] - 16) / 2 * 16 : + 224 + ((a[i] - 32) >> 1 << 1)); + sym_push(sym->v & ~SYM_FIELD, &sym->type, + (a[i] & 1 ? VT_LLOCAL : VT_LOCAL) | lvalue_type(sym->type.t), + off); + + if (a[i] < 16) { + int align, size = type_size(&sym->type, &align); + arm64_func_va_list_gr_offs = (a[i] / 2 - 7 + + (!(a[i] & 1) && size > 8)) * 8; + } + else if (a[i] < 32) { + int hfa = arm64_hfa(&sym->type, 0); + arm64_func_va_list_vr_offs = (a[i] / 2 - 16 + + (hfa ? hfa : 1)) * 16; + } + + // HFAs of float and double need to be written differently: + if (16 <= a[i] && a[i] < 32 && (sym->type.t & VT_BTYPE) == VT_STRUCT) { + int j, sz, k = arm64_hfa(&sym->type, &sz); + if (sz < 16) + for (j = 0; j < k; j++) { + o(0x3d0003e0 | -(sz & 8) << 27 | (sz & 4) << 29 | + ((a[i] - 16) / 2 + j) | (off / sz + j) << 10); + // str ([sdq])(*),[sp,#(j * sz)] + } + } + } + + tcc_free(a); + tcc_free(t); + + o(0x910003fd); // mov x29,sp + arm64_func_sub_sp_offset = ind; + // In gfunc_epilog these will be replaced with code to decrement SP: + o(0xd503201f); // nop + o(0xd503201f); // nop + loc = 0; +} + +ST_FUNC void gen_va_start(void) +{ + int r; + --vtop; // we don't need the "arg" + gaddrof(); + r = intr(gv(RC_INT)); + + if (arm64_func_va_list_stack) { + //xx could use add (immediate) here + arm64_movimm(30, arm64_func_va_list_stack + 224); + o(0x8b1e03be); // add x30,x29,x30 + } + else + o(0x910383be); // add x30,x29,#224 + o(0xf900001e | r << 5); // str x30,[x(r)] + + if (arm64_func_va_list_gr_offs) { + if (arm64_func_va_list_stack) + o(0x910383be); // add x30,x29,#224 + o(0xf900041e | r << 5); // str x30,[x(r),#8] + } + + if (arm64_func_va_list_vr_offs) { + o(0x910243be); // add x30,x29,#144 + o(0xf900081e | r << 5); // str x30,[x(r),#16] + } + + arm64_movimm(30, arm64_func_va_list_gr_offs); + o(0xb900181e | r << 5); // str w30,[x(r),#24] + + arm64_movimm(30, arm64_func_va_list_vr_offs); + o(0xb9001c1e | r << 5); // str w30,[x(r),#28] + + --vtop; +} + +ST_FUNC void gen_va_arg(CType *t) +{ + int align, size = type_size(t, &align); + int fsize, hfa = arm64_hfa(t, &fsize); + uint32_t r0, r1; + + if (is_float(t->t)) { + hfa = 1; + fsize = size; + } + + gaddrof(); + r0 = intr(gv(RC_INT)); + r1 = get_reg(RC_INT); + vtop[0].r = r1 | lvalue_type(t->t); + r1 = intr(r1); + + if (!hfa) { + uint32_t n = size > 16 ? 8 : (size + 7) & -8; + o(0xb940181e | r0 << 5); // ldr w30,[x(r0),#24] // __gr_offs + if (align == 16) { + assert(0); // this path untested but needed for __uint128_t + o(0x11003fde); // add w30,w30,#15 + o(0x121c6fde); // and w30,w30,#-16 + } + o(0x310003c0 | r1 | n << 10); // adds w(r1),w30,#(n) + o(0x540000ad); // b.le .+20 + o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack + o(0x9100001e | r1 << 5 | n << 10); // add x30,x(r1),#(n) + o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack + o(0x14000004); // b .+16 + o(0xb9001800 | r1 | r0 << 5); // str w(r1),[x(r0),#24] // __gr_offs + o(0xf9400400 | r1 | r0 << 5); // ldr x(r1),[x(r0),#8] // __gr_top + o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw + if (size > 16) + o(0xf9400000 | r1 | r1 << 5); // ldr x(r1),[x(r1)] + } + else { + uint32_t rsz = hfa << 4; + uint32_t ssz = (size + 7) & -(uint32_t)8; + uint32_t b1, b2; + o(0xb9401c1e | r0 << 5); // ldr w30,[x(r0),#28] // __vr_offs + o(0x310003c0 | r1 | rsz << 10); // adds w(r1),w30,#(rsz) + b1 = ind; o(0x5400000d); // b.le lab1 + o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack + if (fsize == 16) { + o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15 + o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16 + } + o(0x9100001e | r1 << 5 | ssz << 10); // add x30,x(r1),#(ssz) + o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack + b2 = ind; o(0x14000000); // b lab2 + // lab1: + *(uint32_t *)(cur_text_section->data + b1) = + (0x5400000d | (ind - b1) << 3); + o(0xb9001c00 | r1 | r0 << 5); // str w(r1),[x(r0),#28] // __vr_offs + o(0xf9400800 | r1 | r0 << 5); // ldr x(r1),[x(r0),#16] // __vr_top + if (hfa == 1 || fsize == 16) + o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw + else { + // We need to change the layout of this HFA. + // Get some space on the stack using global variable "loc": + loc = (loc - size) & -(uint32_t)align; + o(0x8b3ec000 | 30 | r1 << 5); // add x30,x(r1),w30,sxtw + arm64_movimm(r1, loc); + o(0x8b0003a0 | r1 | r1 << 16); // add x(r1),x29,x(r1) + o(0x4c402bdc | (uint32_t)fsize << 7 | + (uint32_t)(hfa == 2) << 15 | + (uint32_t)(hfa == 3) << 14); // ld1 {v28.(4s|2d),...},[x30] + o(0x0d00801c | r1 << 5 | (fsize == 8) << 10 | + (uint32_t)(hfa != 2) << 13 | + (uint32_t)(hfa != 3) << 21); // st(hfa) {v28.(s|d),...}[0],[x(r1)] + } + // lab2: + *(uint32_t *)(cur_text_section->data + b2) = + (0x14000000 | (ind - b2) >> 2); + } +} + +ST_FUNC int regargs_nregs(RegArgs *args) +{ + return *args; +} + +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize, RegArgs *args) +{ + *args = 0; + + return 0; +} + +ST_FUNC void greturn(void) +{ + CType *t = &func_vt; + unsigned long a; + + arm64_pcs(0, &t, &a); + switch (a) { + case -1: + break; + case 0: + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + int align, size = type_size(&func_vt, &align); + gaddrof(); + gv(RC_R(0)); + arm64_ldrs(0, size); + } + else + gv(RC_IRET); + break; + case 1: { + CType type = func_vt; + mk_pointer(&type); + vset(&type, VT_LOCAL | VT_LVAL, func_vc); + indir(); + vswap(); + vstore(); + break; + } + case 16: + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + int j, sz, n = arm64_hfa(&vtop->type, &sz); + gaddrof(); + gv(RC_R(0)); + for (j = 0; j < n; j++) + o(0x3d400000 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + j | j << 10); // ldr ([sdq])(*),[x0,#(j * sz)] + } + else + gv(RC_FRET); + break; + default: + assert(0); + } +} + +ST_FUNC void gfunc_epilog(void) +{ + if (loc) { + // Insert instructions to subtract size of stack frame from SP. + uint32_t *ptr = + (uint32_t *)(cur_text_section->data + arm64_func_sub_sp_offset); + uint64_t diff = (-loc + 15) & ~15; + if (!(diff >> 24)) { + if (diff & 0xfff) // sub sp,sp,#(diff & 0xfff) + ptr[0] = 0xd10003ff | (diff & 0xfff) << 10; + if (diff >> 12) // sub sp,sp,#(diff >> 12),lsl #12 + ptr[1] = 0xd14003ff | (diff >> 12) << 10; + } + else { + // In this case we may subtract more than necessary, + // but always less than 17/16 of what we were aiming for. + int i = 0; + int j = 0; + while (diff >> 20) { + diff = (diff + 0xffff) >> 16; + ++i; + } + while (diff >> 16) { + diff = (diff + 1) >> 1; + ++j; + } + ptr[0] = 0xd2800010 | diff << 5 | i << 21; + // mov x16,#(diff),lsl #(16 * i) + ptr[1] = 0xcb3063ff | j << 10; + // sub sp,sp,x16,lsl #(j) + } + } + o(0x910003bf); // mov sp,x29 + o(0xa8ce7bfd); // ldp x29,x30,[sp],#224 + + o(0xd65f03c0); // ret +} + +// Generate forward branch to label: +ST_FUNC int gjmp(int t) +{ + int r = ind; + o(t); + return r; +} + +// Generate branch to known address: +ST_FUNC void gjmp_addr(int a) +{ + assert(a - ind + 0x8000000 < 0x10000000); + o(0x14000000 | ((a - ind) >> 2 & 0x3ffffff)); +} + +ST_FUNC int gtst(int inv, int t) +{ + int bt = vtop->type.t & VT_BTYPE; + if (bt == VT_LDOUBLE) { + int a, b, f = fltr(gv(RC_FLOAT)); + a = get_reg(RC_INT); + vpushi(0); + vtop[0].r = a; + b = get_reg(RC_INT); + a = intr(a); + b = intr(b); + o(0x4e083c00 | a | f << 5); // mov x(a),v(f).d[0] + o(0x4e183c00 | b | f << 5); // mov x(b),v(f).d[1] + o(0xaa000400 | a | a << 5 | b << 16); // orr x(a),x(a),x(b),lsl #1 + o(0xb4000040 | a | !!inv << 24); // cbz/cbnz x(a),.+8 + --vtop; + } + else if (bt == VT_FLOAT || bt == VT_DOUBLE) { + int a = fltr(gv(RC_FLOAT)); + o(0x1e202008 | a << 5 | (bt != VT_FLOAT) << 22); // fcmp + o(0x54000040 | !!inv); // b.eq/b.ne .+8 + } + else { + int ll = (bt == VT_PTR || bt == VT_LLONG); + int a = intr(gv(RC_INT)); + o(0x34000040 | a | !!inv << 24 | ll << 31); // cbz/cbnz wA,.+8 + } + --vtop; + return gjmp(t); +} + +static int arm64_iconst(uint64_t *val, SValue *sv) +{ + if ((sv->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (val) { + int t = sv->type.t & (VT_BTYPE | VT_UNSIGNED); + // It's crazy how TCC has all these alternatives for storing a value: + if (t == (VT_LLONG | VT_UNSIGNED)) + *val = sv->c.ull; + else if (t == VT_LLONG) + *val = sv->c.ll; + else if (t & VT_UNSIGNED) + *val = sv->c.ui; + else + *val = sv->c.i; + } + return 1; +} + +static int arm64_gen_opic(int op, uint32_t l, int rev, uint64_t val, + uint32_t x, uint32_t a) +{ + if (op == '-' && !rev) { + val = -val; + op = '+'; + } + val = l ? val : (uint32_t)val; + + switch (op) { + + case '+': { + int s = l ? val >> 63 : val >> 31; + val = s ? -val : val; + val = l ? val : (uint32_t)val; + if (!(val & ~(uint64_t)0xfff)) + o(0x11000000 | l << 31 | s << 30 | x | a << 5 | val << 10); + else if (!(val & ~(uint64_t)0xfff000)) + o(0x11400000 | l << 31 | s << 30 | x | a << 5 | val >> 12 << 10); + else { + arm64_movimm(30, val); // use x30 + o(0x0b1e0000 | l << 31 | s << 30 | x | a << 5); + } + return 1; + } + + case '-': + if (!val) + o(0x4b0003e0 | l << 31 | x | a << 16); // neg + else if (val == (l ? (uint64_t)-1 : (uint32_t)-1)) + o(0x2a2003e0 | l << 31 | x | a << 16); // mvn + else { + arm64_movimm(30, val); // use x30 + o(0x4b0003c0 | l << 31 | x | a << 16); // sub + } + return 1; + + case '^': + if (val == -1 || (val == 0xffffffff && !l)) { + o(0x2a2003e0 | l << 31 | x | a << 16); // mvn + return 1; + } + // fall through + case '&': + case '|': { + int e = arm64_encode_bimm64(l ? val : val | val << 32); + if (e < 0) + return 0; + o((op == '&' ? 0x12000000 : + op == '|' ? 0x32000000 : 0x52000000) | + l << 31 | x | a << 5 | (uint32_t)e << 10); + return 1; + } + + case TOK_SAR: + case TOK_SHL: + case TOK_SHR: { + uint32_t n = 32 << l; + val = val & (n - 1); + if (rev) + return 0; + if (!val) + assert(0); + else if (op == TOK_SHL) + o(0x53000000 | l << 31 | l << 22 | x | a << 5 | + (n - val) << 16 | (n - 1 - val) << 10); // lsl + else + o(0x13000000 | (op == TOK_SHR) << 30 | l << 31 | l << 22 | + x | a << 5 | val << 16 | (n - 1) << 10); // lsr/asr + return 1; + } + + } + return 0; +} + +static void arm64_gen_opil(int op, uint32_t l) +{ + uint32_t x, a, b; + + // Special treatment for operations with a constant operand: + { + uint64_t val; + int rev = 1; + + if (arm64_iconst(0, &vtop[0])) { + vswap(); + rev = 0; + } + if (arm64_iconst(&val, &vtop[-1])) { + gv(RC_INT); + a = intr(vtop[0].r); + --vtop; + x = get_reg(RC_INT); + ++vtop; + if (arm64_gen_opic(op, l, rev, val, intr(x), a)) { + vtop[0].r = x; + vswap(); + --vtop; + return; + } + } + if (!rev) + vswap(); + } + + gv2(RC_INT, RC_INT); + assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST); + a = intr(vtop[-1].r); + b = intr(vtop[0].r); + vtop -= 2; + x = get_reg(RC_INT); + ++vtop; + vtop[0].r = x; + x = intr(x); + + switch (op) { + case '%': + // Use x30 for quotient: + o(0x1ac00c00 | l << 31 | 30 | a << 5 | b << 16); // sdiv + o(0x1b008000 | l << 31 | x | 30 << 5 | b << 16 | a << 10); // msub + break; + case '&': + o(0x0a000000 | l << 31 | x | a << 5 | b << 16); // and + break; + case '*': + o(0x1b007c00 | l << 31 | x | a << 5 | b << 16); // mul + break; + case '+': + o(0x0b000000 | l << 31 | x | a << 5 | b << 16); // add + break; + case '-': + o(0x4b000000 | l << 31 | x | a << 5 | b << 16); // sub + break; + case '/': + o(0x1ac00c00 | l << 31 | x | a << 5 | b << 16); // sdiv + break; + case '^': + o(0x4a000000 | l << 31 | x | a << 5 | b << 16); // eor + break; + case '|': + o(0x2a000000 | l << 31 | x | a << 5 | b << 16); // orr + break; + case TOK_EQ: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f17e0 | x); // cset wA,eq + break; + case TOK_GE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fb7e0 | x); // cset wA,ge + break; + case TOK_GT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fd7e0 | x); // cset wA,gt + break; + case TOK_LE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fc7e0 | x); // cset wA,le + break; + case TOK_LT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9fa7e0 | x); // cset wA,lt + break; + case TOK_NE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f07e0 | x); // cset wA,ne + break; + case TOK_SAR: + o(0x1ac02800 | l << 31 | x | a << 5 | b << 16); // asr + break; + case TOK_SHL: + o(0x1ac02000 | l << 31 | x | a << 5 | b << 16); // lsl + break; + case TOK_SHR: + o(0x1ac02400 | l << 31 | x | a << 5 | b << 16); // lsr + break; + case TOK_UDIV: + case TOK_PDIV: + o(0x1ac00800 | l << 31 | x | a << 5 | b << 16); // udiv + break; + case TOK_UGE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f37e0 | x); // cset wA,cs + break; + case TOK_UGT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f97e0 | x); // cset wA,hi + break; + case TOK_ULT: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f27e0 | x); // cset wA,cc + break; + case TOK_ULE: + o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp + o(0x1a9f87e0 | x); // cset wA,ls + break; + case TOK_UMOD: + // Use x30 for quotient: + o(0x1ac00800 | l << 31 | 30 | a << 5 | b << 16); // udiv + o(0x1b008000 | l << 31 | x | 30 << 5 | b << 16 | a << 10); // msub + break; + default: + assert(0); + } +} + +ST_FUNC void gen_opi(int op) +{ + arm64_gen_opil(op, 0); +} + +ST_FUNC void gen_opl(int op) +{ + arm64_gen_opil(op, 1); +} + +ST_FUNC void gen_opf(int op) +{ + int x, a, b, dbl; + + if (vtop[0].type.t == VT_LDOUBLE) { + CType type = vtop[0].type; + int func = 0; + int cond = -1; + switch (op) { + case '*': func = TOK___multf3; break; + case '+': func = TOK___addtf3; break; + case '-': func = TOK___subtf3; break; + case '/': func = TOK___divtf3; break; + case TOK_EQ: func = TOK___eqtf2; cond = 1; break; + case TOK_NE: func = TOK___netf2; cond = 0; break; + case TOK_LT: func = TOK___lttf2; cond = 10; break; + case TOK_GE: func = TOK___getf2; cond = 11; break; + case TOK_LE: func = TOK___letf2; cond = 12; break; + case TOK_GT: func = TOK___gttf2; cond = 13; break; + default: assert(0); break; + } + vpush_global_sym(&func_old_type, func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = cond < 0 ? REG_FRET : REG_IRET; + if (cond < 0) + vtop->type = type; + else { + o(0x7100001f); // cmp w0,#0 + o(0x1a9f07e0 | cond << 12); // cset w0,(cond) + } + return; + } + + dbl = vtop[0].type.t != VT_FLOAT; + gv2(RC_FLOAT, RC_FLOAT); + assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST); + a = fltr(vtop[-1].r); + b = fltr(vtop[0].r); + vtop -= 2; + switch (op) { + case TOK_EQ: case TOK_NE: + case TOK_LT: case TOK_GE: case TOK_LE: case TOK_GT: + x = get_reg(RC_INT); + ++vtop; + vtop[0].r = x; + x = intr(x); + break; + default: + x = get_reg(RC_FLOAT); + ++vtop; + vtop[0].r = x; + x = fltr(x); + break; + } + + switch (op) { + case '*': + o(0x1e200800 | dbl << 22 | x | a << 5 | b << 16); // fmul + break; + case '+': + o(0x1e202800 | dbl << 22 | x | a << 5 | b << 16); // fadd + break; + case '-': + o(0x1e203800 | dbl << 22 | x | a << 5 | b << 16); // fsub + break; + case '/': + o(0x1e201800 | dbl << 22 | x | a << 5 | b << 16); // fdiv + break; + case TOK_EQ: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f17e0 | x); // cset w(x),eq + break; + case TOK_GE: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9fb7e0 | x); // cset w(x),ge + break; + case TOK_GT: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9fd7e0 | x); // cset w(x),gt + break; + case TOK_LE: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f87e0 | x); // cset w(x),ls + break; + case TOK_LT: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f57e0 | x); // cset w(x),mi + break; + case TOK_NE: + o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp + o(0x1a9f07e0 | x); // cset w(x),ne + break; + default: + assert(0); + } +} + +// Generate sign extension from 32 to 64 bits: +ST_FUNC void gen_cvt_sxtw(void) +{ + int r = intr(gv(RC_INT)); + o(0x93407c00 | r | r << 5); // sxtw x(r),w(r) +} + +ST_FUNC void gen_cvt_itof(int t) +{ + if (t == VT_LDOUBLE) { + int f = vtop->type.t; + int func = (f & VT_BTYPE) == VT_LLONG ? + (f & VT_UNSIGNED ? TOK___floatunditf : TOK___floatditf) : + (f & VT_UNSIGNED ? TOK___floatunsitf : TOK___floatsitf); + vpush_global_sym(&func_old_type, func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_FRET; + return; + } + else { + int d, n = intr(gv(RC_INT)); + int s = !(vtop->type.t & VT_UNSIGNED); + int l = ((vtop->type.t & VT_BTYPE) == VT_LLONG); + --vtop; + d = get_reg(RC_FLOAT); + ++vtop; + vtop[0].r = d; + o(0x1e220000 | !s << 16 | (t != VT_FLOAT) << 22 | fltr(d) | + l << 31 | n << 5); // [us]cvtf [sd](d),[wx](n) + } +} + +ST_FUNC void gen_cvt_ftoi(int t) +{ + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + int func = (t & VT_BTYPE) == VT_LLONG ? + (t & VT_UNSIGNED ? TOK___fixunstfdi : TOK___fixtfdi) : + (t & VT_UNSIGNED ? TOK___fixunstfsi : TOK___fixtfsi); + vpush_global_sym(&func_old_type, func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_IRET; + return; + } + else { + int d, n = fltr(gv(RC_FLOAT)); + int l = ((vtop->type.t & VT_BTYPE) != VT_FLOAT); + --vtop; + d = get_reg(RC_INT); + ++vtop; + vtop[0].r = d; + o(0x1e380000 | + !!(t & VT_UNSIGNED) << 16 | + ((t & VT_BTYPE) == VT_LLONG) << 31 | intr(d) | + l << 22 | n << 5); // fcvtz[su] [wx](d),[sd](n) + } +} + +ST_FUNC void gen_cvt_ftof(int t) +{ + int f = vtop[0].type.t; + assert(t == VT_FLOAT || t == VT_DOUBLE || t == VT_LDOUBLE); + assert(f == VT_FLOAT || f == VT_DOUBLE || f == VT_LDOUBLE); + if (t == f) + return; + + if (t == VT_LDOUBLE || f == VT_LDOUBLE) { + int func = (t == VT_LDOUBLE) ? + (f == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) : + (t == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2); + vpush_global_sym(&func_old_type, func); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->type.t = t; + vtop->r = REG_FRET; + } + else { + int x, a; + gv(RC_FLOAT); + assert(vtop[0].r < VT_CONST); + a = fltr(vtop[0].r); + --vtop; + x = get_reg(RC_FLOAT); + ++vtop; + vtop[0].r = x; + x = fltr(x); + + if (f == VT_FLOAT) + o(0x1e22c000 | x | a << 5); // fcvt d(x),s(a) + else + o(0x1e624000 | x | a << 5); // fcvt s(x),d(a) + } +} + +ST_FUNC void ggoto(void) +{ + arm64_gen_bl_or_b(1); + --vtop; +} + +ST_FUNC void gen_clear_cache(void) +{ + uint32_t beg, end, dsz, isz, p, lab1, b1; + gv2(RC_INT, RC_INT); + vpushi(0); + vtop->r = get_reg(RC_INT); + vpushi(0); + vtop->r = get_reg(RC_INT); + vpushi(0); + vtop->r = get_reg(RC_INT); + beg = intr(vtop[-4].r); // x0 + end = intr(vtop[-3].r); // x1 + dsz = intr(vtop[-2].r); // x2 + isz = intr(vtop[-1].r); // x3 + p = intr(vtop[0].r); // x4 + vtop -= 5; + + o(0xd53b0020 | isz); // mrs x(isz),ctr_el0 + o(0x52800080 | p); // mov w(p),#4 + o(0x53104c00 | dsz | isz << 5); // ubfx w(dsz),w(isz),#16,#4 + o(0x1ac02000 | dsz | p << 5 | dsz << 16); // lsl w(dsz),w(p),w(dsz) + o(0x12000c00 | isz | isz << 5); // and w(isz),w(isz),#15 + o(0x1ac02000 | isz | p << 5 | isz << 16); // lsl w(isz),w(p),w(isz) + o(0x51000400 | p | dsz << 5); // sub w(p),w(dsz),#1 + o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p) + b1 = ind; o(0x14000000); // b + lab1 = ind; + o(0xd50b7b20 | p); // dc cvau,x(p) + o(0x8b000000 | p | p << 5 | dsz << 16); // add x(p),x(p),x(dsz) + *(uint32_t *)(cur_text_section->data + b1) = + (0x14000000 | (ind - b1) >> 2); + o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end) + o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1 + o(0xd5033b9f); // dsb ish + o(0x51000400 | p | isz << 5); // sub w(p),w(isz),#1 + o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p) + b1 = ind; o(0x14000000); // b + lab1 = ind; + o(0xd50b7520 | p); // ic ivau,x(p) + o(0x8b000000 | p | p << 5 | isz << 16); // add x(p),x(p),x(isz) + *(uint32_t *)(cur_text_section->data + b1) = + (0x14000000 | (ind - b1) >> 2); + o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end) + o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1 + o(0xd5033b9f); // dsb ish + o(0xd5033fdf); // isb +} + +ST_FUNC void gen_vla_sp_save(int addr) { + tcc_error("variable length arrays unsupported for this target"); +} + +ST_FUNC void gen_vla_sp_restore(int addr) { + tcc_error("variable length arrays unsupported for this target"); +} + +ST_FUNC void gen_vla_alloc(CType *type, int align) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* end of A64 code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/src/cil/il-gen.c b/src/cil/il-gen.c new file mode 100644 index 0000000..a4170b0 --- /dev/null +++ b/src/cil/il-gen.c @@ -0,0 +1,655 @@ +/* + * CIL code generator for TCC + * + * Copyright (c) 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 + */ + +/* number of available registers */ +#define NB_REGS 3 + +/* 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_ST 0x0001 /* any stack entry */ +#define RC_ST0 0x0002 /* top of stack */ +#define RC_ST1 0x0004 /* top - 1 */ + +#define RC_INT RC_ST +#define RC_FLOAT RC_ST +#define RC_IRET RC_ST0 /* function return: integer register */ +#define RC_LRET RC_ST0 /* function return: second integer register */ +#define RC_FRET RC_ST0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + REG_ST0 = 0, + REG_ST1, + REG_ST2, +}; + +const int reg_classes[NB_REGS] = { + /* ST0 */ RC_ST | RC_ST0, + /* ST1 */ RC_ST | RC_ST1, + /* ST2 */ RC_ST, +}; + +/* return registers for function */ +#define REG_IRET REG_ST0 /* single word int return register */ +#define REG_LRET REG_ST0 /* second word return register (for long long) */ +#define REG_FRET REG_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 8 +#define LDOUBLE_ALIGN 8 + +/* function call context */ +typedef struct GFuncContext { + int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */ +} GFuncContext; + +/******************************************************/ +/* opcode definitions */ + +#define IL_OP_PREFIX 0xFE + +enum ILOPCodes { +#define OP(name, str, n) IL_OP_ ## name = n, +#include "il-opcodes.h" +#undef OP +}; + +char *il_opcodes_str[] = { +#define OP(name, str, n) [n] = str, +#include "il-opcodes.h" +#undef OP +}; + +/******************************************************/ + +/* arguments variable numbers start from there */ +#define ARG_BASE 0x70000000 + +static FILE *il_outfile; + +static void out_byte(int c) +{ + *(char *)ind++ = c; +} + +static void out_le32(int c) +{ + out_byte(c); + out_byte(c >> 8); + out_byte(c >> 16); + out_byte(c >> 24); +} + +static void init_outfile(void) +{ + if (!il_outfile) { + il_outfile = stdout; + fprintf(il_outfile, + ".assembly extern mscorlib\n" + "{\n" + ".ver 1:0:2411:0\n" + "}\n\n"); + } +} + +static void out_op1(int op) +{ + if (op & 0x100) + out_byte(IL_OP_PREFIX); + out_byte(op & 0xff); +} + +/* output an opcode with prefix */ +static void out_op(int op) +{ + out_op1(op); + fprintf(il_outfile, " %s\n", il_opcodes_str[op]); +} + +static void out_opb(int op, int c) +{ + out_op1(op); + out_byte(c); + fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c); +} + +static void out_opi(int op, int c) +{ + out_op1(op); + out_le32(c); + fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c); +} + +/* XXX: not complete */ +static void il_type_to_str(char *buf, int buf_size, + int t, const char *varstr) +{ + int bt; + Sym *s, *sa; + char buf1[256]; + const char *tstr; + + t = t & VT_TYPE; + bt = t & VT_BTYPE; + buf[0] = '\0'; + 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 = "int8"; + goto add_tstr; + case VT_SHORT: + tstr = "int16"; + goto add_tstr; + case VT_ENUM: + case VT_INT: + case VT_LONG: + tstr = "int32"; + goto add_tstr; + case VT_LLONG: + tstr = "int64"; + goto add_tstr; + case VT_FLOAT: + tstr = "float32"; + goto add_tstr; + case VT_DOUBLE: + case VT_LDOUBLE: + tstr = "float64"; + add_tstr: + pstrcat(buf, buf_size, tstr); + break; + case VT_STRUCT: + tcc_error("structures not handled yet"); + break; + case VT_FUNC: + s = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + il_type_to_str(buf, buf_size, s->t, varstr); + pstrcat(buf, buf_size, "("); + sa = s->next; + while (sa != NULL) { + il_type_to_str(buf1, sizeof(buf1), sa->t, 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 = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + pstrcpy(buf1, sizeof(buf1), "*"); + if (varstr) + pstrcat(buf1, sizeof(buf1), varstr); + il_type_to_str(buf, buf_size, s->t, buf1); + goto no_var; + } + if (varstr) { + pstrcat(buf, buf_size, " "); + pstrcat(buf, buf_size, varstr); + } + no_var: ; +} + + +/* patch relocation entry with value 'val' */ +void greloc_patch1(Reloc *p, int val) +{ +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(t, a) +{ +} + +/* output jump and return symbol */ +static int out_opj(int op, int c) +{ + out_op1(op); + out_le32(0); + if (c == 0) { + c = ind - (int)cur_text_section->data; + } + fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c); + return c; +} + +void gsym(int t) +{ + fprintf(il_outfile, "L%d:\n", t); +} + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, fc, ft; + + v = sv->r & VT_VALMASK; + fc = sv->c.i; + ft = sv->t; + + if (sv->r & VT_LVAL) { + if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_LDARG_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_LDARG_S, fc); + } else { + out_opi(IL_OP_LDARG, fc); + } + } else { + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_LDLOC_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_LDLOC_S, fc); + } else { + out_opi(IL_OP_LDLOC, fc); + } + } + } else if (v == VT_CONST) { + /* XXX: handle globals */ + out_opi(IL_OP_LDSFLD, 0); + } else { + if ((ft & VT_BTYPE) == VT_FLOAT) { + out_op(IL_OP_LDIND_R4); + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + out_op(IL_OP_LDIND_R8); + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + out_op(IL_OP_LDIND_R8); + } else if ((ft & VT_TYPE) == VT_BYTE) + out_op(IL_OP_LDIND_I1); + else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) + out_op(IL_OP_LDIND_U1); + else if ((ft & VT_TYPE) == VT_SHORT) + out_op(IL_OP_LDIND_I2); + else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) + out_op(IL_OP_LDIND_U2); + else + out_op(IL_OP_LDIND_I4); + } + } else { + if (v == VT_CONST) { + /* XXX: handle globals */ + if (fc >= -1 && fc <= 8) { + out_op(IL_OP_LDC_I4_M1 + fc + 1); + } else { + out_opi(IL_OP_LDC_I4, fc); + } + } else if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + if (fc <= 0xff) { + out_opb(IL_OP_LDARGA_S, fc); + } else { + out_opi(IL_OP_LDARGA, fc); + } + } else { + if (fc <= 0xff) { + out_opb(IL_OP_LDLOCA_S, fc); + } else { + out_opi(IL_OP_LDLOCA, fc); + } + } + } else { + /* XXX: do it */ + } + } +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *sv) +{ + int v, fc, ft; + + v = sv->r & VT_VALMASK; + fc = sv->c.i; + ft = sv->t; + if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + /* XXX: check IL arg store semantics */ + if (fc <= 0xff) { + out_opb(IL_OP_STARG_S, fc); + } else { + out_opi(IL_OP_STARG, fc); + } + } else { + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_STLOC_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_STLOC_S, fc); + } else { + out_opi(IL_OP_STLOC, fc); + } + } + } else if (v == VT_CONST) { + /* XXX: handle globals */ + out_opi(IL_OP_STSFLD, 0); + } else { + if ((ft & VT_BTYPE) == VT_FLOAT) + out_op(IL_OP_STIND_R4); + else if ((ft & VT_BTYPE) == VT_DOUBLE) + out_op(IL_OP_STIND_R8); + else if ((ft & VT_BTYPE) == VT_LDOUBLE) + out_op(IL_OP_STIND_R8); + else if ((ft & VT_BTYPE) == VT_BYTE) + out_op(IL_OP_STIND_I1); + else if ((ft & VT_BTYPE) == VT_SHORT) + out_op(IL_OP_STIND_I2); + else + out_op(IL_OP_STIND_I4); + } +} + +/* start function call and return function call context */ +void gfunc_start(GFuncContext *c, int func_call) +{ + c->func_call = func_call; +} + +/* push function parameter which is in (vtop->t, vtop->c). Stack entry + is then popped. */ +void gfunc_param(GFuncContext *c) +{ + if ((vtop->t & VT_BTYPE) == VT_STRUCT) { + tcc_error("structures passed as value not handled yet"); + } else { + /* simply push on stack */ + gv(RC_ST0); + } + vtop--; +} + +/* generate function call with address in (vtop->t, vtop->c) and free function + context. Stack entry is popped */ +void gfunc_call(GFuncContext *c) +{ + char buf[1024]; + + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* XXX: more info needed from tcc */ + il_type_to_str(buf, sizeof(buf), vtop->t, "xxx"); + fprintf(il_outfile, " call %s\n", buf); + } else { + /* indirect call */ + gv(RC_INT); + il_type_to_str(buf, sizeof(buf), vtop->t, NULL); + fprintf(il_outfile, " calli %s\n", buf); + } + vtop--; +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(int t) +{ + int addr, u, func_call; + Sym *sym; + char buf[1024]; + + init_outfile(); + + /* XXX: pass function name to gfunc_prolog */ + il_type_to_str(buf, sizeof(buf), t, funcname); + fprintf(il_outfile, ".method static %s il managed\n", buf); + fprintf(il_outfile, "{\n"); + /* XXX: cannot do better now */ + fprintf(il_outfile, " .maxstack %d\n", NB_REGS); + fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n"); + + if (!strcmp(funcname, "main")) + fprintf(il_outfile, " .entrypoint\n"); + + sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + func_call = sym->r; + + addr = ARG_BASE; + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->t; + func_var = (sym->c == FUNC_ELLIPSIS); + if ((func_vt & VT_BTYPE) == VT_STRUCT) { + func_vc = addr; + addr++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + u = sym->t; + sym_push(sym->v & ~SYM_FIELD, u, + VT_LOCAL | lvalue_type(sym->type.t), addr); + addr++; + } +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + out_op(IL_OP_RET); + fprintf(il_outfile, "}\n\n"); +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + return out_opj(IL_OP_BR, t); +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + /* XXX: handle syms */ + out_opi(IL_OP_BR, a); +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, *p, c; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + c = vtop->c.i ^ inv; + switch(c) { + case TOK_EQ: + c = IL_OP_BEQ; + break; + case TOK_NE: + c = IL_OP_BNE_UN; + break; + case TOK_LT: + c = IL_OP_BLT; + break; + case TOK_LE: + c = IL_OP_BLE; + break; + case TOK_GT: + c = IL_OP_BGT; + break; + case TOK_GE: + c = IL_OP_BGE; + break; + case TOK_ULT: + c = IL_OP_BLT_UN; + break; + case TOK_ULE: + c = IL_OP_BLE_UN; + break; + case TOK_UGT: + c = IL_OP_BGT_UN; + break; + case TOK_UGE: + c = IL_OP_BGE_UN; + break; + } + t = out_opj(c, 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 *)*p; + *p = t; + t = vtop->c.i; + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + gv2(RC_ST1, RC_ST0); + switch(op) { + case '+': + out_op(IL_OP_ADD); + goto std_op; + case '-': + out_op(IL_OP_SUB); + goto std_op; + case '&': + out_op(IL_OP_AND); + goto std_op; + case '^': + out_op(IL_OP_XOR); + goto std_op; + case '|': + out_op(IL_OP_OR); + goto std_op; + case '*': + out_op(IL_OP_MUL); + goto std_op; + case TOK_SHL: + out_op(IL_OP_SHL); + goto std_op; + case TOK_SHR: + out_op(IL_OP_SHR_UN); + goto std_op; + case TOK_SAR: + out_op(IL_OP_SHR); + goto std_op; + case '/': + case TOK_PDIV: + out_op(IL_OP_DIV); + goto std_op; + case TOK_UDIV: + out_op(IL_OP_DIV_UN); + goto std_op; + case '%': + out_op(IL_OP_REM); + goto std_op; + case TOK_UMOD: + out_op(IL_OP_REM_UN); + std_op: + vtop--; + vtop[0].r = REG_ST0; + break; + case TOK_EQ: + case TOK_NE: + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + case TOK_ULT: + case TOK_ULE: + case TOK_UGT: + case TOK_UGE: + vtop--; + vtop[0].r = VT_CMP; + vtop[0].c.i = op; + break; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + /* same as integer */ + gen_opi(op); +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + gv(RC_ST0); + if (t == VT_FLOAT) + out_op(IL_OP_CONV_R4); + else + out_op(IL_OP_CONV_R8); +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + gv(RC_ST0); + switch(t) { + case VT_INT | VT_UNSIGNED: + out_op(IL_OP_CONV_U4); + break; + case VT_LLONG: + out_op(IL_OP_CONV_I8); + break; + case VT_LLONG | VT_UNSIGNED: + out_op(IL_OP_CONV_U8); + break; + default: + out_op(IL_OP_CONV_I4); + break; + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + gv(RC_ST0); + if (t == VT_FLOAT) { + out_op(IL_OP_CONV_R4); + } else { + out_op(IL_OP_CONV_R8); + } +} + +/* end of CIL code generator */ +/*************************************************************/ + diff --git a/src/cil/il-opcodes.h b/src/cil/il-opcodes.h new file mode 100644 index 0000000..adf35cf --- /dev/null +++ b/src/cil/il-opcodes.h @@ -0,0 +1,251 @@ +/* + * CIL opcode definition + * + * Copyright (c) 2002 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +OP(NOP, "nop", 0x00) +OP(BREAK, "break", 0x01) +OP(LDARG_0, "ldarg.0", 0x02) +OP(LDARG_1, "ldarg.1", 0x03) +OP(LDARG_2, "ldarg.2", 0x04) +OP(LDARG_3, "ldarg.3", 0x05) +OP(LDLOC_0, "ldloc.0", 0x06) +OP(LDLOC_1, "ldloc.1", 0x07) +OP(LDLOC_2, "ldloc.2", 0x08) +OP(LDLOC_3, "ldloc.3", 0x09) +OP(STLOC_0, "stloc.0", 0x0a) +OP(STLOC_1, "stloc.1", 0x0b) +OP(STLOC_2, "stloc.2", 0x0c) +OP(STLOC_3, "stloc.3", 0x0d) +OP(LDARG_S, "ldarg.s", 0x0e) +OP(LDARGA_S, "ldarga.s", 0x0f) +OP(STARG_S, "starg.s", 0x10) +OP(LDLOC_S, "ldloc.s", 0x11) +OP(LDLOCA_S, "ldloca.s", 0x12) +OP(STLOC_S, "stloc.s", 0x13) +OP(LDNULL, "ldnull", 0x14) +OP(LDC_I4_M1, "ldc.i4.m1", 0x15) +OP(LDC_I4_0, "ldc.i4.0", 0x16) +OP(LDC_I4_1, "ldc.i4.1", 0x17) +OP(LDC_I4_2, "ldc.i4.2", 0x18) +OP(LDC_I4_3, "ldc.i4.3", 0x19) +OP(LDC_I4_4, "ldc.i4.4", 0x1a) +OP(LDC_I4_5, "ldc.i4.5", 0x1b) +OP(LDC_I4_6, "ldc.i4.6", 0x1c) +OP(LDC_I4_7, "ldc.i4.7", 0x1d) +OP(LDC_I4_8, "ldc.i4.8", 0x1e) +OP(LDC_I4_S, "ldc.i4.s", 0x1f) +OP(LDC_I4, "ldc.i4", 0x20) +OP(LDC_I8, "ldc.i8", 0x21) +OP(LDC_R4, "ldc.r4", 0x22) +OP(LDC_R8, "ldc.r8", 0x23) +OP(LDPTR, "ldptr", 0x24) +OP(DUP, "dup", 0x25) +OP(POP, "pop", 0x26) +OP(JMP, "jmp", 0x27) +OP(CALL, "call", 0x28) +OP(CALLI, "calli", 0x29) +OP(RET, "ret", 0x2a) +OP(BR_S, "br.s", 0x2b) +OP(BRFALSE_S, "brfalse.s", 0x2c) +OP(BRTRUE_S, "brtrue.s", 0x2d) +OP(BEQ_S, "beq.s", 0x2e) +OP(BGE_S, "bge.s", 0x2f) +OP(BGT_S, "bgt.s", 0x30) +OP(BLE_S, "ble.s", 0x31) +OP(BLT_S, "blt.s", 0x32) +OP(BNE_UN_S, "bne.un.s", 0x33) +OP(BGE_UN_S, "bge.un.s", 0x34) +OP(BGT_UN_S, "bgt.un.s", 0x35) +OP(BLE_UN_S, "ble.un.s", 0x36) +OP(BLT_UN_S, "blt.un.s", 0x37) +OP(BR, "br", 0x38) +OP(BRFALSE, "brfalse", 0x39) +OP(BRTRUE, "brtrue", 0x3a) +OP(BEQ, "beq", 0x3b) +OP(BGE, "bge", 0x3c) +OP(BGT, "bgt", 0x3d) +OP(BLE, "ble", 0x3e) +OP(BLT, "blt", 0x3f) +OP(BNE_UN, "bne.un", 0x40) +OP(BGE_UN, "bge.un", 0x41) +OP(BGT_UN, "bgt.un", 0x42) +OP(BLE_UN, "ble.un", 0x43) +OP(BLT_UN, "blt.un", 0x44) +OP(SWITCH, "switch", 0x45) +OP(LDIND_I1, "ldind.i1", 0x46) +OP(LDIND_U1, "ldind.u1", 0x47) +OP(LDIND_I2, "ldind.i2", 0x48) +OP(LDIND_U2, "ldind.u2", 0x49) +OP(LDIND_I4, "ldind.i4", 0x4a) +OP(LDIND_U4, "ldind.u4", 0x4b) +OP(LDIND_I8, "ldind.i8", 0x4c) +OP(LDIND_I, "ldind.i", 0x4d) +OP(LDIND_R4, "ldind.r4", 0x4e) +OP(LDIND_R8, "ldind.r8", 0x4f) +OP(LDIND_REF, "ldind.ref", 0x50) +OP(STIND_REF, "stind.ref", 0x51) +OP(STIND_I1, "stind.i1", 0x52) +OP(STIND_I2, "stind.i2", 0x53) +OP(STIND_I4, "stind.i4", 0x54) +OP(STIND_I8, "stind.i8", 0x55) +OP(STIND_R4, "stind.r4", 0x56) +OP(STIND_R8, "stind.r8", 0x57) +OP(ADD, "add", 0x58) +OP(SUB, "sub", 0x59) +OP(MUL, "mul", 0x5a) +OP(DIV, "div", 0x5b) +OP(DIV_UN, "div.un", 0x5c) +OP(REM, "rem", 0x5d) +OP(REM_UN, "rem.un", 0x5e) +OP(AND, "and", 0x5f) +OP(OR, "or", 0x60) +OP(XOR, "xor", 0x61) +OP(SHL, "shl", 0x62) +OP(SHR, "shr", 0x63) +OP(SHR_UN, "shr.un", 0x64) +OP(NEG, "neg", 0x65) +OP(NOT, "not", 0x66) +OP(CONV_I1, "conv.i1", 0x67) +OP(CONV_I2, "conv.i2", 0x68) +OP(CONV_I4, "conv.i4", 0x69) +OP(CONV_I8, "conv.i8", 0x6a) +OP(CONV_R4, "conv.r4", 0x6b) +OP(CONV_R8, "conv.r8", 0x6c) +OP(CONV_U4, "conv.u4", 0x6d) +OP(CONV_U8, "conv.u8", 0x6e) +OP(CALLVIRT, "callvirt", 0x6f) +OP(CPOBJ, "cpobj", 0x70) +OP(LDOBJ, "ldobj", 0x71) +OP(LDSTR, "ldstr", 0x72) +OP(NEWOBJ, "newobj", 0x73) +OP(CASTCLASS, "castclass", 0x74) +OP(ISINST, "isinst", 0x75) +OP(CONV_R_UN, "conv.r.un", 0x76) +OP(ANN_DATA_S, "ann.data.s", 0x77) +OP(UNBOX, "unbox", 0x79) +OP(THROW, "throw", 0x7a) +OP(LDFLD, "ldfld", 0x7b) +OP(LDFLDA, "ldflda", 0x7c) +OP(STFLD, "stfld", 0x7d) +OP(LDSFLD, "ldsfld", 0x7e) +OP(LDSFLDA, "ldsflda", 0x7f) +OP(STSFLD, "stsfld", 0x80) +OP(STOBJ, "stobj", 0x81) +OP(CONV_OVF_I1_UN, "conv.ovf.i1.un", 0x82) +OP(CONV_OVF_I2_UN, "conv.ovf.i2.un", 0x83) +OP(CONV_OVF_I4_UN, "conv.ovf.i4.un", 0x84) +OP(CONV_OVF_I8_UN, "conv.ovf.i8.un", 0x85) +OP(CONV_OVF_U1_UN, "conv.ovf.u1.un", 0x86) +OP(CONV_OVF_U2_UN, "conv.ovf.u2.un", 0x87) +OP(CONV_OVF_U4_UN, "conv.ovf.u4.un", 0x88) +OP(CONV_OVF_U8_UN, "conv.ovf.u8.un", 0x89) +OP(CONV_OVF_I_UN, "conv.ovf.i.un", 0x8a) +OP(CONV_OVF_U_UN, "conv.ovf.u.un", 0x8b) +OP(BOX, "box", 0x8c) +OP(NEWARR, "newarr", 0x8d) +OP(LDLEN, "ldlen", 0x8e) +OP(LDELEMA, "ldelema", 0x8f) +OP(LDELEM_I1, "ldelem.i1", 0x90) +OP(LDELEM_U1, "ldelem.u1", 0x91) +OP(LDELEM_I2, "ldelem.i2", 0x92) +OP(LDELEM_U2, "ldelem.u2", 0x93) +OP(LDELEM_I4, "ldelem.i4", 0x94) +OP(LDELEM_U4, "ldelem.u4", 0x95) +OP(LDELEM_I8, "ldelem.i8", 0x96) +OP(LDELEM_I, "ldelem.i", 0x97) +OP(LDELEM_R4, "ldelem.r4", 0x98) +OP(LDELEM_R8, "ldelem.r8", 0x99) +OP(LDELEM_REF, "ldelem.ref", 0x9a) +OP(STELEM_I, "stelem.i", 0x9b) +OP(STELEM_I1, "stelem.i1", 0x9c) +OP(STELEM_I2, "stelem.i2", 0x9d) +OP(STELEM_I4, "stelem.i4", 0x9e) +OP(STELEM_I8, "stelem.i8", 0x9f) +OP(STELEM_R4, "stelem.r4", 0xa0) +OP(STELEM_R8, "stelem.r8", 0xa1) +OP(STELEM_REF, "stelem.ref", 0xa2) +OP(CONV_OVF_I1, "conv.ovf.i1", 0xb3) +OP(CONV_OVF_U1, "conv.ovf.u1", 0xb4) +OP(CONV_OVF_I2, "conv.ovf.i2", 0xb5) +OP(CONV_OVF_U2, "conv.ovf.u2", 0xb6) +OP(CONV_OVF_I4, "conv.ovf.i4", 0xb7) +OP(CONV_OVF_U4, "conv.ovf.u4", 0xb8) +OP(CONV_OVF_I8, "conv.ovf.i8", 0xb9) +OP(CONV_OVF_U8, "conv.ovf.u8", 0xba) +OP(REFANYVAL, "refanyval", 0xc2) +OP(CKFINITE, "ckfinite", 0xc3) +OP(MKREFANY, "mkrefany", 0xc6) +OP(ANN_CALL, "ann.call", 0xc7) +OP(ANN_CATCH, "ann.catch", 0xc8) +OP(ANN_DEAD, "ann.dead", 0xc9) +OP(ANN_HOISTED, "ann.hoisted", 0xca) +OP(ANN_HOISTED_CALL, "ann.hoisted.call", 0xcb) +OP(ANN_LAB, "ann.lab", 0xcc) +OP(ANN_DEF, "ann.def", 0xcd) +OP(ANN_REF_S, "ann.ref.s", 0xce) +OP(ANN_PHI, "ann.phi", 0xcf) +OP(LDTOKEN, "ldtoken", 0xd0) +OP(CONV_U2, "conv.u2", 0xd1) +OP(CONV_U1, "conv.u1", 0xd2) +OP(CONV_I, "conv.i", 0xd3) +OP(CONV_OVF_I, "conv.ovf.i", 0xd4) +OP(CONV_OVF_U, "conv.ovf.u", 0xd5) +OP(ADD_OVF, "add.ovf", 0xd6) +OP(ADD_OVF_UN, "add.ovf.un", 0xd7) +OP(MUL_OVF, "mul.ovf", 0xd8) +OP(MUL_OVF_UN, "mul.ovf.un", 0xd9) +OP(SUB_OVF, "sub.ovf", 0xda) +OP(SUB_OVF_UN, "sub.ovf.un", 0xdb) +OP(ENDFINALLY, "endfinally", 0xdc) +OP(LEAVE, "leave", 0xdd) +OP(LEAVE_S, "leave.s", 0xde) +OP(STIND_I, "stind.i", 0xdf) +OP(CONV_U, "conv.u", 0xe0) + +/* prefix instructions. we use an opcode >= 256 to ease coding */ + +OP(ARGLIST, "arglist", 0x100) +OP(CEQ, "ceq", 0x101) +OP(CGT, "cgt", 0x102) +OP(CGT_UN, "cgt.un", 0x103) +OP(CLT, "clt", 0x104) +OP(CLT_UN, "clt.un", 0x105) +OP(LDFTN, "ldftn", 0x106) +OP(LDVIRTFTN, "ldvirtftn", 0x107) +OP(JMPI, "jmpi", 0x108) +OP(LDARG, "ldarg", 0x109) +OP(LDARGA, "ldarga", 0x10a) +OP(STARG, "starg", 0x10b) +OP(LDLOC, "ldloc", 0x10c) +OP(LDLOCA, "ldloca", 0x10d) +OP(STLOC, "stloc", 0x10e) +OP(LOCALLOC, "localloc", 0x10f) +OP(ENDFILTER, "endfilter", 0x111) +OP(UNALIGNED, "unaligned", 0x112) +OP(VOLATILE, "volatile", 0x113) +OP(TAIL, "tail", 0x114) +OP(INITOBJ, "initobj", 0x115) +OP(ANN_LIVE, "ann.live", 0x116) +OP(CPBLK, "cpblk", 0x117) +OP(INITBLK, "initblk", 0x118) +OP(ANN_REF, "ann.ref", 0x119) +OP(RETHROW, "rethrow", 0x11a) +OP(SIZEOF, "sizeof", 0x11c) +OP(REFANYTYPE, "refanytype", 0x11d) +OP(ANN_DATA, "ann.data", 0x122) +OP(ANN_ARG, "ann.arg", 0x123) diff --git a/src/coff.h b/src/coff.h new file mode 100644 index 0000000..63edfe2 --- /dev/null +++ b/src/coff.h @@ -0,0 +1,446 @@ +/**************************************************************************/ +/* COFF.H */ +/* COFF data structures and related definitions used by the linker */ +/**************************************************************************/ + +/*------------------------------------------------------------------------*/ +/* COFF FILE HEADER */ +/*------------------------------------------------------------------------*/ +struct filehdr { + unsigned short f_magic; /* magic number */ + unsigned short f_nscns; /* number of sections */ + long f_timdat; /* time & date stamp */ + long f_symptr; /* file pointer to symtab */ + long f_nsyms; /* number of symtab entries */ + unsigned short f_opthdr; /* sizeof(optional hdr) */ + unsigned short f_flags; /* flags */ + unsigned short f_TargetID; /* for C6x = 0x0099 */ + }; + +/*------------------------------------------------------------------------*/ +/* File header flags */ +/*------------------------------------------------------------------------*/ +#define F_RELFLG 0x01 /* relocation info stripped from file */ +#define F_EXEC 0x02 /* file is executable (no unresolved refs) */ +#define F_LNNO 0x04 /* line nunbers stripped from file */ +#define F_LSYMS 0x08 /* local symbols stripped from file */ +#define F_GSP10 0x10 /* 34010 version */ +#define F_GSP20 0x20 /* 34020 version */ +#define F_SWABD 0x40 /* bytes swabbed (in names) */ +#define F_AR16WR 0x80 /* byte ordering of an AR16WR (PDP-11) */ +#define F_LITTLE 0x100 /* byte ordering of an AR32WR (vax) */ +#define F_BIG 0x200 /* byte ordering of an AR32W (3B, maxi) */ +#define F_PATCH 0x400 /* contains "patch" list in optional header */ +#define F_NODF 0x400 + +#define F_VERSION (F_GSP10 | F_GSP20) +#define F_BYTE_ORDER (F_LITTLE | F_BIG) +#define FILHDR struct filehdr + +/* #define FILHSZ sizeof(FILHDR) */ +#define FILHSZ 22 /* above rounds to align on 4 bytes which causes problems */ + +#define COFF_C67_MAGIC 0x00c2 + +/*------------------------------------------------------------------------*/ +/* Macros to recognize magic numbers */ +/*------------------------------------------------------------------------*/ +#define ISMAGIC(x) (((unsigned short)(x))==(unsigned short)magic) +#define ISARCHIVE(x) ((((unsigned short)(x))==(unsigned short)ARTYPE)) +#define BADMAGIC(x) (((unsigned short)(x) & 0x8080) && !ISMAGIC(x)) + + +/*------------------------------------------------------------------------*/ +/* OPTIONAL FILE HEADER */ +/*------------------------------------------------------------------------*/ +typedef struct aouthdr { + short magic; /* see magic.h */ + short vstamp; /* version stamp */ + long tsize; /* text size in bytes, padded to FW bdry*/ + long dsize; /* initialized data " " */ + long bsize; /* uninitialized data " " */ + long entrypt; /* entry pt. */ + long text_start; /* base of text used for this file */ + long data_start; /* base of data used for this file */ +} AOUTHDR; + +#define AOUTSZ sizeof(AOUTHDR) + +/*----------------------------------------------------------------------*/ +/* When a UNIX aout header is to be built in the optional header, */ +/* the following magic numbers can appear in that header: */ +/* */ +/* AOUT1MAGIC : default : readonly sharable text segment */ +/* AOUT2MAGIC: : writable text segment */ +/* PAGEMAGIC : : configured for paging */ +/*----------------------------------------------------------------------*/ +#define AOUT1MAGIC 0410 +#define AOUT2MAGIC 0407 +#define PAGEMAGIC 0413 + + +/*------------------------------------------------------------------------*/ +/* COMMON ARCHIVE FILE STRUCTURES */ +/* */ +/* ARCHIVE File Organization: */ +/* _______________________________________________ */ +/* |__________ARCHIVE_MAGIC_STRING_______________| */ +/* |__________ARCHIVE_FILE_MEMBER_1______________| */ +/* | | */ +/* | Archive File Header "ar_hdr" | */ +/* |.............................................| */ +/* | Member Contents | */ +/* | 1. External symbol directory | */ +/* | 2. Text file | */ +/* |_____________________________________________| */ +/* |________ARCHIVE_FILE_MEMBER_2________________| */ +/* | "ar_hdr" | */ +/* |.............................................| */ +/* | Member Contents (.o or text file) | */ +/* |_____________________________________________| */ +/* | . . . | */ +/* | . . . | */ +/* | . . . | */ +/* |_____________________________________________| */ +/* |________ARCHIVE_FILE_MEMBER_n________________| */ +/* | "ar_hdr" | */ +/* |.............................................| */ +/* | Member Contents | */ +/* |_____________________________________________| */ +/* */ +/*------------------------------------------------------------------------*/ + +#define COFF_ARMAG "!<arch>\n" +#define SARMAG 8 +#define ARFMAG "`\n" + +struct ar_hdr /* archive file member header - printable ascii */ +{ + char ar_name[16]; /* file member name - `/' terminated */ + char ar_date[12]; /* file member date - decimal */ + char ar_uid[6]; /* file member user id - decimal */ + char ar_gid[6]; /* file member group id - decimal */ + char ar_mode[8]; /* file member mode - octal */ + char ar_size[10]; /* file member size - decimal */ + char ar_fmag[2]; /* ARFMAG - string to end header */ +}; + + +/*------------------------------------------------------------------------*/ +/* SECTION HEADER */ +/*------------------------------------------------------------------------*/ +struct scnhdr { + char s_name[8]; /* section name */ + long s_paddr; /* physical address */ + long s_vaddr; /* virtual address */ + long s_size; /* section size */ + long s_scnptr; /* file ptr to raw data for section */ + long s_relptr; /* file ptr to relocation */ + long s_lnnoptr; /* file ptr to line numbers */ + unsigned int s_nreloc; /* number of relocation entries */ + unsigned int s_nlnno; /* number of line number entries */ + unsigned int s_flags; /* flags */ + unsigned short s_reserved; /* reserved byte */ + unsigned short s_page; /* memory page id */ + }; + +#define SCNHDR struct scnhdr +#define SCNHSZ sizeof(SCNHDR) + +/*------------------------------------------------------------------------*/ +/* Define constants for names of "special" sections */ +/*------------------------------------------------------------------------*/ +/* #define _TEXT ".text" */ +#define _DATA ".data" +#define _BSS ".bss" +#define _CINIT ".cinit" +#define _TV ".tv" + +/*------------------------------------------------------------------------*/ +/* The low 4 bits of s_flags is used as a section "type" */ +/*------------------------------------------------------------------------*/ +#define STYP_REG 0x00 /* "regular" : allocated, relocated, loaded */ +#define STYP_DSECT 0x01 /* "dummy" : not allocated, relocated, not loaded */ +#define STYP_NOLOAD 0x02 /* "noload" : allocated, relocated, not loaded */ +#define STYP_GROUP 0x04 /* "grouped" : formed of input sections */ +#define STYP_PAD 0x08 /* "padding" : not allocated, not relocated, loaded */ +#define STYP_COPY 0x10 /* "copy" : used for C init tables - + not allocated, relocated, + loaded; reloc & lineno + entries processed normally */ +#define STYP_TEXT 0x20 /* section contains text only */ +#define STYP_DATA 0x40 /* section contains data only */ +#define STYP_BSS 0x80 /* section contains bss only */ + +#define STYP_ALIGN 0x100 /* align flag passed by old version assemblers */ +#define ALIGN_MASK 0x0F00 /* part of s_flags that is used for align vals */ +#define ALIGNSIZE(x) (1 << ((x & ALIGN_MASK) >> 8)) + + +/*------------------------------------------------------------------------*/ +/* RELOCATION ENTRIES */ +/*------------------------------------------------------------------------*/ +struct reloc +{ + long r_vaddr; /* (virtual) address of reference */ + short r_symndx; /* index into symbol table */ + unsigned short r_disp; /* additional bits for address calculation */ + unsigned short r_type; /* relocation type */ +}; + +#define RELOC struct reloc +#define RELSZ 10 /* sizeof(RELOC) */ + +/*--------------------------------------------------------------------------*/ +/* define all relocation types */ +/*--------------------------------------------------------------------------*/ + +#define R_ABS 0 /* absolute address - no relocation */ +#define R_DIR16 01 /* UNUSED */ +#define R_REL16 02 /* UNUSED */ +#define R_DIR24 04 /* UNUSED */ +#define R_REL24 05 /* 24 bits, direct */ +#define R_DIR32 06 /* UNUSED */ +#define R_RELBYTE 017 /* 8 bits, direct */ +#define R_RELWORD 020 /* 16 bits, direct */ +#define R_RELLONG 021 /* 32 bits, direct */ +#define R_PCRBYTE 022 /* 8 bits, PC-relative */ +#define R_PCRWORD 023 /* 16 bits, PC-relative */ +#define R_PCRLONG 024 /* 32 bits, PC-relative */ +#define R_OCRLONG 030 /* GSP: 32 bits, one's complement direct */ +#define R_GSPPCR16 031 /* GSP: 16 bits, PC relative (in words) */ +#define R_GSPOPR32 032 /* GSP: 32 bits, direct big-endian */ +#define R_PARTLS16 040 /* Brahma: 16 bit offset of 24 bit address*/ +#define R_PARTMS8 041 /* Brahma: 8 bit page of 24 bit address */ +#define R_PARTLS7 050 /* DSP: 7 bit offset of 16 bit address */ +#define R_PARTMS9 051 /* DSP: 9 bit page of 16 bit address */ +#define R_REL13 052 /* DSP: 13 bits, direct */ + + +/*------------------------------------------------------------------------*/ +/* LINE NUMBER ENTRIES */ +/*------------------------------------------------------------------------*/ +struct lineno +{ + union + { + long l_symndx ; /* sym. table index of function name + iff l_lnno == 0 */ + long l_paddr ; /* (physical) address of line number */ + } l_addr ; + unsigned short l_lnno ; /* line number */ +}; + +#define LINENO struct lineno +#define LINESZ 6 /* sizeof(LINENO) */ + + +/*------------------------------------------------------------------------*/ +/* STORAGE CLASSES */ +/*------------------------------------------------------------------------*/ +#define C_EFCN -1 /* physical end of function */ +#define C_NULL 0 +#define C_AUTO 1 /* automatic variable */ +#define C_EXT 2 /* external symbol */ +#define C_STAT 3 /* static */ +#define C_REG 4 /* register variable */ +#define C_EXTDEF 5 /* external definition */ +#define C_LABEL 6 /* label */ +#define C_ULABEL 7 /* undefined label */ +#define C_MOS 8 /* member of structure */ +#define C_ARG 9 /* function argument */ +#define C_STRTAG 10 /* structure tag */ +#define C_MOU 11 /* member of union */ +#define C_UNTAG 12 /* union tag */ +#define C_TPDEF 13 /* type definition */ +#define C_USTATIC 14 /* undefined static */ +#define C_ENTAG 15 /* enumeration tag */ +#define C_MOE 16 /* member of enumeration */ +#define C_REGPARM 17 /* register parameter */ +#define C_FIELD 18 /* bit field */ + +#define C_BLOCK 100 /* ".bb" or ".eb" */ +#define C_FCN 101 /* ".bf" or ".ef" */ +#define C_EOS 102 /* end of structure */ +#define C_FILE 103 /* file name */ +#define C_LINE 104 /* dummy sclass for line number entry */ +#define C_ALIAS 105 /* duplicate tag */ +#define C_HIDDEN 106 /* special storage class for external */ + /* symbols in dmert public libraries */ + +/*------------------------------------------------------------------------*/ +/* SYMBOL TABLE ENTRIES */ +/*------------------------------------------------------------------------*/ + +#define SYMNMLEN 8 /* Number of characters in a symbol name */ +#define FILNMLEN 14 /* Number of characters in a file name */ +#define DIMNUM 4 /* Number of array dimensions in auxiliary entry */ + + +struct syment +{ + union + { + char _n_name[SYMNMLEN]; /* old COFF version */ + struct + { + long _n_zeroes; /* new == 0 */ + long _n_offset; /* offset into string table */ + } _n_n; + char *_n_nptr[2]; /* allows for overlaying */ + } _n; + long n_value; /* value of symbol */ + short n_scnum; /* section number */ + unsigned short n_type; /* type and derived type */ + char n_sclass; /* storage class */ + char n_numaux; /* number of aux. entries */ +}; + +#define n_name _n._n_name +#define n_nptr _n._n_nptr[1] +#define n_zeroes _n._n_n._n_zeroes +#define n_offset _n._n_n._n_offset + +/*------------------------------------------------------------------------*/ +/* Relocatable symbols have a section number of the */ +/* section in which they are defined. Otherwise, section */ +/* numbers have the following meanings: */ +/*------------------------------------------------------------------------*/ +#define N_UNDEF 0 /* undefined symbol */ +#define N_ABS -1 /* value of symbol is absolute */ +#define N_DEBUG -2 /* special debugging symbol */ +#define N_TV (unsigned short)-3 /* needs transfer vector (preload) */ +#define P_TV (unsigned short)-4 /* needs transfer vector (postload) */ + + +/*------------------------------------------------------------------------*/ +/* The fundamental type of a symbol packed into the low */ +/* 4 bits of the word. */ +/*------------------------------------------------------------------------*/ +#define _EF ".ef" + +#define T_NULL 0 /* no type info */ +#define T_ARG 1 /* function argument (only used by compiler) */ +#define T_CHAR 2 /* character */ +#define T_SHORT 3 /* short integer */ +#define T_INT 4 /* integer */ +#define T_LONG 5 /* long integer */ +#define T_FLOAT 6 /* floating point */ +#define T_DOUBLE 7 /* double word */ +#define T_STRUCT 8 /* structure */ +#define T_UNION 9 /* union */ +#define T_ENUM 10 /* enumeration */ +#define T_MOE 11 /* member of enumeration */ +#define T_UCHAR 12 /* unsigned character */ +#define T_USHORT 13 /* unsigned short */ +#define T_UINT 14 /* unsigned integer */ +#define T_ULONG 15 /* unsigned long */ + +/*------------------------------------------------------------------------*/ +/* derived types are: */ +/*------------------------------------------------------------------------*/ +#define DT_NON 0 /* no derived type */ +#define DT_PTR 1 /* pointer */ +#define DT_FCN 2 /* function */ +#define DT_ARY 3 /* array */ + +#define MKTYPE(basic, d1,d2,d3,d4,d5,d6) \ + ((basic) | ((d1) << 4) | ((d2) << 6) | ((d3) << 8) |\ + ((d4) << 10) | ((d5) << 12) | ((d6) << 14)) + +/*------------------------------------------------------------------------*/ +/* type packing constants and macros */ +/*------------------------------------------------------------------------*/ +#define N_BTMASK_COFF 017 +#define N_TMASK_COFF 060 +#define N_TMASK1_COFF 0300 +#define N_TMASK2_COFF 0360 +#define N_BTSHFT_COFF 4 +#define N_TSHIFT_COFF 2 + +#define BTYPE_COFF(x) ((x) & N_BTMASK_COFF) +#define ISINT(x) (((x) >= T_CHAR && (x) <= T_LONG) || \ + ((x) >= T_UCHAR && (x) <= T_ULONG) || (x) == T_ENUM) +#define ISFLT_COFF(x) ((x) == T_DOUBLE || (x) == T_FLOAT) +#define ISPTR_COFF(x) (((x) & N_TMASK_COFF) == (DT_PTR << N_BTSHFT_COFF)) +#define ISFCN_COFF(x) (((x) & N_TMASK_COFF) == (DT_FCN << N_BTSHFT_COFF)) +#define ISARY_COFF(x) (((x) & N_TMASK_COFF) == (DT_ARY << N_BTSHFT_COFF)) +#define ISTAG_COFF(x) ((x)==C_STRTAG || (x)==C_UNTAG || (x)==C_ENTAG) + +#define INCREF_COFF(x) ((((x)&~N_BTMASK_COFF)<<N_TSHIFT_COFF)|(DT_PTR<<N_BTSHFT_COFF)|(x&N_BTMASK_COFF)) +#define DECREF_COFF(x) ((((x)>>N_TSHIFT_COFF)&~N_BTMASK_COFF)|((x)&N_BTMASK_COFF)) + + +/*------------------------------------------------------------------------*/ +/* AUXILIARY SYMBOL ENTRY */ +/*------------------------------------------------------------------------*/ +union auxent +{ + struct + { + long x_tagndx; /* str, un, or enum tag indx */ + union + { + struct + { + unsigned short x_lnno; /* declaration line number */ + unsigned short x_size; /* str, union, array size */ + } x_lnsz; + long x_fsize; /* size of function */ + } x_misc; + union + { + struct /* if ISFCN, tag, or .bb */ + { + long x_lnnoptr; /* ptr to fcn line # */ + long x_endndx; /* entry ndx past block end */ + } x_fcn; + struct /* if ISARY, up to 4 dimen. */ + { + unsigned short x_dimen[DIMNUM]; + } x_ary; + } x_fcnary; + unsigned short x_regcount; /* number of registers used by func */ + } x_sym; + struct + { + char x_fname[FILNMLEN]; + } x_file; + struct + { + long x_scnlen; /* section length */ + unsigned short x_nreloc; /* number of relocation entries */ + unsigned short x_nlinno; /* number of line numbers */ + } x_scn; +}; + +#define SYMENT struct syment +#define SYMESZ 18 /* sizeof(SYMENT) */ + +#define AUXENT union auxent +#define AUXESZ 18 /* sizeof(AUXENT) */ + +/*------------------------------------------------------------------------*/ +/* NAMES OF "SPECIAL" SYMBOLS */ +/*------------------------------------------------------------------------*/ +#define _STEXT ".text" +#define _ETEXT "etext" +#define _SDATA ".data" +#define _EDATA "edata" +#define _SBSS ".bss" +#define _END "end" +#define _CINITPTR "cinit" + +/*--------------------------------------------------------------------------*/ +/* ENTRY POINT SYMBOLS */ +/*--------------------------------------------------------------------------*/ +#define _START "_start" +#define _MAIN "_main" + /* _CSTART "_c_int00" (defined in params.h) */ + + +#define _TVORIG "_tvorig" +#define _TORIGIN "_torigin" +#define _DORIGIN "_dorigin" + +#define _SORIGIN "_sorigin" diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..c9e1a18 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,7 @@ +#define CONFIG_TCCDIR "${CONFIG_TCCDIR}" +#define TCC_VERSION "${TCC_VERSION}" + +#cmakedefine CONFIG_WIN32 +#cmakedefine CONFIG_WIN64 +#cmakedefine CONFIG_TCC_BCHECK +#cmakedefine CONFIG_TCC_ASSERT diff --git a/src/conftest.c b/src/conftest.c new file mode 100644 index 0000000..fa07a1b --- /dev/null +++ b/src/conftest.c @@ -0,0 +1,77 @@ +#include <stdio.h> + +/* Define architecture */ +#if defined(__i386__) +# define TRIPLET_ARCH "i386" +#elif defined(__x86_64__) +# define TRIPLET_ARCH "x86_64" +#elif defined(__arm__) +# define TRIPLET_ARCH "arm" +#elif defined(__aarch64__) +# define TRIPLET_ARCH "aarch64" +#else +# define TRIPLET_ARCH "unknown" +#endif + +/* Define OS */ +#if defined (__linux__) +# define TRIPLET_OS "linux" +#elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +# define TRIPLET_OS "kfreebsd" +#elif !defined (__GNU__) +# define TRIPLET_OS "unknown" +#endif + +/* Define calling convention and ABI */ +#if defined (__ARM_EABI__) +# if defined (__ARM_PCS_VFP) +# define TRIPLET_ABI "gnueabihf" +# else +# define TRIPLET_ABI "gnueabi" +# endif +#else +# define TRIPLET_ABI "gnu" +#endif + +#ifdef __GNU__ +# define TRIPLET TRIPLET_ARCH "-" TRIPLET_ABI +#else +# define TRIPLET TRIPLET_ARCH "-" TRIPLET_OS "-" TRIPLET_ABI +#endif + +#if defined(_WIN32) +int _CRT_glob = 0; +#endif + +int main(int argc, char *argv[]) +{ + switch(argc == 2 ? argv[1][0] : 0) { + case 'b': + { + volatile unsigned foo = 0x01234567; + puts(*(unsigned char*)&foo == 0x67 ? "no" : "yes"); + break; + } +#ifdef __GNUC__ + case 'm': + printf("%d\n", __GNUC_MINOR__); + break; + case 'v': + printf("%d\n", __GNUC__); + break; +#else + case 'm': + case 'v': + puts("0"); + break; +#endif + case 't': + puts(TRIPLET); + break; + case -1: + /* to test -Wno-unused-result */ + fread(NULL, 1, 1, NULL); + break; + } + return 0; +} diff --git a/src/elf.h b/src/elf.h new file mode 100644 index 0000000..a40c736 --- /dev/null +++ b/src/elf.h @@ -0,0 +1,3228 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2012 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* 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_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#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_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* 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_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#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_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#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_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_AARCH64 183 /* ARM AARCH64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_NUM 192 + +/* 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_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#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_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#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_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* 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; /* Symbol visibility */ + 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; /* Symbol visibility */ + 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 + + +/* 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_GNU_UNIQUE 10 /* Unique symbol. */ +#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_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#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) ((((Elf64_Xword) (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; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* 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_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#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_MASKOS 0x0ff00000 /* OS-specific */ +#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_TASKSTRUCT 4 /* Contains copy of task structure */ +#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_ASRS 8 /* Contains copy of asrset 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 */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ + +/* 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 (deprecated) */ +#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_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* 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_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#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 +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* 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_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* 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 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* 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. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditin required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* 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 */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* 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 +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; /* Entry type */ + union + { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } 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 */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* 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 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* 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 NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* 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 */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* 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 */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +/* 38? */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +/* Keep this the last entry. */ +#define R_386_NUM 43 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* 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_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* 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 /* 10bit with secondary 13bit addend */ +#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_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#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 */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* 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. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 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. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 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_PLT 0x8 +#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 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +/* Keep this the last entry. */ +#define R_MIPS_NUM 128 + +/* 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. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +#define DT_MIPS_NUM 0x35 + +/* 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_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* 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_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* 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_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#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 */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* 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 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ + +/* 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 */ + +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_NUM 1 + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_NUM 3 + + +/* 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_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* 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. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD64 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL64 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL64 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ + +/* 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 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#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_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_THM_JUMP24 30 +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_V4BX 40 +#define R_ARM_PREL31 42 +#define R_ARM_MOVW_ABS_NC 43 +#define R_ARM_MOVT_ABS 44 +#define R_ARM_THM_MOVW_ABS_NC 47 +#define R_ARM_THM_MOVT_ABS 48 +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 +#define R_ARM_THM_TLS_CALL 93 +#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_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_IRELATIVE 160 +#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 */ + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ + +#define R_X86_64_NUM 39 + + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ + +#define R_M32R_GOT24 48 /* 24 bit GOT entry */ +#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + + +#endif /* elf.h */ diff --git a/src/libtcc.c b/src/libtcc.c new file mode 100644 index 0000000..f600280 --- /dev/null +++ b/src/libtcc.c @@ -0,0 +1,2322 @@ +/* + * 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 + */ + +#include "tcc.h" + +/********************************************************/ +/* global variables */ + +/* use GNU C extensions */ +ST_DATA int gnu_ext = 1; + +/* use TinyCC extensions */ +ST_DATA int tcc_ext = 1; + +/* XXX: get rid of this ASAP */ +ST_DATA struct TCCState *tcc_state; + +/********************************************************/ + +#ifdef ONE_SOURCE +#include "tccpp.c" +#include "tccgen.c" +#include "tccelf.c" +#include "tccrun.c" +#ifdef TCC_TARGET_I386 +#include "x86/i386-gen.c" +#endif +#ifdef TCC_TARGET_ARM +#include "arm/arm-gen.c" +#endif +#ifdef TCC_TARGET_ARM64 +#include "arm/arm64-gen.c" +#endif +#ifdef TCC_TARGET_C67 +#include "tms320c67/c67-gen.c" +#endif +#ifdef TCC_TARGET_X86_64 +#include "x86/x86_64-gen.c" +#endif +#ifdef CONFIG_TCC_ASM +#include "tccasm.c" +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +#include "x86/i386-asm.c" +#endif +#endif +#ifdef TCC_TARGET_COFF +#include "tcccoff.c" +#endif +#ifdef TCC_TARGET_PE +#include "tccpe.c" +#endif +#endif /* ONE_SOURCE */ + +/********************************************************/ +#ifndef CONFIG_TCC_ASM +ST_FUNC void asm_instr(void) +{ + tcc_error("inline asm() not supported"); +} +ST_FUNC void asm_global_instr(void) +{ + tcc_error("inline asm() not supported"); +} +#endif + +/********************************************************/ +#ifdef _WIN32 +static char *normalize_slashes(char *path) +{ + char *p; + for (p = path; *p; ++p) + if (*p == '\\') + *p = '/'; + return path; +} + +static HMODULE tcc_module; + +/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */ +static void tcc_set_lib_path_w32(TCCState *s) +{ + char path[1024], *p; + GetModuleFileNameA(tcc_module, path, sizeof path); + p = tcc_basename(normalize_slashes(strlwr(path))); + if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5)) + p -= 5; + else if (p > path) + p--; + *p = 0; + tcc_set_lib_path(s, path); +} + +#ifdef TCC_TARGET_PE +static void tcc_add_systemdir(TCCState *s) +{ + char buf[1000]; + GetSystemDirectory(buf, sizeof buf); + tcc_add_library_path(s, normalize_slashes(buf)); +} +#endif + +#ifndef CONFIG_TCC_STATIC +void dlclose(void *p) +{ + FreeLibrary((HMODULE)p); +} +#endif + +#ifdef LIBTCC_AS_DLL +BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) +{ + if (DLL_PROCESS_ATTACH == dwReason) + tcc_module = hDll; + return TRUE; +} +#endif +#endif + +/********************************************************/ +/* copy a string and truncate it. */ +PUB_FUNC 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. */ +PUB_FUNC 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; +} + +PUB_FUNC char *pstrncpy(char *out, const char *in, size_t num) +{ + memcpy(out, in, num); + out[num] = '\0'; + return out; +} + +/* extract the basename of a file */ +PUB_FUNC char *tcc_basename(const char *name) +{ + char *p = strchr(name, 0); + while (p > name && !IS_DIRSEP(p[-1])) + --p; + return p; +} + +/* extract extension part of a file + * + * (if no extension, return pointer to end-of-string) + */ +PUB_FUNC char *tcc_fileextension (const char *name) +{ + char *b = tcc_basename(name); + char *e = strrchr(b, '.'); + return e ? e : strchr(b, 0); +} + +/********************************************************/ +/* memory management */ + +#undef free +#undef malloc +#undef realloc + +#ifndef MEM_DEBUG + +PUB_FUNC void tcc_free(void *ptr) +{ + free(ptr); +} + +PUB_FUNC void *tcc_malloc(unsigned long size) +{ + void *ptr; + ptr = malloc(size); + if (!ptr && size) + tcc_error("memory full (malloc)"); + return ptr; +} + +PUB_FUNC void *tcc_mallocz(unsigned long size) +{ + void *ptr; + ptr = tcc_malloc(size); + memset(ptr, 0, size); + return ptr; +} + +PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size) +{ + void *ptr1; + ptr1 = realloc(ptr, size); + if (!ptr1 && size) + tcc_error("memory full (realloc)"); + return ptr1; +} + +PUB_FUNC char *tcc_strdup(const char *str) +{ + char *ptr; + ptr = tcc_malloc(strlen(str) + 1); + strcpy(ptr, str); + return ptr; +} + +PUB_FUNC void tcc_memstats(int bench) +{ +} + +#else + +#define MEM_DEBUG_MAGIC1 0xFEEDDEB1 +#define MEM_DEBUG_MAGIC2 0xFEEDDEB2 +#define MEM_DEBUG_FILE_LEN 15 + +struct mem_debug_header { + size_t magic1; + size_t size; + struct mem_debug_header *prev; + struct mem_debug_header *next; + size_t line_num; + char file_name[MEM_DEBUG_FILE_LEN + 1]; + size_t magic2; +}; + +typedef struct mem_debug_header mem_debug_header_t; + +static mem_debug_header_t *mem_debug_chain; +static size_t mem_cur_size; +static size_t mem_max_size; + +PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line) +{ + void *ptr; + mem_debug_header_t *header; + + ptr = malloc(sizeof(mem_debug_header_t) + size); + if (!ptr) + tcc_error("memory full (malloc)"); + + mem_cur_size += size; + if (mem_cur_size > mem_max_size) + mem_max_size = mem_cur_size; + + header = (mem_debug_header_t *)ptr; + + header->magic1 = MEM_DEBUG_MAGIC1; + header->magic2 = MEM_DEBUG_MAGIC2; + header->size = size; + header->line_num = line; + + strncpy(header->file_name, file, MEM_DEBUG_FILE_LEN); + header->file_name[MEM_DEBUG_FILE_LEN] = 0; + + header->next = mem_debug_chain; + header->prev = NULL; + + if (header->next) + header->next->prev = header; + + mem_debug_chain = header; + + ptr = (char *)ptr + sizeof(mem_debug_header_t); + return ptr; +} + +PUB_FUNC void tcc_free_debug(void *ptr) +{ + mem_debug_header_t *header; + + if (!ptr) + return; + + ptr = (char *)ptr - sizeof(mem_debug_header_t); + header = (mem_debug_header_t *)ptr; + if (header->magic1 != MEM_DEBUG_MAGIC1 || + header->magic2 != MEM_DEBUG_MAGIC2 || + header->size == (size_t)-1 ) + { + tcc_error("tcc_free check failed"); + } + + mem_cur_size -= header->size; + header->size = (size_t)-1; + + if (header->next) + header->next->prev = header->prev; + + if (header->prev) + header->prev->next = header->next; + + if (header == mem_debug_chain) + mem_debug_chain = header->next; + + free(ptr); +} + + +PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line) +{ + void *ptr; + ptr = tcc_malloc_debug(size,file,line); + memset(ptr, 0, size); + return ptr; +} + +PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line) +{ + mem_debug_header_t *header; + int mem_debug_chain_update = 0; + + if (!ptr) { + ptr = tcc_malloc_debug(size, file, line); + return ptr; + } + + ptr = (char *)ptr - sizeof(mem_debug_header_t); + header = (mem_debug_header_t *)ptr; + if (header->magic1 != MEM_DEBUG_MAGIC1 || + header->magic2 != MEM_DEBUG_MAGIC2 || + header->size == (size_t)-1 ) + { + check_error: + tcc_error("tcc_realloc check failed"); + } + + mem_debug_chain_update = (header == mem_debug_chain); + + mem_cur_size -= header->size; + ptr = realloc(ptr, sizeof(mem_debug_header_t) + size); + if (!ptr) + tcc_error("memory full (realloc)"); + + header = (mem_debug_header_t *)ptr; + if (header->magic1 != MEM_DEBUG_MAGIC1 || + header->magic2 != MEM_DEBUG_MAGIC2) + { + goto check_error; + } + + mem_cur_size += size; + if (mem_cur_size > mem_max_size) + mem_max_size = mem_cur_size; + + header->size = size; + if (header->next) + header->next->prev = header; + + if (header->prev) + header->prev->next = header; + + if (mem_debug_chain_update) + mem_debug_chain = header; + + ptr = (char *)ptr + sizeof(mem_debug_header_t); + return ptr; +} + +PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line) +{ + char *ptr; + ptr = tcc_malloc_debug(strlen(str) + 1, file, line); + strcpy(ptr, str); + return ptr; +} + +PUB_FUNC void tcc_memstats(int bench) +{ + if (mem_cur_size) { + mem_debug_header_t *header = mem_debug_chain; + + fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n", + mem_cur_size, mem_max_size); + + while (header) { + fprintf(stderr, " file %s, line %u: %u bytes\n", + header->file_name, header->line_num, header->size); + header = header->next; + } + } + else if (bench) + fprintf(stderr, "mem_max_size= %d bytes\n", mem_max_size); +} + +#undef MEM_DEBUG_MAGIC1 +#undef MEM_DEBUG_MAGIC2 +#undef MEM_DEBUG_FILE_LEN + +#endif + +#define free(p) use_tcc_free(p) +#define malloc(s) use_tcc_malloc(s) +#define realloc(p, s) use_tcc_realloc(p, s) + +/********************************************************/ +/* dynarrays */ + +ST_FUNC 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 *)); + *ptab = pp; + } + pp[nb++] = data; + *nb_ptr = nb; +} + +ST_FUNC void dynarray_reset(void *pp, int *n) +{ + void **p; + for (p = *(void***)pp; *n; ++p, --*n) + if (*p) + tcc_free(*p); + tcc_free(*(void**)pp); + *(void**)pp = NULL; +} + +static void tcc_split_path(TCCState *s, void ***p_ary, int *p_nb_ary, const char *in) +{ + const char *p; + do { + int c; + CString str; + + cstr_new(&str); + for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) { + if (c == '{' && p[1] && p[2] == '}') { + c = p[1], p += 2; + if (c == 'B') + cstr_cat(&str, s->tcc_lib_path); + } else { + cstr_ccat(&str, c); + } + } + cstr_ccat(&str, '\0'); + dynarray_add(p_ary, p_nb_ary, str.data); + in = p+1; + } while (*p); +} + +/********************************************************/ + +ST_FUNC 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_RELA: + 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; + } + + if (sh_flags & SHF_PRIVATE) { + dynarray_add((void ***)&s1->priv_sections, &s1->nb_priv_sections, sec); + } else { + 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); +} + +/* realloc section and set its content to zero */ +ST_FUNC 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); + 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. */ +ST_FUNC void *section_ptr_add(Section *sec, addr_t size) +{ + size_t 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; +} + +/* reserve at least 'size' bytes from section start */ +ST_FUNC void section_reserve(Section *sec, unsigned long size) +{ + if (size > sec->data_allocated) + section_realloc(sec, size); + if (size > sec->data_offset) + sec->data_offset = size; +} + +/* return a reference to a section, and create it if it does not + exists */ +ST_FUNC 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); +} + +/* update sym->c so that it points to an external symbol in section + 'section' with value 'value' */ +ST_FUNC void put_extern_sym2(Sym *sym, Section *section, + addr_t value, unsigned long size, + int can_add_underscore) +{ + int sym_type, sym_bind, sh_num, info, other; + ElfW(Sym) *esym; + const char *name; + char buf1[256]; + +#ifdef CONFIG_TCC_BCHECK + char buf[32]; +#endif + + if (section == NULL) + sh_num = SHN_UNDEF; + else if (section == SECTION_ABS) + sh_num = SHN_ABS; + else + sh_num = section->sh_num; + + if ((sym->type.t & VT_BTYPE) == VT_FUNC) { + sym_type = STT_FUNC; + } else if ((sym->type.t & VT_BTYPE) == VT_VOID) { + sym_type = STT_NOTYPE; + } else { + sym_type = STT_OBJECT; + } + + if (sym->type.t & VT_STATIC) + sym_bind = STB_LOCAL; + else { + if (sym->type.t & VT_WEAK) + sym_bind = STB_WEAK; + else + sym_bind = STB_GLOBAL; + } + + if (!sym->c) { + name = get_tok_str(sym->v, NULL); +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) { + /* XXX: avoid doing that for statics ? */ + /* if bound checking is activated, we change some function + names by adding the "__bound" prefix */ + switch(sym->v) { +#ifdef TCC_TARGET_PE + /* 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: + case TOK_alloca: + strcpy(buf, "__bound_"); + strcat(buf, name); + name = buf; + break; + } + } +#endif + other = 0; + +#ifdef TCC_TARGET_PE + if (sym->type.t & VT_EXPORT) + other |= ST_PE_EXPORT; + if (sym_type == STT_FUNC && sym->type.ref) { + Sym *ref = sym->type.ref; + if (ref->a.func_export) + other |= ST_PE_EXPORT; + if (ref->a.func_call == FUNC_STDCALL && can_add_underscore) { + sprintf(buf1, "_%s@%d", name, ref->a.func_args * PTR_SIZE); + name = buf1; + other |= ST_PE_STDCALL; + can_add_underscore = 0; + } + } else { + if (find_elf_sym(tcc_state->dynsymtab_section, name)) + other |= ST_PE_IMPORT; + if (sym->type.t & VT_IMPORT) + other |= ST_PE_IMPORT; + } +#else + if (! (sym->type.t & VT_STATIC)) + other = (sym->type.t & VT_VIS_MASK) >> VT_VIS_SHIFT; +#endif + if (tcc_state->leading_underscore && can_add_underscore) { + buf1[0] = '_'; + pstrcpy(buf1 + 1, sizeof(buf1) - 1, name); + name = buf1; + } + if (sym->asm_label) { + name = sym->asm_label; + } + info = ELFW(ST_INFO)(sym_bind, sym_type); + sym->c = add_elf_sym(symtab_section, value, size, info, other, sh_num, name); + } else { + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + esym->st_value = value; + esym->st_size = size; + esym->st_shndx = sh_num; + } +} + +ST_FUNC void put_extern_sym(Sym *sym, Section *section, + addr_t value, unsigned long size) +{ + put_extern_sym2(sym, section, value, size, 1); +} + +/* add a new relocation entry to symbol 'sym' in section 's' */ +ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, + addr_t addend) +{ + int c = 0; + if (sym) { + if (0 == sym->c) + put_extern_sym(sym, NULL, 0, 0); + c = sym->c; + } + /* now we can add ELF relocation info */ + put_elf_reloca(symtab_section, s, offset, type, c, addend); +} + +ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type) +{ + greloca(s, sym, offset, type, 0); +} + +/********************************************************/ + +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); +} + +static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) +{ + char buf[2048]; + BufferedFile **pf, *f; + + buf[0] = '\0'; + /* use upper file if inline ":asm:" or token ":paste:" */ + for (f = file; f && f->filename[0] == ':'; f = f->prev) + ; + if (f) { + for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++) + strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n", + (*pf)->filename, (*pf)->line_num); + if (f->line_num > 0) { + strcat_printf(buf, sizeof(buf), "%s:%d: ", + f->filename, f->line_num); + } else { + strcat_printf(buf, sizeof(buf), "%s: ", + f->filename); + } + } else { + strcat_printf(buf, sizeof(buf), "tcc: "); + } + if (is_warning) + strcat_printf(buf, sizeof(buf), "warning: "); + else + strcat_printf(buf, sizeof(buf), "error: "); + strcat_vprintf(buf, sizeof(buf), fmt, ap); + + if (!s1->error_func) { + /* default case: stderr */ + if (s1->ppfp) /* print a newline during tcc -E */ + fprintf(s1->ppfp, "\n"), fflush(s1->ppfp); + fprintf(stderr, "%s\n", buf); + fflush(stderr); /* print error/warning now (win32) */ + } else { + s1->error_func(s1->error_opaque, buf); + } + if (!is_warning || s1->warn_error) + s1->nb_errors++; +} + +LIBTCCAPI 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; +} + +/* error without aborting current compilation */ +PUB_FUNC void tcc_error_noabort(const char *fmt, ...) +{ + TCCState *s1 = tcc_state; + va_list ap; + + va_start(ap, fmt); + error1(s1, 0, fmt, ap); + va_end(ap); +} + +PUB_FUNC void tcc_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); + } +} + +PUB_FUNC void tcc_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); +} + +/********************************************************/ +/* I/O layer */ + +ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen) +{ + BufferedFile *bf; + int buflen = initlen ? initlen : IO_BUF_SIZE; + + bf = tcc_mallocz(sizeof(BufferedFile) + buflen); + bf->buf_ptr = bf->buffer; + bf->buf_end = bf->buffer + initlen; + bf->buf_end[0] = CH_EOB; /* put eob symbol */ + pstrcpy(bf->filename, sizeof(bf->filename), filename); +#ifdef _WIN32 + normalize_slashes(bf->filename); +#endif + bf->line_num = 1; + bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; + bf->fd = -1; + bf->prev = file; + file = bf; +} + +ST_FUNC void tcc_close(void) +{ + BufferedFile *bf = file; + if (bf->fd > 0) { + close(bf->fd); + total_lines += bf->line_num; + } + file = bf->prev; + tcc_free(bf); +} + +ST_FUNC int tcc_open(TCCState *s1, const char *filename) +{ + int fd; + if (strcmp(filename, "-") == 0) + fd = 0, filename = "<stdin>"; + else + fd = open(filename, O_RDONLY | O_BINARY); + if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3) + printf("%s %*s%s\n", fd < 0 ? "nf":"->", + (int)(s1->include_stack_ptr - s1->include_stack), "", filename); + if (fd < 0) + return -1; + + tcc_open_bf(s1, filename, 0); + file->fd = fd; + return fd; +} + +/* 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); + + cur_text_section = NULL; + funcname = ""; + anon_sym = SYM_FIRST_ANOM; + + /* file info: full path + filename */ + section_sym = 0; /* avoid warning */ + if (s1->do_debug) { + section_sym = put_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, + text_section->sh_num, NULL); + getcwd(buf, sizeof(buf)); +#ifdef _WIN32 + normalize_slashes(buf); +#endif + 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, + ELFW(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); + +#if PTR_SIZE == 4 + size_type.t = VT_INT; +#else + size_type.t = VT_LLONG; +#endif + + func_old_type.t = VT_FUNC; + func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD); +#ifdef TCC_TARGET_ARM + arm_init(s1); +#endif + +#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; + nocode_wanted = 1; + + 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 | PARSE_FLAG_TOK_STR; + next(); + decl(VT_CONST); + if (tok != TOK_EOF) + expect("declaration"); + check_vstack(); + + /* end of translation unit info */ + if (s1->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); + sym_pop(&local_stack, NULL); + + return s1->nb_errors != 0 ? -1 : 0; +} + +LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str) +{ + int len, ret; + + len = strlen(str); + tcc_open_bf(s, "<string>", len); + memcpy(file->buffer, str, len); + ret = tcc_compile(s); + tcc_close(); + return ret; +} + +/* define a preprocessor symbol. A value can also be provided with the '=' operator */ +LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) +{ + int len1, len2; + /* default value */ + if (!value) + value = "1"; + len1 = strlen(sym); + len2 = strlen(value); + + /* init file structure */ + tcc_open_bf(s1, "<define>", len1 + len2 + 1); + memcpy(file->buffer, sym, len1); + file->buffer[len1] = ' '; + memcpy(file->buffer + len1 + 1, value, len2); + + /* parse with define parser */ + ch = file->buf_ptr[0]; + next_nomacro(); + parse_define(); + + tcc_close(); +} + +/* undefine a preprocessor symbol */ +LIBTCCAPI 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); +} + +/* cleanup all static data used during compilation */ +static void tcc_cleanup(void) +{ + if (NULL == tcc_state) + return; + tcc_state = NULL; + + preprocess_delete(); + + /* free sym_pools */ + dynarray_reset(&sym_pools, &nb_sym_pools); + /* string buffer */ + cstr_free(&tokcstr); + /* reset symbol stack */ + sym_free_first = NULL; +} + +LIBTCCAPI TCCState *tcc_new(void) +{ + TCCState *s; + char buffer[100]; + int a,b,c; + + tcc_cleanup(); + + s = tcc_mallocz(sizeof(TCCState)); + if (!s) + return NULL; + tcc_state = s; +#ifdef _WIN32 + tcc_set_lib_path_w32(s); +#else + tcc_set_lib_path(s, CONFIG_TCCDIR); +#endif + s->output_type = 0; + preprocess_new(); + s->include_stack_ptr = s->include_stack; + + /* 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); + + /* define __TINYC__ 92X */ + sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c); + sprintf(buffer, "%d", a*10000 + b*100 + c); + tcc_define_symbol(s, "__TINYC__", buffer); + + /* standard defines */ + tcc_define_symbol(s, "__STDC__", NULL); + tcc_define_symbol(s, "__STDC_VERSION__", "199901L"); + tcc_define_symbol(s, "__STDC_HOSTED__", NULL); + + /* target defines */ +#if defined(TCC_TARGET_I386) + tcc_define_symbol(s, "__i386__", NULL); + tcc_define_symbol(s, "__i386", NULL); + tcc_define_symbol(s, "i386", NULL); +#elif defined(TCC_TARGET_X86_64) + tcc_define_symbol(s, "__x86_64__", NULL); +#elif 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); + tcc_define_symbol(s, "__ARMEL__", NULL); +#if defined(TCC_ARM_EABI) + tcc_define_symbol(s, "__ARM_EABI__", NULL); +#endif +#if defined(TCC_ARM_HARDFLOAT) + s->float_abi = ARM_HARD_FLOAT; + tcc_define_symbol(s, "__ARM_PCS_VFP", NULL); +#else + s->float_abi = ARM_SOFTFP_FLOAT; +#endif +#elif defined(TCC_TARGET_ARM64) + tcc_define_symbol(s, "__aarch64__", NULL); +#endif + +#ifdef TCC_TARGET_PE + tcc_define_symbol(s, "_WIN32", NULL); +# ifdef TCC_TARGET_X86_64 + tcc_define_symbol(s, "_WIN64", NULL); +# endif +#else + tcc_define_symbol(s, "__unix__", NULL); + tcc_define_symbol(s, "__unix", NULL); + tcc_define_symbol(s, "unix", NULL); +# if defined(__linux) + tcc_define_symbol(s, "__linux__", NULL); + tcc_define_symbol(s, "__linux", NULL); +# endif +# if defined(__FreeBSD__) +# define str(s) #s + tcc_define_symbol(s, "__FreeBSD__", str( __FreeBSD__)); +# undef str +# endif +# if defined(__FreeBSD_kernel__) + tcc_define_symbol(s, "__FreeBSD_kernel__", NULL); +# endif +#endif + + /* TinyCC & gcc defines */ +#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64 + tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long"); + tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long"); +#else + tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long"); + tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long"); +#endif + +#ifdef TCC_TARGET_PE + tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short"); + tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short"); +#else + tcc_define_symbol(s, "__WCHAR_TYPE__", "int"); + /* wint_t is unsigned int by default, but (signed) int on BSDs + and unsigned short on windows. Other OSes might have still + other conventions, sigh. */ +#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) + tcc_define_symbol(s, "__WINT_TYPE__", "int"); +#else + tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int"); +#endif +#endif + +#ifndef TCC_TARGET_PE + /* glibc defines */ + tcc_define_symbol(s, "__REDIRECT(name, proto, alias)", "name proto __asm__ (#alias)"); + tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)", "name proto __asm__ (#alias) __THROW"); + /* paths for crt objects */ + tcc_split_path(s, (void ***)&s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX); +#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; + s->symtab = symtab_section; + + /* 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; + s->nocommon = 1; + s->warn_implicit_function_declaration = 1; + +#ifdef CHAR_IS_UNSIGNED + s->char_is_unsigned = 1; +#endif + /* enable this if you want symbols with leading underscore on windows: */ +#if 0 /* def TCC_TARGET_PE */ + s->leading_underscore = 1; +#endif +#ifdef TCC_TARGET_I386 + s->seg_size = 32; +#endif +#ifdef TCC_IS_NATIVE + s->runtime_main = "main"; +#endif + return s; +} + +LIBTCCAPI void tcc_delete(TCCState *s1) +{ + int i; + int bench = s1->do_bench; + + tcc_cleanup(); + + /* close a preprocessor output */ + if (s1->ppfp && s1->ppfp != stdout) + fclose(s1->ppfp); + + /* free all sections */ + for(i = 1; i < s1->nb_sections; i++) + free_section(s1->sections[i]); + dynarray_reset(&s1->sections, &s1->nb_sections); + + for(i = 0; i < s1->nb_priv_sections; i++) + free_section(s1->priv_sections[i]); + dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections); + + /* free any loaded DLLs */ +#ifdef TCC_IS_NATIVE + for ( i = 0; i < s1->nb_loaded_dlls; i++) { + DLLReference *ref = s1->loaded_dlls[i]; + if ( ref->handle ) + dlclose(ref->handle); + } +#endif + + /* free loaded dlls array */ + dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls); + + /* free library paths */ + dynarray_reset(&s1->library_paths, &s1->nb_library_paths); + dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths); + + /* free include paths */ + dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes); + dynarray_reset(&s1->include_paths, &s1->nb_include_paths); + dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths); + + tcc_free(s1->tcc_lib_path); + tcc_free(s1->soname); + tcc_free(s1->rpath); + tcc_free(s1->init_symbol); + tcc_free(s1->fini_symbol); + tcc_free(s1->outfile); + tcc_free(s1->deps_outfile); + dynarray_reset(&s1->files, &s1->nb_files); + dynarray_reset(&s1->target_deps, &s1->nb_target_deps); + dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs); + +#ifdef TCC_IS_NATIVE +# ifdef HAVE_SELINUX + munmap (s1->write_mem, s1->mem_size); + munmap (s1->runtime_mem, s1->mem_size); +# else + tcc_free(s1->runtime_mem); +# endif +#endif + + tcc_free(s1->sym_attrs); + tcc_free(s1); + tcc_memstats(bench); +} + +LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname) +{ + tcc_split_path(s, (void ***)&s->include_paths, &s->nb_include_paths, pathname); + return 0; +} + +LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname) +{ + tcc_split_path(s, (void ***)&s->sysinclude_paths, &s->nb_sysinclude_paths, pathname); + return 0; +} + +ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags, int filetype) +{ + ElfW(Ehdr) ehdr; + int fd, ret, size; + + parse_flags = 0; +#ifdef CONFIG_TCC_ASM + /* if .S file, define __ASSEMBLER__ like gcc does */ + if ((filetype == TCC_FILETYPE_ASM) || (filetype == TCC_FILETYPE_ASM_PP)) { + tcc_define_symbol(s1, "__ASSEMBLER__", NULL); + parse_flags = PARSE_FLAG_ASM_FILE; + } +#endif + + /* open the file */ + ret = tcc_open(s1, filename); + if (ret < 0) { + if (flags & AFF_PRINT_ERROR) + tcc_error_noabort("file '%s' not found", filename); + return ret; + } + + /* update target deps */ + dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, + tcc_strdup(filename)); + + if (flags & AFF_PREPROCESS) { + ret = tcc_preprocess(s1); + goto the_end; + } + + if (filetype == TCC_FILETYPE_C) { + /* C file assumed */ + ret = tcc_compile(s1); + goto the_end; + } + +#ifdef CONFIG_TCC_ASM + if (filetype == TCC_FILETYPE_ASM_PP) { + /* non preprocessed assembler */ + ret = tcc_assemble(s1, 1); + goto the_end; + } + + if (filetype == TCC_FILETYPE_ASM) { + /* preprocessed assembler */ + ret = tcc_assemble(s1, 0); + goto the_end; + } +#endif + + fd = file->fd; + /* assume executable format: auto guess file type */ + size = read(fd, &ehdr, sizeof(ehdr)); + lseek(fd, 0, SEEK_SET); + if (size <= 0) { + tcc_error_noabort("could not read header"); + goto the_end; + } + + if (size == sizeof(ehdr) && + ehdr.e_ident[0] == ELFMAG0 && + ehdr.e_ident[1] == ELFMAG1 && + ehdr.e_ident[2] == ELFMAG2 && + ehdr.e_ident[3] == ELFMAG3) { + + /* do not display line number if error */ + file->line_num = 0; + if (ehdr.e_type == ET_REL) { + ret = tcc_load_object_file(s1, fd, 0); + goto the_end; + + } +#ifndef TCC_TARGET_PE + if (ehdr.e_type == ET_DYN) { + if (s1->output_type == TCC_OUTPUT_MEMORY) { +#ifdef TCC_IS_NATIVE + void *h; + h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); + if (h) +#endif + ret = 0; + } else { + ret = tcc_load_dll(s1, fd, filename, + (flags & AFF_REFERENCED_DLL) != 0); + } + goto the_end; + } +#endif + tcc_error_noabort("unrecognized ELF file"); + goto the_end; + } + + if (memcmp((char *)&ehdr, ARMAG, 8) == 0) { + file->line_num = 0; /* do not display line number if error */ + ret = tcc_load_archive(s1, fd); + goto the_end; + } + +#ifdef TCC_TARGET_COFF + if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) { + ret = tcc_load_coff(s1, fd); + goto the_end; + } +#endif + +#ifdef TCC_TARGET_PE + ret = pe_load_file(s1, filename, fd); +#else + /* as GNU ld, consider it is an ld script if not recognized */ + ret = tcc_load_ldscript(s1); +#endif + if (ret < 0) + tcc_error_noabort("unrecognized file type"); + +the_end: + tcc_close(); + return ret; +} + +LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename, int filetype) +{ + if (s->output_type == TCC_OUTPUT_PREPROCESS) + return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS, filetype); + else + return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR, filetype); +} + +LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname) +{ + tcc_split_path(s, (void ***)&s->library_paths, &s->nb_library_paths, pathname); + return 0; +} + +static int tcc_add_library_internal(TCCState *s, const char *fmt, + const char *filename, int flags, char **paths, int nb_paths) +{ + char buf[1024]; + int i; + + for(i = 0; i < nb_paths; i++) { + snprintf(buf, sizeof(buf), fmt, paths[i], filename); + if (tcc_add_file_internal(s, buf, flags, TCC_FILETYPE_BINARY) == 0) + return 0; + } + return -1; +} + +#ifndef TCC_TARGET_PE +/* find and load a dll. Return non zero if not found */ +/* XXX: add '-rpath' option support ? */ +ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags) +{ + return tcc_add_library_internal(s, "%s/%s", filename, flags, + s->library_paths, s->nb_library_paths); +} +#endif + +ST_FUNC int tcc_add_crt(TCCState *s, const char *filename) +{ + if (-1 == tcc_add_library_internal(s, "%s/%s", + filename, 0, s->crt_paths, s->nb_crt_paths)) + tcc_error_noabort("file '%s' not found", filename); + return 0; +} + +/* the library name is the same as the argument of the '-l' option */ +LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) +{ +#ifdef TCC_TARGET_PE + const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL }; + const char **pp = s->static_link ? libs + 4 : libs; +#else + const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL }; + const char **pp = s->static_link ? libs + 1 : libs; +#endif + while (*pp) { + if (0 == tcc_add_library_internal(s, *pp, + libraryname, 0, s->library_paths, s->nb_library_paths)) + return 0; + ++pp; + } + return -1; +} + +PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname) +{ + int ret = tcc_add_library(s, libname); + if (ret < 0) + tcc_error_noabort("cannot find library 'lib%s'", libname); + return ret; +} + +/* habdle #pragma comment(lib,) */ +ST_FUNC void tcc_add_pragma_libs(TCCState *s1) +{ + int i; + for (i = 0; i < s1->nb_pragma_libs; i++) + tcc_add_library_err(s1, s1->pragma_libs[i]); +} + +LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val) +{ +#ifdef TCC_TARGET_PE + /* On x86_64 'val' might not be reachable with a 32bit offset. + So it is handled here as if it were in a DLL. */ + pe_putimport(s, 0, name, (uintptr_t)val); +#else + add_elf_sym(symtab_section, (uintptr_t)val, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_ABS, name); +#endif + return 0; +} + +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) +{ + s->output_type = output_type; + + if (!s->nostdinc) { + /* default include paths */ + /* -isystem paths have already been handled */ + tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS); + } + + /* if bound checking, then add corresponding sections */ +#ifdef CONFIG_TCC_BCHECK + if (s->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 (s->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); + } + + tcc_add_library_path(s, CONFIG_TCC_LIBPATHS); +#ifdef TCC_TARGET_PE +# ifdef _WIN32 + tcc_add_systemdir(s); +# endif +#else + /* add libc crt1/crti objects */ + if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) && + !s->nostdlib) { + if (output_type != TCC_OUTPUT_DLL) + tcc_add_crt(s, "crt1.o"); + tcc_add_crt(s, "crti.o"); + } +#endif + +#ifdef CONFIG_TCC_BCHECK + if (s->do_bounds_check && (output_type == TCC_OUTPUT_EXE)) + { + /* force a bcheck.o linking */ + addr_t func = TOK___bound_init; + Sym *sym = external_global_sym(func, &func_old_type, 0); + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + } +#endif + + if (s->output_type == TCC_OUTPUT_PREPROCESS) + print_defines(); + + return 0; +} + +LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path) +{ + tcc_free(s->tcc_lib_path); + s->tcc_lib_path = tcc_strdup(path); +} + +#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" }, +}; + +ST_FUNC 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 */ +static 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" }, + { offsetof(TCCState, ms_extensions), 0, "ms-extensions" }, + { offsetof(TCCState, old_struct_init_code), 0, "old-struct-init-code" }, + { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" }, +}; + +/* set/reset a flag */ +static int tcc_set_flag(TCCState *s, const char *flag_name, int value) +{ + return set_flag(s, flag_defs, countof(flag_defs), + flag_name, value); +} + + +static int strstart(const char *val, const char **str) +{ + const char *p, *q; + p = *str; + q = val; + while (*q) { + if (*p != *q) + return 0; + p++; + q++; + } + *str = p; + return 1; +} + +/* Like strstart, but automatically takes into account that ld options can + * + * - start with double or single dash (e.g. '--soname' or '-soname') + * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so' + * or '-Wl,-soname=x.so') + * + * you provide `val` always in 'option[=]' form (no leading -) + */ +static int link_option(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + + /* there should be 1 or 2 dashes */ + if (*str++ != '-') + return 0; + if (*str == '-') + str++; + + /* then str & val should match (potentialy up to '=') */ + p = str; + q = val; + + while (*q != '\0' && *q != '=') { + if (*p != *q) + return 0; + p++; + q++; + } + + /* '=' near eos means ',' or '=' is ok */ + if (*q == '=') { + if (*p != ',' && *p != '=') + return 0; + p++; + q++; + } + + if (ptr) + *ptr = p; + return 1; +} + +static const char *skip_linker_arg(const char **str) +{ + const char *s1 = *str; + const char *s2 = strchr(s1, ','); + *str = s2 ? s2++ : (s2 = s1 + strlen(s1)); + return s2; +} + +static char *copy_linker_arg(const char *p) +{ + const char *q = p; + skip_linker_arg(&q); + return pstrncpy(tcc_malloc(q - p + 1), p, q - p); +} + +/* set linker options */ +static int tcc_set_linker(TCCState *s, const char *option) +{ + while (option && *option) { + + const char *p = option; + char *end = NULL; + int ignoring = 0; + + if (link_option(option, "Bsymbolic", &p)) { + s->symbolic = 1; + } else if (link_option(option, "nostdlib", &p)) { + s->nostdlib = 1; + } else if (link_option(option, "fini=", &p)) { + s->fini_symbol = copy_linker_arg(p); + ignoring = 1; + } else if (link_option(option, "image-base=", &p) + || link_option(option, "Ttext=", &p)) { + s->text_addr = strtoull(p, &end, 16); + s->has_text_addr = 1; + } else if (link_option(option, "init=", &p)) { + s->init_symbol = copy_linker_arg(p); + ignoring = 1; + } else if (link_option(option, "oformat=", &p)) { +#if defined(TCC_TARGET_PE) + if (strstart("pe-", &p)) { +#elif defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + if (strstart("elf64-", &p)) { +#else + if (strstart("elf32-", &p)) { +#endif + s->output_format = TCC_OUTPUT_FORMAT_ELF; + } else if (!strcmp(p, "binary")) { + s->output_format = TCC_OUTPUT_FORMAT_BINARY; +#ifdef TCC_TARGET_COFF + } else if (!strcmp(p, "coff")) { + s->output_format = TCC_OUTPUT_FORMAT_COFF; +#endif + } else + goto err; + + } else if (link_option(option, "as-needed", &p)) { + ignoring = 1; + } else if (link_option(option, "O", &p)) { + ignoring = 1; + } else if (link_option(option, "rpath=", &p)) { + s->rpath = copy_linker_arg(p); + } else if (link_option(option, "section-alignment=", &p)) { + s->section_align = strtoul(p, &end, 16); + } else if (link_option(option, "soname=", &p)) { + s->soname = copy_linker_arg(p); +#ifdef TCC_TARGET_PE + } else if (link_option(option, "file-alignment=", &p)) { + s->pe_file_align = strtoul(p, &end, 16); + } else if (link_option(option, "stack=", &p)) { + s->pe_stack_size = strtoul(p, &end, 10); + } else if (link_option(option, "subsystem=", &p)) { +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + if (!strcmp(p, "native")) { + s->pe_subsystem = 1; + } else if (!strcmp(p, "console")) { + s->pe_subsystem = 3; + } else if (!strcmp(p, "gui")) { + s->pe_subsystem = 2; + } else if (!strcmp(p, "posix")) { + s->pe_subsystem = 7; + } else if (!strcmp(p, "efiapp")) { + s->pe_subsystem = 10; + } else if (!strcmp(p, "efiboot")) { + s->pe_subsystem = 11; + } else if (!strcmp(p, "efiruntime")) { + s->pe_subsystem = 12; + } else if (!strcmp(p, "efirom")) { + s->pe_subsystem = 13; +#elif defined(TCC_TARGET_ARM) + if (!strcmp(p, "wince")) { + s->pe_subsystem = 9; +#endif + } else + goto err; +#endif + } else + goto err; + + if (ignoring && s->warn_unsupported) err: { + char buf[100], *e; + pstrcpy(buf, sizeof buf, e = copy_linker_arg(option)), tcc_free(e); + if (ignoring) + tcc_warning("unsupported linker option '%s'", buf); + else + tcc_error("unsupported linker option '%s'", buf); + } + option = skip_linker_arg(&p); + } + return 0; +} + +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_P, + 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_dumpversion, + TCC_OPTION_d, + TCC_OPTION_float_abi, + TCC_OPTION_static, + TCC_OPTION_std, + TCC_OPTION_shared, + TCC_OPTION_soname, + TCC_OPTION_o, + TCC_OPTION_r, + TCC_OPTION_s, + TCC_OPTION_traditional, + TCC_OPTION_Wl, + TCC_OPTION_W, + TCC_OPTION_O, + TCC_OPTION_m, + TCC_OPTION_f, + TCC_OPTION_isystem, + TCC_OPTION_iwithprefix, + TCC_OPTION_nostdinc, + TCC_OPTION_nostdlib, + TCC_OPTION_print_search_dirs, + TCC_OPTION_rdynamic, + TCC_OPTION_pedantic, + TCC_OPTION_pthread, + TCC_OPTION_run, + TCC_OPTION_v, + TCC_OPTION_w, + TCC_OPTION_pipe, + TCC_OPTION_E, + TCC_OPTION_MD, + TCC_OPTION_MF, + TCC_OPTION_x, +}; + +#define TCC_OPTION_HAS_ARG 0x0001 +#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ + +static const TCCOption tcc_options[] = { + { "h", TCC_OPTION_HELP, 0 }, + { "-help", 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 }, + { "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "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 }, +#ifdef CONFIG_TCC_BACKTRACE + { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG }, +#endif +#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 }, + { "dumpversion", TCC_OPTION_dumpversion, 0}, + { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, +#ifdef TCC_TARGET_ARM + { "mfloat-abi", TCC_OPTION_float_abi, TCC_OPTION_HAS_ARG }, +#endif + { "static", TCC_OPTION_static, 0 }, + { "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "shared", TCC_OPTION_shared, 0 }, + { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG }, + { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG }, + { "pedantic", TCC_OPTION_pedantic, 0}, + { "pthread", TCC_OPTION_pthread, 0}, + { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "rdynamic", TCC_OPTION_rdynamic, 0 }, + { "r", TCC_OPTION_r, 0 }, + { "s", TCC_OPTION_s, 0 }, + { "traditional", TCC_OPTION_traditional, 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 }, + { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG }, + { "iwithprefix", TCC_OPTION_iwithprefix, TCC_OPTION_HAS_ARG }, + { "nostdinc", TCC_OPTION_nostdinc, 0 }, + { "nostdlib", TCC_OPTION_nostdlib, 0 }, + { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, + { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "w", TCC_OPTION_w, 0 }, + { "pipe", TCC_OPTION_pipe, 0}, + { "E", TCC_OPTION_E, 0}, + { "MD", TCC_OPTION_MD, 0}, + { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, + { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, + { NULL, 0, 0 }, +}; + +static void parse_option_D(TCCState *s1, const char *optarg) +{ + char *sym = tcc_strdup(optarg); + char *value = strchr(sym, '='); + if (value) + *value++ = '\0'; + tcc_define_symbol(s1, sym, value); + tcc_free(sym); +} + +static void args_parser_add_file(TCCState *s, const char* filename, int filetype) +{ + int len = strlen(filename); + char *p = tcc_malloc(len + 2); + if (filetype) { + *p = filetype; + } + else { + /* use a file extension to detect a filetype */ + const char *ext = tcc_fileextension(filename); + if (ext[0]) { + ext++; + if (!strcmp(ext, "S")) + *p = TCC_FILETYPE_ASM_PP; + else + if (!strcmp(ext, "s")) + *p = TCC_FILETYPE_ASM; + else + if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i")) + *p = TCC_FILETYPE_C; + else + *p = TCC_FILETYPE_BINARY; + } + else { + *p = TCC_FILETYPE_C; + } + } + strcpy(p+1, filename); + dynarray_add((void ***)&s->files, &s->nb_files, p); +} + +PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv) +{ + const TCCOption *popt; + const char *optarg, *r; + int run = 0; + int pthread = 0; + int optind = 0; + int filetype = 0; + + /* collect -Wl options for input such as "-Wl,-rpath -Wl,<path>" */ + CString linker_arg; + cstr_new(&linker_arg); + + while (optind < argc) { + + r = argv[optind++]; + if (r[0] != '-' || r[1] == '\0') { + args_parser_add_file(s, r, filetype); + if (run) { + optind--; + /* argv[0] will be this file */ + break; + } + continue; + } + + /* find option in table */ + for(popt = tcc_options; ; ++popt) { + const char *p1 = popt->name; + const char *r1 = r + 1; + if (p1 == NULL) + tcc_error("invalid option -- '%s'", r); + if (!strstart(p1, &r1)) + continue; + optarg = r1; + if (popt->flags & TCC_OPTION_HAS_ARG) { + if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) { + if (optind >= argc) + tcc_error("argument to '%s' is missing", r); + optarg = argv[optind++]; + } + } else if (*r1 != '\0') + continue; + break; + } + + switch(popt->index) { + case TCC_OPTION_HELP: + return 0; + case TCC_OPTION_I: + tcc_add_include_path(s, optarg); + break; + case TCC_OPTION_D: + parse_option_D(s, optarg); + 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_set_lib_path(s, optarg); + break; + case TCC_OPTION_l: + args_parser_add_file(s, r, TCC_FILETYPE_BINARY); + s->nb_libraries++; + break; + case TCC_OPTION_pthread: + parse_option_D(s, "_REENTRANT"); + pthread = 1; + break; + case TCC_OPTION_bench: + s->do_bench = 1; + break; +#ifdef CONFIG_TCC_BACKTRACE + case TCC_OPTION_bt: + tcc_set_num_callers(atoi(optarg)); + break; +#endif +#ifdef CONFIG_TCC_BCHECK + case TCC_OPTION_b: + s->do_bounds_check = 1; + s->do_debug = 1; + break; +#endif + case TCC_OPTION_g: + s->do_debug = 1; + break; + case TCC_OPTION_c: + if (s->output_type) + tcc_warning("-c: some compiler action already specified (%d)", s->output_type); + s->output_type = TCC_OUTPUT_OBJ; + break; + case TCC_OPTION_d: + if (*optarg == 'D') + s->dflag = 1; + else { + if (s->warn_unsupported) + goto unsupported_option; + tcc_error("invalid option -- '%s'", r); + } + break; +#ifdef TCC_TARGET_ARM + case TCC_OPTION_float_abi: + /* tcc doesn't support soft float yet */ + if (!strcmp(optarg, "softfp")) { + s->float_abi = ARM_SOFTFP_FLOAT; + tcc_undefine_symbol(s, "__ARM_PCS_VFP"); + } else if (!strcmp(optarg, "hard")) + s->float_abi = ARM_HARD_FLOAT; + else + tcc_error("unsupported float abi '%s'", optarg); + break; +#endif + case TCC_OPTION_static: + s->static_link = 1; + break; + case TCC_OPTION_std: + /* silently ignore, a current purpose: + allow to use a tcc as a reference compiler for "make test" */ + break; + case TCC_OPTION_shared: + if (s->output_type) + tcc_warning("-shared: some compiler action already specified (%d)", s->output_type); + s->output_type = TCC_OUTPUT_DLL; + break; + case TCC_OPTION_soname: + s->soname = tcc_strdup(optarg); + break; + case TCC_OPTION_m: + s->option_m = tcc_strdup(optarg); + break; + case TCC_OPTION_o: + if (s->outfile) { + tcc_warning("multiple -o option"); + tcc_free(s->outfile); + } + s->outfile = tcc_strdup(optarg); + break; + case TCC_OPTION_r: + /* generate a .o merging several output files */ + if (s->output_type) + tcc_warning("-r: some compiler action already specified (%d)", s->output_type); + s->option_r = 1; + s->output_type = TCC_OUTPUT_OBJ; + break; + case TCC_OPTION_isystem: + tcc_add_sysinclude_path(s, optarg); + break; + case TCC_OPTION_iwithprefix: + if (1) { + char buf[1024]; + int buf_size = sizeof(buf)-1; + char *p = &buf[0]; + + char *sysroot = "{B}/"; + int len = strlen(sysroot); + if (len > buf_size) + len = buf_size; + strncpy(p, sysroot, len); + p += len; + buf_size -= len; + + len = strlen(optarg); + if (len > buf_size) + len = buf_size; + strncpy(p, optarg, len+1); + tcc_add_sysinclude_path(s, buf); + } + break; + case TCC_OPTION_nostdinc: + s->nostdinc = 1; + break; + case TCC_OPTION_nostdlib: + s->nostdlib = 1; + break; + case TCC_OPTION_print_search_dirs: + s->print_search_dirs = 1; + break; + case TCC_OPTION_run: + if (s->output_type) + tcc_warning("-run: some compiler action already specified (%d)", s->output_type); + s->output_type = TCC_OUTPUT_MEMORY; + tcc_set_options(s, optarg); + run = 1; + break; + case TCC_OPTION_v: + do ++s->verbose; while (*optarg++ == 'v'); + break; + 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: + if (linker_arg.size) + --linker_arg.size, cstr_ccat(&linker_arg, ','); + cstr_cat(&linker_arg, optarg); + cstr_ccat(&linker_arg, '\0'); + break; + case TCC_OPTION_E: + if (s->output_type) + tcc_warning("-E: some compiler action already specified (%d)", s->output_type); + s->output_type = TCC_OUTPUT_PREPROCESS; + break; + case TCC_OPTION_P: + s->Pflag = atoi(optarg) + 1; + break; + case TCC_OPTION_MD: + s->gen_deps = 1; + break; + case TCC_OPTION_MF: + s->deps_outfile = tcc_strdup(optarg); + break; + case TCC_OPTION_dumpversion: + printf ("%s\n", TCC_VERSION); + exit(0); + case TCC_OPTION_s: + s->do_strip = 1; + break; + case TCC_OPTION_traditional: + break; + case TCC_OPTION_x: + if (*optarg == 'c') + filetype = TCC_FILETYPE_C; + else + if (*optarg == 'a') + filetype = TCC_FILETYPE_ASM_PP; + else + if (*optarg == 'n') + filetype = 0; + else + tcc_warning("unsupported language '%s'", optarg); + break; + case TCC_OPTION_O: + if (1) { + int opt = atoi(optarg); + char *sym = "__OPTIMIZE__"; + if (opt) + tcc_define_symbol(s, sym, 0); + else + tcc_undefine_symbol(s, sym); + } + break; + case TCC_OPTION_pedantic: + case TCC_OPTION_pipe: + /* ignored */ + break; + default: + if (s->warn_unsupported) { + unsupported_option: + tcc_warning("unsupported option '%s'", r); + } + break; + } + } + + if (s->output_type == 0) + s->output_type = TCC_OUTPUT_EXE; + + if (pthread && s->output_type != TCC_OUTPUT_OBJ) + tcc_set_options(s, "-lpthread"); + + if (s->output_type == TCC_OUTPUT_EXE) + tcc_set_linker(s, (const char *)linker_arg.data); + cstr_free(&linker_arg); + + return optind; +} + +LIBTCCAPI int tcc_set_options(TCCState *s, const char *str) +{ + const char *s1; + char **argv, *arg; + int argc, len; + int ret; + + 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); + pstrncpy(arg, s1, len); + dynarray_add((void ***)&argv, &argc, arg); + } + ret = tcc_parse_args(s, argc, argv); + dynarray_reset(&argv, &argc); + return ret; +} + +PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time) +{ + double tt; + tt = (double)total_time / 1000000.0; + if (tt < 0.001) + tt = 0.001; + if (total_bytes < 1) + total_bytes = 1; + fprintf(stderr, "%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, + tt, (int)(total_lines / tt), + total_bytes / tt / 1000000.0); +} + +PUB_FUNC void tcc_set_environment(TCCState *s) +{ + char * path; + + path = getenv("C_INCLUDE_PATH"); + if(path != NULL) { + tcc_add_include_path(s, path); + } + path = getenv("CPATH"); + if(path != NULL) { + tcc_add_include_path(s, path); + } + path = getenv("LIBRARY_PATH"); + if(path != NULL) { + tcc_add_library_path(s, path); + } +} diff --git a/src/libtcc.h b/src/libtcc.h new file mode 100644 index 0000000..97b8587 --- /dev/null +++ b/src/libtcc.h @@ -0,0 +1,104 @@ +#ifndef LIBTCC_H +#define LIBTCC_H + +#ifndef LIBTCCAPI +# define LIBTCCAPI +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct TCCState; + +typedef struct TCCState TCCState; + +/* create a new TCC compilation context */ +LIBTCCAPI TCCState *tcc_new(void); + +/* free a TCC compilation context */ +LIBTCCAPI void tcc_delete(TCCState *s); + +/* set CONFIG_TCCDIR at runtime */ +LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); + +/* set error/warning display callback */ +LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, + void (*error_func)(void *opaque, const char *msg)); + +/* set options as from command line (multiple supported) */ +LIBTCCAPI int tcc_set_options(TCCState *s, const char *str); + +/*****************************/ +/* preprocessor */ + +/* add include path */ +LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname); + +/* add in system include path */ +LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname); + +/* define preprocessor symbol 'sym'. Can put optional value */ +LIBTCCAPI void tcc_define_symbol(TCCState *s, const char *sym, const char *value); + +/* undefine preprocess symbol 'sym' */ +LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym); + +/*****************************/ +/* compiling */ + +/* add a file (C file, dll, object, library, ld script). Return -1 if error. */ +LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename, int filetype); +#define TCC_FILETYPE_BINARY 1 +#define TCC_FILETYPE_C 2 +#define TCC_FILETYPE_ASM 3 +#define TCC_FILETYPE_ASM_PP 4 + +/* compile a string containing a C source. Return -1 if error. */ +LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf); + +/*****************************/ +/* linking commands */ + +/* set output type. MUST BE CALLED before any compilation */ +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type); +#define TCC_OUTPUT_MEMORY 1 /* output will be run in memory (default) */ +#define TCC_OUTPUT_EXE 2 /* executable file */ +#define TCC_OUTPUT_DLL 3 /* dynamic library */ +#define TCC_OUTPUT_OBJ 4 /* object file */ +#define TCC_OUTPUT_PREPROCESS 5 /* only preprocess (used internally) */ + +/* equivalent to -Lpath option */ +LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname); + +/* the library name is the same as the argument of the '-l' option */ +LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname); + +/* add a symbol to the compiled program */ +LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val); + +/* output an executable, library or object file. DO NOT call + tcc_relocate() before. */ +LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename); + +/* link and run main() function and return its value. DO NOT call + tcc_relocate() before. */ +LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv); + +/* do all relocations (needed before using tcc_get_symbol()) */ +LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr); +/* possible values for 'ptr': + - TCC_RELOCATE_AUTO : Allocate and manage memory internally + - NULL : return required memory size for the step below + - memory address : copy code to memory passed by the caller + returns -1 if error. */ +#define TCC_RELOCATE_AUTO (void*)1 + +/* return symbol value or NULL if not found */ +LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/stab.def b/src/stab.def new file mode 100644 index 0000000..48ea231 --- /dev/null +++ b/src/stab.def @@ -0,0 +1,234 @@ +/* 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. + */ diff --git a/src/stab.h b/src/stab.h new file mode 100644 index 0000000..80bd594 --- /dev/null +++ b/src/stab.h @@ -0,0 +1,17 @@ +#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 +{ +#include "stab.def" +LAST_UNUSED_STAB_CODE +}; + +#undef __define_stab + +#endif /* __GNU_STAB_ */ diff --git a/src/tcc.c b/src/tcc.c new file mode 100644 index 0000000..cce9699 --- /dev/null +++ b/src/tcc.c @@ -0,0 +1,365 @@ +/* + * 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 + */ + +#ifdef ONE_SOURCE +#include "libtcc.c" +#else +#include "tcc.h" +#endif + +static void print_paths(const char *msg, char **paths, int nb_paths) +{ + int i; + printf("%s:\n%s", msg, nb_paths ? "" : " -\n"); + for(i = 0; i < nb_paths; i++) + printf(" %s\n", paths[i]); +} + +static void display_info(TCCState *s, int what) +{ + switch (what) { + case 0: + printf("tcc version %s (" +#ifdef TCC_TARGET_I386 + "i386" +#elif defined TCC_TARGET_X86_64 + "x86-64" +#elif defined TCC_TARGET_C67 + "C67" +#elif defined TCC_TARGET_ARM + "ARM" +# ifdef TCC_ARM_HARDFLOAT + " Hard Float" +# endif +#elif defined TCC_TARGET_ARM64 + "AArch64" +# ifdef TCC_ARM_HARDFLOAT + " Hard Float" +# endif +#endif +#ifdef TCC_TARGET_PE + " Windows" +#else + " Linux" +#endif + ")\n", TCC_VERSION); + break; + case 1: + printf("install: %s\n", s->tcc_lib_path); + /* print_paths("programs", NULL, 0); */ + print_paths("include", s->sysinclude_paths, s->nb_sysinclude_paths); + print_paths("libraries", s->library_paths, s->nb_library_paths); +#ifndef TCC_TARGET_PE + print_paths("crt", s->crt_paths, s->nb_crt_paths); + printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s)); +#endif + break; + } +} + +static void help(void) +{ + printf("Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n" + "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n" + " tcc [options...] -run infile [arguments...]\n" + "General options:\n" + " -c compile only - generate an object file\n" + " -o outfile set output filename\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" + " -v show version\n" + " -vv show included files (as sole argument: show search paths)\n" + " -dumpversion\n" + " -bench show compilation statistics\n" + "Preprocessor options:\n" + " -Idir add include path 'dir'\n" + " -Dsym[=val] define 'sym' with value 'val'\n" + " -Usym undefine 'sym'\n" + " -E preprocess only\n" + " -P[1] no/alternative output of #line directives with -E\n" + " -dD dump defines\n" + "Linker options:\n" + " -Ldir add library path 'dir'\n" + " -llib link with dynamic or static library 'lib'\n" + " -pthread link with -lpthread and -D_REENTRANT (POSIX Linux)\n" + " -r generate (relocatable) object file\n" + " -rdynamic export all global symbols to dynamic linker\n" + " -shared generate a shared library\n" + " -soname set name for shared library to be used at runtime\n" + " -static static linking\n" + " -Wl,-opt[=val] set linker option (see manual)\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 +#ifdef CONFIG_TCC_BACKTRACE + " -bt N show N callers in stack traces\n" +#endif + "Misc options:\n" + " -nostdinc do not use standard system include paths\n" + " -nostdlib do not link with standard crt and libraries\n" + " -Bdir use 'dir' as tcc internal library and include path\n" + " -MD generate target dependencies for make\n" + " -MF depfile put generated dependencies here\n" + ); +} + +/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */ +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +#ifdef _WIN32 +#include <process.h> +static int execvp_win32(const char *prog, char **argv) +{ + int ret = spawnvp(P_NOWAIT, prog, (const char *const*)argv); + if (-1 == ret) + return ret; + cwait(&ret, ret, WAIT_CHILD); + exit(ret); +} +#define execvp execvp_win32 +#endif +static void exec_other_tcc(TCCState *s, char **argv, const char *optarg) +{ + char child_path[4096], *child_name; const char *target; + switch (atoi(optarg)) { +#ifdef TCC_TARGET_I386 + case 32: break; + case 64: target = "x86_64"; +#else + case 64: break; + case 32: target = "i386"; +#endif + pstrcpy(child_path, sizeof child_path - 40, argv[0]); + child_name = tcc_basename(child_path); + strcpy(child_name, target); +#ifdef TCC_TARGET_PE + strcat(child_name, "-win32"); +#endif + strcat(child_name, "-tcc"); + if (strcmp(argv[0], child_path)) { + if (s->verbose > 0) + printf("tcc: using '%s'\n", child_name), fflush(stdout); + execvp(argv[0] = child_path, argv); + } + tcc_error("'%s' not found", child_name); + case 0: /* ignore -march etc. */ + break; + default: + tcc_warning("unsupported option \"-m%s\"", optarg); + } +} +#else +#define exec_other_tcc(s, argv, optarg) +#endif + +static void gen_makedeps(TCCState *s, const char *target, const char *filename) +{ + FILE *depout; + char buf[1024], *ext; + int i; + + if (!filename) { + /* compute filename automatically + * dir/file.o -> dir/file.d */ + pstrcpy(buf, sizeof(buf), target); + ext = tcc_fileextension(buf); + pstrcpy(ext, sizeof(buf) - (ext-buf), ".d"); + filename = buf; + } + + if (s->verbose) + printf("<- %s\n", filename); + + /* XXX return err codes instead of error() ? */ + depout = fopen(filename, "w"); + if (!depout) + tcc_error("could not open '%s'", filename); + + fprintf(depout, "%s : \\\n", target); + for (i=0; i<s->nb_target_deps; ++i) + fprintf(depout, " %s \\\n", s->target_deps[i]); + fprintf(depout, "\n"); + fclose(depout); +} + +static char *default_outputfile(TCCState *s, const char *first_file) +{ + char buf[1024]; + char *ext; + const char *name = "a"; + + if (first_file && strcmp(first_file, "-")) + name = tcc_basename(first_file); + pstrcpy(buf, sizeof(buf), name); + ext = tcc_fileextension(buf); +#ifdef TCC_TARGET_PE + if (s->output_type == TCC_OUTPUT_DLL) + strcpy(ext, ".dll"); + else + if (s->output_type == TCC_OUTPUT_EXE) + strcpy(ext, ".exe"); + else +#endif + if (( (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) || + (s->output_type == TCC_OUTPUT_PREPROCESS) ) + && *ext) + strcpy(ext, ".o"); + else + strcpy(buf, "a.out"); + + return tcc_strdup(buf); +} + +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 +} + +int main(int argc, char **argv) +{ + TCCState *s; + int ret, optind, i; + int64_t start_time = 0; + + s = tcc_new(); + + optind = tcc_parse_args(s, argc - 1, argv + 1); + + if (s->do_bench) + start_time = getclock_us(); + + tcc_set_environment(s); + + if (optind == 0) { + help(); + return 1; + } + + if (s->option_m) + exec_other_tcc(s, argv, s->option_m); + + if (s->verbose) + display_info(s, 0); + + if (s->print_search_dirs || (s->verbose == 2 && optind == 1)) { + tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + display_info(s, 1); + return 0; + } + + if (s->verbose && optind == 1) + return 0; + + if (s->nb_files == 0) + tcc_error("no input files\n"); + + /* check -c consistency : only single file handled. XXX: checks file type */ + if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) { + if (s->nb_libraries != 0) + tcc_error("cannot specify libraries with -c"); + /* accepts only a single input file */ + if ((s->nb_files != 1) && s->outfile) { + tcc_error("cannot specify multiple files with -c and -o"); + } + } + + if (s->output_type == TCC_OUTPUT_PREPROCESS) { + if (!s->outfile) { + s->ppfp = stdout; + } else { + s->ppfp = fopen(s->outfile, "w"); + if (!s->ppfp) + tcc_error("could not write '%s'", s->outfile); + } + } + + tcc_set_output_type(s, s->output_type); + + /* compile or add each files or library */ + for(i = ret = 0; i < s->nb_files && ret == 0; i++) { + int filetype = *(unsigned char *)s->files[i]; + const char *filename = s->files[i] + 1; + if (filename[0] == '-' && filename[1] == 'l') { + if (tcc_add_library(s, filename + 2) < 0) { + tcc_error_noabort("cannot find library 'lib%s'", filename+2); + ret = 1; + } + } else { + if (1 == s->verbose) + printf("-> %s\n", filename); + if (!s->outfile) + s->outfile = default_outputfile(s, filename); + if (tcc_add_file(s, filename, filetype) < 0) + ret = 1; + else + if (s->output_type == TCC_OUTPUT_OBJ) { + ret = !!tcc_output_file(s, s->outfile); + if (s->gen_deps && !ret) + gen_makedeps(s, s->outfile, s->deps_outfile); + if (!ret) { + if ((i+1) < s->nb_files) { + tcc_delete(s); + s = tcc_new(); + tcc_parse_args(s, argc - 1, argv + 1); + tcc_set_environment(s); + if (s->output_type != TCC_OUTPUT_OBJ) + tcc_error("interlnal error"); + tcc_set_output_type(s, s->output_type); + } + } + } + } + } + + if (0 == ret) { + if (s->output_type == TCC_OUTPUT_MEMORY) { +#ifdef TCC_IS_NATIVE + ret = tcc_run(s, argc - 1 - optind, argv + 1 + optind); +#else + tcc_error_noabort("-run is not available in a cross compiler"); + ret = 1; +#endif + } else + if (s->output_type == TCC_OUTPUT_EXE || + s->output_type == TCC_OUTPUT_DLL) + { + ret = !!tcc_output_file(s, s->outfile); + if (s->gen_deps && !ret) + gen_makedeps(s, s->outfile, s->deps_outfile); + } + } + + if (s->do_bench) + tcc_print_stats(s, getclock_us() - start_time); + + tcc_delete(s); + return ret; +} diff --git a/src/tcc.h b/src/tcc.h new file mode 100644 index 0000000..eb0365d --- /dev/null +++ b/src/tcc.h @@ -0,0 +1,1498 @@ +/* + * 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 + */ + +#ifndef _TCC_H +#define _TCC_H + +#define _GNU_SOURCE +#include "config.h" + +#ifdef CONFIG_TCCBOOT +#include "tccboot.h" +#define CONFIG_TCC_STATIC +#else + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <signal.h> +#include <fcntl.h> +#include <setjmp.h> +#include <time.h> + +#ifdef CONFIG_TCCASSERT +#include <assert.h> +#define TCC_ASSERT(ex) assert(ex) +#else +#define TCC_ASSERT(ex) +#endif + +#ifndef _WIN32 +# include <unistd.h> +# include <sys/time.h> +# include <sys/ucontext.h> +# include <sys/mman.h> +# ifndef CONFIG_TCC_STATIC +# include <dlfcn.h> +# endif +/* 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); +#else /* on _WIN32: */ +# include <windows.h> +# include <sys/timeb.h> +# include <io.h> /* open, close etc. */ +# include <direct.h> /* getcwd */ +# ifdef __GNUC__ +# include <stdint.h> +# endif +# define inline __inline +# define inp next_inp +# define snprintf _snprintf +# define vsnprintf _vsnprintf +# ifndef __GNUC__ +# define strtold (long double)strtod +# define strtof (float)strtod +# define strtoll _strtoi64 +# define strtoull _strtoui64 +# endif +# ifdef LIBTCC_AS_DLL +# define LIBTCCAPI __declspec(dllexport) +# define PUB_FUNC LIBTCCAPI +# endif +#endif + +#endif /* !CONFIG_TCCBOOT */ + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifdef __GNUC__ +# define NORETURN __attribute__ ((noreturn)) +#elif defined _MSC_VER +# define NORETURN __declspec(noreturn) +#else +# define NORETURN +#endif + +#ifdef _WIN32 +# define IS_DIRSEP(c) (c == '/' || c == '\\') +# define IS_ABSPATH(p) (IS_DIRSEP(p[0]) || (p[0] && p[1] == ':' && IS_DIRSEP(p[2]))) +# define PATHCMP stricmp +#else +# define IS_DIRSEP(c) (c == '/') +# define IS_ABSPATH(p) IS_DIRSEP(p[0]) +# define PATHCMP strcmp +#endif + +#ifdef TCC_TARGET_PE +#define PATHSEP ';' +#else +#define PATHSEP ':' +#endif + +#include "elf.h" +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) +# define ELFCLASSW ELFCLASS64 +# define ElfW(type) Elf##64##_##type +# define ELFW(type) ELF##64##_##type +# define ElfW_Rel ElfW(Rela) +# define SHT_RELX SHT_RELA +# define REL_SECTION_FMT ".rela%s" +/* XXX: DLL with PLT would only work with x86-64 for now */ +# define TCC_OUTPUT_DLL_WITH_PLT +#else +# define ELFCLASSW ELFCLASS32 +# define ElfW(type) Elf##32##_##type +# define ELFW(type) ELF##32##_##type +# define ElfW_Rel ElfW(Rel) +# define SHT_RELX SHT_REL +# define REL_SECTION_FMT ".rel%s" +#endif + +/* target address type */ +#define addr_t ElfW(Addr) + +#include "stab.h" +#include "libtcc.h" + +/* parser debug */ +/* #define PARSE_DEBUG */ +/* preprocessor debug */ +/* #define PP_DEBUG */ +/* include file debug */ +/* #define INC_DEBUG */ +/* memory leak 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_ARM64 *//* ARMv8 code generator */ +/* #define TCC_TARGET_C67 *//* TMS320C67xx code generator */ +/* #define TCC_TARGET_X86_64 *//* x86-64 code generator */ + +/* default target is I386 */ +#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \ + !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \ + !defined(TCC_TARGET_X86_64) +#define TCC_TARGET_I386 +#endif + +#if !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \ + !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \ + !defined(CONFIG_USE_LIBGCC) +#define CONFIG_TCC_BCHECK /* enable bound checking code */ +#endif + +/* define it to include assembler support */ +#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_ARM64) && \ + !defined(TCC_TARGET_C67) +#define CONFIG_TCC_ASM +#endif + +/* object format selection */ +#if defined(TCC_TARGET_C67) +#define TCC_TARGET_COFF +#endif + +/* only native compiler supports -run */ +#if defined _WIN32 == defined TCC_TARGET_PE +# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386 +# define TCC_IS_NATIVE +# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64 +# define TCC_IS_NATIVE +# elif defined __arm__ && defined TCC_TARGET_ARM +# define TCC_IS_NATIVE +# elif defined __aarch64__ && defined TCC_TARGET_ARM64 +# define TCC_IS_NATIVE +# endif +#endif + +#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT +# define CONFIG_TCC_BACKTRACE +#endif + +/* ------------ path configuration ------------ */ + +#ifndef CONFIG_SYSROOT +# define CONFIG_SYSROOT "" +#endif +#ifndef CONFIG_TCCDIR +# define CONFIG_TCCDIR "." +#endif +#ifndef CONFIG_LDDIR +# ifdef TCC_TARGET_X86_64 +# define CONFIG_LDDIR "lib64" +# else +# define CONFIG_LDDIR "lib" +# endif +#endif + +#ifdef CONFIG_MULTIARCHDIR +# define USE_MUADIR(s) s "/" CONFIG_MULTIARCHDIR +# define ALSO_MUADIR(s) s "/" CONFIG_MULTIARCHDIR ":" s +#else +# define USE_MUADIR(s) s +# define ALSO_MUADIR(s) s +#endif + +/* path to find crt1.o, crti.o and crtn.o */ +#ifndef CONFIG_TCC_CRTPREFIX +# define CONFIG_TCC_CRTPREFIX USE_MUADIR(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) +#endif + +/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */ + +/* system include paths */ +#ifndef CONFIG_TCC_SYSINCLUDEPATHS +# ifdef TCC_TARGET_PE +# define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include;{B}/include/winapi" +# else +# define CONFIG_TCC_SYSINCLUDEPATHS \ + ALSO_MUADIR(CONFIG_SYSROOT "/usr/local/include") \ + ":" ALSO_MUADIR(CONFIG_SYSROOT "/usr/include") \ + ":" "{B}/include" +# endif +#endif + +/* library search paths */ +#ifndef CONFIG_TCC_LIBPATHS +# ifdef TCC_TARGET_PE +# define CONFIG_TCC_LIBPATHS "{B}/lib" +# else +# define CONFIG_TCC_LIBPATHS \ + ALSO_MUADIR(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \ + ":" ALSO_MUADIR(CONFIG_SYSROOT "/" CONFIG_LDDIR) \ + ":" ALSO_MUADIR(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR) +# endif +#endif + +/* name of ELF interpreter */ +#ifndef CONFIG_TCC_ELFINTERP +# if defined __FreeBSD__ +# define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1" +# elif defined __FreeBSD_kernel__ +# if defined(TCC_TARGET_X86_64) +# define CONFIG_TCC_ELFINTERP "/lib/ld-kfreebsd-x86-64.so.1" +# else +# define CONFIG_TCC_ELFINTERP "/lib/ld.so.1" +# endif +# elif defined __DragonFly__ +# define CONFIG_TCC_ELFINTERP "/usr/libexec/ld-elf.so.2" +# elif defined __GNU__ +# define CONFIG_TCC_ELFINTERP "/lib/ld.so" +# elif defined(TCC_TARGET_PE) +# define CONFIG_TCC_ELFINTERP "-" +# elif defined(TCC_UCLIBC) +# define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" /* is there a uClibc for x86_64 ? */ +# elif defined TCC_TARGET_ARM64 +# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-aarch64.so.1" +# elif defined(TCC_TARGET_X86_64) +# define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2" +# elif !defined(TCC_ARM_EABI) +# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2" +# endif +#endif + +/* var elf_interp dans *-gen.c */ +#ifdef CONFIG_TCC_ELFINTERP +# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP +#else +# define DEFAULT_ELFINTERP(s) default_elfinterp(s) +#endif + +/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */ +#define TCC_LIBGCC USE_MUADIR(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1" + +/* -------------------------------------------- */ +/* include the target specific definitions */ + +#define TARGET_DEFS_ONLY +#ifdef TCC_TARGET_I386 +# include "x86/i386-gen.c" +#endif +#ifdef TCC_TARGET_X86_64 +# include "x86/x86_64-gen.c" +#endif +#ifdef TCC_TARGET_ARM +# include "arm/arm-gen.c" +#endif +#ifdef TCC_TARGET_ARM64 +# include "arm/arm64-gen.c" +#endif +#ifdef TCC_TARGET_C67 +# include "coff.h" +# include "tms320c67c67-gen.c" +#endif +#undef TARGET_DEFS_ONLY + +/* -------------------------------------------- */ + +#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; + +#ifdef TCC_TARGET_PE +typedef unsigned short nwchar_t; +#else +typedef int nwchar_t; +#endif + +typedef struct CString { + int size; /* size in bytes */ + void *data; /* either 'char *' or 'nwchar_t *' */ + 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; + addr_t ptr_offset; + int tab[LDOUBLE_SIZE/4]; +} 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; + +struct Attribute { + unsigned + func_call : 3, /* calling convention (0..5), see below */ + aligned : 5, /* alignement (0..16) */ + packed : 1, + func_export : 1, + func_import : 1, + func_args : 5, + func_proto : 1, + mode : 4, + weak : 1, + visibility : 2, + fill : 8; // 8 bits left to fit well in union below +}; + +/* GNUC attribute definition */ +typedef struct AttributeDef { + struct Attribute a; + struct Section *section; + int alias_target; /* token */ +} AttributeDef; + +/* symbol management */ +typedef struct Sym { + int v; /* symbol token */ + char *asm_label; /* associated asm label */ + union { + long r; /* associated register */ + struct Attribute a; + }; + union { + long c; /* associated number */ + int *d; /* define token stream */ + }; + CType type; /* associated type */ + union { + struct Sym *next; /* next related symbol */ + long jnext; /* next jump label */ + }; + 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 + +/* special flag, too */ +#define SECTION_ABS ((void *)1) + +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) */ + addr_t sh_addr; /* address at which the section is relocated */ + unsigned long sh_offset; /* file offset */ + 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; + void *handle; + char name[1]; +} DLLReference; + +/* -------------------------------------------------- */ + +#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 */ +#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */ + +/* 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; + struct BufferedFile *prev; + int line_num; /* current line number - here to simplify code */ + int line_ref; /* tcc -E: last printed line */ + 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 filename[1024]; /* filename */ + unsigned char unget[4]; + unsigned char buffer[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 { + const 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; + /* used to chain token-strings with begin/end_macro() */ + struct TokenString *prev; + const int *prev_ptr; + char alloc; +} TokenString; + +/* inline functions */ +typedef struct InlineFunc { + TokenString func_str; + Sym *sym; + char filename[1]; +} InlineFunc; + +/* 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 filename[1]; /* path specified in #include */ +} CachedInclude; + +#define CACHED_INCLUDES_HASH_SIZE 512 + +#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; +#endif + +struct sym_attr { + unsigned long got_offset; + unsigned long plt_offset; +#ifdef TCC_TARGET_ARM + unsigned char plt_thumb_stub:1; +#endif +}; + +struct TCCState { + + int verbose; /* if true, display some information during compilation */ + 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 */ + int static_link; /* if true, static linking is performed */ + int rdynamic; /* if true, all symbols are exported */ + int symbolic; /* if true, resolve symbols in the current module first */ + int alacarte_link; /* if true, only link in referenced objects from archive */ + + char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */ + char *soname; /* as specified on the command line (-soname) */ + char *rpath; /* as specified on the command line (-Wl,-rpath=) */ + + /* output type, see TCC_OUTPUT_XXX */ + int output_type; + /* output format, see TCC_OUTPUT_FORMAT_xxx */ + int output_format; + + /* C language options */ + int char_is_unsigned; + int leading_underscore; + int ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */ + int old_struct_init_code; /* use old algorithm to init array in struct when there is no '{' used. + Liuux 2.4.26 can't find initrd when compiled with a new algorithm */ + int dollars_in_identifiers; /* allows '$' char in indentifiers */ + + /* warning switches */ + int warn_write_strings; + int warn_unsupported; + int warn_error; + int warn_none; + int warn_implicit_function_declaration; + + /* compile with debug symbol (and use them if error during execution) */ + int do_debug; + int do_strip; +#ifdef CONFIG_TCC_BCHECK + /* compile with built-in memory and bounds checker */ + int do_bounds_check; +#endif +#ifdef TCC_TARGET_ARM + enum float_abi float_abi; /* float ABI of the generated code*/ +#endif + + addr_t text_addr; /* address of text section */ + int has_text_addr; + + unsigned long section_align; /* section alignment */ + + char *init_symbol; /* symbols to call at load-time (not used currently) */ + char *fini_symbol; /* symbols to call at unload-time (not used currently) */ + +#ifdef TCC_TARGET_I386 + int seg_size; /* 32. Can be 16 with i386 assembler (.code16) */ +#endif + + /* array of all loaded dlls (including those referenced by loaded dlls) */ + DLLReference **loaded_dlls; + int nb_loaded_dlls; + + /* include paths */ + char **include_paths; + int nb_include_paths; + + char **sysinclude_paths; + int nb_sysinclude_paths; + + /* library paths */ + char **library_paths; + int nb_library_paths; + + /* crt?.o object path */ + char **crt_paths; + int nb_crt_paths; + + /* 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; + + /* output file for preprocessing (-E) */ + FILE *ppfp; + enum { + LINE_MACRO_OUTPUT_FORMAT_GCC, + LINE_MACRO_OUTPUT_FORMAT_NONE, + LINE_MACRO_OUTPUT_FORMAT_STD, + } Pflag; /* -P switch */ + int dflag; /* -dX value */ + + /* for -MD/-MF: collected dependencies for this compilation */ + char **target_deps; + int nb_target_deps; + + /* compilation */ + BufferedFile *include_stack[INCLUDE_STACK_SIZE]; + BufferedFile **include_stack_ptr; + + int ifdef_stack[IFDEF_STACK_SIZE]; + int *ifdef_stack_ptr; + + /* included files enclosed with #ifndef MACRO */ + int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE]; + CachedInclude **cached_includes; + int nb_cached_includes; + + /* #pragma pack stack */ + int pack_stack[PACK_STACK_SIZE]; + int *pack_stack_ptr; + char **pragma_libs; + int nb_pragma_libs; + + /* inline functions are stored as token lists and compiled last + only if referenced */ + struct InlineFunc **inline_fns; + int nb_inline_fns; + + /* sections */ + Section **sections; + int nb_sections; /* number of sections, including first dummy section */ + + Section **priv_sections; + int nb_priv_sections; /* number of private sections */ + + /* got & plt handling */ + Section *got; + Section *plt; + struct sym_attr *sym_attrs; + int nb_sym_attrs; + /* 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; + /* copy of the gobal symtab_section variable */ + Section *symtab; + /* tiny assembler state */ + Sym *asm_labels; + +#ifdef TCC_TARGET_PE + /* PE info */ + int pe_subsystem; + unsigned pe_file_align; + unsigned pe_stack_size; +# ifdef TCC_TARGET_X86_64 + Section *uw_pdata; + int uw_sym; + unsigned uw_offs; +# endif +#endif + +#ifdef TCC_IS_NATIVE + const char *runtime_main; + /* for tcc_relocate */ + void *runtime_mem; +# ifdef HAVE_SELINUX + void *write_mem; + unsigned long mem_size; +# endif +#endif + + /* used by main and tcc_parse_args only */ + char **files; /* files seen on command line */ + int nb_files; /* number thereof */ + int nb_libraries; /* number of libs thereof */ + char *outfile; /* output filename */ + char *option_m; /* only -m32/-m64 handled */ + int print_search_dirs; /* option */ + int option_r; /* option -r */ + int do_bench; /* option -bench */ + int gen_deps; /* option -MD */ + char *deps_outfile; /* option -MF */ +}; + +/* The current value can be: */ +#define VT_VALMASK 0x003f /* mask for value location, register or: */ +#define VT_CONST 0x0030 /* constant in vc (must be first non register value) */ +#define VT_LLOCAL 0x0031 /* lvalue, offset on stack */ +#define VT_LOCAL 0x0032 /* offset on stack */ +#define VT_CMP 0x0033 /* the value is stored in processor flags (in vc) */ +#define VT_JMP 0x0034 /* value is the consequence of jmp true (even) */ +#define VT_JMPI 0x0035 /* value is the consequence of jmp false (odd) */ +#define VT_REF 0x0040 /* value is pointer to structure rather than address */ +#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_BTYPE 0x000f /* mask for basic type */ +#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_QLONG 14 /* 128-bit integer. No longer used. */ +#define VT_QFLOAT 15 /* 128-bit float. No longer used. */ +#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_DEFSIGN 0x2000 /* signed type */ +#define VT_VLA 0x00020000 /* VLA type (also has VT_PTR and VT_ARRAY) */ + +/* 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_IMPORT 0x00004000 /* win32: extern data imported from dll */ +#define VT_EXPORT 0x00008000 /* win32: data exported from dll */ +#define VT_WEAK 0x00010000 /* weak symbol */ +#define VT_TLS 0x00040000 /* thread-local storage */ +#define VT_VIS_SHIFT 19 /* shift for symbol visibility, overlapping + bitfield values, because bitfields never + have linkage and hence never have + visibility. */ +#define VT_VIS_SIZE 2 /* We have four visibilities. */ +#define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT) + +#define VT_STRUCT_SHIFT 19 /* shift for bitfield shift values (max: 32 - 2*6) */ + + +/* type mask (except storage) */ +#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK | VT_VIS_MASK) +#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_Nset 0x98 +#define TOK_Nclear 0x99 +#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 */ + +/* tokens that carry values (in additional token string space / tokc) --> */ +#define TOK_CCHAR 0xb3 /* char constant in tokc */ +#define TOK_LCHAR 0xb4 +#define TOK_CINT 0xb5 /* number in tokc */ +#define TOK_CUINT 0xb6 /* unsigned int constant */ +#define TOK_CLLONG 0xb7 /* long long constant */ +#define TOK_CULLONG 0xb8 /* unsigned long long constant */ +#define TOK_STR 0xb9 /* pointer to string in tokc */ +#define TOK_LSTR 0xba +#define TOK_CFLOAT 0xbb /* float constant */ +#define TOK_CDOUBLE 0xbc /* double constant */ +#define TOK_CLDOUBLE 0xbd /* long double constant */ +#define TOK_PPNUM 0xbe /* preprocessor number */ +#define TOK_PPSTR 0xbf /* preprocessor string */ +#define TOK_LINENUM 0xc0 /* line number info */ +/* <-- */ + +#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_ARROW 0xc7 +#define TOK_DOTS 0xc8 /* three dots */ +#define TOK_SHR 0xc9 /* unsigned shift right */ +#define TOK_TWOSHARPS 0xca /* ## preprocessing token */ +#define TOK_PLCHLDR 0xcb /* placeholder token as defined in C99 */ +#define TOK_NOSUBST 0xcc /* means following token has already been pp'd */ + +#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 + +#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 + +#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x) +#define TOK_ASM_int TOK_INT +#define TOK_ASM_weak TOK_WEAK1 + +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +/* only used for i386 asm opcodes definitions */ +#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) +#ifdef TCC_TARGET_X86_64 +# define DEF_BWLQ(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 ## q, #x "q") \ + DEF(TOK_ASM_ ## x, #x) +# define DEF_WLQ(x) \ + DEF(TOK_ASM_ ## x ## w, #x "w") \ + DEF(TOK_ASM_ ## x ## l, #x "l") \ + DEF(TOK_ASM_ ## x ## q, #x "q") \ + DEF(TOK_ASM_ ## x, #x) +# define DEF_BWLX DEF_BWLQ +# define DEF_WLX DEF_WLQ +/* number of sizes + 1 */ +# define NBWLX 5 +#else +# define DEF_BWLX DEF_BWL +# define DEF_WLX DEF_WL +/* number of sizes + 1 */ +# define NBWLX 4 +#endif + +#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,suffix) \ + DEF_ASM(x ## o ## suffix) \ + DEF_ASM(x ## no ## suffix) \ + DEF_ASM(x ## b ## suffix) \ + DEF_ASM(x ## c ## suffix) \ + DEF_ASM(x ## nae ## suffix) \ + DEF_ASM(x ## nb ## suffix) \ + DEF_ASM(x ## nc ## suffix) \ + DEF_ASM(x ## ae ## suffix) \ + DEF_ASM(x ## e ## suffix) \ + DEF_ASM(x ## z ## suffix) \ + DEF_ASM(x ## ne ## suffix) \ + DEF_ASM(x ## nz ## suffix) \ + DEF_ASM(x ## be ## suffix) \ + DEF_ASM(x ## na ## suffix) \ + DEF_ASM(x ## nbe ## suffix) \ + DEF_ASM(x ## a ## suffix) \ + DEF_ASM(x ## s ## suffix) \ + DEF_ASM(x ## ns ## suffix) \ + DEF_ASM(x ## p ## suffix) \ + DEF_ASM(x ## pe ## suffix) \ + DEF_ASM(x ## np ## suffix) \ + DEF_ASM(x ## po ## suffix) \ + DEF_ASM(x ## l ## suffix) \ + DEF_ASM(x ## nge ## suffix) \ + DEF_ASM(x ## nl ## suffix) \ + DEF_ASM(x ## ge ## suffix) \ + DEF_ASM(x ## le ## suffix) \ + DEF_ASM(x ## ng ## suffix) \ + DEF_ASM(x ## nle ## suffix) \ + DEF_ASM(x ## g ## suffix) + +#endif /* defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 */ + +enum tcc_token { + TOK_LAST = TOK_IDENT - 1, +#define DEF(id, str) id, +#include "tcctok.h" +#undef DEF +}; + +#define TOK_UIDENT TOK_DEFINE + +/* space exlcuding newline */ +static inline int is_space(int ch) +{ + return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r'; +} + +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) +{ + return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; +} + +#ifndef PUB_FUNC +# define PUB_FUNC +#endif + +#ifdef ONE_SOURCE +#define ST_INLN static inline +#define ST_FUNC static +#define ST_DATA static +#else +#define ST_INLN +#define ST_FUNC +#define ST_DATA extern +#endif + +/* ------------ libtcc.c ------------ */ + +/* use GNU C extensions */ +ST_DATA int gnu_ext; +/* use Tiny C extensions */ +ST_DATA int tcc_ext; +/* XXX: get rid of this ASAP */ +ST_DATA struct TCCState *tcc_state; + +#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */ +#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */ +#define AFF_PREPROCESS 0x0004 /* preprocess file */ + +/* public functions currently used by the tcc main function */ +PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s); +PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s); +PUB_FUNC char *pstrncpy(char *out, const char *in, size_t num); +PUB_FUNC char *tcc_basename(const char *name); +PUB_FUNC char *tcc_fileextension (const char *name); + +#ifndef MEM_DEBUG +PUB_FUNC void tcc_free(void *ptr); +PUB_FUNC void *tcc_malloc(unsigned long size); +PUB_FUNC void *tcc_mallocz(unsigned long size); +PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size); +PUB_FUNC char *tcc_strdup(const char *str); +#else +#define tcc_free(ptr) tcc_free_debug(ptr) +#define tcc_malloc(size) tcc_malloc_debug(size, __FILE__, __LINE__) +#define tcc_mallocz(size) tcc_mallocz_debug(size, __FILE__, __LINE__) +#define tcc_realloc(ptr,size) tcc_realloc_debug(ptr, size, __FILE__, __LINE__) +#define tcc_strdup(str) tcc_strdup_debug(str, __FILE__, __LINE__) +PUB_FUNC void tcc_free_debug(void *ptr); +PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line); +PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line); +PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line); +PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line); +#endif + +#define free(p) use_tcc_free(p) +#define malloc(s) use_tcc_malloc(s) +#define realloc(p, s) use_tcc_realloc(p, s) +#undef strdup +#define strdup(s) use_tcc_strdup(s) +PUB_FUNC void tcc_memstats(int bench); +PUB_FUNC void tcc_error_noabort(const char *fmt, ...); +PUB_FUNC NORETURN void tcc_error(const char *fmt, ...); +PUB_FUNC void tcc_warning(const char *fmt, ...); + +/* other utilities */ +ST_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data); +ST_FUNC void dynarray_reset(void *pp, int *n); +ST_FUNC void cstr_ccat(CString *cstr, int ch); +ST_FUNC void cstr_cat(CString *cstr, const char *str); +ST_FUNC void cstr_wccat(CString *cstr, int ch); +ST_FUNC void cstr_new(CString *cstr); +ST_FUNC void cstr_free(CString *cstr); +ST_FUNC void cstr_reset(CString *cstr); + +ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags); +ST_FUNC void section_realloc(Section *sec, unsigned long new_size); +ST_FUNC void *section_ptr_add(Section *sec, addr_t size); +ST_FUNC void section_reserve(Section *sec, unsigned long size); +ST_FUNC Section *find_section(TCCState *s1, const char *name); + +ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore); +ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size); +ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type); +ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, addr_t addend); + +ST_INLN void sym_free(Sym *sym); +ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c); +ST_FUNC Sym *sym_find2(Sym *s, int v); +ST_FUNC Sym *sym_push(int v, CType *type, int r, int c); +ST_FUNC void sym_pop(Sym **ptop, Sym *b); +ST_INLN Sym *struct_find(int v); +ST_INLN Sym *sym_find(int v); +ST_FUNC Sym *global_identifier_push(int v, int t, int c); + +ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen); +ST_FUNC int tcc_open(TCCState *s1, const char *filename); +ST_FUNC void tcc_close(void); + +ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags, int filetype); +ST_FUNC int tcc_add_crt(TCCState *s, const char *filename); + +#ifndef TCC_TARGET_PE +ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags); +#endif + +ST_FUNC void tcc_add_pragma_libs(TCCState *s1); +PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f); + +PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time); +PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv); +PUB_FUNC void tcc_set_environment(TCCState *s); + +/* ------------ tccpp.c ------------ */ + +ST_DATA struct BufferedFile *file; +ST_DATA int ch, tok; +ST_DATA CValue tokc; +ST_DATA const int *macro_ptr; +ST_DATA int parse_flags; +ST_DATA int tok_flags; +ST_DATA CString tokcstr; /* current parsed string, if any */ + +/* display benchmark infos */ +ST_DATA int total_lines; +ST_DATA int total_bytes; +ST_DATA int tok_ident; +ST_DATA TokenSym **table_ident; + +#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 */ +#define TOK_FLAG_EOF 0x0008 /* end of file */ + +#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_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */ +#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */ +#define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */ +#define PARSE_FLAG_TOK_STR 0x0040 /* return parsed strings instead of TOK_PPSTR */ + +ST_FUNC TokenSym *tok_alloc(const char *str, int len); +ST_FUNC const char *get_tok_str(int v, CValue *cv); +ST_FUNC void begin_macro(TokenString *str, int alloc); +ST_FUNC void end_macro(void); +ST_FUNC void save_parse_state(ParseState *s); +ST_FUNC void restore_parse_state(ParseState *s); +ST_INLN void tok_str_new(TokenString *s); +ST_FUNC void tok_str_free(int *str); +ST_FUNC void tok_str_add(TokenString *s, int t); +ST_FUNC void tok_str_add_tok(TokenString *s); +ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg); +ST_FUNC void define_undef(Sym *s); +ST_INLN Sym *define_find(int v); +ST_FUNC void free_defines(Sym *b); +ST_FUNC void print_defines(void); +ST_FUNC Sym *label_find(int v); +ST_FUNC Sym *label_push(Sym **ptop, int v, int flags); +ST_FUNC void label_pop(Sym **ptop, Sym *slast); +ST_FUNC void parse_define(void); +ST_FUNC void preprocess(int is_bof); +ST_FUNC void next_nomacro(void); +ST_FUNC void next(void); +ST_INLN void unget_tok(int last_tok); +ST_FUNC void preprocess_init(TCCState *s1); +ST_FUNC void preprocess_new(void); +ST_FUNC void preprocess_delete(void); +ST_FUNC int tcc_preprocess(TCCState *s1); +ST_FUNC void skip(int c); +ST_FUNC NORETURN void expect(const char *msg); + +/* ------------ tccgen.c ------------ */ + +ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */ +ST_DATA Section *cur_text_section; /* current section where function code is generated */ +#ifdef CONFIG_TCC_ASM +ST_DATA Section *last_text_section; /* to handle .previous asm directive */ +#endif +#ifdef CONFIG_TCC_BCHECK +/* bound check related sections */ +ST_DATA Section *bounds_section; /* contains global data bound description */ +ST_DATA Section *lbounds_section; /* contains local data bound description */ +#endif +/* symbol sections */ +ST_DATA Section *symtab_section, *strtab_section; +/* debug sections */ +ST_DATA Section *stab_section, *stabstr_section; + +#define SYM_POOL_NB (8192 / sizeof(Sym)) +ST_DATA Sym *sym_free_first; +ST_DATA void **sym_pools; +ST_DATA int nb_sym_pools; + +ST_DATA Sym *global_stack; +ST_DATA Sym *local_stack; +ST_DATA Sym *local_label_stack; +ST_DATA Sym *global_label_stack; +ST_DATA Sym *define_stack; +ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; +ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop; +#define vstack (__vstack + 1) +ST_DATA int rsym, anon_sym, ind, loc; + +ST_DATA int const_wanted; /* true if constant wanted */ +ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */ +ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */ +ST_DATA CType func_vt; /* current function return type (used by return instruction) */ +ST_DATA int func_var; /* true if current function is variadic */ +ST_DATA int func_vc; +ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ +ST_DATA const char *funcname; + +ST_FUNC void check_vstack(void); +ST_INLN int is_float(int t); +ST_FUNC int ieee_finite(double d); +ST_FUNC void test_lvalue(void); +ST_FUNC void swap(int *p, int *q); +ST_FUNC void vpushi(int v); +ST_FUNC Sym *external_global_sym(int v, CType *type, int r); +ST_FUNC void vset(CType *type, int r, int v); +ST_FUNC void vswap(void); +ST_FUNC void vpush_global_sym(CType *type, int v); +ST_FUNC void vrote(SValue *e, int n); +ST_FUNC void vrott(int n); +ST_FUNC void vrotb(int n); +#ifdef TCC_TARGET_ARM +ST_FUNC int get_reg_ex(int rc, int rc2); +ST_FUNC void lexpand_nr(void); +#endif +ST_FUNC void vpushv(SValue *v); +ST_FUNC void save_reg(int r); +ST_FUNC int get_reg(int rc); +ST_FUNC void save_regs(int n); +ST_FUNC void gaddrof(void); +ST_FUNC int gv(int rc); +ST_FUNC void gv2(int rc1, int rc2); +ST_FUNC void vdup(void); +ST_FUNC void vpop(void); +ST_FUNC void gen_op(int op); +ST_FUNC int type_size(CType *type, int *a); +ST_FUNC void mk_pointer(CType *type); +ST_FUNC void vstore(void); +ST_FUNC void inc(int post, int c); +ST_FUNC void parse_asm_str(CString *astr); +ST_FUNC int lvalue_type(int t); +ST_FUNC void indir(void); +ST_FUNC void unary(void); +ST_FUNC void expr_prod(void); +ST_FUNC void expr_sum(void); +ST_FUNC void gexpr(void); +ST_FUNC int expr_const(void); +ST_FUNC void gen_inline_functions(void); +ST_FUNC void decl(int l); +#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67 +ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size); +#endif +#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE +ST_FUNC int classify_x86_64_va_arg(CType *ty); +#endif + +/* ------------ tccelf.c ------------ */ + +#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 */ + +#define ARMAG "!<arch>\012" /* For COFF and a.out archives */ + +typedef struct { + unsigned int 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 int n_value; /* value of symbol */ +} Stab_Sym; + +ST_FUNC 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); + +ST_FUNC int put_elf_str(Section *s, const char *sym); +ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name); +ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int sh_num, const char *name); +ST_FUNC int find_elf_sym(Section *s, const char *name); +ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol); +ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, addr_t addend); + +ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value); +ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index); +ST_FUNC void put_stabn(int type, int other, int desc, int value); +ST_FUNC void put_stabd(int type, int other, int desc); + +ST_FUNC void relocate_common_syms(void); +ST_FUNC void relocate_syms(TCCState *s1, int do_resolve); +ST_FUNC void relocate_section(TCCState *s1, Section *s); +ST_FUNC void relocate_plt(TCCState *s1); + +ST_FUNC void tcc_add_linker_symbols(TCCState *s1); +ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset); +ST_FUNC int tcc_load_archive(TCCState *s1, int fd); +ST_FUNC void tcc_add_bcheck(TCCState *s1); + +ST_FUNC void build_got_entries(TCCState *s1); +ST_FUNC void tcc_add_runtime(TCCState *s1); + +ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err); +#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE +ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name); +#endif + +#ifndef TCC_TARGET_PE +ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level); +ST_FUNC int tcc_load_ldscript(TCCState *s1); +ST_FUNC uint8_t *parse_comment(uint8_t *p); +ST_FUNC void minp(void); +ST_INLN void inp(void); +ST_FUNC int handle_eob(void); +#endif + +/* ------------ xxx-gen.c ------------ */ + +ST_DATA const int reg_classes[NB_REGS]; + +ST_FUNC void gsym_addr(int t, int a); +ST_FUNC void gsym(int t); +ST_FUNC void load(int r, SValue *sv); +ST_FUNC void store(int r, SValue *v); +ST_FUNC int regargs_nregs(RegArgs *args); +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize, RegArgs *args); +ST_FUNC void gfunc_call(int nb_args); +ST_FUNC void gfunc_prolog(CType *func_type); +ST_FUNC void gfunc_epilog(void); +ST_FUNC int gjmp(int t); +ST_FUNC void gjmp_addr(int a); +ST_FUNC int gtst(int inv, int t); +ST_FUNC void gen_opi(int op); +ST_FUNC void gen_opf(int op); +ST_FUNC void gen_cvt_ftoi(int t); +ST_FUNC void gen_cvt_ftof(int t); +ST_FUNC void ggoto(void); +#ifndef TCC_TARGET_C67 +ST_FUNC void o(unsigned int c); +#endif +#ifndef TCC_TARGET_ARM +ST_FUNC void gen_cvt_itof(int t); +#endif +ST_FUNC void gen_vla_sp_save(int addr); +ST_FUNC void gen_vla_sp_restore(int addr); +ST_FUNC void gen_vla_alloc(CType *type, int align); + +/* ------------ i386-gen.c ------------ */ +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +ST_FUNC void g(int c); +ST_FUNC int oad(int c, int s); +ST_FUNC void gen_le16(int c); +ST_FUNC void gen_le32(int c); +ST_FUNC void gen_addr32(int r, Sym *sym, int c); +ST_FUNC void gen_addrpc32(int r, Sym *sym, int c); +#endif + +#ifdef CONFIG_TCC_BCHECK +ST_FUNC void gen_bounded_ptr_add(void); +ST_FUNC void gen_bounded_ptr_deref(void); +#endif + +/* ------------ x86_64-gen.c ------------ */ +#ifdef TCC_TARGET_X86_64 +ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c); +ST_FUNC void gen_opl(int op); +#endif + +/* ------------ arm-gen.c ------------ */ +#ifdef TCC_TARGET_ARM +#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP) +ST_FUNC char *default_elfinterp(struct TCCState *s); +#endif +ST_FUNC void arm_init(struct TCCState *s); +ST_FUNC uint32_t encbranch(int pos, int addr, int fail); +ST_FUNC void gen_cvt_itof1(int t); +#endif + +/* ------------ arm64-gen.c ------------ */ +#ifdef TCC_TARGET_ARM64 +ST_FUNC void gen_cvt_sxtw(void); +ST_FUNC void gen_opl(int op); +ST_FUNC void greturn(void); +ST_FUNC void gen_va_start(void); +ST_FUNC void gen_va_arg(CType *t); +ST_FUNC void gen_clear_cache(void); +#endif + +/* ------------ c67-gen.c ------------ */ +#ifdef TCC_TARGET_C67 +#endif + +/* ------------ tcccoff.c ------------ */ + +#ifdef TCC_TARGET_COFF +ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f); +ST_FUNC int tcc_load_coff(TCCState * s1, int fd); +#endif + +/* ------------ tccasm.c ------------ */ +ST_FUNC void asm_instr(void); +ST_FUNC void asm_global_instr(void); +#ifdef CONFIG_TCC_ASM +ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp); +ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe); +ST_FUNC int asm_int_expr(TCCState *s1); +ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess); +/* ------------ i386-asm.c ------------ */ +ST_FUNC void gen_expr32(ExprValue *pe); +ST_FUNC void asm_opcode(TCCState *s1, int opcode); +ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg); +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier); +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg); +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str); +#endif + +/* ------------ tccpe.c -------------- */ +#ifdef TCC_TARGET_PE +ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd); +ST_FUNC int pe_output_file(TCCState * s1, const char *filename); +ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value); +ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2); +#ifdef TCC_TARGET_X86_64 +ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack); +#endif +/* symbol properties stored in Elf32_Sym->st_other */ +# define ST_PE_EXPORT 0x10 +# define ST_PE_IMPORT 0x20 +# define ST_PE_STDCALL 0x40 +#endif + +/* ------------ tccrun.c ----------------- */ +#ifdef TCC_IS_NATIVE +#ifdef CONFIG_TCC_STATIC +#define RTLD_LAZY 0x001 +#define RTLD_NOW 0x002 +#define RTLD_GLOBAL 0x100 +#define RTLD_DEFAULT NULL +/* dummy function for profiling */ +ST_FUNC void *dlopen(const char *filename, int flag); +ST_FUNC void dlclose(void *p); +ST_FUNC const char *dlerror(void); +ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol); +#elif !defined _WIN32 +ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol); +#endif + +#ifdef CONFIG_TCC_BACKTRACE +ST_DATA int rt_num_callers; +ST_DATA const char **rt_bound_error_msg; +ST_DATA void *rt_prog_main; +ST_FUNC void tcc_set_num_callers(int n); +#endif +#endif + +/********************************************************/ +#undef ST_DATA +#ifdef ONE_SOURCE +#define ST_DATA static +#else +#define ST_DATA +#endif +/********************************************************/ +#endif /* _TCC_H */ diff --git a/src/tccasm.c b/src/tccasm.c new file mode 100644 index 0000000..b184c88 --- /dev/null +++ b/src/tccasm.c @@ -0,0 +1,1130 @@ +/* + * 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 + */ + +#include "tcc.h" +#ifdef CONFIG_TCC_ASM + +ST_FUNC 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; +} + +ST_FUNC 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) + tcc_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 { + tcc_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) + tcc_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 = sym->jnext; + pe->sym = NULL; + } else { + pe->v = 0; + pe->sym = sym; + } + next(); + } else { + tcc_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) + tcc_error("invalid operation with label"); + switch(op) { + case '*': + pe->v *= e2.v; + break; + case '/': + if (e2.v == 0) { + div_error: + tcc_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) + tcc_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 += pe->sym->jnext - e2.sym->jnext; + } else { + goto cannot_relocate; + } + pe->sym = NULL; /* same symbols can be subtracted to NULL */ + } else { + cannot_relocate: + tcc_error("invalid operation with label"); + } + } + } +} + +ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe) +{ + asm_expr_sum(s1, pe); +} + +ST_FUNC 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, int value) +{ + Sym *sym; + + sym = label_find(label); + if (sym) { + if (sym->r) { + /* the label is already defined */ + if (!is_local) { + tcc_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->jnext = 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, s->jnext, 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_p2align: + case TOK_ASM_skip: + case TOK_ASM_space: + tok1 = tok; + next(); + n = asm_int_expr(s1); + if (tok1 == TOK_ASM_p2align) + { + if (n < 0 || n > 30) + tcc_error("invalid p2align, must be between 0 and 30"); + n = 1 << n; + tok1 = TOK_ASM_align; + } + if (tok1 == TOK_ASM_align) { + if (n < 0 || (n & (n-1)) != 0) + tcc_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: + tcc_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) { + tcc_error("repeat < 0; .fill ignored"); + break; + } + size = 1; + val = 0; + if (tok == ',') { + next(); + size = asm_int_expr(s1); + if (size < 0) { + tcc_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) + tcc_error("attempt to .org backwards"); + v = 0; + size = n - ind; + goto zero_pad; + } + break; + case TOK_ASM_globl: + case TOK_ASM_global: + case TOK_ASM_weak: + case TOK_ASM_hidden: + tok1 = tok; + do { + Sym *sym; + + next(); + sym = label_find(tok); + if (!sym) { + sym = label_push(&s1->asm_labels, tok, 0); + sym->type.t = VT_VOID; + } + if (tok1 != TOK_ASM_hidden) + sym->type.t &= ~VT_STATIC; + if (tok1 == TOK_ASM_weak) + sym->type.t |= VT_WEAK; + else if (tok1 == TOK_ASM_hidden) + sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT; + next(); + } while (tok == ','); + 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_ASM_file: + { + char filename[512]; + + filename[0] = '\0'; + next(); + + if (tok == TOK_STR) + pstrcat(filename, sizeof(filename), tokc.cstr->data); + else + pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL)); + + if (s1->warn_unsupported) + tcc_warning("ignoring .file %s", filename); + + next(); + } + break; + case TOK_ASM_ident: + { + char ident[256]; + + ident[0] = '\0'; + next(); + + if (tok == TOK_STR) + pstrcat(ident, sizeof(ident), tokc.cstr->data); + else + pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL)); + + if (s1->warn_unsupported) + tcc_warning("ignoring .ident %s", ident); + + next(); + } + break; + case TOK_ASM_size: + { + Sym *sym; + + next(); + sym = label_find(tok); + if (!sym) { + tcc_error("label not found: %s", get_tok_str(tok, NULL)); + } + + /* XXX .size name,label2-label1 */ + if (s1->warn_unsupported) + tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL)); + + next(); + skip(','); + while (tok != '\n' && tok != CH_EOF) { + next(); + } + } + break; + case TOK_ASM_type: + { + Sym *sym; + const char *newtype; + + next(); + sym = label_find(tok); + if (!sym) { + sym = label_push(&s1->asm_labels, tok, 0); + sym->type.t = VT_VOID; + } + + next(); + skip(','); + if (tok == TOK_STR) { + newtype = tokc.cstr->data; + } else { + if (tok == '@' || tok == '%') + skip(tok); + newtype = get_tok_str(tok, NULL); + } + + if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) { + sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC; + } + else if (s1->warn_unsupported) + tcc_warning("change type of '%s' from 0x%x to '%s' ignored", + get_tok_str(sym->v, NULL), sym->type.t, newtype); + + next(); + } + 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) + tcc_error("no previous section referenced"); + sec = cur_text_section; + use_section1(s1, last_text_section); + last_text_section = sec; + } + break; +#ifdef TCC_TARGET_I386 + case TOK_ASM_code16: + { + next(); + s1->seg_size = 16; + } + break; + case TOK_ASM_code32: + { + next(); + s1->seg_size = 32; + } + break; +#endif +#ifdef TCC_TARGET_X86_64 + /* added for compatibility with GAS */ + case TOK_ASM_code64: + next(); + break; +#endif + default: + tcc_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_FILE | PARSE_FLAG_TOK_STR; + 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 */ +ST_FUNC 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; + + /* 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, + ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, + SHN_ABS, file->filename); + + 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) +{ + int saved_parse_flags; + const int *saved_macro_ptr; + + saved_parse_flags = parse_flags; + saved_macro_ptr = macro_ptr; + + tcc_open_bf(s1, ":asm:", len); + memcpy(file->buffer, str, len); + + macro_ptr = NULL; + tcc_assemble_internal(s1, 0); + tcc_close(); + + parse_flags = saved_parse_flags; + macro_ptr = saved_macro_ptr; +} + +/* 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 */ +ST_FUNC 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) + tcc_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 && op->is_memory) + 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) + tcc_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; + } +} + +/* parse the GCC asm() instruction */ +ST_FUNC void asm_instr(void) +{ + CString astr, astr1; + ASMOperand operands[MAX_ASM_OPERANDS]; + 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(); + if (tok != ')') { + /* 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("';'"); + + /* 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); +} + +ST_FUNC 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); +} +#endif /* CONFIG_TCC_ASM */ diff --git a/src/tcccoff.c b/src/tcccoff.c new file mode 100644 index 0000000..6ef9759 --- /dev/null +++ b/src/tcccoff.c @@ -0,0 +1,924 @@ +/* + * COFF file handling for TCC + * + * Copyright (c) 2003, 2004 TK + * Copyright (c) 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 + */ + +#include "tcc.h" + +#define MAXNSCNS 255 /* MAXIMUM NUMBER OF SECTIONS */ +#define MAX_STR_TABLE 1000000 +AOUTHDR o_filehdr; /* OPTIONAL (A.OUT) FILE HEADER */ + +SCNHDR section_header[MAXNSCNS]; + +#define MAX_FUNCS 1000 +#define MAX_FUNC_NAME_LENGTH 128 + +int nFuncs; +char Func[MAX_FUNCS][MAX_FUNC_NAME_LENGTH]; +char AssociatedFile[MAX_FUNCS][MAX_FUNC_NAME_LENGTH]; +int LineNoFilePtr[MAX_FUNCS]; +int EndAddress[MAX_FUNCS]; +int LastLineNo[MAX_FUNCS]; +int FuncEntries[MAX_FUNCS]; + +int OutputTheSection(Section* sect); +short int GetCoffFlags(const char* s); +void SortSymbolTable(void); +Section* FindSection(TCCState* s1, const char* sname); + +int C67_main_entry_point; + +int FindCoffSymbolIndex(const char* func_name); +int nb_syms; + +typedef struct { + long tag; + long size; + long fileptr; + long nextsym; + short int dummy; +} AUXFUNC; + +typedef struct { + long regmask; + unsigned short lineno; + unsigned short nentries; + int localframe; + int nextentry; + short int dummy; +} AUXBF; + +typedef struct { + long dummy; + unsigned short lineno; + unsigned short dummy1; + int dummy2; + int dummy3; + unsigned short dummy4; +} AUXEF; + +ST_FUNC int tcc_output_coff(TCCState* s1, FILE* f) +{ + Section* tcc_sect; + SCNHDR* coff_sec; + int file_pointer; + char* Coff_str_table, *pCoff_str_table; + int CoffTextSectionNo, coff_nb_syms; + FILHDR file_hdr; /* FILE HEADER STRUCTURE */ + Section* stext, *sdata, *sbss; + int i, NSectionsToOutput = 0; + + Coff_str_table = pCoff_str_table = NULL; + + stext = FindSection(s1, ".text"); + sdata = FindSection(s1, ".data"); + sbss = FindSection(s1, ".bss"); + + nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym); + coff_nb_syms = FindCoffSymbolIndex("XXXXXXXXXX1"); + + file_hdr.f_magic = COFF_C67_MAGIC; /* magic number */ + file_hdr.f_timdat = 0; /* time & date stamp */ + file_hdr.f_opthdr = sizeof(AOUTHDR); /* sizeof(optional hdr) */ + file_hdr.f_flags = 0x1143; /* flags (copied from what code composer does) */ + file_hdr.f_TargetID = 0x99; /* for C6x = 0x0099 */ + + o_filehdr.magic = 0x0108; /* see magic.h */ + o_filehdr.vstamp = 0x0190; /* version stamp */ + o_filehdr.tsize = + stext->data_offset; /* text size in bytes, padded to FW bdry */ + o_filehdr.dsize = sdata->data_offset; /* initialized data " " */ + o_filehdr.bsize = sbss->data_offset; /* uninitialized data " " */ + o_filehdr.entrypt = C67_main_entry_point; /* entry pt. */ + o_filehdr.text_start = stext->sh_addr; /* base of text used for this file */ + o_filehdr.data_start = sdata->sh_addr; /* base of data used for this file */ + + // create all the section headers + + file_pointer = FILHSZ + sizeof(AOUTHDR); + + CoffTextSectionNo = -1; + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + NSectionsToOutput++; + + if (CoffTextSectionNo == -1 && tcc_sect == stext) + CoffTextSectionNo = NSectionsToOutput; // rem which coff sect + // number the .text sect + // is + + strcpy(coff_sec->s_name, tcc_sect->name); /* section name */ + + coff_sec->s_paddr = tcc_sect->sh_addr; /* physical address */ + coff_sec->s_vaddr = tcc_sect->sh_addr; /* virtual address */ + coff_sec->s_size = tcc_sect->data_offset; /* section size */ + coff_sec->s_scnptr = 0; /* file ptr to raw data for section */ + coff_sec->s_relptr = 0; /* file ptr to relocation */ + coff_sec->s_lnnoptr = 0; /* file ptr to line numbers */ + coff_sec->s_nreloc = 0; /* number of relocation entries */ + coff_sec->s_flags = GetCoffFlags(coff_sec->s_name); /* flags */ + coff_sec->s_reserved = 0; /* reserved byte */ + coff_sec->s_page = 0; /* memory page id */ + + file_pointer += sizeof(SCNHDR); + } + } + + file_hdr.f_nscns = NSectionsToOutput; /* number of sections */ + + // now loop through and determine file pointer locations + // for the raw data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put raw data + coff_sec->s_scnptr = + file_pointer; /* file ptr to raw data for section */ + file_pointer += coff_sec->s_size; + } + } + + // now loop through and determine file pointer locations + // for the relocation data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put relocations data + if (coff_sec->s_nreloc > 0) { + coff_sec->s_relptr = file_pointer; /* file ptr to relocation */ + file_pointer += coff_sec->s_nreloc * sizeof(struct reloc); + } + } + } + + // now loop through and determine file pointer locations + // for the line number data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + coff_sec->s_nlnno = 0; + coff_sec->s_lnnoptr = 0; + + if (s1->do_debug && tcc_sect == stext) { + // count how many line nos data + + // also find association between source file name and function + // so we can sort the symbol table + + Stab_Sym* sym, *sym_end; + char func_name[MAX_FUNC_NAME_LENGTH], + last_func_name[MAX_FUNC_NAME_LENGTH]; + unsigned long func_addr, last_pc, pc; + const char* incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num; + const char* str, *p; + + coff_sec->s_lnnoptr = file_pointer; /* file ptr to linno */ + + 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); + + nFuncs = 0; + while (sym < sym_end) { + switch (sym->n_type) { + /* function start or end */ + case N_FUN: + if (sym->n_strx == 0) { + // end of function + + coff_sec->s_nlnno++; + file_pointer += LINESZ; + + pc = sym->n_value + func_addr; + func_name[0] = '\0'; + func_addr = 0; + EndAddress[nFuncs] = pc; + FuncEntries[nFuncs] = + (file_pointer - LineNoFilePtr[nFuncs]) / LINESZ - 1; + LastLineNo[nFuncs++] = last_line_num + 1; + } else { + // beginning of function + + LineNoFilePtr[nFuncs] = file_pointer; + coff_sec->s_nlnno++; + file_pointer += LINESZ; + + str = (const char*)stabstr_section->data + sym->n_strx; + + p = strchr(str, ':'); + if (!p) { + pstrcpy(func_name, sizeof(func_name), str); + pstrcpy(Func[nFuncs], sizeof(func_name), str); + } else { + len = p - str; + if (len > sizeof(func_name) - 1) + len = sizeof(func_name) - 1; + memcpy(func_name, str, len); + memcpy(Func[nFuncs], str, len); + func_name[len] = '\0'; + } + + // save the file that it came in so we can sort later + pstrcpy(AssociatedFile[nFuncs], sizeof(func_name), + incl_files[incl_index - 1]); + + func_addr = sym->n_value; + } + break; + + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + + last_pc = pc; + last_line_num = sym->n_desc; + + /* XXX: slow! */ + strcpy(last_func_name, func_name); + + coff_sec->s_nlnno++; + file_pointer += LINESZ; + break; + /* include files */ + case N_BINCL: + str = (const char*)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 = (const char*)stabstr_section->data + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl; + } + break; + } + sym++; + } + } + } + + file_hdr.f_symptr = file_pointer; /* file pointer to symtab */ + + if (s1->do_debug) + file_hdr.f_nsyms = coff_nb_syms; /* number of symtab entries */ + else + file_hdr.f_nsyms = 0; + + file_pointer += file_hdr.f_nsyms * SYMNMLEN; + + // OK now we are all set to write the file + + fwrite(&file_hdr, FILHSZ, 1, f); + fwrite(&o_filehdr, sizeof(o_filehdr), 1, f); + + // write section headers + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + fwrite(coff_sec, sizeof(SCNHDR), 1, f); + } + } + + // write raw data + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + fwrite(tcc_sect->data, tcc_sect->data_offset, 1, f); + } + } + + // write relocation data + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put relocations data + if (coff_sec->s_nreloc > 0) { + fwrite(tcc_sect->reloc, + coff_sec->s_nreloc * sizeof(struct reloc), 1, f); + } + } + } + + // group the symbols in order of filename, func1, func2, etc + // finally global symbols + + if (s1->do_debug) + SortSymbolTable(); + + // write line no data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (s1->do_debug && tcc_sect == stext) { + // count how many line nos data + + 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; + const char* str, *p; + + LINENO CoffLineNo; + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = 0; + 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) { + // end of function + + CoffLineNo.l_addr.l_paddr = last_pc; + CoffLineNo.l_lnno = last_line_num + 1; + fwrite(&CoffLineNo, 6, 1, f); + + pc = sym->n_value + func_addr; + func_name[0] = '\0'; + func_addr = 0; + } else { + // beginning of function + + str = (const char*)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; + last_pc = func_addr; + last_line_num = -1; + + // output a function begin + + CoffLineNo.l_addr.l_symndx = + FindCoffSymbolIndex(func_name); + CoffLineNo.l_lnno = 0; + + fwrite(&CoffLineNo, 6, 1, f); + } + break; + + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + + /* XXX: slow! */ + strcpy(last_func_name, func_name); + + // output a line reference + + CoffLineNo.l_addr.l_paddr = last_pc; + + if (last_line_num == -1) { + CoffLineNo.l_lnno = sym->n_desc; + } else { + CoffLineNo.l_lnno = last_line_num + 1; + } + + fwrite(&CoffLineNo, 6, 1, f); + + last_pc = pc; + last_line_num = sym->n_desc; + + break; + + /* include files */ + case N_BINCL: + str = (const char*)stabstr_section->data + sym->n_strx; + add_incl2: + 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 = (const char*)stabstr_section->data + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl2; + } + break; + } + sym++; + } + } + } + + // write symbol table + if (s1->do_debug) { + int k; + struct syment csym; + AUXFUNC auxfunc; + AUXBF auxbf; + AUXEF auxef; + int i; + Elf32_Sym* p; + const char* name; + int nstr; + int n = 0; + + Coff_str_table = (char*)tcc_malloc(MAX_STR_TABLE); + pCoff_str_table = Coff_str_table; + nstr = 0; + + p = (Elf32_Sym*)symtab_section->data; + + for (i = 0; i < nb_syms; i++) { + + name = symtab_section->link->data + p->st_name; + + for (k = 0; k < 8; k++) + csym._n._n_name[k] = 0; + + if (strlen(name) <= 8) { + strcpy(csym._n._n_name, name); + } else { + if (pCoff_str_table - Coff_str_table + strlen(name) > + MAX_STR_TABLE - 1) + tcc_error("String table too large"); + + csym._n._n_n._n_zeroes = 0; + csym._n._n_n._n_offset = pCoff_str_table - Coff_str_table + 4; + + strcpy(pCoff_str_table, name); + pCoff_str_table += strlen(name) + 1; // skip over null + nstr++; + } + + if (p->st_info == 4) { + // put a filename symbol + csym.n_value = 33; // ????? + csym.n_scnum = N_DEBUG; + csym.n_type = 0; + csym.n_sclass = C_FILE; + csym.n_numaux = 0; + fwrite(&csym, 18, 1, f); + n++; + + } else if (p->st_info == 0x12) { + // find the function data + + for (k = 0; k < nFuncs; k++) { + if (strcmp(name, Func[k]) == 0) + break; + } + + if (k >= nFuncs) { + tcc_error("debug info can't find function: %s", name); + } + // put a Function Name + + csym.n_value = p->st_value; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = MKTYPE(T_INT, DT_FCN, 0, 0, 0, 0, 0); + csym.n_sclass = C_EXT; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxfunc.tag = 0; + auxfunc.size = EndAddress[k] - p->st_value; + auxfunc.fileptr = LineNoFilePtr[k]; + auxfunc.nextsym = n + 6; // tktk + auxfunc.dummy = 0; + fwrite(&auxfunc, 18, 1, f); + + // put a .bf + + strcpy(csym._n._n_name, ".bf"); + csym.n_value = p->st_value; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = 0; + csym.n_sclass = C_FCN; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxbf.regmask = 0; + auxbf.lineno = 0; + auxbf.nentries = FuncEntries[k]; + auxbf.localframe = 0; + auxbf.nextentry = n + 6; + auxbf.dummy = 0; + fwrite(&auxbf, 18, 1, f); + + // put a .ef + + strcpy(csym._n._n_name, ".ef"); + csym.n_value = EndAddress[k]; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = 0; + csym.n_sclass = C_FCN; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxef.dummy = 0; + auxef.lineno = LastLineNo[k]; + auxef.dummy1 = 0; + auxef.dummy2 = 0; + auxef.dummy3 = 0; + auxef.dummy4 = 0; + fwrite(&auxef, 18, 1, f); + + n += 6; + + } else { + // try an put some type info + + if ((p->st_other & VT_BTYPE) == VT_DOUBLE) { + csym.n_type = T_DOUBLE; // int + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_FLOAT) { + csym.n_type = T_FLOAT; + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_INT) { + csym.n_type = T_INT; // int + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_SHORT) { + csym.n_type = T_SHORT; + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_BYTE) { + csym.n_type = T_CHAR; + csym.n_sclass = C_EXT; + } else { + csym.n_type = T_INT; // just mark as a label + csym.n_sclass = C_LABEL; + } + + csym.n_value = p->st_value; + csym.n_scnum = 2; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + auxfunc.tag = 0; + auxfunc.size = 0x20; + auxfunc.fileptr = 0; + auxfunc.nextsym = 0; + auxfunc.dummy = 0; + fwrite(&auxfunc, 18, 1, f); + n++; + n++; + } + + p++; + } + } + + if (s1->do_debug) { + // write string table + + // first write the size + i = pCoff_str_table - Coff_str_table; + fwrite(&i, 4, 1, f); + + // then write the strings + fwrite(Coff_str_table, i, 1, f); + + tcc_free(Coff_str_table); + } + + return 0; +} + +// group the symbols in order of filename, func1, func2, etc +// finally global symbols + +void SortSymbolTable(void) +{ + int i, j, k, n = 0; + Elf32_Sym* p, *p2, *NewTable; + char* name, *name2; + + NewTable = (Elf32_Sym*)tcc_malloc(nb_syms * sizeof(Elf32_Sym)); + + p = (Elf32_Sym*)symtab_section->data; + + // find a file symbol, copy it over + // then scan the whole symbol list and copy any function + // symbols that match the file association + + for (i = 0; i < nb_syms; i++) { + if (p->st_info == 4) { + name = (char*)symtab_section->link->data + p->st_name; + + // this is a file symbol, copy it over + + NewTable[n++] = *p; + + p2 = (Elf32_Sym*)symtab_section->data; + + for (j = 0; j < nb_syms; j++) { + if (p2->st_info == 0x12) { + // this is a func symbol + + name2 = (char*)symtab_section->link->data + p2->st_name; + + // find the function data index + + for (k = 0; k < nFuncs; k++) { + if (strcmp(name2, Func[k]) == 0) + break; + } + + if (k >= nFuncs) { + tcc_error("debug (sort) info can't find function: %s", + name2); + } + + if (strcmp(AssociatedFile[k], name) == 0) { + // yes they match copy it over + + NewTable[n++] = *p2; + } + } + p2++; + } + } + p++; + } + + // now all the filename and func symbols should have been copied over + // copy all the rest over (all except file and funcs) + + p = (Elf32_Sym*)symtab_section->data; + for (i = 0; i < nb_syms; i++) { + if (p->st_info != 4 && p->st_info != 0x12) { + NewTable[n++] = *p; + } + p++; + } + + if (n != nb_syms) + tcc_error("Internal Compiler error, debug info"); + + // copy it all back + + p = (Elf32_Sym*)symtab_section->data; + for (i = 0; i < nb_syms; i++) { + *p++ = NewTable[i]; + } + + tcc_free(NewTable); +} + +int FindCoffSymbolIndex(const char* func_name) +{ + int i, n = 0; + Elf32_Sym* p; + char* name; + + p = (Elf32_Sym*)symtab_section->data; + + for (i = 0; i < nb_syms; i++) { + + name = (char*)symtab_section->link->data + p->st_name; + + if (p->st_info == 4) { + // put a filename symbol + n++; + } else if (p->st_info == 0x12) { + + if (strcmp(func_name, name) == 0) + return n; + + n += 6; + + // put a Function Name + + // now put aux info + + // put a .bf + + // now put aux info + + // put a .ef + + // now put aux info + + } else { + n += 2; + } + + p++; + } + + return n; // total number of symbols +} + +int OutputTheSection(Section* sect) +{ + const char* s = sect->name; + + if (!strcmp(s, ".text")) + return 1; + else if (!strcmp(s, ".data")) + return 1; + else + return 0; +} + +short int GetCoffFlags(const char* s) +{ + if (!strcmp(s, ".text")) + return STYP_TEXT | STYP_DATA | STYP_ALIGN | 0x400; + else if (!strcmp(s, ".data")) + return STYP_DATA; + else if (!strcmp(s, ".bss")) + return STYP_BSS; + else if (!strcmp(s, ".stack")) + return STYP_BSS | STYP_ALIGN | 0x200; + else if (!strcmp(s, ".cinit")) + return STYP_COPY | STYP_DATA | STYP_ALIGN | 0x200; + else + return 0; +} + +Section* FindSection(TCCState* s1, const char* sname) +{ + Section* s; + int i; + + for (i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + + if (!strcmp(sname, s->name)) + return s; + } + + tcc_error("could not find section %s", sname); + return 0; +} + +ST_FUNC int tcc_load_coff(TCCState* s1, int fd) +{ + // tktk TokenSym *ts; + + FILE* f; + unsigned int str_size; + char* Coff_str_table, *name; + int i, k; + struct syment csym; + char name2[9]; + FILHDR file_hdr; /* FILE HEADER STRUCTURE */ + + f = fdopen(fd, "rb"); + if (!f) { + tcc_error("Unable to open .out file for input"); + } + + if (fread(&file_hdr, FILHSZ, 1, f) != 1) + tcc_error("error reading .out file for input"); + + if (fread(&o_filehdr, sizeof(o_filehdr), 1, f) != 1) + tcc_error("error reading .out file for input"); + + // first read the string table + + if (fseek(f, file_hdr.f_symptr + file_hdr.f_nsyms * SYMESZ, SEEK_SET)) + tcc_error("error reading .out file for input"); + + if (fread(&str_size, sizeof(int), 1, f) != 1) + tcc_error("error reading .out file for input"); + + Coff_str_table = (char*)tcc_malloc(str_size); + + if (fread(Coff_str_table, str_size - 4, 1, f) != 1) + tcc_error("error reading .out file for input"); + + // read/process all the symbols + + // seek back to symbols + + if (fseek(f, file_hdr.f_symptr, SEEK_SET)) + tcc_error("error reading .out file for input"); + + for (i = 0; i < file_hdr.f_nsyms; i++) { + if (fread(&csym, SYMESZ, 1, f) != 1) + tcc_error("error reading .out file for input"); + + if (csym._n._n_n._n_zeroes == 0) { + name = Coff_str_table + csym._n._n_n._n_offset - 4; + } else { + name = csym._n._n_name; + + if (name[7] != 0) { + for (k = 0; k < 8; k++) + name2[k] = name[k]; + + name2[8] = 0; + + name = name2; + } + } + // if (strcmp("_DAC_Buffer",name)==0) // tktk + // name[0]=0; + + if (((csym.n_type & 0x30) == 0x20 && csym.n_sclass == 0x2) || + ((csym.n_type & 0x30) == 0x30 && csym.n_sclass == 0x2) || + (csym.n_type == 0x4 && csym.n_sclass == 0x2) || + (csym.n_type == 0x8 && csym.n_sclass == 0x2) || // structures + (csym.n_type == 0x18 && + csym.n_sclass == 0x2) || // pointer to structure + (csym.n_type == 0x7 && csym.n_sclass == 0x2) || // doubles + (csym.n_type == 0x6 && csym.n_sclass == 0x2)) // floats + { + // strip off any leading underscore (except for other main routine) + + if (name[0] == '_' && strcmp(name, "_main") != 0) + name++; + + tcc_add_symbol(s1, name, (void*)(uintptr_t)csym.n_value); + } + // skip any aux records + + if (csym.n_numaux == 1) { + if (fread(&csym, SYMESZ, 1, f) != 1) + tcc_error("error reading .out file for input"); + i++; + } + } + + return 0; +} diff --git a/src/tccelf.c b/src/tccelf.c new file mode 100644 index 0000000..d00fadd --- /dev/null +++ b/src/tccelf.c @@ -0,0 +1,3504 @@ +/* + * 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 + */ + +#include "tcc.h" + +/* Define this to get some debug output during relocation processing. */ +#undef DEBUG_RELOC + +/* XXX: avoid static variable */ +static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */ + +ST_FUNC 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) +{ + ElfW(Sym) *sym; + int *ptr, *hash, nb_syms, sym_index, h; + unsigned char *strtab; + + strtab = s->link->data; + nb_syms = s->data_offset / sizeof(ElfW(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 = (ElfW(Sym) *)s->data + 1; + for(sym_index = 1; sym_index < nb_syms; sym_index++) { + if (ELFW(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 */ +ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, + int info, int other, int shndx, const char *name) +{ + int name_offset, sym_index; + int nbuckets, h; + ElfW(Sym) *sym; + Section *hs; + + sym = section_ptr_add(s, sizeof(ElfW(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 - (ElfW(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 (ELFW(ST_BIND)(info) != STB_LOCAL) { + /* add another hashing entry */ + nbuckets = base[0]; + h = elf_hash((unsigned char *) 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. */ +ST_FUNC int find_elf_sym(Section *s, const char *name) +{ + ElfW(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((unsigned char *) name) % nbuckets; + sym_index = ((int *)hs->data)[2 + h]; + while (sym_index != 0) { + sym = &((ElfW(Sym) *)s->data)[sym_index]; + name1 = (char *) 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, signal error if 'err' is nonzero */ +ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err) +{ + int sym_index; + ElfW(Sym) *sym; + + sym_index = find_elf_sym(s->symtab, name); + sym = &((ElfW(Sym) *)s->symtab->data)[sym_index]; + if (!sym_index || sym->st_shndx == SHN_UNDEF) { + if (err) + tcc_error("%s not defined", name); + return 0; + } + return sym->st_value; +} + +/* return elf symbol value */ +LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name) +{ + return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0); +} + +#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE +/* return elf symbol value or error */ +ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name) +{ + return (void*)(uintptr_t)get_elf_sym_addr(s, name, 1); +} +#endif + +/* add an elf symbol : check if it is already defined and patch + it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */ +ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size, + int info, int other, int sh_num, const char *name) +{ + ElfW(Sym) *esym; + int sym_bind, sym_index, sym_type, esym_bind; + unsigned char sym_vis, esym_vis, new_vis; + + sym_bind = ELFW(ST_BIND)(info); + sym_type = ELFW(ST_TYPE)(info); + sym_vis = ELFW(ST_VISIBILITY)(other); + + 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 = &((ElfW(Sym) *)s->data)[sym_index]; + if (esym->st_shndx != SHN_UNDEF) { + esym_bind = ELFW(ST_BIND)(esym->st_info); + /* propagate the most constraining visibility */ + /* STV_DEFAULT(0)<STV_PROTECTED(3)<STV_HIDDEN(2)<STV_INTERNAL(1) */ + esym_vis = ELFW(ST_VISIBILITY)(esym->st_other); + if (esym_vis == STV_DEFAULT) { + new_vis = sym_vis; + } else if (sym_vis == STV_DEFAULT) { + new_vis = esym_vis; + } else { + new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis; + } + esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) + | new_vis; + other = esym->st_other; /* in case we have to patch esym */ + 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 (sym_bind == STB_WEAK && esym_bind == STB_WEAK) { + /* keep first-found weak definition, ignore subsequents */ + } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) { + /* ignore hidden symbols after */ + } else if (esym->st_shndx == SHN_COMMON + && (sh_num < SHN_LORESERVE || sh_num == SHN_COMMON)) { + /* gr: Happens with 'tcc ... -static tcctest.c' on e.g. Ubuntu 6.01 + No idea if this is the correct solution ... */ + goto do_patch; + } else if (s == tcc_state->dynsymtab_section) { + /* we accept that two DLL define the same symbol */ + } else { +#if 0 + printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n", + sym_bind, sh_num, new_vis, esym_bind, esym->st_shndx, esym_vis); +#endif + tcc_error_noabort("'%s' defined twice", name); + } + } else { + do_patch: + esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type); + esym->st_shndx = sh_num; + new_undef_sym = 1; + esym->st_value = value; + esym->st_size = size; + esym->st_other = other; + } + } else { + do_def: + sym_index = put_elf_sym(s, value, size, + ELFW(ST_INFO)(sym_bind, sym_type), other, + sh_num, name); + } + return sym_index; +} + +/* put relocation */ +ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, + int type, int symbol, addr_t addend) +{ + char buf[256]; + Section *sr; + ElfW_Rel *rel; + + sr = s->reloc; + if (!sr) { + /* if no relocation section, create it */ + snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name); + /* if the symtab is allocated, then we consider the relocation + are also */ + sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags); + sr->sh_entsize = sizeof(ElfW_Rel); + sr->link = symtab; + sr->sh_info = s->sh_num; + s->reloc = sr; + } + rel = section_ptr_add(sr, sizeof(ElfW_Rel)); + rel->r_offset = offset; + rel->r_info = ELFW(R_INFO)(symbol, type); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + rel->r_addend = addend; +#else + if (addend) + tcc_error("non-zero addend on REL architecture"); +#endif +} + +ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, + int type, int symbol) +{ + put_elf_reloca(symtab, s, offset, type, symbol, 0); +} + +/* put stab debug information */ + +ST_FUNC 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; +} + +ST_FUNC 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 int), + R_DATA_32, sym_index); +} + +ST_FUNC void put_stabn(int type, int other, int desc, int value) +{ + put_stabs(NULL, type, other, desc, value); +} + +ST_FUNC void put_stabd(int type, int other, int desc) +{ + put_stabs(NULL, type, other, desc, 0); +} + +/* Browse each elem of type <type> in section <sec> starting at elem <startoff> + using variable <elem> */ +#define for_each_elem(sec, startoff, elem, type) \ + for (elem = (type *) sec->data + startoff; \ + elem < (type *) (sec->data + sec->data_offset); elem++) + +/* 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; + ElfW(Sym) *new_syms; + int nb_syms, i; + ElfW(Sym) *p, *q; + ElfW_Rel *rel; + Section *sr; + int type, sym_index; + + nb_syms = s->data_offset / sizeof(ElfW(Sym)); + new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym))); + old_to_new_syms = tcc_malloc(nb_syms * sizeof(int)); + + /* first pass for local symbols */ + p = (ElfW(Sym) *)s->data; + q = new_syms; + for(i = 0; i < nb_syms; i++) { + if (ELFW(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 = (ElfW(Sym) *)s->data; + for(i = 0; i < nb_syms; i++) { + if (ELFW(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(ElfW(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_RELX && sr->link == s) { + for_each_elem(sr, 0, rel, ElfW_Rel) { + sym_index = ELFW(R_SYM)(rel->r_info); + type = ELFW(R_TYPE)(rel->r_info); + sym_index = old_to_new_syms[sym_index]; + rel->r_info = ELFW(R_INFO)(sym_index, type); + } + } + } + + tcc_free(old_to_new_syms); +} + +/* relocate common symbols in the .bss section */ +ST_FUNC void relocate_common_syms(void) +{ + ElfW(Sym) *sym; + unsigned long offset, align; + + for_each_elem(symtab_section, 1, sym, ElfW(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. */ +ST_FUNC void relocate_syms(TCCState *s1, int do_resolve) +{ + ElfW(Sym) *sym, *esym; + int sym_bind, sh_num, sym_index; + const char *name; + + for_each_elem(symtab_section, 1, sym, ElfW(Sym)) { + sh_num = sym->st_shndx; + if (sh_num == SHN_UNDEF) { + name = (char *) strtab_section->data + sym->st_name; + /* Use ld.so to resolve symbol for us (for tcc -run) */ + if (do_resolve) { +#if defined TCC_IS_NATIVE && !defined _WIN32 + void *addr; + name = (char *) symtab_section->link->data + sym->st_name; + addr = resolve_sym(s1, name); + if (addr) { + sym->st_value = (addr_t)addr; +#ifdef DEBUG_RELOC + printf ("relocate_sym: %s -> 0x%lx\n", name, sym->st_value); +#endif + goto found; + } +#endif + } else if (s1->dynsym) { + /* if dynamic symbol exist, then use it */ + sym_index = find_elf_sym(s1->dynsym, name); + if (sym_index) { + esym = &((ElfW(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 = ELFW(ST_BIND)(sym->st_info); + if (sym_bind == STB_WEAK) { + sym->st_value = 0; + } else { + tcc_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) by applying the relocations + in the associated relocation section */ +ST_FUNC void relocate_section(TCCState *s1, Section *s) +{ + Section *sr = s->reloc; + ElfW_Rel *rel; + ElfW(Sym) *sym; + int type, sym_index; + unsigned char *ptr; + addr_t val, addr; +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 + ElfW_Rel *qrel = (ElfW_Rel *) sr->data; /* ptr to next reloc entry reused */ + int esym_index; +#endif + + for_each_elem(sr, 0, rel, ElfW_Rel) { + ptr = s->data + rel->r_offset; + + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + val = sym->st_value; +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + val += rel->r_addend; +#endif + type = ELFW(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 = ELFW(R_INFO)(esym_index, R_386_32); + qrel++; + break; + } else { + qrel->r_info = ELFW(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 = ELFW(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->sym_attrs[sym_index].got_offset; + break; + case R_386_16: + if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) { + output_file: + tcc_error("can only produce 16-bit binary files"); + } + *(short *)ptr += val; + break; + case R_386_PC16: + if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) + goto output_file; + *(short *)ptr += val - addr; + break; +#elif defined(TCC_TARGET_ARM) + case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PLT32: + { + int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko; + x = (*(int *) ptr) & 0xffffff; + if (sym->st_shndx == SHN_UNDEF) + val = s1->plt->sh_addr; +#ifdef DEBUG_RELOC + printf ("reloc %d: x=0x%x val=0x%x ", type, x, val); +#endif + (*(int *)ptr) &= 0xff000000; + if (x & 0x800000) + x -= 0x1000000; + x <<= 2; + blx_avail = (TCC_ARM_VERSION >= 5); + is_thumb = val & 1; + is_bl = (*(unsigned *) ptr) >> 24 == 0xeb; + is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl)); + x += val - addr; +#ifdef DEBUG_RELOC + printf (" newx=0x%x name=%s\n", x, + (char *) symtab_section->link->data + sym->st_name); +#endif + h = x & 2; + th_ko = (x & 3) && (!blx_avail || !is_call); + if (th_ko || x >= 0x2000000 || x < -0x2000000) + tcc_error("can't relocate value at %x,%d",addr, type); + x >>= 2; + x &= 0xffffff; + /* Only reached if blx is avail and it is a call */ + if (is_thumb) { + x |= h << 24; + (*(int *)ptr) = 0xfa << 24; /* bl -> blx */ + } + (*(int *) ptr) |= x; + } + break; + /* Since these relocations only concern Thumb-2 and blx instruction was + introduced before Thumb-2, we can assume blx is available and not + guard its use */ + case R_ARM_THM_PC22: + case R_ARM_THM_JUMP24: + { + int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11; + int to_thumb, is_call, to_plt, blx_bit = 1 << 12; + Section *plt; + + /* weak reference */ + if (sym->st_shndx == SHN_UNDEF && + ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + break; + + /* Get initial offset */ + hi = (*(uint16_t *)ptr); + lo = (*(uint16_t *)(ptr+2)); + s = (hi >> 10) & 1; + j1 = (lo >> 13) & 1; + j2 = (lo >> 11) & 1; + i1 = (j1 ^ s) ^ 1; + i2 = (j2 ^ s) ^ 1; + imm10 = hi & 0x3ff; + imm11 = lo & 0x7ff; + x = (s << 24) | (i1 << 23) | (i2 << 22) | + (imm10 << 12) | (imm11 << 1); + if (x & 0x01000000) + x -= 0x02000000; + + /* Relocation infos */ + to_thumb = val & 1; + plt = s1->plt; + to_plt = (val >= plt->sh_addr) && + (val < plt->sh_addr + plt->data_offset); + is_call = (type == R_ARM_THM_PC22); + + /* Compute final offset */ + if (to_plt && !is_call) /* Point to 1st instr of Thumb stub */ + x -= 4; + x += val - addr; + if (!to_thumb && is_call) { + blx_bit = 0; /* bl -> blx */ + x = (x + 3) & -4; /* Compute offset from aligned PC */ + } + + /* Check that relocation is possible + * offset must not be out of range + * if target is to be entered in arm mode: + - bit 1 must not set + - instruction must be a call (bl) or a jump to PLT */ + if (!to_thumb || x >= 0x1000000 || x < -0x1000000) + if (to_thumb || (val & 2) || (!is_call && !to_plt)) + tcc_error("can't relocate value at %x,%d",addr, type); + + /* Compute and store final offset */ + s = (x >> 24) & 1; + i1 = (x >> 23) & 1; + i2 = (x >> 22) & 1; + j1 = s ^ (i1 ^ 1); + j2 = s ^ (i2 ^ 1); + imm10 = (x >> 12) & 0x3ff; + imm11 = (x >> 1) & 0x7ff; + (*(uint16_t *)ptr) = (uint16_t) ((hi & 0xf800) | + (s << 10) | imm10); + (*(uint16_t *)(ptr+2)) = (uint16_t) ((lo & 0xc000) | + (j1 << 13) | blx_bit | (j2 << 11) | + imm11); + } + break; + case R_ARM_MOVT_ABS: + case R_ARM_MOVW_ABS_NC: + { + int x, imm4, imm12; + if (type == R_ARM_MOVT_ABS) + val >>= 16; + imm12 = val & 0xfff; + imm4 = (val >> 12) & 0xf; + x = (imm4 << 16) | imm12; + if (type == R_ARM_THM_MOVT_ABS) + *(int *)ptr |= x; + else + *(int *)ptr += x; + } + break; + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_ABS_NC: + { + int x, i, imm4, imm3, imm8; + if (type == R_ARM_THM_MOVT_ABS) + val >>= 16; + imm8 = val & 0xff; + imm3 = (val >> 8) & 0x7; + i = (val >> 11) & 1; + imm4 = (val >> 12) & 0xf; + x = (imm3 << 28) | (imm8 << 16) | (i << 10) | imm4; + if (type == R_ARM_THM_MOVT_ABS) + *(int *)ptr |= x; + else + *(int *)ptr += x; + } + break; + case R_ARM_PREL31: + { + int x; + x = (*(int *)ptr) & 0x7fffffff; + (*(int *)ptr) &= 0x80000000; + x = (x * 2) / 2; + x += val - addr; + if((x^(x>>1))&0x40000000) + tcc_error("can't relocate value at %x,%d",addr, type); + (*(int *)ptr) |= x & 0x7fffffff; + } + case R_ARM_ABS32: + *(int *)ptr += val; + break; + case R_ARM_REL32: + *(int *)ptr += val - addr; + break; + case R_ARM_GOTPC: + *(int *)ptr += s1->got->sh_addr - addr; + break; + case R_ARM_GOTOFF: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_ARM_GOT32: + /* we load the got offset */ + *(int *)ptr += s1->sym_attrs[sym_index].got_offset; + break; + case R_ARM_COPY: + break; + case R_ARM_V4BX: + /* trade Thumb support for ARMv4 support */ + if ((0x0ffffff0 & *(int*)ptr) == 0x012FFF10) + *(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */ + break; + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + *(addr_t *)ptr = val; + break; + case R_ARM_NONE: + /* Nothing to do. Normally used to indicate a dependency + on a certain symbol (like for exception handling under EABI). */ + break; + default: + fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + break; +#elif defined(TCC_TARGET_ARM64) + case R_AARCH64_ABS64: + *(uint64_t *)ptr = val; + break; + case R_AARCH64_ABS32: + *(uint32_t *)ptr = val; + break; + case R_AARCH64_MOVW_UABS_G0_NC: + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) | + (val & 0xffff) << 5; + break; + case R_AARCH64_MOVW_UABS_G1_NC: + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) | + (val >> 16 & 0xffff) << 5; + break; + case R_AARCH64_MOVW_UABS_G2_NC: + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) | + (val >> 32 & 0xffff) << 5; + break; + case R_AARCH64_MOVW_UABS_G3: + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) | + (val >> 48 & 0xffff) << 5; + break; + case R_AARCH64_ADR_PREL_PG_HI21: { + uint64_t off = (val >> 12) - (addr >> 12); + if ((off + ((uint64_t)1 << 20)) >> 21) + tcc_error("R_AARCH64_ADR_PREL_PG_HI21 relocation failed"); + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0x9f00001f) | + (off & 0x1ffffc) << 3 | (off & 3) << 29; + break; + } + case R_AARCH64_ADD_ABS_LO12_NC: + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffc003ff) | + (val & 0xfff) << 10; + break; + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + /* This check must match the one in build_got_entries, testing + if we really need a PLT slot. */ + if (sym->st_shndx == SHN_UNDEF) + /* We've put the PLT slot offset into r_addend when generating + it, and that's what we must use as relocation value (adjusted + by section offset of course). */ + val = s1->plt->sh_addr + rel->r_addend; +#ifdef DEBUG_RELOC + printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val, + (char *) symtab_section->link->data + sym->st_name); +#endif + if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc) + { + tcc_error("R_AARCH64_(JUMP|CALL)26 relocation failed (val=%lx, addr=%lx)", addr, val); + } + *(uint32_t *)ptr = 0x14000000 | (type == R_AARCH64_CALL26) << 31 | + ((val - addr) >> 2 & 0x3ffffff); + break; + case R_AARCH64_ADR_GOT_PAGE: { + uint64_t off = + (((s1->got->sh_addr + + s1->sym_attrs[sym_index].got_offset) >> 12) - (addr >> 12)); + if ((off + ((uint64_t)1 << 20)) >> 21) + tcc_error("R_AARCH64_ADR_GOT_PAGE relocation failed"); + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0x9f00001f) | + (off & 0x1ffffc) << 3 | (off & 3) << 29; + break; + } + case R_AARCH64_LD64_GOT_LO12_NC: + *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xfff803ff) | + ((s1->got->sh_addr + s1->sym_attrs[sym_index].got_offset) + & 0xff8) << 7; + break; + case R_AARCH64_COPY: + break; + case R_AARCH64_GLOB_DAT: + case R_AARCH64_JUMP_SLOT: + /* They don't need addend */ +#ifdef DEBUG_RELOC + printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, + val - rel->r_addend, + (char *) symtab_section->link->data + sym->st_name); +#endif + *(addr_t *)ptr = val - rel->r_addend; + break; + default: + fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)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 %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + break; +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_64: + 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 = ELFW(R_INFO)(esym_index, R_X86_64_64); + qrel->r_addend = rel->r_addend; + qrel++; + break; + } else { + qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE); + qrel->r_addend = *(long long *)ptr + val; + qrel++; + } + } + *(long long *)ptr += val; + break; + case R_X86_64_32: + case R_X86_64_32S: + if (s1->output_type == TCC_OUTPUT_DLL) { + /* XXX: this logic may depend on TCC's codegen + now TCC uses R_X86_64_32 even for a 64bit pointer */ + qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE); + qrel->r_addend = *(int *)ptr + val; + qrel++; + } + *(int *)ptr += val; + break; + + case R_X86_64_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 = ELFW(R_INFO)(esym_index, R_X86_64_PC32); + qrel->r_addend = *(int *)ptr; + qrel++; + break; + } + } + goto plt32pc32; + + case R_X86_64_PLT32: + /* We've put the PLT slot offset into r_addend when generating + it, and that's what we must use as relocation value (adjusted + by section offset of course). */ + val = s1->plt->sh_addr + rel->r_addend; + /* fallthrough. */ + + plt32pc32: + { + long long diff; + diff = (long long)val - addr; + if (diff < -2147483648LL || diff > 2147483647LL) { + tcc_error("internal error: relocation failed"); + } + *(int *)ptr += diff; + } + break; + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + /* They don't need addend */ + *(addr_t *)ptr = val - rel->r_addend; + break; + case R_X86_64_GOTPCREL: + *(int *)ptr += (s1->got->sh_addr - addr + + s1->sym_attrs[sym_index].got_offset - 4); + break; + case R_X86_64_GOTTPOFF: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_X86_64_GOT32: + /* we load the got offset */ + *(int *)ptr += s1->sym_attrs[sym_index].got_offset; + 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; + ElfW_Rel *rel; + + s = s1->sections[sr->sh_info]; + for_each_elem(sr, 0, rel, ElfW_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) +{ + ElfW_Rel *rel; + int sym_index, esym_index, type, count; + + count = 0; + for_each_elem(sr, 0, rel, ElfW_Rel) { + sym_index = ELFW(R_SYM)(rel->r_info); + type = ELFW(R_TYPE)(rel->r_info); + switch(type) { +#if defined(TCC_TARGET_I386) + case R_386_32: +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_64: +#endif + count++; + break; +#if defined(TCC_TARGET_I386) + case R_386_PC32: +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_PC32: +#endif + 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(ElfW_Rel); + } + return count; +} + +static struct sym_attr *alloc_sym_attr(TCCState *s1, int index) +{ + int n; + struct sym_attr *tab; + + if (index >= s1->nb_sym_attrs) { + /* find immediately bigger power of 2 and reallocate array */ + n = 1; + while (index >= n) + n *= 2; + tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs)); + s1->sym_attrs = tab; + memset(s1->sym_attrs + s1->nb_sym_attrs, 0, + (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs)); + s1->nb_sym_attrs = n; + } + return &s1->sym_attrs[index]; +} + +/* 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) || \ + defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) +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, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), + 0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_"); + ptr = section_ptr_add(s1->got, 3 * PTR_SIZE); +#if PTR_SIZE == 4 + /* keep space for _DYNAMIC pointer, if present */ + put32(ptr, 0); + /* two dummy got entries */ + put32(ptr + 4, 0); + put32(ptr + 8, 0); +#else + /* keep space for _DYNAMIC pointer, if present */ + put32(ptr, 0); + put32(ptr + 4, 0); + /* two dummy got entries */ + put32(ptr + 8, 0); + put32(ptr + 12, 0); + put32(ptr + 16, 0); + put32(ptr + 20, 0); +#endif +} + +/* put a got or plt entry corresponding to a symbol in symtab_section. 'size' + and 'info' can be modifed if more precise info comes from the DLL. + Returns offset of GOT or PLT slot. */ +static unsigned long put_got_entry(TCCState *s1, + int reloc_type, unsigned long size, int info, + int sym_index) +{ + int index, need_plt_entry; + const char *name; + ElfW(Sym) *sym; + unsigned long offset; + int *ptr; + struct sym_attr *symattr; + + if (!s1->got) + build_got(s1); + + need_plt_entry = +#ifdef TCC_TARGET_X86_64 + (reloc_type == R_X86_64_JUMP_SLOT); +#elif defined(TCC_TARGET_I386) + (reloc_type == R_386_JMP_SLOT); +#elif defined(TCC_TARGET_ARM) + (reloc_type == R_ARM_JUMP_SLOT); +#elif defined(TCC_TARGET_ARM64) + (reloc_type == R_AARCH64_JUMP_SLOT); +#else + 0; +#endif + + if (need_plt_entry && !s1->plt) { + /* add PLT */ + s1->plt = new_section(s1, ".plt", SHT_PROGBITS, + SHF_ALLOC | SHF_EXECINSTR); + s1->plt->sh_entsize = 4; + } + + /* If a got/plt entry already exists for that symbol, no need to add one */ + if (sym_index < s1->nb_sym_attrs) { + if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset) + return s1->sym_attrs[sym_index].plt_offset; + else if (!need_plt_entry && s1->sym_attrs[sym_index].got_offset) + return s1->sym_attrs[sym_index].got_offset; + } + + symattr = alloc_sym_attr(s1, sym_index); + + /* Only store the GOT offset if it's not generated for the PLT entry. */ + if (!need_plt_entry) + symattr->got_offset = s1->got->data_offset; + + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + name = (char *) symtab_section->link->data + sym->st_name; + offset = sym->st_value; +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + if (need_plt_entry) { + Section *plt; + uint8_t *p; + int modrm; + unsigned long relofs; + +#if defined(TCC_OUTPUT_DLL_WITH_PLT) + modrm = 0x25; +#else + /* if we build a DLL, we add a %ebx offset */ + if (s1->output_type == TCC_OUTPUT_DLL) + modrm = 0xa3; + else + modrm = 0x25; +#endif + + /* 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 + PTR_SIZE */ + p[1] = modrm + 0x10; + put32(p + 2, PTR_SIZE); + p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ + p[7] = modrm; + put32(p + 8, PTR_SIZE * 2); + } + + /* The PLT slot refers to the relocation entry it needs + via offset. The reloc entry is created below, so its + offset is the current data_offset. */ + relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0; + symattr->plt_offset = plt->data_offset; + 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 */ +#ifdef TCC_TARGET_X86_64 + /* On x86-64, the relocation is referred to by _index_. */ + put32(p + 7, relofs / sizeof (ElfW_Rel)); +#else + put32(p + 7, relofs); +#endif + p[11] = 0xe9; /* jmp plt_start */ + put32(p + 12, -(plt->data_offset)); + + /* If this was an UNDEF symbol set the offset in the + dynsymtab to the PLT slot, so that PC32 relocs to it + can be resolved. */ + if (sym->st_shndx == SHN_UNDEF) + offset = plt->data_offset - 16; + } +#elif defined(TCC_TARGET_ARM) + if (need_plt_entry) { + Section *plt; + uint8_t *p; + + /* if we build a DLL, we add a %ebx offset */ + if (s1->output_type == TCC_OUTPUT_DLL) + tcc_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); /* push {lr} */ + put32(p+4, 0xe59fe010); /* ldr lr, [pc, #16] */ + put32(p+8, 0xe08fe00e); /* add lr, pc, lr */ + put32(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */ + } + + symattr->plt_offset = plt->data_offset; + if (symattr->plt_thumb_stub) { + p = section_ptr_add(plt, 20); + put32(p, 0x4778); /* bx pc */ + put32(p+2, 0x46c0); /* nop */ + p += 4; + } else + p = section_ptr_add(plt, 16); + put32(p, 0xe59fc004); /* ldr ip, [pc, #4] ; GOT entry offset */ + put32(p+4, 0xe08fc00c); /* add ip, pc, ip ; addr of GOT entry */ + put32(p+8, 0xe59cf000); /* ldr pc, [ip] ; jump to GOT entry */ + put32(p+12, s1->got->data_offset); /* GOT entry off once patched */ + + /* the symbol is modified so that it will be relocated to + the PLT */ + if (sym->st_shndx == SHN_UNDEF) + offset = plt->data_offset - 16; + } +#elif defined(TCC_TARGET_ARM64) + if (need_plt_entry) { + Section *plt; + uint8_t *p; + + if (s1->output_type == TCC_OUTPUT_DLL) + tcc_error("DLLs unimplemented!"); + + plt = s1->plt; + if (plt->data_offset == 0) + section_ptr_add(plt, 32); + symattr->plt_offset = plt->data_offset; + p = section_ptr_add(plt, 16); + put32(p, s1->got->data_offset); + put32(p + 4, (uint64_t)s1->got->data_offset >> 32); + + if (sym->st_shndx == SHN_UNDEF) + offset = plt->data_offset - 16; + } +#elif defined(TCC_TARGET_C67) + if (s1->dynsym) { + tcc_error("C67 got not implemented"); + } +#else +#error unsupported CPU +#endif + if (s1->dynsym) { + /* XXX This might generate multiple syms for name. */ + index = put_elf_sym(s1->dynsym, offset, + size, info, 0, sym->st_shndx, name); + /* Create the relocation (it's against the GOT for PLT + and GOT relocs). */ + put_elf_reloc(s1->dynsym, s1->got, + s1->got->data_offset, + reloc_type, index); + } else { + /* Without .dynsym (i.e. static link or memory output) we + still need relocs against the generated got, so as to fill + the entries with the symbol values (determined later). */ + put_elf_reloc(symtab_section, s1->got, + s1->got->data_offset, + reloc_type, sym_index); + } + /* And now create the GOT slot itself. */ + ptr = section_ptr_add(s1->got, PTR_SIZE); + *ptr = 0; + if (need_plt_entry) + return symattr->plt_offset; + else + return symattr->got_offset; +} + +/* build GOT and PLT entries */ +ST_FUNC void build_got_entries(TCCState *s1) +{ + Section *s; + ElfW_Rel *rel; + ElfW(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_RELX) + continue; + /* no need to handle got relocations */ + if (s->link != symtab_section) + continue; + for_each_elem(s, 0, rel, ElfW_Rel) { + type = ELFW(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 = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(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_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_GOT32: + case R_ARM_GOTOFF: + case R_ARM_GOTPC: + case R_ARM_PLT32: + if (!s1->got) + build_got(s1); + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if (type != R_ARM_GOTOFF && type != R_ARM_GOTPC + && sym->st_shndx == SHN_UNDEF) { + unsigned long ofs; + /* 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; + ofs = put_got_entry(s1, reloc_type, sym->st_size, + sym->st_info, sym_index); +#ifdef DEBUG_RELOC + printf ("maybegot: %s, %d, %d --> ofs=0x%x\n", + (char *) symtab_section->link->data + sym->st_name, + type, sym->st_shndx, ofs); +#endif + if (type != R_ARM_GOT32) { + addr_t *ptr = (addr_t*)(s1->sections[s->sh_info]->data + + rel->r_offset); + /* x must be signed! */ + int x = *ptr & 0xffffff; + x = (x << 8) >> 8; + x <<= 2; + x += ofs; + x >>= 2; +#ifdef DEBUG_RELOC + printf ("insn=0x%x --> 0x%x (x==0x%x)\n", *ptr, + (*ptr & 0xff000000) | x, x); +#endif + *ptr = (*ptr & 0xff000000) | x; + } + } + break; + case R_ARM_THM_JUMP24: + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + /* We are relocating a jump from thumb code to arm code */ + if (sym->st_shndx != SHN_UNDEF && !(sym->st_value & 1)) { + int index; + uint8_t *p; + char *name, buf[1024]; + Section *text_section; + + name = (char *) symtab_section->link->data + sym->st_name; + text_section = s1->sections[sym->st_shndx]; + /* Modify reloc to target a thumb stub to switch to ARM */ + snprintf(buf, sizeof(buf), "%s_from_thumb", name); + index = put_elf_sym(symtab_section, + text_section->data_offset + 1, + sym->st_size, sym->st_info, 0, + sym->st_shndx, buf); + rel->r_info = ELFW(R_INFO)(index, type); + /* Create a thumb stub fonction to switch to ARM mode */ + put_elf_reloc(symtab_section, text_section, + text_section->data_offset + 4, R_ARM_JUMP24, + sym_index); + p = section_ptr_add(text_section, 8); + put32(p, 0x4778); /* bx pc */ + put32(p+2, 0x46c0); /* nop */ + put32(p+4, 0xeafffffe); /* b $sym */ + } +#elif defined(TCC_TARGET_ARM64) + //xx Other cases may be required here: + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + if (!s1->got) + build_got(s1); + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + reloc_type = R_AARCH64_GLOB_DAT; + put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, + sym_index); + break; + + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + if (!s1->got) + build_got(s1); + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if (sym->st_shndx == SHN_UNDEF) { + unsigned long ofs; + reloc_type = R_AARCH64_JUMP_SLOT; + ofs = put_got_entry(s1, reloc_type, sym->st_size, + sym->st_info, sym_index); + /* We store the place of the generated PLT slot + in our addend. */ + rel->r_addend += ofs; + } + 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 = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(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; +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_GOT32: + case R_X86_64_GOTTPOFF: + case R_X86_64_GOTPCREL: + case R_X86_64_PLT32: + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if (type == R_X86_64_PLT32 && + ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT) + { + rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32); + break; + } + + if (!s1->got) + build_got(s1); + if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL || + type == R_X86_64_PLT32) { + unsigned long ofs; + /* look at the symbol got offset. If none, then add one */ + if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL) + reloc_type = R_X86_64_GLOB_DAT; + else + reloc_type = R_X86_64_JUMP_SLOT; + ofs = put_got_entry(s1, reloc_type, sym->st_size, + sym->st_info, sym_index); + if (type == R_X86_64_PLT32) + /* We store the place of the generated PLT slot + in our addend. */ + rel->r_addend += ofs; + } + break; +#else +#error unsupported CPU +#endif + default: + break; + } + } + } +} + +ST_FUNC 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(ElfW(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, addr_t val) +{ + ElfW(Dyn) *dyn; + dyn = section_ptr_add(dynamic, sizeof(ElfW(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, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, sym_start); + add_elf_sym(symtab_section, + end_offset, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, sym_end); +} + +static int tcc_add_support(TCCState *s1, const char *filename) +{ + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s/%s", s1->tcc_lib_path, + /* an cpu specific path inside tcc_lib_path, mainly for keeping libtcc1.a */ + #ifdef TCC_TARGET_I386 + "i386" + #endif + #ifdef TCC_TARGET_X86_64 + "x86_64" + #endif + #ifdef TCC_TARGET_ARM + "arm" + #endif + #ifdef TCC_TARGET_ARM64 + "arm64" + #endif + #ifdef TCC_TARGET_C67 + "C67" + #endif + ,filename); + + return tcc_add_file(s1, buf, TCC_FILETYPE_BINARY); +} + +ST_FUNC void tcc_add_bcheck(TCCState *s1) +{ +#ifdef CONFIG_TCC_BCHECK + addr_t *ptr; + + if (0 == s1->do_bounds_check) + return; + + /* XXX: add an object file to do that */ + ptr = section_ptr_add(bounds_section, sizeof(*ptr)); + *ptr = 0; + add_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + bounds_section->sh_num, "__bounds_start"); + if (s1->output_type != TCC_OUTPUT_MEMORY) { + /* add 'call __bound_init()' in .init section */ + + /* XXX not called on MSYS, reason is unknown. For this + case a call to __bound_init is performed in bcheck.c + when __bound_ptr_add, __bound_new_region, + __bound_delete_region called */ + + int sym_index = find_elf_sym(symtab_section, "__bound_init"); + if (sym_index) { + Section *init_section = find_section(s1, ".init"); + unsigned char *pinit = section_ptr_add(init_section, 5); + pinit[0] = 0xe8; + put32(pinit + 1, -4); + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 4, R_386_PC32, sym_index); + } + else + tcc_warning("__bound_init not defined"); + } +#endif +} + +/* add tcc runtime libraries */ +ST_FUNC void tcc_add_runtime(TCCState *s1) +{ + tcc_add_pragma_libs(s1); + + /* add libc */ + if (!s1->nostdlib) { + tcc_add_library(s1, "c"); +#ifdef CONFIG_USE_LIBGCC + if (!s1->static_link) { + tcc_add_file(s1, TCC_LIBGCC, TCC_FILETYPE_BINARY); + } +#endif + tcc_add_support(s1, "libtcc1.a"); + } + + /* tcc_add_bcheck tries to relocate a call to __bound_init in _init so + libtcc1.a must be loaded before for __bound_init to be defined and + crtn.o must be loaded after to not finalize _init too early. */ + tcc_add_bcheck(s1); + + if (!s1->nostdlib) { + /* add crt end if not memory output */ + if (s1->output_type != TCC_OUTPUT_MEMORY) + tcc_add_crt(s1, "crtn.o"); + } +} + +/* add various standard linker symbols (must be done after the + sections are filled (for example after allocating common + symbols)) */ +ST_FUNC void tcc_add_linker_symbols(TCCState *s1) +{ + char buf[1024]; + int i; + Section *s; + + add_elf_sym(symtab_section, + text_section->data_offset, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + text_section->sh_num, "_etext"); + add_elf_sym(symtab_section, + data_section->data_offset, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + data_section->sh_num, "_edata"); + add_elf_sym(symtab_section, + bss_section->data_offset, 0, + ELFW(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, + ELFW(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, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, buf); + } + next_sec: ; + } +} + +static void tcc_output_binary(TCCState *s1, FILE *f, + const int *sec_order) +{ + Section *s; + int i, offset, size; + + offset = 0; + for(i=1;i<s1->nb_sections;i++) { + s = s1->sections[sec_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; + fwrite(s->data, 1, size, f); + offset += size; + } + } +} + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#define HAVE_PHDR 1 +#define EXTRA_RELITEMS 14 + +/* move the relocation value from .dynsym to .got */ +void patch_dynsym_undef(TCCState *s1, Section *s) +{ + uint32_t *gotd = (void *)s1->got->data; + ElfW(Sym) *sym; + + gotd += 3; /* dummy entries in .got */ + /* relocate symbols in .dynsym */ + for_each_elem(s, 1, sym, ElfW(Sym)) { + if (sym->st_shndx == SHN_UNDEF) { + *gotd++ = sym->st_value + 6; /* XXX 6 is magic ? */ + sym->st_value = 0; + } + } +} +#else +#define HAVE_PHDR 1 +#define EXTRA_RELITEMS 9 + +/* zero plt offsets of weak symbols in .dynsym */ +void patch_dynsym_undef(TCCState *s1, Section *s) +{ + ElfW(Sym) *sym; + + for_each_elem(s, 1, sym, ElfW(Sym)) + if (sym->st_shndx == SHN_UNDEF && ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + sym->st_value = 0; +} +#endif + +ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel) +{ + int sym_index = ELFW(R_SYM) (rel->r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; + unsigned long offset; + + if (sym_index >= s1->nb_sym_attrs) + return; + offset = s1->sym_attrs[sym_index].got_offset; + section_reserve(s1->got, offset + PTR_SIZE); +#ifdef TCC_TARGET_X86_64 + /* only works for x86-64 */ + put32(s1->got->data + offset + 4, sym->st_value >> 32); +#endif + put32(s1->got->data + offset, sym->st_value & 0xffffffff); +} + +/* Perform relocation to GOT or PLT entries */ +ST_FUNC void fill_got(TCCState *s1) +{ + Section *s; + ElfW_Rel *rel; + int i; + + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->sh_type != SHT_RELX) + continue; + /* no need to handle got relocations */ + if (s->link != symtab_section) + continue; + for_each_elem(s, 0, rel, ElfW_Rel) { + switch (ELFW(R_TYPE) (rel->r_info)) { + case R_X86_64_GOT32: + case R_X86_64_GOTPCREL: + case R_X86_64_PLT32: + fill_got_entry(s1, rel); + break; + } + } + } +} + +/* Bind symbols of executable: resolve undefined symbols from exported symbols + in shared libraries and export non local defined symbols to shared libraries + if -rdynamic switch was given on command line */ +static void bind_exe_dynsyms(TCCState *s1) +{ + const char *name; + int sym_index, index; + ElfW(Sym) *sym, *esym; + int type; + + /* Resolve undefined symbols from dynamic symbols. When there is a match: + - if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT + - if STT_OBJECT symbol -> add it in .bss section with suitable reloc */ + for_each_elem(symtab_section, 1, sym, ElfW(Sym)) { + if (sym->st_shndx == SHN_UNDEF) { + name = (char *) symtab_section->link->data + sym->st_name; + sym_index = find_elf_sym(s1->dynsymtab_section, name); + if (sym_index) { + esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index]; + type = ELFW(ST_TYPE)(esym->st_info); + if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) { + /* Indirect functions shall have STT_FUNC type in executable + * dynsym section. Indeed, a dlsym call following a lazy + * resolution would pick the symbol value from the + * executable dynsym entry which would contain the address + * of the function wanted by the caller of dlsym instead of + * the address of the function that would return that + * address */ + put_got_entry(s1, R_JMP_SLOT, esym->st_size, + ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), + sym - (ElfW(Sym) *)symtab_section->data); + } else if (type == STT_OBJECT) { + unsigned long offset; + ElfW(Sym) *dynsym; + 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); + /* Ensure R_COPY works for weak symbol aliases */ + if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) { + for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) { + if ((dynsym->st_value == esym->st_value) + && (ELFW(ST_BIND)(dynsym->st_info) == STB_GLOBAL)) { + char *dynname = (char *) s1->dynsymtab_section->link->data + + dynsym->st_name; + put_elf_sym(s1->dynsym, offset, dynsym->st_size, + dynsym->st_info, 0, + bss_section->sh_num, dynname); + break; + } + } + } + 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 (ELFW(ST_BIND)(sym->st_info) == STB_WEAK || + !strcmp(name, "_fp_hw")) { + } else { + tcc_error_noabort("undefined symbol '%s'", name); + } + } + } else if (s1->rdynamic && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { + /* if -rdynamic option, then export all non local symbols */ + name = (char *) 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); + } + } +} + +/* Bind symbols of libraries: export non local symbols of executable that + resolve undefined symbols of shared libraries */ +static void bind_libs_dynsyms(TCCState *s1) +{ + const char *name; + int sym_index; + ElfW(Sym) *sym, *esym; + + /* now look at unresolved dynamic symbols and export + corresponding symbol */ + for_each_elem(s1->dynsymtab_section, 1, esym, ElfW(Sym)) { + name = (char *) 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 = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if (sym->st_shndx != SHN_UNDEF) + put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, + sym->st_info, 0, sym->st_shndx, name); + } else if (esym->st_shndx == SHN_UNDEF) { + /* weak symbols can stay undefined */ + if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK) + tcc_warning("undefined dynamic symbol '%s'", name); + } + } +} + +/* Export all non local symbols (for shared libraries) */ +static void export_global_syms(TCCState *s1) +{ + int nb_syms, dynindex, index; + const char *name; + ElfW(Sym) *sym; + + nb_syms = symtab_section->data_offset / sizeof(ElfW(Sym)); + s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms); + for_each_elem(symtab_section, 1, sym, ElfW(Sym)) { + if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { + name = (char *) symtab_section->link->data + sym->st_name; + dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, + sym->st_info, 0, sym->st_shndx, name); + index = sym - (ElfW(Sym) *) symtab_section->data; + s1->symtab_to_dynsym[index] = dynindex; + } + } +} + +/* relocate the PLT: compute addresses and offsets in the PLT now that final + address for PLT and GOT are known (see fill_program_header) */ +ST_FUNC void relocate_plt(TCCState *s1) +{ + uint8_t *p, *p_end; + + if (!s1->plt) + return; + + 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_X86_64) + int x = s1->got->sh_addr - s1->plt->sh_addr - 6; + put32(p + 2, get32(p + 2) + x); + put32(p + 8, get32(p + 8) + x - 6); + p += 16; + while (p < p_end) { + put32(p + 2, get32(p + 2) + x + s1->plt->data - p); + 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) { + if (get32(p) == 0x46c04778) /* PLT Thumb stub present */ + p += 4; + put32(p + 12, x + get32(p + 12) + s1->plt->data - p); + p += 16; + } +#elif defined(TCC_TARGET_ARM64) + uint64_t plt = s1->plt->sh_addr; + uint64_t got = s1->got->sh_addr; + uint64_t off = (got >> 12) - (plt >> 12); + if ((off + ((uint32_t)1 << 20)) >> 21) + tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", off, got, plt); + put32(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]! + put32(p + 4, (0x90000010 | // adrp x16,... + (off & 0x1ffffc) << 3 | (off & 3) << 29)); + put32(p + 8, (0xf9400211 | // ldr x17,[x16,#...] + (got & 0xff8) << 7)); + put32(p + 12, (0x91000210 | // add x16,x16,#... + (got & 0xfff) << 10)); + put32(p + 16, 0xd61f0220); // br x17 + put32(p + 20, 0xd503201f); // nop + put32(p + 24, 0xd503201f); // nop + put32(p + 28, 0xd503201f); // nop + p += 32; + while (p < p_end) { + uint64_t pc = plt + (p - s1->plt->data); + uint64_t addr = got + + (get32(p) | (uint64_t)get32(p + 4) << 32); + uint32_t off = (addr >> 12) - (pc >> 12); + if ((off + ((uint32_t)1 << 20)) >> 21) + tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc); + put32(p, (0x90000010 | // adrp x16,... + (off & 0x1ffffc) << 3 | (off & 3) << 29)); + put32(p + 4, (0xf9400211 | // ldr x17,[x16,#...] + (addr & 0xff8) << 7)); + put32(p + 8, (0x91000210 | // add x16,x16,#... + (addr & 0xfff) << 10)); + put32(p + 12, 0xd61f0220); // br x17 + p += 16; + } +#elif defined(TCC_TARGET_C67) + /* XXX: TODO */ +#else +#error unsupported CPU +#endif + } +} + +/* 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 ! */ +static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec) +{ + int i; + Section *s; + + /* Allocate strings for section names */ + 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_RELX && + !(s->sh_flags & SHF_ALLOC)) { + /* gr: avoid bogus relocs for empty (debug) sections */ + if (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC) + prepare_dynamic_rel(s1, s); + else if (s1->do_debug) + s->sh_size = s->data_offset; + } else if (s1->do_debug || + file_type == TCC_OUTPUT_OBJ || + file_type == TCC_OUTPUT_EXE || + (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; + } + } +} + +/* Info to be copied in dynamic section */ +struct dyn_inf { + Section *dynamic; + Section *dynstr; + unsigned long dyn_rel_off; + addr_t rel_addr; + addr_t rel_size; +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + addr_t bss_addr; + addr_t bss_size; +#endif +}; + +/* Assign sections to segments and decide how are sections laid out when loaded + in memory. This function also fills corresponding program headers. */ +static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum, + Section *interp, Section* strsec, + struct dyn_inf *dyninf, int *sec_order) +{ + int i, j, k, file_type, sh_order_index, file_offset; + unsigned long s_align; + long long tmp; + addr_t addr; + ElfW(Phdr) *ph; + Section *s; + + file_type = s1->output_type; + sh_order_index = 1; + file_offset = 0; + if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) + file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); + s_align = ELF_PAGE_SIZE; + if (s1->section_align) + s_align = s1->section_align; + + if (phnum > 0) { + 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 = (int) (addr & (s_align - 1)); + p_offset = file_offset & (s_align - 1); + if (a_offset < p_offset) + a_offset += s_align; + 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 & (s_align - 1)); + } + + ph = &phdr[0]; + /* Leave one program headers for the program interpreter and one for + the program header table itself if needed. These are done later as + they require section layout to be done first. */ + if (interp) + ph += 1 + HAVE_PHDR; + + /* dynamic relocation table information, for .dynamic section */ + dyninf->rel_addr = dyninf->rel_size = 0; +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + dyninf->bss_addr = dyninf->bss_size = 0; +#endif + + 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 = s_align; + + /* Decide the layout of sections loaded in memory. This must + be done before program headers are filled since they contain + info about the layout. 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_RELX) { + if (k != 2) + continue; + } else if (s->sh_type == SHT_NOBITS) { + if (k != 4) + continue; + } else { + if (k != 3) + continue; + } + sec_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 += (int) ( 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_RELX) { +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + if (!strcmp(strsec->data + s->sh_name, ".rel.got")) { + dyninf->rel_addr = addr; + dyninf->rel_size += s->sh_size; /* XXX only first rel. */ + } + if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) { + dyninf->bss_addr = addr; + dyninf->bss_size = s->sh_size; /* XXX only first rel. */ + } +#else + if (dyninf->rel_size == 0) + dyninf->rel_addr = addr; + dyninf->rel_size += s->sh_size; +#endif + } + addr += s->sh_size; + if (s->sh_type != SHT_NOBITS) + file_offset += s->sh_size; + } + } + if (j == 0) { + /* Make the first PT_LOAD segment include the program + headers itself (and the ELF header as well), it'll + come out with same memory use but will make various + tools like binutils strip work better. */ + ph->p_offset &= ~(ph->p_align - 1); + ph->p_vaddr &= ~(ph->p_align - 1); + ph->p_paddr &= ~(ph->p_align - 1); + } + 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 & (s_align - 1)) != 0) + addr += s_align; + } else { + addr = (addr + s_align - 1) & ~(s_align - 1); + file_offset = (file_offset + s_align - 1) & ~(s_align - 1); + } + } + } + } + + /* 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; + sec_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; + } + + return file_offset; +} + +static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp, + Section *dynamic) +{ + ElfW(Phdr) *ph; + + /* if interpreter, then add corresponding program header */ + if (interp) { + ph = &phdr[0]; + + if (HAVE_PHDR) + { + int len = phnum * sizeof(ElfW(Phdr)); + + ph->p_type = PT_PHDR; + ph->p_offset = sizeof(ElfW(Ehdr)); + ph->p_vaddr = interp->sh_addr - len; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = ph->p_memsz = len; + ph->p_flags = PF_R | PF_X; + ph->p_align = 4; /* interp->sh_addralign; */ + ph++; + } + + 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 corresponding program header */ + if (dynamic) { + 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; + } +} + +/* Fill the dynamic section with tags describing the address and size of + sections */ +static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf) +{ + Section *dynamic; + + dynamic = dyninf->dynamic; + + /* put dynamic section entries */ + dynamic->data_offset = dyninf->dyn_rel_off; + put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr); + put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr); + put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr); + put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset); + put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym))); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + put_dt(dynamic, DT_RELA, dyninf->rel_addr); + put_dt(dynamic, DT_RELASZ, dyninf->rel_size); + put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel)); +#else +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr); + put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size); + put_dt(dynamic, DT_JMPREL, dyninf->rel_addr); + put_dt(dynamic, DT_PLTREL, DT_REL); + put_dt(dynamic, DT_REL, dyninf->bss_addr); + put_dt(dynamic, DT_RELSZ, dyninf->bss_size); +#else + put_dt(dynamic, DT_REL, dyninf->rel_addr); + put_dt(dynamic, DT_RELSZ, dyninf->rel_size); + put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel)); +#endif +#endif + if (s1->do_debug) + put_dt(dynamic, DT_DEBUG, 0); + put_dt(dynamic, DT_NULL, 0); +} + +/* Relocate remaining sections and symbols (that is those not related to + dynamic linking) */ +static int final_sections_reloc(TCCState *s1) +{ + int i; + Section *s; + + relocate_syms(s1, 0); + + if (s1->nb_errors != 0) + return -1; + + /* relocate sections */ + /* XXX: ignore sections with allocated relocations ? */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; +#ifdef TCC_TARGET_I386 + if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr + /* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC + checking is removed */ +#else + if (s->reloc && s != s1->got) + /* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */ +#endif + 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_RELX) { + relocate_rel(s1, s); + } + } + return 0; +} + +/* Create an ELF file on disk. + This function handle ELF specific layout requirements */ +static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, + int file_offset, int *sec_order) +{ + int i, shnum, offset, size, file_type; + Section *s; + ElfW(Ehdr) ehdr; + ElfW(Shdr) shdr, *sh; + + file_type = s1->output_type; + shnum = s1->nb_sections; + + memset(&ehdr, 0, sizeof(ehdr)); + + if (phnum > 0) { + ehdr.e_phentsize = sizeof(ElfW(Phdr)); + ehdr.e_phnum = phnum; + ehdr.e_phoff = sizeof(ElfW(Ehdr)); + } + + /* 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] = ELFCLASSW; + ehdr.e_ident[5] = ELFDATA2LSB; + ehdr.e_ident[6] = EV_CURRENT; +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; +#endif +#ifdef TCC_TARGET_ARM +#ifdef TCC_ARM_EABI + ehdr.e_ident[EI_OSABI] = 0; + ehdr.e_flags = EF_ARM_EABI_VER4; + if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL) + ehdr.e_flags |= EF_ARM_HASENTRY; + if (s1->float_abi == ARM_HARD_FLOAT) + ehdr.e_flags |= EF_ARM_VFP_FLOAT; + else + ehdr.e_flags |= EF_ARM_SOFT_FLOAT; +#else + ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM; +#endif +#endif + switch(file_type) { + default: + case TCC_OUTPUT_EXE: + ehdr.e_type = ET_EXEC; + ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1); + break; + case TCC_OUTPUT_DLL: + ehdr.e_type = ET_DYN; + ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */ + 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(ElfW(Ehdr)); + ehdr.e_shentsize = sizeof(ElfW(Shdr)); + ehdr.e_shnum = shnum; + ehdr.e_shstrndx = shnum - 1; + + fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f); + fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f); + offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); + + sort_syms(s1, symtab_section); + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[sec_order[i]]; + if (s->sh_type != SHT_NOBITS) { + if (s->sh_type == SHT_DYNSYM) + patch_dynsym_undef(s1, s); + while (offset < s->sh_offset) { + fputc(0, f); + offset++; + } + size = s->sh_size; + 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(ElfW(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; + } + fwrite(sh, 1, sizeof(ElfW(Shdr)), f); + } +} + +/* Write an elf, coff or "binary" file */ +static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum, + ElfW(Phdr) *phdr, int file_offset, int *sec_order) +{ + int fd, mode, file_type; + FILE *f; + + file_type = s1->output_type; + if (file_type == TCC_OUTPUT_OBJ) + mode = 0666; + else + mode = 0777; + unlink(filename); + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); + if (fd < 0) { + tcc_error_noabort("could not write '%s'", filename); + return -1; + } + f = fdopen(fd, "wb"); + if (s1->verbose) + printf("<- %s\n", filename); + +#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) + tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order); + else + tcc_output_binary(s1, f, sec_order); + fclose(f); + + return 0; +} + +/* Output an elf, coff or binary file */ +/* XXX: suppress unneeded sections */ +static int elf_output_file(TCCState *s1, const char *filename) +{ + int i, ret, phnum, shnum, file_type, file_offset, *sec_order; + struct dyn_inf dyninf; + ElfW(Phdr) *phdr; + ElfW(Sym) *sym; + Section *strsec, *interp, *dynamic, *dynstr; + + file_type = s1->output_type; + s1->nb_errors = 0; + + /* if linking, also link in runtime libraries (libc, libgcc, etc.) */ + if (file_type != TCC_OUTPUT_OBJ) { + tcc_add_runtime(s1); + } + + phdr = NULL; + sec_order = NULL; + interp = dynamic = dynstr = NULL; /* avoid warning */ + dyninf.dyn_rel_off = 0; /* avoid warning */ + + if (file_type != TCC_OUTPUT_OBJ) { + relocate_common_syms(); + + tcc_add_linker_symbols(s1); + + if (!s1->static_link) { + if (file_type == TCC_OUTPUT_EXE) { + char *ptr; + /* allow override the dynamic loader */ + const char *elfint = getenv("LD_SO"); + if (elfint == NULL) + elfint = DEFAULT_ELFINTERP(s1); + /* add interpreter section only if executable */ + interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC); + interp->sh_addralign = 1; + ptr = section_ptr_add(interp, 1 + strlen(elfint)); + strcpy(ptr, elfint); + } + + /* 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(ElfW(Dyn)); + + build_got(s1); + + if (file_type == TCC_OUTPUT_EXE) { + bind_exe_dynsyms(s1); + + if (s1->nb_errors) { + ret = -1; + goto the_end; + } + + bind_libs_dynsyms(s1); + } else /* shared library case: simply export all global symbols */ + export_global_syms(s1); + + 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)); + } + + if (s1->rpath) + put_dt(dynamic, DT_RPATH, put_elf_str(dynstr, s1->rpath)); + + /* XXX: currently, since we do not handle PIC code, we + must relocate the readonly segments */ + if (file_type == TCC_OUTPUT_DLL) { + if (s1->soname) + put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname)); + put_dt(dynamic, DT_TEXTREL, 0); + } + + if (s1->symbolic) + put_dt(dynamic, DT_SYMBOLIC, 0); + + /* add necessary space for other entries */ + dyninf.dyn_rel_off = dynamic->data_offset; + dynamic->data_offset += sizeof(ElfW(Dyn)) * EXTRA_RELITEMS; + } else { + /* still need to build got entries in case of static link */ + build_got_entries(s1); + } + } + + /* 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 */ + sec_order = tcc_malloc(sizeof(int) * shnum); + sec_order[0] = 0; + + /* 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 + HAVE_PHDR; + else + phnum = 2; + break; + case TCC_OUTPUT_DLL: + phnum = 3; + break; + } + + /* Allocate strings for section names */ + alloc_sec_names(s1, file_type, strsec); + + /* allocate program segment headers */ + phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); + + /* compute section to program header mapping */ + file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf, + sec_order); + + /* Fill remaining program header and finalize relocation related to dynamic + linking. */ + if (phnum > 0) { + fill_unloadable_phdr(phdr, phnum, interp, dynamic); + if (dynamic) { + dyninf.dynamic = dynamic; + dyninf.dynstr = dynstr; + + fill_dynamic(s1, &dyninf); + + /* put in GOT the dynamic section address and relocate PLT */ + put32(s1->got->data, dynamic->sh_addr); + if (file_type == TCC_OUTPUT_EXE +#if defined(TCC_OUTPUT_DLL_WITH_PLT) + || file_type == TCC_OUTPUT_DLL +#endif + ) + relocate_plt(s1); + + /* relocate symbols in .dynsym now that final addresses are known */ + for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) { + if (sym->st_shndx == SHN_UNDEF) { + /* relocate to PLT if symbol corresponds to a PLT entry, + but not if it's a weak symbol */ + if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) + sym->st_value = 0; + else 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; + } + } + } + } + + /* if building executable or DLL, then relocate each section + except the GOT which is already relocated */ + if (file_type != TCC_OUTPUT_OBJ) { + ret = final_sections_reloc(s1); + if (ret) + goto the_end; + } + + /* Perform relocation to GOT or PLT entries */ + if (file_type == TCC_OUTPUT_EXE && s1->static_link) + fill_got(s1); + + /* Create the ELF file with name 'filename' */ + ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order); + if (s1->do_strip) { + int rc; + const char *strip_cmd = "sstrip "; // super strip utility from ELFkickers + const char *null_dev = " 2> /dev/null"; + char buf[1050]; + snprintf(buf, sizeof(buf), "%s%s%s", strip_cmd, filename, null_dev); + rc = system(buf); + if (rc) + system(buf+1); // call a strip utility from binutils + } + the_end: + tcc_free(s1->symtab_to_dynsym); + tcc_free(sec_order); + tcc_free(phdr); + tcc_free(s1->sym_attrs); + s1->sym_attrs = NULL; + return ret; +} + +LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename) +{ + int ret; +#ifdef TCC_TARGET_PE + if (s->output_type != TCC_OUTPUT_OBJ) { + ret = pe_output_file(s, filename); + } else +#endif + ret = elf_output_file(s, filename); + 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); + 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 */ +ST_FUNC int tcc_load_object_file(TCCState *s1, + int fd, unsigned long file_offset) +{ + ElfW(Ehdr) ehdr; + ElfW(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; + ElfW(Sym) *sym, *symtab; + ElfW_Rel *rel; + Section *s; + + int stab_index; + int stabstr_index; + + stab_index = stabstr_index = 0; + + 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: + tcc_error_noabort("invalid object file"); + return -1; + } + /* read sections */ + shdr = load_data(fd, file_offset + ehdr.e_shoff, + sizeof(ElfW(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) { + tcc_error_noabort("object must contain only one symtab"); + fail: + ret = -1; + goto the_end; + } + nb_syms = sh->sh_size / sizeof(ElfW(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 = (char *) strsec + sh->sh_name; + /* ignore sections types we do not handle */ + if (sh->sh_type != SHT_PROGBITS && + sh->sh_type != SHT_RELX && +#ifdef TCC_ARM_EABI + sh->sh_type != SHT_ARM_EXIDX && +#endif + sh->sh_type != SHT_NOBITS && + sh->sh_type != SHT_PREINIT_ARRAY && + sh->sh_type != SHT_INIT_ARRAY && + sh->sh_type != SHT_FINI_ARRAY && + strcmp(sh_name, ".stabstr") + ) + 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) { + tcc_error_noabort("invalid section type"); + goto fail; + } + + /* align start of section */ + offset = s->data_offset; + + if (0 == strcmp(sh_name, ".stab")) { + stab_index = i; + goto no_align; + } + if (0 == strcmp(sh_name, ".stabstr")) { + stabstr_index = i; + goto no_align; + } + + 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; + no_align: + 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); + read(fd, ptr, size); + } else { + s->data_offset += size; + } + next: ; + } + + /* gr relocate stab strings */ + if (stab_index && stabstr_index) { + Stab_Sym *a, *b; + unsigned o; + s = sm_table[stab_index].s; + a = (Stab_Sym *)(s->data + sm_table[stab_index].offset); + b = (Stab_Sym *)(s->data + s->data_offset); + o = sm_table[stabstr_index].offset; + while (a < b) + a->n_strx += o, a++; + } + + /* second short pass to update sh_link and sh_info fields of new + sections */ + 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_RELX) { + s->sh_info = sm_table[sh->sh_info].s->sh_num; + /* update backward link */ + s1->sections[s->sh_info]->reloc = s; + } + } + sm = sm_table; + + /* 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 (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { + name = (char *) 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 = (char *) 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_RELX: + /* take relocation offset information */ + offseti = sm_table[sh->sh_info].offset; + for_each_elem(s, (offset / sizeof(*rel)), rel, ElfW_Rel) { + int type; + unsigned sym_index; + /* convert symbol index */ + type = ELFW(R_TYPE)(rel->r_info); + sym_index = ELFW(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]; + /* ignore link_once in rel section. */ + if (!sym_index && !sm->link_once +#ifdef TCC_TARGET_ARM + && type != R_ARM_V4BX +#endif + ) { + invalid_reloc: + tcc_error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x", + i, strsec + sh->sh_name, rel->r_offset); + goto fail; + } + rel->r_info = ELFW(R_INFO)(sym_index, type); + /* offset the relocation offset */ + rel->r_offset += offseti; +#ifdef TCC_TARGET_ARM + /* Jumps and branches from a Thumb code to a PLT entry need + special handling since PLT entries are ARM code. + Unconditional bl instructions referencing PLT entries are + handled by converting these instructions into blx + instructions. Other case of instructions referencing a PLT + entry require to add a Thumb stub before the PLT entry to + switch to ARM mode. We set bit plt_thumb_stub of the + attribute of a symbol to indicate such a case. */ + if (type == R_ARM_THM_JUMP24) + alloc_sym_attr(s1, sym_index)->plt_thumb_stub = 1; +#endif + } + 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; +} + +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; + ElfW(Sym) *sym; + + data = tcc_malloc(size); + if (read(fd, data, size) != size) + goto fail; + nsyms = get_be32(data); + ar_index = data + 4; + ar_names = (char *) 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 = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if(sym->st_shndx == SHN_UNDEF) { + off = get_be32(ar_index + i * 4) + sizeof(ArchiveHeader); + ++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 */ +ST_FUNC 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 */ + read(fd, magic, sizeof(magic)); + + for(;;) { + len = read(fd, &hdr, sizeof(hdr)); + if (len == 0) + break; + if (len != sizeof(hdr)) { + tcc_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'; + 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; +} + +#ifndef TCC_TARGET_PE +/* 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) */ +ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) +{ + ElfW(Ehdr) ehdr; + ElfW(Shdr) *shdr, *sh, *sh1; + int i, j, nb_syms, nb_dts, sym_bind, ret; + ElfW(Sym) *sym, *dynsym; + ElfW(Dyn) *dt, *dynamic; + unsigned char *dynstr; + const char *name, *soname; + DLLReference *dllref; + + read(fd, &ehdr, sizeof(ehdr)); + + /* test CPU specific stuff */ + if (ehdr.e_ident[5] != ELFDATA2LSB || + ehdr.e_machine != EM_TCC_TARGET) { + tcc_error_noabort("bad architecture"); + return -1; + } + + /* read sections */ + shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(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(ElfW(Dyn)); + dynamic = load_data(fd, sh->sh_offset, sh->sh_size); + break; + case SHT_DYNSYM: + nb_syms = sh->sh_size / sizeof(ElfW(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 = tcc_basename(filename); + + for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) { + if (dt->d_tag == DT_SONAME) { + soname = (char *) 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; + } + } + + /* add the dll and its level */ + dllref = tcc_mallocz(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 = ELFW(ST_BIND)(sym->st_info); + if (sym_bind == STB_LOCAL) + continue; + name = (char *) 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 = (char *) dynstr + dt->d_un.d_val; + for(j = 0; j < s1->nb_loaded_dlls; j++) { + dllref = s1->loaded_dlls[j]; + if (!strcmp(name, dllref->name)) + goto already_loaded; + } + if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) { + tcc_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 '\\': + ch = handle_eob(); + if (ch != '\\') + goto redo; + /* fall through */ + /* case 'a' ... 'z': */ + 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' ... 'z': */ + 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 '_': + 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; + } + return c; +} + +static int ld_add_file(TCCState *s1, const char filename[]) +{ + int ret; + + ret = tcc_add_file_internal(s1, filename, 0, TCC_FILETYPE_BINARY); + if (ret) + ret = tcc_add_dll(s1, filename, 0); + return ret; +} + +static inline int new_undef_syms(void) +{ + int ret = 0; + ret = new_undef_sym; + new_undef_sym = 0; + return ret; +} + +static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed) +{ + char filename[1024], libname[1024]; + int t, group, nblibs = 0, ret = 0; + char **libs = NULL; + + group = !strcmp(cmd, "GROUP"); + if (!as_needed) + new_undef_syms(); + t = ld_next(s1, filename, sizeof(filename)); + if (t != '(') + expect("("); + t = ld_next(s1, filename, sizeof(filename)); + for(;;) { + libname[0] = '\0'; + if (t == LD_TOK_EOF) { + tcc_error_noabort("unexpected end of file"); + ret = -1; + goto lib_parse_error; + } else if (t == ')') { + break; + } else if (t == '-') { + t = ld_next(s1, filename, sizeof(filename)); + if ((t != LD_TOK_NAME) || (filename[0] != 'l')) { + tcc_error_noabort("library name expected"); + ret = -1; + goto lib_parse_error; + } + pstrcpy(libname, sizeof libname, &filename[1]); + if (s1->static_link) { + snprintf(filename, sizeof filename, "lib%s.a", libname); + } else { + snprintf(filename, sizeof filename, "lib%s.so", libname); + } + } else if (t != LD_TOK_NAME) { + tcc_error_noabort("filename expected"); + ret = -1; + goto lib_parse_error; + } + if (!strcmp(filename, "AS_NEEDED")) { + ret = ld_add_file_list(s1, cmd, 1); + if (ret) + goto lib_parse_error; + } else { + /* TODO: Implement AS_NEEDED support. Ignore it for now */ + if (!as_needed) { + ret = ld_add_file(s1, filename); + if (ret) + goto lib_parse_error; + if (group) { + /* Add the filename *and* the libname to avoid future conversions */ + dynarray_add((void ***) &libs, &nblibs, tcc_strdup(filename)); + if (libname[0] != '\0') + dynarray_add((void ***) &libs, &nblibs, tcc_strdup(libname)); + } + } + } + t = ld_next(s1, filename, sizeof(filename)); + if (t == ',') { + t = ld_next(s1, filename, sizeof(filename)); + } + } + if (group && !as_needed) { + while (new_undef_syms()) { + int i; + + for (i = 0; i < nblibs; i ++) + ld_add_file(s1, libs[i]); + } + } +lib_parse_error: + dynarray_reset(&libs, &nblibs); + return ret; +} + +/* interpret a subset of GNU ldscripts to handle the dummy libc.so + files */ +ST_FUNC int tcc_load_ldscript(TCCState *s1) +{ + char cmd[64]; + char filename[1024]; + int t, ret; + + 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")) { + ret = ld_add_file_list(s1, cmd, 0); + if (ret) + return ret; + } 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) { + tcc_error_noabort("unexpected end of file"); + return -1; + } else if (t == ')') { + break; + } + } + } else { + return -1; + } + } + return 0; +} +#endif /* !TCC_TARGET_PE */ diff --git a/src/tccgen.c b/src/tccgen.c new file mode 100644 index 0000000..cf97d1f --- /dev/null +++ b/src/tccgen.c @@ -0,0 +1,6463 @@ +/* + * 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 + */ + +#include "tcc.h" + +/********************************************************/ +/* global variables */ + +/* loc : local variable index + ind : output code index + rsym: return symbol + anon_sym: anonymous symbol index +*/ +ST_DATA int rsym, anon_sym, ind, loc; + +ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */ +ST_DATA Section *cur_text_section; /* current section where function code is generated */ +#ifdef CONFIG_TCC_ASM +ST_DATA Section *last_text_section; /* to handle .previous asm directive */ +#endif +#ifdef CONFIG_TCC_BCHECK +/* bound check related sections */ +ST_DATA Section *bounds_section; /* contains global data bound description */ +ST_DATA Section *lbounds_section; /* contains local data bound description */ +#endif +/* symbol sections */ +ST_DATA Section *symtab_section, *strtab_section; +/* debug sections */ +ST_DATA Section *stab_section, *stabstr_section; +ST_DATA Sym *sym_free_first; +ST_DATA void **sym_pools; +ST_DATA int nb_sym_pools; + +ST_DATA Sym *global_stack; +ST_DATA Sym *local_stack; +ST_DATA Sym *scope_stack_bottom; +ST_DATA Sym *define_stack; +ST_DATA Sym *global_label_stack; +ST_DATA Sym *local_label_stack; + +ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */ +ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */ +ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */ + +ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop; + +ST_DATA int const_wanted; /* true if constant wanted */ +ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */ +ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */ +ST_DATA CType func_vt; /* current function return type (used by return instruction) */ +ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */ +ST_DATA int func_vc; +ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ +ST_DATA const char *funcname; + +ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; + +/* ------------------------------------------------------------------------- */ +static void gen_cast(CType *type); +static inline CType *pointed_type(CType *type); +static int is_compatible_types(CType *type1, CType *type2); +static int parse_btype(CType *type, AttributeDef *ad); +static void type_decl(CType *type, AttributeDef *ad, int *v, int td); +static void parse_expr_type(CType *type); +static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only); +static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg, int is_expr); +static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, char *asm_label, int scope); +static int decl0(int l, int is_for_loop_init); +static void expr_eq(void); +static void unary_type(CType *type); +static void vla_runtime_type_size(CType *type, int *a); +static void vla_sp_restore(void); +static void vla_sp_restore_root(void); +static int is_compatible_parameter_types(CType *type1, CType *type2); +static void expr_type(CType *type); +ST_FUNC void vpush64(int ty, unsigned long long v); +ST_FUNC void vpush(CType *type); +ST_FUNC int gvtst(int inv, int t); +ST_FUNC int is_btype_size(int bt); + +ST_INLN int is_float(int t) +{ + int bt; + bt = t & VT_BTYPE; + return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT; +} + +/* we use our own 'finite' function to avoid potential problems with + non standard math libs */ +/* XXX: endianness dependent */ +ST_FUNC int ieee_finite(double d) +{ + int p[4]; + memcpy(p, &d, sizeof(double)); + return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; +} + +ST_FUNC void test_lvalue(void) +{ + if (!(vtop->r & VT_LVAL)) + expect("lvalue"); +} + +ST_FUNC void check_vstack(void) +{ + if (pvtop != vtop) + tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop); +} + +/* ------------------------------------------------------------------------- */ +/* symbol allocator */ +static Sym *__sym_malloc(void) +{ + Sym *sym_pool, *sym, *last_sym; + int i; + + sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym)); + dynarray_add(&sym_pools, &nb_sym_pools, sym_pool); + + 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; +} + +ST_INLN void sym_free(Sym *sym) +{ + sym->next = sym_free_first; + tcc_free(sym->asm_label); + sym_free_first = sym; +} + +/* push, without hashing */ +ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c) +{ + Sym *s; + if (ps == &local_stack) { + for (s = *ps; s && s != scope_stack_bottom; s = s->prev) + if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM && s->v == v) + tcc_error("incompatible types for redefinition of '%s'", + get_tok_str(v, NULL)); + } + s = sym_malloc(); + s->asm_label = NULL; + s->v = v; + s->type.t = t; + s->type.ref = NULL; +#ifdef _WIN64 + s->d = NULL; +#endif + 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 */ +ST_FUNC Sym *sym_find2(Sym *s, int v) +{ + while (s) { + if (s->v == v) + return s; + else if (s->v == -1) + return NULL; + s = s->prev; + } + return NULL; +} + +/* structure lookup */ +ST_INLN 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 */ +ST_INLN 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 */ +ST_FUNC 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 */ +ST_FUNC 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' */ +ST_FUNC 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; +} + +static void weaken_symbol(Sym *sym) +{ + sym->type.t |= VT_WEAK; + if (sym->c > 0) { + int esym_type; + ElfW(Sym) *esym; + + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + esym_type = ELFW(ST_TYPE)(esym->st_info); + esym->st_info = ELFW(ST_INFO)(STB_WEAK, esym_type); + } +} + +static void apply_visibility(Sym *sym, CType *type) +{ + int vis = sym->type.t & VT_VIS_MASK; + int vis2 = type->t & VT_VIS_MASK; + if (vis == (STV_DEFAULT << VT_VIS_SHIFT)) + vis = vis2; + else if (vis2 == (STV_DEFAULT << VT_VIS_SHIFT)) + ; + else + vis = (vis < vis2) ? vis : vis2; + sym->type.t &= ~VT_VIS_MASK; + sym->type.t |= vis; + + if (sym->c > 0) { + ElfW(Sym) *esym; + + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + vis >>= VT_VIS_SHIFT; + esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) | vis; + } +} + +/* ------------------------------------------------------------------------- */ + +ST_FUNC void swap(int *p, int *q) +{ + int t; + t = *p; + *p = *q; + *q = t; +} + +static void vsetc(CType *type, int r, CValue *vc) +{ + int v; + + if (vtop >= vstack + (VSTACK_SIZE - 1)) + tcc_error("memory full (vstack)"); + /* 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 constant of type "type" with useless value */ +ST_FUNC void vpush(CType *type) +{ + CValue cval; + vsetc(type, VT_CONST, &cval); +} + +/* push integer constant */ +ST_FUNC void vpushi(int v) +{ + CValue cval; + cval.i = v; + vsetc(&int_type, VT_CONST, &cval); +} + +/* push a pointer sized constant */ +static void vpushs(addr_t v) +{ + CValue cval; + cval.ptr_offset = v; + vsetc(&size_type, VT_CONST, &cval); +} + +/* push arbitrary 64bit constant */ +ST_FUNC void vpush64(int ty, unsigned long long v) +{ + CValue cval; + CType ctype; + ctype.t = ty; + ctype.ref = NULL; + cval.ull = v; + vsetc(&ctype, VT_CONST, &cval); +} + +/* push long long constant */ +static inline void vpushll(long long v) +{ + vpush64(VT_LLONG, v); +} + +/* push a symbol value of TYPE */ +static inline void vpushsym(CType *type, Sym *sym) +{ + CValue cval; + cval.ptr_offset = 0; + vsetc(type, VT_CONST | VT_SYM, &cval); + vtop->sym = sym; +} + +/* Return a static symbol pointing to a section */ +ST_FUNC 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) +{ + vpushsym(type, get_sym_ref(type, sec, offset, size)); +} + +/* define a new external reference to a symbol 'v' of type 'u' */ +ST_FUNC 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' with alternate asm + name 'asm_label' of type 'u'. 'asm_label' is equal to NULL if there + is no alternate name (most cases) */ +static Sym *external_sym(int v, CType *type, int r, char *asm_label) +{ + Sym *s; + + s = sym_find(v); + if (!s) { + /* push forward reference */ + s = sym_push(v, type, r | VT_CONST | VT_SYM, 0); + s->asm_label = asm_label; + s->type.t |= VT_EXTERN; + } else if (s->type.ref == func_old_type.ref) { + s->type.ref = type->ref; + s->r = r | VT_CONST | VT_SYM; + s->type.t |= VT_EXTERN; + } else if (!is_compatible_types(&s->type, type)) { + tcc_error("incompatible types for redefinition of '%s'", + get_tok_str(v, NULL)); + } + /* Merge some storage attributes. */ + if (type->t & VT_WEAK) + weaken_symbol(s); + + if (type->t & VT_VIS_MASK) + apply_visibility(s, type); + + return s; +} + +/* push a reference to global symbol v */ +ST_FUNC void vpush_global_sym(CType *type, int v) +{ + vpushsym(type, external_global_sym(v, type, 0)); +} + +ST_FUNC void vset(CType *type, int r, int v) +{ + CValue cval; + + cval.i = v; + vsetc(type, r, &cval); +} + +static void vseti(int r, int v) +{ + CType type; + type.t = VT_INT; + type.ref = 0; + vset(&type, r, v); +} + +ST_FUNC void vswap(void) +{ + SValue tmp; + /* 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) { + int v = vtop->r & VT_VALMASK; + if (v == VT_CMP || (v & ~1) == VT_JMP) + gv(RC_INT); + } + tmp = vtop[0]; + vtop[0] = vtop[-1]; + vtop[-1] = tmp; + +/* XXX: +2% overall speed possible with optimized memswap + * + * memswap(&vtop[0], &vtop[1], sizeof *vtop); + */ +} + +ST_FUNC void vpushv(SValue *v) +{ + if (vtop >= vstack + (VSTACK_SIZE - 1)) + tcc_error("memory full (vstack)"); + vtop++; + *vtop = *v; +} + +ST_FUNC void vdup(void) +{ + vpushv(vtop); +} + +/* save r to the memory stack, and mark it as being free */ +ST_FUNC 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->type.t & VT_BTYPE) == VT_LLONG && (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)) +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + type = &char_pointer_type; +#else + type = &int_type; +#endif + 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); +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + /* x86 specific: need to pop fp register ST0 if saved */ + if (r == TREG_ST0) { + o(0xd8dd); /* fstp %st(0) */ + } +#endif +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) + /* special long long case */ + if ((type->t & VT_BTYPE) == VT_LLONG) { + sv.c.ul += 4; + store(p->r2, &sv); + } +#endif + 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; + } + } +} + +#ifdef TCC_TARGET_ARM +/* find a register of class 'rc2' with at most one reference on stack. + * If none, call get_reg(rc) */ +ST_FUNC 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); +} +#endif + +/* find a free register of class 'rc'. If none, save one register */ +ST_FUNC 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++) { + /* look at second register (if long long) */ + r = p->r2 & VT_VALMASK; + if (r < VT_CONST && (reg_classes[r] & rc)) + goto save_found; + r = p->r & 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 */ +ST_FUNC 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' (of type 't') to 'r', and flush previous value of r to memory + if needed */ +static void move_reg(int r, int s, int t) +{ + SValue sv; + + if (r != s) { + save_reg(r); + sv.type.t = t; + sv.type.ref = NULL; + sv.r = s; + sv.c.ul = 0; + load(r, &sv); + } +} + +/* get address of vtop (vtop MUST BE an lvalue) */ +ST_FUNC void gaddrof(void) +{ + if (vtop->r & VT_REF && !nocode_wanted) + gv(RC_INT); + 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 */ +static 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_PTR; + 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). */ +ST_FUNC int gv(int rc) +{ + int r, bit_pos, bit_size, size, align, i; + int rc2; + + /* NOTE: get_reg can modify vstack[] */ + if (vtop->type.t & VT_BITFIELD) { + CType type; + int bits = 32; + 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 | (-1 << VT_STRUCT_SHIFT)); + /* cast to int to propagate signedness in following ops */ + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + type.t = VT_LLONG; + bits = 64; + } else + type.t = VT_INT; + if((vtop->type.t & VT_UNSIGNED) || + (vtop->type.t & VT_BTYPE) == VT_BOOL) + type.t |= VT_UNSIGNED; + gen_cast(&type); + /* generate shifts */ + vpushi(bits - (bit_pos + bit_size)); + gen_op(TOK_SHL); + vpushi(bits - 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; +#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) + CValue check; +#endif + + /* 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 */ +#if defined(__i386__) || defined(__x86_64__) + /* Zero pad x87 tenbyte long doubles */ + if (size == LDOUBLE_SIZE) { + vtop->c.tab[2] &= 0xffff; +#if LDOUBLE_SIZE == 16 + vtop->c.tab[3] = 0; +#endif + } +#endif + ptr = section_ptr_add(data_section, size); + size = size >> 2; +#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) + check.d = 1; + if(check.tab[0]) + for(i=0;i<size;i++) + ptr[i] = vtop->c.tab[size-1-i]; + else +#endif + 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.ptr_offset = 0; + } +#ifdef CONFIG_TCC_BCHECK + if (vtop->r & VT_MUSTBOUND) + gbound(); +#endif + + r = vtop->r & VT_VALMASK; + rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT; +#ifndef TCC_TARGET_ARM64 + if (rc == RC_IRET) + rc2 = RC_LRET; +#ifdef TCC_TARGET_X86_64 + else if (rc == RC_FRET) + rc2 = RC_QRET; +#endif +#endif + + /* 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) +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2)) + || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2)) +#else + || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2)) +#endif + ) + { + r = get_reg(rc); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) { + int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; +#else + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + int addr_type = VT_INT, load_size = 4, load_type = VT_INT; + unsigned long long ll; +#endif + int r2, original_type; + original_type = vtop->type.t; + /* two register type load : expand to two words + temporarily */ +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) + 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 +#endif + 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 */ + vtop->type.t = load_type; + load(r, vtop); + vdup(); + vtop[-1].r = r; /* save register value */ + /* increment pointer to get second word */ + vtop->type.t = addr_type; + gaddrof(); + vpushi(load_size); + gen_op('+'); + vtop->r |= VT_LVAL; + vtop->type.t = load_type; + } else { + /* move registers */ + load(r, vtop); + vdup(); + vtop[-1].r = r; /* save register value */ + vtop->r = vtop[-1].r2; + } + /* Allocate second register. Here we rely on the fact that + get_reg() tries first to free r2 of an SValue. */ + r2 = get_reg(rc2); + load(r2, vtop); + vpop(); + /* write second register */ + vtop->r2 = r2; + vtop->type.t = original_type; + } 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_REF) +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + t = VT_PTR; +#else + t = VT_INT; +#endif + else 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 */ +ST_FUNC 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); + } + } +} + +#ifndef TCC_TARGET_ARM64 +/* wrapper around RC_FRET to return a register by type */ +static int rc_fret(int t) +{ +#ifdef TCC_TARGET_X86_64 + if (t == VT_LDOUBLE) { + return RC_ST0; + } +#endif + return RC_FRET; +} +#endif + +/* wrapper around REG_FRET to return a register by type */ +static int reg_fret(int t) +{ +#ifdef TCC_TARGET_X86_64 + if (t == VT_LDOUBLE) { + return TREG_ST0; + } +#endif + return REG_FRET; +} + +/* expand long long on stack in two int registers */ +static void lexpand(void) +{ + int u; + + u = vtop->type.t & (VT_DEFSIGN | 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 */ +ST_FUNC void lexpand_nr(void) +{ + int u,v; + + u = vtop->type.t & (VT_DEFSIGN | 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 */ +static 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] +*/ +ST_FUNC 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 the n elements before entry e towards the top + I1 ... In ... -> In I1 ... I(n-1) ... [top is right] + */ +ST_FUNC void vrote(SValue *e, int n) +{ + int i; + SValue tmp; + + tmp = *e; + for(i = 0;i < n - 1; i++) + e[-i] = e[-i - 1]; + e[-n + 1] = tmp; +} + +/* rotate n first stack elements to the top + I1 ... In -> In I1 ... I(n-1) [top is right] + */ +ST_FUNC void vrott(int n) +{ + vrote(vtop, n); +} + +/* pop stack value */ +ST_FUNC void vpop(void) +{ + int v; + v = vtop->r & VT_VALMASK; +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + /* for x86, we need to pop the FP stack */ + if (v == TREG_ST0 && !nocode_wanted) { + o(0xd8dd); /* fstp %st(0) */ + } 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 */ +static 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; +#ifdef TCC_TARGET_X86_64 + if ((t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + 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 */ + if (r != r1) + vtop->r = r1; + } +} + +/* Generate value test + * + * Generate a test for any value (jump, comparison and integers) */ +ST_FUNC int gvtst(int inv, int t) +{ + int v = vtop->r & VT_VALMASK; + if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) { + 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); + vtop--; + return t; + } + return gtst(inv, t); +} + +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) +/* generate CPU independent (unsigned) long long operations */ +static void gen_opl(int op) +{ + int t, a, b, op1, c, i; + int func; + unsigned short reg_iret = REG_IRET; + unsigned short reg_lret = REG_LRET; + 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_mod_func; + case TOK_UMOD: + func = TOK___umoddi3; + gen_mod_func: +#ifdef TCC_ARM_EABI + reg_iret = TREG_R2; + reg_lret = TREG_R3; +#endif + 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___ashrdi3; + goto gen_func; + case TOK_SHR: + func = TOK___lshrdi3; + goto gen_func; + case TOK_SHL: + func = TOK___ashldi3; + 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 = gvtst(1, 0); + } + if (op != TOK_EQ) { + /* generate non equal test */ + /* XXX: NOT PORTABLE yet */ + if (a == 0) { + b = gvtst(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) || defined(TCC_TARGET_ARM64) + tcc_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 = gvtst(1, a); + gsym(b); + vseti(VT_JMPI, a); + break; + } +} +#endif + +/* handle integer constant optimizations and various machine + independent opt */ +static void gen_opic(int op) +{ + int c1, c2, t1, t2, n; + SValue *v1, *v2; + long long l1, l2; + typedef unsigned long long U; + + v1 = vtop - 1; + v2 = vtop; + t1 = v1->type.t & VT_BTYPE; + t2 = v2->type.t & VT_BTYPE; + + if (t1 == VT_LLONG) + l1 = v1->c.ll; + else if (v1->type.t & VT_UNSIGNED) + l1 = v1->c.ui; + else + l1 = v1->c.i; + + if (t2 == VT_LLONG) + l2 = v2->c.ll; + else if (v2->type.t & VT_UNSIGNED) + l2 = v2->c.ui; + else + l2 = v2->c.i; + + /* 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) { + switch(op) { + case '+': l1 += l2; break; + case '-': l1 -= l2; break; + case '&': l1 &= l2; break; + case '^': l1 ^= l2; break; + case '|': l1 |= l2; break; + case '*': l1 *= l2; break; + + case TOK_PDIV: + case '/': + case '%': + case TOK_UDIV: + case TOK_UMOD: + /* if division by zero, generate explicit division */ + if (l2 == 0) { + if (const_wanted) + tcc_error("division by zero in constant"); + goto general_case; + } + switch(op) { + default: l1 /= l2; break; + case '%': l1 %= l2; break; + case TOK_UDIV: l1 = (U)l1 / l2; break; + case TOK_UMOD: l1 = (U)l1 % l2; break; + } + break; + case TOK_SHL: l1 <<= l2; break; + case TOK_SHR: l1 = (U)l1 >> l2; break; + case TOK_SAR: l1 >>= l2; break; + /* tests */ + case TOK_ULT: l1 = (U)l1 < (U)l2; break; + case TOK_UGE: l1 = (U)l1 >= (U)l2; break; + case TOK_EQ: l1 = l1 == l2; break; + case TOK_NE: l1 = l1 != l2; break; + case TOK_ULE: l1 = (U)l1 <= (U)l2; break; + case TOK_UGT: l1 = (U)l1 > (U)l2; break; + case TOK_LT: l1 = l1 < l2; break; + case TOK_GE: l1 = l1 >= l2; break; + case TOK_LE: l1 = l1 <= l2; break; + case TOK_GT: l1 = l1 > l2; break; + /* logical */ + case TOK_LAND: l1 = l1 && l2; break; + case TOK_LOR: l1 = l1 || l2; break; + default: + goto general_case; + } + v1->c.ll = l1; + vtop--; + } else { + /* if commutative ops, put c2 as constant */ + if (c1 && (op == '+' || op == '&' || op == '^' || + op == '|' || op == '*')) { + vswap(); + c2 = c1; //c = c1, c1 = c2, c2 = c; + l2 = l1; //l = l1, l1 = l2, l2 = l; + } + if (!const_wanted && + c1 && ((l1 == 0 && + (op == TOK_SHL || op == TOK_SHR || op == TOK_SAR)) || + (l1 == -1 && op == TOK_SAR))) { + /* treat (0 << x), (0 >> x) and (-1 >> x) as constant */ + vtop--; + } else if (!const_wanted && + c2 && ((l2 == 0 && (op == '&' || op == '*')) || + (l2 == -1 && op == '|') || + (l2 == 0xffffffff && t2 != VT_LLONG && op == '|') || + (l2 == 1 && (op == '%' || op == TOK_UMOD)))) { + /* treat (x & 0), (x * 0), (x | -1) and (x % 1) as constant */ + if (l2 == 1) + vtop->c.ll = 0; + vswap(); + vtop--; + } else if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV || + op == TOK_PDIV) && + l2 == 1) || + ((op == '+' || op == '-' || op == '|' || op == '^' || + op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) && + l2 == 0) || + (op == '&' && + l2 == -1))) { + /* filter out NOP operations like x*1, x-0, x&-1... */ + vtop--; + } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) { + /* try to use shifts instead of muls or divs */ + if (l2 > 0 && (l2 & (l2 - 1)) == 0) { + n = -1; + while (l2) { + l2 >>= 1; + n++; + } + vtop->c.ll = 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)) + || (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) { + /* symbol + constant case */ + if (op == '-') + l2 = -l2; + vtop--; + vtop->c.ll += l2; + } else { + general_case: + if (!nocode_wanted) { + /* call low level op generator */ + if (t1 == VT_LLONG || t2 == VT_LLONG || + (PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR))) + gen_opl(op); + else + gen_opi(op); + } else { + vtop--; + } + } + } +} + +/* generate a floating point operation with constant propagation */ +static 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) + tcc_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--; + } + } +} + +static int pointed_size(CType *type) +{ + int align; + return type_size(pointed_type(type), &align); +} + +static void vla_runtime_pointed_size(CType *type) +{ + int align; + vla_runtime_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) || + ((p->type.t & VT_BTYPE) == VT_PTR && p->c.ptr_offset == 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 subtraction 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 != '-') { + if (op != TOK_LOR && op != TOK_LAND ) + tcc_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: + tcc_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_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_DEFSIGN | 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 + tcc_warning("comparison of distinct pointer types lacks a cast"); + } +} + +/* generic gen_op: handles types problems */ +ST_FUNC 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_LOR) { + check_comparison_pointer_types(vtop - 1, vtop, op); + /* pointers are handled are unsigned */ +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + t = VT_LLONG | VT_UNSIGNED; +#else + t = VT_INT | VT_UNSIGNED; +#endif + goto std_op; + } + /* if both pointers, then it must be the '-' op */ + if (bt1 == VT_PTR && bt2 == VT_PTR) { + if (op != '-') + tcc_error("cannot use pointers here"); + check_comparison_pointer_types(vtop - 1, vtop, op); + /* XXX: check that types are compatible */ + if (vtop[-1].type.t & VT_VLA) { + vla_runtime_pointed_size(&vtop[-1].type); + } else { + vpushi(pointed_size(&vtop[-1].type)); + } + vrott(3); + gen_opic(op); + /* set to integer type */ +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + vtop->type.t = VT_LLONG; +#else + vtop->type.t = VT_INT; +#endif + vswap(); + gen_op(TOK_PDIV); + } else { + /* exactly one pointer : must be '+' or '-'. */ + if (op != '-' && op != '+') + tcc_error("cannot use pointers here"); + /* Put pointer as first operand */ + if (bt2 == VT_PTR) { + vswap(); + swap(&t1, &t2); + } + type1 = vtop[-1].type; + type1.t &= ~VT_ARRAY; + if (vtop[-1].type.t & VT_VLA) + vla_runtime_pointed_size(&vtop[-1].type); + else { + u = pointed_size(&vtop[-1].type); + if (u < 0) + tcc_error("unknown array element size"); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + vpushll(u); +#else + /* XXX: cast to int ? (long long case) */ + vpushi(u); +#endif + } + gen_op('*'); +#if 0 +/* #ifdef CONFIG_TCC_BCHECK + The main reason to removing this code: + #include <stdio.h> + int main () + { + int v[10]; + int i = 10; + int j = 9; + fprintf(stderr, "v+i-j = %p\n", v+i-j); + fprintf(stderr, "v+(i-j) = %p\n", v+(i-j)); + } + When this code is on. then the output looks like + v+i-j = 0xfffffffe + v+(i-j) = 0xbff84000 + */ + /* if evaluating constant expression, no code should be + generated, so no bound check */ + if (tcc_state->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)) + tcc_error("invalid operands for binary operation"); + goto std_op; + } else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) { + t = bt1 == VT_LLONG ? VT_LLONG : VT_INT; + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (t | VT_UNSIGNED)) + t |= VT_UNSIGNED; + 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 if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { + tcc_error("comparison of struct"); + } 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 + 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; + } + } + // Make sure that we have converted to an rvalue: + if (vtop->r & VT_LVAL && !nocode_wanted) + gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT); +} + +#ifndef TCC_TARGET_ARM +/* generic itof for unsigned long long case */ +static void gen_cvt_itof1(int t) +{ +#ifdef TCC_TARGET_ARM64 + gen_cvt_itof(t); +#else + if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_LLONG | VT_UNSIGNED)) { + + if (t == VT_FLOAT) + vpush_global_sym(&func_old_type, TOK___floatundisf); +#if LDOUBLE_SIZE != 8 + else if (t == VT_LDOUBLE) + vpush_global_sym(&func_old_type, TOK___floatundixf); +#endif + else + vpush_global_sym(&func_old_type, TOK___floatundidf); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->r = reg_fret(t); + } else { + gen_cvt_itof(t); + } +#endif +} +#endif + +/* generic ftoi for unsigned long long case */ +static void gen_cvt_ftoi1(int t) +{ +#ifdef TCC_TARGET_ARM64 + gen_cvt_ftoi(t); +#else + 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); +#if LDOUBLE_SIZE != 8 + else if (st == VT_LDOUBLE) + vpush_global_sym(&func_old_type, TOK___fixunsxfdi); +#endif + else + vpush_global_sym(&func_old_type, TOK___fixunsdfdi); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = REG_LRET; + } else { + gen_cvt_ftoi(t); + } +#endif +} + +/* force char or short cast */ +static 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); + /* result must be signed or the SAR is converted to an SHL + This was not the case when "t" was a signed short + and the last value on the stack was an unsigned int */ + vtop->type.t &= ~VT_UNSIGNED; + 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, p; + + /* 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 && !nocode_wanted) { + gv(RC_INT); + } + + dbt = type->t & (VT_BTYPE | VT_UNSIGNED); + sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED); + + if (sbt != dbt) { + sf = is_float(sbt); + df = is_float(dbt); + c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM); + if (c) { + /* constant case: we can do it now */ + /* XXX: in ISOC, cannot do it if error in convert */ + if (sbt == VT_FLOAT) + vtop->c.ld = vtop->c.f; + else if (sbt == VT_DOUBLE) + vtop->c.ld = vtop->c.d; + + if (df) { + if ((sbt & VT_BTYPE) == VT_LLONG) { + if (sbt & VT_UNSIGNED) + vtop->c.ld = vtop->c.ull; + else + vtop->c.ld = vtop->c.ll; + } else if(!sf) { + if (sbt & VT_UNSIGNED) + vtop->c.ld = vtop->c.ui; + else + vtop->c.ld = vtop->c.i; + } + + if (dbt == VT_FLOAT) + vtop->c.f = (float)vtop->c.ld; + else if (dbt == VT_DOUBLE) + vtop->c.d = (double)vtop->c.ld; + } else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) { + vtop->c.ull = (unsigned long long)vtop->c.ld; + } else if (sf && dbt == VT_BOOL) { + vtop->c.i = (vtop->c.ld != 0); + } else { + if(sf) + vtop->c.ll = (long long)vtop->c.ld; + else if (sbt == (VT_LLONG|VT_UNSIGNED)) + vtop->c.ll = vtop->c.ull; + else if (sbt & VT_UNSIGNED) + vtop->c.ll = vtop->c.ui; +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + else if (sbt == VT_PTR) + ; +#endif + else if (sbt != VT_LLONG) + vtop->c.ll = vtop->c.i; + + if (dbt == (VT_LLONG|VT_UNSIGNED)) + vtop->c.ull = vtop->c.ll; + else if (dbt == VT_BOOL) + vtop->c.i = (vtop->c.ll != 0); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + else if (dbt == VT_PTR) + ; +#endif + else if (dbt != VT_LLONG) { + int s = 0; + if ((dbt & VT_BTYPE) == VT_BYTE) + s = 24; + else if ((dbt & VT_BTYPE) == VT_SHORT) + s = 16; + if(dbt & VT_UNSIGNED) + vtop->c.ui = ((unsigned int)vtop->c.ll << s) >> s; + else + vtop->c.i = ((int)vtop->c.ll << s) >> s; + } + } + } else if (p && dbt == VT_BOOL) { + vtop->r = VT_CONST; + vtop->c.i = 1; + } else if (!nocode_wanted) { + /* non constant case: generate code */ + if (sf && df) { + /* convert from fp to fp */ + gen_cvt_ftof(dbt); + } else if (df) { + /* convert int to fp */ + gen_cvt_itof1(dbt); + } else if (sf) { + /* convert fp to int */ + if (dbt == VT_BOOL) { + vpushi(0); + gen_op(TOK_NE); + } else { + /* 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; + gen_cvt_ftoi1(dbt); + if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) { + /* additional cast for char/short... */ + vtop->type.t = dbt; + gen_cast(type); + } + } +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) + } else if ((dbt & VT_BTYPE) == VT_LLONG) { + if ((sbt & VT_BTYPE) != VT_LLONG && !nocode_wanted) { + /* scalar to long long */ + /* machine independent conversion */ + gv(RC_INT); + /* generate high word */ + if (sbt == (VT_INT | VT_UNSIGNED)) { + vpushi(0); + gv(RC_INT); + } else { + if (sbt == VT_PTR) { + /* cast from pointer to int before we apply + shift operation, which pointers don't support*/ + gen_cast(&int_type); + } + gv_dup(); + vpushi(31); + gen_op(TOK_SAR); + } + /* patch second register */ + vtop[-1].r2 = vtop->r; + vpop(); + } +#else + } else if ((dbt & VT_BTYPE) == VT_LLONG || + (dbt & VT_BTYPE) == VT_PTR || + (dbt & VT_BTYPE) == VT_FUNC) { + if ((sbt & VT_BTYPE) != VT_LLONG && + (sbt & VT_BTYPE) != VT_PTR && + (sbt & VT_BTYPE) != VT_FUNC && !nocode_wanted) { + /* need to convert from 32bit to 64bit */ + gv(RC_INT); + if (sbt != (VT_INT | VT_UNSIGNED)) { +#if defined(TCC_TARGET_ARM64) + gen_cvt_sxtw(); +#elif defined(TCC_TARGET_X86_64) + int r = gv(RC_INT); + /* x86_64 specific: movslq */ + o(0x6348); + o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); +#else +#error +#endif + } + } +#endif + } 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) { + if (sbt == VT_PTR) { + vtop->type.t = VT_INT; + tcc_warning("nonportable conversion from pointer to char/short"); + } + force_charshort_cast(dbt); + } else if ((dbt & VT_BTYPE) == VT_INT) { + /* scalar to int */ + if (sbt == VT_LLONG && !nocode_wanted) { + /* 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) */ + } + } + } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) { + /* if we are casting between pointer types, + we must update the VT_LVAL_xxx size */ + vtop->r = (vtop->r & ~VT_LVAL_TYPE) + | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE); + } + vtop->type = *type; +} + +/* return type size as known at compile time. Put alignment at 'a' */ +ST_FUNC 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) { + int ts; + + s = type->ref; + ts = type_size(&s->type, a); + + if (ts < 0 && s->c < 0) + ts = -ts; + + return ts * 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 +#ifdef TCC_TARGET_PE + *a = 8; +#else + *a = 4; +#endif +#elif defined(TCC_TARGET_ARM) +#ifdef TCC_ARM_EABI + *a = 8; +#else + *a = 4; +#endif +#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 if (bt == VT_QLONG || bt == VT_QFLOAT) { + *a = 8; + return 16; + } else { + /* char, void, function, _Bool */ + *a = 1; + return 1; + } +} + +/* push type size as known at runtime time on top of value stack. Put + alignment at 'a' */ +ST_FUNC void vla_runtime_type_size(CType *type, int *a) +{ + if (type->t & VT_VLA) { + vset(&int_type, VT_LOCAL|VT_LVAL, type->ref->c); + } else { + vpushi(type_size(type, a)); + } +} + +static void vla_sp_restore(void) { + if (vlas_in_scope) { + gen_vla_sp_restore(vla_sp_loc); + } +} + +static void vla_sp_restore_root(void) { + if (vlas_in_scope) { + gen_vla_sp_restore(vla_sp_root_loc); + } +} + +/* return the pointed type of t */ +static inline CType *pointed_type(CType *type) +{ + return &type->ref->type; +} + +/* modify type so that its it is a pointer to type. */ +ST_FUNC 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->a.func_call != s2->a.func_call) + 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_parameter_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 the same. If unqualified is + true, qualifiers on the types are ignored. + + - enums are not checked as gcc __builtin_types_compatible_p () + */ +static int compare_types(CType *type1, CType *type2, int unqualified) +{ + int bt1, t1, t2; + + t1 = type1->t & VT_TYPE; + t2 = type2->t & VT_TYPE; + if (unqualified) { + /* strip qualifiers before comparing */ + t1 &= ~(VT_CONSTANT | VT_VOLATILE); + t2 &= ~(VT_CONSTANT | VT_VOLATILE); + } + /* Default Vs explicit signedness only matters for char */ + if ((t1 & VT_BTYPE) != VT_BYTE) { + t1 &= ~VT_DEFSIGN; + t2 &= ~VT_DEFSIGN; + } + /* 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; + } +} + +/* return true if type1 and type2 are exactly the same (including + qualifiers). +*/ +static int is_compatible_types(CType *type1, CType *type2) +{ + return compare_types(type1,type2,0); +} + +/* return true if type1 and type2 are the same (ignoring qualifiers). +*/ +static int is_compatible_parameter_types(CType *type1, CType *type2) +{ + return compare_types(type1,type2,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 */ +static 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_DEFSIGN | VT_UNSIGNED)) == (VT_DEFSIGN | VT_UNSIGNED)) + pstrcat(buf, buf_size, "unsigned "); + else if (t & VT_DEFSIGN) + pstrcat(buf, buf_size, "signed "); + 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 (sbt == VT_VOID || dbt == VT_VOID) { + if (sbt == VT_VOID && dbt == VT_VOID) + ; /* + It is Ok if both are void + A test program: + void func1() {} + void func2() { + return func1(); + } + gcc accepts this program + */ + else + tcc_error("cannot cast from/to void"); + } + if (dt->t & VT_CONSTANT) + tcc_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)) { + tcc_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)) + tcc_warning("assignment from incompatible pointer type"); + 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_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | + VT_VOLATILE); + tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | + VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) + tcc_warning("assignment from incompatible pointer type"); + } + /* check const and volatile */ + if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || + (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE))) + tcc_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) { + tcc_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); + tcc_error("cannot cast '%s' to '%s'", buf1, buf2); + } + break; + } + type_ok: + gen_cast(dt); +} + +/* store vtop in lvalue pushed on stack */ +ST_FUNC 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)) + && !(vtop->type.t & VT_BITFIELD)) { + /* optimize char/short casts */ + delayed_cast = VT_MUSTCAST; + vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT))); + /* XXX: factorize */ + if (ft & VT_CONSTANT) + tcc_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); + + /* destination */ + vswap(); + vtop->type.t = VT_PTR; + gaddrof(); + + /* address of memcpy() */ +#ifdef TCC_ARM_EABI + if(!(align & 7)) + vpush_global_sym(&func_old_type, TOK_memcpy8); + else if(!(align & 3)) + vpush_global_sym(&func_old_type, TOK_memcpy4); + else +#endif + vpush_global_sym(&func_old_type, TOK_memcpy); + + vswap(); + /* source */ + vpushv(vtop - 2); + vtop->type.t = VT_PTR; + gaddrof(); + /* type size */ + vpushi(size); + gfunc_call(3); + } else { + vswap(); + vpop(); + } + /* leave source on stack */ + } else if (ft & VT_BITFIELD) { + /* bitfield store handling */ + + /* save lvalue as expression result (example: s.b = s.a = n;) */ + vdup(), vtop[-1] = vtop[-2]; + + 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 | (-1 << VT_STRUCT_SHIFT)); + + if((ft & VT_BTYPE) == VT_BOOL) { + gen_cast(&vtop[-1].type); + vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED); + } + + /* duplicate destination */ + vdup(); + vtop[-1] = vtop[-2]; + + /* mask and shift source */ + if((ft & VT_BTYPE) != VT_BOOL) { + if((ft & VT_BTYPE) == VT_LLONG) { + vpushll((1ULL << bit_size) - 1ULL); + } else { + vpushi((1 << bit_size) - 1); + } + gen_op('&'); + } + vpushi(bit_pos); + gen_op(TOK_SHL); + /* load destination, mask and or with source */ + vswap(); + if((ft & VT_BTYPE) == VT_LLONG) { + vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos)); + } else { + vpushi(~(((1 << bit_size) - 1) << bit_pos)); + } + gen_op('&'); + gen_op('|'); + /* store result */ + vstore(); + /* ... and discard */ + vpop(); + + } else { + if (!nocode_wanted) { +#ifdef CONFIG_TCC_BCHECK + /* bound check case */ + if (vtop[-1].r & VT_MUSTBOUND) { + vswap(); + gbound(); + vswap(); + } +#endif + rc = RC_INT; + if (is_float(ft)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } else if ((ft & VT_BTYPE) == VT_QFLOAT) { + rc = RC_FRET; + } +#endif + } + 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); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + sv.type.t = VT_PTR; +#else + sv.type.t = VT_INT; +#endif + sv.r = VT_LOCAL | VT_LVAL; + sv.c.ul = vtop[-1].c.ul; + load(t, &sv); + vtop[-1].r = t | VT_LVAL; + } + /* two word case handling : store second register at word + 4 (or +8 for x86-64) */ +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) { + int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; +#else + if ((ft & VT_BTYPE) == VT_LLONG) { + int addr_type = VT_INT, load_size = 4, load_type = VT_INT; +#endif + vtop[-1].type.t = load_type; + store(r, vtop - 1); + vswap(); + /* convert to int to increment easily */ + vtop->type.t = addr_type; + gaddrof(); + vpushi(load_size); + gen_op('+'); + vtop->r |= VT_LVAL; + vswap(); + vtop[-1].type.t = load_type; + /* XXX: it works because r2 is spilled last ! */ + store(vtop->r2, vtop - 1); + } else { + store(r, 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 -- */ +ST_FUNC void inc(int post, int c) +{ + test_lvalue(); + vdup(); /* save lvalue */ + if (post) { + if (!nocode_wanted) + gv_dup(); /* duplicate value */ + else + vdup(); /* 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_ALIAS1: + case TOK_ALIAS2: + skip('('); + if (tok != TOK_STR) + expect("alias(\"target\")"); + ad->alias_target = /* save string as token, for later */ + tok_alloc((char*)tokc.cstr->data, tokc.cstr->size-1)->tok; + next(); + skip(')'); + break; + case TOK_VISIBILITY1: + case TOK_VISIBILITY2: + skip('('); + if (tok != TOK_STR) + expect("visibility(\"default|hidden|internal|protected\")"); + if (!strcmp (tokc.cstr->data, "default")) + ad->a.visibility = STV_DEFAULT; + else if (!strcmp (tokc.cstr->data, "hidden")) + ad->a.visibility = STV_HIDDEN; + else if (!strcmp (tokc.cstr->data, "internal")) + ad->a.visibility = STV_INTERNAL; + else if (!strcmp (tokc.cstr->data, "protected")) + ad->a.visibility = STV_PROTECTED; + else + expect("visibility(\"default|hidden|internal|protected\")"); + next(); + skip(')'); + break; + case TOK_ALIGNED1: + case TOK_ALIGNED2: + if (tok == '(') { + next(); + n = expr_const(); + if (n <= 0 || (n & (n - 1)) != 0) + tcc_error("alignment must be a positive power of two"); + skip(')'); + } else { + n = MAX_ALIGN; + } + ad->a.aligned = n; + break; + case TOK_PACKED1: + case TOK_PACKED2: + ad->a.packed = 1; + break; + case TOK_WEAK1: + case TOK_WEAK2: + ad->a.weak = 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->a.func_call = FUNC_CDECL; + break; + case TOK_STDCALL1: + case TOK_STDCALL2: + case TOK_STDCALL3: + ad->a.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->a.func_call = FUNC_FASTCALL1 + n - 1; + skip(')'); + break; + case TOK_FASTCALL1: + case TOK_FASTCALL2: + case TOK_FASTCALL3: + ad->a.func_call = FUNC_FASTCALLW; + break; +#endif + case TOK_MODE: + skip('('); + switch(tok) { + case TOK_MODE_DI: + ad->a.mode = VT_LLONG + 1; + break; + case TOK_MODE_HI: + ad->a.mode = VT_SHORT + 1; + break; + case TOK_MODE_SI: + ad->a.mode = VT_INT + 1; + break; + default: + tcc_warning("__mode__(%s) not supported\n", get_tok_str(tok, NULL)); + break; + } + next(); + skip(')'); + break; + case TOK_DLLEXPORT: + ad->a.func_export = 1; + break; + case TOK_DLLIMPORT: + ad->a.func_import = 1; + break; + default: + if (tcc_state->warn_unsupported) + tcc_warning("'%s' attribute ignored", get_tok_str(t, NULL)); + /* skip parameters */ + if (tok == '(') { + int parenthesis = 0; + do { + if (tok == '(') + parenthesis++; + else if (tok == ')') + parenthesis--; + next(); + } while (parenthesis && tok != -1); + } + 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 tdef) +{ + int a, v, size, align, maxalign, c, offset, flexible; + int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; + Sym *s, *ss, *ass, **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) + tcc_error("invalid type"); + goto do_decl; + } else if (tok >= TOK_IDENT && !tdef) + tcc_error("unknown struct/union/enum"); + } else { + v = anon_sym++; + } + type1.t = a; + type1.ref = NULL; + /* 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) + tcc_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"); + ss = sym_find(v); + if (ss && !local_stack) + tcc_error("redefinition of enumerator '%s'", + get_tok_str(v, NULL)); + 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; + } + s->c = type_size(&int_type, &align); + skip('}'); + } else { + maxalign = 1; + ps = &s->next; + prevbt = VT_INT; + bit_pos = 0; + offset = 0; + flexible = 0; + while (tok != '}') { + parse_btype(&btype, &ad); + while (1) { + if (flexible) + tcc_error("flexible array member '%s' not at the end of struct", + get_tok_str(v, NULL)); + bit_size = -1; + v = 0; + type1 = btype; + if (tok != ':') { + type_decl(&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT); + if (v == 0) { + if ((type1.t & VT_BTYPE) != VT_STRUCT) + expect("identifier"); + else { + int v = btype.ref->v; + if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { + if (tcc_state->ms_extensions == 0) + expect("identifier"); + } + } + } + if (type_size(&type1, &align) < 0) { + if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY) && c) + flexible = 1; + else + tcc_error("field '%s' has incomplete type", + get_tok_str(v, NULL)); + } + if ((type1.t & VT_BTYPE) == VT_FUNC || + (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE))) + tcc_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) + tcc_error("negative width in bit-field '%s'", + get_tok_str(v, NULL)); + if (v && bit_size == 0) + tcc_error("zero width for bit-field '%s'", + get_tok_str(v, NULL)); + } + size = type_size(&type1, &align); + if (ad.a.aligned) { + if (align < ad.a.aligned) + align = ad.a.aligned; + } else if (ad.a.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 && + bt != VT_LLONG) + tcc_error("bitfields must have scalar type"); + bsize = size * 8; + if (bit_size > bsize) { + tcc_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 */ + bit_pos = 0; + } else { + /* we do not have enough room ? + did the type change? + is it a union? */ + if ((bit_pos + bit_size) > bsize || + bt != prevbt || a == TOK_UNION) + 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; + } + prevbt = bt; + } else { + bit_pos = 0; + } + if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { + /* add new memory data only if starting + bit field */ + if (lbit_pos == 0) { + if (a == TOK_STRUCT) { + c = (c + align - 1) & -align; + offset = c; + if (size > 0) + 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 + } + if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) { + ass = type1.ref; + while ((ass = ass->next) != NULL) { + ss = sym_push(ass->v, &ass->type, 0, offset + ass->c); + *ps = ss; + ps = &ss->next; + } + } else if (v) { + 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 1 if basic type is a type size (short, long, long long) */ +ST_FUNC int is_btype_size(int bt) +{ + return bt == VT_SHORT || bt == VT_LONG || bt == VT_LLONG; +} + +/* 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, bt_size, complete, type_found, typespec_found; + Sym *s; + CType type1; + + memset(ad, 0, sizeof(AttributeDef)); + complete = 0; + 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 (complete) + tcc_error("too many basic types"); + t |= u; + bt_size = is_btype_size (u & VT_BTYPE); + if (u == VT_INT || (!bt_size && !(t & VT_TYPEDEF))) + complete = 1; + 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: + u = VT_INT; + goto basic_type; + case TOK_LONG: + next(); + if ((t & VT_BTYPE) == VT_DOUBLE) { +#ifndef TCC_TARGET_PE + t = (t & ~VT_BTYPE) | VT_LDOUBLE; +#endif + } else if ((t & VT_BTYPE) == VT_LONG) { + t = (t & ~VT_BTYPE) | VT_LLONG; + } else { + u = VT_LONG; + goto basic_type1; + } + break; +#ifdef TCC_TARGET_ARM64 + case TOK_UINT128: + /* GCC's __uint128_t appears in some Linux header files. Make it a + synonym for long double to get the size and alignment right. */ + u = VT_LDOUBLE; + goto basic_type; +#endif + 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) { +#ifdef TCC_TARGET_PE + t = (t & ~VT_BTYPE) | VT_DOUBLE; +#else + t = (t & ~VT_BTYPE) | VT_LDOUBLE; +#endif + } else { + u = VT_DOUBLE; + goto basic_type1; + } + break; + case TOK_ENUM: + struct_decl(&type1, VT_ENUM, t & (VT_TYPEDEF | VT_EXTERN)); + basic_type2: + u = type1.t; + type->ref = type1.ref; + goto basic_type1; + case TOK_STRUCT: + case TOK_UNION: + struct_decl(&type1, VT_STRUCT, t & (VT_TYPEDEF | VT_EXTERN)); + 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: + if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == (VT_DEFSIGN|VT_UNSIGNED)) + tcc_error("signed and unsigned modifier"); + typespec_found = 1; + t |= VT_DEFSIGN; + next(); + break; + case TOK_REGISTER: + case TOK_AUTO: + case TOK_RESTRICT1: + case TOK_RESTRICT2: + case TOK_RESTRICT3: + next(); + break; + case TOK_UNSIGNED: + if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == VT_DEFSIGN) + tcc_error("signed and unsigned modifier"); + t |= VT_DEFSIGN | 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); + if (ad->a.mode) { + u = ad->a.mode -1; + t = (t & ~VT_BTYPE) | u; + } + break; + /* GNUC typeof */ + case TOK_TYPEOF1: + case TOK_TYPEOF2: + case TOK_TYPEOF3: + next(); + parse_expr_type(&type1); + /* remove all storage modifiers except typedef */ + type1.t &= ~(VT_STORAGE&~VT_TYPEDEF); + 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; + if (s->r) { + /* get attributes from typedef */ + if (0 == ad->a.aligned) + ad->a.aligned = s->a.aligned; + if (0 == ad->a.func_call) + ad->a.func_call = s->a.func_call; + ad->a.packed |= s->a.packed; + } + next(); + typespec_found = 1; + break; + } + type_found = 1; + } +the_end: + if (tcc_state->char_is_unsigned) { + if ((t & (VT_DEFSIGN|VT_BTYPE)) == VT_BYTE) + t |= VT_UNSIGNED; + } + + /* long is never used as type */ + if ((t & VT_BTYPE) == VT_LONG) +#if (!defined TCC_TARGET_X86_64 && !defined TCC_TARGET_ARM64) || \ + defined TCC_TARGET_PE + t = (t & ~VT_BTYPE) | VT_INT; +#else + t = (t & ~VT_BTYPE) | VT_LLONG; +#endif + 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); + } +} + +ST_FUNC 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 an asm label and return the label + * Don't forget to free the CString in the caller! */ +static void asm_label_instr(CString *astr) +{ + next(); + parse_asm_str(astr); + skip(')'); +#ifdef ASM_DEBUG + printf("asm_alias: \"%s\"\n", (char *)astr->data); +#endif +} + +static void post_type(CType *type, AttributeDef *ad) +{ + int n, l, t1, arg_size, align; + Sym **plast, *s, *first; + AttributeDef ad1; + CType pt; + + if (tok == '(') { + /* function declaration */ + next(); + l = 0; + first = NULL; + plast = &first; + arg_size = 0; + if (tok != ')') { + for(;;) { + /* read param name and compute offset */ + if (l != FUNC_OLD) { + if (!parse_btype(&pt, &ad1)) { + if (l) { + tcc_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) + tcc_error("parameter declared as void"); + arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE; + } else { + old_proto: + n = tok; + if (n < TOK_UIDENT) + expect("identifier"); + 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 == ')') + break; + skip(','); + 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(')'); + /* NOTE: const is ignored in returned type as it has a special + meaning in gcc / C++ */ + type->t &= ~VT_CONSTANT; + /* some ancient pre-K&R C allows a function to return an array + and the array brackets to be put after the arguments, such + that "int c()[]" means something like "int[] c()" */ + if (tok == '[') { + next(); + skip(']'); /* only handle simple "[]" */ + type->t |= VT_PTR; + } + /* we push a anonymous symbol which will contain the function prototype */ + ad->a.func_args = arg_size; + s = sym_push(SYM_FIELD, type, 0, l); + s->a = ad->a; + s->next = first; + type->t = VT_FUNC; + type->ref = s; + } else if (tok == '[') { + /* array definition */ + next(); + if (tok == TOK_RESTRICT1) + next(); + n = -1; + t1 = 0; + if (tok != ']') { + if (!local_stack || nocode_wanted) + vpushi(expr_const()); + else gexpr(); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + n = vtop->c.i; + if (n < 0) + tcc_error("invalid array size"); + } else { + if (!is_integer_btype(vtop->type.t & VT_BTYPE)) + tcc_error("size of variable length array should be an integer"); + t1 = VT_VLA; + } + } + skip(']'); + /* parse next post type */ + post_type(type, ad); + if (type->t == VT_FUNC) + tcc_error("declaration of an array of functions"); + t1 |= type->t & VT_VLA; + + if (t1 & VT_VLA) { + loc -= type_size(&int_type, &align); + loc &= -align; + n = loc; + + vla_runtime_type_size(type, &align); + gen_op('*'); + vset(&int_type, VT_LOCAL|VT_LVAL, n); + vswap(); + vstore(); + } + if (n != -1) + vpop(); + + /* we push an anonymous symbol which will contain the array + element type */ + s = sym_push(SYM_FIELD, type, 0, n); + type->t = (t1 ? VT_VLA : 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, storage; + + 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; + } + } + storage = type->t & VT_STORAGE; + type->t &= ~VT_STORAGE; + if (storage & VT_STATIC) { + int saved_nocode_wanted = nocode_wanted; + nocode_wanted = 1; + post_type(type, ad); + nocode_wanted = saved_nocode_wanted; + } else + post_type(type, ad); + type->t |= storage; + 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. */ +ST_FUNC 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 */ +ST_FUNC void indir(void) +{ + if ((vtop->type.t & VT_BTYPE) != VT_PTR) { + if ((vtop->type.t & VT_BTYPE) == VT_FUNC) + return; + expect("pointer"); + } + if ((vtop->r & VT_LVAL) && !nocode_wanted) + gv(RC_INT); + vtop->type = *pointed_type(&vtop->type); + /* Arrays and functions are never lvalues */ + if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA) + && (vtop->type.t & VT_BTYPE) != VT_FUNC) { + vtop->r |= lvalue_type(vtop->type.t); + /* if bound checking, the referenced pointer must be checked */ +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + vtop->r |= VT_MUSTBOUND; +#endif + } +} + +/* 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 (vtop->type.t & VT_BITFIELD) { + type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED); + gen_cast(&type); + } + } else if (arg == NULL) { + tcc_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; + type.ref = 0; + vsetc(&type, VT_CONST, &tokc); +} + +ST_FUNC void unary(void) +{ + int n, t, align, size, r, sizeof_caller; + CType type; + Sym *s; + AttributeDef ad; + static int in_sizeof = 0; + + sizeof_caller = in_sizeof; + in_sizeof = 0; + /* 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: +#ifdef TCC_TARGET_PE + t = VT_SHORT | VT_UNSIGNED; +#else + t = VT_INT; +#endif + 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, NULL, 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, NULL, 0); + } else { + if (sizeof_caller) { + vpush(&type); + return; + } + unary(); + gen_cast(&type); + } + } else if (tok == '{') { + /* + if (nocode_wanted) + tcc_error("statement expression in global scope"); */ + /* this check breaks compilation of the linux 2.4.26 with the meesage: + linux/include/net/tcp.h:945: error: statement expression in global scope */ + + /* 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) && !(vtop->type.t & VT_LLOCAL)) + test_lvalue(); + mk_pointer(&vtop->type); + gaddrof(); + break; + case '!': + next(); + unary(); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + CType boolean; + boolean.t = VT_BOOL; + gen_cast(&boolean); + vtop->c.i = !vtop->c.i; + } else if ((vtop->r & VT_VALMASK) == VT_CMP) + vtop->c.i = vtop->c.i ^ 1; + else if (!nocode_wanted) { + save_regs(1); + vseti(VT_JMP, gvtst(1, 0)); + } + else + vtop--; + break; + case '~': + next(); + unary(); + vpushi(-1); + gen_op('^'); + break; + case '+': + next(); + unary(); + if ((vtop->type.t & VT_BTYPE) == VT_PTR) + tcc_error("pointer not accepted for unary plus"); + /* In order to force cast, we add zero, except for floating point + where we really need an noop (otherwise -0.0 will be transformed + into +0.0). */ + if (!is_float(vtop->type.t)) { + vpushi(0); + gen_op('+'); + } + break; + case TOK_SIZEOF: + case TOK_ALIGNOF1: + case TOK_ALIGNOF2: + t = tok; + next(); + in_sizeof++; + unary_type(&type); // Perform a in_sizeof = 0; + size = type_size(&type, &align); + if (t == TOK_SIZEOF) { + if (!(type.t & VT_VLA)) { + if (size < 0) + tcc_error("sizeof applied to an incomplete type"); + vpushs(size); + } else { + vla_runtime_type_size(&type, &align); + } + } else { + vpushs(align); + } + vtop->type.t |= VT_UNSIGNED; + 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_builtin_frame_address: + case TOK_builtin_return_address: + { + int tok1 = tok; + int level; + CType type; + next(); + skip('('); + if (tok != TOK_CINT || tokc.i < 0) { + tcc_error("%s only takes positive integers", + tok1 == TOK_builtin_return_address ? + "__builtin_return_address" : + "__builtin_frame_address"); + } + level = tokc.i; + next(); + skip(')'); + type.t = VT_VOID; + mk_pointer(&type); + vset(&type, VT_LOCAL, 0); /* local frame */ + while (level--) { + mk_pointer(&vtop->type); + indir(); /* -> parent frame */ + } + if (tok1 == TOK_builtin_return_address) { + // assume return address is just above frame pointer on stack + vpushi(PTR_SIZE); + gen_op('+'); + mk_pointer(&vtop->type); + indir(); + } + } + break; +#ifdef TCC_TARGET_X86_64 +#ifdef TCC_TARGET_PE + case TOK_builtin_va_start: + { + next(); + skip('('); + expr_eq(); + skip(','); + expr_eq(); + skip(')'); + if ((vtop->r & VT_VALMASK) != VT_LOCAL) + tcc_error("__builtin_va_start expects a local variable"); + vtop->r &= ~(VT_LVAL | VT_REF); + vtop->type = char_pointer_type; + vstore(); + } + break; +#else + case TOK_builtin_va_arg_types: + { + CType type; + next(); + skip('('); + parse_type(&type); + skip(')'); + vpushi(classify_x86_64_va_arg(&type)); + } + break; +#endif +#endif + +#ifdef TCC_TARGET_ARM64 + case TOK___va_start: { + if (nocode_wanted) + tcc_error("statement in global scope"); + next(); + skip('('); + expr_eq(); + skip(','); + expr_eq(); + skip(')'); + //xx check types + gen_va_start(); + vpushi(0); + vtop->type.t = VT_VOID; + break; + } + case TOK___va_arg: { + CType type; + if (nocode_wanted) + tcc_error("statement in global scope"); + next(); + skip('('); + expr_eq(); + skip(','); + parse_type(&type); + skip(')'); + //xx check types + gen_va_arg(&type); + vtop->type = type; + break; + } + case TOK___arm64_clear_cache: { + next(); + skip('('); + expr_eq(); + skip(','); + expr_eq(); + skip(')'); + gen_clear_cache(); + vpushi(0); + vtop->type.t = VT_VOID; + break; + } +#endif + /* pre operations */ + case TOK_INC: + case TOK_DEC: + t = tok; + next(); + unary(); + inc(0, t); + break; + case '-': + next(); + unary(); + t = vtop->type.t & VT_BTYPE; + if (is_float(t)) { + /* In IEEE negate(x) isn't subtract(0,x), but rather + subtract(-0, x). */ + vpush(&vtop->type); + if (t == VT_FLOAT) + vtop->c.f = -0.0f; + else if (t == VT_DOUBLE) + vtop->c.d = -0.0; + else + vtop->c.ld = -0.0; + } else + vpushi(0); + vswap(); + 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; + } + vpushsym(&s->type, s); + next(); + break; + + // special qnan , snan and infinity values + case TOK___NAN__: + vpush64(VT_DOUBLE, 0x7ff8000000000000ULL); + next(); + break; + case TOK___SNAN__: + vpush64(VT_DOUBLE, 0x7ff0000000000001ULL); + next(); + break; + case TOK___INF__: + vpush64(VT_DOUBLE, 0x7ff0000000000000ULL); + next(); + break; + + default: + tok_identifier: + t = tok; + next(); + if (t < TOK_UIDENT) + expect("identifier"); + s = sym_find(t); + if (!s) { + const char *name = get_tok_str(t, NULL); + if (tok != '(') + tcc_error("'%s' undeclared", name); + /* for simple function calls, we tolerate undeclared + external reference to int() function */ + if (tcc_state->warn_implicit_function_declaration +#ifdef TCC_TARGET_PE + /* people must be warned about using undeclared WINAPI functions + (which usually start with uppercase letter) */ + || (name[0] >= 'A' && name[0] <= 'Z') +#endif + ) + tcc_warning("implicit declaration of function '%s'", name); + 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.ptr_offset = 0; + } + break; + } + + /* post operations */ + while (1) { + if (tok == TOK_INC || tok == TOK_DEC) { + inc(1, tok); + next(); + } else if (tok == '.' || tok == TOK_ARROW) { + int qualifiers; + /* field */ + if (tok == TOK_ARROW) + indir(); + qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE); + 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) + tcc_error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, NULL)); + /* 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; + vtop->type.t |= qualifiers; + /* an array is never an lvalue */ + if (!(vtop->type.t & VT_ARRAY)) { + vtop->r |= lvalue_type(vtop->type.t); +#ifdef CONFIG_TCC_BCHECK + /* if bound checking, the referenced pointer must be checked */ + if (tcc_state->do_bounds_check) + vtop->r |= VT_MUSTBOUND; +#endif + } + next(); + } else if (tok == '[') { + next(); + gexpr(); + gen_op('+'); + indir(); + skip(']'); + } else if (tok == '(') { + SValue ret; + Sym *sa; + int nb_args, ret_nregs, ret_align, regsize, variadic; + RegArgs 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; + ret.r2 = VT_CONST; + /* compute first implicit argument if a structure is returned */ + if ((s->type.t & VT_BTYPE) == VT_STRUCT) { + variadic = (s->c == FUNC_ELLIPSIS); + gfunc_sret(&s->type, variadic, &ret.type, + &ret_align, ®size, &args); + ret_nregs = regargs_nregs(&args); + + if (!ret_nregs) { + /* get some space for the returned structure */ + size = type_size(&s->type, &align); +#ifdef TCC_TARGET_ARM64 + /* On arm64, a small struct is return in registers. + It is much easier to write it to memory if we know + that we are allowed to write some extra bytes, so + round the allocated space up to a power of 2: */ + if (size < 16) + while (size & (size - 1)) + size = (size | (size - 1)) + 1; +#endif + 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_nregs = 1; + ret.type = s->type; + } + + if (ret_nregs) { + /* return in register */ + if (is_float(ret.type.t)) { + ret.r = reg_fret(ret.type.t); +#ifdef TCC_TARGET_X86_64 + if ((ret.type.t & VT_BTYPE) == VT_QFLOAT) + ret.r2 = REG_QRET; +#endif + } else { +#ifndef TCC_TARGET_ARM64 +#ifdef TCC_TARGET_X86_64 + if ((ret.type.t & VT_BTYPE) == VT_QLONG) +#else + if ((ret.type.t & VT_BTYPE) == VT_LLONG) +#endif + ret.r2 = REG_LRET; +#endif + 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) + tcc_error("too few arguments to function"); + skip(')'); + if (!nocode_wanted) { + gfunc_call(nb_args); + } else { + vtop -= (nb_args + 1); + } + + /* return value */ + for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) { + vsetc(&ret.type, r, &ret.c); + vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */ + } + + /* handle packed struct return */ + if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) { + int addr, offset; +#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE) + int i; +#endif + + + size = type_size(&s->type, &align); + /* We're writing whole regs often, make sure there's enough + space. Assume register size is power of 2. */ + if (regsize > align) + align = regsize; + loc = (loc - size) & -align; + addr = loc; +#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE) + for (i=0; i<REG_ARGS_MAX; i++) { + offset = args.ireg[i]; + + if (offset == -1) + break; + + ret.type.t = VT_LLONG; + vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); + vsetc(&ret.type, i ? REG_LRET : REG_IRET, &ret.c); + vstore(); + vtop--; + vtop--; + } + for (i=0; i<REG_ARGS_MAX; i++) { + offset = args.freg[i]; + + if (offset == -1) + break; + + ret.type.t = VT_DOUBLE; + vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); + vsetc(&ret.type, i ? REG_QRET : REG_FRET, &ret.c); + vstore(); + vtop--; + vtop--; + } +#else + offset = 0; + for (;;) { + vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); + vswap(); + vstore(); + vtop--; + if (--ret_nregs == 0) + break; + offset += regsize; + } +#endif + vset(&s->type, VT_LOCAL | VT_LVAL, addr); + } + } else { + break; + } + } +} + +ST_FUNC void expr_prod(void) +{ + int t; + + unary(); + while (tok == '*' || tok == '/' || tok == '%') { + t = tok; + next(); + unary(); + gen_op(t); + } +} + +ST_FUNC 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; + save_regs(1); + for(;;) { + t = gvtst(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; + save_regs(1); + for(;;) { + t = gvtst(0, t); + if (tok != TOK_LOR) { + vseti(VT_JMP, t); + break; + } + next(); + expr_land(); + } + } +} + +/* XXX: better constant handling */ +static void expr_cond(void) +{ + int tt, u, r1, r2, rc, t1, t2, bt1, bt2; + SValue sv; + CType type, type1, type2; + + if (const_wanted) { + expr_lor_const(); + if (tok == '?') { + CType boolean; + int c; + boolean.t = VT_BOOL; + vdup(); + gen_cast(&boolean); + c = vtop->c.i; + vpop(); + next(); + if (tok != ':' || !gnu_ext) { + vpop(); + gexpr(); + } + if (!c) + vpop(); + skip(':'); + expr_cond(); + if (c) + vpop(); + } + } 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; +#ifdef TCC_TARGET_X86_64 + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } + else + rc = RC_INT; + gv(rc); + save_regs(1); + } + if (tok == ':' && gnu_ext) { + gv_dup(); + tt = gvtst(1, 0); + } else { + tt = gvtst(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_cond(); + 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) { + /* If one is a null ptr constant the result type + is the other. */ + if (is_null_pointer (vtop)) + type = type1; + else if (is_null_pointer (&sv)) + type = type2; + /* XXX: test pointer compatibility, C99 has more elaborate + rules here. */ + else + type = type1; + } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { + /* XXX: test function pointer compatibility */ + type = bt1 == VT_FUNC ? type1 : type2; + } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { + /* XXX: test structure compatibility */ + type = bt1 == VT_STRUCT ? type1 : type2; + } 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); + if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + rc = RC_INT; + if (is_float(type.t)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((type.t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } 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); + if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + r1 = gv(rc); + move_reg(r2, r1, type.t); + vtop->r = r2; + gsym(tt); + } + } +} + +static void expr_eq(void) +{ + int t; + + expr_cond(); + 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(); + } +} + +ST_FUNC 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_cond(); + const_wanted = a; +} + +/* parse an integer constant and return its value. */ +ST_FUNC 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 label_or_decl(int l) +{ + int last_tok; + + /* fast test first */ + if (tok >= TOK_UIDENT) + { + /* no need to save tokc because tok is an identifier */ + last_tok = tok; + next(); + if (tok == ':') { + unget_tok(last_tok); + return; + } + unget_tok(last_tok); + } + decl(l); +} + +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, *frame_bottom; + + /* generate line number info */ + if (tcc_state->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 = gvtst(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; + vla_sp_restore(); + skip('('); + gexpr(); + skip(')'); + a = gvtst(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; + int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope; + + next(); + /* record local declaration stack position */ + s = local_stack; + frame_bottom = sym_push2(&local_stack, SYM_FIELD, 0, 0); + frame_bottom->next = scope_stack_bottom; + scope_stack_bottom = frame_bottom; + 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 != '}') { + label_or_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); + if(is_expr) { + /* XXX: this solution makes only valgrind happy... + triggered by gcc.c-torture/execute/20000917-1.c */ + Sym *p; + switch(vtop->type.t & VT_BTYPE) { + /* case VT_PTR: */ + /* this breaks a compilation of the linux kernel v2.4.26 */ + /* pmd_t *new = ({ __asm__ __volatile__("ud2\n") ; ((pmd_t *)1); }); */ + /* Look a commit a80acab: Display error on statement expressions with complex return type */ + /* A pointer is not a complex return type */ + case VT_STRUCT: + case VT_ENUM: + case VT_FUNC: + for(p=vtop->type.ref;p;p=p->prev) + if(p->prev==s) + tcc_error("unsupported expression type"); + } + } + /* pop locally defined symbols */ + scope_stack_bottom = scope_stack_bottom->next; + sym_pop(&local_stack, s); + + /* Pop VLA frames and restore stack pointer if required */ + if (vlas_in_scope > saved_vlas_in_scope) { + vla_sp_loc = saved_vlas_in_scope ? block_vla_sp_loc : vla_sp_root_loc; + vla_sp_restore(); + } + vlas_in_scope = saved_vlas_in_scope; + + next(); + } else if (tok == TOK_RETURN) { + next(); + if (tok != ';') { + gexpr(); + gen_assign_cast(&func_vt); +#ifdef TCC_TARGET_ARM64 + // Perhaps it would be better to use this for all backends: + greturn(); +#else + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + CType type, ret_type; + int ret_align, ret_nregs, regsize; + RegArgs args; + + gfunc_sret(&func_vt, func_var, &ret_type, + &ret_align, ®size, &args); + ret_nregs = regargs_nregs(&args); + if (0 == ret_nregs) { + /* 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 { + /* returning structure packed into registers */ + int r, size, addr, align; +#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE) + int i; +#endif + size = type_size(&func_vt,&align); + if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) + && (align & (ret_align-1))) { + loc = (loc - size) & -ret_align; + addr = loc; + type = func_vt; + vset(&type, VT_LOCAL | VT_LVAL, addr); + vswap(); + vstore(); + vpop(); + vset(&ret_type, VT_LOCAL | VT_LVAL, addr); + } + vtop->type = ret_type; +#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE) + for (i=0; i<REG_ARGS_MAX; i++) { + int off = args.ireg[i]; + + if (off == -1) + break; + + r = i ? RC_LRET : RC_IRET; + + vdup(); + vtop->c.i += off; + vtop->type.t = VT_LLONG; + gv(r); + vpop(); + } + for (i=0; i<REG_ARGS_MAX; i++) { + int off = args.freg[i]; + + if (off == -1) + break; + + /* We assume that when a structure is returned in multiple + registers, their classes are consecutive values of the + suite s(n) = 2^n */ + r = rc_fret(ret_type.t) << i; + + vdup(); + vtop->c.i += off; + vtop->type.t = VT_DOUBLE; + gv(r); + vpop(); + } +#else + if (is_float(ret_type.t)) + r = rc_fret(ret_type.t); + else + r = RC_IRET; + + for (;;) { + gv(r); + if (--ret_nregs == 0) + break; + /* We assume that when a structure is returned in multiple + registers, their classes are consecutive values of the + suite s(n) = 2^n */ + r <<= 1; + vtop->c.i += regsize; + vtop->r = VT_LOCAL | VT_LVAL; + } +#endif + } + } else if (is_float(func_vt.t)) { + gv(rc_fret(func_vt.t)); + } else { + gv(RC_IRET); + } +#endif + 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) + tcc_error("cannot break"); + *bsym = gjmp(*bsym); + next(); + skip(';'); + } else if (tok == TOK_CONTINUE) { + /* compute jump */ + if (!csym) + tcc_error("cannot continue"); + vla_sp_restore_root(); + *csym = gjmp(*csym); + next(); + skip(';'); + } else if (tok == TOK_FOR) { + int e; + next(); + skip('('); + s = local_stack; + frame_bottom = sym_push2(&local_stack, SYM_FIELD, 0, 0); + frame_bottom->next = scope_stack_bottom; + scope_stack_bottom = frame_bottom; + if (tok != ';') { + /* c99 for-loop init decl? */ + if (!decl0(VT_LOCAL, 1)) { + /* no, regular for-loop init expr */ + gexpr(); + vpop(); + } + } + skip(';'); + d = ind; + c = ind; + vla_sp_restore(); + a = 0; + b = 0; + if (tok != ';') { + gexpr(); + a = gvtst(1, 0); + } + skip(';'); + if (tok != ')') { + e = gjmp(0); + c = ind; + vla_sp_restore(); + 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); + scope_stack_bottom = scope_stack_bottom->next; + sym_pop(&local_stack, s); + } else + if (tok == TOK_DO) { + next(); + a = 0; + b = 0; + d = ind; + vla_sp_restore(); + block(&a, &b, case_sym, def_sym, case_reg, 0); + skip(TOK_WHILE); + skip('('); + gsym(b); + gexpr(); + c = gvtst(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) + tcc_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); + vdup(); + 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); + } + case_reg = gv(RC_INT); + vpop(); + 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) + tcc_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; + } + vla_sp_restore_root(); + if (s->r & LABEL_FORWARD) + s->jnext = gjmp(s->jnext); + else + gjmp_addr(s->jnext); + 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) + tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL)); + gsym(s->jnext); + s->r = LABEL_DEFINED; + } else { + s = label_push(&global_label_stack, b, LABEL_DEFINED); + } + s->jnext = ind; + vla_sp_restore(); + /* we accept this, but it is a mistake */ + block_after_label: + if (tok == '}') { + tcc_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) + tcc_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) + tcc_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) + tcc_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; + /* we'll write at most 16 bytes */ + if (c + 16 > sec->data_allocated) { + section_realloc(sec, c + 16); + } + 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))) + tcc_error("initializer element is not computable at load time"); + switch(bt) { + /* XXX: when cross-compiling we assume that each type has the + same representation on host and target, which is likely to + be wrong in the case of long double */ + case VT_BOOL: + vtop->c.i = (vtop->c.i != 0); + 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; + case VT_PTR: { + addr_t val = (vtop->c.ptr_offset & bit_mask) << bit_pos; +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + if (vtop->r & VT_SYM) + greloca(sec, vtop->sym, c, R_DATA_PTR, val); + else + *(addr_t *)ptr |= val; +#else + if (vtop->r & VT_SYM) + greloc(sec, vtop->sym, c, R_DATA_PTR); + *(addr_t *)ptr |= val; +#endif + break; + } + default: { + int val = (vtop->c.i & bit_mask) << bit_pos; +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + if (vtop->r & VT_SYM) + greloca(sec, vtop->sym, c, R_DATA_PTR, val); + else + *(int *)ptr |= val; +#else + if (vtop->r & VT_SYM) + greloc(sec, vtop->sym, c, R_DATA_PTR); + *(int *)ptr |= val; +#endif + break; + } + } + vtop--; + } else { + vset(&dtype, VT_LOCAL|VT_LVAL, 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); +#ifdef TCC_TARGET_ARM + vpushs(size); + vpushi(0); +#else + vpushi(0); + vpushs(size); +#endif + 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, parlevel1, i; + int size1, align1, expr_type; + Sym *s, *f; + CType *t1; + + if (type->t & VT_VLA) { + int a; + + /* save current stack pointer */ + if (vlas_in_scope == 0) { + if (vla_sp_root_loc == -1) + vla_sp_root_loc = (loc -= PTR_SIZE); + gen_vla_sp_save(vla_sp_root_loc); + } + + vla_runtime_type_size(type, &a); + gen_vla_alloc(type, a); + gen_vla_sp_save(c); + vla_sp_loc = c; + vlas_in_scope++; + } else 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 == '{') { + if (tok != '{') + tcc_error("character array initializer must be a literal," + " optionally enclosed in braces"); + skip('{'); + no_oblock = 0; + } + + /* only parse strings here if correct type (otherwise: handle + them as ((w)char *) expressions */ + if ((tok == TOK_LSTR && +#ifdef TCC_TARGET_PE + (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED) +#else + (t1->t & VT_BTYPE) == VT_INT +#endif + ) || (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(nwchar_t); + cstr_len--; + nb = cstr_len; + if (n >= 0 && nb > (n - array_length)) + nb = n - array_length; + if (!size_only) { + if (cstr_len > nb) + tcc_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 = ((nwchar_t *)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) + tcc_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 == '{')) { + + /* NOTE: the previous test is a specific case for automatic + struct/union init */ + /* XXX: union needs only one init */ + + int par_count = 0; + if (tok == '(') { + AttributeDef ad1; + CType type1; + next(); + if (tcc_state->old_struct_init_code) { + /* an old version of struct initialization. + It have a problems. But with a new version + linux 2.4.26 can't load ramdisk. + */ + 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)) + tcc_error("invalid type for cast"); + #endif + skip(')'); + } + else + { + if (tok != '(') { + if (!parse_btype(&type1, &ad1)) + expect("cast"); + type_decl(&type1, &ad1, &n, TYPE_ABSTRACT); + #if 0 + if (!is_assignable_types(type, &type1)) + tcc_error("invalid type for cast"); + #endif + skip(')'); + } else + unget_tok(tok); + } + } + + 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; + + /* gr: skip fields from same union - ugly. */ + while (f->next) { + int align = 0; + int f_size = type_size(&f->type, &align); + int f_type = (f->type.t & VT_BTYPE); + + ///printf("index: %2d %08x -- %2d %08x\n", f->c, f->type.t, f->next->c, f->next->type.t); + /* test for same offset */ + if (f->next->c != f->c) + break; + if ((f_type == VT_STRUCT) && (f_size == 0)) { + /* + Lets assume a structure of size 0 can't be a member of the union. + This allow to compile the following code from a linux kernel v2.4.26 + typedef struct { } rwlock_t; + struct fs_struct { + int count; + rwlock_t lock; + int umask; + }; + struct fs_struct init_fs = { { (1) }, (rwlock_t) {}, 0022, }; + tcc-0.9.23 can succesfully compile this version of the kernel. + gcc don't have problems with this code too. + */ + break; + } + /* if yes, test for bitfield shift */ + if ((f->type.t & VT_BITFIELD) && (f->next->type.t & VT_BITFIELD)) { + int bit_pos_1 = (f->type.t >> VT_STRUCT_SHIFT) & 0x3f; + int bit_pos_2 = (f->next->type.t >> VT_STRUCT_SHIFT) & 0x3f; + //printf("bitfield %d %d\n", bit_pos_1, bit_pos_2); + if (bit_pos_1 != bit_pos_2) + break; + } + f = f->next; + } + + 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 = parlevel1 = 0; + while ((parlevel > 0 || parlevel1 > 0 || + (tok != '}' && tok != ',')) && tok != -1) { + if (tok == '(') + parlevel++; + else if (tok == ')') { + if (parlevel == 0 && parlevel1 == 0) + break; + parlevel--; + } + else if (tok == '{') + parlevel1++; + else if (tok == '}') { + if (parlevel == 0 && parlevel1 == 0) + break; + parlevel1--; + } + 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' with an associated name represented by 'asm_label' 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, char *asm_label, + int scope) +{ + int size, align, addr, data_offset; + int level; + ParseState saved_parse_state = {0}; + TokenString init_str; + Section *sec; + Sym *flexible_array; + + flexible_array = NULL; + if ((type->t & VT_BTYPE) == VT_STRUCT) { + Sym *field = type->ref->next; + if (field) { + while (field->next) + field = field->next; + if (field->type.t & VT_ARRAY && field->type.ref->c < 0) + flexible_array = field; + } + } + + 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 || (flexible_array && has_init)) { + if (!has_init) + tcc_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) + tcc_error("unexpected end of file in initializer"); + tok_str_add_tok(&init_str); + if (tok == '{') + level++; + else if (tok == '}') { + level--; + if (level <= 0) { + next(); + break; + } + } + next(); + } + } + tok_str_add(&init_str, -1); + tok_str_add(&init_str, 0); + + /* compute size */ + save_parse_state(&saved_parse_state); + + begin_macro(&init_str, 0); + 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) + tcc_error("unknown type size"); + } + if (flexible_array) + size += flexible_array->type.ref->c * pointed_size(&flexible_array->type); + /* take into account specified alignment if bigger */ + if (ad->a.aligned) { + if (ad->a.aligned > align) + align = ad->a.aligned; + } else if (ad->a.packed) { + align = 1; + } + if ((r & VT_VALMASK) == VT_LOCAL) { + sec = NULL; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) { + loc--; + } +#endif + loc = (loc - size) & -align; + addr = loc; +#ifdef CONFIG_TCC_BCHECK + /* handles bounds */ + /* XXX: currently, since we do only one pass, we cannot track + '&' operators, so we add only arrays */ + if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) { + addr_t *bounds_ptr; + /* add padding between regions */ + loc--; + /* then add local bound info */ + bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t)); + bounds_ptr[0] = addr; + bounds_ptr[1] = size; + } +#endif + 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)) + tcc_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 omitted 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; +#ifdef CONFIG_TCC_BCHECK + /* add padding if bound check */ + if (tcc_state->do_bounds_check) + data_offset++; +#endif + 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 || !sym) { + sym = sym_push(v, type, r | VT_SYM, 0); + sym->asm_label = asm_label; + } + /* update symbol definition */ + if (sec) { + put_extern_sym(sym, sec, addr, size); + } else { + ElfW(Sym) *esym; + /* put a common area */ + put_extern_sym(sym, NULL, align, size); + /* XXX: find a nicer way */ + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + esym->st_shndx = SHN_COMMON; + } + } else { + /* push global reference */ + sym = get_sym_ref(type, sec, addr, size); + vpushsym(type, sym); + } + /* patch symbol weakness */ + if (type->t & VT_WEAK) + weaken_symbol(sym); + apply_visibility(sym, type); +#ifdef CONFIG_TCC_BCHECK + /* handles bounds now because the symbol must be defined + before for the relocation */ + if (tcc_state->do_bounds_check) { + addr_t *bounds_ptr; + + greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_PTR); + /* then add global bound info */ + bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t)); + bounds_ptr[0] = 0; /* relocated */ + bounds_ptr[1] = size; + } +#endif + } + if (has_init || (type->t & VT_VLA)) { + decl_initializer(type, sec, addr, 1, 0); + /* restore parse state if needed */ + if (init_str.str) { + end_macro(); + restore_parse_state(&saved_parse_state); + } + /* patch flexible array member size back to -1, */ + /* for possible subsequent similar declarations */ + if (flexible_array) + flexible_array->type.ref->c = -1; + } + no_alloc: ; +} + +static 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); + /* //gr gdb wants a line at the function */ + put_stabn(N_SLINE, 0, file->line_num, 0); + 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 && + tok != TOK_ASM1 && tok != TOK_ASM2 && tok != TOK_ASM3) { + 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; + } + tcc_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) + tcc_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) +{ + int saved_nocode_wanted = nocode_wanted; + + nocode_wanted = 0; + 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; + /* Initialize VLA state */ + vla_sp_loc = -1; + vla_sp_root_loc = -1; + /* put debug symbol */ + if (tcc_state->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); +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && !strcmp(funcname, "main")) { + int i; + Sym *sym; + for (i = 0, sym = local_stack; i < 2; i++, sym = sym->prev) { + if (sym->v & SYM_FIELD || sym->prev->v & SYM_FIELD) + break; + vpush_global_sym(&func_old_type, TOK___bound_main_arg); + vset(&sym->type, sym->r, sym->c); + gfunc_call(1); + } + } +#endif + 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); + /* reset local stack */ + scope_stack_bottom = NULL; + sym_pop(&local_stack, NULL); + /* end of function */ + /* patch symbol size */ + ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = + ind - func_ind; + /* patch symbol weakness (this definition overrules any prototype) */ + if (sym->type.t & VT_WEAK) + weaken_symbol(sym); + apply_visibility(sym, &sym->type); + if (tcc_state->do_debug) { + put_stabn(N_FUN, 0, 0, ind - func_ind); + } + /* It's better to crash than to generate wrong code */ + cur_text_section = NULL; + funcname = ""; /* for safety */ + func_vt.t = VT_VOID; /* for safety */ + func_var = 0; /* for safety */ + ind = 0; /* for safety */ + nocode_wanted = saved_nocode_wanted; + check_vstack(); +} + +ST_FUNC void gen_inline_functions(void) +{ + Sym *sym; + int inline_generated, i, ln; + struct InlineFunc *fn; + + ln = file->line_num; + /* iterate while inline function are referenced */ + for(;;) { + inline_generated = 0; + for (i = 0; i < tcc_state->nb_inline_fns; ++i) { + fn = tcc_state->inline_fns[i]; + sym = fn->sym; + if (sym && sym->c) { + /* the function was used: generate its code and + convert it to a normal function */ + fn->sym = NULL; + if (file) + pstrcpy(file->filename, sizeof file->filename, fn->filename); + sym->r = VT_SYM | VT_CONST; + sym->type.t &= ~VT_INLINE; + + begin_macro(&fn->func_str, 0); + next(); + cur_text_section = text_section; + gen_function(sym); + end_macro(); + + inline_generated = 1; + } + } + if (!inline_generated) + break; + } + file->line_num = ln; + /* free tokens of unused inline functions */ + for (i = 0; i < tcc_state->nb_inline_fns; ++i) { + fn = tcc_state->inline_fns[i]; + if (fn->sym) + tok_str_free(fn->func_str.str); + } + dynarray_reset(&tcc_state->inline_fns, &tcc_state->nb_inline_fns); +} + +/* 'l' is VT_LOCAL or VT_CONST to define default storage type */ +static int decl0(int l, int is_for_loop_init) +{ + int v, has_init, r; + CType type, btype; + Sym *sym; + AttributeDef ad; + + while (1) { + if (!parse_btype(&btype, &ad)) { + if (is_for_loop_init) + return 0; + /* 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 == ';') { + if ((btype.t & VT_BTYPE) == VT_STRUCT) { + int v = btype.ref->v; + if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM) + tcc_warning("unnamed struct/union that defines no instances"); + } + next(); + continue; + } + while (1) { /* iterate thru each declaration */ + char *asm_label; // associated asm label + 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 ((type.t & VT_STATIC) && (l == VT_LOCAL)) { + tcc_error("function without file scope cannot be static"); + } + /* if old style function prototype, we accept a + declaration list */ + sym = type.ref; + if (sym->c == FUNC_OLD) + func_decl_list(sym); + } + + asm_label = NULL; + if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) { + CString astr; + + asm_label_instr(&astr); + asm_label = tcc_strdup(astr.data); + cstr_free(&astr); + + /* parse one last attribute list, after asm label */ + parse_attribute(&ad); + } + + if (ad.a.weak) + type.t |= VT_WEAK; +#ifdef TCC_TARGET_PE + if (ad.a.func_import) + type.t |= VT_IMPORT; + if (ad.a.func_export) + type.t |= VT_EXPORT; +#endif + type.t |= ad.a.visibility << VT_VIS_SHIFT; + + if (tok == '{') { + if (l == VT_LOCAL) + tcc_error("cannot use local functions"); + if ((type.t & VT_BTYPE) != 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) { + Sym *ref; + if ((sym->type.t & VT_BTYPE) != VT_FUNC) + goto func_error1; + + ref = sym->type.ref; + if (0 == ref->a.func_proto) + tcc_error("redefinition of '%s'", get_tok_str(v, NULL)); + + /* use func_call from prototype if not defined */ + if (ref->a.func_call != FUNC_CDECL + && type.ref->a.func_call == FUNC_CDECL) + type.ref->a.func_call = ref->a.func_call; + + /* use export from prototype */ + if (ref->a.func_export) + type.ref->a.func_export = 1; + + /* use static from prototype */ + if (sym->type.t & VT_STATIC) + type.t = (type.t & ~VT_EXTERN) | VT_STATIC; + + /* If the definition has no visibility use the + one from prototype. */ + if (! (type.t & VT_VIS_MASK)) + type.t |= sym->type.t & VT_VIS_MASK; + + if (!is_compatible_types(&sym->type, &type)) { + func_error1: + tcc_error("incompatible types for redefinition of '%s'", + get_tok_str(v, NULL)); + } + type.ref->a.func_proto = 0; + /* 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)) { + int block_level; + struct InlineFunc *fn; + const char *filename; + + filename = file ? file->filename : ""; + fn = tcc_malloc(sizeof *fn + strlen(filename)); + strcpy(fn->filename, filename); + fn->sym = sym; + tok_str_new(&fn->func_str); + + block_level = 0; + for(;;) { + int t; + if (tok == TOK_EOF) + tcc_error("unexpected end of file"); + tok_str_add_tok(&fn->func_str); + t = tok; + next(); + if (t == '{') { + block_level++; + } else if (t == '}') { + block_level--; + if (block_level == 0) + break; + } + } + tok_str_add(&fn->func_str, -1); + tok_str_add(&fn->func_str, 0); + dynarray_add((void ***)&tcc_state->inline_fns, &tcc_state->nb_inline_fns, fn); + + } 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); + } + break; + } else { + if (btype.t & VT_TYPEDEF) { + /* save typedefed type */ + /* XXX: test storage specifiers ? */ + sym = sym_push(v, &type, 0, 0); + sym->a = ad.a; + sym->type.t |= VT_TYPEDEF; + } else { + r = 0; + if ((type.t & VT_BTYPE) == VT_FUNC) { + /* external function definition */ + /* specific case for func_call attribute */ + ad.a.func_proto = 1; + type.ref->a = ad.a; + } else if (!(type.t & VT_ARRAY)) { + /* not lvalue if array */ + r |= lvalue_type(type.t); + } + has_init = (tok == '='); + if (has_init && (type.t & VT_VLA)) + tcc_error("Variable length array cannot be initialized"); + if ((btype.t & VT_EXTERN) || ((type.t & VT_BTYPE) == VT_FUNC) || + ((type.t & VT_ARRAY) && (type.t & VT_STATIC) && + !has_init && l == VT_CONST && type.ref->c < 0)) { + /* external variable or function */ + /* NOTE: as GCC, uninitialized global static + arrays of null size are considered as + extern */ + sym = external_sym(v, &type, r, asm_label); + + if (ad.alias_target) { + Section tsec; + Elf32_Sym *esym; + Sym *alias_target; + + alias_target = sym_find(ad.alias_target); + if (!alias_target || !alias_target->c) + tcc_error("unsupported forward __alias__ attribute"); + esym = &((Elf32_Sym *)symtab_section->data)[alias_target->c]; + tsec.sh_num = esym->st_shndx; + put_extern_sym2(sym, &tsec, esym->st_value, esym->st_size, 0); + } + } else { + type.t |= (btype.t & VT_STATIC); /* Retain "static". */ + if (type.t & VT_STATIC) + r |= VT_CONST; + else + r |= l; + if (has_init) + next(); + decl_initializer_alloc(&type, &ad, r, has_init, v, asm_label, l); + } + } + if (tok != ',') { + if (is_for_loop_init) + return 1; + skip(';'); + break; + } + next(); + } + ad.a.aligned = 0; + } + } + return 0; +} + +ST_FUNC void decl(int l) +{ + decl0(l, 0); +} diff --git a/src/tcclib.h b/src/tcclib.h new file mode 100644 index 0000000..13f2204 --- /dev/null +++ b/src/tcclib.h @@ -0,0 +1,80 @@ +/* Simple libc header for TCC + * + * Add any function you want from the libc there. This file is here + * only for your convenience so that you do not need to put the whole + * glibc include files on your floppy disk + */ +#ifndef _TCCLIB_H +#define _TCCLIB_H + +#include <stddef.h> +#include <stdarg.h> + +/* stdlib.h */ +void *calloc(size_t nmemb, size_t size); +void *malloc(size_t size); +void free(void *ptr); +void *realloc(void *ptr, size_t size); +int atoi(const char *nptr); +long int strtol(const char *nptr, char **endptr, int base); +unsigned long int strtoul(const char *nptr, char **endptr, int base); +void exit(int); + +/* stdio.h */ +typedef struct __FILE FILE; +#define EOF (-1) +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; +FILE *fopen(const char *path, const char *mode); +FILE *fdopen(int fildes, const char *mode); +FILE *freopen(const char *path, const char *mode, FILE *stream); +int fclose(FILE *stream); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); +int fgetc(FILE *stream); +char *fgets(char *s, int size, FILE *stream); +int getc(FILE *stream); +int getchar(void); +char *gets(char *s); +int ungetc(int c, FILE *stream); +int fflush(FILE *stream); +int putchar (int c); + +int printf(const char *format, ...); +int fprintf(FILE *stream, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); +int asprintf(char **strp, const char *format, ...); +int dprintf(int fd, const char *format, ...); +int vprintf(const char *format, va_list ap); +int vfprintf(FILE *stream, const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int vasprintf(char **strp, const char *format, va_list ap); +int vdprintf(int fd, const char *format, va_list ap); + +void perror(const char *s); + +/* string.h */ +char *strcat(char *dest, const char *src); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strcpy(char *dest, const char *src); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *strdup(const char *s); +size_t strlen(const char *s); + +/* dlfcn.h */ +#define RTLD_LAZY 0x001 +#define RTLD_NOW 0x002 +#define RTLD_GLOBAL 0x100 + +void *dlopen(const char *filename, int flag); +const char *dlerror(void); +void *dlsym(void *handle, char *symbol); +int dlclose(void *handle); + +#endif /* _TCCLIB_H */ diff --git a/src/tccpe.c b/src/tccpe.c new file mode 100644 index 0000000..479d084 --- /dev/null +++ b/src/tccpe.c @@ -0,0 +1,1890 @@ +/* + * TCCPE.C - PE file output for the Tiny C Compiler + * + * Copyright (c) 2005-2007 grischka + * + * 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 + */ + +#include "tcc.h" + +#ifndef _WIN32 +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +#define PE_MERGE_DATA +/* #define PE_PRINT_SECTIONS */ + +#ifdef TCC_TARGET_X86_64 +# define ADDR3264 ULONGLONG +# define REL_TYPE_DIRECT R_X86_64_64 +# define R_XXX_THUNKFIX R_X86_64_PC32 +# define R_XXX_RELATIVE R_X86_64_RELATIVE +# define IMAGE_FILE_MACHINE 0x8664 +# define RSRC_RELTYPE 3 + +#elif defined TCC_TARGET_ARM +# define ADDR3264 DWORD +# define REL_TYPE_DIRECT R_ARM_ABS32 +# define R_XXX_THUNKFIX R_ARM_ABS32 +# define R_XXX_RELATIVE R_ARM_RELATIVE +# define IMAGE_FILE_MACHINE 0x01C0 +# define RSRC_RELTYPE 7 /* ??? (not tested) */ + +#elif defined TCC_TARGET_I386 +# define ADDR3264 DWORD +# define REL_TYPE_DIRECT R_386_32 +# define R_XXX_THUNKFIX R_386_32 +# define R_XXX_RELATIVE R_386_RELATIVE +# define IMAGE_FILE_MACHINE 0x014C +# define RSRC_RELTYPE 7 /* DIR32NB */ + +#endif + +#ifdef _WIN32 +void dbg_printf (const char *fmt, ...) +{ + char buffer[4000]; + va_list arg; + int x; + va_start(arg, fmt); + x = vsprintf (buffer, fmt, arg); + strcpy(buffer+x, "\n"); + OutputDebugString(buffer); +} +#endif + +/* ----------------------------------------------------------- */ +#ifndef IMAGE_NT_SIGNATURE +/* ----------------------------------------------------------- */ +/* definitions below are from winnt.h */ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef unsigned long long ULONGLONG; +#pragma pack(push, 1) + +typedef struct _IMAGE_DOS_HEADER { /* DOS .EXE header */ + WORD e_magic; /* Magic number */ + WORD e_cblp; /* Bytes on last page of file */ + WORD e_cp; /* Pages in file */ + WORD e_crlc; /* Relocations */ + WORD e_cparhdr; /* Size of header in paragraphs */ + WORD e_minalloc; /* Minimum extra paragraphs needed */ + WORD e_maxalloc; /* Maximum extra paragraphs needed */ + WORD e_ss; /* Initial (relative) SS value */ + WORD e_sp; /* Initial SP value */ + WORD e_csum; /* Checksum */ + WORD e_ip; /* Initial IP value */ + WORD e_cs; /* Initial (relative) CS value */ + WORD e_lfarlc; /* File address of relocation table */ + WORD e_ovno; /* Overlay number */ + WORD e_res[4]; /* Reserved words */ + WORD e_oemid; /* OEM identifier (for e_oeminfo) */ + WORD e_oeminfo; /* OEM information; e_oemid specific */ + WORD e_res2[10]; /* Reserved words */ + DWORD e_lfanew; /* File address of new exe header */ +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ +#define SIZE_OF_NT_SIGNATURE 4 + +typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + + +#define IMAGE_SIZEOF_FILE_HEADER 20 + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + + +typedef struct _IMAGE_OPTIONAL_HEADER { + /* Standard fields. */ + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; +#ifndef TCC_TARGET_X86_64 + DWORD BaseOfData; +#endif + /* NT additional fields. */ + ADDR3264 ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + ADDR3264 SizeOfStackReserve; + ADDR3264 SizeOfStackCommit; + ADDR3264 SizeOfHeapReserve; + ADDR3264 SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, IMAGE_OPTIONAL_HEADER; + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */ +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */ +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */ +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */ +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */ +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */ +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */ +/* IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 (X86 usage) */ +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 /* Architecture Specific Data */ +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* RVA of GP */ +#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */ +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */ +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */ +#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 /* Delay Load Import Descriptors */ +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 /* COM Runtime descriptor */ + +/* Section header format. */ +#define IMAGE_SIZEOF_SHORT_NAME 8 + +typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; + +#define IMAGE_SIZEOF_SECTION_HEADER 40 + +typedef struct _IMAGE_EXPORT_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Name; + DWORD Base; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; + DWORD AddressOfNames; + DWORD AddressOfNameOrdinals; +} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; + +typedef struct _IMAGE_IMPORT_DESCRIPTOR { + union { + DWORD Characteristics; + DWORD OriginalFirstThunk; + }; + DWORD TimeDateStamp; + DWORD ForwarderChain; + DWORD Name; + DWORD FirstThunk; +} IMAGE_IMPORT_DESCRIPTOR; + +typedef struct _IMAGE_BASE_RELOCATION { + DWORD VirtualAddress; + DWORD SizeOfBlock; +// WORD TypeOffset[1]; +} IMAGE_BASE_RELOCATION; + +#define IMAGE_SIZEOF_BASE_RELOCATION 8 + +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL32 7 + +#pragma pack(pop) + +/* ----------------------------------------------------------- */ +#endif /* ndef IMAGE_NT_SIGNATURE */ +/* ----------------------------------------------------------- */ +#pragma pack(push, 1) + +struct pe_header +{ + IMAGE_DOS_HEADER doshdr; + BYTE dosstub[0x40]; + DWORD nt_sig; + IMAGE_FILE_HEADER filehdr; +#ifdef TCC_TARGET_X86_64 + IMAGE_OPTIONAL_HEADER64 opthdr; +#else +#ifdef _WIN64 + IMAGE_OPTIONAL_HEADER32 opthdr; +#else + IMAGE_OPTIONAL_HEADER opthdr; +#endif +#endif +}; + +struct pe_reloc_header { + DWORD offset; + DWORD size; +}; + +struct pe_rsrc_header { + struct _IMAGE_FILE_HEADER filehdr; + struct _IMAGE_SECTION_HEADER sectionhdr; +}; + +struct pe_rsrc_reloc { + DWORD offset; + DWORD size; + WORD type; +}; + +#pragma pack(pop) + +/* ------------------------------------------------------------- */ +/* internal temporary structures */ + +/* +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 +*/ + +enum { + sec_text = 0, + sec_data , + sec_bss , + sec_idata , + sec_pdata , + sec_other , + sec_rsrc , + sec_stab , + sec_reloc , + sec_last +}; + +static const DWORD pe_sec_flags[] = { + 0x60000020, /* ".text" , */ + 0xC0000040, /* ".data" , */ + 0xC0000080, /* ".bss" , */ + 0x40000040, /* ".idata" , */ + 0x40000040, /* ".pdata" , */ + 0xE0000060, /* < other > , */ + 0x40000040, /* ".rsrc" , */ + 0x42000802, /* ".stab" , */ + 0x42000040, /* ".reloc" , */ +}; + +struct section_info { + int cls, ord; + char name[32]; + DWORD sh_addr; + DWORD sh_size; + DWORD sh_flags; + unsigned char *data; + DWORD data_size; + IMAGE_SECTION_HEADER ish; +}; + +struct import_symbol { + int sym_index; + int iat_index; + int thk_offset; +}; + +struct pe_import_info { + int dll_index; + int sym_count; + struct import_symbol **symbols; +}; + +struct pe_info { + TCCState *s1; + Section *reloc; + Section *thunk; + const char *filename; + int type; + DWORD sizeofheaders; + ADDR3264 imagebase; + DWORD start_addr; + DWORD imp_offs; + DWORD imp_size; + DWORD iat_offs; + DWORD iat_size; + DWORD exp_offs; + DWORD exp_size; + int subsystem; + DWORD section_align; + DWORD file_align; + struct section_info *sec_info; + int sec_count; + struct pe_import_info **imp_info; + int imp_count; +}; + +#define PE_NUL 0 +#define PE_DLL 1 +#define PE_GUI 2 +#define PE_EXE 3 +#define PE_RUN 4 + +/* --------------------------------------------*/ + +static const char *pe_export_name(TCCState *s1, ElfW(Sym) *sym) +{ + const char *name = symtab_section->link->data + sym->st_name; + if (s1->leading_underscore && name[0] == '_' && !(sym->st_other & ST_PE_STDCALL)) + return name + 1; + return name; +} + +static int pe_find_import(TCCState * s1, ElfW(Sym) *sym) +{ + char buffer[200]; + const char *s, *p; + int sym_index = 0, n = 0; + + do { + s = pe_export_name(s1, sym); + if (n) { + /* second try: */ + if (sym->st_other & ST_PE_STDCALL) { + /* try w/0 stdcall deco (windows API convention) */ + p = strrchr(s, '@'); + if (!p || s[0] != '_') + break; + strcpy(buffer, s+1)[p-s-1] = 0; + } else if (s[0] != '_') { /* try non-ansi function */ + buffer[0] = '_', strcpy(buffer + 1, s); + } else if (0 == memcmp(s, "__imp__", 7)) { /* mingw 2.0 */ + strcpy(buffer, s + 6); + } else if (0 == memcmp(s, "_imp___", 7)) { /* mingw 3.7 */ + strcpy(buffer, s + 6); + } else { + break; + } + s = buffer; + } + sym_index = find_elf_sym(s1->dynsymtab_section, s); + // printf("find (%d) %d %s\n", n, sym_index, s); + } while (0 == sym_index && ++n < 2); + return sym_index; +} + +/*----------------------------------------------------------------------------*/ + +static int dynarray_assoc(void **pp, int n, int key) +{ + int i; + for (i = 0; i < n; ++i, ++pp) + if (key == **(int **) pp) + return i; + return -1; +} + +#if 0 +ST_FN DWORD umin(DWORD a, DWORD b) +{ + return a < b ? a : b; +} +#endif + +static DWORD umax(DWORD a, DWORD b) +{ + return a < b ? b : a; +} + +static DWORD pe_file_align(struct pe_info *pe, DWORD n) +{ + return (n + (pe->file_align - 1)) & ~(pe->file_align - 1); +} + +static DWORD pe_virtual_align(struct pe_info *pe, DWORD n) +{ + return (n + (pe->section_align - 1)) & ~(pe->section_align - 1); +} + +static void pe_align_section(Section *s, int a) +{ + int i = s->data_offset & (a-1); + if (i) + section_ptr_add(s, a - i); +} + +static void pe_set_datadir(struct pe_header *hdr, int dir, DWORD addr, DWORD size) +{ + hdr->opthdr.DataDirectory[dir].VirtualAddress = addr; + hdr->opthdr.DataDirectory[dir].Size = size; +} + +static int pe_fwrite(void *data, unsigned len, FILE *fp, DWORD *psum) +{ + if (psum) { + DWORD sum = *psum; + WORD *p = data; + int i; + for (i = len; i > 0; i -= 2) { + sum += (i >= 2) ? *p++ : *(BYTE*)p; + sum = (sum + (sum >> 16)) & 0xFFFF; + } + *psum = sum; + } + return len == fwrite(data, 1, len, fp) ? 0 : -1; +} + +static void pe_fpad(FILE *fp, DWORD new_pos) +{ + DWORD pos = ftell(fp); + while (++pos <= new_pos) + fputc(0, fp); +} + +/*----------------------------------------------------------------------------*/ +static int pe_write(struct pe_info *pe) +{ + static const struct pe_header pe_template = { + { + /* IMAGE_DOS_HEADER doshdr */ + 0x5A4D, /*WORD e_magic; Magic number */ + 0x0090, /*WORD e_cblp; Bytes on last page of file */ + 0x0003, /*WORD e_cp; Pages in file */ + 0x0000, /*WORD e_crlc; Relocations */ + + 0x0004, /*WORD e_cparhdr; Size of header in paragraphs */ + 0x0000, /*WORD e_minalloc; Minimum extra paragraphs needed */ + 0xFFFF, /*WORD e_maxalloc; Maximum extra paragraphs needed */ + 0x0000, /*WORD e_ss; Initial (relative) SS value */ + + 0x00B8, /*WORD e_sp; Initial SP value */ + 0x0000, /*WORD e_csum; Checksum */ + 0x0000, /*WORD e_ip; Initial IP value */ + 0x0000, /*WORD e_cs; Initial (relative) CS value */ + 0x0040, /*WORD e_lfarlc; File address of relocation table */ + 0x0000, /*WORD e_ovno; Overlay number */ + {0,0,0,0}, /*WORD e_res[4]; Reserved words */ + 0x0000, /*WORD e_oemid; OEM identifier (for e_oeminfo) */ + 0x0000, /*WORD e_oeminfo; OEM information; e_oemid specific */ + {0,0,0,0,0,0,0,0,0,0}, /*WORD e_res2[10]; Reserved words */ + 0x00000080 /*DWORD e_lfanew; File address of new exe header */ + },{ + /* BYTE dosstub[0x40] */ + /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */ + 0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68, + 0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f, + 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20, + 0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + }, + 0x00004550, /* DWORD nt_sig = IMAGE_NT_SIGNATURE */ + { + /* IMAGE_FILE_HEADER filehdr */ + IMAGE_FILE_MACHINE, /*WORD Machine; */ + 0x0003, /*WORD NumberOfSections; */ + 0x00000000, /*DWORD TimeDateStamp; */ + 0x00000000, /*DWORD PointerToSymbolTable; */ + 0x00000000, /*DWORD NumberOfSymbols; */ +#if defined(TCC_TARGET_X86_64) + 0x00F0, /*WORD SizeOfOptionalHeader; */ + 0x022F /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x222E +#elif defined(TCC_TARGET_I386) + 0x00E0, /*WORD SizeOfOptionalHeader; */ + 0x030F /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x230E +#elif defined(TCC_TARGET_ARM) + 0x00E0, /*WORD SizeOfOptionalHeader; */ + 0x010F, /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x230F +#endif +},{ + /* IMAGE_OPTIONAL_HEADER opthdr */ + /* Standard fields. */ +#ifdef TCC_TARGET_X86_64 + 0x020B, /*WORD Magic; */ +#else + 0x010B, /*WORD Magic; */ +#endif + 0x06, /*BYTE MajorLinkerVersion; */ + 0x00, /*BYTE MinorLinkerVersion; */ + 0x00000000, /*DWORD SizeOfCode; */ + 0x00000000, /*DWORD SizeOfInitializedData; */ + 0x00000000, /*DWORD SizeOfUninitializedData; */ + 0x00000000, /*DWORD AddressOfEntryPoint; */ + 0x00000000, /*DWORD BaseOfCode; */ +#ifndef TCC_TARGET_X86_64 + 0x00000000, /*DWORD BaseOfData; */ +#endif + /* NT additional fields. */ +#if defined(TCC_TARGET_ARM) + 0x00100000, /*DWORD ImageBase; */ +#else + 0x00400000, /*DWORD ImageBase; */ +#endif + 0x00001000, /*DWORD SectionAlignment; */ + 0x00000200, /*DWORD FileAlignment; */ + 0x0004, /*WORD MajorOperatingSystemVersion; */ + 0x0000, /*WORD MinorOperatingSystemVersion; */ + 0x0000, /*WORD MajorImageVersion; */ + 0x0000, /*WORD MinorImageVersion; */ + 0x0004, /*WORD MajorSubsystemVersion; */ + 0x0000, /*WORD MinorSubsystemVersion; */ + 0x00000000, /*DWORD Win32VersionValue; */ + 0x00000000, /*DWORD SizeOfImage; */ + 0x00000200, /*DWORD SizeOfHeaders; */ + 0x00000000, /*DWORD CheckSum; */ + 0x0002, /*WORD Subsystem; */ + 0x0000, /*WORD DllCharacteristics; */ + 0x00100000, /*DWORD SizeOfStackReserve; */ + 0x00001000, /*DWORD SizeOfStackCommit; */ + 0x00100000, /*DWORD SizeOfHeapReserve; */ + 0x00001000, /*DWORD SizeOfHeapCommit; */ + 0x00000000, /*DWORD LoaderFlags; */ + 0x00000010, /*DWORD NumberOfRvaAndSizes; */ + + /* IMAGE_DATA_DIRECTORY DataDirectory[16]; */ + {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, + {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} + }}; + + struct pe_header pe_header = pe_template; + + int i; + FILE *op; + DWORD file_offset, sum; + struct section_info *si; + IMAGE_SECTION_HEADER *psh; + + op = fopen(pe->filename, "wb"); + if (NULL == op) { + tcc_error_noabort("could not write '%s': %s", pe->filename, strerror(errno)); + return -1; + } + + pe->sizeofheaders = pe_file_align(pe, + sizeof (struct pe_header) + + pe->sec_count * sizeof (IMAGE_SECTION_HEADER) + ); + + file_offset = pe->sizeofheaders; + + if (2 == pe->s1->verbose) + printf("-------------------------------" + "\n virt file size section" "\n"); + for (i = 0; i < pe->sec_count; ++i) { + DWORD addr, size; + const char *sh_name; + + si = pe->sec_info + i; + sh_name = si->name; + addr = si->sh_addr - pe->imagebase; + size = si->sh_size; + psh = &si->ish; + + if (2 == pe->s1->verbose) + printf("%6x %6x %6x %s\n", + (unsigned)addr, (unsigned)file_offset, (unsigned)size, sh_name); + + switch (si->cls) { + case sec_text: + pe_header.opthdr.BaseOfCode = addr; + pe_header.opthdr.AddressOfEntryPoint = addr + pe->start_addr; + break; + + case sec_data: +#ifndef TCC_TARGET_X86_64 + pe_header.opthdr.BaseOfData = addr; +#endif + break; + + case sec_bss: + break; + + case sec_reloc: + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size); + break; + + case sec_rsrc: + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size); + break; + + case sec_pdata: + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXCEPTION, addr, size); + break; + + case sec_stab: + break; + } + + if (pe->thunk == pe->s1->sections[si->ord]) { + if (pe->imp_size) { + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IMPORT, + pe->imp_offs + addr, pe->imp_size); + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IAT, + pe->iat_offs + addr, pe->iat_size); + } + if (pe->exp_size) { + pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXPORT, + pe->exp_offs + addr, pe->exp_size); + } + } + + strncpy((char*)psh->Name, sh_name, sizeof psh->Name); + + psh->Characteristics = pe_sec_flags[si->cls]; + psh->VirtualAddress = addr; + psh->Misc.VirtualSize = size; + pe_header.opthdr.SizeOfImage = + umax(pe_virtual_align(pe, size + addr), pe_header.opthdr.SizeOfImage); + + if (si->data_size) { + psh->PointerToRawData = file_offset; + file_offset = pe_file_align(pe, file_offset + si->data_size); + psh->SizeOfRawData = file_offset - psh->PointerToRawData; + } + } + + //pe_header.filehdr.TimeDateStamp = time(NULL); + pe_header.filehdr.NumberOfSections = pe->sec_count; + pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders; + pe_header.opthdr.ImageBase = pe->imagebase; + pe_header.opthdr.Subsystem = pe->subsystem; + if (pe->s1->pe_stack_size) + pe_header.opthdr.SizeOfStackReserve = pe->s1->pe_stack_size; + if (PE_DLL == pe->type) + pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; + + sum = 0; + pe_fwrite(&pe_header, sizeof pe_header, op, &sum); + for (i = 0; i < pe->sec_count; ++i) + pe_fwrite(&pe->sec_info[i].ish, sizeof(IMAGE_SECTION_HEADER), op, &sum); + pe_fpad(op, pe->sizeofheaders); + for (i = 0; i < pe->sec_count; ++i) { + si = pe->sec_info + i; + psh = &si->ish; + if (si->data_size) { + pe_fwrite(si->data, si->data_size, op, &sum); + file_offset = psh->PointerToRawData + psh->SizeOfRawData; + pe_fpad(op, file_offset); + } + } + + pe_header.opthdr.CheckSum = sum + file_offset; + fseek(op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET); + pe_fwrite(&pe_header.opthdr.CheckSum, sizeof pe_header.opthdr.CheckSum, op, NULL); + fclose (op); + + if (2 == pe->s1->verbose) + printf("-------------------------------\n"); + if (pe->s1->verbose) + printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static struct import_symbol *pe_add_import(struct pe_info *pe, int sym_index) +{ + int i; + int dll_index; + struct pe_import_info *p; + struct import_symbol *s; + ElfW(Sym) *isym; + + isym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index; + dll_index = isym->st_size; + + i = dynarray_assoc ((void**)pe->imp_info, pe->imp_count, dll_index); + if (-1 != i) { + p = pe->imp_info[i]; + goto found_dll; + } + p = tcc_mallocz(sizeof *p); + p->dll_index = dll_index; + dynarray_add((void***)&pe->imp_info, &pe->imp_count, p); + +found_dll: + i = dynarray_assoc ((void**)p->symbols, p->sym_count, sym_index); + if (-1 != i) + return p->symbols[i]; + + s = tcc_mallocz(sizeof *s); + dynarray_add((void***)&p->symbols, &p->sym_count, s); + s->sym_index = sym_index; + return s; +} + +/*----------------------------------------------------------------------------*/ +static void pe_build_imports(struct pe_info *pe) +{ + int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i; + DWORD rva_base = pe->thunk->sh_addr - pe->imagebase; + int ndlls = pe->imp_count; + + for (sym_cnt = i = 0; i < ndlls; ++i) + sym_cnt += pe->imp_info[i]->sym_count; + + if (0 == sym_cnt) + return; + + pe_align_section(pe->thunk, 16); + + pe->imp_offs = dll_ptr = pe->thunk->data_offset; + pe->imp_size = (ndlls + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR); + pe->iat_offs = dll_ptr + pe->imp_size; + pe->iat_size = (sym_cnt + ndlls) * sizeof(ADDR3264); + section_ptr_add(pe->thunk, pe->imp_size + 2*pe->iat_size); + + thk_ptr = pe->iat_offs; + ent_ptr = pe->iat_offs + pe->iat_size; + + for (i = 0; i < pe->imp_count; ++i) { + IMAGE_IMPORT_DESCRIPTOR *hdr; + int k, n, dllindex; + ADDR3264 v; + struct pe_import_info *p = pe->imp_info[i]; + const char *name; + DLLReference *dllref; + + dllindex = p->dll_index; + if (dllindex) + name = (dllref = pe->s1->loaded_dlls[dllindex-1])->name; + else + name = "", dllref = NULL; + + /* put the dll name into the import header */ + v = put_elf_str(pe->thunk, name); + hdr = (IMAGE_IMPORT_DESCRIPTOR*)(pe->thunk->data + dll_ptr); + hdr->FirstThunk = thk_ptr + rva_base; + hdr->OriginalFirstThunk = ent_ptr + rva_base; + hdr->Name = v + rva_base; + + for (k = 0, n = p->sym_count; k <= n; ++k) { + if (k < n) { + int iat_index = p->symbols[k]->iat_index; + int sym_index = p->symbols[k]->sym_index; + ElfW(Sym) *imp_sym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index; + ElfW(Sym) *org_sym = (ElfW(Sym) *)symtab_section->data + iat_index; + const char *name = pe->s1->dynsymtab_section->link->data + imp_sym->st_name; + int ordinal; + + org_sym->st_value = thk_ptr; + org_sym->st_shndx = pe->thunk->sh_num; + + if (dllref) + v = 0, ordinal = imp_sym->st_value; /* ordinal from pe_load_def */ + else + ordinal = 0, v = imp_sym->st_value; /* address from tcc_add_symbol() */ + +#ifdef TCC_IS_NATIVE + if (pe->type == PE_RUN) { + if (dllref) { + if ( !dllref->handle ) + dllref->handle = LoadLibrary(dllref->name); + v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(LPCSTR)NULL+ordinal:name); + } + if (!v) + tcc_error_noabort("can't build symbol '%s'", name); + } else +#endif + if (ordinal) { + v = ordinal | (ADDR3264)1 << (sizeof(ADDR3264)*8 - 1); + } else { + v = pe->thunk->data_offset + rva_base; + section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */ + put_elf_str(pe->thunk, name); + } + + } else { + v = 0; /* last entry is zero */ + } + + *(ADDR3264*)(pe->thunk->data+thk_ptr) = + *(ADDR3264*)(pe->thunk->data+ent_ptr) = v; + thk_ptr += sizeof (ADDR3264); + ent_ptr += sizeof (ADDR3264); + } + dll_ptr += sizeof(IMAGE_IMPORT_DESCRIPTOR); + dynarray_reset(&p->symbols, &p->sym_count); + } + dynarray_reset(&pe->imp_info, &pe->imp_count); +} + +/* ------------------------------------------------------------- */ + +struct pe_sort_sym +{ + int index; + const char *name; +}; + +static int sym_cmp(const void *va, const void *vb) +{ + const char *ca = (*(struct pe_sort_sym**)va)->name; + const char *cb = (*(struct pe_sort_sym**)vb)->name; + return strcmp(ca, cb); +} + +static void pe_build_exports(struct pe_info *pe) +{ + ElfW(Sym) *sym; + int sym_index, sym_end; + DWORD rva_base, func_o, name_o, ord_o, str_o; + IMAGE_EXPORT_DIRECTORY *hdr; + int sym_count, ord; + struct pe_sort_sym **sorted, *p; + + FILE *op; + char buf[MAX_PATH]; + const char *dllname; + const char *name; + + rva_base = pe->thunk->sh_addr - pe->imagebase; + sym_count = 0, sorted = NULL, op = NULL; + + sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + sym = (ElfW(Sym)*)symtab_section->data + sym_index; + name = pe_export_name(pe->s1, sym); + if ((sym->st_other & ST_PE_EXPORT) + /* export only symbols from actually written sections */ + && pe->s1->sections[sym->st_shndx]->sh_addr) { + p = tcc_malloc(sizeof *p); + p->index = sym_index; + p->name = name; + dynarray_add((void***)&sorted, &sym_count, p); + } +#if 0 + if (sym->st_other & ST_PE_EXPORT) + printf("export: %s\n", name); + if (sym->st_other & ST_PE_STDCALL) + printf("stdcall: %s\n", name); +#endif + } + + if (0 == sym_count) + return; + + qsort (sorted, sym_count, sizeof *sorted, sym_cmp); + + pe_align_section(pe->thunk, 16); + dllname = tcc_basename(pe->filename); + + pe->exp_offs = pe->thunk->data_offset; + func_o = pe->exp_offs + sizeof(IMAGE_EXPORT_DIRECTORY); + name_o = func_o + sym_count * sizeof (DWORD); + ord_o = name_o + sym_count * sizeof (DWORD); + str_o = ord_o + sym_count * sizeof(WORD); + + hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs); + hdr->Characteristics = 0; + hdr->Base = 1; + hdr->NumberOfFunctions = sym_count; + hdr->NumberOfNames = sym_count; + hdr->AddressOfFunctions = func_o + rva_base; + hdr->AddressOfNames = name_o + rva_base; + hdr->AddressOfNameOrdinals = ord_o + rva_base; + hdr->Name = str_o + rva_base; + put_elf_str(pe->thunk, dllname); + +#if 1 + /* automatically write exports to <output-filename>.def */ + pstrcpy(buf, sizeof buf, pe->filename); + strcpy(tcc_fileextension(buf), ".def"); + op = fopen(buf, "w"); + if (NULL == op) { + tcc_error_noabort("could not create '%s': %s", buf, strerror(errno)); + } else { + fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname); + if (pe->s1->verbose) + printf("<- %s (%d symbols)\n", buf, sym_count); + } +#endif + + for (ord = 0; ord < sym_count; ++ord) + { + p = sorted[ord], sym_index = p->index, name = p->name; + /* insert actual address later in pe_relocate_rva */ + put_elf_reloc(symtab_section, pe->thunk, + func_o, R_XXX_RELATIVE, sym_index); + *(DWORD*)(pe->thunk->data + name_o) + = pe->thunk->data_offset + rva_base; + *(WORD*)(pe->thunk->data + ord_o) + = ord; + put_elf_str(pe->thunk, name); + func_o += sizeof (DWORD); + name_o += sizeof (DWORD); + ord_o += sizeof (WORD); + if (op) + fprintf(op, "%s\n", name); + } + pe->exp_size = pe->thunk->data_offset - pe->exp_offs; + dynarray_reset(&sorted, &sym_count); + if (op) + fclose(op); +} + +/* ------------------------------------------------------------- */ +static void pe_build_reloc (struct pe_info *pe) +{ + DWORD offset, block_ptr, addr; + int count, i; + ElfW_Rel *rel, *rel_end; + Section *s = NULL, *sr; + + offset = addr = block_ptr = count = i = 0; + rel = rel_end = NULL; + + for(;;) { + if (rel < rel_end) { + int type = ELFW(R_TYPE)(rel->r_info); + addr = rel->r_offset + s->sh_addr; + ++ rel; + if (type != REL_TYPE_DIRECT) + continue; + if (count == 0) { /* new block */ + block_ptr = pe->reloc->data_offset; + section_ptr_add(pe->reloc, sizeof(struct pe_reloc_header)); + offset = addr & 0xFFFFFFFF<<12; + } + if ((addr -= offset) < (1<<12)) { /* one block spans 4k addresses */ + WORD *wp = section_ptr_add(pe->reloc, sizeof (WORD)); + *wp = addr | IMAGE_REL_BASED_HIGHLOW<<12; + ++count; + continue; + } + -- rel; + + } else if (i < pe->sec_count) { + sr = (s = pe->s1->sections[pe->sec_info[i++].ord])->reloc; + if (sr) { + rel = (ElfW_Rel *)sr->data; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + } + continue; + } + + if (count) { + /* store the last block and ready for a new one */ + struct pe_reloc_header *hdr; + if (count & 1) /* align for DWORDS */ + section_ptr_add(pe->reloc, sizeof(WORD)), ++count; + hdr = (struct pe_reloc_header *)(pe->reloc->data + block_ptr); + hdr -> offset = offset - pe->imagebase; + hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header); + count = 0; + } + + if (rel >= rel_end) + break; + } +} + +/* ------------------------------------------------------------- */ +static int pe_section_class(Section *s) +{ + int type, flags; + const char *name; + + type = s->sh_type; + flags = s->sh_flags; + name = s->name; + if (flags & SHF_ALLOC) { + if (type == SHT_PROGBITS) { + if (flags & SHF_EXECINSTR) + return sec_text; + if (flags & SHF_WRITE) + return sec_data; + if (0 == strcmp(name, ".rsrc")) + return sec_rsrc; + if (0 == strcmp(name, ".iedat")) + return sec_idata; + if (0 == strcmp(name, ".pdata")) + return sec_pdata; + return sec_other; + } else if (type == SHT_NOBITS) { + if (flags & SHF_WRITE) + return sec_bss; + } + } else { + if (0 == strcmp(name, ".reloc")) + return sec_reloc; + if (0 == strncmp(name, ".stab", 5)) /* .stab and .stabstr */ + return sec_stab; + } + return -1; +} + +static int pe_assign_addresses (struct pe_info *pe) +{ + int i, k, o, c; + DWORD addr; + int *section_order; + struct section_info *si; + Section *s; + + // pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); + + section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int)); + for (o = k = 0 ; k < sec_last; ++k) { + for (i = 1; i < pe->s1->nb_sections; ++i) { + s = pe->s1->sections[i]; + if (k == pe_section_class(s)) { + // printf("%s %d\n", s->name, k); + s->sh_addr = pe->imagebase; + section_order[o++] = i; + } + } + } + + pe->sec_info = tcc_mallocz(o * sizeof (struct section_info)); + addr = pe->imagebase + 1; + + for (i = 0; i < o; ++i) + { + k = section_order[i]; + s = pe->s1->sections[k]; + c = pe_section_class(s); + si = &pe->sec_info[pe->sec_count]; + +#ifdef PE_MERGE_DATA + if (c == sec_bss && pe->sec_count && si[-1].cls == sec_data) { + /* append .bss to .data */ + s->sh_addr = addr = ((addr-1) | (s->sh_addralign-1)) + 1; + addr += s->data_offset; + si[-1].sh_size = addr - si[-1].sh_addr; + continue; + } +#endif + if (c == sec_stab && 0 == pe->s1->do_debug) + continue; + + strcpy(si->name, s->name); + si->cls = c; + si->ord = k; + si->sh_addr = s->sh_addr = addr = pe_virtual_align(pe, addr); + si->sh_flags = s->sh_flags; + + if (c == sec_data && NULL == pe->thunk) + pe->thunk = s; + + if (s == pe->thunk) { + pe_build_imports(pe); + pe_build_exports(pe); + } + + if (c == sec_reloc) + pe_build_reloc (pe); + + if (s->data_offset) + { + if (s->sh_type != SHT_NOBITS) { + si->data = s->data; + si->data_size = s->data_offset; + } + + addr += s->data_offset; + si->sh_size = s->data_offset; + ++pe->sec_count; + } + // printf("%08x %05x %s\n", si->sh_addr, si->sh_size, si->name); + } + +#if 0 + for (i = 1; i < pe->s1->nb_sections; ++i) { + Section *s = pe->s1->sections[i]; + int type = s->sh_type; + int flags = s->sh_flags; + printf("section %-16s %-10s %5x %s,%s,%s\n", + s->name, + type == SHT_PROGBITS ? "progbits" : + type == SHT_NOBITS ? "nobits" : + type == SHT_SYMTAB ? "symtab" : + type == SHT_STRTAB ? "strtab" : + type == SHT_RELX ? "rel" : "???", + s->data_offset, + flags & SHF_ALLOC ? "alloc" : "", + flags & SHF_WRITE ? "write" : "", + flags & SHF_EXECINSTR ? "exec" : "" + ); + } + pe->s1->verbose = 2; +#endif + + tcc_free(section_order); + return 0; +} + +/* ------------------------------------------------------------- */ +static void pe_relocate_rva (struct pe_info *pe, Section *s) +{ + Section *sr = s->reloc; + ElfW_Rel *rel, *rel_end; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + for(rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) { + if (ELFW(R_TYPE)(rel->r_info) == R_XXX_RELATIVE) { + int sym_index = ELFW(R_SYM)(rel->r_info); + DWORD addr = s->sh_addr; + if (sym_index) { + ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + sym_index; + addr = sym->st_value; + } + // printf("reloc rva %08x %08x %s\n", (DWORD)rel->r_offset, addr, s->name); + *(DWORD*)(s->data + rel->r_offset) += addr - pe->imagebase; + } + } +} + +/*----------------------------------------------------------------------------*/ + +static int pe_isafunc(int sym_index) +{ + Section *sr = text_section->reloc; + ElfW_Rel *rel, *rel_end; + Elf32_Word info = ELF32_R_INFO(sym_index, R_386_PC32); + if (!sr) + return 0; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + for (rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) + if (rel->r_info == info) + return 1; + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int pe_check_symbols(struct pe_info *pe) +{ + ElfW(Sym) *sym; + int sym_index, sym_end; + int ret = 0; + + pe_align_section(text_section, 8); + + sym_end = symtab_section->data_offset / sizeof(ElfW(Sym)); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + + sym = (ElfW(Sym) *)symtab_section->data + sym_index; + if (sym->st_shndx == SHN_UNDEF) { + + const char *name = symtab_section->link->data + sym->st_name; + unsigned type = ELFW(ST_TYPE)(sym->st_info); + int imp_sym = pe_find_import(pe->s1, sym); + struct import_symbol *is; + + if (0 == imp_sym) + goto not_found; + + if (type == STT_NOTYPE) { + /* symbols from assembler have no type, find out which */ + if (pe_isafunc(sym_index)) + type = STT_FUNC; + else + type = STT_OBJECT; + } + + is = pe_add_import(pe, imp_sym); + + if (type == STT_FUNC) { + unsigned long offset = is->thk_offset; + if (offset) { + /* got aliased symbol, like stricmp and _stricmp */ + + } else { + char buffer[100]; + WORD *p; + + offset = text_section->data_offset; + /* add the 'jmp IAT[x]' instruction */ +#ifdef TCC_TARGET_ARM + p = section_ptr_add(text_section, 8+4); // room for code and address + (*(DWORD*)(p)) = 0xE59FC000; // arm code ldr ip, [pc] ; PC+8+0 = 0001xxxx + (*(DWORD*)(p+2)) = 0xE59CF000; // arm code ldr pc, [ip] +#else + p = section_ptr_add(text_section, 8); + *p = 0x25FF; +#ifdef TCC_TARGET_X86_64 + *(DWORD*)(p+1) = (DWORD)-4; +#endif +#endif + /* add a helper symbol, will be patched later in + pe_build_imports */ + sprintf(buffer, "IAT.%s", name); + is->iat_index = put_elf_sym( + symtab_section, 0, sizeof(DWORD), + ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), + 0, SHN_UNDEF, buffer); +#ifdef TCC_TARGET_ARM + put_elf_reloc(symtab_section, text_section, + offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position +#else + put_elf_reloc(symtab_section, text_section, + offset + 2, R_XXX_THUNKFIX, is->iat_index); +#endif + is->thk_offset = offset; + } + + /* tcc_realloc might have altered sym's address */ + sym = (ElfW(Sym) *)symtab_section->data + sym_index; + + /* patch the original symbol */ + sym->st_value = offset; + sym->st_shndx = text_section->sh_num; + sym->st_other &= ~ST_PE_EXPORT; /* do not export */ + continue; + } + + if (type == STT_OBJECT) { /* data, ptr to that should be */ + if (0 == is->iat_index) { + /* original symbol will be patched later in pe_build_imports */ + is->iat_index = sym_index; + continue; + } + } + + not_found: + tcc_error_noabort("undefined symbol '%s'", name); + ret = -1; + + } else if (pe->s1->rdynamic + && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { + /* if -rdynamic option, then export all non local symbols */ + sym->st_other |= ST_PE_EXPORT; + } + } + return ret; +} + +/*----------------------------------------------------------------------------*/ +#ifdef PE_PRINT_SECTIONS +static void pe_print_section(FILE * f, Section * s) +{ + /* just if you'r curious */ + BYTE *p, *e, b; + int i, n, l, m; + p = s->data; + e = s->data + s->data_offset; + l = e - p; + + fprintf(f, "section \"%s\"", s->name); + if (s->link) + fprintf(f, "\nlink \"%s\"", s->link->name); + if (s->reloc) + fprintf(f, "\nreloc \"%s\"", s->reloc->name); + fprintf(f, "\nv_addr %08X", (unsigned)s->sh_addr); + fprintf(f, "\ncontents %08X", (unsigned)l); + fprintf(f, "\n\n"); + + if (s->sh_type == SHT_NOBITS) + return; + + if (0 == l) + return; + + if (s->sh_type == SHT_SYMTAB) + m = sizeof(ElfW(Sym)); + else if (s->sh_type == SHT_RELX) + m = sizeof(ElfW_Rel); + else + m = 16; + + fprintf(f, "%-8s", "offset"); + for (i = 0; i < m; ++i) + fprintf(f, " %02x", i); + n = 56; + + if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_RELX) { + const char *fields1[] = { + "name", + "value", + "size", + "bind", + "type", + "other", + "shndx", + NULL + }; + + const char *fields2[] = { + "offs", + "type", + "symb", + NULL + }; + + const char **p; + + if (s->sh_type == SHT_SYMTAB) + p = fields1, n = 106; + else + p = fields2, n = 58; + + for (i = 0; p[i]; ++i) + fprintf(f, "%6s", p[i]); + fprintf(f, " symbol"); + } + + fprintf(f, "\n"); + for (i = 0; i < n; ++i) + fprintf(f, "-"); + fprintf(f, "\n"); + + for (i = 0; i < l;) + { + fprintf(f, "%08X", i); + for (n = 0; n < m; ++n) { + if (n + i < l) + fprintf(f, " %02X", p[i + n]); + else + fprintf(f, " "); + } + + if (s->sh_type == SHT_SYMTAB) { + ElfW(Sym) *sym = (ElfW(Sym) *) (p + i); + const char *name = s->link->data + sym->st_name; + fprintf(f, " %04X %04X %04X %02X %02X %02X %04X \"%s\"", + (unsigned)sym->st_name, + (unsigned)sym->st_value, + (unsigned)sym->st_size, + (unsigned)ELFW(ST_BIND)(sym->st_info), + (unsigned)ELFW(ST_TYPE)(sym->st_info), + (unsigned)sym->st_other, + (unsigned)sym->st_shndx, + name); + + } else if (s->sh_type == SHT_RELX) { + ElfW_Rel *rel = (ElfW_Rel *) (p + i); + ElfW(Sym) *sym = + (ElfW(Sym) *) s->link->data + ELFW(R_SYM)(rel->r_info); + const char *name = s->link->link->data + sym->st_name; + fprintf(f, " %04X %02X %04X \"%s\"", + (unsigned)rel->r_offset, + (unsigned)ELFW(R_TYPE)(rel->r_info), + (unsigned)ELFW(R_SYM)(rel->r_info), + name); + } else { + fprintf(f, " "); + for (n = 0; n < m; ++n) { + if (n + i < l) { + b = p[i + n]; + if (b < 32 || b >= 127) + b = '.'; + fprintf(f, "%c", b); + } + } + } + i += m; + fprintf(f, "\n"); + } + fprintf(f, "\n\n"); +} + +static void pe_print_sections(TCCState *s1, const char *fname) +{ + Section *s; + FILE *f; + int i; + f = fopen(fname, "w"); + for (i = 1; i < s1->nb_sections; ++i) { + s = s1->sections[i]; + pe_print_section(f, s); + } + pe_print_section(f, s1->dynsymtab_section); + fclose(f); +} +#endif + +/* ------------------------------------------------------------- */ +/* helper function for load/store to insert one more indirection */ + +ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2) +{ + Sym *sym; + ElfW(Sym) *esym; + int r2; + + if ((sv->r & (VT_VALMASK|VT_SYM)) != (VT_CONST|VT_SYM) || (sv->r2 != VT_CONST)) + return sv; + sym = sv->sym; + if ((sym->type.t & (VT_EXTERN|VT_STATIC)) != VT_EXTERN) + return sv; + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + if (!(esym->st_other & ST_PE_IMPORT)) + return sv; + + // printf("import %04x %04x %04x %s\n", sv->type.t, sym->type.t, sv->r, get_tok_str(sv->sym->v, NULL)); + + memset(v2, 0, sizeof *v2); + v2->type.t = VT_PTR; + v2->r = VT_CONST | VT_SYM | VT_LVAL; + v2->sym = sv->sym; + + r2 = get_reg(RC_INT); + load(r2, v2); + v2->r = r2; + + if (sv->c.ui) { + vpushv(v2); + vpushi(sv->c.ui); + gen_opi('+'); + *v2 = *vtop--; + } + + v2->type.t = sv->type.t; + v2->r |= sv->r & VT_LVAL; + return v2; +} + +ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value) +{ + return add_elf_sym( + s1->dynsymtab_section, + value, + dllindex, /* st_size */ + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), + 0, + value ? SHN_ABS : SHN_UNDEF, + name + ); +} + +static int add_dllref(TCCState *s1, const char *dllname) +{ + DLLReference *dllref; + int i; + for (i = 0; i < s1->nb_loaded_dlls; ++i) + if (0 == strcmp(s1->loaded_dlls[i]->name, dllname)) + return i + 1; + dllref = tcc_mallocz(sizeof(DLLReference) + strlen(dllname)); + strcpy(dllref->name, dllname); + dynarray_add((void ***) &s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); + return s1->nb_loaded_dlls; +} + +/* ------------------------------------------------------------- */ + +static int read_mem(int fd, unsigned offset, void *buffer, unsigned len) +{ + lseek(fd, offset, SEEK_SET); + return len == read(fd, buffer, len); +} + +/* ------------------------------------------------------------- + * This is for compiled windows resources in 'coff' format + * as generated by 'windres.exe -O coff ...'. + */ + +static int pe_load_res(TCCState *s1, int fd) +{ + struct pe_rsrc_header hdr; + Section *rsrc_section; + int i, ret = -1; + BYTE *ptr; + unsigned offs; + + if (!read_mem(fd, 0, &hdr, sizeof hdr)) + goto quit; + + if (hdr.filehdr.Machine != IMAGE_FILE_MACHINE + || hdr.filehdr.NumberOfSections != 1 + || strcmp(hdr.sectionhdr.Name, ".rsrc") != 0) + goto quit; + + rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC); + ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData); + offs = hdr.sectionhdr.PointerToRawData; + if (!read_mem(fd, offs, ptr, hdr.sectionhdr.SizeOfRawData)) + goto quit; + offs = hdr.sectionhdr.PointerToRelocations; + for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i) + { + struct pe_rsrc_reloc rel; + if (!read_mem(fd, offs, &rel, sizeof rel)) + goto quit; + // printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type); + if (rel.type != RSRC_RELTYPE) + goto quit; + put_elf_reloc(symtab_section, rsrc_section, + rel.offset, R_XXX_RELATIVE, 0); + offs += sizeof rel; + } + ret = 0; +quit: + return ret; +} + +/* ------------------------------------------------------------- */ +static char *trimfront(char *p) +{ + while (*p && (unsigned char)*p <= ' ') + ++p; + return p; +} + +static char *trimback(char *a, char *e) +{ + while (e > a && (unsigned char)e[-1] <= ' ') + --e; + *e = 0;; + return a; +} + +/* ------------------------------------------------------------- */ +static int pe_load_def(TCCState *s1, int fd) +{ + int state = 0, ret = -1, dllindex = 0, ord; + char line[400], dllname[80], *p, *x; + FILE *fp; + + fp = fdopen(dup(fd), "rb"); + while (fgets(line, sizeof line, fp)) + { + p = trimfront(trimback(line, strchr(line, 0))); + if (0 == *p || ';' == *p) + continue; + + switch (state) { + case 0: + if (0 != strnicmp(p, "LIBRARY", 7)) + goto quit; + pstrcpy(dllname, sizeof dllname, trimfront(p+7)); + ++state; + continue; + + case 1: + if (0 != stricmp(p, "EXPORTS")) + goto quit; + ++state; + continue; + + case 2: + dllindex = add_dllref(s1, dllname); + ++state; + /* fall through */ + default: + /* get ordinal and will store in sym->st_value */ + ord = 0; + x = strchr(p, ' '); + if (x) { + *x = 0, x = strrchr(x + 1, '@'); + if (x) { + char *d; + ord = (int)strtol(x + 1, &d, 10); + if (*d) + ord = 0; + } + } + pe_putimport(s1, dllindex, p, ord); + continue; + } + } + ret = 0; +quit: + fclose(fp); + return ret; +} + +/* ------------------------------------------------------------- */ +#define TINY_IMPDEF_GET_EXPORT_NAMES_ONLY +#include "../win32/tools/tiny_impdef.c" + +static int pe_load_dll(TCCState *s1, const char *dllname, int fd) +{ + char *p, *q; + int index; + p = get_export_names(fd); + if (!p) + return -1; + index = add_dllref(s1, dllname); + for (q = p; *q; q += 1 + strlen(q)) + pe_putimport(s1, index, q, 0); + tcc_free(p); + return 0; +} + +/* ------------------------------------------------------------- */ +ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd) +{ + int ret = -1; + char buf[10]; + if (0 == strcmp(tcc_fileextension(filename), ".def")) + ret = pe_load_def(s1, fd); + else if (pe_load_res(s1, fd) == 0) + ret = 0; + else if (read_mem(fd, 0, buf, sizeof buf) && 0 == strncmp(buf, "MZ", 2)) + ret = pe_load_dll(s1, tcc_basename(filename), fd); + return ret; +} + +/* ------------------------------------------------------------- */ +#ifdef TCC_TARGET_X86_64 +static unsigned pe_add_uwwind_info(TCCState *s1) +{ + if (NULL == s1->uw_pdata) { + s1->uw_pdata = find_section(tcc_state, ".pdata"); + s1->uw_pdata->sh_addralign = 4; + s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0, text_section->sh_num, NULL); + } + + if (0 == s1->uw_offs) { + /* As our functions all have the same stackframe, we use one entry for all */ + static const unsigned char uw_info[] = { + 0x01, // UBYTE: 3 Version , UBYTE: 5 Flags + 0x04, // UBYTE Size of prolog + 0x02, // UBYTE Count of unwind codes + 0x05, // UBYTE: 4 Frame Register (rbp), UBYTE: 4 Frame Register offset (scaled) + // USHORT * n Unwind codes array + // 0x0b, 0x01, 0xff, 0xff, // stack size + 0x04, 0x03, // set frame ptr (mov rsp -> rbp) + 0x01, 0x50 // push reg (rbp) + }; + + Section *s = text_section; + unsigned char *p; + + section_ptr_add(s, -s->data_offset & 3); /* align */ + s1->uw_offs = s->data_offset; + p = section_ptr_add(s, sizeof uw_info); + memcpy(p, uw_info, sizeof uw_info); + } + + return s1->uw_offs; +} + +ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack) +{ + TCCState *s1 = tcc_state; + Section *pd; + unsigned o, n, d; + struct /* _RUNTIME_FUNCTION */ { + DWORD BeginAddress; + DWORD EndAddress; + DWORD UnwindData; + } *p; + + d = pe_add_uwwind_info(s1); + pd = s1->uw_pdata; + o = pd->data_offset; + p = section_ptr_add(pd, sizeof *p); + + /* record this function */ + p->BeginAddress = start; + p->EndAddress = end; + p->UnwindData = d; + + /* put relocations on it */ + for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress) + put_elf_reloc(symtab_section, pd, o, R_X86_64_RELATIVE, s1->uw_sym); +} +#endif +/* ------------------------------------------------------------- */ +#ifdef TCC_TARGET_X86_64 +#define PE_STDSYM(n,s) n +#else +#define PE_STDSYM(n,s) "_" n s +#endif + +static void pe_add_runtime(TCCState *s1, struct pe_info *pe) +{ + const char *start_symbol; + int pe_type = 0; + + if (find_elf_sym(symtab_section, PE_STDSYM("WinMain","@16"))) + pe_type = PE_GUI; + else + if (TCC_OUTPUT_DLL == s1->output_type) { + pe_type = PE_DLL; + /* need this for 'tccelf.c:relocate_section()' */ + s1->output_type = TCC_OUTPUT_EXE; + } + else + pe_type = PE_EXE; + + start_symbol = + TCC_OUTPUT_MEMORY == s1->output_type + ? PE_GUI == pe_type ? "__runwinmain" : "_main" + : PE_DLL == pe_type ? PE_STDSYM("__dllstart","@12") + : PE_GUI == pe_type ? "__winstart" : "__start" + ; + + if (!s1->leading_underscore || strchr(start_symbol, '@')) + ++start_symbol; + + /* grab the startup code from libtcc1 */ + if (TCC_OUTPUT_MEMORY != s1->output_type || PE_GUI == pe_type) + add_elf_sym(symtab_section, + 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, start_symbol); + + tcc_add_pragma_libs(s1); + + if (0 == s1->nostdlib) { + static const char *libs[] = { + "tcc1", "msvcrt", "kernel32", "", "user32", "gdi32", NULL + }; + const char **pp, *p; + for (pp = libs; 0 != (p = *pp); ++pp) { + if (0 == *p) { + if (PE_DLL != pe_type && PE_GUI != pe_type) + break; + } else if (tcc_add_library_err(s1, p) < 0) { + break; + } + } + } + + if (TCC_OUTPUT_MEMORY == s1->output_type) { + pe_type = PE_RUN; +#ifdef TCC_IS_NATIVE + s1->runtime_main = start_symbol; +#endif + } else { + pe->start_addr = (DWORD)(uintptr_t)tcc_get_symbol_err(s1, start_symbol); + } + + pe->type = pe_type; +} + +ST_FUNC int pe_output_file(TCCState * s1, const char *filename) +{ + int ret; + struct pe_info pe; + int i; + + memset(&pe, 0, sizeof pe); + pe.filename = filename; + pe.s1 = s1; + + pe_add_runtime(s1, &pe); + tcc_add_bcheck(s1); + relocate_common_syms(); /* assign bss adresses */ + tcc_add_linker_symbols(s1); + + ret = pe_check_symbols(&pe); + if (ret) + ; + else if (filename) { + if (PE_DLL == pe.type) { + pe.reloc = new_section(pe.s1, ".reloc", SHT_PROGBITS, 0); + /* XXX: check if is correct for arm-pe target */ + pe.imagebase = 0x10000000; + } else { +#if defined(TCC_TARGET_ARM) + pe.imagebase = 0x00010000; +#else + pe.imagebase = 0x00400000; +#endif + } + +#if defined(TCC_TARGET_ARM) + /* we use "console" subsystem by default */ + pe.subsystem = 9; +#else + if (PE_DLL == pe.type || PE_GUI == pe.type) + pe.subsystem = 2; + else + pe.subsystem = 3; +#endif + /* Allow override via -Wl,-subsystem=... option */ + if (s1->pe_subsystem != 0) + pe.subsystem = s1->pe_subsystem; + + /* set default file/section alignment */ + if (pe.subsystem == 1) { + pe.section_align = 0x20; + pe.file_align = 0x20; + } else { + pe.section_align = 0x1000; + pe.file_align = 0x200; + } + + if (s1->section_align != 0) + pe.section_align = s1->section_align; + if (s1->pe_file_align != 0) + pe.file_align = s1->pe_file_align; + + if ((pe.subsystem >= 10) && (pe.subsystem <= 12)) + pe.imagebase = 0; + + if (s1->has_text_addr) + pe.imagebase = s1->text_addr; + + pe_assign_addresses(&pe); + relocate_syms(s1, 0); + for (i = 1; i < s1->nb_sections; ++i) { + Section *s = s1->sections[i]; + if (s->reloc) { + relocate_section(s1, s); + pe_relocate_rva(&pe, s); + } + } + if (s1->nb_errors) + ret = -1; + else + ret = pe_write(&pe); + tcc_free(pe.sec_info); + } else { +#ifdef TCC_IS_NATIVE + pe.thunk = data_section; + pe_build_imports(&pe); +#endif + } + +#ifdef PE_PRINT_SECTIONS + pe_print_sections(s1, "tcc.log"); +#endif + return ret; +} + +/* ------------------------------------------------------------- */ diff --git a/src/tccpp.c b/src/tccpp.c new file mode 100644 index 0000000..eed09b5 --- /dev/null +++ b/src/tccpp.c @@ -0,0 +1,3430 @@ +/* + * 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 + */ + +#include "tcc.h" + +/********************************************************/ +/* global variables */ + +ST_DATA int tok_flags; +ST_DATA int parse_flags; + +ST_DATA struct BufferedFile *file; +ST_DATA int ch, tok; +ST_DATA CValue tokc; +ST_DATA const int *macro_ptr; +ST_DATA CString tokcstr; /* current parsed string, if any */ + +/* display benchmark infos */ +ST_DATA int total_lines; +ST_DATA int total_bytes; +ST_DATA int tok_ident; +ST_DATA TokenSym **table_ident; + +/* ------------------------------------------------------------------------- */ + +static TokenSym *hash_ident[TOK_HASH_SIZE]; +static char token_buf[STRING_MAX_SIZE + 1]; +static unsigned char isidnum_table[256 - CH_EOF]; +/* isidnum_table flags: */ +#define IS_SPC 1 +#define IS_ID 2 +#define IS_NUM 4 + +static TokenString *macro_stack; + +static const char tcc_keywords[] = +#define DEF(id, str) str "\0" +#include "tcctok.h" +#undef DEF +; + +/* WARNING: the content of this string encodes token numbers */ +static const unsigned char tok_two_chars[] = +/* outdated -- gr + "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253" + "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; +*/{ + '<','=', TOK_LE, + '>','=', TOK_GE, + '!','=', TOK_NE, + '&','&', TOK_LAND, + '|','|', TOK_LOR, + '+','+', TOK_INC, + '-','-', TOK_DEC, + '=','=', TOK_EQ, + '<','<', TOK_SHL, + '>','>', TOK_SAR, + '+','=', TOK_A_ADD, + '-','=', TOK_A_SUB, + '*','=', TOK_A_MUL, + '/','=', TOK_A_DIV, + '%','=', TOK_A_MOD, + '&','=', TOK_A_AND, + '^','=', TOK_A_XOR, + '|','=', TOK_A_OR, + '-','>', TOK_ARROW, + '.','.', 0xa8, // C++ token ? + '#','#', TOK_TWOSHARPS, + 0 +}; + +static void next_nomacro_spc(void); + +ST_FUNC void skip(int c) +{ + if (tok != c) + tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc)); + next(); +} + +ST_FUNC void expect(const char *msg) +{ + tcc_error("%s expected", msg); +} + +ST_FUNC void begin_macro(TokenString *str, int alloc) +{ + str->alloc = alloc; + str->prev = macro_stack; + str->prev_ptr = macro_ptr; + macro_ptr = str->str; + macro_stack = str; +} + +ST_FUNC void end_macro(void) +{ + TokenString *str = macro_stack; + macro_stack = str->prev; + macro_ptr = str->prev_ptr; + if (str->alloc == 2) { + str->alloc = 3; /* just mark as finished */ + } else { + tok_str_free(str->str); + if (str->alloc == 1) + tcc_free(str); + } +} + +/* ------------------------------------------------------------------------- */ +/* 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); + cstr->data_allocated = data; + cstr->size_allocated = size; + cstr->data = data; +} + +/* add a byte */ +ST_FUNC 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; +} + +ST_FUNC 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 */ +ST_FUNC void cstr_wccat(CString *cstr, int ch) +{ + int size; + size = cstr->size + sizeof(nwchar_t); + if (size > cstr->size_allocated) + cstr_realloc(cstr, size); + *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch; + cstr->size = size; +} + +ST_FUNC void cstr_new(CString *cstr) +{ + memset(cstr, 0, sizeof(CString)); +} + +/* free string and reset it to NULL */ +ST_FUNC void cstr_free(CString *cstr) +{ + tcc_free(cstr->data_allocated); + cstr_new(cstr); +} + +/* reset string to empty */ +ST_FUNC void cstr_reset(CString *cstr) +{ + cstr->size = 0; +} + +/* 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)); + } + } +} + +/* ------------------------------------------------------------------------- */ +/* 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) + tcc_error("memory full (symbols)"); + + /* 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 *)); + 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 */ +ST_FUNC 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); +} + +/* XXX: buffer overflow */ +/* XXX: float tokens */ +ST_FUNC const char *get_tok_str(int v, CValue *cv) +{ + static char buf[STRING_MAX_SIZE + 1]; + static CString cstr_buf; + CString *cstr; + 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 */ +#ifdef _WIN32 + sprintf(p, "%u", (unsigned)cv->ull); +#else + sprintf(p, "%llu", cv->ull); +#endif + break; + case TOK_LCHAR: + cstr_ccat(&cstr_buf, 'L'); + case TOK_CCHAR: + cstr_ccat(&cstr_buf, '\''); + add_char(&cstr_buf, cv->i); + cstr_ccat(&cstr_buf, '\''); + cstr_ccat(&cstr_buf, '\0'); + break; + case TOK_PPNUM: + case TOK_PPSTR: + return (char*)cv->cstr->data; + case TOK_LSTR: + cstr_ccat(&cstr_buf, 'L'); + case TOK_STR: + 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(nwchar_t)) - 1; + for(i=0;i<len;i++) + add_char(&cstr_buf, ((nwchar_t *)cstr->data)[i]); + } + cstr_ccat(&cstr_buf, '\"'); + cstr_ccat(&cstr_buf, '\0'); + break; + + case TOK_CFLOAT: + case TOK_CDOUBLE: + case TOK_CLDOUBLE: + case TOK_LINENUM: + return NULL; /* should not happen */ + + /* above tokens have value, the ones below don't */ + + case TOK_LT: + v = '<'; + goto addv; + case TOK_GT: + v = '>'; + goto addv; + case TOK_DOTS: + return strcpy(p, "..."); + case TOK_A_SHL: + return strcpy(p, "<<="); + case TOK_A_SAR: + return strcpy(p, ">>="); + default: + if (v < TOK_IDENT) { + /* search in two bytes table */ + const unsigned char *q = tok_two_chars; + while (*q) { + if (q[2] == v) { + *p++ = q[0]; + *p++ = q[1]; + *p = '\0'; + return buf; + } + q += 3; + } + if (v >= 127) { + sprintf(buf, "<%02x>", v); + return buf; + } + 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; +} + +/* return the current character, handling end of block if necessary + (but not stray) */ +ST_FUNC int handle_eob(void) +{ + BufferedFile *bf = file; + 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 = 1; +#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; + } +} + +/* read next char from current input file and handle end of input buffer */ +ST_INLN void inp(void) +{ + ch = *(++(file->buf_ptr)); + /* end of buffer/file handling */ + if (ch == CH_EOB) + ch = handle_eob(); +} + +/* handle '\[\r]\n' */ +static int handle_stray_noerror(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: + return 1; + } + } + return 0; +} + +static void handle_stray(void) +{ + if (handle_stray_noerror()) + tcc_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; + + file->buf_ptr = p; + if (p >= file->buf_end) { + c = handle_eob(); + if (c != '\\') + return c; + p = file->buf_ptr; + } + ch = *p; + if (handle_stray_noerror()) { + if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) + tcc_error("stray '\\' in program"); + *--file->buf_ptr = '\\'; + } + 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 */ +ST_FUNC 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 */ +ST_FUNC 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) { + tcc_error("unexpected end of file in comment"); + } else if (c == '\\') { + p++; + } + } + } + end_of_comment: + p++; + return p; +} + +#define cinp minp + +static inline void skip_spaces(void) +{ + while (isidnum_table[ch - CH_EOF] & IS_SPC) + cinp(); +} + +static inline int check_space(int t, int *spc) +{ + if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) { + if (*spc) + return 1; + *spc = 1; + } else + *spc = 0; + return 0; +} + +/* 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 */ + tcc_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 */ +static void preprocess_skip(void) +{ + int a, start_of_line, c, in_warn_or_error; + uint8_t *p; + + p = file->buf_ptr; + a = 0; +redo_start: + start_of_line = 1; + in_warn_or_error = 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': + file->line_num++; + p++; + goto redo_start; + case '\\': + file->buf_ptr = p; + c = handle_eob(); + if (c == CH_EOF) { + expect("#endif"); + } else if (c == '\\') { + ch = file->buf_ptr[0]; + handle_stray_noerror(); + } + p = file->buf_ptr; + goto redo_no_start; + /* skip strings */ + case '\"': + case '\'': + if (in_warn_or_error) + goto _default; + p = parse_pp_string(p, c, NULL); + break; + /* skip comments */ + case '/': + if (in_warn_or_error) + goto _default; + 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--; + else if( tok == TOK_ERROR || tok == TOK_WARNING) + in_warn_or_error = 1; + else if (tok == TOK_LINEFEED) + goto redo_start; + } else if (parse_flags & PARSE_FLAG_ASM_FILE) + p = parse_line_comment(p); + break; +_default: + 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' */ +ST_FUNC 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' */ +ST_FUNC 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_size(const int *p) +{ + switch(*p) { + /* 4 bytes */ + case TOK_CINT: + case TOK_CUINT: + case TOK_CCHAR: + case TOK_LCHAR: + case TOK_CFLOAT: + case TOK_LINENUM: + return 1 + 1; + case TOK_STR: + case TOK_LSTR: + case TOK_PPNUM: + case TOK_PPSTR: + return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2); + case TOK_CDOUBLE: + case TOK_CLLONG: + case TOK_CULLONG: + return 1 + 2; + case TOK_CLDOUBLE: + return 1 + LDOUBLE_SIZE / 4; + default: + return 1 + 0; + } +} + +/* token string handling */ + +ST_INLN void tok_str_new(TokenString *s) +{ + s->str = NULL; + s->len = 0; + s->allocated_len = 0; + s->last_line_num = -1; +} + +ST_FUNC 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)); + s->allocated_len = len; + s->str = str; + return str; +} + +ST_FUNC 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_PPSTR: + 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 == 16 + case TOK_CLDOUBLE: + str[len++] = cv->tab[0]; + str[len++] = cv->tab[1]; + str[len++] = cv->tab[2]; + str[len++] = cv->tab[3]; +#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' */ +ST_FUNC 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); +} + +/* get a token from an integer array and increment pointer + accordingly. we code it as a macro to avoid pointer aliasing. */ +static inline void TOK_GET(int *t, const int **pp, CValue *cv) +{ + const int *p = *pp; + int n, *tab; + + tab = cv->tab; + switch(*t = *p++) { + case TOK_CINT: + case TOK_CUINT: + case TOK_CCHAR: + case TOK_LCHAR: + case TOK_CFLOAT: + case TOK_LINENUM: + tab[0] = *p++; + break; + case TOK_STR: + case TOK_LSTR: + case TOK_PPNUM: + case TOK_PPSTR: + 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: + n = 2; + goto copy; + case TOK_CLDOUBLE: +#if LDOUBLE_SIZE == 16 + n = 4; +#elif LDOUBLE_SIZE == 12 + n = 3; +#elif LDOUBLE_SIZE == 8 + n = 2; +#else +# error add long double size support +#endif + copy: + do + *tab++ = *p++; + while (--n); + break; + default: + break; + } + *pp = p; +} + +static int macro_is_equal(const int *a, const int *b) +{ + char buf[STRING_MAX_SIZE + 1]; + CValue cv; + int t; + while (*a && *b) { + TOK_GET(&t, &a, &cv); + pstrcpy(buf, sizeof buf, get_tok_str(t, &cv)); + TOK_GET(&t, &b, &cv); + if (strcmp(buf, get_tok_str(t, &cv))) + return 0; + } + return !(*a || *b); +} + +static void pp_line(TCCState *s1, BufferedFile *f, int level) +{ + int d = f->line_num - f->line_ref; + if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE + || (level == 0 && f->line_ref && d < 8)) + { + while (d > 0) + fputs("\n", s1->ppfp), --d; + } + else + if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) { + fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename); + } + else { + fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename, + level > 0 ? " 1" : level < 0 ? " 2" : ""); + } + f->line_ref = f->line_num; +} + +static void tok_print(const char *msg, const int *str) +{ + FILE *pr = tcc_state->ppfp; + int t; + CValue cval; + + fprintf(pr, "%s ", msg); + while (str) { + TOK_GET(&t, &str, &cval); + if (!t) + break; + fprintf(pr,"%s", get_tok_str(t, &cval)); + } + fprintf(pr, "\n"); +} + +static int define_print_prepared(Sym *s) +{ + if (!s || !tcc_state->ppfp || tcc_state->dflag == 0) + return 0; + + if (s->v < TOK_IDENT || s->v >= tok_ident) + return 0; + + if (file) { + file->line_num--; + pp_line(tcc_state, file, 0); + file->line_ref = ++file->line_num; + } + return 1; +} + +static void define_print(int v) +{ + FILE *pr = tcc_state->ppfp; + Sym *s, *a; + + s = define_find(v); + if (define_print_prepared(s) == 0) + return; + + fprintf(pr, "// #define %s", get_tok_str(v, NULL)); + if (s->type.t == MACRO_FUNC) { + a = s->next; + fprintf(pr,"("); + if (a) + for (;;) { + fprintf(pr,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL)); + if (!(a = a->next)) + break; + fprintf(pr,","); + } + fprintf(pr,")"); + } + tok_print("", s->d); +} + +static void undef_print(int v) +{ + FILE *pr = tcc_state->ppfp; + Sym *s; + + s = define_find(v); + if (define_print_prepared(s) == 0) + return; + + fprintf(pr, "// #undef %s\n", get_tok_str(s->v, NULL)); +} + +ST_FUNC void print_defines(void) +{ + Sym *top = define_stack; + while (top) { + define_print(top->v); + top = top->prev; + } +} + +/* defines handling */ +ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg) +{ + Sym *s; + + s = define_find(v); + if (s && !macro_is_equal(s->d, str)) + tcc_warning("%s redefined", get_tok_str(v, NULL)); + + s = sym_push2(&define_stack, v, macro_type, 0); + s->d = str; + s->next = first_arg; + table_ident[v - TOK_IDENT]->sym_define = s; +} + +/* undefined a define symbol. Its name is just set to zero */ +ST_FUNC void define_undef(Sym *s) +{ + int v = s->v; + undef_print(v); + if (v >= TOK_IDENT && v < tok_ident) + table_ident[v - TOK_IDENT]->sym_define = NULL; +} + +ST_INLN 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' */ +ST_FUNC 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->d) + tok_str_free(top->d); + 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 */ +ST_FUNC Sym *label_find(int v) +{ + v -= TOK_IDENT; + if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) + return NULL; + return table_ident[v]->sym_label; +} + +ST_FUNC 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. */ +ST_FUNC 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) { + tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL)); + } else if (s->r == LABEL_FORWARD) { + tcc_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, s->jnext, 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 */ + begin_macro(&str, 0); + next(); + c = expr_const(); + end_macro(); + return c != 0; +} + + +/* parse after #define */ +ST_FUNC void parse_define(void) +{ + Sym *s, *first, **ps; + int v, t, varg, is_vaargs, spc; + int saved_parse_flags = parse_flags; + TokenString str; + + v = tok; + if (v < TOK_IDENT) + tcc_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 */ + parse_flags |= PARSE_FLAG_SPACES; + next_nomacro_spc(); + if (tok == '(') { + next_nomacro(); + ps = &first; + if (tok != ')') for (;;) { + 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) + bad_list: + tcc_error("bad macro parameter list"); + s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0); + *ps = s; + ps = &s->next; + if (tok == ')') + break; + if (tok != ',' || is_vaargs) + goto bad_list; + next_nomacro(); + } + next_nomacro_spc(); + t = MACRO_FUNC; + } + tok_str_new(&str); + spc = 2; + parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED; + while (tok != TOK_LINEFEED && tok != TOK_EOF) { + /* remove spaces around ## and after '#' */ + if (TOK_TWOSHARPS == tok) { + if (2 == spc) + goto bad_twosharp; + if (1 == spc) + --str.len; + spc = 3; + } else if ('#' == tok) { + spc = 4; + } else if (check_space(tok, &spc)) { + goto skip; + } + tok_str_add2(&str, tok, &tokc); + skip: + next_nomacro_spc(); + } + + parse_flags = saved_parse_flags; + if (spc == 1) + --str.len; /* remove trailing space */ + tok_str_add(&str, 0); + if (3 == spc) +bad_twosharp: + tcc_error("'##' cannot appear at either end of macro"); + define_push(v, t, str.str, first); + define_print(v); +} + +static inline int hash_cached_include(const char *filename) +{ + const unsigned char *s; + unsigned int h; + + h = TOK_HASH_INIT; + s = (unsigned char *) filename; + while (*s) { + h = TOK_HASH_FUNC(h, *s); + s++; + } + h &= (CACHED_INCLUDES_HASH_SIZE - 1); + return h; +} + +static CachedInclude *search_cached_include(TCCState *s1, const char *filename) +{ + CachedInclude *e; + int i, h; + h = hash_cached_include(filename); + i = s1->cached_includes_hash[h]; + for(;;) { + if (i == 0) + break; + e = s1->cached_includes[i - 1]; + if (0 == PATHCMP(e->filename, filename)) + return e; + i = e->hash_next; + } + return NULL; +} + +static inline void add_cached_include(TCCState *s1, const char *filename, int ifndef_macro) +{ + CachedInclude *e; + int h; + + if (search_cached_include(s1, 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)); + 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(filename); + e->hash_next = s1->cached_includes_hash[h]; + s1->cached_includes_hash[h] = s1->nb_cached_includes; +} + +static void pragma_parse(TCCState *s1) +{ + next_nomacro(); + if (tok == TOK_push_macro || tok == TOK_pop_macro) { + int t = tok, v; + Sym *s; + + if (next(), tok != '(') + goto pragma_err; + if (next(), tok != TOK_STR) + goto pragma_err; + v = tok_alloc(tokc.cstr->data, tokc.cstr->size - 1)->tok; + if (next(), tok != ')') + goto pragma_err; + if (t == TOK_push_macro) { + while (NULL == (s = define_find(v))) + define_push(v, 0, NULL, NULL); + s->type.ref = s; /* set push boundary */ + } else { + for (s = define_stack; s; s = s->prev) + if (s->v == v && s->type.ref == s) { + s->type.ref = NULL; + break; + } + } + if (s) + table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL; + else + tcc_warning("unbalanced #pragma pop_macro"); + + } else if (tok == TOK_once) { + add_cached_include(s1, file->filename, TOK_once); + + } else if (s1->ppfp) { + /* tcc -E: keep pragmas below unchanged */ + unget_tok(' '); + unget_tok(TOK_PRAGMA); + unget_tok('#'); + unget_tok(TOK_LINEFEED); + + } else 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: + tcc_error("out of pack stack"); + } + s1->pack_stack_ptr--; + } else { + int 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) + goto pragma_err; + val = tokc.i; + if (val < 1 || val > 16 || (val & (val - 1)) != 0) + goto pragma_err; + next(); + } + *s1->pack_stack_ptr = val; + } + if (tok != ')') + goto pragma_err; + + } else if (tok == TOK_comment) { + char *file; + next(); + skip('('); + if (tok != TOK_lib) + goto pragma_warn; + next(); + skip(','); + if (tok != TOK_STR) + goto pragma_err; + file = tcc_strdup((char *)tokc.cstr->data); + dynarray_add((void ***)&s1->pragma_libs, &s1->nb_pragma_libs, file); + next(); + if (tok != ')') + goto pragma_err; + } else { +pragma_warn: + if (s1->warn_unsupported) + tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc)); + } + return; + +pragma_err: + tcc_error("malformed #pragma directive"); + return; +} + +/* is_bof is true if first non space token at beginning of file */ +ST_FUNC void preprocess(int is_bof) +{ + TCCState *s1 = tcc_state; + int i, c, n, saved_parse_flags; + char buf[1024], *q; + Sym *s; + + saved_parse_flags = parse_flags; + parse_flags = PARSE_FLAG_PREPROCESS + | PARSE_FLAG_TOK_NUM + | PARSE_FLAG_TOK_STR + | PARSE_FLAG_LINEFEED + | (parse_flags & PARSE_FLAG_ASM_FILE) + ; + + 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: + inp(); + q = buf; + while (ch != c && ch != '\n' && ch != CH_EOF) { + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; + if (ch == '\\') { + if (handle_stray_noerror() == 0) + --q; + } else + inp(); + } + *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: + tcc_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 = '>'; + } + } + + if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE) + tcc_error("#include recursion too deep"); + /* store current file in stack, but increment stack later below */ + *s1->include_stack_ptr = file; + + n = s1->nb_include_paths + s1->nb_sysinclude_paths; + for (i = -2; i < n; ++i) { + char buf1[sizeof file->filename]; + CachedInclude *e; + BufferedFile **f; + const char *path; + + if (i == -2) { + /* check absolute include path */ + if (!IS_ABSPATH(buf)) + continue; + buf1[0] = 0; + i = n; /* force end loop */ + + } else if (i == -1) { + /* search in current dir if "header.h" */ + if (c != '\"') + continue; + path = file->filename; + pstrncpy(buf1, path, tcc_basename(path) - path); + + } else { + /* search in all the include paths */ + 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); + + if (tok == TOK_INCLUDE_NEXT) + for (f = s1->include_stack_ptr; f >= s1->include_stack; --f) + if (0 == PATHCMP((*f)->filename, buf1)) { +#ifdef INC_DEBUG + printf("%s: #include_next skipping %s\n", file->filename, buf1); +#endif + goto include_trynext; + } + + e = search_cached_include(s1, buf1); + if (e && (define_find(e->ifndef_macro) || e->ifndef_macro == TOK_once)) { + /* no need to parse the include because the 'ifndef macro' + is defined */ +#ifdef INC_DEBUG + printf("%s: skipping cached %s\n", file->filename, buf1); +#endif + goto include_done; + } + + if (tcc_open(s1, buf1) < 0) +include_trynext: + continue; + +#ifdef INC_DEBUG + printf("%s: including %s\n", file->prev->filename, file->filename); +#endif + /* update target deps */ + dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, + tcc_strdup(buf1)); + /* push current file in stack */ + ++s1->include_stack_ptr; + /* add include file debug info */ + if (s1->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; + } + tcc_error("include file '%s' not found", buf); +include_done: + 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) + tcc_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) + tcc_error("memory full (ifdef)"); + *s1->ifdef_stack_ptr++ = c; + goto test_skip; + case TOK_ELSE: + if (s1->ifdef_stack_ptr == s1->ifdef_stack) + tcc_error("#else without matching #if"); + if (s1->ifdef_stack_ptr[-1] & 2) + tcc_error("#else after #else"); + c = (s1->ifdef_stack_ptr[-1] ^= 3); + goto test_else; + case TOK_ELIF: + if (s1->ifdef_stack_ptr == s1->ifdef_stack) + tcc_error("#elif without matching #if"); + c = s1->ifdef_stack_ptr[-1]; + if (c > 1) + tcc_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_else: + if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1) + file->ifndef_macro = 0; + 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) + tcc_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_PPNUM: + n = strtoul((char*)tokc.cstr->data, &q, 10); + goto _line_num; + case TOK_LINE: + next(); + if (tok != TOK_CINT) +_line_err: + tcc_error("wrong #line format"); + n = tokc.i; +_line_num: + next(); + if (tok != TOK_LINEFEED) { + if (tok == TOK_STR) + pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.cstr->data); + else if (parse_flags & PARSE_FLAG_ASM_FILE) + break; + else + goto _line_err; + --n; + } + if (file->fd > 0) + total_lines += file->line_num - n; + file->line_num = n; + if (s1->do_debug) + put_stabs(file->filename, N_BINCL, 0, 0, 0); + 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; + if (ch == '\\') { + if (handle_stray_noerror() == 0) + --q; + } else + inp(); + } + *q = '\0'; + if (c == TOK_ERROR) + tcc_error("#error %s", buf); + else + tcc_warning("#warning %s", buf); + break; + case TOK_PRAGMA: + pragma_parse(s1); + break; + case TOK_LINEFEED: + goto the_end; + default: + /* ignore gas line comment in an 'S' file. */ + if (saved_parse_flags & PARSE_FLAG_ASM_FILE) + goto ignore; + if (tok == '!' && is_bof) + /* '!' is ignored at beginning to allow C scripts. */ + goto ignore; + tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); + ignore: + file->buf_ptr = parse_line_comment(file->buf_ptr); + goto the_end; + } + /* 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': + case 'u': + case 'U': + 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 <= '~') + tcc_warning("unknown escape sequence: \'\\%c\'", c); + else + tcc_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'); +} + +void parse_string(const char *s, int len) +{ + uint8_t buf[1000], *p = buf; + int is_long, sep; + + if ((is_long = *s == 'L')) + ++s, --len; + sep = *s++; + len -= 2; + if (len >= sizeof buf) + p = tcc_malloc(len + 1); + memcpy(p, s, len); + p[len] = 0; + + cstr_reset(&tokcstr); + parse_escape_string(&tokcstr, p, is_long); + if (p != buf) + tcc_free(p); + + if (sep == '\'') { + int char_size; + /* XXX: make it portable */ + if (!is_long) + char_size = 1; + else + char_size = sizeof(nwchar_t); + if (tokcstr.size <= char_size) + tcc_error("empty character constant"); + if (tokcstr.size > 2 * char_size) + tcc_warning("multi-character character constant"); + if (!is_long) { + tokc.i = *(int8_t *)tokcstr.data; + tok = TOK_CCHAR; + } else { + tokc.i = *(nwchar_t *)tokcstr.data; + tok = TOK_LCHAR; + } + } else { + tokc.cstr = &tokcstr; + if (!is_long) + tok = TOK_STR; + else + tok = TOK_LSTR; + } +} + +/* we use 64 bit numbers */ +#define BN_SIZE 2 + +/* bn = (bn << shift) | or_val */ +static 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); + } +} + +static 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 */ +static 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: + tcc_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 = 1; + 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) + tcc_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++; +#ifdef TCC_TARGET_PE + tok = TOK_CDOUBLE; + tokc.d = d; +#else + tok = TOK_CLDOUBLE; + /* XXX: not large enough */ + tokc.ld = (long double)d; +#endif + } 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++; +#ifdef TCC_TARGET_PE + tok = TOK_CDOUBLE; + tokc.d = strtod(token_buf, NULL); +#else + tok = TOK_CLDOUBLE; + tokc.ld = strtold(token_buf, NULL); +#endif + } else { + tok = TOK_CDOUBLE; + tokc.d = strtod(token_buf, NULL); + } + } + } else { + unsigned long long n, n1; + int lcount, ucount, must_64bit; + const char *p1; + + /* 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) + tcc_error("invalid digit"); + n1 = n; + n = n * b + t; + /* detect overflow */ + /* XXX: this test is not reliable */ + if (n < n1) + tcc_error("integer constant overflow"); + } + + /* Determine the characteristics (unsigned and/or 64bit) the type of + the constant must have according to the constant suffix(es) */ + lcount = ucount = must_64bit = 0; + p1 = p; + for(;;) { + t = toup(ch); + if (t == 'L') { + if (lcount >= 2) + tcc_error("three 'l's in integer constant"); + if (lcount && *(p - 1) != ch) + tcc_error("incorrect integer suffix: %s", p1); + lcount++; +#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE + if (lcount == 2) +#endif + must_64bit = 1; + ch = *p++; + } else if (t == 'U') { + if (ucount >= 1) + tcc_error("two 'u's in integer constant"); + ucount++; + ch = *p++; + } else { + break; + } + } + + /* Whether 64 bits are needed to hold the constant's value */ + if (n & 0xffffffff00000000LL || must_64bit) { + tok = TOK_CLLONG; + n1 = n >> 32; + } else { + tok = TOK_CINT; + n1 = n; + } + + /* Whether type must be unsigned to hold the constant's value */ + if (ucount || ((n1 >> 31) && (b != 10))) { + if (tok == TOK_CLLONG) + tok = TOK_CULLONG; + else + tok = TOK_CUINT; + /* If decimal and no unsigned suffix, bump to 64 bits or throw error */ + } else if (n1 >> 31) { + if (tok == TOK_CINT) + tok = TOK_CLLONG; + else + tcc_error("integer constant overflow"); + } + + if (tok == TOK_CINT || tok == TOK_CUINT) + tokc.ui = n; + else + tokc.ull = n; + } + if (ch) + tcc_error("invalid number\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': + tok = c; + p++; + if (parse_flags & PARSE_FLAG_SPACES) + goto keep_tok_flags; + while (isidnum_table[*p - CH_EOF] & IS_SPC) + ++p; + goto redo_no_start; + case '\f': + case '\v': + case '\r': + p++; + goto redo_no_start; + case '\\': + /* first look if it is in fact an end of buffer */ + c = handle_stray1(p); + p = file->buf_ptr; + if (c == '\\') + goto parse_simple; + if (c != CH_EOF) + goto redo_no_start; + { + TCCState *s1 = tcc_state; + if ((parse_flags & PARSE_FLAG_LINEFEED) + && !(tok_flags & TOK_FLAG_EOF)) { + tok_flags |= TOK_FLAG_EOF; + tok = TOK_LINEFEED; + goto keep_tok_flags; + } else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) { + tok = TOK_EOF; + } else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) { + tcc_error("missing #endif"); + } else if (s1->include_stack_ptr == s1->include_stack) { + /* no include left : end of file. */ + tok = TOK_EOF; + } else { + tok_flags &= ~TOK_FLAG_EOF; + /* 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->filename, file->ifndef_macro_saved); + tok_flags &= ~TOK_FLAG_ENDIF; + } + + /* add end of include file debug info */ + if (tcc_state->do_debug) { + put_stabd(N_EINCL, 0, 0); + } + /* pop include stack */ + tcc_close(); + s1->include_stack_ptr--; + p = file->buf_ptr; + goto redo_no_start; + } + } + break; + + case '\n': + file->line_num++; + tok_flags |= TOK_FLAG_BOL; + p++; +maybe_newline: + if (0 == (parse_flags & PARSE_FLAG_LINEFEED)) + goto redo_no_start; + tok = TOK_LINEFEED; + goto keep_tok_flags; + + 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 maybe_newline; + } else { + if (c == '#') { + p++; + tok = TOK_TWOSHARPS; + } else { + if (parse_flags & PARSE_FLAG_ASM_FILE) { + p = parse_line_comment(p - 1); + goto redo_no_start; + } else { + tok = '#'; + } + } + } + break; + + /* dollar is allowed to start identifiers when not parsing asm */ + case '$': + if (!(isidnum_table[c - CH_EOF] & IS_ID) + || (parse_flags & PARSE_FLAG_ASM_FILE)) + goto parse_simple; + + 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); + while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) + h = TOK_HASH_FUNC(h, c); + 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, (char *) 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 - CH_EOF] & (IS_ID|IS_NUM)) { + 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 (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) + || 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: + cstr_reset(&tokcstr); + if (is_long) + cstr_ccat(&tokcstr, 'L'); + cstr_ccat(&tokcstr, c); + p = parse_pp_string(p, c, &tokcstr); + cstr_ccat(&tokcstr, c); + cstr_ccat(&tokcstr, '\0'); + tokc.cstr = &tokcstr; + tok = TOK_PPSTR; + 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); + /* comments replaced by a blank */ + tok = ' '; + goto keep_tok_flags; + } else if (c == '/') { + p = parse_line_comment(p); + tok = ' '; + goto keep_tok_flags; + } 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 */ + parse_simple: + tok = c; + p++; + break; + default: + tcc_error("unrecognized character \\x%02x", c); + break; + } + tok_flags = 0; +keep_tok_flags: + file->buf_ptr = p; +#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_spc(void) +{ + if (macro_ptr) { + redo: + tok = *macro_ptr; + if (tok) { + TOK_GET(&tok, ¯o_ptr, &tokc); + if (tok == TOK_LINENUM) { + file->line_num = tokc.i; + goto redo; + } + } + } else { + next_nomacro1(); + } +} + +ST_FUNC void next_nomacro(void) +{ + do { + next_nomacro_spc(); + } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC)); +} + + +static void macro_subst( + TokenString *tok_str, + Sym **nested_list, + const int *macro_str, + int can_read_stream + ); + +/* substitute arguments in replacement lists in macro_str by the values in + args (field d) and return allocated string */ +static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) +{ + int t, t0, t1, spc; + const int *st; + Sym *s; + CValue cval; + TokenString str; + CString cstr; + + tok_str_new(&str); + t0 = t1 = 0; + while(1) { + TOK_GET(&t, ¯o_str, &cval); + if (!t) + break; + if (t == '#') { + /* stringize */ + TOK_GET(&t, ¯o_str, &cval); + if (!t) + goto bad_stringy; + s = sym_find2(args, t); + if (s) { + cstr_new(&cstr); + cstr_ccat(&cstr, '\"'); + st = s->d; + spc = 0; + while (*st) { + TOK_GET(&t, &st, &cval); + if (t != TOK_PLCHLDR + && t != TOK_NOSUBST + && 0 == check_space(t, &spc)) { + const char *s = get_tok_str(t, &cval); + while (*s) { + if (t == TOK_PPSTR && *s != '\'') + add_char(&cstr, *s); + else + cstr_ccat(&cstr, *s); + ++s; + } + } + } + cstr.size -= spc; + cstr_ccat(&cstr, '\"'); + cstr_ccat(&cstr, '\0'); +#ifdef PP_DEBUG + printf("\nstringize: <%s>\n", (char *)cstr.data); +#endif + /* add string */ + cval.cstr = &cstr; + tok_str_add2(&str, TOK_PPSTR, &cval); + cstr_free(cval.cstr); + } else { + bad_stringy: + expect("macro parameter after '#'"); + } + } else if (t >= TOK_IDENT) { + s = sym_find2(args, t); + if (s) { + int l0 = str.len; + st = s->d; + /* if '##' is present before or after, no arg substitution */ + if (*macro_str == TOK_TWOSHARPS || t1 == TOK_TWOSHARPS) { + /* special case for var arg macros : ## eats the ',' + if empty VA_ARGS variable. */ + if (t1 == TOK_TWOSHARPS && t0 == ',' && gnu_ext && s->type.t) { + if (*st == 0) { + /* suppress ',' '##' */ + str.len -= 2; + } else { + /* suppress '##' and add variable */ + str.len--; + goto add_var; + } + } else { + for(;;) { + int t1; + TOK_GET(&t1, &st, &cval); + if (!t1) + break; + tok_str_add2(&str, t1, &cval); + } + } + + } else { + add_var: + /* NOTE: the stream cannot be read when macro + substituing an argument */ + macro_subst(&str, nested_list, st, 0); + } + if (str.len == l0) /* exanded to empty string */ + tok_str_add(&str, TOK_PLCHLDR); + } else { + tok_str_add(&str, t); + } + } else { + tok_str_add2(&str, t, &cval); + } + t0 = t1, t1 = 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" +}; + +/* peek or read [ws_str == NULL] next token from function macro call, + walking up macro levels up to the file if necessary */ +static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *ws_str) +{ + int t; + const int *p; + Sym *sa; + + for (;;) { + if (macro_ptr) { + p = macro_ptr, t = *p; + if (ws_str) { + while (is_space(t) || TOK_LINEFEED == t) + tok_str_add(ws_str, t), t = *++p; + } + if (t == 0 && can_read_stream) { + end_macro(); + /* also, end of scope for nested defined symbol */ + sa = *nested_list; + while (sa && sa->v == -1) + sa = sa->prev; + if (sa) + sa->v = -1; + continue; + } + } else { + ch = handle_eob(); + if (ws_str) { + while (is_space(ch) || ch == '\n' || ch == '/') { + if (ch == '/') { + int c; + uint8_t *p = file->buf_ptr; + PEEKC(c, p); + if (c == '*') { + p = parse_comment(p); + file->buf_ptr = p - 1; + } else if (c == '/') { + p = parse_line_comment(p); + file->buf_ptr = p - 1; + } else + break; + ch = ' '; + } + tok_str_add(ws_str, ch); + cinp(); + } + } + t = ch; + } + + if (ws_str) + return t; + next_nomacro_spc(); + return tok; + } +} + +/* 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, + int can_read_stream) +{ + Sym *args, *sa, *sa1; + int parlevel, *mstr, t, t1, spc; + 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 { + int saved_parse_flags = parse_flags; + + mstr = s->d; + if (s->type.t == MACRO_FUNC) { + /* whitespace between macro name and argument list */ + TokenString ws_str; + tok_str_new(&ws_str); + + spc = 0; + parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED + | PARSE_FLAG_ACCEPT_STRAYS; + + /* get next token from argument stream */ + t = next_argstream(nested_list, can_read_stream, &ws_str); + if (t != '(') { + /* not a macro substitution after all, restore the + * macro token plus all whitespace we've read. + * whitespace is intentionally not merged to preserve + * newlines. */ + parse_flags = saved_parse_flags; + tok_str_add(tok_str, tok); + if (parse_flags & PARSE_FLAG_SPACES) { + int i; + for (i = 0; i < ws_str.len; i++) + tok_str_add(tok_str, ws_str.str[i]); + } + tok_str_free(ws_str.str); + return 0; + } else { + tok_str_free(ws_str.str); + } + next_nomacro(); /* eat '(' */ + + /* argument macro */ + args = NULL; + sa = s->next; + /* NOTE: empty args are allowed, except if no args */ + for(;;) { + do { + next_argstream(nested_list, can_read_stream, NULL); + } while (is_space(tok) || TOK_LINEFEED == tok); + empty_arg: + /* handle '()' case */ + if (!args && !sa && tok == ')') + break; + if (!sa) + tcc_error("macro '%s' used with too many args", + get_tok_str(s->v, 0)); + tok_str_new(&str); + parlevel = spc = 0; + /* NOTE: non zero sa->t indicates VA_ARGS */ + while ((parlevel > 0 || + (tok != ')' && + (tok != ',' || sa->type.t)))) { + if (tok == TOK_EOF || tok == 0) + break; + if (tok == '(') + parlevel++; + else if (tok == ')') + parlevel--; + if (tok == TOK_LINEFEED) + tok = ' '; + if (!check_space(tok, &spc)) + tok_str_add2(&str, tok, &tokc); + next_argstream(nested_list, can_read_stream, NULL); + } + if (parlevel) + expect(")"); + str.len -= spc; + tok_str_add(&str, 0); + sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); + sa1->d = 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) + goto empty_arg; + break; + } + if (tok != ',') + expect(","); + } + if (sa) { + tcc_error("macro '%s' used with too few args", + get_tok_str(s->v, 0)); + } + + parse_flags = saved_parse_flags; + + /* now subst each arg */ + mstr = macro_arg_subst(nested_list, mstr, args); + /* free memory */ + sa = args; + while (sa) { + sa1 = sa->prev; + tok_str_free(sa->d); + sym_free(sa); + sa = sa1; + } + } + + sym_push2(nested_list, s->v, 0, 0); + parse_flags = saved_parse_flags; + 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 != s->d) + tok_str_free(mstr); + } + return 0; +} + +int paste_tokens(int t1, CValue *v1, int t2, CValue *v2) +{ + CString cstr; + int n; + + cstr_new(&cstr); + if (t1 != TOK_PLCHLDR) + cstr_cat(&cstr, get_tok_str(t1, v1)); + n = cstr.size; + if (t2 != TOK_PLCHLDR) + cstr_cat(&cstr, get_tok_str(t2, v2)); + cstr_ccat(&cstr, '\0'); + + tcc_open_bf(tcc_state, ":paste:", cstr.size); + memcpy(file->buffer, cstr.data, cstr.size); + for (;;) { + next_nomacro1(); + if (0 == *file->buf_ptr) + break; + if (is_space(tok)) + continue; + tcc_warning("pasting <%.*s> and <%s> does not give a valid preprocessing token", + n, cstr.data, (char*)cstr.data + n); + break; + } + tcc_close(); + + //printf("paste <%s>\n", (char*)cstr.data); + cstr_free(&cstr); + 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 *ptr0) +{ + int t; + CValue cval; + TokenString macro_str1; + int start_of_nosubsts = -1; + const int *ptr; + + /* we search the first '##' */ + for (ptr = ptr0;;) { + TOK_GET(&t, &ptr, &cval); + if (t == TOK_TWOSHARPS) + break; + if (t == 0) + return NULL; + } + + tok_str_new(¯o_str1); + + //tok_print(" $$$", ptr0); + for (ptr = ptr0;;) { + TOK_GET(&t, &ptr, &cval); + if (t == 0) + break; + if (t == TOK_TWOSHARPS) + continue; + while (*ptr == TOK_TWOSHARPS) { + int t1; CValue cv1; + /* given 'a##b', remove nosubsts preceding 'a' */ + if (start_of_nosubsts >= 0) + macro_str1.len = start_of_nosubsts; + /* given 'a##b', remove nosubsts preceding 'b' */ + while ((t1 = *++ptr) == TOK_NOSUBST) + ; + if (t1 && t1 != TOK_TWOSHARPS + && t1 != ':') /* 'a##:' don't build a new token */ + { + TOK_GET(&t1, &ptr, &cv1); + if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) { + paste_tokens(t, &cval, t1, &cv1); + t = tok, cval = tokc; + } + } + } + if (t == TOK_NOSUBST) { + if (start_of_nosubsts < 0) + start_of_nosubsts = macro_str1.len; + } else { + start_of_nosubsts = -1; + } + tok_str_add2(¯o_str1, t, &cval); + } + tok_str_add(¯o_str1, 0); + //tok_print(" ###", macro_str1.str); + 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, + int can_read_stream + ) +{ + Sym *s; + const int *ptr; + int t, spc, nosubst; + CValue cval; + int *macro_str1 = NULL; + + /* first scan for '##' operator handling */ + ptr = macro_str; + spc = nosubst = 0; + + /* first scan for '##' operator handling */ + if (can_read_stream) { + macro_str1 = macro_twosharps(ptr); + if (macro_str1) + ptr = macro_str1; + } + + while (1) { + TOK_GET(&t, &ptr, &cval); + if (t == 0) + break; + + if (t >= TOK_IDENT && 0 == nosubst) { + s = define_find(t); + if (s == NULL) + goto no_subst; + + /* if nested substitution, do nothing */ + if (sym_find2(*nested_list, t)) { + /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */ + tok_str_add2(tok_str, TOK_NOSUBST, NULL); + goto no_subst; + } + + { + TokenString str; + str.str = (int*)ptr; + begin_macro(&str, 2); + + tok = t; + macro_subst_tok(tok_str, nested_list, s, can_read_stream); + + if (str.alloc == 3) { + /* already finished by reading function macro arguments */ + break; + } + + ptr = macro_ptr; + end_macro (); + } + + spc = tok_str->len && is_space(tok_str->str[tok_str->len-1]); + + } else { + + if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) + tcc_error("stray '\\' in program"); + +no_subst: + if (!check_space(t, &spc)) + tok_str_add2(tok_str, t, &cval); + nosubst = 0; + if (t == TOK_NOSUBST) + nosubst = 1; + } + } + if (macro_str1) + tok_str_free(macro_str1); + +} + +/* return next token with macro substitution */ +ST_FUNC void next(void) +{ + redo: + if (parse_flags & PARSE_FLAG_SPACES) + next_nomacro_spc(); + else + next_nomacro(); + + if (macro_ptr) { + if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) { + /* discard preprocessor markers */ + goto redo; + } else if (tok == 0) { + /* end of macro or unget token string */ + end_macro(); + goto redo; + } + } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) { + Sym *s; + /* if reading from file, try to substitute macros */ + s = define_find(tok); + if (s) { + static TokenString str; /* using static string for speed */ + Sym *nested_list = NULL; + tok_str_new(&str); + nested_list = NULL; + macro_subst_tok(&str, &nested_list, s, 1); + tok_str_add(&str, 0); + begin_macro(&str, 0); + goto redo; + } + } + /* convert preprocessor tokens into C tokens */ + if (tok == TOK_PPNUM) { + if (parse_flags & PARSE_FLAG_TOK_NUM) + parse_number((char *)tokc.cstr->data); + } else if (tok == TOK_PPSTR) { + if (parse_flags & PARSE_FLAG_TOK_STR) + parse_string((char *)tokc.cstr->data, tokc.cstr->size - 1); + } +} + +/* push back current token and set current token to 'last_tok'. Only + identifier case handled for labels. */ +ST_INLN void unget_tok(int last_tok) +{ + TokenString *str = tcc_malloc(sizeof *str); + tok_str_new(str); + tok_str_add2(str, tok, &tokc); + tok_str_add(str, 0); + begin_macro(str, 1); + tok = last_tok; +} + +/* better than nothing, but needs extension to handle '-E' option + correctly too */ +ST_FUNC 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; + + pvtop = vtop = vstack - 1; + s1->pack_stack[0] = 0; + s1->pack_stack_ptr = s1->pack_stack; + + isidnum_table['$' - CH_EOF] = + tcc_state->dollars_in_identifiers ? IS_ID : 0; +} + +ST_FUNC void preprocess_new(void) +{ + int i, c; + const char *p, *r; + + /* init isid table */ + for(i = CH_EOF; i<256; i++) + isidnum_table[i - CH_EOF] + = is_space(i) ? IS_SPC + : isid(i) ? IS_ID + : isnum(i) ? IS_NUM + : 0; + + 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; + } + tok_alloc(p, r - p - 1); + p = r; + } +} + +ST_FUNC void preprocess_delete(void) +{ + int i, n; + + /* free -D and compiler defines */ + free_defines(NULL); + + /* cleanup from error/setjmp */ + while (macro_stack) + end_macro(); + macro_ptr = NULL; + + /* free tokens */ + n = tok_ident - TOK_IDENT; + for(i = 0; i < n; i++) + tcc_free(table_ident[i]); + tcc_free(table_ident); + table_ident = NULL; +} + +/* Preprocess the current file */ +ST_FUNC int tcc_preprocess(TCCState *s1) +{ + BufferedFile **iptr; + int token_seen, spcs, level; + + preprocess_init(s1); + ch = file->buf_ptr[0]; + tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; + parse_flags = PARSE_FLAG_PREPROCESS + | (parse_flags & PARSE_FLAG_ASM_FILE) + | PARSE_FLAG_LINEFEED + | PARSE_FLAG_SPACES + | PARSE_FLAG_ACCEPT_STRAYS + ; + +#ifdef PP_BENCH + do next(); while (tok != TOK_EOF); return 0; +#endif + + token_seen = spcs = 0; + pp_line(s1, file, 0); + + for (;;) { + iptr = s1->include_stack_ptr; + next(); + if (tok == TOK_EOF) + break; + level = s1->include_stack_ptr - iptr; + if (level) { + if (level > 0) + pp_line(s1, *iptr, 0); + pp_line(s1, file, level); + } + + if (0 == token_seen) { + if (tok == ' ') { + ++spcs; + continue; + } + if (tok == TOK_LINEFEED) { + spcs = 0; + continue; + } + pp_line(s1, file, 0); + while (spcs) + fputs(" ", s1->ppfp), --spcs; + token_seen = 1; + + } else if (tok == TOK_LINEFEED) { + ++file->line_ref; + token_seen = 0; + } + + fputs(get_tok_str(tok, &tokc), s1->ppfp); + } + + return 0; +} diff --git a/src/tccrun.c b/src/tccrun.c new file mode 100644 index 0000000..cb98392 --- /dev/null +++ b/src/tccrun.c @@ -0,0 +1,772 @@ +/* + * TCC - Tiny C Compiler - Support for -run switch + * + * 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 + */ + +#include "tcc.h" + +/* only native compiler supports -run */ +#ifdef TCC_IS_NATIVE + +#ifdef CONFIG_TCC_BACKTRACE +ST_DATA int rt_num_callers = 6; +ST_DATA const char **rt_bound_error_msg; +ST_DATA void *rt_prog_main; +#endif + +#ifdef _WIN32 +#define ucontext_t CONTEXT +#endif + +static void set_pages_executable(void *ptr, unsigned long length); +static void set_exception_handler(void); +static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level); +static void rt_error(ucontext_t *uc, const char *fmt, ...); +static int tcc_relocate_ex(TCCState *s1, void *ptr); + +#ifdef _WIN64 +static void win64_add_function_table(TCCState *s1); +#endif + +/* ------------------------------------------------------------- */ +/* Do all relocations (needed before using tcc_get_symbol()) + Returns -1 on error. */ + +LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) +{ + int ret; + + if (TCC_RELOCATE_AUTO != ptr) + return tcc_relocate_ex(s1, ptr); + + ret = tcc_relocate_ex(s1, NULL); + if (ret < 0) + return ret; + +#ifdef HAVE_SELINUX + { /* Use mmap instead of malloc for Selinux. Ref: + http://www.gnu.org/s/libc/manual/html_node/File-Size.html */ + + char tmpfname[] = "/tmp/.tccrunXXXXXX"; + int fd = mkstemp (tmpfname); + + s1->mem_size = ret; + unlink (tmpfname); + ftruncate (fd, s1->mem_size); + + s1->write_mem = mmap (NULL, ret, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, 0); + if (s1->write_mem == MAP_FAILED) + tcc_error("/tmp not writeable"); + + s1->runtime_mem = mmap (NULL, ret, PROT_READ|PROT_EXEC, + MAP_SHARED, fd, 0); + if (s1->runtime_mem == MAP_FAILED) + tcc_error("/tmp not executable"); + + ret = tcc_relocate_ex(s1, s1->write_mem); + } +#else + s1->runtime_mem = tcc_malloc(ret); + ret = tcc_relocate_ex(s1, s1->runtime_mem); +#endif + return ret; +} + +/* launch the compiled program with the given arguments */ +LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) +{ + int (*prog_main)(int, char **); + int ret; + + if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0) + return -1; + + prog_main = tcc_get_symbol_err(s1, s1->runtime_main); + +#ifdef CONFIG_TCC_BACKTRACE + if (s1->do_debug) { + set_exception_handler(); + rt_prog_main = prog_main; + } +#endif + +#ifdef CONFIG_TCC_BCHECK + if (s1->do_bounds_check) { + void (*bound_init)(void); + void (*bound_exit)(void); + void (*bound_new_region)(void *p, addr_t size); + int (*bound_delete_region)(void *p); + int i; + + /* set error function */ + rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg"); + /* XXX: use .init section so that it also work in binary ? */ + bound_init = tcc_get_symbol_err(s1, "__bound_init"); + bound_exit = tcc_get_symbol_err(s1, "__bound_exit"); + bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region"); + bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region"); + bound_init(); + /* mark argv area as valid */ + bound_new_region(argv, argc*sizeof(argv[0])); + for (i=0; i<argc; ++i) + bound_new_region(argv[i], strlen(argv[i])); + + errno = 0; /* clean errno value */ + ret = (*prog_main)(argc, argv); + + /* unmark argv area */ + for (i=0; i<argc; ++i) + bound_delete_region(argv[i]); + bound_delete_region(argv); + + bound_exit(); + } else +#endif + { + errno = 0; /* clean errno value */ + ret = (*prog_main)(argc, argv); + } + return ret; +} + +/* relocate code. Return -1 on error, required size if ptr is NULL, + otherwise copy code into buffer passed by the caller */ +static int tcc_relocate_ex(TCCState *s1, void *ptr) +{ + Section *s; + unsigned long offset, length; + addr_t mem; + int i; + + if (NULL == ptr) { + s1->nb_errors = 0; +#ifdef TCC_TARGET_PE + pe_output_file(s1, NULL); +#else + tcc_add_runtime(s1); + relocate_common_syms(); + tcc_add_linker_symbols(s1); + build_got_entries(s1); +#endif + if (s1->nb_errors) + return -1; + } + + offset = 0, mem = (addr_t)ptr; + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (0 == (s->sh_flags & SHF_ALLOC)) + continue; + length = s->data_offset; + s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0; + offset = (offset + length + 15) & ~15; + } + offset += 16; + + /* relocate symbols */ + relocate_syms(s1, 1); + if (s1->nb_errors) + return -1; + + if (0 == mem) + return offset; + + /* relocate each section */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->reloc) + relocate_section(s1, s); + } + relocate_plt(s1); + + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (0 == (s->sh_flags & SHF_ALLOC)) + continue; + length = s->data_offset; + // printf("%-12s %08lx %04x\n", s->name, s->sh_addr, length); + ptr = (void*)s->sh_addr; + if (NULL == s->data || s->sh_type == SHT_NOBITS) + memset(ptr, 0, length); + else + memcpy(ptr, s->data, length); + /* mark executable sections as executable in memory */ + if (s->sh_flags & SHF_EXECINSTR) + set_pages_executable(ptr, length); + } + +#ifdef _WIN64 + win64_add_function_table(s1); +#endif + return 0; +} + +/* ------------------------------------------------------------- */ +/* allow to run code in memory */ + +static void set_pages_executable(void *ptr, unsigned long length) +{ +#ifdef _WIN32 + unsigned long old_protect; + VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect); +#else + extern void __clear_cache(char *beginning, char *end); +#ifndef PAGESIZE +# define PAGESIZE 4096 +#endif + addr_t start, end; + start = (addr_t)ptr & ~(PAGESIZE - 1); + end = (addr_t)ptr + length; + end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); + mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); + __clear_cache(ptr, ptr + length); +#endif +} + +/* ------------------------------------------------------------- */ +#ifdef CONFIG_TCC_BACKTRACE + +ST_FUNC void tcc_set_num_callers(int n) +{ + rt_num_callers = n; +} + +/* print the position in the source file of PC value 'pc' by reading + the stabs debug information */ +static addr_t rt_printline(addr_t wanted_pc, const char *msg) +{ + char func_name[128], last_func_name[128]; + addr_t func_addr, last_pc, pc; + const char *incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num, i; + const char *str, *p; + + Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym; + int stab_len = 0; + char *stab_str = NULL; + + if (stab_section) { + stab_len = stab_section->data_offset; + stab_sym = (Stab_Sym *)stab_section->data; + stab_str = (char *) stabstr_section->data; + } + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = (addr_t)-1; + last_line_num = 1; + + if (!stab_sym) + goto no_stabs; + + stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len); + for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) { + 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 = stab_str + 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 = stab_str + 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 = stab_str + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl; + } + break; + } + } + +no_stabs: + /* second pass: we try symtab symbols (no line number info) */ + incl_index = 0; + if (symtab_section) + { + ElfW(Sym) *sym, *sym_end; + int type; + + sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); + for(sym = (ElfW(Sym) *)symtab_section->data + 1; + sym < sym_end; + sym++) { + type = ELFW(ST_TYPE)(sym->st_info); + if (type == STT_FUNC || type == STT_GNU_IFUNC) { + if (wanted_pc >= sym->st_value && + wanted_pc < sym->st_value + sym->st_size) { + pstrcpy(last_func_name, sizeof(last_func_name), + (char *) strtab_section->data + sym->st_name); + func_addr = sym->st_value; + goto found; + } + } + } + } + /* did not find any info: */ + fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc); + fflush(stderr); + return 0; + found: + i = incl_index; + if (i > 0) + fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num); + fprintf(stderr, "%s %p", msg, (void*)wanted_pc); + if (last_func_name[0] != '\0') + fprintf(stderr, " %s()", last_func_name); + if (--i >= 0) { + fprintf(stderr, " (included from "); + for (;;) { + fprintf(stderr, "%s", incl_files[i]); + if (--i < 0) + break; + fprintf(stderr, ", "); + } + fprintf(stderr, ")"); + } + fprintf(stderr, "\n"); + fflush(stderr); + return func_addr; +} + +/* emit a run time error at position 'pc' */ +static void rt_error(ucontext_t *uc, const char *fmt, ...) +{ + va_list ap; + addr_t pc; + int i; + + fprintf(stderr, "Runtime error: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + for(i=0;i<rt_num_callers;i++) { + if (rt_get_caller_pc(&pc, uc, i) < 0) + break; + pc = rt_printline(pc, i ? "by" : "at"); + if (pc == (addr_t)rt_prog_main && pc) + break; + } +} + +/* ------------------------------------------------------------- */ +#ifndef _WIN32 + +/* 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); +} + +#ifndef SA_SIGINFO +# define SA_SIGINFO 0x00000004u +#endif + +/* Generate a stack backtrace when a CPU exception occurs. */ +static void set_exception_handler(void) +{ + 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); +} + +/* ------------------------------------------------------------- */ +#ifdef __i386__ + +/* fix for glibc 2.1 */ +#ifndef REG_EIP +#define REG_EIP EIP +#define REG_EBP EBP +#endif + +/* return the PC at frame level 'level'. Return negative if not found */ +static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) +{ + addr_t fp; + int i; + + if (level == 0) { +#if defined(__APPLE__) + *paddr = uc->uc_mcontext->__ss.__eip; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + *paddr = uc->uc_mcontext.mc_eip; +#elif defined(__dietlibc__) + *paddr = uc->uc_mcontext.eip; +#elif defined(__NetBSD__) + *paddr = uc->uc_mcontext.__gregs[_REG_EIP]; +#else + *paddr = uc->uc_mcontext.gregs[REG_EIP]; +#endif + return 0; + } else { +#if defined(__APPLE__) + fp = uc->uc_mcontext->__ss.__ebp; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + fp = uc->uc_mcontext.mc_ebp; +#elif defined(__dietlibc__) + fp = uc->uc_mcontext.ebp; +#elif defined(__NetBSD__) + fp = uc->uc_mcontext.__gregs[_REG_EBP]; +#else + fp = uc->uc_mcontext.gregs[REG_EBP]; +#endif + for(i=1;i<level;i++) { + /* XXX: check address validity with program info */ + if (fp <= 0x1000 || fp >= 0xc0000000) + return -1; + fp = ((addr_t *)fp)[0]; + } + *paddr = ((addr_t *)fp)[1]; + return 0; + } +} + +/* ------------------------------------------------------------- */ +#elif defined(__x86_64__) + +/* return the PC at frame level 'level'. Return negative if not found */ +static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) +{ + addr_t fp; + int i; + + if (level == 0) { + /* XXX: only support linux */ +#if defined(__APPLE__) + *paddr = uc->uc_mcontext->__ss.__rip; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + *paddr = uc->uc_mcontext.mc_rip; +#elif defined(__NetBSD__) + *paddr = uc->uc_mcontext.__gregs[_REG_RIP]; +#else + *paddr = uc->uc_mcontext.gregs[REG_RIP]; +#endif + return 0; + } else { +#if defined(__APPLE__) + fp = uc->uc_mcontext->__ss.__rbp; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + fp = uc->uc_mcontext.mc_rbp; +#elif defined(__NetBSD__) + fp = uc->uc_mcontext.__gregs[_REG_RBP]; +#else + fp = uc->uc_mcontext.gregs[REG_RBP]; +#endif + for(i=1;i<level;i++) { + /* XXX: check address validity with program info */ + if (fp <= 0x1000) + return -1; + fp = ((addr_t *)fp)[0]; + } + *paddr = ((addr_t *)fp)[1]; + return 0; + } +} + +/* ------------------------------------------------------------- */ +#elif defined(__arm__) + +/* return the PC at frame level 'level'. Return negative if not found */ +static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) +{ + addr_t fp, sp; + int i; + + if (level == 0) { + /* XXX: only supports linux */ +#if defined(__linux__) + *paddr = uc->uc_mcontext.arm_pc; +#else + return -1; +#endif + return 0; + } else { +#if defined(__linux__) + fp = uc->uc_mcontext.arm_fp; + sp = uc->uc_mcontext.arm_sp; + if (sp < 0x1000) + sp = 0x1000; +#else + return -1; +#endif + /* XXX: specific to tinycc stack frames */ + if (fp < sp + 12 || fp & 3) + return -1; + for(i = 1; i < level; i++) { + sp = ((addr_t *)fp)[-2]; + if (sp < fp || sp - fp > 16 || sp & 3) + return -1; + fp = ((addr_t *)fp)[-3]; + if (fp <= sp || fp - sp < 12 || fp & 3) + return -1; + } + /* XXX: check address validity with program info */ + *paddr = ((addr_t *)fp)[-1]; + return 0; + } +} + +/* ------------------------------------------------------------- */ +#elif defined(__aarch64__) + +static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) +{ + if (level < 0) + return -1; + else if (level == 0) { + *paddr = uc->uc_mcontext.pc; + return 0; + } + else { + addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29]; + int i; + for (i = 1; i < level; i++) + fp = (addr_t *)fp[0]; + *paddr = fp[1]; + return 0; + } +} + +/* ------------------------------------------------------------- */ +#else + +#warning add arch specific rt_get_caller_pc() +static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level) +{ + return -1; +} + +#endif /* !__i386__ */ + +/* ------------------------------------------------------------- */ +#else /* WIN32 */ + +static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) +{ + EXCEPTION_RECORD *er = ex_info->ExceptionRecord; + CONTEXT *uc = ex_info->ContextRecord; + switch (er->ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + if (rt_bound_error_msg && *rt_bound_error_msg) + rt_error(uc, *rt_bound_error_msg); + else + rt_error(uc, "access violation"); + break; + case EXCEPTION_STACK_OVERFLOW: + rt_error(uc, "stack overflow"); + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + rt_error(uc, "division by zero"); + break; + default: + rt_error(uc, "exception caught"); + break; + } + return EXCEPTION_EXECUTE_HANDLER; +} + +/* Generate a stack backtrace when a CPU exception occurs. */ +static void set_exception_handler(void) +{ + SetUnhandledExceptionFilter(cpu_exception_handler); +} + +#ifdef _WIN64 +static void win64_add_function_table(TCCState *s1) +{ + RtlAddFunctionTable( + (RUNTIME_FUNCTION*)s1->uw_pdata->sh_addr, + s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION), + text_section->sh_addr + ); +} +#endif + +/* return the PC at frame level 'level'. Return non zero if not found */ +static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level) +{ + addr_t fp, pc; + int i; +#ifdef _WIN64 + pc = uc->Rip; + fp = uc->Rbp; +#else + pc = uc->Eip; + fp = uc->Ebp; +#endif + if (level > 0) { + for(i = 1; i < level; i++) { + /* XXX: check address validity with program info */ + if (fp <= 0x1000 || fp >= 0xc0000000) + return -1; + fp = ((addr_t*)fp)[0]; + } + pc = ((addr_t*)fp)[1]; + } + *paddr = pc; + return 0; +} + +#endif /* _WIN32 */ +#endif /* CONFIG_TCC_BACKTRACE */ +/* ------------------------------------------------------------- */ +#ifdef CONFIG_TCC_STATIC + +/* dummy function for profiling */ +ST_FUNC void *dlopen(const char *filename, int flag) +{ + return NULL; +} + +ST_FUNC void dlclose(void *p) +{ +} + +ST_FUNC const char *dlerror(void) +{ + return "error"; +} + +typedef struct TCCSyms { + char *str; + void *ptr; +} TCCSyms; + + +/* add the symbol you want here if no dynamic linking is done */ +static TCCSyms tcc_syms[] = { +#if !defined(CONFIG_TCCBOOT) +#define TCCSYM(a) { #a, &a, }, + TCCSYM(printf) + TCCSYM(fprintf) + TCCSYM(fopen) + TCCSYM(fclose) +#undef TCCSYM +#endif + { NULL, NULL }, +}; + +ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol) +{ + TCCSyms *p; + p = tcc_syms; + while (p->str != NULL) { + if (!strcmp(p->str, symbol)) + return p->ptr; + p++; + } + return NULL; +} + +#elif !defined(_WIN32) + +ST_FUNC void *resolve_sym(TCCState *s1, const char *sym) +{ + return dlsym(RTLD_DEFAULT, sym); +} + +#endif /* CONFIG_TCC_STATIC */ +#endif /* TCC_IS_NATIVE */ +/* ------------------------------------------------------------- */ diff --git a/src/tcctok.h b/src/tcctok.h new file mode 100644 index 0000000..5972fd6 --- /dev/null +++ b/src/tcctok.h @@ -0,0 +1,335 @@ +/* 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__") + +#ifdef TCC_TARGET_ARM64 + DEF(TOK_UINT128, "__uint128_t") +#endif + +/*********************************************************************/ +/* 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__") + +/* special floating point values */ + DEF(TOK___NAN__, "__nan__") + DEF(TOK___SNAN__, "__snan__") + DEF(TOK___INF__, "__inf__") + +/* 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_WEAK1, "weak") + DEF(TOK_WEAK2, "__weak__") + DEF(TOK_ALIAS1, "alias") + DEF(TOK_ALIAS2, "__alias__") + 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_FASTCALL1, "fastcall") + DEF(TOK_FASTCALL2, "__fastcall") + DEF(TOK_FASTCALL3, "__fastcall__") + DEF(TOK_MODE, "__mode__") + DEF(TOK_MODE_DI, "__DI__") + DEF(TOK_MODE_HI, "__HI__") + DEF(TOK_MODE_SI, "__SI__") + DEF(TOK_DLLEXPORT, "dllexport") + DEF(TOK_DLLIMPORT, "dllimport") + DEF(TOK_NORETURN1, "noreturn") + DEF(TOK_NORETURN2, "__noreturn__") + DEF(TOK_VISIBILITY1, "visibility") + DEF(TOK_VISIBILITY2, "__visibility__") + DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p") + DEF(TOK_builtin_constant_p, "__builtin_constant_p") + DEF(TOK_builtin_frame_address, "__builtin_frame_address") + DEF(TOK_builtin_return_address, "__builtin_return_address") +#ifdef TCC_TARGET_X86_64 +#ifdef TCC_TARGET_PE + DEF(TOK_builtin_va_start, "__builtin_va_start") +#else + DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types") +#endif +#endif + DEF(TOK_REGPARM1, "regparm") + DEF(TOK_REGPARM2, "__regparm__") + +#ifdef TCC_TARGET_ARM64 + DEF(TOK___va_start, "__va_start") + DEF(TOK___va_arg, "__va_arg") +#endif + +/* pragma */ + DEF(TOK_pack, "pack") +#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64) + /* already defined for assembler */ + DEF(TOK_ASM_push, "push") + DEF(TOK_ASM_pop, "pop") +#endif + DEF(TOK_comment, "comment") + DEF(TOK_lib, "lib") + DEF(TOK_push_macro, "push_macro") + DEF(TOK_pop_macro, "pop_macro") + DEF(TOK_once, "once") + +/* builtin functions or variables */ +#ifndef TCC_ARM_EABI + DEF(TOK_memcpy, "memcpy") + DEF(TOK_memset, "memset") + DEF(TOK___divdi3, "__divdi3") + DEF(TOK___moddi3, "__moddi3") + DEF(TOK___udivdi3, "__udivdi3") + DEF(TOK___umoddi3, "__umoddi3") + DEF(TOK___ashrdi3, "__ashrdi3") + DEF(TOK___lshrdi3, "__lshrdi3") + DEF(TOK___ashldi3, "__ashldi3") + DEF(TOK___floatundisf, "__floatundisf") + DEF(TOK___floatundidf, "__floatundidf") +# ifndef TCC_ARM_VFP + DEF(TOK___floatundixf, "__floatundixf") + DEF(TOK___fixunsxfdi, "__fixunsxfdi") +# endif + DEF(TOK___fixunssfdi, "__fixunssfdi") + DEF(TOK___fixunsdfdi, "__fixunsdfdi") +#endif + +#if defined TCC_TARGET_ARM +# ifdef TCC_ARM_EABI + DEF(TOK_memcpy, "__aeabi_memcpy") + DEF(TOK_memcpy4, "__aeabi_memcpy4") + DEF(TOK_memcpy8, "__aeabi_memcpy8") + DEF(TOK_memset, "__aeabi_memset") + DEF(TOK___aeabi_ldivmod, "__aeabi_ldivmod") + DEF(TOK___aeabi_uldivmod, "__aeabi_uldivmod") + DEF(TOK___aeabi_idivmod, "__aeabi_idivmod") + DEF(TOK___aeabi_uidivmod, "__aeabi_uidivmod") + DEF(TOK___divsi3, "__aeabi_idiv") + DEF(TOK___udivsi3, "__aeabi_uidiv") + DEF(TOK___floatdisf, "__aeabi_l2f") + DEF(TOK___floatdidf, "__aeabi_l2d") + DEF(TOK___fixsfdi, "__aeabi_f2lz") + DEF(TOK___fixdfdi, "__aeabi_d2lz") + DEF(TOK___ashrdi3, "__aeabi_lasr") + DEF(TOK___lshrdi3, "__aeabi_llsr") + DEF(TOK___ashldi3, "__aeabi_llsl") + DEF(TOK___floatundisf, "__aeabi_ul2f") + DEF(TOK___floatundidf, "__aeabi_ul2d") + DEF(TOK___fixunssfdi, "__aeabi_f2ulz") + DEF(TOK___fixunsdfdi, "__aeabi_d2ulz") +# else + DEF(TOK___modsi3, "__modsi3") + DEF(TOK___umodsi3, "__umodsi3") + DEF(TOK___divsi3, "__divsi3") + DEF(TOK___udivsi3, "__udivsi3") + DEF(TOK___floatdisf, "__floatdisf") + DEF(TOK___floatdidf, "__floatdidf") +# ifndef TCC_ARM_VFP + DEF(TOK___floatdixf, "__floatdixf") + DEF(TOK___fixunssfsi, "__fixunssfsi") + DEF(TOK___fixunsdfsi, "__fixunsdfsi") + DEF(TOK___fixunsxfsi, "__fixunsxfsi") + DEF(TOK___fixxfdi, "__fixxfdi") +# endif + DEF(TOK___fixsfdi, "__fixsfdi") + DEF(TOK___fixdfdi, "__fixdfdi") +# endif +#endif + +#if 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") +#endif + +#if defined TCC_TARGET_I386 + DEF(TOK___fixsfdi, "__fixsfdi") + DEF(TOK___fixdfdi, "__fixdfdi") + DEF(TOK___fixxfdi, "__fixxfdi") + + #ifndef COMMIT_4ad186c5ef61_IS_FIXED + DEF(TOK___tcc_cvt_ftol, "__tcc_cvt_ftol") + #endif +#endif + +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 + DEF(TOK_alloca, "alloca") +#endif + +#if defined TCC_TARGET_PE + DEF(TOK___chkstk, "__chkstk") +#endif +#ifdef TCC_TARGET_ARM64 + DEF(TOK___arm64_clear_cache, "__arm64_clear_cache") + DEF(TOK___addtf3, "__addtf3") + DEF(TOK___subtf3, "__subtf3") + DEF(TOK___multf3, "__multf3") + DEF(TOK___divtf3, "__divtf3") + DEF(TOK___extendsftf2, "__extendsftf2") + DEF(TOK___extenddftf2, "__extenddftf2") + DEF(TOK___trunctfsf2, "__trunctfsf2") + DEF(TOK___trunctfdf2, "__trunctfdf2") + DEF(TOK___fixtfsi, "__fixtfsi") + DEF(TOK___fixtfdi, "__fixtfdi") + DEF(TOK___fixunstfsi, "__fixunstfsi") + DEF(TOK___fixunstfdi, "__fixunstfdi") + DEF(TOK___floatsitf, "__floatsitf") + DEF(TOK___floatditf, "__floatditf") + DEF(TOK___floatunsitf, "__floatunsitf") + DEF(TOK___floatunditf, "__floatunditf") + DEF(TOK___eqtf2, "__eqtf2") + DEF(TOK___netf2, "__netf2") + DEF(TOK___lttf2, "__lttf2") + DEF(TOK___letf2, "__letf2") + DEF(TOK___gttf2, "__gttf2") + DEF(TOK___getf2, "__getf2") +#endif + +/* 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_main_arg, "__bound_main_arg") + DEF(TOK___bound_local_new, "__bound_local_new") + DEF(TOK___bound_local_delete, "__bound_local_delete") + DEF(TOK___bound_init, "__bound_init") +# ifdef TCC_TARGET_PE + DEF(TOK_malloc, "malloc") + DEF(TOK_free, "free") + DEF(TOK_realloc, "realloc") + DEF(TOK_memalign, "memalign") + DEF(TOK_calloc, "calloc") +# endif + DEF(TOK_memmove, "memmove") + DEF(TOK_strlen, "strlen") + DEF(TOK_strcpy, "strcpy") +#endif + +/* Tiny Assembler */ + DEF_ASM(byte) + DEF_ASM(word) + DEF_ASM(align) + DEF_ASM(p2align) + DEF_ASM(skip) + DEF_ASM(space) + DEF_ASM(string) + DEF_ASM(asciz) + DEF_ASM(ascii) + DEF_ASM(file) + DEF_ASM(globl) + DEF_ASM(global) + DEF_ASM(hidden) + DEF_ASM(ident) + DEF_ASM(size) + DEF_ASM(type) + DEF_ASM(text) + DEF_ASM(data) + DEF_ASM(bss) + DEF_ASM(previous) + DEF_ASM(fill) + DEF_ASM(org) + DEF_ASM(quad) +#if defined(TCC_TARGET_I386) + DEF_ASM(code16) + DEF_ASM(code32) +#elif defined(TCC_TARGET_X86_64) + DEF_ASM(code64) +#endif + +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +#include "x86/i386-tok.h" +#endif diff --git a/src/tms320c67/c67-gen.c b/src/tms320c67/c67-gen.c new file mode 100644 index 0000000..84ff78c --- /dev/null +++ b/src/tms320c67/c67-gen.c @@ -0,0 +1,2559 @@ +/* + * TMS320C67xx code generator for TCC + * + * 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 + */ + +#ifdef TARGET_DEFS_ONLY + +/* #define ASSEMBLY_LISTING_C67 */ + +/* number of available registers */ +#define NB_REGS 24 + +typedef int RegArgs; + +/* 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_INT_BSIDE 0x00000040 /* generic integer register on b side */ +#define RC_C67_A4 0x00000100 +#define RC_C67_A5 0x00000200 +#define RC_C67_B4 0x00000400 +#define RC_C67_B5 0x00000800 +#define RC_C67_A6 0x00001000 +#define RC_C67_A7 0x00002000 +#define RC_C67_B6 0x00004000 +#define RC_C67_B7 0x00008000 +#define RC_C67_A8 0x00010000 +#define RC_C67_A9 0x00020000 +#define RC_C67_B8 0x00040000 +#define RC_C67_B9 0x00080000 +#define RC_C67_A10 0x00100000 +#define RC_C67_A11 0x00200000 +#define RC_C67_B10 0x00400000 +#define RC_C67_B11 0x00800000 +#define RC_C67_A12 0x01000000 +#define RC_C67_A13 0x02000000 +#define RC_C67_B12 0x04000000 +#define RC_C67_B13 0x08000000 +#define RC_IRET RC_C67_A4 /* function return: integer register */ +#define RC_LRET RC_C67_A5 /* function return: second integer register */ +#define RC_FRET RC_C67_A4 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_EAX = 0, // really A2 + TREG_ECX, // really A3 + TREG_EDX, // really B0 + TREG_ST0, // really B1 + TREG_C67_A4, + TREG_C67_A5, + TREG_C67_B4, + TREG_C67_B5, + TREG_C67_A6, + TREG_C67_A7, + TREG_C67_B6, + TREG_C67_B7, + TREG_C67_A8, + TREG_C67_A9, + TREG_C67_B8, + TREG_C67_B9, + TREG_C67_A10, + TREG_C67_A11, + TREG_C67_B10, + TREG_C67_B11, + TREG_C67_A12, + TREG_C67_A13, + TREG_C67_B12, + TREG_C67_B13, +}; + +/* return registers for function */ +#define REG_IRET TREG_C67_A4 /* single word int return register */ +#define REG_LRET TREG_C67_A5 /* second word return register (for long long) */ +#define REG_FRET TREG_C67_A4 /* 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_C60 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_C60_32 +#define R_DATA_PTR R_C60_32 +#define R_JMP_SLOT R_C60_JMP_SLOT +#define R_COPY R_C60_COPY + +#define ELF_START_ADDR 0x00000400 +#define ELF_PAGE_SIZE 0x1000 + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#include "../tcc.h" + +ST_DATA const int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_FLOAT | RC_EAX, + // only allow even regs for floats (allow for doubles) + /* ecx */ RC_INT | RC_ECX, + /* edx */ RC_INT | RC_INT_BSIDE | RC_FLOAT | RC_EDX, + // only allow even regs for floats (allow for doubles) + /* st0 */ RC_INT | RC_INT_BSIDE | RC_ST0, + /* A4 */ RC_C67_A4, + /* A5 */ RC_C67_A5, + /* B4 */ RC_C67_B4, + /* B5 */ RC_C67_B5, + /* A6 */ RC_C67_A6, + /* A7 */ RC_C67_A7, + /* B6 */ RC_C67_B6, + /* B7 */ RC_C67_B7, + /* A8 */ RC_C67_A8, + /* A9 */ RC_C67_A9, + /* B8 */ RC_C67_B8, + /* B9 */ RC_C67_B9, + /* A10 */ RC_C67_A10, + /* A11 */ RC_C67_A11, + /* B10 */ RC_C67_B10, + /* B11 */ RC_C67_B11, + /* A12 */ RC_C67_A10, + /* A13 */ RC_C67_A11, + /* B12 */ RC_C67_B10, + /* B13 */ RC_C67_B11 +}; + +// although tcc thinks it is passing parameters on the stack, +// the C67 really passes up to the first 10 params in special +// regs or regs pairs (for 64 bit params). So keep track of +// the stack offsets so we can translate to the appropriate +// reg (pair) + +#define NoCallArgsPassedOnStack 10 +int NoOfCurFuncArgs; +int TranslateStackToReg[NoCallArgsPassedOnStack]; +int ParamLocOnStack[NoCallArgsPassedOnStack]; +int TotalBytesPushedOnStack; + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +#undef BOOL +#define BOOL int + +#define ALWAYS_ASSERT(x) \ +do {\ + if (!(x))\ + tcc_error("internal compiler error file at %s:%d", __FILE__, __LINE__);\ +} while (0) + +/******************************************************/ +static unsigned long func_sub_sp_offset; +static int func_ret_sub; + +static BOOL C67_invert_test; +static int C67_compare_reg; + +#ifdef ASSEMBLY_LISTING_C67 +FILE *f = NULL; +#endif + +void C67_g(int c) +{ + int ind1; + +#ifdef ASSEMBLY_LISTING_C67 + fprintf(f, " %08X", c); +#endif + ind1 = ind + 4; + if (ind1 > (int) cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c & 0xff; + cur_text_section->data[ind + 1] = (c >> 8) & 0xff; + cur_text_section->data[ind + 2] = (c >> 16) & 0xff; + cur_text_section->data[ind + 3] = (c >> 24) & 0xff; + ind = ind1; +} + + +/* 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); + { + Sym *sym; + + // extract 32 bit address from MVKH/MVKL + n = ((*ptr >> 7) & 0xffff); + n |= ((*(ptr + 1) >> 7) & 0xffff) << 16; + + // define a label that will be relocated + + sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0); + greloc(cur_text_section, sym, t, R_C60LO16); + greloc(cur_text_section, sym, t + 4, R_C60HI16); + + // clear out where the pointer was + + *ptr &= ~(0xffff << 7); + *(ptr + 1) &= ~(0xffff << 7); + } + t = n; + } +} + +void gsym(int t) +{ + gsym_addr(t, ind); +} + +// these are regs that tcc doesn't really know about, +// but assign them unique values so the mapping routines +// can distinguish them + +#define C67_A0 105 +#define C67_SP 106 +#define C67_B3 107 +#define C67_FP 108 +#define C67_B2 109 +#define C67_CREG_ZERO -1 /* Special code for no condition reg test */ + + +int ConvertRegToRegClass(int r) +{ + // only works for A4-B13 + + return RC_C67_A4 << (r - TREG_C67_A4); +} + + +// map TCC reg to C67 reg number + +int C67_map_regn(int r) +{ + if (r == 0) // normal tcc regs + return 0x2; // A2 + else if (r == 1) // normal tcc regs + return 3; // A3 + else if (r == 2) // normal tcc regs + return 0; // B0 + else if (r == 3) // normal tcc regs + return 1; // B1 + else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs + return (((r & 0xfffffffc) >> 1) | (r & 1)) + 2; + else if (r == C67_A0) + return 0; // set to A0 (offset reg) + else if (r == C67_B2) + return 2; // set to B2 (offset reg) + else if (r == C67_B3) + return 3; // set to B3 (return address reg) + else if (r == C67_SP) + return 15; // set to SP (B15) (offset reg) + else if (r == C67_FP) + return 15; // set to FP (A15) (offset reg) + else if (r == C67_CREG_ZERO) + return 0; // Special code for no condition reg test + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +// mapping from tcc reg number to +// C67 register to condition code field +// +// valid condition code regs are: +// +// tcc reg 2 ->B0 -> 1 +// tcc reg 3 ->B1 -> 2 +// tcc reg 0 -> A2 -> 5 +// tcc reg 1 -> A3 -> X +// tcc reg B2 -> 3 + +int C67_map_regc(int r) +{ + if (r == 0) // normal tcc regs + return 0x5; + else if (r == 2) // normal tcc regs + return 0x1; + else if (r == 3) // normal tcc regs + return 0x2; + else if (r == C67_B2) // normal tcc regs + return 0x3; + else if (r == C67_CREG_ZERO) + return 0; // Special code for no condition reg test + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + + +// map TCC reg to C67 reg side A or B + +int C67_map_regs(int r) +{ + if (r == 0) // normal tcc regs + return 0x0; + else if (r == 1) // normal tcc regs + return 0x0; + else if (r == 2) // normal tcc regs + return 0x1; + else if (r == 3) // normal tcc regs + return 0x1; + else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs + return (r & 2) >> 1; + else if (r == C67_A0) + return 0; // set to A side + else if (r == C67_B2) + return 1; // set to B side + else if (r == C67_B3) + return 1; // set to B side + else if (r == C67_SP) + return 0x1; // set to SP (B15) B side + else if (r == C67_FP) + return 0x0; // set to FP (A15) A side + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +int C67_map_S12(char *s) +{ + if (strstr(s, ".S1") != NULL) + return 0; + else if (strcmp(s, ".S2")) + return 1; + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +int C67_map_D12(char *s) +{ + if (strstr(s, ".D1") != NULL) + return 0; + else if (strcmp(s, ".D2")) + return 1; + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + + + +void C67_asm(char *s, int a, int b, int c) +{ + BOOL xpath; + +#ifdef ASSEMBLY_LISTING_C67 + if (!f) { + f = fopen("TCC67_out.txt", "wt"); + } + fprintf(f, "%04X ", ind); +#endif + + if (strstr(s, "MVKL") == s) { + C67_g((C67_map_regn(b) << 23) | + ((a & 0xffff) << 7) | (0x0a << 2) | (C67_map_regs(b) << 1)); + } else if (strstr(s, "MVKH") == s) { + C67_g((C67_map_regn(b) << 23) | + (((a >> 16) & 0xffff) << 7) | + (0x1a << 2) | (C67_map_regs(b) << 1)); + } else if (strstr(s, "STW.D SP POST DEC") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //SP B15 + (2 << 13) | //ucst5 (must keep 8 byte boundary !!) + (0xa << 9) | //mode a = post dec ucst + (0 << 8) | //r (LDDW bit 0) + (1 << 7) | //y D1/D2 use B side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D +*") == s) { + ALWAYS_ASSERT(c < 32); + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (c << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D SP PRE INC") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg B15 + (2 << 13) | //ucst5 (must keep 8 byte boundary) + (9 << 9) | //mode 9 = pre inc ucst5 + (0 << 8) | //r (LDDW bit 0) + (1 << 7) | //y D1/D2 B side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D SP PRE INC") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg B15 + (1 << 13) | //ucst5 (must keep 8 byte boundary) + (9 << 9) | //mode 9 = pre inc ucst5 + (1 << 8) | //r (LDDW bit 1) + (1 << 7) | //y D1/D2 B side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (1 << 8) | //r (LDDW bit 1) + (0 << 7) | //y D1/D2 A side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDHU.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDBU.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (1 << 8) | //r (LDDW bit 1) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDH.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDB.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDHU.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDBU.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D +*") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (1 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "CMPLTSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x3a << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x39 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x38 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } + + else if (strstr(s, "CMPLTDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x2a << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x29 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x28 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPLT") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x57 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGT") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x47 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQ") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x53 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPLTU") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x5f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTU") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x4f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "B DISP") == s) { + C67_g((0 << 29) | //creg + (0 << 28) | //z + (a << 7) | //cnst + (0x4 << 2) | //opcode fixed + (0 << 1) | //S0/S1 + (0 << 0)); //parallel + } else if (strstr(s, "B.") == s) { + xpath = C67_map_regs(c) ^ 1; + + C67_g((C67_map_regc(b) << 29) | //creg + (a << 28) | //inv + (0 << 23) | //dst + (C67_map_regn(c) << 18) | //src2 + (0 << 13) | // + (xpath << 12) | //x cross path if !B side + (0xd << 6) | //opcode + (0x8 << 2) | //opcode fixed + (1 << 1) | //must be S2 + (0 << 0)); //parallel + } else if (strstr(s, "MV.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 (cst5) + (xpath << 12) | //x cross path if opposite sides + (0x2 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SPTRUNC.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0xb << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "DPTRUNC.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x1 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x4a << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTSPU.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x49 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x39 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTDPU.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x3b << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SPDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x2 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "DPSP.L") == s) { + ALWAYS_ASSERT(C67_map_regs(b) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (0 << 12) | //x cross path if opposite sides + (0x9 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADD.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x3 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUB.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "OR.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "AND.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7b << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "XOR.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x6f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADDSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x10 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADDDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x18 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUBSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x11 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUBDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x19 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYSP.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x1c << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYDP.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x0e << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYI.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 (cst5) + (xpath << 12) | //x cross path if opposite sides + (0x4 << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHR.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x37 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHRU.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x27 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHL.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x33 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "||ADDK") == s) { + xpath = 0; // no xpath required just use the side of the src/dst + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(b) << 23) | //dst + (a << 07) | //scst16 + (0x14 << 2) | //opcode fixed + (C67_map_regs(b) << 1) | //side of dst + (1 << 0)); //parallel + } else if (strstr(s, "ADDK") == s) { + xpath = 0; // no xpath required just use the side of the src/dst + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(b) << 23) | //dst + (a << 07) | //scst16 + (0x14 << 2) | //opcode fixed + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "NOP") == s) { + C67_g(((a - 1) << 13) | //no of cycles + (0 << 0)); //parallel + } else + ALWAYS_ASSERT(FALSE); + +#ifdef ASSEMBLY_LISTING_C67 + fprintf(f, " %s %d %d %d\n", s, a, b, c); +#endif + +} + +//r=reg to load, fr=from reg, symbol for relocation, constant + +void C67_MVKL(int r, int fc) +{ + C67_asm("MVKL.", fc, r, 0); +} + +void C67_MVKH(int r, int fc) +{ + C67_asm("MVKH.", fc, r, 0); +} + +void C67_STB_SP_A0(int r) +{ + C67_asm("STB.D *+SP[A0]", r, 0, 0); // STB r,*+SP[A0] +} + +void C67_STH_SP_A0(int r) +{ + C67_asm("STH.D *+SP[A0]", r, 0, 0); // STH r,*+SP[A0] +} + +void C67_STW_SP_A0(int r) +{ + C67_asm("STW.D *+SP[A0]", r, 0, 0); // STW r,*+SP[A0] +} + +void C67_STB_PTR(int r, int r2) +{ + C67_asm("STB.D *", r, r2, 0); // STB r, *r2 +} + +void C67_STH_PTR(int r, int r2) +{ + C67_asm("STH.D *", r, r2, 0); // STH r, *r2 +} + +void C67_STW_PTR(int r, int r2) +{ + C67_asm("STW.D *", r, r2, 0); // STW r, *r2 +} + +void C67_STW_PTR_PRE_INC(int r, int r2, int n) +{ + C67_asm("STW.D +*", r, r2, n); // STW r, *+r2 +} + +void C67_PUSH(int r) +{ + C67_asm("STW.D SP POST DEC", r, 0, 0); // STW r,*SP-- +} + +void C67_LDW_SP_A0(int r) +{ + C67_asm("LDW.D *+SP[A0]", r, 0, 0); // LDW *+SP[A0],r +} + +void C67_LDDW_SP_A0(int r) +{ + C67_asm("LDDW.D *+SP[A0]", r, 0, 0); // LDDW *+SP[A0],r +} + +void C67_LDH_SP_A0(int r) +{ + C67_asm("LDH.D *+SP[A0]", r, 0, 0); // LDH *+SP[A0],r +} + +void C67_LDB_SP_A0(int r) +{ + C67_asm("LDB.D *+SP[A0]", r, 0, 0); // LDB *+SP[A0],r +} + +void C67_LDHU_SP_A0(int r) +{ + C67_asm("LDHU.D *+SP[A0]", r, 0, 0); // LDHU *+SP[A0],r +} + +void C67_LDBU_SP_A0(int r) +{ + C67_asm("LDBU.D *+SP[A0]", r, 0, 0); // LDBU *+SP[A0],r +} + +void C67_LDW_PTR(int r, int r2) +{ + C67_asm("LDW.D *", r, r2, 0); // LDW *r,r2 +} + +void C67_LDDW_PTR(int r, int r2) +{ + C67_asm("LDDW.D *", r, r2, 0); // LDDW *r,r2 +} + +void C67_LDH_PTR(int r, int r2) +{ + C67_asm("LDH.D *", r, r2, 0); // LDH *r,r2 +} + +void C67_LDB_PTR(int r, int r2) +{ + C67_asm("LDB.D *", r, r2, 0); // LDB *r,r2 +} + +void C67_LDHU_PTR(int r, int r2) +{ + C67_asm("LDHU.D *", r, r2, 0); // LDHU *r,r2 +} + +void C67_LDBU_PTR(int r, int r2) +{ + C67_asm("LDBU.D *", r, r2, 0); // LDBU *r,r2 +} + +void C67_LDW_PTR_PRE_INC(int r, int r2) +{ + C67_asm("LDW.D +*", r, r2, 0); // LDW *+r,r2 +} + +void C67_POP(int r) +{ + C67_asm("LDW.D SP PRE INC", r, 0, 0); // LDW *++SP,r +} + +void C67_POP_DW(int r) +{ + C67_asm("LDDW.D SP PRE INC", r, 0, 0); // LDDW *++SP,r +} + +void C67_CMPLT(int s1, int s2, int dst) +{ + C67_asm("CMPLT.L1", s1, s2, dst); +} + +void C67_CMPGT(int s1, int s2, int dst) +{ + C67_asm("CMPGT.L1", s1, s2, dst); +} + +void C67_CMPEQ(int s1, int s2, int dst) +{ + C67_asm("CMPEQ.L1", s1, s2, dst); +} + +void C67_CMPLTU(int s1, int s2, int dst) +{ + C67_asm("CMPLTU.L1", s1, s2, dst); +} + +void C67_CMPGTU(int s1, int s2, int dst) +{ + C67_asm("CMPGTU.L1", s1, s2, dst); +} + + +void C67_CMPLTSP(int s1, int s2, int dst) +{ + C67_asm("CMPLTSP.S1", s1, s2, dst); +} + +void C67_CMPGTSP(int s1, int s2, int dst) +{ + C67_asm("CMPGTSP.S1", s1, s2, dst); +} + +void C67_CMPEQSP(int s1, int s2, int dst) +{ + C67_asm("CMPEQSP.S1", s1, s2, dst); +} + +void C67_CMPLTDP(int s1, int s2, int dst) +{ + C67_asm("CMPLTDP.S1", s1, s2, dst); +} + +void C67_CMPGTDP(int s1, int s2, int dst) +{ + C67_asm("CMPGTDP.S1", s1, s2, dst); +} + +void C67_CMPEQDP(int s1, int s2, int dst) +{ + C67_asm("CMPEQDP.S1", s1, s2, dst); +} + + +void C67_IREG_B_REG(int inv, int r1, int r2) // [!R] B r2 +{ + C67_asm("B.S2", inv, r1, r2); +} + + +// call with how many 32 bit words to skip +// (0 would branch to the branch instruction) + +void C67_B_DISP(int disp) // B +2 Branch with constant displacement +{ + // Branch point is relative to the 8 word fetch packet + // + // we will assume the text section always starts on an 8 word (32 byte boundary) + // + // so add in how many words into the fetch packet the branch is + + + C67_asm("B DISP", disp + ((ind & 31) >> 2), 0, 0); +} + +void C67_NOP(int n) +{ + C67_asm("NOP", n, 0, 0); +} + +void C67_ADDK(int n, int r) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + C67_asm("ADDK", n, r, 0); +} + +void C67_ADDK_PARALLEL(int n, int r) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + C67_asm("||ADDK", n, r, 0); +} + +void C67_Adjust_ADDK(int *inst, int n) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + *inst = (*inst & (~(0xffff << 7))) | ((n & 0xffff) << 7); +} + +void C67_MV(int r, int v) +{ + C67_asm("MV.L", 0, r, v); +} + + +void C67_DPTRUNC(int r, int v) +{ + C67_asm("DPTRUNC.L", 0, r, v); +} + +void C67_SPTRUNC(int r, int v) +{ + C67_asm("SPTRUNC.L", 0, r, v); +} + +void C67_INTSP(int r, int v) +{ + C67_asm("INTSP.L", 0, r, v); +} + +void C67_INTDP(int r, int v) +{ + C67_asm("INTDP.L", 0, r, v); +} + +void C67_INTSPU(int r, int v) +{ + C67_asm("INTSPU.L", 0, r, v); +} + +void C67_INTDPU(int r, int v) +{ + C67_asm("INTDPU.L", 0, r, v); +} + +void C67_SPDP(int r, int v) +{ + C67_asm("SPDP.L", 0, r, v); +} + +void C67_DPSP(int r, int v) // note regs must be on the same side +{ + C67_asm("DPSP.L", 0, r, v); +} + +void C67_ADD(int r, int v) +{ + C67_asm("ADD.L", v, r, v); +} + +void C67_SUB(int r, int v) +{ + C67_asm("SUB.L", v, r, v); +} + +void C67_AND(int r, int v) +{ + C67_asm("AND.L", v, r, v); +} + +void C67_OR(int r, int v) +{ + C67_asm("OR.L", v, r, v); +} + +void C67_XOR(int r, int v) +{ + C67_asm("XOR.L", v, r, v); +} + +void C67_ADDSP(int r, int v) +{ + C67_asm("ADDSP.L", v, r, v); +} + +void C67_SUBSP(int r, int v) +{ + C67_asm("SUBSP.L", v, r, v); +} + +void C67_MPYSP(int r, int v) +{ + C67_asm("MPYSP.M", v, r, v); +} + +void C67_ADDDP(int r, int v) +{ + C67_asm("ADDDP.L", v, r, v); +} + +void C67_SUBDP(int r, int v) +{ + C67_asm("SUBDP.L", v, r, v); +} + +void C67_MPYDP(int r, int v) +{ + C67_asm("MPYDP.M", v, r, v); +} + +void C67_MPYI(int r, int v) +{ + C67_asm("MPYI.M", v, r, v); +} + +void C67_SHL(int r, int v) +{ + C67_asm("SHL.S", r, v, v); +} + +void C67_SHRU(int r, int v) +{ + C67_asm("SHRU.S", r, v, v); +} + +void C67_SHR(int r, int v) +{ + C67_asm("SHR.S", r, v, v); +} + + + +/* load 'r' from value 'sv' */ +void load(int r, SValue * sv) +{ + int v, t, ft, fc, fr, size = 0, element; + BOOL Unsigned = FALSE; + 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; + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + tcc_error("long double not supported"); + } else if ((ft & VT_TYPE) == VT_BYTE) { + size = 1; + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + size = 1; + Unsigned = TRUE; + } else if ((ft & VT_TYPE) == VT_SHORT) { + size = 2; + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + size = 2; + Unsigned = TRUE; + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + size = 8; + } else { + size = 4; + } + + // check if fc is a positive reference on the stack, + // if it is tcc is referencing what it thinks is a parameter + // on the stack, so check if it is really in a register. + + + if (v == VT_LOCAL && fc > 0) { + int stack_pos = 8; + + for (t = 0; t < NoCallArgsPassedOnStack; t++) { + if (fc == stack_pos) + break; + + stack_pos += TranslateStackToReg[t]; + } + + // param has been pushed on stack, get it like a local var + + fc = ParamLocOnStack[t] - 8; + } + + if ((fr & VT_VALMASK) < VT_CONST) // check for pure indirect + { + if (size == 1) { + if (Unsigned) + C67_LDBU_PTR(v, r); // LDBU *v,r + else + C67_LDB_PTR(v, r); // LDB *v,r + } else if (size == 2) { + if (Unsigned) + C67_LDHU_PTR(v, r); // LDHU *v,r + else + C67_LDH_PTR(v, r); // LDH *v,r + } else if (size == 4) { + C67_LDW_PTR(v, r); // LDW *v,r + } else if (size == 8) { + C67_LDDW_PTR(v, r); // LDDW *v,r + } + + C67_NOP(4); // NOP 4 + return; + } else if (fr & VT_SYM) { + greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16); + + + C67_MVKL(C67_A0, fc); //r=reg to load, constant + C67_MVKH(C67_A0, fc); //r=reg to load, constant + + + if (size == 1) { + if (Unsigned) + C67_LDBU_PTR(C67_A0, r); // LDBU *A0,r + else + C67_LDB_PTR(C67_A0, r); // LDB *A0,r + } else if (size == 2) { + if (Unsigned) + C67_LDHU_PTR(C67_A0, r); // LDHU *A0,r + else + C67_LDH_PTR(C67_A0, r); // LDH *A0,r + } else if (size == 4) { + C67_LDW_PTR(C67_A0, r); // LDW *A0,r + } else if (size == 8) { + C67_LDDW_PTR(C67_A0, r); // LDDW *A0,r + } + + C67_NOP(4); // NOP 4 + return; + } else { + element = size; + + // divide offset in bytes to create element index + C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + + if (size == 1) { + if (Unsigned) + C67_LDBU_SP_A0(r); // LDBU r, SP[A0] + else + C67_LDB_SP_A0(r); // LDB r, SP[A0] + } else if (size == 2) { + if (Unsigned) + C67_LDHU_SP_A0(r); // LDHU r, SP[A0] + else + C67_LDH_SP_A0(r); // LDH r, SP[A0] + } else if (size == 4) { + C67_LDW_SP_A0(r); // LDW r, SP[A0] + } else if (size == 8) { + C67_LDDW_SP_A0(r); // LDDW r, SP[A0] + } + + + C67_NOP(4); // NOP 4 + return; + } + } else { + if (v == VT_CONST) { + if (fr & VT_SYM) { + greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16); + } + C67_MVKL(r, fc); //r=reg to load, constant + C67_MVKH(r, fc); //r=reg to load, constant + } else if (v == VT_LOCAL) { + C67_MVKL(r, fc + 8); //r=reg to load, constant C67 stack points to next free + C67_MVKH(r, fc + 8); //r=reg to load, constant + C67_ADD(C67_FP, r); // MV v,r v -> r + } else if (v == VT_CMP) { + C67_MV(C67_compare_reg, r); // MV v,r v -> r + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + C67_B_DISP(4); // Branch with constant displacement, skip over this branch, load, nop, load + C67_MVKL(r, t); // r=reg to load, 0 or 1 (do this while branching) + C67_NOP(4); // NOP 4 + gsym(fc); // modifies other branches to branch here + C67_MVKL(r, t ^ 1); // r=reg to load, 0 or 1 + } else if (v != r) { + C67_MV(v, r); // MV v,r v -> r + + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_MV(v + 1, r + 1); // MV v,r v -> r + } + } +} + + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue * v) +{ + int fr, bt, ft, fc, size, t, element; + + 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_LDOUBLE) { + tcc_error("long double not supported"); + } else { + if (bt == VT_SHORT) + size = 2; + else if (bt == VT_BYTE) + size = 1; + else if (bt == VT_DOUBLE) + size = 8; + else + size = 4; + + if ((v->r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + + if (v->r & VT_SYM) { + greloc(cur_text_section, v->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, v->sym, ind + 4, R_C60HI16); + } + C67_MVKL(C67_A0, fc); //r=reg to load, constant + C67_MVKH(C67_A0, fc); //r=reg to load, constant + + if (size == 1) + C67_STB_PTR(r, C67_A0); // STB r, *A0 + else if (size == 2) + C67_STH_PTR(r, C67_A0); // STH r, *A0 + else if (size == 4 || size == 8) + C67_STW_PTR(r, C67_A0); // STW r, *A0 + + if (size == 8) + C67_STW_PTR_PRE_INC(r + 1, C67_A0, 1); // STW r, *+A0[1] + } else if ((v->r & VT_VALMASK) == VT_LOCAL) { + // check case of storing to passed argument that + // tcc thinks is on the stack but for C67 is + // passed as a reg. However it may have been + // saved to the stack, if that reg was required + // for a call to a child function + + if (fc > 0) // argument ?? + { + // walk through sizes and figure which param + + int stack_pos = 8; + + for (t = 0; t < NoCallArgsPassedOnStack; t++) { + if (fc == stack_pos) + break; + + stack_pos += TranslateStackToReg[t]; + } + + // param has been pushed on stack, get it like a local var + fc = ParamLocOnStack[t] - 8; + } + + if (size == 8) + element = 4; + else + element = size; + + // divide offset in bytes to create word index + C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + + + + if (size == 1) + C67_STB_SP_A0(r); // STB r, SP[A0] + else if (size == 2) + C67_STH_SP_A0(r); // STH r, SP[A0] + else if (size == 4 || size == 8) + C67_STW_SP_A0(r); // STW r, SP[A0] + + if (size == 8) { + C67_ADDK(1, C67_A0); // ADDK 1,A0 + C67_STW_SP_A0(r + 1); // STW r, SP[A0] + } + } else { + if (size == 1) + C67_STB_PTR(r, fr); // STB r, *fr + else if (size == 2) + C67_STH_PTR(r, fr); // STH r, *fr + else if (size == 4 || size == 8) + C67_STW_PTR(r, fr); // STW r, *fr + + if (size == 8) { + C67_STW_PTR_PRE_INC(r + 1, fr, 1); // STW r, *+fr[1] + } + } + } +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + Sym *sym; + + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* constant case */ + if (vtop->r & VT_SYM) { + /* relocation case */ + + // get add into A0, then start the jump B3 + + greloc(cur_text_section, vtop->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, vtop->sym, ind + 4, R_C60HI16); + + C67_MVKL(C67_A0, 0); //r=reg to load, constant + C67_MVKH(C67_A0, 0); //r=reg to load, constant + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // B.S2x A0 + + if (is_jmp) { + C67_NOP(5); // simple jump, just put NOP + } else { + // Call, must load return address into B3 during delay slots + + sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address + greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + C67_MVKL(C67_B3, 0); //r=reg to load, constant + C67_MVKH(C67_B3, 0); //r=reg to load, constant + C67_NOP(3); // put remaining NOPs + } + } else { + /* put an empty PC32 relocation */ + ALWAYS_ASSERT(FALSE); + } + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + C67_IREG_B_REG(0, C67_CREG_ZERO, r); // B.S2x r + + if (is_jmp) { + C67_NOP(5); // simple jump, just put NOP + } else { + // Call, must load return address into B3 during delay slots + + sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address + greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + C67_MVKL(C67_B3, 0); //r=reg to load, constant + C67_MVKH(C67_B3, 0); //r=reg to load, constant + C67_NOP(3); // put remaining NOPs + } + } +} + +ST_FUNC int regargs_nregs(RegArgs *args) +{ + return *args; +} + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) { + *ret_align = 1; // Never have to re-align return values for x86-64 + *args = 0; + + return 0; +} + +/* generate function call with address in (vtop->t, vtop->c) and free function + context. Stack entry is popped */ +void gfunc_call(int nb_args) +{ + int i, r, size = 0; + int args_sizes[NoCallArgsPassedOnStack]; + + if (nb_args > NoCallArgsPassedOnStack) { + tcc_error("more than 10 function params not currently supported"); + // handle more than 10, put some on the stack + } + + for (i = 0; i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + ALWAYS_ASSERT(FALSE); + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + + + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + tcc_error("long long not supported"); + } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + tcc_error("long double not supported"); + } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + size = 8; + } else { + size = 4; + } + + // put the parameter into the corresponding reg (pair) + + r = gv(RC_C67_A4 << (2 * i)); + + // must put on stack because with 1 pass compiler , no way to tell + // if an up coming nested call might overwrite these regs + + C67_PUSH(r); + + if (size == 8) { + C67_STW_PTR_PRE_INC(r + 1, C67_SP, 3); // STW r, *+SP[3] (go back and put the other) + } + args_sizes[i] = size; + } + vtop--; + } + // POP all the params on the stack into registers for the + // immediate call (in reverse order) + + for (i = nb_args - 1; i >= 0; i--) { + + if (args_sizes[i] == 8) + C67_POP_DW(TREG_C67_A4 + i * 2); + else + C67_POP(TREG_C67_A4 + i * 2); + } + gcall_or_jmp(0); + vtop--; +} + + +// to be compatible with Code Composer for the C67 +// the first 10 parameters must be passed in registers +// (pairs for 64 bits) starting wit; A4:A5, then B4:B5 and +// ending with B12:B13. +// +// When a call is made, if the caller has its parameters +// in regs A4-B13 these must be saved before/as the call +// parameters are loaded and restored upon return (or if/when needed). + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType * func_type) +{ + int addr, align, size, func_call, i; + Sym *sym; + CType *type; + + sym = func_type->ref; + func_call = sym->r; + addr = 8; + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->type; + func_var = (sym->c == FUNC_ELLIPSIS); + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + func_vc = addr; + addr += 4; + } + + NoOfCurFuncArgs = 0; + + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr); + size = type_size(type, &align); + size = (size + 3) & ~3; + + // keep track of size of arguments so + // we can translate where tcc thinks they + // are on the stack into the appropriate reg + + TranslateStackToReg[NoOfCurFuncArgs] = size; + NoOfCurFuncArgs++; + +#ifdef FUNC_STRUCT_PARAM_AS_PTR + /* structs are passed as pointer */ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + size = 4; + } +#endif + addr += size; + } + func_ret_sub = 0; + /* pascal type call ? */ + if (func_call == FUNC_STDCALL) + func_ret_sub = addr - 8; + + C67_MV(C67_FP, C67_A0); // move FP -> A0 + C67_MV(C67_SP, C67_FP); // move SP -> FP + + // place all the args passed in regs onto the stack + + loc = 0; + for (i = 0; i < NoOfCurFuncArgs; i++) { + + ParamLocOnStack[i] = loc; // remember where the param is + loc += -8; + + C67_PUSH(TREG_C67_A4 + i * 2); + + if (TranslateStackToReg[i] == 8) { + C67_STW_PTR_PRE_INC(TREG_C67_A4 + i * 2 + 1, C67_SP, 3); // STW r, *+SP[1] (go back and put the other) + } + } + + TotalBytesPushedOnStack = -loc; + + func_sub_sp_offset = ind; // remember where we put the stack instruction + C67_ADDK(0, C67_SP); // ADDK.L2 loc,SP (just put zero temporarily) + + C67_PUSH(C67_A0); + C67_PUSH(C67_B3); +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + { + int local = (-loc + 7) & -8; // stack must stay aligned to 8 bytes for LDDW instr + C67_POP(C67_B3); + C67_NOP(4); // NOP wait for load + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_B3); // B.S2 B3 + C67_POP(C67_FP); + C67_ADDK(local, C67_SP); // ADDK.L2 loc,SP + C67_Adjust_ADDK((int *) (cur_text_section->data + + func_sub_sp_offset), + -local + TotalBytesPushedOnStack); + C67_NOP(3); // NOP + } +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + int ind1 = ind; + + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + return ind1; +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + Sym *sym; + // I guess this routine is used for relative short + // local jumps, for now just handle it as the general + // case + + // define a label that will be relocated + + sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0); + greloc(cur_text_section, sym, ind, R_C60LO16); + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + + gjmp(0); // place a zero there later the symbol will be added to it +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int ind1, n; + int v, *p; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + /* fast case : can jump directly since flags are set */ + // C67 uses B2 sort of as flags register + ind1 = ind; + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + + if (C67_compare_reg != TREG_EAX && // check if not already in a conditional test reg + C67_compare_reg != TREG_EDX && + C67_compare_reg != TREG_ST0 && C67_compare_reg != C67_B2) { + C67_MV(C67_compare_reg, C67_B2); + C67_compare_reg = C67_B2; + } + + C67_IREG_B_REG(C67_invert_test ^ inv, C67_compare_reg, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + t = ind1; //return where we need to patch + + } 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; + + // I guess the idea is to traverse to the + // null at the end of the list and store t + // there + + n = *p; + while (n != 0) { + p = (int *) (cur_text_section->data + n); + + // extract 32 bit address from MVKH/MVKL + n = ((*p >> 7) & 0xffff); + n |= ((*(p + 1) >> 7) & 0xffff) << 16; + } + *p |= (t & 0xffff) << 7; + *(p + 1) |= ((t >> 16) & 0xffff) << 7; + t = vtop->c.i; + + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int r, fr, opc, t; + + switch (op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + + +// C67 can't do const compares, must load into a reg +// so just go to gv2 directly - tktk + + + + if (op >= TOK_ULT && op <= TOK_GT) + gv2(RC_INT_BSIDE, RC_INT); // make sure r (src1) is on the B Side of CPU + else + gv2(RC_INT, RC_INT); + + r = vtop[-1].r; + fr = vtop[0].r; + + C67_compare_reg = C67_B2; + + + if (op == TOK_LT) { + C67_CMPLT(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_GE) { + C67_CMPLT(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_GT) { + C67_CMPGT(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_LE) { + C67_CMPGT(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_EQ) { + C67_CMPEQ(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_NE) { + C67_CMPEQ(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_ULT) { + C67_CMPLTU(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_UGE) { + C67_CMPLTU(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == TOK_UGT) { + C67_CMPGTU(r, fr, C67_B2); + C67_invert_test = FALSE; + } else if (op == TOK_ULE) { + C67_CMPGTU(r, fr, C67_B2); + C67_invert_test = TRUE; + } else if (op == '+') + C67_ADD(fr, r); // ADD r,fr,r + else if (op == '-') + C67_SUB(fr, r); // SUB r,fr,r + else if (op == '&') + C67_AND(fr, r); // AND r,fr,r + else if (op == '|') + C67_OR(fr, r); // OR r,fr,r + else if (op == '^') + C67_XOR(fr, r); // XOR r,fr,r + else + ALWAYS_ASSERT(FALSE); + + 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 '*': + case TOK_UMULL: + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_MPYI(fr, r); // 32 bit bultiply fr,r,fr + C67_NOP(8); // NOP 8 for worst case + break; + case TOK_SHL: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHL(fr, r); // arithmetic/logical shift + break; + + case TOK_SHR: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHRU(fr, r); // logical shift + break; + + case TOK_SAR: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHR(fr, r); // arithmetic shift + break; + + case '/': + t = TOK__divi; + call_func: + vswap(); + /* call generic idiv function */ + vpush_global_sym(&func_old_type, t); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = VT_CONST; + break; + case TOK_UDIV: + case TOK_PDIV: + t = TOK__divu; + goto call_func; + case '%': + t = TOK__remi; + goto call_func; + case TOK_UMOD: + t = TOK__remu; + goto call_func; + + 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 ft, fc, fr, r; + + if (op >= TOK_ULT && op <= TOK_GT) + gv2(RC_EDX, RC_EAX); // make sure src2 is on b side + else + gv2(RC_FLOAT, RC_FLOAT); // make sure src2 is on b side + + ft = vtop->type.t; + fc = vtop->c.ul; + r = vtop->r; + fr = vtop[-1].r; + + + if ((ft & VT_BTYPE) == VT_LDOUBLE) + tcc_error("long doubles not supported"); + + if (op >= TOK_ULT && op <= TOK_GT) { + + r = vtop[-1].r; + fr = vtop[0].r; + + C67_compare_reg = C67_B2; + + if (op == TOK_LT) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPLTDP(r, fr, C67_B2); + else + C67_CMPLTSP(r, fr, C67_B2); + + C67_invert_test = FALSE; + } else if (op == TOK_GE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPLTDP(r, fr, C67_B2); + else + C67_CMPLTSP(r, fr, C67_B2); + + C67_invert_test = TRUE; + } else if (op == TOK_GT) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPGTDP(r, fr, C67_B2); + else + C67_CMPGTSP(r, fr, C67_B2); + + C67_invert_test = FALSE; + } else if (op == TOK_LE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPGTDP(r, fr, C67_B2); + else + C67_CMPGTSP(r, fr, C67_B2); + + C67_invert_test = TRUE; + } else if (op == TOK_EQ) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPEQDP(r, fr, C67_B2); + else + C67_CMPEQSP(r, fr, C67_B2); + + C67_invert_test = FALSE; + } else if (op == TOK_NE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPEQDP(r, fr, C67_B2); + else + C67_CMPEQSP(r, fr, C67_B2); + + C67_invert_test = TRUE; + } else { + ALWAYS_ASSERT(FALSE); + } + vtop->r = VT_CMP; // tell TCC that result is in "flags" actually B2 + } else { + if (op == '+') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_ADDDP(r, fr); // ADD fr,r,fr + C67_NOP(6); + } else { + C67_ADDSP(r, fr); // ADD fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '-') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_SUBDP(r, fr); // SUB fr,r,fr + C67_NOP(6); + } else { + C67_SUBSP(r, fr); // SUB fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '*') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_MPYDP(r, fr); // MPY fr,r,fr + C67_NOP(9); + } else { + C67_MPYSP(r, fr); // MPY fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '/') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + // must call intrinsic DP floating point divide + vswap(); + /* call generic idiv function */ + vpush_global_sym(&func_old_type, TOK__divd); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_FRET; + vtop->r2 = REG_LRET; + + } else { + // must call intrinsic SP floating point divide + vswap(); + /* call generic idiv function */ + vpush_global_sym(&func_old_type, TOK__divf); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_FRET; + vtop->r2 = VT_CONST; + } + } else + ALWAYS_ASSERT(FALSE); + + + } +} + + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + int r; + + gv(RC_INT); + r = vtop->r; + + if ((t & VT_BTYPE) == VT_DOUBLE) { + if (t & VT_UNSIGNED) + C67_INTDPU(r, r); + else + C67_INTDP(r, r); + + C67_NOP(4); + vtop->type.t = VT_DOUBLE; + } else { + if (t & VT_UNSIGNED) + C67_INTSPU(r, r); + else + C67_INTSP(r, r); + C67_NOP(3); + vtop->type.t = VT_FLOAT; + } + +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + int r; + + gv(RC_FLOAT); + r = vtop->r; + + if (t != VT_INT) + tcc_error("long long not supported"); + else { + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + C67_DPTRUNC(r, r); + C67_NOP(3); + } else { + C67_SPTRUNC(r, r); + C67_NOP(3); + } + + vtop->type.t = VT_INT; + + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + int r, r2; + + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE && + (t & VT_BTYPE) == VT_FLOAT) { + // convert double to float + + gv(RC_FLOAT); // get it in a register pair + + r = vtop->r; + + C67_DPSP(r, r); // convert it to SP same register + C67_NOP(3); + + vtop->type.t = VT_FLOAT; + vtop->r2 = VT_CONST; // set this as unused + } else if ((vtop->type.t & VT_BTYPE) == VT_FLOAT && + (t & VT_BTYPE) == VT_DOUBLE) { + // convert float to double + + gv(RC_FLOAT); // get it in a register + + r = vtop->r; + + if (r == TREG_EAX) { // make sure the paired reg is avail + r2 = get_reg(RC_ECX); + } else if (r == TREG_EDX) { + r2 = get_reg(RC_ST0); + } else { + ALWAYS_ASSERT(FALSE); + r2 = 0; /* avoid warning */ + } + + C67_SPDP(r, r); // convert it to DP same register + C67_NOP(1); + + vtop->type.t = VT_DOUBLE; + vtop->r2 = r2; // set this as unused + } else { + ALWAYS_ASSERT(FALSE); + } +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* Save the stack pointer onto the stack and return the location of its address */ +ST_FUNC void gen_vla_sp_save(int addr) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* Restore the SP from a location on the stack */ +ST_FUNC void gen_vla_sp_restore(int addr) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack */ +ST_FUNC void gen_vla_alloc(CType *type, int align) { + tcc_error("variable length arrays unsupported for this target"); +} + +/* end of C67 code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/src/x86/i386-asm.c b/src/x86/i386-asm.c new file mode 100644 index 0000000..adc40b2 --- /dev/null +++ b/src/x86/i386-asm.c @@ -0,0 +1,1502 @@ +/* + * i386 specific functions for TCC assembler + * + * Copyright (c) 2001, 2002 Fabrice Bellard + * Copyright (c) 2009 Frédéric Feret (x86_64 support) + * + * 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 + */ + +#include "../tcc.h" + +/* #define NB_ASM_REGS 8 */ +#define MAX_OPERANDS 3 +#define NB_SAVED_REGS 3 + +#define TOK_ASM_first TOK_ASM_clc +#define TOK_ASM_last TOK_ASM_emms +#define TOK_ASM_alllast TOK_ASM_pxor + +#define OPC_JMP 0x01 /* jmp operand */ +#define OPC_B 0x02 /* only used with 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 */ +#ifdef TCC_TARGET_X86_64 +# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */ +# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */ +# define OPC_WLX OPC_WLQ +#else +# define OPC_WLX OPC_WL +#endif + +#define OPC_GROUP_SHIFT 13 + +/* in order to compress the operand type, we use specific operands and + we or only with EA */ +enum { + OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */ +#ifdef TCC_TARGET_X86_64 + OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */ +#endif + OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_SEG, + OPT_ST, + OPT_IM8, + OPT_IM8S, + OPT_IM16, + OPT_IM32, +#ifdef TCC_TARGET_X86_64 + OPT_IM64, +#endif + OPT_EAX, /* %al, %ax, %eax or %rax register */ + OPT_ST0, /* %st(0) register */ + OPT_CL, /* %cl register */ + OPT_DX, /* %dx register */ + OPT_ADDR, /* OP_EA with only offset */ + OPT_INDIR, /* *(expr) */ + /* composite types */ + OPT_COMPOSITE_FIRST, + OPT_IM, /* IM8 | IM16 | IM32 | IM64 */ + OPT_REG, /* REG8 | REG16 | REG32 | REG64 */ + OPT_REGW, /* REG16 | REG32 | REG64 */ + OPT_IMW, /* IM16 | IM32 | IM64 */ +#ifdef TCC_TARGET_X86_64 + OPT_IMNO64, /* IM16 | IM32 */ +#endif + /* can be ored with any OPT_xxx */ + OPT_EA = 0x80 +}; + +#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) +#ifdef TCC_TARGET_X86_64 +# define OP_REG64 (1 << OPT_REG64) +# define OP_IM64 (1 << OPT_IM64) +#else +# define OP_REG64 0 +# define OP_IM64 0 +#endif + +#define OP_EA 0x40000000 +#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64) + +#ifdef TCC_TARGET_X86_64 +# define OP_IM OP_IM64 +# define TREG_XAX TREG_RAX +# define TREG_XCX TREG_RCX +# define TREG_XDX TREG_RDX +#else +# define OP_IM OP_IM32 +# define TREG_XAX TREG_EAX +# define TREG_XCX TREG_ECX +# define TREG_XDX TREG_EDX +#endif + +typedef struct ASMInstr { + uint16_t sym; + uint16_t opcode; + uint16_t instr_type; + uint8_t nb_ops; + uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ +} ASMInstr; + +typedef struct Operand { + uint32_t type; + 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[9] = { +/* + [OP_REG8] = 0, + [OP_REG16] = 1, + [OP_REG32] = 2, +#ifdef TCC_TARGET_X86_64 + [OP_REG64] = 3, +#endif +*/ + 0, 0, 1, 0, 2, 0, 0, 0, 3 +}; + +#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 uint8_t segment_prefixes[] = { + 0x26, /* es */ + 0x2e, /* cs */ + 0x36, /* ss */ + 0x3e, /* ds */ + 0x64, /* fs */ + 0x65 /* gs */ +}; + +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 }}, +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif + /* 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) +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif +}; + +static inline int get_reg_shift(TCCState *s1) +{ + int shift, v; +#ifdef I386_ASM_16 + if (s1->seg_size == 16) + tcc_error("invalid effective address"); +#endif + 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 = 0; + if (tok != '%') + goto error_32; + next(); + if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { + reg = tok - TOK_ASM_eax; +#ifdef TCC_TARGET_X86_64 + } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { + reg = tok - TOK_ASM_rax; +#endif +#ifdef I386_ASM_16 + } else if (tok >= TOK_ASM_ax && tok <= TOK_ASM_di) { + reg = tok - TOK_ASM_ax; +#endif + } else { + error_32: + expect("register"); + } + next(); + return reg; +} + +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_XAX) + op->type |= OP_EAX; + else if (op->type == OP_REG8 && op->reg == TREG_XCX) + op->type |= OP_CL; + else if (op->type == OP_REG16 && op->reg == TREG_XDX) + 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: + tcc_error("unknown register"); + } + next(); + no_skip: ; + } else if (tok == '$') { + /* constant value */ + next(); + asm_expr(s1, &e); + op->type = OP_IM; + 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; +#ifdef TCC_TARGET_X86_64 + if (op->e.v == (uint32_t)op->e.v) + op->type |= OP_IM32; +#endif + } + } 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 { + next(); + if (tok == '%') { + unget_tok('('); + op->e.v = 0; + op->e.sym = NULL; + } else { + /* bracketed offset expression */ + asm_expr(s1, &e); + if (tok != ')') + expect(")"); + next(); + op->e.v = e.v; + op->e.sym = e.sym; + } + } + if (tok == '(') { + next(); + if (tok != ',') { + op->reg = asm_parse_reg(); + } + if (tok == ',') { + next(); + if (tok != ',') { + op->reg2 = asm_parse_reg(); + } + if (tok == ',') { + next(); + 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 ? */ +ST_FUNC void gen_expr32(ExprValue *pe) +{ + gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); +} + +#ifdef TCC_TARGET_X86_64 +static void gen_expr64(ExprValue *pe) +{ + gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v); +} +#endif + +/* XXX: unify with C code output ? */ +static void gen_disp32(ExprValue *pe) +{ + Sym *sym = pe->sym; + if (sym && 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 + sym->jnext - ind - 4); + } else { + if (sym && sym->type.t == VT_VOID) { + sym->type.t = VT_FUNC; + sym->type.ref = NULL; + } + gen_addrpc32(VT_SYM, sym, pe->v); + } +} + +#ifdef I386_ASM_16 +static void gen_expr16(ExprValue *pe) +{ + if (pe->sym) + greloc(cur_text_section, pe->sym, ind, R_386_16); + gen_le16(pe->v); +} +static void gen_disp16(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_le16(pe->v + sym->jnext - ind - 2); + } else { + greloc(cur_text_section, sym, ind, R_386_PC16); + gen_le16(pe->v - 2); + } + } else { + /* put an empty PC32 relocation */ + put_elf_reloc(symtab_section, cur_text_section, + ind, R_386_PC16, 0); + gen_le16(pe->v - 2); + } +} +#endif + +/* 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 */ +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) { + g(0x06 + (reg << 3)); + gen_expr16(&op->e); + } else if (tcc_state->seg_size == 32) +#endif + { + 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; +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 32) { +#endif + 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); + } +#ifdef I386_ASM_16 + } else if (tcc_state->seg_size == 16) { + /* edi = 7, esi = 6 --> di = 5, si = 4 */ + if ((reg1 == 6) || (reg1 == 7)) { + reg1 -= 2; + /* ebx = 3 --> bx = 7 */ + } else if (reg1 == 3) { + reg1 = 7; + /* o32 = 5 --> o16 = 6 */ + } else if (reg1 == 5) { + reg1 = 6; + /* sib not valid in 16-bit mode */ + } else if (reg1 == 4) { + reg2 = op->reg2; + /* bp + si + offset */ + if ((sib_reg1 == 5) && (reg2 == 6)) { + reg1 = 2; + /* bp + di + offset */ + } else if ((sib_reg1 == 5) && (reg2 == 7)) { + reg1 = 3; + /* bx + si + offset */ + } else if ((sib_reg1 == 3) && (reg2 == 6)) { + reg1 = 0; + /* bx + di + offset */ + } else if ((sib_reg1 == 3) && (reg2 == 7)) { + reg1 = 1; + } else { + tcc_error("invalid effective address"); + } + if (op->e.v == 0) + mod = 0; + } else { + tcc_error("invalid register"); + } + g(mod + (reg << 3) + reg1); + } +#endif + /* add offset */ + if (mod == 0x40) { + g(op->e.v); + } else if (mod == 0x80 || op->reg == -1) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + gen_expr16(&op->e); + else if (tcc_state->seg_size == 32) +#endif + gen_expr32(&op->e); + } + } +} + +ST_FUNC void asm_opcode(TCCState *s1, int opcode) +{ + const ASMInstr *pa; + int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix; + int nb_ops, s; + Operand ops[MAX_OPERANDS], *pop; + int op_type[3]; /* decoded op type */ +#ifdef I386_ASM_16 + static int a32 = 0, o32 = 0, addr32 = 0, data32 = 0; +#endif + + /* force synthetic ';' after prefix instruction, so we can handle */ + /* one-line things like "rep stosb" instead of only "rep\nstosb" */ + if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz) + unget_tok(';'); + + /* get operands */ + pop = ops; + nb_ops = 0; + seg_prefix = 0; + for(;;) { + if (tok == ';' || tok == TOK_LINEFEED) + break; + if (nb_ops >= MAX_OPERANDS) { + tcc_error("incorrect number of operands"); + } + parse_operand(s1, pop); + if (tok == ':') { + if (pop->type != OP_SEG || seg_prefix) + tcc_error("incorrect prefix"); + seg_prefix = segment_prefixes[pop->reg]; + next(); + parse_operand(s1, pop); +#ifndef I386_ASM_16 + if (!(pop->type & OP_EA)) { + tcc_error("segment prefix must be followed by memory reference"); + } +#endif + } + 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*NBWLX)) + continue; + s = (opcode - pa->sym) % NBWLX; + } else if (pa->instr_type & OPC_SHIFT) { + if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX)) + continue; + s = (opcode - pa->sym) % NBWLX; + } 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 + NBWLX)) + continue; + s = opcode - pa->sym; + } else if (pa->instr_type & OPC_WLX) { + if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1)) + 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 | OP_IM64; + break; + case OPT_REG: + v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64; + break; + case OPT_REGW: + v = OP_REG16 | OP_REG32 | OP_REG64; + break; + case OPT_IMW: + v = OP_IM16 | OP_IM32 | OP_IM64; + break; +#ifdef TCC_TARGET_X86_64 + case OPT_IMNO64: + v = OP_IM16 | OP_IM32; + break; +#endif + 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_first && opcode <= TOK_ASM_last) { + int b; + b = op0_codes[opcode - TOK_ASM_first]; +#ifdef I386_ASM_16 + if (opcode == TOK_ASM_o32) { + if (s1->seg_size == 32) + tcc_error("incorrect prefix"); + else + o32 = data32 = 1; + } else if (opcode == TOK_ASM_a32) { + if (s1->seg_size == 32) + tcc_error("incorrect prefix"); + else + a32 = addr32 = 1; + } +#endif + if (b & 0xff00) + g(b >> 8); + g(b); + return; + } else if (opcode <= TOK_ASM_alllast) { + tcc_error("bad operand with opcode '%s'", + get_tok_str(opcode, NULL)); + } else { + tcc_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 == NBWLX-1) { + for(i = 0; s == NBWLX-1 && 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 == NBWLX-1) { + if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && + (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64))) + s = 2; + else + tcc_error("cannot infer opcode suffix"); + } + } + +#ifdef I386_ASM_16 + for(i = 0; i < nb_ops; i++) { + if (ops[i].type & OP_REG32) { + if (s1->seg_size == 16) + o32 = 1; + } else if (!(ops[i].type & OP_REG32)) { + if (s1->seg_size == 32) + o32 = 1; + } + } + + + if (s == 1 || (pa->instr_type & OPC_D16)) { + if (s1->seg_size == 32) + o32 = 1; + } else if (s == 2) { + if (s1->seg_size == 16) { + if (!(pa->instr_type & OPC_D16)) + o32 = 1; + } + } + + /* generate a16/a32 prefix if needed */ + if ((a32 == 1) && (addr32 == 0)) + g(0x67); + /* generate o16/o32 prefix if needed */ + if ((o32 == 1) && (data32 == 0)) + g(0x66); + + addr32 = data32 = 0; +#else + /* generate data16 prefix if needed */ + if (s == 1 || (pa->instr_type & OPC_D16)) + g(0x66); +#ifdef TCC_TARGET_X86_64 + else if (s == 3) { + /* generate REX prefix */ + if ((opcode != TOK_ASM_push && opcode != TOK_ASM_pop) + || !(ops[0].type & OP_REG64)) + g(0x48); + } +#endif +#endif + + /* now generates the operation */ + if (pa->instr_type & OPC_FWAIT) + g(0x9b); + if (seg_prefix) + g(seg_prefix); + + v = pa->opcode; + if ((v == 0x69 || v == 0x6b) && nb_ops == 2) { + /* kludge for imul $im, %reg */ + nb_ops = 3; + ops[2] = ops[1]; + op_type[2] = op_type[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) / NBWLX) << 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 >= 1; + 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 + sym->jnext - 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 { + tcc_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) / NBWLX; + if (reg == 6) + reg = 7; + } else if (pa->instr_type & OPC_ARITH) { + reg = (opcode - pa->sym) / NBWLX; + } 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 + tcc_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 */ +#ifndef TCC_TARGET_X86_64 + if (pa->opcode == 0x9a || pa->opcode == 0xea) { + /* ljmp or lcall kludge */ +#ifdef I386_ASM_16 + if (s1->seg_size == 16 && o32 == 0) + gen_expr16(&ops[1].e); + else +#endif + gen_expr32(&ops[1].e); + if (ops[0].e.sym) + tcc_error("cannot relocate"); + gen_le16(ops[0].e.v); + return; + } +#endif + for(i = 0;i < nb_ops; i++) { + v = op_type[i]; + if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) { + /* if multiple sizes are given it means we must look + at the op size */ + if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) { + if (s == 0) + v = OP_IM8; + else if (s == 1) + v = OP_IM16; + else if (s == 2 || (v & OP_IM64) == 0) + v = OP_IM32; + else + v = OP_IM64; + } + if (v & (OP_IM8 | OP_IM8S)) { + if (ops[i].e.sym) + goto error_relocate; + g(ops[i].e.v); + } else if (v & OP_IM16) { +#ifdef I386_ASM_16 + if (s1->seg_size == 16) + gen_expr16(&ops[i].e); + else +#endif + if (ops[i].e.sym) + error_relocate: + tcc_error("cannot relocate"); + else + gen_le16(ops[i].e.v); + } else { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + if (is_short_jmp) + g(ops[i].e.v); +#ifdef I386_ASM_16 + else if (s1->seg_size == 16) + gen_disp16(&ops[i].e); +#endif + else + gen_disp32(&ops[i].e); + } else { +#ifdef I386_ASM_16 + if (s1->seg_size == 16 && !((o32 == 1) && (v & OP_IM32))) + gen_expr16(&ops[i].e); + else +#endif +#ifdef TCC_TARGET_X86_64 + if (v & OP_IM64) + gen_expr64(&ops[i].e); + else +#endif + gen_expr32(&ops[i].e); + } + } +#ifdef I386_ASM_16 + } else if (v & (OP_REG16 | OP_REG32)) { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + /* jmp $r */ + g(0xE0 + ops[i].reg); + } +#endif +#ifdef TCC_TARGET_X86_64 + } else if (v & (OP_REG32 | OP_REG64)) { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + /* jmp $r */ + g(0xE0 + ops[i].reg); + } +#endif + } + } +#ifdef I386_ASM_16 + a32 = o32 = 0; +#endif +} + +/* 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: + tcc_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) + +ST_FUNC 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) + tcc_error("invalid reference in constraint %d ('%s')", + i, str); + op->ref_index = k; + if (operands[k].input_index >= 0) + tcc_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) + tcc_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_XAX) || + is_reg_allocated(TREG_XDX)) + goto try_next; + op->is_llong = 1; + op->reg = TREG_XAX; + regs_allocated[TREG_XAX] |= reg_mask; + regs_allocated[TREG_XDX] |= reg_mask; + break; + case 'a': + reg = TREG_XAX; + goto alloc_reg; + case 'b': + reg = 3; + goto alloc_reg; + case 'c': + reg = TREG_XCX; + goto alloc_reg; + case 'd': + reg = TREG_XDX; + 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: + tcc_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; + } + tcc_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 +} + +ST_FUNC 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) + tcc_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) + tcc_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; +#ifdef TCC_TARGET_X86_64 + else if ((sv->type.t & VT_BTYPE) == VT_LLONG) + size = 8; +#endif + else + size = 4; + if (size == 1 && reg >= 4) + size = 4; + + if (modifier == 'b') { + if (reg >= 4) + tcc_error("cannot use byte register"); + size = 1; + } else if (modifier == 'h') { + if (reg >= 4) + tcc_error("cannot use byte register"); + size = -1; + } else if (modifier == 'w') { + size = 2; +#ifdef TCC_TARGET_X86_64 + } else if (modifier == 'q') { + size = 8; +#endif + } + + 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; +#ifdef TCC_TARGET_X86_64 + case 8: + reg = TOK_ASM_rax + reg; + break; +#endif + } + snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); + cstr_cat(add_str, buf); + } +} + +/* generate prolog and epilog code for asm statement */ +ST_FUNC 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]) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + g(0x66); +#endif + 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_XDX, &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_XDX, &sv); + } + } + } + } + /* generate reg restore code */ + for(i = NB_SAVED_REGS - 1; i >= 0; i--) { + reg = reg_saved[i]; + if (regs_allocated[reg]) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + g(0x66); +#endif + g(0x58 + reg); + } + } + } +} + +ST_FUNC 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; +#ifdef TCC_TARGET_X86_64 + } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) { + reg -= TOK_ASM_rax; +#endif + } else { + tcc_error("invalid clobber register '%s'", str); + } + clobber_regs[reg] = 1; +} diff --git a/src/x86/i386-asm.h b/src/x86/i386-asm.h new file mode 100644 index 0000000..2e82e0d --- /dev/null +++ b/src/x86/i386-asm.h @@ -0,0 +1,474 @@ + DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */ + 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(pusha, 0x60) + DEF_ASM_OP0(popa, 0x61) + 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(nop, 0x90) + DEF_ASM_OP0(pause, 0xf390) + 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(wait, 0x9b) + DEF_ASM_OP0(fwait, 0x9b) +#ifdef I386_ASM_16 + DEF_ASM_OP0(a32, 0x67) + DEF_ASM_OP0(o32, 0x66) +#else + DEF_ASM_OP0(aword, 0x67) + DEF_ASM_OP0(addr16, 0x67) + ALT(DEF_ASM_OP0(word, 0x66)) + DEF_ASM_OP0(data16, 0x66) +#endif + 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)) +#ifdef I386_ASM_16 +ALT(DEF_ASM_OP1(jmp, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW)) +#endif + +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)) +ALT(DEF_ASM_OP1(setob, 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) + DEF_ASM_OP0(retl,0xc3) +ALT(DEF_ASM_OP1(retl,0xc2, 0, 0, OPT_IM16)) +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_OP2(fadd, 0xdcc0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) +ALT(DEF_ASM_OP2(fmul, 0xdcc8, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) +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) + + /* 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) + +#ifdef I386_ASM_16 + /* 386 */ + DEF_ASM_OP0(loadall386, 0x0f07) +#endif + + /* 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)) +#ifdef I386_ASM_16 +ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmova, 0x0f47, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +#endif + 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 ) /* must be last !OP0 */ + +#undef ALT +#undef DEF_ASM_OP0 +#undef DEF_ASM_OP0L +#undef DEF_ASM_OP1 +#undef DEF_ASM_OP2 +#undef DEF_ASM_OP3 diff --git a/src/x86/i386-gen.c b/src/x86/i386-gen.c new file mode 100644 index 0000000..b8b12aa --- /dev/null +++ b/src/x86/i386-gen.c @@ -0,0 +1,1144 @@ +/* + * 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 + */ + +#ifdef TARGET_DEFS_ONLY + +/* number of available registers */ +#define NB_REGS 4 +#define NB_ASM_REGS 8 + +typedef int RegArgs; + +/* 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, + TREG_ESP = 4 +}; + +/* 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 + + +#define psym oad + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_386 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_386_32 +#define R_DATA_PTR 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 + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#include "../tcc.h" + +ST_DATA const 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, +}; + +static unsigned long func_sub_sp_offset; +static int func_ret_sub; +#ifdef CONFIG_TCC_BCHECK +static addr_t func_bound_offset; +#endif + +/* XXX: make it faster ? */ +ST_FUNC 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; +} + +ST_FUNC void o(unsigned int c) +{ + while (c) { + g(c); + c = c >> 8; + } +} + +ST_FUNC void gen_le16(int v) +{ + g(v); + g(v >> 8); +} + +ST_FUNC 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 */ +ST_FUNC 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; + } +} + +ST_FUNC 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 */ +ST_FUNC 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 */ +ST_FUNC 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); +} + +ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_386_PC32); + gen_le32(c - 4); +} + +/* 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' */ +ST_FUNC void load(int r, SValue *sv) +{ + int v, t, ft, fc, fr; + SValue v1; + +#ifdef TCC_TARGET_PE + SValue v2; + sv = pe_getimport(sv, &v2); +#endif + + 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; + fr = r; + if (!(reg_classes[fr] & RC_INT)) + fr = get_reg(RC_INT); + load(fr, &v1); + } + 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 || (ft & VT_TYPE) == VT_BOOL) { + 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) { + if (fc) { + o(0x8d); /* lea xxx(%ebp), r */ + gen_modrm(r, VT_LOCAL, sv->sym, fc); + } else { + o(0x89); + o(0xe8 + r); /* mov %ebp, r */ + } + } 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' */ +ST_FUNC void store(int r, SValue *v) +{ + int fr, bt, ft, fc; + +#ifdef TCC_TARGET_PE + SValue v2; + v = pe_getimport(v, &v2); +#endif + + 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 */ + } +} + +static void gen_static_call(int v) +{ + Sym *sym; + + sym = external_global_sym(v, &func_old_type, 0); + oad(0xe8, -4); + greloc(cur_text_section, sym, ind-4, R_386_PC32); +} + +/* '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 }; +static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; + +ST_FUNC int regargs_nregs(RegArgs *args) +{ + return *args; +} + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) +{ +#ifdef TCC_TARGET_PE + int size, align; + + *ret_align = 1; // Never have to re-align return values for x86 + *regsize = 4; + size = type_size(vt, &align); + if (size > 8) { + *args = 0; + } else if (size > 4) { + ret->ref = NULL; + ret->t = VT_LLONG; + *args = 1; + } else { + ret->ref = NULL; + ret->t = VT_INT; + *args = 1; + } +#else + *ret_align = 1; // Never have to re-align return values for x86 + *args = 0; +#endif + + return *args != 0; +} + +/* 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. */ +ST_FUNC 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->a.func_call; + /* fast call case */ + if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) || + func_call == FUNC_FASTCALLW) { + int fastcall_nb_regs; + uint8_t *fastcall_regs_ptr; + if (func_call == FUNC_FASTCALLW) { + fastcall_regs_ptr = fastcallw_regs; + fastcall_nb_regs = 2; + } else { + fastcall_regs_ptr = fastcall_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_ptr[i]); /* pop r */ + /* XXX: incorrect for struct/floats */ + args_size -= 4; + } + } +#ifndef TCC_TARGET_PE + else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT) + args_size -= 4; +#endif + gcall_or_jmp(0); + + if (args_size && func_call != 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' */ +ST_FUNC void gfunc_prolog(CType *func_type) +{ + int addr, align, size, func_call, fastcall_nb_regs; + int param_index, param_addr; + uint8_t *fastcall_regs_ptr; + Sym *sym; + CType *type; + + sym = func_type->ref; + func_call = sym->a.func_call; + addr = 8; + loc = 0; + func_vc = 0; + + if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { + fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; + fastcall_regs_ptr = fastcall_regs; + } else if (func_call == FUNC_FASTCALLW) { + fastcall_nb_regs = 2; + fastcall_regs_ptr = fastcallw_regs; + } else { + fastcall_nb_regs = 0; + fastcall_regs_ptr = NULL; + } + 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; + func_var = (sym->c == FUNC_ELLIPSIS); +#ifdef TCC_TARGET_PE + size = type_size(&func_vt,&align); + if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) { +#else + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { +#endif + /* 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_ptr[param_index], VT_LOCAL, NULL, loc); + param_addr = loc; + } else { + param_addr = addr; + addr += size; + } + sym_push(sym->v & ~SYM_FIELD, type, + VT_LOCAL | lvalue_type(type->t), param_addr); + param_index++; + } + func_ret_sub = 0; + /* pascal type call ? */ + if (func_call == FUNC_STDCALL) + func_ret_sub = addr - 8; +#ifndef TCC_TARGET_PE + else if (func_vc) + func_ret_sub = 4; +#endif + +#ifdef CONFIG_TCC_BCHECK + /* leave some room for bound checking code */ + if (tcc_state->do_bounds_check) { + oad(0xb8, 0); /* lbound section pointer */ + oad(0xb8, 0); /* call to function */ + func_bound_offset = lbounds_section->data_offset; + } +#endif +} + +/* generate function epilog */ +ST_FUNC void gfunc_epilog(void) +{ + addr_t v, saved_ind; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check + && func_bound_offset != lbounds_section->data_offset) { + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *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 */ + gen_static_call(TOK___bound_local_new); + + 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 */ + gen_static_call(TOK___bound_local_delete); + + 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) { + oad(0xb8, v); /* mov stacksize, %eax */ + gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */ + } 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 */ +ST_FUNC int gjmp(int t) +{ + return psym(0xe9, t); +} + +/* generate a jump to a fixed address */ +ST_FUNC 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 */ +ST_FUNC 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); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +ST_FUNC 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) { + /* generate inc and dec for smaller code */ + if (c==1 && opc==0) { + o (0x40 | r); // inc + } else if (c==1 && opc==5) { + o (0x48 | r); // dec + } else { + 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 */ +ST_FUNC 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) */ + if (op == TOK_EQ || op == TOK_NE) + o(0xe9da); /* fucompp */ + else + o(0xd9de); /* fcompp */ + 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. */ +ST_FUNC 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 */ +ST_FUNC void gen_cvt_ftoi(int t) +{ + #ifndef COMMIT_4ad186c5ef61_IS_FIXED + /* a good version but it takes a more time to execute */ + gv(RC_FLOAT); + save_reg(TREG_EAX); + save_reg(TREG_EDX); + gen_static_call(TOK___tcc_cvt_ftol); + vtop->r = TREG_EAX; /* mark reg as used */ + if (t == VT_LLONG) + vtop->r2 = TREG_EDX; + #else + /* a new version with a bug: t2a = 44100312 */ + /* + #include<stdio.h> + int main() { + int t1 = 176401255; + float f = 0.25f; + int t2a = (int)(t1 * f); // must be 44100313 + int t2b = (int)(t1 * (float)0.25f); + printf("t2a=%d t2b=%d \n",t2a,t2b); + return 0; + } + */ + int bt = vtop->type.t & VT_BTYPE; + if (bt == VT_FLOAT) + vpush_global_sym(&func_old_type, TOK___fixsfdi); + else if (bt == VT_LDOUBLE) + vpush_global_sym(&func_old_type, TOK___fixxfdi); + else + vpush_global_sym(&func_old_type, TOK___fixdfdi); + vswap(); + gfunc_call(1); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = REG_LRET; + #endif +} + +/* convert from one floating point type to another */ +ST_FUNC 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 */ +ST_FUNC void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* bound check support functions */ +#ifdef CONFIG_TCC_BCHECK + +/* generate a bounded pointer addition */ +ST_FUNC void gen_bounded_ptr_add(void) +{ + /* 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 */ + gen_static_call(TOK___bound_ptr_add); + /* 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 */ +ST_FUNC void gen_bounded_ptr_deref(void) +{ + addr_t func; + addr_t 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: + tcc_error("unhandled size when dereferencing 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 + +/* Save the stack pointer onto the stack */ +ST_FUNC void gen_vla_sp_save(int addr) { + /* mov %esp,addr(%ebp)*/ + o(0x89); + gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr); +} + +/* Restore the SP from a location on the stack */ +ST_FUNC void gen_vla_sp_restore(int addr) { + o(0x8b); + gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack */ +ST_FUNC void gen_vla_alloc(CType *type, int align) { +#ifdef TCC_TARGET_PE + /* alloca does more than just adjust %rsp on Windows */ + vpush_global_sym(&func_old_type, TOK_alloca); + vswap(); /* Move alloca ref past allocation size */ + gfunc_call(1); +#else + int r; + r = gv(RC_INT); /* allocation size */ + /* sub r,%rsp */ + o(0x2b); + o(0xe0 | r); + /* We align to 16 bytes rather than align */ + /* and ~15, %esp */ + o(0xf0e483); + vpop(); +#endif +} + +/* end of X86 code generator */ +/*************************************************************/ +#endif +/*************************************************************/ diff --git a/src/x86/i386-tok.h b/src/x86/i386-tok.h new file mode 100644 index 0000000..b0ca3ed --- /dev/null +++ b/src/x86/i386-tok.h @@ -0,0 +1,244 @@ +/* ------------------------------------------------------------------ */ +/* WARNING: relative order of tokens is important. */ + +/* register */ + 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) +#ifdef TCC_TARGET_X86_64 + DEF_ASM(rax) + DEF_ASM(rcx) + DEF_ASM(rdx) + DEF_ASM(rbx) + DEF_ASM(rsp) + DEF_ASM(rbp) + DEF_ASM(rsi) + DEF_ASM(rdi) +#endif + 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) + + /* generic two operands */ + DEF_BWLX(mov) + + DEF_BWLX(add) + DEF_BWLX(or) + DEF_BWLX(adc) + DEF_BWLX(sbb) + DEF_BWLX(and) + DEF_BWLX(sub) + DEF_BWLX(xor) + DEF_BWLX(cmp) + + /* unary ops */ + DEF_BWLX(inc) + DEF_BWLX(dec) + DEF_BWLX(not) + DEF_BWLX(neg) + DEF_BWLX(mul) + DEF_BWLX(imul) + DEF_BWLX(div) + DEF_BWLX(idiv) + + DEF_BWLX(xchg) + DEF_BWLX(test) + + /* shifts */ + DEF_BWLX(rol) + DEF_BWLX(ror) + DEF_BWLX(rcl) + DEF_BWLX(rcr) + DEF_BWLX(shl) + DEF_BWLX(shr) + DEF_BWLX(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) +#ifdef TCC_TARGET_X86_64 + DEF_ASM(pushq) +#endif + DEF_ASM(push) + + DEF_ASM(popw) + DEF_ASM(popl) +#ifdef TCC_TARGET_X86_64 + DEF_ASM(popq) +#endif + DEF_ASM(pop) + + DEF_BWL(in) + DEF_BWL(out) + + DEF_WL(movzb) + DEF_ASM(movzwl) + DEF_ASM(movsbw) + DEF_ASM(movsbl) + DEF_ASM(movswl) +#ifdef TCC_TARGET_X86_64 + DEF_ASM(movslq) +#endif + + DEF_WLX(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(set,b) + DEF_ASMTEST(cmov,) + + DEF_WLX(bsf) + DEF_WLX(bsr) + DEF_WLX(bt) + DEF_WLX(bts) + DEF_WLX(btr) + DEF_WLX(btc) + + DEF_WLX(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_BWLX(xadd) + DEF_BWLX(cmpxchg) + + /* string ops */ + DEF_BWLX(cmps) + DEF_BWLX(scmp) + DEF_BWL(ins) + DEF_BWL(outs) + DEF_BWLX(lods) + DEF_BWLX(slod) + DEF_BWLX(movs) + DEF_BWLX(smov) + DEF_BWLX(scas) + DEF_BWLX(ssca) + DEF_BWLX(stos) + DEF_BWLX(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) +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif + +#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) +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif diff --git a/src/x86/x86_64-asm.h b/src/x86/x86_64-asm.h new file mode 100644 index 0000000..2f80e4b --- /dev/null +++ b/src/x86/x86_64-asm.h @@ -0,0 +1,449 @@ + DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */ + 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(cqto, 0x4899) + 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_BWLQ)) +ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWLQ)) + +ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWLQ)) +ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWLQ)) + +ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWLQ)) +ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWLQ)) + +ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWLQ)) +ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWLQ)) + +ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWLQ)) +ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWLQ)) + +ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWLQ)) +ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWLQ)) + + /* bits */ + +ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW)) + +ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) + +ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) + +ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) + +ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA)) + + /* prefixes */ + 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_BWLQ, OPT_ADDR, OPT_EAX)) +ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWLQ, OPT_EAX, OPT_ADDR)) +ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) +ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWLQ, OPT_IM, OPT_REG)) +ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWLQ, OPT_IM, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WLQ, OPT_SEG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WLQ, OPT_EA | OPT_REG, OPT_SEG)) + +ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WLQ, OPT_CR, OPT_REG64)) +ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WLQ, OPT_DB, OPT_REG64)) +ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WLQ, OPT_TR, OPT_REG64)) +ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_CR)) +ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_DB)) +ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, 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(movslq, 0x4863, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG)) +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_WLQ, OPT_REG64)) +ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WLQ, OPT_REG64 | OPT_EA)) +ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WLQ, OPT_IM32)) +ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WLQ, OPT_SEG)) + DEF_ASM_OP1(pushb, 0x6a, 0, OPC_B, OPT_IM8S) + +ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLQ, OPT_REGW)) +ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WLQ, OPT_SEG)) + +ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_REG, OPT_EAX)) +ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_EAX, OPT_REG)) +ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, 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_WLQ, 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_BWLQ, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */ +ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) +ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWLQ, OPT_IMNO64, OPT_EAX)) +ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_EA | OPT_REG)) + +ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG)) +ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWLQ, OPT_IMNO64, OPT_EAX)) +ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG)) + +ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WLQ, OPT_REGW)) +ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WLQ, OPT_REGW)) +ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WLQ, OPT_REG | OPT_EA, OPT_REG)) +ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW)) +ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW)) + +ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX)) +ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX)) + + /* shifts */ +ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_EA | OPT_REG)) + +ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, 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, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW)) +ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR)) + +ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA)) +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)) +ALT(DEF_ASM_OP1(setob, 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)) +ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(cmova, 0x0f47, 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 )) +ALT(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 ) /* must be last !OP0 */ + +#undef ALT +#undef DEF_ASM_OP0 +#undef DEF_ASM_OP0L +#undef DEF_ASM_OP1 +#undef DEF_ASM_OP2 +#undef DEF_ASM_OP3 diff --git a/src/x86/x86_64-gen.c b/src/x86/x86_64-gen.c new file mode 100644 index 0000000..0ce5846 --- /dev/null +++ b/src/x86/x86_64-gen.c @@ -0,0 +1,2360 @@ +/* + * x86-64 code generator for TCC + * + * Copyright (c) 2008 Shinichiro Hamaji + * + * Based on i386-gen.c by 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 + */ + +#ifdef TARGET_DEFS_ONLY + +/* number of available registers */ +#define NB_REGS 25 +#define NB_ASM_REGS 8 +#define REG_ARGS_MAX 2 /* at most 2 registers used for each argument */ + +#ifdef TCC_TARGET_PE +typedef int RegArgs; +#else +/* This struct stores the struct offsets at which %rax, %rdx, %xmm0, and + * %xmm1 are to be stored. + * + * struct { long long l; double x; }: ireg = { 0, -1 } freg = { 8, -1 } + * struct { double x; long long l; }: ireg = { 8, -1 } freg = { 0, -1 } + * struct { long long l; long long l2; }: ireg = { 0, 8 } freg = { -1, -1 } + * struct { double x; double x2; }: ireg = { -1, -1 } freg = { 0, 8 } + */ +typedef struct { + int ireg[REG_ARGS_MAX]; + int freg[REG_ARGS_MAX]; +} RegArgs; +#endif + +/* 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_RAX 0x0004 +#define RC_RCX 0x0008 +#define RC_RDX 0x0010 +#define RC_ST0 0x0080 /* only for long double */ +#define RC_R8 0x0100 +#define RC_R9 0x0200 +#define RC_R10 0x0400 +#define RC_R11 0x0800 +#define RC_XMM0 0x1000 +#define RC_XMM1 0x2000 +#define RC_XMM2 0x4000 +#define RC_XMM3 0x8000 +#define RC_XMM4 0x10000 +#define RC_XMM5 0x20000 +#define RC_XMM6 0x40000 +#define RC_XMM7 0x80000 +#define RC_IRET RC_RAX /* function return: integer register */ +#define RC_LRET RC_RDX /* function return: second integer register */ +#define RC_FRET RC_XMM0 /* function return: float register */ +#define RC_QRET RC_XMM1 /* function return: second float register */ + +/* pretty names for the registers */ +enum { + TREG_RAX = 0, + TREG_RCX = 1, + TREG_RDX = 2, + TREG_RSP = 4, + TREG_RSI = 6, + TREG_RDI = 7, + + TREG_R8 = 8, + TREG_R9 = 9, + TREG_R10 = 10, + TREG_R11 = 11, + + TREG_XMM0 = 16, + TREG_XMM1 = 17, + TREG_XMM2 = 18, + TREG_XMM3 = 19, + TREG_XMM4 = 20, + TREG_XMM5 = 21, + TREG_XMM6 = 22, + TREG_XMM7 = 23, + + TREG_ST0 = 24, + + TREG_MEM = 0x20, +}; + +#define REX_BASE(reg) (((reg) >> 3) & 1) +#define REG_VALUE(reg) ((reg) & 7) + +/* return registers for function */ +#define REG_IRET TREG_RAX /* single word int return register */ +#define REG_LRET TREG_RDX /* second word return register (for long long) */ +#define REG_FRET TREG_XMM0 /* float return register */ +#define REG_QRET TREG_XMM1 /* second float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +#define INVERT_FUNC_PARAMS + +/* pointer size, in bytes */ +#define PTR_SIZE 8 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 16 +#define LDOUBLE_ALIGN 16 +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 16 + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_X86_64 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_X86_64_32 +#define R_DATA_PTR R_X86_64_64 +#define R_JMP_SLOT R_X86_64_JUMP_SLOT +#define R_COPY R_X86_64_COPY + +#define ELF_START_ADDR 0x400000 +#define ELF_PAGE_SIZE 0x200000 + +/******************************************************/ +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#include "../tcc.h" +#include <assert.h> + +ST_DATA const int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_RAX, + /* ecx */ RC_INT | RC_RCX, + /* edx */ RC_INT | RC_RDX, + 0, + 0, + 0, + 0, + 0, + RC_R8, + RC_R9, + RC_R10, + RC_R11, + 0, + 0, + 0, + 0, + /* xmm0 */ RC_FLOAT | RC_XMM0, + /* xmm1 */ RC_FLOAT | RC_XMM1, + /* xmm2 */ RC_FLOAT | RC_XMM2, + /* xmm3 */ RC_FLOAT | RC_XMM3, + /* xmm4 */ RC_FLOAT | RC_XMM4, + /* xmm5 */ RC_FLOAT | RC_XMM5, + /* xmm6 an xmm7 are included so gv() can be used on them, + but they are not tagged with RC_FLOAT because they are + callee saved on Windows */ + RC_XMM6, + RC_XMM7, + /* st0 */ RC_ST0 +}; + +static unsigned long func_sub_sp_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_le16(int v) +{ + g(v); + g(v >> 8); +} + +void gen_le32(int c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); +} + +void gen_le64(int64_t c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); + g(c >> 32); + g(c >> 40); + g(c >> 48); + g(c >> 56); +} + +void orex(int ll, int r, int r2, int b) +{ + if ((r & VT_VALMASK) >= VT_CONST) + r = 0; + if ((r2 & VT_VALMASK) >= VT_CONST) + r2 = 0; + if (ll || REX_BASE(r) || REX_BASE(r2)) + o(0x40 | REX_BASE(r) | (REX_BASE(r2) << 2) | (ll << 3)); + o(b); +} + +/* 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 + +static int is64_type(int t) +{ + return ((t & VT_BTYPE) == VT_PTR || + (t & VT_BTYPE) == VT_FUNC || + (t & VT_BTYPE) == VT_LLONG); +} + +/* instruction + 4 bytes data. Return the address of the data */ +ST_FUNC 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; +} + +ST_FUNC void gen_addr32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_X86_64_32); + gen_le32(c); +} + +/* output constant with relocation if 'r & VT_SYM' is true */ +ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_X86_64_64); + gen_le64(c); +} + +/* output constant with relocation if 'r & VT_SYM' is true */ +ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_X86_64_PC32); + gen_le32(c-4); +} + +/* output got address with relocation */ +static void gen_gotpcrel(int r, Sym *sym, int c) +{ +#ifndef TCC_TARGET_PE + Section *sr; + ElfW(Rela) *rel; + greloc(cur_text_section, sym, ind, R_X86_64_GOTPCREL); + sr = cur_text_section->reloc; + rel = (ElfW(Rela) *)(sr->data + sr->data_offset - sizeof(ElfW(Rela))); + rel->r_addend = -4; +#else + tcc_error("internal error: no GOT on PE: %s %x %x | %02x %02x %02x\n", + get_tok_str(sym->v, NULL), c, r, + cur_text_section->data[ind-3], + cur_text_section->data[ind-2], + cur_text_section->data[ind-1] + ); + greloc(cur_text_section, sym, ind, R_X86_64_PC32); +#endif + gen_le32(0); + if (c) { + /* we use add c, %xxx for displacement */ + orex(1, r, 0, 0x81); + o(0xc0 + REG_VALUE(r)); + gen_le32(c); + } +} + +static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got) +{ + op_reg = REG_VALUE(op_reg) << 3; + if ((r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + o(0x05 | op_reg); + if (is_got) { + gen_gotpcrel(r, sym, c); + } else { + gen_addrpc32(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 if ((r & VT_VALMASK) >= TREG_MEM) { + if (c) { + g(0x80 | op_reg | REG_VALUE(r)); + gen_le32(c); + } else { + g(0x00 | op_reg | REG_VALUE(r)); + } + } else { + g(0x00 | op_reg | REG_VALUE(r)); + } +} + +/* 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) +{ + gen_modrm_impl(op_reg, r, sym, c, 0); +} + +/* generate a modrm reference. 'op_reg' contains the addtionnal 3 + opcode bits */ +static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c) +{ + int is_got; + is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC); + orex(1, r, op_reg, opcode); + gen_modrm_impl(op_reg, r, sym, c, is_got); +} + + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, t, ft, fc, fr; + SValue v1; + +#ifdef TCC_TARGET_PE + SValue v2; + sv = pe_getimport(sv, &v2); +#endif + + fr = sv->r; + ft = sv->type.t & ~VT_DEFSIGN; + fc = sv->c.ul; + +#ifndef TCC_TARGET_PE + /* we use indirect access via got */ + if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) && + (fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) { + /* use the result register as a temporal register */ + int tr = r | TREG_MEM; + if (is_float(ft)) { + /* we cannot use float registers as a temporal register */ + tr = get_reg(RC_INT) | TREG_MEM; + } + gen_modrm64(0x8b, tr, fr, sv->sym, 0); + + /* load from the temporal register */ + fr = tr | VT_LVAL; + } +#endif + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + int b, ll; + if (v == VT_LLOCAL) { + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + fr = r; + if (!(reg_classes[fr] & (RC_INT|RC_R11))) + fr = get_reg(RC_INT); + load(fr, &v1); + } + ll = 0; + if ((ft & VT_BTYPE) == VT_FLOAT) { + b = 0x6e0f66; + r = REG_VALUE(r); /* movd */ + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + b = 0x7e0ff3; /* movq */ + r = REG_VALUE(r); + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + b = 0xdb, r = 5; /* fldt */ + } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { + b = 0xbe0f; /* movsbl */ + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + b = 0xb60f; /* movzbl */ + } else if ((ft & VT_TYPE) == VT_SHORT) { + b = 0xbf0f; /* movswl */ + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + b = 0xb70f; /* movzwl */ + } else { + assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG) + || ((ft & VT_BTYPE) == VT_PTR) || ((ft & VT_BTYPE) == VT_ENUM) + || ((ft & VT_BTYPE) == VT_FUNC)); + ll = is64_type(ft); + b = 0x8b; + } + if (ll) { + gen_modrm64(b, r, fr, sv->sym, fc); + } else { + orex(ll, fr, r, b); + gen_modrm(r, fr, sv->sym, fc); + } + } else { + if (v == VT_CONST) { + if (fr & VT_SYM) { +#ifdef TCC_TARGET_PE + orex(1,0,r,0x8d); + o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */ + gen_addrpc32(fr, sv->sym, fc); +#else + if (sv->sym->type.t & VT_STATIC) { + orex(1,0,r,0x8d); + o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */ + gen_addrpc32(fr, sv->sym, fc); + } else { + orex(1,0,r,0x8b); + o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */ + gen_gotpcrel(r, sv->sym, fc); + } +#endif + } else if (is64_type(ft)) { + orex(1,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ + gen_le64(sv->c.ull); + } else { + orex(0,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ + gen_le32(fc); + } + } else if (v == VT_LOCAL) { + orex(1,0,r,0x8d); /* lea xxx(%ebp), r */ + gen_modrm(r, VT_LOCAL, sv->sym, fc); + } else if (v == VT_CMP) { + orex(0,r,0,0); + if ((fc & ~0x100) != TOK_NE) + oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */ + else + oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */ + if (fc & 0x100) { + /* This was a float compare. If the parity bit is + * set the result was unordered, meaning false for everything + * except TOK_NE, and true for TOK_NE. */ + fc &= ~0x100; + o(0x037a + (REX_BASE(r) << 8)); + } + orex(0,r,0, 0x0f); /* setxx %br */ + o(fc); + o(0xc0 + REG_VALUE(r)); + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + orex(0,r,0,0); + oad(0xb8 + REG_VALUE(r), t); /* mov $1, r */ + o(0x05eb + (REX_BASE(r) << 8)); /* jmp after */ + gsym(fc); + orex(0,r,0,0); + oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */ + } else if (v != r) { + if ((r >= TREG_XMM0) && (r <= TREG_XMM7)) { + if (v == TREG_ST0) { + /* gen_cvt_ftof(VT_DOUBLE); */ + o(0xf0245cdd); /* fstpl -0x10(%rsp) */ + /* movsd -0x10(%rsp),%xmmN */ + o(0x100ff2); + o(0x44 + REG_VALUE(r)*8); /* %xmmN */ + o(0xf024); + } else { + assert((v >= TREG_XMM0) && (v <= TREG_XMM7)); + if ((ft & VT_BTYPE) == VT_FLOAT) { + o(0x100ff3); + } else { + assert((ft & VT_BTYPE) == VT_DOUBLE); + o(0x100ff2); + } + o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8); + } + } else if (r == TREG_ST0) { + assert((v >= TREG_XMM0) && (v <= TREG_XMM7)); + /* gen_cvt_ftof(VT_LDOUBLE); */ + /* movsd %xmmN,-0x10(%rsp) */ + o(0x110ff2); + o(0x44 + REG_VALUE(r)*8); /* %xmmN */ + o(0xf024); + o(0xf02444dd); /* fldl -0x10(%rsp) */ + } else { + orex(1,r,v, 0x89); + o(0xc0 + REG_VALUE(r) + REG_VALUE(v) * 8); /* mov v, r */ + } + } + } +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *v) +{ + int fr, bt, ft, fc; + int op64 = 0; + /* store the REX prefix in this variable when PIC is enabled */ + int pic = 0; + +#ifdef TCC_TARGET_PE + SValue v2; + v = pe_getimport(v, &v2); +#endif + + ft = v->type.t; + fc = v->c.ul; + fr = v->r & VT_VALMASK; + bt = ft & VT_BTYPE; + +#ifndef TCC_TARGET_PE + /* we need to access the variable via got */ + if (fr == VT_CONST && (v->r & VT_SYM)) { + /* mov xx(%rip), %r11 */ + o(0x1d8b4c); + gen_gotpcrel(TREG_R11, v->sym, v->c.ul); + pic = is64_type(bt) ? 0x49 : 0x41; + } +#endif + + /* XXX: incorrect if float reg to reg */ + if (bt == VT_FLOAT) { + o(0x66); + o(pic); + o(0x7e0f); /* movd */ + r = REG_VALUE(r); + } else if (bt == VT_DOUBLE) { + o(0x66); + o(pic); + o(0xd60f); /* movq */ + r = REG_VALUE(r); + } else if (bt == VT_LDOUBLE) { + o(0xc0d9); /* fld %st(0) */ + o(pic); + o(0xdb); /* fstpt */ + r = 7; + } else { + if (bt == VT_SHORT) + o(0x66); + o(pic); + if (bt == VT_BYTE || bt == VT_BOOL) + orex(0, 0, r, 0x88); + else if (is64_type(bt)) + op64 = 0x89; + else + orex(0, 0, r, 0x89); + } + if (pic) { + /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */ + if (op64) + o(op64); + o(3 + (r << 3)); + } else if (op64) { + if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { + gen_modrm64(op64, r, v->r, v->sym, fc); + } else if (fr != r) { + /* XXX: don't we really come here? */ + abort(); + o(0xc0 + fr + r * 8); /* mov r, fr */ + } + } else { + if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { + gen_modrm(r, v->r, v->sym, fc); + } else if (fr != r) { + /* XXX: don't we really come here? */ + abort(); + o(0xc0 + fr + r * 8); /* mov r, fr */ + } + } +} + +/* '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 && + ((vtop->r & VT_SYM) || (vtop->c.ll-4) == (int)(vtop->c.ll-4))) { + /* constant case */ + if (vtop->r & VT_SYM) { + /* relocation case */ +#ifdef TCC_TARGET_PE + greloc(cur_text_section, vtop->sym, ind + 1, R_X86_64_PC32); +#else + greloc(cur_text_section, vtop->sym, ind + 1, R_X86_64_PLT32); +#endif + } else { + /* put an empty PC32 relocation */ + put_elf_reloc(symtab_section, cur_text_section, + ind + 1, R_X86_64_PC32, 0); + } + oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ + } else { + /* otherwise, indirect call */ + r = TREG_R11; + load(r, vtop); + o(0x41); /* REX */ + o(0xff); /* call/jmp *r */ + o(0xd0 + REG_VALUE(r) + (is_jmp << 4)); + } +} + +#if defined(CONFIG_TCC_BCHECK) +#ifndef TCC_TARGET_PE +static addr_t func_bound_offset; +static unsigned long func_bound_ind; +#endif + +static void gen_static_call(int v) +{ + Sym *sym = external_global_sym(v, &func_old_type, 0); + oad(0xe8, -4); + greloc(cur_text_section, sym, ind-4, R_X86_64_PC32); +} + +/* generate a bounded pointer addition */ +ST_FUNC void gen_bounded_ptr_add(void) +{ + /* save all temporary registers */ + save_regs(0); + + /* prepare fast x86_64 function call */ + gv(RC_RAX); + o(0xc68948); // mov %rax,%rsi ## second arg in %rsi, this must be size + vtop--; + + gv(RC_RAX); + o(0xc78948); // mov %rax,%rdi ## first arg in %rdi, this must be ptr + vtop--; + + /* do a fast function call */ + gen_static_call(TOK___bound_ptr_add); + + /* returned pointer is in rax */ + vtop++; + vtop->r = TREG_RAX | VT_BOUNDED; + + + /* relocation offset of the bounding function call point */ + vtop->c.ull = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela))); +} + +/* patch pointer addition in vtop so that pointer dereferencing is + also tested */ +ST_FUNC void gen_bounded_ptr_deref(void) +{ + addr_t func; + int size, align; + ElfW(Rela) *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: + tcc_error("unhandled size when dereferencing bounded pointer"); + func = 0; + break; + } + + sym = external_global_sym(func, &func_old_type, 0); + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + + /* patch relocation */ + /* XXX: find a better solution ? */ + + rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.ull); + rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info)); +} +#endif + +#ifdef TCC_TARGET_PE + +#define REGN 4 +static const uint8_t arg_regs[REGN] = { + TREG_RCX, TREG_RDX, TREG_R8, TREG_R9 +}; + +/* Prepare arguments in R10 and R11 rather than RCX and RDX + because gv() will not ever use these */ +static int arg_prepare_reg(int idx) { + if (idx == 0 || idx == 1) + /* idx=0: r10, idx=1: r11 */ + return idx + 10; + else + return arg_regs[idx]; +} + +static int func_scratch; + +/* 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 gen_offs_sp(int b, int r, int d) +{ + orex(1,0,r & 0x100 ? 0 : r, b); + if (d == (char)d) { + o(0x2444 | (REG_VALUE(r) << 3)); + g(d); + } else { + o(0x2484 | (REG_VALUE(r) << 3)); + gen_le32(d); + } +} + +ST_FUNC int regargs_nregs(RegArgs *args) +{ + return *args; +} + +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) +{ + int size, align; + *regsize = 8; + *ret_align = 1; // Never have to re-align return values for x86-64 + size = type_size(vt, &align); + ret->ref = NULL; + if (size > 8) { + *args = 0; + } else if (size > 4) { + ret->t = VT_LLONG; + *args = 1; + } else if (size > 2) { + ret->t = VT_INT; + *args = 1; + } else if (size > 1) { + ret->t = VT_SHORT; + *args = 1; + } else { + ret->t = VT_BYTE; + *args = 1; + } + + return *args != 0; +} + +static int is_sse_float(int t) { + int bt; + bt = t & VT_BTYPE; + return bt == VT_DOUBLE || bt == VT_FLOAT; +} + +int gfunc_arg_size(CType *type) { + int align; + if (type->t & (VT_ARRAY|VT_BITFIELD)) + return 8; + return type_size(type, &align); +} + +void gfunc_call(int nb_args) +{ + int size, r, args_size, i, d, bt, struct_size; + int arg; + + args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE; + arg = nb_args; + + /* for struct arguments, we need to call memcpy and the function + call breaks register passing arguments we are preparing. + So, we process arguments which will be passed by stack first. */ + struct_size = args_size; + for(i = 0; i < nb_args; i++) { + SValue *sv; + + --arg; + sv = &vtop[-i]; + bt = (sv->type.t & VT_BTYPE); + size = gfunc_arg_size(&sv->type); + + if (size <= 8) + continue; /* arguments smaller than 8 bytes passed in registers or on stack */ + + if (bt == VT_STRUCT) { + /* align to stack align size */ + size = (size + 15) & ~15; + /* generate structure store */ + r = get_reg(RC_INT); + gen_offs_sp(0x8d, r, struct_size); + struct_size += size; + + /* generate memcpy call */ + vset(&sv->type, r | VT_LVAL, 0); + vpushv(sv); + vstore(); + --vtop; + } else if (bt == VT_LDOUBLE) { + gv(RC_ST0); + gen_offs_sp(0xdb, 0x107, struct_size); + struct_size += 16; + } + } + + if (func_scratch < struct_size) + func_scratch = struct_size; + + arg = nb_args; + struct_size = args_size; + + for(i = 0; i < nb_args; i++) { + --arg; + bt = (vtop->type.t & VT_BTYPE); + + size = gfunc_arg_size(&vtop->type); + if (size > 8) { + /* align to stack align size */ + size = (size + 15) & ~15; + if (arg >= REGN) { + d = get_reg(RC_INT); + gen_offs_sp(0x8d, d, struct_size); + gen_offs_sp(0x89, d, arg*8); + } else { + d = arg_prepare_reg(arg); + gen_offs_sp(0x8d, d, struct_size); + } + struct_size += size; + } else { + if (is_sse_float(vtop->type.t)) { + gv(RC_XMM0); /* only use one float register */ + if (arg >= REGN) { + /* movq %xmm0, j*8(%rsp) */ + gen_offs_sp(0xd60f66, 0x100, arg*8); + } else { + /* movaps %xmm0, %xmmN */ + o(0x280f); + o(0xc0 + (arg << 3)); + d = arg_prepare_reg(arg); + /* mov %xmm0, %rxx */ + o(0x66); + orex(1,d,0, 0x7e0f); + o(0xc0 + REG_VALUE(d)); + } + } else { + if (bt == VT_STRUCT) { + vtop->type.ref = NULL; + vtop->type.t = size > 4 ? VT_LLONG : size > 2 ? VT_INT + : size > 1 ? VT_SHORT : VT_BYTE; + } + + r = gv(RC_INT); + if (arg >= REGN) { + gen_offs_sp(0x89, r, arg*8); + } else { + d = arg_prepare_reg(arg); + orex(1,d,r,0x89); /* mov */ + o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); + } + } + } + vtop--; + } + save_regs(0); + + /* Copy R10 and R11 into RCX and RDX, respectively */ + if (nb_args > 0) { + o(0xd1894c); /* mov %r10, %rcx */ + if (nb_args > 1) { + o(0xda894c); /* mov %r11, %rdx */ + } + } + + gcall_or_jmp(0); + vtop--; +} + + +#define FUNC_PROLOG_SIZE 11 + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType *func_type) +{ + int addr, reg_param_index, bt, size; + Sym *sym; + CType *type; + + func_ret_sub = 0; + func_scratch = 0; + loc = 0; + + addr = PTR_SIZE * 2; + ind += FUNC_PROLOG_SIZE; + func_sub_sp_offset = ind; + reg_param_index = 0; + + sym = func_type->ref; + + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->type; + func_var = (sym->c == FUNC_ELLIPSIS); + size = gfunc_arg_size(&func_vt); + if (size > 8) { + gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); + func_vc = addr; + reg_param_index++; + addr += 8; + } + + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + bt = type->t & VT_BTYPE; + size = gfunc_arg_size(type); + if (size > 8) { + if (reg_param_index < REGN) { + gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); + } + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL | VT_REF, addr); + } else { + if (reg_param_index < REGN) { + /* save arguments passed by register */ + if ((bt == VT_FLOAT) || (bt == VT_DOUBLE)) { + o(0xd60f66); /* movq */ + gen_modrm(reg_param_index, VT_LOCAL, NULL, addr); + } else { + gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); + } + } + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); + } + addr += 8; + reg_param_index++; + } + + while (reg_param_index < REGN) { + if (func_type->ref->c == FUNC_ELLIPSIS) { + gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); + addr += 8; + } + reg_param_index++; + } +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + int v, saved_ind; + + 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); + } + + saved_ind = ind; + ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; + /* align local size to word & save local variables */ + v = (func_scratch + -loc + 15) & -16; + + 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_X86_64_PC32); + o(0x90); /* fill for FUNC_PROLOG_SIZE = 11 bytes */ + } else { + o(0xe5894855); /* push %rbp, mov %rsp, %rbp */ + o(0xec8148); /* sub rsp, stacksize */ + gen_le32(v); + } + + cur_text_section->data_offset = saved_ind; + pe_add_unwind_data(ind, saved_ind, v); + ind = cur_text_section->data_offset; +} + +#else + +static void gadd_sp(int val) +{ + if (val == (char)val) { + o(0xc48348); + g(val); + } else { + oad(0xc48148, val); /* add $xxx, %rsp */ + } +} + +typedef enum X86_64_Mode { + x86_64_mode_none, + x86_64_mode_memory, + x86_64_mode_integer, + x86_64_mode_sse, + x86_64_mode_x87 +} X86_64_Mode; + +static X86_64_Mode classify_x86_64_merge(X86_64_Mode a, X86_64_Mode b) +{ + if (a == b) + return a; + else if (a == x86_64_mode_none) + return b; + else if (b == x86_64_mode_none) + return a; + else if ((a == x86_64_mode_memory) || (b == x86_64_mode_memory)) + return x86_64_mode_memory; + else if ((a == x86_64_mode_integer) || (b == x86_64_mode_integer)) + return x86_64_mode_integer; + else if ((a == x86_64_mode_x87) || (b == x86_64_mode_x87)) + return x86_64_mode_memory; + else + return x86_64_mode_sse; +} + +/* classify the x86 eightbytes from byte index start to byte index + * end, at offset offset from the root struct */ +static X86_64_Mode classify_x86_64_inner(CType *ty, int offset, int start, int end) +{ + X86_64_Mode mode; + Sym *f; + + switch (ty->t & VT_BTYPE) { + case VT_VOID: return x86_64_mode_none; + + case VT_INT: + case VT_BYTE: + case VT_SHORT: + case VT_LLONG: + case VT_BOOL: + case VT_PTR: + case VT_FUNC: + case VT_ENUM: return x86_64_mode_integer; + + case VT_FLOAT: + case VT_DOUBLE: return x86_64_mode_sse; + + case VT_LDOUBLE: return x86_64_mode_x87; + + case VT_STRUCT: + f = ty->ref; + + mode = x86_64_mode_none; + while ((f = f->next) != NULL) { + if (f->c + offset >= start && f->c + offset < end) + mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type, f->c + offset, start, end)); + } + + return mode; + } + + assert(0); +} + +static X86_64_Mode classify_x86_64_arg_eightbyte(CType *ty, int offset) +{ + X86_64_Mode mode; + + assert((ty->t & VT_BTYPE) == VT_STRUCT); + + mode = classify_x86_64_inner(ty, 0, offset, offset + 8); + + return mode; +} + +static void regargs_init(RegArgs *args) +{ + int i; + for(i=0; i<REG_ARGS_MAX; i++) { + args->ireg[i] = -1; + args->freg[i] = -1; + } +} + +static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, RegArgs *args) +{ + X86_64_Mode mode = x86_64_mode_none; + int size, align, ret_t = 0; + int ireg = 0, freg = 0; + + if (args) + regargs_init(args); + + if (ty->t & (VT_BITFIELD|VT_ARRAY)) { + *psize = 8; + *palign = 8; + if (args) + args->ireg[ireg++] = 0; + ret_t = ty->t; + mode = x86_64_mode_integer; + } else { + size = type_size(ty, &align); + *psize = (size + 7) & ~7; + *palign = (align + 7) & ~7; + + if (size > 16) { + mode = x86_64_mode_memory; + } else { + int start; + + for(start=0; start < size; start += 8) { + if ((ty->t & VT_BTYPE) == VT_STRUCT) { + mode = classify_x86_64_arg_eightbyte(ty, start); + } else { + mode = classify_x86_64_inner(ty, 0, 0, size); + } + + if (mode == x86_64_mode_integer) { + if (args) + args->ireg[ireg++] = start; + ret_t = (size > 4) ? VT_LLONG : VT_INT; + } else if (mode == x86_64_mode_sse) { + if (args) + args->freg[freg++] = start; + ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT; + } else { + ret_t = VT_LDOUBLE; + } + } + } + } + + if (ret) { + ret->ref = NULL; + ret->t = ret_t; + } + + return mode; +} + +ST_FUNC int classify_x86_64_va_arg(CType *ty) +{ + /* This definition must be synced with stdarg.h */ + enum __va_arg_type { + __va_gen_reg, __va_float_reg, __va_stack + }; + int size, align; + X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, NULL); + switch (mode) { + default: return __va_stack; + case x86_64_mode_integer: return __va_gen_reg; + case x86_64_mode_sse: return __va_float_reg; + } +} + +static int regargs_iregs(RegArgs *args) +{ + int i; + int ret = 0; + for(i=0; i<REG_ARGS_MAX; i++) { + if(args->ireg[i] != -1) + ret++; + } + + return ret; +} + +static int regargs_fregs(RegArgs *args) +{ + int i; + int ret = 0; + for(i=0; i<REG_ARGS_MAX; i++) { + if(args->freg[i] != -1) + ret++; + } + + return ret; +} + +/* Count the total number of registers used by args */ +ST_FUNC int regargs_nregs(RegArgs *args) +{ + int i; + int ret = 0; + for(i=0; i<REG_ARGS_MAX; i++) { + if(args->ireg[i] != -1) + ret++; + + if(args->freg[i] != -1) + ret++; + } + + return ret; +} + +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) +{ + int size, align; + X86_64_Mode mode; + *ret_align = 1; // Never have to re-align return values for x86-64 + *regsize = 8; + + mode = classify_x86_64_arg(vt, ret, &size, &align, args); + + return mode != x86_64_mode_memory && + mode != x86_64_mode_none; +} + +#define REGN 6 +static const uint8_t arg_regs[REGN] = { + TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9 +}; + +static int arg_prepare_reg(int idx) { + if (idx == 2 || idx == 3) + /* idx=2: r10, idx=3: r11 */ + return idx + 8; + else + return arg_regs[idx]; +} + +/* 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) +{ + X86_64_Mode mode; + CType type; + int size, align, r, args_size, stack_adjust, run_start, run_end, i; + int nb_reg_args = 0; + int nb_sse_args = 0; + int sse_reg = 0, gen_reg = 0; + RegArgs *reg_args = alloca(nb_args * sizeof *reg_args); + + /* calculate the number of integer/float register arguments */ + for(i = nb_args - 1; i >= 0; i--) { + int fregs, iregs; + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_args[i]); + fregs = regargs_fregs(®_args[i]); + iregs = regargs_iregs(®_args[i]); + + nb_sse_args += fregs; + nb_reg_args += iregs; + + if (sse_reg + fregs > 8 || gen_reg + iregs > REGN) { + regargs_init(®_args[i]); + } else { + sse_reg += fregs; + gen_reg += iregs; + } + } + + /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments + and ended by a 16-byte aligned argument. This is because, from the point of view of + the callee, argument alignment is computed from the bottom up. */ + /* for struct arguments, we need to call memcpy and the function + call breaks register passing arguments we are preparing. + So, we process arguments which will be passed by stack first. */ + gen_reg = nb_reg_args; + sse_reg = nb_sse_args; + run_start = 0; + args_size = 0; + while (run_start != nb_args) { + int run_gen_reg = gen_reg, run_sse_reg = sse_reg; + + run_end = nb_args; + stack_adjust = 0; + for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) { + int stack = regargs_nregs(®_args[i]) == 0; + classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); + + if (stack) { + if (align == 16) + run_end = i; + else + stack_adjust += size; + } + } + + gen_reg = run_gen_reg; + sse_reg = run_sse_reg; + + /* adjust stack to align SSE boundary */ + if (stack_adjust &= 15) { + /* fetch cpu flag before the following sub will change the value */ + if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP) + gv(RC_INT); + + stack_adjust = 16 - stack_adjust; + o(0x48); + oad(0xec81, stack_adjust); /* sub $xxx, %rsp */ + args_size += stack_adjust; + } + + for(i = run_start; i < run_end;) { + int arg_stored = regargs_nregs(®_args[i]) == 0; + SValue tmp; + RegArgs args; + + if (!arg_stored) { + ++i; + continue; + } + + /* Swap argument to top, it will possibly be changed here, + and might use more temps. At the end of the loop we keep + in on the stack and swap it back to its original position + if it is a register. */ + tmp = vtop[0]; + vtop[0] = vtop[-i]; + vtop[-i] = tmp; + + classify_x86_64_arg(&vtop->type, NULL, &size, &align, &args); + + switch (vtop->type.t & VT_BTYPE) { + case VT_STRUCT: + /* allocate the necessary size on stack */ + o(0x48); + oad(0xec81, size); /* sub $xxx, %rsp */ + /* generate structure store */ + r = get_reg(RC_INT); + orex(1, r, 0, 0x89); /* mov %rsp, r */ + o(0xe0 + REG_VALUE(r)); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + args_size += size; + break; + + case VT_LDOUBLE: + assert(0); + break; + + case VT_FLOAT: + case VT_DOUBLE: + r = gv(RC_FLOAT); + o(0x50); /* push $rax */ + /* movq %xmmN, (%rsp) */ + o(0xd60f66); + o(0x04 + REG_VALUE(r)*8); + o(0x24); + args_size += size; + break; + + default: + /* simple type */ + /* XXX: implicit cast ? */ + --gen_reg; + r = gv(RC_INT); + orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ + args_size += size; + break; + } + + /* And swap the argument back to its original position. */ + tmp = vtop[0]; + vtop[0] = vtop[-i]; + vtop[-i] = tmp; + + vrotb(i+1); + assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); + vpop(); + memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); + --nb_args; + --run_end; + } + + /* handle 16 byte aligned arguments at end of run */ + run_start = i = run_end; + while (i < nb_args) { + /* Rotate argument to top since it will always be popped */ + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL); + if (align != 16) + break; + + vrotb(i+1); + + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + gv(RC_ST0); + oad(0xec8148, size); /* sub $xxx, %rsp */ + o(0x7cdb); /* fstpt 0(%rsp) */ + g(0x24); + g(0x00); + args_size += size; + } else { + assert(mode == x86_64_mode_memory); + + /* allocate the necessary size on stack */ + o(0x48); + oad(0xec81, size); /* sub $xxx, %rsp */ + /* generate structure store */ + r = get_reg(RC_INT); + orex(1, r, 0, 0x89); /* mov %rsp, r */ + o(0xe0 + REG_VALUE(r)); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + args_size += size; + } + + vpop(); + memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args); + --nb_args; + } + } + + /* XXX This should be superfluous. */ + save_regs(0); /* save used temporary registers */ + + /* recalculate the number of register arguments there actually + * are. This is slow but more obviously correct than using the + * old counts. */ + gen_reg = 0; + sse_reg = 0; + for(i = 0; i < nb_args; i++) { + gen_reg += regargs_iregs(®_args[i]); + sse_reg += regargs_fregs(®_args[i]); + } + + /* then, we prepare register passing arguments. + Note that we cannot set RDX and RCX in this loop because gv() + may break these temporary registers. Let's use R10 and R11 + instead of them */ + assert(gen_reg <= REGN); + assert(sse_reg <= 8); + for(i = 0; i < nb_args; i++) { + RegArgs args; + + args = reg_args[i]; + + /* Alter stack entry type so that gv() knows how to treat it */ + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + int k; + + for(k=REG_ARGS_MAX-1; k>=0; k--) { + if (args.freg[k] == -1) + continue; + + sse_reg--; + assert(sse_reg >= 0); + + vdup(); + vtop->type.t = VT_DOUBLE; + vtop->c.ull += args.freg[k]; + gv(RC_XMM0 << sse_reg); + vpop(); + } + for(k=REG_ARGS_MAX-1; k>=0; k--) { + int d; + if (args.ireg[k] == -1) + continue; + + gen_reg--; + + vdup(); + vtop->type.t = VT_LLONG; + vtop->c.ull += args.ireg[k]; + r = gv(RC_INT); + d = arg_prepare_reg(gen_reg); + orex(1,d,r,0x89); /* mov */ + o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); + vpop(); + } + } else { + /* XXX is it really necessary to set vtop->type? */ + classify_x86_64_arg(&vtop->type, &type, &size, &align, NULL); + vtop->type = type; + if (args.freg[0] != -1) { + --sse_reg; + /* Load directly to register */ + gv(RC_XMM0 << sse_reg); + } else if (args.ireg[0] != -1) { + int d; + /* simple type */ + /* XXX: implicit cast ? */ + gen_reg--; + r = gv(RC_INT); + d = arg_prepare_reg(gen_reg); + orex(1,d,r,0x89); /* mov */ + o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); + } else { + assert(0); + } + } + vtop--; + } + assert(gen_reg == 0); + assert(sse_reg == 0); + + /* We shouldn't have many operands on the stack anymore, but the + call address itself is still there, and it might be in %eax + (or edx/ecx) currently, which the below writes would clobber. + So evict all remaining operands here. */ + save_regs(0); + + /* Copy R10 and R11 into RDX and RCX, respectively */ + if (nb_reg_args > 2) { + o(0xd2894c); /* mov %r10, %rdx */ + if (nb_reg_args > 3) { + o(0xd9894c); /* mov %r11, %rcx */ + } + } + + oad(0xb8, nb_sse_args < 8 ? nb_sse_args : 8); /* mov nb_sse_args, %eax */ + gcall_or_jmp(0); + if (args_size) + gadd_sp(args_size); + vtop--; +} + + +#define FUNC_PROLOG_SIZE 11 + +static void push_arg_reg(int i) { + loc -= 8; + gen_modrm64(0x89, arg_regs[i], VT_LOCAL, NULL, loc); +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType *func_type) +{ + X86_64_Mode mode; + int i, addr, align, size; + int param_addr = 0, reg_param_index, sse_param_index; + Sym *sym; + CType *type; + + sym = func_type->ref; + addr = PTR_SIZE * 2; + loc = 0; + ind += FUNC_PROLOG_SIZE; + func_sub_sp_offset = ind; + func_ret_sub = 0; + + if (func_type->ref->c == FUNC_ELLIPSIS) { + int seen_reg_num, seen_sse_num, seen_stack_size; + seen_reg_num = seen_sse_num = 0; + /* frame pointer and return address */ + seen_stack_size = PTR_SIZE * 2; + /* count the number of seen parameters */ + sym = func_type->ref; + while ((sym = sym->next) != NULL) { + RegArgs args; + + type = &sym->type; + mode = classify_x86_64_arg(type, NULL, &size, &align, &args); + + switch (mode) { + default: + stack_arg: + seen_stack_size = ((seen_stack_size + align - 1) & -align) + size; + break; + + case x86_64_mode_integer: + case x86_64_mode_sse: { + int stack = 0; + + seen_sse_num += regargs_fregs(&args); + seen_reg_num += regargs_iregs(&args); + + if (seen_reg_num > 8) { + seen_reg_num = 8; + stack = 1; + } + if (seen_sse_num > 8) { + seen_sse_num = 8; + stack = 1; + } + + if (stack) + goto stack_arg; + break; + } + } + } + + loc -= 16; + /* movl $0x????????, -0x10(%rbp) */ + o(0xf045c7); + gen_le32(seen_reg_num * 8); + /* movl $0x????????, -0xc(%rbp) */ + o(0xf445c7); + gen_le32(seen_sse_num * 16 + 48); + /* movl $0x????????, -0x8(%rbp) */ + o(0xf845c7); + gen_le32(seen_stack_size); + + /* save all register passing arguments */ + for (i = 0; i < 8; i++) { + loc -= 16; + o(0xd60f66); /* movq */ + gen_modrm(7 - i, VT_LOCAL, NULL, loc); + /* movq $0, loc+8(%rbp) */ + o(0x85c748); + gen_le32(loc + 8); + gen_le32(0); + } + for (i = 0; i < REGN; i++) { + push_arg_reg(REGN-1-i); + } + } + + sym = func_type->ref; + reg_param_index = 0; + sse_param_index = 0; + + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->type; + mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, NULL); + if (mode == x86_64_mode_memory) { + push_arg_reg(reg_param_index); + func_vc = loc; + reg_param_index++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + RegArgs args; + int reg_count_integer = 0; + int reg_count_sse = 0; + int arg_stored = 1; + + type = &sym->type; + mode = classify_x86_64_arg(type, NULL, &size, &align, &args); + reg_count_integer = regargs_iregs(&args); + reg_count_sse = regargs_fregs(&args); + + switch (mode) { + case x86_64_mode_integer: + case x86_64_mode_sse: + if (reg_count_integer || reg_count_sse) { + if ((reg_count_sse == 0 || sse_param_index + reg_count_sse <= 8) && + (reg_count_integer == 0 || reg_param_index + reg_count_integer <= REGN)) { + /* argument fits into registers */ + arg_stored = 0; + } + } + + if (!arg_stored) { + /* save arguments passed by register */ + loc -= (reg_count_sse + reg_count_integer) * 8; + param_addr = loc; + for (i = 0; i < reg_count_sse; ++i) { + o(0xd60f66); /* movq */ + gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + args.freg[i]); + ++sse_param_index; + } + for (i = 0; i < reg_count_integer; ++i) { + gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + args.ireg[i]); + ++reg_param_index; + } + } else { + addr = (addr + align - 1) & -align; + param_addr = addr; + addr += size; + } + break; + + case x86_64_mode_memory: + case x86_64_mode_x87: + addr = (addr + align - 1) & -align; + param_addr = addr; + addr += size; + break; + default: break; /* nothing to be done for x86_64_mode_none */ + } + sym_push(sym->v & ~SYM_FIELD, type, + VT_LOCAL | VT_LVAL, param_addr); + } + +#ifdef CONFIG_TCC_BCHECK + /* leave some room for bound checking code */ + if (tcc_state->do_bounds_check) { + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + oad(0xb8, 0); /* lbound section pointer */ + o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */ + oad(0xb8, 0); /* call to function */ + } +#endif +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + int v, saved_ind; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check + && func_bound_offset != lbounds_section->data_offset) + { + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + /* generate bound local allocation */ + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, lbounds_section->data_offset); + saved_ind = ind; + ind = func_bound_ind; + greloc(cur_text_section, sym_data, ind + 1, R_386_32); + ind = ind + 5 + 3; + gen_static_call(TOK___bound_local_new); + 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 xxx, %rax */ + o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */ + gen_static_call(TOK___bound_local_delete); + 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 + 15) & -16; + saved_ind = ind; + ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; + o(0xe5894855); /* push %rbp, mov %rsp, %rbp */ + o(0xec8148); /* sub rsp, stacksize */ + gen_le32(v); + ind = saved_ind; +} + +#endif /* not PE */ + +/* generate a jump to a label */ +int 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 */ + if (vtop->c.i & 0x100) + { + /* This was a float compare. If the parity flag is set + the result was unordered. For anything except != this + means false and we don't jump (anding both conditions). + For != this means true (oring both). + Take care about inverting the test. We need to jump + to our target if the result was unordered and test wasn't NE, + otherwise if unordered we don't want to jump. */ + vtop->c.i &= ~0x100; + if (!inv == (vtop->c.i != TOK_NE)) + o(0x067a); /* jp +6 */ + else + { + g(0x0f); + t = psym(0x8a, t); /* jp t */ + } + } + 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); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int r, fr, opc, c; + int ll, uu, cc; + + ll = is64_type(vtop[-1].type.t); + uu = (vtop[-1].type.t & VT_UNSIGNED) != 0; + cc = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + + switch(op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + if (cc && (!ll || (int)vtop->c.ll == vtop->c.ll)) { + /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); + c = vtop->c.i; + if (c == (char)c) { + /* XXX: generate inc and dec for smaller code ? */ + orex(ll, r, 0, 0x83); + o(0xc0 | (opc << 3) | REG_VALUE(r)); + g(c); + } else { + orex(ll, r, 0, 0x81); + oad(0xc0 | (opc << 3) | REG_VALUE(r), c); + } + } else { + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + orex(ll, r, fr, (opc << 3) | 0x01); + o(0xc0 + REG_VALUE(r) + REG_VALUE(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; + orex(ll, fr, r, 0xaf0f); /* imul fr, r */ + o(0xc0 + REG_VALUE(fr) + REG_VALUE(r) * 8); + vtop--; + 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 (cc) { + /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); + orex(ll, r, 0, 0xc1); /* shl/shr/sar $xxx, r */ + o(opc | REG_VALUE(r)); + g(vtop->c.i & (ll ? 63 : 31)); + } else { + /* we generate the shift in ecx */ + gv2(RC_INT, RC_RCX); + r = vtop[-1].r; + orex(ll, r, 0, 0xd3); /* shl/shr/sar %cl, r */ + o(opc | REG_VALUE(r)); + } + vtop--; + break; + case TOK_UDIV: + case TOK_UMOD: + uu = 1; + goto divmod; + case '/': + case '%': + case TOK_PDIV: + uu = 0; + divmod: + /* first operand must be in eax */ + /* XXX: need better constraint for second operand */ + gv2(RC_RAX, RC_RCX); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + save_reg(TREG_RDX); + orex(ll, 0, 0, uu ? 0xd231 : 0x99); /* xor %edx,%edx : cqto */ + orex(ll, fr, 0, 0xf7); /* div fr, %eax */ + o((uu ? 0xf0 : 0xf8) + REG_VALUE(fr)); + if (op == '%' || op == TOK_UMOD) + r = TREG_RDX; + else + r = TREG_RAX; + vtop->r = r; + break; + default: + opc = 7; + goto gen_op8; + } +} + +void gen_opl(int op) +{ + gen_opi(op); +} + +/* 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; + int float_type = + (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT; + + /* convert constants to memory references */ + if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + vswap(); + gv(float_type); + vswap(); + } + if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) + gv(float_type); + + /* must put at least one value in the floating point register */ + if ((vtop[-1].r & VT_LVAL) && + (vtop[0].r & VT_LVAL)) { + vswap(); + gv(float_type); + 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 ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + if (op >= TOK_ULT && op <= TOK_GT) { + /* load on stack second operand */ + load(TREG_ST0, vtop); + save_reg(TREG_RAX); /* 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) */ + if (op == TOK_EQ || op == TOK_NE) + o(0xe9da); /* fucompp */ + else + o(0xd9de); /* fcompp */ + 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 */ + 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; + o(0xde); /* fxxxp %st, %st(1) */ + o(0xc1 + (a << 3)); + vtop--; + } + } else { + if (op >= TOK_ULT && op <= TOK_GT) { + /* if saved lvalue, then we must reload it */ + r = vtop->r; + fc = vtop->c.ul; + if ((r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + + if (op == TOK_EQ || op == TOK_NE) { + swapped = 0; + } else { + if (op == TOK_LE || op == TOK_LT) + swapped = !swapped; + if (op == TOK_LE || op == TOK_GE) { + op = 0x93; /* setae */ + } else { + op = 0x97; /* seta */ + } + } + + if (swapped) { + gv(RC_FLOAT); + vswap(); + } + assert(!(vtop[-1].r & VT_LVAL)); + + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + o(0x66); + if (op == TOK_EQ || op == TOK_NE) + o(0x2e0f); /* ucomisd */ + else + o(0x2f0f); /* comisd */ + + if (vtop->r & VT_LVAL) { + gen_modrm(vtop[-1].r, r, vtop->sym, fc); + } else { + o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8); + } + + vtop--; + vtop->r = VT_CMP; + vtop->c.i = op | 0x100; + } else { + assert((vtop->type.t & VT_BTYPE) != VT_LDOUBLE); + switch(op) { + default: + case '+': + a = 0; + break; + case '-': + a = 4; + break; + case '*': + a = 1; + break; + case '/': + a = 6; + break; + } + ft = vtop->type.t; + fc = vtop->c.ul; + assert((ft & VT_BTYPE) != VT_LDOUBLE); + + r = vtop->r; + /* if saved lvalue, then we must reload it */ + if ((vtop->r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + + assert(!(vtop[-1].r & VT_LVAL)); + if (swapped) { + assert(vtop->r & VT_LVAL); + gv(RC_FLOAT); + vswap(); + } + + if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0xf2); + } else { + o(0xf3); + } + o(0x0f); + o(0x58 + a); + + if (vtop->r & VT_LVAL) { + gen_modrm(vtop[-1].r, r, vtop->sym, fc); + } else { + o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8); + } + + vtop--; + } + } +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + if ((t & VT_BTYPE) == VT_LDOUBLE) { + 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->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%rsp) */ + o(0x08c48348); /* add $8, %rsp */ + } 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 (%rsp) */ + o(0x10c48348); /* add $16, %rsp */ + } else { + /* int to float/double/long double */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x2404db); /* fildl (%rsp) */ + o(0x08c48348); /* add $8, %rsp */ + } + vtop->r = TREG_ST0; + } else { + int r = get_reg(RC_FLOAT); + gv(RC_INT); + o(0xf2 + ((t & VT_BTYPE) == VT_FLOAT?1:0)); + if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED) || + (vtop->type.t & VT_BTYPE) == VT_LLONG) { + o(0x48); /* REX */ + } + o(0x2a0f); + o(0xc0 + (vtop->r & VT_VALMASK) + REG_VALUE(r)*8); /* cvtsi2sd */ + vtop->r = r; + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + int ft, bt, tbt; + + ft = vtop->type.t; + bt = ft & VT_BTYPE; + tbt = t & VT_BTYPE; + + if (bt == VT_FLOAT) { + gv(RC_FLOAT); + if (tbt == VT_DOUBLE) { + o(0x140f); /* unpcklps */ + o(0xc0 + REG_VALUE(vtop->r)*9); + o(0x5a0f); /* cvtps2pd */ + o(0xc0 + REG_VALUE(vtop->r)*9); + } else if (tbt == VT_LDOUBLE) { + save_reg(RC_ST0); + /* movss %xmm0,-0x10(%rsp) */ + o(0x110ff3); + o(0x44 + REG_VALUE(vtop->r)*8); + o(0xf024); + o(0xf02444d9); /* flds -0x10(%rsp) */ + vtop->r = TREG_ST0; + } + } else if (bt == VT_DOUBLE) { + gv(RC_FLOAT); + if (tbt == VT_FLOAT) { + o(0x140f66); /* unpcklpd */ + o(0xc0 + REG_VALUE(vtop->r)*9); + o(0x5a0f66); /* cvtpd2ps */ + o(0xc0 + REG_VALUE(vtop->r)*9); + } else if (tbt == VT_LDOUBLE) { + save_reg(RC_ST0); + /* movsd %xmm0,-0x10(%rsp) */ + o(0x110ff2); + o(0x44 + REG_VALUE(vtop->r)*8); + o(0xf024); + o(0xf02444dd); /* fldl -0x10(%rsp) */ + vtop->r = TREG_ST0; + } + } else { + int r; + gv(RC_ST0); + r = get_reg(RC_FLOAT); + if (tbt == VT_DOUBLE) { + o(0xf0245cdd); /* fstpl -0x10(%rsp) */ + /* movsd -0x10(%rsp),%xmm0 */ + o(0x100ff2); + o(0x44 + REG_VALUE(r)*8); + o(0xf024); + vtop->r = r; + } else if (tbt == VT_FLOAT) { + o(0xf0245cd9); /* fstps -0x10(%rsp) */ + /* movss -0x10(%rsp),%xmm0 */ + o(0x100ff3); + o(0x44 + REG_VALUE(r)*8); + o(0xf024); + vtop->r = r; + } + } +} + +/* convert fp to int 't' type */ +void gen_cvt_ftoi(int t) +{ + int ft, bt, size, r; + ft = vtop->type.t; + bt = ft & VT_BTYPE; + if (bt == VT_LDOUBLE) { + gen_cvt_ftof(VT_DOUBLE); + bt = VT_DOUBLE; + } + + gv(RC_FLOAT); + if (t != VT_INT) + size = 8; + else + size = 4; + + r = get_reg(RC_INT); + if (bt == VT_FLOAT) { + o(0xf3); + } else if (bt == VT_DOUBLE) { + o(0xf2); + } else { + assert(0); + } + orex(size == 8, r, 0, 0x2c0f); /* cvttss2si or cvttsd2si */ + o(0xc0 + REG_VALUE(vtop->r) + REG_VALUE(r)*8); + vtop->r = r; +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* Save the stack pointer onto the stack and return the location of its address */ +ST_FUNC void gen_vla_sp_save(int addr) { + /* mov %rsp,addr(%rbp)*/ + gen_modrm64(0x89, TREG_RSP, VT_LOCAL, NULL, addr); +} + +/* Restore the SP from a location on the stack */ +ST_FUNC void gen_vla_sp_restore(int addr) { + gen_modrm64(0x8b, TREG_RSP, VT_LOCAL, NULL, addr); +} + +/* Subtract from the stack pointer, and push the resulting value onto the stack */ +ST_FUNC void gen_vla_alloc(CType *type, int align) { +#ifdef TCC_TARGET_PE + /* alloca does more than just adjust %rsp on Windows */ + vpush_global_sym(&func_old_type, TOK_alloca); + vswap(); /* Move alloca ref past allocation size */ + gfunc_call(1); + vset(type, REG_IRET, 0); +#else + int r = gv(RC_INT); /* allocation size */ + /* sub r,%rsp */ + o(0x2b48); + o(0xe0 | REG_VALUE(r)); + /* We align to 16 bytes rather than align */ + /* and ~15, %rsp */ + o(0xf0e48348); + vpop(); +#endif +} + + +/* end of x86-64 code generator */ +/*************************************************************/ +#endif /* ! TARGET_DEFS_ONLY */ +/******************************************************/ |
