mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-13 07:57:35 +00:00
784 lines
17 KiB
C
784 lines
17 KiB
C
/*
|
|
* TCC - Tiny C Compiler
|
|
*
|
|
* Copyright (c) 2001-2004 Fabrice Bellard
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#include <r_util.h>
|
|
#include "tcc.h"
|
|
|
|
/********************************************************/
|
|
/* global variables */
|
|
|
|
/* use GNU C extensions */
|
|
ST_DATA int gnu_ext = 1;
|
|
|
|
/* use TinyCC extensions */
|
|
ST_DATA int tcc_ext = 1;
|
|
|
|
/* XXX: get rid of this ASAP */
|
|
ST_DATA struct TCCState *tcc_state;
|
|
|
|
/********************************************************/
|
|
|
|
#ifdef __WINDOWS__
|
|
// GCC appears to use '/' for relative paths and '\\' for absolute paths on Windows
|
|
static char *normalize_slashes(char *path)
|
|
{
|
|
char *p;
|
|
if (path[1] == ':') {
|
|
for (p = path + 2; *p; ++p) {
|
|
if (*p == '/') {
|
|
*p = '\\';
|
|
}
|
|
}
|
|
} else {
|
|
for (p = path; *p; ++p) {
|
|
if (*p == '\\') {
|
|
*p = '/';
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
#endif
|
|
|
|
/********************************************************/
|
|
/* copy a string and truncate it. */
|
|
PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
|
|
{
|
|
char *q, *q_end;
|
|
int c;
|
|
|
|
if (buf_size > 0) {
|
|
q = buf;
|
|
q_end = buf + buf_size - 1;
|
|
while (q < q_end) {
|
|
c = *s++;
|
|
if (c == '\0') {
|
|
break;
|
|
}
|
|
*q++ = c;
|
|
}
|
|
*q = '\0';
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/* strcat and truncate. */
|
|
PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
|
|
{
|
|
int len;
|
|
len = strlen (buf);
|
|
if (len < buf_size) {
|
|
pstrcpy (buf + len, buf_size - len, s);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
PUB_FUNC char *pstrncpy(char *out, const char *in, size_t num)
|
|
{
|
|
memcpy (out, in, num);
|
|
out[num] = '\0';
|
|
return out;
|
|
}
|
|
|
|
/* extract the basename of a file */
|
|
PUB_FUNC char *tcc_basename(const char *name)
|
|
{
|
|
char *p = strchr (name, 0);
|
|
while (p && 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 */
|
|
|
|
|
|
PUB_FUNC void *tcc_mallocz(unsigned long size)
|
|
{
|
|
void *ptr;
|
|
ptr = malloc (size);
|
|
memset (ptr, 0, size);
|
|
return ptr;
|
|
}
|
|
|
|
|
|
PUB_FUNC void tcc_memstats(void)
|
|
{
|
|
#ifdef MEM_DEBUG
|
|
printf ("memory: %d byte(s), max = %d byte(s)\n", mem_cur_size, mem_max_size);
|
|
#endif
|
|
}
|
|
|
|
/********************************************************/
|
|
/* dynarrays */
|
|
|
|
ST_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data)
|
|
{
|
|
int nb, nb_alloc;
|
|
void **pp;
|
|
|
|
nb = *nb_ptr;
|
|
pp = *ptab;
|
|
/* every power of two we double array size */
|
|
if ((nb & (nb - 1)) == 0) {
|
|
if (!nb) {
|
|
nb_alloc = 1;
|
|
} else {
|
|
nb_alloc = nb * 2;
|
|
}
|
|
pp = realloc (pp, nb_alloc * sizeof(void *));
|
|
*ptab = pp;
|
|
}
|
|
pp[nb++] = data;
|
|
*nb_ptr = nb;
|
|
}
|
|
|
|
ST_FUNC void dynarray_reset(void *pp, int *n)
|
|
{
|
|
void **p;
|
|
for (p = *(void ***) pp; *n; ++p, --*n) {
|
|
if (*p) {
|
|
free (*p);
|
|
}
|
|
}
|
|
free (*(void **) pp);
|
|
*(void **) pp = NULL;
|
|
}
|
|
|
|
static void tcc_split_path(TCCState *s, void ***p_ary, int *p_nb_ary, const char *in)
|
|
{
|
|
const char *p;
|
|
do {
|
|
int c;
|
|
CString str;
|
|
|
|
cstr_new (&str);
|
|
for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) {
|
|
if (c == '{' && p[1] && p[2] == '}') {
|
|
c = p[1], p += 2;
|
|
if (c == 'B') {
|
|
cstr_cat (&str, s->tcc_lib_path);
|
|
}
|
|
} else {
|
|
cstr_ccat (&str, c);
|
|
}
|
|
}
|
|
cstr_ccat (&str, '\0');
|
|
dynarray_add (p_ary, p_nb_ary, str.data);
|
|
in = p + 1;
|
|
} while (*p);
|
|
}
|
|
|
|
/********************************************************/
|
|
|
|
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);
|
|
}
|
|
|
|
PUB_FUNC void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start (ap, fmt);
|
|
strcat_vprintf (buf, buf_size, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
|
|
{
|
|
char buf[2048];
|
|
BufferedFile **pf, *f;
|
|
|
|
buf[0] = '\0';
|
|
/* use upper file if inline ":asm:" or token ":paste:" */
|
|
for (f = file; f && f->filename[0] == ':'; f = f->prev) {
|
|
;
|
|
}
|
|
if (f) {
|
|
for (pf = s1->include_stack; pf < s1->include_stack_ptr; pf++) {
|
|
strcat_printf (buf, sizeof(buf), "In file included from %s:%d:\n",
|
|
(*pf)->filename, (*pf)->line_num);
|
|
}
|
|
if (f->line_num > 0) {
|
|
strcat_printf (buf, sizeof(buf), "%s:%d: ",
|
|
f->filename, f->line_num);
|
|
} else {
|
|
strcat_printf (buf, sizeof(buf), "%s: ",
|
|
f->filename);
|
|
}
|
|
} else {
|
|
strcat_printf (buf, sizeof(buf), "tcc: ");
|
|
}
|
|
if (is_warning) {
|
|
strcat_printf (buf, sizeof(buf), "warning: ");
|
|
} else {
|
|
strcat_printf (buf, sizeof(buf), "error: ");
|
|
}
|
|
strcat_vprintf (buf, sizeof(buf), fmt, ap);
|
|
|
|
if (!s1->error_func) {
|
|
/* default case: stderr */
|
|
fprintf (stderr, "%s\n", buf);
|
|
} else {
|
|
s1->error_func (s1->error_opaque, buf);
|
|
}
|
|
if (!is_warning || s1->warn_error) {
|
|
s1->nb_errors++;
|
|
}
|
|
}
|
|
|
|
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
|
|
void (*error_func)(void *opaque, const char *msg))
|
|
{
|
|
s->error_opaque = error_opaque;
|
|
s->error_func = error_func;
|
|
}
|
|
|
|
/* error without aborting current compilation */
|
|
PUB_FUNC void tcc_error(const char *fmt, ...)
|
|
{
|
|
TCCState *s1 = tcc_state;
|
|
va_list ap;
|
|
|
|
va_start (ap, fmt);
|
|
error1 (s1, 0, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
PUB_FUNC void tcc_warning(const char *fmt, ...)
|
|
{
|
|
TCCState *s1 = tcc_state;
|
|
va_list ap;
|
|
|
|
if (s1->warn_none) {
|
|
return;
|
|
}
|
|
|
|
va_start (ap, fmt);
|
|
error1 (s1, 1, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
/********************************************************/
|
|
/* I/O layer */
|
|
|
|
ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
|
|
{
|
|
BufferedFile *bf;
|
|
int buflen = initlen? initlen: IO_BUF_SIZE;
|
|
|
|
bf = malloc (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 __WINDOWS__
|
|
normalize_slashes (bf->filename);
|
|
#endif
|
|
bf->line_num = 1;
|
|
bf->ifndef_macro = 0;
|
|
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
|
|
bf->fd = -1;
|
|
bf->prev = file;
|
|
file = bf;
|
|
}
|
|
|
|
ST_FUNC void tcc_close(void)
|
|
{
|
|
BufferedFile *bf = file;
|
|
if (bf->fd > 0) {
|
|
close (bf->fd);
|
|
total_lines += bf->line_num;
|
|
}
|
|
file = bf->prev;
|
|
free (bf);
|
|
}
|
|
|
|
ST_FUNC int tcc_open(TCCState *s1, const char *filename)
|
|
{
|
|
int fd;
|
|
if (strcmp (filename, "-") == 0) {
|
|
fd = 0, filename = "stdin";
|
|
} else {
|
|
fd = open (filename, O_RDONLY | O_BINARY);
|
|
}
|
|
if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3) {
|
|
printf ("%s %*s%s\n", fd < 0? "nf": "->",
|
|
(int) (s1->include_stack_ptr - s1->include_stack), "", filename);
|
|
}
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
tcc_open_bf (s1, filename, 0);
|
|
file->fd = fd;
|
|
return fd;
|
|
}
|
|
|
|
/* compile the C file opened in 'file'. Return non zero if errors. */
|
|
static int tcc_compile(TCCState *s1)
|
|
{
|
|
Sym *define_start;
|
|
|
|
#ifdef INC_DEBUG
|
|
printf ("%s: **** new file\n", file->filename);
|
|
#endif
|
|
preprocess_init (s1);
|
|
|
|
funcname = "";
|
|
|
|
/* define some often used types */
|
|
int8_type.t = VT_INT8;
|
|
int16_type.t = VT_INT16;
|
|
int32_type.t = VT_INT32;
|
|
int64_type.t = VT_INT64;
|
|
|
|
char_pointer_type.t = VT_INT8;
|
|
mk_pointer (&char_pointer_type);
|
|
|
|
if (tcc_state->bits != 64) {
|
|
size_type.t = VT_INT32;
|
|
} else {
|
|
size_type.t = VT_INT64;
|
|
}
|
|
|
|
func_old_type.t = VT_FUNC;
|
|
func_old_type.ref = sym_push (SYM_FIELD, &int32_type, FUNC_CDECL, FUNC_OLD);
|
|
|
|
// FIXME: Should depend on the target options too
|
|
#ifdef TCC_TARGET_ARM
|
|
arm_init_types ();
|
|
#endif
|
|
|
|
#if 0
|
|
/* define 'void *alloca(unsigned int)' builtin function */
|
|
{
|
|
Sym *s1;
|
|
|
|
p = anon_sym++;
|
|
sym = sym_push (p, mk_pointer (VT_VOID), FUNC_CDECL, FUNC_NEW);
|
|
s1 = sym_push (SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
|
|
s1->next = NULL;
|
|
sym->next = s1;
|
|
sym_push (TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
|
|
}
|
|
#endif
|
|
|
|
define_start = define_stack;
|
|
nocode_wanted = 1;
|
|
|
|
if (setjmp (s1->error_jmp_buf) == 0) {
|
|
s1->nb_errors = 0;
|
|
s1->error_set_jmp_enabled = 1;
|
|
|
|
ch = file->buf_ptr[0];
|
|
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
|
|
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM;
|
|
// pvtop = vtop;
|
|
next ();
|
|
decl (VT_CONST);
|
|
if (tok != TOK_EOF) {
|
|
expect ("declaration");
|
|
}
|
|
#if 0
|
|
if (pvtop != vtop) {
|
|
fprintf (stderr, "internal compiler error:"
|
|
" vstack leak? (%d)", vtop - pvtop);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
s1->error_set_jmp_enabled = 0;
|
|
|
|
/* reset define stack, but leave -Dsymbols (may be incorrect if
|
|
they are undefined) */
|
|
free_defines (define_start);
|
|
|
|
sym_pop (&global_stack, NULL);
|
|
sym_pop (&local_stack, NULL);
|
|
|
|
return s1->nb_errors != 0? -1: 0;
|
|
}
|
|
|
|
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
|
|
{
|
|
int len, ret;
|
|
len = strlen (str);
|
|
|
|
tcc_open_bf (s, "<string>", len);
|
|
memcpy (file->buffer, str, len);
|
|
ret = tcc_compile (s);
|
|
tcc_close ();
|
|
return ret;
|
|
}
|
|
|
|
/* define a preprocessor symbol. A value can also be provided with the '=' operator */
|
|
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
|
|
{
|
|
int len1, len2;
|
|
/* default value */
|
|
if (!value) {
|
|
value = "1";
|
|
}
|
|
len1 = strlen (sym);
|
|
len2 = strlen (value);
|
|
|
|
/* init file structure */
|
|
tcc_open_bf (s1, "<define>", len1 + len2 + 1);
|
|
memcpy (file->buffer, sym, len1);
|
|
file->buffer[len1] = ' ';
|
|
memcpy (file->buffer + len1 + 1, value, len2);
|
|
|
|
/* parse with define parser */
|
|
ch = file->buf_ptr[0];
|
|
next_nomacro ();
|
|
parse_define ();
|
|
|
|
tcc_close ();
|
|
}
|
|
|
|
/* undefine a preprocessor symbol */
|
|
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
|
|
{
|
|
TokenSym *ts;
|
|
Sym *s;
|
|
ts = tok_alloc (sym, strlen (sym));
|
|
s = define_find (ts->tok);
|
|
/* undefine symbol by putting an invalid name */
|
|
if (s) {
|
|
define_undef (s);
|
|
}
|
|
}
|
|
|
|
/* cleanup all static data used during compilation */
|
|
static void tcc_cleanup(void)
|
|
{
|
|
int i, n;
|
|
if (NULL == tcc_state) {
|
|
return;
|
|
}
|
|
tcc_state = NULL;
|
|
|
|
/* free -D defines */
|
|
free_defines (NULL);
|
|
|
|
/* free tokens */
|
|
n = tok_ident - TOK_IDENT;
|
|
for (i = 0; i < n; i++) {
|
|
free (table_ident[i]);
|
|
}
|
|
free (table_ident);
|
|
|
|
/* free sym_pools */
|
|
dynarray_reset (&sym_pools, &nb_sym_pools);
|
|
/* string buffer */
|
|
cstr_free (&tokcstr);
|
|
/* reset symbol stack */
|
|
sym_free_first = NULL;
|
|
/* cleanup from error/setjmp */
|
|
macro_ptr = NULL;
|
|
}
|
|
|
|
LIBTCCAPI TCCState *tcc_new(const char *arch, int bits, const char *os)
|
|
{
|
|
TCCState *s;
|
|
char buffer[100];
|
|
int a, b, c;
|
|
|
|
if (!arch || !os) {
|
|
return NULL;
|
|
}
|
|
tcc_cleanup ();
|
|
s = tcc_mallocz (sizeof(TCCState));
|
|
if (!s) {
|
|
return NULL;
|
|
}
|
|
tcc_state = s;
|
|
s->arch = strdup (arch);
|
|
s->bits = bits;
|
|
s->os = strdup (os);
|
|
s->output_type = TCC_OUTPUT_MEMORY;
|
|
preprocess_new ();
|
|
s->include_stack_ptr = s->include_stack;
|
|
|
|
/* we add dummy defines for some special macros to speed up tests
|
|
and to have working defined() */
|
|
define_push (TOK___LINE__, MACRO_OBJ, NULL, NULL);
|
|
define_push (TOK___FILE__, MACRO_OBJ, NULL, NULL);
|
|
define_push (TOK___DATE__, MACRO_OBJ, NULL, NULL);
|
|
define_push (TOK___TIME__, MACRO_OBJ, NULL, NULL);
|
|
|
|
/* define __TINYC__ 92X */
|
|
sscanf (TCC_VERSION, "%d.%d.%d", &a, &b, &c);
|
|
sprintf (buffer, "%d", a * 10000 + b * 100 + c);
|
|
tcc_define_symbol (s, "__TINYC__", buffer);
|
|
tcc_define_symbol (s, "__R2TINYC__", 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 (!strncmp (arch, "x86", 3)) {
|
|
if (bits == 32 || bits == 16) {
|
|
tcc_define_symbol (s, "__i386__", NULL);
|
|
tcc_define_symbol (s, "__i386", NULL);
|
|
tcc_define_symbol (s, "i386", NULL);
|
|
} else {
|
|
tcc_define_symbol (s, "__x86_64__", NULL);
|
|
}
|
|
} else if (!strncmp (arch, "arm", 3)) {
|
|
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);
|
|
}
|
|
// TODO: Add other architectures
|
|
// TODO: Move that in SDB
|
|
|
|
if (!strncmp (os, "windows", 7)) {
|
|
tcc_define_symbol (s, "__WINDOWS__", NULL);
|
|
if (bits == 64) {
|
|
tcc_define_symbol (s, "_WIN64", NULL);
|
|
}
|
|
} else {
|
|
tcc_define_symbol (s, "__unix__", NULL);
|
|
tcc_define_symbol (s, "__unix", NULL);
|
|
tcc_define_symbol (s, "unix", NULL);
|
|
|
|
if (!strncmp (os, "linux", 5)) {
|
|
tcc_define_symbol (s, "__linux__", NULL);
|
|
tcc_define_symbol (s, "__linux", NULL);
|
|
}
|
|
#define str(s) #s
|
|
if (!strncmp (os, "freebsd", 7)) {
|
|
tcc_define_symbol (s, "__FreeBSD__", str ( __FreeBSD__));
|
|
}
|
|
#undef str
|
|
}
|
|
|
|
/* TinyCC & gcc defines */
|
|
if (!strncmp (os, "windows", 7) && (bits == 64)) {
|
|
tcc_define_symbol (s, "__SIZE_TYPE__", "unsigned long long");
|
|
tcc_define_symbol (s, "__PTRDIFF_TYPE__", "long long");
|
|
} else {
|
|
tcc_define_symbol (s, "__SIZE_TYPE__", "unsigned long");
|
|
tcc_define_symbol (s, "__PTRDIFF_TYPE__", "long");
|
|
}
|
|
|
|
if (!strncmp (os, "windows", 7)) {
|
|
tcc_define_symbol (s, "__WCHAR_TYPE__", "unsigned short");
|
|
} else {
|
|
tcc_define_symbol (s, "__WCHAR_TYPE__", "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");
|
|
}
|
|
|
|
s->alacarte_link = 1;
|
|
s->nocommon = 1;
|
|
|
|
#ifdef CHAR_IS_UNSIGNED
|
|
s->char_is_unsigned = 1;
|
|
#endif
|
|
/* enable this if you want symbols with leading underscore on windows: */
|
|
#if 0 /* def TCC_TARGET_PE */
|
|
s->leading_underscore = 1;
|
|
#endif
|
|
if (!strncmp (arch, "x86", 3)) {
|
|
// TODO: Set it to 16 for 16bit x86
|
|
if (bits == 32 || bits == 16) {
|
|
s->seg_size = 32;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
LIBTCCAPI void tcc_delete(TCCState *s1)
|
|
{
|
|
tcc_cleanup ();
|
|
|
|
/* free library paths */
|
|
dynarray_reset (&s1->library_paths, &s1->nb_library_paths);
|
|
dynarray_reset (&s1->crt_paths, &s1->nb_crt_paths);
|
|
|
|
/* free include paths */
|
|
dynarray_reset (&s1->cached_includes, &s1->nb_cached_includes);
|
|
dynarray_reset (&s1->include_paths, &s1->nb_include_paths);
|
|
dynarray_reset (&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
|
|
|
|
free (s1->tcc_lib_path);
|
|
free (s1->soname);
|
|
free (s1->rpath);
|
|
free (s1->init_symbol);
|
|
free (s1->fini_symbol);
|
|
free (s1->outfile);
|
|
free (s1->deps_outfile);
|
|
dynarray_reset (&s1->files, &s1->nb_files);
|
|
dynarray_reset (&s1->target_deps, &s1->nb_target_deps);
|
|
|
|
/* target config */
|
|
free (s1->arch);
|
|
free (s1->os);
|
|
|
|
#ifdef TCC_IS_NATIVE
|
|
#ifdef HAVE_SELINUX
|
|
munmap (s1->write_mem, s1->mem_size);
|
|
munmap (s1->runtime_mem, s1->mem_size);
|
|
#else
|
|
free (s1->runtime_mem);
|
|
#endif
|
|
#endif
|
|
|
|
free (s1);
|
|
}
|
|
|
|
LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
|
|
{
|
|
tcc_split_path (s, (void ***) &s->include_paths, &s->nb_include_paths, pathname);
|
|
return 0;
|
|
}
|
|
|
|
LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
|
|
{
|
|
tcc_split_path (s, (void ***) &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
|
|
return 0;
|
|
}
|
|
|
|
ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|
{
|
|
const char *ext;
|
|
int ret;
|
|
|
|
/* find source file type with extension */
|
|
ext = tcc_fileextension (filename);
|
|
if (ext[0]) {
|
|
ext++;
|
|
}
|
|
|
|
/* open the file */
|
|
ret = tcc_open (s1, filename);
|
|
if (ret < 0) {
|
|
if (flags & AFF_PRINT_ERROR) {
|
|
tcc_error ("file '%s' not found", filename);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* update target deps */
|
|
dynarray_add ((void ***) &s1->target_deps, &s1->nb_target_deps,
|
|
strdup (filename));
|
|
|
|
if (flags & AFF_PREPROCESS) {
|
|
ret = tcc_preprocess (s1);
|
|
goto the_end;
|
|
}
|
|
|
|
if (!ext[0] || !PATHCMP (ext, "c") || !PATHCMP (ext, "h") || !PATHCMP (ext, "cparse")) {
|
|
/* C file assumed */
|
|
ret = tcc_compile (s1);
|
|
goto the_end;
|
|
}
|
|
if (ret < 0) {
|
|
tcc_error ("unrecognized file type");
|
|
}
|
|
|
|
the_end:
|
|
tcc_close ();
|
|
return ret;
|
|
}
|
|
|
|
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
|
|
{
|
|
if (s->output_type == TCC_OUTPUT_PREPROCESS) {
|
|
return tcc_add_file_internal (s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS);
|
|
} else {
|
|
return tcc_add_file_internal (s, filename, AFF_PRINT_ERROR);
|
|
}
|
|
}
|
|
|
|
#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;
|
|
|
|
ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
|
|
const char *name, int value)
|
|
{
|
|
int i;
|
|
const FlagDef *p;
|
|
const char *r;
|
|
|
|
r = name;
|
|
if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') {
|
|
r += 3;
|
|
value = !value;
|
|
}
|
|
for (i = 0, p = flags; i < nb_flags; i++, p++) {
|
|
if (!strcmp (r, p->name)) {
|
|
goto found;
|
|
}
|
|
}
|
|
return -1;
|
|
found:
|
|
if (p->flags & FD_INVERT) {
|
|
value = !value;
|
|
}
|
|
*(int *) ((uint8_t *) s + p->offset) = value;
|
|
return 0;
|
|
}
|
|
|
|
void (*tcc_cb)(const char *, char **);
|
|
|
|
PUB_FUNC void tcc_set_callback(TCCState *s, void (*cb)(const char *, char **), char **p) {
|
|
tcc_cb = cb;
|
|
tcc_cb_ptr = p;
|
|
}
|
|
|
|
PUB_FUNC void tcc_appendf(const char *fmt, ...) {
|
|
char b[1024];
|
|
va_list ap;
|
|
va_start (ap, fmt);
|
|
vsnprintf (b, sizeof (b), fmt, ap);
|
|
tcc_cb (b, tcc_cb_ptr);
|
|
va_end (ap);
|
|
}
|