Symbol formats
Delphi does not emit one debug-info format. It emits several, into different places, each carrying a different part of the picture — and the debugger reads all of them because no single one is sufficient.
What each artefact buys you
The plain-language version first, because it is what actually matters when a build is misconfigured:
| Artefact | Without it |
|---|---|
TD32 section, .map, or JCL data — any one |
No source lines: no breakpoints, no stepping |
.rsm |
Breakpoints and stepping still work, but local variables, types and expression evaluation are severely limited |
| Optimizations off | Breakpoints land on the wrong line and locals read as garbage, because the code no longer matches the source |
Note the shape of the first row: any one of three sources will give you source lines. That is the whole reason this is not a precedence list.
The five formats
| Format | Where it lives | What it carries |
|---|---|---|
| TD32 | Embedded in the binary — Borland’s .debug PE section, magic FB09 |
Source lines, file names, symbols. The primary line source when present. |
.map |
Sidecar file, emitted by the linker at Detailed | Line numbers and segment/symbol addresses, plus nested-procedure linkage |
.rsm |
Sidecar file, from Include remote debug symbols | Local variables and type information. Optional — the debugger works without it, with reduced fidelity. |
.dcp |
Sidecar file, produced by a package build | The same format as .rsm, supplying a BPL’s debug information |
| JCL | A JCLDEBUG PE section, or a sidecar .jdbg |
Line information. On by default; a fallback line source. |
An external .tds (dcc64 -VT) is also read, carrying the same
kind of content as the embedded TD32 section.
Contribution, not precedence
It is tempting to describe this as an ordered list — try TD32, fall back to MAP, fall back to JCL. That is not what happens, and describing it that way would mislead. Different formats answer different questions, and a build can be missing one capability entirely while another is fully served.
flowchart LR TD32["TD32
.debug PE section"] MAP["MAP
.map sidecar"] JCL["JCL
JCLDEBUG / .jdbg"] RSM["RSM
.rsm sidecar"] DCP["DCP
.dcp, for BPLs"] subgraph LINES["Source lines & breakpoints"] L["any ONE of these is enough"] end subgraph VARS["Local variables & types"] V["RSM or DCP
(optional — absence degrades, does not break)"] end subgraph NEST["Nested-procedure linkage"] N["MAP"] end TD32 --> L MAP --> L JCL --> L RSM --> V DCP --> V MAP --> N
Read it as three independent capabilities, each fed by whatever is available. Lose every
line source and the debugger cannot set a breakpoint at all. Lose only the
.rsm and it still stops exactly where you asked — you just cannot see the
locals when it does.
Per module, not per session
All of this applies to each loaded image independently. A host EXE with complete debug
information and a BPL built without any is entirely normal, and the debugger reports it
per module rather than as a single session-wide state: loaded,
noSymbols, or indexing while an index is still filling.
The formats that registered for a module are reported too, which distinguishes
two situations that look identical from the outside: a binary that carries no debug
information at all, and one whose sidecar simply has not been found. See
get_loaded_modules and
Multi-BPL and modules.
.rsm, .dcp and
.jdbg map addresses but hold no file index, so for a module carrying only
those, the file list is unknown rather than empty — and a breakpoint there may
still bind.
The one thing none of them fix
TD32’s names table stores source files by basename only, with no
directory. Two Oracle.pas files in different folders are therefore
indistinguishable to any consumer of that table, and a frame can resolve to the wrong one
at the “same” line.
There is no downstream fix: the information was not emitted. It is recorded in Known limitations as a property of the format rather than a defect of the debugger, because that distinction determines whether it is worth reporting as a bug.