diff options
-rw-r--r-- | lib/mapfiles/map-file.ts | 40 | ||||
-rw-r--r-- | lib/pe32-support.ts (renamed from lib/pe32-support.js) | 69 | ||||
-rw-r--r-- | test/map-file-tests.js | 74 |
3 files changed, 94 insertions, 89 deletions
diff --git a/lib/mapfiles/map-file.ts b/lib/mapfiles/map-file.ts index 6c7e97de0..adf05b6b3 100644 --- a/lib/mapfiles/map-file.ts +++ b/lib/mapfiles/map-file.ts @@ -33,7 +33,7 @@ type SegmentOffset = { segmentLength: number; }; -type Segment = { +export type Segment = { segment: string; addressInt: number; address: string; @@ -103,7 +103,7 @@ export class MapFileReader { } } - getLineInfoByAddress(segment: string | boolean, address: number) { + getLineInfoByAddress(segment: string | undefined, address: number): LineNumber | undefined { for (let idx = 0; idx < this.lineNumbers.length; idx++) { const lineInfo = this.lineNumbers[idx]; if (!segment && lineInfo.addressInt === address) { @@ -113,7 +113,7 @@ export class MapFileReader { } } - return false; + return undefined; } getSegmentOffset(segment: string): number { @@ -157,7 +157,7 @@ export class MapFileReader { }); } - getSegmentInfoByUnitName(unitName: string) { + getSegmentInfoByUnitName(unitName: string): Segment | undefined { for (let idx = 0; idx < this.segments.length; idx++) { const info = this.segments[idx]; if (info.unitName === unitName) { @@ -165,10 +165,10 @@ export class MapFileReader { } } - return false; + return undefined; } - getICodeSegmentInfoByUnitName(unitName: string) { + getICodeSegmentInfoByUnitName(unitName: string): Segment | undefined { for (let idx = 0; idx < this.isegments.length; idx++) { const info = this.isegments[idx]; if (info.unitName === unitName) { @@ -176,10 +176,10 @@ export class MapFileReader { } } - return false; + return undefined; } - getSegmentIdByUnitName(unitName: string) { + getSegmentIdByUnitName(unitName: string): number | undefined { const info = this.getSegmentInfoByUnitName(unitName); if (info) { return info.id; @@ -191,7 +191,7 @@ export class MapFileReader { /** * Get Segment info for exact address */ - getSegmentInfoByStartingAddress(segment: string | boolean, address: number) { + getSegmentInfoByStartingAddress(segment: string | undefined, address: number): Segment | undefined { for (let idx = 0; idx < this.segments.length; idx++) { const info = this.segments[idx]; if (!segment && info.addressInt === address) { @@ -201,13 +201,13 @@ export class MapFileReader { } } - return false; + return undefined; } /** * Get Segment info for the segment where the given address is in */ - getSegmentInfoAddressIsIn(segment: string | boolean, address: number) { + getSegmentInfoAddressIsIn(segment: string | undefined, address: number): Segment | undefined { for (let idx = 0; idx < this.segments.length; idx++) { const info = this.segments[idx]; if (!segment && address >= info.addressInt && address < info.addressInt + info.segmentLength) { @@ -221,13 +221,13 @@ export class MapFileReader { } } - return false; + return undefined; } /** * Get Segment info for the segment where the given address is in */ - getSegmentInfoAddressWithoutOffsetIsIn(segment: string, address: number) { + getSegmentInfoAddressWithoutOffsetIsIn(segment: string, address: number): Segment | undefined { for (let idx = 0; idx < this.segments.length; idx++) { const info = this.segments[idx]; if ( @@ -239,10 +239,10 @@ export class MapFileReader { } } - return false; + return undefined; } - getSymbolAt(segment: string, address: number) { + getSymbolAt(segment: string | undefined, address: number): Segment | undefined { for (let idx = 0; idx < this.namedAddresses.length; idx++) { const info = this.namedAddresses[idx]; if (!segment && info.addressInt === address) { @@ -252,11 +252,11 @@ export class MapFileReader { } } - return false; + return undefined; } - getSymbolBefore(segment: string, address: number) { - let maxNamed: false | Segment = false; + getSymbolBefore(segment: string, address: number): Segment | undefined { + let maxNamed: Segment | undefined; for (let idx = 0; idx < this.namedAddresses.length; idx++) { const info = this.namedAddresses[idx]; @@ -274,7 +274,7 @@ export class MapFileReader { return maxNamed; } - getSymbolInfoByName(name: string) { + getSymbolInfoByName(name: string): Segment | undefined { for (let idx = 0; idx < this.namedAddresses.length; idx++) { const info = this.namedAddresses[idx]; if (info.displayName === name) { @@ -282,7 +282,7 @@ export class MapFileReader { } } - return false; + return undefined; } addressToObject(segment: string, address: string): LineNumber { diff --git a/lib/pe32-support.js b/lib/pe32-support.ts index f0cec5c7e..fa3d468b0 100644 --- a/lib/pe32-support.js +++ b/lib/pe32-support.ts @@ -22,14 +22,25 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {MapFileReader, Segment} from './mapfiles/map-file'; + export class PELabelReconstructor { - /** - * - * @param {Array} asmLines - * @param {boolean} dontLabelUnmappedAddresses - * @param {MapFileReader} mapFileReader - */ - constructor(asmLines, dontLabelUnmappedAddresses, mapFileReader, needsReconstruction = true) { + public readonly asmLines: string[]; + private readonly addressesToLabel: string[]; + private readonly dontLabelUnmappedAddresses: boolean; + private readonly needsReconstruction: boolean; + private readonly mapFileReader: MapFileReader; + private readonly addressRegex: RegExp; + private readonly jumpRegex: RegExp; + private readonly callRegex: RegExp; + private readonly int3Regex: RegExp; + + constructor( + asmLines: string[], + dontLabelUnmappedAddresses: boolean, + mapFileReader: MapFileReader, + needsReconstruction = true, + ) { this.asmLines = asmLines; this.addressesToLabel = []; this.dontLabelUnmappedAddresses = dontLabelUnmappedAddresses; @@ -47,7 +58,7 @@ export class PELabelReconstructor { * Start reconstructing labels using the mapfile and remove unneccessary assembly * */ - run(/*unitName*/) { + run(_unitName?: string) { this.mapFileReader.run(); //this.deleteEverythingBut(unitName); @@ -86,12 +97,10 @@ export class PELabelReconstructor { /** * Remove any assembly or data that isn't part of the given unit - * - * @param {string} unitName */ - deleteEverythingBut(unitName) { + deleteEverythingBut(unitName: string) { if (this.needsReconstruction) { - let unitAddressSpaces = this.mapFileReader.getReconstructedUnitAddressSpace(unitName); + const unitAddressSpaces = this.mapFileReader.getReconstructedUnitAddressSpace(unitName); for (let idx = 0; idx < this.mapFileReader.reconstructedSegments.length; idx++) { const info = this.mapFileReader.reconstructedSegments[idx]; @@ -146,12 +155,7 @@ export class PELabelReconstructor { } } - /** - * - * @param {number} beginAddress - * @param {number} endAddress - */ - deleteLinesBetweenAddresses(beginAddress, endAddress) { + deleteLinesBetweenAddresses(beginAddress: number, endAddress?: number) { let startIdx = -1; let linesRemoved = false; let lineIdx = 0; @@ -185,19 +189,16 @@ export class PELabelReconstructor { /** * Replaces an address used in a jmp or call instruction by its label. * Does not replace an address if it has an offset. - * - * @param {int} lineIdx - * @param {regex} regex */ - addAddressAsLabelAndReplaceLine(lineIdx, regex) { + addAddressAsLabelAndReplaceLine(lineIdx: number, regex: RegExp) { const line = this.asmLines[lineIdx]; - let matches = line.match(regex); + const matches = line.match(regex); if (matches) { const address = matches[3]; if (!address.includes('+') && !address.includes('-')) { let labelName = 'L' + address; - const namedAddr = this.mapFileReader.getSymbolAt(false, parseInt(address, 16)); - if (namedAddr) { + const namedAddr = this.mapFileReader.getSymbolAt(undefined, parseInt(address, 16)); + if (namedAddr && namedAddr.displayName) { labelName = namedAddr.displayName; } @@ -225,7 +226,7 @@ export class PELabelReconstructor { * if an address doesn't have a mapped name, it is called <Laddress> */ insertLabels() { - let currentSegment = false; + let currentSegment: Segment | undefined; let segmentChanged = false; let lineIdx = 0; @@ -237,21 +238,21 @@ export class PELabelReconstructor { const addressStr = matches[1]; const address = parseInt(addressStr, 16); - const segmentInfo = this.mapFileReader.getSegmentInfoByStartingAddress(false, address); + const segmentInfo = this.mapFileReader.getSegmentInfoByStartingAddress(undefined, address); if (segmentInfo) { currentSegment = segmentInfo; segmentChanged = true; } - let namedAddr = false; - let labelLine = false; + let namedAddr: Segment | undefined; + let labelLine: string | undefined; const isReferenced = this.addressesToLabel.indexOf(addressStr); if (isReferenced === -1) { // we might have missed the reference to this address, // but if it's listed as a symbol, we should still label it. // todo: the call might be in <.itext>, should we include that part of the assembly? - namedAddr = this.mapFileReader.getSymbolAt(false, address); + namedAddr = this.mapFileReader.getSymbolAt(undefined, address); if (namedAddr) { labelLine = matches[1] + ' <' + namedAddr.displayName + '>:'; @@ -261,7 +262,7 @@ export class PELabelReconstructor { } else { labelLine = matches[1] + ' <L' + addressStr + '>:'; - namedAddr = this.mapFileReader.getSymbolAt(false, address); + namedAddr = this.mapFileReader.getSymbolAt(undefined, address); if (namedAddr) { labelLine = matches[1] + ' <' + namedAddr.displayName + '>:'; } @@ -272,11 +273,11 @@ export class PELabelReconstructor { } } - const lineInfo = this.mapFileReader.getLineInfoByAddress(false, address); - if (lineInfo && currentSegment.unitName) { + const lineInfo = this.mapFileReader.getLineInfoByAddress(undefined, address); + if (lineInfo && currentSegment && currentSegment.unitName) { this.asmLines.splice(lineIdx, 0, '/app/' + currentSegment.unitName + ':' + lineInfo.lineNumber); lineIdx++; - } else if (segmentChanged) { + } else if (segmentChanged && currentSegment) { this.asmLines.splice(lineIdx, 0, '/app/' + currentSegment.unitName + ':0'); lineIdx++; } diff --git a/test/map-file-tests.js b/test/map-file-tests.js index 838bd9e72..57f8c6821 100644 --- a/test/map-file-tests.js +++ b/test/map-file-tests.js @@ -25,9 +25,13 @@ import {MapFileReaderDelphi} from '../lib/mapfiles/map-file-delphi'; import {MapFileReaderVS} from '../lib/mapfiles/map-file-vs'; +import {chai} from './utils'; + +const expect = chai.expect; + describe('Map setup', function () { it('VS-map preferred load address', function () { - const reader = new MapFileReaderVS(); + const reader = new MapFileReaderVS(''); reader.preferredLoadAddress.should.equal(0x400000, 'default load address'); reader.tryReadingPreferredAddress(' Preferred load address is 00400000'); @@ -40,27 +44,27 @@ describe('Map setup', function () { describe('Code Segments', function () { it('One normal Delphi-Map segment', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingCodeSegmentInfo(' 0001:00002838 00000080 C=CODE S=.text G=(none) M=output ACBP=A9'); reader.segments.length.should.equal(1); let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838); info.unitName.should.equal('output.pas'); - info = reader.getSegmentInfoByStartingAddress(false, reader.getSegmentOffset('0001') + 0x2838); + info = reader.getSegmentInfoByStartingAddress(undefined, reader.getSegmentOffset('0001') + 0x2838); info.unitName.should.equal('output.pas'); - info = reader.getSegmentInfoByStartingAddress('0001', '2838'); - info.should.equal(false, 'Address should not be a Start for any segment'); + info = reader.getSegmentInfoByStartingAddress('0001', 0x1234); + expect(info, 'Address should not be a Start for any segment').to.be.undefined; info = reader.getSegmentInfoAddressIsIn('0001', 0x2838 + 0x10); info.unitName.should.equal('output.pas'); - info = reader.getSegmentInfoAddressIsIn(false, reader.getSegmentOffset('0001') + 0x2838 + 0x10); + info = reader.getSegmentInfoAddressIsIn(undefined, reader.getSegmentOffset('0001') + 0x2838 + 0x10); info.unitName.should.equal('output.pas'); info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2838 + 0x80 + 1); - info.should.equal(false, 'Address should not be in any segment'); + expect(info, 'Address should not be in any segment').to.be.undefined; info = reader.getSegmentInfoByUnitName('output.pas'); info.unitName.should.equal('output.pas'); @@ -68,37 +72,37 @@ describe('Code Segments', function () { }); it('Not include this segment', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingCodeSegmentInfo(' 0002:000000B0 00000023 C=ICODE S=.itext G=(none) M=output ACBP=A9'); reader.segments.length.should.equal(0); }); it('ICode/IText segments', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingCodeSegmentInfo(' 0002:000000B0 00000023 C=ICODE S=.itext G=(none) M=output ACBP=A9'); reader.isegments.length.should.equal(1); }); it('One normal VS-Map segment', function () { - const reader = new MapFileReaderVS(); + const reader = new MapFileReaderVS(''); reader.tryReadingCodeSegmentInfo(' 0001:00002838 00000080H .text$mn CODE'); reader.segments.length.should.equal(1); let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838); info.addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); - info = reader.getSegmentInfoByStartingAddress(false, 0x403838); + info = reader.getSegmentInfoByStartingAddress(undefined, 0x403838); info.addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); - info = reader.getSegmentInfoAddressIsIn(false, reader.getSegmentOffset('0001') + 0x2838 + 0x10); + info = reader.getSegmentInfoAddressIsIn(undefined, reader.getSegmentOffset('0001') + 0x2838 + 0x10); info.addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2837); - info.should.equal(false); + expect(info).to.be.undefined; }); it('Repair VS-Map code segment info', function () { - const reader = new MapFileReaderVS(); + const reader = new MapFileReaderVS(''); reader.tryReadingCodeSegmentInfo(' 0002:00000000 00004c73H .text$mn CODE'); reader.tryReadingNamedAddress( ' 0002:000007f0 _main 004117f0 f ConsoleApplication1.obj', @@ -109,58 +113,58 @@ describe('Code Segments', function () { reader.getSegmentOffset('0002').should.equal(0x411000); - info = reader.getSegmentInfoByStartingAddress(false, 0x411000); + info = reader.getSegmentInfoByStartingAddress(undefined, 0x411000); info.unitName.should.equal('ConsoleApplication1.obj'); }); }); describe('Symbol info', function () { it('Delphi-Map symbol test', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingNamedAddress(' 0001:00002838 Square'); reader.namedAddresses.length.should.equal(1); let info = reader.getSymbolAt('0001', 0x2838); - info.should.not.equal(false, 'Symbol Square should have been returned 1'); + info.should.not.equal(undefined, 'Symbol Square should have been returned 1'); info.displayName.should.equal('Square'); - info = reader.getSymbolAt(false, reader.getSegmentOffset('0001') + 0x2838); - info.should.not.equal(false, 'Symbol Square should have been returned 2'); + info = reader.getSymbolAt(undefined, reader.getSegmentOffset('0001') + 0x2838); + info.should.not.equal(undefined, 'Symbol Square should have been returned 2'); info.displayName.should.equal('Square'); }); it('Delphi-Map D2009 symbol test', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingNamedAddress(' 0001:00002C4C output.MaxArray'); reader.namedAddresses.length.should.equal(1); let info = reader.getSymbolAt('0001', 0x2c4c); - info.should.not.equal(false, 'Symbol MaxArray should have been returned'); + info.should.not.equal(undefined, 'Symbol MaxArray should have been returned'); info.displayName.should.equal('output.MaxArray'); - - info = reader.getSymbolAt(false, reader.getSegmentOffset('0001') + 0x2c4c); - info.should.not.equal(false, 'Symbol MaxArray should have been returned'); + //todo should not be undefined + info = reader.getSymbolAt(undefined, reader.getSegmentOffset('0001') + 0x2c4c); + info.should.not.equal(undefined, 'Symbol MaxArray should have been returned'); info.displayName.should.equal('output.MaxArray'); }); it('VS-Map symbol test', function () { - const reader = new MapFileReaderVS(); + const reader = new MapFileReaderVS(''); reader.tryReadingNamedAddress( ' 0002:000006b0 ??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ 004116b0 f i ConsoleApplication1.obj', ); reader.namedAddresses.length.should.equal(1); let info = reader.getSymbolAt('0002', 0x6b0); - info.should.not.equal(false, 'Symbol start_verify_argument should have been returned 1'); + info.should.not.equal(undefined, 'Symbol start_verify_argument should have been returned 1'); info.displayName.should.equal('??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ'); - info = reader.getSymbolAt(false, 0x4116b0); - info.should.not.equal(false, 'Symbol start_verify_argument should have been returned 2'); + info = reader.getSymbolAt(undefined, 0x4116b0); + info.should.not.equal(undefined, 'Symbol start_verify_argument should have been returned 2'); info.displayName.should.equal('??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ'); }); it('Delphi-Map Duplication prevention', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingNamedAddress(' 0001:00002838 Square'); reader.namedAddresses.length.should.equal(1); @@ -171,23 +175,23 @@ describe('Symbol info', function () { describe('Delphi-Map Line number info', function () { it('No line', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingLineNumbers('').should.equal(false); }); it('One line', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader.tryReadingLineNumbers(' 17 0001:000028A4').should.equal(true); let lineInfo = reader.getLineInfoByAddress('0001', 0x28a4); lineInfo.lineNumber.should.equal(17); - lineInfo = reader.getLineInfoByAddress(false, reader.getSegmentOffset('0001') + 0x28a4); + lineInfo = reader.getLineInfoByAddress(undefined, reader.getSegmentOffset('0001') + 0x28a4); lineInfo.lineNumber.should.equal(17); }); it('Multiple lines', function () { - const reader = new MapFileReaderDelphi(); + const reader = new MapFileReaderDelphi(''); reader .tryReadingLineNumbers(' 12 0001:00002838 13 0001:0000283B 14 0001:00002854 15 0001:00002858') .should.equal(true); @@ -244,7 +248,7 @@ describe('VS-Map load test', function () { describe('VS-Map address checking', function () { it('Normal defined spaces', function () { - const reader = new MapFileReaderVS(); + const reader = new MapFileReaderVS(''); const mainAddresses = [ {startAddress: 1, startAddressHex: '00000001', endAddress: 10, endAddressHex: '0000000A'}, @@ -259,7 +263,7 @@ describe('VS-Map address checking', function () { }); it('Overlapping regions', function () { - const reader = new MapFileReaderVS(); + const reader = new MapFileReaderVS(''); const mainAddresses = [ {startAddress: 1, startAddressHex: '00000001', endAddress: 10, endAddressHex: '0000000A'}, |