diff options
author | David 'Digit' Turner <digit@google.com> | 2014-09-24 19:03:46 -0700 |
---|---|---|
committer | David 'Digit' Turner <digit@google.com> | 2014-09-24 19:06:49 -0700 |
commit | dfd49832d5d2058a69486af32373fc90ddc69a3d (patch) | |
tree | 3c17f4252e1a0b04ae700410f8094b65add18749 /emulator | |
parent | 9746f34af612629b65507e57f667d05e729fa057 (diff) | |
download | sdk-dfd49832d5d2058a69486af32373fc90ddc69a3d.zip sdk-dfd49832d5d2058a69486af32373fc90ddc69a3d.tar.gz sdk-dfd49832d5d2058a69486af32373fc90ddc69a3d.tar.bz2 |
emulator/opengl/emugen: Ensure correct buffer alignment.
The decoders generated by emugen pass addresses that come directly
from the stream to EGL/GL functions. Sometimes, these addresses are
not properly padded with regards to the type of data being transfered
and this can crash some implementations (e.g. OSMesa being compiled
with -msse by default, and doesn't build without it).
This patch introduces two helper classes in ProtocolUtils.h, named
InputBuffer and OutputBuffer, which are used to auto-align buffer
pointers, then make the generated decoder code use them.
Change-Id: I345c7eecc230f62310ced5378b6344f419647e06
Diffstat (limited to 'emulator')
-rw-r--r-- | emulator/opengl/host/tools/emugen/ApiGen.cpp | 73 | ||||
-rw-r--r-- | emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h | 80 |
2 files changed, 149 insertions, 4 deletions
diff --git a/emulator/opengl/host/tools/emugen/ApiGen.cpp b/emulator/opengl/host/tools/emugen/ApiGen.cpp index 78c15e4..bd8fabf 100644 --- a/emulator/opengl/host/tools/emugen/ApiGen.cpp +++ b/emulator/opengl/host/tools/emugen/ApiGen.cpp @@ -30,6 +30,10 @@ */ #define WITH_LARGE_SUPPORT 1 +// Set to 1 to ensure buffers passed to/from EGL/GL are properly aligned. +// This prevents crashes with certain backends (e.g. OSMesa). +#define USE_ALIGNED_BUFFERS 1 + EntryPoint * ApiGen::findEntryByName(const std::string & name) { EntryPoint * entry = NULL; @@ -786,7 +790,7 @@ int ApiGen::genDecoderImpl(const std::string &filename) \t\tswitch(opcode) {\n"); for (size_t f = 0; f < n; f++) { - enum Pass_t { PASS_TmpBuffAlloc = 0, PASS_MemAlloc, PASS_DebugPrint, PASS_FunctionCall, PASS_Epilog, PASS_LAST }; + enum Pass_t { PASS_TmpBuffAlloc = 0, PASS_MemAlloc, PASS_DebugPrint, PASS_FunctionCall, PASS_FlushOutput, PASS_Epilog, PASS_LAST }; EntryPoint *e = &at(f); // construct a printout string; @@ -859,7 +863,7 @@ int ApiGen::genDecoderImpl(const std::string &filename) fprintf(fp, "Unpack<%s,uint%d_t>(ptr + %s)", v->type()->name().c_str(), - v->type()->bytes() * 8, + (unsigned) v->type()->bytes() * 8U, varoffset.c_str()); } varoffset += " + " + toString(v->type()->bytes()); @@ -868,12 +872,39 @@ int ApiGen::genDecoderImpl(const std::string &filename) if (v->pointerDir() == Var::POINTER_IN || v->pointerDir() == Var::POINTER_INOUT) { - if (pass == PASS_MemAlloc && - v->pointerDir() == Var::POINTER_INOUT) { + if (pass == PASS_MemAlloc) { fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(uint32_t *)(ptr + %s);\n", (unsigned) j, varoffset.c_str()); +#if USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tInputBuffer tmpPtr%u(ptr + %s + 4, tmpPtr%uSize);\n", + (unsigned) j, + varoffset.c_str(), + (unsigned) j); + } + if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u.get())", + (unsigned) j, + v->type()->name().c_str(), + (unsigned) j); + } else { + fprintf(fp, + "(%s)(tmpPtr%u.get())", + v->type()->name().c_str(), + (unsigned) j); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(tmpPtr%u.get()), (uint32_t)tmpPtr%uSize", + v->type()->name().c_str(), + (unsigned) j, + (unsigned) j); + } +#else // !USE_ALIGNED_BUFFERS fprintf(fp, "unsigned char *tmpPtr%u = (ptr + %s + 4);\n", (unsigned) j, @@ -899,6 +930,7 @@ int ApiGen::genDecoderImpl(const std::string &filename) varoffset.c_str(), varoffset.c_str()); } +#endif // !USE_ALIGNED_BUFFERS varoffset += " + 4 + *(tsize_t *)(ptr +" + varoffset + ")"; } else { // out pointer; @@ -922,6 +954,38 @@ int ApiGen::genDecoderImpl(const std::string &filename) totalTmpBuffOffset += std::string(tmpPtrName); totalTmpBuffExist = true; } else if (pass == PASS_MemAlloc) { +#if USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tOutputBuffer tmpPtr%u(&tmpBuf[%s], tmpPtr%uSize);\n", + (unsigned) j, + tmpBufOffset[j].c_str(), + (unsigned) j); + } else if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u.get())", + (unsigned) j, + v->type()->name().c_str(), + (unsigned) j); + } else { + fprintf(fp, + "(%s)(tmpPtr%u.get())", + v->type()->name().c_str(), + (unsigned) j); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(tmpPtr%u.get()), (uint32_t)tmpPtr%uSize", + v->type()->name().c_str(), + (unsigned) j, + (unsigned) j); + } + if (pass == PASS_FlushOutput) { + fprintf(fp, + "\t\t\ttmpPtr%u.flush();\n", + (unsigned) j); + } +#else // !USE_ALIGNED_BUFFERS fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n", (unsigned)j, @@ -950,6 +1014,7 @@ int ApiGen::genDecoderImpl(const std::string &filename) (unsigned) j, varoffset.c_str()); } +#endif // !USE_ALIGNED_BUFFERS varoffset += " + 4"; } } diff --git a/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h b/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h index 1198bd1..472d7ea 100644 --- a/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h +++ b/emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h @@ -3,6 +3,8 @@ #include <stddef.h> #include <stdint.h> +#include <stdlib.h> +#include <string.h> namespace emugl { @@ -102,6 +104,84 @@ inline T Unpack(const void* ptr) { return UnpackerT<T, S, is_pointer<T>::value>::unpack(ptr); } +// Helper class used to ensure input buffers passed to EGL/GL functions +// are properly aligned (preventing crashes with some backends). +// Usage example: +// +// InputBuffer inputBuffer(ptr, size); +// glDoStuff(inputBuffer.get()); +// +// inputBuffer.get() will return the original value of |ptr| if it was +// aligned on an 8-byte boundary. Otherwise, it will return the address +// of an aligned heap-allocated copy of the original |size| bytes starting +// from |ptr|. The heap block is released at scope exit. +class InputBuffer { +public: + InputBuffer(const void* input, size_t size, size_t align = 8) : + mBuff(input), mIsCopy(false) { + if (((uintptr_t)input & (align - 1U)) != 0) { + void* newBuff = malloc(size); + memcpy(newBuff, input, size); + mBuff = newBuff; + mIsCopy = true; + } + } + + ~InputBuffer() { + if (mIsCopy) { + free((void*)mBuff); + } + } + + const void* get() const { + return mBuff; + } + +private: + const void* mBuff; + bool mIsCopy; +}; + +// Helper class used to ensure that output buffers passed to EGL/GL functions +// are aligned on 8-byte addresses. +// Usage example: +// +// ptr = stream->alloc(size); +// OutputBuffer outputBuffer(ptr, size); +// glGetStuff(outputBuffer.get()); +// outputBuffer.flush(); +// +// outputBuffer.get() returns the original value of |ptr| if it was already +// aligned on an 8=byte boundary. Otherwise, it returns the size of an heap +// allocated zeroed buffer of |size| bytes. +// +// outputBuffer.flush() copies the content of the heap allocated buffer back +// to |ptr| explictly, if needed. If a no-op if |ptr| was aligned. +class OutputBuffer { +public: + OutputBuffer(unsigned char* ptr, size_t size, size_t align = 8) : + mOrgBuff(ptr), mBuff(ptr), mSize(size) { + if (((uintptr_t)ptr & (align - 1U)) != 0) { + void* newBuff = calloc(1, size); + mBuff = newBuff; + } + } + + void* get() const { + return mBuff; + } + + void flush() { + if (mBuff != mOrgBuff) { + memcpy(mOrgBuff, mBuff, mSize); + } + } +private: + unsigned char* mOrgBuff; + void* mBuff; + size_t mSize; +}; + } // namespace emugl #endif // EMUGL_PROTOCOL_UTILS_H |