From e4188c6eb77158238cc79fb9fab8c0dfcea5240a Mon Sep 17 00:00:00 2001 From: kamphaus Date: Mon, 30 Apr 2018 05:53:41 +0200 Subject: [PATCH] Implement memset, memcpy and memmove. Fixes #293 (#692) --- darwin/string.go | 16 +++++- noarch/string.go | 45 ++++++++++++++++ program/function_definition.go | 15 ++++++ tests/string.c | 93 +++++++++++++++++++++++++++++++++- transpiler/unary.go | 2 +- util/goast.go | 28 ++++++++++ 6 files changed, 196 insertions(+), 3 deletions(-) diff --git a/darwin/string.go b/darwin/string.go index 4414ee5d5..3859d15fb 100644 --- a/darwin/string.go +++ b/darwin/string.go @@ -1,6 +1,8 @@ package darwin -import "github.com/elliotchance/c2go/noarch" +import ( + "github.com/elliotchance/c2go/noarch" +) // BuiltinStrcpy is for __builtin___strcpy_chk. // https://opensource.apple.com/source/Libc/Libc-498/include/secure/_string.h @@ -25,3 +27,15 @@ func BuiltinStrncpy(dest, src []byte, len, size int32) []byte { func BuiltinStrcat(dest, src []byte, _ int32) []byte { return noarch.Strcat(dest, src) } + +// Memset is for __builtin___memset_chk +// https://opensource.apple.com/source/Libc/Libc-498/include/secure/_string.h +func Memset(dst interface{}, val int32, size int32, _ int32) interface{} { + return noarch.Memset(dst, val, size) +} + +// Memcpy is for __builtin___memcpy_chk and __builtin___memmove_chk +//// https://opensource.apple.com/source/Libc/Libc-498/include/secure/_string.h +func Memcpy(dst interface{}, src interface{}, size int32, _ int32) interface{} { + return noarch.Memcpy(dst, src, size) +} diff --git a/noarch/string.go b/noarch/string.go index 49d21f5b2..b968bc624 100644 --- a/noarch/string.go +++ b/noarch/string.go @@ -2,6 +2,8 @@ package noarch import ( "bytes" + "reflect" + "unsafe" ) // Strlen returns the length of a string. @@ -95,3 +97,46 @@ func Strchr(str []byte, ch int32) []byte { } return nil } + +// Memset treats dst as a binary array and sets size bytes to the value val. +// Returns dst. +func Memset(dst interface{}, val int32, size int32) interface{} { + vDst := reflect.ValueOf(dst).Type() + switch vDst.Kind() { + case reflect.Slice, reflect.Array: + vDst = vDst.Elem() + } + baseSizeDst := int32(vDst.Size()) + data := *(*[]byte)(unsafe.Pointer(UnsafeSliceToSlice(dst, baseSizeDst, int32(1)))) + var i int32 + var vb = byte(val) + for i = 0; i < size; i++ { + data[i] = vb + } + return dst +} + +// Memcpy treats dst and src as binary arrays and copies size bytes from src to dst. +// Returns dst. +// While in C it it is undefined behavior to call memcpy with overlapping regions, +// in Go we rely on the built-in copy function, which has no such limitation. +// To copy overlapping regions in C memmove should be used, so we map that function +// to Memcpy as well. +func Memcpy(dst interface{}, src interface{}, size int32) interface{} { + vDst := reflect.ValueOf(dst).Type() + switch vDst.Kind() { + case reflect.Slice, reflect.Array: + vDst = vDst.Elem() + } + baseSizeDst := int32(vDst.Size()) + vSrc := reflect.ValueOf(src).Type() + switch vSrc.Kind() { + case reflect.Slice, reflect.Array: + vSrc = vSrc.Elem() + } + baseSizeSrc := int32(vSrc.Size()) + bDst := *(*[]byte)(unsafe.Pointer(UnsafeSliceToSlice(dst, baseSizeDst, int32(1)))) + bSrc := *(*[]byte)(unsafe.Pointer(UnsafeSliceToSlice(src, baseSizeSrc, int32(1)))) + copy(bDst[:size], bSrc[:size]) + return dst +} diff --git a/program/function_definition.go b/program/function_definition.go index 040f7c77a..3258ef3a2 100644 --- a/program/function_definition.go +++ b/program/function_definition.go @@ -192,6 +192,15 @@ var builtInFunctionDefinitions = map[string][]string{ // in according to noarch.Strlen "int strlen(const char*) -> noarch.Strlen", + // should be: "void* memset(void *, int, size_t) -> noarch.Memset" + "void* memset(void *, int, int) -> noarch.Memset", + + // should be: "void* memcpy(void *, void *, size_t) -> noarch.Memcpy" + "void* memcpy(void *, void *, int) -> noarch.Memcpy", + + // should be: "void* memmove(void *, void *, size_t) -> noarch.Memcpy" + "void* memmove(void *, void *, int) -> noarch.Memcpy", + // darwin/string.h // should be: const char*, char*, size_t "char* __builtin___strcpy_chk(const char*, char*, int) -> darwin.BuiltinStrcpy", @@ -204,6 +213,12 @@ var builtInFunctionDefinitions = map[string][]string{ // see https://opensource.apple.com/source/Libc/Libc-763.12/include/secure/_string.h.auto.html "char* __builtin___strcat_chk(char *, const char *, int) -> darwin.BuiltinStrcat", "char* __inline_strcat_chk(char *, const char *) -> noarch.Strcat", + "void* __builtin___memset_chk(void *, int, int, int) -> darwin.Memset", + "void* __inline_memset_chk(void *, int, int) -> noarch.Memset", + "void* __builtin___memcpy_chk(void *, void *, int, int) -> darwin.Memcpy", + "void* __inline_memcpy_chk(void *, void *, int) -> noarch.Memcpy", + "void* __builtin___memmove_chk(void *, void *, int, int) -> darwin.Memcpy", + "void* __inline_memmove_chk(void *, void *, int) -> noarch.Memcpy", }, "stdlib.h": []string{ // stdlib.h diff --git a/tests/string.c b/tests/string.c index 5caa3848b..ff55b7af2 100644 --- a/tests/string.c +++ b/tests/string.c @@ -1,9 +1,14 @@ #include #include "tests.h" +typedef struct mem { + int a; + int b; +} mem; + int main() { - plan(29); + plan(60); diag("TODO: __builtin_object_size") // https://github.com/elliotchance/c2go/issues/359 @@ -121,6 +126,92 @@ int main() } is_eq(amount, 4 ); } + { + diag("memset"); + char dest1[40]; + char *dest2; + char *dest3; + dest2 = (char*) memset(dest1, 'a', 4); + dest1[5] = '\0'; + is_streq(dest1, "aaaa"); + is_streq(dest2, "aaaa"); + dest3 = (char*) memset(&dest2[1], 'b', 2); + is_streq(dest1, "abba"); + is_streq(dest2, "abba"); + is_streq(dest3, "bba"); + } + { + diag("memcpy"); + char *src = "aaaabb"; + char dest1[40]; + char *dest2; + char *dest3; + dest2 = (char*) memcpy(dest1, src, 4); + dest1[4] = '\0'; + is_streq(dest1, "aaaa"); + is_streq(dest2, "aaaa"); + dest3 = (char*) memcpy(&dest2[1], &src[4], 2); + is_streq(dest1, "abba"); + is_streq(dest2, "abba"); + is_streq(dest3, "bba"); + } + { + diag("memmove"); + char *src = "aaaabb"; + char dest1[40]; + char *dest2; + char *dest3; + dest2 = (char*) memmove(dest1, src, 4); + dest1[4] = '\0'; + is_streq(dest1, "aaaa"); + is_streq(dest2, "aaaa"); + dest3 = (char*) memmove(&dest2[1], &src[4], 2); + is_streq(dest1, "abba"); + is_streq(dest2, "abba"); + is_streq(dest3, "bba"); + } + { + diag("memset & memcpy of struct / array of struct"); + int dest3 = 4; + int dest4 = 0xf; + memcpy(&dest3, &dest4, sizeof(int)); + is_eq(dest3, 0xf); + memset(&dest4, 0, sizeof(int)); + is_eq(dest4, 0); + mem dest5 = { + .a = 2, + .b = 3.0 + }; + memset(&dest5, 0, sizeof(mem)); + is_eq(dest5.a, 0); + is_eq(dest5.b, 0.0); + mem dest6[] = { + { + .a = 2, + .b = 3.0 + }, + { + .a = 4, + .b = 5.0 + } + }; + mem dest7[2]; + memcpy(dest7, dest6, sizeof(mem)*2); + memset(dest6, 0, sizeof(mem)*2); + is_eq(dest6[0].a, 0); + is_eq(dest6[0].b, 0.0); + is_eq(dest6[1].a, 0); + is_eq(dest6[1].b, 0.0); + is_eq(dest7[0].a, 2); + is_eq(dest7[0].b, 3.0); + is_eq(dest7[1].a, 4); + is_eq(dest7[1].b, 5.0); + memset(&dest7[1], 0, sizeof(mem)); + is_eq(dest7[0].a, 2); + is_eq(dest7[0].b, 3.0); + is_eq(dest7[1].a, 0); + is_eq(dest7[1].b, 0.0); + } done_testing(); } diff --git a/transpiler/unary.go b/transpiler/unary.go index 623713305..d6aefa577 100644 --- a/transpiler/unary.go +++ b/transpiler/unary.go @@ -233,7 +233,7 @@ func transpileUnaryOperatorAmpersant(n *ast.UnaryOperator, p *program.Program) ( } p.AddImport("unsafe") - expr = util.CreateSliceFromReference(resolvedType, expr) + expr = util.CreateUnlimitedSliceFromReference(resolvedType, expr) // We now have a pointer to the original type. eType += " *" diff --git a/util/goast.go b/util/goast.go index 9a2773fcf..99fdfde44 100644 --- a/util/goast.go +++ b/util/goast.go @@ -378,6 +378,34 @@ func CreateSliceFromReference(goType string, expr goast.Expr) *goast.SliceExpr { } } +// CreateUnlimitedSliceFromReference - create a slice, like : +// (*[1000000000]int)(unsafe.Pointer(&a))[:] +func CreateUnlimitedSliceFromReference(goType string, expr goast.Expr) *goast.SliceExpr { + // If the Go type is blank it means that the C type is 'void'. + if goType == "" { + goType = "interface{}" + } + + // This is a hack to convert a reference to a variable into a slice that + // points to the same location. It will look similar to: + // + // (*[1000000000]int)(unsafe.Pointer(&a))[:] + // + // You must always call this Go before using CreateUnlimitedSliceFromReference: + // + // p.AddImport("unsafe") + // + return &goast.SliceExpr{ + X: NewCallExpr( + fmt.Sprintf("(*[1000000000]%s)", goType), + NewCallExpr("unsafe.Pointer", &goast.UnaryExpr{ + X: expr, + Op: token.AND, + }), + ), + } +} + // NewFuncType - create a new function type, example: // func ...(fieldList)(returnType) func NewFuncType(fieldList *goast.FieldList, returnType string, addDefaultReturn bool) *goast.FuncType {