[lldb-dev] C++ API: Passing information from debuggee to debugger

Duane Ellis duane at duaneellis.com
Tue Jun 2 08:48:34 PDT 2015


Eugene>> I need the deguggee to stop and pass some information to the debugger - if the debugger is attached, that is.

This sounds like “ARM SEMI-HOSTING” but as an application under Linux,

There is a simple way to do this, I did this in a previous system it worked very well.

You need some function in the target, it needs to be a single instance, not multiple instances.

Step 1:   Create some function 

	int   DEBUGGER_CALL(  int command, intptr_t param1, intptr_t param2, … )

	You want to model this like a SYSCALL()

	Return value -1 means error
	Return value (0 or positive) means success

	Parameter 0 - is the command or request.
	Parameter 1 - is an “intptr_t” so it can handle anything
	Parmeter N is the last parameter (you need N=5 for most everything)

	Alternative: Create a “struct sys_call” and pass the structure pointer

SUPER IMPORTANT:
	DO NOT do unions
	DO NOT use VARDAC functions
	Make the API very fixed - explicitly fixed very simple
	Otherwise it does not port across targets very well.
	
These things make it hard
		Host /TARGET ENDIAN changes
		Host / TARGET - bit field ENDIAN
		Host/ TARGET structure packing changes
		Host/TARGET enum size scaling 
		Host/TARGET variable sizes  (i.e.:  INT = 32 or 64bits?)

	I faced the above problems between our 24bit cpu, SunOS and Linux 
	These things where painful hence my suggestion to you	

	Thus you might do something like:

	struct  debugger_syscall {
		// The request
		int   request_id;
		// Debugger sets this value
		int  result_code;
		intptr_t param[ 5 ];
	}

Step 2:

	The default implementation of this function is simply:  “return -1”
	This would indicate DEBUGGER NOT PRESENT or ERROR

	For example it might just do this:

	void DEBUGGER_CALL( struct foo *p )
	{
		p->result_code = -1;
	}

	When you compile this function, you might want to implement it in assembly language
	Reason: If you write this in assembly, you can control the exact opcode sequence

	Otherwise different compiler options (optimizations, etc) cause problems
	Besides, this is a super tiny function about 4 instructions in assembler
	It’s not like you need to write a huge amount of code in assembly.

	Don’t forget about optimizing linkers! These can also cause problems.

	HINT:   Make your code something like this, put extra NOP 
	(See below for reason)

		label:
			load   R0, -1 ;  return value is minus 1
			return
			// these are here for use by the debugger
			NOP
			NOP
			NOP
			NOP
			NOP
			return

Step 3:
	The debugger sets a breakpoint on this function.
	
	The “breakpoint action function” (aka: your script)
	Can now examine the parameters (or structure pointer)
	And perform the requested action

In my case we had two builds of the application: 

	Build(HOSTED) ran on linux (and SunOS, pre-solaris)
	Build(TARGET) - ran on our custom CPU target

When the application was under debug/test, we had 2 choices:

Option 1:   Let it run as normal, the default return value was always -1

Option 2:  Under LINUX/SunOS  Use LD_LIBRARY_PATH to insert a replacement function that could perform additional steps.

  This is of course a huge security risk, but it worked great

  We put this local to the directory where the test was being executed.
  Thus, each ‘test directory’ could have a different  “debugger hook function'

When the application was run on the target (bare metal application)

Option 3)  No debugger present, the default library function just returned -1 (error)
    We shipped product this way - in masked ROM chips

Option 4)  With the debugger present,

     we set a breakpoint at that function and setup a script to perform some action on that breakpoint
     In our case, we inspected the opcodes at that location
     If they where incorrect the test session was rejected as failure

     If they where correct, the debugger modified the OPCODES 
	This is why we put the extra NOPs in the function
	We had extra room to do what we required

    On program load - the debugger can look for your “magic symbol name”
	If the magic symbol is present in the symbol table
	The debugger can “attach” and automate much of this process

Worked really well - same code ran on Sun SPARC,  LINUX, and our custom 24bit CPU target under GDB

In our case, on the debugger side, we had some very standard code that extracted the parameter structure

At the bottom of that standard code you have a dispatch table that mapped the request ID to a handler in the debugger script

Worked great

If you implement the above in a standard way, it would be universal to all LLDB instances

Each target wanting to support this would only need to create the *very* tiny 5 to 10 opcode long  “DEBUGGER_CALL” function

I would also suggest a series of standard function numbers (i.e.: the ARM semi-hosting ones are a good start)

-Duane.






More information about the lldb-dev mailing list