For scenarios like this I implemented a gdb RSP (remote serial protocol) server that interprets the content of a trace. The way it works is gdb connects to a remote target through a socket as you usually do while debugging in embedded environments. Gdb speaks RSP protocol. I have a server that basically listens on a socket port and gets an RTL simulation trace with specific updates to registers etc. as input. This server interprets the requests from gdb like step forward, read a register a variable etc and replies based on the contents of the trace.
The effect is that gdb believes is talking to an active CPU while is actually re-playing a trace. gdb is oblivious to it. This has nice properties:
The only caveat is the execution is read-only, I.e. no poking on registers expecting to alter the execution.
See rsp_trace_server project.