Merge remote-tracking branch 'origin/GP-1725_ghizard_Add_second_pass_MDMang_processing_for_nonstandard_mangled_forms--SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-02-04 00:28:15 -05:00
commit e59d58e7fe
15 changed files with 312 additions and 41 deletions

View file

@ -100,7 +100,8 @@ public class MDEncodedNumber extends MDParsableItem {
}
}
else {
throw new MDException("Illegal character in MDEncodedNumber: " + dmang.peek());
throw new MDException("Illegal character at index " + dmang.getIndex() +
" in MDEncodedNumber: " + dmang.peek());
}
}
}

View file

@ -45,6 +45,30 @@ public class MDMang {
protected List<MDContext> contextStack = new ArrayList<>();
public enum ProcessingMode {
DEFAULT_STANDARD, LLVM
}
private ProcessingMode processingMode = ProcessingMode.DEFAULT_STANDARD;
public void setProcessingMode(ProcessingMode processingMode) {
this.processingMode = processingMode;
}
public ProcessingMode getProcessingMode() {
return processingMode;
}
public boolean isProcessingModeActive(ProcessingMode mode) {
switch (mode) {
case LLVM:
return processingMode == mode && (getIndex() == 0);
default:
break;
}
return processingMode == mode;
}
/**
* Demangles the string passed in.
*
@ -69,14 +93,15 @@ public class MDMang {
* @param errorOnRemainingChars
* boolean flag indicating whether remaining characters causes an
* error.
* @return item detected and parsed
* @throws MDException upon error parsing item
*/
public MDParsableItem demangle(boolean errorOnRemainingChars) throws MDException {
if (mangled == null) {
throw new MDException("MDMang: Mangled string is null.");
}
pushContext();
item = MDMangObjectParser.parse(this);
item.parse();
item = MDMangObjectParser.determineItemAndParse(this);
if (item instanceof MDObjectCPP) {
// MDMANG SPECIALIZATION USED.
item = getEmbeddedObject((MDObjectCPP) item);
@ -163,13 +188,21 @@ public class MDMang {
/**
* Returns the current index.
*
* @return the current index.
*/
public int getIndex() {
return iter.getIndex();
}
/**
* Sets the current index.
* @param index the position to set.
* @throws IllegalArgumentException if index is not in range from 0 to string.length()-1
*/
public void setIndex(int index) {
iter.setIndex(index);
}
/**
* Returns the next character without incrementing the current index.
*

View file

@ -64,11 +64,7 @@ public class MDMangGenericize extends MDMang {
throw new MDException("MDMang: Mangled string is null.");
}
pushContext();
item = MDMangObjectParser.parse(this);
if (item != null) {
item.parse();
}
int numCharsRemaining = getNumCharsRemaining();
item = MDMangObjectParser.determineItemAndParse(this);
appendRemainder();
popContext();
// if (errorOnRemainingChars && (numCharsRemaining > 0)) {
@ -108,7 +104,7 @@ public class MDMangGenericize extends MDMang {
* genericizedString. Suggested use is to use peek() and next() when not
* wanting to add the character, but to use getAndIncrement() when wanting
* to add the character.
*
*
* @return the character at the new position or DONE
*/
@Override
@ -121,7 +117,7 @@ public class MDMangGenericize extends MDMang {
* by one. If the resulting index is greater or equal to the end index, the
* current index is reset to the end index and a value of DONE is returned.
* Also adds the character to the genericizedString.
*
*
* @return the character at the new position or DONE
*/
@Override
@ -145,7 +141,7 @@ public class MDMangGenericize extends MDMang {
* Increments the index by count. Does no testing for whether the index
* surpasses the length of the string. Also does internal processing
* for creating a genericized String.
*
*
* @param count
* number of characters to move ahead
*/

View file

@ -17,6 +17,7 @@ package mdemangler.naming;
import ghidra.util.Msg;
import mdemangler.*;
import mdemangler.MDMang.ProcessingMode;
import mdemangler.object.MDObjectCPP;
import mdemangler.template.MDTemplateNameAndArguments;
@ -172,7 +173,11 @@ public class MDBasicName extends MDParsableItem {
@Override
protected void parseInternal() throws MDException {
// First pass can only have name fragment of special name
if (dmang.peek() == '?') {
if (dmang.isProcessingModeActive(ProcessingMode.LLVM)) {
specialName = new MDSpecialName(dmang, 0);
specialName.parse();
}
else if (dmang.peek() == '?') {
if (dmang.peek(1) == '$') {
templateNameAndArguments = new MDTemplateNameAndArguments(dmang);
templateNameAndArguments.parse();

View file

@ -16,6 +16,7 @@
package mdemangler.naming;
import mdemangler.*;
import mdemangler.MDMang.ProcessingMode;
import mdemangler.datatype.MDDataTypeParser;
import mdemangler.object.MDObjectCPP;
@ -117,6 +118,15 @@ public class MDSpecialName extends MDParsableItem {
@Override
protected void parseInternal() throws MDException {
if (dmang.isProcessingModeActive(ProcessingMode.LLVM)) {
parseLLVM();
}
else {
parseDefaultStandard();
}
}
protected void parseDefaultStandard() throws MDException {
isQualified = true;
switch (dmang.getAndIncrement()) {
case '0':
@ -650,7 +660,7 @@ public class MDSpecialName extends MDParsableItem {
dmang.parseInfoPop();
break;
case 'J':
dmang.parseInfoPush(3, "thread guard");
dmang.parseInfoPush(3, "local static thread guard");
name = "`local static thread guard'";
dmang.parseInfoPop();
break;
@ -688,6 +698,69 @@ public class MDSpecialName extends MDParsableItem {
break;
}
}
// Seemingly LLVM-specific. Breaks the "norm" of MSFT model we have been following.
// The output format is our creation (trying to follow MSFT convention).
// The "?$" prefix on these are templates in MSFT's reserved space and could collide
// the a template symbol under the MSFT scheme. Maybe LLVM will eventually fix these???
// I could be wrong in that MSFT also honors this scheme, but it seems whacked in that it
// doesn't conform to the rest of their scheme.
// Following the model of MSFT Guard output strings even though the mangled form does not
// follow MSFT's scheme. Change is that we are not outputting the extraneous tick as is seen
// in the middle of `local static guard'{2}', but we are still increasing the string value
// that is in braces by one from the coded value. Thus, we are outputting
// `thread safe static guard{1}' for "?$TSS0@". We can reconsider this later.
public void parseLLVM() throws MDException {
if (dmang.positionStartsWith("?$TSS")) {
dmang.parseInfoPush(0, "thread safe static guard");
dmang.increment("?$TSS".length());
String guardNumberString = getNumberString();
dmang.parseInfoPop();
name = "`thread safe static guard{" + guardNumberString + "}'";
}
else if (dmang.positionStartsWith("?$S1@")) {
// The '1' in "?$S1" is currently hard-coded in the LLVM code, but I believe we
// should still enclose it in braces... subject to change.
dmang.parseInfoPush(0, "nonvisible static guard");
dmang.increment("?$S1@".length());
name = "`nonvisible static guard{1}'";
dmang.parseInfoPop();
}
else if (dmang.positionStartsWith("?$RT")) {
dmang.parseInfoPush(0, "reference temporary");
dmang.increment("?$RT".length());
String manglingNumberString = getNumberString();
dmang.parseInfoPop();
name = "`reference temporary{" + manglingNumberString + "}'";
}
else {
throw new MDException("Could not match NonStandard Special Name");
}
}
/**
* Get Number (it is output as Number << '@' where Number is an unsigned int, so we are
* capturing it as a string of digits, terminated with an '@' character.
* Built for what seems to be LLVM-specific mangling. Does not follow MSFT model.
* @return a the Number represented by a String (decimal).
* @throws MDException Upon invalid character sequence or out of characters.
*/
private String getNumberString() throws MDException {
char ch;
StringBuilder builder = new StringBuilder();
dmang.parseInfoPush(0, "Number");
while ((ch = dmang.peek()) != '@') {
if (!Character.isDigit(ch)) { // includes end of string (MDMang.DONE)
throw new MDException("Illegal character in Number: " + ch);
}
builder.append(ch);
dmang.next();
}
dmang.next(); // '@'
dmang.parseInfoPop();
return builder.toString();
}
}
/******************************************************************************/

View file

@ -16,6 +16,7 @@
package mdemangler.object;
import mdemangler.*;
import mdemangler.MDMang.ProcessingMode;
import mdemangler.datatype.MDDataTypeParser;
import mdemangler.template.MDTemplateNameAndArguments;
@ -25,8 +26,50 @@ import mdemangler.template.MDTemplateNameAndArguments;
*/
public class MDMangObjectParser {
public static MDParsableItem parse(MDMang dmang) throws MDException {
public static MDParsableItem determineItemAndParse(MDMang dmang) throws MDException {
boolean retry = false;
MDException firstException = null;
MDParsableItem myItem = null;
int index = dmang.getIndex();
try {
myItem = parseDefaultStandard(dmang);
if (myItem != null) {
myItem.parse();
}
else {
retry = true;
}
}
catch (MDException e) {
retry = true;
myItem = null;
firstException = e;
}
if (!retry) {
return myItem;
}
try {
dmang.setIndex(index);
myItem = parseLlvm(dmang);
if (myItem != null) {
myItem.parse();
}
}
catch (MDException e) {
if (firstException != null) {
throw firstException;
}
throw e;
}
if (myItem == null && firstException != null) {
throw firstException;
}
return myItem;
}
public static MDParsableItem parseDefaultStandard(MDMang dmang) throws MDException {
MDParsableItem item;
dmang.setProcessingMode(ProcessingMode.DEFAULT_STANDARD);
if (dmang.peek() == '?') {
if (dmang.peek(1) == '@') {
item = new MDObjectCodeView(dmang);
@ -52,6 +95,17 @@ public class MDMangObjectParser {
return item;
}
public static MDParsableItem parseLlvm(MDMang dmang) {
// Might eliminate next test if we create other "non-standard" processing
// that does not begin with "?$".
if (dmang.peek() != '?' && dmang.peek(1) != '$') {
return null;
}
dmang.setProcessingMode(ProcessingMode.LLVM);
MDObjectCPP item = new MDObjectCPP(dmang);
return item;
}
// Thus far, we have seen (created from forward code example):
// __mep => [MEP], which is presumably "Managed Entry Point"
// __t2m => [T2M], which is presumably "Transition to Managed (code)"
@ -96,7 +150,7 @@ public class MDMangObjectParser {
* @throws MDException Upon <b><code>MDMang</code></b> parsing issues that cause us to fail
* processing.
*/
public static MDParsableItem parseObjectReserved(MDMang dmang) throws MDException {
public static MDParsableItem parseObjectReserved(MDMang dmang) {
MDParsableItem item;
if (dmang.positionStartsWith("__TI")) {
dmang.increment("__TI".length());

View file

@ -17,6 +17,7 @@ package mdemangler.object;
import ghidra.util.Msg;
import mdemangler.*;
import mdemangler.MDMang.ProcessingMode;
import mdemangler.functiontype.MDFunctionType;
import mdemangler.naming.*;
import mdemangler.typeinfo.MDTypeInfo;
@ -119,7 +120,13 @@ public class MDObjectCPP extends MDObject {
if (dmang.peek() != '?') {
throw new MDException("Invalid ObjectCPP");
}
dmang.increment();
if (!dmang.isProcessingModeActive(ProcessingMode.LLVM)) {
// If not LLVM mode, then the '?' seen above is valid as being part of this MDObjectCPP
// parsing, so we should consume it. If, on the other hand, we are in LLVM mode, then
// this '?' is currently part of the possible nonstandard mangling forms that are
// output by LLVM mangling and they are consumed there.
dmang.increment();
}
if ((dmang.peek(0) == '?') && (dmang.peek(1) == '?')) { //??? prefix
embeddedObjectFlag = true;
}

View file

@ -43,8 +43,7 @@ public class MDObjectCatch extends MDObjectReserved {
@Override
protected void parseInternal() throws MDException {
internalItem = MDMangObjectParser.parse(dmang);
internalItem.parse();
internalItem = MDMangObjectParser.determineItemAndParse(dmang);
dmang.increment(); // '$'
//We are assuming that we can have more than one digit.
//TODO: forward programming to test beyond one digit.

View file

@ -37,14 +37,12 @@ public class MDObjectUnwindFunclet extends MDObjectReserved {
@Override
public void insert(StringBuilder builder) {
super.insert(builder);
dmang.appendString(builder,
"[UnwindFunclet," + digits + "]{" + internalItem + "}");
dmang.appendString(builder, "[UnwindFunclet," + digits + "]{" + internalItem + "}");
}
@Override
protected void parseInternal() throws MDException {
internalItem = MDMangObjectParser.parse(dmang);
internalItem.parse();
internalItem = MDMangObjectParser.determineItemAndParse(dmang);
dmang.increment(); // '$'
//Here, we have seen $9 and $10 (two digits for $10).
//TODO: forward programming to test beyond one digit.

View file

@ -43,8 +43,16 @@ public class MDTypeInfo extends MDParsableItem {
_NOT_SPECIFIED, _STATIC, _VIRTUAL
}
/**
* Enum representing pointer format.
*/
enum PointerFormat {
_NOT_SPECIFIED, _NEAR, _FAR //HUGE not present in mangling.
}
private StorageClass storage = StorageClass._NOT_SPECIFIED;
private AccessSpecifier access = AccessSpecifier._NOT_SPECIFIED;
private PointerFormat pointerFormat = PointerFormat._NOT_SPECIFIED;
private boolean isThunk = false;
private boolean isMember = true;
private boolean isExternC = false;
@ -103,6 +111,18 @@ public class MDTypeInfo extends MDParsableItem {
return (storage == StorageClass._VIRTUAL);
}
public void setPointerFormat(PointerFormat pointerFormat) {
this.pointerFormat = pointerFormat;
}
public boolean isNear() {
return (pointerFormat == PointerFormat._NEAR);
}
public boolean isFar() {
return (pointerFormat == PointerFormat._FAR);
}
public void setThunk() {
isThunk = true;
}

View file

@ -18,6 +18,7 @@ package mdemangler.typeinfo;
import mdemangler.MDException;
import mdemangler.MDMang;
import mdemangler.datatype.modifier.MDBasedAttribute;
import mdemangler.typeinfo.MDTypeInfo.PointerFormat;
/**
* This class parses the mangled string at the current offset to determine and
@ -143,11 +144,13 @@ public class MDTypeInfoParser {
// hasArgs = false; //no reason to have set true
// dmang.parseInfoPop();
break;
case 'A':
case 'A': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
case 'B':
dmang.increment();
typeInfo = new MDMemberFunctionInfo(dmang);
typeInfo.setPrivate();
typeInfo.setPointerFormat(
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case 'C':
case 'D':
@ -168,12 +171,16 @@ public class MDTypeInfoParser {
dmang.increment();
typeInfo = new MDVFAdjustor(dmang);
typeInfo.setPrivate();
typeInfo.setPointerFormat(
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case 'I':
case 'I': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
case 'J':
dmang.increment();
typeInfo = new MDMemberFunctionInfo(dmang);
typeInfo.setProtected();
typeInfo.setPointerFormat(
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case 'K':
case 'L':
@ -194,12 +201,16 @@ public class MDTypeInfoParser {
dmang.increment();
typeInfo = new MDVFAdjustor(dmang);
typeInfo.setProtected();
typeInfo.setPointerFormat(
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case 'Q':
case 'Q': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
case 'R':
dmang.increment();
typeInfo = new MDMemberFunctionInfo(dmang);
typeInfo.setPublic();
typeInfo.setPointerFormat(
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case 'S':
case 'T':
@ -220,6 +231,8 @@ public class MDTypeInfoParser {
dmang.increment();
typeInfo = new MDVFAdjustor(dmang);
typeInfo.setPublic();
typeInfo.setPointerFormat(
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case 'Y':
case 'Z':
@ -269,16 +282,22 @@ public class MDTypeInfoParser {
case '1':
typeInfo = new MDVtordisp(dmang);
typeInfo.setPrivate();
typeInfo.setPointerFormat(
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case '2':
case '3':
typeInfo = new MDVtordisp(dmang);
typeInfo.setProtected();
typeInfo.setPointerFormat(
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case '4':
case '5':
typeInfo = new MDVtordisp(dmang);
typeInfo.setPublic();
typeInfo.setPointerFormat(
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
break;
case '$':
char ch2 = dmang.getAndIncrement();

View file

@ -33,8 +33,10 @@ public class MDVtordisp extends MDMemberFunctionInfo {
@Override
protected void parseInternal() throws MDException {
// 20200507: Believe this to be <offset-to-vtordisp>
MDEncodedNumber vtorDisplacement = new MDEncodedNumber(dmang);
vtorDisplacement.parse();
// 20200507: Believe this to be <static-offset>
MDEncodedNumber adjustment = new MDEncodedNumber(dmang);
adjustment.parse();
nameModifier = "`vtordisp{" + vtorDisplacement + "," + adjustment + "}' ";

View file

@ -33,16 +33,16 @@ public class MDVtordispex extends MDMemberFunctionInfo {
@Override
protected void parseInternal() throws MDException {
// TODO: what is this? Possibly the displacement?
// 20200507: Believe this to be <offset-to-vptr>
MDEncodedNumber a = new MDEncodedNumber(dmang);
a.parse();
// TODO: what is this? Possibly the adjustment?
// 20200507: Believe this to be <vbase-offset-offset>
MDEncodedNumber b = new MDEncodedNumber(dmang);
b.parse();
// TODO: what is this?
// 20200507: Believe this to be <offset-to-vtordisp>
MDEncodedNumber c = new MDEncodedNumber(dmang);
c.parse();
// TODO: what is this?
// 20200507: Believe this to be <static-offset>
MDEncodedNumber d = new MDEncodedNumber(dmang);
d.parse();
nameModifier = "`vtordispex{" + a + "," + b + "," + c + "," + d + "}' ";

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -198,6 +198,7 @@ public class MDBaseTestConfiguration {
demangled = demangItem.toString();
}
catch (MDException e) {
Msg.info(this, "Could not demangle: " + mangled, e);
demangItem = null;
demangled = "";
}

View file

@ -14487,24 +14487,87 @@ public class MDMangBaseTest extends AbstractGenericTest {
demangleAndTest();
}
//TODO: considering for Issue 1162
@Ignore
// real symbol (Issue #1162)
// Following the model of MSFT Guard output strings even though the mangled form does not
// follow MSFT's scheme. Change is that we are not outputting the extraneous tick as is seen
// in the middle of `local static guard'{2}', and we are not increasing the string value
// that is in braces by one from the coded "GuardNum" value. Thus, we are outputting
// `thread safe static guard{0}' for "?$TSS0@". We can reconsider this later.
@Test
public void testThreadSafeStaticGuard_1() throws Exception {
mangled =
"?$TSS0@?1??GetCategoryMap@CDynamicRegistrationInfoSource@XPerfAddIn@@SAPEBU_ATL_CATMAP_ENTRY@ATL@@XZ@4HA";
// mangled =
// "?xTSS0@?1??GetCategoryMap@CDynamicRegistrationInfoSource@XPerfAddIn@@SAPEBU_ATL_CATMAP_ENTRY@ATL@@XZ@4HA";
//TODO: investigate and consider what we should have as outputs.
msTruth = "";
mdTruth =
"int `public: static struct ATL::_ATL_CATMAP_ENTRY const * __ptr64 __cdecl XPerfAddIn::CDynamicRegistrationInfoSource::GetCategoryMap(void)'::`2'::`thread safe local static guard'";
"int `public: static struct ATL::_ATL_CATMAP_ENTRY const * __ptr64 __cdecl XPerfAddIn::CDynamicRegistrationInfoSource::GetCategoryMap(void)'::`2'::`thread safe static guard{0}'";
//TODO: Create MDMangVS2015 Specialization for this problem and then remove "mstruth = mdtruth"
msTruth = mdTruth;
demangleAndTest();
}
@Test
public void testSimpleMainTemplateAsCounterpointToThreadSafeStaticGuard() throws Exception {
mangled = "?$TSS0@HH"; // Begins with same pattern as ThreadSafeStaticGuard
msTruth = "TSS0<int,int>";
mdTruth = msTruth;
demangleAndTest();
}
// Manufactured by modifying "??_B?1??name0@name1@name2@@KAHPEBGAEAG@Z@51" which is a
// `local static guard'{2}'. We eliminated the closing 51 that makes it an MDGuard typeinfo
// with value of 2 (1+1). We are also eliminating the extraneous middle closing tick (single
// quote) that MSFT has in their output. We are not incrementing the value of ManglingNumber
// that we are putting in braces (unlike other MSFT guard numbers). Thus, we will output
// `nonvisible static guard{1}' for "?$S1@". We can reconsider this later. We also tacked
// on the "4HA" as is done for the `thread safe static guard' so that it is an "int".
// TODO: Watch for real symbol of this type "?$S1@".
@Test
public void testNonvisibleStaticGuard() throws Exception {
mangled = "?$S1@?1??name0@name1@name2@@KAHPEBGAEAG@Z@4HA";
mdTruth =
"int `protected: static int __cdecl name2::name1::name0(unsigned short const * __ptr64,unsigned short & __ptr64)'::`2'::`nonvisible static guard{1}'";
//TODO: Create MDMangVS2015 Specialization for this problem and then remove "mstruth = mdtruth"
msTruth = mdTruth;
demangleAndTest();
}
@Test
public void testSimpleMainTemplateAsCounterpointToNonvisibleStaticGuard() throws Exception {
mangled = "?$S1@HH"; // Begins with same pattern as ThreadSafeStaticGuard
msTruth = "S1<int,int>";
mdTruth = msTruth;
demangleAndTest();
}
// Manufactured by modifying "??_B?1??name0@name1@name2@@KAHPEBGAEAG@Z@51" which is a
// `local static guard'{2}'. We eliminated the closing 51 that makes it an MDGuard typeinfo
// with value of 2 (1+1). We are also eliminating the extraneous middle closing tick (single
// quote) that MSFT has in their output. We are not incrementing the value of "number"
// that we are putting in braces (unlike other MSFT guard numbers). Thus, we will output
// `reference temporary{1}'" for the "?$RT1@". We can reconsider this later. We also tacked
// on the "4HA" as is done for the `thread safe static guard' so that it is an "int".
// TODO: Watch for real symbol of this type "?$RTnum@".
@Test
public void testReferenceTemporary() throws Exception {
mangled = "?$RT1@?1??name0@name1@name2@@KAHPEBGAEAG@Z@4HA";
mdTruth =
"int `protected: static int __cdecl name2::name1::name0(unsigned short const * __ptr64,unsigned short & __ptr64)'::`2'::`reference temporary{1}'";
//TODO: Create MDMangVS2015 Specialization for this problem and then remove "mstruth = mdtruth"
msTruth = mdTruth;
demangleAndTest();
}
@Test
public void testSimpleMainTemplateAsCounterpointToReferenceTemporary() throws Exception {
mangled = "?$RT1@HH"; // Begins with same pattern as ThreadSafeStaticGuard
msTruth = "RT1<int,int>";
mdTruth = msTruth;
demangleAndTest();
}
//Issue 1344: Long symbols get MD5-hashed.
// We have made up the output format. Nothing is sacrosanct about this output.
@Test
public void testHashedSymbolComponentsLongerThan5096_1() throws Exception {
public void testHashedSymbolComponentsLongerThan4096_1() throws Exception {
mangled = "??@f4873c94f485cd6716c2319fc51ac714@";
msTruth = "";
mdTruth = "`f4873c94f485cd6716c2319fc51ac714'";
@ -14514,7 +14577,7 @@ public class MDMangBaseTest extends AbstractGenericTest {
//Issue 1344: Long symbols get MD5-hashed.
// We have made up the output format. Nothing is sacrosanct about this output.
@Test
public void testHashedSymbolComponentsLongerThan5096_2() throws Exception {
public void testHashedSymbolComponentsLongerThan4096_2() throws Exception {
mangled = "?catch$0@?0???@f4873c94f485cd6716c2319fc51ac714@@4HA";
msTruth = "";
mdTruth = "int ``f4873c94f485cd6716c2319fc51ac714''::`1'::catch$0";