Skip to content

Commit

Permalink
Support for the GNU note with the AARCH64 CPU features (#527)
Browse files Browse the repository at this point in the history
* Support for AARCH64 features GNU note

* Missing D_TAG

* Missing D_TAG, extra GNU CFA opcode

* DW_CFA_AARCH64_negate_ra_state

---------

Co-authored-by: Seva Alekseyev <sevaa@nih.gov>
  • Loading branch information
sevaa and Seva Alekseyev authored Dec 8, 2023
1 parent 8b97f5d commit 596b065
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 29 deletions.
3 changes: 2 additions & 1 deletion elftools/dwarf/callframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def _parse_instructions(self, structs, offset, end_offset):
args = [primary_arg]
# primary == 0 and real opcode is extended
elif opcode in (DW_CFA_nop, DW_CFA_remember_state,
DW_CFA_restore_state):
DW_CFA_restore_state, DW_CFA_AARCH64_negate_ra_state):
args = []
elif opcode == DW_CFA_set_loc:
args = [
Expand Down Expand Up @@ -236,6 +236,7 @@ def _parse_instructions(self, structs, offset, end_offset):
struct_parse(structs.Dwarf_sleb128(''), self.stream)]
elif opcode == DW_CFA_GNU_args_size:
args = [struct_parse(structs.Dwarf_uleb128(''), self.stream)]

else:
dwarf_assert(False, 'Unknown CFI opcode: 0x%x' % opcode)

Expand Down
2 changes: 2 additions & 0 deletions elftools/dwarf/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@
DW_CFA_val_offset = 0x14
DW_CFA_val_offset_sf = 0x15
DW_CFA_val_expression = 0x16
DW_CFA_GNU_window_save = 0x2d # Used on SPARC, not in the corpus
DW_CFA_AARCH64_negate_ra_state = 0x2d
DW_CFA_GNU_args_size = 0x2e


Expand Down
2 changes: 1 addition & 1 deletion elftools/dwarf/descriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _full_reg_name(regnum):
name, factored_offset, factored_offset + pc)
pc += factored_offset
elif name in ( 'DW_CFA_remember_state', 'DW_CFA_restore_state',
'DW_CFA_nop'):
'DW_CFA_nop', 'DW_CFA_AARCH64_negate_ra_state'):
s += ' %s\n' % name
elif name == 'DW_CFA_def_cfa':
s += ' %s: %s ofs %s\n' % (
Expand Down
46 changes: 20 additions & 26 deletions elftools/elf/descriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def describe_ver_flags(x):
VER_FLAGS.VER_FLG_INFO) if x & flag)


def describe_note(x):
def describe_note(x, machine):
n_desc = x['n_desc']
desc = ''
if x['n_type'] == 'NT_GNU_ABI_TAG':
Expand All @@ -215,7 +215,7 @@ def describe_note(x):
elif x['n_type'] == 'NT_GNU_GOLD_VERSION':
desc = '\n Version: %s' % (n_desc)
elif x['n_type'] == 'NT_GNU_PROPERTY_TYPE_0':
desc = '\n Properties: ' + describe_note_gnu_properties(x['n_desc'])
desc = '\n Properties: ' + describe_note_gnu_properties(x['n_desc'], machine)
else:
desc = '\n description data: {}'.format(bytes2hex(n_desc))

Expand Down Expand Up @@ -269,30 +269,14 @@ def describe_attr_tag_riscv(tag, val, extra):
else:
return _DESCR_ATTR_TAG_RISCV[tag] + d_entry[val]



def describe_note_gnu_property_x86_feature_1(value):
descs = []
for mask, desc in _DESCR_NOTE_GNU_PROPERTY_X86_FEATURE_1_FLAGS:
if value & mask:
descs.append(desc)
return 'x86 feature: ' + ', '.join(descs)

def describe_note_gnu_property_x86_feature_2_used(value):
descs = []
for mask, desc in _DESCR_NOTE_GNU_PROPERTY_X86_FEATURE_2_FLAGS:
if value & mask:
descs.append(desc)
return 'x86 feature used: ' + ', '.join(descs)

def describe_note_gnu_property_x86_isa_1(value, verb):
def describe_note_gnu_property_bitmap_and(values, prefix, value):
descs = []
for mask, desc in _DESCR_NOTE_GNU_PROPERTY_X86_ISA_1_FLAGS:
for mask, desc in values:
if value & mask:
descs.append(desc)
return 'x86 ISA %s: %s' % (verb, ', '.join(descs))
return '%s: %s' % (prefix, ', '.join(descs))

def describe_note_gnu_properties(properties):
def describe_note_gnu_properties(properties, machine):
descriptions = []
for prop in properties:
t, d, sz = prop.pr_type, prop.pr_data, prop.pr_datasz
Expand All @@ -310,22 +294,27 @@ def describe_note_gnu_properties(properties):
if sz != 4:
prop_desc = ' <corrupt length: 0x%x>' % sz
else:
prop_desc = describe_note_gnu_property_x86_feature_1(d)
prop_desc = describe_note_gnu_property_bitmap_and(_DESCR_NOTE_GNU_PROPERTY_X86_FEATURE_1_FLAGS, 'x86 feature', d)
elif t == 'GNU_PROPERTY_X86_FEATURE_2_USED':
if sz != 4:
prop_desc = ' <corrupt length: 0x%x>' % sz
else:
prop_desc = describe_note_gnu_property_x86_feature_2_used(d)
prop_desc = describe_note_gnu_property_bitmap_and(_DESCR_NOTE_GNU_PROPERTY_X86_FEATURE_2_FLAGS, 'x86 feature used', d)
elif t == 'GNU_PROPERTY_X86_ISA_1_NEEDED':
if sz != 4:
prop_desc = ' <corrupt length: 0x%x>' % sz
else:
prop_desc = describe_note_gnu_property_x86_isa_1(d, "needed")
prop_desc = describe_note_gnu_property_bitmap_and(_DESCR_NOTE_GNU_PROPERTY_X86_ISA_1_FLAGS, 'x86 ISA needed', d)
elif t == 'GNU_PROPERTY_X86_ISA_1_USED':
if sz != 4:
prop_desc = ' <corrupt length: 0x%x>' % sz
else:
prop_desc = describe_note_gnu_property_x86_isa_1(d, "used")
prop_desc = describe_note_gnu_property_bitmap_and(_DESCR_NOTE_GNU_PROPERTY_X86_ISA_1_FLAGS, 'x86 ISA used', d)
elif t == 'GNU_PROPERTY_AARCH64_FEATURE_1_AND' and machine == 'EM_AARCH64':
if sz != 4:
prop_desc = ' <corrupt length: 0x%x>' % sz
else:
prop_desc = describe_note_gnu_property_bitmap_and(_DESCR_NOTE_GNU_PROPERTY_AARCH64_FEATURE_1_AND, 'aarch64 feature', d)
elif _DESCR_NOTE_GNU_PROPERTY_TYPE_LOPROC <= t <= _DESCR_NOTE_GNU_PROPERTY_TYPE_HIPROC:
prop_desc = '<processor-specific type 0x%x data: %s >' % (t, bytes2hex(d, sep=' '))
elif _DESCR_NOTE_GNU_PROPERTY_TYPE_LOUSER <= t <= _DESCR_NOTE_GNU_PROPERTY_TYPE_HIUSER:
Expand Down Expand Up @@ -673,6 +662,11 @@ def describe_note_gnu_properties(properties):
# TODO; there is a long list
)

# Same for GNU_PROPERTY_AARCH64_FEATURE_1_AND
_DESCR_NOTE_GNU_PROPERTY_AARCH64_FEATURE_1_AND = (
(1, 'bti'),
(2, 'pac'),
)

def _reverse_dict(d, low_priority=()):
"""
Expand Down
6 changes: 6 additions & 0 deletions elftools/elf/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,10 @@
DT_MIPS_RLD_MAP_REL=0x70000035,
)

ENUM_D_TAG_AARCH64 = dict(
DT_AARCH64_BTI_PLT=0x70000001,
)

# Here is the mapping from e_machine enum to the extra dynamic tags which it
# validates. Solaris is missing from this list because its inclusion is not
# controlled by e_machine but rather e_ident[EI_OSABI].
Expand All @@ -628,6 +632,7 @@
ENUMMAP_EXTRA_D_TAG_MACHINE = dict(
EM_MIPS=ENUM_D_TAG_MIPS,
EM_MIPS_RS3_LE=ENUM_D_TAG_MIPS,
EM_AARCH64=ENUM_D_TAG_AARCH64
)

# Here is the full combined mapping from tag name to value
Expand Down Expand Up @@ -1065,6 +1070,7 @@
GNU_PROPERTY_X86_ISA_1_NEEDED=0xc0008002,
GNU_PROPERTY_X86_FEATURE_2_USED=0xc0010001,
GNU_PROPERTY_X86_ISA_1_USED=0xc0010002,
GNU_PROPERTY_AARCH64_FEATURE_1_AND=0xc0000000,
_default_=Pass,
)

Expand Down
3 changes: 3 additions & 0 deletions elftools/elf/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ def classify_pr_data(ctx):
return None
if ctx.pr_type.startswith('GNU_PROPERTY_X86_'):
return ('GNU_PROPERTY_X86_*', 4, 0)
elif ctx.pr_type.startswith('GNU_PROPERTY_AARCH64_'):
return ('GNU_PROPERTY_AARCH64_*', 4, 0)
return (ctx.pr_type, ctx.pr_datasz, self.elfclass)

self.Elf_Prop = Struct('Elf_Prop',
Expand All @@ -413,6 +415,7 @@ def classify_pr_data(ctx):
('GNU_PROPERTY_STACK_SIZE', 4, 32): self.Elf_word('pr_data'),
('GNU_PROPERTY_STACK_SIZE', 8, 64): self.Elf_word64('pr_data'),
('GNU_PROPERTY_X86_*', 4, 0): self.Elf_word('pr_data'),
('GNU_PROPERTY_AARCH64_*', 4, 0): self.Elf_word('pr_data'),
},
default=Field('pr_data', lambda ctx: ctx.pr_datasz)
),
Expand Down
4 changes: 3 additions & 1 deletion scripts/readelf.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ def display_dynamic_tags(self):
elif tag.entry.d_tag in ('DT_MIPS_SYMTABNO',
'DT_MIPS_LOCAL_GOTNO'):
parsed = str(tag.entry.d_val)
elif tag.entry.d_tag == 'DT_AARCH64_BTI_PLT':
parsed = ''
else:
parsed = '%#x' % tag['d_val']

Expand All @@ -573,7 +575,7 @@ def display_notes(self):
self._emitline(' %s %s\t%s' % (
note['n_name'].ljust(20),
self._format_hex(note['n_descsz'], fieldsize=8),
describe_note(note)))
describe_note(note, self.elffile.header.e_machine)))

def display_relocations(self):
""" Display the relocations contained in the file
Expand Down
Binary file added test/testfiles_for_readelf/aarch64-pac-bti.elf
Binary file not shown.

0 comments on commit 596b065

Please sign in to comment.