diff --git a/SOURCES/dyninst-10.1.0-aarch-regs.patch b/SOURCES/dyninst-10.1.0-aarch-regs.patch
new file mode 100644
index 0000000..814137c
--- /dev/null
+++ b/SOURCES/dyninst-10.1.0-aarch-regs.patch
@@ -0,0 +1,309 @@
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/BPatch_addressSpace.C.orig	2019-05-16 14:40:05.000000000 -0400
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/BPatch_addressSpace.C	2019-11-06 10:20:08.567523510 -0500
+--- BPatch_addressSpace.C.orig	2019-05-16 14:40:05.000000000 -0400
++++ BPatch_addressSpace.C	2019-11-15 18:10:31.186122654 -0500
+@@ -1050,5 +1050,2 @@
+    return true;
+-
+-   regs = registers_;
+-   return true;
+ }
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/emit-aarch64.C.orig	2019-05-16 14:40:05.000000000 -0400
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/emit-aarch64.C	2019-11-06 10:20:08.567523510 -0500
+@@ -276,9 +276,2 @@
+ 
+-   if (register_num == REG_SP) {
+-      insnCodeGen::generateAddSubImmediate(gen, insnCodeGen::Add, 0,
+-             TRAMP_FRAME_SIZE_64, destination, REG_SP, true);
+-
+-      return;
+-   }
+-
+    if (src->spilledState == registerSlot::unspilled)
+@@ -293,3 +286,3 @@
+     // its on the stack so load it.
+-    insnCodeGen::restoreRegister(gen, destination, offset + (register_num * gen.width()),
++    insnCodeGen::restoreRegister(gen, destination, offset + (src->encoding() * gen.width()),
+             insnCodeGen::Offset);
+@@ -298 +291,7 @@
+ 
++bool EmitterAARCH64::emitMoveRegToReg(Register src, Register dest, codeGen &gen)
++{
++    insnCodeGen::generateMove(gen, dest, src);
++    return true;
++}
++
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/emit-aarch64.h.orig	2019-05-16 14:40:05.000000000 -0400
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/emit-aarch64.h	2019-11-06 10:20:08.567523510 -0500
+@@ -107,6 +107,3 @@
+ 
+-    virtual bool emitMoveRegToReg(Register, Register, codeGen &) {
+-        assert(0);
+-        return 0;
+-    }
++    virtual bool emitMoveRegToReg(Register, Register, codeGen &);
+ 
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/inst-aarch64.C.orig	2019-05-16 14:40:05.000000000 -0400
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/inst-aarch64.C	2019-11-06 10:20:08.567523510 -0500
+@@ -108,2 +108,4 @@
+     registers.push_back(new registerSlot(r30, "r30", true, registerSlot::liveAlways, registerSlot::GPR));
++    // SP is r31, but also could be considered special. But now it's being added as GPR
++    registers.push_back(new registerSlot(sp, "r31", true, registerSlot::liveAlways, registerSlot::GPR));
+ 
+@@ -111,3 +113,3 @@
+     registers.push_back(new registerSlot(lr, "lr", true, registerSlot::liveAlways, registerSlot::SPR));
+-    registers.push_back(new registerSlot(sp, "sp", true, registerSlot::liveAlways, registerSlot::SPR));
++    //registers.push_back(new registerSlot(sp, "sp", true, registerSlot::liveAlways, registerSlot::SPR));
+     registers.push_back(new registerSlot(pstate, "nzcv", true, registerSlot::liveAlways, registerSlot::SPR));
+@@ -183,9 +185,16 @@
+         registerSlot *reg = theRegSpace->GPRs()[idx];
+-	// We always save FP and LR for stack walking out of instrumentation
+-        if (reg->liveState == registerSlot::live || reg->number == REG_FP || reg->number == REG_LR) {
++        // We always save FP and LR for stack walking out of instrumentation
++        //if (reg->liveState == registerSlot::live || reg->number == REG_FP || reg->number == REG_LR) {
+             int offset_from_sp = offset + (reg->encoding() * gen.width());
+-            insnCodeGen::saveRegister(gen, reg->number, offset_from_sp);
++            if(reg->number != registerSpace::sp)
++                insnCodeGen::saveRegister(gen, reg->number, offset_from_sp);
++            else{
++                // mov SP to x0
++                insnCodeGen::generateAddSubImmediate(gen, insnCodeGen::Add, 0,
++                        TRAMP_FRAME_SIZE_64, REG_SP, 0, true);
++                insnCodeGen::saveRegister(gen, 0, offset_from_sp);
++            }
+             theRegSpace->markSavedRegister(reg->number, offset_from_sp);
+             ret++;
+-        }
++        //}
+     }
+@@ -283,2 +292,4 @@
+         if(reg->liveState == registerSlot::spilled) {
++            if(reg->number == registerSpace::sp)
++                continue;
+             //#sasha this should be GPRSIZE_64 and not gen.width
+@@ -602,4 +613,2 @@
+ {
+-    //#sasha This function implementation is experimental.
+-
+     if (op != callOp) {
+@@ -623,4 +632,2 @@
+     vector<int> savedRegs;
+-
+-    // save r0-r7
+     for(size_t id = 0; id < gen.rs()->numGPRs(); id++)
+@@ -653,2 +660,6 @@
+         assert(reg!=REG_NULL);
++
++        // mark reg offLimits so getScratchRegister won't use it
++        registerSlot *regS = gen.rs()->GPRs()[id];
++        regS->offLimits = true;
+     }
+@@ -691,3 +702,2 @@
+ 
+-    // r7-r0
+     for (signed int ui = savedRegs.size()-1; ui >= 0; ui--) {
+@@ -697,2 +707,9 @@
+ 
++    // Making operand's reg not offLimits again
++    for(size_t id = 0; id < operands.size(); id++)
++    {
++        registerSlot *reg = gen.rs()->GPRs()[id];
++        reg->offLimits = false;
++    }
++
+     return 0;
+@@ -1426,4 +1443,53 @@
+ bool EmitterAARCH64Stat::emitPLTCall(func_instance *callee, codeGen &gen) {
+-    assert(0); //Not implemented
+-    return emitPLTCommon(callee, true, gen);
++    /*
++    Address dest = getInterModuleFuncAddr(callee, gen);
++    //Register scr = gen.rs()->getScratchRegister(gen);
++    //Register lr = gen.rs()->getScratchRegister(gen);
++    //Address pc = emitMovePCToReg(scr, gen);
++
++    Address varOffset = dest - gen.currAddr();
++    //printf("VarOffset  = %d\n", varOffset);
++    //emitLoadRelative(lr, varOffset, scr, gen.width(), gen);
++
++    insnCodeGen::generateBranch(gen, gen.currAddr(), dest, true);
++
++    return true;
++    */ 
++
++
++    Address dest = getInterModuleFuncAddr(callee, gen);
++    Register scr = gen.rs()->getScratchRegister(gen);
++    Register lr = gen.rs()->getScratchRegister(gen);
++    //Register scr = gen.rs()->getRegByName("r2");
++    //Register lr = gen.rs()->getRegByName("r3");
++    emitMovePCToReg(scr, gen);
++
++    Address varOffset = dest - gen.currAddr() + 4;
++    //printf("VarOffset  = %d\n", varOffset);
++    emitLoadRelative(lr, varOffset, scr, gen.width(), gen);
++    insnCodeGen::generateMemAccess(gen, insnCodeGen::Load, lr, lr, 0, 8, insnCodeGen::Offset);
++
++    // indirect branch
++    instruction branchInsn;
++    branchInsn.clear();
++
++    //Set bits which are 0 for both BR and BLR
++    INSN_SET(branchInsn, 0, 4, 0);
++    INSN_SET(branchInsn, 10, 15, 0);
++
++    //Set register
++    INSN_SET(branchInsn, 5, 9, lr);
++
++    //Set other bits. Basically, these are the opcode bits.
++    //The only difference between BR and BLR is that bit 21 is 1 for BLR.
++    INSN_SET(branchInsn, 16, 31, BRegOp);
++    INSN_SET(branchInsn, 21, 21, 1);
++    insnCodeGen::generate(gen, branchInsn);
++    //insnCodeGen::generateBranch(gen, gen.currAddr(), lr, true);
++    //insnCodeGen::generateBranch(gen, gen.currAddr(), gen.currAddr() +varOffset, true);
++
++    return true;
++
++    //assert(0); //Not implemented
++    //return emitPLTCommon(callee, true, gen);
+ }
+@@ -1431,4 +1497,81 @@
+ bool EmitterAARCH64Stat::emitPLTJump(func_instance *callee, codeGen &gen) {
+-    assert(0); //Not implemented
+-    return emitPLTCommon(callee, false, gen);
++    /*
++    Address dest = getInterModuleFuncAddr(callee, gen);
++    //Register scr = gen.rs()->getScratchRegister(gen);
++    //Register lr = gen.rs()->getScratchRegister(gen);
++    Register scr = gen.rs()->getRegByName("r2");
++    Register lr = gen.rs()->getRegByName("r3");
++    //Address pc = emitMovePCToReg(scr, gen);
++
++    Address varOffset = dest - gen.currAddr();
++    //printf("VarOffset  = %d\n", varOffset);
++    emitLoadRelative(lr, varOffset, scr, gen.width(), gen);
++    insnCodeGen::generateMemAccess(gen, insnCodeGen::Load, lr, lr, 0, 8, insnCodeGen::Offset);
++
++    insnCodeGen::generateBranch(gen, gen.currAddr(), lr, false);
++
++    return true;
++    */ 
++
++    /*
++    Address dest = getInterModuleFuncAddr(callee, gen);
++    Register scr = gen.rs()->getScratchRegister(gen);
++    Register lr = gen.rs()->getScratchRegister(gen);
++    Address pc = emitMovePCToReg(scr, gen);
++
++    Address varOffset = dest - pc;
++    printf("VarOffset  = %d\n", varOffset);
++    emitLoadRelative(lr, varOffset, scr, gen.width(), gen);
++
++    insnCodeGen::generateBranch(gen, gen.currAddr(), lr, false);
++    return true;
++    */
++    
++    /*
++    Address dest = getInterModuleFuncAddr(callee, gen);
++    Register scr = gen.rs()->getScratchRegister(gen);
++    Register lr = gen.rs()->getScratchRegister(gen);
++    Address varOffset = dest - gen.currAddr();
++    emitLoadRelative(lr, varOffset, scr, gen.width(), gen);
++    insnCodeGen::generateBranch(gen, gen.currAddr(), gen.currAddr() +varOffset, false);
++
++    return true;
++    */
++
++
++    Address dest = getInterModuleFuncAddr(callee, gen);
++    Register scr = gen.rs()->getScratchRegister(gen);
++    Register lr = gen.rs()->getScratchRegister(gen);
++    //Register scr = gen.rs()->getRegByName("r2");
++    //Register lr = gen.rs()->getRegByName("r3");
++    emitMovePCToReg(scr, gen);
++
++    Address varOffset = dest - gen.currAddr() + 4;
++    //printf("VarOffset  = %d\n", varOffset);
++    emitLoadRelative(lr, varOffset, scr, gen.width(), gen);
++    insnCodeGen::generateMemAccess(gen, insnCodeGen::Load, lr, lr, 0, 8, insnCodeGen::Offset);
++
++    // indirect branch
++    instruction branchInsn;
++    branchInsn.clear();
++
++    //Set bits which are 0 for both BR and BLR
++    INSN_SET(branchInsn, 0, 4, 0);
++    INSN_SET(branchInsn, 10, 15, 0);
++
++    //Set register
++    INSN_SET(branchInsn, 5, 9, lr);
++
++    //Set other bits. Basically, these are the opcode bits.
++    //The only difference between BR and BLR is that bit 21 is 1 for BLR.
++    INSN_SET(branchInsn, 16, 31, BRegOp);
++    INSN_SET(branchInsn, 21, 21, 0);
++    insnCodeGen::generate(gen, branchInsn);
++    //insnCodeGen::generateBranch(gen, gen.currAddr(), lr, true);
++    //insnCodeGen::generateBranch(gen, gen.currAddr(), gen.currAddr() +varOffset, true);
++
++    return true;
++
++    //assert(0); //Not implemented
++    //return emitPLTCommon(callee, false, gen);
+ }
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/inst-aarch64.h.orig	2019-05-16 14:40:05.000000000 -0400
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/inst-aarch64.h	2019-11-06 10:20:08.567523510 -0500
+@@ -74,3 +74,3 @@
+ //TODO Fix for ARM
+-#define GPRSAVE_64  (31*GPRSIZE_64)
++#define GPRSAVE_64  (32*GPRSIZE_64)
+ #define FPRSAVE_64  (32*FPRSIZE_64)
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/registerSpace.C.orig	2019-05-16 14:40:05.000000000 -0400
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/registerSpace.C	2019-11-06 10:20:08.567523510 -0500
+@@ -110,2 +110,4 @@
+ #elif defined(arch_aarch64) 
++    if(number == registerSpace::sp)
++        return REG_SP;
+     switch (type) {
+@@ -342,3 +344,3 @@
+ 
+-    reg->markUsed(true);
++    //reg->markUsed(true);
+     gen.markRegDefined(reg->number);
+
+--- dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/dynProcess.C.orig
++++ dyninst-10.1.0/dyninst-10.1.0/dyninstAPI/src/dynProcess.C
+@@ -3243,25 +3243,13 @@ bool PCProcess::continueSyncRPCThreads() {
+ }
+ 
+ void PCProcess::addTrap(Address from, Address to, codeGen &gen) {
+-    map<Address, Breakpoint::ptr>::iterator breakIter =
+-       installedCtrlBrkpts.find(from);
+-
+-    if( breakIter != installedCtrlBrkpts.end() ) {
+-        if( !pcProc_->rmBreakpoint(from, breakIter->second) ) {
+-	  // Oops? 
+-        }
+-        installedCtrlBrkpts.erase(breakIter);
+-    }
+-    
+-    Breakpoint::ptr newBreak = Breakpoint::newTransferBreakpoint(to);
+-    newBreak->setSuppressCallbacks(true);
+-
+-    if( !pcProc_->addBreakpoint(from, newBreak) ) {
+-      // Oops? 
+-    }
+-
+-    installedCtrlBrkpts.insert(make_pair(from, newBreak));
+-    gen.invalidate();
++   gen.invalidate();
++   gen.allocate(4);
++   gen.setAddrSpace(this);
++   gen.setAddr(from);
++   insnCodeGen::generateTrap(gen);
++   trapMapping.addTrapMapping(from, to, true);
++   springboard_cerr << "Generated springboard trap " << hex << from << "->" << to << dec << endl;
+ }
+ 
+ void PCProcess::removeTrap(Address from) {
diff --git a/SPECS/dyninst.spec b/SPECS/dyninst.spec
index 4eb3eec..9e1fdb6 100644
--- a/SPECS/dyninst.spec
+++ b/SPECS/dyninst.spec
@@ -2,7 +2,7 @@ Summary: An API for Run-time Code Generation
 License: LGPLv2+
 Name: dyninst
 Group: Development/Libraries
-Release: 2%{?dist}
+Release: 4%{?dist}
 URL: http://www.dyninst.org
 Version: 10.1.0
 ExclusiveArch: %{ix86} x86_64 ppc64le aarch64
@@ -13,6 +13,7 @@ Source1: https://github.com/dyninst/testsuite/archive/v%{version}/testsuite-%{ve
 Patch1: dyninst-10.1.0-tbb.patch
 Patch2: dyninst-10.1.0-result.patch
 Patch3: testsuite-10.1.0-386.patch
+Patch4: dyninst-10.1.0-aarch-regs.patch
 
 %global dyninst_base dyninst-%{version}
 %global testsuite_base testsuite-%{version}
@@ -78,7 +79,6 @@ Group: Development/System
 Requires: dyninst = %{version}-%{release}
 Requires: dyninst-devel = %{version}-%{release}
 Requires: dyninst-static = %{version}-%{release}
-Requires: glibc-static
 %description testsuite
 dyninst-testsuite includes the test harness and target programs for
 making sure that dyninst works properly.
@@ -90,6 +90,7 @@ making sure that dyninst works properly.
 %patch1 -p1 -b.tbb
 %patch2 -p1 -b.result
 %patch3 -p1 -b.386
+%patch4 -p1 -b.aarch
 
 # cotire seems to cause non-deterministic gcc errors
 # https://bugzilla.redhat.com/show_bug.cgi?id=1420551
@@ -187,6 +188,13 @@ echo "%{_libdir}/dyninst" > %{buildroot}/etc/ld.so.conf.d/%{name}-%{_arch}.conf
 %attr(644,root,root) %{_libdir}/dyninst/testsuite/*.a
 
 %changelog
+* Tue Nov 19 2019 Stan Cox <scox@redhat.com> - 10.1.0-4
+- Resolves: rhbz#963475 dyninst must be ported to aarch64
+  Remove Requires: glibc-static from %package testsuite
+
+* Fri Nov 15 2019 Stan Cox <scox@redhat.com> - 10.1.0-3
+- Fix rhbz963475 dyninst must be ported to aarch64 
+
 * Tue Jun 04 2019 Stan Cox <scox@redhat.com> - 10.1.0-2
 - Use PRIx64 to fix i386 build