How do you extract local variable information (address and type) from a Delphi program or the compiler-generated debug info?
Asked Answered
B

2

105

My goal is:

  • Given a suspended thread in a Delphi-compiled 32 or 64-bit Windows program, to walk the stack (doable)
  • Given stack entries, to enumerate the local variables in each method and their values. That is, at the very least, find their address and type (integer32/64/signed/unsigned, string, float, record, class...) the combination of which can be used to find their value.

The first is fine and it's the second that this question is about. At a high level, how do you enumerate local variables given a stack entry in Delphi?


At a low level, this is what I've been investigating:

RTTI: does not list this kind of information about methods. This was not something I actually ever thought was a realistic option, but listing here anyway.

Debug information: Loading the debug info produced for a debug build.

  • Map files: even a detailed map file (a text-format file! Open one and have a look) does not contain local variable info. It's basically a list of addresses and source file line numbers. Great for address to file&line correlation, e.g. the blue dots in the gutter; not great for more detailed information
  • Remote debugging information (RSM file) - no known information on its contents or format.
  • TD32/TDS files: my current line of research. They contain global and local symbols among a lot of other information.

The problems I'm encountering here are:

  • There's no documentation of the TD32 file format (that I can find.)
  • Most of my knowledge of them comes from the Jedi JCL code using them (JclTD32.pas) and I'm not sure how to use that code, or whether the structures there are extensive enough to show local vars. I'm pretty certain it will handle global symbols, but I'm very uncertain about local. There are a wide variety of constants defined and without documentation for the format, to read what they mean, I'm left guessing. However, those constants and their names must come from somewhere.
  • Source I can find using TDS info does not load or handle local symbols.

If this is the right approach, then this question becomes 'Is there documentation for the TDS/TD32 file format, and are there any code samples that load local variables?'

A code sample isn't essential but could be very useful, even if it's very minimal.

Bise answered 1/5, 2015 at 16:17 Comment(8)
I haven't actually used the Jedi JCL units to access TD32 information - I have my own proprietary library for that, but it does look like all the basic plumbing you'll need is there in JclTD32.pas. There is no demo code that I can find for accessing the variable information, though, but the sample that is there (in ..\jcl\examples\windows\debug\sourceloc) shows how to get line number information from TD32 data, so you should be able to build on that to get at what you need. Please report back here what you find out :)Provolone
@500-InternalServerError Thanks. Line number info is easy (it's even in map files) - but can you provide any info on what you see in the JCL code that specifically relates to local symbols? Also, out of curiosity, what is your TD32 proprietary library, and is it published / publicly usable or in-house only?Bise
Each procedure/function/method symbol under it in turn contains a list of symbols that are local to it. Most definitions appear to be there in the Jedi unit, but some are comment out. My suggestion would be to create tiny test apps and look at what an enumeration of symbols returns. The code I have is proprietary and not for me to publish. It doesn't cover the topic of local variables anyway. But the information it is based on is semi-public, so I may be able to help out if you run into specific walls.Provolone
tds2pdb (code.google.com/p/map2dbg) seems to have a parser for tds files. It's C# code though.Paulina
Yup, tds2pdb is your route forward.Outer
Is there any documentation for the format that you know about, @500-InternalServerError or David? I haven't found any, and it would help.Bise
There used to be an informal document, yes, but then Borland (at the time) decided to release a dll instead for accessing the debug information so that they could change the internal format and not have to update the documentation. Unfortunately, I can locate neither the original document nor the dll right now. I suggest you contact Embarcadero technical support and ask about it.Provolone
Sidenote: This is currently the lowest viewed question with score 100 or higher in StackOverflow.Plated
T
2

Check if any debugging symbols weren't in binary. Also possible is using GDB (on Windows a port of it). It would be great if you found a .dbg or .dSYM file. They contain source code, eg.

gdb> list foo
56 void foo()
57 {
58  bar();
59  sighandler_t fnc = signal(SIGHUP, SIG_IGN);
60  raise(SIGHUP);
61  signal(SIGHUP, fnc);
62  baz(fnc);
63 }

If you don't have any debugging files, you may try to get MinGW or Cygwin, and use nm(1) (man page). It will read symbol names from binary. They may contain some types, like C++ ones:

int abc::def::Ghi::jkl(const std::string, int, const void*)

Don't forget to add --demangle option then or you'll get something like:

__ZN11MRasterFont21getRasterForCharacterEh

instead of:

MRasterFont::getRasterForCharacter(unsigned char)
Tarah answered 4/11, 2015 at 1:51 Comment(2)
Jakub, thanks for the answer. Unfortunately I probably need to read a specific debug format - TDS. Delphi apps aren't compiled with GDB-compatible debug info on Windows. I'm not sure how nm will help either, since it will rely on a specific debug file format which is probably not one that Delphi generates. Or have I misunderstood your answer - can GDB read Delphi's symbols, for example?Bise
@DavidM, your comment is very crucial. Try to find port of GNU Binutils or GNU Debugger on Windows (I know only Binutils port). There is a BFD library for GDB. It is used also in Binutils. It allows to read multiple file formats and recognise them by their magic numbers. If everything fails, use tool called strings. It will extract strings from any binary file. See man page. This will print strings which may but don't have to be helpfulTarah
T
0

Take a look at the http://download.xskernel.org/docs/file%20formats/omf/borland.txt Open Architecture Handbook. It is old, but maybe you find some relevant information about the file format.

Transhumance answered 23/1, 2020 at 15:22 Comment(2)
Can you please add some context, the link may become broken in the future.Stilwell
The link contains the official document from borland about the OMF file format used by Borland compiler and other binary file formats used by Borland. Some years ago I took a look at the TDS file format and there seemed some parts be compatible with the documented file formats. When trying to gather any information about the local variables from the TDS file, the linked documentation should be used or referenced. If the link comes broken, my answer will be useless and the needed information will be lost. The linked "Open Architecture Handbook" was part of old Turbo Pascal and C releases.Transhumance

© 2022 - 2024 — McMap. All rights reserved.