From dafaabf0b0243c30c81d6121cb1b84cfc37be806 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Sat, 13 Sep 2014 04:11:11 +0200 Subject: emulator/opengl/emugen: Use templates to read values from stream. The decoder didn't properly handle GLsizeiptr and GLintptr values, which are always 32-bit on the wire, but can be 64-bit on the host. I.e. it did something like that to read them from the stream: *(GLsizeiptr*)(ptr + offset) This fixes the issue by using templates to generate host-type-specific functions that properly read data from the stream and convert it to the appropriate host type. Change-Id: I75749bd715456ca143eb1713498f7cf635918801 --- emulator/opengl/host/tools/emugen/ApiGen.cpp | 215 ++++++++++++++------- .../shared/OpenglCodecCommon/ProtocolUtils.h | 107 ++++++++++ 2 files changed, 249 insertions(+), 73 deletions(-) create mode 100644 emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h (limited to 'emulator') diff --git a/emulator/opengl/host/tools/emugen/ApiGen.cpp b/emulator/opengl/host/tools/emugen/ApiGen.cpp index 2e4c8d1..a92ad39 100644 --- a/emulator/opengl/host/tools/emugen/ApiGen.cpp +++ b/emulator/opengl/host/tools/emugen/ApiGen.cpp @@ -761,9 +761,13 @@ int ApiGen::genDecoderImpl(const std::string &filename) fprintf(fp, "\n\n#include \n"); fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str()); + fprintf(fp, "#include \"ProtocolUtils.h\"\n\n"); fprintf(fp, "#include \n\n"); fprintf(fp, "typedef unsigned int tsize_t; // Target \"size_t\", which is 32-bit for now. It may or may not be the same as host's size_t when emugen is compiled.\n\n"); + // helper templates + fprintf(fp, "using namespace emugl;\n\n"); + // decoder switch; fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str()); fprintf(fp, @@ -808,7 +812,9 @@ int ApiGen::genDecoderImpl(const std::string &filename) } for (int pass = PASS_TmpBuffAlloc; pass < PASS_LAST; pass++) { - if (pass == PASS_FunctionCall && !e->retval().isVoid() && !e->retval().isPointer()) { + if (pass == PASS_FunctionCall && + !e->retval().isVoid() && + !e->retval().isPointer()) { fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(), totalTmpBuffOffset.c_str()); } @@ -821,9 +827,14 @@ int ApiGen::genDecoderImpl(const std::string &filename) } } else if (pass == PASS_DebugPrint) { fprintf(fp, "#ifdef DEBUG_PRINTOUT\n"); - fprintf(fp, "\t\t\tfprintf(stderr,\"%s(%%p): %s(%s)\\n\", stream", - m_basename.c_str(), e->name().c_str(), printString.c_str()); - if (e->vars().size() > 0 && !e->vars()[0].isVoid()) fprintf(fp, ","); + fprintf(fp, + "\t\t\tfprintf(stderr,\"%s(%%p): %s(%s)\\n\", stream", + m_basename.c_str(), + e->name().c_str(), + printString.c_str()); + if (e->vars().size() > 0 && !e->vars()[0].isVoid()) { + fprintf(fp, ","); + } } std::string varoffset = "8"; // skip the header @@ -831,89 +842,147 @@ int ApiGen::genDecoderImpl(const std::string &filename) // allocate memory for out pointers; for (size_t j = 0; j < evars.size(); j++) { Var *v = & evars[j]; - if (!v->isVoid()) { - if ((pass == PASS_FunctionCall) && (j != 0 || e->customDecoder())) fprintf(fp, ", "); - if (pass == PASS_DebugPrint && j != 0) fprintf(fp, ", "); + if (v->isVoid()) { + continue; + } + if ((pass == PASS_FunctionCall) && + (j != 0 || e->customDecoder())) { + fprintf(fp, ", "); + } + if (pass == PASS_DebugPrint && j != 0) { + fprintf(fp, ", "); + } - if (!v->isPointer()) { - if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) { - fprintf(fp, "*(%s *)(ptr + %s)", v->type()->name().c_str(), varoffset.c_str()); + if (!v->isPointer()) { + if (pass == PASS_FunctionCall) { + fprintf(fp, + "Unpack<%s,uint%d_t>(ptr + %s)", + v->type()->name().c_str(), + v->type()->bytes() * 8, + varoffset.c_str()); + } + if (pass == PASS_DebugPrint) { + fprintf(fp, + "*(uint%d_t *)(ptr + %s)", + v->type()->bytes() * 8, + varoffset.c_str()); + } + varoffset += " + " + toString(v->type()->bytes()); + continue; + } + + if (v->pointerDir() == Var::POINTER_IN || + v->pointerDir() == Var::POINTER_INOUT) { + if (pass == PASS_MemAlloc && + v->pointerDir() == Var::POINTER_INOUT) { + fprintf(fp, + "\t\t\tsize_t tmpPtr%uSize = (size_t)*(uint32_t *)(ptr + %s);\n", + (unsigned) j, + varoffset.c_str()); + fprintf(fp, + "unsigned char *tmpPtr%u = (ptr + %s + 4);\n", + (unsigned) j, + varoffset.c_str()); + } + if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "*((uint32_t *)(ptr + %s)) == 0 ? NULL : (%s)(ptr + %s + 4)", + varoffset.c_str(), + v->type()->name().c_str(), + varoffset.c_str()); + } else { + fprintf(fp, + "(%s)(ptr + %s + 4)", + v->type()->name().c_str(), + varoffset.c_str()); } - varoffset += " + " + toString(v->type()->bytes()); - } else { - if (v->pointerDir() == Var::POINTER_IN || v->pointerDir() == Var::POINTER_INOUT) { - if (pass == PASS_MemAlloc && v->pointerDir() == Var::POINTER_INOUT) { - fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n", - (unsigned) j, varoffset.c_str()); - fprintf(fp, "unsigned char *tmpPtr%u = (ptr + %s + 4);\n", - (unsigned) j, varoffset.c_str()); - } - if (pass == PASS_FunctionCall) { - if (v->nullAllowed()) { - fprintf(fp, "*((unsigned int *)(ptr + %s)) == 0 ? NULL : (%s)(ptr + %s + 4)", - varoffset.c_str(), v->type()->name().c_str(), varoffset.c_str()); - } else { - fprintf(fp, "(%s)(ptr + %s + 4)", - v->type()->name().c_str(), varoffset.c_str()); - } - } else if (pass == PASS_DebugPrint) { - fprintf(fp, "(%s)(ptr + %s + 4), *(unsigned int *)(ptr + %s)", - v->type()->name().c_str(), varoffset.c_str(), - varoffset.c_str()); - } - varoffset += " + 4 + *(tsize_t *)(ptr +" + varoffset + ")"; - } else { // out pointer; - if (pass == PASS_TmpBuffAlloc) { - fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n", - (unsigned) j, varoffset.c_str()); - if (!totalTmpBuffExist) { - fprintf(fp, "\t\t\tsize_t totalTmpSize = tmpPtr%uSize;\n", (unsigned)j); - } else { - fprintf(fp, "\t\t\ttotalTmpSize += tmpPtr%uSize;\n", (unsigned)j); - } - tmpBufOffset[j] = totalTmpBuffOffset; - char tmpPtrName[16]; - sprintf(tmpPtrName," + tmpPtr%uSize", (unsigned)j); - totalTmpBuffOffset += std::string(tmpPtrName); - totalTmpBuffExist = true; - } else if (pass == PASS_MemAlloc) { - fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n", - (unsigned)j, tmpBufOffset[j].c_str()); - fprintf(fp, "\t\t\tmemset(tmpPtr%u, 0, %s);\n", - (unsigned)j, - toString(v->type()->bytes()).c_str()); - } else if (pass == PASS_FunctionCall) { - if (v->nullAllowed()) { - fprintf(fp, "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u)", - (unsigned) j, v->type()->name().c_str(), (unsigned) j); - } else { - fprintf(fp, "(%s)(tmpPtr%u)", v->type()->name().c_str(), (unsigned) j); - } - } else if (pass == PASS_DebugPrint) { - fprintf(fp, "(%s)(tmpPtr%u), *(unsigned int *)(ptr + %s)", - v->type()->name().c_str(), (unsigned) j, - varoffset.c_str()); - } - varoffset += " + 4"; + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(ptr + %s + 4), *(uint32_t *)(ptr + %s)", + v->type()->name().c_str(), + varoffset.c_str(), + varoffset.c_str()); + } + varoffset += + " + 4 + *(tsize_t *)(ptr +" + varoffset + ")"; + } else { // out pointer; + if (pass == PASS_TmpBuffAlloc) { + fprintf(fp, + "\t\t\tsize_t tmpPtr%uSize = (size_t)*(uint32_t *)(ptr + %s);\n", + (unsigned) j, + varoffset.c_str()); + if (!totalTmpBuffExist) { + fprintf(fp, + "\t\t\tsize_t totalTmpSize = tmpPtr%uSize;\n", + (unsigned)j); + } else { + fprintf(fp, + "\t\t\ttotalTmpSize += tmpPtr%uSize;\n", + (unsigned)j); } + tmpBufOffset[j] = totalTmpBuffOffset; + char tmpPtrName[16]; + sprintf(tmpPtrName," + tmpPtr%uSize", (unsigned)j); + totalTmpBuffOffset += std::string(tmpPtrName); + totalTmpBuffExist = true; + } else if (pass == PASS_MemAlloc) { + fprintf(fp, + "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n", + (unsigned)j, + tmpBufOffset[j].c_str()); + fprintf(fp, + "\t\t\tmemset(tmpPtr%u, 0, %s);\n", + (unsigned)j, + toString(v->type()->bytes()).c_str()); + } else if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u)", + (unsigned) j, + v->type()->name().c_str(), + (unsigned) j); + } else { + fprintf(fp, + "(%s)(tmpPtr%u)", + v->type()->name().c_str(), + (unsigned) j); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(tmpPtr%u), *(uint32_t *)(ptr + %s)", + v->type()->name().c_str(), + (unsigned) j, + varoffset.c_str()); } + varoffset += " + 4"; } } - if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) fprintf(fp, ");\n"); - if (pass == PASS_DebugPrint) fprintf(fp, "#endif\n"); + if (pass == PASS_FunctionCall || + pass == PASS_DebugPrint) { + fprintf(fp, ");\n"); + } + if (pass == PASS_DebugPrint) { + fprintf(fp, "#endif\n"); + } if (pass == PASS_TmpBuffAlloc) { if (!e->retval().isVoid() && !e->retval().isPointer()) { if (!totalTmpBuffExist) - fprintf(fp, "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", retvalType.c_str()); + fprintf(fp, + "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", + retvalType.c_str()); else - fprintf(fp, "\t\t\ttotalTmpSize += sizeof(%s);\n", retvalType.c_str()); + fprintf(fp, + "\t\t\ttotalTmpSize += sizeof(%s);\n", + retvalType.c_str()); totalTmpBuffExist = true; } if (totalTmpBuffExist) { - fprintf(fp, "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n"); + fprintf(fp, + "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n"); } } @@ -923,8 +992,8 @@ int ApiGen::genDecoderImpl(const std::string &filename) fprintf(fp, "\t\t\tstream->flush();\n"); } - fprintf(fp, "\t\t\tpos += *(int *)(ptr + 4);\n"); - fprintf(fp, "\t\t\tptr += *(int *)(ptr + 4);\n"); + fprintf(fp, "\t\t\tpos += *(uint32_t *)(ptr + 4);\n"); + fprintf(fp, "\t\t\tptr += *(uint32_t *)(ptr + 4);\n"); } } // pass; diff --git a/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h b/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h new file mode 100644 index 0000000..1198bd1 --- /dev/null +++ b/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h @@ -0,0 +1,107 @@ +#ifndef EMUGL_PROTOCOL_UTILS_H +#define EMUGL_PROTOCOL_UTILS_H + +#include +#include + +namespace emugl { + +// Helper macro +#define COMPILE_ASSERT(cond) static char kAssert##__LINE__[1 - 2 * !(cond)] __attribute__((unused)) = { 0 } + +// Helper template: is_pointer. +// is_pointer::value is true iff |T| is a pointer type. +template struct is_pointer { + static const bool value = false; +}; + +template struct is_pointer { + static const bool value = true; +}; + +// A helper template to extract values form the wire protocol stream +// and convert them to appropriate host values. +// +// The wire protocol uses 32-bit exclusively when transferring +// GLintptr or GLsizei values, as well as opaque handles like GLeglImage, +// from the guest (even when the guest is 64-bit). +// +// The corresponding host definitions depend on the host bitness. For +// example, GLintptr is 64-bit on linux-x86_64. The following is a set +// of templates that can simplify the conversion of protocol values +// into host ones. +// +// The most important one is: +// +// unpack(const void* ptr) +// +// Which reads bytes from |ptr|, using |SIZE_TYPE| as the underlying +// sized-integer specifier (e.g. 'uint32_t'), and converting the result +// into a |HOST_TYPE| value. For example: +// +// unpack(ptr + 12); +// +// will read a 4-byte value from |ptr + 12| and convert it into +// an EGLImage, which is a host void*. The template detects host +// pointer types to perform proper type casting. +// +// TODO(digit): Add custom unpackers to handle generic opaque void* values. +// and map them to unique 32-bit values. + +template +struct UnpackerT {}; + +template +struct UnpackerT { + static inline T unpack(const void* ptr) { + COMPILE_ASSERT(sizeof(T) == sizeof(S)); + return (T)(*(S*)(ptr)); + } +}; + +template +struct UnpackerT { + static inline T unpack(const void* ptr) { + return (T)(uintptr_t)(*(S*)(ptr)); + } +}; + +template <> +struct UnpackerT { + static inline float unpack(const void* ptr) { + union { + float f; + uint32_t u; + } v; + v.u = *(uint32_t*)(ptr); + return v.f; + } +}; + +template <> +struct UnpackerT { + static inline double unpack(const void* ptr) { + union { + double d; + uint32_t u; + } v; + v.u = *(uint64_t*)(ptr); + return v.d; + } +}; + +template <> +struct UnpackerT { + static inline ssize_t unpack(const void* ptr) { + return (ssize_t)*(int32_t*)(ptr); + } +}; + +template +inline T Unpack(const void* ptr) { + return UnpackerT::value>::unpack(ptr); +} + +} // namespace emugl + +#endif // EMUGL_PROTOCOL_UTILS_H -- cgit v1.1