diff --git a/scripts/build.sh b/scripts/build.sh index d653f32..4e73892 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -70,12 +70,13 @@ $TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/datetime.cpp -o $OUTPUT/lib/$BUIL $TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/regex.cpp -o $OUTPUT/lib/$BUILD/$MM/regex.o $TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/thread.cpp -o $OUTPUT/lib/$BUILD/$MM/thread.o $TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/http_linux.cpp -o $OUTPUT/lib/$BUILD/$MM/http_linux.o +$TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/memory.cpp -o $OUTPUT/lib/$BUILD/$MM/memory.o $BIN_PATH/$TOOL_NAME $DBG_OPTS $MM_OPT --emit=obj --export=none --nowarn --no-default-lib $SRC/src/lib.linux.ts $PIC -o $OUTPUT/lib/$BUILD/$MM/lib.linux.o # Build Lib $BIN_PATH/$TOOL_NAME $DBG_OPTS $MM_OPT --emit=obj --export=none --nowarn --no-default-lib $SRC/src/lib.ts $PIC -o $OUTPUT/lib/$BUILD/$MM/lib.o -$ARC rcs $OUTPUT/lib/$BUILD/$MM/libTypeScriptDefaultLibCore.a $OUTPUT/lib/$BUILD/$MM/lib.o $OUTPUT/lib/$BUILD/$MM/lib.linux.o $OUTPUT/lib/$BUILD/$MM/io.o $OUTPUT/lib/$BUILD/$MM/datetime.o $OUTPUT/lib/$BUILD/$MM/regex.o $OUTPUT/lib/$BUILD/$MM/thread.o $OUTPUT/lib/$BUILD/$MM/http_linux.o +$ARC rcs $OUTPUT/lib/$BUILD/$MM/libTypeScriptDefaultLibCore.a $OUTPUT/lib/$BUILD/$MM/lib.o $OUTPUT/lib/$BUILD/$MM/lib.linux.o $OUTPUT/lib/$BUILD/$MM/io.o $OUTPUT/lib/$BUILD/$MM/datetime.o $OUTPUT/lib/$BUILD/$MM/regex.o $OUTPUT/lib/$BUILD/$MM/thread.o $OUTPUT/lib/$BUILD/$MM/http_linux.o $OUTPUT/lib/$BUILD/$MM/memory.o # A static archive cannot record its own dependencies on ELF (there is no ld.bfd equivalent of # the `#pragma comment(lib, ...)` http.cpp uses on Windows), so programs linking @@ -93,7 +94,7 @@ EOF # undefined on purpose: they must bind to the one collector already in the process # (libTypeScriptRuntime.so under the JIT, libgc.a in an exe); linking libgc.a in here would # give the library a second, separate heap. -g++ -shared $DBG_GCC $OUTPUT/lib/$BUILD/$MM/lib.o $OUTPUT/lib/$BUILD/$MM/lib.linux.o $OUTPUT/lib/$BUILD/$MM/io.o $OUTPUT/lib/$BUILD/$MM/datetime.o $OUTPUT/lib/$BUILD/$MM/regex.o $OUTPUT/lib/$BUILD/$MM/thread.o $OUTPUT/lib/$BUILD/$MM/http_linux.o -lcurl -lm -o $OUTPUT/dll/$BUILD/$MM/libTypeScriptDefaultLib.so +g++ -shared $DBG_GCC $OUTPUT/lib/$BUILD/$MM/lib.o $OUTPUT/lib/$BUILD/$MM/lib.linux.o $OUTPUT/lib/$BUILD/$MM/io.o $OUTPUT/lib/$BUILD/$MM/datetime.o $OUTPUT/lib/$BUILD/$MM/regex.o $OUTPUT/lib/$BUILD/$MM/thread.o $OUTPUT/lib/$BUILD/$MM/http_linux.o $OUTPUT/lib/$BUILD/$MM/memory.o -lcurl -lm -o $OUTPUT/dll/$BUILD/$MM/libTypeScriptDefaultLib.so # Copy # Stage into a single shared defaultlib tree with per-build subfolders under dll/ and lib/, diff --git a/scripts/build_android.bat b/scripts/build_android.bat index 04b14c8..8daed45 100644 --- a/scripts/build_android.bat +++ b/scripts/build_android.bat @@ -102,14 +102,14 @@ if exist "%OBJ%" rd /s /q "%OBJ%" if exist "%LIB_OUT%" rd /s /q "%LIB_OUT%" md "%OBJ%" "%LIB_OUT%" -for %%w in (io datetime regex thread http_stub) do ( +for %%w in (io datetime regex thread http_stub memory) do ( %CXX% -c "%ROOT%\src\wrappers\%%w.cpp" -o "%OBJ%\%%w.o" || exit /b 1 ) %TSC% "%ROOT%\src\lib.linux.ts" -o "%OBJ%\lib.linux.o" || exit /b 1 %TSC% "%ROOT%\src\lib.ts" -o "%OBJ%\lib.o" || exit /b 1 -set OBJS="%OBJ%\lib.o" "%OBJ%\lib.linux.o" "%OBJ%\io.o" "%OBJ%\datetime.o" "%OBJ%\regex.o" "%OBJ%\thread.o" "%OBJ%\http_stub.o" +set OBJS="%OBJ%\lib.o" "%OBJ%\lib.linux.o" "%OBJ%\io.o" "%OBJ%\datetime.o" "%OBJ%\regex.o" "%OBJ%\thread.o" "%OBJ%\http_stub.o" "%OBJ%\memory.o" %AR% rcs "%LIB_OUT%\libTypeScriptDefaultLib.a" %OBJS% || exit /b 1 rd /s /q "%OBJ%" diff --git a/scripts/build_core.bat b/scripts/build_core.bat index d546d4d..9a8069b 100644 --- a/scripts/build_core.bat +++ b/scripts/build_core.bat @@ -205,6 +205,7 @@ rem Build native wrappers for C++ code %TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\regex.cpp %TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\thread.cpp %TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\http.cpp +%TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\memory.cpp rem Build OS-specific Lib echo Build OS-specific Lib @@ -212,7 +213,7 @@ echo Build OS-specific Lib rem Build DLL echo Build DLL -%TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=dll --gc-lib-path=%GC_SHARED_LIB_PATH% %TSLANG_RUNTIME_OPT% --embed-declarations=false --nowarn --no-default-lib %SRC%\src\lib.ts --obj=%OUTPUT%\%LIB_OUT%\lib.win32.obj --obj=%OUTPUT%\%LIB_OUT%\io.obj --obj=%OUTPUT%\%LIB_OUT%\datetime.obj --obj=%OUTPUT%\%LIB_OUT%\regex.obj --obj=%OUTPUT%\%LIB_OUT%\thread.obj --obj=%OUTPUT%\%LIB_OUT%\http.obj -o %OUTPUT%\%DLL_OUT%\TypeScriptDefaultLib.dll +%TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=dll --gc-lib-path=%GC_SHARED_LIB_PATH% %TSLANG_RUNTIME_OPT% --embed-declarations=false --nowarn --no-default-lib %SRC%\src\lib.ts --obj=%OUTPUT%\%LIB_OUT%\lib.win32.obj --obj=%OUTPUT%\%LIB_OUT%\io.obj --obj=%OUTPUT%\%LIB_OUT%\datetime.obj --obj=%OUTPUT%\%LIB_OUT%\regex.obj --obj=%OUTPUT%\%LIB_OUT%\thread.obj --obj=%OUTPUT%\%LIB_OUT%\http.obj --obj=%OUTPUT%\%LIB_OUT%\memory.obj -o %OUTPUT%\%DLL_OUT%\TypeScriptDefaultLib.dll rem Build Lib echo Build Lib @@ -220,7 +221,7 @@ echo Build Lib rem %TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=llvm --export=none %SRC%\src\lib.ts -o %OUTPUT%\%LIB_OUT%\lib.ll rem %TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=mlir --export=none %SRC%\src\lib.ts 2> %OUTPUT%\%LIB_OUT%\lib.mlir -%TSLANG_AR% /out:%OUTPUT%\%LIB_OUT%\TypeScriptDefaultLib.lib %OUTPUT%\%LIB_OUT%\lib.obj %OUTPUT%\%LIB_OUT%\lib.win32.obj %OUTPUT%\%LIB_OUT%\io.obj %OUTPUT%\%LIB_OUT%\datetime.obj %OUTPUT%\%LIB_OUT%\regex.obj %OUTPUT%\%LIB_OUT%\thread.obj %OUTPUT%\%LIB_OUT%\http.obj +%TSLANG_AR% /out:%OUTPUT%\%LIB_OUT%\TypeScriptDefaultLib.lib %OUTPUT%\%LIB_OUT%\lib.obj %OUTPUT%\%LIB_OUT%\lib.win32.obj %OUTPUT%\%LIB_OUT%\io.obj %OUTPUT%\%LIB_OUT%\datetime.obj %OUTPUT%\%LIB_OUT%\regex.obj %OUTPUT%\%LIB_OUT%\thread.obj %OUTPUT%\%LIB_OUT%\http.obj %OUTPUT%\%LIB_OUT%\memory.obj del %OUTPUT%\%LIB_OUT%\lib.obj del %OUTPUT%\%LIB_OUT%\lib.win32.obj @@ -229,6 +230,7 @@ del %OUTPUT%\%LIB_OUT%\datetime.obj del %OUTPUT%\%LIB_OUT%\regex.obj del %OUTPUT%\%LIB_OUT%\thread.obj del %OUTPUT%\%LIB_OUT%\http.obj +del %OUTPUT%\%LIB_OUT%\memory.obj rem Stage into a single shared defaultlib tree with per-build subfolders under rem dll\ and lib\. Only the current build's subfolders are refreshed so the diff --git a/src/lib.ts b/src/lib.ts index 5c62175..af7e9eb 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -576,7 +576,7 @@ export class RegExp // get formatted replacement const len = r.length; let result = "".clone().resize(len); - memcpy(Ref(result[0]), r, len); + boundedCopy(Ref(result[0]), len, r, len); regexp_free_string(r); regexp_free(cmatch); @@ -592,7 +592,7 @@ export class RegExp const r = regexp_replace(this.source, this.flags, s, replacement); const len = r.length; let result = "".clone().resize(len); - memcpy(Ref(result[0]), r, len); + boundedCopy(Ref(result[0]), len, r, len); regexp_free_string(r); return result; } @@ -655,7 +655,7 @@ namespace __String { const newString = this.clone().resize(count); let index = this.length; for (const item of other) { - memcpy(Ref(newString[index]), Ref(item[0]), sizeof() * item.length); + boundedCopy(Ref(newString[index]), sizeof() * (count - index), Ref(item[0]), sizeof() * item.length); index += item.length; } @@ -670,7 +670,7 @@ namespace __String { const newString = "".clone().resize(count); let index = 0; for (const item of parts) { - memcpy(Ref(newString[index]), Ref(item[0]), sizeof() * item.length); + boundedCopy(Ref(newString[index]), sizeof() * (count - index), Ref(item[0]), sizeof() * item.length); index += item.length; } @@ -881,7 +881,7 @@ namespace __String { // the clone holds the first copy already: count - 1 more fill the string, and one more // wrote past its end for (let i = 1; i < count; i++) { - memcpy(Ref(newString[index]), this, byteSize); + boundedCopy(Ref(newString[index]), sizeof() * (newSize - index), this, byteSize); index += this.length; } @@ -980,7 +980,7 @@ namespace __String { const count = indexEnd - indexStart; const newString = "".clone().resize(count); - memcpy(Ref(newString[0]), Ref(this[indexStart]), sizeof() * count); + boundedCopy(Ref(newString[0]), sizeof() * count, Ref(this[indexStart]), sizeof() * count); return newString; } @@ -1075,7 +1075,7 @@ namespace __String { const count = indexEnd - indexStart; const newString = "".clone().resize(count); - memcpy(Ref(newString[0]), Ref(this[indexStart]), sizeof() * count); + boundedCopy(Ref(newString[0]), sizeof() * count, Ref(this[indexStart]), sizeof() * count); return newString; } diff --git a/src/native/lib.native.d.ts b/src/native/lib.native.d.ts index 1f73db5..4ee225c 100644 --- a/src/native/lib.native.d.ts +++ b/src/native/lib.native.d.ts @@ -85,6 +85,9 @@ declare function trunc(x: number): number; // memory declare function memcpy(dest: Opaque, src: Opaque, bytes: index): Opaque; declare function memmove(dest: Opaque, src: Opaque, bytes: index): Opaque; +// wrappers/memory.cpp: memcpy_s on Windows, __memcpy_chk elsewhere - ends the process rather +// than copy more than `destSize` bytes +declare function boundedCopy(dest: Opaque, destSize: index, src: Opaque, count: index): void; declare function memcmp(dest: Opaque, src: Opaque, bytes: index): int; // gc weak references diff --git a/src/wrappers/memory.cpp b/src/wrappers/memory.cpp new file mode 100644 index 0000000..db0eb3e --- /dev/null +++ b/src/wrappers/memory.cpp @@ -0,0 +1,22 @@ +#include +#include + +#include "tslang_export.h" + +// A copy of `count` bytes into `dest`, which has `destSize` bytes left, that ends the process +// rather than write past them. Windows has the CRT's memcpy_s: its invalid parameter handler ends +// the process. glibc and Bionic have no memcpy_s, but have the fortified copy _FORTIFY_SOURCE +// uses, which aborts with "buffer overflow detected"; the builtin calls it, or a plain memcpy +// where the compiler can prove the copy fits. +// +// In C++, not in lib.win32.ts / lib.linux.ts: the compiler emits memcpy_s / __memcpy_chk for +// copies of its own, and a TypeScript declaration of the same function in a module that makes such +// a copy before the declaration is lowered is a second definition of the symbol. +extern "C" TSLANG_EXPORT void boundedCopy(void *dest, size_t destSize, const void *src, size_t count) +{ +#ifdef _WIN32 + memcpy_s(dest, destSize, src, count); +#else + __builtin___memcpy_chk(dest, src, count, destSize); +#endif +} diff --git a/src/wrappers/regex.cpp b/src/wrappers/regex.cpp index a7dd922..3328da0 100644 --- a/src/wrappers/regex.cpp +++ b/src/wrappers/regex.cpp @@ -53,7 +53,11 @@ extern "C" TSLANG_EXPORT char* regexp_match_results_format(std::cmatch *cm, cons std::allocator alloc; char* buffer = alloc.allocate(len + 1); +#ifdef _WIN32 + memcpy_s(buffer, len + 1, result.c_str(), len); +#else std::memcpy(buffer, result.c_str(), len); +#endif buffer[len] = 0; @@ -114,7 +118,11 @@ extern "C" TSLANG_EXPORT char* regexp_replace(const char *expr, const char *flag std::allocator alloc; char* buffer = alloc.allocate(len + 1); +#ifdef _WIN32 + memcpy_s(buffer, len + 1, result.c_str(), len); +#else std::memcpy(buffer, result.c_str(), len); +#endif buffer[len] = 0;