aboutsummaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorCristian Cadar <c.cadar@imperial.ac.uk>2020-04-05 11:46:43 +0100
committerMartinNowack <2443641+MartinNowack@users.noreply.github.com>2020-06-19 18:19:53 +0100
commit5dadac2a6a836c78b3a3dac36a1f4430cedaaddb (patch)
tree13eb1068f74e164ebfa69e69837881aef17efb43 /lib
parentd5398bbb2f3650be6238a5919dd8bfec1fd4be89 (diff)
downloadklee-5dadac2a6a836c78b3a3dac36a1f4430cedaaddb.tar.gz
Correctly copy variadic arguments with byval attribute
Diffstat (limited to 'lib')
-rw-r--r--lib/Core/Executor.cpp113
1 files changed, 75 insertions, 38 deletions
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 67cdc268..83409983 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -68,6 +68,11 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#if LLVM_VERSION_CODE >= LLVM_VERSION(10, 0)
+#include "llvm/Support/TypeSize.h"
+#else
+typedef unsigned TypeSize;
+#endif
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -1460,7 +1465,6 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
if (statsTracker)
statsTracker->framePushed(state, &state.stack[state.stack.size() - 2]);
- // TODO: support "byval" parameter attribute
// TODO: support zeroext, signext, sret attributes
unsigned callingArgs = arguments.size();
@@ -1475,37 +1479,79 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
return;
}
} else {
- Expr::Width WordSize = Context::get().getPointerWidth();
-
if (callingArgs < funcArgs) {
terminateStateOnError(state, "calling function with too few arguments",
User);
return;
}
- StackFrame &sf = state.stack.back();
- unsigned size = 0;
+ // Only x86-32 and x86-64 are supported
+ Expr::Width WordSize = Context::get().getPointerWidth();
+ assert(((WordSize == Expr::Int32) || (WordSize == Expr::Int64)) &&
+ "Unknown word size!");
+
+ uint64_t size = 0; // total size of variadic arguments
bool requires16ByteAlignment = false;
+
+ uint64_t offsets[callingArgs]; // offsets of variadic arguments
+ uint64_t argWidth; // width of current variadic argument
+
+ CallSite cs(i);
for (unsigned k = funcArgs; k < callingArgs; k++) {
- // FIXME: This is really specific to the architecture, not the pointer
- // size. This happens to work for x86-32 and x86-64, however.
+ if (cs.isByValArgument(k)) {
+#if LLVM_VERSION_CODE >= LLVM_VERSION(9, 0)
+ Type *t = cs.getParamByValType(k);
+#else
+ auto arg = cs.getArgument(k);
+ Type *t = arg->getType();
+ assert(t->isPointerTy());
+ t = t->getPointerElementType();
+#endif
+ argWidth = kmodule->targetData->getTypeSizeInBits(t);
+ } else {
+ argWidth = arguments[k]->getWidth();
+ }
+
if (WordSize == Expr::Int32) {
- size += Expr::getMinBytesForWidth(arguments[k]->getWidth());
+ offsets[k] = size;
+ size += Expr::getMinBytesForWidth(argWidth);
} else {
- Expr::Width argWidth = arguments[k]->getWidth();
+#if LLVM_VERSION_CODE > LLVM_VERSION(4, 0)
+ unsigned alignment = cs.getParamAlignment(k);
+#else
+ // getParamAlignment() is buggy for LLVM <= 4, so we instead
+ // get the attribute in a hacky way by parsing the textual
+ // representation
+ unsigned alignment = 0;
+ std::string str;
+ llvm::raw_string_ostream s(str);
+ s << *cs.getArgument(k);
+ size_t pos = str.find("align ");
+ if (pos != std::string::npos)
+ alignment = std::stoi(str.substr(pos + 6));
+#endif
+
// AMD64-ABI 3.5.7p5: Step 7. Align l->overflow_arg_area upwards to a
// 16 byte boundary if alignment needed by type exceeds 8 byte
// boundary.
- //
- // Alignment requirements for scalar types is the same as their size
- if (argWidth > Expr::Int64) {
+ if (!alignment && argWidth > Expr::Int64) {
+ alignment = 16;
+ requires16ByteAlignment = true;
+ }
+
+ if (!alignment)
+ alignment = 8;
+
#if LLVM_VERSION_CODE >= LLVM_VERSION(3, 9)
- size = llvm::alignTo(size, 16);
+ size = llvm::alignTo(size, alignment);
#else
- size = llvm::RoundUpToAlignment(size, 16);
+ size = llvm::RoundUpToAlignment(size, alignment);
#endif
- requires16ByteAlignment = true;
- }
+ offsets[k] = size;
+
+ // AMD64-ABI 3.5.7p5: Step 9. Set l->overflow_arg_area to:
+ // l->overflow_arg_area + sizeof(type)
+ // Step 10. Align l->overflow_arg_area upwards to an 8 byte boundary.
#if LLVM_VERSION_CODE >= LLVM_VERSION(3, 9)
size += llvm::alignTo(argWidth, WordSize) / 8;
#else
@@ -1514,6 +1560,7 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
}
}
+ StackFrame &sf = state.stack.back();
MemoryObject *mo = sf.varargs =
memory->allocate(size, true, false, state.prevPC->inst,
(requires16ByteAlignment ? 16 : 8));
@@ -1531,30 +1578,20 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
}
ObjectState *os = bindObjectInState(state, mo, true);
- unsigned offset = 0;
+
for (unsigned k = funcArgs; k < callingArgs; k++) {
- // FIXME: This is really specific to the architecture, not the pointer
- // size. This happens to work for x86-32 and x86-64, however.
- if (WordSize == Expr::Int32) {
- os->write(offset, arguments[k]);
- offset += Expr::getMinBytesForWidth(arguments[k]->getWidth());
+ if (!cs.isByValArgument(k)) {
+ os->write(offsets[k], arguments[k]);
} else {
- assert(WordSize == Expr::Int64 && "Unknown word size!");
-
- Expr::Width argWidth = arguments[k]->getWidth();
- if (argWidth > Expr::Int64) {
-#if LLVM_VERSION_CODE >= LLVM_VERSION(3, 9)
- offset = llvm::alignTo(offset, 16);
-#else
- offset = llvm::RoundUpToAlignment(offset, 16);
-#endif
- }
- os->write(offset, arguments[k]);
-#if LLVM_VERSION_CODE >= LLVM_VERSION(3, 9)
- offset += llvm::alignTo(argWidth, WordSize) / 8;
-#else
- offset += llvm::RoundUpToAlignment(argWidth, WordSize) / 8;
-#endif
+ ConstantExpr *CE = dyn_cast<ConstantExpr>(arguments[k]);
+ assert(CE); // byval argument needs to be a concrete pointer
+
+ ObjectPair op;
+ state.addressSpace.resolveOne(CE, op);
+ const ObjectState *osarg = op.second;
+ assert(osarg);
+ for (unsigned i = 0; i < osarg->size; i++)
+ os->write(offsets[k] + i, osarg->read8(i));
}
}
}