Skip to content

Commit

Permalink
RISC-V: Separate invalid/internal only ELF relocs
Browse files Browse the repository at this point in the history
After ratification of the RISC-V psABI specification (version 1.0), it
is getting enhanced and improved.

This commit performs following changes.

1.  Reject unknown ELF relocation types when fed into a tool

Before this commit, it accepted unknown (but small) relocation types and
relocation types only for internal uses (linker relaxation).  More
worryingly, some internal only relocation types conflict with global
relocation types in the latest psABI draft [1].

[1] <riscv-non-isa/riscv-elf-psabi-doc@d49e480>

If (a) psABI changes conflict with internal only relocation types and/or
(b) an object (possibly malicious or just from the future) with unknown
relocation type is encountered while linking (by ld) or relocating by
other tools, it can cause a severe failure (with
unpredictable erroneous results).

This commit now rejects small unknown relocation types and internal only
ones when an ELF file with such relocation types is fed into a tool.

2.  Move internal only ELF relocation types after all regular ones

Currently, we have six internal only relocation types but only
R_RISCV_DELETE is distinguished from the regular one (others were defined
in between regular relocation types).  This design caused the conflict
with regular relocation types *and* made fixing the number of such
internal relocation a non-trivial task.

This commit moves all internal only relocation types (not only
R_RISCV_DELETE) after R_RISCV_max and creates separate howto relocation
table for range (R_RISCV_DELETE + 1..R_RISCV_internal_max).

All internal only relocations are defined relative to R_RISCV_max and
will not conflict with regular ones (if psABI started to use large numbers,
internal relocations are automatically adjusted).

3.  Prevent internal only ELF relocation types from emitting

It prevents emitting internal only relocations when the --emit-relocs
option is specified when linking (instead, replaces such internal only
relocations to R_RISCV_NONE).

bfd/ChangeLog:

	* elfnn-riscv.c (R_RISCV_DELETE): Move to elf_riscv_reloc_type.
	(riscv_info_to_howto_rela, bad_static_reloc): Reflect
	riscv_elf_rtype_to_howto changes.
	(riscv_elf_check_relocs): Likewise.  Also reject unknown relocs
	are found.  Reuse howto variable.
	(riscv_elf_relocate_section): Reflect riscv_elf_rtype_to_howto
	changesL but also look up for internal relocs only when necessary.
	Delete internal only relocation after the relocation.
	* elfxx-riscv.c (HOWTO_ISEMPTY): New macro to query whether the
	howto entry is empty.
	(howto_table): Reserve all howto entries defined by the latest
	RISC-V psABI specification except no actual EMPTY_HOWTO defs
	at the end of the list.  Move internal only relocs to...
	(howto_table_internal): ...here.
	(riscv_elf_rtype_to_howto): Add ability to look up internal only
	relocation types only when necessary.
	* elfxx-riscv.h (riscv_elf_rtype_to_howto): Reflect above.

include/ChangeLog:

	* elf/riscv.h (enum elf_riscv_reloc_type): Comment all reserved
	relocation types as defined by the latest RISC-V psABI spec.
	Move all internal relocation types after R_RISCV_max, first being
	R_RISCV_DELETE.  Add R_RISCV_internal_max.  Add safety guard for
	all relocation types on C11 and later.
  • Loading branch information
a4lg committed Oct 19, 2023
1 parent d08515a commit 9e37b98
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 99 deletions.
40 changes: 24 additions & 16 deletions bfd/elfnn-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,6 @@
} \
while (0)

/* Internal relocations used exclusively by the relaxation pass. */
#define R_RISCV_DELETE (R_RISCV_max + 1)

#define ARCH_SIZE NN

#define MINUS_ONE ((bfd_vma)0 - 1)
Expand Down Expand Up @@ -267,7 +264,8 @@ riscv_info_to_howto_rela (bfd *abfd,
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
cache_ptr->howto = riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info));
cache_ptr->howto =
riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info), false);
return cache_ptr->howto != NULL;
}

Expand Down Expand Up @@ -714,7 +712,7 @@ riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
static bool
bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
{
reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type, false);

/* We propably can improve the information to tell users that they
should be recompile the code with -fPIC or -fPIE, just like what
Expand Down Expand Up @@ -756,11 +754,17 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
{
unsigned int r_type;
unsigned int r_symndx;
reloc_howto_type *howto;
struct elf_link_hash_entry *h;
bool is_abs_symbol = false;

r_symndx = ELFNN_R_SYM (rel->r_info);
r_type = ELFNN_R_TYPE (rel->r_info);
howto = riscv_elf_rtype_to_howto (abfd, r_type, false);

/* Reject unknown relocs. */
if (!howto)
return false;

if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
{
Expand Down Expand Up @@ -916,12 +920,10 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
}

reloc_howto_type *r_t =
riscv_elf_rtype_to_howto (abfd, r_type);
_bfd_error_handler
(_("%pB: relocation %s against absolute symbol `%s' can "
"not be used when making a shared object"),
abfd, r_t ? r_t->name : _("<unknown>"), name);
abfd, howto ? howto->name : _("<unknown>"), name);
bfd_set_error (bfd_error_bad_value);
return false;
}
Expand Down Expand Up @@ -959,11 +961,10 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (is_abs_symbol)
break;

reloc_howto_type *r_t = riscv_elf_rtype_to_howto (abfd, r_type);
_bfd_error_handler
(_("%pB: relocation %s against non-absolute symbol `%s' can "
"not be used in RVNN when making a shared object"),
abfd, r_t ? r_t->name : _("<unknown>"),
abfd, howto ? howto->name : _("<unknown>"),
h != NULL ? h->root.root.string : "a local symbol");
bfd_set_error (bfd_error_bad_value);
return false;
Expand Down Expand Up @@ -996,8 +997,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
}
}

reloc_howto_type *r = riscv_elf_rtype_to_howto (abfd, r_type);
if (RISCV_NEED_DYNAMIC_RELOC (r->pc_relative, info, h, sec))
if (RISCV_NEED_DYNAMIC_RELOC (howto->pc_relative, info, h, sec))
{
struct elf_dyn_relocs *p;
struct elf_dyn_relocs **head;
Expand Down Expand Up @@ -1058,7 +1058,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
}

p->count += 1;
p->pc_count += r == NULL ? 0 : r->pc_relative;
p->pc_count += howto == NULL ? 0 : howto->pc_relative;
}

break;
Expand Down Expand Up @@ -2182,7 +2182,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
bool unresolved_reloc, is_ie = false;
bfd_vma pc = sec_addr (input_section) + rel->r_offset;
int r_type = ELFNN_R_TYPE (rel->r_info), tls_type;
reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
reloc_howto_type *howto =
riscv_elf_rtype_to_howto (input_bfd, r_type, true);
const char *msg = NULL;
bool resolved_to_zero;

Expand Down Expand Up @@ -2615,7 +2616,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
howto);
/* Update howto if relocation is changed. */
howto = riscv_elf_rtype_to_howto (input_bfd,
ELFNN_R_TYPE (rel->r_info));
ELFNN_R_TYPE (rel->r_info),
false);
if (howto == NULL)
r = bfd_reloc_notsupported;
else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
Expand Down Expand Up @@ -2766,7 +2768,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
contents, howto);
/* Update howto if relocation is changed. */
howto = riscv_elf_rtype_to_howto (input_bfd,
ELFNN_R_TYPE (rel->r_info));
ELFNN_R_TYPE (rel->r_info),
false);
if (howto == NULL)
r = bfd_reloc_notsupported;
else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
Expand Down Expand Up @@ -3008,6 +3011,11 @@ riscv_elf_relocate_section (bfd *output_bfd,
r = perform_relocation (howto, rel, relocation, input_section,
input_bfd, contents);

/* Delete internal only relocations
(overwrite with R_RISCV_NONE). */
if (r_type >= R_RISCV_max)
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);

/* We should have already detected the error and set message before.
If the error message isn't set since the linker runs out of memory
or we don't set it before, then we should set the default message
Expand Down
202 changes: 126 additions & 76 deletions bfd/elfxx-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ static bfd_reloc_status_type riscv_elf_add_sub_reloc
static bfd_reloc_status_type riscv_elf_ignore_reloc
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);

/* Check whether the given howto entry is empty (unknown). */

#define HOWTO_ISEMPTY(howto) (!((howto).name))

/* The relocation table used for SHT_RELA sections. */

static reloc_howto_type howto_table[] =
Expand Down Expand Up @@ -602,8 +606,8 @@ static reloc_howto_type howto_table[] =
false), /* pcrel_offset */

/* 41 and 42 are reserved. */
EMPTY_HOWTO (0),
EMPTY_HOWTO (0),
EMPTY_HOWTO (41),
EMPTY_HOWTO (42),

/* Indicates an alignment statement. The addend field encodes how many
bytes of NOPs follow the statement. The desired alignment is the
Expand Down Expand Up @@ -652,80 +656,20 @@ static reloc_howto_type howto_table[] =
ENCODE_CJTYPE_IMM (-1U), /* dst_mask */
true), /* pcrel_offset */

/* High 6 bits of 18-bit absolute address. */
HOWTO (R_RISCV_RVC_LUI, /* type */
0, /* rightshift */
2, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_RVC_LUI", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_CITYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
/* 46 is reserved. */
EMPTY_HOWTO (46),

/* GP-relative load. */
HOWTO (R_RISCV_GPREL_I, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_GPREL_I", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
/* Reserved for R_RISCV_GPREL_LO12_I. */
EMPTY_HOWTO (47),

/* GP-relative store. */
HOWTO (R_RISCV_GPREL_S, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_GPREL_S", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
/* Reserved for R_RISCV_GPREL_LO12_S. */
EMPTY_HOWTO (48),

/* TP-relative TLS LE load. */
HOWTO (R_RISCV_TPREL_I, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TPREL_I", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
/* Reserved for R_RISCV_GPREL_HI20. */
EMPTY_HOWTO (49),

/* TP-relative TLS LE store. */
HOWTO (R_RISCV_TPREL_S, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TPREL_S", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
/* 50 is reserved. */
EMPTY_HOWTO (50),

/* The paired relocation may be relaxed. */
HOWTO (R_RISCV_RELAX, /* type */
Expand Down Expand Up @@ -879,6 +823,101 @@ static reloc_howto_type howto_table[] =
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */

/* Reserved for R_RISCV_TLSDESC_HI20. */
/* EMPTY_HOWTO (62), */

/* Reserved for R_RISCV_TLSDESC_LOAD_LO12. */
/* EMPTY_HOWTO (63), */

/* Reserved for R_RISCV_TLSDESC_ADD_LO12. */
/* EMPTY_HOWTO (64), */

/* Reserved for R_RISCV_TLSDESC_CALL. */
/* EMPTY_HOWTO (65), */
};

/* The relocation table used for SHT_RELA sections for internal use.
Those relocations must be removed before we finish linking. */

static reloc_howto_type howto_table_internal[] =
{
/* R_RISCV_DELETE is omitted since this is special. */

/* High 6 bits of 18-bit absolute address. */
HOWTO (R_RISCV_RVC_LUI, /* type */
0, /* rightshift */
2, /* size */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_RVC_LUI", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_CITYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */

/* GP-relative load. */
HOWTO (R_RISCV_GPREL_I, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_GPREL_I", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */

/* GP-relative store. */
HOWTO (R_RISCV_GPREL_S, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_GPREL_S", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */

/* TP-relative TLS LE load. */
HOWTO (R_RISCV_TPREL_I, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TPREL_I", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */

/* TP-relative TLS LE store. */
HOWTO (R_RISCV_TPREL_S, /* type */
0, /* rightshift */
4, /* size */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TPREL_S", /* name */
false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
};

/* A mapping from BFD reloc types to RISC-V ELF reloc types. */
Expand Down Expand Up @@ -973,16 +1012,27 @@ riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
}

reloc_howto_type *
riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type,
bool lookup_internal)
{
if (r_type >= ARRAY_SIZE (howto_table))
reloc_howto_type *ret = NULL;
if (r_type < ARRAY_SIZE (howto_table))
{
ret = &howto_table[r_type];
if (HOWTO_ISEMPTY (*ret))
ret = NULL;
}
else if (lookup_internal
&& r_type > R_RISCV_DELETE
&& r_type < R_RISCV_internal_max)
ret = &howto_table_internal[r_type - (R_RISCV_DELETE + 1)];
if (!ret)
{
(*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
abfd, r_type);
bfd_set_error (bfd_error_bad_value);
return NULL;
}
return &howto_table[r_type];
return ret;
}

/* Special_function of RISCV_ADD and RISCV_SUB relocations. */
Expand Down
3 changes: 2 additions & 1 deletion bfd/elfxx-riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ extern reloc_howto_type *
riscv_reloc_type_lookup (bfd *, bfd_reloc_code_real_type);

extern reloc_howto_type *
riscv_elf_rtype_to_howto (bfd *, unsigned int r_type);
riscv_elf_rtype_to_howto (bfd *, unsigned int r_type,
bool lookup_internal);

/* The information of architecture attribute. */
struct riscv_subset_t
Expand Down
Loading

0 comments on commit 9e37b98

Please sign in to comment.