/programs/develop/metcc/trunk/source/varargs.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/stdbool.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/il-gen.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/i386-asm.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/libtcc1.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/arm-gen.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/stdarg.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/start.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/source/stab.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/tccpe.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/libtcc_test.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/console.asm |
---|
File deleted |
/programs/develop/metcc/trunk/source/il-opcodes.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/VERSION |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/source/tcclib.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/c67-gen.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tcctest.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tccasm.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/float.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/config.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/elf.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/stddef.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/bcheck.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/coff.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/COPYING |
---|
File deleted |
/programs/develop/metcc/trunk/source/tcctok.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/Makefile |
---|
File deleted |
/programs/develop/metcc/trunk/source/tccmeos.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tcc-doc.html |
---|
File deleted |
/programs/develop/metcc/trunk/source/i386-gen.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tccelf.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/stab.def |
---|
File deleted |
/programs/develop/metcc/trunk/source/libtcc.h |
---|
File deleted |
/programs/develop/metcc/trunk/source/boundtest.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tcccoff.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tiny_impdef.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/i386-asm.c |
---|
File deleted |
/programs/develop/metcc/trunk/source/tcc.c |
---|
File deleted |
/programs/develop/metcc/trunk/samples/console/console.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/samples/files/FILES.C |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/samples/simple/simple.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/include/stdio.h |
---|
File deleted |
/programs/develop/metcc/trunk/libc/include/ctype.h |
---|
File deleted |
/programs/develop/metcc/trunk/libc/include/string.h |
---|
File deleted |
/programs/develop/metcc/trunk/libc/include/mesys.h |
---|
File deleted |
/programs/develop/metcc/trunk/libc/include/stdlib.h |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/build.bat |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/strxfrm.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/strchr.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strlen.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strcmp.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/memchr.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/is.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strncat.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/memcmp.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strdup.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strerror.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strcoll.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strcpy.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/strnbrk.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strncmp.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/strtok.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strrchr.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/memset.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/strspn.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/tolower.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/atoi.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/itoa.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strcspn.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/itoab.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/toupper.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/atoib.cpp |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strcat.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strstr.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/string/strncpy.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string/memmove.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/string |
---|
Property changes: |
Deleted: svn:ignore |
-*.o |
-*.s |
/programs/develop/metcc/trunk/libc/public_stdcall.inc |
---|
File deleted |
/programs/develop/metcc/trunk/libc/start/debug-fdo.inc |
---|
File deleted |
/programs/develop/metcc/trunk/libc/start/start.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/start/macros.inc |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/start |
---|
Property changes: |
Deleted: svn:ignore |
-*.o |
-*.s |
/programs/develop/metcc/trunk/libc/makefile |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mem/memalloc.asm |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mem |
---|
Property changes: |
Deleted: svn:ignore |
-*.o |
-*.s |
/programs/develop/metcc/trunk/libc/file/fseek.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/fscanf.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/file/fwrite.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/ftell.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/fopen.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/file/fgetpos.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/fprintf.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/file/fclose.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/feof.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/fputc.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/file/fread.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/fsetpos.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/ungetc.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/file/fflush.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/rewind.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file/fgetc.c |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/file |
---|
Property changes: |
Deleted: svn:ignore |
-*.o |
-*.s |
/programs/develop/metcc/trunk/libc/proc32.inc |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/keyboard.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/cofflib.asm |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/thread.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/_ksys_files_acces.asm |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/exit.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/pci.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/button.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/ipc.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/window_redraw.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/date.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/process.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/screen.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/draw_bar.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/event.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/pixel.asm |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/midi.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/draw_window.asm |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/line.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/sound.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/backgr.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/irq.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/clock.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/dga.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/debug_board.asm |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/delay.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/debug_board_.c |
---|
File deleted |
/programs/develop/metcc/trunk/libc/mesys/write_text.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys/draw_image.asm |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk/libc/mesys |
---|
Property changes: |
Deleted: svn:ignore |
-*.o |
-*.s |
/programs/develop/metcc/trunk/libc |
---|
Property changes: |
Deleted: svn:ignore |
-*.a |
-script.txt |
/programs/develop/metcc/trunk/readme.txt |
---|
File deleted |
\ No newline at end of file |
/programs/develop/metcc/trunk |
---|
Property changes: |
Deleted: svn:ignore |
-debug |
-release |
-*.msp |
-*.vcproj |
-*.sln |
-*.dsw |
-*.dsp |
/programs/develop/ktcc/trunk/libc/build.bat |
---|
0,0 → 1,68 |
@echo off |
echo #################################################### |
echo # Melibc builder # |
echo # usage: build [clean] # |
echo #################################################### |
rem #### CONFIG SECTION #### |
set LIBNAME=libck.a |
set INCLUDE=include |
set CC= |
set CFLAGS=-c -nostdinc -DGNUC -I"%cd%\%INCLUDE%" |
set AR= |
set ASM= |
set dirs=stdio memory kolibrisys string stdlib |
rem #### END OF CONFIG SECTION #### |
set objs= |
set target=%1 |
if not "%1"=="clean" set target=all |
set INCLUDE="%cd%" |
call :Target_%target% |
if ERRORLEVEL 0 goto Exit_OK |
echo Probably at runing has been created error |
echo For help send a report... |
pause |
goto :eof |
:Compile_C |
%CC% %CFLAGS% %1 -o "%~dpn1.o" |
if not %errorlevel%==0 goto Error_Failed |
set objs=%objs% "%~dpn1.o" |
goto :eof |
:Compile_Asm |
%ASM% %1 "%~dpn1.o" |
if not %errorlevel%==0 goto Error_Failed |
set objs=%objs% "%~dpn1.o" |
goto :eof |
:Target_clean |
echo cleaning ... |
for %%a in (%dirs%) do del /Q "%%a\*.o" |
goto :Exit_OK |
:Target_all |
echo building all ... |
for %%a in (%dirs%) do ( |
for %%f in ("%%a\*.asm") do call :Compile_Asm "%%f" |
for %%f in ("%%a\*.c") do call :Compile_C "%%f" |
) |
%AR% -ru %LIBNAME% %objs% |
if not %errorlevel%==0 goto Error_Failed |
goto Exit_OK |
:Error_Failed |
echo error: execution failed |
pause |
exit 1 |
:Exit_OK |
echo #################################################### |
echo # All operations has been done... # |
echo # For cleaning run this script with param " clean" # |
echo #################################################### |
pause |
exit 0 |
/programs/develop/ktcc/trunk/libc/include/ctype.h |
---|
0,0 → 1,35 |
/* |
** All character classification functions except isascii(). |
** Integer argument (c) must be in ASCII range (0-127) for |
** dependable answers. |
*/ |
#define ALNUM 1 |
#define ALPHA 2 |
#define CNTRL 4 |
#define DIGIT 8 |
#define GRAPH 16 |
#define LOWER 32 |
#define PRINT 64 |
#define PUNCT 128 |
#define BLANK 256 |
#define UPPER 512 |
#define XDIGIT 1024 |
extern char _is[128]; |
#define isalnum(c)(_is[c] & ALNUM ) /* 'a'-'z', 'A'-'Z', '0'-'9' */ |
#define isalpha(c)(_is[c] & ALPHA ) /* 'a'-'z', 'A'-'Z' */ |
#define iscntrl(c)(_is[c] & CNTRL ) /* 0-31, 127 */ |
#define isdigit(c)(_is[c] & DIGIT ) /* '0'-'9' */ |
#define isgraph(c)(_is[c] & GRAPH ) /* '!'-'~' */ |
#define islower(c)(_is[c] & LOWER ) /* 'a'-'z' */ |
#define isprint(c)(_is[c] & PRINT ) /* ' '-'~' */ |
#define ispunct(c)(_is[c] & PUNCT ) /* !alnum && !cntrl && !space */ |
#define isspace(c)(_is[c] & BLANK ) /* HT, LF, VT, FF, CR, ' ' */ |
#define isupper(c)(_is[c] & UPPER ) /* 'A'-'Z' */ |
#define isxdigit(c)(_is[c] & XDIGIT) /* '0'-'9', 'a'-'f', 'A'-'F' */ |
#define isascii(c) (!((c)&(~0x7f))) |
#define toascii(c) ((c)&0x7f) |
/programs/develop/ktcc/trunk/libc/include/kolibrisys.h |
---|
0,0 → 1,195 |
#ifndef kolibrisys_h |
#define kolibrisys_h |
/* |
#ifdef GNUC |
#define stdcall __stdcall |
#define cdecl __cdecl |
#else |
#define stdcall ((__stdcall)) |
#define cdecl ((__cdecl)) |
#endif |
*/ |
//#ifdef GNUC |
//#define stdcall __stdcall |
//#else |
#define cdecl __attribute__ ((cdecl)) |
#define stdcall __attribute__ ((stdcall)) |
//#endif |
typedef unsigned int dword; |
typedef unsigned char byte; |
typedef unsigned short word; |
typedef unsigned int fpos_t; |
typedef unsigned int size_t; |
typedef struct process_table_entry{ |
int cpu_usage; //+0 |
int window_pos_info; //+4 |
short int reserved1; //+8 |
char name[12]; //+10 |
int memstart; //+22 |
int memused; //+26 |
int pid; //+30 |
int winx_start; //+34 |
int winy_start; //+38 |
int winx_size; //+42 |
int winy_size; //+46 |
short int slot_info; //+50 |
short int reserved2; //+52 |
int clientx; //+54 |
int clienty; //+58 |
int clientwidth; //+62 |
int clientheight; //+66 |
unsigned char window_state;//+70 |
char reserved3[1024-71]; //+71 |
}__attribute__((packed)); |
//----------------------------------------------------------------------------------- |
//------------------------KolibriOS system acces to files---------------------------- |
//----------------------------------------------------------------------------------- |
extern dword stdcall _ksys_get_filesize(char *filename); |
extern dword stdcall _ksys_readfile(char *filename,dword pos,dword blocksize,void *data); |
extern dword stdcall _ksys_rewritefile(char *filename,dword blocksize,void *data); |
extern dword stdcall _ksys_appendtofile(char *filename,dword pos,dword blocksize,void *data); |
//----------------------------------------------------------------------------------- |
//----------------------Run program--------------------------------------------------- |
extern void stdcall _ksys_run_program(char* filename,char* parameters); |
//------------------------------------------------------------------------------------ |
//--------------------Debug output--------------------------------------------------- |
extern void stdcall _ksys_debug_out(int c); |
extern void stdcall debug_out_str(char* str); |
//----------------------------------------------------------------------------------- |
//--------------------------Mouse state---------------------------------------------- |
extern int stdcall _ksys_GetMouseXY(void); |
extern int stdcall _ksys_GetMouseButtonsState(void); |
//----------------------------------------------------------------------------------- |
//--------------------------get skin height------------------------------------------ |
extern int stdcall _ksys_get_skin_height(void); |
//----------------------------------------------------------------------------------- |
//----------------------------background--------------------------------------------- |
extern void stdcall _ksys_set_background_size(int xsize,int ysize); |
extern void stdcall _ksys_write_background_mem(int pos,int color); |
extern void stdcall _ksys_draw_background(void); |
extern void stdcall _ksys_set_background_draw_type(int type); |
extern void stdcall _ksys_background_blockmove(void* src,int bgr_pos, int count); |
//----------------------------------------------------------------------------------- |
//----------------------------functionf for draw window,lines.bar,etc.--------------- |
extern void stdcall _ksys_draw_window(int xcoord,int ycoord, int xsize, |
int ysize,int workcolor,int type, |
int captioncolor,int windowtype,int bordercolor); |
extern void stdcall _ksys_window_redraw(int status); |
extern int stdcall _ksys_putpixel(int x,int y,int color); |
extern void stdcall _ksys_draw_bar(int x, int y, int xsize, int ysize, int color); |
extern void stdcall _ksys_line(int x1,int y1,int x2,int y2,int color); |
extern void stdcall _ksys_putimage(int x, int y, int xsize, int ysize, void* image); |
//----------------------------------------------------------------------------------- |
//--------------------------write text(system fonts 6x9)----------------------------- |
extern void stdcall _ksys_write_text(int x,int y,int color,char* text,int len); |
//----------------------------------------------------------------------------------- |
//------------------ get screen size and bytes per pixel--------------------------- |
extern int stdcall _ksys_get_screen_size(int* x,int* y); |
extern void stdcall _ksys_dga_get_resolution(int* xres, int* yres, int* bpp, int* bpscan); |
//----------------------------------------------------------------------------------- |
//-------------------------------craete thread--------------------------------------- |
extern void* stdcall _ksys_start_thread(void (* func_ptr)(void),int stack_size,int* pid); |
//----------------------------------------------------------------------------------- |
//------------------system button(Old function. Better use libGUI functions.)-------- |
extern void stdcall _ksys_make_button(int x, int y, int xsize, int ysize, int id, int color); |
extern int stdcall _ksys_get_button_id(void); //get state of system button |
//------------------------------------------------------------------------------------ |
//----------------------system clock(in 1/100 sec.) and date-------------------------- |
extern int stdcall _ksys_get_system_clock(void); |
extern int stdcall _ksys_get_date(void); |
//------------------------------------------------------------------------------------ |
//-------------------------system delay(in 1/100 sec.)------------------------------- |
extern void stdcall _ksys_delay(int m); |
//----------------------------------------------------------------------------------- |
//------------------------system events---------------------------------------------- |
extern int stdcall _ksys_wait_for_event_infinite(void); |
extern int stdcall _ksys_check_for_event(void); |
extern int stdcall _ksys_wait_for_event(int time); |
extern void stdcall _ksys_set_wanted_events(int ev); |
//----------------------------------------------------------------------------------- |
//----------------------------system exit program------------------------------------ |
extern void stdcall _ksys_exit(void); |
//----------------------------------------------------------------------------------- |
//-----------------------------system IPC send message------------------------------- |
extern void stdcall _ksys_send_message(int pid, void* msg, int size); |
//----------------------------------------------------------------------------------- |
//---------------------------system work with IRQ from user mode--------------------- |
extern void stdcall _ksys_define_receive_area(void* area, int size); |
extern int stdcall _ksys_get_irq_owner(int irq); |
extern int stdcall _ksys_get_data_read_by_irq(int irq, int* size, void* data); |
extern int stdcall _ksys_send_data_to_device(int port, unsigned char val); |
extern int stdcall _ksys_receive_data_from_device(int port,unsigned char* data); |
extern void stdcall _ksys_program_irq(void* intrtable, int irq); |
extern void stdcall _ksys_reserve_irq(int irq); |
extern void stdcall _ksys_free_irq(int irq); |
//---------------------------------------------------------------------------------- |
//----------------------------system reserve diapason of ports---------------------- |
extern int stdcall _ksys_reserve_port_area(int start,int end); |
extern int stdcall _ksys_free_port_area(int start,int end); |
//---------------------------------------------------------------------------------- |
//-------------functions get key and set keyboard mode------------------------------ |
extern int stdcall _ksys_get_key(void); |
extern void stdcall _ksys_set_keyboard_mode(int mode); |
//---------------------------------------------------------------------------------- |
//--------------simple work with MPU401 sound device--------------------------------- |
extern void stdcall _ksys_midi_reset(void); |
extern void stdcall _ksys_midi_send(int data); |
//----------------------------------------------------------------------------------- |
//--------------------------acces to PCI BUS from user mode--------------------------- |
extern int stdcall _ksys_get_pci_version(void); |
extern int stdcall _ksys_get_last_pci_bus(void); |
extern int stdcall _ksys_get_pci_access_mechanism(void); |
extern int stdcall _ksys_pci_read_config_byte(int bus,int dev,int fn,int reg); |
extern int stdcall _ksys_pci_read_config_word(int bus,int dev,int fn,int reg); |
extern int stdcall _ksys_pci_read_config_dword(int bus,int dev,int fn,int reg); |
extern int stdcall _ksys_pci_write_config_byte(int bus,int dev,int fn,int reg,int value); |
extern int stdcall _ksys_pci_write_config_word(int bus,int dev,int fn,int reg,int value); |
extern int stdcall _ksys_pci_write_config_value(int bus,int dev,int fn,int reg,int value); |
//-------------------------------------------------------------------------------------- |
//------------------------Process information-------------------------------------- |
extern int stdcall _ksys_get_process_table(struct process_table_entry *proctab,int pid); //if pid=-1 than get info about him. |
//--------------------------------------------------------------------------------- |
//-----------------Old functions for work with sound(Sound Blaster only).--------- |
extern void stdcall _ksys_sound_load_block(void* blockptr); |
extern void stdcall _ksys_sound_play_block(void); |
extern void stdcall _ksys_sound_set_channels(int channels); |
extern void stdcall _ksys_sound_set_data_size(int size); |
extern void stdcall _ksys_sound_set_frequency(int frequency); |
//-------------------------------------------------------------------------------- |
//------------------------------system speaker(integrated speaker)---------------- |
extern void stdcall _ksys_sound_speaker_play(void* data); |
//-------------------------------------------------------------------------------- |
//------------------function for work with Dinamic Link Librarys(DLL)-------------- |
extern dword* stdcall _ksys_cofflib_load(char* name); |
extern char* stdcall _ksys_cofflib_getproc(void* exp,char* sz_name); |
//--------------------------------------------------------------------------------- |
#endif |
/programs/develop/ktcc/trunk/libc/include/math.h |
---|
0,0 → 1,179 |
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ |
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ |
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ |
extern int stdcall integer(float number); |
extern double acos(double _x); |
extern double asin(double _x); |
extern double atan(double _x); |
extern double atan2(double _y, double _x); |
extern double ceil(double _x); |
extern double cos(double _x); |
extern double cosh(double _x); |
extern double exp(double _x); |
extern double fabs(double _x); |
extern double floor(double _x); |
extern double fmod(double _x, double _y); |
extern double frexp(double _x, int *_pexp); |
extern double ldexp(double _x, int _exp); |
extern double log(double _y); |
extern double log10(double _x); |
extern double modf(double _x, double *_pint); |
extern double pow(double _x, double _y); |
extern double sin(double _x); |
extern double sinh(double _x); |
extern double sqrt(double _x); |
extern double tan(double _x); |
extern double tanh(double _x); |
//#ifndef __STRICT_ANSI__ |
//#ifndef _POSIX_SOURCE |
#define M_E 2.7182818284590452354 |
#define M_LOG2E 1.4426950408889634074 |
#define M_LOG10E 0.43429448190325182765 |
#define M_LN2 0.69314718055994530942 |
#define M_LN10 2.30258509299404568402 |
#define M_PI 3.14159265358979323846 |
#define M_PI_2 1.57079632679489661923 |
#define M_PI_4 0.78539816339744830962 |
#define M_1_PI 0.31830988618379067154 |
#define M_2_PI 0.63661977236758134308 |
#define M_2_SQRTPI 1.12837916709551257390 |
#define M_SQRT2 1.41421356237309504880 |
#define M_SQRT1_2 0.70710678118654752440 |
#define PI M_PI |
#define PI2 M_PI_2 |
extern double acosh(double); |
extern double asinh(double); |
extern double atanh(double); |
extern double cbrt(double); |
extern double exp10(double _x); |
extern double exp2(double _x); |
extern double expm1(double); |
extern double hypot(double, double); |
extern double log1p(double); |
extern double log2(double _x); |
extern long double modfl(long double _x, long double *_pint); |
extern double pow10(double _x); |
extern double pow2(double _x); |
extern double powi(double, int); |
extern void sincos(double *, double *, double); |
/* These are in libm.a (Cygnus). You must link -lm to get these */ |
/* See libm/math.h for comments */ |
/* |
#ifndef __cplusplus |
struct exception { |
int type; |
const char *name; |
double arg1; |
double arg2; |
double retval; |
int err; |
}; |
#endif |
*/ |
extern double erf(double); |
extern double erfc(double); |
extern double gamma(double); |
extern int isinf(double); |
extern int isnan(double); |
extern int finite(double); |
extern double j0(double); |
extern double j1(double); |
extern double jn(int, double); |
extern double lgamma(double); |
extern double nan(void); |
extern double y0(double); |
extern double y1(double); |
extern double yn(int, double); |
extern double logb(double); |
extern double nextafter(double, double); |
extern double remainder(double, double); |
extern double scalb(double, double); |
//#ifndef __cplusplus |
//extern int matherr(struct exception *); |
//#endif |
extern double significand(double); |
extern double copysign(double, double); |
extern int ilogb(double); |
extern double rint(double); |
extern double scalbn(double, int); |
extern double drem(double, double); |
extern double gamma_r(double, int *); |
extern double lgamma_r(double, int *); |
extern float acosf(float); |
extern float asinf(float); |
extern float atanf(float); |
extern float atan2f(float, float); |
extern float cosf(float); |
extern float sinf(float); |
extern float tanf(float); |
extern float coshf(float); |
extern float sinhf(float); |
extern float tanhf(float); |
extern float expf(float); |
extern float frexpf(float, int *); |
extern float ldexpf(float, int); |
extern float logf(float); |
extern float log10f(float); |
extern float modff(float, float *); |
extern float powf(float, float); |
extern float sqrtf(float); |
extern float ceilf(float); |
extern float fabsf(float); |
extern float floorf(float); |
extern float fmodf(float, float); |
extern float erff(float); |
extern float erfcf(float); |
extern float gammaf(float); |
extern float hypotf(float, float); |
extern int isinff(float); |
extern int isnanf(float); |
extern int finitef(float); |
extern float j0f(float); |
extern float j1f(float); |
extern float jnf(int, float); |
extern float lgammaf(float); |
extern float nanf(void); |
extern float y0f(float); |
extern float y1f(float); |
extern float ynf(int, float); |
extern float acoshf(float); |
extern float asinhf(float); |
extern float atanhf(float); |
extern float cbrtf(float); |
extern float logbf(float); |
extern float nextafterf(float, float); |
extern float remainderf(float, float); |
extern float scalbf(float, float); |
extern float significandf(float); |
extern float copysignf(float, float); |
extern int ilogbf(float); |
extern float rintf(float); |
extern float scalbnf(float, int); |
extern float dremf(float, float); |
extern float expm1f(float); |
extern float log1pf(float); |
extern float gammaf_r(float, int *); |
extern float lgammaf_r(float, int *); |
//#endif /* !_POSIX_SOURCE */ |
//#endif /* !__STRICT_ANSI__ */ |
//#endif /* !__dj_ENFORCE_ANSI_FREESTANDING */ |
//#ifndef __dj_ENFORCE_FUNCTION_CALLS |
//#endif /* !__dj_ENFORCE_FUNCTION_CALLS */ |
//#ifdef __cplusplus |
//} |
//#endif |
//#endif /* _USE_LIBM_MATH_H */ |
//#endif /* !__dj_include_math_h_ */ |
/programs/develop/ktcc/trunk/libc/include/stdio.h |
---|
0,0 → 1,57 |
#ifndef stdio_h |
#define stdio_h |
#include "kolibrisys.h" |
typedef char *va_list; |
#define _roundsize(n) ( (sizeof(n) + 3) & ~3 ) |
#define va_start(ap,v) (ap = (va_list)&v+_roundsize(v)) |
#define va_arg(ap,t) ( *(t *)((ap += _roundsize(t)) - _roundsize(t)) ) |
#define va_end(ap) (ap = (va_list)0) |
#define NULL ((void*)0) |
//extern int stdcall format_print(char *dest, size_t maxlen, const char *fmt0, va_list argp); |
typedef struct { |
char* buffer; |
dword buffersize; |
dword filesize; |
dword filepos; |
char* filename; |
int mode; |
} FILE; |
#define FILE_OPEN_READ 0 |
#define FILE_OPEN_WRITE 1 |
#define FILE_OPEN_APPEND 2 |
#define FILE_OPEN_TEXT 4 |
#define FILE_OPEN_PLUS 8 |
#define EOF -1 |
extern FILE* fopen(const char* filename, const char *mode); |
extern void fclose(FILE* file); |
extern int feof(FILE* file); |
extern int fflush(FILE* file); |
extern int fgetc(FILE* file); |
extern int fgetpos(FILE* file,fpos_t* pos); |
extern int fsetpos(FILE* file,const fpos_t* pos); |
extern int fputc(int c,FILE* file); |
extern int fread(void* buffer,int size,int count,FILE* file); |
extern int fwrite(void *buffer,int size,int count,FILE* file); |
extern long ftell(FILE* file); |
#define SEEK_CUR 0 |
#define SEEK_END 1 |
#define SEEK_SET 2 |
extern int fseek(FILE* file,long offset,int origin); |
extern void rewind(FILE* file); |
extern int cdecl fprintf(FILE* file, const char* format,...); |
extern int fscanf(FILE* file,const char* format,...); |
extern int ungetc(int c,FILE* file); |
extern int cdecl printf(const char *format,...); |
extern int vsnprintf(char *dest, size_t size,const char *format,va_list ap); |
extern int cdecl snprintf(char *dest, size_t size, const char *format,...); |
extern int cdecl sprintf(char *dest,const char *format,...); |
#endif |
/programs/develop/ktcc/trunk/libc/include/stdlib.h |
---|
0,0 → 1,18 |
#ifndef stdlib_h |
#define stdlib_h |
#include "kolibrisys.h" |
//#define isspace(c) ((c)==' ') |
#define abs(i) (((i)<0)?(-(i)):(i)) |
extern int atoib(char *s,int b); |
extern int atoi(char *s); |
extern unsigned char tolower(unsigned char c); |
extern unsigned char toupper(unsigned char c); |
extern void itoab(int n,char* s,int b); |
extern void itoa(int n,char* s); |
extern void* stdcall malloc(dword size); |
extern void stdcall free(void *pointer); |
extern void* stdcall realloc(void* pointer,dword size); |
#endif |
/programs/develop/ktcc/trunk/libc/include/string.h |
---|
0,0 → 1,25 |
#ifndef string_h |
#define string_h |
extern void* memchr(const void*,int,int); |
extern int memcmp(const void*,const void*,int); |
extern void* memcpy(void*,const void*,int); |
extern void* memmove(void*,const void*,int); |
extern void* memset(void*,int,int); |
extern char* strcat(char*,const char*); |
extern char* strchr(const char*,int); |
extern int strcmp(const char*,const char*); |
extern int strcoll(const char*,const char*); |
extern char* strcpy(char*,const char*); |
extern int strcspn(const char*,const char*); |
extern int strlen(const char*); |
extern char* strncat(char*,const char*,int); |
extern int strncmp(const char*,const char*,int); |
extern char* strncpy(char*,const char*,int); |
extern char* strpbrk(const char*,const char*); |
extern char* strrchr(const char*,int); |
extern int strspn(const char*,const char*); |
extern char* strstr(const char*,const char*); |
extern char* strtok(char*,const char*); |
extern int strxfrm(char*,const char*,int); |
extern char* strdup(const char*); |
#endif |
/programs/develop/ktcc/trunk/libc/kolibrisys/_ksys_files_acces.asm |
---|
0,0 → 1,119 |
format ELF |
section '.text' executable |
include 'proc32.inc' |
public _ksys_get_filesize |
public _ksys_readfile |
public _ksys_rewritefile |
public _ksys_appendtofile |
align 4 |
proc _ksys_get_filesize stdcall, filename:dword |
xor eax,eax |
mov ebx,[filename] |
mov [fileinfo.subproc],dword 5 |
mov [fileinfo.offset_l],eax |
mov [fileinfo.offset_h],eax |
mov [fileinfo.size],eax |
mov [fileinfo.data],dword buffer_for_info |
mov [fileinfo.letter],al |
mov [fileinfo.filename],ebx |
mov eax,70 |
mov ebx,fileinfo |
int 0x40 |
test eax,eax |
jnz error_for_file_size |
mov eax,[buffer_for_info+32] ;file size |
error_for_file_size: |
ret |
endp |
align 4 |
proc _ksys_readfile stdcall,filename:dword,position:dword,sizeblock:dword,buffer:dword |
xor eax,eax |
mov ebx,[position] |
mov ecx,[sizeblock] |
mov edx,[buffer] |
mov esi,[filename] |
mov [fileinfo.subproc],eax |
mov [fileinfo.offset_l],ebx |
mov [fileinfo.offset_h],eax |
mov [fileinfo.size],ecx |
mov [fileinfo.data],edx |
mov [fileinfo.letter],al |
mov [fileinfo.filename],esi |
mov eax,70 |
mov ebx,fileinfo |
int 0x40 |
ret |
endp |
align 4 |
proc _ksys_rewritefile stdcall,filename:dword,sizeblock:dword,data_write:dword |
xor eax,eax |
mov ebx,[sizeblock] |
mov ecx,[data_write] |
mov edx,[filename] |
mov [fileinfo.subproc],dword 2 |
mov [fileinfo.offset_l],eax |
mov [fileinfo.offset_h],eax |
mov [fileinfo.size],ebx |
mov [fileinfo.data],ecx |
mov [fileinfo.letter],al |
mov [fileinfo.filename],edx |
mov eax,70 |
mov ebx,fileinfo |
int 0x40 |
ret |
endp |
align 4 |
proc _ksys_appendtofile stdcall,filename:dword,pos:dword,sizeblock:dword,data_append:dword |
xor eax,eax |
mov ebx,[pos] |
mov ecx,[sizeblock] |
mov edx,[data_append] |
mov esi,[filename] |
mov [fileinfo.subproc],dword 3 |
mov [fileinfo.offset_l],ebx |
mov [fileinfo.offset_h],eax |
mov [fileinfo.size],ecx |
mov [fileinfo.data],edx |
mov [fileinfo.letter],al |
mov [fileinfo.filename],esi |
mov eax,70 |
mov ebx,fileinfo |
int 0x40 |
ret |
endp |
struc FILEIO |
{ |
.subproc rd 1 |
.offset_l rd 1 |
.offset_h rd 1 |
.size rd 1 |
.data rd 1 |
.letter rb 1 |
.filename rd 1 |
} |
fileinfo FILEIO |
buffer_for_info rd 11 |
/programs/develop/ktcc/trunk/libc/kolibrisys/backgr.asm |
---|
0,0 → 1,54 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_set_background_size,8 |
;arg1 - xsize |
;arg2 - ysize |
push ebx |
mov ecx,[esp+8] |
mov edx,[esp+12] |
mov eax,15 |
mov ebx,1 |
int 0x40 |
pop ebx |
ret 8 |
public_stdcall _ksys_write_background_mem,8 |
;arg1 - pos |
;arg2 - color |
push ebx |
mov eax,15 |
mov ebx,2 |
mov ecx,[esp+8] |
mov edx,[esp+12] |
int 0x40 |
pop ebx |
ret 8 |
public_stdcall _ksys_draw_background,0 |
mov edx,ebx |
mov eax,15 |
mov ebx,3 |
int 0x40 |
mov ebx,edx |
ret |
public_stdcall _ksys_set_background_draw_type,4 |
;arg1 - type |
mov edx,ebx |
mov eax,15 |
mov ebx,4 |
mov ecx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
public_stdcall _ksys_background_blockmove,12 |
;arg1 - source |
;arg2 - position in dest |
;arg3 - size |
push ebx esi |
mov eax,15 |
mov ebx,5 |
mov ecx,[esp+12] |
mov edx,[esp+16] |
mov esi,[esp+20] |
int 0x40 |
pop esi ebx |
ret 12 |
/programs/develop/ktcc/trunk/libc/kolibrisys/button.asm |
---|
0,0 → 1,35 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_make_button,24 |
;arg1 - x |
;arg2 - y |
;arg3 - xsize |
;arg4 - ysize |
;arg5 - id |
;arg6 - color |
push ebx esi |
mov ebx,[esp+12] |
shl ebx,16 |
mov bx,[esp+20] |
mov ecx,[esp+16] |
shl ecx,16 |
mov cx,[esp+24] |
mov edx,[esp+28] |
mov esi,[esp+32] |
mov eax,8 |
int 0x40 |
pop esi ebx |
ret 24 |
public_stdcall _ksys_get_button_id,0 |
mov eax,17 |
int 0x40 |
test al,al |
jnz .no_button |
shr eax,8 |
ret |
.no_button: |
xor eax,eax |
dec eax |
ret |
/programs/develop/ktcc/trunk/libc/kolibrisys/clock.asm |
---|
0,0 → 1,15 |
format ELF |
include "proc32.inc" |
section '.text' executable |
public _ksys_get_system_clock |
align 4 |
proc _ksys_get_system_clock stdcall |
mov eax,3 |
int 0x40 |
ret |
endp |
/programs/develop/ktcc/trunk/libc/kolibrisys/cofflib.asm |
---|
0,0 → 1,73 |
format ELF |
include 'proc32.inc' |
section '.text' executable |
public _ksys_cofflib_load |
public _ksys_cofflib_getproc |
proc _ksys_cofflib_load stdcall, name:dword |
mov eax, 68 |
mov ebx, 19 |
mov ecx, [name] |
int 0x40 |
ret |
endp |
proc _ksys_cofflib_getproc stdcall, export:dword,name:dword |
mov ebx,[export] |
next_name_check: |
mov ecx,[ebx] |
test ecx,ecx |
jz end_export |
;cmp export string with name |
mov esi,[name] |
xor edi,edi |
next_simbol_check: |
xor eax,eax |
mov al,[ecx] |
test al,al |
jz exit_check_simbol |
xor edx,edx |
mov dl,[esi] |
cmp al,dl |
je simbols_equvalent |
add edi,1 |
jmp exit_check_simbol |
simbols_equvalent: |
;pushad |
;mov cl,al |
;mov ebx,1 |
;mov eax,63 |
;int 0x40 |
;popad |
add ecx,1 |
add esi,1 |
jmp next_simbol_check |
exit_check_simbol: |
test edi,edi |
jnz function_not_finded |
mov eax,[ebx+4] |
jmp end_export |
function_not_finded: |
add ebx,8 |
jmp next_name_check |
end_export: |
ret |
endp |
/programs/develop/ktcc/trunk/libc/kolibrisys/date.asm |
---|
0,0 → 1,7 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_get_date,0 |
mov eax,29 |
int 0x40 |
ret |
/programs/develop/ktcc/trunk/libc/kolibrisys/debug_board.asm |
---|
0,0 → 1,56 |
format ELF |
include "proc32.inc" |
section '.text' executable |
public _ksys_debug_out |
public debug_out_str |
align 4 |
proc _ksys_debug_out stdcall, c:dword |
pushad |
xor ecx,ecx |
mov cl,byte[c] |
mov ebx,1 |
mov eax,63 |
int 0x40 |
popad |
ret |
endp |
align 4 |
proc debug_out_str stdcall, s:dword |
pushad |
mov eax,[s] ;eax=pointer to string |
next_simbol_print: |
xor ebx,ebx |
mov bl,[eax] |
test bl,bl |
jz exit_print_str |
cmp bl,10 |
jne no_new_line |
mov ecx,13 |
stdcall _ksys_debug_out, ecx |
no_new_line: |
stdcall _ksys_debug_out, ebx |
add eax,1 |
jmp next_simbol_print |
exit_print_str: |
popad |
ret |
endp |
/programs/develop/ktcc/trunk/libc/kolibrisys/delay.asm |
---|
0,0 → 1,11 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_delay,4 |
;arg1 - time |
mov edx,ebx |
mov eax,5 |
mov ebx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
/programs/develop/ktcc/trunk/libc/kolibrisys/dga.asm |
---|
0,0 → 1,34 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_dga_get_resolution,16 |
;arg1 - *xres |
;arg2 - *yres |
;arg3 - *bpp |
;arg4 - *bpscan |
mov edx,ebx |
mov eax,61 |
mov ebx,1 |
int 0x40 |
mov ebx,[esp+8] |
mov [ebx],ax |
mov word [ebx+2],0 |
shr eax,16 |
mov ebx,[esp+4] |
mov [ebx],eax |
mov eax,61 |
mov ebx,2 |
int 0x40 |
mov ebx,[esp+12] |
mov [ebx],eax |
mov eax,61 |
mov ebx,3 |
int 0x40 |
mov ebx,[esp+16] |
mov [ebx],eax |
mov ebx,edx |
ret 16 |
/programs/develop/ktcc/trunk/libc/kolibrisys/draw_bar.asm |
---|
0,0 → 1,21 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_draw_bar,20 |
;arg1 - x |
;arg2 - y |
;arg3 - xsize |
;arg4 - ysize |
;arg5 - color |
push ebx |
mov eax,13 |
mov ebx,[esp+8] |
shl ebx,16 |
mov bx,[esp+16] |
mov ecx,[esp+12] |
shl ecx,16 |
mov cx,[esp+20] |
mov edx,[esp+24] |
int 0x40 |
pop ebx |
ret 20 |
/programs/develop/ktcc/trunk/libc/kolibrisys/draw_image.asm |
---|
0,0 → 1,21 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_putimage,20 |
;arg1 - x |
;arg2 - y |
;arg3 - xsize |
;arg4 - ysize |
;arg5 - image |
push ebx |
mov ebx,[esp+24] |
mov ecx,[esp+16] |
shl ecx,16 |
mov ecx,[esp+20] |
mov ebx,[esp+8] |
shl ebx,16 |
mov ebx,[esp+12] |
mov eax,7 |
int 0x40 |
pop ebx |
ret 20 |
/programs/develop/ktcc/trunk/libc/kolibrisys/draw_window.asm |
---|
0,0 → 1,34 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_draw_window,36 |
;arg1 - xcoord |
;arg2 - ycoord |
;arg3 - xsize |
;arg4 - ysize |
;arg5 - workcolor |
;arg6 - type |
;arg7 - captioncolor |
;arg8 - windowtype |
;arg9 - bordercolor |
push ebp |
mov ebp,esp |
push ebx esi edi |
mov ebx,[ebp+8] |
shl ebx,16 |
mov bx,[ebp+16] |
mov ecx,[ebp+12] |
shl ecx,16 |
mov cx,[ebp+20] |
mov edx,[ebp+28] |
shl edx,24 |
add edx,[ebp+24] |
mov esi,[ebp+36] |
shl esi,24 |
add esi,[ebp+32] |
mov edi,[ebp+40] |
xor eax,eax |
int 0x40 |
pop edi esi ebx |
pop ebp |
ret 36 |
/programs/develop/ktcc/trunk/libc/kolibrisys/event.asm |
---|
0,0 → 1,40 |
format ELF |
section '.text' executable |
public _ksys_wait_for_event_infinite |
public _ksys_check_for_event |
public _ksys_wait_for_event |
public _ksys_set_wanted_events |
_ksys_wait_for_event_infinite: |
mov eax,10 |
int 0x40 |
ret |
_ksys_check_for_event: |
mov eax,11 |
int 0x40 |
ret |
_ksys_wait_for_event: |
;arg1 - time |
mov eax,23 |
mov ebx,[esp+4] |
int 0x40 |
ret 4 |
_ksys_set_wanted_events: |
;arg1 - flags |
mov eax,40 |
mov ebx,[esp+4] |
int 0x40 |
ret 4 |
/programs/develop/ktcc/trunk/libc/kolibrisys/exit.asm |
---|
0,0 → 1,8 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_exit,0 |
xor eax,eax |
dec eax |
int 0x40 |
; ret |
/programs/develop/ktcc/trunk/libc/kolibrisys/ipc.asm |
---|
0,0 → 1,28 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_send_message,12 |
;arg1 - pid |
;arg2 - msg |
;arg3 - size |
push ebx esi |
mov eax,60 |
mov ebx,2 |
mov ecx,[esp+12] |
mov edx,[esp+16] |
mov esi,[esp+20] |
int 0x40 |
pop esi ebx |
ret 12 |
public_stdcall _ksys_define_receive_area,8 |
;arg1 - area |
;arg2 - size |
push ebx |
mov eax,60 |
mov ebx,1 |
mov ecx,[esp+8] |
mov edx,[esp+12] |
int 0x40 |
pop ebx |
ret 8 |
/programs/develop/ktcc/trunk/libc/kolibrisys/irq.asm |
---|
0,0 → 1,119 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_get_irq_owner,4 |
;arg1 - irq |
mov edx,ebx |
mov eax,41 |
mov ebx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
public_stdcall _ksys_get_data_read_by_irq,12 |
;arg1 - irq |
;arg2 - *size |
;arg3 - data |
mov edx,ebx |
mov eax,42 |
mov ebx,[esp+4] |
int 0x40 |
cmp ecx,2 |
jz .not_an_owner |
push ecx |
mov ecx,[esp+16] |
test ecx,ecx |
jz .ignore_data |
mov [ecx],bl |
.ignore_data: |
mov ecx,[esp+12] |
mov [ecx],eax |
pop eax |
mov ebx,edx |
ret 12 |
.not_an_owner: |
mov eax,2 |
mov ebx,edx |
ret |
public_stdcall _ksys_send_data_to_device,8 |
;arg1 - port |
;arg2 - data |
mov edx,ebx |
mov eax,63 |
mov ebx,[esp+8] |
mov ecx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 8 |
public_stdcall _ksys_receive_data_from_device,8 |
;arg1 - port |
;arg2 - data |
mov edx,ebx |
mov eax,43 |
mov ecx,[esp+4] |
add ecx,0x80000000 |
int 0x40 |
mov ecx,[esp+8] |
mov [ecx],bl |
mov ebx,edx |
ret 8 |
public_stdcall _ksys_program_irq,8 |
;arg1 - intrtable |
;arg2 - irq |
mov edx,ebx |
mov eax,44 |
mov ebx,[esp+4] |
mov ecx,[esp+8] |
int 0x40 |
mov ebx,edx |
ret 8 |
public_stdcall _ksys_reserve_irq,4 |
;arg1 - irq |
mov edx,ebx |
mov eax,45 |
xor ebx,ebx |
mov ecx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
public_stdcall _ksys_free_irq,4 |
;arg1 - irq |
mov edx,ebx |
mov eax,45 |
xor ebx,ebx |
inc ebx |
mov ecx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
public_stdcall _ksys_reserve_port_area,8 |
;arg1 - start |
;arg2 - end |
push ebx |
mov eax,46 |
xor ebx,ebx |
mov ecx,[esp+8] |
mov edx,[esp+12] |
int 0x40 |
pop ebx |
ret 8 |
public_stdcall _ksys_free_port_area,8 |
;arg1 - start |
;arg2 - end |
push ebx |
mov eax,46 |
xor ebx,ebx |
inc ebx |
mov ecx,[esp+8] |
mov edx,[esp+12] |
int 0x40 |
pop ebx |
ret 8 |
/programs/develop/ktcc/trunk/libc/kolibrisys/keyboard.asm |
---|
0,0 → 1,29 |
format ELF |
include "proc32.inc" |
section '.text' executable |
public _ksys_get_key |
public _ksys_set_keyboard_mode |
align 4 |
proc _ksys_get_key stdcall |
mov eax,2 |
int 0x40 |
ret |
endp |
align 4 |
proc _ksys_set_keyboard_mode stdcall, mode:dword |
mov edx,ebx |
mov eax,66 |
xor ebx,ebx |
inc ebx |
mov ecx,[mode] |
mov ebx,edx |
ret |
endp |
/programs/develop/ktcc/trunk/libc/kolibrisys/line.asm |
---|
0,0 → 1,21 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_line,20 |
;arg1 - x1 |
;arg2 - y1 |
;arg3 - x2 |
;arg4 - y2 |
;arg5 - color |
push ebx |
mov ebx,[esp+8] |
shl ebx,16 |
mov bx,[esp+16] |
mov ecx,[esp+12] |
shl ecx,16 |
mov cx,[esp+20] |
mov edx,[esp+24] |
mov eax,38 |
int 0x40 |
pop ebx |
ret 20 |
/programs/develop/ktcc/trunk/libc/kolibrisys/midi.asm |
---|
0,0 → 1,21 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_midi_reset,0 |
mov edx,ebx |
mov eax,20 |
xor ebx,ebx |
inc ebx |
int 0x40 |
mov ebx,edx |
ret |
public_stdcall _ksys_midi_send,4 |
;arg1 - data |
mov edx,ebx |
mov eax,20 |
mov ebx,2 |
xor ecx,ecx |
mov cl,[esp+4] |
mov ebx,edx |
ret 4 |
/programs/develop/ktcc/trunk/libc/kolibrisys/mouse.asm |
---|
0,0 → 1,25 |
format ELF |
section '.text' executable |
public _ksys_GetMouseXY |
public _ksys_GetMouseButtonsState |
align 4 |
_ksys_GetMouseXY: |
mov eax,37 |
mov ebx,1 |
int 0x40 |
ret |
align 4 |
_ksys_GetMouseButtonsState: |
mov eax,37 |
mov ebx,2 |
int 0x40 |
ret |
/programs/develop/ktcc/trunk/libc/kolibrisys/pci.asm |
---|
0,0 → 1,138 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_get_pci_version,0 |
mov edx,ebx |
mov eax,62 |
xor ebx,ebx |
int 0x40 |
movzx eax,ax |
mov ebx,edx |
ret |
public_stdcall _ksys_get_last_pci_bus,0 |
mov edx,ebx |
mov eax,62 |
xor ebx,ebx |
inc ebx |
int 0x40 |
movzx eax,al |
mov ebx,edx |
ret |
public_stdcall _ksys_get_pci_access_mechanism,0 |
mov edx,ebx |
mov eax,62 |
mov ebx,2 |
int 0x40 |
movzx eax,al |
mov ebx,edx |
ret |
public_stdcall _ksys_pci_read_config_byte,16 |
;arg1 - bus |
;arg2 - dev |
;arg3 - fn |
;arg4 - reg |
mov edx,ebx |
mov eax,62 |
mov bl,4 |
mov bh,[esp+4] |
mov ch,[esp+8] |
shl ch,3 |
add ch,[esp+12] |
mov cl,[esp+16] |
int 0x40 |
mov ebx,edx |
ret 16 |
public_stdcall _ksys_pci_read_config_word,16 |
;arg1 - bus |
;arg2 - dev |
;arg3 - fn |
;arg4 - reg |
mov edx,ebx |
mov eax,62 |
mov bl,5 |
mov bh,[esp+4] |
mov ch,[esp+8] |
shl ch,3 |
add ch,[esp+12] |
mov cl,[esp+16] |
int 0x40 |
mov ebx,edx |
ret 16 |
public_stdcall _ksys_pci_read_config_dword,16 |
;arg1 - bus |
;arg2 - dev |
;arg3 - fn |
;arg4 - reg |
mov edx,ebx |
mov eax,62 |
mov bl,6 |
mov bh,[esp+4] |
mov ch,[esp+8] |
shl ch,3 |
add ch,[esp+12] |
mov cl,[esp+16] |
int 0x40 |
mov ebx,edx |
ret 16 |
public_stdcall _ksys_pci_write_config_byte,20 |
;arg1 - bus |
;arg2 - dev |
;arg3 - fn |
;arg4 - reg |
;arg5 - value |
push ebx |
mov eax,62 |
mov bl,8 |
mov bh,[esp+8] |
mov ch,[esp+12] |
shl ch,3 |
mov ch,[esp+16] |
mov cl,[esp+20] |
movzx edx,byte [esp+24] |
int 0x40 |
pop ebx |
ret 20 |
public_stdcall _ksys_pci_write_config_word,20 |
;arg1 - bus |
;arg2 - dev |
;arg3 - fn |
;arg4 - reg |
;arg5 - value |
push ebx |
mov eax,62 |
mov bl,9 |
mov bh,[esp+8] |
mov ch,[esp+12] |
shl ch,3 |
mov ch,[esp+16] |
mov cl,[esp+20] |
movzx edx,word [esp+24] |
int 0x40 |
pop ebx |
ret 20 |
public_stdcall _ksys_pci_write_config_dword,20 |
;arg1 - bus |
;arg2 - dev |
;arg3 - fn |
;arg4 - reg |
;arg5 - value |
push ebx |
mov eax,62 |
mov bl,10 |
mov bh,[esp+8] |
mov ch,[esp+12] |
shl ch,3 |
mov ch,[esp+16] |
mov cl,[esp+20] |
mov edx,[esp+24] |
int 0x40 |
pop ebx |
ret 20 |
/programs/develop/ktcc/trunk/libc/kolibrisys/pixel.asm |
---|
0,0 → 1,16 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_putpixel,12 |
;arg1 - x |
;arg2 - y |
;arg3 - color |
push ebx |
xor eax,eax |
mov ebx,[esp+8] |
inc eax |
mov ecx,[esp+12] |
mov edx,[esp+16] |
int 0x40 |
pop ebx |
ret 12 |
/programs/develop/ktcc/trunk/libc/kolibrisys/process.asm |
---|
0,0 → 1,16 |
format ELF |
;include "public_stdcall.inc" |
public _ksys_get_process_table |
section '.text' executable |
_ksys_get_process_table: |
;arg1 - pointer to information |
;arg2 - pid |
mov eax,9 |
mov ebx,[esp+4] |
mov ecx,[esp+8] |
int 0x40 |
ret 8 |
/programs/develop/ktcc/trunk/libc/kolibrisys/screen.asm |
---|
0,0 → 1,15 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_get_screen_size,8 |
;arg1 - x |
;arg2 - y |
mov eax,14 |
int 0x40 |
mov ecx,[esp+8] |
mov [ecx],ax |
mov word [ecx+2],0 |
shr eax,16 |
mov ecx,[esp+4] |
mov [ecx],eax |
ret 8 |
/programs/develop/ktcc/trunk/libc/kolibrisys/skin.asm |
---|
0,0 → 1,13 |
format ELF |
section '.text' executable |
public _ksys_get_skin_height |
_ksys_get_skin_height: |
mov eax,48 |
mov ebx,4 |
int 0x40 |
ret |
/programs/develop/ktcc/trunk/libc/kolibrisys/sound.asm |
---|
0,0 → 1,65 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksy_sound_load_block,4 |
;arg1 - blockptr |
mov edx,ebx |
mov eax,55 |
xor ebx,ebx |
mov ecx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
public_stdcall _ksy_sound_play_block,0 |
mov edx,ebx |
mov eax,55 |
xor ebx,ebx |
inc ebx |
int 0x40 |
mov ebx,edx |
ret |
public_stdcall _ksy_sound_set_channels,4 |
;arg1 - channels |
push ebx |
mov eax,55 |
mov ebx,2 |
xor ecx,ecx |
mov edx,[esp+8] |
int 0x40 |
pop ebx |
ret 4 |
public_stdcall _ksy_sound_set_data_size,4 |
;arg1 - data size |
push ebx |
mov eax,55 |
mov ebx,2 |
xor ecx,ecx |
inc ecx |
mov edx,[esp+8] |
int 0x40 |
pop ebx |
ret 4 |
public_stdcall _ksy_sound_set_frequency,4 |
;arg1 - frequency |
push ebx |
mov eax,55 |
mov ebx,2 |
mov ecx,2 |
mov edx,[esp+8] |
int 0x40 |
pop ebx |
ret 4 |
public_stdcall _ksy_sound_speaker_play,4 |
;arg1 - data |
mov edx,ebx |
mov eax,55 |
mov ebx,55 |
mov ecx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
/programs/develop/ktcc/trunk/libc/kolibrisys/thread.asm |
---|
0,0 → 1,33 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
extrn malloc |
public_stdcall _ksys_start_thread,12 |
;arg1 - proc |
;arg2 - stacksize |
;arg3 - pid |
push dword [esp+8] |
call malloc |
test eax,eax |
jz .no_mem |
push ebx |
mov edx,eax |
add edx,[esp+12] |
mov [edx-4],dword 0 |
mov ecx,[esp+8] |
mov ebx,1 |
mov eax,51 |
int 0x40 |
mov ebx,[esp+16] |
test ebx,ebx |
jz .no_val |
mov [ebx],eax |
.no_val: |
mov eax,edx |
sub eax,[esp+12] |
pop ebx |
ret 12 |
.no_mem: |
mov ecx,[esp+12] |
mov [ecx],eax |
ret 12 |
/programs/develop/ktcc/trunk/libc/kolibrisys/window_redraw.asm |
---|
0,0 → 1,11 |
format ELF |
include "public_stdcall.inc" |
section '.text' executable |
public_stdcall _ksys_window_redraw,4 |
;arg1 - status |
mov edx,ebx |
mov eax,12 |
mov ebx,[esp+4] |
int 0x40 |
mov ebx,edx |
ret 4 |
/programs/develop/ktcc/trunk/libc/kolibrisys/write_text.asm |
---|
0,0 → 1,21 |
format ELF |
section '.text' executable |
public _ksys_write_text |
_ksys_write_text: |
;arg1 - x |
;arg2 - y |
;arg3 - color |
;arg4 - text |
;arg5 - len |
mov eax,4 |
mov ebx,[esp+4] |
shl ebx,16 |
mov bx,[esp+8] |
mov ecx,[esp+12] |
mov edx,[esp+16] |
mov esi,[esp+20] |
int 0x40 |
ret 20 |
/programs/develop/ktcc/trunk/libc/makefile |
---|
0,0 → 1,30 |
INCLUDE = include |
LIBSFORBUILD = math |
LIBNAME = libck.a |
CC = gcc |
CFLAGS = -I$(INCLUDE) -nostdinc -DGNUC -L./ -lm |
DIRS := stdio kolibrisys string stdlib memory math |
############################################################## |
#files := $(foreach dir,$(DIRS),$(dir)/$(wildcard $(dir)/*)) |
asmfiles := $(foreach dir,$(DIRS),$(patsubst %.asm, %.o, $(wildcard $(dir)/*.asm))) |
cfiles := $(foreach dir,$(DIRS),$(patsubst %.c, %.o, $(wildcard $(dir)/*.c))) |
.PHONY: clean all |
ifdef windir |
doClean = del /F /Q $(subst /,\,$(cfiles)) $(subst /,\,$(asmfiles)) |
else |
doClean = rm $(cfiles) $(asmfiles) |
endif |
all: $(cfiles) $(asmfiles) |
ar -ru $(LIBNAME) $^ |
$(cfiles): $(INCLUDE)/*.h |
$(asmfiles): |
fasm $*.asm $*.o |
clean: |
$(doClean) |
/programs/develop/ktcc/trunk/libc/memory/memalloc.asm |
---|
0,0 → 1,38 |
format ELF |
;include "proc32.inc" |
section '.text' executable |
public malloc |
public free |
public realloc |
align 4 |
malloc: |
mov eax,68 |
mov ebx,12 |
mov ecx,[esp+4] ;size |
int 0x40 |
ret 4 |
align 4 |
free: |
mov eax,68 |
mov ebx,13 |
mov ecx,[esp+4] |
int 0x40 |
ret 4 |
align 4 |
realloc: |
mov ebx,20 |
mov eax,68 |
mov ecx,[esp+4] |
mov edx,[esp+8] |
int 0x40 |
ret 8 |
/programs/develop/ktcc/trunk/libc/stdio/fclose.c |
---|
0,0 → 1,9 |
#include <stdio.h> |
#include <string.h> |
#include <stdlib.h> |
void fclose(FILE* file) |
{ |
free(file->buffer); |
free(file); |
} |
/programs/develop/ktcc/trunk/libc/stdio/feof.c |
---|
0,0 → 1,5 |
#include <stdio.h> |
int feof(FILE* file) |
{ |
return file->filepos>=file->filesize; |
} |
/programs/develop/ktcc/trunk/libc/stdio/fflush.c |
---|
0,0 → 1,7 |
#include <stdio.h> |
int fflush(FILE* file) |
{ |
if ((file->mode & 3)==FILE_OPEN_READ) |
return 0; |
return(EOF); |
} |
/programs/develop/ktcc/trunk/libc/stdio/fgetc.c |
---|
0,0 → 1,22 |
#include <stdio.h> |
int fgetc(FILE* file) |
{ |
dword res; |
if ((file->mode & 3!=FILE_OPEN_READ) && (file->mode & FILE_OPEN_PLUS==0)) return EOF; |
if (file->filepos>=file->filesize) |
{ |
return EOF; |
} |
else |
{ |
res=_ksys_readfile(file->filename,file->filepos,1,file->buffer); |
if (res==0) |
{ |
file->filepos++; |
return (int)file->buffer[0]; |
} |
else return(res); |
} |
} |
/programs/develop/ktcc/trunk/libc/stdio/fgetpos.c |
---|
0,0 → 1,6 |
#include <stdio.h> |
int fgetpos(FILE* file,fpos_t* pos) |
{ |
*pos=file->filepos; |
return 0; |
} |
/programs/develop/ktcc/trunk/libc/stdio/fopen.c |
---|
0,0 → 1,138 |
#include <stdio.h> |
#include <string.h> |
#include <stdlib.h> |
extern char __argv; |
extern char __path; |
const char* getfullpath(const char *path){ |
int i,j,relpath_pos,localpath_size; |
int filename_size; |
char local_path; |
char *programpath; |
char *newpath; |
i=0; |
local_path=1; //enable local path |
while((*(path+i)!='\0') || (*(path+i)!=0)) |
{ |
if (*(path+i)=='.') |
{ |
if (*(path+i+1)=='/') |
{ //detected relative path |
relpath_pos=i+2; |
local_path=0; |
break; |
} |
} |
if (*(path+i)=='/') |
{ //disabple local path |
local_path=0; |
return(path); |
} |
i++; |
} |
filename_size=i; |
programpath=&__path; |
if (local_path==1) |
{ |
i=0x400; |
//find local path of program |
while(*(programpath+i)!='/') |
{ |
i--; |
} |
localpath_size=i; |
newpath=malloc(0x400); |
//copy local path to the new path |
for(i=0;i<=localpath_size;i++) |
{ |
*(newpath+i)=*(programpath+i); |
} |
//copy filename to the new path |
for(i=0;i<filename_size;i++) |
{ |
*(newpath+localpath_size+1+i)=*(path+i); |
} |
return(newpath); |
} |
//if we here than path is a relative |
i=0x400; |
//find local path of program |
while(*(programpath+i)!='/') |
{ |
i--; |
} |
localpath_size=i; |
i=0; |
//find file name size |
while((*(path+relpath_pos+i)!='\0') || (*(path+relpath_pos+i)!=0)) |
{ |
i++; |
} |
filename_size=i; |
newpath=malloc(0x400); |
//copy local path to the new path |
for(i=0;i<=localpath_size;i++) |
{ |
*(newpath+i)=*(programpath+i); |
} |
//copy filename to the new path |
for(i=0;i<filename_size;i++) |
{ |
*(newpath+localpath_size+1+i)=*(path+relpath_pos+i); |
} |
return(newpath); |
} |
FILE* fopen(const char* filename, const char *mode) |
{ |
FILE* res; |
int imode; |
imode=0; |
if (*mode=='r') |
{ |
imode=FILE_OPEN_READ; |
mode++; |
}else if (*mode=='w') |
{ |
imode=FILE_OPEN_WRITE; |
mode++; |
}else if (*mode=='a') |
{ |
imode=FILE_OPEN_APPEND; |
mode++; |
}else |
return 0; |
if (*mode=='t') |
{ |
imode|=FILE_OPEN_TEXT; |
mode++; |
}else if (*mode=='b') |
mode++; |
if (*mode=='+') |
{ |
imode|=FILE_OPEN_PLUS; |
mode++; |
} |
if (*mode!=0) |
return 0; |
res=malloc(sizeof(FILE)); |
res->buffer=malloc(256); |
res->buffersize=256; |
res->filesize=0; |
res->filepos=0; |
res->mode=imode; |
res->filename=getfullpath(filename); |
if ((imode==FILE_OPEN_READ) || (imode==FILE_OPEN_APPEND)) |
{ |
res->filesize=_ksys_get_filesize(res->filename); |
} |
return res; |
} |
/programs/develop/ktcc/trunk/libc/stdio/format_print.c |
---|
0,0 → 1,729 |
/* |
function for format output to the string |
*/ |
#include <kolibrisys.h> |
#include <string.h> |
#include <stdio.h> |
//#include <ctype.h> |
#include <math.h> |
int formatted_double_to_string(long double number,int format1,int format2,char *s) |
{ |
double n; |
double nbefor; |
double nafter; |
double v,v2; |
long intdigit; |
long beforpointdigit; |
long div; |
int i; |
int pos; |
int size; |
int fmt1; |
int fmt2; |
long mul; |
char buf[200]; |
size=(int)s; |
n=(double)number; |
if (n<0) {*s='-';s++;n=-n;} |
fmt1=format1; |
fmt2=format2; |
if (fmt2>18) {fmt2=18;} //maximum of size long long type |
//clear array befor output |
for(i=0;i<=200;i++) {buf[i]=0;} |
if ((fmt1>=0) && (n<1)) |
{ //formatted output if 0<=n<1 |
mul=1; |
for(i=0;i<fmt2;i++) |
{n=n*10;mul=mul*10;} |
n=n*10; |
n=ceil(n); |
intdigit=floor(n); |
//intdigit=n; |
intdigit=(intdigit/10); |
pos=0; |
mul=mul/10; |
for(i=0;i<fmt2-1;i++) |
{ |
div=intdigit/mul; |
buf[pos]=(char)div; |
pos++; |
intdigit=intdigit-div*mul; |
mul=mul/10; |
if (mul==1) break; |
} |
buf[pos]=(char)intdigit; |
*s='0';s++; |
*s='.';s++; |
for(i=0;i<format2;i++) |
{ |
if ((buf[i]>=0) && (buf[i]<=9)) {*s='0'+buf[i];} |
else {*s='0';} |
s++; |
} |
} |
else |
{ //if n>=1 |
//v=floorf(n+0.00000000000001); |
beforpointdigit=floor(n+0.00000000000001); |
//beforpointdigit=n; |
nbefor=beforpointdigit; |
nafter=n-nbefor; |
//print part of number befor point |
mul=1; |
for(i=0;i<200-2;i++) |
{ |
mul=mul*10; |
if ((beforpointdigit/mul)==0) {fmt1=i+1;break;} |
} |
pos=0; |
mul=mul/10; |
for(i=0;i<fmt1-1;i++) |
{ |
div=beforpointdigit/mul; |
buf[pos]=(char)div; |
pos++; |
beforpointdigit=beforpointdigit-div*mul; |
mul=mul/10; |
if (mul==1) break; |
} |
buf[pos]=(char)beforpointdigit; |
for(i=0;i<fmt1;i++) |
{ |
if ((buf[i]>=0) && (buf[i]<=9)) {*s='0'+buf[i];} |
s++; |
} |
//print part of number after point |
mul=1; |
for(i=0;i<fmt2;i++) |
{nafter=nafter*10;mul=mul*10;} |
nafter=nafter*10; |
nafter=ceil(nafter); |
intdigit=floor(nafter); |
//intdigit=nafter; |
intdigit=intdigit/10; |
pos=0; |
mul=mul/10; |
for(i=0;i<fmt2-1;i++) |
{ |
div=intdigit/mul; |
buf[pos]=(char)div; |
pos++; |
intdigit=intdigit-div*mul; |
mul=mul/10; |
if (mul==1) break; |
} |
buf[pos]=(char)intdigit; |
*s='.';s++; |
for(i=0;i<format2;i++) |
{ |
if ((buf[i]>=0) && (buf[i]<=9)) {*s='0'+buf[i];} |
else {*s='0';} |
s++; |
} |
} |
size=(int)s-size; |
return(size); |
} |
int formatted_long_to_string(long long number,int fmt1,char *s) |
{ |
int i; |
int pos; |
int fmt; |
int size; |
int difference_pos; |
long digit; |
long mul; |
long div; |
char buf[200]; |
//clear array befor output |
for(i=0;i<200;i++) {buf[i]=0;} |
digit=number; |
size=(int)s; |
if (digit<0) {*s='-';s++;digit=-digit;} |
if (digit==0) {*s='0';s++;goto end;} |
mul=1; |
for(i=0;i<200-2;i++) |
{ |
mul=mul*10; |
if ((digit/mul)==0) {fmt=i+1;break;} |
} |
difference_pos=i+1; |
pos=0; |
mul=mul/10; |
for(i=0;i<fmt-1;i++) |
{ |
div=digit/mul; |
buf[pos]=(char)div; |
pos++; |
digit=digit-div*mul; |
mul=mul/10; |
if (mul==1) break; |
} |
buf[pos]=(char)digit; |
if (fmt1>=difference_pos) fmt=fmt1; |
else |
fmt=difference_pos; |
for(i=0;i<fmt;i++) |
{ |
if (i<difference_pos) |
{ |
if ((buf[i]>=0) && (buf[i]<=9)) {*s='0'+buf[i];} |
} |
else |
{ |
*s=' '; |
} |
s++; |
} |
end: |
size=(int)s-size; |
return(size); |
} |
int formatted_hex_to_string(long long number,int fmt1,char flag_register,char *s) |
{ |
long n; |
int i,pos; |
int fmt; |
long size; |
int difference_pos; |
char xdigs_lower[16]="0123456789abcdef"; |
char xdigs_upper[16]="0123456789ABCDEF"; |
char buf[200]; |
n=(long)number; |
size=(int)s; |
if (n<0) {*s='-';s++;n=-n;} |
if (n==0) {*s='0';s++;goto end;} |
for(i=0;i<200;i++) {buf[i]=0;} |
i=0; |
if (flag_register==0) |
{ |
while (n>0) |
{ |
buf[i]=xdigs_lower[n & 15]; |
n=n>>4; |
i++; |
} |
} |
else |
{ |
while (n>0) |
{ |
buf[i]=xdigs_upper[n & 15]; |
n=n>>4; |
i++; |
} |
} |
pos=i; |
difference_pos=i; |
for(i=pos-1;i>=0;i--) |
{ |
*s=buf[i]; |
s++; |
} |
if (fmt1-difference_pos>0) |
{ |
for(i=difference_pos+1;i<=fmt1;i++) |
{ |
*s=' '; |
s++; |
} |
} |
end:size=(int)s-size; |
return(size); |
} |
int formatted_octa_to_string(long long number,int fmt1,char flag_register,char *s) |
{ |
long n; |
int i,pos; |
int fmt; |
long size; |
int difference_pos; |
char xdigs_lower[16]="012345678"; |
char buf[200]; |
n=number; |
size=(int)s; |
if (n<0) {*s='-';s++;n=-n;} |
if (n==0) {*s='0';s++;goto end;} |
for(i=0;i<200;i++) {buf[i]=0;} |
i=0; |
if (flag_register==0) |
{ |
while (n>0) |
{ |
buf[i]=xdigs_lower[n & 7]; |
n=n>>3; |
i++; |
} |
} |
pos=i; |
difference_pos=i; |
for(i=pos-1;i>=0;i--) |
{ |
*s=buf[i]; |
s++; |
} |
if (fmt1-difference_pos>0) |
{ |
for(i=difference_pos+1;i<=fmt1;i++) |
{ |
*s=' '; |
s++; |
} |
} |
end:size=(int)s-size; |
return(size); |
} |
int format_print(char *dest, size_t maxlen,const char *fmt0, va_list argp) |
{ |
int i,j,k; |
int length; |
int fmt1,fmt2,stepen; |
size_t pos,posc; |
long long intdigit; |
long double doubledigit; |
float floatdigit; |
const char *fmt,*fmtc; |
char *s; |
char *str; |
char buffmt1[30]; |
char buffmt2[30]; |
char buf[1024]; |
char format_flag; |
char flag_point; |
char flag_noformat; |
char flag_long; |
char flag_unsigned; |
char flag_register; |
char flag_plus; |
fmt=fmt0; |
s=dest; |
pos=0; |
while(pos<maxlen) |
{ |
if (*fmt=='%') |
{ |
if (*(fmt+1)=='%') |
{ |
*s='%'; |
s++; |
fmt=fmt+2; |
pos++; |
goto exit_check; |
} |
//checking to containg format in the string |
fmtc=fmt; |
posc=pos; |
format_flag=0; |
flag_long=0; |
flag_unsigned=0; |
flag_register=0; |
flag_plus=0; |
while((*fmtc!='\0') || (*fmtc!=0)) |
{ |
fmtc++; |
posc++; |
switch(*fmtc) |
{ |
case 'c': |
case 'C': |
format_flag=1; |
break; |
case 'd': |
case 'D': |
case 'i': |
case 'I': |
format_flag=1; |
break; |
case 'e': |
format_flag=1; |
break; |
case 'E': |
format_flag=1; |
flag_long=1; |
break; |
case 'f': |
format_flag=1; |
break; |
case 'F': |
format_flag=1; |
flag_long=1; |
break; |
case 'g': |
format_flag=1; |
break; |
case 'G': |
format_flag=1; |
flag_long=1; |
break; |
case 'l': |
flag_long=1; |
break; |
case 'L': |
flag_long=2; |
break; |
case 'o': |
format_flag=1; |
break; |
case 's': |
case 'S': |
format_flag=1; |
break; |
case 'u': |
case 'U': |
format_flag=1; |
flag_unsigned=1; |
break; |
case 'x': |
format_flag=1; |
break; |
case 'X': |
flag_register=1; |
format_flag=1; |
break; |
case 'z': |
case 'Z': |
format_flag=1; |
flag_unsigned=1; |
break; |
case '+': |
flag_plus=1; |
break; |
default:; |
} |
if ((*fmtc=='%') || (*fmtc==' ')) break; |
if (format_flag==1) break; |
} |
if (format_flag==0) |
{ |
*s=*fmt; |
fmt++; |
s++; |
pos++; |
} |
else |
{ |
if ((posc-pos)==1) |
{//simbols % and format simbol near tothere(for example %c ) |
fmt=fmtc+1; |
switch(*fmtc) |
{ |
case 'c': |
case 'C': |
if ((pos+1)<maxlen) |
{ |
//*s=(int)va_arg(argp,char*); |
*s=*((char *)argp); |
argp=argp+4; |
*s++;pos++; |
} |
break; |
case 's': |
case 'S': |
str=va_arg(argp,char*); |
length=strlen(str); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,str,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'd': |
case 'D': |
case 'i': |
case 'I': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
//intdigit=*((long*)argp); |
//argp=argp+4; |
if ((intdigit>0) && (flag_plus==1) && (pos+1<maxlen)) |
{ |
*s='+'; |
s++; |
pos++; |
} |
length=formatted_long_to_string(intdigit,0,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'o': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
//intdigit=*((long int *)argp); |
//argp=argp+4; |
length=formatted_octa_to_string(intdigit,0,flag_register,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'u': |
case 'U': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long int);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
if (flag_unsigned==1) { |
if (intdigit<0) {intdigit=-intdigit;} |
} |
length=formatted_long_to_string(intdigit,0,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'x': |
case 'X': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
//intdigit=*((long int *)argp); |
//argp=argp+4; |
length=formatted_hex_to_string(intdigit,0,flag_register,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'z': |
case 'Z': |
intdigit=va_arg(argp,size_t); |
if (flag_unsigned==1) { |
if (intdigit<0) {intdigit=-intdigit;} |
} |
length=formatted_long_to_string(intdigit,0,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
default:; |
} |
} |
else |
{ |
fmt++; |
flag_point=0; |
flag_noformat=0; |
fmt1=0; |
fmt2=0; |
j=0; |
k=0; |
for(i=pos+1;i<posc;i++) |
{ |
switch(*fmt) |
{ |
case '0': |
case '1': |
case '2': |
case '3': |
case '4': |
case '5': |
case '6': |
case '7': |
case '8': |
case '9': |
if (flag_point==0) |
{ |
buffmt1[j]=*fmt-'0'; |
j++; |
} |
else |
{ |
buffmt2[k]=*fmt-'0'; |
k++; |
} |
break; |
case '.': |
flag_point=1; |
break; |
case 'l': |
case 'L': |
break; |
case '+': |
break; |
default:flag_noformat=1; |
} |
if (flag_noformat==1) break; |
fmt++; |
} |
if (flag_noformat==0) |
{ |
stepen=1; |
for(i=j-1;i>=0;i--) |
{ |
fmt1=fmt1+buffmt1[i]*stepen; |
stepen=stepen*10; |
} |
stepen=1; |
for(i=k-1;i>=0;i--) |
{ |
fmt2=fmt2+buffmt2[i]*stepen; |
stepen=stepen*10; |
} |
switch(*fmtc) |
{ |
case 'f': |
case 'F': |
if (flag_long==0) {doubledigit=va_arg(argp,double);} |
if (flag_long>=1) {doubledigit=va_arg(argp,long double);} |
//doubledigit=*((double *)argp); |
//sargp=argp+8; |
length=formatted_double_to_string(doubledigit,fmt1,fmt2,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'd': |
case 'D': |
case 'i': |
case 'I': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
if ((intdigit>0) && (flag_plus==1) && (pos+1<maxlen)) |
{ |
*s='+'; |
s++; |
pos++; |
} |
length=formatted_long_to_string(intdigit,fmt1,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'o': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
length=formatted_octa_to_string(intdigit,fmt1,flag_register,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'u': |
case 'U': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long int);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
if (flag_unsigned==1) { |
if (intdigit<0) {intdigit=-intdigit;} |
} |
length=formatted_long_to_string(intdigit,fmt1,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'x': |
case 'X': |
if (flag_long==0) {intdigit=va_arg(argp,int);} |
if (flag_long==1) {intdigit=va_arg(argp,long int);} |
if (flag_long==2) {intdigit=va_arg(argp,long long);} |
length=formatted_hex_to_string(intdigit,fmt1,flag_register,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
case 'z': |
case 'Z': |
intdigit=va_arg(argp,size_t); |
if (flag_unsigned==1) { |
if (intdigit<0) {intdigit=-intdigit;} |
} |
length=formatted_long_to_string(intdigit,fmt1,buf); |
if ((pos+length)<maxlen) |
{ |
memcpy(s,buf,length); |
s=s+length;pos=pos+length; |
} |
break; |
default:; |
} |
} |
fmt=fmtc+1; |
} |
} |
} |
else |
{ |
if (*fmt=='\0') {break;} |
*s=*fmt; |
fmt++; |
s++; |
pos++; |
} |
exit_check:; |
} |
return(pos); |
} |
/programs/develop/ktcc/trunk/libc/stdio/fprintf.c |
---|
0,0 → 1,21 |
#include <stdio.h> |
int format_print(char *dest, size_t maxlen, const char *fmt,va_list argp); |
int fprintf(FILE* file, const char* format, ...) |
{ |
va_list arg; |
char *buf; |
int printed; |
//int data[4]; |
va_start (arg, format); |
buf=malloc(4096*2); //8kb max |
//data[0]=(int)&arg-(int)&format; |
printed=format_print(buf,8191, format,arg); |
fwrite(buf,printed,1,file); |
free(buf); |
return(printed); |
} |
/programs/develop/ktcc/trunk/libc/stdio/fputc.c |
---|
0,0 → 1,35 |
#include <stdio.h> |
int fputc(int c,FILE* file) |
{ |
dword res; |
if ((file->mode & 3)==FILE_OPEN_READ) return EOF; |
file->buffer[0]=c; |
if ((file->mode & 3)==FILE_OPEN_APPEND) |
{ |
file->filepos=file->filesize; |
file->filesize++; |
res=_ksys_appendtofile(file->filename,file->filepos,1,file->buffer); |
if (res!=0) return(res); |
file->filepos++; |
return(0); |
} |
if ((file->mode & 3)==FILE_OPEN_WRITE) |
{ |
if (file->filepos==0) |
{ //file not craeted |
res=_ksys_rewritefile(file->filename,1,file->buffer); |
if (res!=0) return(res); |
file->filepos++; |
return 0; |
} |
else |
{ //file craeted and need append one byte |
res=_ksys_appendtofile(file->filename,file->filepos,1,file->buffer); |
if (res!=0) return(res); |
file->filepos++; |
return 0; |
} |
} |
} |
/programs/develop/ktcc/trunk/libc/stdio/fread.c |
---|
0,0 → 1,26 |
#include <stdio.h> |
#include <kolibrisys.h> |
int fread(void *buffer,int size,int count,FILE* file) |
{ |
dword res; |
dword fullsize; |
if ((file->mode!=FILE_OPEN_READ) || (file->mode==FILE_OPEN_PLUS)) return 0; |
fullsize=count*size; |
if ((fullsize+file->filepos)>(file->filesize)) |
{ |
fullsize=file->filesize-file->filepos; |
if (fullsize<=0) return(0); |
} |
res=_ksys_readfile(file->filename,file->filepos,fullsize,buffer); |
if (res==0) |
{ |
file->filepos=file->filepos+fullsize; |
fullsize=fullsize/size; |
return(fullsize); |
} |
else return 0; |
} |
/programs/develop/ktcc/trunk/libc/stdio/fscanf.c |
---|
0,0 → 1,188 |
#include <stdio.h> |
void skipspaces(FILE* file) |
{ |
int c; |
while(1) |
{ |
c=getc(file); |
if (c!=' ' && c!='\r' && c!='\n') |
{ |
ungetc(c,file); |
return; |
} |
} |
} |
int fscanf(FILE* file,const char* format, ...) |
{ |
int res; |
void* arg; |
int i; |
int c; |
int contflag; |
int longflag; |
int sign; |
long long number; |
long double rnumber; |
char* str; |
res=0; |
arg=&format; |
arg+=sizeof(const char*); |
while (*format!='\0') |
{ |
if (*format!='%') |
{ |
c=fgetc(file); |
if (c!=*format) |
{ |
fungetc(c,file); |
return -1; |
} |
format++; |
continue; |
} |
contflag=1; |
longflag=0; |
while (*format && contflag) |
{ |
switch(*format) |
{ |
case '.': |
case '0': |
case '1': |
case '2': |
case '3': |
case '4': |
case '5': |
case '6': |
case '7': |
case '8': |
case '9': |
format++; |
continue; |
break; |
case 'l': |
if (longflag==0) |
longflag=1; |
else |
longflag=2; |
format++; |
break; |
case 'L': |
longflag=2; |
format++; |
break; |
case 'f': |
case 'd': |
case 'c': |
case 's': |
case '%': |
contflag=0; |
break; |
default: |
contflag=0; |
} |
} |
if (contflag) |
break; |
switch(*format) |
{ |
case '%': |
c=fgetc(file); |
if (c!='%') |
{ |
ungetc(c,file); |
return -1; |
} |
res--; |
break; |
case 'd': |
number=0; |
sign=1; |
skipspaces(file); |
c=fgetc(file); |
if (c=='-') |
{ |
sign=-1; |
}else if (c!='+') |
ungetc(c,file); |
contflag=0; |
while(1) |
{ |
c=fgetc(file); |
if (c>='0' && c<='9') |
{ |
contflag++; |
number=number*10+(c-'0'); |
}else |
break; |
} |
ungetc(c,file); |
if (!contflag) |
return res; |
if (longflag<=1) |
{ |
*((int*)arg)=number; |
arg+=sizeof(int); |
}else |
{ |
*((long long*)arg)=number; |
arg+=sizeof(long long); |
} |
break; |
case 'c': |
c=fgetc(file); |
if (c==EOF) |
return res; |
*((char*)arg)=c; |
arg+=sizeof(char); |
break; |
case 's': |
skipspaces(file); |
contflag=0; |
str=*((char**)arg); |
arg+=sizeof(char*); |
while(1) |
{ |
c=fgetc(file); |
if (c==EOF || c==' ' || c=='\n' || c=='\r') |
{ |
ungetc(c,file); |
break; |
} |
*str=c; |
str++; |
contflag++; |
} |
if (!contflag) |
return res; |
break; |
case 'f': |
skipspaces(file); |
// TODO: read real numbers |
rnumber=0; |
switch (longflag) |
{ |
case 0: |
*((float*)arg)=rnumber; |
arg+=sizeof(float); |
break; |
case 1: |
*((double*)arg)=rnumber; |
arg+=sizeof(double); |
break; |
case 2: |
*((long double*)arg)=rnumber; |
arg+=sizeof(long double); |
break; |
default: |
return res; |
} |
break; |
default: |
break; |
} |
format++; |
res++; |
} |
return res; |
} |
/programs/develop/ktcc/trunk/libc/stdio/fseek.c |
---|
0,0 → 1,11 |
#include <stdio.h> |
int fseek(FILE* file,long offset,int origin) |
{ |
if (origin==SEEK_CUR) |
offset+=file->filepos; |
else if (origin==SEEK_END) |
offset+=file->filesize; |
else if (origin!=SEEK_SET) |
return EOF; |
return fsetpos(file,offset); |
} |
/programs/develop/ktcc/trunk/libc/stdio/fsetpos.c |
---|
0,0 → 1,11 |
#include <stdio.h> |
int fsetpos(FILE* file,const fpos_t * pos) |
{ |
if (*pos>=0) |
{ |
file->filepos=*pos; |
return 0; |
} |
else |
return EOF; |
} |
/programs/develop/ktcc/trunk/libc/stdio/ftell.c |
---|
0,0 → 1,5 |
#include <stdio.h> |
long ftell(FILE* file) |
{ |
return file->filepos; |
} |
/programs/develop/ktcc/trunk/libc/stdio/fwrite.c |
---|
0,0 → 1,58 |
#include <stdio.h> |
#include <kolibrisys.h> |
int fwrite(void *buffer,int size,int count,FILE* file) |
{ |
dword res; |
dword fullsize; |
if (file->mode==FILE_OPEN_READ) return 0; |
if (file->mode==FILE_OPEN_APPEND) |
file->filepos=file->filesize; |
fullsize=count*size; |
if ((file->filesize)<(file->filepos+fullsize)) file->filesize=file->filepos+fullsize; |
/* |
if (file->mode==FILE_OPEN_APPEND) |
{ |
file->filepos==file->filesize; |
res=_ksys_appendtofile(file->filename,file->filepos,fullsize,buffer); |
if (res==0) |
{ |
file->filepos+=fullsize; |
fullsize=fullsize/size; |
return(fullsize); |
} |
else return(0); |
} |
*/ |
if ((file->mode==FILE_OPEN_WRITE) || (file->mode==FILE_OPEN_APPEND)) |
{ |
if (file->filepos==0) |
{ //file mot craeted yet |
res=_ksys_rewritefile(file->filename,fullsize,buffer); |
if (res==0) |
{ |
file->filepos+=fullsize; |
fullsize=fullsize/count; |
return(fullsize); |
} |
else return(0); |
} |
else |
{ |
res=_ksys_appendtofile(file->filename,file->filepos,fullsize,buffer); |
if (res==0) |
{ |
file->filepos+=fullsize; |
fullsize=fullsize/count; |
return(fullsize); |
} |
else return(0); |
} |
} |
else return(0); |
} |
/programs/develop/ktcc/trunk/libc/stdio/printf.c |
---|
0,0 → 1,75 |
#include <stdio.h> |
#include <string.h> |
#include <kolibrisys.h> |
char* dllname="/sys/lib/console.obj"; |
int console_init_status; |
char* imports[] = {"START","version","con_init","con_write_asciiz","con_printf","con_exit",NULL}; |
char* caption = "Console test - colors"; |
dword* dll_ver; |
void stdcall (* con_init)(dword wnd_width, dword wnd_height, dword scr_width, dword scr_height, const char* title); |
void stdcall (* con_write_asciiz)(const char* string); |
void cdecl (* con_printf)(const char* format,...); |
void stdcall (* con_exit)(dword bCloseWindow); |
struct import{ |
char *name; |
void *data; |
}; |
void printf_link(struct import *exp, char** imports){ |
dll_ver = (dword*) |
_ksys_cofflib_getproc(exp, imports[1]); |
con_init = (void stdcall (*)(dword , dword, dword, dword, const char*)) |
_ksys_cofflib_getproc(exp, imports[2]); |
con_printf = (void cdecl (*)(const char*,...)) |
_ksys_cofflib_getproc(exp, imports[4]); |
con_exit = (void stdcall (*)(dword)) |
_ksys_cofflib_getproc(exp, imports[5]); |
} |
int init_console(void) |
{ |
struct import * hDll; |
if((hDll = (struct import *)_ksys_cofflib_load(dllname)) == 0){ |
debug_out_str("can't load lib\n"); |
return 1; |
} |
printf_link(hDll, imports); |
debug_out_str("dll loaded\n"); |
con_init(-1, -1, -1, -1, caption); |
return(0); |
} |
int printf(const char *format,...) |
{ |
int i; |
int printed_simbols; |
va_list arg; |
char simbol[]={"%s"}; |
char *s; |
va_start(arg,format); |
if (console_init_status==0) |
{ |
i=init_console(); |
console_init_status=1; |
} |
if (i==0) |
{ |
s=malloc(4096); |
printed_simbols=format_print(s,4096,format,arg); |
con_printf(simbol,s); |
free(s); |
} |
return(printed_simbols); |
} |
/programs/develop/ktcc/trunk/libc/stdio/rewind.c |
---|
0,0 → 1,5 |
#include <stdio.h> |
void rewind(FILE* file) |
{ |
file->filepos=0; |
} |
/programs/develop/ktcc/trunk/libc/stdio/snprintf.c |
---|
0,0 → 1,15 |
#include <kolibrisys.h> |
#include <stdlib.h> |
#include <stdio.h> |
int format_print(char *dest, size_t maxlen, const char *fmt,va_list argp); |
int snprintf(char *dest, size_t size,const char *format,...) |
{ |
va_list arg; |
va_start (arg, format); |
return format_print(dest,size, format, arg); |
} |
/programs/develop/ktcc/trunk/libc/stdio/sprintf.c |
---|
0,0 → 1,15 |
#include <kolibrisys.h> |
#include <stdlib.h> |
#include <stdio.h> |
int format_print(char *dest, size_t maxlen, const char *fmt,va_list argp); |
int sprintf(char *dest,const char *format,...) |
{ |
va_list arg; |
va_start (arg, format); |
return format_print(dest,4096, format, arg); |
} |
/programs/develop/ktcc/trunk/libc/stdio/vsnprintf.c |
---|
0,0 → 1,15 |
#include <kolibrisys.h> |
#include <stdlib.h> |
#include <stdio.h> |
int format_print(char *dest, size_t maxlen, const char *fmt, |
va_list argp); |
int vsnprintf(char *dest, size_t size,const char *format,va_list ap) |
{ |
return format_print(dest,size, format, ap); |
} |
/programs/develop/ktcc/trunk/libc/stdlib/atoi.c |
---|
0,0 → 1,21 |
#include "stdio.h" |
#include "stdlib.h" |
#include "ctype.h" |
/* |
** atoi(s) - convert s to integer. |
*/ |
int atoi(char *s) |
{ |
int sign, n; |
while(isspace(*s)) ++s; |
sign = 1; |
switch(*s) { |
case '-': sign = -1; |
case '+': ++s; |
} |
n = 0; |
while(isdigit(*s)) n = 10 * n + *s++ - '0'; |
return (sign * n); |
} |
/programs/develop/ktcc/trunk/libc/stdlib/atoib.cpp |
---|
0,0 → 1,22 |
#include "stdio.h" |
#include "stdlib.h" |
#include "ctype.h" |
/* |
** atoib(s,b) - Convert s to "unsigned" integer in base b. |
** NOTE: This is a non-standard function. |
*/ |
int atoib(char *s,int b) |
{ |
int n, digit; |
n = 0; |
while(isspace(*s)) ++s; |
while((digit = (127 & *s++)) >= '0') { |
if(digit >= 'a') digit -= 87; |
else if(digit >= 'A') digit -= 55; |
else digit -= '0'; |
if(digit >= b) break; |
n = b * n + digit; |
} |
return (n); |
} |
/programs/develop/ktcc/trunk/libc/stdlib/itoa.c |
---|
0,0 → 1,21 |
#include "stdio.h" |
#include "stdlib.h" |
#include "ctype.h" |
/* |
** itoa(n,s) - Convert n to characters in s |
*/ |
void itoa(int n,char* s) |
{ |
int sign; |
char *ptr; |
ptr = s; |
if ((sign = n) < 0) n = -n; |
do { |
*ptr++ = n % 10 + '0'; |
} while ((n = n / 10) > 0); |
if (sign < 0) *ptr++ = '-'; |
*ptr = '\0'; |
reverse(s); |
} |
/programs/develop/ktcc/trunk/libc/stdlib/itoab.c |
---|
0,0 → 1,24 |
#include "stdio.h" |
#include "stdlib.h" |
#include "ctype.h" |
/* |
** itoab(n,s,b) - Convert "unsigned" n to characters in s using base b. |
** NOTE: This is a non-standard function. |
*/ |
void itoab(int n,char* s,int b) |
{ |
char *ptr; |
int lowbit; |
ptr = s; |
b >>= 1; |
do { |
lowbit = n & 1; |
n = (n >> 1) & 32767; |
*ptr = ((n % b) << 1) + lowbit; |
if(*ptr < 10) *ptr += '0'; else *ptr += 55; |
++ptr; |
} while(n /= b); |
*ptr = 0; |
reverse (s); |
} |
/programs/develop/ktcc/trunk/libc/stdlib/tolower.c |
---|
0,0 → 1,8 |
/* |
** return lower-case of c if upper-case, else c |
*/ |
unsigned char tolower(unsigned char c) |
{ |
if(c<='Z' && c>='A') return (c+32); |
return (c); |
} |
/programs/develop/ktcc/trunk/libc/stdlib/toupper.c |
---|
0,0 → 1,8 |
/* |
** return upper-case of c if it is lower-case, else c |
*/ |
unsigned char toupper(unsigned char c) |
{ |
if(c<='z' && c>='a') return (c-32); |
return (c); |
} |
/programs/develop/ktcc/trunk/libc/string/memmove.asm |
---|
0,0 → 1,35 |
format ELF |
section '.text' executable |
include 'proc32.inc' |
public memcpy |
public memmove |
proc memcpy stdcall, to:dword,from:dword,count:dword |
mov ecx,[count] |
test ecx,ecx |
jz no_copy_block |
mov esi,[from] |
mov edi,[to] |
rep movsb |
no_copy_block: |
ret |
endp |
proc memmove stdcall, to:dword,from:dword,count:dword |
mov ecx,[count] |
test ecx,ecx |
jz no_copy_block_ |
mov esi,[from] |
mov edi,[to] |
rep movsb |
no_copy_block_: |
ret |
endp |
/programs/develop/ktcc/trunk/libc/string/is.c |
---|
0,0 → 1,19 |
#include "ctype.h" |
int __is[128] = { |
0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, |
0x004, 0x104, 0x104, 0x104, 0x104, 0x104, 0x004, 0x004, |
0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, |
0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, |
0x140, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, |
0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, |
0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, |
0x459, 0x459, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, |
0x0D0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253, |
0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, |
0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, |
0x253, 0x253, 0x253, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, |
0x0D0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073, |
0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, |
0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, |
0x073, 0x073, 0x073, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x004 |
}; |
/programs/develop/ktcc/trunk/libc/string/strlen.c |
---|
0,0 → 1,7 |
int strlen(const char* string) |
{ |
int i; |
i=0; |
while (*string++) i++; |
return i; |
} |
/programs/develop/ktcc/trunk/libc/string/strcat.c |
---|
0,0 → 1,8 |
char* strcat(char* strDest, const char* strSource) |
{ |
char* res; |
res=strDest; |
while (*strDest++) ; |
while (*strDest++ = *strSource++) ; |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/strchr.c |
---|
0,0 → 1,10 |
char* strchr(const char* string, int c) |
{ |
while (*string) |
{ |
if (*string==c) |
return (char*)string; |
string++; |
} |
return (char*)0; |
} |
/programs/develop/ktcc/trunk/libc/string/strcpy.c |
---|
0,0 → 1,7 |
char* strcpy(char* strDest,char* strSource) |
{ |
char* res; |
res=strDest; |
while(*strDest++ == strSource++) ; |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/strncat.c |
---|
0,0 → 1,13 |
char* strncat(char* strDest,const char* strSource,int count) |
{ |
char* res; |
res=strDest; |
while (*strDest++) ; |
while(count-->0) |
{ |
if(*strDest++ = *strSource++) continue; |
return(res); |
} |
*strDest = 0; |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/strncmp.c |
---|
0,0 → 1,12 |
int strncmp(const char* string1, const char* string2, int count) |
{ |
while(count>0 && *string1==*string2) |
{ |
if (*string1) return 0; |
++string1; |
++string2; |
--count; |
} |
if(count) return (*string1 - *string2); |
return 0; |
} |
/programs/develop/ktcc/trunk/libc/string/strncpy.c |
---|
0,0 → 1,14 |
char* strncpy(char* strDest,const char* strSource,int count) |
{ |
char* res; |
res=strDest; |
while (count>0) |
{ |
*strDest=*strSource; |
if (*strSource!='\0') |
strSource++; |
strDest++; |
count--; |
} |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/memchr.c |
---|
0,0 → 1,10 |
void* memchr(const void* buf,int c,int count) |
{ |
int i; |
for (i=0;i<count;i++) |
if (*(char*)buf==c) |
return (void*)buf; |
else |
buf++; |
return (void*)0; |
} |
/programs/develop/ktcc/trunk/libc/string/memcmp.c |
---|
0,0 → 1,13 |
typedef unsigned char uc; |
int memcmp(const void* buf1,const void* buf2,int count) |
{ |
int i; |
for (i=0;i<count;i++) |
{ |
if (*(uc*)buf1<*(uc*)buf2) |
return -1; |
if (*(uc*)buf1>*(uc*)buf2) |
return 1; |
} |
return 0; |
} |
/programs/develop/ktcc/trunk/libc/string/memset.asm |
---|
0,0 → 1,15 |
format ELF |
section '.text' executable |
public memset |
memset: |
push edi |
mov edi,[esp+8] |
mov eax,[esp+12] |
mov ecx,[esp+16] |
jecxz .no_set |
cld |
rep stosb |
.no_set: |
pop edi |
ret |
/programs/develop/ktcc/trunk/libc/string/strcmp.c |
---|
0,0 → 1,14 |
int strcmp(const char* string1, const char* string2) |
{ |
while (1) |
{ |
if (*string1<*string2) |
return -1; |
if (*string1>*string2) |
return 1; |
if (*string1=='\0') |
return 0; |
string1++; |
string2++; |
} |
} |
/programs/develop/ktcc/trunk/libc/string/strcoll.c |
---|
0,0 → 1,4 |
int strcoll(const char* string1,const char* string2) |
{ |
return strcmp(string1,string2); |
} |
/programs/develop/ktcc/trunk/libc/string/strcspn.c |
---|
0,0 → 1,17 |
int strcspn(const char* string, const char* strCharSet) |
{ |
const char* temp; |
int i; |
i=0; |
while(1) |
{ |
temp=strCharSet; |
while (*temp!='\0') |
{ |
if (*string==*temp) |
return i; |
temp++; |
} |
i++;string++; |
} |
} |
/programs/develop/ktcc/trunk/libc/string/strdup.c |
---|
0,0 → 1,9 |
char* strdup(char* str) |
{ |
char* res; |
int len; |
len=strlen(str)+1; |
res=malloc(len); |
memcpy(res,str,len); |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/strerror.c |
---|
0,0 → 1,4 |
char* strerror(int err) |
{ |
return (char*)0; |
} |
/programs/develop/ktcc/trunk/libc/string/strnbrk.c |
---|
0,0 → 1,16 |
char* strpbrk(const char* string, const char* strCharSet) |
{ |
char* temp; |
while (*string!='\0') |
{ |
temp=strCharSet; |
while (*temp!='\0') |
{ |
if (*string==*temp) |
return string; |
temp++; |
} |
string++; |
} |
return (char*)0; |
} |
/programs/develop/ktcc/trunk/libc/string/strrchr.c |
---|
0,0 → 1,14 |
char* strrchr(const char* s,int c) |
{ |
char* res; |
res=(char*)0; |
while (1) |
{ |
if (*s==(char)c) |
res=(char*)s; |
if (*s=='\0') |
break; |
s++; |
} |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/strspn.c |
---|
0,0 → 1,20 |
int strspn(const char* string,const char* strCharSet) |
{ |
int i; |
const char* temp; |
i=0; |
while (*string!='\0') |
{ |
temp=strCharSet; |
while (temp!='\0') |
{ |
if (*temp==*string) |
break; |
} |
if (temp=='\0') |
break; |
*string++; |
i++; |
} |
return i; |
} |
/programs/develop/ktcc/trunk/libc/string/strstr.c |
---|
0,0 → 1,13 |
extern int strncmp(char* s1,char* s2,int len); |
char* strstr(const char* s, const char* find) |
{ |
int len; |
len=strlen(find); |
while (1) |
{ |
if (strncmp(s,find,len)==0) return s; |
if (*s=='\0') |
return (char*) 0; |
s++; |
} |
} |
/programs/develop/ktcc/trunk/libc/string/strtok.c |
---|
0,0 → 1,14 |
#include "string.h" |
char* strtok(char* s,const char* delim) |
{ |
char* res; |
if (*s=='\0') |
return (char*)0; |
s+=strspn(s,delim); |
if (*s=='\0') |
return (char*)0; |
res=s; |
s+=strcspn(s,delim); |
*s=='\0'; |
return res; |
} |
/programs/develop/ktcc/trunk/libc/string/strxfrm.c |
---|
0,0 → 1,4 |
int strxfrm(char* strDest, const char* strSource, int count) |
{ |
return 0; |
} |
/programs/develop/ktcc/trunk/libc/string |
---|
Property changes: |
Added: svn:ignore |
+*.o |
+*.s |
/programs/develop/ktcc/trunk/libc/start/start.asm |
---|
0,0 → 1,135 |
format ELF |
section '.text' executable |
public start |
extrn mf_init |
extrn main |
public argc as '__ARGS' |
__DEBUG__ equ 1 |
__DEBUG_LEVEL__ equ 1 |
include 'DEBUG-FDO.INC' |
virtual at 0 |
db 'MENUET01' ; 1. Magic number (8 bytes) |
dd 0x01 ; 2. Version of executable file |
dd 0x0 ; 3. Start address |
dd 0x0 ; 4. Size of image |
dd 0x100000 ; 5. Size of needed memory |
dd 0x100000 ; 6. Pointer to stack |
hparams dd 0x0 ; 7. Pointer to program arguments |
hpath dd 0x0 ; 8. Pointer to program path |
end virtual |
start: |
DEBUGF 1,'Start programm\n' |
xor eax,eax |
call mf_init |
DEBUGF 1,' path "%s"\n params "%s"\n', path, params |
; check for overflow |
mov al, [path+buf_len-1] |
or al, [params+buf_len-1] |
jnz .crash |
; check if path written by OS |
mov eax, [hparams] |
test eax, eax |
jz .without_path |
mov eax, path |
.without_path: |
mov esi, eax |
call push_param |
; retrieving parameters |
mov esi, params |
xor edx, edx ; dl - èä¸ò ïàðàìåòð(1) èëè ðàçäåëèòåëè(0) |
; dh - ñèìâîë ñ êîòîðîãî íà÷àëñÿ ïàðàìåòð (1 êàâû÷êè, 0 îñòàëüíîå) |
mov ecx, 1 ; cl = 1 |
; ch = 0 ïðîñòî íîëü |
.parse: |
lodsb |
test al, al |
jz .run |
test dl, dl |
jnz .findendparam |
;{åñëè áûë ðàçäåëèòåëü |
cmp al, ' ' |
jz .parse ;çàãðóæåí ïðîáåë, ãðóçèì ñëåäóþùèé ñèìâîë |
mov dl, cl ;íà÷èíàåòñÿ ïàðàìåòð |
cmp al, '"' |
jz @f ;çàãðóæåíû êàâû÷êè |
mov dh, ch ;ïàðàìåòð áåç êàâû÷åê |
dec esi |
call push_param |
inc esi |
jmp .parse |
@@: |
mov dh, cl ;ïàðàìåòð â êàâû÷åêàõ |
call push_param ;åñëè íå ïðîáåë çíà÷èò íà÷èíàåòñÿ êàêîé òî ïàðàìåòð |
jmp .parse ;åñëè áûë ðàçäåëèòåëü} |
.findendparam: |
test dh, dh |
jz @f ; áåç êàâû÷åê |
cmp al, '"' |
jz .clear |
jmp .parse |
@@: |
cmp al, ' ' |
jnz .parse |
.clear: |
lea ebx, [esi - 1] |
mov [ebx], ch |
mov dl, ch |
jmp .parse |
.run: |
DEBUGF 1,'call main(%x, %x) with params:\n', [argc], argv |
if __DEBUG__ = 1 |
mov ecx, [argc] |
@@: |
lea esi, [ecx * 4 + argv-4] |
DEBUGF 1,'%d) "%s"\n', cx, [esi] |
loop @b |
end if |
push argv |
push [argc] |
call main |
.exit: |
DEBUGF 1,'Exit from prog with code: %x\n', eax; |
xor eax,eax |
dec eax |
int 0x40 |
dd -1 |
.crash: |
DEBUGF 1,'E:buffer overflowed\n' |
jmp .exit |
;============================ |
push_param: |
;============================ |
;parameters |
; esi - pointer |
;description |
; procedure increase argc |
; and add pointer to array argv |
; procedure changes ebx |
mov ebx, [argc] |
cmp ebx, max_parameters |
jae .dont_add |
mov [argv+4*ebx], esi |
inc [argc] |
.dont_add: |
ret |
;============================== |
public params as '__argv' |
public path as '__path' |
section '.bss' |
buf_len = 0x400 |
max_parameters=0x20 |
argc rd 1 |
argv rd max_parameters |
path rb buf_len |
params rb buf_len |
section '.data' |
include_debug_strings ; ALWAYS present in data section |
/programs/develop/ktcc/trunk/libc/start/debug-fdo.inc |
---|
0,0 → 1,422 |
; |
; Formatted Debug Output (FDO) |
; Copyright (c) 2005-2006, mike.dld |
; Created: 2005-01-29, Changed: 2006-11-10 |
; |
; For questions and bug reports, mail to mike.dld@gmail.com |
; |
; Available format specifiers are: %s, %d, %u, %x (with partial width support) |
; |
; to be defined: |
; __DEBUG__ equ 1 |
; __DEBUG_LEVEL__ equ 5 |
macro debug_func name { |
if used name |
name@of@func equ name |
} |
macro debug_beginf { |
align 4 |
name@of@func: |
} |
debug_endf fix end if |
macro DEBUGS _sign,[_str] { |
common |
local tp |
tp equ 0 |
match _arg:_num,_str \{ |
DEBUGS_N _sign,_num,_arg |
tp equ 1 |
\} |
match =0 _arg,tp _str \{ |
DEBUGS_N _sign,,_arg |
\} |
} |
macro DEBUGS_N _sign,_num,[_str] { |
common |
pushf |
pushad |
local ..str,..label,is_str |
is_str = 0 |
forward |
if _str eqtype '' |
is_str = 1 |
end if |
common |
if is_str = 1 |
jmp ..label |
..str db _str,0 |
..label: |
add esp,4*8+4 |
mov edx,..str |
sub esp,4*8+4 |
else |
mov edx,_str |
end if |
if ~_num eq |
if _num eqtype eax |
if _num in <eax,ebx,ecx,edx,edi,ebp,esp> |
mov esi,_num |
else if ~_num eq esi |
movzx esi,_num |
end if |
else if _num eqtype 0 |
mov esi,_num |
else |
local tp |
tp equ 0 |
match [_arg],_num \{ |
mov esi,dword[_arg] |
tp equ 1 |
\} |
match =0 =dword[_arg],tp _num \{ |
mov esi,dword[_arg] |
tp equ 1 |
\} |
match =0 =word[_arg],tp _num \{ |
movzx esi,word[_arg] |
tp equ 1 |
\} |
match =0 =byte[_arg],tp _num \{ |
movzx esi,byte[_arg] |
tp equ 1 |
\} |
match =0,tp \{ |
'Error: specified string width is incorrect' |
\} |
end if |
else |
mov esi,0x7FFFFFFF |
end if |
call fdo_debug_outstr |
popad |
popf |
} |
macro DEBUGD _sign,_dec { |
local tp |
tp equ 0 |
match _arg:_num,_dec \{ |
DEBUGD_N _sign,_num,_arg |
tp equ 1 |
\} |
match =0 _arg,tp _dec \{ |
DEBUGD_N _sign,,_arg |
\} |
} |
macro DEBUGD_N _sign,_num,_dec { |
pushf |
pushad |
if (~_num eq) |
if (_dec eqtype eax | _dec eqtype 0) |
'Error: precision allowed only for in-memory variables' |
end if |
if (~_num in <1,2,4>) |
if _sign |
'Error: 1, 2 and 4 are only allowed for precision in %d' |
else |
'Error: 1, 2 and 4 are only allowed for precision in %u' |
end if |
end if |
end if |
if _dec eqtype eax |
if _dec in <ebx,ecx,edx,esi,edi,ebp,esp> |
mov eax,_dec |
else if ~_dec eq eax |
if _sign = 1 |
movsx eax,_dec |
else |
movzx eax,_dec |
end if |
end if |
else if _dec eqtype 0 |
mov eax,_dec |
else |
add esp,4*8+4 |
if _num eq |
mov eax,dword _dec |
else if _num = 1 |
if _sign = 1 |
movsx eax,byte _dec |
else |
movzx eax,byte _dec |
end if |
else if _num = 2 |
if _sign = 1 |
movsx eax,word _dec |
else |
movzx eax,word _dec |
end if |
else |
mov eax,dword _dec |
end if |
sub esp,4*8+4 |
end if |
mov cl,_sign |
call fdo_debug_outdec |
popad |
popf |
} |
macro DEBUGH _sign,_hex { |
local tp |
tp equ 0 |
match _arg:_num,_hex \{ |
DEBUGH_N _sign,_num,_arg |
tp equ 1 |
\} |
match =0 _arg,tp _hex \{ |
DEBUGH_N _sign,,_arg |
\} |
} |
macro DEBUGH_N _sign,_num,_hex { |
pushf |
pushad |
if (~_num eq) & (~_num in <1,2,3,4,5,6,7,8>) |
'Error: 1..8 are only allowed for precision in %x' |
end if |
if _hex eqtype eax |
if _hex in <eax,ebx,ecx,edx,esi,edi,ebp,esp> |
if ~_hex eq eax |
mov eax,_hex |
end if |
else if _hex in <ax,bx,cx,dx,si,di,bp,sp> |
if ~_hex eq ax |
movzx eax,_hex |
end if |
shl eax,16 |
if (_num eq) |
mov edx,4 |
end if |
else if _hex in <al,ah,bl,bh,cl,ch,dl,dh> |
if ~_hex eq al |
movzx eax,_hex |
end if |
shl eax,24 |
if (_num eq) |
mov edx,2 |
end if |
end if |
else if _hex eqtype 0 |
mov eax,_hex |
else |
add esp,4*8+4 |
mov eax,dword _hex |
sub esp,4*8+4 |
end if |
if ~_num eq |
mov edx,_num |
else |
mov edx,8 |
end if |
call fdo_debug_outhex |
popad |
popf |
} |
;----------------------------------------------------------------------------- |
debug_func fdo_debug_outchar |
debug_beginf |
pushad |
mov cl,al |
mov ebx,1 |
mov eax,63 |
int 0x40 |
popad |
ret |
debug_endf |
debug_func fdo_debug_outstr |
debug_beginf |
mov eax,63 |
mov ebx,1 |
.l1: dec esi |
js .l2 |
mov cl,[edx] |
or cl,cl |
jz .l2 |
int 0x40 |
inc edx |
jmp .l1 |
.l2: ret |
debug_endf |
debug_func fdo_debug_outdec |
debug_beginf |
or cl,cl |
jz @f |
or eax,eax |
jns @f |
neg eax |
push eax |
mov al,'-' |
call fdo_debug_outchar |
pop eax |
@@: push 10 |
pop ecx |
push -'0' |
.l1: xor edx,edx |
div ecx |
push edx |
test eax,eax |
jnz .l1 |
.l2: pop eax |
add al,'0' |
jz .l3 |
call fdo_debug_outchar |
jmp .l2 |
.l3: ret |
debug_endf |
debug_func fdo_debug_outhex |
__fdo_hexdigits db '0123456789ABCDEF' |
debug_beginf |
mov cl,dl |
neg cl |
add cl,8 |
shl cl,2 |
rol eax,cl |
.l1: rol eax,4 |
push eax |
and eax,0x0000000F |
mov al,[__fdo_hexdigits+eax] |
call fdo_debug_outchar |
pop eax |
dec edx |
jnz .l1 |
ret |
debug_endf |
;----------------------------------------------------------------------------- |
macro DEBUGF _level,_format,[_arg] { |
common |
if __DEBUG__ = 1 & _level >= __DEBUG_LEVEL__ |
local ..f1,f2,a1,a2,c1,c2,c3,..lbl |
_debug_str_ equ __debug_str_ # a1 |
a1 = 0 |
c2 = 0 |
c3 = 0 |
f2 = 0 |
repeat ..lbl-..f1 |
virtual at 0 |
db _format,0,0 |
load c1 word from %-1 |
end virtual |
if c1 = '%s' |
virtual at 0 |
db _format,0,0 |
store word 0 at %-1 |
load c1 from f2-c2 |
end virtual |
if c1 <> 0 |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
c2 = c2 + 1 |
f2 = %+1 |
DEBUGF_HELPER S,a1,0,_arg |
else if c1 = '%x' |
virtual at 0 |
db _format,0,0 |
store word 0 at %-1 |
load c1 from f2-c2 |
end virtual |
if c1 <> 0 |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
c2 = c2 + 1 |
f2 = %+1 |
DEBUGF_HELPER H,a1,0,_arg |
else if c1 = '%d' | c1 = '%u' |
local c4 |
if c1 = '%d' |
c4 = 1 |
else |
c4 = 0 |
end if |
virtual at 0 |
db _format,0,0 |
store word 0 at %-1 |
load c1 from f2-c2 |
end virtual |
if c1 <> 0 |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
c2 = c2 + 1 |
f2 = %+1 |
DEBUGF_HELPER D,a1,c4,_arg |
else if c1 = '\n' |
c3 = c3 + 1 |
end if |
end repeat |
virtual at 0 |
db _format,0,0 |
load c1 from f2-c2 |
end virtual |
if (c1<>0)&(f2<>..lbl-..f1-1) |
DEBUGS 0,_debug_str_+f2-c2 |
end if |
virtual at 0 |
..f1 db _format,0 |
..lbl: |
__debug_strings equ __debug_strings,_debug_str_,<_format>,..lbl-..f1-1-c2-c3 |
end virtual |
end if |
} |
macro __include_debug_strings dummy,[_id,_fmt,_len] { |
common |
local c1,a1,a2 |
forward |
if defined _len & ~_len eq |
_id: |
a1 = 0 |
a2 = 0 |
repeat _len |
virtual at 0 |
db _fmt,0,0 |
load c1 word from %+a2-1 |
end virtual |
if (c1='%s')|(c1='%x')|(c1='%d')|(c1='%u') |
db 0 |
a2 = a2 + 1 |
else if (c1='\n') |
dw $0A0D |
a1 = a1 + 1 |
a2 = a2 + 1 |
else |
db c1 and 0x0FF |
end if |
end repeat |
db 0 |
end if |
} |
macro DEBUGF_HELPER _letter,_num,_sign,[_arg] { |
common |
local num |
num = 0 |
forward |
if num = _num |
DEBUG#_letter _sign,_arg |
end if |
num = num+1 |
common |
_num = _num+1 |
} |
macro include_debug_strings { |
if __DEBUG__ = 1 |
match dbg_str,__debug_strings \{ |
__include_debug_strings dbg_str |
\} |
end if |
} |
/programs/develop/ktcc/trunk/libc/start/macros.inc |
---|
0,0 → 1,337 |
; language for programs |
lang fix en ; ru en fr ge fi |
@^ fix macro comment { |
^@ fix } |
; strings |
macro sz name,[data] { ; from MFAR [mike.dld] |
common |
if used name |
name db data |
.size = $-name |
end if |
} |
macro lsz name,[lng,data] { ; from MFAR [mike.dld] |
common |
if used name |
label name |
forward |
if lang eq lng |
db data |
end if |
common |
.size = $-name |
end if |
} |
macro szc name,elsz,[data] { ; from MFAR [mike.dld] |
common |
local s,m |
m = 0 |
if used name |
label name |
virtual at 0 |
db data |
s = $ |
end virtual |
d#elsz s |
if m < s |
m = s |
end if |
db data |
.size = $-name |
.maxl = m |
end if |
} |
macro lszc name,elsz,[lng,data] { ; from MFAR [mike.dld] |
common |
local s,m |
m = 0 |
if used name |
label name |
forward |
if lang eq lng |
virtual at 0 |
db data |
s = $ |
end virtual |
d#elsz s |
if m < s |
m = s |
end if |
db data |
end if |
common |
.size = $-name |
.maxl = m |
end if |
} |
; easy system call macro |
macro mpack dest, hsrc, lsrc |
{ |
if (hsrc eqtype 0) & (lsrc eqtype 0) |
mov dest, (hsrc) shl 16 + lsrc |
else |
if (hsrc eqtype 0) & (~lsrc eqtype 0) |
mov dest, (hsrc) shl 16 |
add dest, lsrc |
else |
mov dest, hsrc |
shl dest, 16 |
add dest, lsrc |
end if |
end if |
} |
macro __mov reg,a,b { ; mike.dld |
if (~a eq)&(~b eq) |
mpack reg,a,b |
else if (~a eq)&(b eq) |
mov reg,a |
end if |
} |
macro mcall a,b,c,d,e,f { ; mike.dld |
__mov eax,a |
__mov ebx,b |
__mov ecx,c |
__mov edx,d |
__mov esi,e |
__mov edi,f |
int 0x40 |
} |
; ------------------------- |
macro header a,[b] { |
common |
use32 |
org 0 |
db 'MENUET',a |
forward |
if b eq |
dd 0 |
else |
dd b |
end if } |
macro section name,algn |
{ |
local boundary |
boundary = 16 |
if ~algn eq |
boundary = algn |
end if |
align boundary |
label name |
} |
macro func name { |
if ~used name |
display 'FUNC NOT USED: ',`name,13,10 |
else |
align 4 |
name: |
;pushad |
;pushfd |
;dps `name |
;newline |
;mcall 5,1 |
;popfd |
;popad |
} |
macro endf { end if } |
macro jif _op1,_cond,_op2,_label,_op |
{ |
if _op eq |
cmp _op1,_op2 |
else |
_op _op1,_op2 |
end if |
j#_cond _label |
} |
macro diff16 title,l1,l2 |
{ |
local s,d |
s = l2-l1 |
display title,': 0x' |
repeat 8 |
d = '0' + s shr ((8-%) shl 2) and $0F |
if d > '9' |
d = d + 'A'-'9'-1 |
end if |
display d |
end repeat |
display 13,10 |
} |
macro diff10 title,l1,l2 |
{ |
local s,d,z,m |
s = l2-l1 |
z = 0 |
m = 1000000000 |
display title,': ' |
repeat 10 |
d = '0' + s / m |
s = s - (s/m)*m |
m = m / 10 |
if d <> '0' |
z = 1 |
end if |
if z <> 0 |
display d |
end if |
end repeat |
display 13,10 |
} |
; optimize the code for size |
__regs fix <eax,ebx,ecx,edx,esi,edi,ebp,esp> |
macro add arg1,arg2 |
{ |
if (arg2 eqtype 0) |
if (arg2) = 1 |
inc arg1 |
else |
add arg1,arg2 |
end if |
else |
add arg1,arg2 |
end if |
} |
macro sub arg1,arg2 |
{ |
if (arg2 eqtype 0) |
if (arg2) = 1 |
dec arg1 |
else |
sub arg1,arg2 |
end if |
else |
sub arg1,arg2 |
end if |
} |
macro mov arg1,arg2 |
{ |
if (arg1 in __regs) & (arg2 eqtype 0) |
if (arg2) = 0 |
xor arg1,arg1 |
else if (arg2) = 1 |
xor arg1,arg1 |
inc arg1 |
else if (arg2) = -1 |
or arg1,-1 |
else if (arg2) > -128 & (arg2) < 128 |
push arg2 |
pop arg1 |
else |
mov arg1,arg2 |
end if |
else |
mov arg1,arg2 |
end if |
} |
struc POINT _t,_dx,_dy { |
.x _t _dx |
.y _t _dy |
} |
; structure definition helper |
macro struct name, [arg] |
{ |
common |
name@struct fix name |
struc name arg { |
} |
macro struct_helper name |
{ |
virtual at 0 |
name name |
sizeof.#name = $ - name |
name equ sizeof.#name |
end virtual |
} |
ends fix } struct_helper name@struct |
macro union [def] |
{ |
common size@union = 0 |
origin@union = $ |
forward virtual |
def |
if $-origin@union > size@union |
size@union = $-origin@union |
end if |
end virtual |
common rb size@union |
} |
; structures used in MeOS |
struc process_information |
{ |
.cpu_usage dd ? ; +0 |
.window_stack_position dw ? ; +4 |
.window_stack_value dw ? ; +6 |
.not_used1 dw ? ; +8 |
.process_name rb 12 ; +10 |
.memory_start dd ? ; +22 |
.used_memory dd ? ; +26 |
.PID dd ? ; +30 |
.x_start dd ? ; +34 |
.y_start dd ? ; +38 |
.x_size dd ? ; +42 |
.y_size dd ? ; +46 |
.slot_state dw ? ; +50 |
rb (1024-52) |
} |
struc system_colors |
{ |
.frame dd ? |
.grab dd ? |
.grab_button dd ? |
.grab_button_text dd ? |
.grab_text dd ? |
.work dd ? |
.work_button dd ? |
.work_button_text dd ? |
.work_text dd ? |
.work_graph dd ? |
} |
; constants |
; events |
EV_IDLE = 0 |
EV_TIMER = 0 |
EV_REDRAW = 1 |
EV_KEY = 2 |
EV_BUTTON = 3 |
EV_EXIT = 4 |
EV_BACKGROUND = 5 |
EV_MOUSE = 6 |
EV_IPC = 7 |
EV_STACK = 8 |
; event mask bits for function 40 |
EVM_REDRAW = 1b |
EVM_KEY = 10b |
EVM_BUTTON = 100b |
EVM_EXIT = 1000b |
EVM_BACKGROUND = 10000b |
EVM_MOUSE = 100000b |
EVM_IPC = 1000000b |
EVM_STACK = 10000000b |
/programs/develop/ktcc/trunk/libc/start |
---|
Property changes: |
Added: svn:ignore |
+*.o |
+*.s |
/programs/develop/ktcc/trunk/libc/proc32.inc |
---|
0,0 → 1,268 |
; Macroinstructions for defining and calling procedures |
macro stdcall proc,[arg] ; directly call STDCALL procedure |
{ common |
if ~ arg eq |
reverse |
pushd arg |
common |
end if |
call proc } |
macro invoke proc,[arg] ; indirectly call STDCALL procedure |
{ common |
if ~ arg eq |
reverse |
pushd arg |
common |
end if |
call [proc] } |
macro ccall proc,[arg] ; directly call CDECL procedure |
{ common |
size@ccall = 0 |
if ~ arg eq |
reverse |
pushd arg |
size@ccall = size@ccall+4 |
common |
end if |
call proc |
if size@ccall |
add esp,size@ccall |
end if } |
macro cinvoke proc,[arg] ; indirectly call CDECL procedure |
{ common |
size@ccall = 0 |
if ~ arg eq |
reverse |
pushd arg |
size@ccall = size@ccall+4 |
common |
end if |
call [proc] |
if size@ccall |
add esp,size@ccall |
end if } |
macro proc [args] ; define procedure |
{ common |
match name params, args> |
\{ define@proc name,<params \} } |
prologue@proc equ prologuedef |
macro prologuedef procname,flag,parmbytes,localbytes,reglist |
{ if parmbytes | localbytes |
push ebp |
mov ebp,esp |
if localbytes |
sub esp,localbytes |
end if |
end if |
irps reg, reglist \{ push reg \} } |
epilogue@proc equ epiloguedef |
macro epiloguedef procname,flag,parmbytes,localbytes,reglist |
{ irps reg, reglist \{ reverse pop reg \} |
if parmbytes | localbytes |
leave |
end if |
if (flag and 10000b) | (parmbytes=0) |
retn |
else |
retn parmbytes |
end if } |
macro define@proc name,statement |
{ local params,flag,regs,parmbytes,localbytes,current |
if used name |
name: |
match =stdcall args, statement \{ params equ args |
flag = 11b \} |
match =stdcall, statement \{ params equ |
flag = 11b \} |
match =c args, statement \{ params equ args |
flag = 10001b \} |
match =c, statement \{ params equ |
flag = 10001b \} |
match =params, params \{ params equ statement |
flag = 0 \} |
virtual at ebp+8 |
match =uses reglist=,args, params \{ regs equ reglist |
params equ args \} |
match =regs =uses reglist, regs params \{ regs equ reglist |
params equ \} |
match =regs, regs \{ regs equ \} |
match =,args, params \{ defargs@proc args \} |
match =args@proc args, args@proc params \{ defargs@proc args \} |
parmbytes = $ - (ebp+8) |
end virtual |
name # % = parmbytes/4 |
all@vars equ |
current = 0 |
match prologue:reglist, prologue@proc:<regs> \{ prologue name,flag,parmbytes,localbytes,reglist \} |
macro locals |
\{ virtual at ebp-localbytes+current |
macro label . \\{ deflocal@proc .,:, \\} |
struc db [val] \\{ \common deflocal@proc .,db,val \\} |
struc dw [val] \\{ \common deflocal@proc .,dw,val \\} |
struc dp [val] \\{ \common deflocal@proc .,dp,val \\} |
struc dd [val] \\{ \common deflocal@proc .,dd,val \\} |
struc dt [val] \\{ \common deflocal@proc .,dt,val \\} |
struc dq [val] \\{ \common deflocal@proc .,dq,val \\} |
struc rb cnt \\{ deflocal@proc .,rb cnt, \\} |
struc rw cnt \\{ deflocal@proc .,rw cnt, \\} |
struc rp cnt \\{ deflocal@proc .,rp cnt, \\} |
struc rd cnt \\{ deflocal@proc .,rd cnt, \\} |
struc rt cnt \\{ deflocal@proc .,rt cnt, \\} |
struc rq cnt \\{ deflocal@proc .,rq cnt, \\} \} |
macro endl |
\{ purge label |
restruc db,dw,dp,dd,dt,dq |
restruc rb,rw,rp,rd,rt,rq |
restruc byte,word,dword,pword,tword,qword |
current = $-(ebp-localbytes) |
end virtual \} |
macro ret operand |
\{ match any, operand \\{ retn operand \\} |
match , operand \\{ match epilogue:reglist, epilogue@proc:<regs> |
\\\{ epilogue name,flag,parmbytes,localbytes,reglist \\\} \\} \} |
macro finish@proc \{ localbytes = (((current-1) shr 2)+1) shl 2 |
end if \} } |
macro defargs@proc [arg] |
{ common |
if ~ arg eq |
forward |
local ..arg,current@arg |
match argname:type, arg |
\{ current@arg equ argname |
label ..arg type |
argname equ ..arg |
if dqword eq type |
dd ?,?,?,? |
else if tbyte eq type |
dd ?,?,? |
else if qword eq type | pword eq type |
dd ?,? |
else |
dd ? |
end if \} |
match =current@arg,current@arg |
\{ current@arg equ arg |
arg equ ..arg |
..arg dd ? \} |
common |
args@proc equ current@arg |
forward |
restore current@arg |
common |
end if } |
macro deflocal@proc name,def,[val] |
{ common |
match vars, all@vars \{ all@vars equ all@vars, \} |
all@vars equ all@vars name |
forward |
local ..var,..tmp |
..var def val |
match =?, val \{ ..tmp equ \} |
match any =dup (=?), val \{ ..tmp equ \} |
match tmp : value, ..tmp : val |
\{ tmp: end virtual |
initlocal@proc ..var,def value |
virtual at tmp\} |
common |
match first rest, ..var, \{ name equ first \} } |
macro initlocal@proc name,def |
{ virtual at name |
def |
size@initlocal = $ - name |
end virtual |
position@initlocal = 0 |
while size@initlocal > position@initlocal |
virtual at name |
def |
if size@initlocal - position@initlocal < 2 |
current@initlocal = 1 |
load byte@initlocal byte from name+position@initlocal |
else if size@initlocal - position@initlocal < 4 |
current@initlocal = 2 |
load word@initlocal word from name+position@initlocal |
else |
current@initlocal = 4 |
load dword@initlocal dword from name+position@initlocal |
end if |
end virtual |
if current@initlocal = 1 |
mov byte [name+position@initlocal],byte@initlocal |
else if current@initlocal = 2 |
mov word [name+position@initlocal],word@initlocal |
else |
mov dword [name+position@initlocal],dword@initlocal |
end if |
position@initlocal = position@initlocal + current@initlocal |
end while } |
macro endp |
{ purge ret,locals,endl |
finish@proc |
purge finish@proc |
restore regs@proc |
match all,args@proc \{ restore all \} |
restore args@proc |
match all,all@vars \{ restore all \} } |
macro local [var] |
{ common |
locals |
forward done@local equ |
match varname[count]:vartype, var |
\{ match =BYTE, vartype \\{ varname rb count |
restore done@local \\} |
match =WORD, vartype \\{ varname rw count |
restore done@local \\} |
match =DWORD, vartype \\{ varname rd count |
restore done@local \\} |
match =PWORD, vartype \\{ varname rp count |
restore done@local \\} |
match =QWORD, vartype \\{ varname rq count |
restore done@local \\} |
match =TBYTE, vartype \\{ varname rt count |
restore done@local \\} |
match =DQWORD, vartype \\{ label varname dqword |
rq count+count |
restore done@local \\} |
match , done@local \\{ virtual |
varname vartype |
end virtual |
rb count*sizeof.\#vartype |
restore done@local \\} \} |
match :varname:vartype, done@local:var |
\{ match =BYTE, vartype \\{ varname db ? |
restore done@local \\} |
match =WORD, vartype \\{ varname dw ? |
restore done@local \\} |
match =DWORD, vartype \\{ varname dd ? |
restore done@local \\} |
match =PWORD, vartype \\{ varname dp ? |
restore done@local \\} |
match =QWORD, vartype \\{ varname dq ? |
restore done@local \\} |
match =TBYTE, vartype \\{ varname dt ? |
restore done@local \\} |
match =DQWORD, vartype \\{ label varname dqword |
dq ?,? |
restore done@local \\} |
match , done@local \\{ varname vartype |
restore done@local \\} \} |
match ,done@local |
\{ var |
restore done@local \} |
common |
endl } |
/programs/develop/ktcc/trunk/libc/public_stdcall.inc |
---|
0,0 → 1,7 |
macro public_stdcall name,size |
{ |
public name |
public name#@#size |
name: |
name#@#size: |
} |
/programs/develop/ktcc/trunk/libc |
---|
Property changes: |
Added: svn:ignore |
+*.a |
+script.txt |
/programs/develop/ktcc/trunk/samples/console/console.c |
---|
0,0 → 1,70 |
// Console dynamic link library. Sample by Ghost |
#include <stdio.h> |
#include <string.h> |
#include <kolibrisys.h> |
char* dllname="/sys/lib/console.obj"; |
int i; |
char* imports[] = {"START","version","con_init","con_write_asciiz","con_printf","con_exit",NULL}; |
char* caption = "Console test - colors"; |
dword (* dll_start)(dword res); |
dword* dll_ver; |
void stdcall (* con_init)(dword wnd_width, dword wnd_height, dword scr_width, dword scr_height, const char* title); |
void stdcall (* con_write_asciiz)(const char* string); |
void cdecl (* con_printf)(const char* format,...); |
void stdcall (* con_exit)(dword bCloseWindow); |
struct import{ |
char *name; |
void *data; |
}; |
void link(struct import *exp, char** imports){ |
dll_start = (dword (*)(dword)) |
_ksys_cofflib_getproc(exp, imports[0]); |
dll_ver = (dword*) |
_ksys_cofflib_getproc(exp, imports[1]); |
con_init = (void stdcall (*)(dword , dword, dword, dword, const char*)) |
_ksys_cofflib_getproc(exp, imports[2]); |
con_write_asciiz = (void stdcall (*)(const char*)) |
_ksys_cofflib_getproc(exp, imports[3]); |
con_printf = (void cdecl (*)(const char*,...)) |
_ksys_cofflib_getproc(exp, imports[4]); |
con_exit = (void stdcall (*)(dword)) |
_ksys_cofflib_getproc(exp, imports[5]); |
} |
int main(int argc, char **argv){ |
struct import * hDll; |
int a,b,c,d; |
if((hDll = (struct import *)_ksys_cofflib_load(dllname)) == 0){ |
debug_out_str("can't load lib\n"); |
return 1; |
} |
link(hDll, imports); |
debug_out_str("dll loaded\n"); |
if(dll_start(1) == 0){ |
debug_out_str("dll_start failed\n"); |
return 1; |
} |
con_init(-1, -1, -1, -1, caption); |
for(i = 0; i < 256; i++){ |
con_printf("Color 0x%02X: ", i); |
con_write_asciiz("Text sample."); |
con_printf(" printf %s test %d\n", "small", i); |
} |
con_exit(0); |
debug_out_str("all right's ;)\n"); |
} |
/programs/develop/ktcc/trunk/samples/simple/simple.c |
---|
0,0 → 1,61 |
// simple sample by Ghost |
#include <stdio.h> |
#include <string.h> |
#include <kolibrisys.h> |
#define FONT0 0 |
#define FONT1 0x10000000 |
#define BT_NORMAL 0 |
#define BT_DEL 0x80000000 |
#define BT_HIDE 0x40000000 |
#define BT_NOFRAME 0x20000000 |
char header[]={" -= C demo programm. Compiled whith KTCC halyavin and andrew_programmer port =- "}; |
void rotate_str(char *str){ |
char tmp; |
int i; |
tmp = str[0]; |
for(i = 1; str[i]; i++)str[i - 1] = str[i]; |
str[i - 1] = tmp; |
} |
void draw_window(){ |
static int offs = 0; |
static int fcolor = 0; |
static int col = 0; |
_ksys_window_redraw(1); |
_ksys_draw_window(100, 100, 300, 120, 0xaabbcc, 2, 0x5080d0, 0, 0x5080d0); |
_ksys_write_text(6 - offs, 8, fcolor | FONT0, header, strlen(header)); |
_ksys_draw_bar(1, 6, 5, 13, 0x05080d0); |
_ksys_draw_bar(274, 6, 26, 13, 0x05080d0); |
_ksys_make_button(300 - 19, 5, 12, 12, 1 | BT_NORMAL, 0x6688dd); |
_ksys_window_redraw(2); |
offs = (offs + 1) % 6; |
if(!offs)rotate_str(header); |
fcolor += (col)?-0x80808:0x80808; |
if(fcolor > 0xf80000 || fcolor == 0)col = !col; |
} |
int main(int argc, char **argv){ |
while(!0){ |
switch(_ksys_wait_for_event(10)){ |
case 2:return 0; |
case 3: |
if(_ksys_get_button_id() == 1)return 0; |
break; |
default: |
draw_window(); |
break; |
} |
} |
} |
/programs/develop/ktcc/trunk/samples/files/FILES.C |
---|
0,0 → 1,43 |
#include <stdio.h> |
#include <stdlib.h> |
int main(int argc, char **argv) |
{ |
int i; |
char c; |
FILE *f; |
FILE *fin; |
FILE *fout; |
//write to file |
f=fopen("testfile.txt","w"); |
for(i=0;i<50;i++) |
{ |
fputc('1',f); |
} |
fclose(f); |
//append to file |
f=fopen("testfile.txt","a"); |
for(i=0;i<50;i++) |
{ |
fputc('2',f); |
} |
fclose(f); |
//copy from testfile.txt to copyfile.txt |
fin=fopen("testfile.txt","r"); |
fout=fopen("copyfile.txt","w"); |
while((c=fgetc(fin))!=EOF) |
{ |
fputc(c,fout); |
} |
fclose(fin); |
fclose(fout); |
} |
/programs/develop/ktcc/trunk/source/Makefile |
---|
0,0 → 1,4 |
OUTFILE = ktcc.kex |
OBJS = tcc.o console.o |
CGLAGS =-O2 -g -Wall -mpreferred-stack-boundary=2 -march=i386 -falign-functions=0 -fno-strict-aliasing |
include $(MENUETDEV)/makefiles/Makefile_for_program |
/programs/develop/ktcc/trunk/source/console.asm |
---|
0,0 → 1,85 |
format ELF |
section '.text' executable |
public console_init |
public console_printf |
public console_exit |
align 4 |
console_init: |
pushad |
mov eax,[console_init_status] |
test eax,eax |
jnz console_initializated |
mov [console_init_status],1 |
mov eax,68 |
mov ebx,19 |
mov ecx,console_path |
int 0x40 |
test eax,eax |
jz console_not_loaded |
mov ebx,[eax+4] |
mov [con_start],ebx |
mov ebx,[eax+4+16] |
mov [con_init],ebx |
mov ebx,[eax+4+32] |
mov [con_printf],ebx |
push 1 |
call [con_start] |
push caption |
push -1 |
push -1 |
push -1 |
push -1 |
call [con_init] |
console_not_loaded: |
console_initializated: |
popad |
ret |
align 4 |
console_printf: |
pop [return_addres] |
call [con_printf] |
;add esp,8 |
push [return_addres] |
ret |
align 4 |
console_exit: |
push 0 |
call [con_exit] |
ret |
;----------------------------- |
console_path db '/sys/dll/console.obj',0 |
caption db 'Console',0 |
align 4 |
con_start rd 1 |
con_init rd 1 |
con_printf rd 1 |
con_exit rd 1 |
console_init_status rd 1 |
return_addres rd 1 |
/programs/develop/ktcc/trunk/source/start.asm |
---|
0,0 → 1,136 |
format ELF |
section '.text' executable |
public start |
;extrn mf_init |
extrn main |
;include 'debug2.inc' |
__DEBUG__=0 |
;start_: |
virtual at 0 |
db 'MENUET01' ; 1. Magic number (8 bytes) |
dd 0x01 ; 2. Version of executable file |
dd start ; 3. Start address |
dd 0x0 ; 4. Size of image |
dd 0x100000 ; 5. Size of needed memory |
dd 0x100000 ; 6. Pointer to stack |
hparams dd 0x0 ; 7. Pointer to program arguments |
hpath dd 0x0 ; 8. Pointer to program path |
end virtual |
start: |
;DEBUGF 'Start programm\n' |
;init heap of memory |
mov eax,68 |
mov ebx,11 |
int 0x40 |
;DEBUGF ' path "%s"\n params "%s"\n', .path, .params |
; check for overflow |
mov al, [path+buf_len-1] |
or al, [params+buf_len-1] |
jnz .crash |
; check if path written by OS |
mov eax, [hparams] |
test eax, eax |
jz .without_path |
mov eax, path |
.without_path: |
mov esi, eax |
call push_param |
; retrieving parameters |
mov esi, params |
xor edx, edx ; dl - èä¸ò ïàðàìåòð(1) èëè ðàçäåëèòåëè(0) |
; dh - ñèìâîë ñ êîòîðîãî íà÷àëñÿ ïàðàìåòð (1 êàâû÷êè, 0 îñòàëüíîå) |
mov ecx, 1 ; cl = 1 |
; ch = 0 ïðîñòî íîëü |
.parse: |
lodsb |
test al, al |
jz .run |
test dl, dl |
jnz .findendparam |
;{åñëè áûë ðàçäåëèòåëü |
cmp al, ' ' |
jz .parse ;çàãðóæåí ïðîáåë, ãðóçèì ñëåäóþùèé ñèìâîë |
mov dl, cl ;íà÷èíàåòñÿ ïàðàìåòð |
cmp al, '"' |
jz @f ;çàãðóæåíû êàâû÷êè |
mov dh, ch ;ïàðàìåòð áåç êàâû÷åê |
dec esi |
call push_param |
inc esi |
jmp .parse |
@@: |
mov dh, cl ;ïàðàìåòð â êàâû÷åêàõ |
call push_param ;åñëè íå ïðîáåë çíà÷èò íà÷èíàåòñÿ êàêîé òî ïàðàìåòð |
jmp .parse ;åñëè áûë ðàçäåëèòåëü} |
.findendparam: |
test dh, dh |
jz @f ; áåç êàâû÷åê |
cmp al, '"' |
jz .clear |
jmp .parse |
@@: |
cmp al, ' ' |
jnz .parse |
.clear: |
lea ebx, [esi - 1] |
mov [ebx], ch |
mov dl, ch |
jmp .parse |
.run: |
;DEBUGF 'call main(%x, %x) with params:\n', [argc], argv |
if __DEBUG__ = 1 |
mov ecx, [argc] |
@@: |
lea esi, [ecx * 4 + argv-4] |
DEBUGF '0x%x) "%s"\n', cx, [esi] |
loop @b |
end if |
push [argc] |
push argv |
call main |
.exit: |
;DEBUGF 'Exit from prog\n'; |
xor eax,eax |
dec eax |
int 0x40 |
dd -1 |
.crash: |
;DEBUGF 'E:buffer overflowed\n' |
jmp .exit |
;============================ |
push_param: |
;============================ |
;parameters |
; esi - pointer |
;description |
; procedure increase argc |
; and add pointer to array argv |
; procedure changes ebx |
mov ebx, [argc] |
cmp ebx, max_parameters |
jae .dont_add |
mov [argv+4*ebx], esi |
inc [argc] |
.dont_add: |
ret |
;============================== |
public params as '__argv' |
public path as '__path' |
section '.bss' |
buf_len = 0x400 |
max_parameters=0x20 |
argc rd 1 |
argv rd max_parameters |
path rb buf_len |
params rb buf_len |
;section '.data' |
;include_debug_strings ; ALWAYS present in data section |
/programs/develop/ktcc/trunk/source/tcc.c |
---|
0,0 → 1,10756 |
/* |
* TCC - Tiny C Compiler |
* |
* Copyright (c) 2001-2004 Fabrice Bellard |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation; either |
* version 2 of the License, or (at your option) any later version. |
* |
* This library is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
* Lesser General Public License for more details. |
* |
* You should have received a copy of the GNU Lesser General Public |
* License along with this library; if not, write to the Free Software |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
#define _GNU_SOURCE |
#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 <unistd.h> |
//#include <signal.h> |
#include <fcntl.h> |
#include <setjmp.h> |
#include <time.h> |
#ifdef WIN32 |
#include <sys/timeb.h> |
#endif |
//#ifndef WIN32 |
#include <sys/time.h> |
//#include <sys/ucontext.h> |
//#endif |
#endif /* !CONFIG_TCCBOOT */ |
#include "elf.h" |
#include "stab.h" |
#ifndef O_BINARY |
#define O_BINARY 0 |
#endif |
#include "libtcc.h" |
/* parser debug */ |
//#define PARSE_DEBUG |
/* preprocessor debug */ |
//#define PP_DEBUG |
/* include file debug */ |
//#define INC_DEBUG |
//#define MEM_DEBUG |
/* assembler debug */ |
//#define ASM_DEBUG |
/* target selection */ |
//#define TCC_TARGET_I386 /* i386 code generator */ |
//#define TCC_TARGET_ARM /* ARMv4 code generator */ |
//#define TCC_TARGET_C67 /* TMS320C67xx code generator */ |
//---------------------------------------------------------------- |
#define TCC_TARGET_MEOS |
/* default target is I386 */ |
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \ |
!defined(TCC_TARGET_C67) |
#define TCC_TARGET_I386 |
#endif |
#if !defined(WIN32) && !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \ |
!defined(TCC_TARGET_C67) |
#define CONFIG_TCC_BCHECK /* enable bound checking code */ |
#endif |
#if defined(WIN32) && !defined(TCC_TARGET_PE) && !defined(TCC_TARGET_MEOS) |
#define CONFIG_TCC_STATIC |
#endif |
/* define it to include assembler support */ |
#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67) |
#define CONFIG_TCC_ASM |
#endif |
/* object format selection */ |
#if defined(TCC_TARGET_C67) |
#define TCC_TARGET_COFF |
#endif |
#define FALSE 0 |
#define false 0 |
#define TRUE 1 |
#define true 1 |
typedef int BOOL; |
/* path to find crt1.o, crti.o and crtn.o. Only needed when generating |
executables or dlls */ |
#define CONFIG_TCC_CRT_PREFIX "/usr/lib" |
#define INCLUDE_STACK_SIZE 32 |
#define IFDEF_STACK_SIZE 64 |
#define VSTACK_SIZE 256 |
#define STRING_MAX_SIZE 1024 |
#define PACK_STACK_SIZE 8 |
#define TOK_HASH_SIZE 8192 /* must be a power of two */ |
#define TOK_ALLOC_INCR 512 /* must be a power of two */ |
#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */ |
/* token symbol management */ |
typedef struct TokenSym { |
struct TokenSym *hash_next; |
struct Sym *sym_define; /* direct pointer to define */ |
struct Sym *sym_label; /* direct pointer to label */ |
struct Sym *sym_struct; /* direct pointer to structure */ |
struct Sym *sym_identifier; /* direct pointer to identifier */ |
int tok; /* token number */ |
int len; |
char str[1]; |
} TokenSym; |
typedef struct CString { |
int size; /* size in bytes */ |
void *data; /* either 'char *' or 'int *' */ |
int size_allocated; |
void *data_allocated; /* if non NULL, data has been malloced */ |
} CString; |
/* type definition */ |
typedef struct CType { |
int t; |
struct Sym *ref; |
} CType; |
/* constant value */ |
typedef union CValue { |
long double ld; |
double d; |
float f; |
int i; |
unsigned int ui; |
unsigned int ul; /* address (should be unsigned long on 64 bit cpu) */ |
long long ll; |
unsigned long long ull; |
struct CString *cstr; |
void *ptr; |
int tab[1]; |
} CValue; |
/* value on stack */ |
typedef struct SValue { |
CType type; /* type */ |
unsigned short r; /* register + flags */ |
unsigned short r2; /* second register, used for 'long long' |
type. If not used, set to VT_CONST */ |
CValue c; /* constant, if VT_CONST */ |
struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */ |
} SValue; |
/* symbol management */ |
typedef struct Sym { |
int v; /* symbol token */ |
int r; /* associated register */ |
int c; /* associated number */ |
CType type; /* associated type */ |
struct Sym *next; /* next related symbol */ |
struct Sym *prev; /* prev symbol in stack */ |
struct Sym *prev_tok; /* previous symbol for this token */ |
} Sym; |
/* section definition */ |
/* XXX: use directly ELF structure for parameters ? */ |
/* special flag to indicate that the section should not be linked to |
the other ones */ |
#define SHF_PRIVATE 0x80000000 |
typedef struct Section { |
unsigned long data_offset; /* current data offset */ |
unsigned char *data; /* section data */ |
unsigned long data_allocated; /* used for realloc() handling */ |
int sh_name; /* elf section name (only used during output) */ |
int sh_num; /* elf section number */ |
int sh_type; /* elf section type */ |
int sh_flags; /* elf section flags */ |
int sh_info; /* elf section info */ |
int sh_addralign; /* elf section alignment */ |
int sh_entsize; /* elf entry size */ |
unsigned long sh_size; /* section size (only used during output) */ |
unsigned long sh_addr; /* address at which the section is relocated */ |
unsigned long sh_offset; /* address at which the section is relocated */ |
int nb_hashed_syms; /* used to resize the hash table */ |
struct Section *link; /* link to another section */ |
struct Section *reloc; /* corresponding section for relocation, if any */ |
struct Section *hash; /* hash table for symbols */ |
struct Section *next; |
char name[1]; /* section name */ |
} Section; |
typedef struct DLLReference { |
int level; |
char name[1]; |
} DLLReference; |
/* GNUC attribute definition */ |
typedef struct AttributeDef { |
int aligned; |
int packed; |
Section *section; |
unsigned char func_call; /* FUNC_CDECL, FUNC_STDCALL, FUNC_FASTCALLx */ |
unsigned char dllexport; |
} AttributeDef; |
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */ |
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */ |
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */ |
/* stored in 'Sym.c' field */ |
#define FUNC_NEW 1 /* ansi function prototype */ |
#define FUNC_OLD 2 /* old function prototype */ |
#define FUNC_ELLIPSIS 3 /* ansi function prototype with ... */ |
/* stored in 'Sym.r' field */ |
#define FUNC_CDECL 0 /* standard c call */ |
#define FUNC_STDCALL 1 /* pascal c call */ |
#define FUNC_FASTCALL1 2 /* first param in %eax */ |
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */ |
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */ |
/* field 'Sym.t' for macros */ |
#define MACRO_OBJ 0 /* object like macro */ |
#define MACRO_FUNC 1 /* function like macro */ |
/* field 'Sym.r' for C labels */ |
#define LABEL_DEFINED 0 /* label is defined */ |
#define LABEL_FORWARD 1 /* label is forward defined */ |
#define LABEL_DECLARED 2 /* label is declared but never used */ |
/* type_decl() types */ |
#define TYPE_ABSTRACT 1 /* type without variable */ |
#define TYPE_DIRECT 2 /* type with variable */ |
#define IO_BUF_SIZE 8192 |
typedef struct BufferedFile { |
uint8_t *buf_ptr; |
uint8_t *buf_end; |
int fd; |
int line_num; /* current line number - here to simplify code */ |
int ifndef_macro; /* #ifndef macro / #endif search */ |
int ifndef_macro_saved; /* saved ifndef_macro */ |
int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */ |
char inc_type; /* type of include */ |
char inc_filename[512]; /* filename specified by the user */ |
char filename[1024]; /* current filename - here to simplify code */ |
unsigned char buffer[IO_BUF_SIZE + 1]; /* extra size for CH_EOB char */ |
} BufferedFile; |
#define CH_EOB '\\' /* end of buffer or '\0' char in file */ |
#define CH_EOF (-1) /* end of file */ |
/* parsing state (used to save parser state to reparse part of the |
source several times) */ |
typedef struct ParseState { |
int *macro_ptr; |
int line_num; |
int tok; |
CValue tokc; |
} ParseState; |
/* used to record tokens */ |
typedef struct TokenString { |
int *str; |
int len; |
int allocated_len; |
int last_line_num; |
} TokenString; |
/* include file cache, used to find files faster and also to eliminate |
inclusion if the include file is protected by #ifndef ... #endif */ |
typedef struct CachedInclude { |
int ifndef_macro; |
int hash_next; /* -1 if none */ |
char type; /* '"' or '>' to give include type */ |
char filename[1]; /* path specified in #include */ |
} CachedInclude; |
#define CACHED_INCLUDES_HASH_SIZE 512 |
/* parser */ |
static struct BufferedFile *file; |
static int ch, tok; |
static CValue tokc; |
static CString tokcstr; /* current parsed string, if any */ |
/* additional informations about token */ |
static int tok_flags; |
#define TOK_FLAG_BOL 0x0001 /* beginning of line before */ |
#define TOK_FLAG_BOF 0x0002 /* beginning of file before */ |
#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */ |
static int *macro_ptr, *macro_ptr_allocated; |
static int *unget_saved_macro_ptr; |
static int unget_saved_buffer[TOK_MAX_SIZE + 1]; |
static int unget_buffer_enabled; |
static int parse_flags; |
#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */ |
#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */ |
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a |
token. line feed is also |
returned at eof */ |
#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */ |
static Section *text_section, *data_section, *bss_section; /* predefined sections */ |
static Section *cur_text_section; /* current section where function code is |
generated */ |
#ifdef CONFIG_TCC_ASM |
static Section *last_text_section; /* to handle .previous asm directive */ |
#endif |
/* bound check related sections */ |
static Section *bounds_section; /* contains global data bound description */ |
static Section *lbounds_section; /* contains local data bound description */ |
/* symbol sections */ |
static Section *symtab_section, *strtab_section; |
/* debug sections */ |
static Section *stab_section, *stabstr_section; |
/* loc : local variable index |
ind : output code index |
rsym: return symbol |
anon_sym: anonymous symbol index |
*/ |
static int rsym, anon_sym, ind, loc; |
/* expression generation modifiers */ |
static int const_wanted; /* true if constant wanted */ |
static int nocode_wanted; /* true if no code generation wanted for an expression */ |
static int global_expr; /* true if compound literals must be allocated |
globally (used during initializers parsing */ |
static CType func_vt; /* current function return type (used by return |
instruction) */ |
static int func_vc; |
static int last_line_num, last_ind, func_ind; /* debug last line number and pc */ |
static int tok_ident; |
static TokenSym **table_ident; |
static TokenSym *hash_ident[TOK_HASH_SIZE]; |
static char token_buf[STRING_MAX_SIZE + 1]; |
static char *funcname; |
static Sym *global_stack, *local_stack; |
static Sym *define_stack; |
static Sym *global_label_stack, *local_label_stack; |
/* symbol allocator */ |
#define SYM_POOL_NB (8192 / sizeof(Sym)) |
static Sym *sym_free_first; |
static SValue vstack[VSTACK_SIZE], *vtop; |
/* some predefined types */ |
static CType char_pointer_type, func_old_type, int_type; |
/* true if isid(c) || isnum(c) */ |
static unsigned char isidnum_table[256]; |
/* compile with debug symbol (and use them if error during execution) */ |
static int do_debug = 0; |
/* compile with built-in memory and bounds checker */ |
static int do_bounds_check = 0; |
/* display benchmark infos */ |
#if !defined(LIBTCC) |
static int do_bench = 0; |
#endif |
static int total_lines; |
static int total_bytes; |
/* use GNU C extensions */ |
static int gnu_ext = 1; |
/* use Tiny C extensions */ |
static int tcc_ext = 1; |
/* max number of callers shown if error */ |
static int num_callers = 6; |
static const char **rt_bound_error_msg; |
/* XXX: get rid of this ASAP */ |
static struct TCCState *tcc_state; |
/* give the path of the tcc libraries */ |
static const char *tcc_lib_path = CONFIG_TCCDIR; |
struct TCCState { |
int output_type; |
BufferedFile **include_stack_ptr; |
int *ifdef_stack_ptr; |
/* include file handling */ |
char **include_paths; |
int nb_include_paths; |
char **sysinclude_paths; |
int nb_sysinclude_paths; |
CachedInclude **cached_includes; |
int nb_cached_includes; |
char **library_paths; |
int nb_library_paths; |
/* array of all loaded dlls (including those referenced by loaded |
dlls) */ |
DLLReference **loaded_dlls; |
int nb_loaded_dlls; |
/* sections */ |
Section **sections; |
int nb_sections; /* number of sections, including first dummy section */ |
/* got handling */ |
Section *got; |
Section *plt; |
unsigned long *got_offsets; |
int nb_got_offsets; |
/* give the correspondance from symtab indexes to dynsym indexes */ |
int *symtab_to_dynsym; |
/* temporary dynamic symbol sections (for dll loading) */ |
Section *dynsymtab_section; |
/* exported dynamic symbol section */ |
Section *dynsym; |
int nostdinc; /* if true, no standard headers are added */ |
int nostdlib; /* if true, no standard libraries are added */ |
int nocommon; /* if true, do not use common symbols for .bss data */ |
/* if true, static linking is performed */ |
int static_link; |
/* if true, all symbols are exported */ |
int rdynamic; |
/* if true, only link in referenced objects from archive */ |
int alacarte_link; |
/* address of text section */ |
unsigned long text_addr; |
int has_text_addr; |
/* output format, see TCC_OUTPUT_FORMAT_xxx */ |
int output_format; |
/* C language options */ |
int char_is_unsigned; |
int leading_underscore; |
/* warning switches */ |
int warn_write_strings; |
int warn_unsupported; |
int warn_error; |
int warn_none; |
int warn_implicit_function_declaration; |
/* error handling */ |
void *error_opaque; |
void (*error_func)(void *opaque, const char *msg); |
int error_set_jmp_enabled; |
jmp_buf error_jmp_buf; |
int nb_errors; |
/* tiny assembler state */ |
Sym *asm_labels; |
/* see include_stack_ptr */ |
BufferedFile *include_stack[INCLUDE_STACK_SIZE]; |
/* see ifdef_stack_ptr */ |
int ifdef_stack[IFDEF_STACK_SIZE]; |
/* see cached_includes */ |
int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE]; |
/* pack stack */ |
int pack_stack[PACK_STACK_SIZE]; |
int *pack_stack_ptr; |
}; |
/* The current value can be: */ |
#define VT_VALMASK 0x00ff |
#define VT_CONST 0x00f0 /* constant in vc |
(must be first non register value) */ |
#define VT_LLOCAL 0x00f1 /* lvalue, offset on stack */ |
#define VT_LOCAL 0x00f2 /* offset on stack */ |
#define VT_CMP 0x00f3 /* the value is stored in processor flags (in vc) */ |
#define VT_JMP 0x00f4 /* value is the consequence of jmp true (even) */ |
#define VT_JMPI 0x00f5 /* value is the consequence of jmp false (odd) */ |
#define VT_LVAL 0x0100 /* var is an lvalue */ |
#define VT_SYM 0x0200 /* a symbol value is added */ |
#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for |
char/short stored in integer registers) */ |
#define VT_MUSTBOUND 0x0800 /* bound checking must be done before |
dereferencing value */ |
#define VT_BOUNDED 0x8000 /* value is bounded. The address of the |
bounding function call point is in vc */ |
#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */ |
#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */ |
#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */ |
#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED) |
/* types */ |
#define VT_INT 0 /* integer type */ |
#define VT_BYTE 1 /* signed byte type */ |
#define VT_SHORT 2 /* short type */ |
#define VT_VOID 3 /* void type */ |
#define VT_PTR 4 /* pointer */ |
#define VT_ENUM 5 /* enum definition */ |
#define VT_FUNC 6 /* function type */ |
#define VT_STRUCT 7 /* struct/union definition */ |
#define VT_FLOAT 8 /* IEEE float */ |
#define VT_DOUBLE 9 /* IEEE double */ |
#define VT_LDOUBLE 10 /* IEEE long double */ |
#define VT_BOOL 11 /* ISOC99 boolean type */ |
#define VT_LLONG 12 /* 64 bit integer */ |
#define VT_LONG 13 /* long integer (NEVER USED as type, only |
during parsing) */ |
#define VT_BTYPE 0x000f /* mask for basic type */ |
#define VT_UNSIGNED 0x0010 /* unsigned type */ |
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ |
#define VT_BITFIELD 0x0040 /* bitfield modifier */ |
#define VT_CONSTANT 0x0800 /* const modifier */ |
#define VT_VOLATILE 0x1000 /* volatile modifier */ |
#define VT_SIGNED 0x2000 /* signed type */ |
/* storage */ |
#define VT_EXTERN 0x00000080 /* extern definition */ |
#define VT_STATIC 0x00000100 /* static variable */ |
#define VT_TYPEDEF 0x00000200 /* typedef definition */ |
#define VT_INLINE 0x00000400 /* inline definition */ |
#define VT_STRUCT_SHIFT 16 /* shift for bitfield shift values */ |
/* type mask (except storage) */ |
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE) |
#define VT_TYPE (~(VT_STORAGE)) |
/* token values */ |
/* warning: the following compare tokens depend on i386 asm code */ |
#define TOK_ULT 0x92 |
#define TOK_UGE 0x93 |
#define TOK_EQ 0x94 |
#define TOK_NE 0x95 |
#define TOK_ULE 0x96 |
#define TOK_UGT 0x97 |
#define TOK_LT 0x9c |
#define TOK_GE 0x9d |
#define TOK_LE 0x9e |
#define TOK_GT 0x9f |
#define TOK_LAND 0xa0 |
#define TOK_LOR 0xa1 |
#define TOK_DEC 0xa2 |
#define TOK_MID 0xa3 /* inc/dec, to void constant */ |
#define TOK_INC 0xa4 |
#define TOK_UDIV 0xb0 /* unsigned division */ |
#define TOK_UMOD 0xb1 /* unsigned modulo */ |
#define TOK_PDIV 0xb2 /* fast division with undefined rounding for pointers */ |
#define TOK_CINT 0xb3 /* number in tokc */ |
#define TOK_CCHAR 0xb4 /* char constant in tokc */ |
#define TOK_STR 0xb5 /* pointer to string in tokc */ |
#define TOK_TWOSHARPS 0xb6 /* ## preprocessing token */ |
#define TOK_LCHAR 0xb7 |
#define TOK_LSTR 0xb8 |
#define TOK_CFLOAT 0xb9 /* float constant */ |
#define TOK_LINENUM 0xba /* line number info */ |
#define TOK_CDOUBLE 0xc0 /* double constant */ |
#define TOK_CLDOUBLE 0xc1 /* long double constant */ |
#define TOK_UMULL 0xc2 /* unsigned 32x32 -> 64 mul */ |
#define TOK_ADDC1 0xc3 /* add with carry generation */ |
#define TOK_ADDC2 0xc4 /* add with carry use */ |
#define TOK_SUBC1 0xc5 /* add with carry generation */ |
#define TOK_SUBC2 0xc6 /* add with carry use */ |
#define TOK_CUINT 0xc8 /* unsigned int constant */ |
#define TOK_CLLONG 0xc9 /* long long constant */ |
#define TOK_CULLONG 0xca /* unsigned long long constant */ |
#define TOK_ARROW 0xcb |
#define TOK_DOTS 0xcc /* three dots */ |
#define TOK_SHR 0xcd /* unsigned shift right */ |
#define TOK_PPNUM 0xce /* preprocessor number */ |
#define TOK_SHL 0x01 /* shift left */ |
#define TOK_SAR 0x02 /* signed shift right */ |
/* assignement operators : normal operator or 0x80 */ |
#define TOK_A_MOD 0xa5 |
#define TOK_A_AND 0xa6 |
#define TOK_A_MUL 0xaa |
#define TOK_A_ADD 0xab |
#define TOK_A_SUB 0xad |
#define TOK_A_DIV 0xaf |
#define TOK_A_XOR 0xde |
#define TOK_A_OR 0xfc |
#define TOK_A_SHL 0x81 |
#define TOK_A_SAR 0x82 |
#ifndef offsetof |
#define offsetof(type, field) ((size_t) &((type *)0)->field) |
#endif |
#ifndef countof |
#define countof(tab) (sizeof(tab) / sizeof((tab)[0])) |
#endif |
/* WARNING: the content of this string encodes token numbers */ |
static char tok_two_chars[] = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; |
#define TOK_EOF (-1) /* end of file */ |
#define TOK_LINEFEED 10 /* line feed */ |
/* all identificators and strings have token above that */ |
#define TOK_IDENT 256 |
/* only used for i386 asm opcodes definitions */ |
#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x) |
#define DEF_BWL(x) \ |
DEF(TOK_ASM_ ## x ## b, #x "b") \ |
DEF(TOK_ASM_ ## x ## w, #x "w") \ |
DEF(TOK_ASM_ ## x ## l, #x "l") \ |
DEF(TOK_ASM_ ## x, #x) |
#define DEF_WL(x) \ |
DEF(TOK_ASM_ ## x ## w, #x "w") \ |
DEF(TOK_ASM_ ## x ## l, #x "l") \ |
DEF(TOK_ASM_ ## x, #x) |
#define DEF_FP1(x) \ |
DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \ |
DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \ |
DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \ |
DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s") |
#define DEF_FP(x) \ |
DEF(TOK_ASM_ ## f ## x, "f" #x ) \ |
DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \ |
DEF_FP1(x) |
#define DEF_ASMTEST(x) \ |
DEF_ASM(x ## o) \ |
DEF_ASM(x ## no) \ |
DEF_ASM(x ## b) \ |
DEF_ASM(x ## c) \ |
DEF_ASM(x ## nae) \ |
DEF_ASM(x ## nb) \ |
DEF_ASM(x ## nc) \ |
DEF_ASM(x ## ae) \ |
DEF_ASM(x ## e) \ |
DEF_ASM(x ## z) \ |
DEF_ASM(x ## ne) \ |
DEF_ASM(x ## nz) \ |
DEF_ASM(x ## be) \ |
DEF_ASM(x ## na) \ |
DEF_ASM(x ## nbe) \ |
DEF_ASM(x ## a) \ |
DEF_ASM(x ## s) \ |
DEF_ASM(x ## ns) \ |
DEF_ASM(x ## p) \ |
DEF_ASM(x ## pe) \ |
DEF_ASM(x ## np) \ |
DEF_ASM(x ## po) \ |
DEF_ASM(x ## l) \ |
DEF_ASM(x ## nge) \ |
DEF_ASM(x ## nl) \ |
DEF_ASM(x ## ge) \ |
DEF_ASM(x ## le) \ |
DEF_ASM(x ## ng) \ |
DEF_ASM(x ## nle) \ |
DEF_ASM(x ## g) |
#define TOK_ASM_int TOK_INT |
enum tcc_token { |
TOK_LAST = TOK_IDENT - 1, |
#define DEF(id, str) id, |
#include "tcctok.h" |
#undef DEF |
}; |
static const char tcc_keywords[] = |
#define DEF(id, str) str "\0" |
#include "tcctok.h" |
#undef DEF |
; |
#define TOK_UIDENT TOK_DEFINE |
#ifdef WIN32 |
int __stdcall GetModuleFileNameA(void *, char *, int); |
void *__stdcall GetProcAddress(void *, const char *); |
void *__stdcall GetModuleHandleA(const char *); |
void *__stdcall LoadLibraryA(const char *); |
int __stdcall FreeConsole(void); |
#define snprintf _snprintf |
#define vsnprintf _vsnprintf |
#ifndef __GNUC__ |
#define strtold (long double)strtod |
#define strtof (float)strtod |
#define strtoll (long long)strtol |
#endif |
#elif defined(TCC_UCLIBC) || defined(__FreeBSD__) |
long double strtold(const char *nptr, char **endptr) |
{ |
return (long double)strtod(nptr, endptr); |
} |
float strtof(const char *nptr, char **endptr) |
{ |
return (float)strtod(nptr, endptr); |
} |
#else |
/* XXX: need to define this to use them in non ISOC99 context */ |
extern float strtof (const char *__nptr, char **__endptr); |
extern long double strtold (const char *__nptr, char **__endptr); |
//extern long long strtoll(const char *__nptr, char **__endptr, int __base) |
#endif |
#define strtold (long double)strtod |
#define strtof (float)strtod |
#define strtoll (long long)strtol |
static char *pstrcpy(char *buf, int buf_size, const char *s); |
static char *pstrcat(char *buf, int buf_size, const char *s); |
static const char *tcc_basename(const char *name); |
static void next(void); |
static void next_nomacro(void); |
static void parse_expr_type(CType *type); |
static void expr_type(CType *type); |
static void unary_type(CType *type); |
static void block(int *bsym, int *csym, int *case_sym, int *def_sym, |
int case_reg, int is_expr); |
static int expr_const(void); |
static void expr_eq(void); |
static void gexpr(void); |
static void gen_inline_functions(void); |
static void decl(int l); |
static void decl_initializer(CType *type, Section *sec, unsigned long c, |
int first, int size_only); |
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, |
int has_init, int v, int scope); |
int gv(int rc); |
void gv2(int rc1, int rc2); |
void move_reg(int r, int s); |
void save_regs(int n); |
void save_reg(int r); |
void vpop(void); |
void vswap(void); |
void vdup(void); |
int get_reg(int rc); |
int get_reg_ex(int rc,int rc2); |
struct macro_level { |
struct macro_level *prev; |
int *p; |
}; |
static void macro_subst(TokenString *tok_str, Sym **nested_list, |
const int *macro_str, struct macro_level **can_read_stream); |
void gen_op(int op); |
void force_charshort_cast(int t); |
static void gen_cast(CType *type); |
void vstore(void); |
static Sym *sym_find(int v); |
static Sym *sym_push(int v, CType *type, int r, int c); |
/* type handling */ |
static int type_size(CType *type, int *a); |
static inline CType *pointed_type(CType *type); |
static int pointed_size(CType *type); |
static int lvalue_type(int t); |
static int parse_btype(CType *type, AttributeDef *ad); |
static void type_decl(CType *type, AttributeDef *ad, int *v, int td); |
static int is_compatible_types(CType *type1, CType *type2); |
int ieee_finite(double d); |
void error(const char *fmt, ...); |
void vpushi(int v); |
void vrott(int n); |
void vnrott(int n); |
void lexpand_nr(void); |
static void vpush_global_sym(CType *type, int v); |
void vset(CType *type, int r, int v); |
void type_to_str(char *buf, int buf_size, |
CType *type, const char *varstr); |
char *get_tok_str(int v, CValue *cv); |
static Sym *get_sym_ref(CType *type, Section *sec, |
unsigned long offset, unsigned long size); |
static Sym *external_global_sym(int v, CType *type, int r); |
/* section generation */ |
static void section_realloc(Section *sec, unsigned long new_size); |
static void *section_ptr_add(Section *sec, unsigned long size); |
static void put_extern_sym(Sym *sym, Section *section, |
unsigned long value, unsigned long size); |
static void greloc(Section *s, Sym *sym, unsigned long addr, int type); |
static int put_elf_str(Section *s, const char *sym); |
static int put_elf_sym(Section *s, |
unsigned long value, unsigned long size, |
int info, int other, int shndx, const char *name); |
static int add_elf_sym(Section *s, unsigned long value, unsigned long size, |
int info, int other, int sh_num, const char *name); |
static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, |
int type, int symbol); |
static void put_stabs(const char *str, int type, int other, int desc, |
unsigned long value); |
static void put_stabs_r(const char *str, int type, int other, int desc, |
unsigned long value, Section *sec, int sym_index); |
static void put_stabn(int type, int other, int desc, int value); |
static void put_stabd(int type, int other, int desc); |
static int tcc_add_dll(TCCState *s, const char *filename, int flags); |
#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */ |
#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */ |
static int tcc_add_file_internal(TCCState *s, const char *filename, int flags); |
/* tcccoff.c */ |
int tcc_output_coff(TCCState *s1, FILE *f); |
/* tccpe.c */ |
void *resolve_sym(TCCState *s1, const char *sym, int type); |
int pe_load_def_file(struct TCCState *s1, FILE *fp); |
void pe_setup_paths(struct TCCState *s1, int *p_output_type, const char **p_outfile, char *first_file); |
unsigned long pe_add_runtime(struct TCCState *s1); |
int tcc_output_pe(struct TCCState *s1, const char *filename); |
/* tccasm.c */ |
#ifdef CONFIG_TCC_ASM |
typedef struct ExprValue { |
uint32_t v; |
Sym *sym; |
} ExprValue; |
#define MAX_ASM_OPERANDS 30 |
typedef struct ASMOperand { |
int id; /* GCC 3 optionnal identifier (0 if number only supported */ |
char *constraint; |
char asm_str[16]; /* computed asm string for operand */ |
SValue *vt; /* C value of the expression */ |
int ref_index; /* if >= 0, gives reference to a output constraint */ |
int input_index; /* if >= 0, gives reference to an input constraint */ |
int priority; /* priority, used to assign registers */ |
int reg; /* if >= 0, register number used for this operand */ |
int is_llong; /* true if double register value */ |
int is_memory; /* true if memory operand */ |
int is_rw; /* for '+' modifier */ |
} ASMOperand; |
static void asm_expr(TCCState *s1, ExprValue *pe); |
static int asm_int_expr(TCCState *s1); |
static int find_constraint(ASMOperand *operands, int nb_operands, |
const char *name, const char **pp); |
static int tcc_assemble(TCCState *s1, int do_preprocess); |
#endif |
static void asm_instr(void); |
static void asm_global_instr(void); |
/* true if float/double/long double type */ |
static inline int is_float(int t) |
{ |
int bt; |
bt = t & VT_BTYPE; |
return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT; |
} |
#ifdef TCC_TARGET_I386 |
#include "i386-gen.c" |
#endif |
#ifdef TCC_TARGET_ARM |
#include "arm-gen.c" |
#endif |
#ifdef TCC_TARGET_C67 |
#include "c67-gen.c" |
#endif |
#ifdef CONFIG_TCC_STATIC |
#define RTLD_LAZY 0x001 |
#define RTLD_NOW 0x002 |
#define RTLD_GLOBAL 0x100 |
#define RTLD_DEFAULT NULL |
/* dummy function for profiling */ |
void *dlopen(const char *filename, int flag) |
{ |
return NULL; |
} |
const char *dlerror(void) |
{ |
return "error"; |
} |
typedef struct TCCSyms { |
char *str; |
void *ptr; |
} TCCSyms; |
#define TCCSYM(a) { #a, &a, }, |
/* add the symbol you want here if no dynamic linking is done */ |
static TCCSyms tcc_syms[] = { |
#if !defined(CONFIG_TCCBOOT) |
TCCSYM(printf) |
TCCSYM(printf) |
TCCSYM(fopen) |
TCCSYM(fclose) |
#endif |
{ NULL, NULL }, |
}; |
void *resolve_sym(TCCState *s1, const char *symbol, int type) |
{ |
TCCSyms *p; |
p = tcc_syms; |
while (p->str != NULL) { |
if (!strcmp(p->str, symbol)) |
return p->ptr; |
p++; |
} |
return NULL; |
} |
#elif !defined(WIN32) |
//------------------------------------------------------------------------- |
//#include <dlfcn.h> |
void *resolve_sym(TCCState *s1, const char *sym, int type) |
{ |
return(0); |
//return dlsym(RTLD_DEFAULT, sym); |
} |
//------------------------------------------------------------------------- |
#endif |
/********************************************************/ |
/* we use our own 'finite' function to avoid potential problems with |
non standard math libs */ |
/* XXX: endianness dependent */ |
int ieee_finite(double d) |
{ |
int *p = (int *)&d; |
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; |
} |
/* copy a string and truncate it. */ |
static char *pstrcpy(char *buf, int buf_size, const char *s) |
{ |
char *q, *q_end; |
int c; |
if (buf_size > 0) { |
q = buf; |
q_end = buf + buf_size - 1; |
while (q < q_end) { |
c = *s++; |
if (c == '\0') |
break; |
*q++ = c; |
} |
*q = '\0'; |
} |
return buf; |
} |
/* strcat and truncate. */ |
static char *pstrcat(char *buf, int buf_size, const char *s) |
{ |
int len; |
len = strlen(buf); |
if (len < buf_size) |
pstrcpy(buf + len, buf_size - len, s); |
return buf; |
} |
static int strstart(const char *str, const char *val, const char **ptr) |
{ |
const char *p, *q; |
p = str; |
q = val; |
while (*q != '\0') { |
if (*p != *q) |
return 0; |
p++; |
q++; |
} |
if (ptr) |
*ptr = p; |
return 1; |
} |
/* memory management */ |
#ifdef MEM_DEBUG |
int mem_cur_size; |
int mem_max_size; |
#endif |
static inline void tcc_free(void *ptr) |
{ |
#ifdef MEM_DEBUG |
mem_cur_size -= malloc_usable_size(ptr); |
#endif |
free(ptr); |
} |
static void *tcc_malloc(unsigned long size) |
{ |
void *ptr; |
ptr = malloc(size); |
if (!ptr && size) |
error("memory full"); |
#ifdef MEM_DEBUG |
mem_cur_size += malloc_usable_size(ptr); |
if (mem_cur_size > mem_max_size) |
mem_max_size = mem_cur_size; |
#endif |
return ptr; |
} |
static void *tcc_mallocz(unsigned long size) |
{ |
void *ptr; |
ptr = tcc_malloc(size); |
memset(ptr, 0, size); |
return ptr; |
} |
static inline void *tcc_realloc(void *ptr, unsigned long size) |
{ |
void *ptr1; |
#ifdef MEM_DEBUG |
mem_cur_size -= malloc_usable_size(ptr); |
#endif |
ptr1 = realloc(ptr, size); |
#ifdef MEM_DEBUG |
/* NOTE: count not correct if alloc error, but not critical */ |
mem_cur_size += malloc_usable_size(ptr1); |
if (mem_cur_size > mem_max_size) |
mem_max_size = mem_cur_size; |
#endif |
return ptr1; |
} |
static char *tcc_strdup(const char *str) |
{ |
char *ptr; |
ptr = tcc_malloc(strlen(str) + 1); |
strcpy(ptr, str); |
return ptr; |
} |
#define free(p) use_tcc_free(p) |
#define malloc(s) use_tcc_malloc(s) |
#define realloc(p, s) use_tcc_realloc(p, s) |
static void dynarray_add(void ***ptab, int *nb_ptr, void *data) |
{ |
int nb, nb_alloc; |
void **pp; |
nb = *nb_ptr; |
pp = *ptab; |
/* every power of two we double array size */ |
if ((nb & (nb - 1)) == 0) { |
if (!nb) |
nb_alloc = 1; |
else |
nb_alloc = nb * 2; |
pp = tcc_realloc(pp, nb_alloc * sizeof(void *)); |
if (!pp) |
error("memory full"); |
*ptab = pp; |
} |
pp[nb++] = data; |
*nb_ptr = nb; |
} |
/* symbol allocator */ |
static Sym *__sym_malloc(void) |
{ |
Sym *sym_pool, *sym, *last_sym; |
int i; |
sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym)); |
last_sym = sym_free_first; |
sym = sym_pool; |
for(i = 0; i < SYM_POOL_NB; i++) { |
sym->next = last_sym; |
last_sym = sym; |
sym++; |
} |
sym_free_first = last_sym; |
return last_sym; |
} |
static inline Sym *sym_malloc(void) |
{ |
Sym *sym; |
sym = sym_free_first; |
if (!sym) |
sym = __sym_malloc(); |
sym_free_first = sym->next; |
return sym; |
} |
static inline void sym_free(Sym *sym) |
{ |
sym->next = sym_free_first; |
sym_free_first = sym; |
} |
Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags) |
{ |
Section *sec; |
sec = tcc_mallocz(sizeof(Section) + strlen(name)); |
strcpy(sec->name, name); |
sec->sh_type = sh_type; |
sec->sh_flags = sh_flags; |
switch(sh_type) { |
case SHT_HASH: |
case SHT_REL: |
case SHT_DYNSYM: |
case SHT_SYMTAB: |
case SHT_DYNAMIC: |
sec->sh_addralign = 4; |
break; |
case SHT_STRTAB: |
sec->sh_addralign = 1; |
break; |
default: |
sec->sh_addralign = 32; /* default conservative alignment */ |
break; |
} |
/* only add section if not private */ |
if (!(sh_flags & SHF_PRIVATE)) { |
sec->sh_num = s1->nb_sections; |
dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec); |
} |
return sec; |
} |
static void free_section(Section *s) |
{ |
tcc_free(s->data); |
tcc_free(s); |
} |
/* realloc section and set its content to zero */ |
static void section_realloc(Section *sec, unsigned long new_size) |
{ |
unsigned long size; |
unsigned char *data; |
size = sec->data_allocated; |
if (size == 0) |
size = 1; |
while (size < new_size) |
size = size * 2; |
data = tcc_realloc(sec->data, size); |
if (!data) |
error("memory full"); |
memset(data + sec->data_allocated, 0, size - sec->data_allocated); |
sec->data = data; |
sec->data_allocated = size; |
} |
/* reserve at least 'size' bytes in section 'sec' from |
sec->data_offset. */ |
static void *section_ptr_add(Section *sec, unsigned long size) |
{ |
unsigned long offset, offset1; |
offset = sec->data_offset; |
offset1 = offset + size; |
if (offset1 > sec->data_allocated) |
section_realloc(sec, offset1); |
sec->data_offset = offset1; |
return sec->data + offset; |
} |
/* return a reference to a section, and create it if it does not |
exists */ |
Section *find_section(TCCState *s1, const char *name) |
{ |
Section *sec; |
int i; |
for(i = 1; i < s1->nb_sections; i++) { |
sec = s1->sections[i]; |
if (!strcmp(name, sec->name)) |
return sec; |
} |
/* sections are created as PROGBITS */ |
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC); |
} |
#define SECTION_ABS ((void *)1) |
/* update sym->c so that it points to an external symbol in section |
'section' with value 'value' */ |
static void put_extern_sym2(Sym *sym, Section *section, |
unsigned long value, unsigned long size, |
int can_add_underscore) |
{ |
int sym_type, sym_bind, sh_num, info; |
Elf32_Sym *esym; |
const char *name; |
char buf1[256]; |
if (section == NULL) |
sh_num = SHN_UNDEF; |
else if (section == SECTION_ABS) |
sh_num = SHN_ABS; |
else |
sh_num = section->sh_num; |
if (!sym->c) { |
if ((sym->type.t & VT_BTYPE) == VT_FUNC) |
sym_type = STT_FUNC; |
else |
sym_type = STT_OBJECT; |
if (sym->type.t & VT_STATIC) |
sym_bind = STB_LOCAL; |
else |
sym_bind = STB_GLOBAL; |
name = get_tok_str(sym->v, NULL); |
#ifdef CONFIG_TCC_BCHECK |
if (do_bounds_check) { |
char buf[32]; |
/* XXX: avoid doing that for statics ? */ |
/* if bound checking is activated, we change some function |
names by adding the "__bound" prefix */ |
switch(sym->v) { |
#if 0 |
/* XXX: we rely only on malloc hooks */ |
case TOK_malloc: |
case TOK_free: |
case TOK_realloc: |
case TOK_memalign: |
case TOK_calloc: |
#endif |
case TOK_memcpy: |
case TOK_memmove: |
case TOK_memset: |
case TOK_strlen: |
case TOK_strcpy: |
strcpy(buf, "__bound_"); |
strcat(buf, name); |
name = buf; |
break; |
} |
} |
#endif |
if (tcc_state->leading_underscore && can_add_underscore) { |
buf1[0] = '_'; |
pstrcpy(buf1 + 1, sizeof(buf1) - 1, name); |
name = buf1; |
} |
info = ELF32_ST_INFO(sym_bind, sym_type); |
sym->c = add_elf_sym(symtab_section, value, size, info, 0, sh_num, name); |
} else { |
esym = &((Elf32_Sym *)symtab_section->data)[sym->c]; |
esym->st_value = value; |
esym->st_size = size; |
esym->st_shndx = sh_num; |
} |
} |
static void put_extern_sym(Sym *sym, Section *section, |
unsigned long value, unsigned long size) |
{ |
put_extern_sym2(sym, section, value, size, 1); |
} |
/* add a new relocation entry to symbol 'sym' in section 's' */ |
static void greloc(Section *s, Sym *sym, unsigned long offset, int type) |
{ |
if (!sym->c) |
put_extern_sym(sym, NULL, 0, 0); |
/* now we can add ELF relocation info */ |
put_elf_reloc(symtab_section, s, offset, type, sym->c); |
} |
static inline int isid(int c) |
{ |
return (c >= 'a' && c <= 'z') || |
(c >= 'A' && c <= 'Z') || |
c == '_'; |
} |
static inline int isnum(int c) |
{ |
return c >= '0' && c <= '9'; |
} |
static inline int isoct(int c) |
{ |
return c >= '0' && c <= '7'; |
} |
static inline int toup(int c) |
{ |
if (c >= 'a' && c <= 'z') |
return c - 'a' + 'A'; |
else |
return c; |
} |
static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap) |
{ |
int len; |
len = strlen(buf); |
vsnprintf(buf + len, buf_size - len, fmt, ap); |
} |
static void strcat_printf(char *buf, int buf_size, const char *fmt, ...) |
{ |
va_list ap; |
va_start(ap, fmt); |
strcat_vprintf(buf, buf_size, fmt, ap); |
va_end(ap); |
} |
void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) |
{ |
char buf[2048]; |
BufferedFile **f; |
buf[0] = '\0'; |
if (file) { |
for(f = s1->include_stack; f < s1->include_stack_ptr; f++) |
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n", |
(*f)->filename, (*f)->line_num); |
if (file->line_num > 0) { |
strcat_printf(buf, sizeof(buf), |
"%s:%d: ", file->filename, file->line_num); |
} else { |
strcat_printf(buf, sizeof(buf), |
"%s: ", file->filename); |
} |
} else { |
strcat_printf(buf, sizeof(buf), |
"tcc: "); |
} |
if (is_warning) |
strcat_printf(buf, sizeof(buf), "warning: "); |
strcat_vprintf(buf, sizeof(buf), fmt, ap); |
if (!s1->error_func) { |
/* default case: stderr */ |
printf("%s\n", buf); |
} else { |
s1->error_func(s1->error_opaque, buf); |
} |
if (!is_warning || s1->warn_error) |
s1->nb_errors++; |
} |
#ifdef LIBTCC |
void tcc_set_error_func(TCCState *s, void *error_opaque, |
void (*error_func)(void *opaque, const char *msg)) |
{ |
s->error_opaque = error_opaque; |
s->error_func = error_func; |
} |
#endif |
/* error without aborting current compilation */ |
void error_noabort(const char *fmt, ...) |
{ |
TCCState *s1 = tcc_state; |
va_list ap; |
va_start(ap, fmt); |
error1(s1, 0, fmt, ap); |
va_end(ap); |
} |
void error(const char *fmt, ...) |
{ |
TCCState *s1 = tcc_state; |
va_list ap; |
va_start(ap, fmt); |
error1(s1, 0, fmt, ap); |
va_end(ap); |
/* better than nothing: in some cases, we accept to handle errors */ |
if (s1->error_set_jmp_enabled) { |
longjmp(s1->error_jmp_buf, 1); |
} else { |
/* XXX: eliminate this someday */ |
exit(1); |
} |
} |
void expect(const char *msg) |
{ |
error("%s expected", msg); |
} |
void warning(const char *fmt, ...) |
{ |
TCCState *s1 = tcc_state; |
va_list ap; |
if (s1->warn_none) |
return; |
va_start(ap, fmt); |
error1(s1, 1, fmt, ap); |
va_end(ap); |
} |
void skip(int c) |
{ |
if (tok != c) |
error("'%c' expected", c); |
next(); |
} |
static void test_lvalue(void) |
{ |
if (!(vtop->r & VT_LVAL)) |
expect("lvalue"); |
} |
/* allocate a new token */ |
static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len) |
{ |
TokenSym *ts, **ptable; |
int i; |
if (tok_ident >= SYM_FIRST_ANOM) |
error("memory full"); |
/* expand token table if needed */ |
i = tok_ident - TOK_IDENT; |
if ((i % TOK_ALLOC_INCR) == 0) { |
ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *)); |
if (!ptable) |
error("memory full"); |
table_ident = ptable; |
} |
ts = tcc_malloc(sizeof(TokenSym) + len); |
table_ident[i] = ts; |
ts->tok = tok_ident++; |
ts->sym_define = NULL; |
ts->sym_label = NULL; |
ts->sym_struct = NULL; |
ts->sym_identifier = NULL; |
ts->len = len; |
ts->hash_next = NULL; |
memcpy(ts->str, str, len); |
ts->str[len] = '\0'; |
*pts = ts; |
return ts; |
} |
#define TOK_HASH_INIT 1 |
#define TOK_HASH_FUNC(h, c) ((h) * 263 + (c)) |
/* find a token and add it if not found */ |
static TokenSym *tok_alloc(const char *str, int len) |
{ |
TokenSym *ts, **pts; |
int i; |
unsigned int h; |
h = TOK_HASH_INIT; |
for(i=0;i<len;i++) |
h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]); |
h &= (TOK_HASH_SIZE - 1); |
pts = &hash_ident[h]; |
for(;;) { |
ts = *pts; |
if (!ts) |
break; |
if (ts->len == len && !memcmp(ts->str, str, len)) |
return ts; |
pts = &(ts->hash_next); |
} |
return tok_alloc_new(pts, str, len); |
} |
/* CString handling */ |
static void cstr_realloc(CString *cstr, int new_size) |
{ |
int size; |
void *data; |
size = cstr->size_allocated; |
if (size == 0) |
size = 8; /* no need to allocate a too small first string */ |
while (size < new_size) |
size = size * 2; |
data = tcc_realloc(cstr->data_allocated, size); |
if (!data) |
error("memory full"); |
cstr->data_allocated = data; |
cstr->size_allocated = size; |
cstr->data = data; |
} |
/* add a byte */ |
static inline void cstr_ccat(CString *cstr, int ch) |
{ |
int size; |
size = cstr->size + 1; |
if (size > cstr->size_allocated) |
cstr_realloc(cstr, size); |
((unsigned char *)cstr->data)[size - 1] = ch; |
cstr->size = size; |
} |
static void cstr_cat(CString *cstr, const char *str) |
{ |
int c; |
for(;;) { |
c = *str; |
if (c == '\0') |
break; |
cstr_ccat(cstr, c); |
str++; |
} |
} |
/* add a wide char */ |
static void cstr_wccat(CString *cstr, int ch) |
{ |
int size; |
size = cstr->size + sizeof(int); |
if (size > cstr->size_allocated) |
cstr_realloc(cstr, size); |
*(int *)(((unsigned char *)cstr->data) + size - sizeof(int)) = ch; |
cstr->size = size; |
} |
static void cstr_new(CString *cstr) |
{ |
memset(cstr, 0, sizeof(CString)); |
} |
/* free string and reset it to NULL */ |
static void cstr_free(CString *cstr) |
{ |
tcc_free(cstr->data_allocated); |
cstr_new(cstr); |
} |
#define cstr_reset(cstr) cstr_free(cstr) |
/* XXX: unicode ? */ |
static void add_char(CString *cstr, int c) |
{ |
if (c == '\'' || c == '\"' || c == '\\') { |
/* XXX: could be more precise if char or string */ |
cstr_ccat(cstr, '\\'); |
} |
if (c >= 32 && c <= 126) { |
cstr_ccat(cstr, c); |
} else { |
cstr_ccat(cstr, '\\'); |
if (c == '\n') { |
cstr_ccat(cstr, 'n'); |
} else { |
cstr_ccat(cstr, '0' + ((c >> 6) & 7)); |
cstr_ccat(cstr, '0' + ((c >> 3) & 7)); |
cstr_ccat(cstr, '0' + (c & 7)); |
} |
} |
} |
/* XXX: buffer overflow */ |
/* XXX: float tokens */ |
char *get_tok_str(int v, CValue *cv) |
{ |
static char buf[STRING_MAX_SIZE + 1]; |
static CString cstr_buf; |
CString *cstr; |
unsigned char *q; |
char *p; |
int i, len; |
/* NOTE: to go faster, we give a fixed buffer for small strings */ |
cstr_reset(&cstr_buf); |
cstr_buf.data = buf; |
cstr_buf.size_allocated = sizeof(buf); |
p = buf; |
switch(v) { |
case TOK_CINT: |
case TOK_CUINT: |
/* XXX: not quite exact, but only useful for testing */ |
sprintf(p, "%u", cv->ui); |
break; |
case TOK_CLLONG: |
case TOK_CULLONG: |
/* XXX: not quite exact, but only useful for testing */ |
sprintf(p, "%Lu", cv->ull); |
break; |
case TOK_CCHAR: |
case TOK_LCHAR: |
cstr_ccat(&cstr_buf, '\''); |
add_char(&cstr_buf, cv->i); |
cstr_ccat(&cstr_buf, '\''); |
cstr_ccat(&cstr_buf, '\0'); |
break; |
case TOK_PPNUM: |
cstr = cv->cstr; |
len = cstr->size - 1; |
for(i=0;i<len;i++) |
add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]); |
cstr_ccat(&cstr_buf, '\0'); |
break; |
case TOK_STR: |
case TOK_LSTR: |
cstr = cv->cstr; |
cstr_ccat(&cstr_buf, '\"'); |
if (v == TOK_STR) { |
len = cstr->size - 1; |
for(i=0;i<len;i++) |
add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]); |
} else { |
len = (cstr->size / sizeof(int)) - 1; |
for(i=0;i<len;i++) |
add_char(&cstr_buf, ((int *)cstr->data)[i]); |
} |
cstr_ccat(&cstr_buf, '\"'); |
cstr_ccat(&cstr_buf, '\0'); |
break; |
case TOK_LT: |
v = '<'; |
goto addv; |
case TOK_GT: |
v = '>'; |
goto addv; |
case TOK_A_SHL: |
return strcpy(p, "<<="); |
case TOK_A_SAR: |
return strcpy(p, ">>="); |
default: |
if (v < TOK_IDENT) { |
/* search in two bytes table */ |
q = tok_two_chars; |
while (*q) { |
if (q[2] == v) { |
*p++ = q[0]; |
*p++ = q[1]; |
*p = '\0'; |
return buf; |
} |
q += 3; |
} |
addv: |
*p++ = v; |
*p = '\0'; |
} else if (v < tok_ident) { |
return table_ident[v - TOK_IDENT]->str; |
} else if (v >= SYM_FIRST_ANOM) { |
/* special name for anonymous symbol */ |
sprintf(p, "L.%u", v - SYM_FIRST_ANOM); |
} else { |
/* should never happen */ |
return NULL; |
} |
break; |
} |
return cstr_buf.data; |
} |
/* push, without hashing */ |
static Sym *sym_push2(Sym **ps, int v, int t, int c) |
{ |
Sym *s; |
s = sym_malloc(); |
s->v = v; |
s->type.t = t; |
s->c = c; |
s->next = NULL; |
/* add in stack */ |
s->prev = *ps; |
*ps = s; |
return s; |
} |
/* find a symbol and return its associated structure. 's' is the top |
of the symbol stack */ |
static Sym *sym_find2(Sym *s, int v) |
{ |
while (s) { |
if (s->v == v) |
return s; |
s = s->prev; |
} |
return NULL; |
} |
/* structure lookup */ |
static inline Sym *struct_find(int v) |
{ |
v -= TOK_IDENT; |
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) |
return NULL; |
return table_ident[v]->sym_struct; |
} |
/* find an identifier */ |
static inline Sym *sym_find(int v) |
{ |
v -= TOK_IDENT; |
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) |
return NULL; |
return table_ident[v]->sym_identifier; |
} |
/* push a given symbol on the symbol stack */ |
static Sym *sym_push(int v, CType *type, int r, int c) |
{ |
Sym *s, **ps; |
TokenSym *ts; |
if (local_stack) |
ps = &local_stack; |
else |
ps = &global_stack; |
s = sym_push2(ps, v, type->t, c); |
s->type.ref = type->ref; |
s->r = r; |
/* don't record fields or anonymous symbols */ |
/* XXX: simplify */ |
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { |
/* record symbol in token array */ |
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT]; |
if (v & SYM_STRUCT) |
ps = &ts->sym_struct; |
else |
ps = &ts->sym_identifier; |
s->prev_tok = *ps; |
*ps = s; |
} |
return s; |
} |
/* push a global identifier */ |
static Sym *global_identifier_push(int v, int t, int c) |
{ |
Sym *s, **ps; |
s = sym_push2(&global_stack, v, t, c); |
/* don't record anonymous symbol */ |
if (v < SYM_FIRST_ANOM) { |
ps = &table_ident[v - TOK_IDENT]->sym_identifier; |
/* modify the top most local identifier, so that |
sym_identifier will point to 's' when popped */ |
while (*ps != NULL) |
ps = &(*ps)->prev_tok; |
s->prev_tok = NULL; |
*ps = s; |
} |
return s; |
} |
/* pop symbols until top reaches 'b' */ |
static void sym_pop(Sym **ptop, Sym *b) |
{ |
Sym *s, *ss, **ps; |
TokenSym *ts; |
int v; |
s = *ptop; |
while(s != b) { |
ss = s->prev; |
v = s->v; |
/* remove symbol in token array */ |
/* XXX: simplify */ |
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { |
ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT]; |
if (v & SYM_STRUCT) |
ps = &ts->sym_struct; |
else |
ps = &ts->sym_identifier; |
*ps = s->prev_tok; |
} |
sym_free(s); |
s = ss; |
} |
*ptop = b; |
} |
/* I/O layer */ |
BufferedFile *tcc_open(TCCState *s1, const char *filename) |
{ |
int fd; |
BufferedFile *bf; |
fd = open(filename, O_RDONLY | O_BINARY); |
if (fd < 0) |
return NULL; |
bf = tcc_malloc(sizeof(BufferedFile)); |
if (!bf) { |
close(fd); |
return NULL; |
} |
bf->fd = fd; |
bf->buf_ptr = bf->buffer; |
bf->buf_end = bf->buffer; |
bf->buffer[0] = CH_EOB; /* put eob symbol */ |
pstrcpy(bf->filename, sizeof(bf->filename), filename); |
bf->line_num = 1; |
bf->ifndef_macro = 0; |
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; |
// printf("opening '%s'\n", filename); |
return bf; |
} |
void tcc_close(BufferedFile *bf) |
{ |
total_lines += bf->line_num; |
close(bf->fd); |
tcc_free(bf); |
} |
/* fill input buffer and peek next char */ |
static int tcc_peekc_slow(BufferedFile *bf) |
{ |
int len; |
/* only tries to read if really end of buffer */ |
if (bf->buf_ptr >= bf->buf_end) { |
if (bf->fd != -1) { |
#if defined(PARSE_DEBUG) |
len = 8; |
#else |
len = IO_BUF_SIZE; |
#endif |
len = read(bf->fd, bf->buffer, len); |
if (len < 0) |
len = 0; |
} else { |
len = 0; |
} |
total_bytes += len; |
bf->buf_ptr = bf->buffer; |
bf->buf_end = bf->buffer + len; |
*bf->buf_end = CH_EOB; |
} |
if (bf->buf_ptr < bf->buf_end) { |
return bf->buf_ptr[0]; |
} else { |
bf->buf_ptr = bf->buf_end; |
return CH_EOF; |
} |
} |
/* return the current character, handling end of block if necessary |
(but not stray) */ |
static int handle_eob(void) |
{ |
return tcc_peekc_slow(file); |
} |
/* read next char from current input file and handle end of input buffer */ |
static inline void input(void) |
{ |
ch = *(++(file->buf_ptr)); |
/* end of buffer/file handling */ |
if (ch == CH_EOB) |
ch = handle_eob(); |
} |
/* handle '\[\r]\n' */ |
static void handle_stray(void) |
{ |
while (ch == '\\') { |
input(); |
if (ch == '\n') { |
file->line_num++; |
input(); |
} else if (ch == '\r') { |
input(); |
if (ch != '\n') |
goto fail; |
file->line_num++; |
input(); |
} else { |
fail: |
error("stray '\\' in program"); |
} |
} |
} |
/* skip the stray and handle the \\n case. Output an error if |
incorrect char after the stray */ |
static int handle_stray1(uint8_t *p) |
{ |
int c; |
if (p >= file->buf_end) { |
file->buf_ptr = p; |
c = handle_eob(); |
p = file->buf_ptr; |
if (c == '\\') |
goto parse_stray; |
} else { |
parse_stray: |
file->buf_ptr = p; |
ch = *p; |
handle_stray(); |
p = file->buf_ptr; |
c = *p; |
} |
return c; |
} |
/* handle just the EOB case, but not stray */ |
#define PEEKC_EOB(c, p)\ |
{\ |
p++;\ |
c = *p;\ |
if (c == '\\') {\ |
file->buf_ptr = p;\ |
c = handle_eob();\ |
p = file->buf_ptr;\ |
}\ |
} |
/* handle the complicated stray case */ |
#define PEEKC(c, p)\ |
{\ |
p++;\ |
c = *p;\ |
if (c == '\\') {\ |
c = handle_stray1(p);\ |
p = file->buf_ptr;\ |
}\ |
} |
/* input with '\[\r]\n' handling. Note that this function cannot |
handle other characters after '\', so you cannot call it inside |
strings or comments */ |
static void minp(void) |
{ |
input(); |
if (ch == '\\') |
handle_stray(); |
} |
/* single line C++ comments */ |
static uint8_t *parse_line_comment(uint8_t *p) |
{ |
int c; |
p++; |
for(;;) { |
c = *p; |
redo: |
if (c == '\n' || c == CH_EOF) { |
break; |
} else if (c == '\\') { |
file->buf_ptr = p; |
c = handle_eob(); |
p = file->buf_ptr; |
if (c == '\\') { |
PEEKC_EOB(c, p); |
if (c == '\n') { |
file->line_num++; |
PEEKC_EOB(c, p); |
} else if (c == '\r') { |
PEEKC_EOB(c, p); |
if (c == '\n') { |
file->line_num++; |
PEEKC_EOB(c, p); |
} |
} |
} else { |
goto redo; |
} |
} else { |
p++; |
} |
} |
return p; |
} |
/* C comments */ |
static uint8_t *parse_comment(uint8_t *p) |
{ |
int c; |
p++; |
for(;;) { |
/* fast skip loop */ |
for(;;) { |
c = *p; |
if (c == '\n' || c == '*' || c == '\\') |
break; |
p++; |
c = *p; |
if (c == '\n' || c == '*' || c == '\\') |
break; |
p++; |
} |
/* now we can handle all the cases */ |
if (c == '\n') { |
file->line_num++; |
p++; |
} else if (c == '*') { |
p++; |
for(;;) { |
c = *p; |
if (c == '*') { |
p++; |
} else if (c == '/') { |
goto end_of_comment; |
} else if (c == '\\') { |
file->buf_ptr = p; |
c = handle_eob(); |
p = file->buf_ptr; |
if (c == '\\') { |
/* skip '\[\r]\n', otherwise just skip the stray */ |
while (c == '\\') { |
PEEKC_EOB(c, p); |
if (c == '\n') { |
file->line_num++; |
PEEKC_EOB(c, p); |
} else if (c == '\r') { |
PEEKC_EOB(c, p); |
if (c == '\n') { |
file->line_num++; |
PEEKC_EOB(c, p); |
} |
} else { |
goto after_star; |
} |
} |
} |
} else { |
break; |
} |
} |
after_star: ; |
} else { |
/* stray, eob or eof */ |
file->buf_ptr = p; |
c = handle_eob(); |
p = file->buf_ptr; |
if (c == CH_EOF) { |
error("unexpected end of file in comment"); |
} else if (c == '\\') { |
p++; |
} |
} |
} |
end_of_comment: |
p++; |
return p; |
} |
#define cinp minp |
/* space excluding newline */ |
static inline int is_space(int ch) |
{ |
return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r'; |
} |
static inline void skip_spaces(void) |
{ |
while (is_space(ch)) |
cinp(); |
} |
/* parse a string without interpreting escapes */ |
static uint8_t *parse_pp_string(uint8_t *p, |
int sep, CString *str) |
{ |
int c; |
p++; |
for(;;) { |
c = *p; |
if (c == sep) { |
break; |
} else if (c == '\\') { |
file->buf_ptr = p; |
c = handle_eob(); |
p = file->buf_ptr; |
if (c == CH_EOF) { |
unterminated_string: |
/* XXX: indicate line number of start of string */ |
error("missing terminating %c character", sep); |
} else if (c == '\\') { |
/* escape : just skip \[\r]\n */ |
PEEKC_EOB(c, p); |
if (c == '\n') { |
file->line_num++; |
p++; |
} else if (c == '\r') { |
PEEKC_EOB(c, p); |
if (c != '\n') |
expect("'\n' after '\r'"); |
file->line_num++; |
p++; |
} else if (c == CH_EOF) { |
goto unterminated_string; |
} else { |
if (str) { |
cstr_ccat(str, '\\'); |
cstr_ccat(str, c); |
} |
p++; |
} |
} |
} else if (c == '\n') { |
file->line_num++; |
goto add_char; |
} else if (c == '\r') { |
PEEKC_EOB(c, p); |
if (c != '\n') { |
if (str) |
cstr_ccat(str, '\r'); |
} else { |
file->line_num++; |
goto add_char; |
} |
} else { |
add_char: |
if (str) |
cstr_ccat(str, c); |
p++; |
} |
} |
p++; |
return p; |
} |
/* skip block of text until #else, #elif or #endif. skip also pairs of |
#if/#endif */ |
void preprocess_skip(void) |
{ |
int a, start_of_line, c; |
uint8_t *p; |
p = file->buf_ptr; |
start_of_line = 1; |
a = 0; |
for(;;) { |
redo_no_start: |
c = *p; |
switch(c) { |
case ' ': |
case '\t': |
case '\f': |
case '\v': |
case '\r': |
p++; |
goto redo_no_start; |
case '\n': |
start_of_line = 1; |
file->line_num++; |
p++; |
goto redo_no_start; |
case '\\': |
file->buf_ptr = p; |
c = handle_eob(); |
if (c == CH_EOF) { |
expect("#endif"); |
} else if (c == '\\') { |
/* XXX: incorrect: should not give an error */ |
ch = file->buf_ptr[0]; |
handle_stray(); |
} |
p = file->buf_ptr; |
goto redo_no_start; |
/* skip strings */ |
case '\"': |
case '\'': |
p = parse_pp_string(p, c, NULL); |
break; |
/* skip comments */ |
case '/': |
file->buf_ptr = p; |
ch = *p; |
minp(); |
p = file->buf_ptr; |
if (ch == '*') { |
p = parse_comment(p); |
} else if (ch == '/') { |
p = parse_line_comment(p); |
} |
break; |
case '#': |
p++; |
if (start_of_line) { |
file->buf_ptr = p; |
next_nomacro(); |
p = file->buf_ptr; |
if (a == 0 && |
(tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF)) |
goto the_end; |
if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF) |
a++; |
else if (tok == TOK_ENDIF) |
a--; |
} |
break; |
default: |
p++; |
break; |
} |
start_of_line = 0; |
} |
the_end: ; |
file->buf_ptr = p; |
} |
/* ParseState handling */ |
/* XXX: currently, no include file info is stored. Thus, we cannot display |
accurate messages if the function or data definition spans multiple |
files */ |
/* save current parse state in 's' */ |
void save_parse_state(ParseState *s) |
{ |
s->line_num = file->line_num; |
s->macro_ptr = macro_ptr; |
s->tok = tok; |
s->tokc = tokc; |
} |
/* restore parse state from 's' */ |
void restore_parse_state(ParseState *s) |
{ |
file->line_num = s->line_num; |
macro_ptr = s->macro_ptr; |
tok = s->tok; |
tokc = s->tokc; |
} |
/* return the number of additional 'ints' necessary to store the |
token */ |
static inline int tok_ext_size(int t) |
{ |
switch(t) { |
/* 4 bytes */ |
case TOK_CINT: |
case TOK_CUINT: |
case TOK_CCHAR: |
case TOK_LCHAR: |
case TOK_CFLOAT: |
case TOK_LINENUM: |
return 1; |
case TOK_STR: |
case TOK_LSTR: |
case TOK_PPNUM: |
error("unsupported token"); |
return 1; |
case TOK_CDOUBLE: |
case TOK_CLLONG: |
case TOK_CULLONG: |
return 2; |
case TOK_CLDOUBLE: |
return LDOUBLE_SIZE / 4; |
default: |
return 0; |
} |
} |
/* token string handling */ |
static inline void tok_str_new(TokenString *s) |
{ |
s->str = NULL; |
s->len = 0; |
s->allocated_len = 0; |
s->last_line_num = -1; |
} |
static void tok_str_free(int *str) |
{ |
tcc_free(str); |
} |
static int *tok_str_realloc(TokenString *s) |
{ |
int *str, len; |
if (s->allocated_len == 0) { |
len = 8; |
} else { |
len = s->allocated_len * 2; |
} |
str = tcc_realloc(s->str, len * sizeof(int)); |
if (!str) |
error("memory full"); |
s->allocated_len = len; |
s->str = str; |
return str; |
} |
static void tok_str_add(TokenString *s, int t) |
{ |
int len, *str; |
len = s->len; |
str = s->str; |
if (len >= s->allocated_len) |
str = tok_str_realloc(s); |
str[len++] = t; |
s->len = len; |
} |
static void tok_str_add2(TokenString *s, int t, CValue *cv) |
{ |
int len, *str; |
len = s->len; |
str = s->str; |
/* allocate space for worst case */ |
if (len + TOK_MAX_SIZE > s->allocated_len) |
str = tok_str_realloc(s); |
str[len++] = t; |
switch(t) { |
case TOK_CINT: |
case TOK_CUINT: |
case TOK_CCHAR: |
case TOK_LCHAR: |
case TOK_CFLOAT: |
case TOK_LINENUM: |
str[len++] = cv->tab[0]; |
break; |
case TOK_PPNUM: |
case TOK_STR: |
case TOK_LSTR: |
{ |
int nb_words; |
CString *cstr; |
nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2; |
while ((len + nb_words) > s->allocated_len) |
str = tok_str_realloc(s); |
cstr = (CString *)(str + len); |
cstr->data = NULL; |
cstr->size = cv->cstr->size; |
cstr->data_allocated = NULL; |
cstr->size_allocated = cstr->size; |
memcpy((char *)cstr + sizeof(CString), |
cv->cstr->data, cstr->size); |
len += nb_words; |
} |
break; |
case TOK_CDOUBLE: |
case TOK_CLLONG: |
case TOK_CULLONG: |
#if LDOUBLE_SIZE == 8 |
case TOK_CLDOUBLE: |
#endif |
str[len++] = cv->tab[0]; |
str[len++] = cv->tab[1]; |
break; |
#if LDOUBLE_SIZE == 12 |
case TOK_CLDOUBLE: |
str[len++] = cv->tab[0]; |
str[len++] = cv->tab[1]; |
str[len++] = cv->tab[2]; |
#elif LDOUBLE_SIZE != 8 |
#error add long double size support |
#endif |
break; |
default: |
break; |
} |
s->len = len; |
} |
/* add the current parse token in token string 's' */ |
static void tok_str_add_tok(TokenString *s) |
{ |
CValue cval; |
/* save line number info */ |
if (file->line_num != s->last_line_num) { |
s->last_line_num = file->line_num; |
cval.i = s->last_line_num; |
tok_str_add2(s, TOK_LINENUM, &cval); |
} |
tok_str_add2(s, tok, &tokc); |
} |
#if LDOUBLE_SIZE == 12 |
#define LDOUBLE_GET(p, cv) \ |
cv.tab[0] = p[0]; \ |
cv.tab[1] = p[1]; \ |
cv.tab[2] = p[2]; |
#elif LDOUBLE_SIZE == 8 |
#define LDOUBLE_GET(p, cv) \ |
cv.tab[0] = p[0]; \ |
cv.tab[1] = p[1]; |
#else |
#error add long double size support |
#endif |
/* get a token from an integer array and increment pointer |
accordingly. we code it as a macro to avoid pointer aliasing. */ |
#define TOK_GET(t, p, cv) \ |
{ \ |
t = *p++; \ |
switch(t) { \ |
case TOK_CINT: \ |
case TOK_CUINT: \ |
case TOK_CCHAR: \ |
case TOK_LCHAR: \ |
case TOK_CFLOAT: \ |
case TOK_LINENUM: \ |
cv.tab[0] = *p++; \ |
break; \ |
case TOK_STR: \ |
case TOK_LSTR: \ |
case TOK_PPNUM: \ |
cv.cstr = (CString *)p; \ |
cv.cstr->data = (char *)p + sizeof(CString);\ |
p += (sizeof(CString) + cv.cstr->size + 3) >> 2;\ |
break; \ |
case TOK_CDOUBLE: \ |
case TOK_CLLONG: \ |
case TOK_CULLONG: \ |
cv.tab[0] = p[0]; \ |
cv.tab[1] = p[1]; \ |
p += 2; \ |
break; \ |
case TOK_CLDOUBLE: \ |
LDOUBLE_GET(p, cv); \ |
p += LDOUBLE_SIZE / 4; \ |
break; \ |
default: \ |
break; \ |
} \ |
} |
/* defines handling */ |
static inline void define_push(int v, int macro_type, int *str, Sym *first_arg) |
{ |
Sym *s; |
s = sym_push2(&define_stack, v, macro_type, (int)str); |
s->next = first_arg; |
table_ident[v - TOK_IDENT]->sym_define = s; |
} |
/* undefined a define symbol. Its name is just set to zero */ |
static void define_undef(Sym *s) |
{ |
int v; |
v = s->v; |
if (v >= TOK_IDENT && v < tok_ident) |
table_ident[v - TOK_IDENT]->sym_define = NULL; |
s->v = 0; |
} |
static inline Sym *define_find(int v) |
{ |
v -= TOK_IDENT; |
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) |
return NULL; |
return table_ident[v]->sym_define; |
} |
/* free define stack until top reaches 'b' */ |
static void free_defines(Sym *b) |
{ |
Sym *top, *top1; |
int v; |
top = define_stack; |
while (top != b) { |
top1 = top->prev; |
/* do not free args or predefined defines */ |
if (top->c) |
tok_str_free((int *)top->c); |
v = top->v; |
if (v >= TOK_IDENT && v < tok_ident) |
table_ident[v - TOK_IDENT]->sym_define = NULL; |
sym_free(top); |
top = top1; |
} |
define_stack = b; |
} |
/* label lookup */ |
static Sym *label_find(int v) |
{ |
v -= TOK_IDENT; |
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) |
return NULL; |
return table_ident[v]->sym_label; |
} |
static Sym *label_push(Sym **ptop, int v, int flags) |
{ |
Sym *s, **ps; |
s = sym_push2(ptop, v, 0, 0); |
s->r = flags; |
ps = &table_ident[v - TOK_IDENT]->sym_label; |
if (ptop == &global_label_stack) { |
/* modify the top most local identifier, so that |
sym_identifier will point to 's' when popped */ |
while (*ps != NULL) |
ps = &(*ps)->prev_tok; |
} |
s->prev_tok = *ps; |
*ps = s; |
return s; |
} |
/* pop labels until element last is reached. Look if any labels are |
undefined. Define symbols if '&&label' was used. */ |
static void label_pop(Sym **ptop, Sym *slast) |
{ |
Sym *s, *s1; |
for(s = *ptop; s != slast; s = s1) { |
s1 = s->prev; |
if (s->r == LABEL_DECLARED) { |
warning("label '%s' declared but not used", get_tok_str(s->v, NULL)); |
} else if (s->r == LABEL_FORWARD) { |
error("label '%s' used but not defined", |
get_tok_str(s->v, NULL)); |
} else { |
if (s->c) { |
/* define corresponding symbol. A size of |
1 is put. */ |
put_extern_sym(s, cur_text_section, (long)s->next, 1); |
} |
} |
/* remove label */ |
table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok; |
sym_free(s); |
} |
*ptop = slast; |
} |
/* eval an expression for #if/#elif */ |
static int expr_preprocess(void) |
{ |
int c, t; |
TokenString str; |
tok_str_new(&str); |
while (tok != TOK_LINEFEED && tok != TOK_EOF) { |
next(); /* do macro subst */ |
if (tok == TOK_DEFINED) { |
next_nomacro(); |
t = tok; |
if (t == '(') |
next_nomacro(); |
c = define_find(tok) != 0; |
if (t == '(') |
next_nomacro(); |
tok = TOK_CINT; |
tokc.i = c; |
} else if (tok >= TOK_IDENT) { |
/* if undefined macro */ |
tok = TOK_CINT; |
tokc.i = 0; |
} |
tok_str_add_tok(&str); |
} |
tok_str_add(&str, -1); /* simulate end of file */ |
tok_str_add(&str, 0); |
/* now evaluate C constant expression */ |
macro_ptr = str.str; |
next(); |
c = expr_const(); |
macro_ptr = NULL; |
tok_str_free(str.str); |
return c != 0; |
} |
#if defined(PARSE_DEBUG) || defined(PP_DEBUG) |
static void tok_print(int *str) |
{ |
int t; |
CValue cval; |
while (1) { |
TOK_GET(t, str, cval); |
if (!t) |
break; |
printf(" %s", get_tok_str(t, &cval)); |
} |
printf("\n"); |
} |
#endif |
/* parse after #define */ |
static void parse_define(void) |
{ |
Sym *s, *first, **ps; |
int v, t, varg, is_vaargs, c; |
TokenString str; |
v = tok; |
if (v < TOK_IDENT) |
error("invalid macro name '%s'", get_tok_str(tok, &tokc)); |
/* XXX: should check if same macro (ANSI) */ |
first = NULL; |
t = MACRO_OBJ; |
/* '(' must be just after macro definition for MACRO_FUNC */ |
c = file->buf_ptr[0]; |
if (c == '\\') |
c = handle_stray1(file->buf_ptr); |
if (c == '(') { |
next_nomacro(); |
next_nomacro(); |
ps = &first; |
while (tok != ')') { |
varg = tok; |
next_nomacro(); |
is_vaargs = 0; |
if (varg == TOK_DOTS) { |
varg = TOK___VA_ARGS__; |
is_vaargs = 1; |
} else if (tok == TOK_DOTS && gnu_ext) { |
is_vaargs = 1; |
next_nomacro(); |
} |
if (varg < TOK_IDENT) |
error("badly punctuated parameter list"); |
s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0); |
*ps = s; |
ps = &s->next; |
if (tok != ',') |
break; |
next_nomacro(); |
} |
t = MACRO_FUNC; |
} |
tok_str_new(&str); |
next_nomacro(); |
/* EOF testing necessary for '-D' handling */ |
while (tok != TOK_LINEFEED && tok != TOK_EOF) { |
tok_str_add2(&str, tok, &tokc); |
next_nomacro(); |
} |
tok_str_add(&str, 0); |
#ifdef PP_DEBUG |
printf("define %s %d: ", get_tok_str(v, NULL), t); |
tok_print(str.str); |
#endif |
define_push(v, t, str.str, first); |
} |
static inline int hash_cached_include(int type, const char *filename) |
{ |
const unsigned char *s; |
unsigned int h; |
h = TOK_HASH_INIT; |
h = TOK_HASH_FUNC(h, type); |
s = filename; |
while (*s) { |
h = TOK_HASH_FUNC(h, *s); |
s++; |
} |
h &= (CACHED_INCLUDES_HASH_SIZE - 1); |
return h; |
} |
/* XXX: use a token or a hash table to accelerate matching ? */ |
static CachedInclude *search_cached_include(TCCState *s1, |
int type, const char *filename) |
{ |
CachedInclude *e; |
int i, h; |
h = hash_cached_include(type, filename); |
i = s1->cached_includes_hash[h]; |
for(;;) { |
if (i == 0) |
break; |
e = s1->cached_includes[i - 1]; |
if (e->type == type && !strcmp(e->filename, filename)) |
return e; |
i = e->hash_next; |
} |
return NULL; |
} |
static inline void add_cached_include(TCCState *s1, int type, |
const char *filename, int ifndef_macro) |
{ |
CachedInclude *e; |
int h; |
if (search_cached_include(s1, type, filename)) |
return; |
#ifdef INC_DEBUG |
printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL)); |
#endif |
e = tcc_malloc(sizeof(CachedInclude) + strlen(filename)); |
if (!e) |
return; |
e->type = type; |
strcpy(e->filename, filename); |
e->ifndef_macro = ifndef_macro; |
dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e); |
/* add in hash table */ |
h = hash_cached_include(type, filename); |
e->hash_next = s1->cached_includes_hash[h]; |
s1->cached_includes_hash[h] = s1->nb_cached_includes; |
} |
static void pragma_parse(TCCState *s1) |
{ |
int val; |
next(); |
if (tok == TOK_pack) { |
/* |
This may be: |
#pragma pack(1) // set |
#pragma pack() // reset to default |
#pragma pack(push,1) // push & set |
#pragma pack(pop) // restore previous |
*/ |
next(); |
skip('('); |
if (tok == TOK_ASM_pop) { |
next(); |
if (s1->pack_stack_ptr <= s1->pack_stack) { |
stk_error: |
error("out of pack stack"); |
} |
s1->pack_stack_ptr--; |
} else { |
val = 0; |
if (tok != ')') { |
if (tok == TOK_ASM_push) { |
next(); |
if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1) |
goto stk_error; |
s1->pack_stack_ptr++; |
skip(','); |
} |
if (tok != TOK_CINT) { |
pack_error: |
error("invalid pack pragma"); |
} |
val = tokc.i; |
if (val < 1 || val > 16 || (val & (val - 1)) != 0) |
goto pack_error; |
next(); |
} |
*s1->pack_stack_ptr = val; |
skip(')'); |
} |
} |
} |
/* is_bof is true if first non space token at beginning of file */ |
static void preprocess(int is_bof) |
{ |
TCCState *s1 = tcc_state; |
int size, i, c, n, saved_parse_flags; |
char buf[1024], *q, *p; |
char buf1[1024]; |
BufferedFile *f; |
Sym *s; |
CachedInclude *e; |
saved_parse_flags = parse_flags; |
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | |
PARSE_FLAG_LINEFEED; |
next_nomacro(); |
redo: |
switch(tok) { |
case TOK_DEFINE: |
next_nomacro(); |
parse_define(); |
break; |
case TOK_UNDEF: |
next_nomacro(); |
s = define_find(tok); |
/* undefine symbol by putting an invalid name */ |
if (s) |
define_undef(s); |
break; |
case TOK_INCLUDE: |
case TOK_INCLUDE_NEXT: |
ch = file->buf_ptr[0]; |
/* XXX: incorrect if comments : use next_nomacro with a special mode */ |
skip_spaces(); |
if (ch == '<') { |
c = '>'; |
goto read_name; |
} else if (ch == '\"') { |
c = ch; |
read_name: |
/* XXX: better stray handling */ |
minp(); |
q = buf; |
while (ch != c && ch != '\n' && ch != CH_EOF) { |
if ((q - buf) < sizeof(buf) - 1) |
*q++ = ch; |
minp(); |
} |
*q = '\0'; |
minp(); |
#if 0 |
/* eat all spaces and comments after include */ |
/* XXX: slightly incorrect */ |
while (ch1 != '\n' && ch1 != CH_EOF) |
input(); |
#endif |
} else { |
/* computed #include : either we have only strings or |
we have anything enclosed in '<>' */ |
next(); |
buf[0] = '\0'; |
if (tok == TOK_STR) { |
while (tok != TOK_LINEFEED) { |
if (tok != TOK_STR) { |
include_syntax: |
error("'#include' expects \"FILENAME\" or <FILENAME>"); |
} |
pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data); |
next(); |
} |
c = '\"'; |
} else { |
int len; |
while (tok != TOK_LINEFEED) { |
pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc)); |
next(); |
} |
len = strlen(buf); |
/* check syntax and remove '<>' */ |
if (len < 2 || buf[0] != '<' || buf[len - 1] != '>') |
goto include_syntax; |
memmove(buf, buf + 1, len - 2); |
buf[len - 2] = '\0'; |
c = '>'; |
} |
} |
e = search_cached_include(s1, c, buf); |
if (e && define_find(e->ifndef_macro)) { |
/* no need to parse the include because the 'ifndef macro' |
is defined */ |
#ifdef INC_DEBUG |
printf("%s: skipping %s\n", file->filename, buf); |
#endif |
} else { |
if (c == '\"') { |
/* first search in current dir if "header.h" */ |
size = 0; |
p = strrchr(file->filename, '/'); |
if (p) |
size = p + 1 - file->filename; |
if (size > sizeof(buf1) - 1) |
size = sizeof(buf1) - 1; |
memcpy(buf1, file->filename, size); |
buf1[size] = '\0'; |
pstrcat(buf1, sizeof(buf1), buf); |
f = tcc_open(s1, buf1); |
if (f) { |
if (tok == TOK_INCLUDE_NEXT) |
tok = TOK_INCLUDE; |
else |
goto found; |
} |
} |
if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE) |
error("#include recursion too deep"); |
/* now search in all the include paths */ |
n = s1->nb_include_paths + s1->nb_sysinclude_paths; |
for(i = 0; i < n; i++) { |
const char *path; |
if (i < s1->nb_include_paths) |
path = s1->include_paths[i]; |
else |
path = s1->sysinclude_paths[i - s1->nb_include_paths]; |
pstrcpy(buf1, sizeof(buf1), path); |
pstrcat(buf1, sizeof(buf1), "/"); |
pstrcat(buf1, sizeof(buf1), buf); |
f = tcc_open(s1, buf1); |
if (f) { |
if (tok == TOK_INCLUDE_NEXT) |
tok = TOK_INCLUDE; |
else |
goto found; |
} |
} |
error("include file '%s' not found", buf); |
f = NULL; |
found: |
#ifdef INC_DEBUG |
printf("%s: including %s\n", file->filename, buf1); |
#endif |
f->inc_type = c; |
pstrcpy(f->inc_filename, sizeof(f->inc_filename), buf); |
/* push current file in stack */ |
/* XXX: fix current line init */ |
*s1->include_stack_ptr++ = file; |
file = f; |
/* add include file debug info */ |
if (do_debug) { |
put_stabs(file->filename, N_BINCL, 0, 0, 0); |
} |
tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL; |
ch = file->buf_ptr[0]; |
goto the_end; |
} |
break; |
case TOK_IFNDEF: |
c = 1; |
goto do_ifdef; |
case TOK_IF: |
c = expr_preprocess(); |
goto do_if; |
case TOK_IFDEF: |
c = 0; |
do_ifdef: |
next_nomacro(); |
if (tok < TOK_IDENT) |
error("invalid argument for '#if%sdef'", c ? "n" : ""); |
if (is_bof) { |
if (c) { |
#ifdef INC_DEBUG |
printf("#ifndef %s\n", get_tok_str(tok, NULL)); |
#endif |
file->ifndef_macro = tok; |
} |
} |
c = (define_find(tok) != 0) ^ c; |
do_if: |
if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE) |
error("memory full"); |
*s1->ifdef_stack_ptr++ = c; |
goto test_skip; |
case TOK_ELSE: |
if (s1->ifdef_stack_ptr == s1->ifdef_stack) |
error("#else without matching #if"); |
if (s1->ifdef_stack_ptr[-1] & 2) |
error("#else after #else"); |
c = (s1->ifdef_stack_ptr[-1] ^= 3); |
goto test_skip; |
case TOK_ELIF: |
if (s1->ifdef_stack_ptr == s1->ifdef_stack) |
error("#elif without matching #if"); |
c = s1->ifdef_stack_ptr[-1]; |
if (c > 1) |
error("#elif after #else"); |
/* last #if/#elif expression was true: we skip */ |
if (c == 1) |
goto skip; |
c = expr_preprocess(); |
s1->ifdef_stack_ptr[-1] = c; |
test_skip: |
if (!(c & 1)) { |
skip: |
preprocess_skip(); |
is_bof = 0; |
goto redo; |
} |
break; |
case TOK_ENDIF: |
if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr) |
error("#endif without matching #if"); |
s1->ifdef_stack_ptr--; |
/* '#ifndef macro' was at the start of file. Now we check if |
an '#endif' is exactly at the end of file */ |
if (file->ifndef_macro && |
s1->ifdef_stack_ptr == file->ifdef_stack_ptr) { |
file->ifndef_macro_saved = file->ifndef_macro; |
/* need to set to zero to avoid false matches if another |
#ifndef at middle of file */ |
file->ifndef_macro = 0; |
while (tok != TOK_LINEFEED) |
next_nomacro(); |
tok_flags |= TOK_FLAG_ENDIF; |
goto the_end; |
} |
break; |
case TOK_LINE: |
next(); |
if (tok != TOK_CINT) |
error("#line"); |
file->line_num = tokc.i - 1; /* the line number will be incremented after */ |
next(); |
if (tok != TOK_LINEFEED) { |
if (tok != TOK_STR) |
error("#line"); |
pstrcpy(file->filename, sizeof(file->filename), |
(char *)tokc.cstr->data); |
} |
break; |
case TOK_ERROR: |
case TOK_WARNING: |
c = tok; |
ch = file->buf_ptr[0]; |
skip_spaces(); |
q = buf; |
while (ch != '\n' && ch != CH_EOF) { |
if ((q - buf) < sizeof(buf) - 1) |
*q++ = ch; |
minp(); |
} |
*q = '\0'; |
if (c == TOK_ERROR) |
error("#error %s", buf); |
else |
warning("#warning %s", buf); |
break; |
case TOK_PRAGMA: |
pragma_parse(s1); |
break; |
default: |
if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_CINT) { |
/* '!' is ignored to allow C scripts. numbers are ignored |
to emulate cpp behaviour */ |
} else { |
if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS)) |
error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc)); |
} |
break; |
} |
/* ignore other preprocess commands or #! for C scripts */ |
while (tok != TOK_LINEFEED) |
next_nomacro(); |
the_end: |
parse_flags = saved_parse_flags; |
} |
/* evaluate escape codes in a string. */ |
static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long) |
{ |
int c, n; |
const uint8_t *p; |
p = buf; |
for(;;) { |
c = *p; |
if (c == '\0') |
break; |
if (c == '\\') { |
p++; |
/* escape */ |
c = *p; |
switch(c) { |
case '0': case '1': case '2': case '3': |
case '4': case '5': case '6': case '7': |
/* at most three octal digits */ |
n = c - '0'; |
p++; |
c = *p; |
if (isoct(c)) { |
n = n * 8 + c - '0'; |
p++; |
c = *p; |
if (isoct(c)) { |
n = n * 8 + c - '0'; |
p++; |
} |
} |
c = n; |
goto add_char_nonext; |
case 'x': |
p++; |
n = 0; |
for(;;) { |
c = *p; |
if (c >= 'a' && c <= 'f') |
c = c - 'a' + 10; |
else if (c >= 'A' && c <= 'F') |
c = c - 'A' + 10; |
else if (isnum(c)) |
c = c - '0'; |
else |
break; |
n = n * 16 + c; |
p++; |
} |
c = n; |
goto add_char_nonext; |
case 'a': |
c = '\a'; |
break; |
case 'b': |
c = '\b'; |
break; |
case 'f': |
c = '\f'; |
break; |
case 'n': |
c = '\n'; |
break; |
case 'r': |
c = '\r'; |
break; |
case 't': |
c = '\t'; |
break; |
case 'v': |
c = '\v'; |
break; |
case 'e': |
if (!gnu_ext) |
goto invalid_escape; |
c = 27; |
break; |
case '\'': |
case '\"': |
case '\\': |
case '?': |
break; |
default: |
invalid_escape: |
if (c >= '!' && c <= '~') |
warning("unknown escape sequence: \'\\%c\'", c); |
else |
warning("unknown escape sequence: \'\\x%x\'", c); |
break; |
} |
} |
p++; |
add_char_nonext: |
if (!is_long) |
cstr_ccat(outstr, c); |
else |
cstr_wccat(outstr, c); |
} |
/* add a trailing '\0' */ |
if (!is_long) |
cstr_ccat(outstr, '\0'); |
else |
cstr_wccat(outstr, '\0'); |
} |
/* we use 64 bit numbers */ |
#define BN_SIZE 2 |
/* bn = (bn << shift) | or_val */ |
void bn_lshift(unsigned int *bn, int shift, int or_val) |
{ |
int i; |
unsigned int v; |
for(i=0;i<BN_SIZE;i++) { |
v = bn[i]; |
bn[i] = (v << shift) | or_val; |
or_val = v >> (32 - shift); |
} |
} |
void bn_zero(unsigned int *bn) |
{ |
int i; |
for(i=0;i<BN_SIZE;i++) { |
bn[i] = 0; |
} |
} |
/* parse number in null terminated string 'p' and return it in the |
current token */ |
void parse_number(const char *p) |
{ |
int b, t, shift, frac_bits, s, exp_val, ch; |
char *q; |
unsigned int bn[BN_SIZE]; |
double d; |
/* number */ |
q = token_buf; |
ch = *p++; |
t = ch; |
ch = *p++; |
*q++ = t; |
b = 10; |
if (t == '.') { |
goto float_frac_parse; |
} else if (t == '0') { |
if (ch == 'x' || ch == 'X') { |
q--; |
ch = *p++; |
b = 16; |
} else if (tcc_ext && (ch == 'b' || ch == 'B')) { |
q--; |
ch = *p++; |
b = 2; |
} |
} |
/* parse all digits. cannot check octal numbers at this stage |
because of floating point constants */ |
while (1) { |
if (ch >= 'a' && ch <= 'f') |
t = ch - 'a' + 10; |
else if (ch >= 'A' && ch <= 'F') |
t = ch - 'A' + 10; |
else if (isnum(ch)) |
t = ch - '0'; |
else |
break; |
if (t >= b) |
break; |
if (q >= token_buf + STRING_MAX_SIZE) { |
num_too_long: |
error("number too long"); |
} |
*q++ = ch; |
ch = *p++; |
} |
if (ch == '.' || |
((ch == 'e' || ch == 'E') && b == 10) || |
((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) { |
if (b != 10) { |
/* NOTE: strtox should support that for hexa numbers, but |
non ISOC99 libcs do not support it, so we prefer to do |
it by hand */ |
/* hexadecimal or binary floats */ |
/* XXX: handle overflows */ |
*q = '\0'; |
if (b == 16) |
shift = 4; |
else |
shift = 2; |
bn_zero(bn); |
q = token_buf; |
while (1) { |
t = *q++; |
if (t == '\0') { |
break; |
} else if (t >= 'a') { |
t = t - 'a' + 10; |
} else if (t >= 'A') { |
t = t - 'A' + 10; |
} else { |
t = t - '0'; |
} |
bn_lshift(bn, shift, t); |
} |
frac_bits = 0; |
if (ch == '.') { |
ch = *p++; |
while (1) { |
t = ch; |
if (t >= 'a' && t <= 'f') { |
t = t - 'a' + 10; |
} else if (t >= 'A' && t <= 'F') { |
t = t - 'A' + 10; |
} else if (t >= '0' && t <= '9') { |
t = t - '0'; |
} else { |
break; |
} |
if (t >= b) |
error("invalid digit"); |
bn_lshift(bn, shift, t); |
frac_bits += shift; |
ch = *p++; |
} |
} |
if (ch != 'p' && ch != 'P') |
expect("exponent"); |
ch = *p++; |
s = 1; |
exp_val = 0; |
if (ch == '+') { |
ch = *p++; |
} else if (ch == '-') { |
s = -1; |
ch = *p++; |
} |
if (ch < '0' || ch > '9') |
expect("exponent digits"); |
while (ch >= '0' && ch <= '9') { |
exp_val = exp_val * 10 + ch - '0'; |
ch = *p++; |
} |
exp_val = exp_val * s; |
/* now we can generate the number */ |
/* XXX: should patch directly float number */ |
d = (double)bn[1] * 4294967296.0 + (double)bn[0]; |
d = ldexp(d, exp_val - frac_bits); |
t = toup(ch); |
if (t == 'F') { |
ch = *p++; |
tok = TOK_CFLOAT; |
/* float : should handle overflow */ |
tokc.f = (float)d; |
} else if (t == 'L') { |
ch = *p++; |
tok = TOK_CLDOUBLE; |
/* XXX: not large enough */ |
tokc.ld = (long double)d; |
} else { |
tok = TOK_CDOUBLE; |
tokc.d = d; |
} |
} else { |
/* decimal floats */ |
if (ch == '.') { |
if (q >= token_buf + STRING_MAX_SIZE) |
goto num_too_long; |
*q++ = ch; |
ch = *p++; |
float_frac_parse: |
while (ch >= '0' && ch <= '9') { |
if (q >= token_buf + STRING_MAX_SIZE) |
goto num_too_long; |
*q++ = ch; |
ch = *p++; |
} |
} |
if (ch == 'e' || ch == 'E') { |
if (q >= token_buf + STRING_MAX_SIZE) |
goto num_too_long; |
*q++ = ch; |
ch = *p++; |
if (ch == '-' || ch == '+') { |
if (q >= token_buf + STRING_MAX_SIZE) |
goto num_too_long; |
*q++ = ch; |
ch = *p++; |
} |
if (ch < '0' || ch > '9') |
expect("exponent digits"); |
while (ch >= '0' && ch <= '9') { |
if (q >= token_buf + STRING_MAX_SIZE) |
goto num_too_long; |
*q++ = ch; |
ch = *p++; |
} |
} |
*q = '\0'; |
t = toup(ch); |
//errno = 0; |
if (t == 'F') { |
ch = *p++; |
tok = TOK_CFLOAT; |
tokc.f = strtof(token_buf, NULL); |
} else if (t == 'L') { |
ch = *p++; |
tok = TOK_CLDOUBLE; |
tokc.ld = strtold(token_buf, NULL); |
} else { |
tok = TOK_CDOUBLE; |
tokc.d = strtod(token_buf, NULL); |
} |
} |
} else { |
unsigned long long n, n1; |
int lcount, ucount; |
/* integer number */ |
*q = '\0'; |
q = token_buf; |
if (b == 10 && *q == '0') { |
b = 8; |
q++; |
} |
n = 0; |
while(1) { |
t = *q++; |
/* no need for checks except for base 10 / 8 errors */ |
if (t == '\0') { |
break; |
} else if (t >= 'a') { |
t = t - 'a' + 10; |
} else if (t >= 'A') { |
t = t - 'A' + 10; |
} else { |
t = t - '0'; |
if (t >= b) |
error("invalid digit"); |
} |
n1 = n; |
n = n * b + t; |
/* detect overflow */ |
/* XXX: this test is not reliable */ |
if (n < n1) |
error("integer constant overflow"); |
} |
/* XXX: not exactly ANSI compliant */ |
if ((n & 0xffffffff00000000LL) != 0) { |
if ((n >> 63) != 0) |
tok = TOK_CULLONG; |
else |
tok = TOK_CLLONG; |
} else if (n > 0x7fffffff) { |
tok = TOK_CUINT; |
} else { |
tok = TOK_CINT; |
} |
lcount = 0; |
ucount = 0; |
for(;;) { |
t = toup(ch); |
if (t == 'L') { |
if (lcount >= 2) |
error("three 'l's in integer constant"); |
lcount++; |
if (lcount == 2) { |
if (tok == TOK_CINT) |
tok = TOK_CLLONG; |
else if (tok == TOK_CUINT) |
tok = TOK_CULLONG; |
} |
ch = *p++; |
} else if (t == 'U') { |
if (ucount >= 1) |
error("two 'u's in integer constant"); |
ucount++; |
if (tok == TOK_CINT) |
tok = TOK_CUINT; |
else if (tok == TOK_CLLONG) |
tok = TOK_CULLONG; |
ch = *p++; |
} else { |
break; |
} |
} |
if (tok == TOK_CINT || tok == TOK_CUINT) |
tokc.ui = n; |
else |
tokc.ull = n; |
} |
} |
#define PARSE2(c1, tok1, c2, tok2) \ |
case c1: \ |
PEEKC(c, p); \ |
if (c == c2) { \ |
p++; \ |
tok = tok2; \ |
} else { \ |
tok = tok1; \ |
} \ |
break; |
/* return next token without macro substitution */ |
static inline void next_nomacro1(void) |
{ |
int t, c, is_long; |
TokenSym *ts; |
uint8_t *p, *p1; |
unsigned int h; |
p = file->buf_ptr; |
redo_no_start: |
c = *p; |
switch(c) { |
case ' ': |
case '\t': |
case '\f': |
case '\v': |
case '\r': |
p++; |
goto redo_no_start; |
case '\\': |
/* first look if it is in fact an end of buffer */ |
if (p >= file->buf_end) { |
file->buf_ptr = p; |
handle_eob(); |
p = file->buf_ptr; |
if (p >= file->buf_end) |
goto parse_eof; |
else |
goto redo_no_start; |
} else { |
file->buf_ptr = p; |
ch = *p; |
handle_stray(); |
p = file->buf_ptr; |
goto redo_no_start; |
} |
parse_eof: |
{ |
TCCState *s1 = tcc_state; |
if (parse_flags & PARSE_FLAG_LINEFEED) { |
tok = TOK_LINEFEED; |
} else if (s1->include_stack_ptr == s1->include_stack || |
!(parse_flags & PARSE_FLAG_PREPROCESS)) { |
/* no include left : end of file. */ |
tok = TOK_EOF; |
} else { |
/* pop include file */ |
/* test if previous '#endif' was after a #ifdef at |
start of file */ |
if (tok_flags & TOK_FLAG_ENDIF) { |
#ifdef INC_DEBUG |
printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL)); |
#endif |
add_cached_include(s1, file->inc_type, file->inc_filename, |
file->ifndef_macro_saved); |
} |
/* add end of include file debug info */ |
if (do_debug) { |
put_stabd(N_EINCL, 0, 0); |
} |
/* pop include stack */ |
tcc_close(file); |
s1->include_stack_ptr--; |
file = *s1->include_stack_ptr; |
p = file->buf_ptr; |
goto redo_no_start; |
} |
} |
break; |
case '\n': |
if (parse_flags & PARSE_FLAG_LINEFEED) { |
tok = TOK_LINEFEED; |
} else { |
file->line_num++; |
tok_flags |= TOK_FLAG_BOL; |
p++; |
goto redo_no_start; |
} |
break; |
case '#': |
/* XXX: simplify */ |
PEEKC(c, p); |
if ((tok_flags & TOK_FLAG_BOL) && |
(parse_flags & PARSE_FLAG_PREPROCESS)) { |
file->buf_ptr = p; |
preprocess(tok_flags & TOK_FLAG_BOF); |
p = file->buf_ptr; |
goto redo_no_start; |
} else { |
if (c == '#') { |
p++; |
tok = TOK_TWOSHARPS; |
} else { |
if (parse_flags & PARSE_FLAG_ASM_COMMENTS) { |
p = parse_line_comment(p - 1); |
goto redo_no_start; |
} else { |
tok = '#'; |
} |
} |
} |
break; |
case 'a': case 'b': case 'c': case 'd': |
case 'e': case 'f': case 'g': case 'h': |
case 'i': case 'j': case 'k': case 'l': |
case 'm': case 'n': case 'o': case 'p': |
case 'q': case 'r': case 's': case 't': |
case 'u': case 'v': case 'w': case 'x': |
case 'y': case 'z': |
case 'A': case 'B': case 'C': case 'D': |
case 'E': case 'F': case 'G': case 'H': |
case 'I': case 'J': case 'K': |
case 'M': case 'N': case 'O': case 'P': |
case 'Q': case 'R': case 'S': case 'T': |
case 'U': case 'V': case 'W': case 'X': |
case 'Y': case 'Z': |
case '_': |
parse_ident_fast: |
p1 = p; |
h = TOK_HASH_INIT; |
h = TOK_HASH_FUNC(h, c); |
p++; |
for(;;) { |
c = *p; |
if (!isidnum_table[c]) |
break; |
h = TOK_HASH_FUNC(h, c); |
p++; |
} |
if (c != '\\') { |
TokenSym **pts; |
int len; |
/* fast case : no stray found, so we have the full token |
and we have already hashed it */ |
len = p - p1; |
h &= (TOK_HASH_SIZE - 1); |
pts = &hash_ident[h]; |
for(;;) { |
ts = *pts; |
if (!ts) |
break; |
if (ts->len == len && !memcmp(ts->str, p1, len)) |
goto token_found; |
pts = &(ts->hash_next); |
} |
ts = tok_alloc_new(pts, p1, len); |
token_found: ; |
} else { |
/* slower case */ |
cstr_reset(&tokcstr); |
while (p1 < p) { |
cstr_ccat(&tokcstr, *p1); |
p1++; |
} |
p--; |
PEEKC(c, p); |
parse_ident_slow: |
while (isidnum_table[c]) { |
cstr_ccat(&tokcstr, c); |
PEEKC(c, p); |
} |
ts = tok_alloc(tokcstr.data, tokcstr.size); |
} |
tok = ts->tok; |
break; |
case 'L': |
t = p[1]; |
if (t != '\\' && t != '\'' && t != '\"') { |
/* fast case */ |
goto parse_ident_fast; |
} else { |
PEEKC(c, p); |
if (c == '\'' || c == '\"') { |
is_long = 1; |
goto str_const; |
} else { |
cstr_reset(&tokcstr); |
cstr_ccat(&tokcstr, 'L'); |
goto parse_ident_slow; |
} |
} |
break; |
case '0': case '1': case '2': case '3': |
case '4': case '5': case '6': case '7': |
case '8': case '9': |
cstr_reset(&tokcstr); |
/* after the first digit, accept digits, alpha, '.' or sign if |
prefixed by 'eEpP' */ |
parse_num: |
for(;;) { |
t = c; |
cstr_ccat(&tokcstr, c); |
PEEKC(c, p); |
if (!(isnum(c) || isid(c) || c == '.' || |
((c == '+' || c == '-') && |
(t == 'e' || t == 'E' || t == 'p' || t == 'P')))) |
break; |
} |
/* We add a trailing '\0' to ease parsing */ |
cstr_ccat(&tokcstr, '\0'); |
tokc.cstr = &tokcstr; |
tok = TOK_PPNUM; |
break; |
case '.': |
/* special dot handling because it can also start a number */ |
PEEKC(c, p); |
if (isnum(c)) { |
cstr_reset(&tokcstr); |
cstr_ccat(&tokcstr, '.'); |
goto parse_num; |
} else if (c == '.') { |
PEEKC(c, p); |
if (c != '.') |
expect("'.'"); |
PEEKC(c, p); |
tok = TOK_DOTS; |
} else { |
tok = '.'; |
} |
break; |
case '\'': |
case '\"': |
is_long = 0; |
str_const: |
{ |
CString str; |
int sep; |
sep = c; |
/* parse the string */ |
cstr_new(&str); |
p = parse_pp_string(p, sep, &str); |
cstr_ccat(&str, '\0'); |
/* eval the escape (should be done as TOK_PPNUM) */ |
cstr_reset(&tokcstr); |
parse_escape_string(&tokcstr, str.data, is_long); |
cstr_free(&str); |
if (sep == '\'') { |
int char_size; |
/* XXX: make it portable */ |
if (!is_long) |
char_size = 1; |
else |
char_size = sizeof(int); |
if (tokcstr.size <= char_size) |
error("empty character constant"); |
if (tokcstr.size > 2 * char_size) |
warning("multi-character character constant"); |
if (!is_long) { |
tokc.i = *(int8_t *)tokcstr.data; |
tok = TOK_CCHAR; |
} else { |
tokc.i = *(int *)tokcstr.data; |
tok = TOK_LCHAR; |
} |
} else { |
tokc.cstr = &tokcstr; |
if (!is_long) |
tok = TOK_STR; |
else |
tok = TOK_LSTR; |
} |
} |
break; |
case '<': |
PEEKC(c, p); |
if (c == '=') { |
p++; |
tok = TOK_LE; |
} else if (c == '<') { |
PEEKC(c, p); |
if (c == '=') { |
p++; |
tok = TOK_A_SHL; |
} else { |
tok = TOK_SHL; |
} |
} else { |
tok = TOK_LT; |
} |
break; |
case '>': |
PEEKC(c, p); |
if (c == '=') { |
p++; |
tok = TOK_GE; |
} else if (c == '>') { |
PEEKC(c, p); |
if (c == '=') { |
p++; |
tok = TOK_A_SAR; |
} else { |
tok = TOK_SAR; |
} |
} else { |
tok = TOK_GT; |
} |
break; |
case '&': |
PEEKC(c, p); |
if (c == '&') { |
p++; |
tok = TOK_LAND; |
} else if (c == '=') { |
p++; |
tok = TOK_A_AND; |
} else { |
tok = '&'; |
} |
break; |
case '|': |
PEEKC(c, p); |
if (c == '|') { |
p++; |
tok = TOK_LOR; |
} else if (c == '=') { |
p++; |
tok = TOK_A_OR; |
} else { |
tok = '|'; |
} |
break; |
case '+': |
PEEKC(c, p); |
if (c == '+') { |
p++; |
tok = TOK_INC; |
} else if (c == '=') { |
p++; |
tok = TOK_A_ADD; |
} else { |
tok = '+'; |
} |
break; |
case '-': |
PEEKC(c, p); |
if (c == '-') { |
p++; |
tok = TOK_DEC; |
} else if (c == '=') { |
p++; |
tok = TOK_A_SUB; |
} else if (c == '>') { |
p++; |
tok = TOK_ARROW; |
} else { |
tok = '-'; |
} |
break; |
PARSE2('!', '!', '=', TOK_NE) |
PARSE2('=', '=', '=', TOK_EQ) |
PARSE2('*', '*', '=', TOK_A_MUL) |
PARSE2('%', '%', '=', TOK_A_MOD) |
PARSE2('^', '^', '=', TOK_A_XOR) |
/* comments or operator */ |
case '/': |
PEEKC(c, p); |
if (c == '*') { |
p = parse_comment(p); |
goto redo_no_start; |
} else if (c == '/') { |
p = parse_line_comment(p); |
goto redo_no_start; |
} else if (c == '=') { |
p++; |
tok = TOK_A_DIV; |
} else { |
tok = '/'; |
} |
break; |
/* simple tokens */ |
case '(': |
case ')': |
case '[': |
case ']': |
case '{': |
case '}': |
case ',': |
case ';': |
case ':': |
case '?': |
case '~': |
case '$': /* only used in assembler */ |
case '@': /* dito */ |
tok = c; |
p++; |
break; |
default: |
error("unrecognized character \\x%02x", c); |
break; |
} |
file->buf_ptr = p; |
tok_flags = 0; |
#if defined(PARSE_DEBUG) |
printf("token = %s\n", get_tok_str(tok, &tokc)); |
#endif |
} |
/* return next token without macro substitution. Can read input from |
macro_ptr buffer */ |
static void next_nomacro(void) |
{ |
if (macro_ptr) { |
redo: |
tok = *macro_ptr; |
if (tok) { |
TOK_GET(tok, macro_ptr, tokc); |
if (tok == TOK_LINENUM) { |
file->line_num = tokc.i; |
goto redo; |
} |
} |
} else { |
next_nomacro1(); |
} |
} |
/* substitute args in macro_str and return allocated string */ |
static int *macro_arg_subst(Sym **nested_list, int *macro_str, Sym *args) |
{ |
int *st, last_tok, t, notfirst; |
Sym *s; |
CValue cval; |
TokenString str; |
CString cstr; |
tok_str_new(&str); |
last_tok = 0; |
while(1) { |
TOK_GET(t, macro_str, cval); |
if (!t) |
break; |
if (t == '#') { |
/* stringize */ |
TOK_GET(t, macro_str, cval); |
if (!t) |
break; |
s = sym_find2(args, t); |
if (s) { |
cstr_new(&cstr); |
st = (int *)s->c; |
notfirst = 0; |
while (*st) { |
if (notfirst) |
cstr_ccat(&cstr, ' '); |
TOK_GET(t, st, cval); |
cstr_cat(&cstr, get_tok_str(t, &cval)); |
notfirst = 1; |
} |
cstr_ccat(&cstr, '\0'); |
#ifdef PP_DEBUG |
printf("stringize: %s\n", (char *)cstr.data); |
#endif |
/* add string */ |
cval.cstr = &cstr; |
tok_str_add2(&str, TOK_STR, &cval); |
cstr_free(&cstr); |
} else { |
tok_str_add2(&str, t, &cval); |
} |
} else if (t >= TOK_IDENT) { |
s = sym_find2(args, t); |
if (s) { |
st = (int *)s->c; |
/* if '##' is present before or after, no arg substitution */ |
if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) { |
/* special case for var arg macros : ## eats the |
',' if empty VA_ARGS variable. */ |
/* XXX: test of the ',' is not 100% |
reliable. should fix it to avoid security |
problems */ |
if (gnu_ext && s->type.t && |
last_tok == TOK_TWOSHARPS && |
str.len >= 2 && str.str[str.len - 2] == ',') { |
if (*st == 0) { |
/* suppress ',' '##' */ |
str.len -= 2; |
} else { |
/* suppress '##' and add variable */ |
str.len--; |
goto add_var; |
} |
} else { |
int t1; |
add_var: |
for(;;) { |
TOK_GET(t1, st, cval); |
if (!t1) |
break; |
tok_str_add2(&str, t1, &cval); |
} |
} |
} else { |
/* NOTE: the stream cannot be read when macro |
substituing an argument */ |
macro_subst(&str, nested_list, st, NULL); |
} |
} else { |
tok_str_add(&str, t); |
} |
} else { |
tok_str_add2(&str, t, &cval); |
} |
last_tok = t; |
} |
tok_str_add(&str, 0); |
return str.str; |
} |
static char const ab_month_name[12][4] = |
{ |
"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
}; |
/* do macro substitution of current token with macro 's' and add |
result to (tok_str,tok_len). 'nested_list' is the list of all |
macros we got inside to avoid recursing. Return non zero if no |
substitution needs to be done */ |
static int macro_subst_tok(TokenString *tok_str, |
Sym **nested_list, Sym *s, struct macro_level **can_read_stream) |
{ |
Sym *args, *sa, *sa1; |
int mstr_allocated, parlevel, *mstr, t, t1; |
TokenString str; |
char *cstrval; |
CValue cval; |
CString cstr; |
char buf[32]; |
/* if symbol is a macro, prepare substitution */ |
/* special macros */ |
if (tok == TOK___LINE__) { |
snprintf(buf, sizeof(buf), "%d", file->line_num); |
cstrval = buf; |
t1 = TOK_PPNUM; |
goto add_cstr1; |
} else if (tok == TOK___FILE__) { |
cstrval = file->filename; |
goto add_cstr; |
} else if (tok == TOK___DATE__ || tok == TOK___TIME__) { |
time_t ti; |
struct tm *tm; |
time(&ti); |
tm = localtime(&ti); |
if (tok == TOK___DATE__) { |
snprintf(buf, sizeof(buf), "%s %2d %d", |
ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900); |
} else { |
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", |
tm->tm_hour, tm->tm_min, tm->tm_sec); |
} |
cstrval = buf; |
add_cstr: |
t1 = TOK_STR; |
add_cstr1: |
cstr_new(&cstr); |
cstr_cat(&cstr, cstrval); |
cstr_ccat(&cstr, '\0'); |
cval.cstr = &cstr; |
tok_str_add2(tok_str, t1, &cval); |
cstr_free(&cstr); |
} else { |
mstr = (int *)s->c; |
mstr_allocated = 0; |
if (s->type.t == MACRO_FUNC) { |
/* NOTE: we do not use next_nomacro to avoid eating the |
next token. XXX: find better solution */ |
redo: |
if (macro_ptr) { |
t = *macro_ptr; |
if (t == 0 && can_read_stream) { |
/* end of macro stream: we must look at the token |
after in the file */ |
struct macro_level *ml = *can_read_stream; |
macro_ptr = NULL; |
if (ml) |
{ |
macro_ptr = ml->p; |
ml->p = NULL; |
*can_read_stream = ml -> prev; |
} |
goto redo; |
} |
} else { |
/* XXX: incorrect with comments */ |
ch = file->buf_ptr[0]; |
while (is_space(ch) || ch == '\n') |
cinp(); |
t = ch; |
} |
if (t != '(') /* no macro subst */ |
return -1; |
/* argument macro */ |
next_nomacro(); |
next_nomacro(); |
args = NULL; |
sa = s->next; |
/* NOTE: empty args are allowed, except if no args */ |
for(;;) { |
/* handle '()' case */ |
if (!args && !sa && tok == ')') |
break; |
if (!sa) |
error("macro '%s' used with too many args", |
get_tok_str(s->v, 0)); |
tok_str_new(&str); |
parlevel = 0; |
/* NOTE: non zero sa->t indicates VA_ARGS */ |
while ((parlevel > 0 || |
(tok != ')' && |
(tok != ',' || sa->type.t))) && |
tok != -1) { |
if (tok == '(') |
parlevel++; |
else if (tok == ')') |
parlevel--; |
tok_str_add2(&str, tok, &tokc); |
next_nomacro(); |
} |
tok_str_add(&str, 0); |
sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, (int)str.str); |
sa = sa->next; |
if (tok == ')') { |
/* special case for gcc var args: add an empty |
var arg argument if it is omitted */ |
if (sa && sa->type.t && gnu_ext) |
continue; |
else |
break; |
} |
if (tok != ',') |
expect(","); |
next_nomacro(); |
} |
if (sa) { |
error("macro '%s' used with too few args", |
get_tok_str(s->v, 0)); |
} |
/* now subst each arg */ |
mstr = macro_arg_subst(nested_list, mstr, args); |
/* free memory */ |
sa = args; |
while (sa) { |
sa1 = sa->prev; |
tok_str_free((int *)sa->c); |
sym_free(sa); |
sa = sa1; |
} |
mstr_allocated = 1; |
} |
sym_push2(nested_list, s->v, 0, 0); |
macro_subst(tok_str, nested_list, mstr, can_read_stream); |
/* pop nested defined symbol */ |
sa1 = *nested_list; |
*nested_list = sa1->prev; |
sym_free(sa1); |
if (mstr_allocated) |
tok_str_free(mstr); |
} |
return 0; |
} |
/* handle the '##' operator. Return NULL if no '##' seen. Otherwise |
return the resulting string (which must be freed). */ |
static inline int *macro_twosharps(const int *macro_str) |
{ |
TokenSym *ts; |
const int *macro_ptr1, *start_macro_ptr, *ptr, *saved_macro_ptr; |
int t; |
const char *p1, *p2; |
CValue cval; |
TokenString macro_str1; |
CString cstr; |
start_macro_ptr = macro_str; |
/* we search the first '##' */ |
for(;;) { |
macro_ptr1 = macro_str; |
TOK_GET(t, macro_str, cval); |
/* nothing more to do if end of string */ |
if (t == 0) |
return NULL; |
if (*macro_str == TOK_TWOSHARPS) |
break; |
} |
/* we saw '##', so we need more processing to handle it */ |
cstr_new(&cstr); |
tok_str_new(¯o_str1); |
tok = t; |
tokc = cval; |
/* add all tokens seen so far */ |
for(ptr = start_macro_ptr; ptr < macro_ptr1;) { |
TOK_GET(t, ptr, cval); |
tok_str_add2(¯o_str1, t, &cval); |
} |
saved_macro_ptr = macro_ptr; |
/* XXX: get rid of the use of macro_ptr here */ |
macro_ptr = (int *)macro_str; |
for(;;) { |
while (*macro_ptr == TOK_TWOSHARPS) { |
macro_ptr++; |
macro_ptr1 = macro_ptr; |
t = *macro_ptr; |
if (t) { |
TOK_GET(t, macro_ptr, cval); |
/* We concatenate the two tokens if we have an |
identifier or a preprocessing number */ |
cstr_reset(&cstr); |
p1 = get_tok_str(tok, &tokc); |
cstr_cat(&cstr, p1); |
p2 = get_tok_str(t, &cval); |
cstr_cat(&cstr, p2); |
cstr_ccat(&cstr, '\0'); |
if ((tok >= TOK_IDENT || tok == TOK_PPNUM) && |
(t >= TOK_IDENT || t == TOK_PPNUM)) { |
if (tok == TOK_PPNUM) { |
/* if number, then create a number token */ |
/* NOTE: no need to allocate because |
tok_str_add2() does it */ |
tokc.cstr = &cstr; |
} else { |
/* if identifier, we must do a test to |
validate we have a correct identifier */ |
if (t == TOK_PPNUM) { |
const char *p; |
int c; |
p = p2; |
for(;;) { |
c = *p; |
if (c == '\0') |
break; |
p++; |
if (!isnum(c) && !isid(c)) |
goto error_pasting; |
} |
} |
ts = tok_alloc(cstr.data, strlen(cstr.data)); |
tok = ts->tok; /* modify current token */ |
} |
} else { |
const char *str = cstr.data; |
const unsigned char *q; |
/* we look for a valid token */ |
/* XXX: do more extensive checks */ |
if (!strcmp(str, ">>=")) { |
tok = TOK_A_SAR; |
} else if (!strcmp(str, "<<=")) { |
tok = TOK_A_SHL; |
} else if (strlen(str) == 2) { |
/* search in two bytes table */ |
q = tok_two_chars; |
for(;;) { |
if (!*q) |
goto error_pasting; |
if (q[0] == str[0] && q[1] == str[1]) |
break; |
q += 3; |
} |
tok = q[2]; |
} else { |
error_pasting: |
/* NOTE: because get_tok_str use a static buffer, |
we must save it */ |
cstr_reset(&cstr); |
p1 = get_tok_str(tok, &tokc); |
cstr_cat(&cstr, p1); |
cstr_ccat(&cstr, '\0'); |
p2 = get_tok_str(t, &cval); |
warning("pasting \"%s\" and \"%s\" does not give a valid preprocessing token", cstr.data, p2); |
/* cannot merge tokens: just add them separately */ |
tok_str_add2(¯o_str1, tok, &tokc); |
/* XXX: free associated memory ? */ |
tok = t; |
tokc = cval; |
} |
} |
} |
} |
tok_str_add2(¯o_str1, tok, &tokc); |
next_nomacro(); |
if (tok == 0) |
break; |
} |
macro_ptr = (int *)saved_macro_ptr; |
cstr_free(&cstr); |
tok_str_add(¯o_str1, 0); |
return macro_str1.str; |
} |
/* do macro substitution of macro_str and add result to |
(tok_str,tok_len). 'nested_list' is the list of all macros we got |
inside to avoid recursing. */ |
static void macro_subst(TokenString *tok_str, Sym **nested_list, |
const int *macro_str, struct macro_level ** can_read_stream) |
{ |
Sym *s; |
int *macro_str1; |
const int *ptr; |
int t, ret; |
CValue cval; |
struct macro_level ml; |
/* first scan for '##' operator handling */ |
ptr = macro_str; |
macro_str1 = macro_twosharps(ptr); |
if (macro_str1) |
ptr = macro_str1; |
while (1) { |
/* NOTE: ptr == NULL can only happen if tokens are read from |
file stream due to a macro function call */ |
if (ptr == NULL) |
break; |
TOK_GET(t, ptr, cval); |
if (t == 0) |
break; |
s = define_find(t); |
if (s != NULL) { |
/* if nested substitution, do nothing */ |
if (sym_find2(*nested_list, t)) |
goto no_subst; |
ml.p = macro_ptr; |
if (can_read_stream) |
ml.prev = *can_read_stream, *can_read_stream = &ml; |
macro_ptr = (int *)ptr; |
tok = t; |
ret = macro_subst_tok(tok_str, nested_list, s, can_read_stream); |
ptr = (int *)macro_ptr; |
macro_ptr = ml.p; |
if (can_read_stream && *can_read_stream == &ml) |
*can_read_stream = ml.prev; |
if (ret != 0) |
goto no_subst; |
} else { |
no_subst: |
tok_str_add2(tok_str, t, &cval); |
} |
} |
if (macro_str1) |
tok_str_free(macro_str1); |
} |
/* return next token with macro substitution */ |
static void next(void) |
{ |
Sym *nested_list, *s; |
TokenString str; |
struct macro_level *ml; |
redo: |
next_nomacro(); |
if (!macro_ptr) { |
/* if not reading from macro substituted string, then try |
to substitute macros */ |
if (tok >= TOK_IDENT && |
(parse_flags & PARSE_FLAG_PREPROCESS)) { |
s = define_find(tok); |
if (s) { |
/* we have a macro: we try to substitute */ |
tok_str_new(&str); |
nested_list = NULL; |
ml = NULL; |
if (macro_subst_tok(&str, &nested_list, s, &ml) == 0) { |
/* substitution done, NOTE: maybe empty */ |
tok_str_add(&str, 0); |
macro_ptr = str.str; |
macro_ptr_allocated = str.str; |
goto redo; |
} |
} |
} |
} else { |
if (tok == 0) { |
/* end of macro or end of unget buffer */ |
if (unget_buffer_enabled) { |
macro_ptr = unget_saved_macro_ptr; |
unget_buffer_enabled = 0; |
} else { |
/* end of macro string: free it */ |
tok_str_free(macro_ptr_allocated); |
macro_ptr = NULL; |
} |
goto redo; |
} |
} |
/* convert preprocessor tokens into C tokens */ |
if (tok == TOK_PPNUM && |
(parse_flags & PARSE_FLAG_TOK_NUM)) { |
parse_number((char *)tokc.cstr->data); |
} |
} |
/* push back current token and set current token to 'last_tok'. Only |
identifier case handled for labels. */ |
static inline void unget_tok(int last_tok) |
{ |
int i, n; |
int *q; |
unget_saved_macro_ptr = macro_ptr; |
unget_buffer_enabled = 1; |
q = unget_saved_buffer; |
macro_ptr = q; |
*q++ = tok; |
n = tok_ext_size(tok) - 1; |
for(i=0;i<n;i++) |
*q++ = tokc.tab[i]; |
*q = 0; /* end of token string */ |
tok = last_tok; |
} |
void swap(int *p, int *q) |
{ |
int t; |
t = *p; |
*p = *q; |
*q = t; |
} |
void vsetc(CType *type, int r, CValue *vc) |
{ |
int v; |
if (vtop >= vstack + (VSTACK_SIZE - 1)) |
error("memory full"); |
/* cannot let cpu flags if other instruction are generated. Also |
avoid leaving VT_JMP anywhere except on the top of the stack |
because it would complicate the code generator. */ |
if (vtop >= vstack) { |
v = vtop->r & VT_VALMASK; |
if (v == VT_CMP || (v & ~1) == VT_JMP) |
gv(RC_INT); |
} |
vtop++; |
vtop->type = *type; |
vtop->r = r; |
vtop->r2 = VT_CONST; |
vtop->c = *vc; |
} |
/* push integer constant */ |
void vpushi(int v) |
{ |
CValue cval; |
cval.i = v; |
vsetc(&int_type, VT_CONST, &cval); |
} |
/* Return a static symbol pointing to a section */ |
static Sym *get_sym_ref(CType *type, Section *sec, |
unsigned long offset, unsigned long size) |
{ |
int v; |
Sym *sym; |
v = anon_sym++; |
sym = global_identifier_push(v, type->t | VT_STATIC, 0); |
sym->type.ref = type->ref; |
sym->r = VT_CONST | VT_SYM; |
put_extern_sym(sym, sec, offset, size); |
return sym; |
} |
/* push a reference to a section offset by adding a dummy symbol */ |
static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size) |
{ |
CValue cval; |
cval.ul = 0; |
vsetc(type, VT_CONST | VT_SYM, &cval); |
vtop->sym = get_sym_ref(type, sec, offset, size); |
} |
/* define a new external reference to a symbol 'v' of type 'u' */ |
static Sym *external_global_sym(int v, CType *type, int r) |
{ |
Sym *s; |
s = sym_find(v); |
if (!s) { |
/* push forward reference */ |
s = global_identifier_push(v, type->t | VT_EXTERN, 0); |
s->type.ref = type->ref; |
s->r = r | VT_CONST | VT_SYM; |
} |
return s; |
} |
/* define a new external reference to a symbol 'v' of type 'u' */ |
static Sym *external_sym(int v, CType *type, int r) |
{ |
Sym *s; |
s = sym_find(v); |
if (!s) { |
/* push forward reference */ |
s = sym_push(v, type, r | VT_CONST | VT_SYM, 0); |
s->type.t |= VT_EXTERN; |
} else { |
if (!is_compatible_types(&s->type, type)) |
error("incompatible types for redefinition of '%s'", |
get_tok_str(v, NULL)); |
} |
return s; |
} |
/* push a reference to global symbol v */ |
static void vpush_global_sym(CType *type, int v) |
{ |
Sym *sym; |
CValue cval; |
sym = external_global_sym(v, type, 0); |
cval.ul = 0; |
vsetc(type, VT_CONST | VT_SYM, &cval); |
vtop->sym = sym; |
} |
void vset(CType *type, int r, int v) |
{ |
CValue cval; |
cval.i = v; |
vsetc(type, r, &cval); |
} |
void vseti(int r, int v) |
{ |
CType type; |
type.t = VT_INT; |
vset(&type, r, v); |
} |
void vswap(void) |
{ |
SValue tmp; |
tmp = vtop[0]; |
vtop[0] = vtop[-1]; |
vtop[-1] = tmp; |
} |
void vpushv(SValue *v) |
{ |
if (vtop >= vstack + (VSTACK_SIZE - 1)) |
error("memory full"); |
vtop++; |
*vtop = *v; |
} |
void vdup(void) |
{ |
vpushv(vtop); |
} |
/* save r to the memory stack, and mark it as being free */ |
void save_reg(int r) |
{ |
int l, saved, size, align; |
SValue *p, sv; |
CType *type; |
/* modify all stack values */ |
saved = 0; |
l = 0; |
for(p=vstack;p<=vtop;p++) { |
if ((p->r & VT_VALMASK) == r || |
(p->r2 & VT_VALMASK) == r) { |
/* must save value on stack if not already done */ |
if (!saved) { |
/* NOTE: must reload 'r' because r might be equal to r2 */ |
r = p->r & VT_VALMASK; |
/* store register in the stack */ |
type = &p->type; |
if ((p->r & VT_LVAL) || |
(!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) |
type = &int_type; |
size = type_size(type, &align); |
loc = (loc - size) & -align; |
sv.type.t = type->t; |
sv.r = VT_LOCAL | VT_LVAL; |
sv.c.ul = loc; |
store(r, &sv); |
#ifdef TCC_TARGET_I386 |
/* x86 specific: need to pop fp register ST0 if saved */ |
if (r == TREG_ST0) { |
o(0xd9dd); /* fstp %st(1) */ |
} |
#endif |
/* special long long case */ |
if ((type->t & VT_BTYPE) == VT_LLONG) { |
sv.c.ul += 4; |
store(p->r2, &sv); |
} |
l = loc; |
saved = 1; |
} |
/* mark that stack entry as being saved on the stack */ |
if (p->r & VT_LVAL) { |
/* also clear the bounded flag because the |
relocation address of the function was stored in |
p->c.ul */ |
p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL; |
} else { |
p->r = lvalue_type(p->type.t) | VT_LOCAL; |
} |
p->r2 = VT_CONST; |
p->c.ul = l; |
} |
} |
} |
/* find a register of class 'rc2' with at most one reference on stack. |
* If none, call get_reg(rc) */ |
int get_reg_ex(int rc, int rc2) |
{ |
int r; |
SValue *p; |
for(r=0;r<NB_REGS;r++) { |
if (reg_classes[r] & rc2) { |
int n; |
n=0; |
for(p = vstack; p <= vtop; p++) { |
if ((p->r & VT_VALMASK) == r || |
(p->r2 & VT_VALMASK) == r) |
n++; |
} |
if (n <= 1) |
return r; |
} |
} |
return get_reg(rc); |
} |
/* find a free register of class 'rc'. If none, save one register */ |
int get_reg(int rc) |
{ |
int r; |
SValue *p; |
/* find a free register */ |
for(r=0;r<NB_REGS;r++) { |
if (reg_classes[r] & rc) { |
for(p=vstack;p<=vtop;p++) { |
if ((p->r & VT_VALMASK) == r || |
(p->r2 & VT_VALMASK) == r) |
goto notfound; |
} |
return r; |
} |
notfound: ; |
} |
/* no register left : free the first one on the stack (VERY |
IMPORTANT to start from the bottom to ensure that we don't |
spill registers used in gen_opi()) */ |
for(p=vstack;p<=vtop;p++) { |
r = p->r & VT_VALMASK; |
if (r < VT_CONST && (reg_classes[r] & rc)) |
goto save_found; |
/* also look at second register (if long long) */ |
r = p->r2 & VT_VALMASK; |
if (r < VT_CONST && (reg_classes[r] & rc)) { |
save_found: |
save_reg(r); |
return r; |
} |
} |
/* Should never comes here */ |
return -1; |
} |
/* save registers up to (vtop - n) stack entry */ |
void save_regs(int n) |
{ |
int r; |
SValue *p, *p1; |
p1 = vtop - n; |
for(p = vstack;p <= p1; p++) { |
r = p->r & VT_VALMASK; |
if (r < VT_CONST) { |
save_reg(r); |
} |
} |
} |
/* move register 's' to 'r', and flush previous value of r to memory |
if needed */ |
void move_reg(int r, int s) |
{ |
SValue sv; |
if (r != s) { |
save_reg(r); |
sv.type.t = VT_INT; |
sv.r = s; |
sv.c.ul = 0; |
load(r, &sv); |
} |
} |
/* get address of vtop (vtop MUST BE an lvalue) */ |
void gaddrof(void) |
{ |
vtop->r &= ~VT_LVAL; |
/* tricky: if saved lvalue, then we can go back to lvalue */ |
if ((vtop->r & VT_VALMASK) == VT_LLOCAL) |
vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL; |
} |
#ifdef CONFIG_TCC_BCHECK |
/* generate lvalue bound code */ |
void gbound(void) |
{ |
int lval_type; |
CType type1; |
vtop->r &= ~VT_MUSTBOUND; |
/* if lvalue, then use checking code before dereferencing */ |
if (vtop->r & VT_LVAL) { |
/* if not VT_BOUNDED value, then make one */ |
if (!(vtop->r & VT_BOUNDED)) { |
lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL); |
/* must save type because we must set it to int to get pointer */ |
type1 = vtop->type; |
vtop->type.t = VT_INT; |
gaddrof(); |
vpushi(0); |
gen_bounded_ptr_add(); |
vtop->r |= lval_type; |
vtop->type = type1; |
} |
/* then check for dereferencing */ |
gen_bounded_ptr_deref(); |
} |
} |
#endif |
/* store vtop a register belonging to class 'rc'. lvalues are |
converted to values. Cannot be used if cannot be converted to |
register value (such as structures). */ |
int gv(int rc) |
{ |
int r, r2, rc2, bit_pos, bit_size, size, align, i; |
unsigned long long ll; |
/* NOTE: get_reg can modify vstack[] */ |
if (vtop->type.t & VT_BITFIELD) { |
bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; |
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; |
/* remove bit field info to avoid loops */ |
vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); |
/* generate shifts */ |
vpushi(32 - (bit_pos + bit_size)); |
gen_op(TOK_SHL); |
vpushi(32 - bit_size); |
/* NOTE: transformed to SHR if unsigned */ |
gen_op(TOK_SAR); |
r = gv(rc); |
} else { |
if (is_float(vtop->type.t) && |
(vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
Sym *sym; |
int *ptr; |
unsigned long offset; |
/* XXX: unify with initializers handling ? */ |
/* CPUs usually cannot use float constants, so we store them |
generically in data segment */ |
size = type_size(&vtop->type, &align); |
offset = (data_section->data_offset + align - 1) & -align; |
data_section->data_offset = offset; |
/* XXX: not portable yet */ |
ptr = section_ptr_add(data_section, size); |
size = size >> 2; |
for(i=0;i<size;i++) |
ptr[i] = vtop->c.tab[i]; |
sym = get_sym_ref(&vtop->type, data_section, offset, size << 2); |
vtop->r |= VT_LVAL | VT_SYM; |
vtop->sym = sym; |
vtop->c.ul = 0; |
} |
#ifdef CONFIG_TCC_BCHECK |
if (vtop->r & VT_MUSTBOUND) |
gbound(); |
#endif |
r = vtop->r & VT_VALMASK; |
/* need to reload if: |
- constant |
- lvalue (need to dereference pointer) |
- already a register, but not in the right class */ |
if (r >= VT_CONST || |
(vtop->r & VT_LVAL) || |
!(reg_classes[r] & rc) || |
((vtop->type.t & VT_BTYPE) == VT_LLONG && |
!(reg_classes[vtop->r2] & rc))) { |
r = get_reg(rc); |
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
/* two register type load : expand to two words |
temporarily */ |
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
/* load constant */ |
ll = vtop->c.ull; |
vtop->c.ui = ll; /* first word */ |
load(r, vtop); |
vtop->r = r; /* save register value */ |
vpushi(ll >> 32); /* second word */ |
} else if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */ |
(vtop->r & VT_LVAL)) { |
/* We do not want to modifier the long long |
pointer here, so the safest (and less |
efficient) is to save all the other registers |
in the stack. XXX: totally inefficient. */ |
save_regs(1); |
/* load from memory */ |
load(r, vtop); |
vdup(); |
vtop[-1].r = r; /* save register value */ |
/* increment pointer to get second word */ |
vtop->type.t = VT_INT; |
gaddrof(); |
vpushi(4); |
gen_op('+'); |
vtop->r |= VT_LVAL; |
} else { |
/* move registers */ |
load(r, vtop); |
vdup(); |
vtop[-1].r = r; /* save register value */ |
vtop->r = vtop[-1].r2; |
} |
/* allocate second register */ |
rc2 = RC_INT; |
if (rc == RC_IRET) |
rc2 = RC_LRET; |
r2 = get_reg(rc2); |
load(r2, vtop); |
vpop(); |
/* write second register */ |
vtop->r2 = r2; |
} else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) { |
int t1, t; |
/* lvalue of scalar type : need to use lvalue type |
because of possible cast */ |
t = vtop->type.t; |
t1 = t; |
/* compute memory access type */ |
if (vtop->r & VT_LVAL_BYTE) |
t = VT_BYTE; |
else if (vtop->r & VT_LVAL_SHORT) |
t = VT_SHORT; |
if (vtop->r & VT_LVAL_UNSIGNED) |
t |= VT_UNSIGNED; |
vtop->type.t = t; |
load(r, vtop); |
/* restore wanted type */ |
vtop->type.t = t1; |
} else { |
/* one register type load */ |
load(r, vtop); |
} |
} |
vtop->r = r; |
#ifdef TCC_TARGET_C67 |
/* uses register pairs for doubles */ |
if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) |
vtop->r2 = r+1; |
#endif |
} |
return r; |
} |
/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */ |
void gv2(int rc1, int rc2) |
{ |
int v; |
/* generate more generic register first. But VT_JMP or VT_CMP |
values must be generated first in all cases to avoid possible |
reload errors */ |
v = vtop[0].r & VT_VALMASK; |
if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) { |
vswap(); |
gv(rc1); |
vswap(); |
gv(rc2); |
/* test if reload is needed for first register */ |
if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { |
vswap(); |
gv(rc1); |
vswap(); |
} |
} else { |
gv(rc2); |
vswap(); |
gv(rc1); |
vswap(); |
/* test if reload is needed for first register */ |
if ((vtop[0].r & VT_VALMASK) >= VT_CONST) { |
gv(rc2); |
} |
} |
} |
/* expand long long on stack in two int registers */ |
void lexpand(void) |
{ |
int u; |
u = vtop->type.t & VT_UNSIGNED; |
gv(RC_INT); |
vdup(); |
vtop[0].r = vtop[-1].r2; |
vtop[0].r2 = VT_CONST; |
vtop[-1].r2 = VT_CONST; |
vtop[0].type.t = VT_INT | u; |
vtop[-1].type.t = VT_INT | u; |
} |
#ifdef TCC_TARGET_ARM |
/* expand long long on stack */ |
void lexpand_nr(void) |
{ |
int u,v; |
u = vtop->type.t & VT_UNSIGNED; |
vdup(); |
vtop->r2 = VT_CONST; |
vtop->type.t = VT_INT | u; |
v=vtop[-1].r & (VT_VALMASK | VT_LVAL); |
if (v == VT_CONST) { |
vtop[-1].c.ui = vtop->c.ull; |
vtop->c.ui = vtop->c.ull >> 32; |
vtop->r = VT_CONST; |
} else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) { |
vtop->c.ui += 4; |
vtop->r = vtop[-1].r; |
} else if (v > VT_CONST) { |
vtop--; |
lexpand(); |
} else |
vtop->r = vtop[-1].r2; |
vtop[-1].r2 = VT_CONST; |
vtop[-1].type.t = VT_INT | u; |
} |
#endif |
/* build a long long from two ints */ |
void lbuild(int t) |
{ |
gv2(RC_INT, RC_INT); |
vtop[-1].r2 = vtop[0].r; |
vtop[-1].type.t = t; |
vpop(); |
} |
/* rotate n first stack elements to the bottom |
I1 ... In -> I2 ... In I1 [top is right] |
*/ |
void vrotb(int n) |
{ |
int i; |
SValue tmp; |
tmp = vtop[-n + 1]; |
for(i=-n+1;i!=0;i++) |
vtop[i] = vtop[i+1]; |
vtop[0] = tmp; |
} |
/* rotate n first stack elements to the top |
I1 ... In -> In I1 ... I(n-1) [top is right] |
*/ |
void vrott(int n) |
{ |
int i; |
SValue tmp; |
tmp = vtop[0]; |
for(i = 0;i < n - 1; i++) |
vtop[-i] = vtop[-i - 1]; |
vtop[-n + 1] = tmp; |
} |
#ifdef TCC_TARGET_ARM |
/* like vrott but in other direction |
In ... I1 -> I(n-1) ... I1 In [top is right] |
*/ |
void vnrott(int n) |
{ |
int i; |
SValue tmp; |
tmp = vtop[-n + 1]; |
for(i = n - 1; i > 0; i--) |
vtop[-i] = vtop[-i + 1]; |
vtop[0] = tmp; |
} |
#endif |
/* pop stack value */ |
void vpop(void) |
{ |
int v; |
v = vtop->r & VT_VALMASK; |
#ifdef TCC_TARGET_I386 |
/* for x86, we need to pop the FP stack */ |
if (v == TREG_ST0 && !nocode_wanted) { |
o(0xd9dd); /* fstp %st(1) */ |
} else |
#endif |
if (v == VT_JMP || v == VT_JMPI) { |
/* need to put correct jump if && or || without test */ |
gsym(vtop->c.ul); |
} |
vtop--; |
} |
/* convert stack entry to register and duplicate its value in another |
register */ |
void gv_dup(void) |
{ |
int rc, t, r, r1; |
SValue sv; |
t = vtop->type.t; |
if ((t & VT_BTYPE) == VT_LLONG) { |
lexpand(); |
gv_dup(); |
vswap(); |
vrotb(3); |
gv_dup(); |
vrotb(4); |
/* stack: H L L1 H1 */ |
lbuild(t); |
vrotb(3); |
vrotb(3); |
vswap(); |
lbuild(t); |
vswap(); |
} else { |
/* duplicate value */ |
rc = RC_INT; |
sv.type.t = VT_INT; |
if (is_float(t)) { |
rc = RC_FLOAT; |
sv.type.t = t; |
} |
r = gv(rc); |
r1 = get_reg(rc); |
sv.r = r; |
sv.c.ul = 0; |
load(r1, &sv); /* move r to r1 */ |
vdup(); |
/* duplicates value */ |
vtop->r = r1; |
} |
} |
/* generate CPU independent (unsigned) long long operations */ |
void gen_opl(int op) |
{ |
int t, a, b, op1, c, i; |
int func; |
SValue tmp; |
switch(op) { |
case '/': |
case TOK_PDIV: |
func = TOK___divdi3; |
goto gen_func; |
case TOK_UDIV: |
func = TOK___udivdi3; |
goto gen_func; |
case '%': |
func = TOK___moddi3; |
goto gen_func; |
case TOK_UMOD: |
func = TOK___umoddi3; |
gen_func: |
/* call generic long long function */ |
vpush_global_sym(&func_old_type, func); |
vrott(3); |
gfunc_call(2); |
vpushi(0); |
vtop->r = REG_IRET; |
vtop->r2 = REG_LRET; |
break; |
case '^': |
case '&': |
case '|': |
case '*': |
case '+': |
case '-': |
t = vtop->type.t; |
vswap(); |
lexpand(); |
vrotb(3); |
lexpand(); |
/* stack: L1 H1 L2 H2 */ |
tmp = vtop[0]; |
vtop[0] = vtop[-3]; |
vtop[-3] = tmp; |
tmp = vtop[-2]; |
vtop[-2] = vtop[-3]; |
vtop[-3] = tmp; |
vswap(); |
/* stack: H1 H2 L1 L2 */ |
if (op == '*') { |
vpushv(vtop - 1); |
vpushv(vtop - 1); |
gen_op(TOK_UMULL); |
lexpand(); |
/* stack: H1 H2 L1 L2 ML MH */ |
for(i=0;i<4;i++) |
vrotb(6); |
/* stack: ML MH H1 H2 L1 L2 */ |
tmp = vtop[0]; |
vtop[0] = vtop[-2]; |
vtop[-2] = tmp; |
/* stack: ML MH H1 L2 H2 L1 */ |
gen_op('*'); |
vrotb(3); |
vrotb(3); |
gen_op('*'); |
/* stack: ML MH M1 M2 */ |
gen_op('+'); |
gen_op('+'); |
} else if (op == '+' || op == '-') { |
/* XXX: add non carry method too (for MIPS or alpha) */ |
if (op == '+') |
op1 = TOK_ADDC1; |
else |
op1 = TOK_SUBC1; |
gen_op(op1); |
/* stack: H1 H2 (L1 op L2) */ |
vrotb(3); |
vrotb(3); |
gen_op(op1 + 1); /* TOK_xxxC2 */ |
} else { |
gen_op(op); |
/* stack: H1 H2 (L1 op L2) */ |
vrotb(3); |
vrotb(3); |
/* stack: (L1 op L2) H1 H2 */ |
gen_op(op); |
/* stack: (L1 op L2) (H1 op H2) */ |
} |
/* stack: L H */ |
lbuild(t); |
break; |
case TOK_SAR: |
case TOK_SHR: |
case TOK_SHL: |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
t = vtop[-1].type.t; |
vswap(); |
lexpand(); |
vrotb(3); |
/* stack: L H shift */ |
c = (int)vtop->c.i; |
/* constant: simpler */ |
/* NOTE: all comments are for SHL. the other cases are |
done by swaping words */ |
vpop(); |
if (op != TOK_SHL) |
vswap(); |
if (c >= 32) { |
/* stack: L H */ |
vpop(); |
if (c > 32) { |
vpushi(c - 32); |
gen_op(op); |
} |
if (op != TOK_SAR) { |
vpushi(0); |
} else { |
gv_dup(); |
vpushi(31); |
gen_op(TOK_SAR); |
} |
vswap(); |
} else { |
vswap(); |
gv_dup(); |
/* stack: H L L */ |
vpushi(c); |
gen_op(op); |
vswap(); |
vpushi(32 - c); |
if (op == TOK_SHL) |
gen_op(TOK_SHR); |
else |
gen_op(TOK_SHL); |
vrotb(3); |
/* stack: L L H */ |
vpushi(c); |
if (op == TOK_SHL) |
gen_op(TOK_SHL); |
else |
gen_op(TOK_SHR); |
gen_op('|'); |
} |
if (op != TOK_SHL) |
vswap(); |
lbuild(t); |
} else { |
/* XXX: should provide a faster fallback on x86 ? */ |
switch(op) { |
case TOK_SAR: |
func = TOK___sardi3; |
goto gen_func; |
case TOK_SHR: |
func = TOK___shrdi3; |
goto gen_func; |
case TOK_SHL: |
func = TOK___shldi3; |
goto gen_func; |
} |
} |
break; |
default: |
/* compare operations */ |
t = vtop->type.t; |
vswap(); |
lexpand(); |
vrotb(3); |
lexpand(); |
/* stack: L1 H1 L2 H2 */ |
tmp = vtop[-1]; |
vtop[-1] = vtop[-2]; |
vtop[-2] = tmp; |
/* stack: L1 L2 H1 H2 */ |
/* compare high */ |
op1 = op; |
/* when values are equal, we need to compare low words. since |
the jump is inverted, we invert the test too. */ |
if (op1 == TOK_LT) |
op1 = TOK_LE; |
else if (op1 == TOK_GT) |
op1 = TOK_GE; |
else if (op1 == TOK_ULT) |
op1 = TOK_ULE; |
else if (op1 == TOK_UGT) |
op1 = TOK_UGE; |
a = 0; |
b = 0; |
gen_op(op1); |
if (op1 != TOK_NE) { |
a = gtst(1, 0); |
} |
if (op != TOK_EQ) { |
/* generate non equal test */ |
/* XXX: NOT PORTABLE yet */ |
if (a == 0) { |
b = gtst(0, 0); |
} else { |
#if defined(TCC_TARGET_I386) |
b = psym(0x850f, 0); |
#elif defined(TCC_TARGET_ARM) |
b = ind; |
o(0x1A000000 | encbranch(ind, 0, 1)); |
#elif defined(TCC_TARGET_C67) |
error("not implemented"); |
#else |
#error not supported |
#endif |
} |
} |
/* compare low. Always unsigned */ |
op1 = op; |
if (op1 == TOK_LT) |
op1 = TOK_ULT; |
else if (op1 == TOK_LE) |
op1 = TOK_ULE; |
else if (op1 == TOK_GT) |
op1 = TOK_UGT; |
else if (op1 == TOK_GE) |
op1 = TOK_UGE; |
gen_op(op1); |
a = gtst(1, a); |
gsym(b); |
vseti(VT_JMPI, a); |
break; |
} |
} |
/* handle integer constant optimizations and various machine |
independent opt */ |
void gen_opic(int op) |
{ |
int fc, c1, c2, n; |
SValue *v1, *v2; |
v1 = vtop - 1; |
v2 = vtop; |
/* currently, we cannot do computations with forward symbols */ |
c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; |
c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; |
if (c1 && c2) { |
fc = v2->c.i; |
switch(op) { |
case '+': v1->c.i += fc; break; |
case '-': v1->c.i -= fc; break; |
case '&': v1->c.i &= fc; break; |
case '^': v1->c.i ^= fc; break; |
case '|': v1->c.i |= fc; break; |
case '*': v1->c.i *= fc; break; |
case TOK_PDIV: |
case '/': |
case '%': |
case TOK_UDIV: |
case TOK_UMOD: |
/* if division by zero, generate explicit division */ |
if (fc == 0) { |
if (const_wanted) |
error("division by zero in constant"); |
goto general_case; |
} |
switch(op) { |
default: v1->c.i /= fc; break; |
case '%': v1->c.i %= fc; break; |
case TOK_UDIV: v1->c.i = (unsigned)v1->c.i / fc; break; |
case TOK_UMOD: v1->c.i = (unsigned)v1->c.i % fc; break; |
} |
break; |
case TOK_SHL: v1->c.i <<= fc; break; |
case TOK_SHR: v1->c.i = (unsigned)v1->c.i >> fc; break; |
case TOK_SAR: v1->c.i >>= fc; break; |
/* tests */ |
case TOK_ULT: v1->c.i = (unsigned)v1->c.i < (unsigned)fc; break; |
case TOK_UGE: v1->c.i = (unsigned)v1->c.i >= (unsigned)fc; break; |
case TOK_EQ: v1->c.i = v1->c.i == fc; break; |
case TOK_NE: v1->c.i = v1->c.i != fc; break; |
case TOK_ULE: v1->c.i = (unsigned)v1->c.i <= (unsigned)fc; break; |
case TOK_UGT: v1->c.i = (unsigned)v1->c.i > (unsigned)fc; break; |
case TOK_LT: v1->c.i = v1->c.i < fc; break; |
case TOK_GE: v1->c.i = v1->c.i >= fc; break; |
case TOK_LE: v1->c.i = v1->c.i <= fc; break; |
case TOK_GT: v1->c.i = v1->c.i > fc; break; |
/* logical */ |
case TOK_LAND: v1->c.i = v1->c.i && fc; break; |
case TOK_LOR: v1->c.i = v1->c.i || fc; break; |
default: |
goto general_case; |
} |
vtop--; |
} else { |
/* if commutative ops, put c2 as constant */ |
if (c1 && (op == '+' || op == '&' || op == '^' || |
op == '|' || op == '*')) { |
vswap(); |
swap(&c1, &c2); |
} |
fc = vtop->c.i; |
if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV || |
op == TOK_PDIV) && |
fc == 1) || |
((op == '+' || op == '-' || op == '|' || op == '^' || |
op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) && |
fc == 0) || |
(op == '&' && |
fc == -1))) { |
/* nothing to do */ |
vtop--; |
} else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) { |
/* try to use shifts instead of muls or divs */ |
if (fc > 0 && (fc & (fc - 1)) == 0) { |
n = -1; |
while (fc) { |
fc >>= 1; |
n++; |
} |
vtop->c.i = n; |
if (op == '*') |
op = TOK_SHL; |
else if (op == TOK_PDIV) |
op = TOK_SAR; |
else |
op = TOK_SHR; |
} |
goto general_case; |
} else if (c2 && (op == '+' || op == '-') && |
(vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == |
(VT_CONST | VT_SYM)) { |
/* symbol + constant case */ |
if (op == '-') |
fc = -fc; |
vtop--; |
vtop->c.i += fc; |
} else { |
general_case: |
if (!nocode_wanted) { |
/* call low level op generator */ |
gen_opi(op); |
} else { |
vtop--; |
} |
} |
} |
} |
/* generate a floating point operation with constant propagation */ |
void gen_opif(int op) |
{ |
int c1, c2; |
SValue *v1, *v2; |
long double f1, f2; |
v1 = vtop - 1; |
v2 = vtop; |
/* currently, we cannot do computations with forward symbols */ |
c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; |
c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; |
if (c1 && c2) { |
if (v1->type.t == VT_FLOAT) { |
f1 = v1->c.f; |
f2 = v2->c.f; |
} else if (v1->type.t == VT_DOUBLE) { |
f1 = v1->c.d; |
f2 = v2->c.d; |
} else { |
f1 = v1->c.ld; |
f2 = v2->c.ld; |
} |
/* NOTE: we only do constant propagation if finite number (not |
NaN or infinity) (ANSI spec) */ |
if (!ieee_finite(f1) || !ieee_finite(f2)) |
goto general_case; |
switch(op) { |
case '+': f1 += f2; break; |
case '-': f1 -= f2; break; |
case '*': f1 *= f2; break; |
case '/': |
if (f2 == 0.0) { |
if (const_wanted) |
error("division by zero in constant"); |
goto general_case; |
} |
f1 /= f2; |
break; |
/* XXX: also handles tests ? */ |
default: |
goto general_case; |
} |
/* XXX: overflow test ? */ |
if (v1->type.t == VT_FLOAT) { |
v1->c.f = f1; |
} else if (v1->type.t == VT_DOUBLE) { |
v1->c.d = f1; |
} else { |
v1->c.ld = f1; |
} |
vtop--; |
} else { |
general_case: |
if (!nocode_wanted) { |
gen_opf(op); |
} else { |
vtop--; |
} |
} |
} |
static int pointed_size(CType *type) |
{ |
int align; |
return type_size(pointed_type(type), &align); |
} |
static inline int is_null_pointer(SValue *p) |
{ |
if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) |
return 0; |
return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) || |
((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0); |
} |
static inline int is_integer_btype(int bt) |
{ |
return (bt == VT_BYTE || bt == VT_SHORT || |
bt == VT_INT || bt == VT_LLONG); |
} |
/* check types for comparison or substraction of pointers */ |
static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op) |
{ |
CType *type1, *type2, tmp_type1, tmp_type2; |
int bt1, bt2; |
/* null pointers are accepted for all comparisons as gcc */ |
if (is_null_pointer(p1) || is_null_pointer(p2)) |
return; |
type1 = &p1->type; |
type2 = &p2->type; |
bt1 = type1->t & VT_BTYPE; |
bt2 = type2->t & VT_BTYPE; |
/* accept comparison between pointer and integer with a warning */ |
if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') { |
warning("comparison between pointer and integer"); |
return; |
} |
/* both must be pointers or implicit function pointers */ |
if (bt1 == VT_PTR) { |
type1 = pointed_type(type1); |
} else if (bt1 != VT_FUNC) |
goto invalid_operands; |
if (bt2 == VT_PTR) { |
type2 = pointed_type(type2); |
} else if (bt2 != VT_FUNC) { |
invalid_operands: |
error("invalid operands to binary %s", get_tok_str(op, NULL)); |
} |
if ((type1->t & VT_BTYPE) == VT_VOID || |
(type2->t & VT_BTYPE) == VT_VOID) |
return; |
tmp_type1 = *type1; |
tmp_type2 = *type2; |
tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); |
tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); |
if (!is_compatible_types(&tmp_type1, &tmp_type2)) { |
/* gcc-like error if '-' is used */ |
if (op == '-') |
goto invalid_operands; |
else |
warning("comparison of distinct pointer types lacks a cast"); |
} |
} |
/* generic gen_op: handles types problems */ |
void gen_op(int op) |
{ |
int u, t1, t2, bt1, bt2, t; |
CType type1; |
t1 = vtop[-1].type.t; |
t2 = vtop[0].type.t; |
bt1 = t1 & VT_BTYPE; |
bt2 = t2 & VT_BTYPE; |
if (bt1 == VT_PTR || bt2 == VT_PTR) { |
/* at least one operand is a pointer */ |
/* relationnal op: must be both pointers */ |
if (op >= TOK_ULT && op <= TOK_GT) { |
check_comparison_pointer_types(vtop - 1, vtop, op); |
/* pointers are handled are unsigned */ |
t = VT_INT | VT_UNSIGNED; |
goto std_op; |
} |
/* if both pointers, then it must be the '-' op */ |
if (bt1 == VT_PTR && bt2 == VT_PTR) { |
if (op != '-') |
error("cannot use pointers here"); |
check_comparison_pointer_types(vtop - 1, vtop, op); |
/* XXX: check that types are compatible */ |
u = pointed_size(&vtop[-1].type); |
gen_opic(op); |
/* set to integer type */ |
vtop->type.t = VT_INT; |
vpushi(u); |
gen_op(TOK_PDIV); |
} else { |
/* exactly one pointer : must be '+' or '-'. */ |
if (op != '-' && op != '+') |
error("cannot use pointers here"); |
/* Put pointer as first operand */ |
if (bt2 == VT_PTR) { |
vswap(); |
swap(&t1, &t2); |
} |
type1 = vtop[-1].type; |
/* XXX: cast to int ? (long long case) */ |
vpushi(pointed_size(&vtop[-1].type)); |
gen_op('*'); |
#ifdef CONFIG_TCC_BCHECK |
/* if evaluating constant expression, no code should be |
generated, so no bound check */ |
if (do_bounds_check && !const_wanted) { |
/* if bounded pointers, we generate a special code to |
test bounds */ |
if (op == '-') { |
vpushi(0); |
vswap(); |
gen_op('-'); |
} |
gen_bounded_ptr_add(); |
} else |
#endif |
{ |
gen_opic(op); |
} |
/* put again type if gen_opic() swaped operands */ |
vtop->type = type1; |
} |
} else if (is_float(bt1) || is_float(bt2)) { |
/* compute bigger type and do implicit casts */ |
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { |
t = VT_LDOUBLE; |
} else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { |
t = VT_DOUBLE; |
} else { |
t = VT_FLOAT; |
} |
/* floats can only be used for a few operations */ |
if (op != '+' && op != '-' && op != '*' && op != '/' && |
(op < TOK_ULT || op > TOK_GT)) |
error("invalid operands for binary operation"); |
goto std_op; |
} else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { |
/* cast to biggest op */ |
t = VT_LLONG; |
/* convert to unsigned if it does not fit in a long long */ |
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || |
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) |
t |= VT_UNSIGNED; |
goto std_op; |
} else { |
/* integer operations */ |
t = VT_INT; |
/* convert to unsigned if it does not fit in an integer */ |
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || |
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) |
t |= VT_UNSIGNED; |
std_op: |
/* XXX: currently, some unsigned operations are explicit, so |
we modify them here */ |
if (t & VT_UNSIGNED) { |
if (op == TOK_SAR) |
op = TOK_SHR; |
else if (op == '/') |
op = TOK_UDIV; |
else if (op == '%') |
op = TOK_UMOD; |
else if (op == TOK_LT) |
op = TOK_ULT; |
else if (op == TOK_GT) |
op = TOK_UGT; |
else if (op == TOK_LE) |
op = TOK_ULE; |
else if (op == TOK_GE) |
op = TOK_UGE; |
} |
vswap(); |
type1.t = t; |
gen_cast(&type1); |
vswap(); |
/* special case for shifts and long long: we keep the shift as |
an integer */ |
if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) |
type1.t = VT_INT; |
gen_cast(&type1); |
if (is_float(t)) |
gen_opif(op); |
else if ((t & VT_BTYPE) == VT_LLONG) |
gen_opl(op); |
else |
gen_opic(op); |
if (op >= TOK_ULT && op <= TOK_GT) { |
/* relationnal op: the result is an int */ |
vtop->type.t = VT_INT; |
} else { |
vtop->type.t = t; |
} |
} |
} |
/* generic itof for unsigned long long case */ |
void gen_cvt_itof1(int t) |
{ |
if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == |
(VT_LLONG | VT_UNSIGNED)) { |
if (t == VT_FLOAT) |
vpush_global_sym(&func_old_type, TOK___ulltof); |
else if (t == VT_DOUBLE) |
vpush_global_sym(&func_old_type, TOK___ulltod); |
else |
vpush_global_sym(&func_old_type, TOK___ulltold); |
vrott(2); |
gfunc_call(1); |
vpushi(0); |
vtop->r = REG_FRET; |
} else { |
gen_cvt_itof(t); |
} |
} |
/* generic ftoi for unsigned long long case */ |
void gen_cvt_ftoi1(int t) |
{ |
int st; |
if (t == (VT_LLONG | VT_UNSIGNED)) { |
/* not handled natively */ |
st = vtop->type.t & VT_BTYPE; |
if (st == VT_FLOAT) |
vpush_global_sym(&func_old_type, TOK___fixunssfdi); |
else if (st == VT_DOUBLE) |
vpush_global_sym(&func_old_type, TOK___fixunsdfdi); |
else |
vpush_global_sym(&func_old_type, TOK___fixunsxfdi); |
vrott(2); |
gfunc_call(1); |
vpushi(0); |
vtop->r = REG_IRET; |
vtop->r2 = REG_LRET; |
} else { |
gen_cvt_ftoi(t); |
} |
} |
/* force char or short cast */ |
void force_charshort_cast(int t) |
{ |
int bits, dbt; |
dbt = t & VT_BTYPE; |
/* XXX: add optimization if lvalue : just change type and offset */ |
if (dbt == VT_BYTE) |
bits = 8; |
else |
bits = 16; |
if (t & VT_UNSIGNED) { |
vpushi((1 << bits) - 1); |
gen_op('&'); |
} else { |
bits = 32 - bits; |
vpushi(bits); |
gen_op(TOK_SHL); |
vpushi(bits); |
gen_op(TOK_SAR); |
} |
} |
/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */ |
static void gen_cast(CType *type) |
{ |
int sbt, dbt, sf, df, c; |
/* special delayed cast for char/short */ |
/* XXX: in some cases (multiple cascaded casts), it may still |
be incorrect */ |
if (vtop->r & VT_MUSTCAST) { |
vtop->r &= ~VT_MUSTCAST; |
force_charshort_cast(vtop->type.t); |
} |
/* bitfields first get cast to ints */ |
if (vtop->type.t & VT_BITFIELD) { |
gv(RC_INT); |
} |
dbt = type->t & (VT_BTYPE | VT_UNSIGNED); |
sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED); |
if (sbt != dbt && !nocode_wanted) { |
sf = is_float(sbt); |
df = is_float(dbt); |
c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; |
if (sf && df) { |
/* convert from fp to fp */ |
if (c) { |
/* constant case: we can do it now */ |
/* XXX: in ISOC, cannot do it if error in convert */ |
if (dbt == VT_FLOAT && sbt == VT_DOUBLE) |
vtop->c.f = (float)vtop->c.d; |
else if (dbt == VT_FLOAT && sbt == VT_LDOUBLE) |
vtop->c.f = (float)vtop->c.ld; |
else if (dbt == VT_DOUBLE && sbt == VT_FLOAT) |
vtop->c.d = (double)vtop->c.f; |
else if (dbt == VT_DOUBLE && sbt == VT_LDOUBLE) |
vtop->c.d = (double)vtop->c.ld; |
else if (dbt == VT_LDOUBLE && sbt == VT_FLOAT) |
vtop->c.ld = (long double)vtop->c.f; |
else if (dbt == VT_LDOUBLE && sbt == VT_DOUBLE) |
vtop->c.ld = (long double)vtop->c.d; |
} else { |
/* non constant case: generate code */ |
gen_cvt_ftof(dbt); |
} |
} else if (df) { |
/* convert int to fp */ |
if (c) { |
switch(sbt) { |
case VT_LLONG | VT_UNSIGNED: |
case VT_LLONG: |
/* XXX: add const cases for long long */ |
goto do_itof; |
case VT_INT | VT_UNSIGNED: |
switch(dbt) { |
case VT_FLOAT: vtop->c.f = (float)vtop->c.ui; break; |
case VT_DOUBLE: vtop->c.d = (double)vtop->c.ui; break; |
case VT_LDOUBLE: vtop->c.ld = (long double)vtop->c.ui; break; |
} |
break; |
default: |
switch(dbt) { |
case VT_FLOAT: vtop->c.f = (float)vtop->c.i; break; |
case VT_DOUBLE: vtop->c.d = (double)vtop->c.i; break; |
case VT_LDOUBLE: vtop->c.ld = (long double)vtop->c.i; break; |
} |
break; |
} |
} else { |
do_itof: |
#if !defined(TCC_TARGET_ARM) |
gen_cvt_itof1(dbt); |
#else |
gen_cvt_itof(dbt); |
#endif |
} |
} else if (sf) { |
/* convert fp to int */ |
/* we handle char/short/etc... with generic code */ |
if (dbt != (VT_INT | VT_UNSIGNED) && |
dbt != (VT_LLONG | VT_UNSIGNED) && |
dbt != VT_LLONG) |
dbt = VT_INT; |
if (c) { |
switch(dbt) { |
case VT_LLONG | VT_UNSIGNED: |
case VT_LLONG: |
/* XXX: add const cases for long long */ |
goto do_ftoi; |
case VT_INT | VT_UNSIGNED: |
switch(sbt) { |
case VT_FLOAT: vtop->c.ui = (unsigned int)vtop->c.d; break; |
case VT_DOUBLE: vtop->c.ui = (unsigned int)vtop->c.d; break; |
case VT_LDOUBLE: vtop->c.ui = (unsigned int)vtop->c.d; break; |
} |
break; |
default: |
/* int case */ |
switch(sbt) { |
case VT_FLOAT: vtop->c.i = (int)vtop->c.d; break; |
case VT_DOUBLE: vtop->c.i = (int)vtop->c.d; break; |
case VT_LDOUBLE: vtop->c.i = (int)vtop->c.d; break; |
} |
break; |
} |
} else { |
do_ftoi: |
gen_cvt_ftoi1(dbt); |
} |
if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) { |
/* additional cast for char/short/bool... */ |
vtop->type.t = dbt; |
gen_cast(type); |
} |
} else if ((dbt & VT_BTYPE) == VT_LLONG) { |
if ((sbt & VT_BTYPE) != VT_LLONG) { |
/* scalar to long long */ |
if (c) { |
if (sbt == (VT_INT | VT_UNSIGNED)) |
vtop->c.ll = vtop->c.ui; |
else |
vtop->c.ll = vtop->c.i; |
} else { |
/* machine independent conversion */ |
gv(RC_INT); |
/* generate high word */ |
if (sbt == (VT_INT | VT_UNSIGNED)) { |
vpushi(0); |
gv(RC_INT); |
} else { |
gv_dup(); |
vpushi(31); |
gen_op(TOK_SAR); |
} |
/* patch second register */ |
vtop[-1].r2 = vtop->r; |
vpop(); |
} |
} |
} else if (dbt == VT_BOOL) { |
/* scalar to bool */ |
vpushi(0); |
gen_op(TOK_NE); |
} else if ((dbt & VT_BTYPE) == VT_BYTE || |
(dbt & VT_BTYPE) == VT_SHORT) { |
force_charshort_cast(dbt); |
} else if ((dbt & VT_BTYPE) == VT_INT) { |
/* scalar to int */ |
if (sbt == VT_LLONG) { |
/* from long long: just take low order word */ |
lexpand(); |
vpop(); |
} |
/* if lvalue and single word type, nothing to do because |
the lvalue already contains the real type size (see |
VT_LVAL_xxx constants) */ |
} |
} |
vtop->type = *type; |
} |
/* return type size. Put alignment at 'a' */ |
static int type_size(CType *type, int *a) |
{ |
Sym *s; |
int bt; |
int size; |
bt = type->t & VT_BTYPE; |
if (bt == VT_STRUCT) { |
/* struct/union */ |
s = type->ref; |
*a = s->r; |
return s->c; |
} else if (bt == VT_PTR) { |
if (type->t & VT_ARRAY) { |
s = type->ref; |
size=type_size(&s->type, a) * s->c; |
return size;//type_size(&s->type, a) * s->c; |
} else { |
*a = PTR_SIZE; |
return PTR_SIZE; |
} |
} else if (bt == VT_LDOUBLE) { |
*a = LDOUBLE_ALIGN; |
return LDOUBLE_SIZE; |
} else if (bt == VT_DOUBLE || bt == VT_LLONG) { |
#ifdef TCC_TARGET_I386 |
*a = 4; |
#else |
*a = 8; |
#endif |
return 8; |
} else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) { |
*a = 4; |
return 4; |
} else if (bt == VT_SHORT) { |
*a = 2; |
return 2; |
} else { |
/* char, void, function, _Bool */ |
*a = 1; |
return 1; |
} |
} |
/* 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. */ |
static void mk_pointer(CType *type) |
{ |
Sym *s; |
s = sym_push(SYM_FIELD, type, 0, -1); |
type->t = VT_PTR | (type->t & ~VT_TYPE); |
type->ref = s; |
} |
/* compare function types. OLD functions match any new functions */ |
static int is_compatible_func(CType *type1, CType *type2) |
{ |
Sym *s1, *s2; |
s1 = type1->ref; |
s2 = type2->ref; |
if (!is_compatible_types(&s1->type, &s2->type)) |
return 0; |
/* check func_call */ |
if (s1->r != s2->r) |
return 0; |
/* XXX: not complete */ |
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD) |
return 1; |
if (s1->c != s2->c) |
return 0; |
while (s1 != NULL) { |
if (s2 == NULL) |
return 0; |
if (!is_compatible_types(&s1->type, &s2->type)) |
return 0; |
s1 = s1->next; |
s2 = s2->next; |
} |
if (s2) |
return 0; |
return 1; |
} |
/* return true if type1 and type2 are exactly the same (including |
qualifiers). |
- enums are not checked as gcc __builtin_types_compatible_p () |
*/ |
static int is_compatible_types(CType *type1, CType *type2) |
{ |
int bt1, t1, t2; |
t1 = type1->t & VT_TYPE; |
t2 = type2->t & VT_TYPE; |
/* XXX: bitfields ? */ |
if (t1 != t2) |
return 0; |
/* test more complicated cases */ |
bt1 = t1 & VT_BTYPE; |
if (bt1 == VT_PTR) { |
type1 = pointed_type(type1); |
type2 = pointed_type(type2); |
return is_compatible_types(type1, type2); |
} else if (bt1 == VT_STRUCT) { |
return (type1->ref == type2->ref); |
} else if (bt1 == VT_FUNC) { |
return is_compatible_func(type1, type2); |
} else { |
return 1; |
} |
} |
/* print a type. If 'varstr' is not NULL, then the variable is also |
printed in the type */ |
/* XXX: union */ |
/* XXX: add array and function pointers */ |
void type_to_str(char *buf, int buf_size, |
CType *type, const char *varstr) |
{ |
int bt, v, t; |
Sym *s, *sa; |
char buf1[256]; |
const char *tstr; |
t = type->t & VT_TYPE; |
bt = t & VT_BTYPE; |
buf[0] = '\0'; |
if (t & VT_CONSTANT) |
pstrcat(buf, buf_size, "const "); |
if (t & VT_VOLATILE) |
pstrcat(buf, buf_size, "volatile "); |
if (t & VT_UNSIGNED) |
pstrcat(buf, buf_size, "unsigned "); |
switch(bt) { |
case VT_VOID: |
tstr = "void"; |
goto add_tstr; |
case VT_BOOL: |
tstr = "_Bool"; |
goto add_tstr; |
case VT_BYTE: |
tstr = "char"; |
goto add_tstr; |
case VT_SHORT: |
tstr = "short"; |
goto add_tstr; |
case VT_INT: |
tstr = "int"; |
goto add_tstr; |
case VT_LONG: |
tstr = "long"; |
goto add_tstr; |
case VT_LLONG: |
tstr = "long long"; |
goto add_tstr; |
case VT_FLOAT: |
tstr = "float"; |
goto add_tstr; |
case VT_DOUBLE: |
tstr = "double"; |
goto add_tstr; |
case VT_LDOUBLE: |
tstr = "long double"; |
add_tstr: |
pstrcat(buf, buf_size, tstr); |
break; |
case VT_ENUM: |
case VT_STRUCT: |
if (bt == VT_STRUCT) |
tstr = "struct "; |
else |
tstr = "enum "; |
pstrcat(buf, buf_size, tstr); |
v = type->ref->v & ~SYM_STRUCT; |
if (v >= SYM_FIRST_ANOM) |
pstrcat(buf, buf_size, "<anonymous>"); |
else |
pstrcat(buf, buf_size, get_tok_str(v, NULL)); |
break; |
case VT_FUNC: |
s = type->ref; |
type_to_str(buf, buf_size, &s->type, varstr); |
pstrcat(buf, buf_size, "("); |
sa = s->next; |
while (sa != NULL) { |
type_to_str(buf1, sizeof(buf1), &sa->type, NULL); |
pstrcat(buf, buf_size, buf1); |
sa = sa->next; |
if (sa) |
pstrcat(buf, buf_size, ", "); |
} |
pstrcat(buf, buf_size, ")"); |
goto no_var; |
case VT_PTR: |
s = type->ref; |
pstrcpy(buf1, sizeof(buf1), "*"); |
if (varstr) |
pstrcat(buf1, sizeof(buf1), varstr); |
type_to_str(buf, buf_size, &s->type, buf1); |
goto no_var; |
} |
if (varstr) { |
pstrcat(buf, buf_size, " "); |
pstrcat(buf, buf_size, varstr); |
} |
no_var: ; |
} |
/* verify type compatibility to store vtop in 'dt' type, and generate |
casts if needed. */ |
static void gen_assign_cast(CType *dt) |
{ |
CType *st, *type1, *type2, tmp_type1, tmp_type2; |
char buf1[256], buf2[256]; |
int dbt, sbt; |
st = &vtop->type; /* source type */ |
dbt = dt->t & VT_BTYPE; |
sbt = st->t & VT_BTYPE; |
if (dt->t & VT_CONSTANT) |
warning("assignment of read-only location"); |
switch(dbt) { |
case VT_PTR: |
/* special cases for pointers */ |
/* '0' can also be a pointer */ |
if (is_null_pointer(vtop)) |
goto type_ok; |
/* accept implicit pointer to integer cast with warning */ |
if (is_integer_btype(sbt)) { |
warning("assignment makes pointer from integer without a cast"); |
goto type_ok; |
} |
type1 = pointed_type(dt); |
/* a function is implicitely a function pointer */ |
if (sbt == VT_FUNC) { |
if ((type1->t & VT_BTYPE) != VT_VOID && |
!is_compatible_types(pointed_type(dt), st)) |
goto error; |
else |
goto type_ok; |
} |
if (sbt != VT_PTR) |
goto error; |
type2 = pointed_type(st); |
if ((type1->t & VT_BTYPE) == VT_VOID || |
(type2->t & VT_BTYPE) == VT_VOID) { |
/* void * can match anything */ |
} else { |
/* exact type match, except for unsigned */ |
tmp_type1 = *type1; |
tmp_type2 = *type2; |
tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); |
tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); |
if (!is_compatible_types(&tmp_type1, &tmp_type2)) |
goto error; |
} |
/* check const and volatile */ |
if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || |
(!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE))) |
warning("assignment discards qualifiers from pointer target type"); |
break; |
case VT_BYTE: |
case VT_SHORT: |
case VT_INT: |
case VT_LLONG: |
if (sbt == VT_PTR || sbt == VT_FUNC) { |
warning("assignment makes integer from pointer without a cast"); |
} |
/* XXX: more tests */ |
break; |
case VT_STRUCT: |
tmp_type1 = *dt; |
tmp_type2 = *st; |
tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE); |
tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE); |
if (!is_compatible_types(&tmp_type1, &tmp_type2)) { |
error: |
type_to_str(buf1, sizeof(buf1), st, NULL); |
type_to_str(buf2, sizeof(buf2), dt, NULL); |
error("cannot cast '%s' to '%s'", buf1, buf2); |
} |
break; |
} |
type_ok: |
gen_cast(dt); |
} |
/* store vtop in lvalue pushed on stack */ |
void vstore(void) |
{ |
int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast; |
ft = vtop[-1].type.t; |
sbt = vtop->type.t & VT_BTYPE; |
dbt = ft & VT_BTYPE; |
if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || |
(sbt == VT_INT && dbt == VT_SHORT)) { |
/* optimize char/short casts */ |
delayed_cast = VT_MUSTCAST; |
vtop->type.t = ft & VT_TYPE; |
/* XXX: factorize */ |
if (ft & VT_CONSTANT) |
warning("assignment of read-only location"); |
} else { |
delayed_cast = 0; |
if (!(ft & VT_BITFIELD)) |
gen_assign_cast(&vtop[-1].type); |
} |
if (sbt == VT_STRUCT) { |
/* if structure, only generate pointer */ |
/* structure assignment : generate memcpy */ |
/* XXX: optimize if small size */ |
if (!nocode_wanted) { |
size = type_size(&vtop->type, &align); |
vpush_global_sym(&func_old_type, TOK_memcpy); |
/* destination */ |
vpushv(vtop - 2); |
vtop->type.t = VT_INT; |
gaddrof(); |
/* source */ |
vpushv(vtop - 2); |
vtop->type.t = VT_INT; |
gaddrof(); |
/* type size */ |
vpushi(size); |
gfunc_call(3); |
vswap(); |
vpop(); |
} else { |
vswap(); |
vpop(); |
} |
/* leave source on stack */ |
} else if (ft & VT_BITFIELD) { |
/* bitfield store handling */ |
bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f; |
bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f; |
/* remove bit field info to avoid loops */ |
vtop[-1].type.t = ft & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); |
/* duplicate destination */ |
vdup(); |
vtop[-1] = vtop[-2]; |
/* mask and shift source */ |
vpushi((1 << bit_size) - 1); |
gen_op('&'); |
vpushi(bit_pos); |
gen_op(TOK_SHL); |
/* load destination, mask and or with source */ |
vswap(); |
vpushi(~(((1 << bit_size) - 1) << bit_pos)); |
gen_op('&'); |
gen_op('|'); |
/* store result */ |
vstore(); |
} else { |
#ifdef CONFIG_TCC_BCHECK |
/* bound check case */ |
if (vtop[-1].r & VT_MUSTBOUND) { |
vswap(); |
gbound(); |
vswap(); |
} |
#endif |
if (!nocode_wanted) { |
rc = RC_INT; |
if (is_float(ft)) |
rc = RC_FLOAT; |
r = gv(rc); /* generate value */ |
/* if lvalue was saved on stack, must read it */ |
if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { |
SValue sv; |
t = get_reg(RC_INT); |
sv.type.t = VT_INT; |
sv.r = VT_LOCAL | VT_LVAL; |
sv.c.ul = vtop[-1].c.ul; |
load(t, &sv); |
vtop[-1].r = t | VT_LVAL; |
} |
store(r, vtop - 1); |
/* two word case handling : store second register at word + 4 */ |
if ((ft & VT_BTYPE) == VT_LLONG) { |
vswap(); |
/* convert to int to increment easily */ |
vtop->type.t = VT_INT; |
gaddrof(); |
vpushi(4); |
gen_op('+'); |
vtop->r |= VT_LVAL; |
vswap(); |
/* XXX: it works because r2 is spilled last ! */ |
store(vtop->r2, vtop - 1); |
} |
} |
vswap(); |
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ |
vtop->r |= delayed_cast; |
} |
} |
/* post defines POST/PRE add. c is the token ++ or -- */ |
void inc(int post, int c) |
{ |
test_lvalue(); |
vdup(); /* save lvalue */ |
if (post) { |
gv_dup(); /* duplicate value */ |
vrotb(3); |
vrotb(3); |
} |
/* add constant */ |
vpushi(c - TOK_MID); |
gen_op('+'); |
vstore(); /* store value */ |
if (post) |
vpop(); /* if post op, return saved value */ |
} |
/* Parse GNUC __attribute__ extension. Currently, the following |
extensions are recognized: |
- aligned(n) : set data/function alignment. |
- packed : force data alignment to 1 |
- section(x) : generate data/code in this section. |
- unused : currently ignored, but may be used someday. |
- regparm(n) : pass function parameters in registers (i386 only) |
*/ |
static void parse_attribute(AttributeDef *ad) |
{ |
int t, n; |
while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) { |
next(); |
skip('('); |
skip('('); |
while (tok != ')') { |
if (tok < TOK_IDENT) |
expect("attribute name"); |
t = tok; |
next(); |
switch(t) { |
case TOK_SECTION1: |
case TOK_SECTION2: |
skip('('); |
if (tok != TOK_STR) |
expect("section name"); |
ad->section = find_section(tcc_state, (char *)tokc.cstr->data); |
next(); |
skip(')'); |
break; |
case TOK_ALIGNED1: |
case TOK_ALIGNED2: |
if (tok == '(') { |
next(); |
n = expr_const(); |
if (n <= 0 || (n & (n - 1)) != 0) |
error("alignment must be a positive power of two"); |
skip(')'); |
} else { |
n = MAX_ALIGN; |
} |
ad->aligned = n; |
break; |
case TOK_PACKED1: |
case TOK_PACKED2: |
ad->packed = 1; |
break; |
case TOK_UNUSED1: |
case TOK_UNUSED2: |
/* currently, no need to handle it because tcc does not |
track unused objects */ |
break; |
case TOK_NORETURN1: |
case TOK_NORETURN2: |
/* currently, no need to handle it because tcc does not |
track unused objects */ |
break; |
case TOK_CDECL1: |
case TOK_CDECL2: |
case TOK_CDECL3: |
ad->func_call = FUNC_CDECL; |
break; |
case TOK_STDCALL1: |
case TOK_STDCALL2: |
case TOK_STDCALL3: |
ad->func_call = FUNC_STDCALL; |
break; |
#ifdef TCC_TARGET_I386 |
case TOK_REGPARM1: |
case TOK_REGPARM2: |
skip('('); |
n = expr_const(); |
if (n > 3) |
n = 3; |
else if (n < 0) |
n = 0; |
if (n > 0) |
ad->func_call = FUNC_FASTCALL1 + n - 1; |
skip(')'); |
break; |
#endif |
case TOK_DLLEXPORT: |
ad->dllexport = 1; |
break; |
default: |
if (tcc_state->warn_unsupported) |
warning("'%s' attribute ignored", get_tok_str(t, NULL)); |
/* skip parameters */ |
/* XXX: skip parenthesis too */ |
if (tok == '(') { |
next(); |
while (tok != ')' && tok != -1) |
next(); |
next(); |
} |
break; |
} |
if (tok != ',') |
break; |
next(); |
} |
skip(')'); |
skip(')'); |
} |
} |
/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ |
static void struct_decl(CType *type, int u) |
{ |
int a, v, size, align, maxalign, c, offset; |
int bit_size, bit_pos, bsize, bt, lbit_pos; |
Sym *s, *ss, **ps; |
AttributeDef ad; |
CType type1, btype; |
a = tok; /* save decl type */ |
next(); |
if (tok != '{') { |
v = tok; |
next(); |
/* struct already defined ? return it */ |
if (v < TOK_IDENT) |
expect("struct/union/enum name"); |
s = struct_find(v); |
if (s) { |
if (s->type.t != a) |
error("invalid type"); |
goto do_decl; |
} |
} else { |
v = anon_sym++; |
} |
type1.t = a; |
/* we put an undefined size for struct/union */ |
s = sym_push(v | SYM_STRUCT, &type1, 0, -1); |
s->r = 0; /* default alignment is zero as gcc */ |
/* put struct/union/enum name in type */ |
do_decl: |
type->t = u; |
type->ref = s; |
if (tok == '{') { |
next(); |
if (s->c != -1) |
error("struct/union/enum already defined"); |
/* cannot be empty */ |
c = 0; |
/* non empty enums are not allowed */ |
if (a == TOK_ENUM) { |
for(;;) { |
v = tok; |
if (v < TOK_UIDENT) |
expect("identifier"); |
next(); |
if (tok == '=') { |
next(); |
c = expr_const(); |
} |
/* enum symbols have static storage */ |
ss = sym_push(v, &int_type, VT_CONST, c); |
ss->type.t |= VT_STATIC; |
if (tok != ',') |
break; |
next(); |
c++; |
/* NOTE: we accept a trailing comma */ |
if (tok == '}') |
break; |
} |
skip('}'); |
} else { |
maxalign = 1; |
ps = &s->next; |
bit_pos = 0; |
offset = 0; |
while (tok != '}') { |
parse_btype(&btype, &ad); |
while (1) { |
bit_size = -1; |
v = 0; |
type1 = btype; |
if (tok != ':') { |
type_decl(&type1, &ad, &v, TYPE_DIRECT); |
if ((type1.t & VT_BTYPE) == VT_FUNC || |
(type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE))) |
error("invalid type for '%s'", |
get_tok_str(v, NULL)); |
} |
if (tok == ':') { |
next(); |
bit_size = expr_const(); |
/* XXX: handle v = 0 case for messages */ |
if (bit_size < 0) |
error("negative width in bit-field '%s'", |
get_tok_str(v, NULL)); |
if (v && bit_size == 0) |
error("zero width for bit-field '%s'", |
get_tok_str(v, NULL)); |
} |
size = type_size(&type1, &align); |
if (ad.aligned) { |
if (align < ad.aligned) |
align = ad.aligned; |
} else if (ad.packed) { |
align = 1; |
} else if (*tcc_state->pack_stack_ptr) { |
if (align > *tcc_state->pack_stack_ptr) |
align = *tcc_state->pack_stack_ptr; |
} |
lbit_pos = 0; |
if (bit_size >= 0) { |
bt = type1.t & VT_BTYPE; |
if (bt != VT_INT && |
bt != VT_BYTE && |
bt != VT_SHORT && |
bt != VT_BOOL && |
bt != VT_ENUM) |
error("bitfields must have scalar type"); |
bsize = size * 8; |
if (bit_size > bsize) { |
error("width of '%s' exceeds its type", |
get_tok_str(v, NULL)); |
} else if (bit_size == bsize) { |
/* no need for bit fields */ |
bit_pos = 0; |
} else if (bit_size == 0) { |
/* XXX: what to do if only padding in a |
structure ? */ |
/* zero size: means to pad */ |
if (bit_pos > 0) |
bit_pos = bsize; |
} else { |
/* we do not have enough room ? */ |
if ((bit_pos + bit_size) > bsize) |
bit_pos = 0; |
lbit_pos = bit_pos; |
/* XXX: handle LSB first */ |
type1.t |= VT_BITFIELD | |
(bit_pos << VT_STRUCT_SHIFT) | |
(bit_size << (VT_STRUCT_SHIFT + 6)); |
bit_pos += bit_size; |
} |
} else { |
bit_pos = 0; |
} |
if (v) { |
/* add new memory data only if starting |
bit field */ |
if (lbit_pos == 0) { |
if (a == TOK_STRUCT) { |
//17.09.2007 |
//c = (c + align - 1) & -align; |
offset = c; |
//c += size; |
if (size<=4) {c=c+size;} |
else |
{c=c+align;} |
} else { |
offset = 0; |
if (size > c) |
c = size; |
} |
if (align > maxalign) |
maxalign = align; |
} |
#if 0 |
printf("add field %s offset=%d", |
get_tok_str(v, NULL), offset); |
if (type1.t & VT_BITFIELD) { |
printf(" pos=%d size=%d", |
(type1.t >> VT_STRUCT_SHIFT) & 0x3f, |
(type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f); |
} |
printf("\n"); |
#endif |
ss = sym_push(v | SYM_FIELD, &type1, 0, offset); |
*ps = ss; |
ps = &ss->next; |
} |
if (tok == ';' || tok == TOK_EOF) |
break; |
skip(','); |
} |
skip(';'); |
} |
skip('}'); |
/* store size and alignment */ |
s->c = (c + maxalign - 1) & -maxalign; |
s->r = maxalign; |
} |
} |
} |
/* return 0 if no type declaration. otherwise, return the basic type |
and skip it. |
*/ |
static int parse_btype(CType *type, AttributeDef *ad) |
{ |
int t, u, type_found, typespec_found; |
Sym *s; |
CType type1; |
memset(ad, 0, sizeof(AttributeDef)); |
type_found = 0; |
typespec_found = 0; |
t = 0; |
while(1) { |
switch(tok) { |
case TOK_EXTENSION: |
/* currently, we really ignore extension */ |
next(); |
continue; |
/* basic types */ |
case TOK_CHAR: |
u = VT_BYTE; |
basic_type: |
next(); |
basic_type1: |
if ((t & VT_BTYPE) != 0) |
error("too many basic types"); |
t |= u; |
typespec_found = 1; |
break; |
case TOK_VOID: |
u = VT_VOID; |
goto basic_type; |
case TOK_SHORT: |
u = VT_SHORT; |
goto basic_type; |
case TOK_INT: |
next(); |
typespec_found = 1; |
break; |
case TOK_LONG: |
next(); |
if ((t & VT_BTYPE) == VT_DOUBLE) { |
t = (t & ~VT_BTYPE) | VT_LDOUBLE; |
} else if ((t & VT_BTYPE) == VT_LONG) { |
t = (t & ~VT_BTYPE) | VT_LLONG; |
} else { |
u = VT_LONG; |
goto basic_type1; |
} |
break; |
case TOK_BOOL: |
u = VT_BOOL; |
goto basic_type; |
case TOK_FLOAT: |
u = VT_FLOAT; |
goto basic_type; |
case TOK_DOUBLE: |
next(); |
if ((t & VT_BTYPE) == VT_LONG) { |
t = (t & ~VT_BTYPE) | VT_LDOUBLE; |
} else { |
u = VT_DOUBLE; |
goto basic_type1; |
} |
break; |
case TOK_ENUM: |
struct_decl(&type1, VT_ENUM); |
basic_type2: |
u = type1.t; |
type->ref = type1.ref; |
goto basic_type1; |
case TOK_STRUCT: |
case TOK_UNION: |
struct_decl(&type1, VT_STRUCT); |
goto basic_type2; |
/* type modifiers */ |
case TOK_CONST1: |
case TOK_CONST2: |
case TOK_CONST3: |
t |= VT_CONSTANT; |
next(); |
break; |
case TOK_VOLATILE1: |
case TOK_VOLATILE2: |
case TOK_VOLATILE3: |
t |= VT_VOLATILE; |
next(); |
break; |
case TOK_SIGNED1: |
case TOK_SIGNED2: |
case TOK_SIGNED3: |
typespec_found = 1; |
t |= VT_SIGNED; |
next(); |
break; |
case TOK_REGISTER: |
case TOK_AUTO: |
case TOK_RESTRICT1: |
case TOK_RESTRICT2: |
case TOK_RESTRICT3: |
next(); |
break; |
case TOK_UNSIGNED: |
t |= VT_UNSIGNED; |
next(); |
typespec_found = 1; |
break; |
/* storage */ |
case TOK_EXTERN: |
t |= VT_EXTERN; |
next(); |
break; |
case TOK_STATIC: |
t |= VT_STATIC; |
next(); |
break; |
case TOK_TYPEDEF: |
t |= VT_TYPEDEF; |
next(); |
break; |
case TOK_INLINE1: |
case TOK_INLINE2: |
case TOK_INLINE3: |
t |= VT_INLINE; |
next(); |
break; |
/* GNUC attribute */ |
case TOK_ATTRIBUTE1: |
case TOK_ATTRIBUTE2: |
parse_attribute(ad); |
break; |
/* GNUC typeof */ |
case TOK_TYPEOF1: |
case TOK_TYPEOF2: |
case TOK_TYPEOF3: |
next(); |
parse_expr_type(&type1); |
goto basic_type2; |
default: |
if (typespec_found) |
goto the_end; |
s = sym_find(tok); |
if (!s || !(s->type.t & VT_TYPEDEF)) |
goto the_end; |
t |= (s->type.t & ~VT_TYPEDEF); |
type->ref = s->type.ref; |
next(); |
break; |
} |
type_found = 1; |
} |
the_end: |
if ((t & (VT_SIGNED|VT_UNSIGNED)) == (VT_SIGNED|VT_UNSIGNED)) |
error("signed and unsigned modifier"); |
if (tcc_state->char_is_unsigned) { |
if ((t & (VT_SIGNED|VT_UNSIGNED|VT_BTYPE)) == VT_BYTE) |
t |= VT_UNSIGNED; |
} |
t &= ~VT_SIGNED; |
/* long is never used as type */ |
if ((t & VT_BTYPE) == VT_LONG) |
t = (t & ~VT_BTYPE) | VT_INT; |
type->t = t; |
return type_found; |
} |
/* convert a function parameter type (array to pointer and function to |
function pointer) */ |
static inline void convert_parameter_type(CType *pt) |
{ |
/* remove const and volatile qualifiers (XXX: const could be used |
to indicate a const function parameter */ |
pt->t &= ~(VT_CONSTANT | VT_VOLATILE); |
/* array must be transformed to pointer according to ANSI C */ |
pt->t &= ~VT_ARRAY; |
if ((pt->t & VT_BTYPE) == VT_FUNC) { |
mk_pointer(pt); |
} |
} |
static void post_type(CType *type, AttributeDef *ad) |
{ |
int n, l, t1; |
Sym **plast, *s, *first; |
AttributeDef ad1; |
CType pt; |
if (tok == '(') { |
/* function declaration */ |
next(); |
l = 0; |
first = NULL; |
plast = &first; |
while (tok != ')') { |
/* read param name and compute offset */ |
if (l != FUNC_OLD) { |
if (!parse_btype(&pt, &ad1)) { |
if (l) { |
error("invalid type"); |
} else { |
l = FUNC_OLD; |
goto old_proto; |
} |
} |
l = FUNC_NEW; |
if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')') |
break; |
type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT); |
if ((pt.t & VT_BTYPE) == VT_VOID) |
error("parameter declared as void"); |
} else { |
old_proto: |
n = tok; |
pt.t = VT_INT; |
next(); |
} |
convert_parameter_type(&pt); |
s = sym_push(n | SYM_FIELD, &pt, 0, 0); |
*plast = s; |
plast = &s->next; |
if (tok == ',') { |
next(); |
if (l == FUNC_NEW && tok == TOK_DOTS) { |
l = FUNC_ELLIPSIS; |
next(); |
break; |
} |
} |
} |
/* if no parameters, then old type prototype */ |
if (l == 0) |
l = FUNC_OLD; |
skip(')'); |
t1 = type->t & VT_STORAGE; |
/* NOTE: const is ignored in returned type as it has a special |
meaning in gcc / C++ */ |
type->t &= ~(VT_STORAGE | VT_CONSTANT); |
post_type(type, ad); |
/* we push a anonymous symbol which will contain the function prototype */ |
s = sym_push(SYM_FIELD, type, ad->func_call, l); |
s->next = first; |
type->t = t1 | VT_FUNC; |
type->ref = s; |
} else if (tok == '[') { |
/* array definition */ |
next(); |
n = -1; |
if (tok != ']') { |
n = expr_const(); |
if (n < 0) |
error("invalid array size"); |
} |
skip(']'); |
/* parse next post type */ |
t1 = type->t & VT_STORAGE; |
type->t &= ~VT_STORAGE; |
post_type(type, ad); |
/* we push a anonymous symbol which will contain the array |
element type */ |
s = sym_push(SYM_FIELD, type, 0, n); |
type->t = t1 | VT_ARRAY | VT_PTR; |
type->ref = s; |
} |
} |
/* Parse a type declaration (except basic type), and return the type |
in 'type'. 'td' is a bitmask indicating which kind of type decl is |
expected. 'type' should contain the basic type. 'ad' is the |
attribute definition of the basic type. It can be modified by |
type_decl(). |
*/ |
static void type_decl(CType *type, AttributeDef *ad, int *v, int td) |
{ |
Sym *s; |
CType type1, *type2; |
int qualifiers; |
while (tok == '*') { |
qualifiers = 0; |
redo: |
next(); |
switch(tok) { |
case TOK_CONST1: |
case TOK_CONST2: |
case TOK_CONST3: |
qualifiers |= VT_CONSTANT; |
goto redo; |
case TOK_VOLATILE1: |
case TOK_VOLATILE2: |
case TOK_VOLATILE3: |
qualifiers |= VT_VOLATILE; |
goto redo; |
case TOK_RESTRICT1: |
case TOK_RESTRICT2: |
case TOK_RESTRICT3: |
goto redo; |
} |
mk_pointer(type); |
type->t |= qualifiers; |
} |
/* XXX: clarify attribute handling */ |
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) |
parse_attribute(ad); |
/* recursive type */ |
/* XXX: incorrect if abstract type for functions (e.g. 'int ()') */ |
type1.t = 0; /* XXX: same as int */ |
if (tok == '(') { |
next(); |
/* XXX: this is not correct to modify 'ad' at this point, but |
the syntax is not clear */ |
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) |
parse_attribute(ad); |
type_decl(&type1, ad, v, td); |
skip(')'); |
} else { |
/* type identifier */ |
if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) { |
*v = tok; |
next(); |
} else { |
if (!(td & TYPE_ABSTRACT)) |
expect("identifier"); |
*v = 0; |
} |
} |
post_type(type, ad); |
if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) |
parse_attribute(ad); |
if (!type1.t) |
return; |
/* append type at the end of type1 */ |
type2 = &type1; |
for(;;) { |
s = type2->ref; |
type2 = &s->type; |
if (!type2->t) { |
*type2 = *type; |
break; |
} |
} |
*type = type1; |
} |
/* compute the lvalue VT_LVAL_xxx needed to match type t. */ |
static int lvalue_type(int t) |
{ |
int bt, r; |
r = VT_LVAL; |
bt = t & VT_BTYPE; |
if (bt == VT_BYTE || bt == VT_BOOL) |
r |= VT_LVAL_BYTE; |
else if (bt == VT_SHORT) |
r |= VT_LVAL_SHORT; |
else |
return r; |
if (t & VT_UNSIGNED) |
r |= VT_LVAL_UNSIGNED; |
return r; |
} |
/* indirection with full error checking and bound check */ |
static void indir(void) |
{ |
if ((vtop->type.t & VT_BTYPE) != VT_PTR) |
expect("pointer"); |
if ((vtop->r & VT_LVAL) && !nocode_wanted) |
gv(RC_INT); |
vtop->type = *pointed_type(&vtop->type); |
/* an array is never an lvalue */ |
if (!(vtop->type.t & VT_ARRAY)) { |
vtop->r |= lvalue_type(vtop->type.t); |
/* if bound checking, the referenced pointer must be checked */ |
if (do_bounds_check) |
vtop->r |= VT_MUSTBOUND; |
} |
} |
/* pass a parameter to a function and do type checking and casting */ |
static void gfunc_param_typed(Sym *func, Sym *arg) |
{ |
int func_type; |
CType type; |
func_type = func->c; |
if (func_type == FUNC_OLD || |
(func_type == FUNC_ELLIPSIS && arg == NULL)) { |
/* default casting : only need to convert float to double */ |
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) { |
type.t = VT_DOUBLE; |
gen_cast(&type); |
} |
} else if (arg == NULL) { |
error("too many arguments to function"); |
} else { |
type = arg->type; |
type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ |
gen_assign_cast(&type); |
} |
} |
/* parse an expression of the form '(type)' or '(expr)' and return its |
type */ |
static void parse_expr_type(CType *type) |
{ |
int n; |
AttributeDef ad; |
skip('('); |
if (parse_btype(type, &ad)) { |
type_decl(type, &ad, &n, TYPE_ABSTRACT); |
} else { |
expr_type(type); |
} |
skip(')'); |
} |
static void parse_type(CType *type) |
{ |
AttributeDef ad; |
int n; |
if (!parse_btype(type, &ad)) { |
expect("type"); |
} |
type_decl(type, &ad, &n, TYPE_ABSTRACT); |
} |
static void vpush_tokc(int t) |
{ |
CType type; |
type.t = t; |
vsetc(&type, VT_CONST, &tokc); |
} |
static void unary(void) |
{ |
int n, t, align, size, r; |
CType type; |
Sym *s; |
AttributeDef ad; |
/* XXX: GCC 2.95.3 does not generate a table although it should be |
better here */ |
tok_next: |
switch(tok) { |
case TOK_EXTENSION: |
next(); |
goto tok_next; |
case TOK_CINT: |
case TOK_CCHAR: |
case TOK_LCHAR: |
vpushi(tokc.i); |
next(); |
break; |
case TOK_CUINT: |
vpush_tokc(VT_INT | VT_UNSIGNED); |
next(); |
break; |
case TOK_CLLONG: |
vpush_tokc(VT_LLONG); |
next(); |
break; |
case TOK_CULLONG: |
vpush_tokc(VT_LLONG | VT_UNSIGNED); |
next(); |
break; |
case TOK_CFLOAT: |
vpush_tokc(VT_FLOAT); |
next(); |
break; |
case TOK_CDOUBLE: |
vpush_tokc(VT_DOUBLE); |
next(); |
break; |
case TOK_CLDOUBLE: |
vpush_tokc(VT_LDOUBLE); |
next(); |
break; |
case TOK___FUNCTION__: |
if (!gnu_ext) |
goto tok_identifier; |
/* fall thru */ |
case TOK___FUNC__: |
{ |
void *ptr; |
int len; |
/* special function name identifier */ |
len = strlen(funcname) + 1; |
/* generate char[len] type */ |
type.t = VT_BYTE; |
mk_pointer(&type); |
type.t |= VT_ARRAY; |
type.ref->c = len; |
vpush_ref(&type, data_section, data_section->data_offset, len); |
ptr = section_ptr_add(data_section, len); |
memcpy(ptr, funcname, len); |
next(); |
} |
break; |
case TOK_LSTR: |
t = VT_INT; |
goto str_init; |
case TOK_STR: |
/* string parsing */ |
t = VT_BYTE; |
str_init: |
if (tcc_state->warn_write_strings) |
t |= VT_CONSTANT; |
type.t = t; |
mk_pointer(&type); |
type.t |= VT_ARRAY; |
memset(&ad, 0, sizeof(AttributeDef)); |
decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0); |
break; |
case '(': |
next(); |
/* cast ? */ |
if (parse_btype(&type, &ad)) { |
type_decl(&type, &ad, &n, TYPE_ABSTRACT); |
skip(')'); |
/* check ISOC99 compound literal */ |
if (tok == '{') { |
/* data is allocated locally by default */ |
if (global_expr) |
r = VT_CONST; |
else |
r = VT_LOCAL; |
/* all except arrays are lvalues */ |
if (!(type.t & VT_ARRAY)) |
r |= lvalue_type(type.t); |
memset(&ad, 0, sizeof(AttributeDef)); |
decl_initializer_alloc(&type, &ad, r, 1, 0, 0); |
} else { |
unary(); |
gen_cast(&type); |
} |
} else if (tok == '{') { |
/* save all registers */ |
save_regs(0); |
/* statement expression : we do not accept break/continue |
inside as GCC does */ |
block(NULL, NULL, NULL, NULL, 0, 1); |
skip(')'); |
} else { |
gexpr(); |
skip(')'); |
} |
break; |
case '*': |
next(); |
unary(); |
indir(); |
break; |
case '&': |
next(); |
unary(); |
/* functions names must be treated as function pointers, |
except for unary '&' and sizeof. Since we consider that |
functions are not lvalues, we only have to handle it |
there and in function calls. */ |
/* arrays can also be used although they are not lvalues */ |
if ((vtop->type.t & VT_BTYPE) != VT_FUNC && |
!(vtop->type.t & VT_ARRAY)) |
test_lvalue(); |
mk_pointer(&vtop->type); |
gaddrof(); |
break; |
case '!': |
next(); |
unary(); |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) |
vtop->c.i = !vtop->c.i; |
else if ((vtop->r & VT_VALMASK) == VT_CMP) |
vtop->c.i = vtop->c.i ^ 1; |
else |
vseti(VT_JMP, gtst(1, 0)); |
break; |
case '~': |
next(); |
unary(); |
vpushi(-1); |
gen_op('^'); |
break; |
case '+': |
next(); |
/* in order to force cast, we add zero */ |
unary(); |
if ((vtop->type.t & VT_BTYPE) == VT_PTR) |
error("pointer not accepted for unary plus"); |
vpushi(0); |
gen_op('+'); |
break; |
case TOK_SIZEOF: |
case TOK_ALIGNOF1: |
case TOK_ALIGNOF2: |
t = tok; |
next(); |
if (tok == '(') { |
parse_expr_type(&type); |
} else { |
unary_type(&type); |
} |
size = type_size(&type, &align); |
if (t == TOK_SIZEOF) { |
if (size < 0) |
error("sizeof applied to an incomplete type"); |
vpushi(size); |
} else { |
vpushi(align); |
} |
break; |
case TOK_builtin_types_compatible_p: |
{ |
CType type1, type2; |
next(); |
skip('('); |
parse_type(&type1); |
skip(','); |
parse_type(&type2); |
skip(')'); |
type1.t &= ~(VT_CONSTANT | VT_VOLATILE); |
type2.t &= ~(VT_CONSTANT | VT_VOLATILE); |
vpushi(is_compatible_types(&type1, &type2)); |
} |
break; |
case TOK_builtin_constant_p: |
{ |
int saved_nocode_wanted, res; |
next(); |
skip('('); |
saved_nocode_wanted = nocode_wanted; |
nocode_wanted = 1; |
gexpr(); |
res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; |
vpop(); |
nocode_wanted = saved_nocode_wanted; |
skip(')'); |
vpushi(res); |
} |
break; |
case TOK_INC: |
case TOK_DEC: |
t = tok; |
next(); |
unary(); |
inc(0, t); |
break; |
case '-': |
next(); |
vpushi(0); |
unary(); |
gen_op('-'); |
break; |
case TOK_LAND: |
if (!gnu_ext) |
goto tok_identifier; |
next(); |
/* allow to take the address of a label */ |
if (tok < TOK_UIDENT) |
expect("label identifier"); |
s = label_find(tok); |
if (!s) { |
s = label_push(&global_label_stack, tok, LABEL_FORWARD); |
} else { |
if (s->r == LABEL_DECLARED) |
s->r = LABEL_FORWARD; |
} |
if (!s->type.t) { |
s->type.t = VT_VOID; |
mk_pointer(&s->type); |
s->type.t |= VT_STATIC; |
} |
vset(&s->type, VT_CONST | VT_SYM, 0); |
vtop->sym = s; |
next(); |
break; |
default: |
tok_identifier: |
t = tok; |
next(); |
if (t < TOK_UIDENT) |
expect("identifier"); |
s = sym_find(t); |
if (!s) { |
if (tok != '(') |
error("'%s' undeclared", get_tok_str(t, NULL)); |
/* for simple function calls, we tolerate undeclared |
external reference to int() function */ |
if (tcc_state->warn_implicit_function_declaration) |
warning("implicit declaration of function '%s'", |
get_tok_str(t, NULL)); |
s = external_global_sym(t, &func_old_type, 0); |
} |
if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) == |
(VT_STATIC | VT_INLINE | VT_FUNC)) { |
/* if referencing an inline function, then we generate a |
symbol to it if not already done. It will have the |
effect to generate code for it at the end of the |
compilation unit. Inline function as always |
generated in the text section. */ |
if (!s->c) |
put_extern_sym(s, text_section, 0, 0); |
r = VT_SYM | VT_CONST; |
} else { |
r = s->r; |
} |
vset(&s->type, r, s->c); |
/* if forward reference, we must point to s */ |
if (vtop->r & VT_SYM) { |
vtop->sym = s; |
vtop->c.ul = 0; |
} |
break; |
} |
/* post operations */ |
while (1) { |
if (tok == TOK_INC || tok == TOK_DEC) { |
inc(1, tok); |
next(); |
} else if (tok == '.' || tok == TOK_ARROW) { |
/* field */ |
if (tok == TOK_ARROW) |
indir(); |
test_lvalue(); |
gaddrof(); |
next(); |
/* expect pointer on structure */ |
if ((vtop->type.t & VT_BTYPE) != VT_STRUCT) |
expect("struct or union"); |
s = vtop->type.ref; |
/* find field */ |
tok |= SYM_FIELD; |
while ((s = s->next) != NULL) { |
if (s->v == tok) |
break; |
} |
if (!s) |
error("field not found"); |
/* add field offset to pointer */ |
vtop->type = char_pointer_type; /* change type to 'char *' */ |
vpushi(s->c); |
gen_op('+'); |
/* change type to field type, and set to lvalue */ |
vtop->type = s->type; |
/* an array is never an lvalue */ |
if (!(vtop->type.t & VT_ARRAY)) { |
vtop->r |= lvalue_type(vtop->type.t); |
/* if bound checking, the referenced pointer must be checked */ |
if (do_bounds_check) |
vtop->r |= VT_MUSTBOUND; |
} |
next(); |
} else if (tok == '[') { |
next(); |
gexpr(); |
gen_op('+'); |
indir(); |
skip(']'); |
} else if (tok == '(') { |
SValue ret; |
Sym *sa; |
int nb_args; |
/* function call */ |
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { |
/* pointer test (no array accepted) */ |
if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) { |
vtop->type = *pointed_type(&vtop->type); |
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) |
goto error_func; |
} else { |
error_func: |
expect("function pointer"); |
} |
} else { |
vtop->r &= ~VT_LVAL; /* no lvalue */ |
} |
/* get return type */ |
s = vtop->type.ref; |
next(); |
sa = s->next; /* first parameter */ |
nb_args = 0; |
/* compute first implicit argument if a structure is returned */ |
if ((s->type.t & VT_BTYPE) == VT_STRUCT) { |
/* get some space for the returned structure */ |
size = type_size(&s->type, &align); |
loc = (loc - size) & -align; |
ret.type = s->type; |
ret.r = VT_LOCAL | VT_LVAL; |
/* pass it as 'int' to avoid structure arg passing |
problems */ |
vseti(VT_LOCAL, loc); |
ret.c = vtop->c; |
nb_args++; |
} else { |
ret.type = s->type; |
ret.r2 = VT_CONST; |
/* return in register */ |
if (is_float(ret.type.t)) { |
ret.r = REG_FRET; |
} else { |
if ((ret.type.t & VT_BTYPE) == VT_LLONG) |
ret.r2 = REG_LRET; |
ret.r = REG_IRET; |
} |
ret.c.i = 0; |
} |
if (tok != ')') { |
for(;;) { |
expr_eq(); |
gfunc_param_typed(s, sa); |
nb_args++; |
if (sa) |
sa = sa->next; |
if (tok == ')') |
break; |
skip(','); |
} |
} |
if (sa) |
error("too few arguments to function"); |
skip(')'); |
if (!nocode_wanted) { |
gfunc_call(nb_args); |
} else { |
vtop -= (nb_args + 1); |
} |
/* return value */ |
vsetc(&ret.type, ret.r, &ret.c); |
vtop->r2 = ret.r2; |
} else { |
break; |
} |
} |
} |
static void uneq(void) |
{ |
int t; |
unary(); |
if (tok == '=' || |
(tok >= TOK_A_MOD && tok <= TOK_A_DIV) || |
tok == TOK_A_XOR || tok == TOK_A_OR || |
tok == TOK_A_SHL || tok == TOK_A_SAR) { |
test_lvalue(); |
t = tok; |
next(); |
if (t == '=') { |
expr_eq(); |
} else { |
vdup(); |
expr_eq(); |
gen_op(t & 0x7f); |
} |
vstore(); |
} |
} |
static void expr_prod(void) |
{ |
int t; |
uneq(); |
while (tok == '*' || tok == '/' || tok == '%') { |
t = tok; |
next(); |
uneq(); |
gen_op(t); |
} |
} |
static void expr_sum(void) |
{ |
int t; |
expr_prod(); |
while (tok == '+' || tok == '-') { |
t = tok; |
next(); |
expr_prod(); |
gen_op(t); |
} |
} |
static void expr_shift(void) |
{ |
int t; |
expr_sum(); |
while (tok == TOK_SHL || tok == TOK_SAR) { |
t = tok; |
next(); |
expr_sum(); |
gen_op(t); |
} |
} |
static void expr_cmp(void) |
{ |
int t; |
expr_shift(); |
while ((tok >= TOK_ULE && tok <= TOK_GT) || |
tok == TOK_ULT || tok == TOK_UGE) { |
t = tok; |
next(); |
expr_shift(); |
gen_op(t); |
} |
} |
static void expr_cmpeq(void) |
{ |
int t; |
expr_cmp(); |
while (tok == TOK_EQ || tok == TOK_NE) { |
t = tok; |
next(); |
expr_cmp(); |
gen_op(t); |
} |
} |
static void expr_and(void) |
{ |
expr_cmpeq(); |
while (tok == '&') { |
next(); |
expr_cmpeq(); |
gen_op('&'); |
} |
} |
static void expr_xor(void) |
{ |
expr_and(); |
while (tok == '^') { |
next(); |
expr_and(); |
gen_op('^'); |
} |
} |
static void expr_or(void) |
{ |
expr_xor(); |
while (tok == '|') { |
next(); |
expr_xor(); |
gen_op('|'); |
} |
} |
/* XXX: fix this mess */ |
static void expr_land_const(void) |
{ |
expr_or(); |
while (tok == TOK_LAND) { |
next(); |
expr_or(); |
gen_op(TOK_LAND); |
} |
} |
/* XXX: fix this mess */ |
static void expr_lor_const(void) |
{ |
expr_land_const(); |
while (tok == TOK_LOR) { |
next(); |
expr_land_const(); |
gen_op(TOK_LOR); |
} |
} |
/* only used if non constant */ |
static void expr_land(void) |
{ |
int t; |
expr_or(); |
if (tok == TOK_LAND) { |
t = 0; |
for(;;) { |
t = gtst(1, t); |
if (tok != TOK_LAND) { |
vseti(VT_JMPI, t); |
break; |
} |
next(); |
expr_or(); |
} |
} |
} |
static void expr_lor(void) |
{ |
int t; |
expr_land(); |
if (tok == TOK_LOR) { |
t = 0; |
for(;;) { |
t = gtst(0, t); |
if (tok != TOK_LOR) { |
vseti(VT_JMP, t); |
break; |
} |
next(); |
expr_land(); |
} |
} |
} |
/* XXX: better constant handling */ |
static void expr_eq(void) |
{ |
int tt, u, r1, r2, rc, t1, t2, bt1, bt2; |
SValue sv; |
CType type, type1, type2; |
if (const_wanted) { |
int c1, c; |
expr_lor_const(); |
if (tok == '?') { |
c = vtop->c.i; |
vpop(); |
next(); |
if (tok == ':' && gnu_ext) { |
c1 = c; |
} else { |
gexpr(); |
c1 = vtop->c.i; |
vpop(); |
} |
skip(':'); |
expr_eq(); |
if (c) |
vtop->c.i = c1; |
} |
} else { |
expr_lor(); |
if (tok == '?') { |
next(); |
if (vtop != vstack) { |
/* needed to avoid having different registers saved in |
each branch */ |
if (is_float(vtop->type.t)) |
rc = RC_FLOAT; |
else |
rc = RC_INT; |
gv(rc); |
save_regs(1); |
} |
if (tok == ':' && gnu_ext) { |
gv_dup(); |
tt = gtst(1, 0); |
} else { |
tt = gtst(1, 0); |
gexpr(); |
} |
type1 = vtop->type; |
sv = *vtop; /* save value to handle it later */ |
vtop--; /* no vpop so that FP stack is not flushed */ |
skip(':'); |
u = gjmp(0); |
gsym(tt); |
expr_eq(); |
type2 = vtop->type; |
t1 = type1.t; |
bt1 = t1 & VT_BTYPE; |
t2 = type2.t; |
bt2 = t2 & VT_BTYPE; |
/* cast operands to correct type according to ISOC rules */ |
if (is_float(bt1) || is_float(bt2)) { |
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { |
type.t = VT_LDOUBLE; |
} else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { |
type.t = VT_DOUBLE; |
} else { |
type.t = VT_FLOAT; |
} |
} else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { |
/* cast to biggest op */ |
type.t = VT_LLONG; |
/* convert to unsigned if it does not fit in a long long */ |
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || |
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) |
type.t |= VT_UNSIGNED; |
} else if (bt1 == VT_PTR || bt2 == VT_PTR) { |
/* XXX: test pointer compatibility */ |
type = type1; |
} else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { |
/* XXX: test structure compatibility */ |
type = type1; |
} else if (bt1 == VT_VOID || bt2 == VT_VOID) { |
/* NOTE: as an extension, we accept void on only one side */ |
type.t = VT_VOID; |
} else { |
/* integer operations */ |
type.t = VT_INT; |
/* convert to unsigned if it does not fit in an integer */ |
if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || |
(t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) |
type.t |= VT_UNSIGNED; |
} |
/* now we convert second operand */ |
gen_cast(&type); |
rc = RC_INT; |
if (is_float(type.t)) { |
rc = RC_FLOAT; |
} else if ((type.t & VT_BTYPE) == VT_LLONG) { |
/* for long longs, we use fixed registers to avoid having |
to handle a complicated move */ |
rc = RC_IRET; |
} |
r2 = gv(rc); |
/* this is horrible, but we must also convert first |
operand */ |
tt = gjmp(0); |
gsym(u); |
/* put again first value and cast it */ |
*vtop = sv; |
gen_cast(&type); |
r1 = gv(rc); |
move_reg(r2, r1); |
vtop->r = r2; |
gsym(tt); |
} |
} |
} |
static void gexpr(void) |
{ |
while (1) { |
expr_eq(); |
if (tok != ',') |
break; |
vpop(); |
next(); |
} |
} |
/* parse an expression and return its type without any side effect. */ |
static void expr_type(CType *type) |
{ |
int saved_nocode_wanted; |
saved_nocode_wanted = nocode_wanted; |
nocode_wanted = 1; |
gexpr(); |
*type = vtop->type; |
vpop(); |
nocode_wanted = saved_nocode_wanted; |
} |
/* parse a unary expression and return its type without any side |
effect. */ |
static void unary_type(CType *type) |
{ |
int a; |
a = nocode_wanted; |
nocode_wanted = 1; |
unary(); |
*type = vtop->type; |
vpop(); |
nocode_wanted = a; |
} |
/* parse a constant expression and return value in vtop. */ |
static void expr_const1(void) |
{ |
int a; |
a = const_wanted; |
const_wanted = 1; |
expr_eq(); |
const_wanted = a; |
} |
/* parse an integer constant and return its value. */ |
static int expr_const(void) |
{ |
int c; |
expr_const1(); |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) |
expect("constant expression"); |
c = vtop->c.i; |
vpop(); |
return c; |
} |
/* return the label token if current token is a label, otherwise |
return zero */ |
static int is_label(void) |
{ |
int last_tok; |
/* fast test first */ |
if (tok < TOK_UIDENT) |
return 0; |
/* no need to save tokc because tok is an identifier */ |
last_tok = tok; |
next(); |
if (tok == ':') { |
next(); |
return last_tok; |
} else { |
unget_tok(last_tok); |
return 0; |
} |
} |
static void block(int *bsym, int *csym, int *case_sym, int *def_sym, |
int case_reg, int is_expr) |
{ |
int a, b, c, d; |
Sym *s; |
/* generate line number info */ |
if (do_debug && |
(last_line_num != file->line_num || last_ind != ind)) { |
put_stabn(N_SLINE, 0, file->line_num, ind - func_ind); |
last_ind = ind; |
last_line_num = file->line_num; |
} |
if (is_expr) { |
/* default return value is (void) */ |
vpushi(0); |
vtop->type.t = VT_VOID; |
} |
if (tok == TOK_IF) { |
/* if test */ |
next(); |
skip('('); |
gexpr(); |
skip(')'); |
a = gtst(1, 0); |
block(bsym, csym, case_sym, def_sym, case_reg, 0); |
c = tok; |
if (c == TOK_ELSE) { |
next(); |
d = gjmp(0); |
gsym(a); |
block(bsym, csym, case_sym, def_sym, case_reg, 0); |
gsym(d); /* patch else jmp */ |
} else |
gsym(a); |
} else if (tok == TOK_WHILE) { |
next(); |
d = ind; |
skip('('); |
gexpr(); |
skip(')'); |
a = gtst(1, 0); |
b = 0; |
block(&a, &b, case_sym, def_sym, case_reg, 0); |
gjmp_addr(d); |
gsym(a); |
gsym_addr(b, d); |
} else if (tok == '{') { |
Sym *llabel; |
next(); |
/* record local declaration stack position */ |
s = local_stack; |
llabel = local_label_stack; |
/* handle local labels declarations */ |
if (tok == TOK_LABEL) { |
next(); |
for(;;) { |
if (tok < TOK_UIDENT) |
expect("label identifier"); |
label_push(&local_label_stack, tok, LABEL_DECLARED); |
next(); |
if (tok == ',') { |
next(); |
} else { |
skip(';'); |
break; |
} |
} |
} |
while (tok != '}') { |
decl(VT_LOCAL); |
if (tok != '}') { |
if (is_expr) |
vpop(); |
block(bsym, csym, case_sym, def_sym, case_reg, is_expr); |
} |
} |
/* pop locally defined labels */ |
label_pop(&local_label_stack, llabel); |
/* pop locally defined symbols */ |
sym_pop(&local_stack, s); |
next(); |
} else if (tok == TOK_RETURN) { |
next(); |
if (tok != ';') { |
gexpr(); |
gen_assign_cast(&func_vt); |
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
CType type; |
/* if returning structure, must copy it to implicit |
first pointer arg location */ |
type = func_vt; |
mk_pointer(&type); |
vset(&type, VT_LOCAL | VT_LVAL, func_vc); |
indir(); |
vswap(); |
/* copy structure value to pointer */ |
vstore(); |
} else if (is_float(func_vt.t)) { |
gv(RC_FRET); |
} else { |
gv(RC_IRET); |
} |
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ |
} |
skip(';'); |
rsym = gjmp(rsym); /* jmp */ |
} else if (tok == TOK_BREAK) { |
/* compute jump */ |
if (!bsym) |
error("cannot break"); |
*bsym = gjmp(*bsym); |
next(); |
skip(';'); |
} else if (tok == TOK_CONTINUE) { |
/* compute jump */ |
if (!csym) |
error("cannot continue"); |
*csym = gjmp(*csym); |
next(); |
skip(';'); |
} else if (tok == TOK_FOR) { |
int e; |
next(); |
skip('('); |
if (tok != ';') { |
gexpr(); |
vpop(); |
} |
skip(';'); |
d = ind; |
c = ind; |
a = 0; |
b = 0; |
if (tok != ';') { |
gexpr(); |
a = gtst(1, 0); |
} |
skip(';'); |
if (tok != ')') { |
e = gjmp(0); |
c = ind; |
gexpr(); |
vpop(); |
gjmp_addr(d); |
gsym(e); |
} |
skip(')'); |
block(&a, &b, case_sym, def_sym, case_reg, 0); |
gjmp_addr(c); |
gsym(a); |
gsym_addr(b, c); |
} else |
if (tok == TOK_DO) { |
next(); |
a = 0; |
b = 0; |
d = ind; |
block(&a, &b, case_sym, def_sym, case_reg, 0); |
skip(TOK_WHILE); |
skip('('); |
gsym(b); |
gexpr(); |
c = gtst(0, 0); |
gsym_addr(c, d); |
skip(')'); |
gsym(a); |
skip(';'); |
} else |
if (tok == TOK_SWITCH) { |
next(); |
skip('('); |
gexpr(); |
/* XXX: other types than integer */ |
case_reg = gv(RC_INT); |
vpop(); |
skip(')'); |
a = 0; |
b = gjmp(0); /* jump to first case */ |
c = 0; |
block(&a, csym, &b, &c, case_reg, 0); |
/* if no default, jmp after switch */ |
if (c == 0) |
c = ind; |
/* default label */ |
gsym_addr(b, c); |
/* break label */ |
gsym(a); |
} else |
if (tok == TOK_CASE) { |
int v1, v2; |
if (!case_sym) |
expect("switch"); |
next(); |
v1 = expr_const(); |
v2 = v1; |
if (gnu_ext && tok == TOK_DOTS) { |
next(); |
v2 = expr_const(); |
if (v2 < v1) |
warning("empty case range"); |
} |
/* since a case is like a label, we must skip it with a jmp */ |
b = gjmp(0); |
gsym(*case_sym); |
vseti(case_reg, 0); |
vpushi(v1); |
if (v1 == v2) { |
gen_op(TOK_EQ); |
*case_sym = gtst(1, 0); |
} else { |
gen_op(TOK_GE); |
*case_sym = gtst(1, 0); |
vseti(case_reg, 0); |
vpushi(v2); |
gen_op(TOK_LE); |
*case_sym = gtst(1, *case_sym); |
} |
gsym(b); |
skip(':'); |
is_expr = 0; |
goto block_after_label; |
} else |
if (tok == TOK_DEFAULT) { |
next(); |
skip(':'); |
if (!def_sym) |
expect("switch"); |
if (*def_sym) |
error("too many 'default'"); |
*def_sym = ind; |
is_expr = 0; |
goto block_after_label; |
} else |
if (tok == TOK_GOTO) { |
next(); |
if (tok == '*' && gnu_ext) { |
/* computed goto */ |
next(); |
gexpr(); |
if ((vtop->type.t & VT_BTYPE) != VT_PTR) |
expect("pointer"); |
ggoto(); |
} else if (tok >= TOK_UIDENT) { |
s = label_find(tok); |
/* put forward definition if needed */ |
if (!s) { |
s = label_push(&global_label_stack, tok, LABEL_FORWARD); |
} else { |
if (s->r == LABEL_DECLARED) |
s->r = LABEL_FORWARD; |
} |
/* label already defined */ |
if (s->r & LABEL_FORWARD) |
s->next = (void *)gjmp((long)s->next); |
else |
gjmp_addr((long)s->next); |
next(); |
} else { |
expect("label identifier"); |
} |
skip(';'); |
} else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) { |
asm_instr(); |
} else { |
b = is_label(); |
if (b) { |
/* label case */ |
s = label_find(b); |
if (s) { |
if (s->r == LABEL_DEFINED) |
error("duplicate label '%s'", get_tok_str(s->v, NULL)); |
gsym((long)s->next); |
s->r = LABEL_DEFINED; |
} else { |
s = label_push(&global_label_stack, b, LABEL_DEFINED); |
} |
s->next = (void *)ind; |
/* we accept this, but it is a mistake */ |
block_after_label: |
if (tok == '}') { |
warning("deprecated use of label at end of compound statement"); |
} else { |
if (is_expr) |
vpop(); |
block(bsym, csym, case_sym, def_sym, case_reg, is_expr); |
} |
} else { |
/* expression case */ |
if (tok != ';') { |
if (is_expr) { |
vpop(); |
gexpr(); |
} else { |
gexpr(); |
vpop(); |
} |
} |
skip(';'); |
} |
} |
} |
/* t is the array or struct type. c is the array or struct |
address. cur_index/cur_field is the pointer to the current |
value. 'size_only' is true if only size info is needed (only used |
in arrays) */ |
static void decl_designator(CType *type, Section *sec, unsigned long c, |
int *cur_index, Sym **cur_field, |
int size_only) |
{ |
Sym *s, *f; |
int notfirst, index, index_last, align, l, nb_elems, elem_size; |
CType type1; |
notfirst = 0; |
elem_size = 0; |
nb_elems = 1; |
if (gnu_ext && (l = is_label()) != 0) |
goto struct_field; |
while (tok == '[' || tok == '.') { |
if (tok == '[') { |
if (!(type->t & VT_ARRAY)) |
expect("array type"); |
s = type->ref; |
next(); |
index = expr_const(); |
if (index < 0 || (s->c >= 0 && index >= s->c)) |
expect("invalid index"); |
if (tok == TOK_DOTS && gnu_ext) { |
next(); |
index_last = expr_const(); |
if (index_last < 0 || |
(s->c >= 0 && index_last >= s->c) || |
index_last < index) |
expect("invalid index"); |
} else { |
index_last = index; |
} |
skip(']'); |
if (!notfirst) |
*cur_index = index_last; |
type = pointed_type(type); |
elem_size = type_size(type, &align); |
c += index * elem_size; |
/* NOTE: we only support ranges for last designator */ |
nb_elems = index_last - index + 1; |
if (nb_elems != 1) { |
notfirst = 1; |
break; |
} |
} else { |
next(); |
l = tok; |
next(); |
struct_field: |
if ((type->t & VT_BTYPE) != VT_STRUCT) |
expect("struct/union type"); |
s = type->ref; |
l |= SYM_FIELD; |
f = s->next; |
while (f) { |
if (f->v == l) |
break; |
f = f->next; |
} |
if (!f) |
expect("field"); |
if (!notfirst) |
*cur_field = f; |
/* XXX: fix this mess by using explicit storage field */ |
type1 = f->type; |
type1.t |= (type->t & ~VT_TYPE); |
type = &type1; |
c += f->c; |
} |
notfirst = 1; |
} |
if (notfirst) { |
if (tok == '=') { |
next(); |
} else { |
if (!gnu_ext) |
expect("="); |
} |
} else { |
if (type->t & VT_ARRAY) { |
index = *cur_index; |
type = pointed_type(type); |
c += index * type_size(type, &align); |
} else { |
f = *cur_field; |
if (!f) |
error("too many field init"); |
/* XXX: fix this mess by using explicit storage field */ |
type1 = f->type; |
type1.t |= (type->t & ~VT_TYPE); |
type = &type1; |
c += f->c; |
} |
} |
decl_initializer(type, sec, c, 0, size_only); |
/* XXX: make it more general */ |
if (!size_only && nb_elems > 1) { |
unsigned long c_end; |
uint8_t *src, *dst; |
int i; |
if (!sec) |
error("range init not supported yet for dynamic storage"); |
c_end = c + nb_elems * elem_size; |
if (c_end > sec->data_allocated) |
section_realloc(sec, c_end); |
src = sec->data + c; |
dst = src; |
for(i = 1; i < nb_elems; i++) { |
dst += elem_size; |
memcpy(dst, src, elem_size); |
} |
} |
} |
#define EXPR_VAL 0 |
#define EXPR_CONST 1 |
#define EXPR_ANY 2 |
/* store a value or an expression directly in global data or in local array */ |
static void init_putv(CType *type, Section *sec, unsigned long c, |
int v, int expr_type) |
{ |
int saved_global_expr, bt, bit_pos, bit_size; |
void *ptr; |
unsigned long long bit_mask; |
CType dtype; |
switch(expr_type) { |
case EXPR_VAL: |
vpushi(v); |
break; |
case EXPR_CONST: |
/* compound literals must be allocated globally in this case */ |
saved_global_expr = global_expr; |
global_expr = 1; |
expr_const1(); |
global_expr = saved_global_expr; |
/* NOTE: symbols are accepted */ |
if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST) |
error("initializer element is not constant"); |
break; |
case EXPR_ANY: |
expr_eq(); |
break; |
} |
dtype = *type; |
dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ |
if (sec) { |
/* XXX: not portable */ |
/* XXX: generate error if incorrect relocation */ |
gen_assign_cast(&dtype); |
bt = type->t & VT_BTYPE; |
ptr = sec->data + c; |
/* XXX: make code faster ? */ |
if (!(type->t & VT_BITFIELD)) { |
bit_pos = 0; |
bit_size = 32; |
bit_mask = -1LL; |
} else { |
bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; |
bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; |
bit_mask = (1LL << bit_size) - 1; |
} |
if ((vtop->r & VT_SYM) && |
(bt == VT_BYTE || |
bt == VT_SHORT || |
bt == VT_DOUBLE || |
bt == VT_LDOUBLE || |
bt == VT_LLONG || |
(bt == VT_INT && bit_size != 32))) |
error("initializer element is not computable at load time"); |
switch(bt) { |
case VT_BYTE: |
*(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos; |
break; |
case VT_SHORT: |
*(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos; |
break; |
case VT_DOUBLE: |
*(double *)ptr = vtop->c.d; |
break; |
case VT_LDOUBLE: |
*(long double *)ptr = vtop->c.ld; |
break; |
case VT_LLONG: |
*(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos; |
break; |
default: |
if (vtop->r & VT_SYM) { |
greloc(sec, vtop->sym, c, R_DATA_32); |
} |
*(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos; |
break; |
} |
vtop--; |
} else { |
vset(&dtype, VT_LOCAL, c); |
vswap(); |
vstore(); |
vpop(); |
} |
} |
/* put zeros for variable based init */ |
static void init_putz(CType *t, Section *sec, unsigned long c, int size) |
{ |
if (sec) { |
/* nothing to do because globals are already set to zero */ |
} else { |
vpush_global_sym(&func_old_type, TOK_memset); |
vseti(VT_LOCAL, c); |
vpushi(0); |
vpushi(size); |
gfunc_call(3); |
} |
} |
/* 't' contains the type and storage info. 'c' is the offset of the |
object in section 'sec'. If 'sec' is NULL, it means stack based |
allocation. 'first' is true if array '{' must be read (multi |
dimension implicit array init handling). 'size_only' is true if |
size only evaluation is wanted (only for arrays). */ |
static void decl_initializer(CType *type, Section *sec, unsigned long c, |
int first, int size_only) |
{ |
int index, array_length, n, no_oblock, nb, parlevel, i; |
int size1, align1, expr_type; |
Sym *s, *f; |
CType *t1; |
if (type->t & VT_ARRAY) { |
s = type->ref; |
n = s->c; |
array_length = 0; |
t1 = pointed_type(type); |
size1 = type_size(t1, &align1); |
no_oblock = 1; |
if ((first && tok != TOK_LSTR && tok != TOK_STR) || |
tok == '{') { |
skip('{'); |
no_oblock = 0; |
} |
/* only parse strings here if correct type (otherwise: handle |
them as ((w)char *) expressions */ |
if ((tok == TOK_LSTR && |
(t1->t & VT_BTYPE) == VT_INT) || |
(tok == TOK_STR && |
(t1->t & VT_BTYPE) == VT_BYTE)) { |
while (tok == TOK_STR || tok == TOK_LSTR) { |
int cstr_len, ch; |
CString *cstr; |
cstr = tokc.cstr; |
/* compute maximum number of chars wanted */ |
if (tok == TOK_STR) |
cstr_len = cstr->size; |
else |
cstr_len = cstr->size / sizeof(int); |
cstr_len--; |
nb = cstr_len; |
if (n >= 0 && nb > (n - array_length)) |
nb = n - array_length; |
if (!size_only) { |
if (cstr_len > nb) |
warning("initializer-string for array is too long"); |
/* in order to go faster for common case (char |
string in global variable, we handle it |
specifically */ |
if (sec && tok == TOK_STR && size1 == 1) { |
memcpy(sec->data + c + array_length, cstr->data, nb); |
} else { |
for(i=0;i<nb;i++) { |
if (tok == TOK_STR) |
ch = ((unsigned char *)cstr->data)[i]; |
else |
ch = ((int *)cstr->data)[i]; |
init_putv(t1, sec, c + (array_length + i) * size1, |
ch, EXPR_VAL); |
} |
} |
} |
array_length += nb; |
next(); |
} |
/* only add trailing zero if enough storage (no |
warning in this case since it is standard) */ |
if (n < 0 || array_length < n) { |
if (!size_only) { |
init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL); |
} |
array_length++; |
} |
} else { |
index = 0; |
while (tok != '}') { |
decl_designator(type, sec, c, &index, NULL, size_only); |
if (n >= 0 && index >= n) |
error("index too large"); |
/* must put zero in holes (note that doing it that way |
ensures that it even works with designators) */ |
if (!size_only && array_length < index) { |
init_putz(t1, sec, c + array_length * size1, |
(index - array_length) * size1); |
} |
index++; |
if (index > array_length) |
array_length = index; |
/* special test for multi dimensional arrays (may not |
be strictly correct if designators are used at the |
same time) */ |
if (index >= n && no_oblock) |
break; |
if (tok == '}') |
break; |
skip(','); |
} |
} |
if (!no_oblock) |
skip('}'); |
/* put zeros at the end */ |
if (!size_only && n >= 0 && array_length < n) { |
init_putz(t1, sec, c + array_length * size1, |
(n - array_length) * size1); |
} |
/* patch type size if needed */ |
if (n < 0) |
s->c = array_length; |
} else if ((type->t & VT_BTYPE) == VT_STRUCT && |
(sec || !first || tok == '{')) { |
int par_count; |
/* NOTE: the previous test is a specific case for automatic |
struct/union init */ |
/* XXX: union needs only one init */ |
/* XXX: this test is incorrect for local initializers |
beginning with ( without {. It would be much more difficult |
to do it correctly (ideally, the expression parser should |
be used in all cases) */ |
par_count = 0; |
if (tok == '(') { |
AttributeDef ad1; |
CType type1; |
next(); |
while (tok == '(') { |
par_count++; |
next(); |
} |
if (!parse_btype(&type1, &ad1)) |
expect("cast"); |
type_decl(&type1, &ad1, &n, TYPE_ABSTRACT); |
#if 0 |
if (!is_assignable_types(type, &type1)) |
error("invalid type for cast"); |
#endif |
skip(')'); |
} |
no_oblock = 1; |
if (first || tok == '{') { |
skip('{'); |
no_oblock = 0; |
} |
s = type->ref; |
f = s->next; |
array_length = 0; |
index = 0; |
n = s->c; |
while (tok != '}') { |
decl_designator(type, sec, c, NULL, &f, size_only); |
index = f->c; |
if (!size_only && array_length < index) { |
init_putz(type, sec, c + array_length, |
index - array_length); |
} |
index = index + type_size(&f->type, &align1); |
if (index > array_length) |
array_length = index; |
f = f->next; |
if (no_oblock && f == NULL) |
break; |
if (tok == '}') |
break; |
skip(','); |
} |
/* put zeros at the end */ |
if (!size_only && array_length < n) { |
init_putz(type, sec, c + array_length, |
n - array_length); |
} |
if (!no_oblock) |
skip('}'); |
while (par_count) { |
skip(')'); |
par_count--; |
} |
} else if (tok == '{') { |
next(); |
decl_initializer(type, sec, c, first, size_only); |
skip('}'); |
} else if (size_only) { |
/* just skip expression */ |
parlevel = 0; |
while ((parlevel > 0 || (tok != '}' && tok != ',')) && |
tok != -1) { |
if (tok == '(') |
parlevel++; |
else if (tok == ')') |
parlevel--; |
next(); |
} |
} else { |
/* currently, we always use constant expression for globals |
(may change for scripting case) */ |
expr_type = EXPR_CONST; |
if (!sec) |
expr_type = EXPR_ANY; |
init_putv(type, sec, c, 0, expr_type); |
} |
} |
/* parse an initializer for type 't' if 'has_init' is non zero, and |
allocate space in local or global data space ('r' is either |
VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated |
variable 'v' of scope 'scope' is declared before initializers are |
parsed. If 'v' is zero, then a reference to the new object is put |
in the value stack. If 'has_init' is 2, a special parsing is done |
to handle string constants. */ |
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, |
int has_init, int v, int scope) |
{ |
int size, align, addr, data_offset; |
int level; |
ParseState saved_parse_state; |
TokenString init_str; |
Section *sec; |
size = type_size(type, &align); |
/* If unknown size, we must evaluate it before |
evaluating initializers because |
initializers can generate global data too |
(e.g. string pointers or ISOC99 compound |
literals). It also simplifies local |
initializers handling */ |
tok_str_new(&init_str); |
if (size < 0) { |
if (!has_init) |
error("unknown type size"); |
/* get all init string */ |
if (has_init == 2) { |
/* only get strings */ |
while (tok == TOK_STR || tok == TOK_LSTR) { |
tok_str_add_tok(&init_str); |
next(); |
} |
} else { |
level = 0; |
while (level > 0 || (tok != ',' && tok != ';')) { |
if (tok < 0) |
error("unexpected end of file in initializer"); |
tok_str_add_tok(&init_str); |
if (tok == '{') |
level++; |
else if (tok == '}') { |
if (level == 0) |
break; |
level--; |
} |
next(); |
} |
} |
tok_str_add(&init_str, -1); |
tok_str_add(&init_str, 0); |
/* compute size */ |
save_parse_state(&saved_parse_state); |
macro_ptr = init_str.str; |
next(); |
decl_initializer(type, NULL, 0, 1, 1); |
/* prepare second initializer parsing */ |
macro_ptr = init_str.str; |
next(); |
/* if still unknown size, error */ |
size = type_size(type, &align); |
if (size < 0) |
error("unknown type size"); |
} |
/* take into account specified alignment if bigger */ |
if (ad->aligned) { |
if (ad->aligned > align) |
align = ad->aligned; |
} else if (ad->packed) { |
align = 1; |
} |
if ((r & VT_VALMASK) == VT_LOCAL) { |
sec = NULL; |
if (do_bounds_check && (type->t & VT_ARRAY)) |
loc--; |
loc = (loc - size) & -align; |
addr = loc; |
/* handles bounds */ |
/* XXX: currently, since we do only one pass, we cannot track |
'&' operators, so we add only arrays */ |
if (do_bounds_check && (type->t & VT_ARRAY)) { |
unsigned long *bounds_ptr; |
/* add padding between regions */ |
loc--; |
/* then add local bound info */ |
bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(unsigned long)); |
bounds_ptr[0] = addr; |
bounds_ptr[1] = size; |
} |
if (v) { |
/* local variable */ |
sym_push(v, type, r, addr); |
} else { |
/* push local reference */ |
vset(type, r, addr); |
} |
} else { |
Sym *sym; |
sym = NULL; |
if (v && scope == VT_CONST) { |
/* see if the symbol was already defined */ |
sym = sym_find(v); |
if (sym) { |
if (!is_compatible_types(&sym->type, type)) |
error("incompatible types for redefinition of '%s'", |
get_tok_str(v, NULL)); |
if (sym->type.t & VT_EXTERN) { |
/* if the variable is extern, it was not allocated */ |
sym->type.t &= ~VT_EXTERN; |
/* set array size if it was ommited in extern |
declaration */ |
if ((sym->type.t & VT_ARRAY) && |
sym->type.ref->c < 0 && |
type->ref->c >= 0) |
sym->type.ref->c = type->ref->c; |
} else { |
/* we accept several definitions of the same |
global variable. this is tricky, because we |
must play with the SHN_COMMON type of the symbol */ |
/* XXX: should check if the variable was already |
initialized. It is incorrect to initialized it |
twice */ |
/* no init data, we won't add more to the symbol */ |
if (!has_init) |
goto no_alloc; |
} |
} |
} |
/* allocate symbol in corresponding section */ |
sec = ad->section; |
if (!sec) { |
if (has_init) |
sec = data_section; |
else if (tcc_state->nocommon) |
sec = bss_section; |
} |
if (sec) { |
data_offset = sec->data_offset; |
data_offset = (data_offset + align - 1) & -align; |
addr = data_offset; |
/* very important to increment global pointer at this time |
because initializers themselves can create new initializers */ |
data_offset += size; |
/* add padding if bound check */ |
if (do_bounds_check) |
data_offset++; |
sec->data_offset = data_offset; |
/* allocate section space to put the data */ |
if (sec->sh_type != SHT_NOBITS && |
data_offset > sec->data_allocated) |
section_realloc(sec, data_offset); |
/* align section if needed */ |
if (align > sec->sh_addralign) |
sec->sh_addralign = align; |
} else { |
addr = 0; /* avoid warning */ |
} |
if (v) { |
if (scope == VT_CONST) { |
if (!sym) |
goto do_def; |
} else { |
do_def: |
sym = sym_push(v, type, r | VT_SYM, 0); |
} |
/* update symbol definition */ |
if (sec) { |
put_extern_sym(sym, sec, addr, size); |
} else { |
Elf32_Sym *esym; |
/* put a common area */ |
put_extern_sym(sym, NULL, align, size); |
/* XXX: find a nicer way */ |
esym = &((Elf32_Sym *)symtab_section->data)[sym->c]; |
esym->st_shndx = SHN_COMMON; |
} |
} else { |
CValue cval; |
/* push global reference */ |
sym = get_sym_ref(type, sec, addr, size); |
cval.ul = 0; |
vsetc(type, VT_CONST | VT_SYM, &cval); |
vtop->sym = sym; |
} |
/* handles bounds now because the symbol must be defined |
before for the relocation */ |
if (do_bounds_check) { |
unsigned long *bounds_ptr; |
greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_32); |
/* then add global bound info */ |
bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(long)); |
bounds_ptr[0] = 0; /* relocated */ |
bounds_ptr[1] = size; |
} |
} |
if (has_init) { |
decl_initializer(type, sec, addr, 1, 0); |
/* restore parse state if needed */ |
if (init_str.str) { |
tok_str_free(init_str.str); |
restore_parse_state(&saved_parse_state); |
} |
} |
no_alloc: ; |
} |
void put_func_debug(Sym *sym) |
{ |
char buf[512]; |
/* stabs info */ |
/* XXX: we put here a dummy type */ |
snprintf(buf, sizeof(buf), "%s:%c1", |
funcname, sym->type.t & VT_STATIC ? 'f' : 'F'); |
put_stabs_r(buf, N_FUN, 0, file->line_num, 0, |
cur_text_section, sym->c); |
last_ind = 0; |
last_line_num = 0; |
} |
/* parse an old style function declaration list */ |
/* XXX: check multiple parameter */ |
static void func_decl_list(Sym *func_sym) |
{ |
AttributeDef ad; |
int v; |
Sym *s; |
CType btype, type; |
/* parse each declaration */ |
while (tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF) { |
if (!parse_btype(&btype, &ad)) |
expect("declaration list"); |
if (((btype.t & VT_BTYPE) == VT_ENUM || |
(btype.t & VT_BTYPE) == VT_STRUCT) && |
tok == ';') { |
/* we accept no variable after */ |
} else { |
for(;;) { |
type = btype; |
type_decl(&type, &ad, &v, TYPE_DIRECT); |
/* find parameter in function parameter list */ |
s = func_sym->next; |
while (s != NULL) { |
if ((s->v & ~SYM_FIELD) == v) |
goto found; |
s = s->next; |
} |
error("declaration for parameter '%s' but no such parameter", |
get_tok_str(v, NULL)); |
found: |
/* check that no storage specifier except 'register' was given */ |
if (type.t & VT_STORAGE) |
error("storage class specified for '%s'", get_tok_str(v, NULL)); |
convert_parameter_type(&type); |
/* we can add the type (NOTE: it could be local to the function) */ |
s->type = type; |
/* accept other parameters */ |
if (tok == ',') |
next(); |
else |
break; |
} |
} |
skip(';'); |
} |
} |
/* parse a function defined by symbol 'sym' and generate its code in |
'cur_text_section' */ |
static void gen_function(Sym *sym) |
{ |
ind = cur_text_section->data_offset; |
/* NOTE: we patch the symbol size later */ |
put_extern_sym(sym, cur_text_section, ind, 0); |
funcname = get_tok_str(sym->v, NULL); |
func_ind = ind; |
/* put debug symbol */ |
if (do_debug) |
put_func_debug(sym); |
/* push a dummy symbol to enable local sym storage */ |
sym_push2(&local_stack, SYM_FIELD, 0, 0); |
gfunc_prolog(&sym->type); |
rsym = 0; |
block(NULL, NULL, NULL, NULL, 0, 0); |
gsym(rsym); |
gfunc_epilog(); |
cur_text_section->data_offset = ind; |
label_pop(&global_label_stack, NULL); |
sym_pop(&local_stack, NULL); /* reset local stack */ |
/* end of function */ |
/* patch symbol size */ |
((Elf32_Sym *)symtab_section->data)[sym->c].st_size = |
ind - func_ind; |
if (do_debug) { |
put_stabn(N_FUN, 0, 0, ind - func_ind); |
} |
funcname = ""; /* for safety */ |
func_vt.t = VT_VOID; /* for safety */ |
ind = 0; /* for safety */ |
} |
static void gen_inline_functions(void) |
{ |
Sym *sym; |
CType *type; |
int *str, inline_generated; |
/* iterate while inline function are referenced */ |
for(;;) { |
inline_generated = 0; |
for(sym = global_stack; sym != NULL; sym = sym->prev) { |
type = &sym->type; |
if (((type->t & VT_BTYPE) == VT_FUNC) && |
(type->t & (VT_STATIC | VT_INLINE)) == |
(VT_STATIC | VT_INLINE) && |
sym->c != 0) { |
/* the function was used: generate its code and |
convert it to a normal function */ |
str = (int *)sym->r; |
sym->r = VT_SYM | VT_CONST; |
type->t &= ~VT_INLINE; |
macro_ptr = str; |
next(); |
cur_text_section = text_section; |
gen_function(sym); |
macro_ptr = NULL; /* fail safe */ |
tok_str_free(str); |
inline_generated = 1; |
} |
} |
if (!inline_generated) |
break; |
} |
/* free all remaining inline function tokens */ |
for(sym = global_stack; sym != NULL; sym = sym->prev) { |
type = &sym->type; |
if (((type->t & VT_BTYPE) == VT_FUNC) && |
(type->t & (VT_STATIC | VT_INLINE)) == |
(VT_STATIC | VT_INLINE)) { |
str = (int *)sym->r; |
tok_str_free(str); |
sym->r = 0; /* fail safe */ |
} |
} |
} |
/* 'l' is VT_LOCAL or VT_CONST to define default storage type */ |
static void decl(int l) |
{ |
int v, has_init, r; |
CType type, btype; |
Sym *sym; |
AttributeDef ad; |
while (1) { |
if (!parse_btype(&btype, &ad)) { |
/* skip redundant ';' */ |
/* XXX: find more elegant solution */ |
if (tok == ';') { |
next(); |
continue; |
} |
if (l == VT_CONST && |
(tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) { |
/* global asm block */ |
asm_global_instr(); |
continue; |
} |
/* special test for old K&R protos without explicit int |
type. Only accepted when defining global data */ |
if (l == VT_LOCAL || tok < TOK_DEFINE) |
break; |
btype.t = VT_INT; |
} |
if (((btype.t & VT_BTYPE) == VT_ENUM || |
(btype.t & VT_BTYPE) == VT_STRUCT) && |
tok == ';') { |
/* we accept no variable after */ |
next(); |
continue; |
} |
while (1) { /* iterate thru each declaration */ |
type = btype; |
type_decl(&type, &ad, &v, TYPE_DIRECT); |
#if 0 |
{ |
char buf[500]; |
type_to_str(buf, sizeof(buf), t, get_tok_str(v, NULL)); |
printf("type = '%s'\n", buf); |
} |
#endif |
if ((type.t & VT_BTYPE) == VT_FUNC) { |
/* if old style function prototype, we accept a |
declaration list */ |
sym = type.ref; |
if (sym->c == FUNC_OLD) |
func_decl_list(sym); |
} |
if (tok == '{') { |
if (l == VT_LOCAL) |
error("cannot use local functions"); |
if (!(type.t & VT_FUNC)) |
expect("function definition"); |
/* reject abstract declarators in function definition */ |
sym = type.ref; |
while ((sym = sym->next) != NULL) |
if (!(sym->v & ~SYM_FIELD)) |
expect("identifier"); |
/* XXX: cannot do better now: convert extern line to static inline */ |
if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) |
type.t = (type.t & ~VT_EXTERN) | VT_STATIC; |
sym = sym_find(v); |
if (sym) { |
if ((sym->type.t & VT_BTYPE) != VT_FUNC) |
goto func_error1; |
/* specific case: if not func_call defined, we put |
the one of the prototype */ |
/* XXX: should have default value */ |
if (sym->type.ref->r != FUNC_CDECL && |
type.ref->r == FUNC_CDECL) |
type.ref->r = sym->type.ref->r; |
if (!is_compatible_types(&sym->type, &type)) { |
func_error1: |
error("incompatible types for redefinition of '%s'", |
get_tok_str(v, NULL)); |
} |
/* if symbol is already defined, then put complete type */ |
sym->type = type; |
} else { |
/* put function symbol */ |
sym = global_identifier_push(v, type.t, 0); |
sym->type.ref = type.ref; |
} |
/* static inline functions are just recorded as a kind |
of macro. Their code will be emitted at the end of |
the compilation unit only if they are used */ |
if ((type.t & (VT_INLINE | VT_STATIC)) == |
(VT_INLINE | VT_STATIC)) { |
TokenString func_str; |
int block_level; |
tok_str_new(&func_str); |
block_level = 0; |
for(;;) { |
int t; |
if (tok == TOK_EOF) |
error("unexpected end of file"); |
tok_str_add_tok(&func_str); |
t = tok; |
next(); |
if (t == '{') { |
block_level++; |
} else if (t == '}') { |
block_level--; |
if (block_level == 0) |
break; |
} |
} |
tok_str_add(&func_str, -1); |
tok_str_add(&func_str, 0); |
sym->r = (int)func_str.str; |
} else { |
/* compute text section */ |
cur_text_section = ad.section; |
if (!cur_text_section) |
cur_text_section = text_section; |
sym->r = VT_SYM | VT_CONST; |
gen_function(sym); |
#ifdef TCC_TARGET_PE |
if (ad.dllexport) { |
((Elf32_Sym *)symtab_section->data)[sym->c].st_other |= 1; |
} |
#endif |
} |
break; |
} else { |
if (btype.t & VT_TYPEDEF) { |
/* save typedefed type */ |
/* XXX: test storage specifiers ? */ |
sym = sym_push(v, &type, 0, 0); |
sym->type.t |= VT_TYPEDEF; |
} else if ((type.t & VT_BTYPE) == VT_FUNC) { |
/* external function definition */ |
/* specific case for func_call attribute */ |
if (ad.func_call) |
type.ref->r = ad.func_call; |
external_sym(v, &type, 0); |
} else { |
/* not lvalue if array */ |
r = 0; |
if (!(type.t & VT_ARRAY)) |
r |= lvalue_type(type.t); |
has_init = (tok == '='); |
if ((btype.t & VT_EXTERN) || |
((type.t & VT_ARRAY) && (type.t & VT_STATIC) && |
!has_init && l == VT_CONST && type.ref->c < 0)) { |
/* external variable */ |
/* NOTE: as GCC, uninitialized global static |
arrays of null size are considered as |
extern */ |
external_sym(v, &type, r); |
} else { |
if (type.t & VT_STATIC) |
r |= VT_CONST; |
else |
r |= l; |
if (has_init) |
next(); |
decl_initializer_alloc(&type, &ad, r, |
has_init, v, l); |
} |
} |
if (tok != ',') { |
skip(';'); |
break; |
} |
next(); |
} |
} |
} |
} |
/* better than nothing, but needs extension to handle '-E' option |
correctly too */ |
static void preprocess_init(TCCState *s1) |
{ |
s1->include_stack_ptr = s1->include_stack; |
/* XXX: move that before to avoid having to initialize |
file->ifdef_stack_ptr ? */ |
s1->ifdef_stack_ptr = s1->ifdef_stack; |
file->ifdef_stack_ptr = s1->ifdef_stack_ptr; |
/* XXX: not ANSI compliant: bound checking says error */ |
vtop = vstack - 1; |
s1->pack_stack[0] = 0; |
s1->pack_stack_ptr = s1->pack_stack; |
} |
/* compile the C file opened in 'file'. Return non zero if errors. */ |
static int tcc_compile(TCCState *s1) |
{ |
Sym *define_start; |
char buf[512]; |
volatile int section_sym; |
#ifdef INC_DEBUG |
printf("%s: **** new file\n", file->filename); |
#endif |
preprocess_init(s1); |
funcname = ""; |
anon_sym = SYM_FIRST_ANOM; |
/* file info: full path + filename */ |
section_sym = 0; /* avoid warning */ |
if (do_debug) { |
section_sym = put_elf_sym(symtab_section, 0, 0, |
ELF32_ST_INFO(STB_LOCAL, STT_SECTION), 0, |
text_section->sh_num, NULL); |
getcwd(buf, sizeof(buf)); |
pstrcat(buf, sizeof(buf), "/"); |
put_stabs_r(buf, N_SO, 0, 0, |
text_section->data_offset, text_section, section_sym); |
put_stabs_r(file->filename, N_SO, 0, 0, |
text_section->data_offset, text_section, section_sym); |
} |
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL |
symbols can be safely used */ |
put_elf_sym(symtab_section, 0, 0, |
ELF32_ST_INFO(STB_LOCAL, STT_FILE), 0, |
SHN_ABS, file->filename); |
/* define some often used types */ |
int_type.t = VT_INT; |
char_pointer_type.t = VT_BYTE; |
mk_pointer(&char_pointer_type); |
func_old_type.t = VT_FUNC; |
func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD); |
#if 0 |
/* define 'void *alloca(unsigned int)' builtin function */ |
{ |
Sym *s1; |
p = anon_sym++; |
sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW); |
s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0); |
s1->next = NULL; |
sym->next = s1; |
sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0); |
} |
#endif |
define_start = define_stack; |
if (setjmp(s1->error_jmp_buf) == 0) { |
s1->nb_errors = 0; |
s1->error_set_jmp_enabled = 1; |
ch = file->buf_ptr[0]; |
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; |
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM; |
next(); |
decl(VT_CONST); |
if (tok != TOK_EOF) |
expect("declaration"); |
/* end of translation unit info */ |
if (do_debug) { |
put_stabs_r(NULL, N_SO, 0, 0, |
text_section->data_offset, text_section, section_sym); |
} |
} |
s1->error_set_jmp_enabled = 0; |
/* reset define stack, but leave -Dsymbols (may be incorrect if |
they are undefined) */ |
free_defines(define_start); |
gen_inline_functions(); |
sym_pop(&global_stack, NULL); |
return s1->nb_errors != 0 ? -1 : 0; |
} |
#ifdef LIBTCC |
int tcc_compile_string(TCCState *s, const char *str) |
{ |
BufferedFile bf1, *bf = &bf1; |
int ret, len; |
char *buf; |
/* init file structure */ |
bf->fd = -1; |
/* XXX: avoid copying */ |
len = strlen(str); |
buf = tcc_malloc(len + 1); |
if (!buf) |
return -1; |
memcpy(buf, str, len); |
buf[len] = CH_EOB; |
bf->buf_ptr = buf; |
bf->buf_end = buf + len; |
pstrcpy(bf->filename, sizeof(bf->filename), "<string>"); |
bf->line_num = 1; |
file = bf; |
ret = tcc_compile(s); |
tcc_free(buf); |
/* currently, no need to close */ |
return ret; |
} |
#endif |
/* define a preprocessor symbol. A value can also be provided with the '=' operator */ |
void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) |
{ |
BufferedFile bf1, *bf = &bf1; |
pstrcpy(bf->buffer, IO_BUF_SIZE, sym); |
pstrcat(bf->buffer, IO_BUF_SIZE, " "); |
/* default value */ |
if (!value) |
value = "1"; |
pstrcat(bf->buffer, IO_BUF_SIZE, value); |
/* init file structure */ |
bf->fd = -1; |
bf->buf_ptr = bf->buffer; |
bf->buf_end = bf->buffer + strlen(bf->buffer); |
*bf->buf_end = CH_EOB; |
bf->filename[0] = '\0'; |
bf->line_num = 1; |
file = bf; |
s1->include_stack_ptr = s1->include_stack; |
/* parse with define parser */ |
ch = file->buf_ptr[0]; |
next_nomacro(); |
parse_define(); |
file = NULL; |
} |
/* undefine a preprocessor symbol */ |
void tcc_undefine_symbol(TCCState *s1, const char *sym) |
{ |
TokenSym *ts; |
Sym *s; |
ts = tok_alloc(sym, strlen(sym)); |
s = define_find(ts->tok); |
/* undefine symbol by putting an invalid name */ |
if (s) |
define_undef(s); |
} |
#ifdef CONFIG_TCC_ASM |
#ifdef TCC_TARGET_I386 |
#include "i386-asm.c" |
#endif |
#include "tccasm.c" |
#else |
static void asm_instr(void) |
{ |
error("inline asm() not supported"); |
} |
static void asm_global_instr(void) |
{ |
error("inline asm() not supported"); |
} |
#endif |
#include "tccelf.c" |
#ifdef TCC_TARGET_COFF |
#include "tcccoff.c" |
#endif |
#if defined(TCC_TARGET_PE) || defined(TCC_TARGET_MEOS) |
#include "tccpe.c" |
#endif |
#ifdef TCC_TARGET_MEOS |
#include "tccmeos.c" |
#endif |
/* print the position in the source file of PC value 'pc' by reading |
the stabs debug information */ |
static void rt_printline(unsigned long wanted_pc) |
{ |
Stab_Sym *sym, *sym_end; |
char func_name[128], last_func_name[128]; |
unsigned long func_addr, last_pc, pc; |
const char *incl_files[INCLUDE_STACK_SIZE]; |
int incl_index, len, last_line_num, i; |
const char *str, *p; |
printf("0x%08lx:", wanted_pc); |
func_name[0] = '\0'; |
func_addr = 0; |
incl_index = 0; |
last_func_name[0] = '\0'; |
last_pc = 0xffffffff; |
last_line_num = 1; |
sym = (Stab_Sym *)stab_section->data + 1; |
sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset); |
while (sym < sym_end) { |
switch(sym->n_type) { |
/* function start or end */ |
case N_FUN: |
if (sym->n_strx == 0) { |
/* we test if between last line and end of function */ |
pc = sym->n_value + func_addr; |
if (wanted_pc >= last_pc && wanted_pc < pc) |
goto found; |
func_name[0] = '\0'; |
func_addr = 0; |
} else { |
str = stabstr_section->data + sym->n_strx; |
p = strchr(str, ':'); |
if (!p) { |
pstrcpy(func_name, sizeof(func_name), str); |
} else { |
len = p - str; |
if (len > sizeof(func_name) - 1) |
len = sizeof(func_name) - 1; |
memcpy(func_name, str, len); |
func_name[len] = '\0'; |
} |
func_addr = sym->n_value; |
} |
break; |
/* line number info */ |
case N_SLINE: |
pc = sym->n_value + func_addr; |
if (wanted_pc >= last_pc && wanted_pc < pc) |
goto found; |
last_pc = pc; |
last_line_num = sym->n_desc; |
/* XXX: slow! */ |
strcpy(last_func_name, func_name); |
break; |
/* include files */ |
case N_BINCL: |
str = stabstr_section->data + sym->n_strx; |
add_incl: |
if (incl_index < INCLUDE_STACK_SIZE) { |
incl_files[incl_index++] = str; |
} |
break; |
case N_EINCL: |
if (incl_index > 1) |
incl_index--; |
break; |
case N_SO: |
if (sym->n_strx == 0) { |
incl_index = 0; /* end of translation unit */ |
} else { |
str = stabstr_section->data + sym->n_strx; |
/* do not add path */ |
len = strlen(str); |
if (len > 0 && str[len - 1] != '/') |
goto add_incl; |
} |
break; |
} |
sym++; |
} |
/* second pass: we try symtab symbols (no line number info) */ |
incl_index = 0; |
{ |
Elf32_Sym *sym, *sym_end; |
int type; |
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset); |
for(sym = (Elf32_Sym *)symtab_section->data + 1; |
sym < sym_end; |
sym++) { |
type = ELF32_ST_TYPE(sym->st_info); |
if (type == STT_FUNC) { |
if (wanted_pc >= sym->st_value && |
wanted_pc < sym->st_value + sym->st_size) { |
pstrcpy(last_func_name, sizeof(last_func_name), |
strtab_section->data + sym->st_name); |
goto found; |
} |
} |
} |
} |
/* did not find any info: */ |
printf(" ???\n"); |
return; |
found: |
if (last_func_name[0] != '\0') { |
printf(" %s()", last_func_name); |
} |
if (incl_index > 0) { |
printf(" (%s:%d", |
incl_files[incl_index - 1], last_line_num); |
for(i = incl_index - 2; i >= 0; i--) |
printf(", included from %s", incl_files[i]); |
printf(")"); |
} |
printf("\n"); |
} |
#if !defined(WIN32) && !defined(CONFIG_TCCBOOT) |
#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 non zero if not found */ |
/* |
static int rt_get_caller_pc(unsigned long *paddr, |
ucontext_t *uc, int level) |
{ |
unsigned long fp; |
int i; |
if (level == 0) { |
#if defined(__FreeBSD__) |
*paddr = uc->uc_mcontext.mc_eip; |
#elif defined(__dietlibc__) |
*paddr = uc->uc_mcontext.eip; |
#else |
*paddr = uc->uc_mcontext.gregs[REG_EIP]; |
#endif |
return 0; |
} else { |
#if defined(__FreeBSD__) |
fp = uc->uc_mcontext.mc_ebp; |
#elif defined(__dietlibc__) |
fp = uc->uc_mcontext.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 = ((unsigned long *)fp)[0]; |
} |
*paddr = ((unsigned long *)fp)[1]; |
return 0; |
} |
} |
*/ |
#else |
#warning add arch specific rt_get_caller_pc() |
/* |
static int rt_get_caller_pc(unsigned long *paddr, |
ucontext_t *uc, int level) |
{ |
return -1; |
} |
*/ |
#endif |
/* emit a run time error at position 'pc' */ |
/* |
void rt_error(ucontext_t *uc, const char *fmt, ...) |
{ |
va_list ap; |
unsigned long pc; |
int i; |
va_start(ap, fmt); |
printf("Runtime error: "); |
//vfprintf(stderr, fmt, ap); |
printf("\n"); |
for(i=0;i<num_callers;i++) { |
if (rt_get_caller_pc(&pc, uc, i) < 0) |
break; |
if (i == 0) |
printf("at "); |
else |
printf("by "); |
rt_printline(pc); |
} |
exit(255); |
va_end(ap); |
} |
*/ |
/* signal handler for fatal errors */ |
/* |
static void sig_error(int signum, siginfo_t *siginf, void *puc) |
{ |
ucontext_t *uc = puc; |
switch(signum) { |
case SIGFPE: |
switch(siginf->si_code) { |
case FPE_INTDIV: |
case FPE_FLTDIV: |
rt_error(uc, "division by zero"); |
break; |
default: |
rt_error(uc, "floating point exception"); |
break; |
} |
break; |
case SIGBUS: |
case SIGSEGV: |
if (rt_bound_error_msg && *rt_bound_error_msg) |
rt_error(uc, *rt_bound_error_msg); |
else |
rt_error(uc, "dereferencing invalid pointer"); |
break; |
case SIGILL: |
rt_error(uc, "illegal instruction"); |
break; |
case SIGABRT: |
rt_error(uc, "abort() called"); |
break; |
default: |
rt_error(uc, "caught signal %d", signum); |
break; |
} |
exit(255); |
} |
*/ |
#endif |
/* do all relocations (needed before using tcc_get_symbol()) */ |
int tcc_relocate(TCCState *s1) |
{ |
Section *s; |
int i; |
s1->nb_errors = 0; |
#if defined(TCC_TARGET_PE) || defined(TCC_TARGET_MEOS) |
pe_add_runtime(s1); |
#else |
tcc_add_runtime(s1); |
#endif |
relocate_common_syms(); |
tcc_add_linker_symbols(s1); |
build_got_entries(s1); |
/* compute relocation address : section are relocated in place. We |
also alloc the bss space */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if (s->sh_flags & SHF_ALLOC) { |
if (s->sh_type == SHT_NOBITS) |
s->data = tcc_mallocz(s->data_offset); |
s->sh_addr = (unsigned long)s->data; |
} |
} |
relocate_syms(s1, 1); |
if (s1->nb_errors != 0) |
return -1; |
/* relocate each section */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if (s->reloc) |
relocate_section(s1, s); |
} |
return 0; |
} |
/* launch the compiled program with the given arguments */ |
int tcc_run(TCCState *s1, int argc, char **argv) |
{ |
int (*prog_main)(int, char **); |
if (tcc_relocate(s1) < 0) |
return -1; |
prog_main = tcc_get_symbol_err(s1, "main"); |
if (do_debug) { |
#if defined(WIN32) || defined(CONFIG_TCCBOOT) |
error("debug mode currently not available for Windows"); |
#else |
//struct sigaction sigact; |
/* install TCC signal handlers to print debug info on fatal |
runtime errors */ |
//sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; |
//sigact.sa_sigaction = sig_error; |
//sigemptyset(&sigact.sa_mask); |
//sigaction(SIGFPE, &sigact, NULL); |
//sigaction(SIGILL, &sigact, NULL); |
//sigaction(SIGSEGV, &sigact, NULL); |
//sigaction(SIGBUS, &sigact, NULL); |
//sigaction(SIGABRT, &sigact, NULL); |
#endif |
} |
#ifdef CONFIG_TCC_BCHECK |
if (do_bounds_check) { |
void (*bound_init)(void); |
/* set error function */ |
rt_bound_error_msg = (void *)tcc_get_symbol_err(s1, |
"__bound_error_msg"); |
/* XXX: use .init section so that it also work in binary ? */ |
bound_init = (void *)tcc_get_symbol_err(s1, "__bound_init"); |
bound_init(); |
} |
#endif |
return (*prog_main)(argc, argv); |
} |
TCCState *tcc_new(void) |
{ |
const char *p, *r; |
TCCState *s; |
TokenSym *ts; |
int i, c; |
s = tcc_mallocz(sizeof(TCCState)); |
if (!s) |
return NULL; |
tcc_state = s; |
s->output_type = TCC_OUTPUT_MEMORY; |
/* init isid table */ |
for(i=0;i<256;i++) |
isidnum_table[i] = isid(i) || isnum(i); |
/* add all tokens */ |
table_ident = NULL; |
memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *)); |
tok_ident = TOK_IDENT; |
p = tcc_keywords; |
while (*p) { |
r = p; |
for(;;) { |
c = *r++; |
if (c == '\0') |
break; |
} |
ts = tok_alloc(p, r - p - 1); |
p = r; |
} |
/* we add dummy defines for some special macros to speed up tests |
and to have working defined() */ |
define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL); |
define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL); |
define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL); |
define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL); |
/* standard defines */ |
tcc_define_symbol(s, "__STDC__", NULL); |
#if defined(TCC_TARGET_I386) |
tcc_define_symbol(s, "__i386__", NULL); |
#endif |
#if defined(TCC_TARGET_ARM) |
tcc_define_symbol(s, "__ARM_ARCH_4__", NULL); |
tcc_define_symbol(s, "__arm_elf__", NULL); |
tcc_define_symbol(s, "__arm_elf", NULL); |
tcc_define_symbol(s, "arm_elf", NULL); |
tcc_define_symbol(s, "__arm__", NULL); |
tcc_define_symbol(s, "__arm", NULL); |
tcc_define_symbol(s, "arm", NULL); |
tcc_define_symbol(s, "__APCS_32__", NULL); |
#endif |
#if defined(linux) |
tcc_define_symbol(s, "__linux__", NULL); |
tcc_define_symbol(s, "linux", NULL); |
#endif |
/* tiny C specific defines */ |
tcc_define_symbol(s, "__TINYC__", NULL); |
/* tiny C & gcc defines */ |
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int"); |
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int"); |
tcc_define_symbol(s, "__WCHAR_TYPE__", "int"); |
/* default library paths */ |
#ifdef TCC_TARGET_PE |
{ |
char buf[1024]; |
snprintf(buf, sizeof(buf), "%s/lib", tcc_lib_path); |
tcc_add_library_path(s, buf); |
} |
#else |
#ifdef TCC_TARGET_MEOS |
tcc_add_library_path(s, ".//lib"); |
#else |
tcc_add_library_path(s, "/usr/local/lib"); |
tcc_add_library_path(s, "/usr/lib"); |
tcc_add_library_path(s, "/lib"); |
#endif |
#endif |
/* no section zero */ |
dynarray_add((void ***)&s->sections, &s->nb_sections, NULL); |
/* create standard sections */ |
text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); |
data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); |
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); |
/* symbols are always generated for linking stage */ |
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0, |
".strtab", |
".hashtab", SHF_PRIVATE); |
strtab_section = symtab_section->link; |
/* private symbol table for dynamic symbols */ |
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE, |
".dynstrtab", |
".dynhashtab", SHF_PRIVATE); |
s->alacarte_link = 1; |
#ifdef CHAR_IS_UNSIGNED |
s->char_is_unsigned = 1; |
#endif |
#if defined(TCC_TARGET_PE) && 0 |
/* XXX: currently the PE linker is not ready to support that */ |
s->leading_underscore = 1; |
#endif |
return s; |
} |
void tcc_delete(TCCState *s1) |
{ |
int i, n; |
/* free -D defines */ |
free_defines(NULL); |
/* free tokens */ |
n = tok_ident - TOK_IDENT; |
for(i = 0; i < n; i++) |
tcc_free(table_ident[i]); |
tcc_free(table_ident); |
/* free all sections */ |
free_section(symtab_section->hash); |
free_section(s1->dynsymtab_section->hash); |
free_section(s1->dynsymtab_section->link); |
free_section(s1->dynsymtab_section); |
for(i = 1; i < s1->nb_sections; i++) |
free_section(s1->sections[i]); |
tcc_free(s1->sections); |
/* free loaded dlls array */ |
for(i = 0; i < s1->nb_loaded_dlls; i++) |
tcc_free(s1->loaded_dlls[i]); |
tcc_free(s1->loaded_dlls); |
/* library paths */ |
for(i = 0; i < s1->nb_library_paths; i++) |
tcc_free(s1->library_paths[i]); |
tcc_free(s1->library_paths); |
/* cached includes */ |
for(i = 0; i < s1->nb_cached_includes; i++) |
tcc_free(s1->cached_includes[i]); |
tcc_free(s1->cached_includes); |
for(i = 0; i < s1->nb_include_paths; i++) |
tcc_free(s1->include_paths[i]); |
tcc_free(s1->include_paths); |
for(i = 0; i < s1->nb_sysinclude_paths; i++) |
tcc_free(s1->sysinclude_paths[i]); |
tcc_free(s1->sysinclude_paths); |
tcc_free(s1); |
} |
int tcc_add_include_path(TCCState *s1, const char *pathname) |
{ |
char *pathname1; |
pathname1 = tcc_strdup(pathname); |
dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1); |
return 0; |
} |
int tcc_add_sysinclude_path(TCCState *s1, const char *pathname) |
{ |
char *pathname1; |
pathname1 = tcc_strdup(pathname); |
dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1); |
return 0; |
} |
static int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) |
{ |
const char *ext, *filename1; |
Elf32_Ehdr ehdr; |
int fd, ret; |
BufferedFile *saved_file; |
/* find source file type with extension */ |
filename1 = strrchr(filename, '/'); |
if (filename1) |
filename1++; |
else |
filename1 = filename; |
ext = strrchr(filename1, '.'); |
if (ext) |
ext++; |
/* open the file */ |
saved_file = file; |
file = tcc_open(s1, filename); |
if (!file) { |
if (flags & AFF_PRINT_ERROR) { |
error_noabort("file '%s' not found", filename); |
} |
ret = -1; |
goto fail1; |
} |
if (!ext || !strcmp(ext, "c")) { |
/* C file assumed */ |
ret = tcc_compile(s1); |
} else |
#ifdef CONFIG_TCC_ASM |
if (!strcmp(ext, "S")) { |
/* preprocessed assembler */ |
ret = tcc_assemble(s1, 1); |
} else if (!strcmp(ext, "s")) { |
/* non preprocessed assembler */ |
ret = tcc_assemble(s1, 0); |
} else |
#endif |
#ifdef TCC_TARGET_PE |
if (!strcmp(ext, "def")) { |
ret = pe_load_def_file(s1, fdopen(file->fd, "rb")); |
} else |
#endif |
{ |
fd = file->fd; |
/* assume executable format: auto guess file type */ |
ret = read(fd, &ehdr, sizeof(ehdr)); |
lseek(fd, 0, SEEK_SET); |
if (ret <= 0) { |
error_noabort("could not read header"); |
goto fail; |
} else if (ret != sizeof(ehdr)) { |
goto try_load_script; |
} |
if (ehdr.e_ident[0] == ELFMAG0 && |
ehdr.e_ident[1] == ELFMAG1 && |
ehdr.e_ident[2] == ELFMAG2 && |
ehdr.e_ident[3] == ELFMAG3) { |
file->line_num = 0; /* do not display line number if error */ |
if (ehdr.e_type == ET_REL) { |
ret = tcc_load_object_file(s1, fd, 0); |
} else if (ehdr.e_type == ET_DYN) { |
if (s1->output_type == TCC_OUTPUT_MEMORY) { |
#if defined(TCC_TARGET_PE) || defined(TCC_TARGET_MEOS) |
ret = -1; |
#else |
void *h; |
h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); |
if (h) |
ret = 0; |
else |
ret = -1; |
#endif |
} else { |
ret = tcc_load_dll(s1, fd, filename, |
(flags & AFF_REFERENCED_DLL) != 0); |
} |
} else { |
error_noabort("unrecognized ELF file"); |
goto fail; |
} |
} else if (memcmp((char *)&ehdr, ARMAG, 8) == 0) { |
file->line_num = 0; /* do not display line number if error */ |
ret = tcc_load_archive(s1, fd); |
} else |
#ifdef TCC_TARGET_COFF |
if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) { |
ret = tcc_load_coff(s1, fd); |
} else |
#endif |
{ |
/* as GNU ld, consider it is an ld script if not recognized */ |
try_load_script: |
ret = tcc_load_ldscript(s1); |
if (ret < 0) { |
error_noabort("unrecognized file type"); |
goto fail; |
} |
} |
} |
the_end: |
tcc_close(file); |
fail1: |
file = saved_file; |
return ret; |
fail: |
ret = -1; |
goto the_end; |
} |
int tcc_add_file(TCCState *s, const char *filename) |
{ |
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR); |
} |
int tcc_add_library_path(TCCState *s, const char *pathname) |
{ |
char *pathname1; |
pathname1 = tcc_strdup(pathname); |
dynarray_add((void ***)&s->library_paths, &s->nb_library_paths, pathname1); |
return 0; |
} |
/* find and load a dll. Return non zero if not found */ |
/* XXX: add '-rpath' option support ? */ |
static int tcc_add_dll(TCCState *s, const char *filename, int flags) |
{ |
char buf[1024]; |
int i; |
for(i = 0; i < s->nb_library_paths; i++) { |
snprintf(buf, sizeof(buf), "%s/%s", |
s->library_paths[i], filename); |
if (tcc_add_file_internal(s, buf, flags) == 0) |
return 0; |
} |
return -1; |
} |
/* the library name is the same as the argument of the '-l' option */ |
int tcc_add_library(TCCState *s, const char *libraryname) |
{ |
char buf[1024]; |
int i; |
/* first we look for the dynamic library if not static linking */ |
if (!s->static_link) { |
#ifdef TCC_TARGET_PE |
snprintf(buf, sizeof(buf), "%s.def", libraryname); |
#else |
snprintf(buf, sizeof(buf), "lib%s.so", libraryname); |
#endif |
if (tcc_add_dll(s, buf, 0) == 0) |
return 0; |
} |
/* then we look for the static library */ |
for(i = 0; i < s->nb_library_paths; i++) { |
snprintf(buf, sizeof(buf), "%s/lib%s.a", |
s->library_paths[i], libraryname); |
if (tcc_add_file_internal(s, buf, 0) == 0) |
return 0; |
} |
return -1; |
} |
int tcc_add_symbol(TCCState *s, const char *name, unsigned long val) |
{ |
add_elf_sym(symtab_section, val, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
SHN_ABS, name); |
return 0; |
} |
int tcc_set_output_type(TCCState *s, int output_type) |
{ |
s->output_type = output_type; |
if (!s->nostdinc) { |
char buf[1024]; |
/* default include paths */ |
/* XXX: reverse order needed if -isystem support */ |
#if !defined(TCC_TARGET_PE) && !defined(TCC_TARGET_MEOS) |
tcc_add_sysinclude_path(s, "/usr/local/include"); |
tcc_add_sysinclude_path(s, "/usr/include"); |
#endif |
#if defined(TCC_TARGET_MEOS) |
tcc_add_sysinclude_path(s, ".//include"); |
#endif |
snprintf(buf, sizeof(buf), "%s/include", tcc_lib_path); |
tcc_add_sysinclude_path(s, buf); |
#ifdef TCC_TARGET_PE |
snprintf(buf, sizeof(buf), "%s/include/winapi", tcc_lib_path); |
tcc_add_sysinclude_path(s, buf); |
#endif |
} |
/* if bound checking, then add corresponding sections */ |
#ifdef CONFIG_TCC_BCHECK |
if (do_bounds_check) { |
/* define symbol */ |
tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL); |
/* create bounds sections */ |
bounds_section = new_section(s, ".bounds", |
SHT_PROGBITS, SHF_ALLOC); |
lbounds_section = new_section(s, ".lbounds", |
SHT_PROGBITS, SHF_ALLOC); |
} |
#endif |
if (s->char_is_unsigned) { |
tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL); |
} |
/* add debug sections */ |
if (do_debug) { |
/* stab symbols */ |
stab_section = new_section(s, ".stab", SHT_PROGBITS, 0); |
stab_section->sh_entsize = sizeof(Stab_Sym); |
stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0); |
put_elf_str(stabstr_section, ""); |
stab_section->link = stabstr_section; |
/* put first entry */ |
put_stabs("", 0, 0, 0, 0); |
} |
/* add libc crt1/crti objects */ |
#if !defined(TCC_TARGET_PE) && !defined(TCC_TARGET_MEOS) |
if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) && |
!s->nostdlib) { |
if (output_type != TCC_OUTPUT_DLL) |
tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crt1.o"); |
tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crti.o"); |
} |
#endif |
#if defined(TCC_TARGET_MEOS) |
if (s->output_type != TCC_OUTPUT_OBJ) |
tcc_add_file(s,".//start.o"); |
#endif |
return 0; |
} |
#define WD_ALL 0x0001 /* warning is activated when using -Wall */ |
#define FD_INVERT 0x0002 /* invert value before storing */ |
typedef struct FlagDef { |
uint16_t offset; |
uint16_t flags; |
const char *name; |
} FlagDef; |
static const FlagDef warning_defs[] = { |
{ offsetof(TCCState, warn_unsupported), 0, "unsupported" }, |
{ offsetof(TCCState, warn_write_strings), 0, "write-strings" }, |
{ offsetof(TCCState, warn_error), 0, "error" }, |
{ offsetof(TCCState, warn_implicit_function_declaration), WD_ALL, |
"implicit-function-declaration" }, |
}; |
static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags, |
const char *name, int value) |
{ |
int i; |
const FlagDef *p; |
const char *r; |
r = name; |
if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') { |
r += 3; |
value = !value; |
} |
for(i = 0, p = flags; i < nb_flags; i++, p++) { |
if (!strcmp(r, p->name)) |
goto found; |
} |
return -1; |
found: |
if (p->flags & FD_INVERT) |
value = !value; |
*(int *)((uint8_t *)s + p->offset) = value; |
return 0; |
} |
/* set/reset a warning */ |
int tcc_set_warning(TCCState *s, const char *warning_name, int value) |
{ |
int i; |
const FlagDef *p; |
if (!strcmp(warning_name, "all")) { |
for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) { |
if (p->flags & WD_ALL) |
*(int *)((uint8_t *)s + p->offset) = 1; |
} |
return 0; |
} else { |
return set_flag(s, warning_defs, countof(warning_defs), |
warning_name, value); |
} |
} |
static const FlagDef flag_defs[] = { |
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" }, |
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" }, |
{ offsetof(TCCState, nocommon), FD_INVERT, "common" }, |
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" }, |
}; |
/* set/reset a flag */ |
int tcc_set_flag(TCCState *s, const char *flag_name, int value) |
{ |
return set_flag(s, flag_defs, countof(flag_defs), |
flag_name, value); |
} |
#if !defined(LIBTCC) |
/* extract the basename of a file */ |
static const char *tcc_basename(const char *name) |
{ |
const char *p; |
p = strrchr(name, '/'); |
#ifdef WIN32 |
if (!p) |
p = strrchr(name, '\\'); |
#endif |
if (!p) |
p = name; |
else |
p++; |
return p; |
} |
static int64_t getclock_us(void) |
{ |
#ifdef WIN32 |
struct _timeb tb; |
_ftime(&tb); |
return (tb.time * 1000LL + tb.millitm) * 1000LL; |
#else |
struct timeval tv; |
gettimeofday(&tv, NULL); |
return tv.tv_sec * 1000000LL + tv.tv_usec; |
#endif |
} |
void help(void) |
{ |
printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001-2005 Fabrice Bellard\n" |
"usage: tcc [-v] [-c] [-o outfile] [-Bdir] [-bench] [-Idir] [-Dsym[=val]] [-Usym]\n" |
" [-Wwarn] [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-static]\n" |
" [infile1 infile2...] [-run infile args...]\n" |
"\n" |
"General options:\n" |
" -v display current version\n" |
" -c compile only - generate an object file\n" |
" -o outfile set output filename\n" |
" -Bdir set tcc internal library path\n" |
" -bench output compilation statistics\n" |
" -run run compiled source\n" |
" -fflag set or reset (with 'no-' prefix) 'flag' (see man page)\n" |
" -Wwarning set or reset (with 'no-' prefix) 'warning' (see man page)\n" |
" -w disable all warnings\n" |
"Preprocessor options:\n" |
" -Idir add include path 'dir'\n" |
" -Dsym[=val] define 'sym' with value 'val'\n" |
" -Usym undefine 'sym'\n" |
"Linker options:\n" |
" -Ldir add library path 'dir'\n" |
" -llib link with dynamic or static library 'lib'\n" |
" -shared generate a shared library\n" |
" -static static linking\n" |
" -rdynamic export all global symbols to dynamic linker\n" |
" -r relocatable output\n" |
"Debugger options:\n" |
" -g generate runtime debug info\n" |
" -bt N show N callers in stack traces\n" |
); |
} |
#define TCC_OPTION_HAS_ARG 0x0001 |
#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ |
typedef struct TCCOption { |
const char *name; |
uint16_t index; |
uint16_t flags; |
} TCCOption; |
enum { |
TCC_OPTION_HELP, |
TCC_OPTION_I, |
TCC_OPTION_D, |
TCC_OPTION_U, |
TCC_OPTION_L, |
TCC_OPTION_B, |
TCC_OPTION_l, |
TCC_OPTION_bench, |
TCC_OPTION_bt, |
TCC_OPTION_b, |
TCC_OPTION_g, |
TCC_OPTION_c, |
TCC_OPTION_static, |
TCC_OPTION_shared, |
TCC_OPTION_o, |
TCC_OPTION_r, |
TCC_OPTION_Wl, |
TCC_OPTION_W, |
TCC_OPTION_O, |
TCC_OPTION_m, |
TCC_OPTION_f, |
TCC_OPTION_nostdinc, |
TCC_OPTION_nostdlib, |
TCC_OPTION_print_search_dirs, |
TCC_OPTION_rdynamic, |
TCC_OPTION_run, |
TCC_OPTION_v, |
TCC_OPTION_w, |
TCC_OPTION_pipe, |
}; |
static const TCCOption tcc_options[] = { |
{ "h", TCC_OPTION_HELP, 0 }, |
{ "?", TCC_OPTION_HELP, 0 }, |
{ "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG }, |
{ "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG }, |
{ "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG }, |
{ "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG }, |
{ "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG }, |
{ "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "bench", TCC_OPTION_bench, 0 }, |
{ "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG }, |
#ifdef CONFIG_TCC_BCHECK |
{ "b", TCC_OPTION_b, 0 }, |
#endif |
{ "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "c", TCC_OPTION_c, 0 }, |
{ "static", TCC_OPTION_static, 0 }, |
{ "shared", TCC_OPTION_shared, 0 }, |
{ "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG }, |
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "rdynamic", TCC_OPTION_rdynamic, 0 }, |
{ "r", TCC_OPTION_r, 0 }, |
{ "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG }, |
{ "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, |
{ "nostdinc", TCC_OPTION_nostdinc, 0 }, |
{ "nostdlib", TCC_OPTION_nostdlib, 0 }, |
{ "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, |
{ "v", TCC_OPTION_v, 0 }, |
{ "w", TCC_OPTION_w, 0 }, |
{ "pipe", TCC_OPTION_pipe, 0}, |
{ NULL }, |
}; |
/* convert 'str' into an array of space separated strings */ |
static int expand_args(char ***pargv, const char *str) |
{ |
const char *s1; |
char **argv, *arg; |
int argc, len; |
argc = 0; |
argv = NULL; |
for(;;) { |
while (is_space(*str)) |
str++; |
if (*str == '\0') |
break; |
s1 = str; |
while (*str != '\0' && !is_space(*str)) |
str++; |
len = str - s1; |
arg = tcc_malloc(len + 1); |
memcpy(arg, s1, len); |
arg[len] = '\0'; |
dynarray_add((void ***)&argv, &argc, arg); |
} |
*pargv = argv; |
return argc; |
} |
static char **files; |
static int nb_files, nb_libraries; |
static int multiple_files; |
static int print_search_dirs; |
static int output_type; |
static int reloc_output; |
static const char *outfile; |
int parse_args(TCCState *s, int argc, char **argv) |
{ |
int optind; |
int i; |
const TCCOption *popt; |
const char *optarg, *p1, *r1; |
char *r; |
/* |
printf("\n%d\n",argc); |
for(i=0;i<argc;i++) |
{ |
printf("\n parameter %d = %s",i+1,argv[i]); |
} |
printf("\n"); |
*/ |
optind = 0; |
while (1) { |
if (optind >= argc) { |
if (nb_files == 0 && !print_search_dirs) |
goto show_help; |
else |
break; |
} |
r = argv[optind++]; |
if (r[0] != '-') { |
/* add a new file */ |
dynarray_add((void ***)&files, &nb_files, r); |
if (!multiple_files) { |
optind--; |
/* argv[0] will be this file */ |
break; |
} |
} else { |
/* find option in table (match only the first chars */ |
popt = tcc_options; |
for(;;) { |
p1 = popt->name; |
if (p1 == NULL) |
printf("\n invalid option -- '%s'", r); |
r1 = r + 1; |
for(;;) { |
if (*p1 == '\0') |
goto option_found; |
if (*r1 != *p1) |
break; |
p1++; |
r1++; |
} |
popt++; |
} |
option_found: |
if (popt->flags & TCC_OPTION_HAS_ARG) { |
if (*r1 != '\0' || (popt->flags & TCC_OPTION_NOSEP)) { |
optarg = r1; |
} else { |
if (optind >= argc) |
printf("\n argument to '%s' is missing", r); |
optarg = argv[optind++]; |
} |
} else { |
if (*r1 != '\0') |
goto show_help; |
optarg = NULL; |
} |
switch(popt->index) { |
case TCC_OPTION_HELP: |
show_help: |
help(); |
exit(1); |
case TCC_OPTION_I: |
if (tcc_add_include_path(s, optarg) < 0) |
printf("\n too many include paths"); |
break; |
case TCC_OPTION_D: |
{ |
char *sym, *value; |
sym = (char *)optarg; |
value = strchr(sym, '='); |
if (value) { |
*value = '\0'; |
value++; |
} |
tcc_define_symbol(s, sym, value); |
} |
break; |
case TCC_OPTION_U: |
tcc_undefine_symbol(s, optarg); |
break; |
case TCC_OPTION_L: |
tcc_add_library_path(s, optarg); |
break; |
case TCC_OPTION_B: |
/* set tcc utilities path (mainly for tcc development) */ |
tcc_lib_path = optarg; |
break; |
case TCC_OPTION_l: |
dynarray_add((void ***)&files, &nb_files, r); |
nb_libraries++; |
break; |
case TCC_OPTION_bench: |
do_bench = 1; |
break; |
case TCC_OPTION_bt: |
num_callers = atoi(optarg); |
break; |
#ifdef CONFIG_TCC_BCHECK |
case TCC_OPTION_b: |
do_bounds_check = 1; |
do_debug = 1; |
break; |
#endif |
case TCC_OPTION_g: |
do_debug = 1; |
break; |
case TCC_OPTION_c: |
multiple_files = 1; |
output_type = TCC_OUTPUT_OBJ; |
break; |
case TCC_OPTION_static: |
s->static_link = 1; |
break; |
case TCC_OPTION_shared: |
output_type = TCC_OUTPUT_DLL; |
break; |
case TCC_OPTION_o: |
multiple_files = 1; |
outfile = optarg; |
break; |
case TCC_OPTION_r: |
/* generate a .o merging several output files */ |
reloc_output = 1; |
output_type = TCC_OUTPUT_OBJ; |
break; |
case TCC_OPTION_nostdinc: |
s->nostdinc = 1; |
break; |
case TCC_OPTION_nostdlib: |
s->nostdlib = 1; |
break; |
case TCC_OPTION_print_search_dirs: |
print_search_dirs = 1; |
break; |
case TCC_OPTION_run: |
{ |
int argc1; |
char **argv1; |
argc1 = expand_args(&argv1, optarg); |
if (argc1 > 0) { |
parse_args(s, argc1, argv1); |
} |
multiple_files = 0; |
output_type = TCC_OUTPUT_MEMORY; |
} |
break; |
case TCC_OPTION_v: |
printf("tcc version %s\n", TCC_VERSION); |
exit(0); |
case TCC_OPTION_f: |
if (tcc_set_flag(s, optarg, 1) < 0 && s->warn_unsupported) |
goto unsupported_option; |
break; |
case TCC_OPTION_W: |
if (tcc_set_warning(s, optarg, 1) < 0 && |
s->warn_unsupported) |
goto unsupported_option; |
break; |
case TCC_OPTION_w: |
s->warn_none = 1; |
break; |
case TCC_OPTION_rdynamic: |
s->rdynamic = 1; |
break; |
case TCC_OPTION_Wl: |
{ |
const char *p; |
if (strstart(optarg, "-Ttext,", &p)) { |
s->text_addr = strtoul(p, NULL, 16); |
s->has_text_addr = 1; |
} else if (strstart(optarg, "--oformat,", &p)) { |
if (strstart(p, "elf32-", NULL)) { |
s->output_format = TCC_OUTPUT_FORMAT_ELF; |
} else if (!strcmp(p, "binary")) { |
s->output_format = TCC_OUTPUT_FORMAT_BINARY; |
} else |
#ifdef TCC_TARGET_COFF |
if (!strcmp(p, "coff")) { |
s->output_format = TCC_OUTPUT_FORMAT_COFF; |
} else |
#endif |
{ |
printf("\n target %s not found", p); |
} |
} else { |
printf("\n unsupported linker option '%s'", optarg); |
} |
} |
break; |
default: |
if (s->warn_unsupported) { |
unsupported_option: |
warning("unsupported option '%s'", r); |
printf("\n unsupported option '%s'", r); |
} |
break; |
} |
} |
} |
return optind; |
} |
int app_main(int argc, char **argv) |
{ |
int i; |
TCCState *s; |
int nb_objfiles, ret, optind; |
char objfilename[1024]; |
int64_t start_time = 0; |
int bug; |
printf("\nTinyC compiler started.\n "); |
#ifdef WIN32 |
/* on win32, we suppose the lib and includes are at the location |
of 'tcc.exe' */ |
{ |
static char path[1024]; |
char *p, *d; |
GetModuleFileNameA(NULL, path, sizeof path); |
p = d = strlwr(path); |
while (*d) |
{ |
if (*d == '\\') *d = '/', p = d; |
++d; |
} |
*p = '\0'; |
tcc_lib_path = path; |
} |
#endif |
s = tcc_new(); |
output_type = TCC_OUTPUT_EXE; |
outfile = NULL; |
multiple_files = 1; |
files = NULL; |
nb_files = 0; |
nb_libraries = 0; |
reloc_output = 0; |
print_search_dirs = 0; |
printf("TinyC initializated.\n"); |
bug=argc; |
if (bug==0) {bug==1;} |
optind = parse_args(s, bug - 1, argv + 1) + 1; |
printf("\n Arguments parsed.\n"); |
if (print_search_dirs) { |
/* enough for Linux kernel */ |
printf("install: %s/\n", tcc_lib_path); |
return 0; |
} |
nb_objfiles = nb_files - nb_libraries; |
/* if outfile provided without other options, we output an |
executable */ |
if (outfile && output_type == TCC_OUTPUT_MEMORY) |
output_type = TCC_OUTPUT_EXE; |
/* check -c |
consistency : only single file handled. XXX: checks file type */ |
if (output_type == TCC_OUTPUT_OBJ && !reloc_output) { |
/* accepts only a single input file */ |
if (nb_objfiles != 1) |
printf("\n cannot specify multiple files with -c"); |
if (nb_libraries != 0) |
printf("\n cannot specify libraries with -c"); |
} |
if (output_type != TCC_OUTPUT_MEMORY) { |
if (!outfile) { |
/* compute default outfile name */ |
pstrcpy(objfilename, sizeof(objfilename) - 1, |
/* strip path */ |
tcc_basename(files[0])); |
#ifdef TCC_TARGET_PE |
pe_guess_outfile(objfilename, output_type); |
#else |
if (output_type == TCC_OUTPUT_OBJ && !reloc_output) { |
char *ext = strrchr(objfilename, '.'); |
if (!ext) |
goto default_outfile; |
/* add .o extension */ |
strcpy(ext + 1, "o"); |
} else { |
default_outfile: |
pstrcpy(objfilename, sizeof(objfilename), "a.out"); |
} |
#endif |
outfile = objfilename; |
} |
} |
if (do_bench) { |
start_time = getclock_us(); |
} |
tcc_set_output_type(s, output_type); |
/* compile or add each files or library */ |
for(i = 0;i < nb_files; i++) { |
const char *filename; |
filename = files[i]; |
if (filename[0] == '-') { |
if (tcc_add_library(s, filename + 2) < 0) |
error("cannot find %s", filename); |
} else { |
if (tcc_add_file(s, filename) < 0) { |
ret = 1; |
goto the_end; |
} |
} |
} |
/* free all files */ |
tcc_free(files); |
if (do_bench) { |
double total_time; |
total_time = (double)(getclock_us() - start_time) / 1000000.0; |
if (total_time < 0.001) |
total_time = 0.001; |
if (total_bytes < 1) |
total_bytes = 1; |
printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n", |
tok_ident - TOK_IDENT, total_lines, total_bytes, |
total_time, (int)(total_lines / total_time), |
total_bytes / total_time / 1000000.0); |
} |
if (s->output_type == TCC_OUTPUT_MEMORY) { |
ret = tcc_run(s, argc - optind, argv + optind); |
} else |
#ifdef TCC_TARGET_PE |
if (s->output_type != TCC_OUTPUT_OBJ) { |
ret = tcc_output_pe(s, outfile); |
} else |
#else |
#ifdef TCC_TARGET_MEOS |
if (s->output_type != TCC_OUTPUT_OBJ) { |
ret = tcc_output_me(s, outfile); |
} else |
#endif |
#endif |
{ |
tcc_output_file(s, outfile); |
ret = 0; |
} |
the_end: |
/* XXX: cannot do it with bound checking because of the malloc hooks */ |
if (!do_bounds_check) |
tcc_delete(s); |
#ifdef MEM_DEBUG |
if (do_bench) { |
printf("memory: %d bytes, max = %d bytes\n", mem_cur_size, mem_max_size); |
} |
#endif |
printf("\n TinyC finished work\n"); |
return ret; |
} |
#endif |
/programs/develop/ktcc/trunk/source/tccelf.c |
---|
0,0 → 1,2338 |
/* |
* ELF file handling for TCC |
* |
* Copyright (c) 2001-2004 Fabrice Bellard |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation; either |
* version 2 of the License, or (at your option) any later version. |
* |
* This library is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
* Lesser General Public License for more details. |
* |
* You should have received a copy of the GNU Lesser General Public |
* License along with this library; if not, write to the Free Software |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
static int put_elf_str(Section *s, const char *sym) |
{ |
int offset, len; |
char *ptr; |
len = strlen(sym) + 1; |
offset = s->data_offset; |
ptr = section_ptr_add(s, len); |
memcpy(ptr, sym, len); |
return offset; |
} |
/* elf symbol hashing function */ |
static unsigned long elf_hash(const unsigned char *name) |
{ |
unsigned long h = 0, g; |
while (*name) { |
h = (h << 4) + *name++; |
g = h & 0xf0000000; |
if (g) |
h ^= g >> 24; |
h &= ~g; |
} |
return h; |
} |
/* rebuild hash table of section s */ |
/* NOTE: we do factorize the hash table code to go faster */ |
static void rebuild_hash(Section *s, unsigned int nb_buckets) |
{ |
Elf32_Sym *sym; |
int *ptr, *hash, nb_syms, sym_index, h; |
char *strtab; |
strtab = s->link->data; |
nb_syms = s->data_offset / sizeof(Elf32_Sym); |
s->hash->data_offset = 0; |
ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int)); |
ptr[0] = nb_buckets; |
ptr[1] = nb_syms; |
ptr += 2; |
hash = ptr; |
memset(hash, 0, (nb_buckets + 1) * sizeof(int)); |
ptr += nb_buckets + 1; |
sym = (Elf32_Sym *)s->data + 1; |
for(sym_index = 1; sym_index < nb_syms; sym_index++) { |
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { |
h = elf_hash(strtab + sym->st_name) % nb_buckets; |
*ptr = hash[h]; |
hash[h] = sym_index; |
} else { |
*ptr = 0; |
} |
ptr++; |
sym++; |
} |
} |
/* return the symbol number */ |
static int put_elf_sym(Section *s, |
unsigned long value, unsigned long size, |
int info, int other, int shndx, const char *name) |
{ |
int name_offset, sym_index; |
int nbuckets, h; |
Elf32_Sym *sym; |
Section *hs; |
sym = section_ptr_add(s, sizeof(Elf32_Sym)); |
if (name) |
name_offset = put_elf_str(s->link, name); |
else |
name_offset = 0; |
/* XXX: endianness */ |
sym->st_name = name_offset; |
sym->st_value = value; |
sym->st_size = size; |
sym->st_info = info; |
sym->st_other = other; |
sym->st_shndx = shndx; |
sym_index = sym - (Elf32_Sym *)s->data; |
hs = s->hash; |
if (hs) { |
int *ptr, *base; |
ptr = section_ptr_add(hs, sizeof(int)); |
base = (int *)hs->data; |
/* only add global or weak symbols */ |
if (ELF32_ST_BIND(info) != STB_LOCAL) { |
/* add another hashing entry */ |
nbuckets = base[0]; |
h = elf_hash(name) % nbuckets; |
*ptr = base[2 + h]; |
base[2 + h] = sym_index; |
base[1]++; |
/* we resize the hash table */ |
hs->nb_hashed_syms++; |
if (hs->nb_hashed_syms > 2 * nbuckets) { |
rebuild_hash(s, 2 * nbuckets); |
} |
} else { |
*ptr = 0; |
base[1]++; |
} |
} |
return sym_index; |
} |
/* find global ELF symbol 'name' and return its index. Return 0 if not |
found. */ |
static int find_elf_sym(Section *s, const char *name) |
{ |
Elf32_Sym *sym; |
Section *hs; |
int nbuckets, sym_index, h; |
const char *name1; |
hs = s->hash; |
if (!hs) |
return 0; |
nbuckets = ((int *)hs->data)[0]; |
h = elf_hash(name) % nbuckets; |
sym_index = ((int *)hs->data)[2 + h]; |
while (sym_index != 0) { |
sym = &((Elf32_Sym *)s->data)[sym_index]; |
name1 = s->link->data + sym->st_name; |
if (!strcmp(name, name1)) |
return sym_index; |
sym_index = ((int *)hs->data)[2 + nbuckets + sym_index]; |
} |
return 0; |
} |
/* return elf symbol value or error */ |
int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name) |
{ |
int sym_index; |
Elf32_Sym *sym; |
sym_index = find_elf_sym(symtab_section, name); |
if (!sym_index) |
return -1; |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
*pval = sym->st_value; |
return 0; |
} |
void *tcc_get_symbol_err(TCCState *s, const char *name) |
{ |
unsigned long val; |
if (tcc_get_symbol(s, &val, name) < 0) |
error("%s not defined", name); |
return (void *)val; |
} |
/* add an elf symbol : check if it is already defined and patch |
it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */ |
static int add_elf_sym(Section *s, unsigned long value, unsigned long size, |
int info, int other, int sh_num, const char *name) |
{ |
Elf32_Sym *esym; |
int sym_bind, sym_index, sym_type, esym_bind; |
sym_bind = ELF32_ST_BIND(info); |
sym_type = ELF32_ST_TYPE(info); |
if (sym_bind != STB_LOCAL) { |
/* we search global or weak symbols */ |
sym_index = find_elf_sym(s, name); |
if (!sym_index) |
goto do_def; |
esym = &((Elf32_Sym *)s->data)[sym_index]; |
if (esym->st_shndx != SHN_UNDEF) { |
esym_bind = ELF32_ST_BIND(esym->st_info); |
if (sh_num == SHN_UNDEF) { |
/* ignore adding of undefined symbol if the |
corresponding symbol is already defined */ |
} else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) { |
/* global overrides weak, so patch */ |
goto do_patch; |
} else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) { |
/* weak is ignored if already global */ |
} else { |
#if 0 |
printf("new_bind=%d new_shndx=%d last_bind=%d old_shndx=%d\n", |
sym_bind, sh_num, esym_bind, esym->st_shndx); |
#endif |
/* NOTE: we accept that two DLL define the same symbol */ |
if (s != tcc_state->dynsymtab_section) |
error_noabort("'%s' defined twice", name); |
} |
} else { |
do_patch: |
esym->st_info = ELF32_ST_INFO(sym_bind, sym_type); |
esym->st_shndx = sh_num; |
esym->st_value = value; |
esym->st_size = size; |
esym->st_other = other; |
} |
} else { |
do_def: |
sym_index = put_elf_sym(s, value, size, |
ELF32_ST_INFO(sym_bind, sym_type), other, |
sh_num, name); |
} |
return sym_index; |
} |
/* put relocation */ |
static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, |
int type, int symbol) |
{ |
char buf[256]; |
Section *sr; |
Elf32_Rel *rel; |
sr = s->reloc; |
if (!sr) { |
/* if no relocation section, create it */ |
snprintf(buf, sizeof(buf), ".rel%s", s->name); |
/* if the symtab is allocated, then we consider the relocation |
are also */ |
sr = new_section(tcc_state, buf, SHT_REL, symtab->sh_flags); |
sr->sh_entsize = sizeof(Elf32_Rel); |
sr->link = symtab; |
sr->sh_info = s->sh_num; |
s->reloc = sr; |
} |
rel = section_ptr_add(sr, sizeof(Elf32_Rel)); |
rel->r_offset = offset; |
rel->r_info = ELF32_R_INFO(symbol, type); |
} |
/* put stab debug information */ |
typedef struct { |
unsigned long n_strx; /* index into string table of name */ |
unsigned char n_type; /* type of symbol */ |
unsigned char n_other; /* misc info (usually empty) */ |
unsigned short n_desc; /* description field */ |
unsigned long n_value; /* value of symbol */ |
} Stab_Sym; |
static void put_stabs(const char *str, int type, int other, int desc, |
unsigned long value) |
{ |
Stab_Sym *sym; |
sym = section_ptr_add(stab_section, sizeof(Stab_Sym)); |
if (str) { |
sym->n_strx = put_elf_str(stabstr_section, str); |
} else { |
sym->n_strx = 0; |
} |
sym->n_type = type; |
sym->n_other = other; |
sym->n_desc = desc; |
sym->n_value = value; |
} |
static void put_stabs_r(const char *str, int type, int other, int desc, |
unsigned long value, Section *sec, int sym_index) |
{ |
put_stabs(str, type, other, desc, value); |
put_elf_reloc(symtab_section, stab_section, |
stab_section->data_offset - sizeof(unsigned long), |
R_DATA_32, sym_index); |
} |
static void put_stabn(int type, int other, int desc, int value) |
{ |
put_stabs(NULL, type, other, desc, value); |
} |
static void put_stabd(int type, int other, int desc) |
{ |
put_stabs(NULL, type, other, desc, 0); |
} |
/* In an ELF file symbol table, the local symbols must appear below |
the global and weak ones. Since TCC cannot sort it while generating |
the code, we must do it after. All the relocation tables are also |
modified to take into account the symbol table sorting */ |
static void sort_syms(TCCState *s1, Section *s) |
{ |
int *old_to_new_syms; |
Elf32_Sym *new_syms; |
int nb_syms, i; |
Elf32_Sym *p, *q; |
Elf32_Rel *rel, *rel_end; |
Section *sr; |
int type, sym_index; |
nb_syms = s->data_offset / sizeof(Elf32_Sym); |
new_syms = tcc_malloc(nb_syms * sizeof(Elf32_Sym)); |
old_to_new_syms = tcc_malloc(nb_syms * sizeof(int)); |
/* first pass for local symbols */ |
p = (Elf32_Sym *)s->data; |
q = new_syms; |
for(i = 0; i < nb_syms; i++) { |
if (ELF32_ST_BIND(p->st_info) == STB_LOCAL) { |
old_to_new_syms[i] = q - new_syms; |
*q++ = *p; |
} |
p++; |
} |
/* save the number of local symbols in section header */ |
s->sh_info = q - new_syms; |
/* then second pass for non local symbols */ |
p = (Elf32_Sym *)s->data; |
for(i = 0; i < nb_syms; i++) { |
if (ELF32_ST_BIND(p->st_info) != STB_LOCAL) { |
old_to_new_syms[i] = q - new_syms; |
*q++ = *p; |
} |
p++; |
} |
/* we copy the new symbols to the old */ |
memcpy(s->data, new_syms, nb_syms * sizeof(Elf32_Sym)); |
tcc_free(new_syms); |
/* now we modify all the relocations */ |
for(i = 1; i < s1->nb_sections; i++) { |
sr = s1->sections[i]; |
if (sr->sh_type == SHT_REL && sr->link == s) { |
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset); |
for(rel = (Elf32_Rel *)sr->data; |
rel < rel_end; |
rel++) { |
sym_index = ELF32_R_SYM(rel->r_info); |
type = ELF32_R_TYPE(rel->r_info); |
sym_index = old_to_new_syms[sym_index]; |
rel->r_info = ELF32_R_INFO(sym_index, type); |
} |
} |
} |
tcc_free(old_to_new_syms); |
} |
/* relocate common symbols in the .bss section */ |
static void relocate_common_syms(void) |
{ |
Elf32_Sym *sym, *sym_end; |
unsigned long offset, align; |
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset); |
for(sym = (Elf32_Sym *)symtab_section->data + 1; |
sym < sym_end; |
sym++) { |
if (sym->st_shndx == SHN_COMMON) { |
/* align symbol */ |
align = sym->st_value; |
offset = bss_section->data_offset; |
offset = (offset + align - 1) & -align; |
sym->st_value = offset; |
sym->st_shndx = bss_section->sh_num; |
offset += sym->st_size; |
bss_section->data_offset = offset; |
} |
} |
} |
/* relocate symbol table, resolve undefined symbols if do_resolve is |
true and output error if undefined symbol. */ |
static void relocate_syms(TCCState *s1, int do_resolve) |
{ |
Elf32_Sym *sym, *esym, *sym_end; |
int sym_bind, sh_num, sym_index; |
const char *name; |
unsigned long addr; |
sym_end = (Elf32_Sym *)(symtab_section->data + symtab_section->data_offset); |
for(sym = (Elf32_Sym *)symtab_section->data + 1; |
sym < sym_end; |
sym++) { |
sh_num = sym->st_shndx; |
if (sh_num == SHN_UNDEF) { |
name = strtab_section->data + sym->st_name; |
if (do_resolve) { |
name = symtab_section->link->data + sym->st_name; |
addr = (unsigned long)resolve_sym(s1, name, ELF32_ST_TYPE(sym->st_info)); |
if (addr) { |
sym->st_value = addr; |
goto found; |
} |
} else if (s1->dynsym) { |
/* if dynamic symbol exist, then use it */ |
sym_index = find_elf_sym(s1->dynsym, name); |
if (sym_index) { |
esym = &((Elf32_Sym *)s1->dynsym->data)[sym_index]; |
sym->st_value = esym->st_value; |
goto found; |
} |
} |
/* XXX: _fp_hw seems to be part of the ABI, so we ignore |
it */ |
if (!strcmp(name, "_fp_hw")) |
goto found; |
/* only weak symbols are accepted to be undefined. Their |
value is zero */ |
sym_bind = ELF32_ST_BIND(sym->st_info); |
if (sym_bind == STB_WEAK) { |
sym->st_value = 0; |
} else { |
error_noabort("undefined symbol '%s'", name); |
} |
} else if (sh_num < SHN_LORESERVE) { |
/* add section base */ |
sym->st_value += s1->sections[sym->st_shndx]->sh_addr; |
} |
found: ; |
} |
} |
/* relocate a given section (CPU dependent) */ |
static void relocate_section(TCCState *s1, Section *s) |
{ |
Section *sr; |
Elf32_Rel *rel, *rel_end, *qrel; |
Elf32_Sym *sym; |
int type, sym_index; |
unsigned char *ptr; |
unsigned long val, addr; |
#if defined(TCC_TARGET_I386) |
int esym_index; |
#endif |
sr = s->reloc; |
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset); |
qrel = (Elf32_Rel *)sr->data; |
for(rel = qrel; |
rel < rel_end; |
rel++) { |
ptr = s->data + rel->r_offset; |
sym_index = ELF32_R_SYM(rel->r_info); |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
val = sym->st_value; |
type = ELF32_R_TYPE(rel->r_info); |
addr = s->sh_addr + rel->r_offset; |
/* CPU specific */ |
switch(type) { |
#if defined(TCC_TARGET_I386) |
case R_386_32: |
if (s1->output_type == TCC_OUTPUT_DLL) { |
esym_index = s1->symtab_to_dynsym[sym_index]; |
qrel->r_offset = rel->r_offset; |
if (esym_index) { |
qrel->r_info = ELF32_R_INFO(esym_index, R_386_32); |
qrel++; |
break; |
} else { |
qrel->r_info = ELF32_R_INFO(0, R_386_RELATIVE); |
qrel++; |
} |
} |
*(int *)ptr += val; |
break; |
case R_386_PC32: |
if (s1->output_type == TCC_OUTPUT_DLL) { |
/* DLL relocation */ |
esym_index = s1->symtab_to_dynsym[sym_index]; |
if (esym_index) { |
qrel->r_offset = rel->r_offset; |
qrel->r_info = ELF32_R_INFO(esym_index, R_386_PC32); |
qrel++; |
break; |
} |
} |
*(int *)ptr += val - addr; |
break; |
case R_386_PLT32: |
*(int *)ptr += val - addr; |
break; |
case R_386_GLOB_DAT: |
case R_386_JMP_SLOT: |
*(int *)ptr = val; |
break; |
case R_386_GOTPC: |
*(int *)ptr += s1->got->sh_addr - addr; |
break; |
case R_386_GOTOFF: |
*(int *)ptr += val - s1->got->sh_addr; |
break; |
case R_386_GOT32: |
/* we load the got offset */ |
*(int *)ptr += s1->got_offsets[sym_index]; |
break; |
#elif defined(TCC_TARGET_ARM) |
case R_ARM_PC24: |
case R_ARM_PLT32: |
{ |
int x; |
x = (*(int *)ptr)&0xffffff; |
(*(int *)ptr) &= 0xff000000; |
if (x & 0x800000) |
x -= 0x1000000; |
x *= 4; |
x += val - addr; |
if((x & 3) != 0 || x >= 0x4000000 || x < -0x4000000) |
error("can't relocate value at %x",addr); |
x >>= 2; |
x &= 0xffffff; |
(*(int *)ptr) |= x; |
} |
break; |
case R_ARM_ABS32: |
*(int *)ptr += val; |
break; |
case R_ARM_GOTPC: |
*(int *)ptr += s1->got->sh_addr - addr; |
break; |
case R_ARM_GOT32: |
/* we load the got offset */ |
*(int *)ptr += s1->got_offsets[sym_index]; |
break; |
case R_ARM_COPY: |
break; |
default: |
fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n", |
type,addr,(unsigned int )ptr,val); |
break; |
#elif defined(TCC_TARGET_C67) |
case R_C60_32: |
*(int *)ptr += val; |
break; |
case R_C60LO16: |
{ |
uint32_t orig; |
/* put the low 16 bits of the absolute address */ |
// add to what is already there |
orig = ((*(int *)(ptr )) >> 7) & 0xffff; |
orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16; |
//patch both at once - assumes always in pairs Low - High |
*(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) | (((val+orig) & 0xffff) << 7); |
*(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | ((((val+orig)>>16) & 0xffff) << 7); |
} |
break; |
case R_C60HI16: |
break; |
default: |
fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n", |
type,addr,(unsigned int )ptr,val); |
break; |
#else |
#error unsupported processor |
#endif |
} |
} |
/* if the relocation is allocated, we change its symbol table */ |
if (sr->sh_flags & SHF_ALLOC) |
sr->link = s1->dynsym; |
} |
/* relocate relocation table in 'sr' */ |
static void relocate_rel(TCCState *s1, Section *sr) |
{ |
Section *s; |
Elf32_Rel *rel, *rel_end; |
s = s1->sections[sr->sh_info]; |
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset); |
for(rel = (Elf32_Rel *)sr->data; |
rel < rel_end; |
rel++) { |
rel->r_offset += s->sh_addr; |
} |
} |
/* count the number of dynamic relocations so that we can reserve |
their space */ |
static int prepare_dynamic_rel(TCCState *s1, Section *sr) |
{ |
Elf32_Rel *rel, *rel_end; |
int sym_index, esym_index, type, count; |
count = 0; |
rel_end = (Elf32_Rel *)(sr->data + sr->data_offset); |
for(rel = (Elf32_Rel *)sr->data; rel < rel_end; rel++) { |
sym_index = ELF32_R_SYM(rel->r_info); |
type = ELF32_R_TYPE(rel->r_info); |
switch(type) { |
case R_386_32: |
count++; |
break; |
case R_386_PC32: |
esym_index = s1->symtab_to_dynsym[sym_index]; |
if (esym_index) |
count++; |
break; |
default: |
break; |
} |
} |
if (count) { |
/* allocate the section */ |
sr->sh_flags |= SHF_ALLOC; |
sr->sh_size = count * sizeof(Elf32_Rel); |
} |
return count; |
} |
static void put_got_offset(TCCState *s1, int index, unsigned long val) |
{ |
int n; |
unsigned long *tab; |
if (index >= s1->nb_got_offsets) { |
/* find immediately bigger power of 2 and reallocate array */ |
n = 1; |
while (index >= n) |
n *= 2; |
tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long)); |
if (!tab) |
error("memory full"); |
s1->got_offsets = tab; |
memset(s1->got_offsets + s1->nb_got_offsets, 0, |
(n - s1->nb_got_offsets) * sizeof(unsigned long)); |
s1->nb_got_offsets = n; |
} |
s1->got_offsets[index] = val; |
} |
/* XXX: suppress that */ |
static void put32(unsigned char *p, uint32_t val) |
{ |
p[0] = val; |
p[1] = val >> 8; |
p[2] = val >> 16; |
p[3] = val >> 24; |
} |
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM) |
static uint32_t get32(unsigned char *p) |
{ |
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); |
} |
#endif |
static void build_got(TCCState *s1) |
{ |
unsigned char *ptr; |
/* if no got, then create it */ |
s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); |
s1->got->sh_entsize = 4; |
add_elf_sym(symtab_section, 0, 4, ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), |
0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_"); |
ptr = section_ptr_add(s1->got, 3 * sizeof(int)); |
/* keep space for _DYNAMIC pointer, if present */ |
put32(ptr, 0); |
/* two dummy got entries */ |
put32(ptr + 4, 0); |
put32(ptr + 8, 0); |
} |
/* put a got entry corresponding to a symbol in symtab_section. 'size' |
and 'info' can be modifed if more precise info comes from the DLL */ |
static void put_got_entry(TCCState *s1, |
int reloc_type, unsigned long size, int info, |
int sym_index) |
{ |
int index; |
const char *name; |
Elf32_Sym *sym; |
unsigned long offset; |
int *ptr; |
if (!s1->got) |
build_got(s1); |
/* if a got entry already exists for that symbol, no need to add one */ |
if (sym_index < s1->nb_got_offsets && |
s1->got_offsets[sym_index] != 0) |
return; |
put_got_offset(s1, sym_index, s1->got->data_offset); |
if (s1->dynsym) { |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
name = symtab_section->link->data + sym->st_name; |
offset = sym->st_value; |
#ifdef TCC_TARGET_I386 |
if (reloc_type == R_386_JMP_SLOT) { |
Section *plt; |
uint8_t *p; |
int modrm; |
/* if we build a DLL, we add a %ebx offset */ |
if (s1->output_type == TCC_OUTPUT_DLL) |
modrm = 0xa3; |
else |
modrm = 0x25; |
/* add a PLT entry */ |
plt = s1->plt; |
if (plt->data_offset == 0) { |
/* first plt entry */ |
p = section_ptr_add(plt, 16); |
p[0] = 0xff; /* pushl got + 4 */ |
p[1] = modrm + 0x10; |
put32(p + 2, 4); |
p[6] = 0xff; /* jmp *(got + 8) */ |
p[7] = modrm; |
put32(p + 8, 8); |
} |
p = section_ptr_add(plt, 16); |
p[0] = 0xff; /* jmp *(got + x) */ |
p[1] = modrm; |
put32(p + 2, s1->got->data_offset); |
p[6] = 0x68; /* push $xxx */ |
put32(p + 7, (plt->data_offset - 32) >> 1); |
p[11] = 0xe9; /* jmp plt_start */ |
put32(p + 12, -(plt->data_offset)); |
/* the symbol is modified so that it will be relocated to |
the PLT */ |
if (s1->output_type == TCC_OUTPUT_EXE) |
offset = plt->data_offset - 16; |
} |
#elif defined(TCC_TARGET_ARM) |
if (reloc_type == R_ARM_JUMP_SLOT) { |
Section *plt; |
uint8_t *p; |
/* if we build a DLL, we add a %ebx offset */ |
if (s1->output_type == TCC_OUTPUT_DLL) |
error("DLLs unimplemented!"); |
/* add a PLT entry */ |
plt = s1->plt; |
if (plt->data_offset == 0) { |
/* first plt entry */ |
p = section_ptr_add(plt, 16); |
put32(p , 0xe52de004); |
put32(p + 4, 0xe59fe010); |
put32(p + 8, 0xe08fe00e); |
put32(p + 12, 0xe5bef008); |
} |
p = section_ptr_add(plt, 16); |
put32(p , 0xe59fc004); |
put32(p+4, 0xe08fc00c); |
put32(p+8, 0xe59cf000); |
put32(p+12, s1->got->data_offset); |
/* the symbol is modified so that it will be relocated to |
the PLT */ |
if (s1->output_type == TCC_OUTPUT_EXE) |
offset = plt->data_offset - 16; |
} |
#elif defined(TCC_TARGET_C67) |
error("C67 got not implemented"); |
#else |
#error unsupported CPU |
#endif |
index = put_elf_sym(s1->dynsym, offset, |
size, info, 0, sym->st_shndx, name); |
/* put a got entry */ |
put_elf_reloc(s1->dynsym, s1->got, |
s1->got->data_offset, |
reloc_type, index); |
} |
ptr = section_ptr_add(s1->got, sizeof(int)); |
*ptr = 0; |
} |
/* build GOT and PLT entries */ |
static void build_got_entries(TCCState *s1) |
{ |
Section *s, *symtab; |
Elf32_Rel *rel, *rel_end; |
Elf32_Sym *sym; |
int i, type, reloc_type, sym_index; |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if (s->sh_type != SHT_REL) |
continue; |
/* no need to handle got relocations */ |
if (s->link != symtab_section) |
continue; |
symtab = s->link; |
rel_end = (Elf32_Rel *)(s->data + s->data_offset); |
for(rel = (Elf32_Rel *)s->data; |
rel < rel_end; |
rel++) { |
type = ELF32_R_TYPE(rel->r_info); |
switch(type) { |
#if defined(TCC_TARGET_I386) |
case R_386_GOT32: |
case R_386_GOTOFF: |
case R_386_GOTPC: |
case R_386_PLT32: |
if (!s1->got) |
build_got(s1); |
if (type == R_386_GOT32 || type == R_386_PLT32) { |
sym_index = ELF32_R_SYM(rel->r_info); |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
/* look at the symbol got offset. If none, then add one */ |
if (type == R_386_GOT32) |
reloc_type = R_386_GLOB_DAT; |
else |
reloc_type = R_386_JMP_SLOT; |
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, |
sym_index); |
} |
break; |
#elif defined(TCC_TARGET_ARM) |
case R_ARM_GOT32: |
case R_ARM_GOTOFF: |
case R_ARM_GOTPC: |
case R_ARM_PLT32: |
if (!s1->got) |
build_got(s1); |
if (type == R_ARM_GOT32 || type == R_ARM_PLT32) { |
sym_index = ELF32_R_SYM(rel->r_info); |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
/* look at the symbol got offset. If none, then add one */ |
if (type == R_ARM_GOT32) |
reloc_type = R_ARM_GLOB_DAT; |
else |
reloc_type = R_ARM_JUMP_SLOT; |
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, |
sym_index); |
} |
break; |
#elif defined(TCC_TARGET_C67) |
case R_C60_GOT32: |
case R_C60_GOTOFF: |
case R_C60_GOTPC: |
case R_C60_PLT32: |
if (!s1->got) |
build_got(s1); |
if (type == R_C60_GOT32 || type == R_C60_PLT32) { |
sym_index = ELF32_R_SYM(rel->r_info); |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
/* look at the symbol got offset. If none, then add one */ |
if (type == R_C60_GOT32) |
reloc_type = R_C60_GLOB_DAT; |
else |
reloc_type = R_C60_JMP_SLOT; |
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, |
sym_index); |
} |
break; |
#else |
#error unsupported CPU |
#endif |
default: |
break; |
} |
} |
} |
} |
static Section *new_symtab(TCCState *s1, |
const char *symtab_name, int sh_type, int sh_flags, |
const char *strtab_name, |
const char *hash_name, int hash_sh_flags) |
{ |
Section *symtab, *strtab, *hash; |
int *ptr, nb_buckets; |
symtab = new_section(s1, symtab_name, sh_type, sh_flags); |
symtab->sh_entsize = sizeof(Elf32_Sym); |
strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags); |
put_elf_str(strtab, ""); |
symtab->link = strtab; |
put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL); |
nb_buckets = 1; |
hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags); |
hash->sh_entsize = sizeof(int); |
symtab->hash = hash; |
hash->link = symtab; |
ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int)); |
ptr[0] = nb_buckets; |
ptr[1] = 1; |
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int)); |
return symtab; |
} |
/* put dynamic tag */ |
static void put_dt(Section *dynamic, int dt, unsigned long val) |
{ |
Elf32_Dyn *dyn; |
dyn = section_ptr_add(dynamic, sizeof(Elf32_Dyn)); |
dyn->d_tag = dt; |
dyn->d_un.d_val = val; |
} |
static void add_init_array_defines(TCCState *s1, const char *section_name) |
{ |
Section *s; |
long end_offset; |
char sym_start[1024]; |
char sym_end[1024]; |
snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1); |
snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1); |
s = find_section(s1, section_name); |
if (!s) { |
end_offset = 0; |
s = data_section; |
} else { |
end_offset = s->data_offset; |
} |
add_elf_sym(symtab_section, |
0, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
s->sh_num, sym_start); |
add_elf_sym(symtab_section, |
end_offset, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
s->sh_num, sym_end); |
} |
/* add tcc runtime libraries */ |
static void tcc_add_runtime(TCCState *s1) |
{ |
char buf[1024]; |
#ifdef CONFIG_TCC_BCHECK |
if (do_bounds_check) { |
unsigned long *ptr; |
Section *init_section; |
unsigned char *pinit; |
int sym_index; |
/* XXX: add an object file to do that */ |
ptr = section_ptr_add(bounds_section, sizeof(unsigned long)); |
*ptr = 0; |
add_elf_sym(symtab_section, 0, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
bounds_section->sh_num, "__bounds_start"); |
/* add bound check code */ |
snprintf(buf, sizeof(buf), "%s/%s", tcc_lib_path, "bcheck.o"); |
tcc_add_file(s1, buf); |
#ifdef TCC_TARGET_I386 |
if (s1->output_type != TCC_OUTPUT_MEMORY) { |
/* add 'call __bound_init()' in .init section */ |
init_section = find_section(s1, ".init"); |
pinit = section_ptr_add(init_section, 5); |
pinit[0] = 0xe8; |
put32(pinit + 1, -4); |
sym_index = find_elf_sym(symtab_section, "__bound_init"); |
put_elf_reloc(symtab_section, init_section, |
init_section->data_offset - 4, R_386_PC32, sym_index); |
} |
#endif |
} |
#endif |
/* add libc */ |
if (!s1->nostdlib) { |
tcc_add_library(s1, "c"); |
snprintf(buf, sizeof(buf), "%s/%s", tcc_lib_path, "libtcc1.a"); |
tcc_add_file(s1, buf); |
} |
/* add crt end if not memory output */ |
if (s1->output_type != TCC_OUTPUT_MEMORY && !s1->nostdlib) { |
tcc_add_file(s1, CONFIG_TCC_CRT_PREFIX "/crtn.o"); |
} |
} |
/* add various standard linker symbols (must be done after the |
sections are filled (for example after allocating common |
symbols)) */ |
static void tcc_add_linker_symbols(TCCState *s1) |
{ |
char buf[1024]; |
int i; |
Section *s; |
add_elf_sym(symtab_section, |
text_section->data_offset, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
text_section->sh_num, "_etext"); |
add_elf_sym(symtab_section, |
data_section->data_offset, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
data_section->sh_num, "_edata"); |
add_elf_sym(symtab_section, |
bss_section->data_offset, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
bss_section->sh_num, "_end"); |
/* horrible new standard ldscript defines */ |
add_init_array_defines(s1, ".preinit_array"); |
add_init_array_defines(s1, ".init_array"); |
add_init_array_defines(s1, ".fini_array"); |
/* add start and stop symbols for sections whose name can be |
expressed in C */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if (s->sh_type == SHT_PROGBITS && |
(s->sh_flags & SHF_ALLOC)) { |
const char *p; |
int ch; |
/* check if section name can be expressed in C */ |
p = s->name; |
for(;;) { |
ch = *p; |
if (!ch) |
break; |
if (!isid(ch) && !isnum(ch)) |
goto next_sec; |
p++; |
} |
snprintf(buf, sizeof(buf), "__start_%s", s->name); |
add_elf_sym(symtab_section, |
0, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
s->sh_num, buf); |
snprintf(buf, sizeof(buf), "__stop_%s", s->name); |
add_elf_sym(symtab_section, |
s->data_offset, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
s->sh_num, buf); |
} |
next_sec: ; |
} |
} |
/* name of ELF interpreter */ |
#ifdef __FreeBSD__ |
static char elf_interp[] = "/usr/libexec/ld-elf.so.1"; |
#else |
static char elf_interp[] = "/lib/ld-linux.so.2"; |
#endif |
static void tcc_output_binary(TCCState *s1, FILE *f, |
const int *section_order) |
{ |
Section *s; |
int i, offset, size; |
offset = 0; |
for(i=1;i<s1->nb_sections;i++) { |
s = s1->sections[section_order[i]]; |
if (s->sh_type != SHT_NOBITS && |
(s->sh_flags & SHF_ALLOC)) { |
while (offset < s->sh_offset) { |
fputc(0, f); |
offset++; |
} |
size = s->sh_size; |
fwrite(s->data, 1, size, f); |
offset += size; |
} |
} |
} |
/* output an ELF file */ |
/* XXX: suppress unneeded sections */ |
int tcc_output_file(TCCState *s1, const char *filename) |
{ |
Elf32_Ehdr ehdr; |
FILE *f; |
int fd, mode, ret; |
int *section_order; |
int shnum, i, phnum, file_offset, offset, size, j, tmp, sh_order_index, k; |
unsigned long addr; |
Section *strsec, *s; |
Elf32_Shdr shdr, *sh; |
Elf32_Phdr *phdr, *ph; |
Section *interp, *dynamic, *dynstr; |
unsigned long saved_dynamic_data_offset; |
Elf32_Sym *sym; |
int type, file_type; |
unsigned long rel_addr, rel_size; |
file_type = s1->output_type; |
s1->nb_errors = 0; |
if (file_type != TCC_OUTPUT_OBJ) { |
tcc_add_runtime(s1); |
} |
phdr = NULL; |
section_order = NULL; |
interp = NULL; |
dynamic = NULL; |
dynstr = NULL; /* avoid warning */ |
saved_dynamic_data_offset = 0; /* avoid warning */ |
if (file_type != TCC_OUTPUT_OBJ) { |
relocate_common_syms(); |
tcc_add_linker_symbols(s1); |
if (!s1->static_link) { |
const char *name; |
int sym_index, index; |
Elf32_Sym *esym, *sym_end; |
if (file_type == TCC_OUTPUT_EXE) { |
char *ptr; |
/* add interpreter section only if executable */ |
interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC); |
interp->sh_addralign = 1; |
ptr = section_ptr_add(interp, sizeof(elf_interp)); |
strcpy(ptr, elf_interp); |
} |
/* add dynamic symbol table */ |
s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC, |
".dynstr", |
".hash", SHF_ALLOC); |
dynstr = s1->dynsym->link; |
/* add dynamic section */ |
dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC, |
SHF_ALLOC | SHF_WRITE); |
dynamic->link = dynstr; |
dynamic->sh_entsize = sizeof(Elf32_Dyn); |
/* add PLT */ |
s1->plt = new_section(s1, ".plt", SHT_PROGBITS, |
SHF_ALLOC | SHF_EXECINSTR); |
s1->plt->sh_entsize = 4; |
build_got(s1); |
/* scan for undefined symbols and see if they are in the |
dynamic symbols. If a symbol STT_FUNC is found, then we |
add it in the PLT. If a symbol STT_OBJECT is found, we |
add it in the .bss section with a suitable relocation */ |
sym_end = (Elf32_Sym *)(symtab_section->data + |
symtab_section->data_offset); |
if (file_type == TCC_OUTPUT_EXE) { |
for(sym = (Elf32_Sym *)symtab_section->data + 1; |
sym < sym_end; |
sym++) { |
if (sym->st_shndx == SHN_UNDEF) { |
name = symtab_section->link->data + sym->st_name; |
sym_index = find_elf_sym(s1->dynsymtab_section, name); |
if (sym_index) { |
esym = &((Elf32_Sym *)s1->dynsymtab_section->data)[sym_index]; |
type = ELF32_ST_TYPE(esym->st_info); |
if (type == STT_FUNC) { |
put_got_entry(s1, R_JMP_SLOT, esym->st_size, |
esym->st_info, |
sym - (Elf32_Sym *)symtab_section->data); |
} else if (type == STT_OBJECT) { |
unsigned long offset; |
offset = bss_section->data_offset; |
/* XXX: which alignment ? */ |
offset = (offset + 16 - 1) & -16; |
index = put_elf_sym(s1->dynsym, offset, esym->st_size, |
esym->st_info, 0, |
bss_section->sh_num, name); |
put_elf_reloc(s1->dynsym, bss_section, |
offset, R_COPY, index); |
offset += esym->st_size; |
bss_section->data_offset = offset; |
} |
} else { |
/* STB_WEAK undefined symbols are accepted */ |
/* XXX: _fp_hw seems to be part of the ABI, so we ignore |
it */ |
if (ELF32_ST_BIND(sym->st_info) == STB_WEAK || |
!strcmp(name, "_fp_hw")) { |
} else { |
error_noabort("undefined symbol '%s'", name); |
} |
} |
} else if (s1->rdynamic && |
ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { |
/* if -rdynamic option, then export all non |
local symbols */ |
name = symtab_section->link->data + sym->st_name; |
put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, |
sym->st_info, 0, |
sym->st_shndx, name); |
} |
} |
if (s1->nb_errors) |
goto fail; |
/* now look at unresolved dynamic symbols and export |
corresponding symbol */ |
sym_end = (Elf32_Sym *)(s1->dynsymtab_section->data + |
s1->dynsymtab_section->data_offset); |
for(esym = (Elf32_Sym *)s1->dynsymtab_section->data + 1; |
esym < sym_end; |
esym++) { |
if (esym->st_shndx == SHN_UNDEF) { |
name = s1->dynsymtab_section->link->data + esym->st_name; |
sym_index = find_elf_sym(symtab_section, name); |
if (sym_index) { |
/* XXX: avoid adding a symbol if already |
present because of -rdynamic ? */ |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, |
sym->st_info, 0, |
sym->st_shndx, name); |
} else { |
if (ELF32_ST_BIND(esym->st_info) == STB_WEAK) { |
/* weak symbols can stay undefined */ |
} else { |
warning("undefined dynamic symbol '%s'", name); |
} |
} |
} |
} |
} else { |
int nb_syms; |
/* shared library case : we simply export all the global symbols */ |
nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym); |
s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms); |
for(sym = (Elf32_Sym *)symtab_section->data + 1; |
sym < sym_end; |
sym++) { |
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { |
name = symtab_section->link->data + sym->st_name; |
index = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, |
sym->st_info, 0, |
sym->st_shndx, name); |
s1->symtab_to_dynsym[sym - |
(Elf32_Sym *)symtab_section->data] = |
index; |
} |
} |
} |
build_got_entries(s1); |
/* add a list of needed dlls */ |
for(i = 0; i < s1->nb_loaded_dlls; i++) { |
DLLReference *dllref = s1->loaded_dlls[i]; |
if (dllref->level == 0) |
put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name)); |
} |
/* XXX: currently, since we do not handle PIC code, we |
must relocate the readonly segments */ |
if (file_type == TCC_OUTPUT_DLL) |
put_dt(dynamic, DT_TEXTREL, 0); |
/* add necessary space for other entries */ |
saved_dynamic_data_offset = dynamic->data_offset; |
dynamic->data_offset += 8 * 9; |
} else { |
/* still need to build got entries in case of static link */ |
build_got_entries(s1); |
} |
} |
memset(&ehdr, 0, sizeof(ehdr)); |
/* we add a section for symbols */ |
strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0); |
put_elf_str(strsec, ""); |
/* compute number of sections */ |
shnum = s1->nb_sections; |
/* this array is used to reorder sections in the output file */ |
section_order = tcc_malloc(sizeof(int) * shnum); |
section_order[0] = 0; |
sh_order_index = 1; |
/* compute number of program headers */ |
switch(file_type) { |
default: |
case TCC_OUTPUT_OBJ: |
phnum = 0; |
break; |
case TCC_OUTPUT_EXE: |
if (!s1->static_link) |
phnum = 4; |
else |
phnum = 2; |
break; |
case TCC_OUTPUT_DLL: |
phnum = 3; |
break; |
} |
/* allocate strings for section names and decide if an unallocated |
section should be output */ |
/* NOTE: the strsec section comes last, so its size is also |
correct ! */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
s->sh_name = put_elf_str(strsec, s->name); |
/* when generating a DLL, we include relocations but we may |
patch them */ |
if (file_type == TCC_OUTPUT_DLL && |
s->sh_type == SHT_REL && |
!(s->sh_flags & SHF_ALLOC)) { |
prepare_dynamic_rel(s1, s); |
} else if (do_debug || |
file_type == TCC_OUTPUT_OBJ || |
(s->sh_flags & SHF_ALLOC) || |
i == (s1->nb_sections - 1)) { |
/* we output all sections if debug or object file */ |
s->sh_size = s->data_offset; |
} |
} |
/* allocate program segment headers */ |
phdr = tcc_mallocz(phnum * sizeof(Elf32_Phdr)); |
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { |
file_offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr); |
} else { |
file_offset = 0; |
} |
if (phnum > 0) { |
/* compute section to program header mapping */ |
if (s1->has_text_addr) { |
int a_offset, p_offset; |
addr = s1->text_addr; |
/* we ensure that (addr % ELF_PAGE_SIZE) == file_offset % |
ELF_PAGE_SIZE */ |
a_offset = addr & (ELF_PAGE_SIZE - 1); |
p_offset = file_offset & (ELF_PAGE_SIZE - 1); |
if (a_offset < p_offset) |
a_offset += ELF_PAGE_SIZE; |
file_offset += (a_offset - p_offset); |
} else { |
if (file_type == TCC_OUTPUT_DLL) |
addr = 0; |
else |
addr = ELF_START_ADDR; |
/* compute address after headers */ |
addr += (file_offset & (ELF_PAGE_SIZE - 1)); |
} |
/* dynamic relocation table information, for .dynamic section */ |
rel_size = 0; |
rel_addr = 0; |
/* leave one program header for the program interpreter */ |
ph = &phdr[0]; |
if (interp) |
ph++; |
for(j = 0; j < 2; j++) { |
ph->p_type = PT_LOAD; |
if (j == 0) |
ph->p_flags = PF_R | PF_X; |
else |
ph->p_flags = PF_R | PF_W; |
ph->p_align = ELF_PAGE_SIZE; |
/* we do the following ordering: interp, symbol tables, |
relocations, progbits, nobits */ |
/* XXX: do faster and simpler sorting */ |
for(k = 0; k < 5; k++) { |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
/* compute if section should be included */ |
if (j == 0) { |
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) != |
SHF_ALLOC) |
continue; |
} else { |
if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) != |
(SHF_ALLOC | SHF_WRITE)) |
continue; |
} |
if (s == interp) { |
if (k != 0) |
continue; |
} else if (s->sh_type == SHT_DYNSYM || |
s->sh_type == SHT_STRTAB || |
s->sh_type == SHT_HASH) { |
if (k != 1) |
continue; |
} else if (s->sh_type == SHT_REL) { |
if (k != 2) |
continue; |
} else if (s->sh_type == SHT_NOBITS) { |
if (k != 4) |
continue; |
} else { |
if (k != 3) |
continue; |
} |
section_order[sh_order_index++] = i; |
/* section matches: we align it and add its size */ |
tmp = addr; |
addr = (addr + s->sh_addralign - 1) & |
~(s->sh_addralign - 1); |
file_offset += addr - tmp; |
s->sh_offset = file_offset; |
s->sh_addr = addr; |
/* update program header infos */ |
if (ph->p_offset == 0) { |
ph->p_offset = file_offset; |
ph->p_vaddr = addr; |
ph->p_paddr = ph->p_vaddr; |
} |
/* update dynamic relocation infos */ |
if (s->sh_type == SHT_REL) { |
if (rel_size == 0) |
rel_addr = addr; |
rel_size += s->sh_size; |
} |
addr += s->sh_size; |
if (s->sh_type != SHT_NOBITS) |
file_offset += s->sh_size; |
} |
} |
ph->p_filesz = file_offset - ph->p_offset; |
ph->p_memsz = addr - ph->p_vaddr; |
ph++; |
if (j == 0) { |
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { |
/* if in the middle of a page, we duplicate the page in |
memory so that one copy is RX and the other is RW */ |
if ((addr & (ELF_PAGE_SIZE - 1)) != 0) |
addr += ELF_PAGE_SIZE; |
} else { |
addr = (addr + ELF_PAGE_SIZE - 1) & ~(ELF_PAGE_SIZE - 1); |
file_offset = (file_offset + ELF_PAGE_SIZE - 1) & |
~(ELF_PAGE_SIZE - 1); |
} |
} |
} |
/* if interpreter, then add corresponing program header */ |
if (interp) { |
ph = &phdr[0]; |
ph->p_type = PT_INTERP; |
ph->p_offset = interp->sh_offset; |
ph->p_vaddr = interp->sh_addr; |
ph->p_paddr = ph->p_vaddr; |
ph->p_filesz = interp->sh_size; |
ph->p_memsz = interp->sh_size; |
ph->p_flags = PF_R; |
ph->p_align = interp->sh_addralign; |
} |
/* if dynamic section, then add corresponing program header */ |
if (dynamic) { |
Elf32_Sym *sym_end; |
ph = &phdr[phnum - 1]; |
ph->p_type = PT_DYNAMIC; |
ph->p_offset = dynamic->sh_offset; |
ph->p_vaddr = dynamic->sh_addr; |
ph->p_paddr = ph->p_vaddr; |
ph->p_filesz = dynamic->sh_size; |
ph->p_memsz = dynamic->sh_size; |
ph->p_flags = PF_R | PF_W; |
ph->p_align = dynamic->sh_addralign; |
/* put GOT dynamic section address */ |
put32(s1->got->data, dynamic->sh_addr); |
/* relocate the PLT */ |
if (file_type == TCC_OUTPUT_EXE) { |
uint8_t *p, *p_end; |
p = s1->plt->data; |
p_end = p + s1->plt->data_offset; |
if (p < p_end) { |
#if defined(TCC_TARGET_I386) |
put32(p + 2, get32(p + 2) + s1->got->sh_addr); |
put32(p + 8, get32(p + 8) + s1->got->sh_addr); |
p += 16; |
while (p < p_end) { |
put32(p + 2, get32(p + 2) + s1->got->sh_addr); |
p += 16; |
} |
#elif defined(TCC_TARGET_ARM) |
int x; |
x=s1->got->sh_addr - s1->plt->sh_addr - 12; |
p +=16; |
while (p < p_end) { |
put32(p + 12, x + get32(p + 12) + s1->plt->data - p); |
p += 16; |
} |
#elif defined(TCC_TARGET_C67) |
/* XXX: TODO */ |
#else |
#error unsupported CPU |
#endif |
} |
} |
/* relocate symbols in .dynsym */ |
sym_end = (Elf32_Sym *)(s1->dynsym->data + s1->dynsym->data_offset); |
for(sym = (Elf32_Sym *)s1->dynsym->data + 1; |
sym < sym_end; |
sym++) { |
if (sym->st_shndx == SHN_UNDEF) { |
/* relocate to the PLT if the symbol corresponds |
to a PLT entry */ |
if (sym->st_value) |
sym->st_value += s1->plt->sh_addr; |
} else if (sym->st_shndx < SHN_LORESERVE) { |
/* do symbol relocation */ |
sym->st_value += s1->sections[sym->st_shndx]->sh_addr; |
} |
} |
/* put dynamic section entries */ |
dynamic->data_offset = saved_dynamic_data_offset; |
put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr); |
put_dt(dynamic, DT_STRTAB, dynstr->sh_addr); |
put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr); |
put_dt(dynamic, DT_STRSZ, dynstr->data_offset); |
put_dt(dynamic, DT_SYMENT, sizeof(Elf32_Sym)); |
put_dt(dynamic, DT_REL, rel_addr); |
put_dt(dynamic, DT_RELSZ, rel_size); |
put_dt(dynamic, DT_RELENT, sizeof(Elf32_Rel)); |
put_dt(dynamic, DT_NULL, 0); |
} |
ehdr.e_phentsize = sizeof(Elf32_Phdr); |
ehdr.e_phnum = phnum; |
ehdr.e_phoff = sizeof(Elf32_Ehdr); |
} |
/* all other sections come after */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if (phnum > 0 && (s->sh_flags & SHF_ALLOC)) |
continue; |
section_order[sh_order_index++] = i; |
file_offset = (file_offset + s->sh_addralign - 1) & |
~(s->sh_addralign - 1); |
s->sh_offset = file_offset; |
if (s->sh_type != SHT_NOBITS) |
file_offset += s->sh_size; |
} |
/* if building executable or DLL, then relocate each section |
except the GOT which is already relocated */ |
if (file_type != TCC_OUTPUT_OBJ) { |
relocate_syms(s1, 0); |
if (s1->nb_errors != 0) { |
fail: |
ret = -1; |
goto the_end; |
} |
/* relocate sections */ |
/* XXX: ignore sections with allocated relocations ? */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if (s->reloc && s != s1->got) |
relocate_section(s1, s); |
} |
/* relocate relocation entries if the relocation tables are |
allocated in the executable */ |
for(i = 1; i < s1->nb_sections; i++) { |
s = s1->sections[i]; |
if ((s->sh_flags & SHF_ALLOC) && |
s->sh_type == SHT_REL) { |
relocate_rel(s1, s); |
} |
} |
/* get entry point address */ |
if (file_type == TCC_OUTPUT_EXE) |
ehdr.e_entry = (unsigned long)tcc_get_symbol_err(s1, "_start"); |
else |
ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */ |
} |
/* write elf file */ |
if (file_type == TCC_OUTPUT_OBJ) |
mode = 0666; |
else |
mode = 0777; |
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); |
if (fd < 0) { |
error_noabort("could not write '%s'", filename); |
goto fail; |
} |
f = fdopen(fd, "wb"); |
#ifdef TCC_TARGET_COFF |
if (s1->output_format == TCC_OUTPUT_FORMAT_COFF) { |
tcc_output_coff(s1, f); |
} else |
#endif |
if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { |
sort_syms(s1, symtab_section); |
/* align to 4 */ |
file_offset = (file_offset + 3) & -4; |
/* fill header */ |
ehdr.e_ident[0] = ELFMAG0; |
ehdr.e_ident[1] = ELFMAG1; |
ehdr.e_ident[2] = ELFMAG2; |
ehdr.e_ident[3] = ELFMAG3; |
ehdr.e_ident[4] = ELFCLASS32; |
ehdr.e_ident[5] = ELFDATA2LSB; |
ehdr.e_ident[6] = EV_CURRENT; |
#ifdef __FreeBSD__ |
ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; |
#endif |
#ifdef TCC_TARGET_ARM |
ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM; |
#endif |
switch(file_type) { |
default: |
case TCC_OUTPUT_EXE: |
ehdr.e_type = ET_EXEC; |
break; |
case TCC_OUTPUT_DLL: |
ehdr.e_type = ET_DYN; |
break; |
case TCC_OUTPUT_OBJ: |
ehdr.e_type = ET_REL; |
break; |
} |
ehdr.e_machine = EM_TCC_TARGET; |
ehdr.e_version = EV_CURRENT; |
ehdr.e_shoff = file_offset; |
ehdr.e_ehsize = sizeof(Elf32_Ehdr); |
ehdr.e_shentsize = sizeof(Elf32_Shdr); |
ehdr.e_shnum = shnum; |
ehdr.e_shstrndx = shnum - 1; |
fwrite(&ehdr, 1, sizeof(Elf32_Ehdr), f); |
fwrite(phdr, 1, phnum * sizeof(Elf32_Phdr), f); |
offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr); |
for(i=1;i<s1->nb_sections;i++) { |
s = s1->sections[section_order[i]]; |
if (s->sh_type != SHT_NOBITS) { |
while (offset < s->sh_offset) { |
fputc(0, f); |
offset++; |
} |
size = s->sh_size; |
fwrite(s->data, 1, size, f); |
offset += size; |
} |
} |
/* output section headers */ |
while (offset < ehdr.e_shoff) { |
fputc(0, f); |
offset++; |
} |
for(i=0;i<s1->nb_sections;i++) { |
sh = &shdr; |
memset(sh, 0, sizeof(Elf32_Shdr)); |
s = s1->sections[i]; |
if (s) { |
sh->sh_name = s->sh_name; |
sh->sh_type = s->sh_type; |
sh->sh_flags = s->sh_flags; |
sh->sh_entsize = s->sh_entsize; |
sh->sh_info = s->sh_info; |
if (s->link) |
sh->sh_link = s->link->sh_num; |
sh->sh_addralign = s->sh_addralign; |
sh->sh_addr = s->sh_addr; |
sh->sh_offset = s->sh_offset; |
sh->sh_size = s->sh_size; |
} |
fwrite(sh, 1, sizeof(Elf32_Shdr), f); |
} |
} else { |
tcc_output_binary(s1, f, section_order); |
} |
fclose(f); |
ret = 0; |
the_end: |
tcc_free(s1->symtab_to_dynsym); |
tcc_free(section_order); |
tcc_free(phdr); |
tcc_free(s1->got_offsets); |
return ret; |
} |
static void *load_data(int fd, unsigned long file_offset, unsigned long size) |
{ |
void *data; |
data = tcc_malloc(size); |
lseek(fd, file_offset, SEEK_SET); |
read(fd, data, size); |
return data; |
} |
typedef struct SectionMergeInfo { |
Section *s; /* corresponding existing section */ |
unsigned long offset; /* offset of the new section in the existing section */ |
uint8_t new_section; /* true if section 's' was added */ |
uint8_t link_once; /* true if link once section */ |
} SectionMergeInfo; |
/* load an object file and merge it with current files */ |
/* XXX: handle correctly stab (debug) info */ |
static int tcc_load_object_file(TCCState *s1, |
int fd, unsigned long file_offset) |
{ |
Elf32_Ehdr ehdr; |
Elf32_Shdr *shdr, *sh; |
int size, i, j, offset, offseti, nb_syms, sym_index, ret; |
unsigned char *strsec, *strtab; |
int *old_to_new_syms; |
char *sh_name, *name; |
SectionMergeInfo *sm_table, *sm; |
Elf32_Sym *sym, *symtab; |
Elf32_Rel *rel, *rel_end; |
Section *s; |
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) |
goto fail1; |
if (ehdr.e_ident[0] != ELFMAG0 || |
ehdr.e_ident[1] != ELFMAG1 || |
ehdr.e_ident[2] != ELFMAG2 || |
ehdr.e_ident[3] != ELFMAG3) |
goto fail1; |
/* test if object file */ |
if (ehdr.e_type != ET_REL) |
goto fail1; |
/* test CPU specific stuff */ |
if (ehdr.e_ident[5] != ELFDATA2LSB || |
ehdr.e_machine != EM_TCC_TARGET) { |
fail1: |
error_noabort("invalid object file"); |
return -1; |
} |
/* read sections */ |
shdr = load_data(fd, file_offset + ehdr.e_shoff, |
sizeof(Elf32_Shdr) * ehdr.e_shnum); |
sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum); |
/* load section names */ |
sh = &shdr[ehdr.e_shstrndx]; |
strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size); |
/* load symtab and strtab */ |
old_to_new_syms = NULL; |
symtab = NULL; |
strtab = NULL; |
nb_syms = 0; |
for(i = 1; i < ehdr.e_shnum; i++) { |
sh = &shdr[i]; |
if (sh->sh_type == SHT_SYMTAB) { |
if (symtab) { |
error_noabort("object must contain only one symtab"); |
fail: |
ret = -1; |
goto the_end; |
} |
nb_syms = sh->sh_size / sizeof(Elf32_Sym); |
symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size); |
sm_table[i].s = symtab_section; |
/* now load strtab */ |
sh = &shdr[sh->sh_link]; |
strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size); |
} |
} |
/* now examine each section and try to merge its content with the |
ones in memory */ |
for(i = 1; i < ehdr.e_shnum; i++) { |
/* no need to examine section name strtab */ |
if (i == ehdr.e_shstrndx) |
continue; |
sh = &shdr[i]; |
sh_name = strsec + sh->sh_name; |
/* ignore sections types we do not handle */ |
if (sh->sh_type != SHT_PROGBITS && |
sh->sh_type != SHT_REL && |
sh->sh_type != SHT_NOBITS) |
continue; |
if (sh->sh_addralign < 1) |
sh->sh_addralign = 1; |
/* find corresponding section, if any */ |
for(j = 1; j < s1->nb_sections;j++) { |
s = s1->sections[j]; |
if (!strcmp(s->name, sh_name)) { |
if (!strncmp(sh_name, ".gnu.linkonce", |
sizeof(".gnu.linkonce") - 1)) { |
/* if a 'linkonce' section is already present, we |
do not add it again. It is a little tricky as |
symbols can still be defined in |
it. */ |
sm_table[i].link_once = 1; |
goto next; |
} else { |
goto found; |
} |
} |
} |
/* not found: create new section */ |
s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags); |
/* take as much info as possible from the section. sh_link and |
sh_info will be updated later */ |
s->sh_addralign = sh->sh_addralign; |
s->sh_entsize = sh->sh_entsize; |
sm_table[i].new_section = 1; |
found: |
if (sh->sh_type != s->sh_type) { |
error_noabort("invalid section type"); |
goto fail; |
} |
/* align start of section */ |
offset = s->data_offset; |
size = sh->sh_addralign - 1; |
offset = (offset + size) & ~size; |
if (sh->sh_addralign > s->sh_addralign) |
s->sh_addralign = sh->sh_addralign; |
s->data_offset = offset; |
sm_table[i].offset = offset; |
sm_table[i].s = s; |
/* concatenate sections */ |
size = sh->sh_size; |
if (sh->sh_type != SHT_NOBITS) { |
unsigned char *ptr; |
lseek(fd, file_offset + sh->sh_offset, SEEK_SET); |
ptr = section_ptr_add(s, size); |
read(fd, ptr, size); |
} else { |
s->data_offset += size; |
} |
next: ; |
} |
/* second short pass to update sh_link and sh_info fields of new |
sections */ |
sm = sm_table; |
for(i = 1; i < ehdr.e_shnum; i++) { |
s = sm_table[i].s; |
if (!s || !sm_table[i].new_section) |
continue; |
sh = &shdr[i]; |
if (sh->sh_link > 0) |
s->link = sm_table[sh->sh_link].s; |
if (sh->sh_type == SHT_REL) { |
s->sh_info = sm_table[sh->sh_info].s->sh_num; |
/* update backward link */ |
s1->sections[s->sh_info]->reloc = s; |
} |
} |
/* resolve symbols */ |
old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int)); |
sym = symtab + 1; |
for(i = 1; i < nb_syms; i++, sym++) { |
if (sym->st_shndx != SHN_UNDEF && |
sym->st_shndx < SHN_LORESERVE) { |
sm = &sm_table[sym->st_shndx]; |
if (sm->link_once) { |
/* if a symbol is in a link once section, we use the |
already defined symbol. It is very important to get |
correct relocations */ |
if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { |
name = strtab + sym->st_name; |
sym_index = find_elf_sym(symtab_section, name); |
if (sym_index) |
old_to_new_syms[i] = sym_index; |
} |
continue; |
} |
/* if no corresponding section added, no need to add symbol */ |
if (!sm->s) |
continue; |
/* convert section number */ |
sym->st_shndx = sm->s->sh_num; |
/* offset value */ |
sym->st_value += sm->offset; |
} |
/* add symbol */ |
name = strtab + sym->st_name; |
sym_index = add_elf_sym(symtab_section, sym->st_value, sym->st_size, |
sym->st_info, sym->st_other, |
sym->st_shndx, name); |
old_to_new_syms[i] = sym_index; |
} |
/* third pass to patch relocation entries */ |
for(i = 1; i < ehdr.e_shnum; i++) { |
s = sm_table[i].s; |
if (!s) |
continue; |
sh = &shdr[i]; |
offset = sm_table[i].offset; |
switch(s->sh_type) { |
case SHT_REL: |
/* take relocation offset information */ |
offseti = sm_table[sh->sh_info].offset; |
rel_end = (Elf32_Rel *)(s->data + s->data_offset); |
for(rel = (Elf32_Rel *)(s->data + offset); |
rel < rel_end; |
rel++) { |
int type; |
unsigned sym_index; |
/* convert symbol index */ |
type = ELF32_R_TYPE(rel->r_info); |
sym_index = ELF32_R_SYM(rel->r_info); |
/* NOTE: only one symtab assumed */ |
if (sym_index >= nb_syms) |
goto invalid_reloc; |
sym_index = old_to_new_syms[sym_index]; |
if (!sym_index) { |
invalid_reloc: |
error_noabort("Invalid relocation entry"); |
goto fail; |
} |
rel->r_info = ELF32_R_INFO(sym_index, type); |
/* offset the relocation offset */ |
rel->r_offset += offseti; |
} |
break; |
default: |
break; |
} |
} |
ret = 0; |
the_end: |
tcc_free(symtab); |
tcc_free(strtab); |
tcc_free(old_to_new_syms); |
tcc_free(sm_table); |
tcc_free(strsec); |
tcc_free(shdr); |
return ret; |
} |
#define ARMAG "!<arch>\012" /* For COFF and a.out archives */ |
typedef struct ArchiveHeader { |
char ar_name[16]; /* name of this member */ |
char ar_date[12]; /* file mtime */ |
char ar_uid[6]; /* owner uid; printed as decimal */ |
char ar_gid[6]; /* owner gid; printed as decimal */ |
char ar_mode[8]; /* file mode, printed as octal */ |
char ar_size[10]; /* file size, printed as decimal */ |
char ar_fmag[2]; /* should contain ARFMAG */ |
} ArchiveHeader; |
static int get_be32(const uint8_t *b) |
{ |
return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24); |
} |
/* load only the objects which resolve undefined symbols */ |
static int tcc_load_alacarte(TCCState *s1, int fd, int size) |
{ |
int i, bound, nsyms, sym_index, off, ret; |
uint8_t *data; |
const char *ar_names, *p; |
const uint8_t *ar_index; |
Elf32_Sym *sym; |
data = tcc_malloc(size); |
if (read(fd, data, size) != size) |
goto fail; |
nsyms = get_be32(data); |
ar_index = data + 4; |
ar_names = ar_index + nsyms * 4; |
do { |
bound = 0; |
for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) { |
sym_index = find_elf_sym(symtab_section, p); |
if(sym_index) { |
sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; |
if(sym->st_shndx == SHN_UNDEF) { |
off = get_be32(ar_index + i * 4) + sizeof(ArchiveHeader); |
#if 0 |
printf("%5d\t%s\t%08x\n", i, p, sym->st_shndx); |
#endif |
++bound; |
lseek(fd, off, SEEK_SET); |
if(tcc_load_object_file(s1, fd, off) < 0) { |
fail: |
ret = -1; |
goto the_end; |
} |
} |
} |
} |
} while(bound); |
ret = 0; |
the_end: |
tcc_free(data); |
return ret; |
} |
/* load a '.a' file */ |
static int tcc_load_archive(TCCState *s1, int fd) |
{ |
ArchiveHeader hdr; |
char ar_size[11]; |
char ar_name[17]; |
char magic[8]; |
int size, len, i; |
unsigned long file_offset; |
/* skip magic which was already checked */ |
read(fd, magic, sizeof(magic)); |
for(;;) { |
len = read(fd, &hdr, sizeof(hdr)); |
if (len == 0) |
break; |
if (len != sizeof(hdr)) { |
error_noabort("invalid archive"); |
return -1; |
} |
memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size)); |
ar_size[sizeof(hdr.ar_size)] = '\0'; |
size = strtol(ar_size, NULL, 0); |
memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name)); |
for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) { |
if (ar_name[i] != ' ') |
break; |
} |
ar_name[i + 1] = '\0'; |
// printf("name='%s' size=%d %s\n", ar_name, size, ar_size); |
file_offset = lseek(fd, 0, SEEK_CUR); |
/* align to even */ |
size = (size + 1) & ~1; |
if (!strcmp(ar_name, "/")) { |
/* coff symbol table : we handle it */ |
if(s1->alacarte_link) |
return tcc_load_alacarte(s1, fd, size); |
} else if (!strcmp(ar_name, "//") || |
!strcmp(ar_name, "__.SYMDEF") || |
!strcmp(ar_name, "__.SYMDEF/") || |
!strcmp(ar_name, "ARFILENAMES/")) { |
/* skip symbol table or archive names */ |
} else { |
if (tcc_load_object_file(s1, fd, file_offset) < 0) |
return -1; |
} |
lseek(fd, file_offset + size, SEEK_SET); |
} |
return 0; |
} |
/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL |
is referenced by the user (so it should be added as DT_NEEDED in |
the generated ELF file) */ |
static int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) |
{ |
Elf32_Ehdr ehdr; |
Elf32_Shdr *shdr, *sh, *sh1; |
int i, nb_syms, nb_dts, sym_bind, ret; |
Elf32_Sym *sym, *dynsym; |
Elf32_Dyn *dt, *dynamic; |
unsigned char *dynstr; |
const char *name, *soname, *p; |
DLLReference *dllref; |
read(fd, &ehdr, sizeof(ehdr)); |
/* test CPU specific stuff */ |
if (ehdr.e_ident[5] != ELFDATA2LSB || |
ehdr.e_machine != EM_TCC_TARGET) { |
error_noabort("bad architecture"); |
return -1; |
} |
/* read sections */ |
shdr = load_data(fd, ehdr.e_shoff, sizeof(Elf32_Shdr) * ehdr.e_shnum); |
/* load dynamic section and dynamic symbols */ |
nb_syms = 0; |
nb_dts = 0; |
dynamic = NULL; |
dynsym = NULL; /* avoid warning */ |
dynstr = NULL; /* avoid warning */ |
for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) { |
switch(sh->sh_type) { |
case SHT_DYNAMIC: |
nb_dts = sh->sh_size / sizeof(Elf32_Dyn); |
dynamic = load_data(fd, sh->sh_offset, sh->sh_size); |
break; |
case SHT_DYNSYM: |
nb_syms = sh->sh_size / sizeof(Elf32_Sym); |
dynsym = load_data(fd, sh->sh_offset, sh->sh_size); |
sh1 = &shdr[sh->sh_link]; |
dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size); |
break; |
default: |
break; |
} |
} |
/* compute the real library name */ |
soname = filename; |
p = strrchr(soname, '/'); |
if (p) |
soname = p + 1; |
for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) { |
if (dt->d_tag == DT_SONAME) { |
soname = dynstr + dt->d_un.d_val; |
} |
} |
/* if the dll is already loaded, do not load it */ |
for(i = 0; i < s1->nb_loaded_dlls; i++) { |
dllref = s1->loaded_dlls[i]; |
if (!strcmp(soname, dllref->name)) { |
/* but update level if needed */ |
if (level < dllref->level) |
dllref->level = level; |
ret = 0; |
goto the_end; |
} |
} |
// printf("loading dll '%s'\n", soname); |
/* add the dll and its level */ |
dllref = tcc_malloc(sizeof(DLLReference) + strlen(soname)); |
dllref->level = level; |
strcpy(dllref->name, soname); |
dynarray_add((void ***)&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); |
/* add dynamic symbols in dynsym_section */ |
for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) { |
sym_bind = ELF32_ST_BIND(sym->st_info); |
if (sym_bind == STB_LOCAL) |
continue; |
name = dynstr + sym->st_name; |
add_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size, |
sym->st_info, sym->st_other, sym->st_shndx, name); |
} |
/* load all referenced DLLs */ |
for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) { |
switch(dt->d_tag) { |
case DT_NEEDED: |
name = dynstr + dt->d_un.d_val; |
for(i = 0; i < s1->nb_loaded_dlls; i++) { |
dllref = s1->loaded_dlls[i]; |
if (!strcmp(name, dllref->name)) |
goto already_loaded; |
} |
if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) { |
error_noabort("referenced dll '%s' not found", name); |
ret = -1; |
goto the_end; |
} |
already_loaded: |
break; |
} |
} |
ret = 0; |
the_end: |
tcc_free(dynstr); |
tcc_free(dynsym); |
tcc_free(dynamic); |
tcc_free(shdr); |
return ret; |
} |
#define LD_TOK_NAME 256 |
#define LD_TOK_EOF (-1) |
/* return next ld script token */ |
static int ld_next(TCCState *s1, char *name, int name_size) |
{ |
int c; |
char *q; |
redo: |
switch(ch) { |
case ' ': |
case '\t': |
case '\f': |
case '\v': |
case '\r': |
case '\n': |
input(); |
goto redo; |
case '/': |
minp(); |
if (ch == '*') { |
file->buf_ptr = parse_comment(file->buf_ptr); |
ch = file->buf_ptr[0]; |
goto redo; |
} else { |
q = name; |
*q++ = '/'; |
goto parse_name; |
} |
break; |
case 'a' ... 'z': |
case 'A' ... 'Z': |
case '_': |
case '\\': |
case '.': |
case '$': |
case '~': |
q = name; |
parse_name: |
for(;;) { |
if (!((ch >= 'a' && ch <= 'z') || |
(ch >= 'A' && ch <= 'Z') || |
(ch >= '0' && ch <= '9') || |
strchr("/.-_+=$:\\,~", ch))) |
break; |
if ((q - name) < name_size - 1) { |
*q++ = ch; |
} |
minp(); |
} |
*q = '\0'; |
c = LD_TOK_NAME; |
break; |
case CH_EOF: |
c = LD_TOK_EOF; |
break; |
default: |
c = ch; |
input(); |
break; |
} |
#if 0 |
printf("tok=%c %d\n", c, c); |
if (c == LD_TOK_NAME) |
printf(" name=%s\n", name); |
#endif |
return c; |
} |
/* interpret a subset of GNU ldscripts to handle the dummy libc.so |
files */ |
static int tcc_load_ldscript(TCCState *s1) |
{ |
char cmd[64]; |
char filename[1024]; |
int t; |
ch = file->buf_ptr[0]; |
ch = handle_eob(); |
for(;;) { |
t = ld_next(s1, cmd, sizeof(cmd)); |
if (t == LD_TOK_EOF) |
return 0; |
else if (t != LD_TOK_NAME) |
return -1; |
if (!strcmp(cmd, "INPUT") || |
!strcmp(cmd, "GROUP")) { |
t = ld_next(s1, cmd, sizeof(cmd)); |
if (t != '(') |
expect("("); |
t = ld_next(s1, filename, sizeof(filename)); |
for(;;) { |
if (t == LD_TOK_EOF) { |
error_noabort("unexpected end of file"); |
return -1; |
} else if (t == ')') { |
break; |
} else if (t != LD_TOK_NAME) { |
error_noabort("filename expected"); |
return -1; |
} |
tcc_add_file(s1, filename); |
t = ld_next(s1, filename, sizeof(filename)); |
if (t == ',') { |
t = ld_next(s1, filename, sizeof(filename)); |
} |
} |
} else if (!strcmp(cmd, "OUTPUT_FORMAT") || |
!strcmp(cmd, "TARGET")) { |
/* ignore some commands */ |
t = ld_next(s1, cmd, sizeof(cmd)); |
if (t != '(') |
expect("("); |
for(;;) { |
t = ld_next(s1, filename, sizeof(filename)); |
if (t == LD_TOK_EOF) { |
error_noabort("unexpected end of file"); |
return -1; |
} else if (t == ')') { |
break; |
} |
} |
} else { |
return -1; |
} |
} |
return 0; |
} |
/programs/develop/ktcc/trunk/source/tccmeos.c |
---|
0,0 → 1,248 |
/* |
* TCCMEOS.C - KolibriOS/MenuetOS file output for the TinyC Compiler |
* |
* Copyright (c) 2006 Andrey Khalyavin |
* |
* 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 |
*/ |
typedef struct { |
char magic[8]; |
int version; |
int entry_point; |
int image_size; |
int memory_size; |
int stack; |
int params; |
int argv; |
} IMAGE_MEOS_FILE_HEADER,*PIMAGE_MEOS_FILE_HEADER; |
typedef struct _meos_section_info{ |
int sh_addr; |
void* data; |
int data_size; |
int sec_num; |
struct _meos_section_info* next; |
} meos_section_info; |
typedef struct { |
TCCState* s1; |
IMAGE_MEOS_FILE_HEADER header; |
meos_section_info* code_sections; |
meos_section_info* data_sections; |
meos_section_info* bss_sections; |
} me_info; |
meos_section_info* findsection(me_info* me,int num) |
{ |
meos_section_info* si; |
for(si=me->code_sections;si;si=si->next) |
{ |
if (si->sec_num==num) |
return si; |
} |
for (si=me->data_sections;si;si=si->next) |
{ |
if (si->sec_num==num) |
return si; |
} |
for (si=me->bss_sections;si;si=si->next) |
{ |
if (si->sec_num==num) |
return si; |
} |
return (meos_section_info*)0; |
} |
void build_reloc(me_info* me) |
{ |
int flag; |
Elf32_Rel *rel, *rel_, *rel_end; |
Section *sr; |
meos_section_info* s; |
meos_section_info* ss; |
s=me->code_sections; |
rel=0; |
rel_end=0; |
flag=0; |
for(;;) |
{ |
sr=me->s1->sections[s->sec_num]->reloc; |
if (sr) |
{ |
rel = (Elf32_Rel *) sr->data; |
rel_end = (Elf32_Rel *) (sr->data + sr->data_offset); |
} |
rel_=rel; |
while (rel_<rel_end){ |
rel=rel_; |
int type = ELF32_R_TYPE(rel->r_info); |
rel_=rel+1; |
if (type != R_386_PC32 && type != R_386_32) |
continue; |
int sym = ELF32_R_SYM(rel->r_info); |
if (sym>symtab_section->data_offset/sizeof(Elf32_Sym)) |
continue; |
Elf32_Sym* esym = ((Elf32_Sym *)symtab_section->data)+sym; |
int sect=esym->st_shndx; |
ss=findsection(me,sect); |
if (ss==0) continue; |
if (rel->r_offset>s->data_size) |
continue; |
if (type==R_386_PC32) |
*(int*)(rel->r_offset+s->data)+=ss->sh_addr+esym->st_value-rel->r_offset-s->sh_addr; |
else if (type==R_386_32) |
*(int*)(rel->r_offset+s->data)+=ss->sh_addr+esym->st_value; |
} |
rel=rel_; |
s=s->next; |
if (s==0) |
{ |
if (flag) break; |
s=me->data_sections; |
if (s==0) break; |
flag=1; |
continue; |
} |
} |
} |
void assign_addresses(me_info* me) |
{ |
int i; |
meos_section_info* si; |
for (i=1;i<me->s1->nb_sections;i++) |
{ |
Section* s=me->s1->sections[i]; |
if (strcmp(".text",s->name)==0) |
{ |
si=tcc_malloc(sizeof(meos_section_info)); |
si->data=s->data; |
si->data_size=s->data_offset; |
si->next=me->code_sections; |
si->sec_num=i; |
me->code_sections=si; |
continue; |
} |
if (strcmp(".data",s->name)==0) |
{ |
si=tcc_malloc(sizeof(meos_section_info)); |
si->data=s->data; |
si->data_size=s->data_offset; |
si->next=me->data_sections; |
si->sec_num=i; |
me->data_sections=si; |
continue; |
} |
if (strcmp(".bss",s->name)==0) |
{ |
si=tcc_malloc(sizeof(meos_section_info)); |
si->data_size=s->data_offset; |
si->next=me->bss_sections; |
si->sec_num=i; |
me->bss_sections=si; |
continue; |
} |
} |
int addr; |
addr=sizeof(IMAGE_MEOS_FILE_HEADER); |
for (si=me->code_sections;si;si=si->next) |
{ |
si->sh_addr=addr; |
addr+=si->data_size; |
} |
for (si=me->data_sections;si;si=si->next) |
{ |
si->sh_addr=addr; |
addr+=si->data_size; |
} |
me->header.image_size=addr; |
for (si=me->bss_sections;si;si=si->next) |
{ |
si->sh_addr=addr; |
addr+=si->data_size; |
} |
addr+=4096; |
addr=(addr+4)&(~3); |
me->header.stack=addr; |
me->header.memory_size=addr; |
build_reloc(me); |
} |
int tcc_find_symbol_me(me_info* me, const char *sym_name) |
{ |
int i; |
int symtab; |
int strtab; |
symtab=0; |
strtab=0; |
for (i=1;i<me->s1->nb_sections;i++) |
{ |
Section* s; |
s=me->s1->sections[i]; |
if (strcmp(s->name,".symtab")==0) |
{ |
symtab=i; |
} |
if (strcmp(s->name,".strtab")==0) |
{ |
strtab=i; |
} |
} |
if (symtab==0 || strtab==0) |
return 0; |
Elf32_Sym* s,*se; |
char* name; |
s=(Elf32_Sym*)me->s1->sections[symtab]->data; |
se=(Elf32_Sym*)(((void*)s)+me->s1->sections[symtab]->data_offset); |
name=(char*)me->s1->sections[strtab]->data; |
while (s<se) |
{ |
if (strcmp(name+s->st_name,sym_name)==0) |
{ |
return s->st_value+findsection(me,s->st_shndx)->sh_addr; |
} |
s++; |
} |
return 0; |
} |
const char* me_magic="MENUET01"; |
int tcc_output_me(TCCState* s1,const char *filename) |
{ |
me_info me; |
int i; |
FILE* f; |
//printf("%d\n",s1->nb_sections); |
memset(&me,0,sizeof(me)); |
me.s1=s1; |
relocate_common_syms(); |
assign_addresses(&me); |
me.header.entry_point=tcc_find_symbol_me(&me,"start"); |
me.header.params= tcc_find_symbol_me(&me,"__argv"); // <-- |
me.header.argv= tcc_find_symbol_me(&me,"__path"); // <-- |
f=fopen(filename,"wb"); |
for (i=0;i<8;i++) |
me.header.magic[i]=me_magic[i]; |
/*me.header.magic[0]='M';me.header.magic[1]='E'; |
me.header.magic[2]='N';me.header.magic[3]='U'; |
me.header.magic[4]='E';me.header.magic[5]='T'; |
me.header.magic[6]='0';me.header.magic[7]='1';*/ |
fwrite(&me.header,1,sizeof(IMAGE_MEOS_FILE_HEADER),f); |
meos_section_info* si; |
for(si=me.code_sections;si;si=si->next) |
fwrite(si->data,1,si->data_size,f); |
for (si=me.data_sections;si;si=si->next) |
fwrite(si->data,1,si->data_size,f); |
fclose(f); |
return 0; |
} |
/programs/develop/ktcc/trunk/source/tccpe.c |
---|
0,0 → 1,1244 |
/* |
* TCCPE.C - PE file output for the TinyC Compiler |
* |
* Copyright (c) 2005 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 |
*/ |
typedef unsigned char BYTE; |
typedef unsigned short WORD; |
typedef unsigned long DWORD; |
#define ST static |
/* XXX: move that to TCC ? */ |
int verbose = 0; |
/* definitions below are from winnt.h */ |
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 */ |
BYTE e_code[0x40]; |
} 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; |
DWORD BaseOfData; |
/* NT additional fields. */ |
DWORD 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; |
DWORD SizeOfStackReserve; |
DWORD SizeOfStackCommit; |
DWORD SizeOfHeapReserve; |
DWORD SizeOfHeapCommit; |
DWORD LoaderFlags; |
DWORD NumberOfRvaAndSizes; |
IMAGE_DATA_DIRECTORY DataDirectory[16]; |
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; |
#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_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 |
/* ----------------------------------------------------------- */ |
/* ----------------------------------------------------------- */ |
IMAGE_DOS_HEADER pe_dos_hdr = { |
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 */ |
{ /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */ |
/*0040 */ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, |
0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, |
/*0050 */ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, |
0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, |
/*0060 */ 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, |
0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, |
/*0070 */ 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
/*0080 */ |
} |
}; |
DWORD pe_magic = IMAGE_NT_SIGNATURE; |
IMAGE_FILE_HEADER pe_file_hdr = { |
0x014C, /*WORD Machine; */ |
0x0003, /*WORD NumberOfSections; */ |
0x00000000, /*DWORD TimeDateStamp; */ |
0x00000000, /*DWORD PointerToSymbolTable; */ |
0x00000000, /*DWORD NumberOfSymbols; */ |
0x00E0, /*WORD SizeOfOptionalHeader; */ |
0x030F /*WORD Characteristics; */ |
}; |
IMAGE_OPTIONAL_HEADER32 pe_opt_hdr = { |
/* Standard fields. */ |
0x010B, /*WORD Magic; */ |
0x06, /*BYTE MajorLinkerVersion; */ |
0x00, /*BYTE MinorLinkerVersion; */ |
0x00000000, /*DWORD SizeOfCode; */ |
0x00000000, /*DWORD SizeOfInitializedData; */ |
0x00000000, /*DWORD SizeOfUninitializedData; */ |
0x00000000, /*DWORD AddressOfEntryPoint; */ |
0x00000000, /*DWORD BaseOfCode; */ |
0x00000000, /*DWORD BaseOfData; */ |
/* NT additional fields. */ |
0x00400000, /*DWORD ImageBase; */ |
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_import_header { |
DWORD first_entry; |
DWORD time_date; |
DWORD forwarder; |
DWORD lib_name_offset; |
DWORD first_thunk; |
}; |
struct pe_export_header { |
DWORD Characteristics; |
DWORD TimeDateStamp; |
DWORD Version; |
DWORD Name; |
DWORD Base; |
DWORD NumberOfFunctions; |
DWORD NumberOfNames; |
DWORD AddressOfFunctions; |
DWORD AddressOfNames; |
DWORD AddressOfNameOrdinals; |
}; |
struct pe_reloc_header { |
DWORD offset; |
DWORD size; |
}; |
/* ------------------------------------------------------------- */ |
/* internal temporary structures */ |
ST const char *pe_sec_names[] = { |
".text", |
".data", |
".bss", |
".rsrc", |
".reloc", |
".stab", |
".stabstr" |
}; |
enum { |
sec_text = 0, |
sec_data, |
sec_bss, |
sec_rsrc, |
sec_reloc, |
sec_stab, |
sec_stabstr, |
pe_sec_number |
}; |
ST DWORD pe_flags[] = { |
0x60000020, /* ".text", */ |
0xC0000040, /* ".data", */ |
0xC0000080, /* ".bss", */ |
0x40000040, /* ".rsrc", */ |
0x42000040, /* ".reloc", */ |
0x42000802, /* ".stab", */ |
0x42000802 /* ".stabstr", */ |
}; |
struct section_info { |
struct section_info *next; |
int id; |
DWORD sh_addr; |
DWORD sh_size; |
unsigned char *data; |
DWORD data_size; |
}; |
struct import_symbol { |
int sym_index; |
int offset; |
}; |
struct pe_import_info { |
int dll_index; |
int sym_count; |
struct import_symbol **symbols; |
}; |
struct pe_info { |
const char *filename; |
DWORD sizeofheaders; |
DWORD imagebase; |
DWORD start_addr; |
DWORD imp_offs; |
DWORD imp_size; |
DWORD iat_offs; |
DWORD iat_size; |
DWORD exp_offs; |
DWORD exp_size; |
struct section_info sh_info[pe_sec_number]; |
int sec_count; |
struct pe_import_info **imp_info; |
int imp_count; |
Section *reloc; |
Section *thunk; |
TCCState *s1; |
}; |
/* ------------------------------------------------------------- */ |
#define PE_MERGE_DATA |
// #define PE_PRINT_SECTIONS |
#ifndef MAX_PATH |
#define MAX_PATH 260 |
#endif |
void error_noabort(const char *, ...); |
ST char pe_type; |
#define PE_NUL 0 |
#define PE_DLL 1 |
#define PE_GUI 2 |
#define PE_EXE 3 |
ST int pe_find_import(TCCState * s1, const char *symbol, char *ret) |
{ |
int sym_index = find_elf_sym(s1->dynsymtab_section, symbol); |
if (0 == sym_index) { |
/* Hm, maybe it's '_symbol' instead of 'symbol' or '__imp__symbol' */ |
char buffer[100]; |
if (0 == memcmp(symbol, "__imp__", 7)) |
symbol += 6; |
else |
buffer[0] = '_', strcpy(buffer + 1, symbol), symbol = buffer; |
sym_index = find_elf_sym(s1->dynsymtab_section, symbol); |
} |
if (ret) |
strcpy(ret, symbol); |
return sym_index; |
} |
#ifdef WIN32 |
ST void **pe_imp; |
ST int nb_pe_imp; |
void *resolve_sym(struct TCCState *s1, const char *symbol, int type) |
{ |
char buffer[100], *p = buffer; |
void *a = NULL; |
int sym_index = pe_find_import(s1, symbol, p); |
int dll_index; |
const char *dll_name; |
void *hm; |
if (sym_index) { |
dll_index = ((Elf32_Sym *) s1->dynsymtab_section->data)[sym_index]. |
st_other; |
dll_name = s1->loaded_dlls[dll_index]->name; |
hm = GetModuleHandleA(dll_name); |
if (NULL == hm) |
hm = LoadLibraryA(dll_name); |
if (hm) { |
a = GetProcAddress(hm, buffer); |
if (a && STT_OBJECT == type) { |
// need to return a pointer to the address for data objects |
dynarray_add(&pe_imp, &nb_pe_imp, a); |
a = &pe_imp[nb_pe_imp - 1]; |
} |
} |
} |
return a; |
} |
#endif |
#define for_sym_in_symtab(sym) \ |
for (sym = (Elf32_Sym *)symtab_section->data + 1; \ |
sym < (Elf32_Sym *)(symtab_section->data + \ |
symtab_section->data_offset); \ |
++sym) |
#define pe_set_datadir(dir,addr,size) \ |
pe_opt_hdr.DataDirectory[dir].VirtualAddress = addr, \ |
pe_opt_hdr.DataDirectory[dir].Size = size |
/*----------------------------------------------------------------------------*/ |
ST void dynarray_reset(void ***pp, int *n) |
{ |
int i; |
for (i = 0; i < *n; ++i) |
tcc_free((*pp)[i]); |
tcc_free(*pp); |
*pp = NULL; |
*n = 0; |
} |
ST 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 DWORD umin(DWORD a, DWORD b) |
{ |
return a < b ? a : b; |
} |
#endif |
ST DWORD umax(DWORD a, DWORD b) |
{ |
return a < b ? b : a; |
} |
ST void pe_fpad(FILE * fp, DWORD new_pos) |
{ |
DWORD pos = ftell(fp); |
while (++pos <= new_pos) |
fputc(0, fp); |
} |
ST DWORD pe_file_align(DWORD n) |
{ |
return (n + (0x200 - 1)) & ~(0x200 - 1); |
} |
ST DWORD pe_virtual_align(DWORD n) |
{ |
return (n + (0x1000 - 1)) & ~(0x1000 - 1); |
} |
ST void pe_align_section(Section * s, int a) |
{ |
int i = s->data_offset & (a - 1); |
if (i) |
section_ptr_add(s, a - i); |
} |
/*----------------------------------------------------------------------------*/ |
ST int pe_write_pe(struct pe_info *pe) |
{ |
int i; |
FILE *op; |
DWORD file_offset; |
IMAGE_SECTION_HEADER ish[pe_sec_number], *psh; |
int sec_index = 0; |
op = fopen(pe->filename, "wb"); |
if (NULL == op) { |
error_noabort("could not create file: %s", pe->filename); |
return 1; |
} |
memset(&ish, 0, sizeof ish); |
pe->sizeofheaders = pe_file_align(sizeof pe_dos_hdr |
+ sizeof pe_magic |
+ sizeof pe_file_hdr |
+ sizeof pe_opt_hdr |
+ |
pe->sec_count * |
sizeof(IMAGE_SECTION_HEADER) |
); |
file_offset = pe->sizeofheaders; |
pe_fpad(op, file_offset); |
if (2 == verbose) |
printf("-------------------------------" |
"\n virt file size section" "\n"); |
for (i = 0; i < pe->sec_count; ++i) { |
struct section_info *si = pe->sh_info + i; |
const char *sh_name = pe_sec_names[si->id]; |
unsigned long addr = si->sh_addr - pe->imagebase; |
unsigned long size = si->sh_size; |
if (2 == verbose) |
printf("%6lx %6lx %6lx %s\n", |
addr, file_offset, size, sh_name); |
switch (si->id) { |
case sec_text: |
pe_opt_hdr.BaseOfCode = addr; |
pe_opt_hdr.AddressOfEntryPoint = addr + pe->start_addr; |
break; |
case sec_data: |
pe_opt_hdr.BaseOfData = addr; |
if (pe->imp_size) { |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_IMPORT, |
pe->imp_offs + addr, pe->imp_size); |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_IAT, |
pe->iat_offs + addr, pe->iat_size); |
} |
if (pe->exp_size) { |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_EXPORT, |
pe->exp_offs + addr, pe->exp_size); |
} |
break; |
case sec_bss: |
break; |
case sec_reloc: |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size); |
break; |
case sec_rsrc: |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size); |
break; |
case sec_stab: |
break; |
case sec_stabstr: |
break; |
} |
psh = &ish[sec_index++]; |
strcpy((char *) psh->Name, sh_name); |
psh->Characteristics = pe_flags[si->id]; |
psh->VirtualAddress = addr; |
psh->Misc.VirtualSize = size; |
pe_opt_hdr.SizeOfImage = |
umax(psh->VirtualAddress + psh->Misc.VirtualSize, |
pe_opt_hdr.SizeOfImage); |
if (si->data_size) { |
psh->PointerToRawData = file_offset; |
fwrite(si->data, 1, si->data_size, op); |
file_offset = pe_file_align(file_offset + si->data_size); |
psh->SizeOfRawData = file_offset - psh->PointerToRawData; |
pe_fpad(op, file_offset); |
} |
} |
/*----------------------------------------------------- */ |
pe_file_hdr.NumberOfSections = sec_index; |
pe_opt_hdr.SizeOfHeaders = pe->sizeofheaders; |
pe_opt_hdr.ImageBase = pe->imagebase; |
if (PE_DLL == pe_type) |
pe_file_hdr.Characteristics = 0x230E; |
else if (PE_GUI != pe_type) |
pe_opt_hdr.Subsystem = 3; |
fseek(op, SEEK_SET, 0); |
fwrite(&pe_dos_hdr, 1, sizeof pe_dos_hdr, op); |
fwrite(&pe_magic, 1, sizeof pe_magic, op); |
fwrite(&pe_file_hdr, 1, sizeof pe_file_hdr, op); |
fwrite(&pe_opt_hdr, 1, sizeof pe_opt_hdr, op); |
for (i = 0; i < sec_index; ++i) |
fwrite(&ish[i], 1, sizeof(IMAGE_SECTION_HEADER), op); |
fclose(op); |
if (2 == verbose) |
printf("-------------------------------\n"); |
if (verbose) |
printf("<-- %s (%lu bytes)\n", pe->filename, file_offset); |
return 0; |
} |
/*----------------------------------------------------------------------------*/ |
ST int pe_add_import(struct pe_info *pe, int sym_index, DWORD offset) |
{ |
int i; |
int dll_index; |
struct pe_import_info *p; |
struct import_symbol *s; |
dll_index = |
((Elf32_Sym *) pe->s1->dynsymtab_section->data)[sym_index]. |
st_other; |
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) |
goto found_sym; |
s = tcc_mallocz(sizeof *s); |
s->sym_index = sym_index; |
s->offset = offset; |
dynarray_add((void ***) &p->symbols, &p->sym_count, s); |
found_sym: |
return 1; |
} |
/*----------------------------------------------------------------------------*/ |
ST void pe_build_imports(struct pe_info *pe) |
{ |
int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i; |
DWORD voffset = 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(struct pe_import_header); |
pe->iat_offs = dll_ptr + pe->imp_size; |
pe->iat_size = (sym_cnt + ndlls) * sizeof(DWORD); |
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) { |
struct pe_import_header *hdr; |
int k, n, v; |
struct pe_import_info *p = pe->imp_info[i]; |
const char *name = pe->s1->loaded_dlls[p->dll_index]->name; |
/* put the dll name into the import header */ |
if (0 == strncmp(name, "lib", 3)) |
name += 3; |
v = put_elf_str(pe->thunk, name); |
hdr = (struct pe_import_header *) (pe->thunk->data + dll_ptr); |
hdr->first_thunk = thk_ptr + voffset; |
hdr->first_entry = ent_ptr + voffset; |
hdr->lib_name_offset = v + voffset; |
for (k = 0, n = p->sym_count; k <= n; ++k) { |
if (k < n) { |
DWORD offset = p->symbols[k]->offset; |
int sym_index = p->symbols[k]->sym_index; |
Elf32_Sym *sym = |
(Elf32_Sym *) pe->s1->dynsymtab_section->data + |
sym_index; |
const char *name = |
pe->s1->dynsymtab_section->link->data + sym->st_name; |
if (offset & 0x80000000) { /* ref to data */ |
Elf32_Sym *sym = |
&((Elf32_Sym *) symtab_section-> |
data)[offset & 0x7FFFFFFF]; |
sym->st_value = thk_ptr; |
sym->st_shndx = pe->thunk->sh_num; |
} else { /* ref to function */ |
char buffer[100]; |
sprintf(buffer, "IAT.%s", name); |
sym_index = |
put_elf_sym(symtab_section, thk_ptr, sizeof(DWORD), |
ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), |
0, pe->thunk->sh_num, buffer); |
put_elf_reloc(symtab_section, text_section, offset, R_386_32, /*R_JMP_SLOT, */ |
sym_index); |
} |
v = pe->thunk->data_offset + voffset; |
section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */ |
put_elf_str(pe->thunk, name); |
} else { |
v = 0; // last entry is zero |
} |
*(DWORD *) (pe->thunk->data + thk_ptr) = |
*(DWORD *) (pe->thunk->data + ent_ptr) = v; |
thk_ptr += sizeof(DWORD); |
ent_ptr += sizeof(DWORD); |
} |
dll_ptr += sizeof(struct pe_import_header); |
dynarray_reset((void ***) &p->symbols, &p->sym_count); |
} |
dynarray_reset((void ***) &pe->imp_info, &pe->imp_count); |
} |
/* ------------------------------------------------------------- */ |
ST int sym_cmp(const void *va, const void *vb) |
{ |
Elf32_Sym *sa = (Elf32_Sym *)symtab_section->data + *(int*)va; |
Elf32_Sym *sb = (Elf32_Sym *)symtab_section->data + *(int*)vb; |
const char *ca = symtab_section->link->data + sa->st_name; |
const char *cb = symtab_section->link->data + sb->st_name; |
return strcmp(ca, cb); |
} |
ST void pe_build_exports(struct pe_info *pe) |
{ |
Elf32_Sym *sym; |
DWORD func_offset, voffset; |
struct pe_export_header *hdr; |
int sym_count, n, ord, *sorted; |
voffset = pe->thunk->sh_addr - pe->imagebase; |
sym_count = 0, n = 1, sorted = NULL; |
// for simplicity only functions are exported |
for_sym_in_symtab(sym) |
{ |
if ((sym->st_other & 1) |
&& sym->st_shndx == text_section->sh_num) |
dynarray_add((void***)&sorted, &sym_count, (void*)n); |
++n; |
} |
if (0 == sym_count) |
return; |
qsort (sorted, sym_count, sizeof sorted[0], sym_cmp); |
pe_align_section(pe->thunk, 16); |
pe->exp_offs = pe->thunk->data_offset; |
hdr = section_ptr_add(pe->thunk, |
sizeof(struct pe_export_header) + |
sym_count * (2 * sizeof(DWORD) + sizeof(WORD))); |
func_offset = pe->exp_offs + sizeof(struct pe_export_header); |
hdr->Characteristics = 0; |
hdr->Base = 1; |
hdr->NumberOfFunctions = sym_count; |
hdr->NumberOfNames = sym_count; |
hdr->AddressOfFunctions = func_offset + voffset; |
hdr->AddressOfNames = hdr->AddressOfFunctions + sym_count * sizeof(DWORD); |
hdr->AddressOfNameOrdinals = hdr->AddressOfNames + sym_count * sizeof(DWORD); |
hdr->Name = pe->thunk->data_offset + voffset; |
put_elf_str(pe->thunk, tcc_basename(pe->filename)); |
for (ord = 0; ord < sym_count; ++ord) |
{ |
char *name; DWORD *p, *pfunc, *pname; WORD *pord; |
sym = (Elf32_Sym *)symtab_section->data + sorted[ord]; |
name = symtab_section->link->data + sym->st_name; |
p = (DWORD*)(pe->thunk->data + func_offset); |
pfunc = p + ord; |
pname = p + sym_count + ord; |
pord = (WORD *)(p + 2*sym_count) + ord; |
*pfunc = sym->st_value + pe->s1->sections[sym->st_shndx]->sh_addr - pe->imagebase; |
*pname = pe->thunk->data_offset + voffset; |
*pord = ord; |
put_elf_str(pe->thunk, name); |
/* printf("export: %s\n", name); */ |
} |
pe->exp_size = pe->thunk->data_offset - pe->exp_offs; |
tcc_free(sorted); |
} |
/* ------------------------------------------------------------- */ |
ST void pe_build_reloc(struct pe_info *pe, int *section_order, |
int section_count) |
{ |
DWORD offset, block_ptr, addr; |
int count, i; |
Elf32_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 = ELF32_R_TYPE(rel->r_info); |
addr = rel->r_offset + s->sh_addr; |
++rel; |
if (type != R_386_32) |
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 < section_count) { |
sr = (s = pe->s1->sections[section_order[i++]])->reloc; |
if (sr) { |
rel = (Elf32_Rel *) sr->data; |
rel_end = (Elf32_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) |
section_ptr_add(pe->reloc, 2), ++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; |
} |
} |
/* ------------------------------------------------------------- */ |
ST int pe_assign_addresses(struct pe_info *pe) |
{ |
int i, k, n; |
DWORD addr; |
int section_order[pe_sec_number]; |
struct section_info *si_data = NULL; |
pe->imagebase = PE_DLL == pe_type ? 0x10000000 : 0x00400000; |
addr = pe->imagebase + 1; |
if (PE_DLL == pe_type) |
pe->reloc = new_section(pe->s1, ".reloc", SHT_DYNAMIC, SHF_ALLOC); |
for (n = k = 0; n < pe_sec_number; ++n) { |
for (i = 1; i < pe->s1->nb_sections; ++i) { |
Section *s = pe->s1->sections[i]; |
if (0 == strcmp(s->name, pe_sec_names[n])) { |
struct section_info *si = &pe->sh_info[pe->sec_count]; |
#ifdef PE_MERGE_DATA |
if (n == sec_bss && si_data) { |
/* append .bss to .data */ |
s->sh_addr = addr = ((addr - 1) | 15) + 1; |
addr += s->data_offset; |
si_data->sh_size = addr - si_data->sh_addr; |
} else |
#endif |
{ |
si->sh_addr = s->sh_addr = addr = |
pe_virtual_align(addr); |
si->id = n; |
if (n == sec_data) { |
pe->thunk = s; |
si_data = si; |
pe_build_imports(pe); |
pe_build_exports(pe); |
} else if (n == sec_reloc) { |
pe_build_reloc(pe, section_order, k); |
} |
if (s->data_offset) { |
if (n != sec_bss) { |
si->data = s->data; |
si->data_size = s->data_offset; |
} |
addr += s->data_offset; |
si->sh_size = s->data_offset; |
++pe->sec_count; |
} |
//printf("Section %08X %04X %s\n", si->sh_addr, si->data_size, s->name); |
} |
section_order[k] = i, ++k; |
} |
} |
} |
return 0; |
} |
/*----------------------------------------------------------------------------*/ |
ST int pe_check_symbols(struct pe_info *pe) |
{ |
Elf32_Sym *sym; |
int ret = 0; |
pe_align_section(text_section, 8); |
for_sym_in_symtab(sym) { |
if (sym->st_shndx == SHN_UNDEF) { |
const char *symbol = symtab_section->link->data + sym->st_name; |
unsigned type = ELF32_ST_TYPE(sym->st_info); |
int sym_index = pe_find_import(pe->s1, symbol, NULL); |
if (sym_index) { |
if (type == STT_FUNC) { |
unsigned long offset = text_section->data_offset; |
if (pe_add_import(pe, sym_index, offset + 2)) { |
/* add the 'jmp IAT[x]' instruction */ |
*(WORD *) section_ptr_add(text_section, 8) = |
0x25FF; |
/* patch the symbol */ |
sym->st_shndx = text_section->sh_num; |
sym->st_value = offset; |
continue; |
} |
} else if (type == STT_OBJECT) { /* data, ptr to that should be */ |
if (pe_add_import(pe, sym_index, |
(sym - |
(Elf32_Sym *) symtab_section->data) | |
0x80000000)) |
continue; |
} |
} |
error_noabort("undefined symbol '%s'", symbol); |
ret = 1; |
} else |
if (pe->s1->rdynamic |
&& ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { |
/* if -rdynamic option, then export all non local symbols */ |
sym->st_other |= 1; |
} |
} |
return ret; |
} |
/*----------------------------------------------------------------------------*/ |
#ifdef PE_PRINT_SECTIONS |
ST 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", s->sh_addr); |
fprintf(f, "\ncontents %08X", l); |
fprintf(f, "\n\n"); |
if (s->sh_type == SHT_NOBITS) |
return; |
if (s->sh_type == SHT_SYMTAB) |
m = sizeof(Elf32_Sym); |
if (s->sh_type == SHT_REL) |
m = sizeof(Elf32_Rel); |
else |
m = 16; |
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) { |
Elf32_Sym *sym = (Elf32_Sym *) (p + i); |
const char *name = s->link->data + sym->st_name; |
fprintf(f, |
" name:%04X" |
" value:%04X" |
" size:%04X" |
" bind:%02X" |
" type:%02X" |
" other:%02X" |
" shndx:%04X" |
" \"%s\"", |
sym->st_name, |
sym->st_value, |
sym->st_size, |
ELF32_ST_BIND(sym->st_info), |
ELF32_ST_TYPE(sym->st_info), |
sym->st_other, sym->st_shndx, name); |
} else if (s->sh_type == SHT_REL) { |
Elf32_Rel *rel = (Elf32_Rel *) (p + i); |
Elf32_Sym *sym = |
(Elf32_Sym *) s->link->data + ELF32_R_SYM(rel->r_info); |
const char *name = s->link->link->data + sym->st_name; |
fprintf(f, |
" offset:%04X" |
" type:%02X" |
" symbol:%04X" |
" \"%s\"", |
rel->r_offset, |
ELF32_R_TYPE(rel->r_info), |
ELF32_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"); |
} |
#endif |
static int pe_test_cmd(const char **pp, const char *cmd) |
{ |
const char *p; |
char *q, buf[16]; |
int ret; |
p = *pp; |
q = buf; |
while (*p != '\0' && !is_space(*p)) { |
if ((q - buf) < sizeof(buf) - 1) |
*q++ = toup(*p); |
p++; |
} |
*q = '\0'; |
ret = !strcmp(buf, cmd); |
*pp = p; |
return ret; |
} |
/* ------------------------------------------------------------- */ |
int pe_load_def_file(TCCState * s1, FILE * fp) |
{ |
DLLReference *dllref; |
int f = 0, sym_index; |
char *p, line[120], dllname[40]; |
while (fgets(line, sizeof line, fp)) { |
//p = strchr(line, 0); |
while (p > line && p[-1] <= ' ') |
--p; |
*p = 0; |
p = line; |
while (*p && *p <= ' ') |
++p; |
if (*p && ';' != *p) |
switch (f) { |
case 0: |
if (!pe_test_cmd((const char **)&p, "LIBRARY")) |
return -1; |
while (is_space(*p)) |
p++; |
pstrcpy(dllname, sizeof(dllname), p); |
++f; |
continue; |
case 1: |
if (!pe_test_cmd((const char **)&p, "EXPORTS")) |
return -1; |
++f; |
continue; |
case 2: |
dllref = |
tcc_malloc(sizeof(DLLReference) + strlen(dllname)); |
strcpy(dllref->name, dllname); |
dllref->level = 0; |
dynarray_add((void ***) &s1->loaded_dlls, |
&s1->nb_loaded_dlls, dllref); |
++f; |
default: |
/* tccpe needs to know from what dll it should import |
the sym */ |
sym_index = add_elf_sym(s1->dynsymtab_section, |
0, 0, ELF32_ST_INFO(STB_GLOBAL, |
STT_FUNC), |
s1->nb_loaded_dlls - 1, |
text_section->sh_num, p); |
continue; |
} |
} |
return 0; |
} |
/* ------------------------------------------------------------- */ |
void pe_guess_outfile(char *objfilename, int output_type) |
{ |
char *ext = strrchr(objfilename, '.'); |
if (NULL == ext) |
//ext = strchr(objfilename, 0); |
if (output_type == TCC_OUTPUT_DLL) |
strcpy(ext, ".dll"); |
else |
if (output_type == TCC_OUTPUT_EXE) |
strcpy(ext, ".exe"); |
else |
if (output_type == TCC_OUTPUT_OBJ && strcmp(ext, ".o")) |
strcpy(ext, ".o"); |
else |
error("no outputfile given"); |
} |
/* ------------------------------------------------------------- */ |
unsigned long pe_add_runtime(TCCState * s1) |
{ |
const char *start_symbol; |
unsigned long addr; |
if (find_elf_sym(symtab_section, "WinMain")) |
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; |
} |
start_symbol = |
TCC_OUTPUT_MEMORY == s1->output_type |
? PE_GUI == pe_type ? "_runwinmain" : NULL |
: PE_DLL == pe_type ? "_dllstart" |
: PE_GUI == pe_type ? "_winstart" : "_start"; |
/* grab the startup code from libtcc1 */ |
if (start_symbol) |
add_elf_sym(symtab_section, |
0, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
SHN_UNDEF, start_symbol); |
if (0 == s1->nostdlib) { |
tcc_add_library(s1, "tcc1"); |
tcc_add_library(s1, "msvcrt"); |
if (PE_DLL == pe_type || PE_GUI == pe_type) { |
tcc_add_library(s1, "kernel32"); |
tcc_add_library(s1, "user32"); |
tcc_add_library(s1, "gdi32"); |
} |
} |
addr = start_symbol ? |
(unsigned long) tcc_get_symbol_err(s1, start_symbol) : 0; |
if (s1->output_type == TCC_OUTPUT_MEMORY && addr) { |
/* for -run GUI's, put '_runwinmain' instead of 'main' */ |
add_elf_sym(symtab_section, |
addr, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
text_section->sh_num, "main"); |
/* FreeConsole(); */ |
} |
return addr; |
} |
int tcc_output_pe(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.start_addr = pe_add_runtime(s1); |
relocate_common_syms(); /* assign bss adresses */ |
ret = pe_check_symbols(&pe); |
if (0 == ret) { |
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); |
} |
ret = pe_write_pe(&pe); |
} |
#ifdef PE_PRINT_SECTIONS |
{ |
Section *s; |
FILE *f; |
f = fopen("tccpe.log", "wt"); |
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 |
return ret; |
} |
/*----------------------------------------------------------------------------*/ |
/programs/develop/ktcc/trunk/source/tcctest.c |
---|
0,0 → 1,1990 |
/* |
* TCC auto test program |
*/ |
#include "config.h" |
#if GCC_MAJOR >= 3 |
/* Unfortunately, gcc version < 3 does not handle that! */ |
#define ALL_ISOC99 |
/* only gcc 3 handles _Bool correctly */ |
#define BOOL_ISOC99 |
/* gcc 2.95.3 does not handle correctly CR in strings or after strays */ |
#define CORRECT_CR_HANDLING |
#endif |
/* deprecated and no longer supported in gcc 3.3 */ |
//#define ACCEPT_CR_IN_STRINGS |
/* __VA_ARGS__ and __func__ support */ |
#define C99_MACROS |
/* test various include syntaxes */ |
#define TCCLIB_INC <tcclib.h> |
#define TCCLIB_INC1 <tcclib |
#define TCCLIB_INC2 h> |
#define TCCLIB_INC3 "tcclib" |
#include TCCLIB_INC |
#include TCCLIB_INC1.TCCLIB_INC2 |
#include TCCLIB_INC1.h> |
/* gcc 3.2 does not accept that (bug ?) */ |
//#include TCCLIB_INC3 ".h" |
#include <tcclib.h> |
#include "tcclib.h" |
void string_test(); |
void expr_test(); |
void macro_test(); |
void scope_test(); |
void forward_test(); |
void funcptr_test(); |
void loop_test(); |
void switch_test(); |
void goto_test(); |
void enum_test(); |
void typedef_test(); |
void struct_test(); |
void array_test(); |
void expr_ptr_test(); |
void bool_test(); |
void expr2_test(); |
void constant_expr_test(); |
void expr_cmp_test(); |
void char_short_test(); |
void init_test(void); |
void compound_literal_test(void); |
int kr_test(); |
void struct_assign_test(void); |
void cast_test(void); |
void bitfield_test(void); |
void c99_bool_test(void); |
void float_test(void); |
void longlong_test(void); |
void stdarg_test(void); |
void whitespace_test(void); |
void relocation_test(void); |
void old_style_function(void); |
void sizeof_test(void); |
void typeof_test(void); |
void local_label_test(void); |
void statement_expr_test(void); |
void asm_test(void); |
void builtin_test(void); |
int fib(int n); |
void num(int n); |
void forward_ref(void); |
int isid(int c); |
#define A 2 |
#define N 1234 + A |
#define pf printf |
#define M1(a, b) (a) + (b) |
#define str\ |
(s) # s |
#define glue(a, b) a ## b |
#define xglue(a, b) glue(a, b) |
#define HIGHLOW "hello" |
#define LOW LOW ", world" |
#define min(a, b) ((a) < (b) ? (a) : (b)) |
#ifdef C99_MACROS |
#define dprintf(level,...) printf(__VA_ARGS__) |
#endif |
/* gcc vararg macros */ |
#define dprintf1(level, fmt, args...) printf(fmt, ## args) |
#define MACRO_NOARGS() |
#define AAA 3 |
#undef AAA |
#define AAA 4 |
#if 1 |
#define B3 1 |
#elif 1 |
#define B3 2 |
#elif 0 |
#define B3 3 |
#else |
#define B3 4 |
#endif |
#define __INT64_C(c) c ## LL |
#define INT64_MIN (-__INT64_C(9223372036854775807)-1) |
int qq(int x) |
{ |
return x + 40; |
} |
#define qq(x) x |
#define spin_lock(lock) do { } while (0) |
#define wq_spin_lock spin_lock |
#define TEST2() wq_spin_lock(a) |
void macro_test(void) |
{ |
printf("macro:\n"); |
pf("N=%d\n", N); |
printf("aaa=%d\n", AAA); |
printf("min=%d\n", min(1, min(2, -1))); |
printf("s1=%s\n", glue(HIGH, LOW)); |
printf("s2=%s\n", xglue(HIGH, LOW)); |
printf("s3=%s\n", str("c")); |
printf("s4=%s\n", str(a1)); |
printf("B3=%d\n", B3); |
#ifdef A |
printf("A defined\n"); |
#endif |
#ifdef B |
printf("B defined\n"); |
#endif |
#ifdef A |
printf("A defined\n"); |
#else |
printf("A not defined\n"); |
#endif |
#ifdef B |
printf("B defined\n"); |
#else |
printf("B not defined\n"); |
#endif |
#ifdef A |
printf("A defined\n"); |
#ifdef B |
printf("B1 defined\n"); |
#else |
printf("B1 not defined\n"); |
#endif |
#else |
printf("A not defined\n"); |
#ifdef B |
printf("B2 defined\n"); |
#else |
printf("B2 not defined\n"); |
#endif |
#endif |
#if 1+1 |
printf("test true1\n"); |
#endif |
#if 0 |
printf("test true2\n"); |
#endif |
#if 1-1 |
printf("test true3\n"); |
#endif |
#if defined(A) |
printf("test trueA\n"); |
#endif |
#if defined(B) |
printf("test trueB\n"); |
#endif |
#if 0 |
printf("test 0\n"); |
#elif 0 |
printf("test 1\n"); |
#elif 2 |
printf("test 2\n"); |
#else |
printf("test 3\n"); |
#endif |
MACRO_NOARGS(); |
#ifdef __LINE__ |
printf("__LINE__ defined\n"); |
#endif |
printf("__LINE__=%d __FILE__=%s\n", |
__LINE__, __FILE__); |
#line 200 |
printf("__LINE__=%d __FILE__=%s\n", |
__LINE__, __FILE__); |
#line 203 "test" |
printf("__LINE__=%d __FILE__=%s\n", |
__LINE__, __FILE__); |
#line 220 "tcctest.c" |
/* not strictly preprocessor, but we test it there */ |
#ifdef C99_MACROS |
printf("__func__ = %s\n", __func__); |
dprintf(1, "vaarg=%d\n", 1); |
#endif |
dprintf1(1, "vaarg1\n"); |
dprintf1(1, "vaarg1=%d\n", 2); |
dprintf1(1, "vaarg1=%d %d\n", 1, 2); |
/* gcc extension */ |
printf("func='%s'\n", __FUNCTION__); |
/* complicated macros in glibc */ |
printf("INT64_MIN=%Ld\n", INT64_MIN); |
{ |
int a; |
a = 1; |
glue(a+, +); |
printf("a=%d\n", a); |
glue(a <, <= 2); |
printf("a=%d\n", a); |
} |
/* macro function with argument outside the macro string */ |
#define MF_s MF_hello |
#define MF_hello(msg) printf("%s\n",msg) |
#define MF_t printf("tralala\n"); MF_hello |
MF_s("hi"); |
MF_t("hi"); |
/* test macro substituion inside args (should not eat stream) */ |
printf("qq=%d\n", qq(qq)(2)); |
/* test zero argument case. NOTE: gcc 2.95.x does not accept a |
null argument without a space. gcc 3.2 fixes that. */ |
#define qq1(x) 1 |
printf("qq1=%d\n", qq1( )); |
/* comment with stray handling *\ |
/ |
/* this is a valid *\/ comment */ |
/* this is a valid comment *\*/ |
// this is a valid\ |
comment |
/* test function macro substitution when the function name is |
substituted */ |
TEST2(); |
} |
int op(a,b) |
{ |
return a / b; |
} |
int ret(a) |
{ |
if (a == 2) |
return 1; |
if (a == 3) |
return 2; |
return 0; |
} |
void ps(const char *s) |
{ |
int c; |
while (1) { |
c = *s; |
if (c == 0) |
break; |
printf("%c", c); |
s++; |
} |
} |
const char foo1_string[] = "\ |
bar\n\ |
test\14\ |
1"; |
void string_test() |
{ |
int b; |
printf("string:\n"); |
printf("\141\1423\143\n");/* dezdez test */ |
printf("\x41\x42\x43\x3a\n"); |
printf("c=%c\n", 'r'); |
printf("wc=%C 0x%lx %C\n", L'a', L'\x1234', L'c'); |
printf("foo1_string='%s'\n", foo1_string); |
#if 0 |
printf("wstring=%S\n", L"abc"); |
printf("wstring=%S\n", L"abc" L"def" "ghi"); |
printf("'\\377'=%d '\\xff'=%d\n", '\377', '\xff'); |
printf("L'\\377'=%d L'\\xff'=%d\n", L'\377', L'\xff'); |
#endif |
ps("test\n"); |
b = 32; |
while ((b = b + 1) < 96) { |
printf("%c", b); |
} |
printf("\n"); |
printf("fib=%d\n", fib(33)); |
b = 262144; |
while (b != 0x80000000) { |
num(b); |
b = b * 2; |
} |
} |
void loop_test() |
{ |
int i; |
i = 0; |
while (i < 10) |
printf("%d", i++); |
printf("\n"); |
for(i = 0; i < 10;i++) |
printf("%d", i); |
printf("\n"); |
i = 0; |
do { |
printf("%d", i++); |
} while (i < 10); |
printf("\n"); |
/* break/continue tests */ |
i = 0; |
while (1) { |
if (i == 6) |
break; |
i++; |
if (i == 3) |
continue; |
printf("%d", i); |
} |
printf("\n"); |
/* break/continue tests */ |
i = 0; |
do { |
if (i == 6) |
break; |
i++; |
if (i == 3) |
continue; |
printf("%d", i); |
} while(1); |
printf("\n"); |
for(i = 0;i < 10;i++) { |
if (i == 3) |
continue; |
printf("%d", i); |
} |
printf("\n"); |
} |
void goto_test() |
{ |
int i; |
static void *label_table[3] = { &&label1, &&label2, &&label3 }; |
printf("goto:\n"); |
i = 0; |
s_loop: |
if (i >= 10) |
goto s_end; |
printf("%d", i); |
i++; |
goto s_loop; |
s_end: |
printf("\n"); |
/* we also test computed gotos (GCC extension) */ |
for(i=0;i<3;i++) { |
goto *label_table[i]; |
label1: |
printf("label1\n"); |
goto next; |
label2: |
printf("label2\n"); |
goto next; |
label3: |
printf("label3\n"); |
next: ; |
} |
} |
enum { |
E0, |
E1 = 2, |
E2 = 4, |
E3, |
E4, |
}; |
enum test { |
E5 = 1000, |
}; |
void enum_test() |
{ |
enum test b1; |
printf("enum:\n%d %d %d %d %d %d\n", |
E0, E1, E2, E3, E4, E5); |
b1 = 1; |
printf("b1=%d\n", b1); |
} |
typedef int *my_ptr; |
void typedef_test() |
{ |
my_ptr a; |
int b; |
a = &b; |
*a = 1234; |
printf("typedef:\n"); |
printf("a=%d\n", *a); |
} |
void forward_test() |
{ |
printf("forward:\n"); |
forward_ref(); |
forward_ref(); |
} |
void forward_ref(void) |
{ |
printf("forward ok\n"); |
} |
typedef struct struct1 { |
int f1; |
int f2, f3; |
union union1 { |
int v1; |
int v2; |
} u; |
char str[3]; |
} struct1; |
struct struct2 { |
int a; |
char b; |
}; |
union union2 { |
int w1; |
int w2; |
}; |
struct struct1 st1, st2; |
int main(int argc, char **argv) |
{ |
string_test(); |
expr_test(); |
macro_test(); |
scope_test(); |
forward_test(); |
funcptr_test(); |
loop_test(); |
switch_test(); |
goto_test(); |
enum_test(); |
typedef_test(); |
struct_test(); |
array_test(); |
expr_ptr_test(); |
bool_test(); |
expr2_test(); |
constant_expr_test(); |
expr_cmp_test(); |
char_short_test(); |
init_test(); |
compound_literal_test(); |
kr_test(); |
struct_assign_test(); |
cast_test(); |
bitfield_test(); |
c99_bool_test(); |
float_test(); |
longlong_test(); |
stdarg_test(); |
whitespace_test(); |
relocation_test(); |
old_style_function(); |
sizeof_test(); |
typeof_test(); |
statement_expr_test(); |
local_label_test(); |
asm_test(); |
builtin_test(); |
return 0; |
} |
int tab[3]; |
int tab2[3][2]; |
int g; |
void f1(g) |
{ |
printf("g1=%d\n", g); |
} |
void scope_test() |
{ |
printf("scope:\n"); |
g = 2; |
f1(1); |
printf("g2=%d\n", g); |
{ |
int g; |
g = 3; |
printf("g3=%d\n", g); |
{ |
int g; |
g = 4; |
printf("g4=%d\n", g); |
} |
} |
printf("g5=%d\n", g); |
} |
void array_test(int a[4]) |
{ |
int i, j; |
printf("array:\n"); |
printf("sizeof(a) = %d\n", sizeof(a)); |
printf("sizeof(\"a\") = %d\n", sizeof("a")); |
#ifdef C99_MACROS |
printf("sizeof(__func__) = %d\n", sizeof(__func__)); |
#endif |
printf("sizeof tab %d\n", sizeof(tab)); |
printf("sizeof tab2 %d\n", sizeof tab2); |
tab[0] = 1; |
tab[1] = 2; |
tab[2] = 3; |
printf("%d %d %d\n", tab[0], tab[1], tab[2]); |
for(i=0;i<3;i++) |
for(j=0;j<2;j++) |
tab2[i][j] = 10 * i + j; |
for(i=0;i<3*2;i++) { |
printf(" %3d", ((int *)tab2)[i]); |
} |
printf("\n"); |
} |
void expr_test() |
{ |
int a, b; |
a = 0; |
printf("%d\n", a += 1); |
printf("%d\n", a -= 2); |
printf("%d\n", a *= 31232132); |
printf("%d\n", a /= 4); |
printf("%d\n", a %= 20); |
printf("%d\n", a &= 6); |
printf("%d\n", a ^= 7); |
printf("%d\n", a |= 8); |
printf("%d\n", a >>= 3); |
printf("%d\n", a <<= 4); |
a = 22321; |
b = -22321; |
printf("%d\n", a + 1); |
printf("%d\n", a - 2); |
printf("%d\n", a * 312); |
printf("%d\n", a / 4); |
printf("%d\n", b / 4); |
printf("%d\n", (unsigned)b / 4); |
printf("%d\n", a % 20); |
printf("%d\n", b % 20); |
printf("%d\n", (unsigned)b % 20); |
printf("%d\n", a & 6); |
printf("%d\n", a ^ 7); |
printf("%d\n", a | 8); |
printf("%d\n", a >> 3); |
printf("%d\n", b >> 3); |
printf("%d\n", (unsigned)b >> 3); |
printf("%d\n", a << 4); |
printf("%d\n", ~a); |
printf("%d\n", -a); |
printf("%d\n", +a); |
printf("%d\n", 12 + 1); |
printf("%d\n", 12 - 2); |
printf("%d\n", 12 * 312); |
printf("%d\n", 12 / 4); |
printf("%d\n", 12 % 20); |
printf("%d\n", 12 & 6); |
printf("%d\n", 12 ^ 7); |
printf("%d\n", 12 | 8); |
printf("%d\n", 12 >> 2); |
printf("%d\n", 12 << 4); |
printf("%d\n", ~12); |
printf("%d\n", -12); |
printf("%d\n", +12); |
printf("%d %d %d %d\n", |
isid('a'), |
isid('g'), |
isid('T'), |
isid('(')); |
} |
int isid(int c) |
{ |
return (c >= 'a' & c <= 'z') | (c >= 'A' & c <= 'Z') | c == '_'; |
} |
/**********************/ |
int vstack[10], *vstack_ptr; |
void vpush(int vt, int vc) |
{ |
*vstack_ptr++ = vt; |
*vstack_ptr++ = vc; |
} |
void vpop(int *ft, int *fc) |
{ |
*fc = *--vstack_ptr; |
*ft = *--vstack_ptr; |
} |
void expr2_test() |
{ |
int a, b; |
printf("expr2:\n"); |
vstack_ptr = vstack; |
vpush(1432432, 2); |
vstack_ptr[-2] &= ~0xffffff80; |
vpop(&a, &b); |
printf("res= %d %d\n", a, b); |
} |
void constant_expr_test() |
{ |
int a; |
printf("constant_expr:\n"); |
a = 3; |
printf("%d\n", a * 16); |
printf("%d\n", a * 1); |
printf("%d\n", a + 0); |
} |
int tab4[10]; |
void expr_ptr_test() |
{ |
int *p, *q; |
printf("expr_ptr:\n"); |
p = tab4; |
q = tab4 + 10; |
printf("diff=%d\n", q - p); |
p++; |
printf("inc=%d\n", p - tab4); |
p--; |
printf("dec=%d\n", p - tab4); |
++p; |
printf("inc=%d\n", p - tab4); |
--p; |
printf("dec=%d\n", p - tab4); |
printf("add=%d\n", p + 3 - tab4); |
printf("add=%d\n", 3 + p - tab4); |
} |
void expr_cmp_test() |
{ |
int a, b; |
printf("constant_expr:\n"); |
a = -1; |
b = 1; |
printf("%d\n", a == a); |
printf("%d\n", a != a); |
printf("%d\n", a < b); |
printf("%d\n", a <= b); |
printf("%d\n", a <= a); |
printf("%d\n", b >= a); |
printf("%d\n", a >= a); |
printf("%d\n", b > a); |
printf("%d\n", (unsigned)a < b); |
printf("%d\n", (unsigned)a <= b); |
printf("%d\n", (unsigned)a <= a); |
printf("%d\n", (unsigned)b >= a); |
printf("%d\n", (unsigned)a >= a); |
printf("%d\n", (unsigned)b > a); |
} |
struct empty { |
}; |
struct aligntest1 { |
char a[10]; |
}; |
struct aligntest2 { |
int a; |
char b[10]; |
}; |
struct aligntest3 { |
double a, b; |
}; |
struct aligntest4 { |
double a[0]; |
}; |
void struct_test() |
{ |
struct1 *s; |
union union2 u; |
printf("struct:\n"); |
printf("sizes: %d %d %d %d\n", |
sizeof(struct struct1), |
sizeof(struct struct2), |
sizeof(union union1), |
sizeof(union union2)); |
st1.f1 = 1; |
st1.f2 = 2; |
st1.f3 = 3; |
printf("st1: %d %d %d\n", |
st1.f1, st1.f2, st1.f3); |
st1.u.v1 = 1; |
st1.u.v2 = 2; |
printf("union1: %d\n", st1.u.v1); |
u.w1 = 1; |
u.w2 = 2; |
printf("union2: %d\n", u.w1); |
s = &st2; |
s->f1 = 3; |
s->f2 = 2; |
s->f3 = 1; |
printf("st2: %d %d %d\n", |
s->f1, s->f2, s->f3); |
printf("str_addr=%x\n", (int)st1.str - (int)&st1.f1); |
/* align / size tests */ |
printf("aligntest1 sizeof=%d alignof=%d\n", |
sizeof(struct aligntest1), __alignof__(struct aligntest1)); |
printf("aligntest2 sizeof=%d alignof=%d\n", |
sizeof(struct aligntest2), __alignof__(struct aligntest2)); |
printf("aligntest3 sizeof=%d alignof=%d\n", |
sizeof(struct aligntest3), __alignof__(struct aligntest3)); |
printf("aligntest4 sizeof=%d alignof=%d\n", |
sizeof(struct aligntest4), __alignof__(struct aligntest4)); |
/* empty structures (GCC extension) */ |
printf("sizeof(struct empty) = %d\n", sizeof(struct empty)); |
printf("alignof(struct empty) = %d\n", __alignof__(struct empty)); |
} |
/* XXX: depend on endianness */ |
void char_short_test() |
{ |
int var1, var2; |
printf("char_short:\n"); |
var1 = 0x01020304; |
var2 = 0xfffefdfc; |
printf("s8=%d %d\n", |
*(char *)&var1, *(char *)&var2); |
printf("u8=%d %d\n", |
*(unsigned char *)&var1, *(unsigned char *)&var2); |
printf("s16=%d %d\n", |
*(short *)&var1, *(short *)&var2); |
printf("u16=%d %d\n", |
*(unsigned short *)&var1, *(unsigned short *)&var2); |
printf("s32=%d %d\n", |
*(int *)&var1, *(int *)&var2); |
printf("u32=%d %d\n", |
*(unsigned int *)&var1, *(unsigned int *)&var2); |
*(char *)&var1 = 0x08; |
printf("var1=%x\n", var1); |
*(short *)&var1 = 0x0809; |
printf("var1=%x\n", var1); |
*(int *)&var1 = 0x08090a0b; |
printf("var1=%x\n", var1); |
} |
/******************/ |
typedef struct Sym { |
int v; |
int t; |
int c; |
struct Sym *next; |
struct Sym *prev; |
} Sym; |
#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') |
#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) |
static int toupper1(int a) |
{ |
return TOUPPER(a); |
} |
void bool_test() |
{ |
int *s, a, b, t, f, i; |
a = 0; |
s = (void*)0; |
printf("!s=%d\n", !s); |
if (!s || !s[0]) |
a = 1; |
printf("a=%d\n", a); |
printf("a=%d %d %d\n", 0 || 0, 0 || 1, 1 || 1); |
printf("a=%d %d %d\n", 0 && 0, 0 && 1, 1 && 1); |
printf("a=%d %d\n", 1 ? 1 : 0, 0 ? 1 : 0); |
#if 1 && 1 |
printf("a1\n"); |
#endif |
#if 1 || 0 |
printf("a2\n"); |
#endif |
#if 1 ? 0 : 1 |
printf("a3\n"); |
#endif |
#if 0 ? 0 : 1 |
printf("a4\n"); |
#endif |
a = 4; |
printf("b=%d\n", a + (0 ? 1 : a / 2)); |
/* test register spilling */ |
a = 10; |
b = 10; |
a = (a + b) * ((a < b) ? |
((b - a) * (a - b)): a + b); |
printf("a=%d\n", a); |
/* test complex || or && expressions */ |
t = 1; |
f = 0; |
a = 32; |
printf("exp=%d\n", f == (32 <= a && a <= 3)); |
printf("r=%d\n", (t || f) + (t && f)); |
/* test ? : cast */ |
{ |
int aspect_on; |
int aspect_native = 65536; |
double bfu_aspect = 1.0; |
int aspect; |
for(aspect_on = 0; aspect_on < 2; aspect_on++) { |
aspect=aspect_on?(aspect_native*bfu_aspect+0.5):65535UL; |
printf("aspect=%d\n", aspect); |
} |
} |
/* test ? : GCC extension */ |
{ |
static int v1 = 34 ? : -1; /* constant case */ |
static int v2 = 0 ? : -1; /* constant case */ |
int a = 30; |
printf("%d %d\n", v1, v2); |
printf("%d %d\n", a - 30 ? : a * 2, a + 1 ? : a * 2); |
} |
/* again complex expression */ |
for(i=0;i<256;i++) { |
if (toupper1 (i) != TOUPPER (i)) |
printf("error %d\n", i); |
} |
} |
/* GCC accepts that */ |
static int tab_reinit[]; |
static int tab_reinit[10]; |
//int cinit1; /* a global variable can be defined several times without error ! */ |
int cinit1; |
int cinit1; |
int cinit1 = 0; |
int *cinit2 = (int []){3, 2, 1}; |
void compound_literal_test(void) |
{ |
int *p, i; |
char *q, *q3; |
printf("compound_test:\n"); |
p = (int []){1, 2, 3}; |
for(i=0;i<3;i++) |
printf(" %d", p[i]); |
printf("\n"); |
for(i=0;i<3;i++) |
printf("%d", cinit2[i]); |
printf("\n"); |
q = "tralala1"; |
printf("q1=%s\n", q); |
q = (char *){ "tralala2" }; |
printf("q2=%s\n", q); |
q3 = (char *){ q }; |
printf("q3=%s\n", q3); |
q = (char []){ "tralala3" }; |
printf("q4=%s\n", q); |
#ifdef ALL_ISOC99 |
p = (int []){1, 2, cinit1 + 3}; |
for(i=0;i<3;i++) |
printf(" %d", p[i]); |
printf("\n"); |
for(i=0;i<3;i++) { |
p = (int []){1, 2, 4 + i}; |
printf("%d %d %d\n", |
p[0], |
p[1], |
p[2]); |
} |
#endif |
} |
/* K & R protos */ |
kr_func1(a, b) |
{ |
return a + b; |
} |
int kr_func2(a, b) |
{ |
return a + b; |
} |
kr_test() |
{ |
printf("kr_test:\n"); |
printf("func1=%d\n", kr_func1(3, 4)); |
printf("func2=%d\n", kr_func2(3, 4)); |
return 0; |
} |
void num(int n) |
{ |
char *tab, *p; |
tab = (char*)malloc(20); |
p = tab; |
while (1) { |
*p = 48 + (n % 10); |
p++; |
n = n / 10; |
if (n == 0) |
break; |
} |
while (p != tab) { |
p--; |
printf("%c", *p); |
} |
printf("\n"); |
} |
/* structure assignment tests */ |
struct structa1 { |
int f1; |
char f2; |
}; |
struct structa1 ssta1; |
void struct_assign_test1(struct structa1 s1, int t) |
{ |
printf("%d %d %d\n", s1.f1, s1.f2, t); |
} |
struct structa1 struct_assign_test2(struct structa1 s1, int t) |
{ |
s1.f1 += t; |
s1.f2 -= t; |
return s1; |
} |
void struct_assign_test(void) |
{ |
struct structa1 lsta1, lsta2; |
#if 0 |
printf("struct_assign_test:\n"); |
lsta1.f1 = 1; |
lsta1.f2 = 2; |
printf("%d %d\n", lsta1.f1, lsta1.f2); |
lsta2 = lsta1; |
printf("%d %d\n", lsta2.f1, lsta2.f2); |
#else |
lsta2.f1 = 1; |
lsta2.f2 = 2; |
#endif |
struct_assign_test1(lsta2, 3); |
printf("before call: %d %d\n", lsta2.f1, lsta2.f2); |
lsta2 = struct_assign_test2(lsta2, 4); |
printf("after call: %d %d\n", lsta2.f1, lsta2.f2); |
} |
/* casts to short/char */ |
void cast1(char a, short b, unsigned char c, unsigned short d) |
{ |
printf("%d %d %d %d\n", a, b, c, d); |
} |
char bcast; |
short scast; |
void cast_test() |
{ |
int a; |
char c; |
char tab[10]; |
printf("cast_test:\n"); |
a = 0xfffff; |
cast1(a, a, a, a); |
a = 0xffffe; |
printf("%d %d %d %d\n", |
(char)(a + 1), |
(short)(a + 1), |
(unsigned char)(a + 1), |
(unsigned short)(a + 1)); |
printf("%d %d %d %d\n", |
(char)0xfffff, |
(short)0xfffff, |
(unsigned char)0xfffff, |
(unsigned short)0xfffff); |
a = (bcast = 128) + 1; |
printf("%d\n", a); |
a = (scast = 65536) + 1; |
printf("%d\n", a); |
printf("sizeof(c) = %d, sizeof((int)c) = %d\n", sizeof(c), sizeof((int)c)); |
/* test implicit int casting for array accesses */ |
c = 0; |
tab[1] = 2; |
tab[c] = 1; |
printf("%d %d\n", tab[0], tab[1]); |
/* test implicit casting on some operators */ |
printf("sizeof(+(char)'a') = %d\n", sizeof(+(char)'a')); |
printf("sizeof(-(char)'a') = %d\n", sizeof(-(char)'a')); |
printf("sizeof(~(char)'a') = %d\n", sizeof(-(char)'a')); |
} |
/* initializers tests */ |
struct structinit1 { |
int f1; |
char f2; |
short f3; |
int farray[3]; |
}; |
int sinit1 = 2; |
int sinit2 = { 3 }; |
int sinit3[3] = { 1, 2, {{3}}, }; |
int sinit4[3][2] = { {1, 2}, {3, 4}, {5, 6} }; |
int sinit5[3][2] = { 1, 2, 3, 4, 5, 6 }; |
int sinit6[] = { 1, 2, 3 }; |
int sinit7[] = { [2] = 3, [0] = 1, 2 }; |
char sinit8[] = "hello" "trala"; |
struct structinit1 sinit9 = { 1, 2, 3 }; |
struct structinit1 sinit10 = { .f2 = 2, 3, .f1 = 1 }; |
struct structinit1 sinit11 = { .f2 = 2, 3, .f1 = 1, |
#ifdef ALL_ISOC99 |
.farray[0] = 10, |
.farray[1] = 11, |
.farray[2] = 12, |
#endif |
}; |
char *sinit12 = "hello world"; |
char *sinit13[] = { |
"test1", |
"test2", |
"test3", |
}; |
char sinit14[10] = { "abc" }; |
int sinit15[3] = { sizeof(sinit15), 1, 2 }; |
struct { int a[3], b; } sinit16[] = { { 1 }, 2 }; |
struct bar { |
char *s; |
int len; |
} sinit17[] = { |
"a1", 4, |
"a2", 1 |
}; |
int sinit18[10] = { |
[2 ... 5] = 20, |
2, |
[8] = 10, |
}; |
void init_test(void) |
{ |
int linit1 = 2; |
int linit2 = { 3 }; |
int linit4[3][2] = { {1, 2}, {3, 4}, {5, 6} }; |
int linit6[] = { 1, 2, 3 }; |
int i, j; |
char linit8[] = "hello" "trala"; |
int linit12[10] = { 1, 2 }; |
int linit13[10] = { 1, 2, [7] = 3, [3] = 4, }; |
char linit14[10] = "abc"; |
int linit15[10] = { linit1, linit1 + 1, [6] = linit1 + 2, }; |
struct linit16 { int a1, a2, a3, a4; } linit16 = { 1, .a3 = 2 }; |
int linit17 = sizeof(linit17); |
printf("init_test:\n"); |
printf("sinit1=%d\n", sinit1); |
printf("sinit2=%d\n", sinit2); |
printf("sinit3=%d %d %d %d\n", |
sizeof(sinit3), |
sinit3[0], |
sinit3[1], |
sinit3[2] |
); |
printf("sinit6=%d\n", sizeof(sinit6)); |
printf("sinit7=%d %d %d %d\n", |
sizeof(sinit7), |
sinit7[0], |
sinit7[1], |
sinit7[2] |
); |
printf("sinit8=%s\n", sinit8); |
printf("sinit9=%d %d %d\n", |
sinit9.f1, |
sinit9.f2, |
sinit9.f3 |
); |
printf("sinit10=%d %d %d\n", |
sinit10.f1, |
sinit10.f2, |
sinit10.f3 |
); |
printf("sinit11=%d %d %d %d %d %d\n", |
sinit11.f1, |
sinit11.f2, |
sinit11.f3, |
sinit11.farray[0], |
sinit11.farray[1], |
sinit11.farray[2] |
); |
for(i=0;i<3;i++) |
for(j=0;j<2;j++) |
printf("[%d][%d] = %d %d %d\n", |
i, j, sinit4[i][j], sinit5[i][j], linit4[i][j]); |
printf("linit1=%d\n", linit1); |
printf("linit2=%d\n", linit2); |
printf("linit6=%d\n", sizeof(linit6)); |
printf("linit8=%d %s\n", sizeof(linit8), linit8); |
printf("sinit12=%s\n", sinit12); |
printf("sinit13=%d %s %s %s\n", |
sizeof(sinit13), |
sinit13[0], |
sinit13[1], |
sinit13[2]); |
printf("sinit14=%s\n", sinit14); |
for(i=0;i<10;i++) printf(" %d", linit12[i]); |
printf("\n"); |
for(i=0;i<10;i++) printf(" %d", linit13[i]); |
printf("\n"); |
for(i=0;i<10;i++) printf(" %d", linit14[i]); |
printf("\n"); |
for(i=0;i<10;i++) printf(" %d", linit15[i]); |
printf("\n"); |
printf("%d %d %d %d\n", |
linit16.a1, |
linit16.a2, |
linit16.a3, |
linit16.a4); |
/* test that initialisation is done after variable declare */ |
printf("linit17=%d\n", linit17); |
printf("sinit15=%d\n", sinit15[0]); |
printf("sinit16=%d %d\n", sinit16[0].a[0], sinit16[1].a[0]); |
printf("sinit17=%s %d %s %d\n", |
sinit17[0].s, sinit17[0].len, |
sinit17[1].s, sinit17[1].len); |
for(i=0;i<10;i++) |
printf("%x ", sinit18[i]); |
printf("\n"); |
} |
void switch_test() |
{ |
int i; |
for(i=0;i<15;i++) { |
switch(i) { |
case 0: |
case 1: |
printf("a"); |
break; |
default: |
printf("%d", i); |
break; |
case 8 ... 12: |
printf("c"); |
break; |
case 3: |
printf("b"); |
break; |
} |
} |
printf("\n"); |
} |
/* ISOC99 _Bool type */ |
void c99_bool_test(void) |
{ |
#ifdef BOOL_ISOC99 |
int a; |
_Bool b; |
printf("bool_test:\n"); |
printf("sizeof(_Bool) = %d\n", sizeof(_Bool)); |
a = 3; |
printf("cast: %d %d %d\n", (_Bool)10, (_Bool)0, (_Bool)a); |
b = 3; |
printf("b = %d\n", b); |
b++; |
printf("b = %d\n", b); |
#endif |
} |
void bitfield_test(void) |
{ |
int a; |
struct sbf1 { |
int f1 : 3; |
int : 2; |
int f2 : 1; |
int : 0; |
int f3 : 5; |
int f4 : 7; |
unsigned int f5 : 7; |
} st1; |
printf("bitfield_test:"); |
printf("sizeof(st1) = %d\n", sizeof(st1)); |
st1.f1 = 3; |
st1.f2 = 1; |
st1.f3 = 15; |
a = 120; |
st1.f4 = a; |
st1.f5 = a; |
st1.f5++; |
printf("%d %d %d %d %d\n", |
st1.f1, st1.f2, st1.f3, st1.f4, st1.f5); |
st1.f1 = 7; |
if (st1.f1 == -1) |
printf("st1.f1 == -1\n"); |
else |
printf("st1.f1 != -1\n"); |
if (st1.f2 == -1) |
printf("st1.f2 == -1\n"); |
else |
printf("st1.f2 != -1\n"); |
} |
#define FTEST(prefix, type, fmt)\ |
void prefix ## cmp(type a, type b)\ |
{\ |
printf("%d %d %d %d %d %d\n",\ |
a == b,\ |
a != b,\ |
a < b,\ |
a > b,\ |
a >= b,\ |
a <= b);\ |
printf(fmt " " fmt " " fmt " " fmt " " fmt " " fmt " " fmt "\n",\ |
a,\ |
b,\ |
a + b,\ |
a - b,\ |
a * b,\ |
a / b,\ |
-a);\ |
printf(fmt "\n", ++a);\ |
printf(fmt "\n", a++);\ |
printf(fmt "\n", a);\ |
}\ |
void prefix ## fcast(type a)\ |
{\ |
float fa;\ |
double da;\ |
long double la;\ |
int ia;\ |
unsigned int ua;\ |
type b;\ |
fa = a;\ |
da = a;\ |
la = a;\ |
printf("ftof: %f %f %Lf\n", fa, da, la);\ |
ia = (int)a;\ |
ua = (unsigned int)a;\ |
printf("ftoi: %d %u\n", ia, ua);\ |
ia = -1234;\ |
ua = 0x81234500;\ |
b = ia;\ |
printf("itof: " fmt "\n", b);\ |
b = ua;\ |
printf("utof: " fmt "\n", b);\ |
}\ |
\ |
void prefix ## test(void)\ |
{\ |
printf("testing '%s'\n", #type);\ |
prefix ## cmp(1, 2.5);\ |
prefix ## cmp(2, 1.5);\ |
prefix ## cmp(1, 1);\ |
prefix ## fcast(234.6);\ |
prefix ## fcast(-2334.6);\ |
} |
FTEST(f, float, "%f") |
FTEST(d, double, "%f") |
FTEST(ld, long double, "%Lf") |
double ftab1[3] = { 1.2, 3.4, -5.6 }; |
void float_test(void) |
{ |
float fa, fb; |
double da, db; |
int a; |
unsigned int b; |
printf("float_test:\n"); |
printf("sizeof(float) = %d\n", sizeof(float)); |
printf("sizeof(double) = %d\n", sizeof(double)); |
printf("sizeof(long double) = %d\n", sizeof(long double)); |
ftest(); |
dtest(); |
ldtest(); |
printf("%f %f %f\n", ftab1[0], ftab1[1], ftab1[2]); |
printf("%f %f %f\n", 2.12, .5, 2.3e10); |
// printf("%f %f %f\n", 0x1234p12, 0x1e23.23p10, 0x12dp-10); |
da = 123; |
printf("da=%f\n", da); |
fa = 123; |
printf("fa=%f\n", fa); |
a = 4000000000; |
da = a; |
printf("da = %f\n", da); |
b = 4000000000; |
db = b; |
printf("db = %f\n", db); |
} |
int fib(int n) |
{ |
if (n <= 2) |
return 1; |
else |
return fib(n-1) + fib(n-2); |
} |
void funcptr_test() |
{ |
void (*func)(int); |
int a; |
struct { |
int dummy; |
void (*func)(int); |
} st1; |
printf("funcptr:\n"); |
func = # |
(*func)(12345); |
func = num; |
a = 1; |
a = 1; |
func(12345); |
/* more complicated pointer computation */ |
st1.func = num; |
st1.func(12346); |
printf("sizeof1 = %d\n", sizeof(funcptr_test)); |
printf("sizeof2 = %d\n", sizeof funcptr_test); |
printf("sizeof3 = %d\n", sizeof(&funcptr_test)); |
printf("sizeof4 = %d\n", sizeof &funcptr_test); |
} |
void lloptest(long long a, long long b) |
{ |
unsigned long long ua, ub; |
ua = a; |
ub = b; |
/* arith */ |
printf("arith: %Ld %Ld %Ld\n", |
a + b, |
a - b, |
a * b); |
if (b != 0) { |
printf("arith1: %Ld %Ld\n", |
a / b, |
a % b); |
} |
/* binary */ |
printf("bin: %Ld %Ld %Ld\n", |
a & b, |
a | b, |
a ^ b); |
/* tests */ |
printf("test: %d %d %d %d %d %d\n", |
a == b, |
a != b, |
a < b, |
a > b, |
a >= b, |
a <= b); |
printf("utest: %d %d %d %d %d %d\n", |
ua == ub, |
ua != ub, |
ua < ub, |
ua > ub, |
ua >= ub, |
ua <= ub); |
/* arith2 */ |
a++; |
b++; |
printf("arith2: %Ld %Ld\n", a, b); |
printf("arith2: %Ld %Ld\n", a++, b++); |
printf("arith2: %Ld %Ld\n", --a, --b); |
printf("arith2: %Ld %Ld\n", a, b); |
} |
void llshift(long long a, int b) |
{ |
printf("shift: %Ld %Ld %Ld\n", |
(unsigned long long)a >> b, |
a >> b, |
a << b); |
printf("shiftc: %Ld %Ld %Ld\n", |
(unsigned long long)a >> 3, |
a >> 3, |
a << 3); |
printf("shiftc: %Ld %Ld %Ld\n", |
(unsigned long long)a >> 35, |
a >> 35, |
a << 35); |
} |
void llfloat(void) |
{ |
float fa; |
double da; |
long double lda; |
long long la, lb, lc; |
unsigned long long ula, ulb, ulc; |
la = 0x12345678; |
ula = 0x72345678; |
la = (la << 20) | 0x12345; |
ula = ula << 33; |
printf("la=%Ld ula=%Lu\n", la, ula); |
fa = la; |
da = la; |
lda = la; |
printf("lltof: %f %f %Lf\n", fa, da, lda); |
la = fa; |
lb = da; |
lc = lda; |
printf("ftoll: %Ld %Ld %Ld\n", la, lb, lc); |
fa = ula; |
da = ula; |
lda = ula; |
printf("ulltof: %f %f %Lf\n", fa, da, lda); |
ula = fa; |
ulb = da; |
ulc = lda; |
printf("ftoull: %Lu %Lu %Lu\n", ula, ulb, ulc); |
} |
long long llfunc1(int a) |
{ |
return a * 2; |
} |
struct S { |
int id; |
char item; |
}; |
long long int value(struct S *v) |
{ |
return ((long long int)v->item); |
} |
void longlong_test(void) |
{ |
long long a, b, c; |
int ia; |
unsigned int ua; |
printf("longlong_test:\n"); |
printf("sizeof(long long) = %d\n", sizeof(long long)); |
ia = -1; |
ua = -2; |
a = ia; |
b = ua; |
printf("%Ld %Ld\n", a, b); |
printf("%Ld %Ld %Ld %Lx\n", |
(long long)1, |
(long long)-2, |
1LL, |
0x1234567812345679); |
a = llfunc1(-3); |
printf("%Ld\n", a); |
lloptest(1000, 23); |
lloptest(0xff, 0x1234); |
b = 0x72345678 << 10; |
lloptest(-3, b); |
llshift(0x123, 5); |
llshift(-23, 5); |
b = 0x72345678LL << 10; |
llshift(b, 47); |
llfloat(); |
#if 1 |
b = 0x12345678; |
a = -1; |
c = a + b; |
printf("%Lx\n", c); |
#endif |
/* long long reg spill test */ |
{ |
struct S a; |
a.item = 3; |
printf("%lld\n", value(&a)); |
} |
lloptest(0x80000000, 0); |
/* another long long spill test */ |
{ |
long long *p, v; |
v = 1; |
p = &v; |
p[0]++; |
printf("%lld\n", *p); |
} |
} |
void vprintf1(const char *fmt, ...) |
{ |
va_list ap; |
const char *p; |
int c, i; |
double d; |
long long ll; |
va_start(ap, fmt); |
p = fmt; |
for(;;) { |
c = *p; |
if (c == '\0') |
break; |
p++; |
if (c == '%') { |
c = *p; |
switch(c) { |
case '\0': |
goto the_end; |
case 'd': |
i = va_arg(ap, int); |
printf("%d", i); |
break; |
case 'f': |
d = va_arg(ap, double); |
printf("%f", d); |
break; |
case 'l': |
ll = va_arg(ap, long long); |
printf("%Ld", ll); |
break; |
} |
p++; |
} else { |
putchar(c); |
} |
} |
the_end: |
va_end(ap); |
} |
void stdarg_test(void) |
{ |
vprintf1("%d %d %d\n", 1, 2, 3); |
vprintf1("%f %d %f\n", 1.0, 2, 3.0); |
vprintf1("%l %l %d %f\n", 1234567891234LL, 987654321986LL, 3, 1234.0); |
} |
void whitespace_test(void) |
{ |
char *str; |
#if 1 |
pri\ |
ntf("whitspace:\n"); |
#endif |
pf("N=%d\n", 2); |
#ifdef CORRECT_CR_HANDLING |
pri\ |
ntf("aaa=%d\n", 3); |
#endif |
pri\ |
\ |
ntf("min=%d\n", 4); |
#ifdef ACCEPT_CR_IN_STRINGS |
printf("len1=%d\n", strlen(" |
")); |
#ifdef CORRECT_CR_HANDLING |
str = " |
"; |
printf("len1=%d str[0]=%d\n", strlen(str), str[0]); |
#endif |
printf("len1=%d\n", strlen(" |
a |
")); |
#endif /* ACCEPT_CR_IN_STRINGS */ |
} |
int reltab[3] = { 1, 2, 3 }; |
int *rel1 = &reltab[1]; |
int *rel2 = &reltab[2]; |
void relocation_test(void) |
{ |
printf("*rel1=%d\n", *rel1); |
printf("*rel2=%d\n", *rel2); |
} |
void old_style_f(a,b,c) |
int a, b; |
double c; |
{ |
printf("a=%d b=%d b=%f\n", a, b, c); |
} |
void decl_func1(int cmpfn()) |
{ |
printf("cmpfn=%lx\n", (long)cmpfn); |
} |
void decl_func2(cmpfn) |
int cmpfn(); |
{ |
printf("cmpfn=%lx\n", (long)cmpfn); |
} |
void old_style_function(void) |
{ |
old_style_f((void *)1, 2, 3.0); |
decl_func1(NULL); |
decl_func2(NULL); |
} |
void sizeof_test(void) |
{ |
int a; |
int **ptr; |
printf("sizeof(int) = %d\n", sizeof(int)); |
printf("sizeof(unsigned int) = %d\n", sizeof(unsigned int)); |
printf("sizeof(short) = %d\n", sizeof(short)); |
printf("sizeof(unsigned short) = %d\n", sizeof(unsigned short)); |
printf("sizeof(char) = %d\n", sizeof(char)); |
printf("sizeof(unsigned char) = %d\n", sizeof(unsigned char)); |
printf("sizeof(func) = %d\n", sizeof sizeof_test()); |
a = 1; |
printf("sizeof(a++) = %d\n", sizeof a++); |
printf("a=%d\n", a); |
ptr = NULL; |
printf("sizeof(**ptr) = %d\n", sizeof (**ptr)); |
/* some alignof tests */ |
printf("__alignof__(int) = %d\n", __alignof__(int)); |
printf("__alignof__(unsigned int) = %d\n", __alignof__(unsigned int)); |
printf("__alignof__(short) = %d\n", __alignof__(short)); |
printf("__alignof__(unsigned short) = %d\n", __alignof__(unsigned short)); |
printf("__alignof__(char) = %d\n", __alignof__(char)); |
printf("__alignof__(unsigned char) = %d\n", __alignof__(unsigned char)); |
printf("__alignof__(func) = %d\n", __alignof__ sizeof_test()); |
} |
void typeof_test(void) |
{ |
double a; |
typeof(a) b; |
typeof(float) c; |
a = 1.5; |
b = 2.5; |
c = 3.5; |
printf("a=%f b=%f c=%f\n", a, b, c); |
} |
void statement_expr_test(void) |
{ |
int a, i; |
a = 0; |
for(i=0;i<10;i++) { |
a += 1 + |
( { int b, j; |
b = 0; |
for(j=0;j<5;j++) |
b += j; b; |
} ); |
} |
printf("a=%d\n", a); |
} |
void local_label_test(void) |
{ |
int a; |
goto l1; |
l2: |
a = 1 + ({ |
__label__ l1, l2, l3; |
goto l4; |
l5: |
printf("aa1\n"); |
goto l1; |
l2: |
printf("aa3\n"); |
goto l3; |
l1: |
printf("aa2\n"); |
goto l2; |
l3:; |
1; |
}); |
printf("a=%d\n", a); |
return; |
l1: |
printf("bb1\n"); |
goto l2; |
l4: |
printf("bb2\n"); |
goto l5; |
} |
/* inline assembler test */ |
#ifdef __i386__ |
/* from linux kernel */ |
static char * strncat1(char * dest,const char * src,size_t count) |
{ |
int d0, d1, d2, d3; |
__asm__ __volatile__( |
"repne\n\t" |
"scasb\n\t" |
"decl %1\n\t" |
"movl %8,%3\n" |
"1:\tdecl %3\n\t" |
"js 2f\n\t" |
"lodsb\n\t" |
"stosb\n\t" |
"testb %%al,%%al\n\t" |
"jne 1b\n" |
"2:\txorl %2,%2\n\t" |
"stosb" |
: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3) |
: "0" (src),"1" (dest),"2" (0),"3" (0xffffffff), "g" (count) |
: "memory"); |
return dest; |
} |
static inline void * memcpy1(void * to, const void * from, size_t n) |
{ |
int d0, d1, d2; |
__asm__ __volatile__( |
"rep ; movsl\n\t" |
"testb $2,%b4\n\t" |
"je 1f\n\t" |
"movsw\n" |
"1:\ttestb $1,%b4\n\t" |
"je 2f\n\t" |
"movsb\n" |
"2:" |
: "=&c" (d0), "=&D" (d1), "=&S" (d2) |
:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from) |
: "memory"); |
return (to); |
} |
static __inline__ void sigaddset1(unsigned int *set, int _sig) |
{ |
__asm__("btsl %1,%0" : "=m"(*set) : "Ir"(_sig - 1) : "cc"); |
} |
static __inline__ void sigdelset1(unsigned int *set, int _sig) |
{ |
asm("btrl %1,%0" : "=m"(*set) : "Ir"(_sig - 1) : "cc"); |
} |
static __inline__ __const__ unsigned int swab32(unsigned int x) |
{ |
__asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ |
"rorl $16,%0\n\t" /* swap words */ |
"xchgb %b0,%h0" /* swap higher bytes */ |
:"=q" (x) |
: "0" (x)); |
return x; |
} |
static __inline__ unsigned long long mul64(unsigned int a, unsigned int b) |
{ |
unsigned long long res; |
__asm__("mull %2" : "=A" (res) : "a" (a), "r" (b)); |
return res; |
} |
static __inline__ unsigned long long inc64(unsigned long long a) |
{ |
unsigned long long res; |
__asm__("addl $1, %%eax ; adcl $0, %%edx" : "=A" (res) : "A" (a)); |
return res; |
} |
unsigned int set; |
void asm_test(void) |
{ |
char buf[128]; |
unsigned int val; |
printf("inline asm:\n"); |
/* test the no operand case */ |
asm volatile ("xorl %eax, %eax"); |
memcpy1(buf, "hello", 6); |
strncat1(buf, " worldXXXXX", 3); |
printf("%s\n", buf); |
/* 'A' constraint test */ |
printf("mul64=0x%Lx\n", mul64(0x12345678, 0xabcd1234)); |
printf("inc64=0x%Lx\n", inc64(0x12345678ffffffff)); |
set = 0xff; |
sigdelset1(&set, 2); |
sigaddset1(&set, 16); |
/* NOTE: we test here if C labels are correctly restored after the |
asm statement */ |
goto label1; |
label2: |
__asm__("btsl %1,%0" : "=m"(set) : "Ir"(20) : "cc"); |
printf("set=0x%x\n", set); |
val = 0x01020304; |
printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val)); |
return; |
label1: |
goto label2; |
} |
#else |
void asm_test(void) |
{ |
} |
#endif |
#define COMPAT_TYPE(type1, type2) \ |
{\ |
printf("__builtin_types_compatible_p(%s, %s) = %d\n", #type1, #type2, \ |
__builtin_types_compatible_p (type1, type2));\ |
} |
int constant_p_var; |
void builtin_test(void) |
{ |
#if GCC_MAJOR >= 3 |
COMPAT_TYPE(int, int); |
COMPAT_TYPE(int, unsigned int); |
COMPAT_TYPE(int, char); |
COMPAT_TYPE(int, const int); |
COMPAT_TYPE(int, volatile int); |
COMPAT_TYPE(int *, int *); |
COMPAT_TYPE(int *, void *); |
COMPAT_TYPE(int *, const int *); |
COMPAT_TYPE(char *, unsigned char *); |
/* space is needed because tcc preprocessor introduces a space between each token */ |
COMPAT_TYPE(char * *, void *); |
#endif |
printf("res = %d\n", __builtin_constant_p(1)); |
printf("res = %d\n", __builtin_constant_p(1 + 2)); |
printf("res = %d\n", __builtin_constant_p(&constant_p_var)); |
printf("res = %d\n", __builtin_constant_p(constant_p_var)); |
} |
void const_func(const int a) |
{ |
} |
void const_warn_test(void) |
{ |
const_func(1); |
} |
/programs/develop/ktcc/trunk/source/COPYING |
---|
0,0 → 1,504 |
GNU LESSER GENERAL PUBLIC LICENSE |
Version 2.1, February 1999 |
Copyright (C) 1991, 1999 Free Software Foundation, Inc. |
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
Everyone is permitted to copy and distribute verbatim copies |
of this license document, but changing it is not allowed. |
[This is the first released version of the Lesser GPL. It also counts |
as the successor of the GNU Library Public License, version 2, hence |
the version number 2.1.] |
Preamble |
The licenses for most software are designed to take away your |
freedom to share and change it. By contrast, the GNU General Public |
Licenses are intended to guarantee your freedom to share and change |
free software--to make sure the software is free for all its users. |
This license, the Lesser General Public License, applies to some |
specially designated software packages--typically libraries--of the |
Free Software Foundation and other authors who decide to use it. You |
can use it too, but we suggest you first think carefully about whether |
this license or the ordinary General Public License is the better |
strategy to use in any particular case, based on the explanations below. |
When we speak of free software, we are referring to freedom of use, |
not price. Our General Public Licenses are designed to make sure that |
you have the freedom to distribute copies of free software (and charge |
for this service if you wish); that you receive source code or can get |
it if you want it; that you can change the software and use pieces of |
it in new free programs; and that you are informed that you can do |
these things. |
To protect your rights, we need to make restrictions that forbid |
distributors to deny you these rights or to ask you to surrender these |
rights. These restrictions translate to certain responsibilities for |
you if you distribute copies of the library or if you modify it. |
For example, if you distribute copies of the library, whether gratis |
or for a fee, you must give the recipients all the rights that we gave |
you. You must make sure that they, too, receive or can get the source |
code. If you link other code with the library, you must provide |
complete object files to the recipients, so that they can relink them |
with the library after making changes to the library and recompiling |
it. And you must show them these terms so they know their rights. |
We protect your rights with a two-step method: (1) we copyright the |
library, and (2) we offer you this license, which gives you legal |
permission to copy, distribute and/or modify the library. |
To protect each distributor, we want to make it very clear that |
there is no warranty for the free library. Also, if the library is |
modified by someone else and passed on, the recipients should know |
that what they have is not the original version, so that the original |
author's reputation will not be affected by problems that might be |
introduced by others. |
Finally, software patents pose a constant threat to the existence of |
any free program. We wish to make sure that a company cannot |
effectively restrict the users of a free program by obtaining a |
restrictive license from a patent holder. Therefore, we insist that |
any patent license obtained for a version of the library must be |
consistent with the full freedom of use specified in this license. |
Most GNU software, including some libraries, is covered by the |
ordinary GNU General Public License. This license, the GNU Lesser |
General Public License, applies to certain designated libraries, and |
is quite different from the ordinary General Public License. We use |
this license for certain libraries in order to permit linking those |
libraries into non-free programs. |
When a program is linked with a library, whether statically or using |
a shared library, the combination of the two is legally speaking a |
combined work, a derivative of the original library. The ordinary |
General Public License therefore permits such linking only if the |
entire combination fits its criteria of freedom. The Lesser General |
Public License permits more lax criteria for linking other code with |
the library. |
We call this license the "Lesser" General Public License because it |
does Less to protect the user's freedom than the ordinary General |
Public License. It also provides other free software developers Less |
of an advantage over competing non-free programs. These disadvantages |
are the reason we use the ordinary General Public License for many |
libraries. However, the Lesser license provides advantages in certain |
special circumstances. |
For example, on rare occasions, there may be a special need to |
encourage the widest possible use of a certain library, so that it becomes |
a de-facto standard. To achieve this, non-free programs must be |
allowed to use the library. A more frequent case is that a free |
library does the same job as widely used non-free libraries. In this |
case, there is little to gain by limiting the free library to free |
software only, so we use the Lesser General Public License. |
In other cases, permission to use a particular library in non-free |
programs enables a greater number of people to use a large body of |
free software. For example, permission to use the GNU C Library in |
non-free programs enables many more people to use the whole GNU |
operating system, as well as its variant, the GNU/Linux operating |
system. |
Although the Lesser General Public License is Less protective of the |
users' freedom, it does ensure that the user of a program that is |
linked with the Library has the freedom and the wherewithal to run |
that program using a modified version of the Library. |
The precise terms and conditions for copying, distribution and |
modification follow. Pay close attention to the difference between a |
"work based on the library" and a "work that uses the library". The |
former contains code derived from the library, whereas the latter must |
be combined with the library in order to run. |
GNU LESSER GENERAL PUBLIC LICENSE |
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
0. This License Agreement applies to any software library or other |
program which contains a notice placed by the copyright holder or |
other authorized party saying it may be distributed under the terms of |
this Lesser General Public License (also called "this License"). |
Each licensee is addressed as "you". |
A "library" means a collection of software functions and/or data |
prepared so as to be conveniently linked with application programs |
(which use some of those functions and data) to form executables. |
The "Library", below, refers to any such software library or work |
which has been distributed under these terms. A "work based on the |
Library" means either the Library or any derivative work under |
copyright law: that is to say, a work containing the Library or a |
portion of it, either verbatim or with modifications and/or translated |
straightforwardly into another language. (Hereinafter, translation is |
included without limitation in the term "modification".) |
"Source code" for a work means the preferred form of the work for |
making modifications to it. For a library, complete source code means |
all the source code for all modules it contains, plus any associated |
interface definition files, plus the scripts used to control compilation |
and installation of the library. |
Activities other than copying, distribution and modification are not |
covered by this License; they are outside its scope. The act of |
running a program using the Library is not restricted, and output from |
such a program is covered only if its contents constitute a work based |
on the Library (independent of the use of the Library in a tool for |
writing it). Whether that is true depends on what the Library does |
and what the program that uses the Library does. |
1. You may copy and distribute verbatim copies of the Library's |
complete source code as you receive it, in any medium, provided that |
you conspicuously and appropriately publish on each copy an |
appropriate copyright notice and disclaimer of warranty; keep intact |
all the notices that refer to this License and to the absence of any |
warranty; and distribute a copy of this License along with the |
Library. |
You may charge a fee for the physical act of transferring a copy, |
and you may at your option offer warranty protection in exchange for a |
fee. |
2. You may modify your copy or copies of the Library or any portion |
of it, thus forming a work based on the Library, and copy and |
distribute such modifications or work under the terms of Section 1 |
above, provided that you also meet all of these conditions: |
a) The modified work must itself be a software library. |
b) You must cause the files modified to carry prominent notices |
stating that you changed the files and the date of any change. |
c) You must cause the whole of the work to be licensed at no |
charge to all third parties under the terms of this License. |
d) If a facility in the modified Library refers to a function or a |
table of data to be supplied by an application program that uses |
the facility, other than as an argument passed when the facility |
is invoked, then you must make a good faith effort to ensure that, |
in the event an application does not supply such function or |
table, the facility still operates, and performs whatever part of |
its purpose remains meaningful. |
(For example, a function in a library to compute square roots has |
a purpose that is entirely well-defined independent of the |
application. Therefore, Subsection 2d requires that any |
application-supplied function or table used by this function must |
be optional: if the application does not supply it, the square |
root function must still compute square roots.) |
These requirements apply to the modified work as a whole. If |
identifiable sections of that work are not derived from the Library, |
and can be reasonably considered independent and separate works in |
themselves, then this License, and its terms, do not apply to those |
sections when you distribute them as separate works. But when you |
distribute the same sections as part of a whole which is a work based |
on the Library, the distribution of the whole must be on the terms of |
this License, whose permissions for other licensees extend to the |
entire whole, and thus to each and every part regardless of who wrote |
it. |
Thus, it is not the intent of this section to claim rights or contest |
your rights to work written entirely by you; rather, the intent is to |
exercise the right to control the distribution of derivative or |
collective works based on the Library. |
In addition, mere aggregation of another work not based on the Library |
with the Library (or with a work based on the Library) on a volume of |
a storage or distribution medium does not bring the other work under |
the scope of this License. |
3. You may opt to apply the terms of the ordinary GNU General Public |
License instead of this License to a given copy of the Library. To do |
this, you must alter all the notices that refer to this License, so |
that they refer to the ordinary GNU General Public License, version 2, |
instead of to this License. (If a newer version than version 2 of the |
ordinary GNU General Public License has appeared, then you can specify |
that version instead if you wish.) Do not make any other change in |
these notices. |
Once this change is made in a given copy, it is irreversible for |
that copy, so the ordinary GNU General Public License applies to all |
subsequent copies and derivative works made from that copy. |
This option is useful when you wish to copy part of the code of |
the Library into a program that is not a library. |
4. You may copy and distribute the Library (or a portion or |
derivative of it, under Section 2) in object code or executable form |
under the terms of Sections 1 and 2 above provided that you accompany |
it with the complete corresponding machine-readable source code, which |
must be distributed under the terms of Sections 1 and 2 above on a |
medium customarily used for software interchange. |
If distribution of object code is made by offering access to copy |
from a designated place, then offering equivalent access to copy the |
source code from the same place satisfies the requirement to |
distribute the source code, even though third parties are not |
compelled to copy the source along with the object code. |
5. A program that contains no derivative of any portion of the |
Library, but is designed to work with the Library by being compiled or |
linked with it, is called a "work that uses the Library". Such a |
work, in isolation, is not a derivative work of the Library, and |
therefore falls outside the scope of this License. |
However, linking a "work that uses the Library" with the Library |
creates an executable that is a derivative of the Library (because it |
contains portions of the Library), rather than a "work that uses the |
library". The executable is therefore covered by this License. |
Section 6 states terms for distribution of such executables. |
When a "work that uses the Library" uses material from a header file |
that is part of the Library, the object code for the work may be a |
derivative work of the Library even though the source code is not. |
Whether this is true is especially significant if the work can be |
linked without the Library, or if the work is itself a library. The |
threshold for this to be true is not precisely defined by law. |
If such an object file uses only numerical parameters, data |
structure layouts and accessors, and small macros and small inline |
functions (ten lines or less in length), then the use of the object |
file is unrestricted, regardless of whether it is legally a derivative |
work. (Executables containing this object code plus portions of the |
Library will still fall under Section 6.) |
Otherwise, if the work is a derivative of the Library, you may |
distribute the object code for the work under the terms of Section 6. |
Any executables containing that work also fall under Section 6, |
whether or not they are linked directly with the Library itself. |
6. As an exception to the Sections above, you may also combine or |
link a "work that uses the Library" with the Library to produce a |
work containing portions of the Library, and distribute that work |
under terms of your choice, provided that the terms permit |
modification of the work for the customer's own use and reverse |
engineering for debugging such modifications. |
You must give prominent notice with each copy of the work that the |
Library is used in it and that the Library and its use are covered by |
this License. You must supply a copy of this License. If the work |
during execution displays copyright notices, you must include the |
copyright notice for the Library among them, as well as a reference |
directing the user to the copy of this License. Also, you must do one |
of these things: |
a) Accompany the work with the complete corresponding |
machine-readable source code for the Library including whatever |
changes were used in the work (which must be distributed under |
Sections 1 and 2 above); and, if the work is an executable linked |
with the Library, with the complete machine-readable "work that |
uses the Library", as object code and/or source code, so that the |
user can modify the Library and then relink to produce a modified |
executable containing the modified Library. (It is understood |
that the user who changes the contents of definitions files in the |
Library will not necessarily be able to recompile the application |
to use the modified definitions.) |
b) Use a suitable shared library mechanism for linking with the |
Library. A suitable mechanism is one that (1) uses at run time a |
copy of the library already present on the user's computer system, |
rather than copying library functions into the executable, and (2) |
will operate properly with a modified version of the library, if |
the user installs one, as long as the modified version is |
interface-compatible with the version that the work was made with. |
c) Accompany the work with a written offer, valid for at |
least three years, to give the same user the materials |
specified in Subsection 6a, above, for a charge no more |
than the cost of performing this distribution. |
d) If distribution of the work is made by offering access to copy |
from a designated place, offer equivalent access to copy the above |
specified materials from the same place. |
e) Verify that the user has already received a copy of these |
materials or that you have already sent this user a copy. |
For an executable, the required form of the "work that uses the |
Library" must include any data and utility programs needed for |
reproducing the executable from it. However, as a special exception, |
the materials to be distributed need not include anything that is |
normally distributed (in either source or binary form) with the major |
components (compiler, kernel, and so on) of the operating system on |
which the executable runs, unless that component itself accompanies |
the executable. |
It may happen that this requirement contradicts the license |
restrictions of other proprietary libraries that do not normally |
accompany the operating system. Such a contradiction means you cannot |
use both them and the Library together in an executable that you |
distribute. |
7. You may place library facilities that are a work based on the |
Library side-by-side in a single library together with other library |
facilities not covered by this License, and distribute such a combined |
library, provided that the separate distribution of the work based on |
the Library and of the other library facilities is otherwise |
permitted, and provided that you do these two things: |
a) Accompany the combined library with a copy of the same work |
based on the Library, uncombined with any other library |
facilities. This must be distributed under the terms of the |
Sections above. |
b) Give prominent notice with the combined library of the fact |
that part of it is a work based on the Library, and explaining |
where to find the accompanying uncombined form of the same work. |
8. You may not copy, modify, sublicense, link with, or distribute |
the Library except as expressly provided under this License. Any |
attempt otherwise to copy, modify, sublicense, link with, or |
distribute the Library is void, and will automatically terminate your |
rights under this License. However, parties who have received copies, |
or rights, from you under this License will not have their licenses |
terminated so long as such parties remain in full compliance. |
9. You are not required to accept this License, since you have not |
signed it. However, nothing else grants you permission to modify or |
distribute the Library or its derivative works. These actions are |
prohibited by law if you do not accept this License. Therefore, by |
modifying or distributing the Library (or any work based on the |
Library), you indicate your acceptance of this License to do so, and |
all its terms and conditions for copying, distributing or modifying |
the Library or works based on it. |
10. Each time you redistribute the Library (or any work based on the |
Library), the recipient automatically receives a license from the |
original licensor to copy, distribute, link with or modify the Library |
subject to these terms and conditions. You may not impose any further |
restrictions on the recipients' exercise of the rights granted herein. |
You are not responsible for enforcing compliance by third parties with |
this License. |
11. If, as a consequence of a court judgment or allegation of patent |
infringement or for any other reason (not limited to patent issues), |
conditions are imposed on you (whether by court order, agreement or |
otherwise) that contradict the conditions of this License, they do not |
excuse you from the conditions of this License. If you cannot |
distribute so as to satisfy simultaneously your obligations under this |
License and any other pertinent obligations, then as a consequence you |
may not distribute the Library at all. For example, if a patent |
license would not permit royalty-free redistribution of the Library by |
all those who receive copies directly or indirectly through you, then |
the only way you could satisfy both it and this License would be to |
refrain entirely from distribution of the Library. |
If any portion of this section is held invalid or unenforceable under any |
particular circumstance, the balance of the section is intended to apply, |
and the section as a whole is intended to apply in other circumstances. |
It is not the purpose of this section to induce you to infringe any |
patents or other property right claims or to contest validity of any |
such claims; this section has the sole purpose of protecting the |
integrity of the free software distribution system which is |
implemented by public license practices. Many people have made |
generous contributions to the wide range of software distributed |
through that system in reliance on consistent application of that |
system; it is up to the author/donor to decide if he or she is willing |
to distribute software through any other system and a licensee cannot |
impose that choice. |
This section is intended to make thoroughly clear what is believed to |
be a consequence of the rest of this License. |
12. If the distribution and/or use of the Library is restricted in |
certain countries either by patents or by copyrighted interfaces, the |
original copyright holder who places the Library under this License may add |
an explicit geographical distribution limitation excluding those countries, |
so that distribution is permitted only in or among countries not thus |
excluded. In such case, this License incorporates the limitation as if |
written in the body of this License. |
13. The Free Software Foundation may publish revised and/or new |
versions of the Lesser General Public License from time to time. |
Such new versions will be similar in spirit to the present version, |
but may differ in detail to address new problems or concerns. |
Each version is given a distinguishing version number. If the Library |
specifies a version number of this License which applies to it and |
"any later version", you have the option of following the terms and |
conditions either of that version or of any later version published by |
the Free Software Foundation. If the Library does not specify a |
license version number, you may choose any version ever published by |
the Free Software Foundation. |
14. If you wish to incorporate parts of the Library into other free |
programs whose distribution conditions are incompatible with these, |
write to the author to ask for permission. For software which is |
copyrighted by the Free Software Foundation, write to the Free |
Software Foundation; we sometimes make exceptions for this. Our |
decision will be guided by the two goals of preserving the free status |
of all derivatives of our free software and of promoting the sharing |
and reuse of software generally. |
NO WARRANTY |
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO |
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. |
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR |
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY |
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE |
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME |
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN |
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY |
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU |
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE |
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING |
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A |
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF |
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
DAMAGES. |
END OF TERMS AND CONDITIONS |
How to Apply These Terms to Your New Libraries |
If you develop a new library, and you want it to be of the greatest |
possible use to the public, we recommend making it free software that |
everyone can redistribute and change. You can do so by permitting |
redistribution under these terms (or, alternatively, under the terms of the |
ordinary General Public License). |
To apply these terms, attach the following notices to the library. It is |
safest to attach them to the start of each source file to most effectively |
convey the exclusion of warranty; and each file should have at least the |
"copyright" line and a pointer to where the full notice is found. |
<one line to give the library's name and a brief idea of what it does.> |
Copyright (C) <year> <name of author> |
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 |
Also add information on how to contact you by electronic and paper mail. |
You should also get your employer (if you work as a programmer) or your |
school, if any, to sign a "copyright disclaimer" for the library, if |
necessary. Here is a sample; alter the names: |
Yoyodyne, Inc., hereby disclaims all copyright interest in the |
library `Frob' (a library for tweaking knobs) written by James Random Hacker. |
<signature of Ty Coon>, 1 April 1990 |
Ty Coon, President of Vice |
That's all there is to it! |
/programs/develop/ktcc/trunk/source/VERSION |
---|
0,0 → 1,0 |
0.9.23 |
/programs/develop/ktcc/trunk/source/arm-gen.c |
---|
0,0 → 1,1386 |
/* |
* ARMv4 code generator for TCC |
* |
* Copyright (c) 2003 Daniel Glöckner |
* |
* 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 |
*/ |
/* number of available registers */ |
#define NB_REGS 9 |
/* 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 |
#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, |
}; |
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, |
}; |
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); |
} |
/* 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 */ |
/* 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 4 |
/* 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_JMP_SLOT R_ARM_JUMP_SLOT |
#define R_COPY R_ARM_COPY |
#define ELF_START_ADDR 0x00008000 |
#define ELF_PAGE_SIZE 0x1000 |
/******************************************************/ |
static unsigned long func_sub_sp_offset,last_itod_magic; |
void o(unsigned long i) |
{ |
/* this is a good place to start adding big-endian support*/ |
int ind1; |
ind1 = ind + 4; |
if (!cur_text_section) |
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 unsigned long stuff_const(unsigned long op,unsigned long c) |
{ |
int try_neg=0; |
unsigned long 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 { |
unsigned long 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(unsigned long op,unsigned long v) { |
unsigned long x; |
x=stuff_const(op,v); |
if(x) |
o(x); |
else { |
unsigned long 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<13+i;i++) |
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<13+i;i++) |
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;i<12;i++) |
for(k=j+4;k<13+i;i++) |
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;i<12;i++) |
for(k=j+4;k<13+i;i++) |
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])); |
} |
} |
unsigned long encbranch(int pos,int addr,int fail) |
{ |
addr-=pos+8; |
addr/=4; |
if(addr>=0x1000000 || addr<-0x1000000) { |
if(fail) |
error("FIXME: function bigger than 32MB"); |
return 0; |
} |
return 0x0A000000|(addr&0xffffff); |
} |
int decbranch(int pos) |
{ |
int x; |
x=*(int *)(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) |
{ |
unsigned long *x; |
int lt; |
while(t) { |
x=(unsigned long *)(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); |
} |
static unsigned long fpr(int r) |
{ |
if(r<TREG_F0 || r>TREG_F3) |
error("compiler error! register %i is no fp register\n",r); |
return r-5; |
} |
static unsigned long intr(int r) |
{ |
if(r==4) |
return 12; |
if((r<0 || r>4) && r!=14) |
error("compiler error! register %i is no int register\n",r); |
return r; |
} |
static void calcaddr(unsigned long *base,int *off,int *sgn,int maxoff,unsigned shift) |
{ |
if(*off>maxoff || *off&((1<<shift)-1)) { |
unsigned long 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 unsigned long mapcc(int cc) |
{ |
switch(cc) |
{ |
case TOK_ULT: |
return 0x30000000; |
case TOK_UGE: |
return 0x20000000; |
case TOK_EQ: |
return 0x00000000; |
case TOK_NE: |
return 0x10000000; |
case TOK_ULE: |
return 0x90000000; |
case TOK_UGT: |
return 0x80000000; |
case TOK_LT: |
return 0xB0000000; |
case TOK_GE: |
return 0xA0000000; |
case TOK_LE: |
return 0xD0000000; |
case TOK_GT: |
return 0xC0000000; |
} |
error("unexpected condition code"); |
return 0xE0000000; |
} |
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_LT: |
return TOK_GE; |
case TOK_GE: |
return TOK_LT; |
case TOK_LE: |
return TOK_GT; |
case TOK_GT: |
return TOK_LE; |
} |
error("unexpected condition code"); |
return TOK_NE; |
} |
/* load 'r' from value 'sv' */ |
void load(int r, SValue *sv) |
{ |
int v, ft, fc, fr, sign; |
unsigned long 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) { |
unsigned long 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); |
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)); |
} else if((ft & VT_TYPE) == 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) |
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)) |
o(0xEE008180|(fpr(r)<<12)|fpr(v)); |
else |
o(0xE1A00000|(intr(r)<<12)|intr(v)); |
return; |
} |
} |
error("load unimplemented!"); |
} |
/* store register 'r' in lvalue 'v' */ |
void store(int r, SValue *sv) |
{ |
SValue v1; |
int v, ft, fc, fr, sign; |
unsigned long 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) { |
unsigned long 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); |
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)); |
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) |
op|=0x400000; |
o(op|(intr(r)<<12)|fc|(base<<16)); |
} |
return; |
} |
} |
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) { |
unsigned long 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 |
} |
} |
/* Generate function call. The function address is pushed first, then |
all the parameters in call order. This functions pops all the |
parameters and the function address. */ |
void gfunc_call(int nb_args) |
{ |
int size, align, r, args_size, i; |
Sym *func_sym; |
signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; |
int todo=0xf, keep, plan2[4]={0,0,0,0}; |
r = vtop->r & VT_VALMASK; |
if (r == VT_CMP || (r & ~1) == VT_JMP) |
gv(RC_INT); |
args_size = 0; |
for(i = nb_args ; i-- && args_size < 16 ;) { |
if ((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT) { |
size = type_size(&vtop[-i].type, &align); |
size = (size + 3) & ~3; |
args_size += size; |
} else if ((vtop[-i].type.t & VT_BTYPE) == VT_FLOAT) |
args_size += 4; |
else if ((vtop[-i].type.t & VT_BTYPE) == VT_DOUBLE) |
args_size += 8; |
else if ((vtop[-i].type.t & VT_BTYPE) == VT_LDOUBLE) |
args_size += LDOUBLE_SIZE; |
else { |
plan[nb_args-1-i][0]=args_size/4; |
args_size += 4; |
if ((vtop[-i].type.t & VT_BTYPE) == VT_LLONG && args_size < 16) { |
plan[nb_args-1-i][1]=args_size/4; |
args_size += 4; |
} |
} |
} |
args_size = keep = 0; |
for(i = 0;i < nb_args; i++) { |
vnrott(keep+1); |
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 */ |
gadd_sp(-size); |
/* generate structure store */ |
r = get_reg(RC_INT); |
o(0xE1A0000D|(intr(r)<<12)); |
vset(&vtop->type, r | VT_LVAL, 0); |
vswap(); |
vstore(); |
vtop--; |
args_size += size; |
} else if (is_float(vtop->type.t)) { |
r=fpr(gv(RC_FLOAT))<<12; |
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) |
size = 4; |
else if ((vtop->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)); |
vtop--; |
args_size += size; |
} else { |
int s; |
/* simple type (currently always same size) */ |
/* XXX: implicit cast ? */ |
size=4; |
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
lexpand_nr(); |
s=RC_INT; |
if(nb_args-i<5 && plan[nb_args-i-1][1]!=-1) { |
s=regmask(plan[nb_args-i-1][1]); |
todo&=~(1<<plan[nb_args-i-1][1]); |
} |
if(s==RC_INT) { |
r = gv(s); |
o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ |
vtop--; |
} else { |
plan2[keep]=s; |
keep++; |
vswap(); |
} |
size = 8; |
} |
s=RC_INT; |
if(nb_args-i<5 && plan[nb_args-i-1][0]!=-1) { |
s=regmask(plan[nb_args-i-1][0]); |
todo&=~(1<<plan[nb_args-i-1][0]); |
} |
if(s==RC_INT) { |
r = gv(s); |
o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ |
vtop--; |
} else { |
plan2[keep]=s; |
keep++; |
} |
args_size += size; |
} |
} |
for(i=keep;i--;) { |
gv(plan2[i]); |
vrott(keep); |
} |
save_regs(keep); /* save used temporary registers */ |
keep++; |
if(args_size) { |
int n; |
n=args_size/4; |
if(n>4) |
n=4; |
todo&=((1<<n)-1); |
if(todo) { |
int i; |
o(0xE8BD0000|todo); |
for(i=0;i<4;i++) |
if(todo&(1<<i)) { |
vpushi(0); |
vtop->r=i; |
keep++; |
} |
} |
args_size-=n*4; |
} |
vnrott(keep); |
func_sym = vtop->type.ref; |
gcall_or_jmp(0); |
if (args_size) |
gadd_sp(args_size); |
vtop-=keep; |
} |
/* generate function prolog of type 't' */ |
void gfunc_prolog(CType *func_type) |
{ |
Sym *sym,*sym2; |
int n,addr,size,align; |
sym = func_type->ref; |
func_vt = sym->type; |
n=0; |
addr=12; |
if((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
func_vc = addr; |
addr += 4; |
n++; |
} |
for(sym2=sym->next;sym2 && n<4;sym2=sym2->next) { |
size = type_size(&sym2->type, &align); |
size = (size + 3) & ~3; |
n+=size/4; |
} |
o(0xE1A0C00D); /* mov ip,sp */ |
if(func_type->ref->c == FUNC_ELLIPSIS) |
n=4; |
if(n) { |
if(n>4) |
n=4; |
o(0xE92D0000|((1<<n)-1)); /* save r0-r4 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 */ |
while ((sym = sym->next)) { |
CType *type; |
type = &sym->type; |
sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); |
size = type_size(type, &align); |
size = (size + 3) & ~3; |
addr += size; |
} |
last_itod_magic=0; |
loc = 0; |
} |
/* generate function epilog */ |
void gfunc_epilog(void) |
{ |
unsigned long x; |
o(0xE89BA800); /* restore fp, sp, pc */ |
if(loc) { |
x=stuff_const(0xE24DD000, (-loc + 3) & -4); /* sub sp,sp,# */ |
if(x) |
*(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = x; |
else { |
unsigned long addr; |
addr=ind; |
o(0xE59FC004); /* ldr ip,[pc+4] */ |
o(0xE04DD00C); /* sub sp,sp,ip */ |
o(0xE1A0F00E); /* mov pc,lr */ |
o((-loc + 3) & -4); |
*(unsigned long *)(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; |
unsigned long 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 { |
unsigned long *x; |
int p,lp; |
if(t) { |
p = vtop->c.i; |
do { |
p = decbranch(lp=p); |
} while(p); |
x = (unsigned long *)(cur_text_section->data + lp); |
*x &= 0xff000000; |
*x |= encbranch(lp,t,1); |
} |
t = vtop->c.i; |
} |
} else { |
t = gjmp(t); |
gsym(vtop->c.i); |
} |
} else { |
if (is_float(vtop->type.t)) { |
r=gv(RC_FLOAT); |
o(0xEE90F118|fpr(r)<<16); |
vtop->r = VT_CMP; |
vtop->c.i = TOK_NE; |
return gtst(inv, t); |
} else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
/* constant jmp optimization */ |
if ((vtop->c.i != 0) != inv) |
t = gjmp(t); |
} else { |
v = gv(RC_INT); |
o(0xE3300000|(intr(v)<<16)); |
vtop->r = VT_CMP; |
vtop->c.i = TOK_NE; |
return gtst(inv, t); |
} |
} |
vtop--; |
return t; |
} |
/* generate an integer binary operation */ |
void gen_opi(int op) |
{ |
int c, func = 0; |
unsigned long opc = 0,r,fr; |
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 '%': |
func=TOK___modsi3; |
c=3; |
break; |
case TOK_UMOD: |
func=TOK___umodsi3; |
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) { |
unsigned long 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 = REG_IRET; |
break; |
default: |
error("gen_opi %i unimplemented!",op); |
} |
} |
static int is_fconst() |
{ |
long double f; |
int 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) |
{ |
unsigned long x; |
int 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 |
switch(op) { |
case TOK_ULT: |
case TOK_UGE: |
case TOK_ULE: |
case TOK_UGT: |
fputs("unsigned comparision on floats?\n",stderr); |
break; |
case TOK_LT: |
op=TOK_ULT; |
break; |
case TOK_GE: |
op=TOK_UGE; |
break; |
case TOK_LE: |
op=TOK_ULE; |
break; |
case TOK_GT: |
op=TOK_UGT; |
break; |
case TOK_EQ: |
case TOK_NE: |
x&=~0x400000; // cmfe -> cmf |
break; |
} |
if(c1 && !c2) { |
c2=c1; |
vswap(); |
switch(op) { |
case TOK_ULT: |
op=TOK_UGT; |
break; |
case TOK_UGE: |
op=TOK_ULE; |
break; |
case TOK_ULE: |
op=TOK_UGE; |
break; |
case TOK_UGT: |
op=TOK_ULT; |
break; |
} |
} |
// bug (intention?) in Linux FPU emulator |
// doesn't set carry if equal |
if(op==TOK_ULT) |
op=TOK_LT; |
else if(op==TOK_UGE) |
op=TOK_GE; |
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 { |
error("unknown fp op %x!\n",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); |
} |
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' |
and 'long long' cases. */ |
void gen_cvt_itof(int t) |
{ |
int r,r2,bt; |
bt=vtop->type.t & VT_BTYPE; |
if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { |
r=intr(gv(RC_INT)); |
r2=fpr(vtop->r=get_reg(RC_FLOAT)); |
o(0xEE000190|(r2<<16)|(r<<12)); |
if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { |
unsigned int off=0; |
o(0xE3500000|(r<<12)); |
r=fpr(get_reg(RC_FLOAT)); |
if(last_itod_magic) { |
off=ind+8-last_itod_magic; |
off/=4; |
if(off>255) |
off=0; |
} |
o(0xBD1F8100|(r<<12)|off); |
if(!off) { |
o(0xEA000001); |
last_itod_magic=ind; |
o(0x41F00000); |
o(0); |
} |
o(0xBE000180|(r2<<16)|(r2<<12)|r); |
} |
return; |
} else if(bt == VT_LLONG) { |
int func; |
if(vtop->type.t & VT_UNSIGNED) |
func=TOK___ulltold; |
else |
func=TOK___slltold; |
vpush_global_sym(&func_old_type, func); |
vswap(); |
gfunc_call(1); |
vpushi(0); |
vtop->r=TREG_F0; |
return; |
} |
error("unimplemented gen_cvt_itof %x!",vtop->type.t); |
} |
/* convert fp to int 't' type */ |
void gen_cvt_ftoi(int t) |
{ |
int r,r2,u,func=0; |
u=t&VT_UNSIGNED; |
t&=VT_BTYPE; |
r2=vtop->type.t & VT_BTYPE; |
if(t==VT_INT) { |
if(u) { |
if(r2 == VT_FLOAT) |
func=TOK___fixunssfsi; |
else if(r2 == VT_DOUBLE) |
func=TOK___fixunsdfsi; |
else if(r2 == VT_LDOUBLE) |
#if LDOUBLE_SIZE == 8 |
func=TOK___fixunsdfsi; |
#else |
func=TOK___fixunsxfsi; |
#endif |
} else { |
r=fpr(gv(RC_FLOAT)); |
r2=intr(vtop->r=get_reg(RC_INT)); |
o(0xEE100170|(r2<<12)|r); |
return; |
} |
} else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 |
if(r2 == VT_FLOAT) |
func=TOK___fixsfdi; |
else if(r2 == VT_DOUBLE) |
func=TOK___fixdfdi; |
else if(r2 == VT_LDOUBLE) |
#if LDOUBLE_SIZE == 8 |
func=TOK___fixdfdi; |
#else |
func=TOK___fixxfdi; |
#endif |
} |
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; |
} |
error("unimplemented gen_cvt_ftoi!"); |
} |
/* convert from one floating point type to another */ |
void gen_cvt_ftof(int t) |
{ |
/* all we have to do on i386 and ARM is to put the float in a register */ |
gv(RC_FLOAT); |
} |
/* computed goto support */ |
void ggoto(void) |
{ |
gcall_or_jmp(1); |
vtop--; |
} |
/* end of ARM code generator */ |
/*************************************************************/ |
/programs/develop/ktcc/trunk/source/bcheck.c |
---|
0,0 → 1,867 |
/* |
* Tiny C Memory and bounds checker |
* |
* 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. |
*/ |
#include <stdlib.h> |
#include <stdio.h> |
#include <stdarg.h> |
#include <string.h> |
#ifndef __FreeBSD__ |
#include <malloc.h> |
#endif |
//#define BOUND_DEBUG |
/* define so that bound array is static (faster, but use memory if |
bound checking not used) */ |
//#define BOUND_STATIC |
/* use malloc hooks. Currently the code cannot be reliable if no hooks */ |
#define CONFIG_TCC_MALLOC_HOOKS |
#define HAVE_MEMALIGN |
#if defined(__FreeBSD__) || defined(__dietlibc__) |
#warning Bound checking not fully supported on FreeBSD |
#undef CONFIG_TCC_MALLOC_HOOKS |
#undef HAVE_MEMALIGN |
#endif |
#define BOUND_T1_BITS 13 |
#define BOUND_T2_BITS 11 |
#define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS) |
#define BOUND_T1_SIZE (1 << BOUND_T1_BITS) |
#define BOUND_T2_SIZE (1 << BOUND_T2_BITS) |
#define BOUND_T3_SIZE (1 << BOUND_T3_BITS) |
#define BOUND_E_BITS 4 |
#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS) |
#define BOUND_T23_SIZE (1 << BOUND_T23_BITS) |
/* this pointer is generated when bound check is incorrect */ |
#define INVALID_POINTER ((void *)(-2)) |
/* size of an empty region */ |
#define EMPTY_SIZE 0xffffffff |
/* size of an invalid region */ |
#define INVALID_SIZE 0 |
typedef struct BoundEntry { |
unsigned long start; |
unsigned long size; |
struct BoundEntry *next; |
unsigned long is_invalid; /* true if pointers outside region are invalid */ |
} BoundEntry; |
/* external interface */ |
void __bound_init(void); |
void __bound_new_region(void *p, unsigned long size); |
int __bound_delete_region(void *p); |
#define FASTCALL __attribute__((regparm(3))) |
void *__bound_malloc(size_t size, const void *caller); |
void *__bound_memalign(size_t size, size_t align, const void *caller); |
void __bound_free(void *ptr, const void *caller); |
void *__bound_realloc(void *ptr, size_t size, const void *caller); |
static void *libc_malloc(size_t size); |
static void libc_free(void *ptr); |
static void install_malloc_hooks(void); |
static void restore_malloc_hooks(void); |
#ifdef CONFIG_TCC_MALLOC_HOOKS |
static void *saved_malloc_hook; |
static void *saved_free_hook; |
static void *saved_realloc_hook; |
static void *saved_memalign_hook; |
#endif |
/* linker definitions */ |
extern char _end; |
/* TCC definitions */ |
extern char __bounds_start; /* start of static bounds table */ |
/* error message, just for TCC */ |
const char *__bound_error_msg; |
/* runtime error output */ |
extern void rt_error(unsigned long pc, const char *fmt, ...); |
#ifdef BOUND_STATIC |
static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */ |
#else |
static BoundEntry **__bound_t1; /* page table */ |
#endif |
static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */ |
static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */ |
static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) |
{ |
unsigned long addr, tmp; |
BoundEntry *e; |
e = e1; |
while (e != NULL) { |
addr = (unsigned long)p; |
addr -= e->start; |
if (addr <= e->size) { |
/* put region at the head */ |
tmp = e1->start; |
e1->start = e->start; |
e->start = tmp; |
tmp = e1->size; |
e1->size = e->size; |
e->size = tmp; |
return e1; |
} |
e = e->next; |
} |
/* no entry found: return empty entry or invalid entry */ |
if (e1->is_invalid) |
return __bound_invalid_t2; |
else |
return __bound_empty_t2; |
} |
/* print a bound error message */ |
static void bound_error(const char *fmt, ...) |
{ |
__bound_error_msg = fmt; |
*(int *)0 = 0; /* force a runtime error */ |
} |
static void bound_alloc_error(void) |
{ |
bound_error("not enough memory for bound checking code"); |
} |
/* currently, tcc cannot compile that because we use GNUC extensions */ |
#if !defined(__TINYC__) |
/* return '(p + offset)' for pointer arithmetic (a pointer can reach |
the end of a region in this case */ |
void * FASTCALL __bound_ptr_add(void *p, int offset) |
{ |
unsigned long addr = (unsigned long)p; |
BoundEntry *e; |
#if defined(BOUND_DEBUG) |
printf("add: 0x%x %d\n", (int)p, offset); |
#endif |
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; |
e = (BoundEntry *)((char *)e + |
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); |
addr -= e->start; |
if (addr > e->size) { |
e = __bound_find_region(e, p); |
addr = (unsigned long)p - e->start; |
} |
addr += offset; |
if (addr > e->size) |
return INVALID_POINTER; /* return an invalid pointer */ |
return p + offset; |
} |
/* return '(p + offset)' for pointer indirection (the resulting must |
be strictly inside the region */ |
#define BOUND_PTR_INDIR(dsize) \ |
void * FASTCALL __bound_ptr_indir ## dsize (void *p, int offset) \ |
{ \ |
unsigned long addr = (unsigned long)p; \ |
BoundEntry *e; \ |
\ |
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \ |
e = (BoundEntry *)((char *)e + \ |
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \ |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \ |
addr -= e->start; \ |
if (addr > e->size) { \ |
e = __bound_find_region(e, p); \ |
addr = (unsigned long)p - e->start; \ |
} \ |
addr += offset + dsize; \ |
if (addr > e->size) \ |
return INVALID_POINTER; /* return an invalid pointer */ \ |
return p + offset; \ |
} |
#ifdef __i386__ |
/* return the frame pointer of the caller */ |
#define GET_CALLER_FP(fp)\ |
{\ |
unsigned long *fp1;\ |
__asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\ |
fp = fp1[0];\ |
} |
#else |
#error put code to extract the calling frame pointer |
#endif |
/* called when entering a function to add all the local regions */ |
void FASTCALL __bound_local_new(void *p1) |
{ |
unsigned long addr, size, fp, *p = p1; |
GET_CALLER_FP(fp); |
for(;;) { |
addr = p[0]; |
if (addr == 0) |
break; |
addr += fp; |
size = p[1]; |
p += 2; |
__bound_new_region((void *)addr, size); |
} |
} |
/* called when leaving a function to delete all the local regions */ |
void FASTCALL __bound_local_delete(void *p1) |
{ |
unsigned long addr, fp, *p = p1; |
GET_CALLER_FP(fp); |
for(;;) { |
addr = p[0]; |
if (addr == 0) |
break; |
addr += fp; |
p += 2; |
__bound_delete_region((void *)addr); |
} |
} |
#else |
void __bound_local_new(void *p) |
{ |
} |
void __bound_local_delete(void *p) |
{ |
} |
void *__bound_ptr_add(void *p, int offset) |
{ |
return p + offset; |
} |
#define BOUND_PTR_INDIR(dsize) \ |
void *__bound_ptr_indir ## dsize (void *p, int offset) \ |
{ \ |
return p + offset; \ |
} |
#endif |
BOUND_PTR_INDIR(1) |
BOUND_PTR_INDIR(2) |
BOUND_PTR_INDIR(4) |
BOUND_PTR_INDIR(8) |
BOUND_PTR_INDIR(12) |
BOUND_PTR_INDIR(16) |
static BoundEntry *__bound_new_page(void) |
{ |
BoundEntry *page; |
int i; |
page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); |
if (!page) |
bound_alloc_error(); |
for(i=0;i<BOUND_T2_SIZE;i++) { |
/* put empty entries */ |
page[i].start = 0; |
page[i].size = EMPTY_SIZE; |
page[i].next = NULL; |
page[i].is_invalid = 0; |
} |
return page; |
} |
/* currently we use malloc(). Should use bound_new_page() */ |
static BoundEntry *bound_new_entry(void) |
{ |
BoundEntry *e; |
e = libc_malloc(sizeof(BoundEntry)); |
return e; |
} |
static void bound_free_entry(BoundEntry *e) |
{ |
libc_free(e); |
} |
static inline BoundEntry *get_page(int index) |
{ |
BoundEntry *page; |
page = __bound_t1[index]; |
if (page == __bound_empty_t2 || page == __bound_invalid_t2) { |
/* create a new page if necessary */ |
page = __bound_new_page(); |
__bound_t1[index] = page; |
} |
return page; |
} |
/* mark a region as being invalid (can only be used during init) */ |
static void mark_invalid(unsigned long addr, unsigned long size) |
{ |
unsigned long start, end; |
BoundEntry *page; |
int t1_start, t1_end, i, j, t2_start, t2_end; |
start = addr; |
end = addr + size; |
t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS; |
if (end != 0) |
t2_end = end >> BOUND_T3_BITS; |
else |
t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS); |
#if 0 |
printf("mark_invalid: start = %x %x\n", t2_start, t2_end); |
#endif |
/* first we handle full pages */ |
t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS; |
t1_end = t2_end >> BOUND_T2_BITS; |
i = t2_start & (BOUND_T2_SIZE - 1); |
j = t2_end & (BOUND_T2_SIZE - 1); |
if (t1_start == t1_end) { |
page = get_page(t2_start >> BOUND_T2_BITS); |
for(; i < j; i++) { |
page[i].size = INVALID_SIZE; |
page[i].is_invalid = 1; |
} |
} else { |
if (i > 0) { |
page = get_page(t2_start >> BOUND_T2_BITS); |
for(; i < BOUND_T2_SIZE; i++) { |
page[i].size = INVALID_SIZE; |
page[i].is_invalid = 1; |
} |
} |
for(i = t1_start; i < t1_end; i++) { |
__bound_t1[i] = __bound_invalid_t2; |
} |
if (j != 0) { |
page = get_page(t1_end); |
for(i = 0; i < j; i++) { |
page[i].size = INVALID_SIZE; |
page[i].is_invalid = 1; |
} |
} |
} |
} |
void __bound_init(void) |
{ |
int i; |
BoundEntry *page; |
unsigned long start, size; |
int *p; |
/* save malloc hooks and install bound check hooks */ |
install_malloc_hooks(); |
#ifndef BOUND_STATIC |
__bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *)); |
if (!__bound_t1) |
bound_alloc_error(); |
#endif |
__bound_empty_t2 = __bound_new_page(); |
for(i=0;i<BOUND_T1_SIZE;i++) { |
__bound_t1[i] = __bound_empty_t2; |
} |
page = __bound_new_page(); |
for(i=0;i<BOUND_T2_SIZE;i++) { |
/* put invalid entries */ |
page[i].start = 0; |
page[i].size = INVALID_SIZE; |
page[i].next = NULL; |
page[i].is_invalid = 1; |
} |
__bound_invalid_t2 = page; |
/* invalid pointer zone */ |
start = (unsigned long)INVALID_POINTER & ~(BOUND_T23_SIZE - 1); |
size = BOUND_T23_SIZE; |
mark_invalid(start, size); |
#if !defined(__TINYC__) && defined(CONFIG_TCC_MALLOC_HOOKS) |
/* malloc zone is also marked invalid. can only use that with |
hooks because all libs should use the same malloc. The solution |
would be to build a new malloc for tcc. */ |
start = (unsigned long)&_end; |
size = 128 * 0x100000; |
mark_invalid(start, size); |
#endif |
/* add all static bound check values */ |
p = (int *)&__bounds_start; |
while (p[0] != 0) { |
__bound_new_region((void *)p[0], p[1]); |
p += 2; |
} |
} |
static inline void add_region(BoundEntry *e, |
unsigned long start, unsigned long size) |
{ |
BoundEntry *e1; |
if (e->start == 0) { |
/* no region : add it */ |
e->start = start; |
e->size = size; |
} else { |
/* already regions in the list: add it at the head */ |
e1 = bound_new_entry(); |
e1->start = e->start; |
e1->size = e->size; |
e1->next = e->next; |
e->start = start; |
e->size = size; |
e->next = e1; |
} |
} |
/* create a new region. It should not already exist in the region list */ |
void __bound_new_region(void *p, unsigned long size) |
{ |
unsigned long start, end; |
BoundEntry *page, *e, *e2; |
int t1_start, t1_end, i, t2_start, t2_end; |
start = (unsigned long)p; |
end = start + size; |
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); |
t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); |
/* start */ |
page = get_page(t1_start); |
t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
#ifdef BOUND_DEBUG |
printf("new %lx %lx %x %x %x %x\n", |
start, end, t1_start, t1_end, t2_start, t2_end); |
#endif |
e = (BoundEntry *)((char *)page + t2_start); |
add_region(e, start, size); |
if (t1_end == t1_start) { |
/* same ending page */ |
e2 = (BoundEntry *)((char *)page + t2_end); |
if (e2 > e) { |
e++; |
for(;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
add_region(e, start, size); |
} |
} else { |
/* mark until end of page */ |
e2 = page + BOUND_T2_SIZE; |
e++; |
for(;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
/* mark intermediate pages, if any */ |
for(i=t1_start+1;i<t1_end;i++) { |
page = get_page(i); |
e2 = page + BOUND_T2_SIZE; |
for(e=page;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
} |
/* last page */ |
page = get_page(t1_end); |
e2 = (BoundEntry *)((char *)page + t2_end); |
for(e=page;e<e2;e++) { |
e->start = start; |
e->size = size; |
} |
add_region(e, start, size); |
} |
} |
/* delete a region */ |
static inline void delete_region(BoundEntry *e, |
void *p, unsigned long empty_size) |
{ |
unsigned long addr; |
BoundEntry *e1; |
addr = (unsigned long)p; |
addr -= e->start; |
if (addr <= e->size) { |
/* region found is first one */ |
e1 = e->next; |
if (e1 == NULL) { |
/* no more region: mark it empty */ |
e->start = 0; |
e->size = empty_size; |
} else { |
/* copy next region in head */ |
e->start = e1->start; |
e->size = e1->size; |
e->next = e1->next; |
bound_free_entry(e1); |
} |
} else { |
/* find the matching region */ |
for(;;) { |
e1 = e; |
e = e->next; |
/* region not found: do nothing */ |
if (e == NULL) |
break; |
addr = (unsigned long)p - e->start; |
if (addr <= e->size) { |
/* found: remove entry */ |
e1->next = e->next; |
bound_free_entry(e); |
break; |
} |
} |
} |
} |
/* WARNING: 'p' must be the starting point of the region. */ |
/* return non zero if error */ |
int __bound_delete_region(void *p) |
{ |
unsigned long start, end, addr, size, empty_size; |
BoundEntry *page, *e, *e2; |
int t1_start, t1_end, t2_start, t2_end, i; |
start = (unsigned long)p; |
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); |
t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
/* find region size */ |
page = __bound_t1[t1_start]; |
e = (BoundEntry *)((char *)page + t2_start); |
addr = start - e->start; |
if (addr > e->size) |
e = __bound_find_region(e, p); |
/* test if invalid region */ |
if (e->size == EMPTY_SIZE || (unsigned long)p != e->start) |
return -1; |
/* compute the size we put in invalid regions */ |
if (e->is_invalid) |
empty_size = INVALID_SIZE; |
else |
empty_size = EMPTY_SIZE; |
size = e->size; |
end = start + size; |
/* now we can free each entry */ |
t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); |
t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS); |
delete_region(e, p, empty_size); |
if (t1_end == t1_start) { |
/* same ending page */ |
e2 = (BoundEntry *)((char *)page + t2_end); |
if (e2 > e) { |
e++; |
for(;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
delete_region(e, p, empty_size); |
} |
} else { |
/* mark until end of page */ |
e2 = page + BOUND_T2_SIZE; |
e++; |
for(;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
/* mark intermediate pages, if any */ |
/* XXX: should free them */ |
for(i=t1_start+1;i<t1_end;i++) { |
page = get_page(i); |
e2 = page + BOUND_T2_SIZE; |
for(e=page;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
} |
/* last page */ |
page = get_page(t2_end); |
e2 = (BoundEntry *)((char *)page + t2_end); |
for(e=page;e<e2;e++) { |
e->start = 0; |
e->size = empty_size; |
} |
delete_region(e, p, empty_size); |
} |
return 0; |
} |
/* return the size of the region starting at p, or EMPTY_SIZE if non |
existant region. */ |
static unsigned long get_region_size(void *p) |
{ |
unsigned long addr = (unsigned long)p; |
BoundEntry *e; |
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; |
e = (BoundEntry *)((char *)e + |
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & |
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); |
addr -= e->start; |
if (addr > e->size) |
e = __bound_find_region(e, p); |
if (e->start != (unsigned long)p) |
return EMPTY_SIZE; |
return e->size; |
} |
/* patched memory functions */ |
static void install_malloc_hooks(void) |
{ |
#ifdef CONFIG_TCC_MALLOC_HOOKS |
saved_malloc_hook = __malloc_hook; |
saved_free_hook = __free_hook; |
saved_realloc_hook = __realloc_hook; |
saved_memalign_hook = __memalign_hook; |
__malloc_hook = __bound_malloc; |
__free_hook = __bound_free; |
__realloc_hook = __bound_realloc; |
__memalign_hook = __bound_memalign; |
#endif |
} |
static void restore_malloc_hooks(void) |
{ |
#ifdef CONFIG_TCC_MALLOC_HOOKS |
__malloc_hook = saved_malloc_hook; |
__free_hook = saved_free_hook; |
__realloc_hook = saved_realloc_hook; |
__memalign_hook = saved_memalign_hook; |
#endif |
} |
static void *libc_malloc(size_t size) |
{ |
void *ptr; |
restore_malloc_hooks(); |
ptr = malloc(size); |
install_malloc_hooks(); |
return ptr; |
} |
static void libc_free(void *ptr) |
{ |
restore_malloc_hooks(); |
free(ptr); |
install_malloc_hooks(); |
} |
/* XXX: we should use a malloc which ensure that it is unlikely that |
two malloc'ed data have the same address if 'free' are made in |
between. */ |
void *__bound_malloc(size_t size, const void *caller) |
{ |
void *ptr; |
/* we allocate one more byte to ensure the regions will be |
separated by at least one byte. With the glibc malloc, it may |
be in fact not necessary */ |
ptr = libc_malloc(size + 1); |
if (!ptr) |
return NULL; |
__bound_new_region(ptr, size); |
return ptr; |
} |
void *__bound_memalign(size_t size, size_t align, const void *caller) |
{ |
void *ptr; |
restore_malloc_hooks(); |
#ifndef HAVE_MEMALIGN |
if (align > 4) { |
/* XXX: handle it ? */ |
ptr = NULL; |
} else { |
/* we suppose that malloc aligns to at least four bytes */ |
ptr = malloc(size + 1); |
} |
#else |
/* we allocate one more byte to ensure the regions will be |
separated by at least one byte. With the glibc malloc, it may |
be in fact not necessary */ |
ptr = memalign(size + 1, align); |
#endif |
install_malloc_hooks(); |
if (!ptr) |
return NULL; |
__bound_new_region(ptr, size); |
return ptr; |
} |
void __bound_free(void *ptr, const void *caller) |
{ |
if (ptr == NULL) |
return; |
if (__bound_delete_region(ptr) != 0) |
bound_error("freeing invalid region"); |
libc_free(ptr); |
} |
void *__bound_realloc(void *ptr, size_t size, const void *caller) |
{ |
void *ptr1; |
int old_size; |
if (size == 0) { |
__bound_free(ptr, caller); |
return NULL; |
} else { |
ptr1 = __bound_malloc(size, caller); |
if (ptr == NULL || ptr1 == NULL) |
return ptr1; |
old_size = get_region_size(ptr); |
if (old_size == EMPTY_SIZE) |
bound_error("realloc'ing invalid pointer"); |
memcpy(ptr1, ptr, old_size); |
__bound_free(ptr, caller); |
return ptr1; |
} |
} |
#ifndef CONFIG_TCC_MALLOC_HOOKS |
void *__bound_calloc(size_t nmemb, size_t size) |
{ |
void *ptr; |
size = size * nmemb; |
ptr = __bound_malloc(size, NULL); |
if (!ptr) |
return NULL; |
memset(ptr, 0, size); |
return ptr; |
} |
#endif |
#if 0 |
static void bound_dump(void) |
{ |
BoundEntry *page, *e; |
int i, j; |
printf("region dump:\n"); |
for(i=0;i<BOUND_T1_SIZE;i++) { |
page = __bound_t1[i]; |
for(j=0;j<BOUND_T2_SIZE;j++) { |
e = page + j; |
/* do not print invalid or empty entries */ |
if (e->size != EMPTY_SIZE && e->start != 0) { |
printf("%08x:", |
(i << (BOUND_T2_BITS + BOUND_T3_BITS)) + |
(j << BOUND_T3_BITS)); |
do { |
printf(" %08lx:%08lx", e->start, e->start + e->size); |
e = e->next; |
} while (e != NULL); |
printf("\n"); |
} |
} |
} |
} |
#endif |
/* some useful checked functions */ |
/* check that (p ... p + size - 1) lies inside 'p' region, if any */ |
static void __bound_check(const void *p, size_t size) |
{ |
if (size == 0) |
return; |
p = __bound_ptr_add((void *)p, size); |
if (p == INVALID_POINTER) |
bound_error("invalid pointer"); |
} |
void *__bound_memcpy(void *dst, const void *src, size_t size) |
{ |
__bound_check(dst, size); |
__bound_check(src, size); |
/* check also region overlap */ |
if (src >= dst && src < dst + size) |
bound_error("overlapping regions in memcpy()"); |
return memcpy(dst, src, size); |
} |
void *__bound_memmove(void *dst, const void *src, size_t size) |
{ |
__bound_check(dst, size); |
__bound_check(src, size); |
return memmove(dst, src, size); |
} |
void *__bound_memset(void *dst, int c, size_t size) |
{ |
__bound_check(dst, size); |
return memset(dst, c, size); |
} |
/* XXX: could be optimized */ |
int __bound_strlen(const char *s) |
{ |
const char *p; |
int len; |
len = 0; |
for(;;) { |
p = __bound_ptr_indir1((char *)s, len); |
if (p == INVALID_POINTER) |
bound_error("bad pointer in strlen()"); |
if (*p == '\0') |
break; |
len++; |
} |
return len; |
} |
char *__bound_strcpy(char *dst, const char *src) |
{ |
int len; |
len = __bound_strlen(src); |
return __bound_memcpy(dst, src, len + 1); |
} |
/programs/develop/ktcc/trunk/source/boundtest.c |
---|
0,0 → 1,214 |
#include <stdlib.h> |
#include <stdio.h> |
#define NB_ITS 1000000 |
//#define NB_ITS 1 |
#define TAB_SIZE 100 |
int tab[TAB_SIZE]; |
int ret_sum; |
char tab3[256]; |
int test1(void) |
{ |
int i, sum = 0; |
for(i=0;i<TAB_SIZE;i++) { |
sum += tab[i]; |
} |
return sum; |
} |
/* error */ |
int test2(void) |
{ |
int i, sum = 0; |
for(i=0;i<TAB_SIZE + 1;i++) { |
sum += tab[i]; |
} |
return sum; |
} |
/* actually, profiling test */ |
int test3(void) |
{ |
int sum; |
int i, it; |
sum = 0; |
for(it=0;it<NB_ITS;it++) { |
for(i=0;i<TAB_SIZE;i++) { |
sum += tab[i]; |
} |
} |
return sum; |
} |
/* ok */ |
int test4(void) |
{ |
int i, sum = 0; |
int *tab4; |
tab4 = malloc(20 * sizeof(int)); |
for(i=0;i<20;i++) { |
sum += tab4[i]; |
} |
free(tab4); |
return sum; |
} |
/* error */ |
int test5(void) |
{ |
int i, sum = 0; |
int *tab4; |
tab4 = malloc(20 * sizeof(int)); |
for(i=0;i<21;i++) { |
sum += tab4[i]; |
} |
free(tab4); |
return sum; |
} |
/* error */ |
/* XXX: currently: bug */ |
int test6(void) |
{ |
int i, sum = 0; |
int *tab4; |
tab4 = malloc(20 * sizeof(int)); |
free(tab4); |
for(i=0;i<21;i++) { |
sum += tab4[i]; |
} |
return sum; |
} |
/* error */ |
int test7(void) |
{ |
int i, sum = 0; |
int *p; |
for(i=0;i<TAB_SIZE + 1;i++) { |
p = &tab[i]; |
if (i == TAB_SIZE) |
printf("i=%d %x\n", i, p); |
sum += *p; |
} |
return sum; |
} |
/* ok */ |
int test8(void) |
{ |
int i, sum = 0; |
int tab[10]; |
for(i=0;i<10;i++) { |
sum += tab[i]; |
} |
return sum; |
} |
/* error */ |
int test9(void) |
{ |
int i, sum = 0; |
char tab[10]; |
for(i=0;i<11;i++) { |
sum += tab[i]; |
} |
return sum; |
} |
/* ok */ |
int test10(void) |
{ |
char tab[10]; |
char tab1[10]; |
memset(tab, 0, 10); |
memcpy(tab, tab1, 10); |
memmove(tab, tab1, 10); |
return 0; |
} |
/* error */ |
int test11(void) |
{ |
char tab[10]; |
memset(tab, 0, 11); |
return 0; |
} |
/* error */ |
int test12(void) |
{ |
void *ptr; |
ptr = malloc(10); |
free(ptr); |
free(ptr); |
return 0; |
} |
/* error */ |
int test13(void) |
{ |
char pad1 = 0; |
char tab[10]; |
char pad2 = 0; |
memset(tab, 'a', sizeof(tab)); |
return strlen(tab); |
} |
int (*table_test[])(void) = { |
test1, |
test1, |
test2, |
test3, |
test4, |
test5, |
test6, |
test7, |
test8, |
test9, |
test10, |
test11, |
test12, |
test13, |
}; |
int main(int argc, char **argv) |
{ |
int index; |
int (*ftest)(void); |
if (argc < 2) { |
printf("usage: boundtest n\n" |
"test TCC bound checking system\n" |
); |
exit(1); |
} |
index = 0; |
if (argc >= 2) |
index = atoi(argv[1]); |
/* well, we also use bounds on this ! */ |
ftest = table_test[index]; |
ftest(); |
return 0; |
} |
/* |
* without bound 0.77 s |
* with bounds 4.73 |
*/ |
/programs/develop/ktcc/trunk/source/c67-gen.c |
---|
0,0 → 1,2548 |
/* |
* 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 |
*/ |
//#define ASSEMBLY_LISTING_C67 |
/* number of available registers */ |
#define NB_REGS 24 |
/* 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, |
}; |
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 |
}; |
/* 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 */ |
#define ALWAYS_ASSERT(x) \ |
do {\ |
if (!(x))\ |
error("internal compiler error file at %s:%d", __FILE__, __LINE__);\ |
} while (0) |
// 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; |
/* 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_JMP_SLOT R_C60_JMP_SLOT |
#define R_COPY R_C60_COPY |
#define ELF_START_ADDR 0x00000400 |
#define ELF_PAGE_SIZE 0x1000 |
/******************************************************/ |
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 asign them unique values so the mapping routines |
// can distinquish 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) { |
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) { |
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 |
} |
} |
} |
/* 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) { |
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 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) { |
error("long long not supported"); |
} else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { |
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; |
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 | VT_LVAL, 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); |
} |
} else { |
if (is_float(vtop->type.t)) { |
vpushi(0); |
gen_op(TOK_NE); |
} |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
/* constant jmp optimization */ |
if ((vtop->c.i != 0) != inv) |
t = gjmp(t); |
} else { |
// I think we need to get the value on the stack |
// into a register, test it, and generate a branch |
// return the address of the branch, so it can be |
// later patched |
v = gv(RC_INT); // get value into a reg |
ind1 = ind; |
C67_MVKL(C67_A0, t); //r=reg to load, constant |
C67_MVKH(C67_A0, t); //r=reg to load, constant |
if (v != TREG_EAX && // check if not already in a conditional test reg |
v != TREG_EDX && v != TREG_ST0 && v != C67_B2) { |
C67_MV(v, C67_B2); |
v = C67_B2; |
} |
C67_IREG_B_REG(inv, v, C67_A0); // [!R] B.S2x A0 |
C67_NOP(5); |
t = ind1; //return where we need to patch |
ind1 = ind; |
} |
} |
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) |
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) |
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--; |
} |
/* end of X86 code generator */ |
/*************************************************************/ |
/programs/develop/ktcc/trunk/source/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" |
/programs/develop/ktcc/trunk/source/config.h |
---|
0,0 → 1,7 |
/* Automatically generated by configure - do not modify */ |
#define CONFIG_TCCDIR "/usr/local/lib/tcc" |
#define GCC_MAJOR 3 |
#define HOST_I386 1 |
#define TCC_VERSION "0.9.23" |
//#define TCC_TARGET_PE |
#define TCC_TARGET_MEOS |
/programs/develop/ktcc/trunk/source/elf.h |
---|
0,0 → 1,1627 |
/* This file defines standard ELF types, structures, and macros. |
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. |
This file is part of the GNU C Library. |
Contributed by Ian Lance Taylor <ian@cygnus.com>. |
The GNU C Library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Library General Public License as |
published by the Free Software Foundation; either version 2 of the |
License, or (at your option) any later version. |
The GNU C Library is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Library General Public License for more details. |
You should have received a copy of the GNU Library General Public |
License along with the GNU C Library; see the file COPYING.LIB. If not, |
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
#ifndef _ELF_H |
#define _ELF_H 1 |
#ifndef WIN32 |
#include <inttypes.h> |
#else |
#ifndef __int8_t_defined |
#define __int8_t_defined |
typedef signed char int8_t; |
typedef short int int16_t; |
typedef int int32_t; |
typedef long long int int64_t; |
#endif |
typedef unsigned char uint8_t; |
typedef unsigned short int uint16_t; |
typedef unsigned int uint32_t; |
typedef unsigned long long int uint64_t; |
#endif |
/* Standard ELF types. */ |
/* Type for a 16-bit quantity. */ |
typedef uint16_t Elf32_Half; |
typedef uint16_t Elf64_Half; |
/* Types for signed and unsigned 32-bit quantities. */ |
typedef uint32_t Elf32_Word; |
typedef int32_t Elf32_Sword; |
typedef uint32_t Elf64_Word; |
typedef int32_t Elf64_Sword; |
/* Types for signed and unsigned 64-bit quantities. */ |
typedef uint64_t Elf32_Xword; |
typedef int64_t Elf32_Sxword; |
typedef uint64_t Elf64_Xword; |
typedef int64_t Elf64_Sxword; |
/* Type of addresses. */ |
typedef uint32_t Elf32_Addr; |
typedef uint64_t Elf64_Addr; |
/* Type of file offsets. */ |
typedef uint32_t Elf32_Off; |
typedef uint64_t Elf64_Off; |
/* Type for section indices, which are 16-bit quantities. */ |
typedef uint16_t Elf32_Section; |
typedef uint16_t Elf64_Section; |
/* Type of symbol indices. */ |
typedef uint32_t Elf32_Symndx; |
typedef uint64_t Elf64_Symndx; |
/* The ELF file header. This appears at the start of every ELF file. */ |
#define EI_NIDENT (16) |
typedef struct |
{ |
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ |
Elf32_Half e_type; /* Object file type */ |
Elf32_Half e_machine; /* Architecture */ |
Elf32_Word e_version; /* Object file version */ |
Elf32_Addr e_entry; /* Entry point virtual address */ |
Elf32_Off e_phoff; /* Program header table file offset */ |
Elf32_Off e_shoff; /* Section header table file offset */ |
Elf32_Word e_flags; /* Processor-specific flags */ |
Elf32_Half e_ehsize; /* ELF header size in bytes */ |
Elf32_Half e_phentsize; /* Program header table entry size */ |
Elf32_Half e_phnum; /* Program header table entry count */ |
Elf32_Half e_shentsize; /* Section header table entry size */ |
Elf32_Half e_shnum; /* Section header table entry count */ |
Elf32_Half e_shstrndx; /* Section header string table index */ |
} Elf32_Ehdr; |
typedef struct |
{ |
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ |
Elf64_Half e_type; /* Object file type */ |
Elf64_Half e_machine; /* Architecture */ |
Elf64_Word e_version; /* Object file version */ |
Elf64_Addr e_entry; /* Entry point virtual address */ |
Elf64_Off e_phoff; /* Program header table file offset */ |
Elf64_Off e_shoff; /* Section header table file offset */ |
Elf64_Word e_flags; /* Processor-specific flags */ |
Elf64_Half e_ehsize; /* ELF header size in bytes */ |
Elf64_Half e_phentsize; /* Program header table entry size */ |
Elf64_Half e_phnum; /* Program header table entry count */ |
Elf64_Half e_shentsize; /* Section header table entry size */ |
Elf64_Half e_shnum; /* Section header table entry count */ |
Elf64_Half e_shstrndx; /* Section header string table index */ |
} Elf64_Ehdr; |
/* Fields in the e_ident array. The EI_* macros are indices into the |
array. The macros under each EI_* macro are the values the byte |
may have. */ |
#define EI_MAG0 0 /* File identification byte 0 index */ |
#define ELFMAG0 0x7f /* Magic number byte 0 */ |
#define EI_MAG1 1 /* File identification byte 1 index */ |
#define ELFMAG1 'E' /* Magic number byte 1 */ |
#define EI_MAG2 2 /* File identification byte 2 index */ |
#define ELFMAG2 'L' /* Magic number byte 2 */ |
#define EI_MAG3 3 /* File identification byte 3 index */ |
#define ELFMAG3 'F' /* Magic number byte 3 */ |
/* Conglomeration of the identification bytes, for easy testing as a word. */ |
#define ELFMAG "\177ELF" |
#define SELFMAG 4 |
#define EI_CLASS 4 /* File class byte index */ |
#define ELFCLASSNONE 0 /* Invalid class */ |
#define ELFCLASS32 1 /* 32-bit objects */ |
#define ELFCLASS64 2 /* 64-bit objects */ |
#define ELFCLASSNUM 3 |
#define EI_DATA 5 /* Data encoding byte index */ |
#define ELFDATANONE 0 /* Invalid data encoding */ |
#define ELFDATA2LSB 1 /* 2's complement, little endian */ |
#define ELFDATA2MSB 2 /* 2's complement, big endian */ |
#define ELFDATANUM 3 |
#define EI_VERSION 6 /* File version byte index */ |
/* Value must be EV_CURRENT */ |
#define EI_OSABI 7 /* OS ABI identification */ |
#define ELFOSABI_SYSV 0 /* UNIX System V ABI */ |
#define ELFOSABI_HPUX 1 /* HP-UX */ |
#define ELFOSABI_FREEBSD 9 /* Free BSD */ |
#define ELFOSABI_ARM 97 /* ARM */ |
#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ |
#define EI_ABIVERSION 8 /* ABI version */ |
#define EI_PAD 9 /* Byte index of padding bytes */ |
/* Legal values for e_type (object file type). */ |
#define ET_NONE 0 /* No file type */ |
#define ET_REL 1 /* Relocatable file */ |
#define ET_EXEC 2 /* Executable file */ |
#define ET_DYN 3 /* Shared object file */ |
#define ET_CORE 4 /* Core file */ |
#define ET_NUM 5 /* Number of defined types */ |
#define ET_LOPROC 0xff00 /* Processor-specific */ |
#define ET_HIPROC 0xffff /* Processor-specific */ |
/* Legal values for e_machine (architecture). */ |
#define EM_NONE 0 /* No machine */ |
#define EM_M32 1 /* AT&T WE 32100 */ |
#define EM_SPARC 2 /* SUN SPARC */ |
#define EM_386 3 /* Intel 80386 */ |
#define EM_68K 4 /* Motorola m68k family */ |
#define EM_88K 5 /* Motorola m88k family */ |
#define EM_486 6 /* Intel 80486 */ |
#define EM_860 7 /* Intel 80860 */ |
#define EM_MIPS 8 /* MIPS R3000 big-endian */ |
#define EM_S370 9 /* Amdahl */ |
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ |
#define EM_RS6000 11 /* RS6000 */ |
#define EM_PARISC 15 /* HPPA */ |
#define EM_nCUBE 16 /* nCUBE */ |
#define EM_VPP500 17 /* Fujitsu VPP500 */ |
#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ |
#define EM_960 19 /* Intel 80960 */ |
#define EM_PPC 20 /* PowerPC */ |
#define EM_V800 36 /* NEC V800 series */ |
#define EM_FR20 37 /* Fujitsu FR20 */ |
#define EM_RH32 38 /* TRW RH32 */ |
#define EM_MMA 39 /* Fujitsu MMA */ |
#define EM_ARM 40 /* ARM */ |
#define EM_FAKE_ALPHA 41 /* Digital Alpha */ |
#define EM_SH 42 /* Hitachi SH */ |
#define EM_SPARCV9 43 /* SPARC v9 64-bit */ |
#define EM_TRICORE 44 /* Siemens Tricore */ |
#define EM_ARC 45 /* Argonaut RISC Core */ |
#define EM_H8_300 46 /* Hitachi H8/300 */ |
#define EM_H8_300H 47 /* Hitachi H8/300H */ |
#define EM_H8S 48 /* Hitachi H8S */ |
#define EM_H8_500 49 /* Hitachi H8/500 */ |
#define EM_IA_64 50 /* Intel Merced */ |
#define EM_MIPS_X 51 /* Stanford MIPS-X */ |
#define EM_COLDFIRE 52 /* Motorola Coldfire */ |
#define EM_68HC12 53 /* Motorola M68HC12 */ |
#define EM_NUM 54 |
/* If it is necessary to assign new unofficial EM_* values, please |
pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the |
chances of collision with official or non-GNU unofficial values. */ |
#define EM_ALPHA 0x9026 |
#define EM_C60 0x9c60 |
/* Legal values for e_version (version). */ |
#define EV_NONE 0 /* Invalid ELF version */ |
#define EV_CURRENT 1 /* Current version */ |
#define EV_NUM 2 |
/* Section header. */ |
typedef struct |
{ |
Elf32_Word sh_name; /* Section name (string tbl index) */ |
Elf32_Word sh_type; /* Section type */ |
Elf32_Word sh_flags; /* Section flags */ |
Elf32_Addr sh_addr; /* Section virtual addr at execution */ |
Elf32_Off sh_offset; /* Section file offset */ |
Elf32_Word sh_size; /* Section size in bytes */ |
Elf32_Word sh_link; /* Link to another section */ |
Elf32_Word sh_info; /* Additional section information */ |
Elf32_Word sh_addralign; /* Section alignment */ |
Elf32_Word sh_entsize; /* Entry size if section holds table */ |
} Elf32_Shdr; |
typedef struct |
{ |
Elf64_Word sh_name; /* Section name (string tbl index) */ |
Elf64_Word sh_type; /* Section type */ |
Elf64_Xword sh_flags; /* Section flags */ |
Elf64_Addr sh_addr; /* Section virtual addr at execution */ |
Elf64_Off sh_offset; /* Section file offset */ |
Elf64_Xword sh_size; /* Section size in bytes */ |
Elf64_Word sh_link; /* Link to another section */ |
Elf64_Word sh_info; /* Additional section information */ |
Elf64_Xword sh_addralign; /* Section alignment */ |
Elf64_Xword sh_entsize; /* Entry size if section holds table */ |
} Elf64_Shdr; |
/* Special section indices. */ |
#define SHN_UNDEF 0 /* Undefined section */ |
#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ |
#define SHN_LOPROC 0xff00 /* Start of processor-specific */ |
#define SHN_HIPROC 0xff1f /* End of processor-specific */ |
#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ |
#define SHN_COMMON 0xfff2 /* Associated symbol is common */ |
#define SHN_HIRESERVE 0xffff /* End of reserved indices */ |
/* Legal values for sh_type (section type). */ |
#define SHT_NULL 0 /* Section header table entry unused */ |
#define SHT_PROGBITS 1 /* Program data */ |
#define SHT_SYMTAB 2 /* Symbol table */ |
#define SHT_STRTAB 3 /* String table */ |
#define SHT_RELA 4 /* Relocation entries with addends */ |
#define SHT_HASH 5 /* Symbol hash table */ |
#define SHT_DYNAMIC 6 /* Dynamic linking information */ |
#define SHT_NOTE 7 /* Notes */ |
#define SHT_NOBITS 8 /* Program space with no data (bss) */ |
#define SHT_REL 9 /* Relocation entries, no addends */ |
#define SHT_SHLIB 10 /* Reserved */ |
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ |
#define SHT_NUM 12 /* Number of defined types. */ |
#define SHT_LOOS 0x60000000 /* Start OS-specific */ |
#define SHT_LOSUNW 0x6ffffffb /* Sun-specific low bound. */ |
#define SHT_SUNW_COMDAT 0x6ffffffb |
#define SHT_SUNW_syminfo 0x6ffffffc |
#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ |
#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ |
#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ |
#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ |
#define SHT_HIOS 0x6fffffff /* End OS-specific type */ |
#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ |
#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ |
#define SHT_LOUSER 0x80000000 /* Start of application-specific */ |
#define SHT_HIUSER 0x8fffffff /* End of application-specific */ |
/* Legal values for sh_flags (section flags). */ |
#define SHF_WRITE (1 << 0) /* Writable */ |
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ |
#define SHF_EXECINSTR (1 << 2) /* Executable */ |
#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ |
/* Symbol table entry. */ |
typedef struct |
{ |
Elf32_Word st_name; /* Symbol name (string tbl index) */ |
Elf32_Addr st_value; /* Symbol value */ |
Elf32_Word st_size; /* Symbol size */ |
unsigned char st_info; /* Symbol type and binding */ |
unsigned char st_other; /* No defined meaning, 0 */ |
Elf32_Section st_shndx; /* Section index */ |
} Elf32_Sym; |
typedef struct |
{ |
Elf64_Word st_name; /* Symbol name (string tbl index) */ |
unsigned char st_info; /* Symbol type and binding */ |
unsigned char st_other; /* No defined meaning, 0 */ |
Elf64_Section st_shndx; /* Section index */ |
Elf64_Addr st_value; /* Symbol value */ |
Elf64_Xword st_size; /* Symbol size */ |
} Elf64_Sym; |
/* The syminfo section if available contains additional information about |
every dynamic symbol. */ |
typedef struct |
{ |
Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ |
Elf32_Half si_flags; /* Per symbol flags */ |
} Elf32_Syminfo; |
typedef struct |
{ |
Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ |
Elf64_Half si_flags; /* Per symbol flags */ |
} Elf64_Syminfo; |
/* Possible values for si_boundto. */ |
#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ |
#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ |
#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ |
/* Possible bitmasks for si_flags. */ |
#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ |
#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ |
#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ |
#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy |
loaded */ |
/* Syminfo version values. */ |
#define SYMINFO_NONE 0 |
#define SYMINFO_CURRENT 1 |
#define SYMINFO_NUM 2 |
/* Special section index. */ |
#define SHN_UNDEF 0 /* No section, undefined symbol. */ |
/* How to extract and insert information held in the st_info field. */ |
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) |
#define ELF32_ST_TYPE(val) ((val) & 0xf) |
#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) |
/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ |
#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) |
#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) |
#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) |
/* Legal values for ST_BIND subfield of st_info (symbol binding). */ |
#define STB_LOCAL 0 /* Local symbol */ |
#define STB_GLOBAL 1 /* Global symbol */ |
#define STB_WEAK 2 /* Weak symbol */ |
#define STB_NUM 3 /* Number of defined types. */ |
#define STB_LOOS 10 /* Start of OS-specific */ |
#define STB_HIOS 12 /* End of OS-specific */ |
#define STB_LOPROC 13 /* Start of processor-specific */ |
#define STB_HIPROC 15 /* End of processor-specific */ |
/* Legal values for ST_TYPE subfield of st_info (symbol type). */ |
#define STT_NOTYPE 0 /* Symbol type is unspecified */ |
#define STT_OBJECT 1 /* Symbol is a data object */ |
#define STT_FUNC 2 /* Symbol is a code object */ |
#define STT_SECTION 3 /* Symbol associated with a section */ |
#define STT_FILE 4 /* Symbol's name is file name */ |
#define STT_NUM 5 /* Number of defined types. */ |
#define STT_LOOS 11 /* Start of OS-specific */ |
#define STT_HIOS 12 /* End of OS-specific */ |
#define STT_LOPROC 13 /* Start of processor-specific */ |
#define STT_HIPROC 15 /* End of processor-specific */ |
/* Symbol table indices are found in the hash buckets and chain table |
of a symbol hash table section. This special index value indicates |
the end of a chain, meaning no further symbols are found in that bucket. */ |
#define STN_UNDEF 0 /* End of a chain. */ |
/* How to extract and insert information held in the st_other field. */ |
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) |
/* For ELF64 the definitions are the same. */ |
#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) |
/* Symbol visibility specification encoded in the st_other field. */ |
#define STV_DEFAULT 0 /* Default symbol visibility rules */ |
#define STV_INTERNAL 1 /* Processor specific hidden class */ |
#define STV_HIDDEN 2 /* Sym unavailable in other modules */ |
#define STV_PROTECTED 3 /* Not preemptible, not exported */ |
/* Relocation table entry without addend (in section of type SHT_REL). */ |
typedef struct |
{ |
Elf32_Addr r_offset; /* Address */ |
Elf32_Word r_info; /* Relocation type and symbol index */ |
} Elf32_Rel; |
/* I have seen two different definitions of the Elf64_Rel and |
Elf64_Rela structures, so we'll leave them out until Novell (or |
whoever) gets their act together. */ |
/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ |
typedef struct |
{ |
Elf64_Addr r_offset; /* Address */ |
Elf64_Xword r_info; /* Relocation type and symbol index */ |
} Elf64_Rel; |
/* Relocation table entry with addend (in section of type SHT_RELA). */ |
typedef struct |
{ |
Elf32_Addr r_offset; /* Address */ |
Elf32_Word r_info; /* Relocation type and symbol index */ |
Elf32_Sword r_addend; /* Addend */ |
} Elf32_Rela; |
typedef struct |
{ |
Elf64_Addr r_offset; /* Address */ |
Elf64_Xword r_info; /* Relocation type and symbol index */ |
Elf64_Sxword r_addend; /* Addend */ |
} Elf64_Rela; |
/* How to extract and insert information held in the r_info field. */ |
#define ELF32_R_SYM(val) ((val) >> 8) |
#define ELF32_R_TYPE(val) ((val) & 0xff) |
#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) |
#define ELF64_R_SYM(i) ((i) >> 32) |
#define ELF64_R_TYPE(i) ((i) & 0xffffffff) |
#define ELF64_R_INFO(sym,type) (((sym) << 32) + (type)) |
/* Program segment header. */ |
typedef struct |
{ |
Elf32_Word p_type; /* Segment type */ |
Elf32_Off p_offset; /* Segment file offset */ |
Elf32_Addr p_vaddr; /* Segment virtual address */ |
Elf32_Addr p_paddr; /* Segment physical address */ |
Elf32_Word p_filesz; /* Segment size in file */ |
Elf32_Word p_memsz; /* Segment size in memory */ |
Elf32_Word p_flags; /* Segment flags */ |
Elf32_Word p_align; /* Segment alignment */ |
} Elf32_Phdr; |
typedef struct |
{ |
Elf64_Word p_type; /* Segment type */ |
Elf64_Word p_flags; /* Segment flags */ |
Elf64_Off p_offset; /* Segment file offset */ |
Elf64_Addr p_vaddr; /* Segment virtual address */ |
Elf64_Addr p_paddr; /* Segment physical address */ |
Elf64_Xword p_filesz; /* Segment size in file */ |
Elf64_Xword p_memsz; /* Segment size in memory */ |
Elf64_Xword p_align; /* Segment alignment */ |
} Elf64_Phdr; |
/* Legal values for p_type (segment type). */ |
#define PT_NULL 0 /* Program header table entry unused */ |
#define PT_LOAD 1 /* Loadable program segment */ |
#define PT_DYNAMIC 2 /* Dynamic linking information */ |
#define PT_INTERP 3 /* Program interpreter */ |
#define PT_NOTE 4 /* Auxiliary information */ |
#define PT_SHLIB 5 /* Reserved */ |
#define PT_PHDR 6 /* Entry for header table itself */ |
#define PT_NUM 7 /* Number of defined types. */ |
#define PT_LOOS 0x60000000 /* Start of OS-specific */ |
#define PT_HIOS 0x6fffffff /* End of OS-specific */ |
#define PT_LOPROC 0x70000000 /* Start of processor-specific */ |
#define PT_HIPROC 0x7fffffff /* End of processor-specific */ |
/* Legal values for p_flags (segment flags). */ |
#define PF_X (1 << 0) /* Segment is executable */ |
#define PF_W (1 << 1) /* Segment is writable */ |
#define PF_R (1 << 2) /* Segment is readable */ |
#define PF_MASKPROC 0xf0000000 /* Processor-specific */ |
/* Legal values for note segment descriptor types for core files. */ |
#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ |
#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ |
#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ |
#define NT_PRXREG 4 /* Contains copy of prxregset struct */ |
#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ |
#define NT_AUXV 6 /* Contains copy of auxv array */ |
#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ |
#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ |
#define NT_PSINFO 13 /* Contains copy of psinfo struct */ |
#define NT_PRCRED 14 /* Contains copy of prcred struct */ |
#define NT_UTSNAME 15 /* Contains copy of utsname struct */ |
#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ |
#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ |
/* Legal values for the note segment descriptor types for object files. */ |
#define NT_VERSION 1 /* Contains a version string. */ |
/* Dynamic section entry. */ |
typedef struct |
{ |
Elf32_Sword d_tag; /* Dynamic entry type */ |
union |
{ |
Elf32_Word d_val; /* Integer value */ |
Elf32_Addr d_ptr; /* Address value */ |
} d_un; |
} Elf32_Dyn; |
typedef struct |
{ |
Elf64_Sxword d_tag; /* Dynamic entry type */ |
union |
{ |
Elf64_Xword d_val; /* Integer value */ |
Elf64_Addr d_ptr; /* Address value */ |
} d_un; |
} Elf64_Dyn; |
/* Legal values for d_tag (dynamic entry type). */ |
#define DT_NULL 0 /* Marks end of dynamic section */ |
#define DT_NEEDED 1 /* Name of needed library */ |
#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ |
#define DT_PLTGOT 3 /* Processor defined value */ |
#define DT_HASH 4 /* Address of symbol hash table */ |
#define DT_STRTAB 5 /* Address of string table */ |
#define DT_SYMTAB 6 /* Address of symbol table */ |
#define DT_RELA 7 /* Address of Rela relocs */ |
#define DT_RELASZ 8 /* Total size of Rela relocs */ |
#define DT_RELAENT 9 /* Size of one Rela reloc */ |
#define DT_STRSZ 10 /* Size of string table */ |
#define DT_SYMENT 11 /* Size of one symbol table entry */ |
#define DT_INIT 12 /* Address of init function */ |
#define DT_FINI 13 /* Address of termination function */ |
#define DT_SONAME 14 /* Name of shared object */ |
#define DT_RPATH 15 /* Library search path */ |
#define DT_SYMBOLIC 16 /* Start symbol search here */ |
#define DT_REL 17 /* Address of Rel relocs */ |
#define DT_RELSZ 18 /* Total size of Rel relocs */ |
#define DT_RELENT 19 /* Size of one Rel reloc */ |
#define DT_PLTREL 20 /* Type of reloc in PLT */ |
#define DT_DEBUG 21 /* For debugging; unspecified */ |
#define DT_TEXTREL 22 /* Reloc might modify .text */ |
#define DT_JMPREL 23 /* Address of PLT relocs */ |
#define DT_BIND_NOW 24 /* Process relocations of object */ |
#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ |
#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ |
#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ |
#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ |
#define DT_NUM 29 /* Number used */ |
#define DT_LOOS 0x60000000 /* Start of OS-specific */ |
#define DT_HIOS 0x6fffffff /* End of OS-specific */ |
#define DT_LOPROC 0x70000000 /* Start of processor-specific */ |
#define DT_HIPROC 0x7fffffff /* End of processor-specific */ |
#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ |
/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the |
Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's |
approach. */ |
#define DT_VALRNGLO 0x6ffffd00 |
#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting |
the following DT_* entry. */ |
#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ |
#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ |
#define DT_VALRNGHI 0x6ffffdff |
/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the |
Dyn.d_un.d_ptr field of the Elf*_Dyn structure. |
If any adjustment is made to the ELF object after it has been |
built these entries will need to be adjusted. */ |
#define DT_ADDRRNGLO 0x6ffffe00 |
#define DT_SYMINFO 0x6ffffeff /* syminfo table */ |
#define DT_ADDRRNGHI 0x6ffffeff |
/* The versioning entry types. The next are defined as part of the |
GNU extension. */ |
#define DT_VERSYM 0x6ffffff0 |
/* These were chosen by Sun. */ |
#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ |
#define DT_VERDEF 0x6ffffffc /* Address of version definition |
table */ |
#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ |
#define DT_VERNEED 0x6ffffffe /* Address of table with needed |
versions */ |
#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ |
#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ |
#define DT_VERSIONTAGNUM 16 |
/* Sun added these machine-independent extensions in the "processor-specific" |
range. Be compatible. */ |
#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ |
#define DT_FILTER 0x7fffffff /* Shared object to get values from */ |
#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) |
#define DT_EXTRANUM 3 |
/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 |
entry in the dynamic section. */ |
#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ |
#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ |
#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ |
#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ |
#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ |
#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ |
#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ |
/* Version definition sections. */ |
typedef struct |
{ |
Elf32_Half vd_version; /* Version revision */ |
Elf32_Half vd_flags; /* Version information */ |
Elf32_Half vd_ndx; /* Version Index */ |
Elf32_Half vd_cnt; /* Number of associated aux entries */ |
Elf32_Word vd_hash; /* Version name hash value */ |
Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ |
Elf32_Word vd_next; /* Offset in bytes to next verdef |
entry */ |
} Elf32_Verdef; |
typedef struct |
{ |
Elf64_Half vd_version; /* Version revision */ |
Elf64_Half vd_flags; /* Version information */ |
Elf64_Half vd_ndx; /* Version Index */ |
Elf64_Half vd_cnt; /* Number of associated aux entries */ |
Elf64_Word vd_hash; /* Version name hash value */ |
Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ |
Elf64_Word vd_next; /* Offset in bytes to next verdef |
entry */ |
} Elf64_Verdef; |
/* Legal values for vd_version (version revision). */ |
#define VER_DEF_NONE 0 /* No version */ |
#define VER_DEF_CURRENT 1 /* Current version */ |
#define VER_DEF_NUM 2 /* Given version number */ |
/* Legal values for vd_flags (version information flags). */ |
#define VER_FLG_BASE 0x1 /* Version definition of file itself */ |
#define VER_FLG_WEAK 0x2 /* Weak version identifier */ |
/* Auxialiary version information. */ |
typedef struct |
{ |
Elf32_Word vda_name; /* Version or dependency names */ |
Elf32_Word vda_next; /* Offset in bytes to next verdaux |
entry */ |
} Elf32_Verdaux; |
typedef struct |
{ |
Elf64_Word vda_name; /* Version or dependency names */ |
Elf64_Word vda_next; /* Offset in bytes to next verdaux |
entry */ |
} Elf64_Verdaux; |
/* Version dependency section. */ |
typedef struct |
{ |
Elf32_Half vn_version; /* Version of structure */ |
Elf32_Half vn_cnt; /* Number of associated aux entries */ |
Elf32_Word vn_file; /* Offset of filename for this |
dependency */ |
Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ |
Elf32_Word vn_next; /* Offset in bytes to next verneed |
entry */ |
} Elf32_Verneed; |
typedef struct |
{ |
Elf64_Half vn_version; /* Version of structure */ |
Elf64_Half vn_cnt; /* Number of associated aux entries */ |
Elf64_Word vn_file; /* Offset of filename for this |
dependency */ |
Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ |
Elf64_Word vn_next; /* Offset in bytes to next verneed |
entry */ |
} Elf64_Verneed; |
/* Legal values for vn_version (version revision). */ |
#define VER_NEED_NONE 0 /* No version */ |
#define VER_NEED_CURRENT 1 /* Current version */ |
#define VER_NEED_NUM 2 /* Given version number */ |
/* Auxiliary needed version information. */ |
typedef struct |
{ |
Elf32_Word vna_hash; /* Hash value of dependency name */ |
Elf32_Half vna_flags; /* Dependency specific information */ |
Elf32_Half vna_other; /* Unused */ |
Elf32_Word vna_name; /* Dependency name string offset */ |
Elf32_Word vna_next; /* Offset in bytes to next vernaux |
entry */ |
} Elf32_Vernaux; |
typedef struct |
{ |
Elf64_Word vna_hash; /* Hash value of dependency name */ |
Elf64_Half vna_flags; /* Dependency specific information */ |
Elf64_Half vna_other; /* Unused */ |
Elf64_Word vna_name; /* Dependency name string offset */ |
Elf64_Word vna_next; /* Offset in bytes to next vernaux |
entry */ |
} Elf64_Vernaux; |
/* Legal values for vna_flags. */ |
#define VER_FLG_WEAK 0x2 /* Weak version identifier */ |
/* Auxiliary vector. */ |
/* This vector is normally only used by the program interpreter. The |
usual definition in an ABI supplement uses the name auxv_t. The |
vector is not usually defined in a standard <elf.h> file, but it |
can't hurt. We rename it to avoid conflicts. The sizes of these |
types are an arrangement between the exec server and the program |
interpreter, so we don't fully specify them here. */ |
typedef struct |
{ |
int a_type; /* Entry type */ |
union |
{ |
long int a_val; /* Integer value */ |
void *a_ptr; /* Pointer value */ |
void (*a_fcn) (void); /* Function pointer value */ |
} a_un; |
} Elf32_auxv_t; |
typedef struct |
{ |
long int a_type; /* Entry type */ |
union |
{ |
long int a_val; /* Integer value */ |
void *a_ptr; /* Pointer value */ |
void (*a_fcn) (void); /* Function pointer value */ |
} a_un; |
} Elf64_auxv_t; |
/* Legal values for a_type (entry type). */ |
#define AT_NULL 0 /* End of vector */ |
#define AT_IGNORE 1 /* Entry should be ignored */ |
#define AT_EXECFD 2 /* File descriptor of program */ |
#define AT_PHDR 3 /* Program headers for program */ |
#define AT_PHENT 4 /* Size of program header entry */ |
#define AT_PHNUM 5 /* Number of program headers */ |
#define AT_PAGESZ 6 /* System page size */ |
#define AT_BASE 7 /* Base address of interpreter */ |
#define AT_FLAGS 8 /* Flags */ |
#define AT_ENTRY 9 /* Entry point of program */ |
#define AT_NOTELF 10 /* Program is not ELF */ |
#define AT_UID 11 /* Real uid */ |
#define AT_EUID 12 /* Effective uid */ |
#define AT_GID 13 /* Real gid */ |
#define AT_EGID 14 /* Effective gid */ |
/* Some more special a_type values describing the hardware. */ |
#define AT_PLATFORM 15 /* String identifying platform. */ |
#define AT_HWCAP 16 /* Machine dependent hints about |
processor capabilities. */ |
/* This entry gives some information about the FPU initialization |
performed by the kernel. */ |
#define AT_FPUCW 17 /* Used FPU control word. */ |
/* Note section contents. Each entry in the note section begins with |
a header of a fixed form. */ |
typedef struct |
{ |
Elf32_Word n_namesz; /* Length of the note's name. */ |
Elf32_Word n_descsz; /* Length of the note's descriptor. */ |
Elf32_Word n_type; /* Type of the note. */ |
} Elf32_Nhdr; |
typedef struct |
{ |
Elf64_Word n_namesz; /* Length of the note's name. */ |
Elf64_Word n_descsz; /* Length of the note's descriptor. */ |
Elf64_Word n_type; /* Type of the note. */ |
} Elf64_Nhdr; |
/* Known names of notes. */ |
/* Solaris entries in the note section have this name. */ |
#define ELF_NOTE_SOLARIS "SUNW Solaris" |
/* Note entries for GNU systems have this name. */ |
#define ELF_NOTE_GNU "GNU" |
/* Defined types of notes for Solaris. */ |
/* Value of descriptor (one word) is desired pagesize for the binary. */ |
#define ELF_NOTE_PAGESIZE_HINT 1 |
/* Defined note types for GNU systems. */ |
/* ABI information. The descriptor consists of words: |
word 0: OS descriptor |
word 1: major version of the ABI |
word 2: minor version of the ABI |
word 3: subminor version of the ABI |
*/ |
#define ELF_NOTE_ABI 1 |
/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI |
note section entry. */ |
#define ELF_NOTE_OS_LINUX 0 |
#define ELF_NOTE_OS_GNU 1 |
#define ELF_NOTE_OS_SOLARIS2 2 |
/* Motorola 68k specific definitions. */ |
/* m68k relocs. */ |
#define R_68K_NONE 0 /* No reloc */ |
#define R_68K_32 1 /* Direct 32 bit */ |
#define R_68K_16 2 /* Direct 16 bit */ |
#define R_68K_8 3 /* Direct 8 bit */ |
#define R_68K_PC32 4 /* PC relative 32 bit */ |
#define R_68K_PC16 5 /* PC relative 16 bit */ |
#define R_68K_PC8 6 /* PC relative 8 bit */ |
#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ |
#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ |
#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ |
#define R_68K_GOT32O 10 /* 32 bit GOT offset */ |
#define R_68K_GOT16O 11 /* 16 bit GOT offset */ |
#define R_68K_GOT8O 12 /* 8 bit GOT offset */ |
#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ |
#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ |
#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ |
#define R_68K_PLT32O 16 /* 32 bit PLT offset */ |
#define R_68K_PLT16O 17 /* 16 bit PLT offset */ |
#define R_68K_PLT8O 18 /* 8 bit PLT offset */ |
#define R_68K_COPY 19 /* Copy symbol at runtime */ |
#define R_68K_GLOB_DAT 20 /* Create GOT entry */ |
#define R_68K_JMP_SLOT 21 /* Create PLT entry */ |
#define R_68K_RELATIVE 22 /* Adjust by program base */ |
/* Keep this the last entry. */ |
#define R_68K_NUM 23 |
/* Intel 80386 specific definitions. */ |
/* i386 relocs. */ |
#define R_386_NONE 0 /* No reloc */ |
#define R_386_32 1 /* Direct 32 bit */ |
#define R_386_PC32 2 /* PC relative 32 bit */ |
#define R_386_GOT32 3 /* 32 bit GOT entry */ |
#define R_386_PLT32 4 /* 32 bit PLT address */ |
#define R_386_COPY 5 /* Copy symbol at runtime */ |
#define R_386_GLOB_DAT 6 /* Create GOT entry */ |
#define R_386_JMP_SLOT 7 /* Create PLT entry */ |
#define R_386_RELATIVE 8 /* Adjust by program base */ |
#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ |
#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ |
/* Keep this the last entry. */ |
#define R_386_NUM 11 |
/* SUN SPARC specific definitions. */ |
/* Values for Elf64_Ehdr.e_flags. */ |
#define EF_SPARCV9_MM 3 |
#define EF_SPARCV9_TSO 0 |
#define EF_SPARCV9_PSO 1 |
#define EF_SPARCV9_RMO 2 |
#define EF_SPARC_EXT_MASK 0xFFFF00 |
#define EF_SPARC_SUN_US1 0x000200 |
#define EF_SPARC_HAL_R1 0x000400 |
/* SPARC relocs. */ |
#define R_SPARC_NONE 0 /* No reloc */ |
#define R_SPARC_8 1 /* Direct 8 bit */ |
#define R_SPARC_16 2 /* Direct 16 bit */ |
#define R_SPARC_32 3 /* Direct 32 bit */ |
#define R_SPARC_DISP8 4 /* PC relative 8 bit */ |
#define R_SPARC_DISP16 5 /* PC relative 16 bit */ |
#define R_SPARC_DISP32 6 /* PC relative 32 bit */ |
#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ |
#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ |
#define R_SPARC_HI22 9 /* High 22 bit */ |
#define R_SPARC_22 10 /* Direct 22 bit */ |
#define R_SPARC_13 11 /* Direct 13 bit */ |
#define R_SPARC_LO10 12 /* Truncated 10 bit */ |
#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ |
#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ |
#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ |
#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ |
#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ |
#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ |
#define R_SPARC_COPY 19 /* Copy symbol at runtime */ |
#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ |
#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ |
#define R_SPARC_RELATIVE 22 /* Adjust by program base */ |
#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ |
/* Additional Sparc64 relocs. */ |
#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ |
#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ |
#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ |
#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ |
#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ |
#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ |
#define R_SPARC_10 30 /* Direct 10 bit */ |
#define R_SPARC_11 31 /* Direct 11 bit */ |
#define R_SPARC_64 32 /* Direct 64 bit */ |
#define R_SPARC_OLO10 33 /* ?? */ |
#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ |
#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ |
#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ |
#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ |
#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ |
#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ |
#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ |
#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ |
#define R_SPARC_7 43 /* Direct 7 bit */ |
#define R_SPARC_5 44 /* Direct 5 bit */ |
#define R_SPARC_6 45 /* Direct 6 bit */ |
#define R_SPARC_DISP64 46 /* PC relative 64 bit */ |
#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ |
#define R_SPARC_HIX22 48 /* High 22 bit complemented */ |
#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ |
#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ |
#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ |
#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ |
#define R_SPARC_REGISTER 53 /* Global register usage */ |
#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ |
#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ |
/* Keep this the last entry. */ |
#define R_SPARC_NUM 56 |
/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ |
#define DT_SPARC_REGISTER 0x70000001 |
#define DT_SPARC_NUM 2 |
/* Bits present in AT_HWCAP, primarily for Sparc32. */ |
#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ |
#define HWCAP_SPARC_STBAR 2 |
#define HWCAP_SPARC_SWAP 4 |
#define HWCAP_SPARC_MULDIV 8 |
#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ |
/* MIPS R3000 specific definitions. */ |
/* Legal values for e_flags field of Elf32_Ehdr. */ |
#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ |
#define EF_MIPS_PIC 2 /* Contains PIC code */ |
#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ |
#define EF_MIPS_XGOT 8 |
#define EF_MIPS_64BIT_WHIRL 16 |
#define EF_MIPS_ABI2 32 |
#define EF_MIPS_ABI_ON32 64 |
#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ |
/* Legal values for MIPS architecture level. */ |
#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ |
#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ |
#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ |
#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ |
#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ |
/* The following are non-official names and should not be used. */ |
#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ |
#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ |
#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ |
#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ |
#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ |
/* Special section indices. */ |
#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ |
#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ |
#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ |
#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ |
#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ |
/* Legal values for sh_type field of Elf32_Shdr. */ |
#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ |
#define SHT_MIPS_MSYM 0x70000001 |
#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ |
#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ |
#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ |
#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ |
#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ |
#define SHT_MIPS_PACKAGE 0x70000007 |
#define SHT_MIPS_PACKSYM 0x70000008 |
#define SHT_MIPS_RELD 0x70000009 |
#define SHT_MIPS_IFACE 0x7000000b |
#define SHT_MIPS_CONTENT 0x7000000c |
#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ |
#define SHT_MIPS_SHDR 0x70000010 |
#define SHT_MIPS_FDESC 0x70000011 |
#define SHT_MIPS_EXTSYM 0x70000012 |
#define SHT_MIPS_DENSE 0x70000013 |
#define SHT_MIPS_PDESC 0x70000014 |
#define SHT_MIPS_LOCSYM 0x70000015 |
#define SHT_MIPS_AUXSYM 0x70000016 |
#define SHT_MIPS_OPTSYM 0x70000017 |
#define SHT_MIPS_LOCSTR 0x70000018 |
#define SHT_MIPS_LINE 0x70000019 |
#define SHT_MIPS_RFDESC 0x7000001a |
#define SHT_MIPS_DELTASYM 0x7000001b |
#define SHT_MIPS_DELTAINST 0x7000001c |
#define SHT_MIPS_DELTACLASS 0x7000001d |
#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ |
#define SHT_MIPS_DELTADECL 0x7000001f |
#define SHT_MIPS_SYMBOL_LIB 0x70000020 |
#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ |
#define SHT_MIPS_TRANSLATE 0x70000022 |
#define SHT_MIPS_PIXIE 0x70000023 |
#define SHT_MIPS_XLATE 0x70000024 |
#define SHT_MIPS_XLATE_DEBUG 0x70000025 |
#define SHT_MIPS_WHIRL 0x70000026 |
#define SHT_MIPS_EH_REGION 0x70000027 |
#define SHT_MIPS_XLATE_OLD 0x70000028 |
#define SHT_MIPS_PDR_EXCEPTION 0x70000029 |
/* Legal values for sh_flags field of Elf32_Shdr. */ |
#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ |
#define SHF_MIPS_MERGE 0x20000000 |
#define SHF_MIPS_ADDR 0x40000000 |
#define SHF_MIPS_STRINGS 0x80000000 |
#define SHF_MIPS_NOSTRIP 0x08000000 |
#define SHF_MIPS_LOCAL 0x04000000 |
#define SHF_MIPS_NAMES 0x02000000 |
#define SHF_MIPS_NODUPE 0x01000000 |
/* Symbol tables. */ |
/* MIPS specific values for `st_other'. */ |
#define STO_MIPS_DEFAULT 0x0 |
#define STO_MIPS_INTERNAL 0x1 |
#define STO_MIPS_HIDDEN 0x2 |
#define STO_MIPS_PROTECTED 0x3 |
#define STO_MIPS_SC_ALIGN_UNUSED 0xff |
/* MIPS specific values for `st_info'. */ |
#define STB_MIPS_SPLIT_COMMON 13 |
/* Entries found in sections of type SHT_MIPS_GPTAB. */ |
typedef union |
{ |
struct |
{ |
Elf32_Word gt_current_g_value; /* -G value used for compilation */ |
Elf32_Word gt_unused; /* Not used */ |
} gt_header; /* First entry in section */ |
struct |
{ |
Elf32_Word gt_g_value; /* If this value were used for -G */ |
Elf32_Word gt_bytes; /* This many bytes would be used */ |
} gt_entry; /* Subsequent entries in section */ |
} Elf32_gptab; |
/* Entry found in sections of type SHT_MIPS_REGINFO. */ |
typedef struct |
{ |
Elf32_Word ri_gprmask; /* General registers used */ |
Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ |
Elf32_Sword ri_gp_value; /* $gp register value */ |
} Elf32_RegInfo; |
/* Entries found in sections of type SHT_MIPS_OPTIONS. */ |
typedef struct |
{ |
unsigned char kind; /* Determines interpretation of the |
variable part of descriptor. */ |
unsigned char size; /* Size of descriptor, including header. */ |
Elf32_Section section; /* Section header index of section affected, |
0 for global options. */ |
Elf32_Word info; /* Kind-specific information. */ |
} Elf_Options; |
/* Values for `kind' field in Elf_Options. */ |
#define ODK_NULL 0 /* Undefined. */ |
#define ODK_REGINFO 1 /* Register usage information. */ |
#define ODK_EXCEPTIONS 2 /* Exception processing options. */ |
#define ODK_PAD 3 /* Section padding options. */ |
#define ODK_HWPATCH 4 /* Hardware workarounds performed */ |
#define ODK_FILL 5 /* record the fill value used by the linker. */ |
#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ |
#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ |
#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ |
/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ |
#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ |
#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ |
#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ |
#define OEX_SMM 0x20000 /* Force sequential memory mode? */ |
#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ |
#define OEX_PRECISEFP OEX_FPDBUG |
#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ |
#define OEX_FPU_INVAL 0x10 |
#define OEX_FPU_DIV0 0x08 |
#define OEX_FPU_OFLO 0x04 |
#define OEX_FPU_UFLO 0x02 |
#define OEX_FPU_INEX 0x01 |
/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ |
#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ |
#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ |
#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ |
#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ |
#define OPAD_PREFIX 0x1 |
#define OPAD_POSTFIX 0x2 |
#define OPAD_SYMBOL 0x4 |
/* Entry found in `.options' section. */ |
typedef struct |
{ |
Elf32_Word hwp_flags1; /* Extra flags. */ |
Elf32_Word hwp_flags2; /* Extra flags. */ |
} Elf_Options_Hw; |
/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ |
#define OHWA0_R4KEOP_CHECKED 0x00000001 |
#define OHWA1_R4KEOP_CLEAN 0x00000002 |
/* MIPS relocs. */ |
#define R_MIPS_NONE 0 /* No reloc */ |
#define R_MIPS_16 1 /* Direct 16 bit */ |
#define R_MIPS_32 2 /* Direct 32 bit */ |
#define R_MIPS_REL32 3 /* PC relative 32 bit */ |
#define R_MIPS_26 4 /* Direct 26 bit shifted */ |
#define R_MIPS_HI16 5 /* High 16 bit */ |
#define R_MIPS_LO16 6 /* Low 16 bit */ |
#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ |
#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ |
#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ |
#define R_MIPS_PC16 10 /* PC relative 16 bit */ |
#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ |
#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ |
#define R_MIPS_SHIFT5 16 |
#define R_MIPS_SHIFT6 17 |
#define R_MIPS_64 18 |
#define R_MIPS_GOT_DISP 19 |
#define R_MIPS_GOT_PAGE 20 |
#define R_MIPS_GOT_OFST 21 |
#define R_MIPS_GOT_HI16 22 |
#define R_MIPS_GOT_LO16 23 |
#define R_MIPS_SUB 24 |
#define R_MIPS_INSERT_A 25 |
#define R_MIPS_INSERT_B 26 |
#define R_MIPS_DELETE 27 |
#define R_MIPS_HIGHER 28 |
#define R_MIPS_HIGHEST 29 |
#define R_MIPS_CALL_HI16 30 |
#define R_MIPS_CALL_LO16 31 |
#define R_MIPS_SCN_DISP 32 |
#define R_MIPS_REL16 33 |
#define R_MIPS_ADD_IMMEDIATE 34 |
#define R_MIPS_PJUMP 35 |
#define R_MIPS_RELGOT 36 |
#define R_MIPS_JALR 37 |
/* Keep this the last entry. */ |
#define R_MIPS_NUM 38 |
/* Legal values for p_type field of Elf32_Phdr. */ |
#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ |
#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ |
#define PT_MIPS_OPTIONS 0x70000002 |
/* Special program header types. */ |
#define PF_MIPS_LOCAL 0x10000000 |
/* Legal values for d_tag field of Elf32_Dyn. */ |
#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ |
#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ |
#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ |
#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ |
#define DT_MIPS_FLAGS 0x70000005 /* Flags */ |
#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ |
#define DT_MIPS_MSYM 0x70000007 |
#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ |
#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ |
#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ |
#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ |
#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ |
#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ |
#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ |
#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ |
#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ |
#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ |
#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ |
#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in |
DT_MIPS_DELTA_CLASS. */ |
#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ |
#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in |
DT_MIPS_DELTA_INSTANCE. */ |
#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ |
#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in |
DT_MIPS_DELTA_RELOC. */ |
#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta |
relocations refer to. */ |
#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in |
DT_MIPS_DELTA_SYM. */ |
#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the |
class declaration. */ |
#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in |
DT_MIPS_DELTA_CLASSSYM. */ |
#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ |
#define DT_MIPS_PIXIE_INIT 0x70000023 |
#define DT_MIPS_SYMBOL_LIB 0x70000024 |
#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 |
#define DT_MIPS_LOCAL_GOTIDX 0x70000026 |
#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 |
#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 |
#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ |
#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ |
#define DT_MIPS_DYNSTR_ALIGN 0x7000002b |
#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ |
#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve |
function stored in GOT. */ |
#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added |
by rld on dlopen() calls. */ |
#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ |
#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ |
#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ |
#define DT_MIPS_NUM 0x32 |
/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ |
#define RHF_NONE 0 /* No flags */ |
#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ |
#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ |
#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ |
#define RHF_NO_MOVE (1 << 3) |
#define RHF_SGI_ONLY (1 << 4) |
#define RHF_GUARANTEE_INIT (1 << 5) |
#define RHF_DELTA_C_PLUS_PLUS (1 << 6) |
#define RHF_GUARANTEE_START_INIT (1 << 7) |
#define RHF_PIXIE (1 << 8) |
#define RHF_DEFAULT_DELAY_LOAD (1 << 9) |
#define RHF_REQUICKSTART (1 << 10) |
#define RHF_REQUICKSTARTED (1 << 11) |
#define RHF_CORD (1 << 12) |
#define RHF_NO_UNRES_UNDEF (1 << 13) |
#define RHF_RLD_ORDER_SAFE (1 << 14) |
/* Entries found in sections of type SHT_MIPS_LIBLIST. */ |
typedef struct |
{ |
Elf32_Word l_name; /* Name (string table index) */ |
Elf32_Word l_time_stamp; /* Timestamp */ |
Elf32_Word l_checksum; /* Checksum */ |
Elf32_Word l_version; /* Interface version */ |
Elf32_Word l_flags; /* Flags */ |
} Elf32_Lib; |
typedef struct |
{ |
Elf64_Word l_name; /* Name (string table index) */ |
Elf64_Word l_time_stamp; /* Timestamp */ |
Elf64_Word l_checksum; /* Checksum */ |
Elf64_Word l_version; /* Interface version */ |
Elf64_Word l_flags; /* Flags */ |
} Elf64_Lib; |
/* Legal values for l_flags. */ |
#define LL_NONE 0 |
#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ |
#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ |
#define LL_REQUIRE_MINOR (1 << 2) |
#define LL_EXPORTS (1 << 3) |
#define LL_DELAY_LOAD (1 << 4) |
#define LL_DELTA (1 << 5) |
/* Entries found in sections of type SHT_MIPS_CONFLICT. */ |
typedef Elf32_Addr Elf32_Conflict; |
/* HPPA specific definitions. */ |
/* Legal values for e_flags field of Elf32_Ehdr. */ |
#define EF_PARISC_TRAPNL 1 /* Trap nil pointer dereference. */ |
#define EF_PARISC_EXT 2 /* Program uses arch. extensions. */ |
#define EF_PARISC_ARCH 0xffff0000 /* Architecture version. */ |
/* Defined values are: |
0x020b PA-RISC 1.0 big-endian |
0x0210 PA-RISC 1.1 big-endian |
0x028b PA-RISC 1.0 little-endian |
0x0290 PA-RISC 1.1 little-endian |
*/ |
/* Legal values for sh_type field of Elf32_Shdr. */ |
#define SHT_PARISC_GOT 0x70000000 /* GOT for external data. */ |
#define SHT_PARISC_ARCH 0x70000001 /* Architecture extensions. */ |
#define SHT_PARISC_GLOBAL 0x70000002 /* Definition of $global$. */ |
#define SHT_PARISC_MILLI 0x70000003 /* Millicode routines. */ |
#define SHT_PARISC_UNWIND 0x70000004 /* Unwind information. */ |
#define SHT_PARISC_PLT 0x70000005 /* Procedure linkage table. */ |
#define SHT_PARISC_SDATA 0x70000006 /* Short initialized data. */ |
#define SHT_PARISC_SBSS 0x70000007 /* Short uninitialized data. */ |
#define SHT_PARISC_SYMEXTN 0x70000008 /* Argument/relocation info. */ |
#define SHT_PARISC_STUBS 0x70000009 /* Linker stubs. */ |
/* Legal values for sh_flags field of Elf32_Shdr. */ |
#define SHF_PARISC_GLOBAL 0x10000000 /* Section defines dp. */ |
#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ |
/* Legal values for ST_TYPE subfield of st_info (symbol type). */ |
#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ |
/* HPPA relocs. */ |
#define R_PARISC_NONE 0 /* No reloc. */ |
#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ |
#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ |
#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ |
#define R_PARISC_DIR14R 4 /* Right 14 bits of eff. address. */ |
#define R_PARISC_PCREL21L 5 /* PC-relative, left 21 bits. */ |
#define R_PARISC_PCREL14R 6 /* PC-relative, right 14 bits. */ |
#define R_PARISC_PCREL17C 7 /* Conditional PC-relative, ignore |
if displacement > 17bits. */ |
#define R_PARISC_PCREL17F 8 /* Conditional PC-relative, must |
fit in 17bits. */ |
#define R_PARISC_DPREL21L 9 /* DP-relative, left 21 bits. */ |
#define R_PARISC_DPREL14R 10 /* DP-relative, right 14 bits. */ |
#define R_PARISC_DPREL14F 11 /* DP-relative, must bit in 14 bits. */ |
#define R_PARISC_DLTREL21L 12 /* DLT-relative, left 21 bits. */ |
#define R_PARISC_DLTREL14R 13 /* DLT-relative, right 14 bits. */ |
#define R_PARISC_DLTREL14F 14 /* DLT-relative, must fit in 14 bits.*/ |
#define R_PARISC_DLTIND21L 15 /* DLT-relative indirect, left |
21 bits. */ |
#define R_PARISC_DLTIND14R 16 /* DLT-relative indirect, right |
14 bits. */ |
#define R_PARISC_DLTIND14F 17 /* DLT-relative indirect, must fit |
int 14 bits. */ |
#define R_PARISC_PLABEL32 18 /* Direct 32-bit reference to proc. */ |
/* Alpha specific definitions. */ |
/* Legal values for e_flags field of Elf64_Ehdr. */ |
#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ |
#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ |
/* Legal values for sh_type field of Elf64_Shdr. */ |
/* These two are primerily concerned with ECOFF debugging info. */ |
#define SHT_ALPHA_DEBUG 0x70000001 |
#define SHT_ALPHA_REGINFO 0x70000002 |
/* Legal values for sh_flags field of Elf64_Shdr. */ |
#define SHF_ALPHA_GPREL 0x10000000 |
/* Legal values for st_other field of Elf64_Sym. */ |
#define STO_ALPHA_NOPV 0x80 /* No PV required. */ |
#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ |
/* Alpha relocs. */ |
#define R_ALPHA_NONE 0 /* No reloc */ |
#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ |
#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ |
#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ |
#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ |
#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ |
#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ |
#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ |
#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ |
#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ |
#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ |
#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ |
#define R_ALPHA_OP_PUSH 12 /* OP stack push */ |
#define R_ALPHA_OP_STORE 13 /* OP stack pop and store */ |
#define R_ALPHA_OP_PSUB 14 /* OP stack subtract */ |
#define R_ALPHA_OP_PRSHIFT 15 /* OP stack right shift */ |
#define R_ALPHA_GPVALUE 16 |
#define R_ALPHA_GPRELHIGH 17 |
#define R_ALPHA_GPRELLOW 18 |
#define R_ALPHA_IMMED_GP_16 19 |
#define R_ALPHA_IMMED_GP_HI32 20 |
#define R_ALPHA_IMMED_SCN_HI32 21 |
#define R_ALPHA_IMMED_BR_HI32 22 |
#define R_ALPHA_IMMED_LO32 23 |
#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ |
#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ |
#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ |
#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ |
/* Keep this the last entry. */ |
#define R_ALPHA_NUM 28 |
/* PowerPC specific declarations */ |
/* PowerPC relocations defined by the ABIs */ |
#define R_PPC_NONE 0 |
#define R_PPC_ADDR32 1 /* 32bit absolute address */ |
#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ |
#define R_PPC_ADDR16 3 /* 16bit absolute address */ |
#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ |
#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ |
#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ |
#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ |
#define R_PPC_ADDR14_BRTAKEN 8 |
#define R_PPC_ADDR14_BRNTAKEN 9 |
#define R_PPC_REL24 10 /* PC relative 26 bit */ |
#define R_PPC_REL14 11 /* PC relative 16 bit */ |
#define R_PPC_REL14_BRTAKEN 12 |
#define R_PPC_REL14_BRNTAKEN 13 |
#define R_PPC_GOT16 14 |
#define R_PPC_GOT16_LO 15 |
#define R_PPC_GOT16_HI 16 |
#define R_PPC_GOT16_HA 17 |
#define R_PPC_PLTREL24 18 |
#define R_PPC_COPY 19 |
#define R_PPC_GLOB_DAT 20 |
#define R_PPC_JMP_SLOT 21 |
#define R_PPC_RELATIVE 22 |
#define R_PPC_LOCAL24PC 23 |
#define R_PPC_UADDR32 24 |
#define R_PPC_UADDR16 25 |
#define R_PPC_REL32 26 |
#define R_PPC_PLT32 27 |
#define R_PPC_PLTREL32 28 |
#define R_PPC_PLT16_LO 29 |
#define R_PPC_PLT16_HI 30 |
#define R_PPC_PLT16_HA 31 |
#define R_PPC_SDAREL16 32 |
#define R_PPC_SECTOFF 33 |
#define R_PPC_SECTOFF_LO 34 |
#define R_PPC_SECTOFF_HI 35 |
#define R_PPC_SECTOFF_HA 36 |
/* Keep this the last entry. */ |
#define R_PPC_NUM 37 |
/* The remaining relocs are from the Embedded ELF ABI, and are not |
in the SVR4 ELF ABI. */ |
#define R_PPC_EMB_NADDR32 101 |
#define R_PPC_EMB_NADDR16 102 |
#define R_PPC_EMB_NADDR16_LO 103 |
#define R_PPC_EMB_NADDR16_HI 104 |
#define R_PPC_EMB_NADDR16_HA 105 |
#define R_PPC_EMB_SDAI16 106 |
#define R_PPC_EMB_SDA2I16 107 |
#define R_PPC_EMB_SDA2REL 108 |
#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ |
#define R_PPC_EMB_MRKREF 110 |
#define R_PPC_EMB_RELSEC16 111 |
#define R_PPC_EMB_RELST_LO 112 |
#define R_PPC_EMB_RELST_HI 113 |
#define R_PPC_EMB_RELST_HA 114 |
#define R_PPC_EMB_BIT_FLD 115 |
#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ |
/* Diab tool relocations. */ |
#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ |
#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ |
#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ |
#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ |
#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ |
#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ |
/* This is a phony reloc to handle any old fashioned TOC16 references |
that may still be in object files. */ |
#define R_PPC_TOC16 255 |
/* ARM specific declarations */ |
/* Processor specific flags for the ELF header e_flags field. */ |
#define EF_ARM_RELEXEC 0x01 |
#define EF_ARM_HASENTRY 0x02 |
#define EF_ARM_INTERWORK 0x04 |
#define EF_ARM_APCS_26 0x08 |
#define EF_ARM_APCS_FLOAT 0x10 |
#define EF_ARM_PIC 0x20 |
#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ |
#define EF_NEW_ABI 0x80 |
#define EF_OLD_ABI 0x100 |
/* Additional symbol types for Thumb */ |
#define STT_ARM_TFUNC 0xd |
/* ARM-specific values for sh_flags */ |
#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ |
#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined |
in the input to a link step */ |
/* ARM-specific program header flags */ |
#define PF_ARM_SB 0x10000000 /* Segment contains the location |
addressed by the static base */ |
/* ARM relocs. */ |
#define R_ARM_NONE 0 /* No reloc */ |
#define R_ARM_PC24 1 /* PC relative 26 bit branch */ |
#define R_ARM_ABS32 2 /* Direct 32 bit */ |
#define R_ARM_REL32 3 /* PC relative 32 bit */ |
#define R_ARM_PC13 4 |
#define R_ARM_ABS16 5 /* Direct 16 bit */ |
#define R_ARM_ABS12 6 /* Direct 12 bit */ |
#define R_ARM_THM_ABS5 7 |
#define R_ARM_ABS8 8 /* Direct 8 bit */ |
#define R_ARM_SBREL32 9 |
#define R_ARM_THM_PC22 10 |
#define R_ARM_THM_PC8 11 |
#define R_ARM_AMP_VCALL9 12 |
#define R_ARM_SWI24 13 |
#define R_ARM_THM_SWI8 14 |
#define R_ARM_XPC25 15 |
#define R_ARM_THM_XPC22 16 |
#define R_ARM_COPY 20 /* Copy symbol at runtime */ |
#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ |
#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ |
#define R_ARM_RELATIVE 23 /* Adjust by program base */ |
#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ |
#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ |
#define R_ARM_GOT32 26 /* 32 bit GOT entry */ |
#define R_ARM_PLT32 27 /* 32 bit PLT address */ |
#define R_ARM_GNU_VTENTRY 100 |
#define R_ARM_GNU_VTINHERIT 101 |
#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ |
#define R_ARM_THM_PC9 103 /* thumb conditional branch */ |
#define R_ARM_RXPC25 249 |
#define R_ARM_RSBREL32 250 |
#define R_ARM_THM_RPC22 251 |
#define R_ARM_RREL32 252 |
#define R_ARM_RABS22 253 |
#define R_ARM_RPC24 254 |
#define R_ARM_RBASE 255 |
/* Keep this the last entry. */ |
#define R_ARM_NUM 256 |
/* TMS320C67xx specific declarations */ |
/* XXX: no ELF standard yet */ |
/* TMS320C67xx relocs. */ |
#define R_C60_32 1 |
#define R_C60_GOT32 3 /* 32 bit GOT entry */ |
#define R_C60_PLT32 4 /* 32 bit PLT address */ |
#define R_C60_COPY 5 /* Copy symbol at runtime */ |
#define R_C60_GLOB_DAT 6 /* Create GOT entry */ |
#define R_C60_JMP_SLOT 7 /* Create PLT entry */ |
#define R_C60_RELATIVE 8 /* Adjust by program base */ |
#define R_C60_GOTOFF 9 /* 32 bit offset to GOT */ |
#define R_C60_GOTPC 10 /* 32 bit PC relative offset to GOT */ |
#define R_C60HI16 0x55 // high 16 bit MVKH embedded |
#define R_C60LO16 0x54 // low 16 bit MVKL embedded |
#endif /* elf.h */ |
/programs/develop/ktcc/trunk/source/float.h |
---|
0,0 → 1,57 |
#ifndef _FLOAT_H_ |
#define _FLOAT_H_ |
#define FLT_RADIX 2 |
/* IEEE float */ |
#define FLT_MANT_DIG 24 |
#define FLT_DIG 6 |
#define FLT_ROUNDS 1 |
#define FLT_EPSILON 1.19209290e-07F |
#define FLT_MIN_EXP (-125) |
#define FLT_MIN 1.17549435e-38F |
#define FLT_MIN_10_EXP (-37) |
#define FLT_MAX_EXP 128 |
#define FLT_MAX 3.40282347e+38F |
#define FLT_MAX_10_EXP 38 |
/* IEEE double */ |
#define DBL_MANT_DIG 53 |
#define DBL_DIG 15 |
#define DBL_EPSILON 2.2204460492503131e-16 |
#define DBL_MIN_EXP (-1021) |
#define DBL_MIN 2.2250738585072014e-308 |
#define DBL_MIN_10_EXP (-307) |
#define DBL_MAX_EXP 1024 |
#define DBL_MAX 1.7976931348623157e+308 |
#define DBL_MAX_10_EXP 308 |
/* horrible intel long double */ |
#ifdef __i386__ |
#define LDBL_MANT_DIG 64 |
#define LDBL_DIG 18 |
#define LDBL_EPSILON 1.08420217248550443401e-19L |
#define LDBL_MIN_EXP (-16381) |
#define LDBL_MIN 3.36210314311209350626e-4932L |
#define LDBL_MIN_10_EXP (-4931) |
#define LDBL_MAX_EXP 16384 |
#define LDBL_MAX 1.18973149535723176502e+4932L |
#define LDBL_MAX_10_EXP 4932 |
#else |
/* same as IEEE double */ |
#define LDBL_MANT_DIG 53 |
#define LDBL_DIG 15 |
#define LDBL_EPSILON 2.2204460492503131e-16 |
#define LDBL_MIN_EXP (-1021) |
#define LDBL_MIN 2.2250738585072014e-308 |
#define LDBL_MIN_10_EXP (-307) |
#define LDBL_MAX_EXP 1024 |
#define LDBL_MAX 1.7976931348623157e+308 |
#define LDBL_MAX_10_EXP 308 |
#endif |
#endif /* _FLOAT_H_ */ |
/programs/develop/ktcc/trunk/source/i386-asm.c |
---|
0,0 → 1,1183 |
/* |
* i386 specific functions for TCC assembler |
* |
* Copyright (c) 2001, 2002 Fabrice Bellard |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation; either |
* version 2 of the License, or (at your option) any later version. |
* |
* This library is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
* Lesser General Public License for more details. |
* |
* You should have received a copy of the GNU Lesser General Public |
* License along with this library; if not, write to the Free Software |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
#define MAX_OPERANDS 3 |
typedef struct ASMInstr { |
uint16_t sym; |
uint16_t opcode; |
uint16_t instr_type; |
#define OPC_JMP 0x01 /* jmp operand */ |
#define OPC_B 0x02 /* only used zith OPC_WL */ |
#define OPC_WL 0x04 /* accepts w, l or no suffix */ |
#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ |
#define OPC_REG 0x08 /* register is added to opcode */ |
#define OPC_MODRM 0x10 /* modrm encoding */ |
#define OPC_FWAIT 0x20 /* add fwait opcode */ |
#define OPC_TEST 0x40 /* test opcodes */ |
#define OPC_SHIFT 0x80 /* shift opcodes */ |
#define OPC_D16 0x0100 /* generate data16 prefix */ |
#define OPC_ARITH 0x0200 /* arithmetic opcodes */ |
#define OPC_SHORTJMP 0x0400 /* short jmp operand */ |
#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ |
#define OPC_GROUP_SHIFT 13 |
/* in order to compress the operand type, we use specific operands and |
we or only with EA */ |
#define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_MMX 3 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_SSE 4 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_CR 5 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_TR 6 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_DB 7 /* warning: value is hardcoded from TOK_ASM_xxx */ |
#define OPT_SEG 8 |
#define OPT_ST 9 |
#define OPT_IM8 10 |
#define OPT_IM8S 11 |
#define OPT_IM16 12 |
#define OPT_IM32 13 |
#define OPT_EAX 14 /* %al, %ax or %eax register */ |
#define OPT_ST0 15 /* %st(0) register */ |
#define OPT_CL 16 /* %cl register */ |
#define OPT_DX 17 /* %dx register */ |
#define OPT_ADDR 18 /* OP_EA with only offset */ |
#define OPT_INDIR 19 /* *(expr) */ |
/* composite types */ |
#define OPT_COMPOSITE_FIRST 20 |
#define OPT_IM 20 /* IM8 | IM16 | IM32 */ |
#define OPT_REG 21 /* REG8 | REG16 | REG32 */ |
#define OPT_REGW 22 /* REG16 | REG32 */ |
#define OPT_IMW 23 /* IM16 | IM32 */ |
/* can be ored with any OPT_xxx */ |
#define OPT_EA 0x80 |
uint8_t nb_ops; |
uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ |
} ASMInstr; |
typedef struct Operand { |
uint32_t type; |
#define OP_REG8 (1 << OPT_REG8) |
#define OP_REG16 (1 << OPT_REG16) |
#define OP_REG32 (1 << OPT_REG32) |
#define OP_MMX (1 << OPT_MMX) |
#define OP_SSE (1 << OPT_SSE) |
#define OP_CR (1 << OPT_CR) |
#define OP_TR (1 << OPT_TR) |
#define OP_DB (1 << OPT_DB) |
#define OP_SEG (1 << OPT_SEG) |
#define OP_ST (1 << OPT_ST) |
#define OP_IM8 (1 << OPT_IM8) |
#define OP_IM8S (1 << OPT_IM8S) |
#define OP_IM16 (1 << OPT_IM16) |
#define OP_IM32 (1 << OPT_IM32) |
#define OP_EAX (1 << OPT_EAX) |
#define OP_ST0 (1 << OPT_ST0) |
#define OP_CL (1 << OPT_CL) |
#define OP_DX (1 << OPT_DX) |
#define OP_ADDR (1 << OPT_ADDR) |
#define OP_INDIR (1 << OPT_INDIR) |
#define OP_EA 0x40000000 |
#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32) |
#define OP_IM OP_IM32 |
int8_t reg; /* register, -1 if none */ |
int8_t reg2; /* second register, -1 if none */ |
uint8_t shift; |
ExprValue e; |
} Operand; |
static const uint8_t reg_to_size[5] = { |
[OP_REG8] = 0, |
[OP_REG16] = 1, |
[OP_REG32] = 2, |
}; |
#define WORD_PREFIX_OPCODE 0x66 |
#define NB_TEST_OPCODES 30 |
static const uint8_t test_bits[NB_TEST_OPCODES] = { |
0x00, /* o */ |
0x01, /* no */ |
0x02, /* b */ |
0x02, /* c */ |
0x02, /* nae */ |
0x03, /* nb */ |
0x03, /* nc */ |
0x03, /* ae */ |
0x04, /* e */ |
0x04, /* z */ |
0x05, /* ne */ |
0x05, /* nz */ |
0x06, /* be */ |
0x06, /* na */ |
0x07, /* nbe */ |
0x07, /* a */ |
0x08, /* s */ |
0x09, /* ns */ |
0x0a, /* p */ |
0x0a, /* pe */ |
0x0b, /* np */ |
0x0b, /* po */ |
0x0c, /* l */ |
0x0c, /* nge */ |
0x0d, /* nl */ |
0x0d, /* ge */ |
0x0e, /* le */ |
0x0e, /* ng */ |
0x0f, /* nle */ |
0x0f, /* g */ |
}; |
static const ASMInstr asm_instrs[] = { |
#define ALT(x) x |
#define DEF_ASM_OP0(name, opcode) |
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, |
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, |
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, |
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }}, |
#include "i386-asm.h" |
/* 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) |
#include "i386-asm.h" |
}; |
static inline int get_reg_shift(TCCState *s1) |
{ |
int shift, v; |
v = asm_int_expr(s1); |
switch(v) { |
case 1: |
shift = 0; |
break; |
case 2: |
shift = 1; |
break; |
case 4: |
shift = 2; |
break; |
case 8: |
shift = 3; |
break; |
default: |
expect("1, 2, 4 or 8 constant"); |
shift = 0; |
break; |
} |
return shift; |
} |
static int asm_parse_reg(void) |
{ |
int reg; |
if (tok != '%') |
goto error_32; |
next(); |
if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { |
reg = tok - TOK_ASM_eax; |
next(); |
return reg; |
} else { |
error_32: |
expect("32 bit register"); |
return 0; |
} |
} |
static void parse_operand(TCCState *s1, Operand *op) |
{ |
ExprValue e; |
int reg, indir; |
const char *p; |
indir = 0; |
if (tok == '*') { |
next(); |
indir = OP_INDIR; |
} |
if (tok == '%') { |
next(); |
if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { |
reg = tok - TOK_ASM_al; |
op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ |
op->reg = reg & 7; |
if ((op->type & OP_REG) && op->reg == TREG_EAX) |
op->type |= OP_EAX; |
else if (op->type == OP_REG8 && op->reg == TREG_ECX) |
op->type |= OP_CL; |
else if (op->type == OP_REG16 && op->reg == TREG_EDX) |
op->type |= OP_DX; |
} else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { |
op->type = OP_DB; |
op->reg = tok - TOK_ASM_dr0; |
} else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { |
op->type = OP_SEG; |
op->reg = tok - TOK_ASM_es; |
} else if (tok == TOK_ASM_st) { |
op->type = OP_ST; |
op->reg = 0; |
next(); |
if (tok == '(') { |
next(); |
if (tok != TOK_PPNUM) |
goto reg_error; |
p = tokc.cstr->data; |
reg = p[0] - '0'; |
if ((unsigned)reg >= 8 || p[1] != '\0') |
goto reg_error; |
op->reg = reg; |
next(); |
skip(')'); |
} |
if (op->reg == 0) |
op->type |= OP_ST0; |
goto no_skip; |
} else { |
reg_error: |
error("unknown register"); |
} |
next(); |
no_skip: ; |
} else if (tok == '$') { |
/* constant value */ |
next(); |
asm_expr(s1, &e); |
op->type = OP_IM32; |
op->e.v = e.v; |
op->e.sym = e.sym; |
if (!op->e.sym) { |
if (op->e.v == (uint8_t)op->e.v) |
op->type |= OP_IM8; |
if (op->e.v == (int8_t)op->e.v) |
op->type |= OP_IM8S; |
if (op->e.v == (uint16_t)op->e.v) |
op->type |= OP_IM16; |
} |
} else { |
/* address(reg,reg2,shift) with all variants */ |
op->type = OP_EA; |
op->reg = -1; |
op->reg2 = -1; |
op->shift = 0; |
if (tok != '(') { |
asm_expr(s1, &e); |
op->e.v = e.v; |
op->e.sym = e.sym; |
} else { |
op->e.v = 0; |
op->e.sym = NULL; |
} |
if (tok == '(') { |
next(); |
if (tok != ',') { |
op->reg = asm_parse_reg(); |
} |
if (tok == ',') { |
next(); |
if (tok != ',') { |
op->reg2 = asm_parse_reg(); |
} |
skip(','); |
op->shift = get_reg_shift(s1); |
} |
skip(')'); |
} |
if (op->reg == -1 && op->reg2 == -1) |
op->type |= OP_ADDR; |
} |
op->type |= indir; |
} |
/* XXX: unify with C code output ? */ |
static void gen_expr32(ExprValue *pe) |
{ |
if (pe->sym) |
greloc(cur_text_section, pe->sym, ind, R_386_32); |
gen_le32(pe->v); |
} |
/* XXX: unify with C code output ? */ |
static void gen_disp32(ExprValue *pe) |
{ |
Sym *sym; |
sym = pe->sym; |
if (sym) { |
if (sym->r == cur_text_section->sh_num) { |
/* same section: we can output an absolute value. Note |
that the TCC compiler behaves differently here because |
it always outputs a relocation to ease (future) code |
elimination in the linker */ |
gen_le32(pe->v + (long)sym->next - ind - 4); |
} else { |
greloc(cur_text_section, sym, ind, R_386_PC32); |
gen_le32(pe->v - 4); |
} |
} else { |
/* put an empty PC32 relocation */ |
put_elf_reloc(symtab_section, cur_text_section, |
ind, R_386_PC32, 0); |
gen_le32(pe->v - 4); |
} |
} |
static void gen_le16(int v) |
{ |
g(v); |
g(v >> 8); |
} |
/* generate the modrm operand */ |
static inline void asm_modrm(int reg, Operand *op) |
{ |
int mod, reg1, reg2, sib_reg1; |
if (op->type & (OP_REG | OP_MMX | OP_SSE)) { |
g(0xc0 + (reg << 3) + op->reg); |
} else if (op->reg == -1 && op->reg2 == -1) { |
/* displacement only */ |
g(0x05 + (reg << 3)); |
gen_expr32(&op->e); |
} else { |
sib_reg1 = op->reg; |
/* fist compute displacement encoding */ |
if (sib_reg1 == -1) { |
sib_reg1 = 5; |
mod = 0x00; |
} else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { |
mod = 0x00; |
} else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { |
mod = 0x40; |
} else { |
mod = 0x80; |
} |
/* compute if sib byte needed */ |
reg1 = op->reg; |
if (op->reg2 != -1) |
reg1 = 4; |
g(mod + (reg << 3) + reg1); |
if (reg1 == 4) { |
/* add sib byte */ |
reg2 = op->reg2; |
if (reg2 == -1) |
reg2 = 4; /* indicate no index */ |
g((op->shift << 6) + (reg2 << 3) + sib_reg1); |
} |
/* add offset */ |
if (mod == 0x40) { |
g(op->e.v); |
} else if (mod == 0x80 || op->reg == -1) { |
gen_expr32(&op->e); |
} |
} |
} |
static void asm_opcode(TCCState *s1, int opcode) |
{ |
const ASMInstr *pa; |
int i, modrm_index, reg, v, op1, is_short_jmp; |
int nb_ops, s, ss; |
Operand ops[MAX_OPERANDS], *pop; |
int op_type[3]; /* decoded op type */ |
/* get operands */ |
pop = ops; |
nb_ops = 0; |
for(;;) { |
if (tok == ';' || tok == TOK_LINEFEED) |
break; |
if (nb_ops >= MAX_OPERANDS) { |
error("incorrect number of operands"); |
} |
parse_operand(s1, pop); |
pop++; |
nb_ops++; |
if (tok != ',') |
break; |
next(); |
} |
is_short_jmp = 0; |
s = 0; /* avoid warning */ |
/* optimize matching by using a lookup table (no hashing is needed |
!) */ |
for(pa = asm_instrs; pa->sym != 0; pa++) { |
s = 0; |
if (pa->instr_type & OPC_FARITH) { |
v = opcode - pa->sym; |
if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) |
continue; |
} else if (pa->instr_type & OPC_ARITH) { |
if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4)) |
continue; |
goto compute_size; |
} else if (pa->instr_type & OPC_SHIFT) { |
if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4)) |
continue; |
goto compute_size; |
} else if (pa->instr_type & OPC_TEST) { |
if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) |
continue; |
} else if (pa->instr_type & OPC_B) { |
if (!(opcode >= pa->sym && opcode <= pa->sym + 3)) |
continue; |
compute_size: |
s = (opcode - pa->sym) & 3; |
} else if (pa->instr_type & OPC_WL) { |
if (!(opcode >= pa->sym && opcode <= pa->sym + 2)) |
continue; |
s = opcode - pa->sym + 1; |
} else { |
if (pa->sym != opcode) |
continue; |
} |
if (pa->nb_ops != nb_ops) |
continue; |
/* now decode and check each operand */ |
for(i = 0; i < nb_ops; i++) { |
int op1, op2; |
op1 = pa->op_type[i]; |
op2 = op1 & 0x1f; |
switch(op2) { |
case OPT_IM: |
v = OP_IM8 | OP_IM16 | OP_IM32; |
break; |
case OPT_REG: |
v = OP_REG8 | OP_REG16 | OP_REG32; |
break; |
case OPT_REGW: |
v = OP_REG16 | OP_REG32; |
break; |
case OPT_IMW: |
v = OP_IM16 | OP_IM32; |
break; |
default: |
v = 1 << op2; |
break; |
} |
if (op1 & OPT_EA) |
v |= OP_EA; |
op_type[i] = v; |
if ((ops[i].type & v) == 0) |
goto next; |
} |
/* all is matching ! */ |
break; |
next: ; |
} |
if (pa->sym == 0) { |
if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) { |
int b; |
b = op0_codes[opcode - TOK_ASM_pusha]; |
if (b & 0xff00) |
g(b >> 8); |
g(b); |
return; |
} else { |
error("unknown opcode '%s'", |
get_tok_str(opcode, NULL)); |
} |
} |
/* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ |
if (s == 3) { |
for(i = 0; s == 3 && i < nb_ops; i++) { |
if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) |
s = reg_to_size[ops[i].type & OP_REG]; |
} |
if (s == 3) { |
if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && |
(ops[0].type & (OP_SEG | OP_IM8S | OP_IM32))) |
s = 2; |
else |
error("cannot infer opcode suffix"); |
} |
} |
/* generate data16 prefix if needed */ |
ss = s; |
if (s == 1 || (pa->instr_type & OPC_D16)) |
g(WORD_PREFIX_OPCODE); |
else if (s == 2) |
s = 1; |
/* now generates the operation */ |
if (pa->instr_type & OPC_FWAIT) |
g(0x9b); |
v = pa->opcode; |
if (v == 0x69 || v == 0x69) { |
/* kludge for imul $im, %reg */ |
nb_ops = 3; |
ops[2] = ops[1]; |
} else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { |
v--; /* int $3 case */ |
nb_ops = 0; |
} else if ((v == 0x06 || v == 0x07)) { |
if (ops[0].reg >= 4) { |
/* push/pop %fs or %gs */ |
v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); |
} else { |
v += ops[0].reg << 3; |
} |
nb_ops = 0; |
} else if (v <= 0x05) { |
/* arith case */ |
v += ((opcode - TOK_ASM_addb) >> 2) << 3; |
} else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { |
/* fpu arith case */ |
v += ((opcode - pa->sym) / 6) << 3; |
} |
if (pa->instr_type & OPC_REG) { |
for(i = 0; i < nb_ops; i++) { |
if (op_type[i] & (OP_REG | OP_ST)) { |
v += ops[i].reg; |
break; |
} |
} |
/* mov $im, %reg case */ |
if (pa->opcode == 0xb0 && s >= 1) |
v += 7; |
} |
if (pa->instr_type & OPC_B) |
v += s; |
if (pa->instr_type & OPC_TEST) |
v += test_bits[opcode - pa->sym]; |
if (pa->instr_type & OPC_SHORTJMP) { |
Sym *sym; |
int jmp_disp; |
/* see if we can really generate the jump with a byte offset */ |
sym = ops[0].e.sym; |
if (!sym) |
goto no_short_jump; |
if (sym->r != cur_text_section->sh_num) |
goto no_short_jump; |
jmp_disp = ops[0].e.v + (long)sym->next - ind - 2; |
if (jmp_disp == (int8_t)jmp_disp) { |
/* OK to generate jump */ |
is_short_jmp = 1; |
ops[0].e.v = jmp_disp; |
} else { |
no_short_jump: |
if (pa->instr_type & OPC_JMP) { |
/* long jump will be allowed. need to modify the |
opcode slightly */ |
if (v == 0xeb) |
v = 0xe9; |
else |
v += 0x0f10; |
} else { |
error("invalid displacement"); |
} |
} |
} |
op1 = v >> 8; |
if (op1) |
g(op1); |
g(v); |
/* search which operand will used for modrm */ |
modrm_index = 0; |
if (pa->instr_type & OPC_SHIFT) { |
reg = (opcode - pa->sym) >> 2; |
if (reg == 6) |
reg = 7; |
} else if (pa->instr_type & OPC_ARITH) { |
reg = (opcode - pa->sym) >> 2; |
} else if (pa->instr_type & OPC_FARITH) { |
reg = (opcode - pa->sym) / 6; |
} else { |
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; |
} |
if (pa->instr_type & OPC_MODRM) { |
/* first look for an ea operand */ |
for(i = 0;i < nb_ops; i++) { |
if (op_type[i] & OP_EA) |
goto modrm_found; |
} |
/* then if not found, a register or indirection (shift instructions) */ |
for(i = 0;i < nb_ops; i++) { |
if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) |
goto modrm_found; |
} |
#ifdef ASM_DEBUG |
error("bad op table"); |
#endif |
modrm_found: |
modrm_index = i; |
/* if a register is used in another operand then it is |
used instead of group */ |
for(i = 0;i < nb_ops; i++) { |
v = op_type[i]; |
if (i != modrm_index && |
(v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { |
reg = ops[i].reg; |
break; |
} |
} |
asm_modrm(reg, &ops[modrm_index]); |
} |
/* emit constants */ |
if (pa->opcode == 0x9a || pa->opcode == 0xea) { |
/* ljmp or lcall kludge */ |
gen_expr32(&ops[1].e); |
if (ops[0].e.sym) |
error("cannot relocate"); |
gen_le16(ops[0].e.v); |
} else { |
for(i = 0;i < nb_ops; i++) { |
v = op_type[i]; |
if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) { |
/* if multiple sizes are given it means we must look |
at the op size */ |
if (v == (OP_IM8 | OP_IM16 | OP_IM32) || |
v == (OP_IM16 | OP_IM32)) { |
if (ss == 0) |
v = OP_IM8; |
else if (ss == 1) |
v = OP_IM16; |
else |
v = OP_IM32; |
} |
if (v & (OP_IM8 | OP_IM8S)) { |
if (ops[i].e.sym) |
goto error_relocate; |
g(ops[i].e.v); |
} else if (v & OP_IM16) { |
if (ops[i].e.sym) { |
error_relocate: |
error("cannot relocate"); |
} |
gen_le16(ops[i].e.v); |
} else { |
if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { |
if (is_short_jmp) |
g(ops[i].e.v); |
else |
gen_disp32(&ops[i].e); |
} else { |
gen_expr32(&ops[i].e); |
} |
} |
} |
} |
} |
} |
#define NB_SAVED_REGS 3 |
#define NB_ASM_REGS 8 |
/* return the constraint priority (we allocate first the lowest |
numbered constraints) */ |
static inline int constraint_priority(const char *str) |
{ |
int priority, c, pr; |
/* we take the lowest priority */ |
priority = 0; |
for(;;) { |
c = *str; |
if (c == '\0') |
break; |
str++; |
switch(c) { |
case 'A': |
pr = 0; |
break; |
case 'a': |
case 'b': |
case 'c': |
case 'd': |
case 'S': |
case 'D': |
pr = 1; |
break; |
case 'q': |
pr = 2; |
break; |
case 'r': |
pr = 3; |
break; |
case 'N': |
case 'M': |
case 'I': |
case 'i': |
case 'm': |
case 'g': |
pr = 4; |
break; |
default: |
error("unknown constraint '%c'", c); |
pr = 0; |
} |
if (pr > priority) |
priority = pr; |
} |
return priority; |
} |
static const char *skip_constraint_modifiers(const char *p) |
{ |
while (*p == '=' || *p == '&' || *p == '+' || *p == '%') |
p++; |
return p; |
} |
#define REG_OUT_MASK 0x01 |
#define REG_IN_MASK 0x02 |
#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) |
static void asm_compute_constraints(ASMOperand *operands, |
int nb_operands, int nb_outputs, |
const uint8_t *clobber_regs, |
int *pout_reg) |
{ |
ASMOperand *op; |
int sorted_op[MAX_ASM_OPERANDS]; |
int i, j, k, p1, p2, tmp, reg, c, reg_mask; |
const char *str; |
uint8_t regs_allocated[NB_ASM_REGS]; |
/* init fields */ |
for(i=0;i<nb_operands;i++) { |
op = &operands[i]; |
op->input_index = -1; |
op->ref_index = -1; |
op->reg = -1; |
op->is_memory = 0; |
op->is_rw = 0; |
} |
/* compute constraint priority and evaluate references to output |
constraints if input constraints */ |
for(i=0;i<nb_operands;i++) { |
op = &operands[i]; |
str = op->constraint; |
str = skip_constraint_modifiers(str); |
if (isnum(*str) || *str == '[') { |
/* this is a reference to another constraint */ |
k = find_constraint(operands, nb_operands, str, NULL); |
if ((unsigned)k >= i || i < nb_outputs) |
error("invalid reference in constraint %d ('%s')", |
i, str); |
op->ref_index = k; |
if (operands[k].input_index >= 0) |
error("cannot reference twice the same operand"); |
operands[k].input_index = i; |
op->priority = 5; |
} else { |
op->priority = constraint_priority(str); |
} |
} |
/* sort operands according to their priority */ |
for(i=0;i<nb_operands;i++) |
sorted_op[i] = i; |
for(i=0;i<nb_operands - 1;i++) { |
for(j=i+1;j<nb_operands;j++) { |
p1 = operands[sorted_op[i]].priority; |
p2 = operands[sorted_op[j]].priority; |
if (p2 < p1) { |
tmp = sorted_op[i]; |
sorted_op[i] = sorted_op[j]; |
sorted_op[j] = tmp; |
} |
} |
} |
for(i = 0;i < NB_ASM_REGS; i++) { |
if (clobber_regs[i]) |
regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; |
else |
regs_allocated[i] = 0; |
} |
/* esp cannot be used */ |
regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK; |
/* ebp cannot be used yet */ |
regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK; |
/* allocate registers and generate corresponding asm moves */ |
for(i=0;i<nb_operands;i++) { |
j = sorted_op[i]; |
op = &operands[j]; |
str = op->constraint; |
/* no need to allocate references */ |
if (op->ref_index >= 0) |
continue; |
/* select if register is used for output, input or both */ |
if (op->input_index >= 0) { |
reg_mask = REG_IN_MASK | REG_OUT_MASK; |
} else if (j < nb_outputs) { |
reg_mask = REG_OUT_MASK; |
} else { |
reg_mask = REG_IN_MASK; |
} |
try_next: |
c = *str++; |
switch(c) { |
case '=': |
goto try_next; |
case '+': |
op->is_rw = 1; |
/* FALL THRU */ |
case '&': |
if (j >= nb_outputs) |
error("'%c' modifier can only be applied to outputs", c); |
reg_mask = REG_IN_MASK | REG_OUT_MASK; |
goto try_next; |
case 'A': |
/* allocate both eax and edx */ |
if (is_reg_allocated(TREG_EAX) || |
is_reg_allocated(TREG_EDX)) |
goto try_next; |
op->is_llong = 1; |
op->reg = TREG_EAX; |
regs_allocated[TREG_EAX] |= reg_mask; |
regs_allocated[TREG_EDX] |= reg_mask; |
break; |
case 'a': |
reg = TREG_EAX; |
goto alloc_reg; |
case 'b': |
reg = 3; |
goto alloc_reg; |
case 'c': |
reg = TREG_ECX; |
goto alloc_reg; |
case 'd': |
reg = TREG_EDX; |
goto alloc_reg; |
case 'S': |
reg = 6; |
goto alloc_reg; |
case 'D': |
reg = 7; |
alloc_reg: |
if (is_reg_allocated(reg)) |
goto try_next; |
goto reg_found; |
case 'q': |
/* eax, ebx, ecx or edx */ |
for(reg = 0; reg < 4; reg++) { |
if (!is_reg_allocated(reg)) |
goto reg_found; |
} |
goto try_next; |
case 'r': |
/* any general register */ |
for(reg = 0; reg < 8; reg++) { |
if (!is_reg_allocated(reg)) |
goto reg_found; |
} |
goto try_next; |
reg_found: |
/* now we can reload in the register */ |
op->is_llong = 0; |
op->reg = reg; |
regs_allocated[reg] |= reg_mask; |
break; |
case 'i': |
if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) |
goto try_next; |
break; |
case 'I': |
case 'N': |
case 'M': |
if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) |
goto try_next; |
break; |
case 'm': |
case 'g': |
/* nothing special to do because the operand is already in |
memory, except if the pointer itself is stored in a |
memory variable (VT_LLOCAL case) */ |
/* XXX: fix constant case */ |
/* if it is a reference to a memory zone, it must lie |
in a register, so we reserve the register in the |
input registers and a load will be generated |
later */ |
if (j < nb_outputs || c == 'm') { |
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { |
/* any general register */ |
for(reg = 0; reg < 8; reg++) { |
if (!(regs_allocated[reg] & REG_IN_MASK)) |
goto reg_found1; |
} |
goto try_next; |
reg_found1: |
/* now we can reload in the register */ |
regs_allocated[reg] |= REG_IN_MASK; |
op->reg = reg; |
op->is_memory = 1; |
} |
} |
break; |
default: |
error("asm constraint %d ('%s') could not be satisfied", |
j, op->constraint); |
break; |
} |
/* if a reference is present for that operand, we assign it too */ |
if (op->input_index >= 0) { |
operands[op->input_index].reg = op->reg; |
operands[op->input_index].is_llong = op->is_llong; |
} |
} |
/* compute out_reg. It is used to store outputs registers to memory |
locations references by pointers (VT_LLOCAL case) */ |
*pout_reg = -1; |
for(i=0;i<nb_operands;i++) { |
op = &operands[i]; |
if (op->reg >= 0 && |
(op->vt->r & VT_VALMASK) == VT_LLOCAL && |
!op->is_memory) { |
for(reg = 0; reg < 8; reg++) { |
if (!(regs_allocated[reg] & REG_OUT_MASK)) |
goto reg_found2; |
} |
error("could not find free output register for reloading"); |
reg_found2: |
*pout_reg = reg; |
break; |
} |
} |
/* print sorted constraints */ |
#ifdef ASM_DEBUG |
for(i=0;i<nb_operands;i++) { |
j = sorted_op[i]; |
op = &operands[j]; |
printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", |
j, |
op->id ? get_tok_str(op->id, NULL) : "", |
op->constraint, |
op->vt->r, |
op->reg); |
} |
if (*pout_reg >= 0) |
printf("out_reg=%d\n", *pout_reg); |
#endif |
} |
static void subst_asm_operand(CString *add_str, |
SValue *sv, int modifier) |
{ |
int r, reg, size, val; |
char buf[64]; |
r = sv->r; |
if ((r & VT_VALMASK) == VT_CONST) { |
if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') |
cstr_ccat(add_str, '$'); |
if (r & VT_SYM) { |
cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); |
if (sv->c.i != 0) { |
cstr_ccat(add_str, '+'); |
} else { |
return; |
} |
} |
val = sv->c.i; |
if (modifier == 'n') |
val = -val; |
snprintf(buf, sizeof(buf), "%d", sv->c.i); |
cstr_cat(add_str, buf); |
} else if ((r & VT_VALMASK) == VT_LOCAL) { |
snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); |
cstr_cat(add_str, buf); |
} else if (r & VT_LVAL) { |
reg = r & VT_VALMASK; |
if (reg >= VT_CONST) |
error("internal compiler error"); |
snprintf(buf, sizeof(buf), "(%%%s)", |
get_tok_str(TOK_ASM_eax + reg, NULL)); |
cstr_cat(add_str, buf); |
} else { |
/* register case */ |
reg = r & VT_VALMASK; |
if (reg >= VT_CONST) |
error("internal compiler error"); |
/* choose register operand size */ |
if ((sv->type.t & VT_BTYPE) == VT_BYTE) |
size = 1; |
else if ((sv->type.t & VT_BTYPE) == VT_SHORT) |
size = 2; |
else |
size = 4; |
if (size == 1 && reg >= 4) |
size = 4; |
if (modifier == 'b') { |
if (reg >= 4) |
error("cannot use byte register"); |
size = 1; |
} else if (modifier == 'h') { |
if (reg >= 4) |
error("cannot use byte register"); |
size = -1; |
} else if (modifier == 'w') { |
size = 2; |
} |
switch(size) { |
case -1: |
reg = TOK_ASM_ah + reg; |
break; |
case 1: |
reg = TOK_ASM_al + reg; |
break; |
case 2: |
reg = TOK_ASM_ax + reg; |
break; |
default: |
reg = TOK_ASM_eax + reg; |
break; |
} |
snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); |
cstr_cat(add_str, buf); |
} |
} |
/* generate prolog and epilog code for asm statment */ |
static void asm_gen_code(ASMOperand *operands, int nb_operands, |
int nb_outputs, int is_output, |
uint8_t *clobber_regs, |
int out_reg) |
{ |
uint8_t regs_allocated[NB_ASM_REGS]; |
ASMOperand *op; |
int i, reg; |
static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; |
/* mark all used registers */ |
memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); |
for(i = 0; i < nb_operands;i++) { |
op = &operands[i]; |
if (op->reg >= 0) |
regs_allocated[op->reg] = 1; |
} |
if (!is_output) { |
/* generate reg save code */ |
for(i = 0; i < NB_SAVED_REGS; i++) { |
reg = reg_saved[i]; |
if (regs_allocated[reg]) |
g(0x50 + reg); |
} |
/* generate load code */ |
for(i = 0; i < nb_operands; i++) { |
op = &operands[i]; |
if (op->reg >= 0) { |
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && |
op->is_memory) { |
/* memory reference case (for both input and |
output cases) */ |
SValue sv; |
sv = *op->vt; |
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; |
load(op->reg, &sv); |
} else if (i >= nb_outputs || op->is_rw) { |
/* load value in register */ |
load(op->reg, op->vt); |
if (op->is_llong) { |
SValue sv; |
sv = *op->vt; |
sv.c.ul += 4; |
load(TREG_EDX, &sv); |
} |
} |
} |
} |
} else { |
/* generate save code */ |
for(i = 0 ; i < nb_outputs; i++) { |
op = &operands[i]; |
if (op->reg >= 0) { |
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { |
if (!op->is_memory) { |
SValue sv; |
sv = *op->vt; |
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; |
load(out_reg, &sv); |
sv.r = (sv.r & ~VT_VALMASK) | out_reg; |
store(op->reg, &sv); |
} |
} else { |
store(op->reg, op->vt); |
if (op->is_llong) { |
SValue sv; |
sv = *op->vt; |
sv.c.ul += 4; |
store(TREG_EDX, &sv); |
} |
} |
} |
} |
/* generate reg restore code */ |
for(i = NB_SAVED_REGS - 1; i >= 0; i--) { |
reg = reg_saved[i]; |
if (regs_allocated[reg]) |
g(0x58 + reg); |
} |
} |
} |
static void asm_clobber(uint8_t *clobber_regs, const char *str) |
{ |
int reg; |
TokenSym *ts; |
if (!strcmp(str, "memory") || |
!strcmp(str, "cc")) |
return; |
ts = tok_alloc(str, strlen(str)); |
reg = ts->tok; |
if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { |
reg -= TOK_ASM_eax; |
} else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { |
reg -= TOK_ASM_ax; |
} else { |
error("invalid clobber register '%s'", str); |
} |
clobber_regs[reg] = 1; |
} |
/programs/develop/ktcc/trunk/source/i386-asm.h |
---|
0,0 → 1,446 |
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */ |
DEF_ASM_OP0(popa, 0x61) |
DEF_ASM_OP0(clc, 0xf8) |
DEF_ASM_OP0(cld, 0xfc) |
DEF_ASM_OP0(cli, 0xfa) |
DEF_ASM_OP0(clts, 0x0f06) |
DEF_ASM_OP0(cmc, 0xf5) |
DEF_ASM_OP0(lahf, 0x9f) |
DEF_ASM_OP0(sahf, 0x9e) |
DEF_ASM_OP0(pushfl, 0x9c) |
DEF_ASM_OP0(popfl, 0x9d) |
DEF_ASM_OP0(pushf, 0x9c) |
DEF_ASM_OP0(popf, 0x9d) |
DEF_ASM_OP0(stc, 0xf9) |
DEF_ASM_OP0(std, 0xfd) |
DEF_ASM_OP0(sti, 0xfb) |
DEF_ASM_OP0(aaa, 0x37) |
DEF_ASM_OP0(aas, 0x3f) |
DEF_ASM_OP0(daa, 0x27) |
DEF_ASM_OP0(das, 0x2f) |
DEF_ASM_OP0(aad, 0xd50a) |
DEF_ASM_OP0(aam, 0xd40a) |
DEF_ASM_OP0(cbw, 0x6698) |
DEF_ASM_OP0(cwd, 0x6699) |
DEF_ASM_OP0(cwde, 0x98) |
DEF_ASM_OP0(cdq, 0x99) |
DEF_ASM_OP0(cbtw, 0x6698) |
DEF_ASM_OP0(cwtl, 0x98) |
DEF_ASM_OP0(cwtd, 0x6699) |
DEF_ASM_OP0(cltd, 0x99) |
DEF_ASM_OP0(int3, 0xcc) |
DEF_ASM_OP0(into, 0xce) |
DEF_ASM_OP0(iret, 0xcf) |
DEF_ASM_OP0(rsm, 0x0faa) |
DEF_ASM_OP0(hlt, 0xf4) |
DEF_ASM_OP0(wait, 0x9b) |
DEF_ASM_OP0(nop, 0x90) |
DEF_ASM_OP0(xlat, 0xd7) |
/* strings */ |
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL)) |
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL)) |
/* bits */ |
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW)) |
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW)) |
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) |
/* prefixes */ |
DEF_ASM_OP0(aword, 0x67) |
DEF_ASM_OP0(addr16, 0x67) |
DEF_ASM_OP0(word, 0x66) |
DEF_ASM_OP0(data16, 0x66) |
DEF_ASM_OP0(lock, 0xf0) |
DEF_ASM_OP0(rep, 0xf3) |
DEF_ASM_OP0(repe, 0xf3) |
DEF_ASM_OP0(repz, 0xf3) |
DEF_ASM_OP0(repne, 0xf2) |
DEF_ASM_OP0(repnz, 0xf2) |
DEF_ASM_OP0(invd, 0x0f08) |
DEF_ASM_OP0(wbinvd, 0x0f09) |
DEF_ASM_OP0(cpuid, 0x0fa2) |
DEF_ASM_OP0(wrmsr, 0x0f30) |
DEF_ASM_OP0(rdtsc, 0x0f31) |
DEF_ASM_OP0(rdmsr, 0x0f32) |
DEF_ASM_OP0(rdpmc, 0x0f33) |
DEF_ASM_OP0(ud2, 0x0f0b) |
/* NOTE: we took the same order as gas opcode definition order */ |
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX)) |
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR)) |
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) |
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG)) |
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG)) |
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32)) |
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32)) |
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32)) |
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR)) |
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB)) |
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR)) |
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16)) |
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW)) |
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW)) |
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S)) |
ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32)) |
ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG)) |
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW)) |
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA)) |
ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG)) |
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX)) |
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG)) |
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) |
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX)) |
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8)) |
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX)) |
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX)) |
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8)) |
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8)) |
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX)) |
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX)) |
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG)) |
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) |
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) |
/* arith */ |
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */ |
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) |
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX)) |
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) |
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX)) |
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW)) |
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW)) |
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG)) |
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW)) |
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW)) |
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW)) |
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW)) |
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX)) |
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) |
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX)) |
/* shifts */ |
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG)) |
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) |
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) |
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW)) |
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) |
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) |
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW)) |
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR)) |
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR)) |
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR)) |
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR)) |
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32)) |
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA)) |
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32)) |
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA)) |
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8)) |
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA)) |
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8) |
DEF_ASM_OP0(leave, 0xc9) |
DEF_ASM_OP0(ret, 0xc3) |
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16)) |
DEF_ASM_OP0(lret, 0xcb) |
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16)) |
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR)) |
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) |
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) |
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) |
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) |
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR) |
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR) |
/* float */ |
/* specific fcomp handling */ |
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0)) |
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST)) |
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) |
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH)) |
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST)) |
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) |
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) |
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH)) |
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) |
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) |
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) |
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) |
DEF_ASM_OP0(fucompp, 0xdae9) |
DEF_ASM_OP0(ftst, 0xd9e4) |
DEF_ASM_OP0(fxam, 0xd9e5) |
DEF_ASM_OP0(fld1, 0xd9e8) |
DEF_ASM_OP0(fldl2t, 0xd9e9) |
DEF_ASM_OP0(fldl2e, 0xd9ea) |
DEF_ASM_OP0(fldpi, 0xd9eb) |
DEF_ASM_OP0(fldlg2, 0xd9ec) |
DEF_ASM_OP0(fldln2, 0xd9ed) |
DEF_ASM_OP0(fldz, 0xd9ee) |
DEF_ASM_OP0(f2xm1, 0xd9f0) |
DEF_ASM_OP0(fyl2x, 0xd9f1) |
DEF_ASM_OP0(fptan, 0xd9f2) |
DEF_ASM_OP0(fpatan, 0xd9f3) |
DEF_ASM_OP0(fxtract, 0xd9f4) |
DEF_ASM_OP0(fprem1, 0xd9f5) |
DEF_ASM_OP0(fdecstp, 0xd9f6) |
DEF_ASM_OP0(fincstp, 0xd9f7) |
DEF_ASM_OP0(fprem, 0xd9f8) |
DEF_ASM_OP0(fyl2xp1, 0xd9f9) |
DEF_ASM_OP0(fsqrt, 0xd9fa) |
DEF_ASM_OP0(fsincos, 0xd9fb) |
DEF_ASM_OP0(frndint, 0xd9fc) |
DEF_ASM_OP0(fscale, 0xd9fd) |
DEF_ASM_OP0(fsin, 0xd9fe) |
DEF_ASM_OP0(fcos, 0xd9ff) |
DEF_ASM_OP0(fchs, 0xd9e0) |
DEF_ASM_OP0(fabs, 0xd9e1) |
DEF_ASM_OP0(fninit, 0xdbe3) |
DEF_ASM_OP0(fnclex, 0xdbe2) |
DEF_ASM_OP0(fnop, 0xd9d0) |
DEF_ASM_OP0(fwait, 0x9b) |
/* fp load */ |
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST) |
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST) |
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA) |
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA)) |
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA) |
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA) |
/* fp store */ |
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST) |
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST) |
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA) |
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA)) |
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST) |
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA) |
/* exchange */ |
DEF_ASM_OP0(fxch, 0xd9c9) |
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST)) |
/* misc FPU */ |
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST ) |
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST ) |
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT) |
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ) |
DEF_ASM_OP0(fnstsw, 0xdfe0) |
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX )) |
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA )) |
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX ) |
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT)) |
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )) |
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT) |
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) |
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) |
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST ) |
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST ) |
DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA ) |
/* segments */ |
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA) |
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32) |
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG) |
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG) |
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG)) |
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG) |
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA) |
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA) |
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA) |
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA) |
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA) |
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA) |
/* 486 */ |
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 ) |
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) |
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) |
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA ) |
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA) |
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA) |
/* pentium */ |
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA ) |
/* pentium pro */ |
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) |
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 ) |
/* mmx */ |
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */ |
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX ) |
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 )) |
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX )) |
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) |
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) |
#undef ALT |
#undef DEF_ASM_OP0 |
#undef DEF_ASM_OP0L |
#undef DEF_ASM_OP1 |
#undef DEF_ASM_OP2 |
#undef DEF_ASM_OP3 |
/programs/develop/ktcc/trunk/source/i386-gen.c |
---|
0,0 → 1,1017 |
/* |
* X86 code generator for TCC |
* |
* Copyright (c) 2001-2004 Fabrice Bellard |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation; either |
* version 2 of the License, or (at your option) any later version. |
* |
* This library is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
* Lesser General Public License for more details. |
* |
* You should have received a copy of the GNU Lesser General Public |
* License along with this library; if not, write to the Free Software |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
/* number of available registers */ |
#define NB_REGS 4 |
/* a register can belong to several classes. The classes must be |
sorted from more general to more precise (see gv2() code which does |
assumptions on it). */ |
#define RC_INT 0x0001 /* generic integer register */ |
#define RC_FLOAT 0x0002 /* generic float register */ |
#define RC_EAX 0x0004 |
#define RC_ST0 0x0008 |
#define RC_ECX 0x0010 |
#define RC_EDX 0x0020 |
#define RC_IRET RC_EAX /* function return: integer register */ |
#define RC_LRET RC_EDX /* function return: second integer register */ |
#define RC_FRET RC_ST0 /* function return: float register */ |
/* pretty names for the registers */ |
enum { |
TREG_EAX = 0, |
TREG_ECX, |
TREG_EDX, |
TREG_ST0, |
}; |
int reg_classes[NB_REGS] = { |
/* eax */ RC_INT | RC_EAX, |
/* ecx */ RC_INT | RC_ECX, |
/* edx */ RC_INT | RC_EDX, |
/* st0 */ RC_FLOAT | RC_ST0, |
}; |
/* return registers for function */ |
#define REG_IRET TREG_EAX /* single word int return register */ |
#define REG_LRET TREG_EDX /* second word return register (for long long) */ |
#define REG_FRET TREG_ST0 /* float return register */ |
/* defined if function parameters must be evaluated in reverse order */ |
#define INVERT_FUNC_PARAMS |
/* defined if structures are passed as pointers. Otherwise structures |
are directly pushed on stack. */ |
//#define FUNC_STRUCT_PARAM_AS_PTR |
/* pointer size, in bytes */ |
#define PTR_SIZE 4 |
/* long double size and alignment, in bytes */ |
#define LDOUBLE_SIZE 12 |
#define LDOUBLE_ALIGN 4 |
/* maximum alignment (for aligned attribute support) */ |
#define MAX_ALIGN 8 |
/******************************************************/ |
/* ELF defines */ |
#define EM_TCC_TARGET EM_386 |
/* relocation type for 32 bit data relocation */ |
#define R_DATA_32 R_386_32 |
#define R_JMP_SLOT R_386_JMP_SLOT |
#define R_COPY R_386_COPY |
#define ELF_START_ADDR 0x08048000 |
#define ELF_PAGE_SIZE 0x1000 |
/******************************************************/ |
static unsigned long func_sub_sp_offset; |
static unsigned long func_bound_offset; |
static int func_ret_sub; |
/* XXX: make it faster ? */ |
void g(int c) |
{ |
int ind1; |
ind1 = ind + 1; |
if (ind1 > cur_text_section->data_allocated) |
section_realloc(cur_text_section, ind1); |
cur_text_section->data[ind] = c; |
ind = ind1; |
} |
void o(unsigned int c) |
{ |
while (c) { |
g(c); |
c = c >> 8; |
} |
} |
void gen_le32(int c) |
{ |
g(c); |
g(c >> 8); |
g(c >> 16); |
g(c >> 24); |
} |
/* output a symbol and patch all calls to it */ |
void gsym_addr(int t, int a) |
{ |
int n, *ptr; |
while (t) { |
ptr = (int *)(cur_text_section->data + t); |
n = *ptr; /* next value */ |
*ptr = a - t - 4; |
t = n; |
} |
} |
void gsym(int t) |
{ |
gsym_addr(t, ind); |
} |
/* psym is used to put an instruction with a data field which is a |
reference to a symbol. It is in fact the same as oad ! */ |
#define psym oad |
/* instruction + 4 bytes data. Return the address of the data */ |
static int oad(int c, int s) |
{ |
int ind1; |
o(c); |
ind1 = ind + 4; |
if (ind1 > cur_text_section->data_allocated) |
section_realloc(cur_text_section, ind1); |
*(int *)(cur_text_section->data + ind) = s; |
s = ind; |
ind = ind1; |
return s; |
} |
/* output constant with relocation if 'r & VT_SYM' is true */ |
static void gen_addr32(int r, Sym *sym, int c) |
{ |
if (r & VT_SYM) |
greloc(cur_text_section, sym, ind, R_386_32); |
gen_le32(c); |
} |
/* generate a modrm reference. 'op_reg' contains the addtionnal 3 |
opcode bits */ |
static void gen_modrm(int op_reg, int r, Sym *sym, int c) |
{ |
op_reg = op_reg << 3; |
if ((r & VT_VALMASK) == VT_CONST) { |
/* constant memory reference */ |
o(0x05 | op_reg); |
gen_addr32(r, sym, c); |
} else if ((r & VT_VALMASK) == VT_LOCAL) { |
/* currently, we use only ebp as base */ |
if (c == (char)c) { |
/* short reference */ |
o(0x45 | op_reg); |
g(c); |
} else { |
oad(0x85 | op_reg, c); |
} |
} else { |
g(0x00 | op_reg | (r & VT_VALMASK)); |
} |
} |
/* load 'r' from value 'sv' */ |
void load(int r, SValue *sv) |
{ |
int v, t, ft, fc, fr; |
SValue v1; |
fr = sv->r; |
ft = sv->type.t; |
fc = sv->c.ul; |
v = fr & VT_VALMASK; |
if (fr & VT_LVAL) { |
if (v == VT_LLOCAL) { |
v1.type.t = VT_INT; |
v1.r = VT_LOCAL | VT_LVAL; |
v1.c.ul = fc; |
load(r, &v1); |
fr = r; |
} |
if ((ft & VT_BTYPE) == VT_FLOAT) { |
o(0xd9); /* flds */ |
r = 0; |
} else if ((ft & VT_BTYPE) == VT_DOUBLE) { |
o(0xdd); /* fldl */ |
r = 0; |
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) { |
o(0xdb); /* fldt */ |
r = 5; |
} else if ((ft & VT_TYPE) == VT_BYTE) { |
o(0xbe0f); /* movsbl */ |
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { |
o(0xb60f); /* movzbl */ |
} else if ((ft & VT_TYPE) == VT_SHORT) { |
o(0xbf0f); /* movswl */ |
} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { |
o(0xb70f); /* movzwl */ |
} else { |
o(0x8b); /* movl */ |
} |
gen_modrm(r, fr, sv->sym, fc); |
} else { |
if (v == VT_CONST) { |
o(0xb8 + r); /* mov $xx, r */ |
gen_addr32(fr, sv->sym, fc); |
} else if (v == VT_LOCAL) { |
o(0x8d); /* lea xxx(%ebp), r */ |
gen_modrm(r, VT_LOCAL, sv->sym, fc); |
} else if (v == VT_CMP) { |
oad(0xb8 + r, 0); /* mov $0, r */ |
o(0x0f); /* setxx %br */ |
o(fc); |
o(0xc0 + r); |
} else if (v == VT_JMP || v == VT_JMPI) { |
t = v & 1; |
oad(0xb8 + r, t); /* mov $1, r */ |
o(0x05eb); /* jmp after */ |
gsym(fc); |
oad(0xb8 + r, t ^ 1); /* mov $0, r */ |
} else if (v != r) { |
o(0x89); |
o(0xc0 + r + v * 8); /* mov v, r */ |
} |
} |
} |
/* store register 'r' in lvalue 'v' */ |
void store(int r, SValue *v) |
{ |
int fr, bt, ft, fc; |
ft = v->type.t; |
fc = v->c.ul; |
fr = v->r & VT_VALMASK; |
bt = ft & VT_BTYPE; |
/* XXX: incorrect if float reg to reg */ |
if (bt == VT_FLOAT) { |
o(0xd9); /* fsts */ |
r = 2; |
} else if (bt == VT_DOUBLE) { |
o(0xdd); /* fstpl */ |
r = 2; |
} else if (bt == VT_LDOUBLE) { |
o(0xc0d9); /* fld %st(0) */ |
o(0xdb); /* fstpt */ |
r = 7; |
} else { |
if (bt == VT_SHORT) |
o(0x66); |
if (bt == VT_BYTE || bt == VT_BOOL) |
o(0x88); |
else |
o(0x89); |
} |
if (fr == VT_CONST || |
fr == VT_LOCAL || |
(v->r & VT_LVAL)) { |
gen_modrm(r, v->r, v->sym, fc); |
} else if (fr != r) { |
o(0xc0 + fr + r * 8); /* mov r, fr */ |
} |
} |
static void gadd_sp(int val) |
{ |
if (val == (char)val) { |
o(0xc483); |
g(val); |
} else { |
oad(0xc481, val); /* add $xxx, %esp */ |
} |
} |
/* 'is_jmp' is '1' if it is a jump */ |
static void gcall_or_jmp(int is_jmp) |
{ |
int r; |
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
/* constant case */ |
if (vtop->r & VT_SYM) { |
/* relocation case */ |
greloc(cur_text_section, vtop->sym, |
ind + 1, R_386_PC32); |
} else { |
/* put an empty PC32 relocation */ |
put_elf_reloc(symtab_section, cur_text_section, |
ind + 1, R_386_PC32, 0); |
} |
oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ |
} else { |
/* otherwise, indirect call */ |
r = gv(RC_INT); |
o(0xff); /* call/jmp *r */ |
o(0xd0 + r + (is_jmp << 4)); |
} |
} |
static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; |
/* Generate function call. The function address is pushed first, then |
all the parameters in call order. This functions pops all the |
parameters and the function address. */ |
void gfunc_call(int nb_args) |
{ |
int size, align, r, args_size, i, func_call; |
Sym *func_sym; |
args_size = 0; |
for(i = 0;i < nb_args; i++) { |
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { |
size = type_size(&vtop->type, &align); |
/* align to stack align size */ |
size = (size + 3) & ~3; |
/* allocate the necessary size on stack */ |
oad(0xec81, size); /* sub $xxx, %esp */ |
/* generate structure store */ |
r = get_reg(RC_INT); |
o(0x89); /* mov %esp, r */ |
o(0xe0 + r); |
vset(&vtop->type, r | VT_LVAL, 0); |
vswap(); |
vstore(); |
args_size += size; |
} else if (is_float(vtop->type.t)) { |
gv(RC_FLOAT); /* only one float register */ |
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) |
size = 4; |
else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) |
size = 8; |
else |
size = 12; |
oad(0xec81, size); /* sub $xxx, %esp */ |
if (size == 12) |
o(0x7cdb); |
else |
o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ |
g(0x24); |
g(0x00); |
args_size += size; |
} else { |
/* simple type (currently always same size) */ |
/* XXX: implicit cast ? */ |
r = gv(RC_INT); |
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
size = 8; |
o(0x50 + vtop->r2); /* push r */ |
} else { |
size = 4; |
} |
o(0x50 + r); /* push r */ |
args_size += size; |
} |
vtop--; |
} |
save_regs(0); /* save used temporary registers */ |
func_sym = vtop->type.ref; |
func_call = func_sym->r; |
/* fast call case */ |
if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { |
int fastcall_nb_regs; |
fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; |
for(i = 0;i < fastcall_nb_regs; i++) { |
if (args_size <= 0) |
break; |
o(0x58 + fastcall_regs[i]); /* pop r */ |
/* XXX: incorrect for struct/floats */ |
args_size -= 4; |
} |
} |
gcall_or_jmp(0); |
if (args_size && func_sym->r != FUNC_STDCALL) |
gadd_sp(args_size); |
vtop--; |
} |
#ifdef TCC_TARGET_PE |
#define FUNC_PROLOG_SIZE 10 |
#else |
#define FUNC_PROLOG_SIZE 9 |
#endif |
/* generate function prolog of type 't' */ |
void gfunc_prolog(CType *func_type) |
{ |
int addr, align, size, func_call, fastcall_nb_regs; |
int param_index, param_addr; |
Sym *sym; |
CType *type; |
sym = func_type->ref; |
func_call = sym->r; |
addr = 8; |
loc = 0; |
if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { |
fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; |
} else { |
fastcall_nb_regs = 0; |
} |
param_index = 0; |
ind += FUNC_PROLOG_SIZE; |
func_sub_sp_offset = ind; |
/* if the function returns a structure, then add an |
implicit pointer parameter */ |
func_vt = sym->type; |
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
/* XXX: fastcall case ? */ |
func_vc = addr; |
addr += 4; |
param_index++; |
} |
/* define parameters */ |
while ((sym = sym->next) != NULL) { |
type = &sym->type; |
size = type_size(type, &align); |
size = (size + 3) & ~3; |
#ifdef FUNC_STRUCT_PARAM_AS_PTR |
/* structs are passed as pointer */ |
if ((type->t & VT_BTYPE) == VT_STRUCT) { |
size = 4; |
} |
#endif |
if (param_index < fastcall_nb_regs) { |
/* save FASTCALL register */ |
loc -= 4; |
o(0x89); /* movl */ |
gen_modrm(fastcall_regs[param_index], VT_LOCAL, NULL, loc); |
param_addr = loc; |
} else { |
param_addr = addr; |
addr += size; |
} |
sym_push(sym->v & ~SYM_FIELD, type, |
VT_LOCAL | VT_LVAL, param_addr); |
param_index++; |
} |
func_ret_sub = 0; |
/* pascal type call ? */ |
if (func_call == FUNC_STDCALL) |
func_ret_sub = addr - 8; |
/* leave some room for bound checking code */ |
if (do_bounds_check) { |
oad(0xb8, 0); /* lbound section pointer */ |
oad(0xb8, 0); /* call to function */ |
func_bound_offset = lbounds_section->data_offset; |
} |
} |
/* generate function epilog */ |
void gfunc_epilog(void) |
{ |
int v, saved_ind; |
#ifdef CONFIG_TCC_BCHECK |
if (do_bounds_check && func_bound_offset != lbounds_section->data_offset) { |
int saved_ind; |
int *bounds_ptr; |
Sym *sym, *sym_data; |
/* add end of table info */ |
bounds_ptr = section_ptr_add(lbounds_section, sizeof(int)); |
*bounds_ptr = 0; |
/* generate bound local allocation */ |
saved_ind = ind; |
ind = func_sub_sp_offset; |
sym_data = get_sym_ref(&char_pointer_type, lbounds_section, |
func_bound_offset, lbounds_section->data_offset); |
greloc(cur_text_section, sym_data, |
ind + 1, R_386_32); |
oad(0xb8, 0); /* mov %eax, xxx */ |
sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0); |
greloc(cur_text_section, sym, |
ind + 1, R_386_PC32); |
oad(0xe8, -4); |
ind = saved_ind; |
/* generate bound check local freeing */ |
o(0x5250); /* save returned value, if any */ |
greloc(cur_text_section, sym_data, |
ind + 1, R_386_32); |
oad(0xb8, 0); /* mov %eax, xxx */ |
sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0); |
greloc(cur_text_section, sym, |
ind + 1, R_386_PC32); |
oad(0xe8, -4); |
o(0x585a); /* restore returned value, if any */ |
} |
#endif |
o(0xc9); /* leave */ |
if (func_ret_sub == 0) { |
o(0xc3); /* ret */ |
} else { |
o(0xc2); /* ret n */ |
g(func_ret_sub); |
g(func_ret_sub >> 8); |
} |
/* align local size to word & save local variables */ |
v = (-loc + 3) & -4; |
saved_ind = ind; |
ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; |
#ifdef TCC_TARGET_PE |
if (v >= 4096) { |
Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); |
oad(0xb8, v); /* mov stacksize, %eax */ |
oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */ |
greloc(cur_text_section, sym, ind-4, R_386_PC32); |
} else |
#endif |
{ |
o(0xe58955); /* push %ebp, mov %esp, %ebp */ |
o(0xec81); /* sub esp, stacksize */ |
gen_le32(v); |
#if FUNC_PROLOG_SIZE == 10 |
o(0x90); /* adjust to FUNC_PROLOG_SIZE */ |
#endif |
} |
ind = saved_ind; |
} |
/* generate a jump to a label */ |
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 */ |
g(0x0f); |
t = psym((vtop->c.i - 16) ^ inv, t); |
} else if (v == VT_JMP || v == VT_JMPI) { |
/* && or || optimization */ |
if ((v & 1) == inv) { |
/* insert vtop->c jump list in t */ |
p = &vtop->c.i; |
while (*p != 0) |
p = (int *)(cur_text_section->data + *p); |
*p = t; |
t = vtop->c.i; |
} else { |
t = gjmp(t); |
gsym(vtop->c.i); |
} |
} else { |
if (is_float(vtop->type.t)) { |
vpushi(0); |
gen_op(TOK_NE); |
} |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
/* constant jmp optimization */ |
if ((vtop->c.i != 0) != inv) |
t = gjmp(t); |
} else { |
v = gv(RC_INT); |
o(0x85); |
o(0xc0 + v * 9); |
g(0x0f); |
t = psym(0x85 ^ inv, t); |
} |
} |
vtop--; |
return t; |
} |
/* generate an integer binary operation */ |
void gen_opi(int op) |
{ |
int r, fr, opc, c; |
switch(op) { |
case '+': |
case TOK_ADDC1: /* add with carry generation */ |
opc = 0; |
gen_op8: |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
/* constant case */ |
vswap(); |
r = gv(RC_INT); |
vswap(); |
c = vtop->c.i; |
if (c == (char)c) { |
/* XXX: generate inc and dec for smaller code ? */ |
o(0x83); |
o(0xc0 | (opc << 3) | r); |
g(c); |
} else { |
o(0x81); |
oad(0xc0 | (opc << 3) | r, c); |
} |
} else { |
gv2(RC_INT, RC_INT); |
r = vtop[-1].r; |
fr = vtop[0].r; |
o((opc << 3) | 0x01); |
o(0xc0 + r + fr * 8); |
} |
vtop--; |
if (op >= TOK_ULT && op <= TOK_GT) { |
vtop->r = VT_CMP; |
vtop->c.i = op; |
} |
break; |
case '-': |
case TOK_SUBC1: /* sub with carry generation */ |
opc = 5; |
goto gen_op8; |
case TOK_ADDC2: /* add with carry use */ |
opc = 2; |
goto gen_op8; |
case TOK_SUBC2: /* sub with carry use */ |
opc = 3; |
goto gen_op8; |
case '&': |
opc = 4; |
goto gen_op8; |
case '^': |
opc = 6; |
goto gen_op8; |
case '|': |
opc = 1; |
goto gen_op8; |
case '*': |
gv2(RC_INT, RC_INT); |
r = vtop[-1].r; |
fr = vtop[0].r; |
vtop--; |
o(0xaf0f); /* imul fr, r */ |
o(0xc0 + fr + r * 8); |
break; |
case TOK_SHL: |
opc = 4; |
goto gen_shift; |
case TOK_SHR: |
opc = 5; |
goto gen_shift; |
case TOK_SAR: |
opc = 7; |
gen_shift: |
opc = 0xc0 | (opc << 3); |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
/* constant case */ |
vswap(); |
r = gv(RC_INT); |
vswap(); |
c = vtop->c.i & 0x1f; |
o(0xc1); /* shl/shr/sar $xxx, r */ |
o(opc | r); |
g(c); |
} else { |
/* we generate the shift in ecx */ |
gv2(RC_INT, RC_ECX); |
r = vtop[-1].r; |
o(0xd3); /* shl/shr/sar %cl, r */ |
o(opc | r); |
} |
vtop--; |
break; |
case '/': |
case TOK_UDIV: |
case TOK_PDIV: |
case '%': |
case TOK_UMOD: |
case TOK_UMULL: |
/* first operand must be in eax */ |
/* XXX: need better constraint for second operand */ |
gv2(RC_EAX, RC_ECX); |
r = vtop[-1].r; |
fr = vtop[0].r; |
vtop--; |
save_reg(TREG_EDX); |
if (op == TOK_UMULL) { |
o(0xf7); /* mul fr */ |
o(0xe0 + fr); |
vtop->r2 = TREG_EDX; |
r = TREG_EAX; |
} else { |
if (op == TOK_UDIV || op == TOK_UMOD) { |
o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ |
o(0xf0 + fr); |
} else { |
o(0xf799); /* cltd, idiv fr, %eax */ |
o(0xf8 + fr); |
} |
if (op == '%' || op == TOK_UMOD) |
r = TREG_EDX; |
else |
r = TREG_EAX; |
} |
vtop->r = r; |
break; |
default: |
opc = 7; |
goto gen_op8; |
} |
} |
/* generate a floating point operation 'v = t1 op t2' instruction. The |
two operands are guaranted to have the same floating point type */ |
/* XXX: need to use ST1 too */ |
void gen_opf(int op) |
{ |
int a, ft, fc, swapped, r; |
/* convert constants to memory references */ |
if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
vswap(); |
gv(RC_FLOAT); |
vswap(); |
} |
if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) |
gv(RC_FLOAT); |
/* must put at least one value in the floating point register */ |
if ((vtop[-1].r & VT_LVAL) && |
(vtop[0].r & VT_LVAL)) { |
vswap(); |
gv(RC_FLOAT); |
vswap(); |
} |
swapped = 0; |
/* swap the stack if needed so that t1 is the register and t2 is |
the memory reference */ |
if (vtop[-1].r & VT_LVAL) { |
vswap(); |
swapped = 1; |
} |
if (op >= TOK_ULT && op <= TOK_GT) { |
/* load on stack second operand */ |
load(TREG_ST0, vtop); |
save_reg(TREG_EAX); /* eax is used by FP comparison code */ |
if (op == TOK_GE || op == TOK_GT) |
swapped = !swapped; |
else if (op == TOK_EQ || op == TOK_NE) |
swapped = 0; |
if (swapped) |
o(0xc9d9); /* fxch %st(1) */ |
o(0xe9da); /* fucompp */ |
o(0xe0df); /* fnstsw %ax */ |
if (op == TOK_EQ) { |
o(0x45e480); /* and $0x45, %ah */ |
o(0x40fC80); /* cmp $0x40, %ah */ |
} else if (op == TOK_NE) { |
o(0x45e480); /* and $0x45, %ah */ |
o(0x40f480); /* xor $0x40, %ah */ |
op = TOK_NE; |
} else if (op == TOK_GE || op == TOK_LE) { |
o(0x05c4f6); /* test $0x05, %ah */ |
op = TOK_EQ; |
} else { |
o(0x45c4f6); /* test $0x45, %ah */ |
op = TOK_EQ; |
} |
vtop--; |
vtop->r = VT_CMP; |
vtop->c.i = op; |
} else { |
/* no memory reference possible for long double operations */ |
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { |
load(TREG_ST0, vtop); |
swapped = !swapped; |
} |
switch(op) { |
default: |
case '+': |
a = 0; |
break; |
case '-': |
a = 4; |
if (swapped) |
a++; |
break; |
case '*': |
a = 1; |
break; |
case '/': |
a = 6; |
if (swapped) |
a++; |
break; |
} |
ft = vtop->type.t; |
fc = vtop->c.ul; |
if ((ft & VT_BTYPE) == VT_LDOUBLE) { |
o(0xde); /* fxxxp %st, %st(1) */ |
o(0xc1 + (a << 3)); |
} else { |
/* if saved lvalue, then we must reload it */ |
r = vtop->r; |
if ((r & VT_VALMASK) == VT_LLOCAL) { |
SValue v1; |
r = get_reg(RC_INT); |
v1.type.t = VT_INT; |
v1.r = VT_LOCAL | VT_LVAL; |
v1.c.ul = fc; |
load(r, &v1); |
fc = 0; |
} |
if ((ft & VT_BTYPE) == VT_DOUBLE) |
o(0xdc); |
else |
o(0xd8); |
gen_modrm(a, r, vtop->sym, fc); |
} |
vtop--; |
} |
} |
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' |
and 'long long' cases. */ |
void gen_cvt_itof(int t) |
{ |
save_reg(TREG_ST0); |
gv(RC_INT); |
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
/* signed long long to float/double/long double (unsigned case |
is handled generically) */ |
o(0x50 + vtop->r2); /* push r2 */ |
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ |
o(0x242cdf); /* fildll (%esp) */ |
o(0x08c483); /* add $8, %esp */ |
} else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == |
(VT_INT | VT_UNSIGNED)) { |
/* unsigned int to float/double/long double */ |
o(0x6a); /* push $0 */ |
g(0x00); |
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ |
o(0x242cdf); /* fildll (%esp) */ |
o(0x08c483); /* add $8, %esp */ |
} else { |
/* int to float/double/long double */ |
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ |
o(0x2404db); /* fildl (%esp) */ |
o(0x04c483); /* add $4, %esp */ |
} |
vtop->r = TREG_ST0; |
} |
/* convert fp to int 't' type */ |
/* XXX: handle long long case */ |
void gen_cvt_ftoi(int t) |
{ |
int r, r2, size; |
Sym *sym; |
CType ushort_type; |
ushort_type.t = VT_SHORT | VT_UNSIGNED; |
gv(RC_FLOAT); |
if (t != VT_INT) |
size = 8; |
else |
size = 4; |
o(0x2dd9); /* ldcw xxx */ |
sym = external_global_sym(TOK___tcc_int_fpu_control, |
&ushort_type, VT_LVAL); |
greloc(cur_text_section, sym, |
ind, R_386_32); |
gen_le32(0); |
oad(0xec81, size); /* sub $xxx, %esp */ |
if (size == 4) |
o(0x1cdb); /* fistpl */ |
else |
o(0x3cdf); /* fistpll */ |
o(0x24); |
o(0x2dd9); /* ldcw xxx */ |
sym = external_global_sym(TOK___tcc_fpu_control, |
&ushort_type, VT_LVAL); |
greloc(cur_text_section, sym, |
ind, R_386_32); |
gen_le32(0); |
r = get_reg(RC_INT); |
o(0x58 + r); /* pop r */ |
if (size == 8) { |
if (t == VT_LLONG) { |
vtop->r = r; /* mark reg as used */ |
r2 = get_reg(RC_INT); |
o(0x58 + r2); /* pop r2 */ |
vtop->r2 = r2; |
} else { |
o(0x04c483); /* add $4, %esp */ |
} |
} |
vtop->r = r; |
} |
/* convert from one floating point type to another */ |
void gen_cvt_ftof(int t) |
{ |
/* all we have to do on i386 is to put the float in a register */ |
gv(RC_FLOAT); |
} |
/* computed goto support */ |
void ggoto(void) |
{ |
gcall_or_jmp(1); |
vtop--; |
} |
/* bound check support functions */ |
#ifdef CONFIG_TCC_BCHECK |
/* generate a bounded pointer addition */ |
void gen_bounded_ptr_add(void) |
{ |
Sym *sym; |
/* prepare fast i386 function call (args in eax and edx) */ |
gv2(RC_EAX, RC_EDX); |
/* save all temporary registers */ |
vtop -= 2; |
save_regs(0); |
/* do a fast function call */ |
sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0); |
greloc(cur_text_section, sym, |
ind + 1, R_386_PC32); |
oad(0xe8, -4); |
/* returned pointer is in eax */ |
vtop++; |
vtop->r = TREG_EAX | VT_BOUNDED; |
/* address of bounding function call point */ |
vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); |
} |
/* patch pointer addition in vtop so that pointer dereferencing is |
also tested */ |
void gen_bounded_ptr_deref(void) |
{ |
int func; |
int size, align; |
Elf32_Rel *rel; |
Sym *sym; |
size = 0; |
/* XXX: put that code in generic part of tcc */ |
if (!is_float(vtop->type.t)) { |
if (vtop->r & VT_LVAL_BYTE) |
size = 1; |
else if (vtop->r & VT_LVAL_SHORT) |
size = 2; |
} |
if (!size) |
size = type_size(&vtop->type, &align); |
switch(size) { |
case 1: func = TOK___bound_ptr_indir1; break; |
case 2: func = TOK___bound_ptr_indir2; break; |
case 4: func = TOK___bound_ptr_indir4; break; |
case 8: func = TOK___bound_ptr_indir8; break; |
case 12: func = TOK___bound_ptr_indir12; break; |
case 16: func = TOK___bound_ptr_indir16; break; |
default: |
error("unhandled size when derefencing bounded pointer"); |
func = 0; |
break; |
} |
/* patch relocation */ |
/* XXX: find a better solution ? */ |
rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul); |
sym = external_global_sym(func, &func_old_type, 0); |
if (!sym->c) |
put_extern_sym(sym, NULL, 0, 0); |
rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); |
} |
#endif |
/* end of X86 code generator */ |
/*************************************************************/ |
/programs/develop/ktcc/trunk/source/il-gen.c |
---|
0,0 → 1,667 |
/* |
* CIL code generator for TCC |
* |
* 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. |
*/ |
/* 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, |
}; |
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: |
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) { |
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; |
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 | VT_LVAL, 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); |
} |
} else { |
if (is_float(vtop->t)) { |
vpushi(0); |
gen_op(TOK_NE); |
} |
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { |
/* constant jmp optimization */ |
if ((vtop->c.i != 0) != inv) |
t = gjmp(t); |
} else { |
v = gv(RC_INT); |
t = out_opj(IL_OP_BRTRUE - inv, t); |
} |
} |
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 */ |
/*************************************************************/ |
/programs/develop/ktcc/trunk/source/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) |
/programs/develop/ktcc/trunk/source/libtcc.h |
---|
0,0 → 1,97 |
#ifndef LIBTCC_H |
#define LIBTCC_H |
#ifdef __cplusplus |
extern "C" { |
#endif |
struct TCCState; |
typedef struct TCCState TCCState; |
/* create a new TCC compilation context */ |
TCCState *tcc_new(void); |
/* free a TCC compilation context */ |
void tcc_delete(TCCState *s); |
/* add debug information in the generated code */ |
void tcc_enable_debug(TCCState *s); |
/* set error/warning display callback */ |
void tcc_set_error_func(TCCState *s, void *error_opaque, |
void (*error_func)(void *opaque, const char *msg)); |
/* set/reset a warning */ |
int tcc_set_warning(TCCState *s, const char *warning_name, int value); |
/*****************************/ |
/* preprocessor */ |
/* add include path */ |
int tcc_add_include_path(TCCState *s, const char *pathname); |
/* add in system include path */ |
int tcc_add_sysinclude_path(TCCState *s, const char *pathname); |
/* define preprocessor symbol 'sym'. Can put optional value */ |
void tcc_define_symbol(TCCState *s, const char *sym, const char *value); |
/* undefine preprocess symbol 'sym' */ |
void tcc_undefine_symbol(TCCState *s, const char *sym); |
/*****************************/ |
/* compiling */ |
/* add a file (either a C file, dll, an object, a library or an ld |
script). Return -1 if error. */ |
int tcc_add_file(TCCState *s, const char *filename); |
/* compile a string containing a C source. Return non zero if |
error. */ |
int tcc_compile_string(TCCState *s, const char *buf); |
/*****************************/ |
/* linking commands */ |
/* set output type. MUST BE CALLED before any compilation */ |
#define TCC_OUTPUT_MEMORY 0 /* output will be ran in memory (no |
output file) (default) */ |
#define TCC_OUTPUT_EXE 1 /* executable file */ |
#define TCC_OUTPUT_DLL 2 /* dynamic library */ |
#define TCC_OUTPUT_OBJ 3 /* object file */ |
int tcc_set_output_type(TCCState *s, int output_type); |
#define TCC_OUTPUT_FORMAT_ELF 0 /* default output format: ELF */ |
#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */ |
#define TCC_OUTPUT_FORMAT_COFF 2 /* COFF */ |
/* equivalent to -Lpath option */ |
int tcc_add_library_path(TCCState *s, const char *pathname); |
/* the library name is the same as the argument of the '-l' option */ |
int tcc_add_library(TCCState *s, const char *libraryname); |
/* add a symbol to the compiled program */ |
int tcc_add_symbol(TCCState *s, const char *name, unsigned long val); |
/* output an executable, library or object file. DO NOT call |
tcc_relocate() before. */ |
int tcc_output_file(TCCState *s, const char *filename); |
/* link and run main() function and return its value. DO NOT call |
tcc_relocate() before. */ |
int tcc_run(TCCState *s, int argc, char **argv); |
/* do all relocations (needed before using tcc_get_symbol()). Return |
non zero if link error. */ |
int tcc_relocate(TCCState *s); |
/* return symbol value. return 0 if OK, -1 if symbol not found */ |
int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name); |
#ifdef __cplusplus |
} |
#endif |
#endif |
/programs/develop/ktcc/trunk/source/libtcc1.c |
---|
0,0 → 1,602 |
/* TCC runtime library. |
Parts of this code are (c) 2002 Fabrice Bellard |
Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc. |
This file 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, or (at your option) any |
later version. |
In addition to the permissions in the GNU General Public License, the |
Free Software Foundation gives you unlimited permission to link the |
compiled version of this file into combinations with other programs, |
and to distribute those combinations without any restriction coming |
from the use of this file. (The General Public License restrictions |
do apply in other respects; for example, they cover modification of |
the file, and distribution when not linked into a combine |
executable.) |
This file 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; see the file COPYING. If not, write to |
the Free Software Foundation, 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. |
*/ |
#define W_TYPE_SIZE 32 |
#define BITS_PER_UNIT 8 |
typedef int Wtype; |
typedef unsigned int UWtype; |
typedef unsigned int USItype; |
typedef long long DWtype; |
typedef unsigned long long UDWtype; |
struct DWstruct { |
Wtype low, high; |
}; |
typedef union |
{ |
struct DWstruct s; |
DWtype ll; |
} DWunion; |
typedef long double XFtype; |
#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) |
/* the following deal with IEEE single-precision numbers */ |
#define EXCESS 126 |
#define SIGNBIT 0x80000000 |
#define HIDDEN (1 << 23) |
#define SIGN(fp) ((fp) & SIGNBIT) |
#define EXP(fp) (((fp) >> 23) & 0xFF) |
#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) |
#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) |
/* the following deal with IEEE double-precision numbers */ |
#define EXCESSD 1022 |
#define HIDDEND (1 << 20) |
#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) |
#define SIGND(fp) ((fp.l.upper) & SIGNBIT) |
#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \ |
(fp.l.lower >> 22)) |
#define HIDDEND_LL ((long long)1 << 52) |
#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) |
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) |
/* the following deal with x86 long double-precision numbers */ |
#define EXCESSLD 16382 |
#define EXPLD(fp) (fp.l.upper & 0x7fff) |
#define SIGNLD(fp) ((fp.l.upper) & 0x8000) |
/* only for x86 */ |
union ldouble_long { |
long double ld; |
struct { |
unsigned long long lower; |
unsigned short upper; |
} l; |
}; |
union double_long { |
double d; |
#if 1 |
struct { |
unsigned long lower; |
long upper; |
} l; |
#else |
struct { |
long upper; |
unsigned long lower; |
} l; |
#endif |
long long ll; |
}; |
union float_long { |
float f; |
long l; |
}; |
/* XXX: use gcc/tcc intrinsic ? */ |
#if defined(__i386__) |
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ |
__asm__ ("subl %5,%1\n\tsbbl %3,%0" \ |
: "=r" ((USItype) (sh)), \ |
"=&r" ((USItype) (sl)) \ |
: "0" ((USItype) (ah)), \ |
"g" ((USItype) (bh)), \ |
"1" ((USItype) (al)), \ |
"g" ((USItype) (bl))) |
#define umul_ppmm(w1, w0, u, v) \ |
__asm__ ("mull %3" \ |
: "=a" ((USItype) (w0)), \ |
"=d" ((USItype) (w1)) \ |
: "%0" ((USItype) (u)), \ |
"rm" ((USItype) (v))) |
#define udiv_qrnnd(q, r, n1, n0, dv) \ |
__asm__ ("divl %4" \ |
: "=a" ((USItype) (q)), \ |
"=d" ((USItype) (r)) \ |
: "0" ((USItype) (n0)), \ |
"1" ((USItype) (n1)), \ |
"rm" ((USItype) (dv))) |
#define count_leading_zeros(count, x) \ |
do { \ |
USItype __cbtmp; \ |
__asm__ ("bsrl %1,%0" \ |
: "=r" (__cbtmp) : "rm" ((USItype) (x))); \ |
(count) = __cbtmp ^ 31; \ |
} while (0) |
#else |
#error unsupported CPU type |
#endif |
/* most of this code is taken from libgcc2.c from gcc */ |
static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) |
{ |
DWunion ww; |
DWunion nn, dd; |
DWunion rr; |
UWtype d0, d1, n0, n1, n2; |
UWtype q0, q1; |
UWtype b, bm; |
nn.ll = n; |
dd.ll = d; |
d0 = dd.s.low; |
d1 = dd.s.high; |
n0 = nn.s.low; |
n1 = nn.s.high; |
#if !UDIV_NEEDS_NORMALIZATION |
if (d1 == 0) |
{ |
if (d0 > n1) |
{ |
/* 0q = nn / 0D */ |
udiv_qrnnd (q0, n0, n1, n0, d0); |
q1 = 0; |
/* Remainder in n0. */ |
} |
else |
{ |
/* qq = NN / 0d */ |
if (d0 == 0) |
d0 = 1 / d0; /* Divide intentionally by zero. */ |
udiv_qrnnd (q1, n1, 0, n1, d0); |
udiv_qrnnd (q0, n0, n1, n0, d0); |
/* Remainder in n0. */ |
} |
if (rp != 0) |
{ |
rr.s.low = n0; |
rr.s.high = 0; |
*rp = rr.ll; |
} |
} |
#else /* UDIV_NEEDS_NORMALIZATION */ |
if (d1 == 0) |
{ |
if (d0 > n1) |
{ |
/* 0q = nn / 0D */ |
count_leading_zeros (bm, d0); |
if (bm != 0) |
{ |
/* Normalize, i.e. make the most significant bit of the |
denominator set. */ |
d0 = d0 << bm; |
n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); |
n0 = n0 << bm; |
} |
udiv_qrnnd (q0, n0, n1, n0, d0); |
q1 = 0; |
/* Remainder in n0 >> bm. */ |
} |
else |
{ |
/* qq = NN / 0d */ |
if (d0 == 0) |
d0 = 1 / d0; /* Divide intentionally by zero. */ |
count_leading_zeros (bm, d0); |
if (bm == 0) |
{ |
/* From (n1 >= d0) /\ (the most significant bit of d0 is set), |
conclude (the most significant bit of n1 is set) /\ (the |
leading quotient digit q1 = 1). |
This special case is necessary, not an optimization. |
(Shifts counts of W_TYPE_SIZE are undefined.) */ |
n1 -= d0; |
q1 = 1; |
} |
else |
{ |
/* Normalize. */ |
b = W_TYPE_SIZE - bm; |
d0 = d0 << bm; |
n2 = n1 >> b; |
n1 = (n1 << bm) | (n0 >> b); |
n0 = n0 << bm; |
udiv_qrnnd (q1, n1, n2, n1, d0); |
} |
/* n1 != d0... */ |
udiv_qrnnd (q0, n0, n1, n0, d0); |
/* Remainder in n0 >> bm. */ |
} |
if (rp != 0) |
{ |
rr.s.low = n0 >> bm; |
rr.s.high = 0; |
*rp = rr.ll; |
} |
} |
#endif /* UDIV_NEEDS_NORMALIZATION */ |
else |
{ |
if (d1 > n1) |
{ |
/* 00 = nn / DD */ |
q0 = 0; |
q1 = 0; |
/* Remainder in n1n0. */ |
if (rp != 0) |
{ |
rr.s.low = n0; |
rr.s.high = n1; |
*rp = rr.ll; |
} |
} |
else |
{ |
/* 0q = NN / dd */ |
count_leading_zeros (bm, d1); |
if (bm == 0) |
{ |
/* From (n1 >= d1) /\ (the most significant bit of d1 is set), |
conclude (the most significant bit of n1 is set) /\ (the |
quotient digit q0 = 0 or 1). |
This special case is necessary, not an optimization. */ |
/* The condition on the next line takes advantage of that |
n1 >= d1 (true due to program flow). */ |
if (n1 > d1 || n0 >= d0) |
{ |
q0 = 1; |
sub_ddmmss (n1, n0, n1, n0, d1, d0); |
} |
else |
q0 = 0; |
q1 = 0; |
if (rp != 0) |
{ |
rr.s.low = n0; |
rr.s.high = n1; |
*rp = rr.ll; |
} |
} |
else |
{ |
UWtype m1, m0; |
/* Normalize. */ |
b = W_TYPE_SIZE - bm; |
d1 = (d1 << bm) | (d0 >> b); |
d0 = d0 << bm; |
n2 = n1 >> b; |
n1 = (n1 << bm) | (n0 >> b); |
n0 = n0 << bm; |
udiv_qrnnd (q0, n1, n2, n1, d1); |
umul_ppmm (m1, m0, q0, d0); |
if (m1 > n1 || (m1 == n1 && m0 > n0)) |
{ |
q0--; |
sub_ddmmss (m1, m0, m1, m0, d1, d0); |
} |
q1 = 0; |
/* Remainder in (n1n0 - m1m0) >> bm. */ |
if (rp != 0) |
{ |
sub_ddmmss (n1, n0, n1, n0, m1, m0); |
rr.s.low = (n1 << b) | (n0 >> bm); |
rr.s.high = n1 >> bm; |
*rp = rr.ll; |
} |
} |
} |
} |
ww.s.low = q0; |
ww.s.high = q1; |
return ww.ll; |
} |
#define __negdi2(a) (-(a)) |
long long __divdi3(long long u, long long v) |
{ |
int c = 0; |
DWunion uu, vv; |
DWtype w; |
uu.ll = u; |
vv.ll = v; |
if (uu.s.high < 0) { |
c = ~c; |
uu.ll = __negdi2 (uu.ll); |
} |
if (vv.s.high < 0) { |
c = ~c; |
vv.ll = __negdi2 (vv.ll); |
} |
w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); |
if (c) |
w = __negdi2 (w); |
return w; |
} |
long long __moddi3(long long u, long long v) |
{ |
int c = 0; |
DWunion uu, vv; |
DWtype w; |
uu.ll = u; |
vv.ll = v; |
if (uu.s.high < 0) { |
c = ~c; |
uu.ll = __negdi2 (uu.ll); |
} |
if (vv.s.high < 0) |
vv.ll = __negdi2 (vv.ll); |
__udivmoddi4 (uu.ll, vv.ll, &w); |
if (c) |
w = __negdi2 (w); |
return w; |
} |
unsigned long long __udivdi3(unsigned long long u, unsigned long long v) |
{ |
return __udivmoddi4 (u, v, (UDWtype *) 0); |
} |
unsigned long long __umoddi3(unsigned long long u, unsigned long long v) |
{ |
UDWtype w; |
__udivmoddi4 (u, v, &w); |
return w; |
} |
/* XXX: fix tcc's code generator to do this instead */ |
long long __sardi3(long long a, int b) |
{ |
#ifdef __TINYC__ |
DWunion u; |
u.ll = a; |
if (b >= 32) { |
u.s.low = u.s.high >> (b - 32); |
u.s.high = u.s.high >> 31; |
} else if (b != 0) { |
u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); |
u.s.high = u.s.high >> b; |
} |
return u.ll; |
#else |
return a >> b; |
#endif |
} |
/* XXX: fix tcc's code generator to do this instead */ |
unsigned long long __shrdi3(unsigned long long a, int b) |
{ |
#ifdef __TINYC__ |
DWunion u; |
u.ll = a; |
if (b >= 32) { |
u.s.low = (unsigned)u.s.high >> (b - 32); |
u.s.high = 0; |
} else if (b != 0) { |
u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); |
u.s.high = (unsigned)u.s.high >> b; |
} |
return u.ll; |
#else |
return a >> b; |
#endif |
} |
/* XXX: fix tcc's code generator to do this instead */ |
long long __shldi3(long long a, int b) |
{ |
#ifdef __TINYC__ |
DWunion u; |
u.ll = a; |
if (b >= 32) { |
u.s.high = (unsigned)u.s.low << (b - 32); |
u.s.low = 0; |
} else if (b != 0) { |
u.s.high = ((unsigned)u.s.high << b) | (u.s.low >> (32 - b)); |
u.s.low = (unsigned)u.s.low << b; |
} |
return u.ll; |
#else |
return a << b; |
#endif |
} |
#if defined(__i386__) |
/* FPU control word for rounding to nearest mode */ |
unsigned short __tcc_fpu_control = 0x137f; |
/* FPU control word for round to zero mode for int conversion */ |
unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00; |
#endif |
/* XXX: fix tcc's code generator to do this instead */ |
float __ulltof(unsigned long long a) |
{ |
DWunion uu; |
XFtype r; |
uu.ll = a; |
if (uu.s.high >= 0) { |
return (float)uu.ll; |
} else { |
r = (XFtype)uu.ll; |
r += 18446744073709551616.0; |
return (float)r; |
} |
} |
double __ulltod(unsigned long long a) |
{ |
DWunion uu; |
XFtype r; |
uu.ll = a; |
if (uu.s.high >= 0) { |
return (double)uu.ll; |
} else { |
r = (XFtype)uu.ll; |
r += 18446744073709551616.0; |
return (double)r; |
} |
} |
long double __ulltold(unsigned long long a) |
{ |
DWunion uu; |
XFtype r; |
uu.ll = a; |
if (uu.s.high >= 0) { |
return (long double)uu.ll; |
} else { |
r = (XFtype)uu.ll; |
r += 18446744073709551616.0; |
return (long double)r; |
} |
} |
unsigned long long __fixunssfdi (float a1) |
{ |
register union float_long fl1; |
register int exp; |
register unsigned long l; |
fl1.f = a1; |
if (fl1.l == 0) |
return (0); |
exp = EXP (fl1.l) - EXCESS - 24; |
l = MANT(fl1.l); |
if (exp >= 41) |
return (unsigned long long)-1; |
else if (exp >= 0) |
return (unsigned long long)l << exp; |
else if (exp >= -23) |
return l >> -exp; |
else |
return 0; |
} |
unsigned long long __fixunsdfdi (double a1) |
{ |
register union double_long dl1; |
register int exp; |
register unsigned long long l; |
dl1.d = a1; |
if (dl1.ll == 0) |
return (0); |
exp = EXPD (dl1) - EXCESSD - 53; |
l = MANTD_LL(dl1); |
if (exp >= 12) |
return (unsigned long long)-1; |
else if (exp >= 0) |
return l << exp; |
else if (exp >= -52) |
return l >> -exp; |
else |
return 0; |
} |
unsigned long long __fixunsxfdi (long double a1) |
{ |
register union ldouble_long dl1; |
register int exp; |
register unsigned long long l; |
dl1.ld = a1; |
if (dl1.l.lower == 0 && dl1.l.upper == 0) |
return (0); |
exp = EXPLD (dl1) - EXCESSLD - 64; |
l = dl1.l.lower; |
if (exp > 0) |
return (unsigned long long)-1; |
else if (exp >= -63) |
return l >> -exp; |
else |
return 0; |
} |
/programs/develop/ktcc/trunk/source/libtcc_test.c |
---|
0,0 → 1,65 |
/* |
* Simple Test program for libtcc |
* |
* libtcc can be useful to use tcc as a "backend" for a code generator. |
*/ |
#include <stdlib.h> |
#include <stdio.h> |
#include "libtcc.h" |
/* this function is called by the generated code */ |
int add(int a, int b) |
{ |
return a + b; |
} |
char my_program[] = |
"int fib(int n)\n" |
"{\n" |
" if (n <= 2)\n" |
" return 1;\n" |
" else\n" |
" return fib(n-1) + fib(n-2);\n" |
"}\n" |
"\n" |
"int foo(int n)\n" |
"{\n" |
" printf(\"Hello World!\\n\");\n" |
" printf(\"fib(%d) = %d\\n\", n, fib(n));\n" |
" printf(\"add(%d, %d) = %d\\n\", n, 2 * n, add(n, 2 * n));\n" |
" return 0;\n" |
"}\n"; |
int main(int argc, char **argv) |
{ |
TCCState *s; |
int (*func)(int); |
unsigned long val; |
s = tcc_new(); |
if (!s) { |
fprintf(stderr, "Could not create tcc state\n"); |
exit(1); |
} |
/* MUST BE CALLED before any compilation or file loading */ |
tcc_set_output_type(s, TCC_OUTPUT_MEMORY); |
tcc_compile_string(s, my_program); |
/* as a test, we add a symbol that the compiled program can be |
linked with. You can have a similar result by opening a dll |
with tcc_add_dll(() and using its symbols directly. */ |
tcc_add_symbol(s, "add", (unsigned long)&add); |
tcc_relocate(s); |
tcc_get_symbol(s, &val, "foo"); |
func = (void *)val; |
func(32); |
tcc_delete(s); |
return 0; |
} |
/programs/develop/ktcc/trunk/source/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. |
*/ |
/programs/develop/ktcc/trunk/source/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_ */ |
/programs/develop/ktcc/trunk/source/stdarg.h |
---|
0,0 → 1,15 |
#ifndef _STDARG_H |
#define _STDARG_H |
typedef char *va_list; |
/* only correct for i386 */ |
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3) |
#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3))) |
#define va_end(ap) |
/* fix a buggy dependency on GCC in libio.h */ |
typedef va_list __gnuc_va_list; |
#define _VA_LIST_DEFINED |
#endif |
/programs/develop/ktcc/trunk/source/stdbool.h |
---|
0,0 → 1,10 |
#ifndef _STDBOOL_H |
#define _STDBOOL_H |
/* ISOC99 boolean */ |
#define bool _Bool |
#define true 1 |
#define false 0 |
#endif /* _STDBOOL_H */ |
/programs/develop/ktcc/trunk/source/stddef.h |
---|
0,0 → 1,21 |
#ifndef _STDDEF_H |
#define _STDDEF_H |
#define NULL ((void *)0) |
typedef __SIZE_TYPE__ size_t; |
typedef __WCHAR_TYPE__ wchar_t; |
typedef __PTRDIFF_TYPE__ ptrdiff_t; |
#define offsetof(type, field) ((size_t) &((type *)0)->field) |
/* need to do that because of glibc 2.1 bug (should have a way to test |
presence of 'long long' without __GNUC__, or TCC should define |
__GNUC__ ? */ |
#if !defined(__int8_t_defined) && !defined(__dietlibc__) |
#define __int8_t_defined |
typedef char int8_t; |
typedef short int int16_t; |
typedef int int32_t; |
typedef long long int int64_t; |
#endif |
#endif |
/programs/develop/ktcc/trunk/source/tcc-doc.html |
---|
0,0 → 1,1809 |
<HTML> |
<HEAD> |
<!-- Created by texi2html 1.56k from tcc-doc.texi on 18 June 2005 --> |
<TITLE>Tiny C Compiler Reference Documentation</TITLE> |
</HEAD> |
<BODY> |
<H1>Tiny C Compiler Reference Documentation</H1> |
<P> |
<P><HR><P> |
<H1>Table of Contents</H1> |
<UL> |
<LI><A NAME="TOC1" HREF="tcc-doc.html#SEC1">1. Introduction</A> |
<LI><A NAME="TOC2" HREF="tcc-doc.html#SEC2">2. Command line invocation</A> |
<UL> |
<LI><A NAME="TOC3" HREF="tcc-doc.html#SEC3">2.1 Quick start</A> |
<LI><A NAME="TOC4" HREF="tcc-doc.html#SEC4">2.2 Option summary</A> |
</UL> |
<LI><A NAME="TOC5" HREF="tcc-doc.html#SEC5">3. C language support</A> |
<UL> |
<LI><A NAME="TOC6" HREF="tcc-doc.html#SEC6">3.1 ANSI C</A> |
<LI><A NAME="TOC7" HREF="tcc-doc.html#SEC7">3.2 ISOC99 extensions</A> |
<LI><A NAME="TOC8" HREF="tcc-doc.html#SEC8">3.3 GNU C extensions</A> |
<LI><A NAME="TOC9" HREF="tcc-doc.html#SEC9">3.4 TinyCC extensions</A> |
</UL> |
<LI><A NAME="TOC10" HREF="tcc-doc.html#SEC10">4. TinyCC Assembler</A> |
<UL> |
<LI><A NAME="TOC11" HREF="tcc-doc.html#SEC11">4.1 Syntax</A> |
<LI><A NAME="TOC12" HREF="tcc-doc.html#SEC12">4.2 Expressions</A> |
<LI><A NAME="TOC13" HREF="tcc-doc.html#SEC13">4.3 Labels</A> |
<LI><A NAME="TOC14" HREF="tcc-doc.html#SEC14">4.4 Directives</A> |
<LI><A NAME="TOC15" HREF="tcc-doc.html#SEC15">4.5 X86 Assembler</A> |
</UL> |
<LI><A NAME="TOC16" HREF="tcc-doc.html#SEC16">5. TinyCC Linker</A> |
<UL> |
<LI><A NAME="TOC17" HREF="tcc-doc.html#SEC17">5.1 ELF file generation</A> |
<LI><A NAME="TOC18" HREF="tcc-doc.html#SEC18">5.2 ELF file loader</A> |
<LI><A NAME="TOC19" HREF="tcc-doc.html#SEC19">5.3 PE-i386 file generation</A> |
<LI><A NAME="TOC20" HREF="tcc-doc.html#SEC20">5.4 GNU Linker Scripts</A> |
</UL> |
<LI><A NAME="TOC21" HREF="tcc-doc.html#SEC21">6. TinyCC Memory and Bound checks</A> |
<LI><A NAME="TOC22" HREF="tcc-doc.html#SEC22">7. The <CODE>libtcc</CODE> library</A> |
<LI><A NAME="TOC23" HREF="tcc-doc.html#SEC23">8. Developer's guide</A> |
<UL> |
<LI><A NAME="TOC24" HREF="tcc-doc.html#SEC24">8.1 File reading</A> |
<LI><A NAME="TOC25" HREF="tcc-doc.html#SEC25">8.2 Lexer</A> |
<LI><A NAME="TOC26" HREF="tcc-doc.html#SEC26">8.3 Parser</A> |
<LI><A NAME="TOC27" HREF="tcc-doc.html#SEC27">8.4 Types</A> |
<LI><A NAME="TOC28" HREF="tcc-doc.html#SEC28">8.5 Symbols</A> |
<LI><A NAME="TOC29" HREF="tcc-doc.html#SEC29">8.6 Sections</A> |
<LI><A NAME="TOC30" HREF="tcc-doc.html#SEC30">8.7 Code generation</A> |
<UL> |
<LI><A NAME="TOC31" HREF="tcc-doc.html#SEC31">8.7.1 Introduction</A> |
<LI><A NAME="TOC32" HREF="tcc-doc.html#SEC32">8.7.2 The value stack</A> |
<LI><A NAME="TOC33" HREF="tcc-doc.html#SEC33">8.7.3 Manipulating the value stack</A> |
<LI><A NAME="TOC34" HREF="tcc-doc.html#SEC34">8.7.4 CPU dependent code generation</A> |
</UL> |
<LI><A NAME="TOC35" HREF="tcc-doc.html#SEC35">8.8 Optimizations done</A> |
</UL> |
<LI><A NAME="TOC36" HREF="tcc-doc.html#SEC36">Concept Index</A> |
</UL> |
<P><HR><P> |
<H1><A NAME="SEC1" HREF="tcc-doc.html#TOC1">1. Introduction</A></H1> |
<P> |
TinyCC (aka TCC) is a small but hyper fast C compiler. Unlike other C |
compilers, it is meant to be self-relying: you do not need an |
external assembler or linker because TCC does that for you. |
<P> |
TCC compiles so <EM>fast</EM> that even for big projects <CODE>Makefile</CODE>s may |
not be necessary. |
<P> |
TCC not only supports ANSI C, but also most of the new ISO C99 |
standard and many GNUC extensions including inline assembly. |
<P> |
TCC can also be used to make <EM>C scripts</EM>, i.e. pieces of C source |
that you run as a Perl or Python script. Compilation is so fast that |
your script will be as fast as if it was an executable. |
<P> |
TCC can also automatically generate memory and bound checks |
(see section <A HREF="tcc-doc.html#SEC21">6. TinyCC Memory and Bound checks</A>) while allowing all C pointers operations. TCC can do |
these checks even if non patched libraries are used. |
<P> |
With <CODE>libtcc</CODE>, you can use TCC as a backend for dynamic code |
generation (see section <A HREF="tcc-doc.html#SEC22">7. The <CODE>libtcc</CODE> library</A>). |
<P> |
TCC mainly supports the i386 target on Linux and Windows. There are alpha |
ports for the ARM (<CODE>arm-tcc</CODE>) and the TMS320C67xx targets |
(<CODE>c67-tcc</CODE>). More information about the ARM port is available at |
<A HREF="http://lists.gnu.org/archive/html/tinycc-devel/2003-10/msg00044.html">http://lists.gnu.org/archive/html/tinycc-devel/2003-10/msg00044.html</A>. |
<H1><A NAME="SEC2" HREF="tcc-doc.html#TOC2">2. Command line invocation</A></H1> |
<P> |
[This manual documents version 0.9.23 of the Tiny C Compiler] |
<H2><A NAME="SEC3" HREF="tcc-doc.html#TOC3">2.1 Quick start</A></H2> |
<PRE> |
usage: tcc [options] [<VAR>infile1</VAR> <VAR>infile2</VAR>...] [<SAMP>`-run'</SAMP> <VAR>infile</VAR> <VAR>args</VAR>...] |
</PRE> |
<P> |
TCC options are a very much like gcc options. The main difference is that TCC |
can also execute directly the resulting program and give it runtime |
arguments. |
<P> |
Here are some examples to understand the logic: |
<DL COMPACT> |
<DT><CODE><SAMP>`tcc -run a.c'</SAMP></CODE> |
<DD> |
Compile <TT>`a.c'</TT> and execute it directly |
<DT><CODE><SAMP>`tcc -run a.c arg1'</SAMP></CODE> |
<DD> |
Compile a.c and execute it directly. arg1 is given as first argument to |
the <CODE>main()</CODE> of a.c. |
<DT><CODE><SAMP>`tcc a.c -run b.c arg1'</SAMP></CODE> |
<DD> |
Compile <TT>`a.c'</TT> and <TT>`b.c'</TT>, link them together and execute them. arg1 is given |
as first argument to the <CODE>main()</CODE> of the resulting program. Because |
multiple C files are specified, <SAMP>`--'</SAMP> are necessary to clearly separate the |
program arguments from the TCC options. |
<DT><CODE><SAMP>`tcc -o myprog a.c b.c'</SAMP></CODE> |
<DD> |
Compile <TT>`a.c'</TT> and <TT>`b.c'</TT>, link them and generate the executable <TT>`myprog'</TT>. |
<DT><CODE><SAMP>`tcc -o myprog a.o b.o'</SAMP></CODE> |
<DD> |
link <TT>`a.o'</TT> and <TT>`b.o'</TT> together and generate the executable <TT>`myprog'</TT>. |
<DT><CODE><SAMP>`tcc -c a.c'</SAMP></CODE> |
<DD> |
Compile <TT>`a.c'</TT> and generate object file <TT>`a.o'</TT>. |
<DT><CODE><SAMP>`tcc -c asmfile.S'</SAMP></CODE> |
<DD> |
Preprocess with C preprocess and assemble <TT>`asmfile.S'</TT> and generate |
object file <TT>`asmfile.o'</TT>. |
<DT><CODE><SAMP>`tcc -c asmfile.s'</SAMP></CODE> |
<DD> |
Assemble (but not preprocess) <TT>`asmfile.s'</TT> and generate object file |
<TT>`asmfile.o'</TT>. |
<DT><CODE><SAMP>`tcc -r -o ab.o a.c b.c'</SAMP></CODE> |
<DD> |
Compile <TT>`a.c'</TT> and <TT>`b.c'</TT>, link them together and generate the object file <TT>`ab.o'</TT>. |
</DL> |
<P> |
Scripting: |
<P> |
TCC can be invoked from <EM>scripts</EM>, just as shell scripts. You just |
need to add <CODE>#!/usr/local/bin/tcc -run</CODE> at the start of your C source: |
<PRE> |
#!/usr/local/bin/tcc -run |
#include <stdio.h> |
int main() |
{ |
printf("Hello World\n"); |
return 0; |
} |
</PRE> |
<H2><A NAME="SEC4" HREF="tcc-doc.html#TOC4">2.2 Option summary</A></H2> |
<P> |
General Options: |
<DL COMPACT> |
<DT><SAMP>`-v'</SAMP> |
<DD> |
Display current TCC version. |
<DT><SAMP>`-c'</SAMP> |
<DD> |
Generate an object file (<SAMP>`-o'</SAMP> option must also be given). |
<DT><SAMP>`-o outfile'</SAMP> |
<DD> |
Put object file, executable, or dll into output file <TT>`outfile'</TT>. |
<DT><SAMP>`-Bdir'</SAMP> |
<DD> |
Set the path where the tcc internal libraries can be found (default is |
<TT>`PREFIX/lib/tcc'</TT>). |
<DT><SAMP>`-bench'</SAMP> |
<DD> |
Output compilation statistics. |
<DT><SAMP>`-run source [args...]'</SAMP> |
<DD> |
Compile file <VAR>source</VAR> and run it with the command line arguments |
<VAR>args</VAR>. In order to be able to give more than one argument to a |
script, several TCC options can be given <EM>after</EM> the |
<SAMP>`-run'</SAMP> option, separated by spaces. Example: |
<PRE> |
tcc "-run -L/usr/X11R6/lib -lX11" ex4.c |
</PRE> |
In a script, it gives the following header: |
<PRE> |
#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11 |
#include <stdlib.h> |
int main(int argc, char **argv) |
{ |
... |
} |
</PRE> |
</DL> |
<P> |
Preprocessor options: |
<DL COMPACT> |
<DT><SAMP>`-Idir'</SAMP> |
<DD> |
Specify an additional include path. Include paths are searched in the |
order they are specified. |
System include paths are always searched after. The default system |
include paths are: <TT>`/usr/local/include'</TT>, <TT>`/usr/include'</TT> |
and <TT>`PREFIX/lib/tcc/include'</TT>. (<TT>`PREFIX'</TT> is usually |
<TT>`/usr'</TT> or <TT>`/usr/local'</TT>). |
<DT><SAMP>`-Dsym[=val]'</SAMP> |
<DD> |
Define preprocessor symbol <SAMP>`sym'</SAMP> to |
val. If val is not present, its value is <SAMP>`1'</SAMP>. Function-like macros can |
also be defined: <SAMP>`-DF(a)=a+1'</SAMP> |
<DT><SAMP>`-Usym'</SAMP> |
<DD> |
Undefine preprocessor symbol <SAMP>`sym'</SAMP>. |
</DL> |
<P> |
Compilation flags: |
<P> |
Note: each of the following warning options has a negative form beginning with |
<SAMP>`-fno-'</SAMP>. |
<DL COMPACT> |
<DT><SAMP>`-funsigned-char'</SAMP> |
<DD> |
Let the <CODE>char</CODE> type be unsigned. |
<DT><SAMP>`-fsigned-char'</SAMP> |
<DD> |
Let the <CODE>char</CODE> type be signed. |
<DT><SAMP>`-fno-common'</SAMP> |
<DD> |
Do not generate common symbols for uninitialized data. |
<DT><SAMP>`-fleading-underscore'</SAMP> |
<DD> |
Add a leading underscore at the beginning of each C symbol. |
</DL> |
<P> |
Warning options: |
<DL COMPACT> |
<DT><SAMP>`-w'</SAMP> |
<DD> |
Disable all warnings. |
</DL> |
<P> |
Note: each of the following warning options has a negative form beginning with |
<SAMP>`-Wno-'</SAMP>. |
<DL COMPACT> |
<DT><SAMP>`-Wimplicit-function-declaration'</SAMP> |
<DD> |
Warn about implicit function declaration. |
<DT><SAMP>`-Wunsupported'</SAMP> |
<DD> |
Warn about unsupported GCC features that are ignored by TCC. |
<DT><SAMP>`-Wwrite-strings'</SAMP> |
<DD> |
Make string constants be of type <CODE>const char *</CODE> instead of <CODE>char |
*</CODE>. |
<DT><SAMP>`-Werror'</SAMP> |
<DD> |
Abort compilation if warnings are issued. |
<DT><SAMP>`-Wall'</SAMP> |
<DD> |
Activate all warnings, except <SAMP>`-Werror'</SAMP>, <SAMP>`-Wunusupported'</SAMP> and |
<SAMP>`-Wwrite-strings'</SAMP>. |
</DL> |
<P> |
Linker options: |
<DL COMPACT> |
<DT><SAMP>`-Ldir'</SAMP> |
<DD> |
Specify an additional static library path for the <SAMP>`-l'</SAMP> option. The |
default library paths are <TT>`/usr/local/lib'</TT>, <TT>`/usr/lib'</TT> and <TT>`/lib'</TT>. |
<DT><SAMP>`-lxxx'</SAMP> |
<DD> |
Link your program with dynamic library libxxx.so or static library |
libxxx.a. The library is searched in the paths specified by the |
<SAMP>`-L'</SAMP> option. |
<DT><SAMP>`-shared'</SAMP> |
<DD> |
Generate a shared library instead of an executable (<SAMP>`-o'</SAMP> option |
must also be given). |
<DT><SAMP>`-static'</SAMP> |
<DD> |
Generate a statically linked executable (default is a shared linked |
executable) (<SAMP>`-o'</SAMP> option must also be given). |
<DT><SAMP>`-rdynamic'</SAMP> |
<DD> |
Export global symbols to the dynamic linker. It is useful when a library |
opened with <CODE>dlopen()</CODE> needs to access executable symbols. |
<DT><SAMP>`-r'</SAMP> |
<DD> |
Generate an object file combining all input files (<SAMP>`-o'</SAMP> option must |
also be given). |
<DT><SAMP>`-Wl,-Ttext,address'</SAMP> |
<DD> |
Set the start of the .text section to <VAR>address</VAR>. |
<DT><SAMP>`-Wl,--oformat,fmt'</SAMP> |
<DD> |
Use <VAR>fmt</VAR> as output format. The supported output formats are: |
<DL COMPACT> |
<DT><CODE>elf32-i386</CODE> |
<DD> |
ELF output format (default) |
<DT><CODE>binary</CODE> |
<DD> |
Binary image (only for executable output) |
<DT><CODE>coff</CODE> |
<DD> |
COFF output format (only for executable output for TMS320C67xx target) |
</DL> |
</DL> |
<P> |
Debugger options: |
<DL COMPACT> |
<DT><SAMP>`-g'</SAMP> |
<DD> |
Generate run time debug information so that you get clear run time |
error messages: <CODE> test.c:68: in function 'test5()': dereferencing |
invalid pointer</CODE> instead of the laconic <CODE>Segmentation |
fault</CODE>. |
<DT><SAMP>`-b'</SAMP> |
<DD> |
Generate additional support code to check |
memory allocations and array/pointer bounds. <SAMP>`-g'</SAMP> is implied. Note |
that the generated code is slower and bigger in this case. |
<DT><SAMP>`-bt N'</SAMP> |
<DD> |
Display N callers in stack traces. This is useful with <SAMP>`-g'</SAMP> or |
<SAMP>`-b'</SAMP>. |
</DL> |
<P> |
Note: GCC options <SAMP>`-Ox'</SAMP>, <SAMP>`-fx'</SAMP> and <SAMP>`-mx'</SAMP> are |
ignored. |
<H1><A NAME="SEC5" HREF="tcc-doc.html#TOC5">3. C language support</A></H1> |
<H2><A NAME="SEC6" HREF="tcc-doc.html#TOC6">3.1 ANSI C</A></H2> |
<P> |
TCC implements all the ANSI C standard, including structure bit fields |
and floating point numbers (<CODE>long double</CODE>, <CODE>double</CODE>, and |
<CODE>float</CODE> fully supported). |
<H2><A NAME="SEC7" HREF="tcc-doc.html#TOC7">3.2 ISOC99 extensions</A></H2> |
<P> |
TCC implements many features of the new C standard: ISO C99. Currently |
missing items are: complex and imaginary numbers and variable length |
arrays. |
<P> |
Currently implemented ISOC99 features: |
<UL> |
<LI>64 bit <CODE>long long</CODE> types are fully supported. |
<LI>The boolean type <CODE>_Bool</CODE> is supported. |
<LI><CODE>__func__</CODE> is a string variable containing the current |
function name. |
<LI>Variadic macros: <CODE>__VA_ARGS__</CODE> can be used for |
function-like macros: |
<PRE> |
#define dprintf(level, __VA_ARGS__) printf(__VA_ARGS__) |
</PRE> |
<CODE>dprintf</CODE> can then be used with a variable number of parameters. |
<LI>Declarations can appear anywhere in a block (as in C++). |
<LI>Array and struct/union elements can be initialized in any order by |
using designators: |
<PRE> |
struct { int x, y; } st[10] = { [0].x = 1, [0].y = 2 }; |
int tab[10] = { 1, 2, [5] = 5, [9] = 9}; |
</PRE> |
<LI>Compound initializers are supported: |
<PRE> |
int *p = (int []){ 1, 2, 3 }; |
</PRE> |
to initialize a pointer pointing to an initialized array. The same |
works for structures and strings. |
<LI>Hexadecimal floating point constants are supported: |
<PRE> |
double d = 0x1234p10; |
</PRE> |
is the same as writing |
<PRE> |
double d = 4771840.0; |
</PRE> |
<LI><CODE>inline</CODE> keyword is ignored. |
<LI><CODE>restrict</CODE> keyword is ignored. |
</UL> |
<H2><A NAME="SEC8" HREF="tcc-doc.html#TOC8">3.3 GNU C extensions</A></H2> |
<P> |
<A NAME="IDX1"></A> |
<A NAME="IDX2"></A> |
<A NAME="IDX3"></A> |
<A NAME="IDX4"></A> |
<A NAME="IDX5"></A> |
<A NAME="IDX6"></A> |
<A NAME="IDX7"></A> |
<P> |
TCC implements some GNU C extensions: |
<UL> |
<LI>array designators can be used without '=': |
<PRE> |
int a[10] = { [0] 1, [5] 2, 3, 4 }; |
</PRE> |
<LI>Structure field designators can be a label: |
<PRE> |
struct { int x, y; } st = { x: 1, y: 1}; |
</PRE> |
instead of |
<PRE> |
struct { int x, y; } st = { .x = 1, .y = 1}; |
</PRE> |
<LI><CODE>\e</CODE> is ASCII character 27. |
<LI>case ranges : ranges can be used in <CODE>case</CODE>s: |
<PRE> |
switch(a) { |
case 1 ... 9: |
printf("range 1 to 9\n"); |
break; |
default: |
printf("unexpected\n"); |
break; |
} |
</PRE> |
<LI>The keyword <CODE>__attribute__</CODE> is handled to specify variable or |
function attributes. The following attributes are supported: |
<UL> |
<LI><CODE>aligned(n)</CODE>: align a variable or a structure field to n bytes |
(must be a power of two). |
<LI><CODE>packed</CODE>: force alignment of a variable or a structure field to |
1. |
<LI><CODE>section(name)</CODE>: generate function or data in assembly section |
name (name is a string containing the section name) instead of the default |
section. |
<LI><CODE>unused</CODE>: specify that the variable or the function is unused. |
<LI><CODE>cdecl</CODE>: use standard C calling convention (default). |
<LI><CODE>stdcall</CODE>: use Pascal-like calling convention. |
<LI><CODE>regparm(n)</CODE>: use fast i386 calling convention. <VAR>n</VAR> must be |
between 1 and 3. The first <VAR>n</VAR> function parameters are respectively put in |
registers <CODE>%eax</CODE>, <CODE>%edx</CODE> and <CODE>%ecx</CODE>. |
</UL> |
Here are some examples: |
<PRE> |
int a __attribute__ ((aligned(8), section(".mysection"))); |
</PRE> |
align variable <CODE>a</CODE> to 8 bytes and put it in section <CODE>.mysection</CODE>. |
<PRE> |
int my_add(int a, int b) __attribute__ ((section(".mycodesection"))) |
{ |
return a + b; |
} |
</PRE> |
generate function <CODE>my_add</CODE> in section <CODE>.mycodesection</CODE>. |
<LI>GNU style variadic macros: |
<PRE> |
#define dprintf(fmt, args...) printf(fmt, ## args) |
dprintf("no arg\n"); |
dprintf("one arg %d\n", 1); |
</PRE> |
<LI><CODE>__FUNCTION__</CODE> is interpreted as C99 <CODE>__func__</CODE> |
(so it has not exactly the same semantics as string literal GNUC |
where it is a string literal). |
<LI>The <CODE>__alignof__</CODE> keyword can be used as <CODE>sizeof</CODE> |
to get the alignment of a type or an expression. |
<LI>The <CODE>typeof(x)</CODE> returns the type of <CODE>x</CODE>. |
<CODE>x</CODE> is an expression or a type. |
<LI>Computed gotos: <CODE>&&label</CODE> returns a pointer of type |
<CODE>void *</CODE> on the goto label <CODE>label</CODE>. <CODE>goto *expr</CODE> can be |
used to jump on the pointer resulting from <CODE>expr</CODE>. |
<LI>Inline assembly with asm instruction: |
<A NAME="IDX8"></A> |
<A NAME="IDX9"></A> |
<A NAME="IDX10"></A> |
<PRE> |
static inline void * my_memcpy(void * to, const void * from, size_t n) |
{ |
int d0, d1, d2; |
__asm__ __volatile__( |
"rep ; movsl\n\t" |
"testb $2,%b4\n\t" |
"je 1f\n\t" |
"movsw\n" |
"1:\ttestb $1,%b4\n\t" |
"je 2f\n\t" |
"movsb\n" |
"2:" |
: "=&c" (d0), "=&D" (d1), "=&S" (d2) |
:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from) |
: "memory"); |
return (to); |
} |
</PRE> |
<A NAME="IDX11"></A> |
TCC includes its own x86 inline assembler with a <CODE>gas</CODE>-like (GNU |
assembler) syntax. No intermediate files are generated. GCC 3.x named |
operands are supported. |
<LI><CODE>__builtin_types_compatible_p()</CODE> and <CODE>__builtin_constant_p()</CODE> |
are supported. |
<LI><CODE>#pragma pack</CODE> is supported for win32 compatibility. |
</UL> |
<H2><A NAME="SEC9" HREF="tcc-doc.html#TOC9">3.4 TinyCC extensions</A></H2> |
<UL> |
<LI><CODE>__TINYC__</CODE> is a predefined macro to <CODE>1</CODE> to |
indicate that you use TCC. |
<LI><CODE>#!</CODE> at the start of a line is ignored to allow scripting. |
<LI>Binary digits can be entered (<CODE>0b101</CODE> instead of |
<CODE>5</CODE>). |
<LI><CODE>__BOUNDS_CHECKING_ON</CODE> is defined if bound checking is activated. |
</UL> |
<H1><A NAME="SEC10" HREF="tcc-doc.html#TOC10">4. TinyCC Assembler</A></H1> |
<P> |
Since version 0.9.16, TinyCC integrates its own assembler. TinyCC |
assembler supports a gas-like syntax (GNU assembler). You can |
desactivate assembler support if you want a smaller TinyCC executable |
(the C compiler does not rely on the assembler). |
<P> |
TinyCC Assembler is used to handle files with <TT>`.S'</TT> (C |
preprocessed assembler) and <TT>`.s'</TT> extensions. It is also used to |
handle the GNU inline assembler with the <CODE>asm</CODE> keyword. |
<H2><A NAME="SEC11" HREF="tcc-doc.html#TOC11">4.1 Syntax</A></H2> |
<P> |
TinyCC Assembler supports most of the gas syntax. The tokens are the |
same as C. |
<UL> |
<LI>C and C++ comments are supported. |
<LI>Identifiers are the same as C, so you cannot use '.' or '$'. |
<LI>Only 32 bit integer numbers are supported. |
</UL> |
<H2><A NAME="SEC12" HREF="tcc-doc.html#TOC12">4.2 Expressions</A></H2> |
<UL> |
<LI>Integers in decimal, octal and hexa are supported. |
<LI>Unary operators: +, -, ~. |
<LI>Binary operators in decreasing priority order: |
<OL> |
<LI>*, /, % |
<LI>&, |, ^ |
<LI>+, - |
</OL> |
<LI>A value is either an absolute number or a label plus an offset. |
All operators accept absolute values except '+' and '-'. '+' or '-' can be |
used to add an offset to a label. '-' supports two labels only if they |
are the same or if they are both defined and in the same section. |
</UL> |
<H2><A NAME="SEC13" HREF="tcc-doc.html#TOC13">4.3 Labels</A></H2> |
<UL> |
<LI>All labels are considered as local, except undefined ones. |
<LI>Numeric labels can be used as local <CODE>gas</CODE>-like labels. |
They can be defined several times in the same source. Use 'b' |
(backward) or 'f' (forward) as suffix to reference them: |
<PRE> |
1: |
jmp 1b /* jump to '1' label before */ |
jmp 1f /* jump to '1' label after */ |
1: |
</PRE> |
</UL> |
<H2><A NAME="SEC14" HREF="tcc-doc.html#TOC14">4.4 Directives</A></H2> |
<P> |
<A NAME="IDX12"></A> |
<A NAME="IDX13"></A> |
<A NAME="IDX14"></A> |
<A NAME="IDX15"></A> |
<A NAME="IDX16"></A> |
<A NAME="IDX17"></A> |
<A NAME="IDX18"></A> |
<A NAME="IDX19"></A> |
<A NAME="IDX20"></A> |
<A NAME="IDX21"></A> |
<A NAME="IDX22"></A> |
<A NAME="IDX23"></A> |
<A NAME="IDX24"></A> |
<A NAME="IDX25"></A> |
<A NAME="IDX26"></A> |
<A NAME="IDX27"></A> |
<A NAME="IDX28"></A> |
<A NAME="IDX29"></A> |
<A NAME="IDX30"></A> |
<A NAME="IDX31"></A> |
<A NAME="IDX32"></A> |
<A NAME="IDX33"></A> |
<A NAME="IDX34"></A> |
<P> |
All directives are preceeded by a '.'. The following directives are |
supported: |
<UL> |
<LI>.align n[,value] |
<LI>.skip n[,value] |
<LI>.space n[,value] |
<LI>.byte value1[,...] |
<LI>.word value1[,...] |
<LI>.short value1[,...] |
<LI>.int value1[,...] |
<LI>.long value1[,...] |
<LI>.quad immediate_value1[,...] |
<LI>.globl symbol |
<LI>.global symbol |
<LI>.section section |
<LI>.text |
<LI>.data |
<LI>.bss |
<LI>.fill repeat[,size[,value]] |
<LI>.org n |
<LI>.previous |
<LI>.string string[,...] |
<LI>.asciz string[,...] |
<LI>.ascii string[,...] |
</UL> |
<H2><A NAME="SEC15" HREF="tcc-doc.html#TOC15">4.5 X86 Assembler</A></H2> |
<P> |
<A NAME="IDX35"></A> |
<P> |
All X86 opcodes are supported. Only ATT syntax is supported (source |
then destination operand order). If no size suffix is given, TinyCC |
tries to guess it from the operand sizes. |
<P> |
Currently, MMX opcodes are supported but not SSE ones. |
<H1><A NAME="SEC16" HREF="tcc-doc.html#TOC16">5. TinyCC Linker</A></H1> |
<P> |
<A NAME="IDX36"></A> |
<H2><A NAME="SEC17" HREF="tcc-doc.html#TOC17">5.1 ELF file generation</A></H2> |
<P> |
<A NAME="IDX37"></A> |
<P> |
TCC can directly output relocatable ELF files (object files), |
executable ELF files and dynamic ELF libraries without relying on an |
external linker. |
<P> |
Dynamic ELF libraries can be output but the C compiler does not generate |
position independent code (PIC). It means that the dynamic library |
code generated by TCC cannot be factorized among processes yet. |
<P> |
TCC linker eliminates unreferenced object code in libraries. A single pass is |
done on the object and library list, so the order in which object files and |
libraries are specified is important (same constraint as GNU ld). No grouping |
options (<SAMP>`--start-group'</SAMP> and <SAMP>`--end-group'</SAMP>) are supported. |
<H2><A NAME="SEC18" HREF="tcc-doc.html#TOC18">5.2 ELF file loader</A></H2> |
<P> |
TCC can load ELF object files, archives (.a files) and dynamic |
libraries (.so). |
<H2><A NAME="SEC19" HREF="tcc-doc.html#TOC19">5.3 PE-i386 file generation</A></H2> |
<P> |
<A NAME="IDX38"></A> |
<P> |
TCC for Windows supports the native Win32 executable file format (PE-i386). It |
generates both EXE and DLL files. DLL symbols can be imported thru DEF files |
generated with the <CODE>tiny_impdef</CODE> tool. |
<P> |
Currently TCC for Windows cannot generate nor read PE object files, so ELF |
object files are used for that purpose. It can be a problem if |
interoperability with MSVC is needed. Moreover, no leading underscore is |
currently generated in the ELF symbols. |
<H2><A NAME="SEC20" HREF="tcc-doc.html#TOC20">5.4 GNU Linker Scripts</A></H2> |
<P> |
<A NAME="IDX39"></A> |
<A NAME="IDX40"></A> |
<A NAME="IDX41"></A> |
<A NAME="IDX42"></A> |
<A NAME="IDX43"></A> |
<A NAME="IDX44"></A> |
<P> |
Because on many Linux systems some dynamic libraries (such as |
<TT>`/usr/lib/libc.so'</TT>) are in fact GNU ld link scripts (horrible!), |
the TCC linker also supports a subset of GNU ld scripts. |
<P> |
The <CODE>GROUP</CODE> and <CODE>FILE</CODE> commands are supported. <CODE>OUTPUT_FORMAT</CODE> |
and <CODE>TARGET</CODE> are ignored. |
<P> |
Example from <TT>`/usr/lib/libc.so'</TT>: |
<PRE> |
/* GNU ld script |
Use the shared library, but some functions are only in |
the static library, so try that secondarily. */ |
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a ) |
</PRE> |
<H1><A NAME="SEC21" HREF="tcc-doc.html#TOC21">6. TinyCC Memory and Bound checks</A></H1> |
<P> |
<A NAME="IDX45"></A> |
<A NAME="IDX46"></A> |
<P> |
This feature is activated with the <SAMP>`-b'</SAMP> (see section <A HREF="tcc-doc.html#SEC2">2. Command line invocation</A>). |
<P> |
Note that pointer size is <EM>unchanged</EM> and that code generated |
with bound checks is <EM>fully compatible</EM> with unchecked |
code. When a pointer comes from unchecked code, it is assumed to be |
valid. Even very obscure C code with casts should work correctly. |
<P> |
For more information about the ideas behind this method, see |
<A HREF="http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html">http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html</A>. |
<P> |
Here are some examples of caught errors: |
<DL COMPACT> |
<DT>Invalid range with standard string function: |
<DD> |
<PRE> |
{ |
char tab[10]; |
memset(tab, 0, 11); |
} |
</PRE> |
<DT>Out of bounds-error in global or local arrays: |
<DD> |
<PRE> |
{ |
int tab[10]; |
for(i=0;i<11;i++) { |
sum += tab[i]; |
} |
} |
</PRE> |
<DT>Out of bounds-error in malloc'ed data: |
<DD> |
<PRE> |
{ |
int *tab; |
tab = malloc(20 * sizeof(int)); |
for(i=0;i<21;i++) { |
sum += tab4[i]; |
} |
free(tab); |
} |
</PRE> |
<DT>Access of freed memory: |
<DD> |
<PRE> |
{ |
int *tab; |
tab = malloc(20 * sizeof(int)); |
free(tab); |
for(i=0;i<20;i++) { |
sum += tab4[i]; |
} |
} |
</PRE> |
<DT>Double free: |
<DD> |
<PRE> |
{ |
int *tab; |
tab = malloc(20 * sizeof(int)); |
free(tab); |
free(tab); |
} |
</PRE> |
</DL> |
<H1><A NAME="SEC22" HREF="tcc-doc.html#TOC22">7. The <CODE>libtcc</CODE> library</A></H1> |
<P> |
The <CODE>libtcc</CODE> library enables you to use TCC as a backend for |
dynamic code generation. |
<P> |
Read the <TT>`libtcc.h'</TT> to have an overview of the API. Read |
<TT>`libtcc_test.c'</TT> to have a very simple example. |
<P> |
The idea consists in giving a C string containing the program you want |
to compile directly to <CODE>libtcc</CODE>. Then you can access to any global |
symbol (function or variable) defined. |
<H1><A NAME="SEC23" HREF="tcc-doc.html#TOC23">8. Developer's guide</A></H1> |
<P> |
This chapter gives some hints to understand how TCC works. You can skip |
it if you do not intend to modify the TCC code. |
<H2><A NAME="SEC24" HREF="tcc-doc.html#TOC24">8.1 File reading</A></H2> |
<P> |
The <CODE>BufferedFile</CODE> structure contains the context needed to read a |
file, including the current line number. <CODE>tcc_open()</CODE> opens a new |
file and <CODE>tcc_close()</CODE> closes it. <CODE>inp()</CODE> returns the next |
character. |
<H2><A NAME="SEC25" HREF="tcc-doc.html#TOC25">8.2 Lexer</A></H2> |
<P> |
<CODE>next()</CODE> reads the next token in the current |
file. <CODE>next_nomacro()</CODE> reads the next token without macro |
expansion. |
<P> |
<CODE>tok</CODE> contains the current token (see <CODE>TOK_xxx</CODE>) |
constants. Identifiers and keywords are also keywords. <CODE>tokc</CODE> |
contains additional infos about the token (for example a constant value |
if number or string token). |
<H2><A NAME="SEC26" HREF="tcc-doc.html#TOC26">8.3 Parser</A></H2> |
<P> |
The parser is hardcoded (yacc is not necessary). It does only one pass, |
except: |
<UL> |
<LI>For initialized arrays with unknown size, a first pass |
is done to count the number of elements. |
<LI>For architectures where arguments are evaluated in |
reverse order, a first pass is done to reverse the argument order. |
</UL> |
<H2><A NAME="SEC27" HREF="tcc-doc.html#TOC27">8.4 Types</A></H2> |
<P> |
The types are stored in a single 'int' variable. It was choosen in the |
first stages of development when tcc was much simpler. Now, it may not |
be the best solution. |
<PRE> |
#define VT_INT 0 /* integer type */ |
#define VT_BYTE 1 /* signed byte type */ |
#define VT_SHORT 2 /* short type */ |
#define VT_VOID 3 /* void type */ |
#define VT_PTR 4 /* pointer */ |
#define VT_ENUM 5 /* enum definition */ |
#define VT_FUNC 6 /* function type */ |
#define VT_STRUCT 7 /* struct/union definition */ |
#define VT_FLOAT 8 /* IEEE float */ |
#define VT_DOUBLE 9 /* IEEE double */ |
#define VT_LDOUBLE 10 /* IEEE long double */ |
#define VT_BOOL 11 /* ISOC99 boolean type */ |
#define VT_LLONG 12 /* 64 bit integer */ |
#define VT_LONG 13 /* long integer (NEVER USED as type, only |
during parsing) */ |
#define VT_BTYPE 0x000f /* mask for basic type */ |
#define VT_UNSIGNED 0x0010 /* unsigned type */ |
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ |
#define VT_BITFIELD 0x0040 /* bitfield modifier */ |
#define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */ |
</PRE> |
<P> |
When a reference to another type is needed (for pointers, functions and |
structures), the <CODE>32 - VT_STRUCT_SHIFT</CODE> high order bits are used to |
store an identifier reference. |
<P> |
The <CODE>VT_UNSIGNED</CODE> flag can be set for chars, shorts, ints and long |
longs. |
<P> |
Arrays are considered as pointers <CODE>VT_PTR</CODE> with the flag |
<CODE>VT_ARRAY</CODE> set. |
<P> |
The <CODE>VT_BITFIELD</CODE> flag can be set for chars, shorts, ints and long |
longs. If it is set, then the bitfield position is stored from bits |
VT_STRUCT_SHIFT to VT_STRUCT_SHIFT + 5 and the bit field size is stored |
from bits VT_STRUCT_SHIFT + 6 to VT_STRUCT_SHIFT + 11. |
<P> |
<CODE>VT_LONG</CODE> is never used except during parsing. |
<P> |
During parsing, the storage of an object is also stored in the type |
integer: |
<PRE> |
#define VT_EXTERN 0x00000080 /* extern definition */ |
#define VT_STATIC 0x00000100 /* static variable */ |
#define VT_TYPEDEF 0x00000200 /* typedef definition */ |
</PRE> |
<H2><A NAME="SEC28" HREF="tcc-doc.html#TOC28">8.5 Symbols</A></H2> |
<P> |
All symbols are stored in hashed symbol stacks. Each symbol stack |
contains <CODE>Sym</CODE> structures. |
<P> |
<CODE>Sym.v</CODE> contains the symbol name (remember |
an idenfier is also a token, so a string is never necessary to store |
it). <CODE>Sym.t</CODE> gives the type of the symbol. <CODE>Sym.r</CODE> is usually |
the register in which the corresponding variable is stored. <CODE>Sym.c</CODE> is |
usually a constant associated to the symbol. |
<P> |
Four main symbol stacks are defined: |
<DL COMPACT> |
<DT><CODE>define_stack</CODE> |
<DD> |
for the macros (<CODE>#define</CODE>s). |
<DT><CODE>global_stack</CODE> |
<DD> |
for the global variables, functions and types. |
<DT><CODE>local_stack</CODE> |
<DD> |
for the local variables, functions and types. |
<DT><CODE>global_label_stack</CODE> |
<DD> |
for the local labels (for <CODE>goto</CODE>). |
<DT><CODE>label_stack</CODE> |
<DD> |
for GCC block local labels (see the <CODE>__label__</CODE> keyword). |
</DL> |
<P> |
<CODE>sym_push()</CODE> is used to add a new symbol in the local symbol |
stack. If no local symbol stack is active, it is added in the global |
symbol stack. |
<P> |
<CODE>sym_pop(st,b)</CODE> pops symbols from the symbol stack <VAR>st</VAR> until |
the symbol <VAR>b</VAR> is on the top of stack. If <VAR>b</VAR> is NULL, the stack |
is emptied. |
<P> |
<CODE>sym_find(v)</CODE> return the symbol associated to the identifier |
<VAR>v</VAR>. The local stack is searched first from top to bottom, then the |
global stack. |
<H2><A NAME="SEC29" HREF="tcc-doc.html#TOC29">8.6 Sections</A></H2> |
<P> |
The generated code and datas are written in sections. The structure |
<CODE>Section</CODE> contains all the necessary information for a given |
section. <CODE>new_section()</CODE> creates a new section. ELF file semantics |
is assumed for each section. |
<P> |
The following sections are predefined: |
<DL COMPACT> |
<DT><CODE>text_section</CODE> |
<DD> |
is the section containing the generated code. <VAR>ind</VAR> contains the |
current position in the code section. |
<DT><CODE>data_section</CODE> |
<DD> |
contains initialized data |
<DT><CODE>bss_section</CODE> |
<DD> |
contains uninitialized data |
<DT><CODE>bounds_section</CODE> |
<DD> |
<DT><CODE>lbounds_section</CODE> |
<DD> |
are used when bound checking is activated |
<DT><CODE>stab_section</CODE> |
<DD> |
<DT><CODE>stabstr_section</CODE> |
<DD> |
are used when debugging is actived to store debug information |
<DT><CODE>symtab_section</CODE> |
<DD> |
<DT><CODE>strtab_section</CODE> |
<DD> |
contain the exported symbols (currently only used for debugging). |
</DL> |
<H2><A NAME="SEC30" HREF="tcc-doc.html#TOC30">8.7 Code generation</A></H2> |
<P> |
<A NAME="IDX47"></A> |
<H3><A NAME="SEC31" HREF="tcc-doc.html#TOC31">8.7.1 Introduction</A></H3> |
<P> |
The TCC code generator directly generates linked binary code in one |
pass. It is rather unusual these days (see gcc for example which |
generates text assembly), but it can be very fast and surprisingly |
little complicated. |
<P> |
The TCC code generator is register based. Optimization is only done at |
the expression level. No intermediate representation of expression is |
kept except the current values stored in the <EM>value stack</EM>. |
<P> |
On x86, three temporary registers are used. When more registers are |
needed, one register is spilled into a new temporary variable on the stack. |
<H3><A NAME="SEC32" HREF="tcc-doc.html#TOC32">8.7.2 The value stack</A></H3> |
<P> |
<A NAME="IDX48"></A> |
<P> |
When an expression is parsed, its value is pushed on the value stack |
(<VAR>vstack</VAR>). The top of the value stack is <VAR>vtop</VAR>. Each value |
stack entry is the structure <CODE>SValue</CODE>. |
<P> |
<CODE>SValue.t</CODE> is the type. <CODE>SValue.r</CODE> indicates how the value is |
currently stored in the generated code. It is usually a CPU register |
index (<CODE>REG_xxx</CODE> constants), but additional values and flags are |
defined: |
<PRE> |
#define VT_CONST 0x00f0 |
#define VT_LLOCAL 0x00f1 |
#define VT_LOCAL 0x00f2 |
#define VT_CMP 0x00f3 |
#define VT_JMP 0x00f4 |
#define VT_JMPI 0x00f5 |
#define VT_LVAL 0x0100 |
#define VT_SYM 0x0200 |
#define VT_MUSTCAST 0x0400 |
#define VT_MUSTBOUND 0x0800 |
#define VT_BOUNDED 0x8000 |
#define VT_LVAL_BYTE 0x1000 |
#define VT_LVAL_SHORT 0x2000 |
#define VT_LVAL_UNSIGNED 0x4000 |
#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED) |
</PRE> |
<DL COMPACT> |
<DT><CODE>VT_CONST</CODE> |
<DD> |
indicates that the value is a constant. It is stored in the union |
<CODE>SValue.c</CODE>, depending on its type. |
<DT><CODE>VT_LOCAL</CODE> |
<DD> |
indicates a local variable pointer at offset <CODE>SValue.c.i</CODE> in the |
stack. |
<DT><CODE>VT_CMP</CODE> |
<DD> |
indicates that the value is actually stored in the CPU flags (i.e. the |
value is the consequence of a test). The value is either 0 or 1. The |
actual CPU flags used is indicated in <CODE>SValue.c.i</CODE>. |
If any code is generated which destroys the CPU flags, this value MUST be |
put in a normal register. |
<DT><CODE>VT_JMP</CODE> |
<DD> |
<DT><CODE>VT_JMPI</CODE> |
<DD> |
indicates that the value is the consequence of a conditional jump. For VT_JMP, |
it is 1 if the jump is taken, 0 otherwise. For VT_JMPI it is inverted. |
These values are used to compile the <CODE>||</CODE> and <CODE>&&</CODE> logical |
operators. |
If any code is generated, this value MUST be put in a normal |
register. Otherwise, the generated code won't be executed if the jump is |
taken. |
<DT><CODE>VT_LVAL</CODE> |
<DD> |
is a flag indicating that the value is actually an lvalue (left value of |
an assignment). It means that the value stored is actually a pointer to |
the wanted value. |
Understanding the use <CODE>VT_LVAL</CODE> is very important if you want to |
understand how TCC works. |
<DT><CODE>VT_LVAL_BYTE</CODE> |
<DD> |
<DT><CODE>VT_LVAL_SHORT</CODE> |
<DD> |
<DT><CODE>VT_LVAL_UNSIGNED</CODE> |
<DD> |
if the lvalue has an integer type, then these flags give its real |
type. The type alone is not enough in case of cast optimisations. |
<DT><CODE>VT_LLOCAL</CODE> |
<DD> |
is a saved lvalue on the stack. <CODE>VT_LLOCAL</CODE> should be eliminated |
ASAP because its semantics are rather complicated. |
<DT><CODE>VT_MUSTCAST</CODE> |
<DD> |
indicates that a cast to the value type must be performed if the value |
is used (lazy casting). |
<DT><CODE>VT_SYM</CODE> |
<DD> |
indicates that the symbol <CODE>SValue.sym</CODE> must be added to the constant. |
<DT><CODE>VT_MUSTBOUND</CODE> |
<DD> |
<DT><CODE>VT_BOUNDED</CODE> |
<DD> |
are only used for optional bound checking. |
</DL> |
<H3><A NAME="SEC33" HREF="tcc-doc.html#TOC33">8.7.3 Manipulating the value stack</A></H3> |
<P> |
<A NAME="IDX49"></A> |
<P> |
<CODE>vsetc()</CODE> and <CODE>vset()</CODE> pushes a new value on the value |
stack. If the previous <VAR>vtop</VAR> was stored in a very unsafe place(for |
example in the CPU flags), then some code is generated to put the |
previous <VAR>vtop</VAR> in a safe storage. |
<P> |
<CODE>vpop()</CODE> pops <VAR>vtop</VAR>. In some cases, it also generates cleanup |
code (for example if stacked floating point registers are used as on |
x86). |
<P> |
The <CODE>gv(rc)</CODE> function generates code to evaluate <VAR>vtop</VAR> (the |
top value of the stack) into registers. <VAR>rc</VAR> selects in which |
register class the value should be put. <CODE>gv()</CODE> is the <EM>most |
important function</EM> of the code generator. |
<P> |
<CODE>gv2()</CODE> is the same as <CODE>gv()</CODE> but for the top two stack |
entries. |
<H3><A NAME="SEC34" HREF="tcc-doc.html#TOC34">8.7.4 CPU dependent code generation</A></H3> |
<P> |
<A NAME="IDX50"></A> |
See the <TT>`i386-gen.c'</TT> file to have an example. |
<DL COMPACT> |
<DT><CODE>load()</CODE> |
<DD> |
must generate the code needed to load a stack value into a register. |
<DT><CODE>store()</CODE> |
<DD> |
must generate the code needed to store a register into a stack value |
lvalue. |
<DT><CODE>gfunc_start()</CODE> |
<DD> |
<DT><CODE>gfunc_param()</CODE> |
<DD> |
<DT><CODE>gfunc_call()</CODE> |
<DD> |
should generate a function call |
<DT><CODE>gfunc_prolog()</CODE> |
<DD> |
<DT><CODE>gfunc_epilog()</CODE> |
<DD> |
should generate a function prolog/epilog. |
<DT><CODE>gen_opi(op)</CODE> |
<DD> |
must generate the binary integer operation <VAR>op</VAR> on the two top |
entries of the stack which are guaranted to contain integer types. |
The result value should be put on the stack. |
<DT><CODE>gen_opf(op)</CODE> |
<DD> |
same as <CODE>gen_opi()</CODE> for floating point operations. The two top |
entries of the stack are guaranted to contain floating point values of |
same types. |
<DT><CODE>gen_cvt_itof()</CODE> |
<DD> |
integer to floating point conversion. |
<DT><CODE>gen_cvt_ftoi()</CODE> |
<DD> |
floating point to integer conversion. |
<DT><CODE>gen_cvt_ftof()</CODE> |
<DD> |
floating point to floating point of different size conversion. |
<DT><CODE>gen_bounded_ptr_add()</CODE> |
<DD> |
<DT><CODE>gen_bounded_ptr_deref()</CODE> |
<DD> |
are only used for bounds checking. |
</DL> |
<H2><A NAME="SEC35" HREF="tcc-doc.html#TOC35">8.8 Optimizations done</A></H2> |
<P> |
<A NAME="IDX51"></A> |
<A NAME="IDX52"></A> |
<A NAME="IDX53"></A> |
<A NAME="IDX54"></A> |
<A NAME="IDX55"></A> |
<A NAME="IDX56"></A> |
<A NAME="IDX57"></A> |
Constant propagation is done for all operations. Multiplications and |
divisions are optimized to shifts when appropriate. Comparison |
operators are optimized by maintaining a special cache for the |
processor flags. &&, || and ! are optimized by maintaining a special |
'jump target' value. No other jump optimization is currently performed |
because it would require to store the code in a more abstract fashion. |
<H1><A NAME="SEC36" HREF="tcc-doc.html#TOC36">Concept Index</A></H1> |
<P> |
Jump to: |
<A HREF="#cindex__">_</A> |
- |
<A HREF="#cindex_a">a</A> |
- |
<A HREF="#cindex_b">b</A> |
- |
<A HREF="#cindex_c">c</A> |
- |
<A HREF="#cindex_d">d</A> |
- |
<A HREF="#cindex_e">e</A> |
- |
<A HREF="#cindex_f">f</A> |
- |
<A HREF="#cindex_g">g</A> |
- |
<A HREF="#cindex_i">i</A> |
- |
<A HREF="#cindex_j">j</A> |
- |
<A HREF="#cindex_l">l</A> |
- |
<A HREF="#cindex_m">m</A> |
- |
<A HREF="#cindex_o">o</A> |
- |
<A HREF="#cindex_p">p</A> |
- |
<A HREF="#cindex_q">q</A> |
- |
<A HREF="#cindex_r">r</A> |
- |
<A HREF="#cindex_s">s</A> |
- |
<A HREF="#cindex_t">t</A> |
- |
<A HREF="#cindex_u">u</A> |
- |
<A HREF="#cindex_v">v</A> |
- |
<A HREF="#cindex_w">w</A> |
<P> |
<H2><A NAME="cindex__">_</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX10">__asm__</A> |
</DIR> |
<H2><A NAME="cindex_a">a</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX14">align directive</A> |
<LI><A HREF="tcc-doc.html#IDX1">aligned attribute</A> |
<LI><A HREF="tcc-doc.html#IDX34">ascii directive</A> |
<LI><A HREF="tcc-doc.html#IDX33">asciz directive</A> |
<LI><A HREF="tcc-doc.html#IDX35">assembler</A> |
<LI><A HREF="tcc-doc.html#IDX12">assembler directives</A> |
<LI><A HREF="tcc-doc.html#IDX9">assembly, inline</A> |
</DIR> |
<H2><A NAME="cindex_b">b</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX45">bound checks</A> |
<LI><A HREF="tcc-doc.html#IDX28">bss directive</A> |
<LI><A HREF="tcc-doc.html#IDX17">byte directive</A> |
</DIR> |
<H2><A NAME="cindex_c">c</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX55">caching processor flags</A> |
<LI><A HREF="tcc-doc.html#IDX5">cdecl attribute</A> |
<LI><A HREF="tcc-doc.html#IDX47">code generation</A> |
<LI><A HREF="tcc-doc.html#IDX54">comparison operators</A> |
<LI><A HREF="tcc-doc.html#IDX52">constant propagation</A> |
<LI><A HREF="tcc-doc.html#IDX50">CPU dependent</A> |
</DIR> |
<H2><A NAME="cindex_d">d</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX27">data directive</A> |
<LI><A HREF="tcc-doc.html#IDX13">directives, assembler</A> |
</DIR> |
<H2><A NAME="cindex_e">e</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX37">ELF</A> |
</DIR> |
<H2><A NAME="cindex_f">f</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX42">FILE, linker command</A> |
<LI><A HREF="tcc-doc.html#IDX29">fill directive</A> |
<LI><A HREF="tcc-doc.html#IDX56">flags, caching</A> |
</DIR> |
<H2><A NAME="cindex_g">g</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX11">gas</A> |
<LI><A HREF="tcc-doc.html#IDX24">global directive</A> |
<LI><A HREF="tcc-doc.html#IDX23">globl directive</A> |
<LI><A HREF="tcc-doc.html#IDX41">GROUP, linker command</A> |
</DIR> |
<H2><A NAME="cindex_i">i</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX8">inline assembly</A> |
<LI><A HREF="tcc-doc.html#IDX20">int directive</A> |
</DIR> |
<H2><A NAME="cindex_j">j</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX57">jump optimization</A> |
</DIR> |
<H2><A NAME="cindex_l">l</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX36">linker</A> |
<LI><A HREF="tcc-doc.html#IDX40">linker scripts</A> |
<LI><A HREF="tcc-doc.html#IDX21">long directive</A> |
</DIR> |
<H2><A NAME="cindex_m">m</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX46">memory checks</A> |
</DIR> |
<H2><A NAME="cindex_o">o</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX51">optimizations</A> |
<LI><A HREF="tcc-doc.html#IDX30">org directive</A> |
<LI><A HREF="tcc-doc.html#IDX43">OUTPUT_FORMAT, linker command</A> |
</DIR> |
<H2><A NAME="cindex_p">p</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX2">packed attribute</A> |
<LI><A HREF="tcc-doc.html#IDX38">PE-i386</A> |
<LI><A HREF="tcc-doc.html#IDX31">previous directive</A> |
</DIR> |
<H2><A NAME="cindex_q">q</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX22">quad directive</A> |
</DIR> |
<H2><A NAME="cindex_r">r</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX7">regparm attribute</A> |
</DIR> |
<H2><A NAME="cindex_s">s</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX39">scripts, linker</A> |
<LI><A HREF="tcc-doc.html#IDX3">section attribute</A> |
<LI><A HREF="tcc-doc.html#IDX25">section directive</A> |
<LI><A HREF="tcc-doc.html#IDX19">short directive</A> |
<LI><A HREF="tcc-doc.html#IDX15">skip directive</A> |
<LI><A HREF="tcc-doc.html#IDX16">space directive</A> |
<LI><A HREF="tcc-doc.html#IDX6">stdcall attribute</A> |
<LI><A HREF="tcc-doc.html#IDX53">strength reduction</A> |
<LI><A HREF="tcc-doc.html#IDX32">string directive</A> |
</DIR> |
<H2><A NAME="cindex_t">t</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX44">TARGET, linker command</A> |
<LI><A HREF="tcc-doc.html#IDX26">text directive</A> |
</DIR> |
<H2><A NAME="cindex_u">u</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX4">unused attribute</A> |
</DIR> |
<H2><A NAME="cindex_v">v</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX49">value stack</A> |
<LI><A HREF="tcc-doc.html#IDX48">value stack, introduction</A> |
</DIR> |
<H2><A NAME="cindex_w">w</A></H2> |
<DIR> |
<LI><A HREF="tcc-doc.html#IDX18">word directive</A> |
</DIR> |
<P><HR><P> |
This document was generated on 18 June 2005 using |
<A HREF="http://wwwinfo.cern.ch/dis/texi2html/">texi2html</A> 1.56k. |
</BODY> |
</HTML> |
/programs/develop/ktcc/trunk/source/tccasm.c |
---|
0,0 → 1,1019 |
/* |
* GAS like assembler for TCC |
* |
* Copyright (c) 2001-2004 Fabrice Bellard |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation; either |
* version 2 of the License, or (at your option) any later version. |
* |
* This library is distributed in the hope that it will be useful, |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
* Lesser General Public License for more details. |
* |
* You should have received a copy of the GNU Lesser General Public |
* License along with this library; if not, write to the Free Software |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
static int asm_get_local_label_name(TCCState *s1, unsigned int n) |
{ |
char buf[64]; |
TokenSym *ts; |
snprintf(buf, sizeof(buf), "L..%u", n); |
ts = tok_alloc(buf, strlen(buf)); |
return ts->tok; |
} |
static void asm_expr(TCCState *s1, ExprValue *pe); |
/* We do not use the C expression parser to handle symbols. Maybe the |
C expression parser could be tweaked to do so. */ |
static void asm_expr_unary(TCCState *s1, ExprValue *pe) |
{ |
Sym *sym; |
int op, n, label; |
const char *p; |
switch(tok) { |
case TOK_PPNUM: |
p = tokc.cstr->data; |
n = strtoul(p, (char **)&p, 0); |
if (*p == 'b' || *p == 'f') { |
/* backward or forward label */ |
label = asm_get_local_label_name(s1, n); |
sym = label_find(label); |
if (*p == 'b') { |
/* backward : find the last corresponding defined label */ |
if (sym && sym->r == 0) |
sym = sym->prev_tok; |
if (!sym) |
error("local label '%d' not found backward", n); |
} else { |
/* forward */ |
if (!sym || sym->r) { |
/* if the last label is defined, then define a new one */ |
sym = label_push(&s1->asm_labels, label, 0); |
sym->type.t = VT_STATIC | VT_VOID; |
} |
} |
pe->v = 0; |
pe->sym = sym; |
} else if (*p == '\0') { |
pe->v = n; |
pe->sym = NULL; |
} else { |
error("invalid number syntax"); |
} |
next(); |
break; |
case '+': |
next(); |
asm_expr_unary(s1, pe); |
break; |
case '-': |
case '~': |
op = tok; |
next(); |
asm_expr_unary(s1, pe); |
if (pe->sym) |
error("invalid operation with label"); |
if (op == '-') |
pe->v = -pe->v; |
else |
pe->v = ~pe->v; |
break; |
case TOK_CCHAR: |
case TOK_LCHAR: |
pe->v = tokc.i; |
pe->sym = NULL; |
next(); |
break; |
case '(': |
next(); |
asm_expr(s1, pe); |
skip(')'); |
break; |
default: |
if (tok >= TOK_IDENT) { |
/* label case : if the label was not found, add one */ |
sym = label_find(tok); |
if (!sym) { |
sym = label_push(&s1->asm_labels, tok, 0); |
/* NOTE: by default, the symbol is global */ |
sym->type.t = VT_VOID; |
} |
if (sym->r == SHN_ABS) { |
/* if absolute symbol, no need to put a symbol value */ |
pe->v = (long)sym->next; |
pe->sym = NULL; |
} else { |
pe->v = 0; |
pe->sym = sym; |
} |
next(); |
} else { |
error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); |
} |
break; |
} |
} |
static void asm_expr_prod(TCCState *s1, ExprValue *pe) |
{ |
int op; |
ExprValue e2; |
asm_expr_unary(s1, pe); |
for(;;) { |
op = tok; |
if (op != '*' && op != '/' && op != '%' && |
op != TOK_SHL && op != TOK_SAR) |
break; |
next(); |
asm_expr_unary(s1, &e2); |
if (pe->sym || e2.sym) |
error("invalid operation with label"); |
switch(op) { |
case '*': |
pe->v *= e2.v; |
break; |
case '/': |
if (e2.v == 0) { |
div_error: |
error("division by zero"); |
} |
pe->v /= e2.v; |
break; |
case '%': |
if (e2.v == 0) |
goto div_error; |
pe->v %= e2.v; |
break; |
case TOK_SHL: |
pe->v <<= e2.v; |
break; |
default: |
case TOK_SAR: |
pe->v >>= e2.v; |
break; |
} |
} |
} |
static void asm_expr_logic(TCCState *s1, ExprValue *pe) |
{ |
int op; |
ExprValue e2; |
asm_expr_prod(s1, pe); |
for(;;) { |
op = tok; |
if (op != '&' && op != '|' && op != '^') |
break; |
next(); |
asm_expr_prod(s1, &e2); |
if (pe->sym || e2.sym) |
error("invalid operation with label"); |
switch(op) { |
case '&': |
pe->v &= e2.v; |
break; |
case '|': |
pe->v |= e2.v; |
break; |
default: |
case '^': |
pe->v ^= e2.v; |
break; |
} |
} |
} |
static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) |
{ |
int op; |
ExprValue e2; |
asm_expr_logic(s1, pe); |
for(;;) { |
op = tok; |
if (op != '+' && op != '-') |
break; |
next(); |
asm_expr_logic(s1, &e2); |
if (op == '+') { |
if (pe->sym != NULL && e2.sym != NULL) |
goto cannot_relocate; |
pe->v += e2.v; |
if (pe->sym == NULL && e2.sym != NULL) |
pe->sym = e2.sym; |
} else { |
pe->v -= e2.v; |
/* NOTE: we are less powerful than gas in that case |
because we store only one symbol in the expression */ |
if (!pe->sym && !e2.sym) { |
/* OK */ |
} else if (pe->sym && !e2.sym) { |
/* OK */ |
} else if (pe->sym && e2.sym) { |
if (pe->sym == e2.sym) { |
/* OK */ |
} else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) { |
/* we also accept defined symbols in the same section */ |
pe->v += (long)pe->sym->next - (long)e2.sym->next; |
} else { |
goto cannot_relocate; |
} |
pe->sym = NULL; /* same symbols can be substracted to NULL */ |
} else { |
cannot_relocate: |
error("invalid operation with label"); |
} |
} |
} |
} |
static void asm_expr(TCCState *s1, ExprValue *pe) |
{ |
asm_expr_sum(s1, pe); |
} |
static int asm_int_expr(TCCState *s1) |
{ |
ExprValue e; |
asm_expr(s1, &e); |
if (e.sym) |
expect("constant"); |
return e.v; |
} |
/* NOTE: the same name space as C labels is used to avoid using too |
much memory when storing labels in TokenStrings */ |
static void asm_new_label1(TCCState *s1, int label, int is_local, |
int sh_num, int value) |
{ |
Sym *sym; |
sym = label_find(label); |
if (sym) { |
if (sym->r) { |
/* the label is already defined */ |
if (!is_local) { |
error("assembler label '%s' already defined", |
get_tok_str(label, NULL)); |
} else { |
/* redefinition of local labels is possible */ |
goto new_label; |
} |
} |
} else { |
new_label: |
sym = label_push(&s1->asm_labels, label, 0); |
sym->type.t = VT_STATIC | VT_VOID; |
} |
sym->r = sh_num; |
sym->next = (void *)value; |
} |
static void asm_new_label(TCCState *s1, int label, int is_local) |
{ |
asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind); |
} |
static void asm_free_labels(TCCState *st) |
{ |
Sym *s, *s1; |
Section *sec; |
for(s = st->asm_labels; s != NULL; s = s1) { |
s1 = s->prev; |
/* define symbol value in object file */ |
if (s->r) { |
if (s->r == SHN_ABS) |
sec = SECTION_ABS; |
else |
sec = st->sections[s->r]; |
put_extern_sym2(s, sec, (long)s->next, 0, 0); |
} |
/* remove label */ |
table_ident[s->v - TOK_IDENT]->sym_label = NULL; |
sym_free(s); |
} |
st->asm_labels = NULL; |
} |
static void use_section1(TCCState *s1, Section *sec) |
{ |
cur_text_section->data_offset = ind; |
cur_text_section = sec; |
ind = cur_text_section->data_offset; |
} |
static void use_section(TCCState *s1, const char *name) |
{ |
Section *sec; |
sec = find_section(s1, name); |
use_section1(s1, sec); |
} |
static void asm_parse_directive(TCCState *s1) |
{ |
int n, offset, v, size, tok1; |
Section *sec; |
uint8_t *ptr; |
/* assembler directive */ |
next(); |
sec = cur_text_section; |
switch(tok) { |
case TOK_ASM_align: |
case TOK_ASM_skip: |
case TOK_ASM_space: |
tok1 = tok; |
next(); |
n = asm_int_expr(s1); |
if (tok1 == TOK_ASM_align) { |
if (n < 0 || (n & (n-1)) != 0) |
error("alignment must be a positive power of two"); |
offset = (ind + n - 1) & -n; |
size = offset - ind; |
/* the section must have a compatible alignment */ |
if (sec->sh_addralign < n) |
sec->sh_addralign = n; |
} else { |
size = n; |
} |
v = 0; |
if (tok == ',') { |
next(); |
v = asm_int_expr(s1); |
} |
zero_pad: |
if (sec->sh_type != SHT_NOBITS) { |
sec->data_offset = ind; |
ptr = section_ptr_add(sec, size); |
memset(ptr, v, size); |
} |
ind += size; |
break; |
case TOK_ASM_quad: |
next(); |
for(;;) { |
uint64_t vl; |
const char *p; |
p = tokc.cstr->data; |
if (tok != TOK_PPNUM) { |
error_constant: |
error("64 bit constant"); |
} |
vl = strtoll(p, (char **)&p, 0); |
if (*p != '\0') |
goto error_constant; |
next(); |
if (sec->sh_type != SHT_NOBITS) { |
/* XXX: endianness */ |
gen_le32(vl); |
gen_le32(vl >> 32); |
} else { |
ind += 8; |
} |
if (tok != ',') |
break; |
next(); |
} |
break; |
case TOK_ASM_byte: |
size = 1; |
goto asm_data; |
case TOK_ASM_word: |
case TOK_SHORT: |
size = 2; |
goto asm_data; |
case TOK_LONG: |
case TOK_INT: |
size = 4; |
asm_data: |
next(); |
for(;;) { |
ExprValue e; |
asm_expr(s1, &e); |
if (sec->sh_type != SHT_NOBITS) { |
if (size == 4) { |
gen_expr32(&e); |
} else { |
if (e.sym) |
expect("constant"); |
if (size == 1) |
g(e.v); |
else |
gen_le16(e.v); |
} |
} else { |
ind += size; |
} |
if (tok != ',') |
break; |
next(); |
} |
break; |
case TOK_ASM_fill: |
{ |
int repeat, size, val, i, j; |
uint8_t repeat_buf[8]; |
next(); |
repeat = asm_int_expr(s1); |
if (repeat < 0) { |
error("repeat < 0; .fill ignored"); |
break; |
} |
size = 1; |
val = 0; |
if (tok == ',') { |
next(); |
size = asm_int_expr(s1); |
if (size < 0) { |
error("size < 0; .fill ignored"); |
break; |
} |
if (size > 8) |
size = 8; |
if (tok == ',') { |
next(); |
val = asm_int_expr(s1); |
} |
} |
/* XXX: endianness */ |
repeat_buf[0] = val; |
repeat_buf[1] = val >> 8; |
repeat_buf[2] = val >> 16; |
repeat_buf[3] = val >> 24; |
repeat_buf[4] = 0; |
repeat_buf[5] = 0; |
repeat_buf[6] = 0; |
repeat_buf[7] = 0; |
for(i = 0; i < repeat; i++) { |
for(j = 0; j < size; j++) { |
g(repeat_buf[j]); |
} |
} |
} |
break; |
case TOK_ASM_org: |
{ |
unsigned long n; |
next(); |
/* XXX: handle section symbols too */ |
n = asm_int_expr(s1); |
if (n < ind) |
error("attempt to .org backwards"); |
v = 0; |
size = n - ind; |
goto zero_pad; |
} |
break; |
case TOK_ASM_globl: |
case TOK_ASM_global: |
{ |
Sym *sym; |
next(); |
sym = label_find(tok); |
if (!sym) { |
sym = label_push(&s1->asm_labels, tok, 0); |
sym->type.t = VT_VOID; |
} |
sym->type.t &= ~VT_STATIC; |
next(); |
} |
break; |
case TOK_ASM_string: |
case TOK_ASM_ascii: |
case TOK_ASM_asciz: |
{ |
const uint8_t *p; |
int i, size, t; |
t = tok; |
next(); |
for(;;) { |
if (tok != TOK_STR) |
expect("string constant"); |
p = tokc.cstr->data; |
size = tokc.cstr->size; |
if (t == TOK_ASM_ascii && size > 0) |
size--; |
for(i = 0; i < size; i++) |
g(p[i]); |
next(); |
if (tok == ',') { |
next(); |
} else if (tok != TOK_STR) { |
break; |
} |
} |
} |
break; |
case TOK_ASM_text: |
case TOK_ASM_data: |
case TOK_ASM_bss: |
{ |
char sname[64]; |
tok1 = tok; |
n = 0; |
next(); |
if (tok != ';' && tok != TOK_LINEFEED) { |
n = asm_int_expr(s1); |
next(); |
} |
sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n); |
use_section(s1, sname); |
} |
break; |
case TOK_SECTION1: |
{ |
char sname[256]; |
/* XXX: support more options */ |
next(); |
sname[0] = '\0'; |
while (tok != ';' && tok != TOK_LINEFEED && tok != ',') { |
if (tok == TOK_STR) |
pstrcat(sname, sizeof(sname), tokc.cstr->data); |
else |
pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL)); |
next(); |
} |
if (tok == ',') { |
/* skip section options */ |
next(); |
if (tok != TOK_STR) |
expect("string constant"); |
next(); |
} |
last_text_section = cur_text_section; |
use_section(s1, sname); |
} |
break; |
case TOK_ASM_previous: |
{ |
Section *sec; |
next(); |
if (!last_text_section) |
error("no previous section referenced"); |
sec = cur_text_section; |
use_section1(s1, last_text_section); |
last_text_section = sec; |
} |
break; |
default: |
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); |
break; |
} |
} |
/* assemble a file */ |
static int tcc_assemble_internal(TCCState *s1, int do_preprocess) |
{ |
int opcode; |
#if 0 |
/* print stats about opcodes */ |
{ |
const ASMInstr *pa; |
int freq[4]; |
int op_vals[500]; |
int nb_op_vals, i, j; |
nb_op_vals = 0; |
memset(freq, 0, sizeof(freq)); |
for(pa = asm_instrs; pa->sym != 0; pa++) { |
freq[pa->nb_ops]++; |
for(i=0;i<pa->nb_ops;i++) { |
for(j=0;j<nb_op_vals;j++) { |
if (pa->op_type[i] == op_vals[j]) |
goto found; |
} |
op_vals[nb_op_vals++] = pa->op_type[i]; |
found: ; |
} |
} |
for(i=0;i<nb_op_vals;i++) { |
int v = op_vals[i]; |
if ((v & (v - 1)) != 0) |
printf("%3d: %08x\n", i, v); |
} |
printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n", |
sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr), |
freq[0], freq[1], freq[2], freq[3]); |
} |
#endif |
/* XXX: undefine C labels */ |
ch = file->buf_ptr[0]; |
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; |
parse_flags = PARSE_FLAG_ASM_COMMENTS; |
if (do_preprocess) |
parse_flags |= PARSE_FLAG_PREPROCESS; |
next(); |
for(;;) { |
if (tok == TOK_EOF) |
break; |
parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ |
redo: |
if (tok == '#') { |
/* horrible gas comment */ |
while (tok != TOK_LINEFEED) |
next(); |
} else if (tok == '.') { |
asm_parse_directive(s1); |
} else if (tok == TOK_PPNUM) { |
const char *p; |
int n; |
p = tokc.cstr->data; |
n = strtoul(p, (char **)&p, 10); |
if (*p != '\0') |
expect("':'"); |
/* new local label */ |
asm_new_label(s1, asm_get_local_label_name(s1, n), 1); |
next(); |
skip(':'); |
goto redo; |
} else if (tok >= TOK_IDENT) { |
/* instruction or label */ |
opcode = tok; |
next(); |
if (tok == ':') { |
/* new label */ |
asm_new_label(s1, opcode, 0); |
next(); |
goto redo; |
} else if (tok == '=') { |
int n; |
next(); |
n = asm_int_expr(s1); |
asm_new_label1(s1, opcode, 0, SHN_ABS, n); |
goto redo; |
} else { |
asm_opcode(s1, opcode); |
} |
} |
/* end of line */ |
if (tok != ';' && tok != TOK_LINEFEED){ |
expect("end of line"); |
} |
parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ |
next(); |
} |
asm_free_labels(s1); |
return 0; |
} |
/* Assemble the current file */ |
static int tcc_assemble(TCCState *s1, int do_preprocess) |
{ |
Sym *define_start; |
int ret; |
preprocess_init(s1); |
/* default section is text */ |
cur_text_section = text_section; |
ind = cur_text_section->data_offset; |
define_start = define_stack; |
ret = tcc_assemble_internal(s1, do_preprocess); |
cur_text_section->data_offset = ind; |
free_defines(define_start); |
return ret; |
} |
/********************************************************************/ |
/* GCC inline asm support */ |
/* assemble the string 'str' in the current C compilation unit without |
C preprocessing. NOTE: str is modified by modifying the '\0' at the |
end */ |
static void tcc_assemble_inline(TCCState *s1, char *str, int len) |
{ |
BufferedFile *bf, *saved_file; |
int saved_parse_flags, *saved_macro_ptr; |
bf = tcc_malloc(sizeof(BufferedFile)); |
memset(bf, 0, sizeof(BufferedFile)); |
bf->fd = -1; |
bf->buf_ptr = str; |
bf->buf_end = str + len; |
str[len] = CH_EOB; |
/* same name as current file so that errors are correctly |
reported */ |
pstrcpy(bf->filename, sizeof(bf->filename), file->filename); |
bf->line_num = file->line_num; |
saved_file = file; |
file = bf; |
saved_parse_flags = parse_flags; |
saved_macro_ptr = macro_ptr; |
macro_ptr = NULL; |
tcc_assemble_internal(s1, 0); |
parse_flags = saved_parse_flags; |
macro_ptr = saved_macro_ptr; |
file = saved_file; |
tcc_free(bf); |
} |
/* find a constraint by its number or id (gcc 3 extended |
syntax). return -1 if not found. Return in *pp in char after the |
constraint */ |
static int find_constraint(ASMOperand *operands, int nb_operands, |
const char *name, const char **pp) |
{ |
int index; |
TokenSym *ts; |
const char *p; |
if (isnum(*name)) { |
index = 0; |
while (isnum(*name)) { |
index = (index * 10) + (*name) - '0'; |
name++; |
} |
if ((unsigned)index >= nb_operands) |
index = -1; |
} else if (*name == '[') { |
name++; |
p = strchr(name, ']'); |
if (p) { |
ts = tok_alloc(name, p - name); |
for(index = 0; index < nb_operands; index++) { |
if (operands[index].id == ts->tok) |
goto found; |
} |
index = -1; |
found: |
name = p + 1; |
} else { |
index = -1; |
} |
} else { |
index = -1; |
} |
if (pp) |
*pp = name; |
return index; |
} |
static void subst_asm_operands(ASMOperand *operands, int nb_operands, |
int nb_outputs, |
CString *out_str, CString *in_str) |
{ |
int c, index, modifier; |
const char *str; |
ASMOperand *op; |
SValue sv; |
cstr_new(out_str); |
str = in_str->data; |
for(;;) { |
c = *str++; |
if (c == '%') { |
if (*str == '%') { |
str++; |
goto add_char; |
} |
modifier = 0; |
if (*str == 'c' || *str == 'n' || |
*str == 'b' || *str == 'w' || *str == 'h') |
modifier = *str++; |
index = find_constraint(operands, nb_operands, str, &str); |
if (index < 0) |
error("invalid operand reference after %%"); |
op = &operands[index]; |
sv = *op->vt; |
if (op->reg >= 0) { |
sv.r = op->reg; |
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) |
sv.r |= VT_LVAL; |
} |
subst_asm_operand(out_str, &sv, modifier); |
} else { |
add_char: |
cstr_ccat(out_str, c); |
if (c == '\0') |
break; |
} |
} |
} |
static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, |
int is_output) |
{ |
ASMOperand *op; |
int nb_operands; |
if (tok != ':') { |
nb_operands = *nb_operands_ptr; |
for(;;) { |
if (nb_operands >= MAX_ASM_OPERANDS) |
error("too many asm operands"); |
op = &operands[nb_operands++]; |
op->id = 0; |
if (tok == '[') { |
next(); |
if (tok < TOK_IDENT) |
expect("identifier"); |
op->id = tok; |
next(); |
skip(']'); |
} |
if (tok != TOK_STR) |
expect("string constant"); |
op->constraint = tcc_malloc(tokc.cstr->size); |
strcpy(op->constraint, tokc.cstr->data); |
next(); |
skip('('); |
gexpr(); |
if (is_output) { |
test_lvalue(); |
} else { |
/* we want to avoid LLOCAL case, except when the 'm' |
constraint is used. Note that it may come from |
register storage, so we need to convert (reg) |
case */ |
if ((vtop->r & VT_LVAL) && |
((vtop->r & VT_VALMASK) == VT_LLOCAL || |
(vtop->r & VT_VALMASK) < VT_CONST) && |
!strchr(op->constraint, 'm')) { |
gv(RC_INT); |
} |
} |
op->vt = vtop; |
skip(')'); |
if (tok == ',') { |
next(); |
} else { |
break; |
} |
} |
*nb_operands_ptr = nb_operands; |
} |
} |
static void parse_asm_str(CString *astr) |
{ |
skip('('); |
/* read the string */ |
if (tok != TOK_STR) |
expect("string constant"); |
cstr_new(astr); |
while (tok == TOK_STR) { |
/* XXX: add \0 handling too ? */ |
cstr_cat(astr, tokc.cstr->data); |
next(); |
} |
cstr_ccat(astr, '\0'); |
} |
/* parse the GCC asm() instruction */ |
static void asm_instr(void) |
{ |
CString astr, astr1; |
ASMOperand operands[MAX_ASM_OPERANDS]; |
int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg; |
uint8_t clobber_regs[NB_ASM_REGS]; |
next(); |
/* since we always generate the asm() instruction, we can ignore |
volatile */ |
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) { |
next(); |
} |
parse_asm_str(&astr); |
nb_operands = 0; |
nb_outputs = 0; |
must_subst = 0; |
memset(clobber_regs, 0, sizeof(clobber_regs)); |
if (tok == ':') { |
next(); |
must_subst = 1; |
/* output args */ |
parse_asm_operands(operands, &nb_operands, 1); |
nb_outputs = nb_operands; |
if (tok == ':') { |
next(); |
/* input args */ |
parse_asm_operands(operands, &nb_operands, 0); |
if (tok == ':') { |
/* clobber list */ |
/* XXX: handle registers */ |
next(); |
for(;;) { |
if (tok != TOK_STR) |
expect("string constant"); |
asm_clobber(clobber_regs, tokc.cstr->data); |
next(); |
if (tok == ',') { |
next(); |
} else { |
break; |
} |
} |
} |
} |
} |
skip(')'); |
/* NOTE: we do not eat the ';' so that we can restore the current |
token after the assembler parsing */ |
if (tok != ';') |
expect("';'"); |
nb_inputs = nb_operands - nb_outputs; |
/* save all values in the memory */ |
save_regs(0); |
/* compute constraints */ |
asm_compute_constraints(operands, nb_operands, nb_outputs, |
clobber_regs, &out_reg); |
/* substitute the operands in the asm string. No substitution is |
done if no operands (GCC behaviour) */ |
#ifdef ASM_DEBUG |
printf("asm: \"%s\"\n", (char *)astr.data); |
#endif |
if (must_subst) { |
subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr); |
cstr_free(&astr); |
} else { |
astr1 = astr; |
} |
#ifdef ASM_DEBUG |
printf("subst_asm: \"%s\"\n", (char *)astr1.data); |
#endif |
/* generate loads */ |
asm_gen_code(operands, nb_operands, nb_outputs, 0, |
clobber_regs, out_reg); |
/* assemble the string with tcc internal assembler */ |
tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1); |
/* restore the current C token */ |
next(); |
/* store the output values if needed */ |
asm_gen_code(operands, nb_operands, nb_outputs, 1, |
clobber_regs, out_reg); |
/* free everything */ |
for(i=0;i<nb_operands;i++) { |
ASMOperand *op; |
op = &operands[i]; |
tcc_free(op->constraint); |
vpop(); |
} |
cstr_free(&astr1); |
} |
static void asm_global_instr(void) |
{ |
CString astr; |
next(); |
parse_asm_str(&astr); |
skip(')'); |
/* NOTE: we do not eat the ';' so that we can restore the current |
token after the assembler parsing */ |
if (tok != ';') |
expect("';'"); |
#ifdef ASM_DEBUG |
printf("asm_global: \"%s\"\n", (char *)astr.data); |
#endif |
cur_text_section = text_section; |
ind = cur_text_section->data_offset; |
/* assemble the string with tcc internal assembler */ |
tcc_assemble_inline(tcc_state, astr.data, astr.size - 1); |
cur_text_section->data_offset = ind; |
/* restore the current C token */ |
next(); |
cstr_free(&astr); |
} |
/programs/develop/ktcc/trunk/source/tcccoff.c |
---|
0,0 → 1,955 |
/* |
* 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 "coff.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]; |
BOOL 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; |
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; |
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 (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 (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 (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 (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 (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) |
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) { |
char s[256]; |
sprintf(s, "debug info can't find function: %s", name); |
error(s); |
} |
// 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 (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) { |
char s[256]; |
sprintf(s, |
"debug (sort) info can't find function: %s", |
name2); |
error(s); |
} |
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) |
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 |
} |
BOOL OutputTheSection(Section * sect) |
{ |
const char *s = sect->name; |
if (!strcmp(s, ".text")) |
return true; |
else if (!strcmp(s, ".data")) |
return true; |
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; |
} |
error("could not find section %s", sname); |
return 0; |
} |
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) { |
error("Unable to open .out file for input"); |
} |
if (fread(&file_hdr, FILHSZ, 1, f) != 1) |
error("error reading .out file for input"); |
if (fread(&o_filehdr, sizeof(o_filehdr), 1, f) != 1) |
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)) |
error("error reading .out file for input"); |
if (fread(&str_size, sizeof(int), 1, f) != 1) |
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) |
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)) |
error("error reading .out file for input"); |
for (i = 0; i < file_hdr.f_nsyms; i++) { |
if (fread(&csym, SYMESZ, 1, f) != 1) |
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, csym.n_value); |
} |
// skip any aux records |
if (csym.n_numaux == 1) { |
if (fread(&csym, SYMESZ, 1, f) != 1) |
error("error reading .out file for input"); |
i++; |
} |
} |
return 0; |
} |
/programs/develop/ktcc/trunk/source/tcclib.h |
---|
0,0 → 1,77 |
/* 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); |
/* 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 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); |
/* 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 */ |
/programs/develop/ktcc/trunk/source/tcctok.h |
---|
0,0 → 1,425 |
/* keywords */ |
DEF(TOK_INT, "int") |
DEF(TOK_VOID, "void") |
DEF(TOK_CHAR, "char") |
DEF(TOK_IF, "if") |
DEF(TOK_ELSE, "else") |
DEF(TOK_WHILE, "while") |
DEF(TOK_BREAK, "break") |
DEF(TOK_RETURN, "return") |
DEF(TOK_FOR, "for") |
DEF(TOK_EXTERN, "extern") |
DEF(TOK_STATIC, "static") |
DEF(TOK_UNSIGNED, "unsigned") |
DEF(TOK_GOTO, "goto") |
DEF(TOK_DO, "do") |
DEF(TOK_CONTINUE, "continue") |
DEF(TOK_SWITCH, "switch") |
DEF(TOK_CASE, "case") |
DEF(TOK_CONST1, "const") |
DEF(TOK_CONST2, "__const") /* gcc keyword */ |
DEF(TOK_CONST3, "__const__") /* gcc keyword */ |
DEF(TOK_VOLATILE1, "volatile") |
DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */ |
DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */ |
DEF(TOK_LONG, "long") |
DEF(TOK_REGISTER, "register") |
DEF(TOK_SIGNED1, "signed") |
DEF(TOK_SIGNED2, "__signed") /* gcc keyword */ |
DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */ |
DEF(TOK_AUTO, "auto") |
DEF(TOK_INLINE1, "inline") |
DEF(TOK_INLINE2, "__inline") /* gcc keyword */ |
DEF(TOK_INLINE3, "__inline__") /* gcc keyword */ |
DEF(TOK_RESTRICT1, "restrict") |
DEF(TOK_RESTRICT2, "__restrict") |
DEF(TOK_RESTRICT3, "__restrict__") |
DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */ |
DEF(TOK_FLOAT, "float") |
DEF(TOK_DOUBLE, "double") |
DEF(TOK_BOOL, "_Bool") |
DEF(TOK_SHORT, "short") |
DEF(TOK_STRUCT, "struct") |
DEF(TOK_UNION, "union") |
DEF(TOK_TYPEDEF, "typedef") |
DEF(TOK_DEFAULT, "default") |
DEF(TOK_ENUM, "enum") |
DEF(TOK_SIZEOF, "sizeof") |
DEF(TOK_ATTRIBUTE1, "__attribute") |
DEF(TOK_ATTRIBUTE2, "__attribute__") |
DEF(TOK_ALIGNOF1, "__alignof") |
DEF(TOK_ALIGNOF2, "__alignof__") |
DEF(TOK_TYPEOF1, "typeof") |
DEF(TOK_TYPEOF2, "__typeof") |
DEF(TOK_TYPEOF3, "__typeof__") |
DEF(TOK_LABEL, "__label__") |
DEF(TOK_ASM1, "asm") |
DEF(TOK_ASM2, "__asm") |
DEF(TOK_ASM3, "__asm__") |
/*********************************************************************/ |
/* the following are not keywords. They are included to ease parsing */ |
/* preprocessor only */ |
DEF(TOK_DEFINE, "define") |
DEF(TOK_INCLUDE, "include") |
DEF(TOK_INCLUDE_NEXT, "include_next") |
DEF(TOK_IFDEF, "ifdef") |
DEF(TOK_IFNDEF, "ifndef") |
DEF(TOK_ELIF, "elif") |
DEF(TOK_ENDIF, "endif") |
DEF(TOK_DEFINED, "defined") |
DEF(TOK_UNDEF, "undef") |
DEF(TOK_ERROR, "error") |
DEF(TOK_WARNING, "warning") |
DEF(TOK_LINE, "line") |
DEF(TOK_PRAGMA, "pragma") |
DEF(TOK___LINE__, "__LINE__") |
DEF(TOK___FILE__, "__FILE__") |
DEF(TOK___DATE__, "__DATE__") |
DEF(TOK___TIME__, "__TIME__") |
DEF(TOK___FUNCTION__, "__FUNCTION__") |
DEF(TOK___VA_ARGS__, "__VA_ARGS__") |
/* special identifiers */ |
DEF(TOK___FUNC__, "__func__") |
/* attribute identifiers */ |
/* XXX: handle all tokens generically since speed is not critical */ |
DEF(TOK_SECTION1, "section") |
DEF(TOK_SECTION2, "__section__") |
DEF(TOK_ALIGNED1, "aligned") |
DEF(TOK_ALIGNED2, "__aligned__") |
DEF(TOK_PACKED1, "packed") |
DEF(TOK_PACKED2, "__packed__") |
DEF(TOK_UNUSED1, "unused") |
DEF(TOK_UNUSED2, "__unused__") |
DEF(TOK_CDECL1, "cdecl") |
DEF(TOK_CDECL2, "__cdecl") |
DEF(TOK_CDECL3, "__cdecl__") |
DEF(TOK_STDCALL1, "stdcall") |
DEF(TOK_STDCALL2, "__stdcall") |
DEF(TOK_STDCALL3, "__stdcall__") |
DEF(TOK_DLLEXPORT, "dllexport") |
DEF(TOK_NORETURN1, "noreturn") |
DEF(TOK_NORETURN2, "__noreturn__") |
DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p") |
DEF(TOK_builtin_constant_p, "__builtin_constant_p") |
DEF(TOK_REGPARM1, "regparm") |
DEF(TOK_REGPARM2, "__regparm__") |
/* pragma */ |
DEF(TOK_pack, "pack") |
#if !defined(TCC_TARGET_I386) |
/* already defined for assembler */ |
DEF(TOK_ASM_push, "push") |
DEF(TOK_ASM_pop, "pop") |
#endif |
/* builtin functions or variables */ |
DEF(TOK_memcpy, "memcpy") |
DEF(TOK_memset, "memset") |
DEF(TOK_alloca, "alloca") |
DEF(TOK___divdi3, "__divdi3") |
DEF(TOK___moddi3, "__moddi3") |
DEF(TOK___udivdi3, "__udivdi3") |
DEF(TOK___umoddi3, "__umoddi3") |
#if defined(TCC_TARGET_ARM) |
DEF(TOK___divsi3, "__divsi3") |
DEF(TOK___modsi3, "__modsi3") |
DEF(TOK___udivsi3, "__udivsi3") |
DEF(TOK___umodsi3, "__umodsi3") |
DEF(TOK___sardi3, "__ashrdi3") |
DEF(TOK___shrdi3, "__lshrdi3") |
DEF(TOK___shldi3, "__ashldi3") |
DEF(TOK___slltold, "__slltold") |
DEF(TOK___fixunssfsi, "__fixunssfsi") |
DEF(TOK___fixunsdfsi, "__fixunsdfsi") |
DEF(TOK___fixunsxfsi, "__fixunsxfsi") |
DEF(TOK___fixsfdi, "__fixsfdi") |
DEF(TOK___fixdfdi, "__fixdfdi") |
DEF(TOK___fixxfdi, "__fixxfdi") |
#elif defined(TCC_TARGET_C67) |
DEF(TOK__divi, "_divi") |
DEF(TOK__divu, "_divu") |
DEF(TOK__divf, "_divf") |
DEF(TOK__divd, "_divd") |
DEF(TOK__remi, "_remi") |
DEF(TOK__remu, "_remu") |
DEF(TOK___sardi3, "__sardi3") |
DEF(TOK___shrdi3, "__shrdi3") |
DEF(TOK___shldi3, "__shldi3") |
#else |
/* XXX: same names on i386 ? */ |
DEF(TOK___sardi3, "__sardi3") |
DEF(TOK___shrdi3, "__shrdi3") |
DEF(TOK___shldi3, "__shldi3") |
#endif |
DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control") |
DEF(TOK___tcc_fpu_control, "__tcc_fpu_control") |
DEF(TOK___ulltof, "__ulltof") |
DEF(TOK___ulltod, "__ulltod") |
DEF(TOK___ulltold, "__ulltold") |
DEF(TOK___fixunssfdi, "__fixunssfdi") |
DEF(TOK___fixunsdfdi, "__fixunsdfdi") |
DEF(TOK___fixunsxfdi, "__fixunsxfdi") |
DEF(TOK___chkstk, "__chkstk") |
/* bound checking symbols */ |
#ifdef CONFIG_TCC_BCHECK |
DEF(TOK___bound_ptr_add, "__bound_ptr_add") |
DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1") |
DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2") |
DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4") |
DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8") |
DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12") |
DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16") |
DEF(TOK___bound_local_new, "__bound_local_new") |
DEF(TOK___bound_local_delete, "__bound_local_delete") |
DEF(TOK_malloc, "malloc") |
DEF(TOK_free, "free") |
DEF(TOK_realloc, "realloc") |
DEF(TOK_memalign, "memalign") |
DEF(TOK_calloc, "calloc") |
DEF(TOK_memmove, "memmove") |
DEF(TOK_strlen, "strlen") |
DEF(TOK_strcpy, "strcpy") |
#endif |
/* Tiny Assembler */ |
DEF_ASM(byte) |
DEF_ASM(align) |
DEF_ASM(skip) |
DEF_ASM(space) |
DEF_ASM(string) |
DEF_ASM(asciz) |
DEF_ASM(ascii) |
DEF_ASM(globl) |
DEF_ASM(global) |
DEF_ASM(text) |
DEF_ASM(data) |
DEF_ASM(bss) |
DEF_ASM(previous) |
DEF_ASM(fill) |
DEF_ASM(org) |
DEF_ASM(quad) |
#ifdef TCC_TARGET_I386 |
/* WARNING: relative order of tokens is important. */ |
DEF_ASM(al) |
DEF_ASM(cl) |
DEF_ASM(dl) |
DEF_ASM(bl) |
DEF_ASM(ah) |
DEF_ASM(ch) |
DEF_ASM(dh) |
DEF_ASM(bh) |
DEF_ASM(ax) |
DEF_ASM(cx) |
DEF_ASM(dx) |
DEF_ASM(bx) |
DEF_ASM(sp) |
DEF_ASM(bp) |
DEF_ASM(si) |
DEF_ASM(di) |
DEF_ASM(eax) |
DEF_ASM(ecx) |
DEF_ASM(edx) |
DEF_ASM(ebx) |
DEF_ASM(esp) |
DEF_ASM(ebp) |
DEF_ASM(esi) |
DEF_ASM(edi) |
DEF_ASM(mm0) |
DEF_ASM(mm1) |
DEF_ASM(mm2) |
DEF_ASM(mm3) |
DEF_ASM(mm4) |
DEF_ASM(mm5) |
DEF_ASM(mm6) |
DEF_ASM(mm7) |
DEF_ASM(xmm0) |
DEF_ASM(xmm1) |
DEF_ASM(xmm2) |
DEF_ASM(xmm3) |
DEF_ASM(xmm4) |
DEF_ASM(xmm5) |
DEF_ASM(xmm6) |
DEF_ASM(xmm7) |
DEF_ASM(cr0) |
DEF_ASM(cr1) |
DEF_ASM(cr2) |
DEF_ASM(cr3) |
DEF_ASM(cr4) |
DEF_ASM(cr5) |
DEF_ASM(cr6) |
DEF_ASM(cr7) |
DEF_ASM(tr0) |
DEF_ASM(tr1) |
DEF_ASM(tr2) |
DEF_ASM(tr3) |
DEF_ASM(tr4) |
DEF_ASM(tr5) |
DEF_ASM(tr6) |
DEF_ASM(tr7) |
DEF_ASM(db0) |
DEF_ASM(db1) |
DEF_ASM(db2) |
DEF_ASM(db3) |
DEF_ASM(db4) |
DEF_ASM(db5) |
DEF_ASM(db6) |
DEF_ASM(db7) |
DEF_ASM(dr0) |
DEF_ASM(dr1) |
DEF_ASM(dr2) |
DEF_ASM(dr3) |
DEF_ASM(dr4) |
DEF_ASM(dr5) |
DEF_ASM(dr6) |
DEF_ASM(dr7) |
DEF_ASM(es) |
DEF_ASM(cs) |
DEF_ASM(ss) |
DEF_ASM(ds) |
DEF_ASM(fs) |
DEF_ASM(gs) |
DEF_ASM(st) |
DEF_BWL(mov) |
/* generic two operands */ |
DEF_BWL(add) |
DEF_BWL(or) |
DEF_BWL(adc) |
DEF_BWL(sbb) |
DEF_BWL(and) |
DEF_BWL(sub) |
DEF_BWL(xor) |
DEF_BWL(cmp) |
/* unary ops */ |
DEF_BWL(inc) |
DEF_BWL(dec) |
DEF_BWL(not) |
DEF_BWL(neg) |
DEF_BWL(mul) |
DEF_BWL(imul) |
DEF_BWL(div) |
DEF_BWL(idiv) |
DEF_BWL(xchg) |
DEF_BWL(test) |
/* shifts */ |
DEF_BWL(rol) |
DEF_BWL(ror) |
DEF_BWL(rcl) |
DEF_BWL(rcr) |
DEF_BWL(shl) |
DEF_BWL(shr) |
DEF_BWL(sar) |
DEF_ASM(shldw) |
DEF_ASM(shldl) |
DEF_ASM(shld) |
DEF_ASM(shrdw) |
DEF_ASM(shrdl) |
DEF_ASM(shrd) |
DEF_ASM(pushw) |
DEF_ASM(pushl) |
DEF_ASM(push) |
DEF_ASM(popw) |
DEF_ASM(popl) |
DEF_ASM(pop) |
DEF_BWL(in) |
DEF_BWL(out) |
DEF_WL(movzb) |
DEF_ASM(movzwl) |
DEF_ASM(movsbw) |
DEF_ASM(movsbl) |
DEF_ASM(movswl) |
DEF_WL(lea) |
DEF_ASM(les) |
DEF_ASM(lds) |
DEF_ASM(lss) |
DEF_ASM(lfs) |
DEF_ASM(lgs) |
DEF_ASM(call) |
DEF_ASM(jmp) |
DEF_ASM(lcall) |
DEF_ASM(ljmp) |
DEF_ASMTEST(j) |
DEF_ASMTEST(set) |
DEF_ASMTEST(cmov) |
DEF_WL(bsf) |
DEF_WL(bsr) |
DEF_WL(bt) |
DEF_WL(bts) |
DEF_WL(btr) |
DEF_WL(btc) |
DEF_WL(lsl) |
/* generic FP ops */ |
DEF_FP(add) |
DEF_FP(mul) |
DEF_ASM(fcom) |
DEF_ASM(fcom_1) /* non existant op, just to have a regular table */ |
DEF_FP1(com) |
DEF_FP(comp) |
DEF_FP(sub) |
DEF_FP(subr) |
DEF_FP(div) |
DEF_FP(divr) |
DEF_BWL(xadd) |
DEF_BWL(cmpxchg) |
/* string ops */ |
DEF_BWL(cmps) |
DEF_BWL(scmp) |
DEF_BWL(ins) |
DEF_BWL(outs) |
DEF_BWL(lods) |
DEF_BWL(slod) |
DEF_BWL(movs) |
DEF_BWL(smov) |
DEF_BWL(scas) |
DEF_BWL(ssca) |
DEF_BWL(stos) |
DEF_BWL(ssto) |
/* generic asm ops */ |
#define ALT(x) |
#define DEF_ASM_OP0(name, opcode) DEF_ASM(name) |
#define DEF_ASM_OP0L(name, opcode, group, instr_type) |
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) |
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) |
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) |
#include "i386-asm.h" |
#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) |
#include "i386-asm.h" |
#endif |
/programs/develop/ktcc/trunk/source/tiny_impdef.c |
---|
0,0 → 1,372 |
/* -------------------------------------------------------------- */ |
/* |
"tiny_impdef creates a .def file from a dll" |
"Usage: tiny_impdef [-p] <library.dll> [-o outputfile]" |
"Options:" |
" -p print to stdout" |
*/ |
#include <windows.h> |
#include <stdio.h> |
/* Offset to PE file signature */ |
#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + \ |
((PIMAGE_DOS_HEADER)a)->e_lfanew)) |
/* MS-OS header identifies the NT PEFile signature dword; |
the PEFILE header exists just after that dword. */ |
#define PEFHDROFFSET(a) ((LPVOID)((BYTE *)a + \ |
((PIMAGE_DOS_HEADER)a)->e_lfanew + \ |
SIZE_OF_NT_SIGNATURE)) |
/* PE optional header is immediately after PEFile header. */ |
#define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \ |
((PIMAGE_DOS_HEADER)a)->e_lfanew + \ |
SIZE_OF_NT_SIGNATURE + \ |
sizeof (IMAGE_FILE_HEADER))) |
/* Section headers are immediately after PE optional header. */ |
#define SECHDROFFSET(a) ((LPVOID)((BYTE *)a + \ |
((PIMAGE_DOS_HEADER)a)->e_lfanew + \ |
SIZE_OF_NT_SIGNATURE + \ |
sizeof (IMAGE_FILE_HEADER) + \ |
sizeof (IMAGE_OPTIONAL_HEADER))) |
#define SIZE_OF_NT_SIGNATURE 4 |
/* -------------------------------------------------------------- */ |
int WINAPI NumOfSections ( |
LPVOID lpFile) |
{ |
/* Number of sections is indicated in file header. */ |
return (int) |
((PIMAGE_FILE_HEADER) |
PEFHDROFFSET(lpFile))->NumberOfSections; |
} |
/* -------------------------------------------------------------- */ |
LPVOID WINAPI ImageDirectoryOffset ( |
LPVOID lpFile, |
DWORD dwIMAGE_DIRECTORY) |
{ |
PIMAGE_OPTIONAL_HEADER poh; |
PIMAGE_SECTION_HEADER psh; |
int nSections = NumOfSections (lpFile); |
int i = 0; |
LPVOID VAImageDir; |
/* Retrieve offsets to optional and section headers. */ |
poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET (lpFile); |
psh = (PIMAGE_SECTION_HEADER)SECHDROFFSET (lpFile); |
/* Must be 0 thru (NumberOfRvaAndSizes-1). */ |
if (dwIMAGE_DIRECTORY >= poh->NumberOfRvaAndSizes) |
return NULL; |
/* Locate image directory's relative virtual address. */ |
VAImageDir = (LPVOID)poh->DataDirectory |
[dwIMAGE_DIRECTORY].VirtualAddress; |
/* Locate section containing image directory. */ |
while (i++<nSections) |
{ |
if (psh->VirtualAddress <= (DWORD)VAImageDir |
&& psh->VirtualAddress + psh->SizeOfRawData > (DWORD)VAImageDir) |
break; |
psh++; |
} |
if (i > nSections) |
return NULL; |
/* Return image import directory offset. */ |
return (LPVOID)(((int)lpFile + |
(int)VAImageDir - psh->VirtualAddress) + |
(int)psh->PointerToRawData); |
} |
/* -------------------------------------------------------------- */ |
BOOL WINAPI GetSectionHdrByName ( |
LPVOID lpFile, |
IMAGE_SECTION_HEADER *sh, |
char *szSection) |
{ |
PIMAGE_SECTION_HEADER psh; |
int nSections = NumOfSections (lpFile); |
int i; |
if ((psh = (PIMAGE_SECTION_HEADER)SECHDROFFSET (lpFile)) != |
NULL) |
{ |
/* find the section by name */ |
for (i=0; i<nSections; i++) |
{ |
if (!strcmp (psh->Name, szSection)) |
{ |
/* copy data to header */ |
memcpy ((LPVOID)sh, |
(LPVOID)psh, |
sizeof (IMAGE_SECTION_HEADER)); |
return TRUE; |
} |
else |
psh++; |
} |
} |
return FALSE; |
} |
/* -------------------------------------------------------------- */ |
BOOL WINAPI GetSectionHdrByAddress ( |
LPVOID lpFile, |
IMAGE_SECTION_HEADER *sh, |
DWORD addr) |
{ |
PIMAGE_SECTION_HEADER psh; |
int nSections = NumOfSections (lpFile); |
int i; |
if ((psh = (PIMAGE_SECTION_HEADER)SECHDROFFSET (lpFile)) != |
NULL) |
{ |
/* find the section by name */ |
for (i=0; i<nSections; i++) |
{ |
if (addr >= psh->VirtualAddress && addr < psh->VirtualAddress + psh->SizeOfRawData) |
{ |
/* copy data to header */ |
memcpy ((LPVOID)sh, |
(LPVOID)psh, |
sizeof (IMAGE_SECTION_HEADER)); |
return TRUE; |
} |
else |
psh++; |
} |
} |
return FALSE; |
} |
/* -------------------------------------------------------------- */ |
int WINAPI GetExportFunctionNames ( |
LPVOID lpFile, |
HANDLE hHeap, |
char **pszFunctions) |
{ |
IMAGE_SECTION_HEADER sh; |
PIMAGE_EXPORT_DIRECTORY ped; |
int *pNames, *pCnt; |
char *pSrc, *pDest; |
int i, nCnt; |
DWORD VAImageDir; |
PIMAGE_OPTIONAL_HEADER poh; |
char *pOffset; |
/* Get section header and pointer to data directory |
for .edata section. */ |
if ((ped = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryOffset |
(lpFile, IMAGE_DIRECTORY_ENTRY_EXPORT)) == NULL) |
return 0; |
poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET (lpFile); |
VAImageDir = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; |
if (FALSE == GetSectionHdrByAddress (lpFile, &sh, VAImageDir)) return 0; |
pOffset = (char *)lpFile + (sh.PointerToRawData - sh.VirtualAddress); |
pNames = (int *)(pOffset + (DWORD)ped->AddressOfNames); |
/* Figure out how much memory to allocate for all strings. */ |
nCnt = 1; |
for (i=0, pCnt = pNames; i<(int)ped->NumberOfNames; i++) |
{ |
pSrc = (pOffset + *pCnt++); |
if (pSrc) nCnt += strlen(pSrc)+1; |
} |
/* Allocate memory off heap for function names. */ |
pDest = *pszFunctions = HeapAlloc (hHeap, HEAP_ZERO_MEMORY, nCnt); |
/* Copy all strings to buffer. */ |
for (i=0, pCnt = pNames; i<(int)ped->NumberOfNames; i++) |
{ |
pSrc = (pOffset + *pCnt++); |
if (pSrc) { strcpy(pDest, pSrc); pDest += strlen(pSrc)+1; } |
} |
*pDest = 0; |
return ped->NumberOfNames; |
} |
/* -------------------------------------------------------------- */ |
int main(int argc, char **argv) |
{ |
HANDLE hHeap; HANDLE hFile; HANDLE hMapObject; VOID *pMem; |
int nCnt, ret, argind, std; |
char *pNames; |
char infile[MAX_PATH]; |
char buffer[MAX_PATH]; |
char outfile[MAX_PATH]; |
char libname[80]; |
hHeap = NULL; |
hFile = NULL; |
hMapObject = NULL; |
pMem = NULL; |
infile[0] = 0; |
outfile[0] = 0; |
ret = 0; |
std = 0; |
for (argind = 1; argind < argc; ++argind) |
{ |
const char *a = argv[argind]; |
if ('-' == a[0]) |
{ |
if (0 == strcmp(a, "-p")) |
std = 1; |
else |
if (0 == strcmp(a, "-o")) |
{ |
if (++argind == argc) goto usage; |
strcpy(outfile, argv[argind]); |
} |
else |
goto usage; |
} |
else |
if (0 == infile[0]) |
strcpy(infile, a); |
else |
goto usage; |
} |
if (0 == infile[0]) |
{ |
usage: |
fprintf(stderr, |
"tiny_impdef creates a .def file from a dll\n" |
"Usage: tiny_impdef [-p] <library.dll> [-o outputfile]\n" |
"Options:\n" |
" -p print to stdout\n" |
); |
error: |
ret = 1; |
goto the_end; |
} |
if (SearchPath(NULL, infile, ".dll", sizeof buffer, buffer, NULL)) |
strcpy(infile, buffer); |
if (0 == outfile[0]) |
{ |
char *p; |
p = strrchr(strcpy(outfile, infile), '\\'); |
if (NULL == p) |
p = strrchr(outfile, '/'); |
if (p) strcpy(outfile, p+1); |
p = strrchr(outfile, '.'); |
if (NULL == p) p = strchr(outfile, 0); |
strcpy(p, ".def"); |
} |
hFile=CreateFile( |
infile, |
GENERIC_READ, |
FILE_SHARE_READ, |
NULL, |
OPEN_EXISTING, |
0, |
NULL |
); |
if (hFile == INVALID_HANDLE_VALUE) |
{ |
fprintf(stderr, "file not found: %s\n", infile); |
goto error; |
} |
if (!std) printf("--> %s\n", infile); |
hMapObject = CreateFileMapping( |
hFile, |
NULL, |
PAGE_READONLY, |
0, 0, |
NULL |
); |
if (NULL == hMapObject) |
{ |
fprintf(stderr, "could not create file mapping.\n"); |
goto error; |
} |
pMem = MapViewOfFile( |
hMapObject, // object to map view of |
FILE_MAP_READ, // read access |
0, // high offset: map from |
0, // low offset: beginning |
0); // default: map entire file |
if (NULL == pMem) |
{ |
fprintf(stderr, "could not map view of file.\n"); |
goto error; |
} |
hHeap = GetProcessHeap(); |
nCnt = GetExportFunctionNames(pMem, hHeap, &pNames); |
{ |
FILE *op; char *p; int n; |
if (!std) printf("<-- %s\n", outfile); |
if (std) |
op = stdout; |
else |
op = fopen(outfile, "wt"); |
if (NULL == op) |
{ |
fprintf(stderr, "could not create file: %s\n", outfile); |
goto error; |
} |
p = strrchr(infile, '\\'); |
if (NULL == p) |
p = strrchr(infile, '/'); |
if (NULL == p) p = infile; else ++p; |
fprintf(op, "LIBRARY %s\n\nEXPORTS", p); |
if (std) fprintf(op, " (%d)", nCnt); |
fprintf(op, "\n"); |
for (n = 0, p = pNames; n < nCnt; ++n) |
{ |
fprintf(op, "%s\n", p); |
while (*p++); |
} |
if (!std) fclose(op); |
} |
the_end: |
if (pMem) UnmapViewOfFile(pMem); |
if (hMapObject) CloseHandle(hMapObject); |
if (hFile) CloseHandle(hFile); |
return ret; |
} |
/* -------------------------------------------------------------- */ |
/programs/develop/ktcc/trunk/source/varargs.h |
---|
0,0 → 1,11 |
#ifndef _VARARGS_H |
#define _VARARGS_H |
#include <stdarg.h> |
#define va_dcl |
#define va_alist __va_alist |
#undef va_start |
#define va_start(ap) ap = __builtin_varargs_start |
#endif |
/programs/develop/ktcc/trunk/readme.txt |
---|
0,0 → 1,12 |
The main file of metcc is "tcc.c". It certainly can be compiled by MinGW Studio. |
In order to compile MenuetOS program you must have start.o, metcc.exe in the same |
directory. The command line should be of type "metcc.exe program.c melibc.a -oprogram". |
In order to compile "melibc.a" you should configure paths is compile.js and run it. |
------------------------------------------------------------------------------------ |
Äëÿ êîìïèëÿöèè melibc íåîáõîäèìî çàïóñòèòü ñêðèïò libc/make.cmd |
ïî óìîë÷àíèþ ñ÷èòàåòñÿ ÷òî â ïåðåìåííîé îêðóæåíèÿ PATH ó âàñ óêàçàí ïóòü ê ïàêåòó mingw32 |
è ê àññåìáëåðó fasm. |
------------------------------------------------------------------------------------ |
Äëÿ áîëåå ïîäðîáíûõ èíñòðóêöèé îáðàùàòåñü íà ôîðóì â òåìó |
http://meos.sysbin.com/viewtopic.php?t=565&highlight=metcc |
For more help go to link above |
/programs/develop/ktcc/trunk |
---|
Property changes: |
Added: svn:ignore |
+debug |
+release |
+*.msp |
+*.vcproj |
+*.sln |
+*.dsw |
+*.dsp |