📄 jcldebug.pas
字号:
ImageStream: TMemoryStream;
NtHeaders: PImageNtHeaders;
Sections, LastSection, JclDebugSection: PImageSectionHeader;
VirtualAlignedSize: DWORD;
I, X, NeedFill: Integer;
procedure RoundUpToAlignment(var Value: DWORD; Alignment: DWORD);
begin
if (Value mod Alignment) <> 0 then
Value := ((Value div Alignment) + 1) * Alignment;
end;
begin
MapFileSize := 0;
JclDebugDataSize := 0;
LineNumberErrors := 0;
LinkerBugUnit := '';
if BinDebug.Stream <> nil then
begin
Result := True;
if BinDebug.LinkerBug then
begin
LinkerBugUnit := BinDebug.LinkerBugUnitName;
LineNumberErrors := BinDebug.LineNumberErrors;
end;
end
else
Result := False;
if not Result then
Exit;
ImageStream := TMemoryStream.Create;
try
try
ImageStream.LoadFromFile(ExecutableFileName);
MapFileSize := BinDebug.Stream.Size;
JclDebugDataSize := BinDebug.DataStream.Size;
NtHeaders := PeMapImgNtHeaders(ImageStream.Memory);
Assert(NtHeaders <> nil);
Sections := PeMapImgSections(NtHeaders);
Assert(Sections <> nil);
// Check whether there is not a section with the name already. If so, return True (#0000069)
if PeMapImgFindSection(NtHeaders, JclDbgDataResName) <> nil then
begin
Result := True;
Exit;
end;
LastSection := Sections;
Inc(LastSection, NtHeaders^.FileHeader.NumberOfSections - 1);
JclDebugSection := LastSection;
Inc(JclDebugSection);
// Increase the number of sections
Inc(NtHeaders^.FileHeader.NumberOfSections);
FillChar(JclDebugSection^, SizeOf(TImageSectionHeader), #0);
// JCLDEBUG Virtual Address
JclDebugSection^.VirtualAddress := LastSection^.VirtualAddress + LastSection^.Misc.VirtualSize;
RoundUpToAlignment(JclDebugSection^.VirtualAddress, NtHeaders^.OptionalHeader.SectionAlignment);
// JCLDEBUG Physical Offset
JclDebugSection^.PointerToRawData := LastSection^.PointerToRawData + LastSection^.SizeOfRawData;
RoundUpToAlignment(JclDebugSection^.PointerToRawData, NtHeaders^.OptionalHeader.FileAlignment);
// JCLDEBUG Section name
StrPLCopy(PChar(@JclDebugSection^.Name), JclDbgDataResName, IMAGE_SIZEOF_SHORT_NAME);
// JCLDEBUG Characteristics flags
JclDebugSection^.Characteristics := IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;
// Size of virtual data area
JclDebugSection^.Misc.VirtualSize := JclDebugDataSize;
VirtualAlignedSize := JclDebugDataSize;
RoundUpToAlignment(VirtualAlignedSize, NtHeaders^.OptionalHeader.SectionAlignment);
// Update Size of Image
Inc(NtHeaders^.OptionalHeader.SizeOfImage, VirtualAlignedSize);
// Raw data size
JclDebugSection^.SizeOfRawData := JclDebugDataSize;
RoundUpToAlignment(JclDebugSection^.SizeOfRawData, NtHeaders^.OptionalHeader.FileAlignment);
// Update Initialized data size
Inc(NtHeaders^.OptionalHeader.SizeOfInitializedData, JclDebugSection^.SizeOfRawData);
// Fill data to alignment
NeedFill := Integer(JclDebugSection^.SizeOfRawData) - JclDebugDataSize;
// Note: Delphi linker seems to generate incorrect (unaligned) size of
// the executable when adding TD32 debug data so the position could be
// behind the size of the file then.
ImageStream.Seek(JclDebugSection^.PointerToRawData, soFromBeginning);
ImageStream.CopyFrom(BinDebug.DataStream, 0);
X := 0;
for I := 1 to NeedFill do
ImageStream.WriteBuffer(X, 1);
ImageStream.SaveToFile(ExecutableFileName);
except
Result := False;
end;
finally
ImageStream.Free;
end;
end;
//=== { TJclBinDebugGenerator } ==============================================
constructor TJclBinDebugGenerator.Create(const MapFileName: TFileName);
begin
inherited Create(MapFileName);
FDataStream := TMemoryStream.Create;
FMapFileName := MapFileName;
if FStream <> nil then
CreateData;
end;
destructor TJclBinDebugGenerator.Destroy;
begin
FreeAndNil(FDataStream);
inherited Destroy;
end;
{$OVERFLOWCHECKS OFF}
function TJclBinDebugGenerator.CalculateCheckSum: Boolean;
var
Header: PJclDbgHeader;
P, EndData: PChar;
CheckSum: Integer;
begin
Result := DataStream.Size >= SizeOf(TJclDbgHeader);
if Result then
begin
P := DataStream.Memory;
EndData := P + DataStream.Size;
Header := PJclDbgHeader(P);
CheckSum := 0;
Header^.CheckSum := 0;
Header^.CheckSumValid := True;
while P < EndData do
begin
Inc(CheckSum, PInteger(P)^);
Inc(PInteger(P));
end;
Header^.CheckSum := CheckSum;
end;
end;
{$IFDEF OVERFLOWCHECKS_ON}
{$OVERFLOWCHECKS ON}
{$ENDIF OVERFLOWCHECKS_ON}
procedure TJclBinDebugGenerator.CreateData;
var
FileHeader: TJclDbgHeader;
WordList: TStringList;
WordStream: TMemoryStream;
I, D: Integer;
S: string;
L1, L2, L3: Integer;
FirstWord, SecondWord: Integer;
function AddWord(const S: string): Integer;
var
N: Integer;
E: string;
begin
if S = '' then
begin
Result := 0;
Exit;
end;
N := WordList.IndexOf(S);
if N = -1 then
begin
Result := WordStream.Position;
E := EncodeNameString(S);
WordStream.WriteBuffer(Pointer(E)^, Length(E));
WordList.AddObject(S, TObject(Result));
end
else
Result := DWORD(WordList.Objects[N]);
Inc(Result);
end;
procedure WriteValue(Value: Integer);
var
L: Integer;
D: DWORD;
P: array [1..5] of Byte;
begin
D := Value;
L := 0;
while D > $7F do
begin
Inc(L);
P[L] := (D and $7F) or $80;
D := D shr 7;
end;
Inc(L);
P[L] := (D and $7F);
FDataStream.WriteBuffer(P, L);
end;
procedure WriteValueOfs(Value: Integer; var LastValue: Integer);
begin
WriteValue(Value - LastValue);
LastValue := Value;
end;
begin
WordStream := TMemoryStream.Create;
WordList := TStringList.Create;
try
WordList.Sorted := True;
WordList.Duplicates := dupError;
FileHeader.Signature := JclDbgDataSignature;
FileHeader.Version := JclDbgHeaderVersion;
FileHeader.CheckSum := 0;
FileHeader.CheckSumValid := False;
FileHeader.ModuleName := AddWord(PathExtractFileNameNoExt(FMapFileName));
FDataStream.WriteBuffer(FileHeader, SizeOf(FileHeader));
FileHeader.Units := FDataStream.Position;
L1 := 0;
L2 := 0;
for I := 0 to Length(FSegments) - 1 do
begin
WriteValueOfs(FSegments[I].StartAddr, L1);
WriteValueOfs(AddWord(MapStringToStr(FSegments[I].UnitName)), L2);
end;
WriteValue(MaxInt);
FileHeader.SourceNames := FDataStream.Position;
L1 := 0;
L2 := 0;
for I := 0 to Length(FSourceNames) - 1 do
begin
WriteValueOfs(FSourceNames[I].Addr, L1);
WriteValueOfs(AddWord(MapStringToStr(FSourceNames[I].ProcName)), L2);
end;
WriteValue(MaxInt);
FileHeader.Symbols := FDataStream.Position;
L1 := 0;
L2 := 0;
L3 := 0;
for I := 0 to Length(FProcNames) - 1 do
begin
WriteValueOfs(FProcNames[I].Addr, L1);
S := MapStringToStr(FProcNames[I].ProcName);
D := Pos('.', S);
if D = 1 then
begin
FirstWord := 0;
SecondWord := 0;
end
else
if D = 0 then
begin
FirstWord := AddWord(S);
SecondWord := 0;
end
else
begin
FirstWord := AddWord(Copy(S, 1, D - 1));
SecondWord := AddWord(Copy(S, D + 1, Length(S)));
end;
WriteValueOfs(FirstWord, L2);
WriteValueOfs(SecondWord, L3);
end;
WriteValue(MaxInt);
FileHeader.LineNumbers := FDataStream.Position;
L1 := 0;
L2 := 0;
for I := 0 to Length(FLineNumbers) - 1 do
begin
WriteValueOfs(FLineNumbers[I].Addr, L1);
WriteValueOfs(FLineNumbers[I].LineNumber, L2);
end;
WriteValue(MaxInt);
FileHeader.Words := FDataStream.Position;
FDataStream.CopyFrom(WordStream, 0);
I := 0;
while FDataStream.Size mod 4 <> 0 do
FDataStream.WriteBuffer(I, 1);
FDataStream.Seek(0, soFromBeginning);
FDataStream.WriteBuffer(FileHeader, SizeOf(FileHeader));
finally
WordStream.Free;
WordList.Free;
end;
end;
//=== { TJclBinDebugScanner } ================================================
constructor TJclBinDebugScanner.Create(AStream: TCustomMemoryStream; CacheData: Boolean);
begin
FCacheData := CacheData;
FStream := AStream;
CheckFormat;
end;
procedure TJclBinDebugScanner.CacheLineNumbers;
var
P: Pointer;
Value, LineNumber, C: Integer;
CurrAddr: DWORD;
begin
if FLineNumbers = nil then
begin
LineNumber := 0;
CurrAddr := 0;
C := 0;
P := MakePtr(PJclDbgHeader(FStream.Memory)^.LineNumbers);
while ReadValue(P, Value) do
begin
Inc(CurrAddr, Value);
ReadValue(P, Value);
Inc(LineNumber, Value);
SetLength(FLineNumbers, C + 1);
FLineNumbers[C].Addr := CurrAddr;
FLineNumbers[C].LineNumber := LineNumber;
Inc(C);
end;
end;
end;
procedure TJclBinDebugScanner.CacheProcNames;
var
P: Pointer;
Value, FirstWord, SecondWord, C: Integer;
CurrAddr: DWORD;
begin
if FProcNames = nil then
begin
FirstWord := 0;
SecondWord := 0;
CurrAddr := 0;
C := 0;
P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);
while ReadValue(P, Value) do
begin
Inc(CurrAddr, Value);
ReadValue(P, Value);
Inc(FirstWord, Value);
ReadValue(P, Value);
Inc(SecondWord, Value);
SetLength(FProcNames, C + 1);
FProcNames[C].Addr := CurrAddr;
FProcNames[C].FirstWord := FirstWord;
FProcNames[C].SecondWord := SecondWord;
Inc(C);
end;
end;
end;
{$OVERFLOWCHECKS OFF}
procedure TJclBinDebugScanner.CheckFormat;
var
CheckSum: Integer;
Data, EndData: PChar;
Header: PJclDbgHeader;
begin
Data := FStream.Memory;
Header := PJclDbgHeader(Data);
FValidFormat := (Data <> nil) and (FStream.Size > SizeOf(TJclDbgHeader)) and
(FStream.Size mod 4 = 0) and
(Header^.Signature = JclDbgDataSignature) and (Header^.Version = JclDbgHeaderVersion);
if FValidFormat and Header^.CheckSumValid then
begin
CheckSum := -Header^.CheckSum;
EndData := Data + FStream.Size;
while Data < EndData do
begin
Inc(CheckSum, PInteger(Data)^);
Inc(PInteger(Data));
end;
CheckSum := (CheckSum shr 8) or (CheckSum shl 24);
FValidFormat := (CheckSum = Header^.CheckSum);
end;
end;
{$IFDEF OVERFLOWCHECKS_ON}
{$OVERFLOWCHECKS ON}
{$ENDIF OVERFLOWCHECKS_ON}
function TJclBinDebugScanner.DataToStr(A: Integer): string;
var
P: PChar;
begin
if A = 0 then
Result := ''
else
begin
P := PChar(DWORD(A) + DWORD(FStream.Memory) + DWORD(PJclDbgHeader(FStream.Memory)^.Words) - 1);
Result := DecodeNameString(P);
end;
end;
function TJclBinDebugScanner.GetModuleName: string;
begin
Result := DataToStr(PJclDbgHeader(FStream.Memory)^
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -