tinycc/libtcc.c
2019-12-16 21:37:44 +01:00

2092 lines
60 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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
*/
#if !defined ONE_SOURCE || ONE_SOURCE
#include "tccpp.c"
#include "tccgen.c"
#include "tccelf.c"
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#include "i386-link.c"
#include "i386-asm.c"
#elif defined(TCC_TARGET_ARM)
#include "arm-gen.c"
#include "arm-link.c"
#include "arm-asm.c"
#elif defined(TCC_TARGET_ARM64)
#include "arm64-gen.c"
#include "arm64-link.c"
#elif defined(TCC_TARGET_C67)
#include "c67-gen.c"
#include "c67-link.c"
#include "tcccoff.c"
#elif defined(TCC_TARGET_X86_64)
#include "x86_64-gen.c"
#include "x86_64-link.c"
#include "i386-asm.c"
#elif defined(TCC_TARGET_RISCV64)
#include "riscv64-gen.c"
#include "riscv64-link.c"
#else
#error unknown target
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
#endif
#ifdef TCC_TARGET_PE
#include "tccpe.c"
#endif
#endif /* ONE_SOURCE */
#include "tcc.h"
/********************************************************/
/* global variables */
/* XXX: get rid of this ASAP (or maybe not) */
ST_DATA struct TCCState *tcc_state;
#ifdef MEM_DEBUG
static int nb_states;
#endif
/********************************************************/
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path)
{
char *p;
for (p = path; *p; ++p)
if (*p == '\\')
*p = '/';
return path;
}
static HMODULE tcc_module;
/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static void tcc_set_lib_path_w32(TCCState *s)
{
char path[1024], *p;
GetModuleFileNameA(tcc_module, path, sizeof path);
p = tcc_basename(normalize_slashes(strlwr(path)));
if (p > path)
--p;
*p = 0;
tcc_set_lib_path(s, path);
}
#ifdef TCC_TARGET_PE
static void tcc_add_systemdir(TCCState *s)
{
char buf[1000];
GetSystemDirectory(buf, sizeof buf);
tcc_add_library_path(s, normalize_slashes(buf));
}
#endif
#ifdef LIBTCC_AS_DLL
BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
if (DLL_PROCESS_ATTACH == dwReason)
tcc_module = hDll;
return TRUE;
}
#endif
#endif
/********************************************************/
#ifndef CONFIG_TCC_SEMLOCK
#define WAIT_SEM()
#define POST_SEM()
#elif defined _WIN32
static int tcc_sem_init;
static CRITICAL_SECTION tcc_cr;
static void wait_sem(void)
{
if (!tcc_sem_init)
InitializeCriticalSection(&tcc_cr), tcc_sem_init = 1;
EnterCriticalSection(&tcc_cr);
}
#define WAIT_SEM() wait_sem()
#define POST_SEM() LeaveCriticalSection(&tcc_cr);
#else
#include <semaphore.h>
static int tcc_sem_init;
static sem_t tcc_sem;
static void wait_sem(void)
{
if (!tcc_sem_init)
sem_init(&tcc_sem, 0, 1), tcc_sem_init = 1;
while (sem_wait (&tcc_sem) < 0 && errno == EINTR);
}
#define WAIT_SEM() wait_sem()
#define POST_SEM() sem_post(&tcc_sem)
#endif
/********************************************************/
/* copy a string and truncate it. */
ST_FUNC char *pstrcpy(char *buf, size_t 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. */
ST_FUNC char *pstrcat(char *buf, size_t buf_size, const char *s)
{
size_t len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
ST_FUNC char *pstrncpy(char *out, const char *in, size_t num)
{
memcpy(out, in, num);
out[num] = '\0';
return out;
}
/* extract the basename of a file */
PUB_FUNC char *tcc_basename(const char *name)
{
char *p = strchr(name, 0);
while (p > name && !IS_DIRSEP(p[-1]))
--p;
return p;
}
/* extract extension part of a file
*
* (if no extension, return pointer to end-of-string)
*/
PUB_FUNC char *tcc_fileextension (const char *name)
{
char *b = tcc_basename(name);
char *e = strrchr(b, '.');
return e ? e : strchr(b, 0);
}
/********************************************************/
/* memory management */
#undef free
#undef malloc
#undef realloc
#ifndef MEM_DEBUG
PUB_FUNC void tcc_free(void *ptr)
{
free(ptr);
}
PUB_FUNC void *tcc_malloc(unsigned long size)
{
void *ptr;
ptr = malloc(size);
if (!ptr && size)
_tcc_error("memory full (malloc)");
return ptr;
}
PUB_FUNC void *tcc_mallocz(unsigned long size)
{
void *ptr;
ptr = tcc_malloc(size);
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
void *ptr1;
ptr1 = realloc(ptr, size);
if (!ptr1 && size)
_tcc_error("memory full (realloc)");
return ptr1;
}
PUB_FUNC char *tcc_strdup(const char *str)
{
char *ptr;
ptr = tcc_malloc(strlen(str) + 1);
strcpy(ptr, str);
return ptr;
}
#else
#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
#define MEM_DEBUG_MAGIC3 0xFEEDDEB3
#define MEM_DEBUG_FILE_LEN 40
#define MEM_DEBUG_CHECK3(header) \
((mem_debug_header_t*)((char*)header + header->size))->magic3
#define MEM_USER_PTR(header) \
((char *)header + offsetof(mem_debug_header_t, magic3))
#define MEM_HEADER_PTR(ptr) \
(mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3))
struct mem_debug_header {
unsigned magic1;
unsigned size;
struct mem_debug_header *prev;
struct mem_debug_header *next;
int line_num;
char file_name[MEM_DEBUG_FILE_LEN + 1];
unsigned magic2;
ALIGNED(16) unsigned magic3;
};
typedef struct mem_debug_header mem_debug_header_t;
static mem_debug_header_t *mem_debug_chain;
static unsigned mem_cur_size;
static unsigned mem_max_size;
static mem_debug_header_t *malloc_check(void *ptr, const char *msg)
{
mem_debug_header_t * header = MEM_HEADER_PTR(ptr);
if (header->magic1 != MEM_DEBUG_MAGIC1 ||
header->magic2 != MEM_DEBUG_MAGIC2 ||
MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 ||
header->size == (unsigned)-1) {
fprintf(stderr, "%s check failed\n", msg);
if (header->magic1 == MEM_DEBUG_MAGIC1)
fprintf(stderr, "%s:%u: block allocated here.\n",
header->file_name, header->line_num);
exit(1);
}
return header;
}
PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
{
int ofs;
mem_debug_header_t *header;
header = malloc(sizeof(mem_debug_header_t) + size);
if (!header)
_tcc_error("memory full (malloc)");
header->magic1 = MEM_DEBUG_MAGIC1;
header->magic2 = MEM_DEBUG_MAGIC2;
header->size = size;
MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
header->line_num = line;
ofs = strlen(file) - MEM_DEBUG_FILE_LEN;
strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN);
header->file_name[MEM_DEBUG_FILE_LEN] = 0;
header->next = mem_debug_chain;
header->prev = NULL;
if (header->next)
header->next->prev = header;
mem_debug_chain = header;
mem_cur_size += size;
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
return MEM_USER_PTR(header);
}
PUB_FUNC void tcc_free_debug(void *ptr)
{
mem_debug_header_t *header;
if (!ptr)
return;
header = malloc_check(ptr, "tcc_free");
mem_cur_size -= header->size;
header->size = (unsigned)-1;
if (header->next)
header->next->prev = header->prev;
if (header->prev)
header->prev->next = header->next;
if (header == mem_debug_chain)
mem_debug_chain = header->next;
free(header);
}
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
{
void *ptr;
ptr = tcc_malloc_debug(size,file,line);
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line)
{
mem_debug_header_t *header;
int mem_debug_chain_update = 0;
if (!ptr)
return tcc_malloc_debug(size, file, line);
header = malloc_check(ptr, "tcc_realloc");
mem_cur_size -= header->size;
mem_debug_chain_update = (header == mem_debug_chain);
header = realloc(header, sizeof(mem_debug_header_t) + size);
if (!header)
_tcc_error("memory full (realloc)");
header->size = size;
MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
if (header->next)
header->next->prev = header;
if (header->prev)
header->prev->next = header;
if (mem_debug_chain_update)
mem_debug_chain = header;
mem_cur_size += size;
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
return MEM_USER_PTR(header);
}
PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
{
char *ptr;
ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
strcpy(ptr, str);
return ptr;
}
PUB_FUNC void tcc_memcheck(void)
{
if (mem_cur_size) {
mem_debug_header_t *header = mem_debug_chain;
fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
mem_cur_size, mem_max_size);
while (header) {
fprintf(stderr, "%s:%u: error: %u bytes leaked\n",
header->file_name, header->line_num, header->size);
header = header->next;
}
#if MEM_DEBUG-0 == 2
exit(2);
#endif
}
}
#endif /* MEM_DEBUG */
#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
/********************************************************/
/* dynarrays */
ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data)
{
int nb, nb_alloc;
void **pp;
nb = *nb_ptr;
pp = *(void ***)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 *));
*(void***)ptab = pp;
}
pp[nb++] = data;
*nb_ptr = nb;
}
ST_FUNC void dynarray_reset(void *pp, int *n)
{
void **p;
for (p = *(void***)pp; *n; ++p, --*n)
if (*p)
tcc_free(*p);
tcc_free(*(void**)pp);
*(void**)pp = NULL;
}
static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in)
{
const char *p;
do {
int c;
CString str;
cstr_new(&str);
for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; ++p) {
if (c == '{' && p[1] && p[2] == '}') {
c = p[1], p += 2;
if (c == 'B')
cstr_cat(&str, s->tcc_lib_path, -1);
} else {
cstr_ccat(&str, c);
}
}
if (str.size) {
cstr_ccat(&str, '\0');
dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data));
}
cstr_free(&str);
in = p+1;
} while (*p);
}
/********************************************************/
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);
}
#define ERROR_WARN 0
#define ERROR_NOABORT 1
#define ERROR_ERROR 2
PUB_FUNC void tcc_enter_state(TCCState *s1)
{
WAIT_SEM();
tcc_state = s1;
}
static void error1(int mode, const char *fmt, va_list ap)
{
char buf[2048];
BufferedFile **pf, *f;
TCCState *s1 = tcc_state;
/* 's1->error_set_jmp_enabled' means that we're called from
within the parser/generator and 'tcc_state' was already
set (i.e. not by the function above).
Otherwise, 's1 = NULL' means we're called because of severe
problems from tcc_malloc() which under normal conditions
should never happen. */
if (s1 && !s1->error_set_jmp_enabled) {
tcc_state = NULL;
POST_SEM();
}
if (mode == ERROR_WARN) {
if (s1->warn_none)
return;
if (s1->warn_error)
mode = ERROR_ERROR;
}
buf[0] = '\0';
/* use upper file if inline ":asm:" or token ":paste:" */
for (f = file; f && f->filename[0] == ':'; f = f->prev)
;
if (f) {
for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
(*pf)->filename, (*pf)->line_num);
if (s1->error_set_jmp_enabled) {
strcat_printf(buf, sizeof(buf), "%s:%d: ",
f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL));
} else {
strcat_printf(buf, sizeof(buf), "%s: ",
f->filename);
}
} else {
strcat_printf(buf, sizeof(buf), "tcc: ");
}
if (mode == ERROR_WARN)
strcat_printf(buf, sizeof(buf), "warning: ");
else
strcat_printf(buf, sizeof(buf), "error: ");
strcat_vprintf(buf, sizeof(buf), fmt, ap);
if (!s1 || !s1->error_func) {
/* default case: stderr */
if (s1 && s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout)
/* print a newline during tcc -E */
printf("\n"), fflush(stdout);
fflush(stdout); /* flush -v output */
fprintf(stderr, "%s\n", buf);
fflush(stderr); /* print error/warning now (win32) */
} else {
s1->error_func(s1->error_opaque, buf);
}
if (s1) {
if (mode != ERROR_WARN)
s1->nb_errors++;
if (mode != ERROR_ERROR)
return;
if (s1->error_set_jmp_enabled)
longjmp(s1->error_jmp_buf, 1);
}
exit(1);
}
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc error_func)
{
s->error_opaque = error_opaque;
s->error_func = error_func;
}
LIBTCCAPI TCCErrorFunc tcc_get_error_func(TCCState *s)
{
return s->error_func;
}
LIBTCCAPI void *tcc_get_error_opaque(TCCState *s)
{
return s->error_opaque;
}
/* error without aborting current compilation */
PUB_FUNC void _tcc_error_noabort(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error1(ERROR_NOABORT, fmt, ap);
va_end(ap);
}
PUB_FUNC void _tcc_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
for (;;) error1(ERROR_ERROR, fmt, ap);
}
PUB_FUNC void _tcc_warning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error1(ERROR_WARN, fmt, ap);
va_end(ap);
}
/********************************************************/
/* I/O layer */
ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
BufferedFile *bf;
int buflen = initlen ? initlen : IO_BUF_SIZE;
bf = tcc_mallocz(sizeof(BufferedFile) + buflen);
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer + initlen;
bf->buf_end[0] = CH_EOB; /* put eob symbol */
pstrcpy(bf->filename, sizeof(bf->filename), filename);
#ifdef _WIN32
normalize_slashes(bf->filename);
#endif
bf->true_filename = bf->filename;
bf->line_num = 1;
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
bf->fd = -1;
bf->prev = file;
file = bf;
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
}
ST_FUNC void tcc_close(void)
{
TCCState *s1 = tcc_state;
BufferedFile *bf = file;
if (bf->fd > 0) {
close(bf->fd);
total_lines += bf->line_num;
}
if (bf->true_filename != bf->filename)
tcc_free(bf->true_filename);
file = bf->prev;
tcc_free(bf);
}
static int _tcc_open(TCCState *s1, const char *filename)
{
int fd;
if (strcmp(filename, "-") == 0)
fd = 0, filename = "<stdin>";
else
fd = open(filename, O_RDONLY | O_BINARY);
if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
printf("%s %*s%s\n", fd < 0 ? "nf":"->",
(int)(s1->include_stack_ptr - s1->include_stack), "", filename);
return fd;
}
ST_FUNC int tcc_open(TCCState *s1, const char *filename)
{
int fd = _tcc_open(s1, filename);
if (fd < 0)
return -1;
tcc_open_bf(s1, filename, 0);
file->fd = fd;
return 0;
}
/* compile the file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
{
/* Here we enter the code section where we use the global variables for
parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c).
Other threads need to wait until we're done.
Alternatively we could use thread local storage for those global
variables, which may or may not have advantages */
WAIT_SEM();
tcc_state = s1;
if (setjmp(s1->error_jmp_buf) == 0) {
int is_asm;
s1->error_set_jmp_enabled = 1;
s1->nb_errors = 0;
if (fd == -1) {
int len = strlen(str);
tcc_open_bf(s1, "<string>", len);
memcpy(file->buffer, str, len);
} else {
tcc_open_bf(s1, str, 0);
file->fd = fd;
}
is_asm = !!(filetype & (AFF_TYPE_ASM|AFF_TYPE_ASMPP));
tccelf_begin_file(s1);
preprocess_start(s1, is_asm);
tccgen_init(s1);
if (s1->output_type == TCC_OUTPUT_PREPROCESS) {
tcc_preprocess(s1);
} else if (is_asm) {
#ifdef CONFIG_TCC_ASM
tcc_assemble(s1, !!(filetype & AFF_TYPE_ASMPP));
#else
tcc_error_noabort("asm not supported");
#endif
} else {
tccgen_compile(s1);
}
}
s1->error_set_jmp_enabled = 0;
tccgen_finish(s1);
preprocess_end(s1);
tccelf_end_file(s1);
tcc_state = NULL;
POST_SEM();
return s1->nb_errors != 0 ? -1 : 0;
}
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
return tcc_compile(s, s->filetype, str, -1);
}
/* define a preprocessor symbol. A value can also be provided with the '=' operator */
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
if (!value)
value = "1";
cstr_printf(&s1->cmdline_defs, "#define %s %s\n", sym, value);
}
/* undefine a preprocessor symbol */
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
cstr_printf(&s1->cmdline_defs, "#undef %s\n", sym);
}
LIBTCCAPI TCCState *tcc_new(void)
{
TCCState *s;
s = tcc_mallocz(sizeof(TCCState));
if (!s)
return NULL;
#ifdef MEM_DEBUG
++nb_states;
#endif
#undef gnu_ext
s->gnu_ext = 1;
s->tcc_ext = 1;
s->nocommon = 1;
s->dollars_in_identifiers = 1; /*on by default like in gcc/clang*/
s->cversion = 199901; /* default unless -std=c11 is supplied */
s->warn_implicit_function_declaration = 1;
s->ms_extensions = 1;
#ifdef CHAR_IS_UNSIGNED
s->char_is_unsigned = 1;
#endif
#ifdef TCC_TARGET_I386
s->seg_size = 32;
#endif
/* enable this if you want symbols with leading underscore on windows: */
#if 0 /* def TCC_TARGET_PE */
s->leading_underscore = 1;
#endif
#ifdef CONFIG_TCC_BACKTRACE
s->rt_num_callers = 6;
#endif
s->ppfp = stdout;
/* might be used in error() before preprocess_start() */
s->include_stack_ptr = s->include_stack;
tccelf_new(s);
#ifdef _WIN32
tcc_set_lib_path_w32(s);
#else
tcc_set_lib_path(s, CONFIG_TCCDIR);
#endif
{
/* define __TINYC__ 92X */
char buffer[32]; int a,b,c;
sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
sprintf(buffer, "%d", a*10000 + b*100 + c);
tcc_define_symbol(s, "__TINYC__", buffer);
}
/* standard defines */
tcc_define_symbol(s, "__STDC__", NULL);
tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
tcc_define_symbol(s, "__STDC_HOSTED__", NULL);
/* target defines */
#if defined(TCC_TARGET_I386)
tcc_define_symbol(s, "__i386__", NULL);
tcc_define_symbol(s, "__i386", NULL);
tcc_define_symbol(s, "i386", NULL);
#elif defined(TCC_TARGET_X86_64)
tcc_define_symbol(s, "__x86_64__", NULL);
#elif defined(TCC_TARGET_ARM)
tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
tcc_define_symbol(s, "__arm_elf__", NULL);
tcc_define_symbol(s, "__arm_elf", NULL);
tcc_define_symbol(s, "arm_elf", NULL);
tcc_define_symbol(s, "__arm__", NULL);
tcc_define_symbol(s, "__arm", NULL);
tcc_define_symbol(s, "arm", NULL);
tcc_define_symbol(s, "__APCS_32__", NULL);
tcc_define_symbol(s, "__ARMEL__", NULL);
#if defined(TCC_ARM_EABI)
tcc_define_symbol(s, "__ARM_EABI__", NULL);
#endif
#if defined(TCC_ARM_HARDFLOAT)
s->float_abi = ARM_HARD_FLOAT;
tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
#else
s->float_abi = ARM_SOFTFP_FLOAT;
#endif
#elif defined(TCC_TARGET_ARM64)
tcc_define_symbol(s, "__aarch64__", NULL);
#elif defined TCC_TARGET_C67
tcc_define_symbol(s, "__C67__", NULL);
#elif defined TCC_TARGET_RISCV64
tcc_define_symbol(s, "__riscv", NULL);
tcc_define_symbol(s, "__riscv_xlen", "64");
tcc_define_symbol(s, "__riscv_flen", "64");
tcc_define_symbol(s, "__riscv_div", NULL);
tcc_define_symbol(s, "__riscv_mul", NULL);
tcc_define_symbol(s, "__riscv_fdiv", NULL);
tcc_define_symbol(s, "__riscv_fsqrt", NULL);
tcc_define_symbol(s, "__riscv_float_abi_double", NULL);
#endif
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "_WIN32", NULL);
# ifdef TCC_TARGET_X86_64
tcc_define_symbol(s, "_WIN64", NULL);
# endif
#else
tcc_define_symbol(s, "__unix__", NULL);
tcc_define_symbol(s, "__unix", NULL);
tcc_define_symbol(s, "unix", NULL);
# if defined(__linux__)
tcc_define_symbol(s, "__linux__", NULL);
tcc_define_symbol(s, "__linux", NULL);
# endif
# if defined(__FreeBSD__)
tcc_define_symbol(s, "__FreeBSD__", "__FreeBSD__");
/* No 'Thread Storage Local' on FreeBSD with tcc */
tcc_define_symbol(s, "__NO_TLS", NULL);
# endif
# if defined(__FreeBSD_kernel__)
tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
# endif
# if defined(__NetBSD__)
tcc_define_symbol(s, "__NetBSD__", "__NetBSD__");
# endif
# if defined(__OpenBSD__)
tcc_define_symbol(s, "__OpenBSD__", "__OpenBSD__");
# endif
#endif
/* TinyCC & gcc defines */
#if PTR_SIZE == 4
/* 32bit systems. */
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int");
tcc_define_symbol(s, "__ILP32__", NULL);
#elif LONG_SIZE == 4
/* 64bit Windows. */
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
tcc_define_symbol(s, "__LLP64__", NULL);
#else
/* Other 64bit systems. */
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
tcc_define_symbol(s, "__LP64__", NULL);
#endif
tcc_define_symbol(s, "__SIZEOF_POINTER__", PTR_SIZE == 4 ? "4" : "8");
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short");
#else
tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
/* wint_t is unsigned int by default, but (signed) int on BSDs
and unsigned short on windows. Other OSes might have still
other conventions, sigh. */
# if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
tcc_define_symbol(s, "__WINT_TYPE__", "int");
# ifdef __FreeBSD__
/* define __GNUC__ to have some useful stuff from sys/cdefs.h
that are unconditionally used in FreeBSDs other system headers :/ */
tcc_define_symbol(s, "__GNUC__", "2");
tcc_define_symbol(s, "__GNUC_MINOR__", "7");
tcc_define_symbol(s, "__builtin_alloca", "alloca");
# endif
# else
tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int");
/* glibc defines */
tcc_define_symbol(s, "__REDIRECT(name, proto, alias)",
"name proto __asm__ (#alias)");
tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)",
"name proto __asm__ (#alias) __THROW");
# endif
# if defined(TCC_MUSL)
tcc_define_symbol(s, "__DEFINED_va_list", "");
tcc_define_symbol(s, "__DEFINED___isoc_va_list", "");
tcc_define_symbol(s, "__isoc_va_list", "void *");
# endif /* TCC_MUSL */
/* Some GCC builtins that are simple to express as macros. */
tcc_define_symbol(s, "__builtin_extract_return_addr(x)", "x");
#endif /* ndef TCC_TARGET_PE */
return s;
}
LIBTCCAPI void tcc_delete(TCCState *s1)
{
/* free sections */
tccelf_delete(s1);
/* free library paths */
dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);
/* free include paths */
dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
tcc_free(s1->tcc_lib_path);
tcc_free(s1->soname);
tcc_free(s1->rpath);
tcc_free(s1->init_symbol);
tcc_free(s1->fini_symbol);
tcc_free(s1->outfile);
tcc_free(s1->deps_outfile);
dynarray_reset(&s1->files, &s1->nb_files);
dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
dynarray_reset(&s1->argv, &s1->argc);
cstr_free(&s1->cmdline_defs);
cstr_free(&s1->cmdline_incl);
#ifdef TCC_IS_NATIVE
/* free runtime memory */
tcc_run_free(s1);
#endif
tcc_free(s1);
#ifdef MEM_DEBUG
if (0 == --nb_states)
tcc_memcheck();
#endif
}
LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
{
s->output_type = output_type;
/* always elf for objects */
if (output_type == TCC_OUTPUT_OBJ)
s->output_format = TCC_OUTPUT_FORMAT_ELF;
if (s->char_is_unsigned)
tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
if (!s->nostdinc) {
/* default include paths */
/* -isystem paths have already been handled */
tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
}
#ifdef CONFIG_TCC_BCHECK
if (s->do_bounds_check) {
/* if bound checking, then add corresponding sections */
tccelf_bounds_new(s);
/* define symbol */
tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
}
#endif
if (s->do_debug) {
/* add debug sections */
tccelf_stab_new(s);
}
tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
#ifdef TCC_TARGET_PE
# ifdef _WIN32
if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ)
tcc_add_systemdir(s);
# endif
#else
/* paths for crt objects */
tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
/* add libc crt1/crti objects */
if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
!s->nostdlib) {
if (output_type != TCC_OUTPUT_DLL)
tcc_add_crt(s, "crt1.o");
tcc_add_crt(s, "crti.o");
}
#endif
return 0;
}
LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
return 0;
}
LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
return 0;
}
ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
int fd, ret;
/* open the file */
fd = _tcc_open(s1, filename);
if (fd < 0) {
if (flags & AFF_PRINT_ERROR)
tcc_error_noabort("file '%s' not found", filename);
return -1;
}
/* update target deps */
dynarray_add(&s1->target_deps, &s1->nb_target_deps,
tcc_strdup(filename));
if (flags & AFF_TYPE_BIN) {
ElfW(Ehdr) ehdr;
int obj_type;
obj_type = tcc_object_type(fd, &ehdr);
lseek(fd, 0, SEEK_SET);
#ifdef TCC_TARGET_MACHO
if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), ".dylib"))
obj_type = AFF_BINTYPE_DYN;
#endif
switch (obj_type) {
case AFF_BINTYPE_REL:
ret = tcc_load_object_file(s1, fd, 0);
break;
#ifndef TCC_TARGET_PE
case AFF_BINTYPE_DYN:
if (s1->output_type == TCC_OUTPUT_MEMORY) {
ret = 0;
#ifdef TCC_IS_NATIVE
if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY))
ret = -1;
#endif
} else {
ret = tcc_load_dll(s1, fd, filename,
(flags & AFF_REFERENCED_DLL) != 0);
}
break;
#endif
case AFF_BINTYPE_AR:
ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE));
break;
#ifdef TCC_TARGET_COFF
case AFF_BINTYPE_C67:
ret = tcc_load_coff(s1, fd);
break;
#endif
default:
#ifdef TCC_TARGET_PE
ret = pe_load_file(s1, filename, fd);
#else
/* as GNU ld, consider it is an ld script if not recognized */
ret = tcc_load_ldscript(s1, fd);
#endif
if (ret < 0)
tcc_error_noabort("unrecognized file type");
break;
}
close(fd);
} else {
ret = tcc_compile(s1, flags, filename, fd);
}
return ret;
}
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
{
int filetype = s->filetype;
if (0 == (filetype & AFF_TYPE_MASK)) {
/* use a file extension to detect a filetype */
const char *ext = tcc_fileextension(filename);
if (ext[0]) {
ext++;
if (!strcmp(ext, "S"))
filetype = AFF_TYPE_ASMPP;
else if (!strcmp(ext, "s"))
filetype = AFF_TYPE_ASM;
else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
filetype = AFF_TYPE_C;
else
filetype |= AFF_TYPE_BIN;
} else {
filetype = AFF_TYPE_C;
}
}
return tcc_add_file_internal(s, filename, filetype | AFF_PRINT_ERROR);
}
LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
return 0;
}
static int tcc_add_library_internal(TCCState *s, const char *fmt,
const char *filename, int flags, char **paths, int nb_paths)
{
char buf[1024];
int i;
for(i = 0; i < nb_paths; i++) {
snprintf(buf, sizeof(buf), fmt, paths[i], filename);
if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0)
return 0;
}
return -1;
}
/* find and load a dll. Return non zero if not found */
/* XXX: add '-rpath' option support ? */
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
{
return tcc_add_library_internal(s, "%s/%s", filename, flags,
s->library_paths, s->nb_library_paths);
}
#ifndef TCC_TARGET_PE
ST_FUNC int tcc_add_crt(TCCState *s1, const char *filename)
{
if (-1 == tcc_add_library_internal(s1, "%s/%s",
filename, 0, s1->crt_paths, s1->nb_crt_paths))
tcc_error_noabort("file '%s' not found", filename);
return 0;
}
#endif
/* the library name is the same as the argument of the '-l' option */
LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
{
#if defined TCC_TARGET_PE
const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 4 : libs;
#elif defined TCC_TARGET_MACHO
const char *libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 1 : libs;
#else
const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 1 : libs;
#endif
int flags = s->filetype & AFF_WHOLE_ARCHIVE;
while (*pp) {
if (0 == tcc_add_library_internal(s, *pp,
libraryname, flags, s->library_paths, s->nb_library_paths))
return 0;
++pp;
}
return -1;
}
PUB_FUNC int tcc_add_library_err(TCCState *s1, const char *libname)
{
int ret = tcc_add_library(s1, libname);
if (ret < 0)
tcc_error_noabort("library '%s' not found", libname);
return ret;
}
/* handle #pragma comment(lib,) */
ST_FUNC void tcc_add_pragma_libs(TCCState *s1)
{
int i;
for (i = 0; i < s1->nb_pragma_libs; i++)
tcc_add_library_err(s1, s1->pragma_libs[i]);
}
LIBTCCAPI int tcc_add_symbol(TCCState *s1, const char *name, const void *val)
{
#ifdef TCC_TARGET_PE
/* On x86_64 'val' might not be reachable with a 32bit offset.
So it is handled here as if it were in a DLL. */
pe_putimport(s1, 0, name, (uintptr_t)val);
#else
set_elf_sym(symtab_section, (uintptr_t)val, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
SHN_ABS, name);
#endif
return 0;
}
LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
{
tcc_free(s->tcc_lib_path);
s->tcc_lib_path = tcc_strdup(path);
}
#define WD_ALL 0x0001 /* warning is activated when using -Wall */
#define FD_INVERT 0x0002 /* invert value before storing */
typedef struct FlagDef {
uint16_t offset;
uint16_t flags;
const char *name;
} FlagDef;
static int no_flag(const char **pp)
{
const char *p = *pp;
if (*p != 'n' || *++p != 'o' || *++p != '-')
return 0;
*pp = p + 1;
return 1;
}
ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name)
{
int value, ret;
const FlagDef *p;
const char *r;
value = 1;
r = name;
if (no_flag(&r))
value = 0;
for (ret = -1, p = flags; p->name; ++p) {
if (ret) {
if (strcmp(r, p->name))
continue;
} else {
if (0 == (p->flags & WD_ALL))
continue;
}
if (p->offset) {
*((unsigned char *)s + p->offset) =
p->flags & FD_INVERT ? !value : value;
if (ret)
return 0;
} else {
ret = 0;
}
}
return ret;
}
static int strstart(const char *val, const char **str)
{
const char *p, *q;
p = *str;
q = val;
while (*q) {
if (*p != *q)
return 0;
p++;
q++;
}
*str = p;
return 1;
}
/* Like strstart, but automatically takes into account that ld options can
*
* - start with double or single dash (e.g. '--soname' or '-soname')
* - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
* or '-Wl,-soname=x.so')
*
* you provide `val` always in 'option[=]' form (no leading -)
*/
static int link_option(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
int ret;
/* there should be 1 or 2 dashes */
if (*str++ != '-')
return 0;
if (*str == '-')
str++;
/* then str & val should match (potentially up to '=') */
p = str;
q = val;
ret = 1;
if (q[0] == '?') {
++q;
if (no_flag(&p))
ret = -1;
}
while (*q != '\0' && *q != '=') {
if (*p != *q)
return 0;
p++;
q++;
}
/* '=' near eos means ',' or '=' is ok */
if (*q == '=') {
if (*p == 0)
*ptr = p;
if (*p != ',' && *p != '=')
return 0;
p++;
} else if (*p) {
return 0;
}
*ptr = p;
return ret;
}
static const char *skip_linker_arg(const char **str)
{
const char *s1 = *str;
const char *s2 = strchr(s1, ',');
*str = s2 ? s2++ : (s2 = s1 + strlen(s1));
return s2;
}
static void copy_linker_arg(char **pp, const char *s, int sep)
{
const char *q = s;
char *p = *pp;
int l = 0;
if (p && sep)
p[l = strlen(p)] = sep, ++l;
skip_linker_arg(&q);
pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s);
}
/* set linker options */
static int tcc_set_linker(TCCState *s, const char *option)
{
TCCState *s1 = s;
while (*option) {
const char *p = NULL;
char *end = NULL;
int ignoring = 0;
int ret;
if (link_option(option, "Bsymbolic", &p)) {
s->symbolic = 1;
} else if (link_option(option, "nostdlib", &p)) {
s->nostdlib = 1;
} else if (link_option(option, "fini=", &p)) {
copy_linker_arg(&s->fini_symbol, p, 0);
ignoring = 1;
} else if (link_option(option, "image-base=", &p)
|| link_option(option, "Ttext=", &p)) {
s->text_addr = strtoull(p, &end, 16);
s->has_text_addr = 1;
} else if (link_option(option, "init=", &p)) {
copy_linker_arg(&s->init_symbol, p, 0);
ignoring = 1;
} else if (link_option(option, "oformat=", &p)) {
#if defined(TCC_TARGET_PE)
if (strstart("pe-", &p)) {
#elif PTR_SIZE == 8
if (strstart("elf64-", &p)) {
#else
if (strstart("elf32-", &p)) {
#endif
s->output_format = TCC_OUTPUT_FORMAT_ELF;
} else if (!strcmp(p, "binary")) {
s->output_format = TCC_OUTPUT_FORMAT_BINARY;
#ifdef TCC_TARGET_COFF
} else if (!strcmp(p, "coff")) {
s->output_format = TCC_OUTPUT_FORMAT_COFF;
#endif
} else
goto err;
} else if (link_option(option, "as-needed", &p)) {
ignoring = 1;
} else if (link_option(option, "O", &p)) {
ignoring = 1;
} else if (link_option(option, "export-all-symbols", &p)) {
s->rdynamic = 1;
} else if (link_option(option, "export-dynamic", &p)) {
s->rdynamic = 1;
} else if (link_option(option, "rpath=", &p)) {
copy_linker_arg(&s->rpath, p, ':');
} else if (link_option(option, "enable-new-dtags", &p)) {
s->enable_new_dtags = 1;
} else if (link_option(option, "section-alignment=", &p)) {
s->section_align = strtoul(p, &end, 16);
} else if (link_option(option, "soname=", &p)) {
copy_linker_arg(&s->soname, p, 0);
#ifdef TCC_TARGET_PE
} else if (link_option(option, "large-address-aware", &p)) {
s->pe_characteristics |= 0x20;
} else if (link_option(option, "file-alignment=", &p)) {
s->pe_file_align = strtoul(p, &end, 16);
} else if (link_option(option, "stack=", &p)) {
s->pe_stack_size = strtoul(p, &end, 10);
} else if (link_option(option, "subsystem=", &p)) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
if (!strcmp(p, "native")) {
s->pe_subsystem = 1;
} else if (!strcmp(p, "console")) {
s->pe_subsystem = 3;
} else if (!strcmp(p, "gui") || !strcmp(p, "windows")) {
s->pe_subsystem = 2;
} else if (!strcmp(p, "posix")) {
s->pe_subsystem = 7;
} else if (!strcmp(p, "efiapp")) {
s->pe_subsystem = 10;
} else if (!strcmp(p, "efiboot")) {
s->pe_subsystem = 11;
} else if (!strcmp(p, "efiruntime")) {
s->pe_subsystem = 12;
} else if (!strcmp(p, "efirom")) {
s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
if (!strcmp(p, "wince")) {
s->pe_subsystem = 9;
#endif
} else
goto err;
#endif
} else if (ret = link_option(option, "?whole-archive", &p), ret) {
if (ret > 0)
s->filetype |= AFF_WHOLE_ARCHIVE;
else
s->filetype &= ~AFF_WHOLE_ARCHIVE;
} else if (p) {
return 0;
} else {
err:
tcc_error("unsupported linker option '%s'", option);
}
if (ignoring && s->warn_unsupported)
tcc_warning("unsupported linker option '%s'", option);
option = skip_linker_arg(&p);
}
return 1;
}
typedef struct TCCOption {
const char *name;
uint16_t index;
uint16_t flags;
} TCCOption;
enum {
TCC_OPTION_HELP,
TCC_OPTION_HELP2,
TCC_OPTION_v,
TCC_OPTION_I,
TCC_OPTION_D,
TCC_OPTION_U,
TCC_OPTION_P,
TCC_OPTION_L,
TCC_OPTION_B,
TCC_OPTION_l,
TCC_OPTION_bench,
TCC_OPTION_bt,
TCC_OPTION_b,
TCC_OPTION_ba,
TCC_OPTION_g,
TCC_OPTION_c,
TCC_OPTION_dumpversion,
TCC_OPTION_d,
TCC_OPTION_static,
TCC_OPTION_std,
TCC_OPTION_shared,
TCC_OPTION_soname,
TCC_OPTION_o,
TCC_OPTION_r,
TCC_OPTION_s,
TCC_OPTION_traditional,
TCC_OPTION_Wl,
TCC_OPTION_Wp,
TCC_OPTION_W,
TCC_OPTION_O,
TCC_OPTION_mfloat_abi,
TCC_OPTION_m,
TCC_OPTION_f,
TCC_OPTION_isystem,
TCC_OPTION_iwithprefix,
TCC_OPTION_include,
TCC_OPTION_nostdinc,
TCC_OPTION_nostdlib,
TCC_OPTION_print_search_dirs,
TCC_OPTION_rdynamic,
TCC_OPTION_param,
TCC_OPTION_pedantic,
TCC_OPTION_pthread,
TCC_OPTION_run,
TCC_OPTION_w,
TCC_OPTION_pipe,
TCC_OPTION_E,
TCC_OPTION_MD,
TCC_OPTION_MF,
TCC_OPTION_x,
TCC_OPTION_ar,
TCC_OPTION_impdef
};
#define TCC_OPTION_HAS_ARG 0x0001
#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */
static const TCCOption tcc_options[] = {
{ "h", TCC_OPTION_HELP, 0 },
{ "-help", TCC_OPTION_HELP, 0 },
{ "?", TCC_OPTION_HELP, 0 },
{ "hh", TCC_OPTION_HELP2, 0 },
{ "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
{ "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
{ "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
{ "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
{ "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
{ "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG },
{ "bench", TCC_OPTION_bench, 0 },
#ifdef CONFIG_TCC_BACKTRACE
{ "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
#endif
#ifdef CONFIG_TCC_BCHECK
{ "b", TCC_OPTION_b, 0 },
#endif
{ "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "c", TCC_OPTION_c, 0 },
{ "dumpversion", TCC_OPTION_dumpversion, 0},
{ "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "static", TCC_OPTION_static, 0 },
{ "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "shared", TCC_OPTION_shared, 0 },
{ "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
{ "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
{ "-param", TCC_OPTION_param, TCC_OPTION_HAS_ARG },
{ "pedantic", TCC_OPTION_pedantic, 0},
{ "pthread", TCC_OPTION_pthread, 0},
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "rdynamic", TCC_OPTION_rdynamic, 0 },
{ "r", TCC_OPTION_r, 0 },
{ "s", TCC_OPTION_s, 0 },
{ "traditional", TCC_OPTION_traditional, 0 },
{ "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "Wp,", TCC_OPTION_Wp, 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 },
#ifdef TCC_TARGET_ARM
{ "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG },
#endif
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
{ "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG },
{ "nostdinc", TCC_OPTION_nostdinc, 0 },
{ "nostdlib", TCC_OPTION_nostdlib, 0 },
{ "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
{ "w", TCC_OPTION_w, 0 },
{ "pipe", TCC_OPTION_pipe, 0},
{ "E", TCC_OPTION_E, 0},
{ "MD", TCC_OPTION_MD, 0},
{ "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
{ "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
{ "ar", TCC_OPTION_ar, 0},
#ifdef TCC_TARGET_PE
{ "impdef", TCC_OPTION_impdef, 0},
#endif
{ NULL, 0, 0 },
};
static const FlagDef options_W[] = {
{ 0, 0, "all" },
{ offsetof(TCCState, warn_unsupported), 0, "unsupported" },
{ offsetof(TCCState, warn_write_strings), 0, "write-strings" },
{ offsetof(TCCState, warn_error), 0, "error" },
{ offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" },
{ offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
"implicit-function-declaration" },
{ 0, 0, NULL }
};
static const FlagDef options_f[] = {
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
{ offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
{ offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
{ 0, 0, NULL }
};
static const FlagDef options_m[] = {
{ offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" },
#ifdef TCC_TARGET_X86_64
{ offsetof(TCCState, nosse), FD_INVERT, "sse" },
#endif
{ 0, 0, NULL }
};
static void parse_option_D(TCCState *s1, const char *optarg)
{
char *sym = tcc_strdup(optarg);
char *value = strchr(sym, '=');
if (value)
*value++ = '\0';
tcc_define_symbol(s1, sym, value);
tcc_free(sym);
}
static void args_parser_add_file(TCCState *s, const char* filename, int filetype)
{
struct filespec *f = tcc_malloc(sizeof *f + strlen(filename));
f->type = filetype;
strcpy(f->name, filename);
dynarray_add(&s->files, &s->nb_files, f);
}
static int args_parser_make_argv(const char *r, int *argc, char ***argv)
{
int ret = 0, q, c;
CString str;
for(;;) {
while (c = (unsigned char)*r, c && c <= ' ')
++r;
if (c == 0)
break;
q = 0;
cstr_new(&str);
while (c = (unsigned char)*r, c) {
++r;
if (c == '\\' && (*r == '"' || *r == '\\')) {
c = *r++;
} else if (c == '"') {
q = !q;
continue;
} else if (q == 0 && c <= ' ') {
break;
}
cstr_ccat(&str, c);
}
cstr_ccat(&str, 0);
//printf("<%s>\n", str.data), fflush(stdout);
dynarray_add(argv, argc, tcc_strdup(str.data));
cstr_free(&str);
++ret;
}
return ret;
}
/* read list file */
static void args_parser_listfile(TCCState *s,
const char *filename, int optind, int *pargc, char ***pargv)
{
TCCState *s1 = s;
int fd, i;
size_t len;
char *p;
int argc = 0;
char **argv = NULL;
fd = open(filename, O_RDONLY | O_BINARY);
if (fd < 0)
tcc_error("listfile '%s' not found", filename);
len = lseek(fd, 0, SEEK_END);
p = tcc_malloc(len + 1), p[len] = 0;
lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd);
for (i = 0; i < *pargc; ++i)
if (i == optind)
args_parser_make_argv(p, &argc, &argv);
else
dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i]));
tcc_free(p);
dynarray_reset(&s->argv, &s->argc);
*pargc = s->argc = argc, *pargv = s->argv = argv;
}
PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
{
TCCState *s1 = s;
const TCCOption *popt;
const char *optarg, *r;
const char *run = NULL;
int last_o = -1;
int x;
CString linker_arg; /* collect -Wl options */
int tool = 0, arg_start = 0, noaction = optind;
char **argv = *pargv;
int argc = *pargc;
cstr_new(&linker_arg);
while (optind < argc) {
r = argv[optind];
if (r[0] == '@' && r[1] != '\0') {
args_parser_listfile(s, r + 1, optind, &argc, &argv);
continue;
}
optind++;
if (tool) {
if (r[0] == '-' && r[1] == 'v' && r[2] == 0)
++s->verbose;
continue;
}
reparse:
if (r[0] != '-' || r[1] == '\0') {
if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */
args_parser_add_file(s, r, s->filetype);
if (run) {
tcc_set_options(s, run);
arg_start = optind - 1;
break;
}
continue;
}
/* find option in table */
for(popt = tcc_options; ; ++popt) {
const char *p1 = popt->name;
const char *r1 = r + 1;
if (p1 == NULL)
tcc_error("invalid option -- '%s'", r);
if (!strstart(p1, &r1))
continue;
optarg = r1;
if (popt->flags & TCC_OPTION_HAS_ARG) {
if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) {
if (optind >= argc)
arg_err:
tcc_error("argument to '%s' is missing", r);
optarg = argv[optind++];
}
} else if (*r1 != '\0')
continue;
break;
}
switch(popt->index) {
case TCC_OPTION_HELP:
return OPT_HELP;
case TCC_OPTION_HELP2:
return OPT_HELP2;
case TCC_OPTION_I:
tcc_add_include_path(s, optarg);
break;
case TCC_OPTION_D:
parse_option_D(s, optarg);
break;
case TCC_OPTION_U:
tcc_undefine_symbol(s, optarg);
break;
case TCC_OPTION_L:
tcc_add_library_path(s, optarg);
break;
case TCC_OPTION_B:
/* set tcc utilities path (mainly for tcc development) */
tcc_set_lib_path(s, optarg);
break;
case TCC_OPTION_l:
args_parser_add_file(s, optarg, AFF_TYPE_LIB | (s->filetype & ~AFF_TYPE_MASK));
s->nb_libraries++;
break;
case TCC_OPTION_pthread:
parse_option_D(s, "_REENTRANT");
s->option_pthread = 1;
break;
case TCC_OPTION_bench:
s->do_bench = 1;
break;
#ifdef CONFIG_TCC_BACKTRACE
case TCC_OPTION_bt:
s->rt_num_callers = atoi(optarg);
break;
#endif
#ifdef CONFIG_TCC_BCHECK
case TCC_OPTION_b:
s->do_bounds_check = 1;
s->do_debug = 1;
break;
#endif
case TCC_OPTION_g:
s->do_debug = 1;
break;
case TCC_OPTION_c:
x = TCC_OUTPUT_OBJ;
set_output_type:
if (s->output_type)
tcc_warning("-%s: overriding compiler action already specified", popt->name);
s->output_type = x;
break;
case TCC_OPTION_d:
if (*optarg == 'D')
s->dflag = 3;
else if (*optarg == 'M')
s->dflag = 7;
else if (*optarg == 't')
s->dflag = 16;
else if (isnum(*optarg))
s->g_debug |= atoi(optarg);
else
goto unsupported_option;
break;
case TCC_OPTION_static:
s->static_link = 1;
break;
case TCC_OPTION_std:
if (*optarg == '=') {
if (strcmp(optarg, "=c11") == 0) {
tcc_undefine_symbol(s, "__STDC_VERSION__");
tcc_define_symbol(s, "__STDC_VERSION__", "201112L");
/*
* The integer constant 1, intended to indicate
* that the implementation does not support atomic
* types (including the _Atomic type qualifier) and
* the <stdatomic.h> header.
*/
tcc_define_symbol(s, "__STDC_NO_ATOMICS__", "1");
/*
* The integer constant 1, intended to indicate
* that the implementation does not support complex
* types or the <complex.h> header.
*/
tcc_define_symbol(s, "__STDC_NO_COMPLEX__", "1");
/*
* The integer constant 1, intended to indicate
* that the implementation does not support the
* <threads.h> header.
*/
tcc_define_symbol(s, "__STDC_NO_THREADS__", "1");
/*
* __STDC_NO_VLA__, tcc supports VLA.
* The integer constant 1, intended to indicate
* that the implementation does not support
* variable length arrays or variably modified
* types.
*/
#if !defined(TCC_TARGET_PE)
/*
* An integer constant of the form yyyymmL (for
* example, 199712L). If this symbol is defined,
* then every character in the Unicode required
* set, when stored in an object of type
* wchar_t, has the same value as the short
* identifier of that character.
*/
#if 0
/* on Linux, this conflicts with a define introduced by
* /usr/include/stdc-predef.h included by glibc libs;
* clang doesn't define it at all so it's probably not necessary
*/
tcc_define_symbol(s, "__STDC_ISO_10646__", "201605L");
#endif
/*
* The integer constant 1, intended to indicate
* that values of type char16_t are UTF16
* encoded. If some other encoding is used, the
* macro shall not be defined and the actual
* encoding used is implementation defined.
*/
tcc_define_symbol(s, "__STDC_UTF_16__", "1");
/*
* The integer constant 1, intended to indicate
* that values of type char32_t are UTF32
* encoded. If some other encoding is used, the
* macro shall not be defined and the actual
* encoding used is implementationdefined.
*/
tcc_define_symbol(s, "__STDC_UTF_32__", "1");
#endif /* !TCC_TARGET_PE */
s->cversion = 201112;
}
}
/*
* silently ignore other values, a current purpose:
* allow to use a tcc as a reference compiler for "make test"
*/
break;
case TCC_OPTION_shared:
x = TCC_OUTPUT_DLL;
goto set_output_type;
case TCC_OPTION_soname:
s->soname = tcc_strdup(optarg);
break;
case TCC_OPTION_o:
if (s->outfile) {
tcc_warning("multiple -o option");
tcc_free(s->outfile);
}
s->outfile = tcc_strdup(optarg);
break;
case TCC_OPTION_r:
/* generate a .o merging several output files */
s->option_r = 1;
x = TCC_OUTPUT_OBJ;
goto set_output_type;
case TCC_OPTION_isystem:
tcc_add_sysinclude_path(s, optarg);
break;
case TCC_OPTION_include:
cstr_printf(&s->cmdline_incl, "#include \"%s\"\n", optarg);
break;
case TCC_OPTION_nostdinc:
s->nostdinc = 1;
break;
case TCC_OPTION_nostdlib:
s->nostdlib = 1;
break;
case TCC_OPTION_run:
#ifndef TCC_IS_NATIVE
tcc_error("-run is not available in a cross compiler");
#endif
run = optarg;
x = TCC_OUTPUT_MEMORY;
goto set_output_type;
case TCC_OPTION_v:
do ++s->verbose; while (*optarg++ == 'v');
++noaction;
break;
case TCC_OPTION_f:
if (set_flag(s, options_f, optarg) < 0)
goto unsupported_option;
break;
#ifdef TCC_TARGET_ARM
case TCC_OPTION_mfloat_abi:
/* tcc doesn't support soft float yet */
if (!strcmp(optarg, "softfp")) {
s->float_abi = ARM_SOFTFP_FLOAT;
tcc_undefine_symbol(s, "__ARM_PCS_VFP");
} else if (!strcmp(optarg, "hard"))
s->float_abi = ARM_HARD_FLOAT;
else
tcc_error("unsupported float abi '%s'", optarg);
break;
#endif
case TCC_OPTION_m:
if (set_flag(s, options_m, optarg) < 0) {
if (x = atoi(optarg), x != 32 && x != 64)
goto unsupported_option;
if (PTR_SIZE != x/8)
return x;
++noaction;
}
break;
case TCC_OPTION_W:
s->warn_none = 0;
if (optarg[0] && set_flag(s, options_W, optarg) < 0)
goto unsupported_option;
break;
case TCC_OPTION_w:
s->warn_none = 1;
break;
case TCC_OPTION_rdynamic:
s->rdynamic = 1;
break;
case TCC_OPTION_Wl:
if (linker_arg.size)
--linker_arg.size, cstr_ccat(&linker_arg, ',');
cstr_cat(&linker_arg, optarg, 0);
if (tcc_set_linker(s, linker_arg.data))
cstr_free(&linker_arg);
break;
case TCC_OPTION_Wp:
r = optarg;
goto reparse;
case TCC_OPTION_E:
x = TCC_OUTPUT_PREPROCESS;
goto set_output_type;
case TCC_OPTION_P:
s->Pflag = atoi(optarg) + 1;
break;
case TCC_OPTION_MD:
s->gen_deps = 1;
break;
case TCC_OPTION_MF:
s->deps_outfile = tcc_strdup(optarg);
break;
case TCC_OPTION_dumpversion:
printf ("%s\n", TCC_VERSION);
exit(0);
break;
case TCC_OPTION_x:
x = 0;
if (*optarg == 'c')
x = AFF_TYPE_C;
else if (*optarg == 'a')
x = AFF_TYPE_ASMPP;
else if (*optarg == 'b')
x = AFF_TYPE_BIN;
else if (*optarg == 'n')
x = AFF_TYPE_NONE;
else
tcc_warning("unsupported language '%s'", optarg);
s->filetype = x | (s->filetype & ~AFF_TYPE_MASK);
break;
case TCC_OPTION_O:
last_o = atoi(optarg);
break;
case TCC_OPTION_print_search_dirs:
x = OPT_PRINT_DIRS;
goto extra_action;
case TCC_OPTION_impdef:
x = OPT_IMPDEF;
goto extra_action;
case TCC_OPTION_ar:
x = OPT_AR;
extra_action:
arg_start = optind - 1;
if (arg_start != noaction)
tcc_error("cannot parse %s here", r);
tool = x;
break;
case TCC_OPTION_traditional:
case TCC_OPTION_pedantic:
case TCC_OPTION_pipe:
case TCC_OPTION_s:
/* ignored */
break;
default:
unsupported_option:
if (s->warn_unsupported)
tcc_warning("unsupported option '%s'", r);
break;
}
}
if (last_o > 0)
tcc_define_symbol(s, "__OPTIMIZE__", NULL);
if (linker_arg.size) {
r = linker_arg.data;
goto arg_err;
}
*pargc = argc - arg_start;
*pargv = argv + arg_start;
if (tool)
return tool;
if (optind != noaction)
return 0;
if (s->verbose == 2)
return OPT_PRINT_DIRS;
if (s->verbose)
return OPT_V;
return OPT_HELP;
}
LIBTCCAPI void tcc_set_options(TCCState *s, const char *r)
{
char **argv = NULL;
int argc = 0;
args_parser_make_argv(r, &argc, &argv);
tcc_parse_args(s, &argc, &argv, 0);
dynarray_reset(&argv, &argc);
}
PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time)
{
if (total_time < 1)
total_time = 1;
if (total_bytes < 1)
total_bytes = 1;
fprintf(stderr, "* %d idents, %d lines, %d bytes\n"
"* %0.3f s, %u lines/s, %0.1f MB/s\n",
total_idents, total_lines, total_bytes,
(double)total_time/1000,
(unsigned)total_lines*1000/total_time,
(double)total_bytes/1000/total_time);
#ifdef MEM_DEBUG
fprintf(stderr, "* %d bytes memory used\n", mem_max_size);
#endif
}