diff --git a/include/keystone/keystone.h b/include/keystone/keystone.h
index 5420c41418d467ff8e8002703778a3fc5a4a5d25..50af3baae45d2c79c7595a6464095db631eae8c1 100644
--- a/include/keystone/keystone.h
+++ b/include/keystone/keystone.h
@@ -140,10 +140,20 @@ typedef enum ks_err {
     KS_ERR_ASM_MNEMONICFAIL,
 } ks_err;
 
+// Resolver callback to provide value for a missing symbol in @symbol.
+// To handle a symbol, the resolver must put value of the symbol in @value,
+// then returns True.
+// If we do not resolve a missing symbol, this function must return False.
+// In that case, ks_asm() would eventually return with error KS_ERR_ASM_SYMBOL_MISSING.
+
+// To register the resolver, pass its function address to ks_option(), using
+// option KS_OPT_SYM_RESOLVER. For example, see samples/sample.c.
+typedef bool (*ks_sym_resolver)(const char *symbol, uint64_t *value);
 
 // Runtime option for the Keystone engine
 typedef enum ks_opt_type {
-	KS_OPT_SYNTAX = 1,	// Choose syntax for input assembly
+	KS_OPT_SYNTAX = 1,    // Choose syntax for input assembly
+	KS_OPT_SYM_RESOLVER,  // Set symbol resolver callback
 } ks_opt_type;
 
 
@@ -257,7 +267,7 @@ const char *ks_strerror(ks_err code);
  Set option for Keystone engine at runtime
 
  @ks: handle returned by ks_open()
- @type: type of option to be set
+ @type: type of option to be set. See ks_opt_type
  @value: option value corresponding with @type
 
  @return: KS_ERR_OK on success, or other value on failure.
diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h
index 582476464d13cf15a5a392125050506f266a28c6..0f897811356c30025081f7c11b20f3a36da0f14f 100644
--- a/llvm/include/llvm/MC/MCAssembler.h
+++ b/llvm/include/llvm/MC/MCAssembler.h
@@ -23,6 +23,8 @@
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/MC/MCSymbol.h"
 
+#include "keystone/keystone.h"
+
 namespace llvm {
 class raw_ostream;
 class MCAsmLayout;
@@ -56,10 +58,15 @@ struct DataRegionData {
 class MCAssembler {
   friend class MCAsmLayout;
   mutable unsigned KsError;
+  mutable void *KsSymResolver;
 
 public:
   void setError(unsigned E) const { KsError = E; }
   unsigned getError() const { return KsError; }
+
+  void setSymResolver(void *h) const { KsSymResolver = h; }
+  void *getSymResolver() const { return KsSymResolver; }
+
   typedef std::vector<MCSection *> SectionListType;
   typedef std::vector<const MCSymbol *> SymbolDataListType;
 
diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index 42e5a980a5e88553b4b9dd61d2d7c5e1a99fed0c..22db69f0d088b7289b4cee71aa50921d8c05ac9f 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -156,6 +156,7 @@ private:
 /// a .s file, and implementations that write out .o files of various formats.
 ///
 class MCStreamer {
+  mutable void *KsSymResolver;
   MCContext &Context;
   std::unique_ptr<MCTargetStreamer> TargetStreamer;
 
@@ -197,6 +198,9 @@ protected:
 public:
   virtual ~MCStreamer();
 
+  void setSymResolver(void *h) const { KsSymResolver = h; }
+  void *getSymResolver() const { return KsSymResolver; }
+
   void visitUsedExpr(const MCExpr &Expr);
   virtual void visitUsedSymbol(const MCSymbol &Sym);
 
diff --git a/llvm/keystone/ks.cpp b/llvm/keystone/ks.cpp
index fbd38ca21e10277cb73199da99a26c88ef9a23cd..83826c386bb5e432e5fe515d5c4af7112cb785b5 100644
--- a/llvm/keystone/ks.cpp
+++ b/llvm/keystone/ks.cpp
@@ -514,6 +514,9 @@ ks_err ks_option(ks_engine *ks, ks_opt_type type, size_t value)
                     break;
             }
 
+            return KS_ERR_OK;
+        case KS_OPT_SYM_RESOLVER:
+            ks->sym_resolver = (ks_sym_resolver)value;
             return KS_ERR_OK;
     }
 
@@ -571,6 +574,8 @@ int ks_asm(ks_engine *ks,
     ks->SrcMgr.clearBuffers();
     ks->SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
 
+    Streamer->setSymResolver((void *)(ks->sym_resolver));
+
     MCAsmParser *Parser = createMCAsmParser(ks->SrcMgr, Ctx, *Streamer, *ks->MAI);
     if (!Parser) {
         delete Streamer;
diff --git a/llvm/keystone/ks_priv.h b/llvm/keystone/ks_priv.h
index 4995fc7a7aeee779085e839e2c9f9ffdae4ff4a8..db8dfe43141a5d9e47b45f0dac6b7fad55cc30ec 100644
--- a/llvm/keystone/ks_priv.h
+++ b/llvm/keystone/ks_priv.h
@@ -55,6 +55,7 @@ struct ks_struct {
     std::string FeaturesStr;
     MCSubtargetInfo *STI;
     MCObjectFileInfo MOFI;
+    ks_sym_resolver sym_resolver;
 };
 
 
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp
index 799bf58f1dc1cda21bf470aca10cd3f5b62681d4..22591c54752e8585c0f1d00ba8c3f13150868d75 100644
--- a/llvm/lib/MC/MCAssembler.cpp
+++ b/llvm/lib/MC/MCAssembler.cpp
@@ -202,10 +202,27 @@ bool MCAssembler::evaluateFixup(const MCAsmLayout &Layout,
         return false;
       }
     } else {
-        KsError = KS_ERR_ASM_SYMBOL_MISSING;
-        return false;
+        // a missing symbol. is there any resolver registered?
+        if (KsSymResolver) {
+            uint64_t imm;
+            ks_sym_resolver resolver = (ks_sym_resolver)KsSymResolver;
+            if (resolver(Sym.getName().str().c_str(), &imm)) {
+                // resolver handled this symbol
+                Value = imm;
+                IsResolved = true;
+            } else {
+                // resolver did not handle this symbol
+                KsError = KS_ERR_ASM_SYMBOL_MISSING;
+                return false;
+            }
+        } else {
+            // no resolver registered
+            KsError = KS_ERR_ASM_SYMBOL_MISSING;
+            return false;
+        }
     }
   }
+
   if (const MCSymbolRefExpr *B = Target.getSymB()) {
     const MCSymbol &Sym = B->getSymbol();
     bool valid;
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index 64c9446083f33a4156ee1f0c3b194cb851385b50..cbab37fd0d6b1471cf8a2eaad194a7208d7a8375 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -630,13 +630,13 @@ void MCELFStreamer::EmitBundleUnlock() {
     Sec.setBundleLockState(MCSection::NotBundleLocked);
 }
 
-unsigned int MCELFStreamer::FinishImpl() {
+unsigned int MCELFStreamer::FinishImpl()
+{
   // Ensure the last section gets aligned if necessary.
   MCSection *CurSection = getCurrentSectionOnly();
   setSectionAlignmentForBundling(getAssembler(), CurSection);
 
   EmitFrames(nullptr);
-
   return this->MCObjectStreamer::FinishImpl();
 }
 
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index 5d208c7c49ab363973c496e922cb8b90c97f9848..cbb96e5a7e8e3550d297c99eef4ccf7746aa75fd 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -510,7 +510,8 @@ void MCObjectStreamer::EmitFill(uint64_t NumBytes, uint8_t FillValue) {
   insert(new MCFillFragment(FillValue, NumBytes));
 }
 
-unsigned int MCObjectStreamer::FinishImpl() {
+unsigned int MCObjectStreamer::FinishImpl()
+{
   unsigned int KsError = 0;
   // If we are generating dwarf for assembly source files dump out the sections.
   //if (getContext().getGenDwarfForAssembly())
@@ -520,6 +521,7 @@ unsigned int MCObjectStreamer::FinishImpl() {
   //MCDwarfLineTable::Emit(this, getAssembler().getDWARFLinetableParams());
 
   flushPendingLabels(nullptr);
+  getAssembler().setSymResolver(getSymResolver());
   getAssembler().Finish(KsError);
 
   return KsError;