Skip to content

Commit

Permalink
Implement memset, memcpy and memmove. Fixes #293 (#692)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamphaus authored and elliotchance committed Apr 30, 2018
1 parent fe521e6 commit e4188c6
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 3 deletions.
16 changes: 15 additions & 1 deletion darwin/string.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
}
45 changes: 45 additions & 0 deletions noarch/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package noarch

import (
"bytes"
"reflect"
"unsafe"
)

// Strlen returns the length of a string.
Expand Down Expand Up @@ -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
}
15 changes: 15 additions & 0 deletions program/function_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
Expand Down
93 changes: 92 additions & 1 deletion tests/string.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#include <string.h>
#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
Expand Down Expand Up @@ -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();
}
2 changes: 1 addition & 1 deletion transpiler/unary.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 += " *"
Expand Down
28 changes: 28 additions & 0 deletions util/goast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit e4188c6

Please sign in to comment.