Skip to content

Commit

Permalink
Fix the LoongArch support code and some more (#483)
Browse files Browse the repository at this point in the history
* Fix LoongArch support in dwarfdump.py

The e_machine constant is EM_LOONGARCH, and the emulation name is just
elf{32,64}-loongarch without the endian prefix.

Fixes: 6c36d79 ("add support for loongarch64 to dwarfdump (#458)")
Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* Fix the EF_LOONGARCH_* symbol names

The current code gets the logic right, but not the symbol names. Fix
them for consistency with the canonical definition that's binutils.

Fixes: 2059475 ("Add support for LoongArch (#470)")
Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* Fix a missing description string in elffile.py for LoongArch

Fixes: 2059475 ("Add support for LoongArch (#470)")
Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* Provide EF_LARCH_* name aliases for the EF_LOONGARCH_* constants

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* Add definitions for LoongArch relocations

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* Add support for basic 32- and 64-bit LoongArch relocations

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* scripts/readelf.py: Properly format control chars in symbol names

This is necessary to match readelf behavior on fake symbol names, that
usually look like "L0^A" when rendered (being "L0\x01" in reality).

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* scripts/readelf.py: Fix register display order in _dump_debug_frames_interp

According to binutils sources (function frame_display_row in
binutils/dwarf.c), the apparent ordering of the ra register after other
registers is merely a side effect of most architectures allocating a
larger DWARF register number for their respective ra registers. This has
no effect on all readelf test cases, but is necessary for a future
LoongArch test binary to pass comparisons.

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

* Add test program and artifact covering basic LoongArch relocations

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>

---------

Signed-off-by: WANG Xuerui <xen0n@gentoo.org>
  • Loading branch information
xen0n authored Sep 6, 2023
1 parent c9e558f commit 20cc45c
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 30 deletions.
23 changes: 16 additions & 7 deletions elftools/elf/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,22 @@ class E_FLAGS(object):
EF_RISCV_RVE=0x00000008
EF_RISCV_TSO=0x00000010

EF_LOONGARCH_ABI=0x000000C0
EF_LOONGARCH_ABI_V0=0x00000000
EF_LOONGARCH_ABI_V1=0x00000040
EF_LOONGARCH_FLOAT_ABI=0x00000003
EF_LOONGARCH_FLOAT_ABI_SOFT=0x00000001
EF_LOONGARCH_FLOAT_ABI_SINGLE=0x00000002
EF_LOONGARCH_FLOAT_ABI_DOUBLE=0x00000003
EF_LOONGARCH_OBJABI_MASK=0x000000C0
EF_LOONGARCH_OBJABI_V0=0x00000000
EF_LOONGARCH_OBJABI_V1=0x00000040
EF_LOONGARCH_ABI_MODIFIER_MASK=0x00000007
EF_LOONGARCH_ABI_SOFT_FLOAT=0x00000001
EF_LOONGARCH_ABI_SINGLE_FLOAT=0x00000002
EF_LOONGARCH_ABI_DOUBLE_FLOAT=0x00000003
# The names in the glibc elf.h say "LARCH" instead of "LOONGARCH",
# provide these names for users' convenience.
EF_LARCH_OBJABI_MASK = EF_LOONGARCH_OBJABI_MASK
EF_LARCH_OBJABI_V0 = EF_LOONGARCH_OBJABI_V0
EF_LARCH_OBJABI_V1 = EF_LOONGARCH_OBJABI_V1
EF_LARCH_ABI_MODIFIER_MASK = EF_LOONGARCH_ABI_MODIFIER_MASK
EF_LARCH_ABI_SOFT_FLOAT = EF_LOONGARCH_ABI_SOFT_FLOAT
EF_LARCH_ABI_SINGLE_FLOAT = EF_LOONGARCH_ABI_SINGLE_FLOAT
EF_LARCH_ABI_DOUBLE_FLOAT = EF_LOONGARCH_ABI_DOUBLE_FLOAT

class E_FLAGS_MASKS(object):
"""Masks to be used for convenience when working with E_FLAGS
Expand Down
5 changes: 4 additions & 1 deletion elftools/elf/descriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64,
ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_PPC64,
ENUM_RELOC_TYPE_MIPS, ENUM_ATTR_TAG_ARM, ENUM_ATTR_TAG_RISCV,
ENUM_DT_FLAGS, ENUM_DT_FLAGS_1)
ENUM_RELOC_TYPE_LOONGARCH, ENUM_DT_FLAGS, ENUM_DT_FLAGS_1)
from .constants import (
P_FLAGS, RH_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS, VER_FLAGS)
from ..common.utils import bytes2hex
Expand Down Expand Up @@ -152,6 +152,8 @@ def describe_reloc_type(x, elffile):
return _DESCR_RELOC_TYPE_PPC64.get(x, _unknown)
elif arch == 'MIPS':
return _DESCR_RELOC_TYPE_MIPS.get(x, _unknown)
elif arch == 'LoongArch':
return _DESCR_RELOC_TYPE_LOONGARCH.get(x, _unknown)
else:
return 'unrecognized: %-7x' % (x & 0xFFFFFFFF)

Expand Down Expand Up @@ -690,6 +692,7 @@ def _reverse_dict(d, low_priority=()):
_DESCR_RELOC_TYPE_AARCH64 = _reverse_dict(ENUM_RELOC_TYPE_AARCH64)
_DESCR_RELOC_TYPE_PPC64 = _reverse_dict(ENUM_RELOC_TYPE_PPC64)
_DESCR_RELOC_TYPE_MIPS = _reverse_dict(ENUM_RELOC_TYPE_MIPS)
_DESCR_RELOC_TYPE_LOONGARCH = _reverse_dict(ENUM_RELOC_TYPE_LOONGARCH)

_low_priority_D_TAG = (
# these are 'meta-tags' marking semantics of numeric ranges of the enum
Expand Down
1 change: 1 addition & 0 deletions elftools/elf/elffile.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ def get_machine_arch(self):
'EM_RISCV' : 'RISC-V',
'EM_BPF' : 'Linux BPF - in-kernel virtual machine',
'EM_CSKY' : 'C-SKY',
'EM_LOONGARCH' : 'LoongArch',
'EM_FRV' : 'Fujitsu FR-V'
}

Expand Down
103 changes: 103 additions & 0 deletions elftools/elf/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,109 @@
_default_=Pass,
)

# https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc
ENUM_RELOC_TYPE_LOONGARCH = dict(
R_LARCH_NONE=0,
R_LARCH_32=1,
R_LARCH_64=2,
R_LARCH_RELATIVE=3,
R_LARCH_COPY=4,
R_LARCH_JUMP_SLOT=5,
R_LARCH_TLS_DTPMOD32=6,
R_LARCH_TLS_DTPMOD64=7,
R_LARCH_TLS_DTPREL32=8,
R_LARCH_TLS_DTPREL64=9,
R_LARCH_TLS_TPREL32=10,
R_LARCH_TLS_TPREL64=11,
R_LARCH_IRELATIVE=12,
R_LARCH_MARK_LA=20,
R_LARCH_MARK_PCREL=21,
R_LARCH_SOP_PUSH_PCREL=22,
R_LARCH_SOP_PUSH_ABSOLUTE=23,
R_LARCH_SOP_PUSH_DUP=24,
R_LARCH_SOP_PUSH_GPREL=25,
R_LARCH_SOP_PUSH_TLS_TPREL=26,
R_LARCH_SOP_PUSH_TLS_GOT=27,
R_LARCH_SOP_PUSH_TLS_GD=28,
R_LARCH_SOP_PUSH_PLT_PCREL=29,
R_LARCH_SOP_ASSERT=30,
R_LARCH_SOP_NOT=31,
R_LARCH_SOP_SUB=32,
R_LARCH_SOP_SL=33,
R_LARCH_SOP_SR=34,
R_LARCH_SOP_ADD=35,
R_LARCH_SOP_AND=36,
R_LARCH_SOP_IF_ELSE=37,
R_LARCH_SOP_POP_32_S_10_5=38,
R_LARCH_SOP_POP_32_U_10_12=39,
R_LARCH_SOP_POP_32_S_10_12=40,
R_LARCH_SOP_POP_32_S_10_16=41,
R_LARCH_SOP_POP_32_S_10_16_S2=42,
R_LARCH_SOP_POP_32_S_5_20=43,
R_LARCH_SOP_POP_32_S_0_5_10_16_S2=44,
R_LARCH_SOP_POP_32_S_0_10_10_16_S2=45,
R_LARCH_SOP_POP_32_U=46,
R_LARCH_ADD8=47,
R_LARCH_ADD16=48,
R_LARCH_ADD24=49,
R_LARCH_ADD32=50,
R_LARCH_ADD64=51,
R_LARCH_SUB8=52,
R_LARCH_SUB16=53,
R_LARCH_SUB24=54,
R_LARCH_SUB32=55,
R_LARCH_SUB64=56,
R_LARCH_GNU_VTINHERIT=57,
R_LARCH_GNU_VTENTRY=58,
R_LARCH_B16=64,
R_LARCH_B21=65,
R_LARCH_B26=66,
R_LARCH_ABS_HI20=67,
R_LARCH_ABS_LO12=68,
R_LARCH_ABS64_LO20=69,
R_LARCH_ABS64_HI12=70,
R_LARCH_PCALA_HI20=71,
R_LARCH_PCALA_LO12=72,
R_LARCH_PCALA64_LO20=73,
R_LARCH_PCALA64_HI12=74,
R_LARCH_GOT_PC_HI20=75,
R_LARCH_GOT_PC_LO12=76,
R_LARCH_GOT64_PC_LO20=77,
R_LARCH_GOT64_PC_HI12=78,
R_LARCH_GOT_HI20=79,
R_LARCH_GOT_LO12=80,
R_LARCH_GOT64_LO20=81,
R_LARCH_GOT64_HI12=82,
R_LARCH_TLS_LE_HI20=83,
R_LARCH_TLS_LE_LO12=84,
R_LARCH_TLS_LE64_LO20=85,
R_LARCH_TLS_LE64_HI12=86,
R_LARCH_TLS_IE_PC_HI20=87,
R_LARCH_TLS_IE_PC_LO12=88,
R_LARCH_TLS_IE64_PC_LO20=89,
R_LARCH_TLS_IE64_PC_HI12=90,
R_LARCH_TLS_IE_HI20=91,
R_LARCH_TLS_IE_LO12=92,
R_LARCH_TLS_IE64_LO20=93,
R_LARCH_TLS_IE64_HI12=94,
R_LARCH_TLS_LD_PC_HI20=95,
R_LARCH_TLS_LD_HI20=96,
R_LARCH_TLS_GD_PC_HI20=97,
R_LARCH_TLS_GD_HI20=98,
R_LARCH_32_PCREL=99,
R_LARCH_RELAX=100,
R_LARCH_DELETE=101,
R_LARCH_ALIGN=102,
R_LARCH_PCREL20_S2=103,
R_LARCH_CFA=104,
R_LARCH_ADD6=105,
R_LARCH_SUB6=106,
R_LARCH_ADD_ULEB128=107,
R_LARCH_SUB_ULEB128=108,
R_LARCH_64_PCREL=109,
_default_=Pass,
)

# Sunw Syminfo Bound To special values
ENUM_SUNW_SYMINFO_BOUNDTO = dict(
SYMINFO_BT_SELF=0xffff,
Expand Down
56 changes: 55 additions & 1 deletion elftools/elf/relocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .enums import (
ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, ENUM_RELOC_TYPE_MIPS,
ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_PPC64,
ENUM_RELOC_TYPE_BPF, ENUM_D_TAG)
ENUM_RELOC_TYPE_BPF, ENUM_RELOC_TYPE_LOONGARCH, ENUM_D_TAG)
from ..construct import Container


Expand Down Expand Up @@ -259,6 +259,11 @@ def _do_apply_relocation(self, stream, reloc, symtab):
recipe = self._RELOCATION_RECIPES_PPC64.get(reloc_type, None)
elif self.elffile.get_machine_arch() == 'Linux BPF - in-kernel virtual machine':
recipe = self._RELOCATION_RECIPES_EBPF.get(reloc_type, None)
elif self.elffile.get_machine_arch() == 'LoongArch':
if not reloc.is_RELA():
raise ELFRelocationError(
'Unexpected REL relocation for LoongArch: %s' % reloc)
recipe = self._RELOCATION_RECIPES_LOONGARCH.get(reloc_type, None)

if recipe is None:
raise ELFRelocationError(
Expand All @@ -273,6 +278,10 @@ def _do_apply_relocation(self, stream, reloc, symtab):
value_struct = self.elffile.structs.Elf_word('')
elif recipe.bytesize == 8:
value_struct = self.elffile.structs.Elf_word64('')
elif recipe.bytesize == 1:
value_struct = self.elffile.structs.Elf_byte('')
elif recipe.bytesize == 2:
value_struct = self.elffile.structs.Elf_half('')
else:
raise ELFRelocationError('Invalid bytesize %s for relocation' %
recipe.bytesize)
Expand Down Expand Up @@ -322,6 +331,9 @@ def _reloc_calc_sym_plus_addend(value, sym_value, offset, addend=0):
def _reloc_calc_sym_plus_addend_pcrel(value, sym_value, offset, addend=0):
return sym_value + addend - offset

def _reloc_calc_value_minus_sym_addend(value, sym_value, offset, addend=0):
return value - sym_value - addend

def _arm_reloc_calc_sym_plus_value_pcrel(value, sym_value, offset, addend=0):
return sym_value // 4 + value - offset // 4

Expand Down Expand Up @@ -416,4 +428,46 @@ def _bpf_64_32_reloc_calc_sym_plus_addend(value, sym_value, offset, addend=0):
bytesize=4, has_addend=False, calc_func=_reloc_calc_identity),
}

# https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc
_RELOCATION_RECIPES_LOONGARCH = {
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_NONE']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=False, calc_func=_reloc_calc_identity),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_32']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=True,
calc_func=_reloc_calc_sym_plus_addend),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_64']: _RELOCATION_RECIPE_TYPE(
bytesize=8, has_addend=True,
calc_func=_reloc_calc_sym_plus_addend),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_ADD8']: _RELOCATION_RECIPE_TYPE(
bytesize=1, has_addend=True,
calc_func=_reloc_calc_sym_plus_value),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_SUB8']: _RELOCATION_RECIPE_TYPE(
bytesize=1, has_addend=True,
calc_func=_reloc_calc_value_minus_sym_addend),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_ADD16']: _RELOCATION_RECIPE_TYPE(
bytesize=2, has_addend=True,
calc_func=_reloc_calc_sym_plus_value),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_SUB16']: _RELOCATION_RECIPE_TYPE(
bytesize=2, has_addend=True,
calc_func=_reloc_calc_value_minus_sym_addend),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_ADD32']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=True,
calc_func=_reloc_calc_sym_plus_value),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_SUB32']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=True,
calc_func=_reloc_calc_value_minus_sym_addend),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_ADD64']: _RELOCATION_RECIPE_TYPE(
bytesize=8, has_addend=True,
calc_func=_reloc_calc_sym_plus_value),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_SUB64']: _RELOCATION_RECIPE_TYPE(
bytesize=8, has_addend=True,
calc_func=_reloc_calc_value_minus_sym_addend),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_32_PCREL']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=True,
calc_func=_reloc_calc_sym_plus_addend_pcrel),
ENUM_RELOC_TYPE_LOONGARCH['R_LARCH_64_PCREL']: _RELOCATION_RECIPE_TYPE(
bytesize=8, has_addend=True,
calc_func=_reloc_calc_sym_plus_addend_pcrel),
}


2 changes: 1 addition & 1 deletion scripts/dwarfdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def __init__(self, filename, file, output):
self.elffile = ELFFile(file)
self.output = output
self._dwarfinfo = self.elffile.get_dwarf_info()
arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH64": "loongarch64", "EM_RISCV": "littleriscv", "EM_MIPS": "mips"}
arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH": "loongarch", "EM_RISCV": "littleriscv", "EM_MIPS": "mips"}
arch = arches[self.elffile['e_machine']]
bits = self.elffile.elfclass
self._emitline("%s: file format elf%d-%s" % (filename, bits, arch))
Expand Down
44 changes: 24 additions & 20 deletions scripts/readelf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#-------------------------------------------------------------------------------
import argparse
import os, sys
import re
import string
import traceback
import itertools
Expand Down Expand Up @@ -95,6 +96,13 @@ def _get_cu_base(cu):
else:
raise ValueError("Can't find the base IP (low_pc) for a CU")

# Matcher for all control characters, for transforming them into "^X" form when
# formatting symbol names for display.
_CONTROL_CHAR_RE = re.compile(r'[\x01-\x1f]')

def _format_symbol_name(s):
return _CONTROL_CHAR_RE.sub(lambda match: '^' + chr(0x40 + ord(match[0])), s)

class ReadElf(object):
""" display_* methods are used to emit output into the output stream
"""
Expand Down Expand Up @@ -260,15 +268,15 @@ def decode_flags(self, flags):
description += ", quad-float ABI"

elif self.elffile['e_machine'] == "EM_LOONGARCH":
if (flags & E_FLAGS.EF_LOONGARCH_FLOAT_ABI) == E_FLAGS.EF_LOONGARCH_FLOAT_ABI_SOFT:
if (flags & E_FLAGS.EF_LOONGARCH_ABI_MODIFIER_MASK) == E_FLAGS.EF_LOONGARCH_ABI_SOFT_FLOAT:
description += ", SOFT-FLOAT"
if (flags & E_FLAGS.EF_LOONGARCH_FLOAT_ABI) == E_FLAGS.EF_LOONGARCH_FLOAT_ABI_SINGLE:
if (flags & E_FLAGS.EF_LOONGARCH_ABI_MODIFIER_MASK) == E_FLAGS.EF_LOONGARCH_ABI_SINGLE_FLOAT:
description += ", SINGLE-FLOAT"
if (flags & E_FLAGS.EF_LOONGARCH_FLOAT_ABI) == E_FLAGS.EF_LOONGARCH_FLOAT_ABI_DOUBLE:
if (flags & E_FLAGS.EF_LOONGARCH_ABI_MODIFIER_MASK) == E_FLAGS.EF_LOONGARCH_ABI_DOUBLE_FLOAT:
description += ", DOUBLE-FLOAT"
if (flags & E_FLAGS.EF_LOONGARCH_ABI) == E_FLAGS.EF_LOONGARCH_ABI_V0:
if (flags & E_FLAGS.EF_LOONGARCH_OBJABI_MASK) == E_FLAGS.EF_LOONGARCH_OBJABI_V0:
description += ", OBJ-v0"
if (flags & E_FLAGS.EF_LOONGARCH_ABI) == E_FLAGS.EF_LOONGARCH_ABI_V1:
if (flags & E_FLAGS.EF_LOONGARCH_OBJABI_MASK) == E_FLAGS.EF_LOONGARCH_OBJABI_V1:
description += ", OBJ-v1"

return description
Expand Down Expand Up @@ -495,7 +503,7 @@ def display_symbol_tables(self):
describe_symbol_shndx(self._get_symbol_shndx(symbol,
nsym,
section_index)),
symbol_name,
_format_symbol_name(symbol_name),
version_info))

def display_dynamic_tags(self):
Expand Down Expand Up @@ -632,7 +640,7 @@ def display_relocations(self):
self._format_hex(
symbol['st_value'],
fullhex=True, lead0x=False),
symbol_name))
_format_symbol_name(symbol_name)))
if section.is_RELA():
self._emit(' %s %x' % (
'+' if rel['r_addend'] >= 0 else '-',
Expand Down Expand Up @@ -1451,24 +1459,20 @@ def _dump_frames_interp_info(self, section, cfi_entries):

# Look at the registers the decoded table describes.
# We build reg_order here to match readelf's order. In particular,
# registers are sorted by their number, and the register matching
# ra_regnum is always listed last with a special heading.
# registers are sorted by their number, so that the register
# matching ra_regnum is usually listed last with a special heading.
# (LoongArch is a notable exception in that its return register's
# DWARF register number is not greater than other GPRs.)
decoded_table = entry.get_decoded()
reg_order = sorted(filter(
lambda r: r != ra_regnum,
decoded_table.reg_order))
reg_order = sorted(decoded_table.reg_order)
if len(decoded_table.reg_order):

# Headings for the registers
for regnum in reg_order:
if regnum == ra_regnum:
self._emit('ra ')
continue
self._emit('%-6s' % describe_reg_name(regnum))
self._emitline('ra ')

# Now include ra_regnum in reg_order to print its values
# similarly to the other registers.
reg_order.append(ra_regnum)
else:
self._emitline()
self._emitline()

for line in decoded_table.table:
self._emit(self._format_hex(
Expand Down
18 changes: 18 additions & 0 deletions test/testfiles_for_readelf/loongarch-relocs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* This source was compiled for LoongArch64.
loongarch64-unknown-linux-gnu-gcc -c -o loongarch64-relocs.o.elf loongarch-relocs.c -g
Upstream support for LoongArch32 is not yet mature, so it is not covered.
*/

extern struct {
int i, j;
} data;

extern int bar (void);

int
foo (int a)
{
data.i += a;
data.j -= bar();
return 0;
}
Binary file not shown.

0 comments on commit 20cc45c

Please sign in to comment.