Batch file to get a (non-sorted non-unique) list of units from a Delphi compiled map file
Posted by jpluimers on 2017/01/10
A few weeks ago in When you get “TfsScript.Execute” throwing a “Unregistered version of FastScript.” I wrote about “a process that explains any modules in the MAP file not resulting in DCU files”.
The below batch file aids in that process.
It takes a MAP file from your Delphi compiled executable that has debug information in text format which means you need to set your project linker options to generate detailed MAP files.
The Map Debug File (*.map) – RAD Studio documentation hasn’t much information but points to Detailed-Segments Map File – RAD Studio which has a bit more. Neither contain information on Delphi units as they focus too much on the C++ side of things. Then there is a tiny bit information in Understanding Delphi MAP File – Stack Overflow.
So I did some spelunking and came up with this batch-file which will likely work back until about the Delphi 7 era:
@echo off
if ["%*"]==[] goto :help
:logic
if not exist %1 goto :notExist
setlocal
:: do not use periods (.) in delims as unit names may contain them!
:: it might be that the segment (%%a) for C=CODE/S=.text/G=(none)/M=*/ACBP=A9 is always 0001
:: it might be that the segment (%%a) for C=ICODE/S=.itext/G=(none)/M=*/ACBP=A9 is always 0002
:: it might be that the segment (%%a) for C=DATA/S=.data/G=DGROUP/M=*/ACBP=A9 is always 0003
:: it might be that the segment (%%a) for C=BSS/S=.bss/G=DGROUP/M=*/ACBP=A9 is always 0004
:: it might be that the segment (%%a) for C=TLS/S=.tls/G=(none)/M=*/ACBP=A9 is always 0005
:: the `if ["%%h"]==["(none)"] ` trick is to prevent this error:
:: ) was unexpected at this time.
for /f "tokens=1,2,3,4,5,6,7,8,9,10,11,12,13,14,* delims=()= " %%a IN (%1) do (
if [%%c]==[C] if [%%e]==[S] if [%%g]==[G] if [%%i]==[M] if [%%k]==[ACBP] if [%%l]==[A9] if [%%m]==[] (
if [%%d]==[CODE] if [%%f]==[.text] if ["%%h"]==["(none)"] echo %%j
if [%%d]==[ICODE] if [%%f]==[.itext] if ["%%h"]==["(none)"] echo %%j
if [%%d]==[DATA] if [%%f]==[.data] if ["%%h"]==["DGROUP"] echo %%j
if [%%d]==[BSS] if [%%f]==[.bss] if ["%%h"]==["DGROUP"] echo %%j
if [%%d]==[TLS] if [%%f]==[.tls] if ["%%h"]==["(none)"] echo %%j
)
if [%%a]==[line] if [%%b]==[numbers] if [%%c]==[for] if [%%f]==[segment] if [%%g]==[.text] if [%%h]==[] (
echo %%d
)
)
endlocal
goto :eof
echo a=%%a;b=%%b;b=%%b;c=%%c;d=%%d;e=%%e;f=%%f;g=%%g;h=%%h;i=%%i;j=%%j;k=%%k;l=%%l;m=%%m;n=%%n
if [%%c]==[C] if [%%d]==[CODE] if [%%e]==[S] if [%%f]==[.text] if [%%g]==[G] if ["%%h"]==["(none)"] if [%%i]==[M] if [%%k]==[ACBP] if [%%l]==[A9] if [%%m]==[] echo %%j
:notExist
echo %* does not exist.
:help
echo syntax: %0 MAP-file
echo will list all the Delphi units that got compiled into your EXE in a non-sorted non-unique way
goto :eof
Pipe it through sort and then to some sort of uniq script like this PowerShell one.
Then check it against the sorted list of .DCU files the Delphi compiler produced for your project (ensure you cleaned the .DCU directory before compiling).
I usually use Beyond Compare to do this comparison for me.
–jeroen






Thomas Mueller said
Delphi version of it (independent implementation): https://sourceforge.net/p/dzlib/code/HEAD/tree/dzlib/trunk/src/u_dzMapFileReader.pas