Test if a variable is unavailable in GDB
GDB on macOS can’t display a few of the segment registers, which results in that the respective variables are not available for use. Trying to use them will only print $var is not available
and skip the rest of any currently running function.
Merely executing this line
printf " %04X ", $ds
without $ds
being available would stop executing the whole function.
To lazy to read and just want the solution? Skip to the bottom.
This posed an issue, since I have a function for displaying all current registers, the stack and the current plus the 5 next instructions, which would stop executing in the middle due to the not available register. For a while now I’ve simply commented the part of the script out, due to there being no obvious way of testing if a variable is available. As soon as you try to access it, you get the error.
This was frustrating and I wanted a solution, so I started investigating.
After googling and going through the docs for a few minutes, I noticed I was in for a fun ride. The only relevant source I found was an unanswered question on reverseengineering.stackexchange.com (which, as a result of this, could answer).
A little more googling revealed a question, and answer, on GDB’s mailing list on how to ignore errors in user defined commands, this was making clever use of GDB’s python API. The first solution was found.
I copied the ignore-errors
function into a python script, sourced it in my .gdbinit
and replaced all printf
’s with
ignore-errors printf " %04X ", $ds
It worked! Hooray!
But it was ugly, the printf
wasn’t executed at all, leaving empty spaces. So I decided to search for a better solution.
I know knew you can catch errors in a python script, so I thought there must also be a way to inspect the variable in python, without GDB throwing an error? I wrote a little function to test my theory:
class IsValid (gdb.Function):
def __init__ (self):
super (IsValid, self).__init__("isvalid")
def invoke (self, var):
print "var: ", var
return 0
IsValid ()
It printed var: <unavailable>
! The same you would get in GDB, but without any errors. I was on the right track.
But since I haven’t written any python in a few years, and was therefore a bit rusty and I had to figure out how to get the value print is getting from the variable, without throwing an error. The obvious gdb.Variable.string()
function was throwing errors on me if I tried to access the value through it. After a little of of try and error I figure out __str__()
would get me where I wanted to.
Resulting in this final function:
class IsValid (gdb.Function):
def __init__ (self):
super (IsValid, self).__init__("isvalid")
def invoke (self, var):
if var.__str__() == "<unavailable>":
return 0
else:
return 1
IsValid ()
Now I could replace the printf
lines with this and it would work absolutely lovely:
if ($isvalid($ds))
printf " %04X ", $ds
else
printf " ---- "
endif
One last test, and … Hooray! It worked.
You can get my fixed .gdbinit
from my dotfiles repository on GitHub.