[DynInst_API:] saving registers on aarch64


Date: Fri, 27 Sep 2019 16:08:44 -0400
From: Stan Cox <scox@xxxxxxxxxx>
Subject: [DynInst_API:] saving registers on aarch64
Systemtap can access program variables in a probe. By using dwarf it knows a variable will be at, say [sp, #12], so it uses a cached set of registers, does the address evaluation and fetches the value. For the dyninst backend we load the cache with the register values pulled via BPatch_registerExpr. For x8664 this works fine; we can load the cache with the register values in one call. Since ppcle and aarch64 are limited to the number of arguments that will fit in registers; it is done in chunks. This is fine on ppcle, but on aarch64 I am seeing ARCH64::emitCall(opCode, codeGen&, const std::vector<boost::shared_ptr<AstNode> >&, bool, func_instance*): Assertion `scratch != REG_NULL && "cannot get a scratch register"' failed.


With the attached program, uncommenting the line at "This fails" fails the same way.

PS Is there a way to grab sp (r31 depended on usage) because in some cases, especially without optimization the variable value is at sp+offset

#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <iostream>

// dyninst libraries

#include <dyninst/BPatch.h>
#include <dyninst/BPatch_addressSpace.h>
#include <dyninst/BPatch_process.h>
#include <dyninst/BPatch_function.h>
#include <dyninst/BPatch_point.h>

using namespace std;

BPatch bpatch;

static void
usage (bool ok)
{
  if (!ok)
    {
      cerr << "Usage mutator [-static] [-dynamic] PID|FILE.\n";
      exit (1);
    }
}

bool is_number(const std::string& s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it)) ++it;
    return !s.empty() && it == s.end();
}

// Create and insert a printf snippet
bool insert_printf_snippet(BPatch_addressSpace* app,
			   std::vector<BPatch_point*>* points) 
{
  vector<BPatch_register> bpregs;
  app->getRegisters(bpregs);
  ostream& debug_reg = cerr;
  debug_reg << bpregs.size() << " registers available:";
  for (size_t i = 0; i < bpregs.size(); ++i)
    debug_reg << " " << bpregs[i].name();
  debug_reg << endl;

  BPatch_image* appImage = app->getImage();

  std::vector<BPatch_snippet*> printfArgs;
  BPatch_snippet* fmt = new BPatch_constExpr("In main %#lx %#lx %#lx %#lx %#lx %#lx %#lx\n");
  printfArgs.push_back(fmt);
  printfArgs.insert(printfArgs.end(), 7, new BPatch_constExpr(0));

  std::vector<BPatch_function*> printfFuncs;
  appImage->findFunction("printf", printfFuncs);
  if (printfFuncs.size() == 0) 
    {
      fprintf(stderr, "Could not find printf\n");
      return false;
    }

  // This is the goal:
  // int first_reg [] = {7, 14, 21, 28, 31};
  // This fails
  // int first_reg [] = {0, 7, 14, 21, 28};
  // This works
  int first_reg [] = {0, 7, 14, 21};
  int pfarg;
  for (int i = 0; i < (sizeof(first_reg)/sizeof(int))-1; i++)
    {
      pfarg = 1;
      for (int r = first_reg[i]; r < first_reg[i+1]; r++)
	{
	  printf("arg[%d]=reg[%d] ",pfarg,r);
	  printfArgs[pfarg++] = (new BPatch_registerExpr(bpregs[r]));
	}
      printf("\n");
      BPatch_funcCallExpr printfCall(*(printfFuncs[0]), printfArgs);

      if (!app->insertSnippet(printfCall, *points))/*, BPatch_callBefore, BPatch_firstSnippet))*/
	{
	  fprintf(stderr, "insertSnippet failed\n");
	  return false;
	}
    }

  return true;
}


int main(int argc, char **argv)
{
	int pid;
	//	BPatch_binaryEdit *app_bin;
	BPatch_addressSpace *app_bin;
	BPatch_addressSpace *aspace;
	BPatch_image *image;
	enum {dyn_static, dyn_dynamic} dyn_type;
	char * p ;
	int argn = 1;
	
	usage (argn < argc);
	
	if (strncmp (argv[argn], "-h", 2) == 0)
	  usage (0);
	else if (strcmp (argv[argn], "-static") == 0)
	  {
	    dyn_type = dyn_static;
	    argn += 1;
	  }
	else if (strcmp (argv[argn], "-dynamic") == 0)
	  {
	    dyn_type = dyn_dynamic;
	    argn += 1;
	  }
	
	usage (argn < argc);
	if (is_number (argv[argn]))
	  {
	    pid = atoi(argv[1]);
	    if (pid == 0)
	      {
		cerr << "ERROR ## Wrong PID " << pid << ", please use another.\n";
		return 2;
	      }
	    app_bin = bpatch.processAttach(NULL, pid);
	    dyn_type = dyn_dynamic;
	  }
	else
	  {
	    const char* prog_argv[] = {argv[argn], "-v", "5", NULL};
	    if (dyn_type == dyn_dynamic)
	      app_bin = bpatch.processCreate(argv[argn], prog_argv);
	    else /* assume dyn_static */
	      app_bin = bpatch.openBinary(argv[argn]);
	  }
	
	aspace = app_bin;
	image = aspace->getImage();
	
	const char *inst_library = "libc.so.6";
	if (!app_bin->loadLibrary (inst_library)) {
	  cerr << "Failed to open libc" << endl;
	  return EXIT_FAILURE;
	}

 	vector<BPatch_function *> main_function;
	image->findFunction("main", main_function);
	std::vector<BPatch_point*>* main_entry_point = main_function[0]->findPoint(BPatch_entry);
	if (!main_entry_point || main_entry_point->size() == 0) {
	  fprintf(stderr, "No entry points for main\n");
	  exit(1);
	}

	if (!insert_printf_snippet(aspace, main_entry_point)) 
	  {
	    fprintf(stderr, "insert_printf_snippet failed\n");
	    exit(1);
	  }

	if (dyn_type == dyn_static)
	  {
	    string exefile = argv[argn];
	    std::size_t found = exefile.find_last_of(".");
	    if (found == string::npos)
	      exefile += "-mutated";
	    else
	      exefile = exefile.substr(0,found) + "-mutated" + exefile.substr(found);
	    cout << "Mutation done. Created " + exefile << endl;
	    BPatch_binaryEdit* appbined = dynamic_cast<BPatch_binaryEdit*>(app_bin);
	    appbined->writeFile(exefile.c_str());
	  }
	else if (dyn_type == dyn_dynamic)
	  {
	    cout << "Mutation done. Running mutatee\n";
	    BPatch_process* appproc = dynamic_cast<BPatch_process*>(app_bin);
	    appproc->continueExecution();
	    appproc->detach(true);
	  }
	
	return 0;
}
[← Prev in Thread] Current Thread [Next in Thread→]