diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfSymbolNameUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfSymbolNameUtils.java new file mode 100644 index 0000000000..64f138b068 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfSymbolNameUtils.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.elf; + +import ghidra.program.model.symbol.SymbolUtilities; + +public class ElfSymbolNameUtils { + + /** + * Converts a string with possible invalid characters into a valid symbol string. + *
+ * See {@link #getBadElfSymbolStringCodePointReplacement(int, int)}
+ *
+ * @param str symbol string to fix, null ok
+ * @return original str instance if already valid, otherwise fixed value
+ */
+ public static String replaceInvalidChars(String str) {
+ return SymbolUtilities.replaceInvalidChars(str,
+ ElfSymbolNameUtils::getBadElfSymbolStringCodePointReplacement);
+ }
+
+ /**
+ * Returns a replacement value for any bad code points found in an Elf symbol string.
+ *
+ * @param index index of the bad code point in the original string
+ * @param cp the bad code point
+ * @return replacement value to use instead of the bad code point
+ */
+ public static String getBadElfSymbolStringCodePointReplacement(int index, int cp) {
+ if (cp < 0x20) {
+ // Format as ^Control character for consistency with readelf
+ // will range between ^@ .. ^_ (0..31)
+ return "^%c".formatted('@' + cp);
+ }
+ else if (cp == 0x7F) {
+ // Format as ^? character for consistency with readelf
+ return "^?";
+ }
+ else if (cp == ' ') {
+ return "_";
+ }
+ else {
+ return null; // omit the bad codepoint that caused this callback to be invoked
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/AbstractElfRelocationHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/AbstractElfRelocationHandler.java
index 0d4b18a7d2..a0d06dd15a 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/AbstractElfRelocationHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/relocation/AbstractElfRelocationHandler.java
@@ -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.
@@ -18,8 +18,7 @@ package ghidra.app.util.bin.format.elf.relocation;
import java.util.HashMap;
import java.util.Map;
-import ghidra.app.util.bin.format.elf.ElfRelocation;
-import ghidra.app.util.bin.format.elf.ElfSymbol;
+import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.BookmarkType;
@@ -103,6 +102,7 @@ abstract public class AbstractElfRelocationHandler
+ * See {@link #isInvalidCodePoint(int)} for better method that uses code points instead of
+ * chars.
+ *
* @param c the character to be tested as a valid symbol character.
- * @return return true if c is an invalid char within a symbol name, else false
+ * @return boolean true if c is an invalid char within a symbol name, else false
*/
public static boolean isInvalidChar(char c) {
- if (c < ' ') { // non-printable ASCII
- return true;
- }
+ return isInvalidCodePoint(c);
+ }
- for (char element : INVALIDCHARS) {
- if (c == element) {
+ /**
+ * Returns true if the specified code point is not valid for use in a symbol name.
+ *
+ * @param cp the code point to be tested as a valid symbol character.
+ * @return boolean true if the code point is an invalid character within a symbol name,
+ * else false
+ */
+ public static boolean isInvalidCodePoint(int cp) {
+ // Invisible / unprintable / whitespace unicode character categories.
+ // This bad list + the good list in the following comment are an exhaustive list of all
+ // unicode categories
+ switch (Character.getType(cp)) {
+ case Character.SPACE_SEPARATOR:
+ case Character.COMBINING_SPACING_MARK:
+ case Character.CONTROL:
+ case Character.ENCLOSING_MARK:
+ case Character.FORMAT:
+ case Character.LINE_SEPARATOR:
+ case Character.NON_SPACING_MARK:
+ case Character.PARAGRAPH_SEPARATOR:
+ case Character.PRIVATE_USE:
+ case Character.SURROGATE:
+ case Character.UNASSIGNED:
return true;
- }
+ /*
+ Unicode Character categories that are allowed:
+ Character.UPPERCASE_LETTER
+ Character.LOWERCASE_LETTER
+ Character.TITLECASE_LETTER
+ Character.MODIFIER_LETTER
+ Character.OTHER_LETTER
+ Character.DECIMAL_DIGIT_NUMBER
+ Character.LETTER_NUMBER
+ Character.OTHER_NUMBER
+ Character.DASH_PUNCTUATION
+ Character.START_PUNCTUATION
+ Character.END_PUNCTUATION
+ Character.CONNECTOR_PUNCTUATION
+ Character.OTHER_PUNCTUATION
+ Character.MATH_SYMBOL
+ Character.CURRENCY_SYMBOL
+ Character.MODIFIER_SYMBOL
+ Character.OTHER_SYMBOL
+ Character.INITIAL_QUOTE_PUNCTUATION
+ Character.FINAL_QUOTE_PUNCTUATION
+ */
}
return false;
}
/**
- * Removes from the given string any invalid characters or replaces
- * them with underscores.
- *
- * For example:
- * given "a:b*c", the return value would be "a_b_c"
- *
- * @param str the string to have invalid chars converted to underscores or removed.
- * @param replaceWithUnderscore - true means replace the invalid
- * chars with underscore. if false, then just drop the invalid chars
- * @return modified string
+ * Callback functional interface, called by
+ * {@link SymbolUtilities#replaceInvalidChars(String, BadCharFixupFunc)} when it encounters a
+ * bad code point that needs addressing. (good characters in a string are NOT sent to
+ * this method)
+ */
+ public interface BadCharFixupFunc {
+ String fixBadChar(int origIndex, int badCodePoint);
+ }
+
+ /**
+ * BadCharFixupFunc that replaces bad characters with '_' underscores
+ */
+ public static final BadCharFixupFunc USE_UNDERSCORES = (i, cp) -> "_";
+ /**
+ * BadCharFixupFunc that removes bad characters from the string
+ */
+ public static final BadCharFixupFunc OMIT_BAD_CHARS = (i, cp) -> null;
+
+ /**
+ * Converts a string with possible invalid characters into a valid symbol string.
+ *
+ * @param str String to fix, {@code null} ok
+ * @param replaceWithUnderscore - true means replace the invalid chars with underscores, else
+ * if false, then just drop the invalid chars
+ * @return either the original String instance if already valid (or {@code null}), or a new
+ * string that contains the valid portions of the original with any bad chars removed or
+ * replaced with underscores.
*/
public static String replaceInvalidChars(String str, boolean replaceWithUnderscore) {
+ return replaceInvalidChars(str, replaceWithUnderscore ? USE_UNDERSCORES : OMIT_BAD_CHARS);
+ }
+
+ /**
+ * Converts a string with possible invalid characters into a valid symbol string.
+ *
+ * @param str String to fix, {@code null} ok
+ * @param badCharFixup callback that controls how each bad char is fixed. It should return
+ * a string that should be used in place of the invalid character, or {@code null} if nothing
+ * should be used.
+ * @return either the original String instance if already valid (or {@code null}), or a new
+ * string that contains the valid portions of the original with any fixed-ups as returned by
+ * the badCharFixup callback.
+ */
+ public static String replaceInvalidChars(String str, BadCharFixupFunc badCharFixup) {
if (str == null) {
return null;
}
- int len = str.length();
- StringBuilder buf = new StringBuilder(len);
- for (int i = 0; i < len; ++i) {
- char c = str.charAt(i);
- if (isInvalidChar(c)) {
- if (replaceWithUnderscore) {
- buf.append(UNDERSCORE);
+ StringBuilder result = null;
+ for (int i = 0; i < str.length();) {
+ int codePoint = str.codePointAt(i);
+ if (isInvalidCodePoint(codePoint)) {
+ if (result == null) {
+ result = new StringBuilder(str.length());
+ result.append(str.substring(0, i));
+ }
+ String replacement = badCharFixup.fixBadChar(i, codePoint);
+ if (replacement != null) {
+ result.append(replacement);
}
}
- else {
- buf.append(c);
+ else if (result != null) {
+ result.appendCodePoint(codePoint);
}
+ i += Character.charCount(codePoint);
}
- return buf.toString();
+ return result != null ? result.toString() : str;
}
/**