Skip to content

Instantly share code, notes, and snippets.

@Bktero
Last active January 14, 2020 09:27
Show Gist options
  • Save Bktero/3124aa0ee78a2fb80e557c05cfe77960 to your computer and use it in GitHub Desktop.
Save Bktero/3124aa0ee78a2fb80e557c05cfe77960 to your computer and use it in GitHub Desktop.
[Python 3] Use objdump to compute the footprint of a set of symbols in an elf file
import re
import sys
def text_contains(text, *patterns):
""" Check if 'text' contains at least one of the strings given in 'patterns'."""
for p in patterns:
if re.search(p, text, re.IGNORECASE):
# One pattern was found so the text contains at least of them
# So it is true that "it contains"
return True
return False
def text_doesnt_contain(text, *patterns):
""" Check if 'text' does NOT contain any of the strings given in 'patterns'."""
for p in patterns:
# if re.search(p, text, re.IGNORECASE):
# # cannot be used here because we are looking for *ABS* dans * has a meaning for regexp...
if text.find(p) != -1:
# One pattern was found so the text contains at least of them
# So it is false that "it doesn't contain"
return False
return True
def human(size):
""" Convert a number of bytes to a human readeable string, in the form 'x.y ko'.
See the -h option of various commands from the GCC tool collection.
"""
unit = "o"
if size > 1024:
size /= 1024
unit = "ko"
if size > 1024:
size /= 1024
unit = "Mo"
return str(size) + " " + unit
def trace(*elements):
"""Print element (and is easy to modify to remove traces)."""
# print(*elements)
def footprint(filename, *patterns):
""" Compute the footprint of symbols that match one of the patterns in an elf file.
Print the matching symbols with their details and the total size.
Note: objdump must be in the path
"""
# Get the symbol table of the ELF file
from subprocess import check_output
result = check_output(["objdump", "-t", filename])
# Convert to UTF-8 text
result = result.decode("utf-8")
# Split into a list of lines
result = result.split("\r\n")
# Look for interesting elements
# Exemple of item:
# 08039578 g O .rodata 00000023 acGUI_FontMontserrat_BLACK_17px_0076
# Note: it seems that different versions of objdump will produce different line format
# Here is another example (not supported by the code below):
# 08013890 g O P1 ro 00000060 kerning_CompatilFactLTCom_Rg_16_2bpp
# Consequence? The field indexes after spliting the lines may have to be changed
interesting = list()
for r in result:
if text_contains(r, *patterns) and text_doesnt_contain(r, "*ABS*"):
interesting.append(r)
print("Interesting line:", r)
# Extract sizes from interesting elements
flash = 0
ram = 0
bad_line_count = 0
for i in interesting:
trace('---')
try:
fields = i.split()
trace("Fields: ", fields)
kind = fields[3]
trace("Kind: ", kind)
size = int(fields[4], base=16)
trace("Size :", size)
# https://mcuoneclipse.com/2013/04/14/text-data-and-bss-code-and-data-size-explained/
if kind == "ro" or kind == ".text" or kind == ".rodata":
flash += size
elif kind == "zi" or kind == ".bss":
ram += size
elif kind == ".data":
flash += size
ram += size
else:
print("Bad line: ", i)
print("Unknown kind of memory:", kind)
bad_line_count += 1
except Exception as e:
print("Bad line:", i)
print("Exception is", e)
bad_line_count += 1
print("(", bad_line_count, "lines of out ", len(interesting), "were rejected)")
print("flash", human(flash))
print("ram", human(ram))
if __name__ == '__main__':
elf = r"path/to/elf/file"
if len(sys.argv) == 1:
patterns = "list", "your", "patterns"
else:
patterns = sys.argv
print("Footprint for patterns:", patterns)
footprint(elf, *patterns)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment