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.

comments powered by Disqus