diff --git a/build/init.mk b/build/init.mk index 75d33db..16c580f 100644 --- a/build/init.mk +++ b/build/init.mk @@ -15,9 +15,9 @@ CFLAGS ?= -O2 -fomit-frame-pointer ${cflags_stack_protect} cflags := ${CFLAGS} ${cflags_includes} -SOURCES := src/file.o src/cjit.o \ +SOURCES := src/file.o src/cjit.o src/elflinker.o \ src/main.o src/assets.o \ - src/cwalk.o \ + src/cwalk.o src/array.o \ src/muntar.o src/tinflate.o src/tinfgzip.o \ src/embed_libtcc1.a.o src/embed_include.o #src/embed_source.o diff --git a/build/linux.mk b/build/linux.mk index 987bc06..d97f843 100644 --- a/build/linux.mk +++ b/build/linux.mk @@ -9,6 +9,7 @@ cflags += -DCJIT_BUILD_LINUX all: embed-posix cjit tinycc_config += --with-libgcc +$(info Ignore any error about sestatus below) ifeq ($(shell sestatus | awk -F': *' '/SELinux status:/ {print $2}'), enabled) tinycc_config += --with-selinux endif diff --git a/src/array.c b/src/array.c new file mode 100644 index 0000000..cb2a290 --- /dev/null +++ b/src/array.c @@ -0,0 +1,542 @@ +/*! + * @file libxutils/src/data/array.c + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (s.kalatoz@gmail.com) + * + * @brief Dynamically allocated data holder + * with some sorting and search algorithms. + */ + +#include +#include "array.h" + +xarray_data_t *XArray_NewData(void *pData, size_t nSize, uint32_t nKey) +{ + xarray_data_t *pNewData = (xarray_data_t*)malloc(sizeof(xarray_data_t)); + if (pNewData == NULL) return NULL; + + if (pData != NULL && nSize > 0) + { + pNewData->pData = malloc(nSize + 1); + if (pNewData->pData == NULL) + { + free(pNewData); + return NULL; + } + + memcpy(pNewData->pData, pData, nSize); + pNewData->nSize = nSize; + } + else + { + pNewData->pData = pData; + pNewData->nSize = 0; + } + + pNewData->nKey = nKey; + return pNewData; +} + +void XArray_FreeData(xarray_data_t *pArrData) +{ + if (pArrData != NULL) + { + if (pArrData->pData && + pArrData->nSize > 0) + free(pArrData->pData); + + free(pArrData); + } +} + +void XArray_ClearData(xarray_t *pArr, xarray_data_t *pArrData) +{ + if (pArr != NULL && + pArrData != NULL && + pArr->clearCb != NULL) + { + pArr->clearCb(pArrData); + pArrData->pData = NULL; + pArrData->nSize = 0; + } + + XArray_FreeData(pArrData); +} + +void* XArray_Init(xarray_t *pArr, size_t nSize, uint8_t nFixed) +{ + pArr->eStatus = XARRAY_STATUS_EMPTY; + pArr->clearCb = NULL; + pArr->pData = NULL; + pArr->nSize = nSize; + pArr->nFixed = nFixed; + pArr->nAlloc = 0; + pArr->nUsed = 0; + + if (nSize) + { + pArr->pData = (xarray_data_t**)malloc(nSize * sizeof(xarray_data_t*)); + if (pArr->pData == NULL) return NULL; + } + + size_t i; + for (i = 0; i < nSize; i++) + pArr->pData[i] = NULL; + + return pArr->pData; +} + +xarray_t* XArray_New(size_t nSize, uint8_t nFixed) +{ + xarray_t *pArr = (xarray_t*)malloc(sizeof(xarray_t)); + if (pArr == NULL) return NULL; + + if (XArray_Init(pArr, nSize, nFixed) == NULL && nSize) + { + free(pArr); + return NULL; + } + + pArr->nAlloc = 1; + return pArr; +} + +void XArray_Clear(xarray_t *pArr) +{ + if (pArr->pData != NULL) + { + size_t i; + for (i = 0; i < pArr->nSize; i++) + { + XArray_ClearData(pArr, pArr->pData[i]); + pArr->pData[i] = NULL; + } + } + + pArr->eStatus = XARRAY_STATUS_EMPTY; + pArr->nUsed = 0; +} + +void XArray_Destroy(xarray_t *pArr) +{ + XArray_Clear(pArr); + if (pArr->pData != NULL) + { + free(pArr->pData); + pArr->pData = NULL; + } + + pArr->nSize = 0; + pArr->nFixed = 0; + + if (pArr->nAlloc) + free(pArr); +} + +void XArray_Free(xarray_t **ppArr) +{ + if (ppArr == NULL || + *ppArr == NULL) return; + + xarray_t *pArr = *ppArr; + XArray_Destroy(pArr); + *ppArr = NULL; +} + +uint8_t XArray_Contains(xarray_t *pArr, size_t nIndex) +{ + if (pArr == NULL || nIndex >= pArr->nSize) return 0; + return pArr->pData[nIndex] != NULL ? 1 : 0; +} + +size_t XArray_Realloc(xarray_t *pArr) +{ + if (pArr->nFixed) return pArr->nSize; + size_t nSize = 0, nUsed = pArr->nUsed; + float fQuotient = (float)nUsed / (float)pArr->nSize; + + if (nUsed && nUsed == pArr->nSize) nSize = pArr->nSize * 2; + else if (nUsed && fQuotient < 0.25) nSize = pArr->nSize / 2; + + if (nSize) + { + xarray_data_t **pData = (xarray_data_t**)malloc(sizeof(xarray_data_t*) * nSize); + if (pData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return 0; + } + + size_t nCopySize = sizeof(xarray_data_t*) * pArr->nSize; + memcpy(pData, pArr->pData, nCopySize); + + free(pArr->pData); + pArr->pData = pData; + pArr->nSize = nSize; + + size_t i; + for (i = nUsed; i < nSize; i++) + pArr->pData[i] = NULL; + } + + return pArr->nSize; +} + +size_t XArray_CheckSpace(xarray_t *pArr) +{ + if (pArr == NULL) return 0; + + if (pArr->pData == NULL) + { + uint8_t nAlloc = pArr->nAlloc; + xarray_clear_cb_t clearCb = pArr->clearCb; + XArray_Init(pArr, XARRAY_INITIAL_SIZE, 0); + pArr->clearCb = clearCb; + pArr->nAlloc = nAlloc; + } + else if (pArr->nUsed >= pArr->nSize) + return XArray_Realloc(pArr); + + return pArr->pData == NULL ? 0 : 1; +} + +int XArray_Add(xarray_t *pArr, xarray_data_t *pNewData) +{ + if (!XArray_CheckSpace(pArr)) + { + XArray_ClearData(pArr, pNewData); + return XARRAY_FAILURE; + } + + pArr->pData[pArr->nUsed++] = pNewData; + return (int)pArr->nUsed - 1; +} + +int XArray_AddData(xarray_t *pArr, void *pData, size_t nSize) +{ + if (pArr == NULL) return XARRAY_FAILURE; + xarray_data_t *pNewData = XArray_NewData(pData, nSize, 0); + + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return XARRAY_FAILURE; + } + + return XArray_Add(pArr, pNewData); +} + +int XArray_PushData(xarray_t *pArr, void *pData, size_t nSize) +{ + xarray_data_t *pNewData = XArray_NewData(pData, 0, 0); + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return XARRAY_FAILURE; + } + + pNewData->nSize = nSize; + return XArray_Add(pArr, pNewData); +} + +int XArray_AddDataKey(xarray_t *pArr, void *pData, size_t nSize, uint32_t nKey) +{ + xarray_data_t *pNewData = XArray_NewData(pData, nSize, nKey); + + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return XARRAY_FAILURE; + } + + return XArray_Add(pArr, pNewData); +} + +xarray_data_t* XArray_Get(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return NULL; + return pArr->pData[nIndex]; +} + +void* XArray_GetData(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return NULL; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->pData : NULL; +} + +void* XArray_GetDataOr(xarray_t *pArr, size_t nIndex, void *pRet) +{ + if (nIndex >= pArr->nSize) return pRet; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->pData : pRet; +} + +size_t XArray_GetSize(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return 0; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->nSize : 0; +} + +uint32_t XArray_GetKey(xarray_t *pArr, size_t nIndex) +{ + if (nIndex >= pArr->nSize) return 0; + xarray_data_t *pArrData = pArr->pData[nIndex]; + return pArrData ? pArrData->nKey : 0; +} + +xarray_data_t* XArray_Remove(xarray_t *pArr, size_t nIndex) +{ + xarray_data_t *pData = XArray_Get(pArr, nIndex); + if (pData == NULL) return NULL; + + size_t i; + for (i = nIndex; i < pArr->nUsed; i++) + { + if ((i + 1) >= pArr->nUsed) break; + pArr->pData[i] = pArr->pData[i+1]; + } + + pArr->pData[--pArr->nUsed] = NULL; + XArray_Realloc(pArr); + return pData; +} + +void XArray_Delete(xarray_t *pArr, size_t nIndex) +{ + xarray_data_t *pData = XArray_Get(pArr, nIndex); + if (pData != NULL) XArray_ClearData(pArr, pData); +} + +xarray_data_t* XArray_Set(xarray_t *pArr, size_t nIndex, xarray_data_t *pNewData) +{ + xarray_data_t *pOldData = NULL; + if (nIndex < pArr->nSize) + { + pOldData = pArr->pData[nIndex]; + pArr->pData[nIndex] = pNewData; + } + + return pOldData; +} + +xarray_data_t* XArray_SetData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize) +{ + xarray_data_t *pNewData = XArray_NewData(pData, nSize, 0); + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return NULL; + } + + xarray_data_t *pOldData = XArray_Set(pArr, nIndex, pNewData); + return pOldData; +} + +xarray_data_t* XArray_Insert(xarray_t *pArr, size_t nIndex, xarray_data_t *pData) +{ + if (!XArray_CheckSpace(pArr)) return NULL; + + xarray_data_t *pOldData = XArray_Set(pArr, nIndex, pData); + if (pOldData == NULL) return NULL; + + size_t i, nNextIndex = nIndex + 1; + + for (i = nNextIndex; i < pArr->nUsed; i++) + pOldData = XArray_Set(pArr, i, pOldData); + + XArray_Add(pArr, pOldData); + return pArr->pData[nNextIndex]; +} + +xarray_data_t* XArray_InsertData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize) +{ + if (!XArray_CheckSpace(pArr)) return NULL; + + xarray_data_t *pNewData = XArray_NewData(pData, nSize, 0); + if (pNewData == NULL) + { + pArr->eStatus = XARRAY_STATUS_NO_MEMORY; + return NULL; + } + + return XArray_Insert(pArr, nIndex, pNewData); +} + +void XArray_Swap(xarray_t *pArr, size_t nIndex1, size_t nIndex2) +{ + if (nIndex1 >= pArr->nUsed || + nIndex2 >= pArr->nUsed) return; + + xarray_data_t *pData1 = pArr->pData[nIndex1]; + pArr->pData[nIndex1] = pArr->pData[nIndex2]; + pArr->pData[nIndex2] = pData1; +} + +static int XArray_CompareSize(const void *pData1, const void *pData2, void *pCtx) +{ + (void)pCtx; + xarray_data_t *pFirst = (xarray_data_t*)pData1; + xarray_data_t *pSecond = (xarray_data_t*)pData2; + return (int)pFirst->nSize - (int)pSecond->nSize; +} + +static int XArray_CompareKey(const void *pData1, const void *pData2, void *pCtx) +{ + (void)pCtx; + xarray_data_t *pFirst = (xarray_data_t*)pData1; + xarray_data_t *pSecond = (xarray_data_t*)pData2; + return (int)pFirst->nKey - (int)pSecond->nKey; +} + +int XArray_Partitioning(xarray_t *pArr, xarray_comparator_t compare, void *pCtx, int nStart, int nFinish) +{ + int nPivot = nStart; + while(1) + { + while (compare((void*)pArr->pData[nStart], (void*)pArr->pData[nPivot], pCtx) < 0) nStart++; + while (compare((void*)pArr->pData[nFinish], (void*)pArr->pData[nPivot], pCtx) > 0) nFinish--; + if (!compare((void*)pArr->pData[nStart], (void*)pArr->pData[nFinish], pCtx)) nFinish--; + if (nStart >= nFinish) return nStart; + XArray_Swap(pArr, nStart, nFinish); + } + + return nStart; +} + +void XArray_QuickSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx, int nStart, int nFinish) +{ + if (nStart < nFinish) + { + int nPartitioning = XArray_Partitioning(pArr, compare, pCtx, nStart, nFinish); + XArray_QuickSort(pArr, compare, pCtx, nStart, nPartitioning); + XArray_QuickSort(pArr, compare, pCtx, nPartitioning+1, nFinish); + } +} + +void XArray_Sort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx) +{ + if (pArr == NULL || !pArr->nUsed) return; + XArray_QuickSort(pArr, compare, pCtx, 0, (int)pArr->nUsed-1); +} + +void XArray_SortBy(xarray_t *pArr, int nSortBy) +{ + if (nSortBy == XARRAY_SORTBY_SIZE) + XArray_Sort(pArr, XArray_CompareSize, NULL); + else if (nSortBy == XARRAY_SORTBY_KEY) + XArray_Sort(pArr, XArray_CompareKey, NULL); +} + +void XArray_BubbleSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx) +{ + if (pArr == NULL || !pArr->nUsed) return; + size_t i, j; + + for (i = 0; i < pArr->nUsed-1; i++) + { + for (j = 0 ; j < pArr->nUsed-i-1; j++) + { + if (compare((void*)pArr->pData[j], (void*)pArr->pData[j+1], pCtx)) + { + xarray_data_t *pData = pArr->pData[j]; + pArr->pData[j] = pArr->pData[j+1]; + pArr->pData[j+1] = pData; + } + } + } +} + +int XArray_LinearSearch(xarray_t *pArr, uint32_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + size_t i = 0; + + for (i = 0; i < pArr->nUsed; i++) + { + xarray_data_t *pData = pArr->pData[i]; + if (nKey == pData->nKey) return (int)i; + } + + return XARRAY_FAILURE; +} + +int XArray_SentinelSearch(xarray_t *pArr, uint32_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + int i, nRet = 0, nLast = (int)pArr->nUsed - 1; + + xarray_data_t *pLast = pArr->pData[nLast]; + if (pLast->nKey == nKey) return nLast; + + xarray_data_t term = {.nKey = nKey, .nSize = 0, .pData = NULL}; + pArr->pData[nLast] = &term; + + for (i = 0;; i++) + { + xarray_data_t *pData = pArr->pData[i]; + if (nKey == pData->nKey) + { + pArr->pData[nLast] = pLast; + nRet = (i < nLast) ? i : -1; + break; + } + } + + return nRet; +} + +int XArray_DoubleSearch(xarray_t *pArr, uint32_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + int nFront = 0, nBack = (int)pArr->nUsed - 1; + + while (nFront <= nBack) + { + xarray_data_t *pData = pArr->pData[nFront]; + if (nKey == pData->nKey) return nFront; + + pData = pArr->pData[nBack]; + if (nKey == pData->nKey) return nBack; + + nFront++; + nBack--; + } + + return XARRAY_FAILURE; +} + +int XArray_BinarySearch(xarray_t *pArr, uint32_t nKey) +{ + if (pArr == NULL || !pArr->nUsed) return XARRAY_FAILURE; + int nLeft = 0, nRight = (int)pArr->nUsed - 1; + + xarray_data_t *pData = pArr->pData[nLeft]; + if (pData->nKey == nKey) return nLeft; + + while (nLeft <= nRight) + { + int nMiddle = nLeft + (nRight - nLeft) / 2; + pData = pArr->pData[nMiddle]; + + if (pData->nKey < nKey) nLeft = nMiddle + 1; + else if (pData->nKey == nKey) return nMiddle; + else nRight = nMiddle - 1; + } + + return XARRAY_FAILURE; +} + +size_t XArray_Used(xarray_t *pArr) +{ + if (pArr == NULL) return 0; + return pArr->nUsed; +} + +size_t XArray_Size(xarray_t *pArr) +{ + if (pArr == NULL) return 0; + return pArr->nSize; +} diff --git a/src/array.h b/src/array.h new file mode 100644 index 0000000..3cdb7e4 --- /dev/null +++ b/src/array.h @@ -0,0 +1,101 @@ +/*! + * @file libxutils/src/data/array.h + * + * This source is part of "libxutils" project + * 2015-2020 Sun Dro (s.kalatoz@gmail.com) + * + * @brief Dynamically allocated data holder + * with some sorting and search algorithms. + */ + +#ifndef __XUTILS_XARRAY_H__ +#define __XUTILS_XARRAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define XARRAY_SUCCESS 0 +#define XARRAY_FAILURE -1 + +#define XARRAY_INITIAL_SIZE 8 +#define XARRAY_SORTBY_SIZE 1 +#define XARRAY_SORTBY_KEY 0 + +typedef enum { + XARRAY_STATUS_OK = (uint8_t)0, + XARRAY_STATUS_EMPTY, + XARRAY_STATUS_NO_MEMORY +} xarray_status_t; + +typedef struct XArrayData { + void* pData; + size_t nSize; + uint32_t nKey; +} xarray_data_t; + +typedef int(*xarray_comparator_t)(const void*, const void*, void*); +typedef void(*xarray_clear_cb_t)(xarray_data_t *pArrData); + +typedef struct XArray_ { + xarray_data_t** pData; + xarray_clear_cb_t clearCb; + xarray_status_t eStatus; + uint8_t nFixed; + uint8_t nAlloc; + size_t nSize; + size_t nUsed; +} xarray_t; + +xarray_data_t *XArray_NewData(void *pData, size_t nSize, uint32_t nKey); +void XArray_FreeData(xarray_data_t *pArrData); +void XArray_ClearData(xarray_t *pArr, xarray_data_t *pArrData); + +xarray_t* XArray_New(size_t nSize, uint8_t nFixed); +void* XArray_Init(xarray_t *pArr, size_t nSize, uint8_t nFixed); +size_t XArray_Realloc(xarray_t *pArr); +void XArray_Destroy(xarray_t *pArr); +void XArray_Clear(xarray_t *pArr); +void XArray_Free(xarray_t **ppArr); + +int XArray_Add(xarray_t *pArr, xarray_data_t *pNewData); +int XArray_AddData(xarray_t *pArr, void* pData, size_t nSize); +int XArray_PushData(xarray_t *pArr, void *pData, size_t nSize); +int XArray_AddDataKey(xarray_t *pArr, void* pData, size_t nSize, uint32_t nKey); +void* XArray_GetDataOr(xarray_t *pArr, size_t nIndex, void *pRet); +void* XArray_GetData(xarray_t *pArr, size_t nIndex); +size_t XArray_GetSize(xarray_t *pArr, size_t nIndex); +uint32_t XArray_GetKey(xarray_t *pArr, size_t nIndex); +uint8_t XArray_Contains(xarray_t *pArr, size_t nIndex); + +xarray_data_t* XArray_Remove(xarray_t *pArr, size_t nIndex); +void XArray_Delete(xarray_t *pArr, size_t nIndex); +void XArray_Swap(xarray_t *pArr, size_t nIndex1, size_t nIndex2); + +void XArray_Sort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx); +void XArray_BubbleSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx); +void XArray_QuickSort(xarray_t *pArr, xarray_comparator_t compare, void *pCtx, int nStart, int nFinish); +void XArray_SortBy(xarray_t *pArr, int nSortBy); + +int XArray_SentinelSearch(xarray_t *pArr, uint32_t nKey); +int XArray_LinearSearch(xarray_t *pArr, uint32_t nKey); +int XArray_DoubleSearch(xarray_t *pArr, uint32_t nKey); +int XArray_BinarySearch(xarray_t *pArr, uint32_t nKey); + +xarray_data_t* XArray_Get(xarray_t *pArr, size_t nIndex); +xarray_data_t* XArray_Set(xarray_t *pArr, size_t nIndex, xarray_data_t *pNewData); +xarray_data_t* XArray_Insert(xarray_t *pArr, size_t nIndex, xarray_data_t *pData); +xarray_data_t* XArray_SetData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize); +xarray_data_t* XArray_InsertData(xarray_t *pArr, size_t nIndex, void *pData, size_t nSize); + +size_t XArray_Used(xarray_t *pArr); +size_t XArray_Size(xarray_t *pArr); + +#ifdef __cplusplus +} +#endif + +#endif /* __XUTILS_XARRAY_H__ */ diff --git a/src/cjit.c b/src/cjit.c index b6b3d59..50fd473 100644 --- a/src/cjit.c +++ b/src/cjit.c @@ -20,7 +20,8 @@ #include #include #include - +#include +#include #include #include #include // _err/_out @@ -38,6 +39,8 @@ #define tcc(cjit) (TCCState*)cjit->TCC #define setup if(!cjit->done_setup)cjit_setup(cjit) #define debug(fmt,par) if(cjit->verbose)_err(fmt,par) +#define add(buf,s) if(s!=NULL)XArray_AddData((xarray_t*)cjit->buf,(char*)s,strlen(s)+1); else _err("!!! NULL var added to array: %s","buf") + // declared at bottom void _out(const char *fmt, ...); void _err(const char *fmt, ...); @@ -47,19 +50,21 @@ extern void win_compat_usleep(unsigned int microseconds); extern ssize_t win_compat_getline(char **lineptr, size_t *n, FILE *stream); extern bool get_winsdkpath(char *dst, size_t destlen); +// from file.c +extern char *new_abspath(const char *path); + static bool cjit_mkdtemp(CJITState *cjit) { char tempDir[MAX_PATH]; #if defined(WINDOWS) char tempPath[MAX_PATH]; - char filename [64]; - snprintf(filename,63,"CJIT-%s",VERSION); + char dirname [64]; + snprintf(dirname,63,"CJIT-%s",VERSION); // Get the temporary path if (GetTempPath(MAX_PATH, tempPath) == 0) { _err("Failed to get temporary path"); return false; } - PathCombine(tempDir, tempPath, filename); - // return already if found existing + cwk_path_join(tempPath,dirname,tempDir,MAX_PATH); DWORD attributes = GetFileAttributes(tempDir); if (attributes == INVALID_FILE_ATTRIBUTES) { // The path does not exist @@ -136,6 +141,10 @@ CJITState* cjit_new() { } // error handler callback for TCC tcc_set_error_func(tcc(cjit), stderr, cjit_tcc_handle_error); + // initialize internal arrays + cjit->sources = (void*)XArray_New(2, 0); + cjit->libs = (void*)XArray_New(2, 0); + cjit->libpaths = (void*)XArray_New(2, 0); return(cjit); } @@ -155,7 +164,7 @@ static bool cjit_setup(CJITState *cjit) { _err("CFLAGS: %s",extra_cflags); tcc_set_options(tcc(cjit), extra_cflags); } -#if defined(_WIN32) +#if defined(WINDOWS) // add symbols for windows compatibility tcc_add_symbol(tcc(cjit), "usleep", &win_compat_usleep); tcc_add_symbol(tcc(cjit), "getline", &win_compat_getline); @@ -165,39 +174,53 @@ static bool cjit_setup(CJITState *cjit) { tcc_define_symbol(tcc(cjit),"SDL_MAIN_HANDLED",NULL); // where is libtcc1.a found - tcc_add_library_path(tcc(cjit), cjit->tmpdir); - - // tcc_set_lib_path(TCC,tmpdir); // this overrides all? - + // add(libpaths,cjit->tmpdir); + // tinyCC needs libtcc1.a in library path (not added as file) + tcc_add_library_path(tcc(cjit),cjit->tmpdir); + { // search libs also in current dir + char pwd[MAX_PATH]; + // Get the current working directory + if(getcwd(pwd, MAX_PATH)) { + add(libpaths,pwd); + } + } tcc_add_sysinclude_path(tcc(cjit), cjit->tmpdir); - tcc_add_sysinclude_path(tcc(cjit), "."); - tcc_add_sysinclude_path(tcc(cjit), "include"); - tcc_add_library_path(tcc(cjit), "."); +// tcc_add_sysinclude_path(tcc(cjit), "."); +// tcc_add_sysinclude_path(tcc(cjit), "include"); // TODO: check if exists -#if defined(_WIN32) +#if defined(WINDOWS) { - // windows system32 libraries - //tcc_add_library_path(TCC, "C:\\Windows\\System32") - // 64bit + size_t plen; + char *tpath; + // TODO: support WIN32 here: "C:\\Windows\\System32" + add(libpaths,"C:\\Windows\\SysWOW64"); tcc_add_library_path(tcc(cjit), "C:\\Windows\\SysWOW64"); // tinycc win32 headers - char *tpath = malloc(strlen(cjit->tmpdir)+32); - strcpy(tpath,cjit->tmpdir); - strcat(tpath,"/tinycc_win32/winapi"); + plen = strlen(cjit->tmpdir)+strlen("/tinycc_win32/winapi")+8; + tpath = malloc(plen); + cwk_path_join(cjit->tmpdir,"/tinycc_win32/winapi",tpath,plen); tcc_add_sysinclude_path(tcc(cjit), tpath); free(tpath); // windows SDK headers char *sdkpath = malloc(512); if( get_winsdkpath(sdkpath,511) ) { - int pathend = strlen(sdkpath); - strcpy(&sdkpath[pathend],"\\um"); // um/GL - tcc_add_sysinclude_path(tcc(cjit), sdkpath); - strcpy(&sdkpath[pathend],"\\shared"); // winapifamili.h etc. - tcc_add_sysinclude_path(tcc(cjit), sdkpath); + plen = strlen(sdkpath)+16; + tpath = malloc(plen); + cwk_path_join(sdkpath,"/um",tpath,plen); // um/GL + tcc_add_sysinclude_path(tcc(cjit), tpath); + cwk_path_join(sdkpath,"/shared",tpath,plen); // winapifamili.h etc. + tcc_add_sysinclude_path(tcc(cjit), tpath); + free(tpath); } free(sdkpath); } #endif + +#if defined(UNIX) + read_ldsoconf(cjit->libpaths,"/etc/ld.so.conf"); + read_ldsoconf_dir(cjit->libpaths,"/etc/ld.so.conf.d"); +#endif + cjit->done_setup = true; return(true); } @@ -234,6 +257,34 @@ bool cjit_status(CJITState *cjit) { #if !(defined TCC_TARGET_PE || defined TCC_TARGET_MACHO) _err("ELF interpreter: %s",CONFIG_TCC_ELFINTERP); #endif + + //////////////////////// + // call cjit_setup here + setup; + { + int i; + size_t used; + used= XArray_Used(cjit->sources); + if(used) { + _err("Sources (%u)",used); + for(i=0;isources,i)); + } + used = XArray_Used(cjit->libpaths); + if(used) { + _err("Library paths (%u)",used); + for(i=0;ilibpaths,i); + _err("+ %s",d?d:"(null)"); + } + } + used = XArray_Used(cjit->libs); + if(used) { + _err("Libraries (%u)",used); + for(int i=0;ilibs,i)); + } + } return true; } @@ -340,14 +391,16 @@ bool cjit_add_source(CJITState *cjit, const char *path) { fread(contents+spath_len, 1, length, file); contents[length+spath_len] = 0x0; fclose(file); - size_t dirname; - cwk_path_get_dirname(path,&dirname); - if(dirname) { - char *tmp = malloc(dirname+1); - strncpy(tmp,path,dirname); - tmp[dirname] = 0x0; - tcc_add_include_path(tcc(cjit),tmp); - free(tmp); + { // if inside a dir then add dir to includes too + size_t dirname; + cwk_path_get_dirname(path,&dirname); + if(dirname) { + char *tmp = malloc(dirname+1); + strncpy(tmp,path,dirname); + tmp[dirname] = 0x0; + tcc_add_include_path(tcc(cjit),tmp); + free(tmp); + } } tcc_compile_string(tcc(cjit),contents); free(contents); @@ -521,6 +574,9 @@ void cjit_free(CJITState *cjit) { if(cjit->entry) free(cjit->entry); if(cjit->output_filename) free(cjit->output_filename); if(cjit->TCC) tcc_delete(tcc(cjit)); + XArray_Free((xarray_t**)&cjit->sources); + XArray_Free((xarray_t**)&cjit->libs); + XArray_Free((xarray_t**)&cjit->libpaths); free(cjit); } @@ -537,17 +593,31 @@ void cjit_define_symbol(CJITState *cjit, const char *sym, const char *value) { if(cjit->verbose)_err("+D %s %s",sym,value?value:""); } void cjit_add_include_path(CJITState *cjit, const char *path) { - tcc_add_include_path(tcc(cjit), path); + char *restrict toadd = new_abspath(path); + if(!toadd) { + _err("%s: absolute path error: %s",__func__,path); + return; + } + tcc_add_include_path(tcc(cjit), toadd); + free(toadd); debug("+I %s",path); } // TODO: temporary, to be reimplemented in linker.c void cjit_add_library_path(CJITState *cjit, const char *path) { - tcc_add_library_path(tcc(cjit), path); - debug("+L %s",path); + char *restrict toadd = new_abspath(path); + if(!toadd) { + _err("%s: absolute path error: %s",__func__,path); + return; + } + tcc_add_library_path(tcc(cjit), toadd); + add(libpaths,toadd); + free(toadd); + debug("+L %s",toadd); } // TODO: temporary, to be reimplemented in linker.c void cjit_add_library(CJITState *cjit, const char *path) { tcc_add_library(tcc(cjit), path); + add(libs,path); debug("+l %s",path); } void cjit_set_tcc_options(CJITState *cjit, const char *opts) { diff --git a/src/cjit.h b/src/cjit.h index 3d0eeec..5e9fef7 100644 --- a/src/cjit.h +++ b/src/cjit.h @@ -23,6 +23,10 @@ #include #include +#if !defined(MAX_PATH) +#define MAX_PATH 512 +#endif + // passed to cjit_exec with CJIT execution parameters struct CJITState { void *TCC; // the tinyCC context @@ -42,6 +46,11 @@ struct CJITState { char *output_filename; // output in case of compilation mode bool done_setup; bool done_exec; + // INTERNAL + // sources and libs used and paths to libs + void *sources; + void *libs; + void *libpaths; }; typedef struct CJITState CJITState; @@ -82,10 +91,6 @@ extern bool extract_assets(CJITState *CJIT); ///////////// // from file.c extern char* file_load(const char *filename, unsigned int *len); -extern char *load_stdin(); -extern char* dir_load(const char *path); -extern bool write_to_file(const char *path, const char *filename, - const char *buf, unsigned int len); // terminal printing functions extern void _out(const char *fmt, ...); @@ -94,8 +99,5 @@ extern void _err(const char *fmt, ...); ///////////// // from repl.c extern int cjit_cli_tty(CJITState *cjit); -#ifdef KILO_SUPPORTED -extern int cjit_cli_kilo(CJITState *cjit); -#endif #endif diff --git a/src/elflinker.c b/src/elflinker.c new file mode 100644 index 0000000..3d279d2 --- /dev/null +++ b/src/elflinker.c @@ -0,0 +1,431 @@ +/* + * Taken from ELF file handling in TCC + * + * Copyright (c) 2001-2004 Fabrice Bellard + * Copyright (c) 2024-2025 Dyne.org + * + * 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 + */ + +// interpret a subset of GNU ldscripts to handle dummy .so files + +#include +#if defined(POSIX) +#define MAX_PATH 512 +#include +#include +#include + +#define CH_EOF (-1) //end of file +#define LD_TOK_NAME 256 +#define LD_TOK_EOF (-1) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// tinyCC internals used here +extern void dynarray_add(void *ptab, int *nb_ptr, void *data); +extern void dynarray_reset(void *pp, int *n); +extern char *tcc_strdup(const char *str); +extern char *pstrcpy(char *buf, size_t buf_size, const char *s); + +bool read_ldsoconf(xarray_t *dest, char *path) { + FILE *file = fopen(path, "r"); + if (!file) { + _err("%s: error opening file: %s",__func__,path); + _err("%s: %s",strerror(errno)); + return false; + } + // TODO: optimize by allocating the buffer inside a xarray data + // struct to avoid double memcpy + char line[MAX_PATH]; + while (fgets(line, MAX_PATH, file) != NULL) { + // Skip lines that aren't paths + if (line[0] != '/') continue; + size_t len = strlen(line); + if(line[len-1]=='\n') line[len-1]=0x0; + // add the line to the result array + XArray_AddData(dest,line,len+1); + } + fclose(file); + return true; +} + +bool read_ldsoconf_dir(xarray_t *dest, const char *directory) { + DIR *dir; + struct dirent *entry; + char path[MAX_PATH]; + dir = opendir(directory); + if (dir == NULL) { + _err("%s: error reading directory: %s",__func__,directory); + return false; + } + while ((entry = readdir(dir)) != NULL) { + struct stat st; + cwk_path_join(directory,entry->d_name,path,MAX_PATH); + // Check if it's a regular file + if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { + if(! read_ldsoconf(dest,path) ) { + _err("%s: Xarray_AddData error: %s",__func__,directory); + continue; + } + } + } + closedir(dir); + return true; +} + +void detect_file_type(const char *filename) { + struct stat st; + if (lstat(filename, &st) == -1) { + perror("lstat"); + return; + } + + if (S_ISLNK(st.st_mode)) { + printf("%s is a symbolic link.\n", filename); + return; + } + + int fd = open(filename, O_RDONLY); + if (fd == -1) { + perror("open"); + return; + } + + unsigned char buffer[512]; + ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); + if (bytes_read == -1) { + perror("read"); + close(fd); + return; + } + + close(fd); + + // Check for plain text ASCII + int is_ascii = 1; + for (ssize_t i = 0; i < bytes_read; i++) { + if (buffer[i] > 127 || (buffer[i] < 32 && buffer[i] != '\n' && buffer[i] != '\r' && buffer[i] != '\t')) { + is_ascii = 0; + break; + } + } + + if (is_ascii) { + printf("%s is a plain text ASCII file.\n", filename); + return; + } + + // Check for ELF binary (common format for shared libraries) + if (bytes_read >= 4 && buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') { + printf("%s is a binary shared library (ELF).\n", filename); + return; + } + + printf("%s is an unknown binary file.\n", filename); +} + +#if defined(TEST_LINKER) +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + detect_file_type(argv[1]); + return EXIT_SUCCESS; +} +#endif + +static int ld_inp(LDState *s1) +{ + char b; + if (s1->cc != -1) { + int c = s1->cc; + s1->cc = -1; + return c; + } + if (1 == read(s1->fd, &b, 1)) + return b; + return CH_EOF; +} + +/* return next ld script token */ +static int ld_next(LDState *s1, char *name, int name_size) +{ + int c, d, ch; + char *q; + + redo: + ch = ld_inp(s1); + switch(ch) { + case ' ': + case '\t': + case '\f': + case '\v': + case '\r': + case '\n': + goto redo; + case '/': + ch = ld_inp(s1); + if (ch == '*') { /* comment */ + for (d = 0;; d = ch) { + ch = ld_inp(s1); + if (ch == CH_EOF || (ch == '/' && d == '*')) + break; + } + goto redo; + } else { + q = name; + *q++ = '/'; + goto parse_name; + } + break; + case '\\': + /* case 'a' ... 'z': */ + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + /* case 'A' ... 'z': */ + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case '.': + case '$': + case '~': + q = name; + parse_name: + for(;;) { + if (!((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + strchr("/.-_+=$:\\,~", ch))) + break; + if ((q - name) < name_size - 1) { + *q++ = ch; + } + ch = ld_inp(s1); + } + s1->cc = ch; + *q = '\0'; + c = LD_TOK_NAME; + break; + case CH_EOF: + c = LD_TOK_EOF; + break; + default: + c = ch; + break; + } + return c; +} + +static int ld_add_file(LDState *s1, const char filename[]) { + _err("LD: %s",filename); + return 1; +} + +static int tcc_error_noabort(char *msg) { + _err("Error in ldscript parser: %s",msg); + return 1; +} + +static int ld_add_file_list(LDState *s1, const char *cmd, int as_needed) +{ + char filename[1024], libname[1024]; + int t, group, nblibs = 0, ret = 0; + char **libs = NULL; + + group = !strcmp(cmd, "GROUP"); + if (!as_needed) + s1->new_undef_sym = 0; + t = ld_next(s1, filename, sizeof(filename)); + if (t != '(') { + ret = tcc_error_noabort("( expected"); + goto lib_parse_error; + } + t = ld_next(s1, filename, sizeof(filename)); + for(;;) { + libname[0] = '\0'; + if (t == LD_TOK_EOF) { + ret = tcc_error_noabort("unexpected end of file"); + goto lib_parse_error; + } else if (t == ')') { + break; + } else if (t == '-') { + t = ld_next(s1, filename, sizeof(filename)); + if ((t != LD_TOK_NAME) || (filename[0] != 'l')) { + ret = tcc_error_noabort("library name expected"); + goto lib_parse_error; + } + pstrcpy(libname, sizeof libname, &filename[1]); + if (s1->static_link) { + snprintf(filename, sizeof filename, "lib%s.a", libname); + } else { + snprintf(filename, sizeof filename, "lib%s.so", libname); + } + } else if (t != LD_TOK_NAME) { + ret = tcc_error_noabort("filename expected"); + goto lib_parse_error; + } + if (!strcmp(filename, "AS_NEEDED")) { + ret = ld_add_file_list(s1, cmd, 1); + if (ret) + goto lib_parse_error; + } else { + /* TODO: Implement AS_NEEDED support. */ + /* DT_NEEDED is not used any more so ignore as_needed */ + if (1 || !as_needed) { + ret = ld_add_file(s1, filename); + if (ret) + goto lib_parse_error; + if (group) { + /* Add the filename *and* the libname to avoid future conversions */ + dynarray_add(&libs, &nblibs, tcc_strdup(filename)); + if (libname[0] != '\0') + dynarray_add(&libs, &nblibs, tcc_strdup(libname)); + } + } + } + t = ld_next(s1, filename, sizeof(filename)); + if (t == ',') { + t = ld_next(s1, filename, sizeof(filename)); + } + } + if (group && !as_needed) { + while (s1->new_undef_sym) { + int i; + s1->new_undef_sym = 0; + for (i = 0; i < nblibs; i ++) + ld_add_file(s1, libs[i]); + } + } +lib_parse_error: + dynarray_reset(&libs, &nblibs); + return ret; +} + +// fd = open(filename, O_RDONLY | O_BINARY); +int cjit_load_ldscript(LDState *s1, char *path) { + char cmd[64]; + char filename[1024]; + int t, ret; + int fd = open(path, O_RDONLY | O_BINARY); + + s1->fd = fd; + s1->cc = -1; + for(;;) { + t = ld_next(s1, cmd, sizeof(cmd)); + if (t == LD_TOK_EOF) { + close(fd); + return 0; + } + else if (t != LD_TOK_NAME) { + close(fd); + return -1; + } + if (!strcmp(cmd, "INPUT") || + !strcmp(cmd, "GROUP")) { + ret = ld_add_file_list(s1, cmd, 0); + if (ret) { + close(fd); + return ret; + } + } else if (!strcmp(cmd, "OUTPUT_FORMAT") || + !strcmp(cmd, "TARGET")) { + /* ignore some commands */ + t = ld_next(s1, cmd, sizeof(cmd)); + if (t != '(') { + _err("%s: ( expected while parsing %s", + __func__,path); + close(fd); + return 0; + } + for(;;) { + t = ld_next(s1, filename, sizeof(filename)); + if (t == LD_TOK_EOF) { + _err("%s: unexpected end of file in %s", + __func__,path); + close(fd); + return 0; + } else if (t == ')') { + break; + } + } + } else { + close(fd); + return -1; + } + } + close(fd); + return 0; +} + +#endif // only GNU/Linux platform diff --git a/src/elflinker.h b/src/elflinker.h new file mode 100644 index 0000000..104c186 --- /dev/null +++ b/src/elflinker.h @@ -0,0 +1,18 @@ +#ifndef __ELFLINKER_H__ +#define __ELFLINKER_H__ + +#include + +struct LDState { + int cc; + int fd; + int new_undef_sym; + int static_link; +}; +typedef struct LDState LDState; + +bool read_ldsoconf(xarray_t *dest, char *path); +bool read_ldsoconf_dir(xarray_t *dest, const char *directory); +int cjit_load_ldscript(LDState *s1, char *path); + +#endif diff --git a/src/file.c b/src/file.c index a3959ce..61bfde9 100644 --- a/src/file.c +++ b/src/file.c @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -103,6 +104,48 @@ char *load_stdin() { #endif } +char *new_abspath(const char *path) { + char tpath[MAX_PATH]; + char *res = NULL; + size_t len; + if(path[0]=='.' && path[1]==0x0) { + // argument is just . + if( getcwd(tpath,MAX_PATH) ) { + res = malloc(strlen(tpath)+1); + strcpy(res,tpath); + return(res); + } + } + if(path[0]=='.' && path[1]=='/') { + if( !getcwd(tpath,MAX_PATH) ) { + _err("%s: getcwd error: %s",__func__,strerror(errno)); + return(NULL); + } + res = malloc(strlen(tpath)+1); + strcpy(res,tpath); + len = cwk_path_get_absolute(res,path,tpath,MAX_PATH); + res = realloc(res, len+1); + strcpy(res,tpath); + return(res); + } + if(path[0]!='/') { + if( !getcwd(tpath,MAX_PATH) ) { + _err("%s: getcwd error: %s",__func__,strerror(errno)); + return(NULL); + } + res = malloc(strlen(tpath)+strlen(path)+16); + strcpy(res,tpath); + len = cwk_path_get_absolute(res,path,tpath,MAX_PATH); + res = realloc(res,len+1); + strcpy(res,tpath); + return(res); + } + len = cwk_path_normalize(path,tpath,MAX_PATH); + res = malloc(len+1); + strcpy(res,tpath); + return(res); +} + bool write_to_file(const char *path, const char *filename, const char *buf, unsigned int len) { FILE *fd; size_t written; @@ -128,6 +171,7 @@ bool write_to_file(const char *path, const char *filename, const char *buf, unsi return true; } +#if 0 // unused for now, dangerous to have if unnecessary static int rm_ftw(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) { @@ -162,7 +206,10 @@ bool rm_recursive(char *path) { } return true; } +#endif + +#if 0 #if !defined(WINDOWS) static char *full_content = NULL; @@ -230,3 +277,4 @@ char *dir_load(const char *path) #endif +#endif // 0 diff --git a/src/main.c b/src/main.c index 7fdc2d0..23d2e46 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,8 @@ #include #include +extern char *load_stdin(); + #define MAX_ARG_STRING 1024 static int parse_value(char *str) { int i = 0; @@ -141,19 +143,19 @@ int main(int argc, char **argv) { strcpy(CJIT->output_filename,opt.arg); cjit_set_output(CJIT, EXE); } else if (c == 'L') { // library path - if(!CJIT->quiet)_err("lib path: %s",opt.arg); + if(CJIT->verbose)_err("arg lib path: %s",opt.arg); cjit_add_library_path(CJIT, opt.arg); } else if (c == 'l') { // library link - if(!CJIT->quiet)_err("lib: %s",opt.arg); + if(CJIT->verbose)_err("arg lib: %s",opt.arg); cjit_add_library(CJIT, opt.arg); } else if (c == 'C') { // cflags compiler options - if(!CJIT->quiet)_err("cflags: %s",opt.arg); + if(CJIT->verbose)_err("arg cflags: %s",opt.arg); cjit_set_tcc_options(CJIT->TCC, opt.arg); } else if (c == 'I') { // include paths in cflags - if(!CJIT->quiet)_err("inc: %s",opt.arg); + if(CJIT->verbose)_err("arg inc: %s",opt.arg); cjit_add_include_path(CJIT, opt.arg); } else if (c == 'e') { // entry point (default main) - if(!CJIT->quiet)_err("entry: %s",opt.arg); + if(!CJIT->quiet)_err("entry function: %s",opt.arg); if(CJIT->entry) free(CJIT->entry); CJIT->entry = malloc(strlen(opt.arg)+1); strcpy(CJIT->entry,opt.arg); diff --git a/src/platforms.h b/src/platforms.h index 7308d61..31bb0e9 100644 --- a/src/platforms.h +++ b/src/platforms.h @@ -19,18 +19,24 @@ #endif #if defined(__linux__) #define LINUX + #define POSIX + #define UNIX #define PLATFORM "GNU/Linux" #endif #if defined(__APPLE__) #define APPLE + #define POSIX #define PLATFORM "Apple/OSX" #endif #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define BSD + #define POSIX + #define UNIX #define PLATFORM "BSD" #endif #if defined(__ANDROID__) #define ANDROID + #define POSIX #define PLATFORM "Android" #endif #if defined(__EMSCRIPTEN__) @@ -39,10 +45,12 @@ #endif #if defined(__BEOS__) || defined(__HAIKU__) #define BEOS + #define POSIX #define PLATFORM "BEOS" #endif #if defined(__HAIKU__) #define HAIKU + #define POSIX #define PLATFORM "Haiku" #endif #endif diff --git a/test/linux.bats b/test/linux.bats new file mode 100644 index 0000000..4d1d6ed --- /dev/null +++ b/test/linux.bats @@ -0,0 +1,17 @@ +load bats_setup + +@test "linker resolution of simple gnu ld scripts" { + cat << EOF > ldscript_test.c +#include +int main(int argc, char **argv) { + LDState s1; + int res; + res = cjit_load_ldscript(&s1,"/usr/lib/x86_64-linux-gnu/libm.so"); + return(res); +} +EOF + run ${CJIT} -DVERSION=debug --verb ldscript_test.c \ + -I ${R}/src ${R}/src/cjit.c ${R}/src/linker.c \ + ${R}/lib/tinycc/libtcc.a + assert_success +}