diff options
author | Jeremy Compostella <jeremy.compostella@intel.com> | 2023-09-07 20:26:56 -0700 |
---|---|---|
committer | Matt DeVillier <matt.devillier@amd.corp-partner.google.com> | 2023-10-20 14:32:20 +0000 |
commit | 621ccf8a975de10a641ba36c9f8065b7fb659611 (patch) | |
tree | 85a7f3e5642a6cc3ea310ad03b9377aab11f9ea6 /util/cbfstool | |
parent | 7f1f2973c543b7b16ca98d39382eee060ee5ae0c (diff) |
cbfstool: Skip relocation entries pointing to undefined symbol
The linker can make relocation entries of a symbol which has a value
of zero point to the undefined symbol entry. It is permitted since
when the symbol value is zero as the documentation of the relocation
entry `r_info' field states:
"If the index is STN_UNDEF, the undefined symbol index, the relocation
uses 0 as the symbol value."
The ELF binary does not really have any missing symbols. It is an
optimization as the symbol points to the undefined symbol because its
value is zero.
A typical way to hit this cbfstool limitation is to define an empty
region using the REGION macro in the linker script. Here is an
example if we assume `CONFIG_MY_REGION' is set to 0:
.car.data {
[...]
REGION(my_region, CONFIG_MY_REGION_SIZE)
[...]
}
A region is defined as follow:
#define REGION_SIZE(name) ((size_t)_##name##_size)
#define DECLARE_REGION(name) \
extern u8 _##name[]; \
extern u8 _e##name[]; \
extern u8 _##name##_size[];
So the size of the region is actually the address of the
`_##name##_size' symbol. Therefore, the `_my_region_size' symbol
address is zero and the linker can make the relocation entry of this
symbol point to the undefined symbol index.
In such a situation, cbfstool hits a segmentation fault when it
attempts to relocate the symbol in `parse_elf_to_xip_stage()'
function. We resolves this issue by making cbfstool skips relocation
entries pointing to the undefined symbol similarly to the way it skips
relocation relative to absolute symbols. A symbol which value is zero
can be considered an absolute symbol and therefore should not be
relocated.
Of course, we could argue that we could just prevent the declaration
of an empty region as illustrated in the following example:
.car.data {
[...]
#if CONFIG_MY_REGION_SIZE > 0
REGION(my_region, CONFIG_MY_REGION_SIZE)
#endif
[...]
}
However, this is not a satisfying solution because:
1. It requires to add unnecessary code in the linker script as an empty
region is a valid declaration. Such a workaround requires the code
using it to mark the region symbols as weak symbols to handle the
situation where the region is not defined.
2. There could be other situations which have yet to be uncovered which
would lead the same cbfstool crash.
3. A binary with an empty region is a valid ELF file and cbfstool
should not crash when it is asked to create an eXecute-In-Place stage
out of it.
Change-Id: I2803fd3e96e7ff7a0b22d72d50bfbce7acaeb941
Signed-off-by: Jeremy Compostella <jeremy.compostella@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/77699
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Diffstat (limited to 'util/cbfstool')
-rw-r--r-- | util/cbfstool/rmodule.c | 16 |
1 files changed, 16 insertions, 0 deletions
diff --git a/util/cbfstool/rmodule.c b/util/cbfstool/rmodule.c index b21f777cd2..d47639c8c1 100644 --- a/util/cbfstool/rmodule.c +++ b/util/cbfstool/rmodule.c @@ -168,6 +168,19 @@ static int relocation_for_weak_extern_symbols(struct rmod_context *ctx, Elf64_Re return 0; } +static int relocation_for_undefined_symbol(struct rmod_context *ctx, Elf64_Rela *r) +{ + Elf64_Sym *s = &ctx->pelf.syms[ELF64_R_SYM(r->r_info)]; + + if (s->st_shndx == SHN_UNDEF) { + DEBUG("Omitting relocation for undefined symbol: %s\n", + &ctx->strtab[s->st_name]); + return 1; + } + + return 0; +} + /* * Relocation processing loops. */ @@ -213,6 +226,9 @@ static int for_each_reloc(struct rmod_context *ctx, struct reloc_filter *f, if (relocation_for_weak_extern_symbols(ctx, r)) continue; + if (relocation_for_undefined_symbol(ctx, r)) + continue; + /* Allow the provided filter to have precedence. */ if (f != NULL) { filter_emit = f->filter(f, r); |