cstringdebuggingassemblygdb

How can I make gdb print unprintable characters of a string in hex instead of octal while preserving the ascii characters in ascii form?


Suppose I have a buffer buf whose c string representation is

 char* buf = "Hello World \x1c"

When I print this buf in gdb using the command p buf, I get the following

 $1 = "Hello World \034"

Is there a print command or a gdb setting that will print the following instead?

$1 = "Hello World \x1c"

I have tried various format parameters such as /c and /x, but none of them get the effect that I am looking for. I have also played with printf but was unable to achieve the desired effect.

Update: I am using "GNU gdb (GDB) 7.0.1-debian".

Update: I have played with x as well.

If I do x/c it prints octal and decimal for nonprintable characters, and then prints printable characters with the ascii and decimal.

If I do x/s it outputs exactly the same as the p command.

If I do x/x it just outputs hex but then we lose the ascii characters for the printable part.

Update: This reference, unless incomplete, suggests that what I desire is not available, but can anyone confirm?


Solution

  • In the absence of an existing solution, I created this gdb command which prints ascii and hex for strings that have mixed printable ascii and non-printable characters. The source is reproduced below.

        from __future__ import print_function
        
        import gdb
        import string
        class PrettyPrintString (gdb.Command):
            "Command to print strings with a mix of ascii and hex."
        
            def __init__(self):
                super (PrettyPrintString, self).__init__("ascii-print",
                        gdb.COMMAND_DATA,
                        gdb.COMPLETE_EXPRESSION, True)
                gdb.execute("alias -a pp = ascii-print", True)
        
            def invoke(self, arg, from_tty):
                arg = arg.strip()
                if arg == "":
                    print("Argument required (starting display address).")
                    return
                startingAddress = gdb.parse_and_eval(arg)
                p = 0
                print('"', end='')
                while startingAddress[p] != ord("\0"):
                    charCode = int(startingAddress[p].cast(gdb.lookup_type("char")))
                    if chr(charCode) in string.printable:
                        print("%c" % chr(charCode), end='')
                    else:
                        print("\\x%x" % charCode, end='')
                    p += 1
                print('"')
        
        PrettyPrintString()
    

    To use this, one can simply put the source AsciiPrintCommand.py and then run the following in gdb. For convenience, one can put put the above source command into their $HOME/.gdbinit.

    ascii-print buf
    "Hello World \x1c"
    

    Here are step-by-step instructions for gdb v12.1 on Ubuntu x64 22.04 LTS:

    1 - Create a folder called ~/.config/gdb" 2 - In that folder create a text file called "AsciiPrintCommand.py".
    3 - Copy this solutions source code into that file - Important remove all excess indentation from all lines (e.g. 1st line should read "from" and not "    from"
    4 - In that same folder create another file "gdbinit" containing... set output-radix 16
    source ~/.config/gdb/AsciiPrintCommand.py
    5 - run up gdb
    6 - to test it worked type ---> ascii-print "hello"
    This method works for starting gdb from any dir, and also from inside a 32bit schroot. Putting these files into ~/.config avoids the gdb "file or directory doesn't exist" bug when trying to gdbinit from mounted paths.