1

Setup: GDB running the internal python interpreter with a script. The content of the script runs the disas on some memory.

I need the output of this command in a python string. How can I do this?

Thought about logging, but it turned out that only the first line of the command is logged:

Dump of assembler code from 0x8000 to 0x8030:

The expected result of the disas is on the screen, but NOT in the logfile.

0x00008000: fsw fa0,24(a5)
0x00008002: fsd fs6,16(sp)
0x00008004: addi    s1,sp,920
...

Is it possible to redirect stdout into a stream or to get the info on somehow other way?

EDIT: I've connected GDB to an openOCD server. On my target are the assembler commands. There is no elf-file ore so on, where I get the source code from. It's just pure machine instructions residing on the controllers program memory and I need to get this printed.

Cutton Eye
  • 3,207
  • 3
  • 20
  • 39

1 Answers1

1

The simplest way to get the output of disassemble into a string is to give gdb.execute a to_string=Trueargument.

(gdb) start
...
(gdb) pi
>>> import pprint
>>> pp=pprint.PrettyPrinter()
>>> pp.pprint(gdb.execute("disassemble main",to_string=True))
('Dump of assembler code for function main:\n'
 '   0x00005555555546a4 <+0>:\tpush   %rbp\n'
 '   0x00005555555546a5 <+1>:\tmov    %rsp,%rbp\n'
 '=> 0x00005555555546a8 <+4>:\tcallq  0x555555554560 <pause@plt>\n'
 '   0x00005555555546ad <+9>:\tmov    $0x0,%eax\n'
 '   0x00005555555546b2 <+14>:\tpop    %rbp\n'
 '   0x00005555555546b3 <+15>:\tretq   \n'
 'End of assembler dump.\n')
>>> 

(I'm using pprint here so that it displays nicely in a terminal session.)

You can write this to a file:

>>> with open("logfile","w") as log:
...   log.write(gdb.execute("disassemble main",to_string=True))
... 
335

It's not too hard to parse this, but as long as you're using gdb's python extensions, you might want to use the gdb.Architecture.disassemble method, which does most of the work for you:

>>> frame=gdb.selected_frame()
>>> hex(frame.block().start)
'0x5555555546a4'
>>> hex(frame.block().end) # doc says this is "one past the last address that appears in the block"
'0x5555555546b4'
>>> arch=frame.architecture()
>>> arch.name()
'i386:x86-64'
>>> pp.pprint(arch.disassemble(frame.block().start, frame.block().end - 1))
[{'addr': 93824992233124, 'asm': 'push   %rbp', 'length': 1},
 {'addr': 93824992233125, 'asm': 'mov    %rsp,%rbp', 'length': 3},
 {'addr': 93824992233128, 'asm': 'callq  0x555555554560 <pause@plt>', 'length': 5},
 {'addr': 93824992233133, 'asm': 'mov    $0x0,%eax', 'length': 5},
 {'addr': 93824992233138, 'asm': 'pop    %rbp', 'length': 1},
 {'addr': 93824992233139, 'asm': 'retq   ', 'length': 1}]
>>>

If there's no debug info for your program, frame.block() will fail with RuntimeError: Cannot locate block for frame.. You can still successfully call arch.disassemble or the gdb disassemble command; just use numeric arguments.

Mark Plotnick
  • 9,598
  • 1
  • 24
  • 40
  • thx for your answere. can't test it now, get hands on HQ after Easter. Question: If I do not have the elf-file provided (see edit), does your solution also work? It looks like to me as your solution does build on the fact, that GDB alrerady has the info provided in it's elf-file? sry for forgetting to mention the "detail" with the openOCD ;) – Cutton Eye Apr 14 '19 at 20:38
  • 1
    I will check. If gdb has no way to detemine function start and end addresses as I used in my example, you'll just need to pass integer arguments to `arch.disassemble`. – Mark Plotnick Apr 15 '19 at 14:44