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
$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
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 ()
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.