blob: 84f4e16ba7ab193e93bf123b6e5d2c14022d4d5d [file] [log] [blame]
#
# Copyright (c) 2021 Project CHIP Authors
#
# 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.
#
"""Collect memory information using elftools."""
import pathlib
from elftools.elf.elffile import ELFFile # type: ignore
import elftools.elf.descriptions # type: ignore
import elftools.elf.sections # type: ignore
import elftools.elf.constants # type: ignore
import memdf.name
from memdf.collector.util import simplify_source
from memdf.util.config import Config, ConfigDescription
from memdf.df import DFs, SegmentDF, SectionDF, SymbolDF
CONFIG: ConfigDescription = {}
def read_segments(config: Config, ef: ELFFile) -> SegmentDF:
"""Read a segment table from an ELFFile."""
columns = ['type', 'vaddress', 'paddress', 'size', 'flags']
rows = []
for segment in ef.iter_segments():
rows.append([
segment['p_type'],
segment['p_vaddr'], segment['p_paddr'], segment['p_memsz'],
segment['p_flags']
])
return SegmentDF(rows, columns=columns)
def read_sections(config: Config, ef: ELFFile) -> SectionDF:
"""Read a section table from an ELFFile."""
columns = ['section', 'type', 'address', 'size', 'flags', 'segment']
index = []
rows = []
for i, section in enumerate(ef.iter_sections()):
index.append(i)
segment_number = -1
for j, segment in enumerate(ef.iter_segments()):
if segment.section_in_segment(section):
segment_number = j
break
rows.append([
section.name,
elftools.elf.descriptions.describe_sh_type(section['sh_type']),
section['sh_addr'], section['sh_size'], section['sh_flags'],
segment_number
])
return SectionDF(rows, index=index, columns=columns)
def read_symbols(config: Config, ef: ELFFile, sections: SectionDF) -> SymbolDF:
"""Read a symbol table from an ELFFile."""
section_map = dict(sections.section)
section_map.update({
0: memdf.name.UNDEF,
'SHN_UNDEF': memdf.name.UNDEF,
'SHN_ABS': memdf.name.ABS
})
columns = ['symbol', 'address', 'size', 'section', 'type', 'bind']
rows = []
for section_id, section in enumerate(ef.iter_sections()):
if not isinstance(section, elftools.elf.sections.SymbolTableSection):
continue
for symbol_id, symbol in enumerate(section.iter_symbols()):
st_type = elftools.elf.descriptions.describe_symbol_type(
symbol['st_info']['type'])
st_bind = elftools.elf.descriptions.describe_symbol_bind(
symbol['st_info']['bind'])
st_shndx = symbol['st_shndx'] # TBD: look up indirect segment ids
rows.append([
symbol.name, # column: 'symbol'
symbol['st_value'], # column: 'address'
symbol['st_size'], # column: 'size'
section_map.get(st_shndx,
memdf.name.UNKNOWN), # column: 'section'
st_type, # column: 'type'
st_bind, # column: 'bind'
])
return SymbolDF(rows, columns=columns)
def cu_offset_to_path_map(config: Config, dwarf_info):
"""Return a map from Dwarf compilation unit offsets to source paths."""
prefixes = config.get_re('collect.prefix')
address_map = {}
for compilation_unit in dwarf_info.iter_CUs():
path = pathlib.Path(compilation_unit.get_top_DIE().get_full_path())
source = simplify_source(str(path.resolve()), prefixes)
address_map[compilation_unit.cu_offset] = source
return address_map
def read_file(config: Config, filename: str, method: str = None) -> DFs:
"""Collect memory information using elftools."""
with open(filename, 'rb') as fp:
ef = ELFFile(fp)
segments = read_segments(config, ef)
sections = read_sections(config, ef)
symbols = read_symbols(config, ef, sections)
if config['args.need_cu']:
dwarf_info = ef.get_dwarf_info()
aranges = dwarf_info.get_aranges()
m = cu_offset_to_path_map(config, dwarf_info)
symbols['cu'] = symbols['address'].apply(lambda a: m.get(
aranges.cu_offset_at_addr(a), '')).astype('string')
if config['args.tag_inputs']:
symbols['input'] = filename
return {
SegmentDF.name: segments,
SectionDF.name: sections,
SymbolDF.name: symbols
}