aboutsummaryrefslogtreecommitdiffstats
path: root/emulator
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2014-09-24 19:03:46 -0700
committerbohu <bohu@google.com>2014-11-25 12:30:46 -0800
commit7d18e39ea46b467cd916d79525d6d978be7e70df (patch)
treeb528ca0b711ce738b75e004bdf763830492a2e65 /emulator
parenta3afda277fd2624c648739e0273f569fd093fd22 (diff)
downloadsdk-7d18e39ea46b467cd916d79525d6d978be7e70df.zip
sdk-7d18e39ea46b467cd916d79525d6d978be7e70df.tar.gz
sdk-7d18e39ea46b467cd916d79525d6d978be7e70df.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.cpp73
-rw-r--r--emulator/opengl/shared/OpenglCodecCommon/ProtocolUtils.h80
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