mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 16:35:33 +00:00
192 lines
6.3 KiB
ObjectPascal
192 lines
6.3 KiB
ObjectPascal
unit Keyman.System.MapToSym;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes,
|
|
System.StrUtils,
|
|
System.SysUtils,
|
|
Winapi.Windows,
|
|
|
|
Keyman.System.JclMapScanner;
|
|
|
|
type
|
|
TMapToSymFindFileProc = function(const UnitName: string): string;
|
|
|
|
function CreateSymFromMap(const exeFile, mapFile, symFile, codeId: string;
|
|
isAmd64: Boolean; guid: TGUID; age: Integer; findFileProc: TMapToSymFindFileProc): Boolean;
|
|
function GuidToDebugId(guid: TGUID; age: Integer): string;
|
|
|
|
implementation
|
|
|
|
type
|
|
TMapToSym = class(TJclMapScanner)
|
|
function Write(const exeFile, symFile, codeId: string; isAmd64: Boolean; guid: TGUID; age: Integer): Boolean;
|
|
private
|
|
FFindFileProc: TMapToSymFindFileProc;
|
|
function UnitIndexFromVA(va: DWORD): Integer;
|
|
property FindFileProc: TMapToSymFindFileProc read FFindFileProc write FFindFileProc;
|
|
end;
|
|
|
|
// Crack access to TJclMapScanner private symbols
|
|
TJclMapScannerCracker = class(TJclAbstractMapParser)
|
|
private
|
|
FSegmentClasses: array of TJclMapSegmentClass;
|
|
FLineNumbers: array of TJclMapLineNumber;
|
|
FProcNames: array of TJclMapProcName;
|
|
FSegments: array of TJclMapSegment;
|
|
end;
|
|
|
|
{ TMapToSym }
|
|
|
|
function GuidToDebugId(guid: TGUID; age: Integer): string;
|
|
begin
|
|
Result := GuidToString(guid);
|
|
Delete(Result, 1, 1);
|
|
Delete(Result, 9, 1);
|
|
Delete(Result, 13, 1);
|
|
Delete(Result, 17, 1);
|
|
Delete(Result, 21, 1);
|
|
Delete(Result, 33, 1);
|
|
Result := Result + IntToStr(age);
|
|
end;
|
|
|
|
function TMapToSym.UnitIndexFromVA(va: DWORD): Integer;
|
|
var
|
|
s: TJclMapScannerCracker;
|
|
i: Integer;
|
|
begin
|
|
s := TJclMapScannerCracker(Self);
|
|
for i := 0 to Length(s.FSegments) div 2 - 1 do
|
|
begin
|
|
if (s.FSegments[i].StartVA <= va) and (s.FSegments[i].EndVA > va) then Exit(i);
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
function TMapToSym.Write(const exeFile, symFile, codeId: string; isAmd64: Boolean; guid: TGUID; age: Integer): Boolean;
|
|
var
|
|
s: TJclMapScannerCracker;
|
|
r: TStringList;
|
|
i: Integer;
|
|
fileName, unitName: string;
|
|
procCount, lineCount: Integer;
|
|
procLen, lineIndex, lineLen: Integer;
|
|
procName: string;
|
|
FTextSegment, FITextSegment: Word;
|
|
begin
|
|
r := TStringList.Create;
|
|
try
|
|
s := TJclMapScannerCracker(Self); // gain access to private members
|
|
// (these should have been protected, really)
|
|
Self.Parse;
|
|
|
|
// The funcs are listed twice; we only need the first reference
|
|
procCount := Length(s.FProcNames) div 2 - 1;
|
|
lineCount := Length(s.FLineNumbers) div 2 - 1;
|
|
|
|
// Header information
|
|
r.Add(Format('MODULE windows %s %s %s', [IfThen(isAmd64, 'x86_64', 'x86'), GuidToDebugId(guid, age), ExtractFileName(exeFile)]));
|
|
r.Add(Format('INFO CODE_ID %s %s', [codeId, ExtractFileName(exeFile)]));
|
|
|
|
FTextSegment := $FFFF;
|
|
FITextSegment := $FFFF; // only for x86
|
|
for i := 0 to High(s.FSegmentClasses) do
|
|
begin
|
|
if MapStringCacheToStr(s.FSegmentClasses[i].GroupName) = 'CODE' then
|
|
FTextSegment := s.FSegmentClasses[i].Segment
|
|
else if MapStringCacheToStr(s.FSegmentClasses[i].GroupName) = 'ICODE' then
|
|
FITextSegment := s.FSegmentClasses[i].Segment;
|
|
end;
|
|
|
|
// Find unit names;
|
|
for i := 0 to High(s.FSegments) do
|
|
begin
|
|
if s.FSegments[i].Segment = FTextSegment then
|
|
// (s.FSegments[i].FProcNames[i].Segment <> FITextSegment) then Continue;
|
|
|
|
// groupName := MapStringCacheToStr(s.FSegmentClasses[s.FSegments[i].Segment].GroupName);
|
|
// if groupName = 'CODE' then
|
|
begin
|
|
unitName := MapStringCacheToStr(s.FSegments[i].UnitName);
|
|
|
|
if Assigned(FFindFileProc)
|
|
then fileName := FFindFileProc(unitName)
|
|
else fileName := '';
|
|
|
|
if fileName = '' then
|
|
fileName := unitName + '.pas';
|
|
|
|
r.Add(Format('FILE %d %s', [i+1, fileName])); // Note: occasional units may be .inc, .cpp
|
|
end;
|
|
end;
|
|
|
|
// Write out functions and line numbers
|
|
lineIndex := 0;
|
|
for i := 0 to procCount - 1 do
|
|
begin
|
|
if (i > 0) and (s.FProcNames[i].VA < s.FProcNames[i-1].VA) then
|
|
Break;
|
|
|
|
if i < procCount - 1
|
|
then procLen := s.FProcNames[i+1].VA - s.FProcNames[i].VA
|
|
else procLen := 0; // TODO: look at module size for maximum here?
|
|
|
|
if (s.FProcNames[i].Segment <> FTextSegment) and
|
|
(s.FProcNames[i].Segment <> FITextSegment) then Continue;
|
|
|
|
procName := MapStringCacheToStr(s.FProcNames[i].ProcName);
|
|
r.Add(Format('FUNC %x %x 0 %s', [s.FProcNames[i].VA + $1000 {constant entry point}, procLen, procName]));
|
|
|
|
if (lineIndex >= lineCount) or (s.FLineNumbers[lineIndex].VA >= s.FProcNames[i].VA + Cardinal(procLen)) then
|
|
begin
|
|
// No line numbers, let's add an artificial line, otherwise Sentry does
|
|
// not use it for symbolication
|
|
r.Add(Format('%x %x %d %d', [s.FProcNames[i].VA + $1000, procLen, 1, UnitIndexFromVA(s.FProcNames[i].VA)+1]));
|
|
end;
|
|
|
|
while (lineIndex < lineCount) and (s.FLineNumbers[lineIndex].VA < s.FProcNames[i].VA + Cardinal(procLen)) do
|
|
begin
|
|
if lineIndex < lineCount - 1
|
|
then lineLen := s.FLineNumbers[lineIndex+1].VA - s.FLineNumbers[lineIndex].VA
|
|
else lineLen := 0; // TODO: look at module size for maximum here?
|
|
|
|
// We never want the range to go outside the bounds of the function -- if there are no
|
|
// line numbers for the next function, then we ensure we end at the function boundary
|
|
if s.FLineNumbers[lineIndex].VA + Cardinal(lineLen) > s.FProcNames[i].VA + Cardinal(procLen) then
|
|
lineLen := s.FProcNames[i].VA + Cardinal(procLen) - s.FLineNumbers[lineIndex].VA;
|
|
|
|
r.Add(Format('%x %x %d %d', [s.FLineNumbers[lineIndex].VA + $1000 {constant entry point},
|
|
lineLen, s.FLineNumbers[lineIndex].LineNumber, UnitIndexFromVA(s.FLineNumbers[lineIndex].VA)+1]));
|
|
Inc(lineIndex);
|
|
end;
|
|
end;
|
|
|
|
r.SaveToFile(symFile);
|
|
finally
|
|
r.Free;
|
|
end;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
function CreateSymFromMap(const exeFile, mapFile, symFile, codeId: string; isAmd64: Boolean; guid: TGUID; age: Integer; findFileProc: TMapToSymFindFileProc): Boolean;
|
|
var
|
|
p: TMapToSym;
|
|
begin
|
|
if not FileExists(mapFile) then
|
|
begin
|
|
writeln('ERROR: file '+mapFile+' does not exist.');
|
|
Exit(False);
|
|
end;
|
|
|
|
p := TMapToSym.Create(mapFile, 0);
|
|
try
|
|
p.FindFileProc := findFileProc;
|
|
Result := p.Write(exeFile, symFile, codeId, isAmd64, guid, age);
|
|
finally
|
|
p.Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|